So, I'm just found code from this question. I'm trying to print the current time in hours, but output not expected.
This is screenshoot for wroing output
Expected output is current time : 14 16 (secs)
BITS 16 ORG 0x7C00 _start: mov ax, 07C0h add ax, 288 mov ss, ax ; ss = stack space mov sp, 4096 ; sp = stack pointer mov ax, 07C0h mov ds, ax ; ds = data segment call time call cvthrs call cvtmin call cvtsec call dsptime cli endloop: hlt jmp endloop time: ;Get time from the system mov ah,02h int 1Ah ;ret ;CH - Hours ;CL - Minutes ;DH - Seconds cvthrs: ;Converts the system time from BCD to ASCII mov bh,ch ;copy contents of hours (ch) to bh shr bh, 4 add bh,30h ;add 30h to convert to ascii mov [tmfld],bh mov bh,ch ;and bh,0fh add bh,30h mov [tmfld 1],bh ret cvtmin: mov bh,cl ;copy contents of minutes (cl) to bh shr bh, 4 add bh,30h ;add 30h to convert to ascii mov [tmfld 3],bh mov bh,cl and bh,0fh add bh,30h mov [tmfld 4],bh ret cvtsec: mov bh,dh ;copy contents of seconds (dh) to bh shr bh, 4 add bh,30h ;add 30h to convert to ascii mov [tmfld 6],bh mov bh,dh and bh,0fh add bh,30h mov [tmfld 7],bh ret tmfld: db '00:00:00' dsptime: ;Display the system time mov ah,13h ;function 13h (Display String) mov al,0 ;Write mode is zero mov bh,0 ;Use video page of zero mov bl,0x0f;Attribute mov cx,8 ;Character string is 8 long mov dh,5 ;position on row 5 mov dl,0;and column 0 push ds ;put ds register on stack pop es ;pop it into es register lea bp,[tmfld] ;load the offset address of string into BP int 10H ret
Compile and run using QEMU:
nasm time.asm -fbin -o time.img
qemu-system-i386 -drive format=raw,file=./time.img
How to fix this ?
CodePudding user response:
The output does not show the current time because the far pointer in ES:BP
that you give to the BIOS.WriteString function 13h is wrong.
The problem is with the ORG 0x7C00
directive and how you setup the DS
segment register.
When you specify an origin of 7C00h, you instruct the assembler to consider the first byte of your code to reside at the offset address of 7C00h. That means that the offset address of 0000h where the segment starts and where DS
should point to is now way down in memory. The only correct value that you should load in DS
is 0.
There's also the possibility to specify ORG 0000h
, and then the correct value to load in the DS
segment register would be 07C0h (like you did).
;ret in time ;and bh,0fh in cvthrs
For correct results you need to uncomment these lines in your time and cvthrs subroutines.
This is a quick rewrite with some improvements:
BITS 16
ORG 0000h
_start:
mov ax, 07C0h
mov ds, ax
add ax, 32
mov ss, ax ; ss = 07E0h
mov sp, 4096 ; sp = stack pointer
call time
mov al, ch ; hours
call cvt ; -> AX
mov [tmfld 0], ax
mov al, cl ; minutes
call cvt ; -> AX
mov [tmfld 3], ax
mov al, dh ; seconds
call cvt ; -> AX
mov [tmfld 6], ax
call dsptime
cli
endloop:
hlt
jmp endloop
time: ; Get time from the system
mov ah, 02h
int 1Ah ; BIOS.ReadRealTimeClock
jnc OK
mov cx, 9999h ; Displays 99:99:99 if clock is malfunctioning
mov dh, 99h
OK:
ret
;CH - Hours
;CL - Minutes
;DH - Seconds
cvt:
mov ah, al
shr al, 4
and ah, 0Fh
add ax, 3030h
ret
tmfld: db '00:00:00'
dsptime:
;Display the system time
mov ah, 13h ;function 13h (Display String)
mov al, 0 ;Write mode is zero
mov bh, 0 ;Use video page of zero
mov bl, 0Fh ;Attribute
mov cx, 8 ;Character string is 8 long
mov dh, 5 ;position on row 5
mov dl, 0 ;and column 0
push ds ;put ds register on stack
pop es ;pop it into es register
mov bp, tmfld ;load the offset address of string into BP
int 10h
ret
times 510-($-$$) db 0
dw 0AA55h
It's 1 byte shorter if you load the address of tmfld using a mov
instruction.
You can further improve this by combining loads to related byte-sized registers into 1 load of the corresponding word-sized register:
e.g. Change:
mov dh, 5 ;position on row 5
mov dl, 0 ;and column 0
into
mov dx, 0500h ; DH=5 (row), DL=0 (column)