The MASM Forum Archive 2004 to 2012

Miscellaneous Forums => 16 bit DOS Programming => Topic started by: amrac on December 29, 2009, 04:33:21 PM

Title: Animation with int 21h ah=2ch
Post by: amrac on December 29, 2009, 04:33:21 PM
I have a little program that paints a little pixel on the screen and each time the user presses a key the pixel moves down. I want to move the pixel down but I want to do it within a certain interval of time, say 500 milliseconds. How can I do that? I know that I have to use int 21h that puts milliseconds in ah, but I don´t know how to start. Here goes my code:
STACK SEGMENT PARA STACK
   DB   64 DUP ('MYSTACK ')
STACK ENDS

MYCODE SEGMENT PARA 'CODE'
MYPROC PROC FAR
   ASSUME CS:MYCODE,SS:STACK
      PUSH DS
      SUB AX,AX
      PUSH AX
      
      MOV AH, 00h
      MOV AL, 04h
      INT 10h
      
      ; MOV AH, 11
      ; MOV BH, 00
      ; MOV BL, 01
      ; INT 10h
      
      MOV AH, 11
      MOV BH, 01
      MOV BL, 00
      INT 10h
      
      MOV DX, 64h
      MOV CX,20
      
Game:
      PUSH CX
      MOV AL, 02      ;pixel cor vermelha
      MOV AH, 12
      MOV CX, 9Eh
      INT 10h
      
      MOV ah, 01
      INT 21h      ;espera por uma tecla
      
      MOV AL, 00      ;pixel cor preto
      MOV AH, 12
      MOV CX, 9Eh
      INT 10h
      
      INC DX
      POP CX
      Loop Game
      
      MOV ah, 01
      INT 21h
      
      MOV AH, 00h
      MOV AL, 02h
      INT 10h
      
      RET
MYPROC ENDP
MYCODE ENDS

   END
Title: Re: Animation with int 21h ah=2ch
Post by: dedndave on December 29, 2009, 04:41:47 PM
i would use BIOS INT 1Ah, function 0

;===================================================================

DELAY   PROC    NEAR
;
;time delay
;
;AX = tick count to wait, 0 to 255 (0 to 14 seconds)
;
;----------------------------------------

        OR      AX,AX
        JNZ     EXECUTE_DELAY

        RET                       ;if AX = 0, exit immediately

EXECUTE_DELAY:
        PUSH    CX
        PUSH    DX
        PUSH    BX
        PUSH    AX
        OR      AH,AH
        JZ      UNDER_LIMIT

        MOV     AX,0FFh           ;max limit = 255 ticks (~14 seconds)

UNDER_LIMIT:
        PUSH    AX
        INT     1Ah               ;CX:DX = tick count
        POP     AX
        SUB     BX,BX             ;BX:AX = wait tick count

        ADD     AX,DX
        ADC     BX,CX             ;BX:AX = timeout value

        MOV     DX,0B0h
        MOV     CX,18h            ;CX:DX = midnight rollover value

        SUB     AX,DX
        SBB     BX,CX             ;subtract rollover
        JS      OVERFLOW_ADJUST

        PUSH    BX
        PUSH    AX

ROLLOVER_LOOP:
        MOV     AH,0
        INT     1Ah
        JCXZ    ROLLOVER_ENTRY

        JMP     ROLLOVER_LOOP

OVERFLOW_ADJUST:
        ADD     AX,DX
        ADC     BX,CX

WAIT_LOOP:
        PUSH    BX
        PUSH    AX
        MOV     AH,0
        INT     1Ah

ROLLOVER_ENTRY:
        POP     AX
        POP     BX
        CMP     BX,CX
        JB      DELAY_EXIT

        JA      WAIT_LOOP

        CMP     AX,DX
        JA      WAIT_LOOP

DELAY_EXIT:
        POP     AX
        POP     BX
        POP     DX
        POP     CX
        RET

DELAY   ENDP

;===================================================================
Title: Re: Animation with int 21h ah=2ch
Post by: amrac on December 29, 2009, 04:56:03 PM
What I don´t understand is how can I interoperate this routine with my code.
Title: Re: Animation with int 21h ah=2ch
Post by: dedndave on December 29, 2009, 05:32:13 PM
each clock tick is ~55 ms
500 ms is about 9 ticks, so...

        mov     ax,9
        call    DELAY

