Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I create a sleep function in 16bit MASM Assembly x86?

I am trying to create a sleep/delay procedure in 16bit MASM Assembly x86 that will, say, print a character on the screen every 500ms. From the research I have done, it seems that there are three methods to achieve this - I would like to use the one that uses CPU clock ticks.

Please note I am running Windows XP through VMWare Fusion on Mac OS X Snow Leopard - I am not sure if that affects anything.

Could someone please point me in the right direction, or provide a working piece of code I can tweak? Thank you!

The code I have found is supposed to print 'A' on the screen every second, but does not work (I'd like to use milliseconds anyways).

TOP:
MOV AH,2C
INT 21
MOV BH,DH  ; DH has current second
GETSEC:      ; Loops until the current second is not equal to the last, in BH
MOV AH,2C
INT 21
CMP BH,DH  ; Here is the comparison to exit the loop and print 'A'
JNE PRINTA
JMP GETSEC
PRINTA:
MOV AH,02
MOV DL,41
INT 21
JMP TOP

EDIT: Following GJ's advice, here's a working procedure. Just call it

DELAY PROC
 TIMER:
 MOV     AH, 00H
 INT     1AH
 CMP     DX,WAIT_TIME
 JB      TIMER
 ADD     DX,3         ;1-18, where smaller is faster and 18 is close to 1 second
 MOV     WAIT_TIME,DX
 RET
DELAY ENDP
like image 256
Yuval Karmi Avatar asked Dec 07 '09 08:12

Yuval Karmi


4 Answers

This cannot be done in pure MASM. All the old tricks for setting a fixed delay operate on the assumption that you have total control of the machine and are the only thread running on a CPU, so that if you wait 500 million cycles, exactly 500,000,000/f seconds will have elapsed (for a CPU at frequency f); that'd be 500ms for a 1GHz processor.

Because you are running on a modern operating system, you are sharing the CPU with many other threads (among them, the kernel -- no matter what you do, you cannot take priority over the kernel!), so waiting 500 million cycles in only your thread will mean that more than 500 million cycles elapse in the real world. This problem cannot be solved by userspace code alone; you are going to need the cooperation of the kernel.

The proper way to solve this is to look up what Win32 API function will suspend your thread for a specified number of milliseconds, then just call that function. You should be able to do this directly from assembly, possibly with additional arguments to your linker. Or, there might be an NT kernel system call to perform this function (I have very little experience with NT system calls, and honestly have no idea what the NT system call table looks like, but a sleep function is the sort of thing I might expect to see). If a system call is available, then issuing a direct system call from assembly is probably the quickest way to do what you want; it's also the least portable (but then, you're writing assembly!).

Edit: Looking at the NT kernel system call table, there don't appear to be any calls related to sleeping or getting the date and time (like your original code uses), but there are several system calls to set up and query timers. Spinning while you wait for a timer to reach the desired delay is one effective, if inelegant, solution.

like image 52
kquinn Avatar answered Nov 19 '22 23:11

kquinn


Well, then. An old style, non constant, power consuming delay loop which will make other threads running slow down would look like:

       delay equ 5000

top:   mov ax, delay
loopa: mov bx, delay
loopb: dec bx
       jnc loopb
       dec ax
       jnc loopa

       mov ah,2
       mov dl,'A'
       int 21
       jmp top

The delay is quadratic to the constant. But if you use this delay loop, somewhere in the world a young innocent kitten will die.

like image 21
Gunther Piez Avatar answered Oct 12 '22 20:10

Gunther Piez


use INT 15h, function 86h:

Call With: AH = 86h CX:DX = interval in uS

like image 7
Dave Avatar answered Nov 20 '22 00:11

Dave


Actually you can use ROM BIOS interrupt 1Ah function 00h, 'Read Current Clock Count'. Or you can read dword at address $40:$6C but you must ensure atomic read. It is incremented by MS-DOS at about 18.2 Hz.

For more information read: The DOS Clock

like image 3
GJ. Avatar answered Nov 20 '22 00:11

GJ.