How to set 1 second time delay at assembly language 8086
Asked Answered
D

6

10

My problem is that I have written a code that is supposed to output a result into a set of LEDs connected to the parallel port. When I ran the code it pretty much did nothing. My instructor told me that the code ran too fast that my eyes did not see what happened.

I have found that there are a couple of ways to do a time delay, I have tried to loop the NOP but I think I cannot really determine what is going on. Is there any better way?

I have here a part of the code where I have to add a time delay into:

org 100h

mov ax, 0
mov dx, 378
out dx, ax
mov ax, 1  

; 1st

mov cx, 1ah
start1st:
mov ax, 1
left:
out dx, ax 
; --------------------------------> how to loop?
mov bx, 2
mul bx
cmp ax, 80h
jl left
dec cx
cmp cx,0
jg start1st
; end 1st 
Disherison answered 4/3, 2013 at 12:47 Comment(6)
This typically requires an OS or BIOS call, or knowing the CPU's clock frequency and very carefully constructing a loop that will delay for the proper number of cycles (and assuming that the clock frequency doesn't change). What operating system are you doing this on?Forney
The best way, if you have the hardware, is to use a dedicated timer to do the timing. It will then notify the CPU through an interrupt when the delay has expired. This allows the CPU to do other work while waiting, which is often very nice.Rafat
@ Jim Mischel Hi, i am using a windows xp in running the code. Building the code was on an 8086 emulator. Will the NOP loop be able to do this? I am planning to loop it at a certain time, but i don't know how many cycles each instruction is done. I have no clue about it.Disherison
Why are you doing this in DOS mode in the first place? Your emulated real mode DOS won't give you better timing that host system's Windows will.Sorb
@Seva - Because some schools haven't updated their courses in the last 20 years. They still teach 16-bit assembly for MS-DOS. They also probably use Ford model T for their driving classes.Antimony
@SevaAlekseyev - +1 they should dump this crap now. On 32-bit it's a trivial Sleep() call. Ancient rubbish..Sinkhole
D
3

What i finally ended up using was the nop loop

; start delay

mov bp, 43690
mov si, 43690
delay2:
dec bp
nop
jnz delay2
dec si
cmp si,0    
jnz delay2
; end delay

I used two registers which I set them both to any high value and its gonna keep on looping until both values go to zero

What I used here was AAAA for both SI and BP, i ended up with roughly 1 second for each delay loop.

