Search code examples
assemblyx86nasmsleep

Sleep for x milliseconds in 16 bit bare metal nasm assembly


I am writing a program on bare metal 16 bit real mode assembly with nasm. I want to sleep (pause execution) for x amount of milliseconds however I have not found a way to do this.

Edit: This is my code. I want to add about 0.3 seconds of delay between each character getting typed to the screen.

[bits 16]    ; use 16 bits
[org 0x7c00] ; sets the start address

init: 
  mov si, msg  ; loads the address of "msg" into SI register
  mov ah, 0x0e ; sets AH to 0xe (function teletype)
print_char:
  lodsb     ; loads the current byte from SI into AL and increments the address in SI
  cmp al, 0 ; compares AL to zero
  je done   ; if AL == 0, jump to "done"
  int 0x10  ; print to screen using function 0xe of interrupt 0x10
  jmp print_char ; repeat with next byte
done:
  hlt ; stop execution

msg: db "The quick brown fox jumps over the lazy dog.", 0 ; we need to explicitely put the zero byte here

times 510-($-$$) db 0           ; fill the output file with zeroes until 510 bytes are full
dw 0xaa55                       ; magic number that tells the BIOS this is bootable

Solution

  • One day, I too needed a delay routine capable of doing delays ranging from 0.5 sec to just a few msec. Read all about it in this CodeReview question, and especially the reason why I needed to take this approach.

    My solution was to find out how many iterations a delay routine can do in the interval between 2 ticks of the standard 18.2Hz timer. Those ticks are 55 msec apart. Because sometimes measurements can be erratic I only accepted the results if 2 consecutive measurements varied by less than 1%%. Finally I divided the good measurement by 55 to obtain the number of iterations per msec aka SpeedFactor. Hereafter, whenever I wanted to pause the program I multiplied the desired delay expressed in msec by this SpeedFactor and then performed that number of iterations within the delay routine.

    The full code:

    [bits 16]
    [org 0x7C00]
    
                    xor     ax, ax
                    mov     ds, ax
                    mov     es, ax
                    mov     ss, ax
                    mov     sp, 0x7C00
                    cld
    
    ; Measure the number of iterations (within the ShortWait routine) per msec
    ; Only accept if consecutive measurements vary by less than 1%%
    ; If measurements remain erratic than do accept the last one
                    mov     bp, 10                  ; Max try
                    call    GetSpeedFactor          ; -> DX:AX
    .a:             xchg    si, ax                  ; 'mov si, ax'
                    mov     di, dx
                    call    GetSpeedFactor          ; -> DX:AX
                    push    ax dx                   ; (1)
    .b:             sub     ax, si
                    sbb     dx, di
                    jnb     .c
                    add     ax, si
                    adc     dx, di
                    xchg    si, ax
                    xchg    di, dx
                    jmp     .b
    .c:             mov     cx, 1000
                    xchg    ax, cx
                    mul     dx
                    xchg    ax, cx
                    mov     dx, 1000
                    mul     dx
                    add     dx, cx
                    sub     si, ax
                    sbb     di, dx
                    pop     dx ax                   ;(1)
                    cmc
                    dec     bp
                    jnbe    .a
                    mov     [SpeedFactor], ax
                    mov     [SpeedFactor+2], dx
    
                    mov     si, msg
                    lodsb
    More:           mov     bx, 0x0007              ; BH DisplayPage 0, BL GraphicsColor 7
                    mov     ah, 0x0E                ; BIOS.Teletype
                    int     10h
                    mov     bx, 300                 ; 0.3 sec 
                    call    Pause
                    lodsb
                    cmp     al, 0
                    jne     More
    
                    cli
                    hlt
                    jmps    $-2
    
    msg             db      "The quick brown fox jumps over the lazy dog.", 0
    SpeedFactor     dd      0
    ; ----------------------------------------------
    ; IN () OUT (dx:ax)
    ; Wait for the start of a new TimerTick period (54.9254 msec)
    ; Then measure a 4 tick period (219.7016 msec)
    GetSpeedFactor: push    bx cx
                    mov     bx, 1
                    call    .ShortWait              ; -> DX:AX BX=0
                    mov     bl, 4                   ; BH=0
                    call    .ShortWait              ; -> DX:AX BX=0
                    mov     cx, 10
                    xchg    ax, cx
                    mul     dx
                    xchg    ax, cx
                    mov     dx, 10
                    mul     dx
                    add     dx, cx
                    mov     cx, 2197
                    xchg    ax, bx                  ; BX=0
                    xchg    dx, ax
                    div     cx
                    xchg    ax, bx
                    div     cx
                    mov     dx, bx
                    pop     cx bx
                    ret
    ; - - - - - - - - - - - - - - - - - - - - - - -
    .ShortWait:     mov     ax, -1
                    cwd
    ; ---   ---   ---   ---   ---   ---   ---   ---
    ; IN (dx:ax,bx) OUT (dx:ax,bx)
    ; Do DX:AX iterations or loop until Timer did BX Ticks
    ShortWait:      push    ds cx si di
                    xchg    si, ax                  ; 'mov si, ax'
                    mov     di, dx
                    xor     ax, ax
                    cwd
                    mov     ds, ax
    .a:             mov     cx, [046Ch]             ; BIOS Timer
    .b:             sub     si, 1
                    sbb     di, 0
                    jb      .c
                    add     ax, 1
                    adc     dx, 0
                    cmp     cx, [046Ch]
                    je      .b
                    dec     bx
                    jnz     .a
    .c:             pop     di si cx ds
                    ret
    ; ----------------------------------------------
    ; IN (bx) OUT ()
    Pause:          push    ax bx dx
                    mov     ax, [SpeedFactor+2]
                    mul     bx
                    xchg    bx, ax
                    mul     word [SpeedFactor]
                    add     dx, bx
                    mov     bx, -1
                    call    ShortWait               ; -> DX:AX BX
                    pop     dx bx ax
                    ret
    ; ----------------------------------------------
    
    times 510-($-$$) db 0
    dw 0xAA55   
    

    The code assembles with FASM. For NASM, you will need to change code like

    push ax bx dx
    ...
    pop  dx bx ax
    

    into

    push ax
    push bx
    push dx
    ...
    pop  dx
    pop  bx
    pop  ax