EDIT there are better ways to get a more repeatable delay
that involves revectoring one of the timer interrupts
this is a simple delay - it won't be exactly 500 ms everytime you call it
Title: Re: Animation with int 21h ah=2ch
Post by: amrac on December 29, 2009, 07:49:03 PM
Well I discovered that it has to be faster but it works just fine. Thanks!!
Title: Re: Animation with int 21h ah=2ch
Post by: amrac on December 29, 2009, 08:28:14 PM
I´ve putten the smallest delay. Is there some code in the procedure that I can change to make the delay smaller? Here goes "my" code:
STACK SEGMENT PARA STACK
   DB   64 DUP ('MYSTACK ')
STACK ENDS

MYCODE SEGMENT PARA 'CODE'
MYPROC PROC FAR
   ASSUME CS:MYCODE,SS:STACK
   
      PUSH DS
      SUB AX,AX
      PUSH AX
      
      MOV AH, 00h
      MOV AL, 04h
      INT 10h
      
      ; MOV AH, 11
      ; MOV BH, 00
      ; MOV BL, 01
      ; INT 10h
      
      MOV AH, 11
      MOV BH, 01
      MOV BL, 00
      INT 10h
      
      mov dx,10h      ;selecciona a linha 100
      mov cx,9eh       ; seleciona a coluna 158
   
      
      ;MOV DX, 64h
      MOV CX,180
      
Game:
      PUSH CX
      MOV AL, 02      ;pixel cor vermelha
      MOV AH, 12
      MOV CX, 9Eh
      INT 10h
      
      ;MOV ah, 01
      ;INT 21h      ;espera por uma tecla

      mov     ax, 1      ;-------------------------------------------------------------------------------------------------<<<<<HERE IS THE SMALLER DELAY THAT I COULD MAKE
                call    DELAY
      
      MOV AL, 00      ;pixel cor preto
      MOV AH, 12
      MOV CX, 9Eh
      INT 10h
      
      INC DX
      POP CX
      Loop Game
      
      MOV ah, 01
      INT 21h
      
      MOV AH, 00h
      MOV AL, 02h
      INT 10h
      
      RET
MYPROC ENDP
DELAY   PROC    NEAR
;
;time delay
;
;AX = tick count to wait, 0 to 255 (0 to 14 seconds)
;
;----------------------------------------

        OR      AX,AX
        JNZ     EXECUTE_DELAY

        RET                       ;if AX = 0, exit immediately

EXECUTE_DELAY:
        PUSH    CX
        PUSH    DX
        PUSH    BX
        PUSH    AX
        OR      AH,AH
        JZ      UNDER_LIMIT

        MOV     AX,0FFh           ;max limit = 255 ticks (~14 seconds)

UNDER_LIMIT:
        PUSH    AX
        INT     1Ah               ;CX:DX = tick count
        POP     AX
        SUB     BX,BX             ;BX:AX = wait tick count

        ADD     AX,DX
        ADC     BX,CX             ;BX:AX = timeout value

        MOV     DX,0B0h
        MOV     CX,18h            ;CX:DX = midnight rollover value

        SUB     AX,DX
        SBB     BX,CX             ;subtract rollover
        JS      OVERFLOW_ADJUST

        PUSH    BX
        PUSH    AX

ROLLOVER_LOOP:
        MOV     AH,0
        INT     1Ah
        JCXZ    ROLLOVER_ENTRY

        JMP     ROLLOVER_LOOP

OVERFLOW_ADJUST:
        ADD     AX,DX
        ADC     BX,CX

WAIT_LOOP:
        PUSH    BX
        PUSH    AX
        MOV     AH,0
        INT     1Ah

ROLLOVER_ENTRY:
        POP     AX
        POP     BX
        CMP     BX,CX
        JB      DELAY_EXIT

        JA      WAIT_LOOP

        CMP     AX,DX
        JA      WAIT_LOOP

DELAY_EXIT:
        POP     AX
        POP     BX
        POP     DX
        POP     CX
        RET

DELAY   ENDP
MYCODE ENDS

   END
Title: Re: Animation with int 21h ah=2ch
Post by: dedndave on December 29, 2009, 09:18:40 PM
for very small delays, you can just use a loop
the value you place into CX will deteremine how long the loop takes

        mov     cx,DelayCount       ;this can be a constant

