News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Timer not being displayed

Started by zak100, January 04, 2010, 10:13:26 AM

Previous topic - Next topic

zak100

Hi,
I am trying to display a timer in the format:
hh:mm:ss
but its not displaying any of its components. It displays a mesg before coming to timer code.

Can somebody plz help me with this prob.?


.MODEL  TINY

install macro intNum,Newisr_add

  push ax
  mov ax, 0
  push ds
  mov ds,ax
  cli                                      ;disable interrupts for the change
  mov word ptr ds:[intNum*4],Newisr_add
  mov word ptr ds:[intNum*4+2],cs          ;word ptr may not be needed here
  sti
  pop ds
  pop ax
endm


        .CODE

;----------------------------------------------------------------------------------

LoadOfs EQU     0               ;must match the value in the bootloader source file
INT1C   EQU     01CH

;----------------------------------------------------------------------------------

;---------------------- initialize ES segment register

        ORG     0

Start:  push    cs
        pop     ds
       
;-----------clear screen
mov ax, 3
int 10h

   
;---------------------- writing a message on screen at startup, character by character- we can't use int 21h
overdata:
       
        xor di, di
        mov ax, 0B800h
        mov es, ax
        mov si, offset msg0+LoadOfs
       mov ah, 41h; attribute byte
       cld;
msgloop:
        lodsb; loads al with a byte of data pted by ds:si
        or al, al
        jz Timer
        stosw; transfers the contents of al to mem location ptd by es:di
        jmp msgloop
       
;---------------------- done - halt
Timer:install 1Ch, INT1Ch

        xor di, di
        mov ax, 0B820h
        mov es, ax
        mov si, offset msgA+LoadOfs
       mov ah, 41h; attribute byte
       cld;
msgAloop:
        lodsb; loads al with a byte of data pted by ds:si
        or al, al
        jz Halt0
        stosw; transfers the contents of al to mem location ptd by es:di
        jmp msgAloop

Halt0: hlt
jmp     Halt0

;---------------------- data area in code segment


Msg0    db      "We be bootin234!",0
msgA db 'Total minutes elapsed since Kernel start is',0
clkcounter db 0
secs db 0
mins db 0
hrs  db 0
cnt  db 0; Its value represents the digits of Timer
s    db 0; selector for secs minutes and hrs used in displayCnt

INT1Ch:
  cli    ;not needed at the beginning of an interrupt handler - the interrupt clears that flag automatically

  inc byte ptr [cs:clkcounter]
  cmp byte ptr [cs:clkcounter],18; if clkcounter is 18, it means 1 sec
  jz handle_secs
  sti
  iret
handle_secs:
  mov byte ptr[cs:clkcounter],0
  inc byte ptr[cs:secs]
  cmp byte ptr[cs:secs],60;if secs is 60, it means 1 min
  jz handle_mins
  push Ax
  push BX
  mov bh, byte ptr[cs:secs]
  mov s,0;--------------- selector for sec
  call PrintSMH;--------- displaying seconds
  pop BX
  pop AX
  sti
  iret
handle_mins:
  mov byte ptr[cs:secs],0
  mov s,0;------------------ selector for secs
  call PrintSMH; ----------- displaying seconds
  inc byte ptr[cs:mins]; we are done, now print the number of minutes elapsed
  cmp byte ptr[cs:mins],60
  jz handle_hrs
  mov s,2;------------------selctor for minutes
  call PrintSMH;------------displaying minutes
  sti
  iret
handle_hrs:
  mov byte ptr[cs:mins],0
  mov s,2;-----------------selctor for minutes
  call PrintSMH;------------displaying minutes 
  inc byte ptr[cs:hrs]
  cmp byte ptr[cs:hrs],24
  jz hrsZero
  mov s,4;----------------selctor for hrs
  call PrintSMH;-----------displaying hours
  sti
  iret
hrsZero:
  mov byte ptr[cs:hrs], 0
  mov s,4;-----------------selctor for hrs
  call PrintSMH;----------displaying hours
  sti
  iret
