Jer Yango
Jer Yango

Reputation: 592

How to set 1 second time delay at assembly language 8086

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 

Upvotes: 10

Views: 117935

Answers (8)

Faiq Saeed
Faiq Saeed

Reputation: 1

    mov al, 0xB6 ; Control word for Mode 3, channel 0, binary count
out 0x43, al

mov al, 0x00 ; Low byte of divisor
out 0x40, al

mov al, 0x90 ; High byte of divisor
out 0x40, al

you can use this code to make intell 8088 timer to generate interupt after every 1sec

Upvotes: 0

Sep Roland
Sep Roland

Reputation: 39676

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.

Even if it didn't run too fast, you would not have been able to see anything!

Why is this? Well, your inner loop, that sends AX to the port DX, is using the mul bx instruction in order to double the value in AX. But since the word-sized multiplication actually leaves its product in the combo DX:AX and in this case that leads to DX becoming zero, you will have lost the correct port number to output to!
The solution as often is the case is to not use an actual multiplication to multiply by 2, but rather a single shift to the left. That also will double the value and with less fuss.

I have to add a time delay

A simple solution would use the BIOS timer at linear address 046Ch. It advances at a rate of 18.2065 Hz. So, monitoring the counter until 18 changes have occured, will delay for almost 1 second. This is not perfect, but for your specific use-case it is totally fine.

    ORG   256

    xor   ax, ax
    mov   dx, 378
    out   dx, ax

    mov   cx, 26
start1st:
    mov   ax, 1
left:
    out   dx, ax
    call  Wait1sec
    shl   al, 1
    jns   left                ; Loop while less than 128
    loop  start1st            ; Loop 26 times
                              ; In a program that spends almost all
    ...                       ; of its time waiting, there's nothing
                              ; against using the LOOP instruction.
    int   20h                 ; DOS.Terminate
; ----------------------------
; IN () OUT ()
Wait1sec:
    push  ax ds
    xor   ax, ax
    mov   ds, ax
.a: mov   ah, [046Ch]         ; BIOS.Timer (lowest byte)
.b: cmp   ah, [046Ch]
    je    .b
    inc   ax                  ; `inc al`
    cmp   al, 18
    jb    .a
    pop   ds ax
    ret

Getting a bit closer to the real time

A better solution takes into account that 0.2 part from 18.2065. It does so by maintaining a global variable that decrements from 5. Each time it reaches 0, it gets reset to 5 and then the code will monitor 19 changes of the timer (instead of 18).

Next is a small demo that pauzes for several minutes. You can easily verify that it closely follows the wallclock time. If you were to substitute the previous version, you'd notice that it looses a half second each minute.

    ORG   256

    mov   dx, msg1
    mov   ah, 09h
    int   21h
    mov   ah, 00h
    int   16h
    mov   cx, 240             ; 4 minutes
.a: call  BetterWait1sec
    loop  .a
    mov   dx, msg2
    mov   ah, 09h
    int   21h
    ret
; ----------------------------
; IN () OUT ()
BetterWait1sec:
    push  ax bx ds
    mov   al, [zzzz]          ; -> AL=[1,5]
    mov   ah, 18
    dec   al
    jnz   .a
    mov   ax, 1305h
.a: mov   [zzzz], al

    xor   bx, bx
    mov   ds, bx
.b: mov   bh, [046Ch]         ; BIOS.Timer (lowest byte)
.c: cmp   bh, [046Ch]
    je    .c
    inc   bx
    cmp   bl, ah              ; AH=[18,19]
    jb    .b
    pop   ds bx ax
    ret
; ----------------------------
msg1: db 'Press key to begin waiting 4 minutes ...$'
msg2: db 'Done. Hope you looked at the wall clock', 13, 10, '$'
zzzz: db 5

Upvotes: 2

Michael
Michael

Reputation: 58507

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.

Upvotes: 8

kinAfro
kinAfro

Reputation: 9

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

Upvotes: -4

Alfred Benigno
Alfred Benigno

Reputation: 45

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

Upvotes: 1

john
john

Reputation: 347

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

Upvotes: 16

maks nosov
maks nosov

Reputation: 19

.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 ;)

Upvotes: -1

Jer Yango
Jer Yango

Reputation: 592

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 :(

Upvotes: 4

Related Questions