Delay0: loop    Delay0

here is another example where i place a loop inside another loop
it can create somewhat longer delays

        mov     cx,DelayCount       ;this can be a constant

Delay0: push    cx
        xor     cx,cx

Delay1: loop    Delay1

        pop     cx
        loop    Delay0

the problem with these kinds of delay loops is - they run at different speeds on different machines

a more complicated approach is to use one of the counter/timers
it takes a bit more programming, but will yield the same results on most machines
Title: Re: Animation with int 21h ah=2ch
Post by: FORTRANS on December 29, 2009, 09:56:06 PM
Hi,

   Depending on your system, INT 15H function 86H in a
wait function.  See Ralf Brown's Interrupt List (RBIL).


AH = 86H
CX:DX = 32 bit count in microseconds (resolution is 976 us)
INT 15H


   It did not work on my Windows 2000 system, but someone
mentioned a service pack may have changed that.  Instead
of the 18.2 times a second the INT 1CH gives you (55
millisecond wait), you get about a millisecond wait.

   You should be able to read the timer counting down as well
as it generates the 18.2 per second interrupts.  That could
give you very short delays.  That is the ports from 40H to
43H.  Again see RIBL.


Port  (data from "Undocumented DOS")

40H = Timer 0, system ticks
41H = Timer 1, DRAM refresh
42H = Timer 2, general use
43H = Tomers 0 - 2 mode control


Regards,

Steve N.
Title: Re: Animation with int 21h ah=2ch
Post by: amrac on December 29, 2009, 11:10:54 PM
Thanks once again for your reply. Is there a way in which I can freeze the process of making the little pixel fall? I need to have that functionality in my game. I mean the pixel is falling but when the user presses a key the pixel stops.
Title: Re: Animation with int 21h ah=2ch
Post by: MichaelW on December 30, 2009, 07:35:30 AM
Here is another delay procedure.

; -----------------------------------------------------------
; This proc delays for the specified number of milliseconds.
; It does this by counting memory refresh requests, which
; for AT-class systems are generated by system timer 1,
; normally programmed by the BIOS to generate an output once
; every 18/1193182 = 15.09 microseconds. Each request toggles
; bit 4 of I/O port 61h. We are counting full cycles of the
; bit, with two toggles per cycle, so each cycle takes 30.18
; microseconds and we count 34 cycles for each millisecond.
;
; Call with the number of milliseconds in AX.
;
; Preserves all registers other than AX.
;
; Note that the delay will be much shorter when running
; under the NT versions of Windows.
; -----------------------------------------------------------

MsDelay proc
    push cx

    mov cx, ax            ; load msLoop count
  msLoop:
    push cx               ; preserve msLoop counter
    mov cx, 34            ; load repeat count
  wait1:
    in  al, 61h           ; read byte from port
    test al, 10h          ; test bit 4
    jz  wait1             ; jump if bit clear
  wait2:
    in  al, 61h           ; read byte from port
    test al, 10h          ; test bit 4
    jnz wait2             ; jump if bit not clear
    dec cx                ; decrement repeat count
    jnz wait1             ; jump if count not zero
    pop cx                ; recover msLoop counter
    dec cx                ; decrement msLoop count
    jnz msLoop            ; jump if count not zero

    pop cx
    ret
MsDelay endp

Title: Re: Animation with int 21h ah=2ch
Post by: dedndave on December 30, 2009, 10:10:48 AM
lol - that is some old-timie stuff, Michael
how old are you, anyways ?
Title: Re: Animation with int 21h ah=2ch
Post by: amrac on December 30, 2009, 10:29:35 AM
I don´t understand. Does this mean that the second procedure permits me to stop at any moment the pixel from falling and the first procedure doesn't?
Title: Re: Animation with int 21h ah=2ch
Post by: amrac on December 30, 2009, 10:42:14 AM
I tried the second procedure and it runs much faster. But can I stop the pixel from moving. I tried putting the value in ax to zero but it still moves from times to times.
Title: Re: Animation with int 21h ah=2ch
Post by: dedndave on December 30, 2009, 10:52:43 AM
well - stopping the pixel should not be dependant on a delay routine
that is more of a program flow issue
Title: Re: Animation with int 21h ah=2ch
Post by: FORTRANS on December 30, 2009, 02:25:18 PM
Hi,

   Looks good Michael.  Will check it out on some different