PrintSMH:
  mov ax, 100; For two digits, we have  'subtract' =10^1 and 10^0
  mov bl, 10
NextDigit: Div BL; AL=AX/BL
  mov ah, 0;value of AL would be the value of AX
  cmp AL,0; until 'subtract'=0
  je done
againSub: Sub bh, AL;e.g to print 58,First print 5,58-10-10-10-10-10-10, count=6, digit=count-1=5
                    ;Note after subtraction bh=-2, add back i.e -2+10=8 which is the next digit
                    ;to print 8,8-1-1-1-1-1-1-1-1-1,count=9, digit=count-1=8
          cmp bh,0;when bh=-ve we stop subtraction
          jb NoSub
          inc cnt
          jmp againSub
NoSub: add bh, al; add back
       call DisplayCnt
       inc s
       jmp NextDigit
    done:iret
DisplayCnt:
  cmp s, 0
  je FirstDigitOfSecs
  cmp s, 1
  je SecondDigitOfSecs
  cmp s, 2
  je FirstDigitOfMins
  cmp s, 3
  je SecondDigitOfMins
  cmp s,4
  je FirstDigitofHrs
  cmp s,5
  je SecondDigitofHrs
  iret
FirstDigitOfSecs: cld
    mov ax,0B880h
    mov es,ax
    xor di,di
    mov ah,1Fh
    add bh, 30h
    mov al,bh
    mov cx,2
    rep stosw
    iret
SecondDigitOfSecs:cld;----------------- Displaying colon also
    mov ax,0B882h
    mov es,ax
    xor di,di
    mov ah,1Fh
    add bh, 30h
    mov al,bh
    mov cx,2
    rep stosw
    mov ax,0B884h
    mov es,ax
    xor di,di
    mov ah,1Fh
    mov al,':'
    mov cx,2
    rep stosw
    iret
FirstDigitOfMins:cld
    mov ax,0B886h
    mov es,ax
    xor di,di
    mov ah,1Fh
    add bh, 30h
    mov al,bh
    mov cx,2
    rep stosw
    iret
SecondDigitOfMins:cld;----------------- Displaying colon also
    mov ax,0B888h
    mov es,ax
    xor di,di
    mov ah,1Fh
    add bh, 30h
    mov al,bh
    mov cx,2
    rep stosw
    mov ax,0B88Ah
    mov es,ax
    xor di,di
    mov ah,1Fh
    mov al,':'
    mov cx,2
    rep stosw
    iret
FirstDigitofHrs:cld
    mov ax,0B88Ch
    mov es,ax
    xor di,di
    mov ah,1Fh
    add bh, 30h
    mov al,bh
    mov cx,2
    rep stosw
    iret
SecondDigitOfHrs:cld
    mov ax,0B88Eh
    mov es,ax
    xor di,di
    mov ah,1Fh
    add bh, 30h
    mov al,bh
    mov cx,2
    rep stosw
    iret   

       
       


;----------------------------------------------------------------------------------

        END     Start




Zulfi.

dedndave

Zulfi, Zulfi - lol
i see no place where the time is displayed
the string is - not the time, though
and - to display it, it needs to be converted from binary to ASCII numeric

finally, if you count 18 ticks as one second, you will have a fast-running clock
it is much simpler to use the BIOS ticks-since-midnight count and calculate the time from that value

zak100

Hi,

The following code is doing the conversion:

           PrintSMH:
  mov ax, 100; For two digits, we have  'subtract' =10^1 and 10^0
  mov bl, 10
NextDigit: Div BL; AL=AX/BL
  mov ah, 0;value of AL would be the value of AX
  cmp AL,0; until 'subtract'=0
  je done
