How to sleep in bare-metal 16 bit x86 assembly?
Asked Answered
B

1

7

I want to halt the execution about 0.1 seconds, regardless of the clock speed of the CPU. The code should run directly from a boot device, it should therefore not use DOS interrupts.

I'm currently using int 15h, but this seems to conflict with the beep tones I'm modulating with channel 2 of the PIT. I heard of channel 0, but I have no clue about how to set this up.

The accuracy is not that important, however, it should run on old and modern computers at the same speed. So just looping instructions is not an option.

The beep code and the sleep are just a bunch of macros for changing the frequency and turning the speaker on and off. The beep seems to not stop if I call sleep right before beepoff.

Here are the beep macros:

%macro beepinit 0
    mov al, 182
    out 43h, al
%endmacro

%macro beepfreq 0
    out 42h, al
    mov al, ah
    out 42h, al
%endmacro

%macro beepon 0
    in al, 61h
    or al, 00000011b
    out 61h, al
%endmacro

%macro beepoff 0
    in al, 61h
    and al, 11111100b
    out 61h, al
%endmacro

and the sleep one:

%macro sleep 2
    push dx
    mov ah, 86h
    mov cx, %1
    mov dx, %2
    int 15h
    pop dx
%endmacro

I'm using the NASM assembler.

This is not a duplicate of How can I create a sleep function in 16bit MASM Assembly x86?, because this one is for bare-metal assembly outside of Windows or DOS.

Bulley answered 11/4, 2016 at 9:47 Comment(4)
I would suggest to make the title “How to sleep in bare-metal 16-bit x86 assembly” to distinguish the questions from an OS-dependent one. Perhaps indicating in the question how you have tried to use int 15h would help the question not to be too broad (it is borderline in its current form).Relaxation
@PascalCuoq Ok, I edited it a bit.Bulley
Related: https://mcmap.net/q/896899/-how-can-i-create-a-sleep-function-in-16bit-masm-assembly-x86Reyreyes
Possible duplicate of How can I create a sleep function in 16bit MASM Assembly x86?Hoofer
H
4

The programmable interval timer is the way to go. If you will only deal with newer systems, look into HPET. For PIT there are a few things to know. Firstly, to set it up you need to use port 0x43 as the control/command port to configure the channel zero timer. The byte that we want to send there is a bit mapped field:

  7  |  6  |  5  |  4  |  3  |  2  |  1  |  0 
+----------------------------------------------+
| Channel  |  RW Mode  |   Channel Mode  | BCD?|
+----------------------------------------------+

Channel will be cleared to select channel zero.

The RW Model can be 1-LSB, 2-MSB or 3-LSB followed by MSB. We want both bits on (bit pattern of 3, 011) because we need to send a 16 bit value (LSB then MSB)

For channel mode we want a square wave. This is a bit pattern of 3 (011)

We want to send a 16 bit divisor for the counter, not a BCD value, so the lowest bit is cleared.

This gives us: 000110110 in binary or 0x36 in hex. Now we set it up:

mov al, 0x36    ; 0x36 from our work above
out 0x43, al    ; Send that byte to the control port

mov ax, 11931   ; The timer ticks at  1193182. 100hz would be 1193182/11931
out 0x40, al    ; send low byte
out 0x40, ah    ; send high byte

At this point you need to decide if you are going to react to an interrupt (IRQ 0) or simply want to read the timer. I'll point you to this excellent reference on OSDev which has a brilliant writeup on both with example code.

Hexagonal answered 11/4, 2016 at 10:46 Comment(3)
The problem is that the example code on OSDev is made for 32-bit assembly.Bulley
...why is that a problem? It's pretty simple to translate that to 16 bit or, alternatively, to leave BIOS interrupts behind and go to protected mode.Hexagonal
For instance, with the code I've posted here (all 16 bit) you can either periodically read channel zero to get the transition count or set up an ISR for IRQ 0. Nothing really changes between 32 and 16 bit code as far as this goes except the size of the addresses.Hexagonal

© 2022 - 2024 — McMap. All rights reserved.