Thanks for the help guys, and yes, we still use MS DOS for this assembly language course :(

Disherison answered 6/3, 2013 at 15:43 Comment(4)
A constant-count delay loop is totally inappropriate on modern x86. First of all, it only works on CPUs that run exactly as fast as yours does. 2nd, modern x86 CPUs don't even run at a constant speed, especially not over as long as time as a whole second. If the OS schedules another process onto the other hyperthread, your loop may slow down. The CPU frequency cam ramp up or down on its own (Turbo / power-save). Even if you booted real DOS, turbo is still a thing unless you disable it in the BIOS. (This might be ok in a cycle-accurate simulator for old CPUs, though).Changeling
But anyway, for very short time intervals, so short that querying a proper timer would be too slow (so you delay too long), a delay loop with lfence or cpuid to serialize out-of-order execution might be your only option. But a whole second is billions of clock cycles, you can just check a timer in a loop. e.g. with RDTSC which ticks at a constant speed on modern x86 (regardless of actual core clock speed changes). See a code-golf stopwatch that uses RDTSC with a known reference-clock speed.Changeling
I know the question says 8086, but most 8086 code runs in a virtual / emulated 8086 these days, or on a modern CPU, and often not with a cycle-accurate simulator.Changeling
There was a followup Q&A about this code: How much delay is generated by this assembly code in linux. I posted an answer expanding on what I commented here.Changeling
P
16

Set 1 million microseconds interval (1 second) By using below instruction .

MOV     CX, 0FH
MOV     DX, 4240H
MOV     AH, 86H
INT     15H

You can set multiple second delay by using 86H and INT 15H

check these links for more details

Waits a specified number of microseconds before returning control to the caller

INT 15H 86H: Wait

Perithecium answered 4/3, 2014 at 18:20 Comment(2)
For anyone interested, nothing alike exists in 86 64bit. Something like this does https://mcmap.net/q/22965/-nasm-assembly-linux-timer-or-sleepLib
There is no service 86h of int 15h in the IBM-PC nor IBM-PC/XT BIOS, it was introduced later, in the IBM-PC/AT systems.Farver
M
8

You can use interrupt 1Ah / function 00h (GET SYSTEM TIME) to get the number of clock ticks (18.2/s) since midnight in CX:DX.

So to wait approximately 1 second using this method you'd execute this interrupt function once, save CX:DX in a variable, then execute the same interrupt in a loop until the absolute value of CX:DX - firstCX:DX is greater than 18.

Merger answered 4/3, 2013 at 13:28 Comment(5)
I'm sorry for being too oblivious right now, is this CX:DX treated as one variable? if so do you mean i add the values of cx and dx and store them in a variable?Disherison
CX:DX means that you treat CX and DX as a 32-bit value, where DX contains the least significant 16 bits and CX the most significant 16 bits.Merger
@ Michael If that is the case then where should I store a 32 bit variable? For all i know the registers only accommodate 16 bit valuesDisherison
In memory. Just reserve space for a 32-bit variable right after the end of your code using DD, DWORD, or whatever directive your assembler uses.Merger
@JerYango: note as long as your desired delay interval fits in the low half of an extended precision integer, you can ignore the upper half of the result. Calculate the low half of the difference (which will be a small positive integer regardless of whether the inputs are on opposite sides of a 16-bit wrap-around), and compare that against 18. See this code-golf question where I did the same thing with 32-bit registers to spin on RDTSC for a 1 second delay (if clock speed < ~4.3GHz).Changeling
D
3

What i finally ended up using was the nop loop

; start delay

mov bp, 43690
mov si, 43690
delay2:
dec bp
nop
jnz delay2
dec si
cmp si,0    
jnz delay2
; end delay

I used two registers which I set them both to any high value and its gonna keep on looping until both values go to zero

What I used here was AAAA for both SI and BP, i ended up with roughly 1 second for each delay loop.

Thanks for the help guys, and yes, we still use MS DOS for this assembly language course :(

Disherison answered 6/3, 2013 at 15:43 Comment(4)
A constant-count delay loop is totally inappropriate on modern x86. First of all, it only works on CPUs that run exactly as fast as yours does. 2nd, modern x86 CPUs don't even run at a constant speed, especially not over as long as time as a whole second. If the OS schedules another process onto the other hyperthread, your loop may slow down. The CPU frequency cam ramp up or down on its own (Turbo / power-save). Even if you booted real DOS, turbo is still a thing unless you disable it in the BIOS. (This might be ok in a cycle-accurate simulator for old CPUs, though).Changeling
But anyway, for very short time intervals, so short that querying a proper timer would be too slow (so you delay too long), a delay loop with lfence or cpuid to serialize out-of-order execution might be your only option. But a whole second is billions of clock cycles, you can just check a timer in a loop. e.g. with RDTSC which ticks at a constant speed on modern x86 (regardless of actual core clock speed changes). See a code-golf stopwatch that uses RDTSC with a known reference-clock speed.Changeling
I know the question says 8086, but most 8086 code runs in a virtual / emulated 8086 these days, or on a modern CPU, and often not with a cycle-accurate simulator.Changeling
There was a followup Q&A about this code: How much delay is generated by this assembly code in linux. I posted an answer expanding on what I commented here.Changeling
K
1

Alternatively, you can create a process and call it every time you want to delay using only the counter register and stack implementation.

Example below delays roughly 1/4 a sec.

delay       proc
            mov     cx, 003H
    delRep: push    cx
            mov     cx, 0D090H
    delDec: dec     cx
            jnz     delDec
            pop     cx
            dec     cx
            jnz     delRep
            ret
delay       endp
Kyle answered 17/9, 2015 at 15:27 Comment(2)
On what CPU microarchitecture, at what clock speed? This is silly; at least use RDTSC on modern x86, because CPU frequency varies with time these days. Most 8086 code runs in a virtual / emulated 8086 these days, and usually not with a cycle-accurate simulator.Changeling
You usually start learning from the basics, using a bare minimum setup, just to "feel" the concept. If this is an 8086/8088 system, then its clock is fixed at 4.77 MHz and can be used for timing. On your first programs, you are not using any interrupts nor peripherals.Farver
D
0
.DATA TIK DW ?
...
MOV AX,00H
INT 1AH

MOV TIK,DX
ADD TIK, 12H

DELAY:
MOV AX,00H
INT 1AH
CMP TIK, DX
JGE DELAY

I'm from mobile. Sorry for my enters ;)

Dated answered 23/9, 2013 at 17:10 Comment(0)
R
-2
DELAY_1SEC: MOV R3,#0AH;10D
LOOP1:      MOV R2,#64H;100D
LOOP2:      MOV R1,#0FAH;250D
LOOP3:      NOP
            NOP
            DJNZ R1,LOOP3;4x250Dx1,085us=1,085ms (0s.001ms010)/cycle
            DJNZ R2,LOOP2;3x100Dx1,085ms=325,5ms (0s.100ms309)/cycle
            DJNZ R3,LOOP1;3x10Dx325,5us=976,5ms (1s.604ms856)/cycle
            RET
Robbyrobbyn answered 5/3, 2016 at 13:11 Comment(1)
This isn't even x86 code. Looks like AVR, I think. A fixed-count delay loop is not appropriate for a sleep that long on x86.Changeling

© 2022 - 2024 — McMap. All rights reserved.