againSub: Sub bh, AL;e.g to print 58,First print 5,58-10-10-10-10-10-10, count=6, digit=count-1=5
                    ;Note after subtraction bh=-2, add back i.e -2+10=8 which is the next digit
                    ;to print 8,8-1-1-1-1-1-1-1-1-1,count=9, digit=count-1=8
          cmp bh,0;when bh=-ve we stop subtraction
          jb NoSub
          inc cnt
          jmp againSub
NoSub: add bh, al; add back
       call DisplayCnt
       inc s
       jmp NextDigit
    done:iret


Following code and its variants like SecondDigitOfSecs, FirstDigitOfMins and so on is doing the printing.

       FirstDigitOfSecs: cld
    mov ax,0B880h
    mov es,ax
    xor di,di
    mov ah,1Fh
    add bh, 30h
    mov al,bh
    mov cx,2
    rep stosw
    iret




Kindly tell me the prob with this code.

Zulfi.

dedndave

ahhhhhhhhh - lol
i didn't see that code way down there
holy cow - lol
interrupt handlers should be short and sweet
i am not sure i would display anything anything at all during handler execution
i would count the tick, and let the program display the time
in fact, the ticks are already counted for you by BIOS
all you have to do is go get the value and update the display if the count has changed
give me a little time and i will write a little program for you...

zak100

Hi,
I have found one mistake in the printing code. Instead of bh, cnt should contain the value to print. But I have replace bh by cnt but its still not printing. Computer becomes very busy as ctrl-alt-del doesnt work and I have to manually reset the system.

Zulfi.

zak100

Hi,
I highly appreciate your intentions. It would tell me another approach of solving the problem. Kindly give me some suggestions also to make this code running.

Zulfi.

MichaelW

Zulfi,

One major problem with your interrupt handler is that it modifies registers without preserving them. This in general is not workable for hardware interrupt handlers, or for software interrupt handlers called by hardware interrupt handlers, as is the case for Interrupt 1Ch. Changing register values in a handler can cause severe problems in the code that was executing when the hardware interrupt was called.

Another major problem is that in your handler you call code that instead of returning to the caller executes an IRET. The call places a return address on the stack, and this will prevent the IRET from returning to the correct address.

Also, an STI before an IRET makes no sense because the IRET will restore the flags to what they were when the interrupt was called.

eschew obfuscation

dedndave

#7
Zulfi
here is a simple program to get the INT 1Ah tick count and display it in HH:MM:SS.FF format
DOS is not used to display it

in your boot-code, you may have to execute INT 1Ah, function 0Fh to initialize the BIOS tick counter from the RTC

EDIT - oops - found a line missing - lol - fixed it
EDIT - dang - fixed another bug - i was sleepy when i wrote it   :P

zak100

Hi Dave,
Thanks ffor this code. I have downloaded it but I cant figure out its logic.

Hi MichealW,
Quote

Another major problem is that in your handler you call code that instead of returning to the caller executes an IRET. The call places a return address on the stack, and this will prevent the IRET from returning to the correct address.



If I dont use the IRET then how to end the procedures?

Zulfi.

MichaelW

QuoteIf I dont use the IRET then how to end the procedures?

The problem is not the IRET itself, but the state of the stack when the IRET is executed. When the handler receives control, the contents of the stack, relative to SP, are:

[sp+4] value of the flags register at the point the interrupt was called
[sp+2] return CS for the interrupt call
[sp+0] return IP for the interrupt call

The IRET instruction expects the contents of the stack to be as they were when the handler received control, with the return address and flags value at the "top" of the stack, and the instruction will fail if they are not. Here is an example of the problem in your code:

handle_secs:
  mov byte ptr[cs:clkcounter],0
  inc byte ptr[cs:secs]
  cmp byte ptr[cs:secs],60
  jz handle_mins
  push Ax
  push BX
  mov bh, byte ptr[cs:secs]
  mov s,0
  call PrintSMH
