Reputation: 592
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
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
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
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
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
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
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
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
Upvotes: 16
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
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