systems.  Do you have a correction for the "NT versions"?

Regards,

Steve N.
Title: Re: Animation with int 21h ah=2ch
Post by: amrac on December 30, 2009, 04:58:25 PM
Yes, It does work faster. The first procedure is too slow even if I put it to work at the fastest it can get. And it has much less code.
Title: Re: Animation with int 21h ah=2ch
Post by: MichaelW on December 30, 2009, 10:12:59 PM
Quote from: dedndave on December 30, 2009, 10:10:48 AM
lol - that is some old-timie stuff, Michael
how old are you, anyways ?

I first noticed the technique when I was examining some BIOS code. I think it was code that operated the diskette or hard drive. I'm half way to 118, so I'm approaching middle age :lol

Quote from: FORTRANS on December 30, 2009, 02:25:18 PM
Do you have a correction for the "NT versions"?

I never considered trying to derive a correction. For a delay value of 20000, on my Windows 2000 system it delays for ~6990ms. So applying a correction factor of 3, for a delay value of 60000 it delays for ~20900ms.
Title: Re: Animation with int 21h ah=2ch
Post by: FORTRANS on December 31, 2009, 04:42:28 PM
Hi,

   Well, Michael's routine does perform differently on different
systems.  So, before using it one should probably run some
checks or a calibration.  My Windows 2000 performs like
Michael's, about 3x.

   On two machines running DOS it runs pretty well as posted.
For a 15 second delay, one was 15 seconds or just over. the
other one was a second or two slow.

   On an PC/XT compatible it just locks up as expected as
that port changed on the PC/AT.

   On two OS/2 VDM's, one ran 2x and one was 7x.  (?)
May have to retry that one with a cold boot.

   The Int 15H wait function 86H does not work on the
Windows 2000 and PC/XT machines and returns immediately.
The rest ran okay.

   It is nice that Michael's routine works on Win2k, where the
Int 15 wait does not.  And it provides significantly shorter
delays than the ones I was using there to get around Int 15
not working.

Thanks,

Steve N.
Title: Re: Animation with int 21h ah=2ch
Post by: amrac on January 01, 2010, 11:00:13 PM
With int 21h and ah equal to 2ch I get the milliseconds in al. But I find this strange because I can only have a maximum value of 255 milliseconds. Is this correct?
Title: Re: Animation with int 21h ah=2ch
Post by: dedndave on January 02, 2010, 02:59:48 AM
those are probably 100th's of a second (0-99)
as a side-note - the dos clock updates 18.2 times per second, so the hundredths do not step "evenly"
the hundredths skips over several values on each clock tick (it doesn't count 1,2,3,4...)
Title: Re: Animation with int 21h ah=2ch
Post by: sinsi on January 02, 2010, 04:16:50 AM
AL isn't a return value -

INT 21 - DOS 1+ - GET SYSTEM TIME
AH = 2Ch
Return: CH = hour
CL = minute
DH = second
DL = 1/100 seconds
Note:   on most systems, the resolution of the system clock is about 5/100sec,
so returned times generally do not increment by 1
on some systems, DL may always return 00h

Title: Re: Animation with int 21h ah=2ch
Post by: MichaelW on January 02, 2010, 05:56:38 AM
For Function 2Ch, Get Time, the documented (in the Microsoft MS-DOS Programmer's Reference) return values are:

CH = hour in 24-hour format
CL = minutes (0 to 59)
DH = seconds (0 to 59)
DL = hundredths of a second (0 to 99)

Running under Windows 2000, on return AL is always zero, and the value in DL changes in increments of 5 or 6 counts so the resolution is effectively the period of the timer tick ~ 55ms. The code that generated this displayed the return values each time the value in DL changed:

AL CH  CL  DH  DL
0  19  10  41  78
0  19  10  41  83
0  19  10  41  89
0  19  10  41  94
0  19  10  42  0
0  19  10  42  5
0  19  10  42  11
0  19  10  42  16
0  19  10  42  22
0  19  10  42  27
0  19  10  42  33
0  19  10  42  38
0  19  10  42  44
0  19  10  42  49
0  19  10  42  55
0  19  10  42  60
0  19  10  42  66
0  19  10  42  71
0  19  10  42  77
0  19  10  42  82


To get a higher resolution you need to access a counter that runs faster than the system timer tick. Running under Windows you have only a limited number of choices.