...
PrintSMH:
    mov ax, 100
    mov bl, 10
  NextDigit:
    Div BL
    mov ah, 0
    cmp AL,0
    je done         <===========
  againSub:
    Sub bh, AL
    cmp bh,0
    jb NoSub
    inc cnt
    jmp againSub
  NoSub:
    add bh, al; add back
    call DisplayCnt
    inc s
    jmp NextDigit
  done:
    iret            <===========


After the call PrintSMH executes, the contents of the stack, relative to SP, are:

[sp+10] value of the flags register at the point the interrupt was called
[sp+8] return CS for the interrupt call
[sp+6] return IP for the interrupt call
[sp+4] preserved AX
[sp+2] preserved BX
[sp+0] return IP for the near call

Obviously, this will not work, because the IRET will attempt to use the return IP for the near call as the return IP for the interrupt call, the preserved value of BX as the return CS for the interrupt call, etc.

You need to ensure that anything your code places on the stack is removed from the stack before you execute the IRET. And the same concept applies for any code that is CALLed, where the return instruction is expecting the return address to be at the "top" of the stack.


eschew obfuscation

dedndave

#10
i fixed a mistake in the Time_1Ah code above

here it is, adapted to "Zulfi boot code" ...

        .MODEL  TINY

;---------------------------------------------------------------------------------

LoadOfs EQU     0               ;must match the value in the bootloader source file

;---------------------------------------------------------------------------------

        .CODE

        ORG     0

_main   PROC    FAR

;set video mode 3 - clear screen

        mov     ax,3
        int     10h

;set segment registers

        push    cs
        pop     ds
        mov     ax,0B800h
        mov     es,ax
        cld

;time display loop

TLoop0: mov     ah,0
        int     1Ah
        cmp     dx,TickLo+LoadOfs
        jz      TLoop0

        mov     TickLo+LoadOfs,dx
        mov     si,offset Hours+LoadOfs
        call    UpdCk
        mov     [si+9],ax
        xor     di,di
        call    Dsply
        jmp     TLoop0

_main   ENDP

;---------------------------------------------------------------------------------

UpdCk   PROC    NEAR

;update clock string

;CX:DX = INT 1Ah clock tick
;SI = string address

        shl     dx,1                   ;multiply by 2
        rcl     cx,1
        mov     ax,cx
        xchg    ax,dx
        mov     bx,ax
        shl     ax,1                   ;multiply by 9
        rcl     dx,1
        shl     ax,1
        rcl     dx,1
        shl     ax,1
        rcl     dx,1
        add     ax,bx
        adc     dx,cx
        mov     bx,19663
        div     bx                     ;divide 28314702 max by 19663
        mov     cx,60
        push    dx
        xor     dx,dx
        div     cx                     ;divide 1439 max by 60
        call    UAscii
        mov     [si],ax
        mov     ax,dx
        call    UAscii
        mov     [si+3],ax
        pop     ax
        mov     dx,6000
        mul     dx
        div     bx                     ;divide 117972000 max by 19663
        mov     cl,100
        xor     dx,dx
        div     cx                     ;divide 5999 max by 100
        call    UAscii
        mov     [si+6],ax
        mov     ax,dx

UAscii: aam
        or      ax,3030h
        xchg    al,ah
        ret

UpdCk   ENDP

;---------------------------------------------------------------------------------

Dsply   PROC    NEAR

        mov     ah,0Ah                 ;AH = attribute
        jmp short Dsply1

Dsply0: stosw

Dsply1: lodsb
        or      al,al
        jnz     Dsply0

        ret

Dsply   ENDP

;---------------------------------------------------------------------------------

TickLo  dw 0

Hours   db '00:00:00.00',0

;---------------------------------------------------------------------------------

        END     _main

EDIT - fixed a bug   :bg

sinsi

Any reason for using int 1a? If you want h:m:s just use the cmos clock - all nicely bcd already.
Light travels faster than sound, that's why some people seem bright until you hear them.

dedndave

well - he wanted to use the tick counter, so i stayed with that   :P
i wanted to show him that it could be done without revectoring the 1Ch interrupt

zak100

