Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to display a number on the screen and and sleep for one second with DOS x86 assembly?

I'm using NASM 16 BITS. I'm trying to do a simple assembly code that prints the numbers from 0 to 255 with 1 second interval between each number. This is what I have so far:

[bits 16]

mov ax,cs
mov ds,ax
mov cx,255
mov ax,0

myloop:
    ;print in screen ax value
    ;wait 1 second
    inc ax

loop myloop

I'm not sure how to print the value of ax in the screen, and how to wait 1 second(placed them in a comment in the code).

like image 870
Jean Carlos Suárez Marranzini Avatar asked Apr 02 '12 05:04

Jean Carlos Suárez Marranzini


1 Answers

There's a 4-byte counter at segment 0 offset 46Ch (or alternatively at seg 40h, offs 6Ch) maintained and updated by the PC BIOS. It's incremented 18.2 times per second. Counting 18 changes in the lowest byte or word of this counter is probably the simplest way of waiting out approximately a second:

mov  ax, 0
mov  ds, ax
mov  cx, 18
mov  bx, [46Ch]
WaitForAnotherChange:
NoChange:
mov  ax, [46Ch]
cmp  ax, bx
je   NoChange
mov  bx, ax
loop WaitForAnotherChange

To print decimal numbers you need to convert binary numbers into decimal, get individual digits and print them. You divide the number by 10 and collect remainders. e.g.:

123:
123 / 10: quotient 12, remainder 3
12 / 10: quotient 1, remainder 2
1 / 10: quotient 0, remainder 1

By repeatedly dividing by 10 you get the individual digits in the remainders in the reverse order: 3,2,1. Then you print them using DOS int 21h function 2 (load 2 into AH, load the character's ASCII code into DL, execute int 21h).

An alternative variant, quite suited to your problem, would be to use the DAA instruction to increment the number directly in decimal without any conversion.

Here's how it all can be done:

; file: counter.asm
; assemble: nasm.exe counter.asm -f bin -o counter.com

bits 16
org 0x100

    mov  ax, 0 ; initial number
    mov  cx, 256 ; how many numbers

NextNumber:
%if 1 ; change to 0 to use the DAA-based method
    push ax

    mov  dx, 0
    div  word [ten]
    push dx

    mov  dx, 0
    div  word [ten]
    push dx

    mov  dx, 0
    div  word [ten]
    push dx

    pop  dx
    call PrintDigit
    pop  dx
    call PrintDigit
    pop  dx
    call PrintDigit

    pop  ax

    call PrintNewLine
    call Wait1s

    inc  ax
%else
    mov  dl, ah
    call PrintDigit

    mov  dl, al
    shr  dl, 4
    call PrintDigit

    mov  dl, al
    and  dl, 0Fh
    call PrintDigit

    call PrintNewLine
    call Wait1s

    add  al, 1
    daa
    adc  ah, 0
%endif

    loop NextNumber
    ret

PrintDigit:
    pusha
    mov   ah, 2
    add   dl, '0'
    int   21h
    popa
    ret

PrintNewLine:
    pusha
    mov   dx, CRLF
    mov   ah, 9
    int   21h
    popa
    ret

Wait1s:
    pusha
    push ds

    mov  ax, 0
    mov  ds, ax

    mov  cx, 18
    mov  bx, [46Ch]
WaitForAnotherChange:
NoChange:
    mov  ax, [46Ch]
    cmp  ax, bx
    je   NoChange
    mov  bx, ax
    loop WaitForAnotherChange

    pop  ds
    popa
    ret

ten dw 10
CRLF db 13,10,"$"

If you don't like the leading zeroes or the last 1-second delay, you can conditionally skip them.

Download Intel and/or AMD x86 CPU manuals that describe how each instruction works. Read them. Also, download the Ralf Brown's Interrupt List, which describes every BIOS and DOS function. You need to know some of them to do I/O. There are also HelpPC and TechHelp that conveniently describe many BIOS and DOS things like the BIOS Data Area where the aforementioned counter lives.

like image 71
Alexey Frunze Avatar answered Nov 01 '22 09:11

Alexey Frunze