Thanks for removing the data segment. Great work. However I am not able to under your conversion logic. I would try your code and then modify it according to my understanding. In the meantime I have changed my original code somewhat in the light of MichealW and your's comment but still its not printing anything. Somebody kindly help me with this.


.MODEL  TINY

install macro intNum,Newisr_add

  push ax
  mov ax, 0
  push ds
  mov ds,ax
  cli                                      ;disable interrupts for the change
  mov word ptr ds:[intNum*4],Newisr_add
  mov word ptr ds:[intNum*4+2],cs          ;word ptr may not be needed here
  sti
  pop ds
  pop ax
endm


        .CODE

;----------------------------------------------------------------------------------

LoadOfs EQU     0               ;must match the value in the bootloader source file
INT1C   EQU     01CH

;----------------------------------------------------------------------------------

;---------------------- initialize ES segment register

        ORG     0

Start:  push    cs
        pop     ds
       
;-----------clear screen
mov ax, 3
int 10h

   
;---------------------- writing a message on screen at startup, character by character- we can't use int 21h
overdata:
       
        xor di, di
        mov ax, 0B800h
        mov es, ax
        mov si, offset msg0+LoadOfs
       mov ah, 41h; attribute byte
       cld;
msgloop:
        lodsb; loads al with a byte of data pted by ds:si
        or al, al
        jz TimerMesg
        stosw; transfers the contents of al to mem location ptd by es:di
        jmp msgloop
       
;---------------------- done - halt
TimerMesg:

        xor di, di
        mov ax, 0B820h
        mov es, ax
        mov si, offset msgA+LoadOfs
       mov ah, 41h; attribute byte
       cld;
msgAloop:
        lodsb; loads al with a byte of data pted by ds:si
        or al, al
        jz DISPLAY_TIMER
        stosw; transfers the contents of al to mem location ptd by es:di
        jmp msgAloop
        mov cx, 1000
DISPLAY_TIMER:install 1Ch, INT1Ch
        mov bh, byte ptr[cs:secs]
        mov s,0;--------------- selector for sec
        call PrintSMH;--------- displaying seconds/minutes/hours
        mov bh, byte ptr[cs:mins]
        mov s,2;--------------- selector for min
        call PrintSMH;--------- displaying seconds/minutes/hours
        mov bh, byte ptr[cs:hrs]
        mov s,4;--------------- selector for hrs
        call PrintSMH;--------- displaying seconds/minutes/hours
        Loop Display_TIMER
Halt0: hlt
jmp     Halt0

;---------------------- data area in code segment


Msg0    db      "We be bootin234!",0
msgA db 'Total minutes elapsed since Kernel start is',0
clkcounter db 0
secs db 0
mins db 0
hrs  db 0
cnt  db 0; Its value represents the digits of Timer
s    db 0; selector for secs minutes and hrs used in displayCnt

INT1Ch:
  cli    ;not needed at the beginning of an interrupt handler - the interrupt clears that flag automatically

  inc byte ptr [cs:clkcounter]
  cmp byte ptr [cs:clkcounter],18; if clkcounter is 18, it means 1 sec
  jz handle_secs
  sti
  iret
handle_secs:
  mov byte ptr[cs:clkcounter],0
  inc byte ptr[cs:secs]
  cmp byte ptr[cs:secs],60;if secs is 60, it means 1 min
  jz handle_mins
  sti
  iret
handle_mins:
  mov byte ptr[cs:secs],0
  inc byte ptr[cs:mins]; we are done, now print the number of minutes elapsed
  cmp byte ptr[cs:mins],60
  jz handle_hrs
  sti
  iret
handle_hrs:
  mov byte ptr[cs:mins],0
  inc byte ptr[cs:hrs]
  cmp byte ptr[cs:hrs],24
  jz hrsZero
  sti
  iret
hrsZero:
  mov byte ptr[cs:hrs], 0
  sti
  iret
PrintSMH:
  mov ax, 100; For two digits, we have  'subtract' =10^1 and 10^0
  mov bl, 10
NextDigit: Div BL; AL=AX/BL
  mov ah, 0;value of AL would be the value of AX
  cmp AL,0; until 'subtract'=0
  je done
againSub: Sub bh, AL;e.g to print 58,First print 5,58-10-10-10-10-10-10, count=6, digit=count-1=5
                    ;Note after subtraction bh=-2, add back i.e -2+10=8 which is the next digit
                    ;to print 8,8-1-1-1-1-1-1-1-1-1,count=9, digit=count-1=8
          cmp bh,0;when bh=-ve we stop subtraction
          jb NoSub
          inc cnt
          jmp againSub
NoSub: add bh, al; add back
       call DisplayCnt
       inc s
       jmp NextDigit
    done:ret
DisplayCnt:
  cmp s, 0
  je FirstDigitOfSecs
  cmp s, 1
  je SecondDigitOfSecs
  cmp s, 2
  je FirstDigitOfMins
  cmp s, 3
  je SecondDigitOfMins
  cmp s,4
  je FirstDigitofHrs
  cmp s,5
  je SecondDigitofHrs
  ret
FirstDigitOfSecs: cld
    mov ax,0B880h
    mov es,ax
    xor di,di
    mov ah,1Fh
    add cnt, 30h
    mov al,bh
    mov cx,2
    rep stosw
    ret
SecondDigitOfSecs:cld;----------------- Displaying colon also
    mov ax,0B882h
    mov es,ax
    xor di,di
    mov ah,1Fh
    add cnt, 30h
    mov al,bh
    mov cx,2
    rep stosw
    mov ax,0B884h
    mov es,ax
    xor di,di
    mov ah,1Fh
    mov al,':'
    mov cx,2
    rep stosw
    ret
FirstDigitOfMins:cld
    mov ax,0B886h
    mov es,ax
    xor di,di
    mov ah,1Fh
    add cnt, 30h
    mov al,bh
    mov cx,2
    rep stosw
    ret
SecondDigitOfMins:cld;----------------- Displaying colon also
    mov ax,0B888h
    mov es,ax
    xor di,di
    mov ah,1Fh
    add cnt, 30h
    mov al,bh
    mov cx,2
    rep stosw
    mov ax,0B88Ah
    mov es,ax
    xor di,di
    mov ah,1Fh
    mov al,':'
    mov cx,2
    rep stosw
    ret
FirstDigitofHrs:cld
    mov ax,0B88Ch
    mov es,ax
    xor di,di
    mov ah,1Fh
    add cnt, 30h
    mov al,bh
    mov cx,2
    rep stosw
    ret
SecondDigitOfHrs:cld
    mov ax,0B88Eh
    mov es,ax
    xor di,di
    mov ah,1Fh
    add cnt, 30h
    mov al,bh
    mov cx,2
    rep stosw
    ret 

       
       


;----------------------------------------------------------------------------------

        END     Start




Zulfi.

dedndave

Zulfi - i fixed a bug in both the posted code and the d/l attachment
the UpdCk routine simply calculates the hours, minutes, seconds, hundredths from the clock tick count value
i may do as Sinsi suggested and write one to get the time from the RTC (it should be faster)

i think you misunderstood much of what Michael said

interrupt handlers need to preserve all registers used, as well as the state of the machine
the flags are saved for you by the interrupt mechanism
when an interrupt occurs, the current flags are pushed onto the stack, then the interrupt flag is cleared (as though CLI)
after that, the CS and IP registers are pushed just like a normal far call
once the critical part of the interrupt handler has executed, you may use STI to enable
other interrupts during execution of the remainder of your handler code

after an interrupt has been handled, the machine needs to return to it's original execution
it must appear as though nothing has changed - i.e. the registers and flags must remain unaltered
it is also important to note that the FPU registers should remain unaltered, unless perhaps, it is an FPU exception handler

go back and re-read what Michael posted - a lot of good info, there   :bg