How PoP counts time (technical)

Discuss PoP1 for DOS here.
Post Reply
David
The Prince of Persia
The Prince of Persia
Posts: 2848
Joined: December 11th, 2008, 9:48 pm
Location: Hungary

How PoP counts time (technical)

Post by David »

from viewtopic.php?p=13724#p13724
Jakim wrote:Anyway, did anybody check how PoP counts time? Hardware/software? I've had various cycles during game and I'm curious how it its affect is.
(Scroll to the bottom for the interesting part)

PoP has 4 timers, numbered 0 to 3.
All 4 timers are used like this:
- Set it to the time you want to wait.
- Wait until it becomes zero.

Interrupt 8 decreases each timer periodically:

Code: Select all

seg009:79C5 new_int_8_timer2 proc far               ; CODE XREF: int8_speaker_sound_2+2FP
seg009:79C5                                         ; new_int_8_midi+87P
seg009:79C5                                         ; DATA XREF: ...
seg009:79C5                 push    ds
seg009:79C6                 push    ax
seg009:79C7                 push    bx
seg009:79C8                 mov     ax, seg data
seg009:79CB                 mov     ds, ax
seg009:79CD                 mov     bx, 6           ; 6, 4, 2, 0 -> 4 timers
seg009:79D0                 inc     word_1DEC8
seg009:79D4                 jnz     loc_1416A
seg009:79D6                 inc     word_1DECA
seg009:79DA 
seg009:79DA loc_1416A:                              ; CODE XREF: new_int_8_timer2+Fj
seg009:79DA                                         ; new_int_8_timer2+23j
seg009:79DA                 cmp     wait_time0[bx], 0
seg009:79DF                 jz      loc_14175
seg009:79E1                 dec     wait_time0[bx]
seg009:79E5 
seg009:79E5 loc_14175:                              ; CODE XREF: new_int_8_timer2+1Aj
seg009:79E5                 sub     bx, 2
seg009:79E8                 jnb     loc_1416A
seg009:79EA                 mov     ax, word_1DEE2
seg009:79ED                 add     word_1DED6, ax
seg009:79F1                 jnb     loc_1418A
seg009:79F3                 pushf
seg009:79F4                 call    dword_1DED8
seg009:79F8                 jmp     short loc_1418E
seg009:79FA ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
seg009:79FA 
seg009:79FA loc_1418A:                              ; CODE XREF: new_int_8_timer2+2Cj
seg009:79FA                 mov     al, 20h ; ' '
seg009:79FC                 out     20h, al         ; Interrupt controller, 8259A.
seg009:79FE 
seg009:79FE loc_1418E:                              ; CODE XREF: new_int_8_timer2+33j
seg009:79FE                 pop     bx
seg009:79FF                 pop     ax
seg009:7A00                 pop     ds
seg009:7A01                 iret
seg009:7A01 new_int_8_timer2 endp

Code: Select all

000142BA:i1E                             push      ds
000142BB:i50                             push      ax
000142BC:i53                             push      bx
000142BD:iB8CE1A                         mov       ax,1ACE
000142C0:i8ED8                           mov       ds,ax
000142C2:iBB0600                         mov       bx,0006
000142C5:iFF06E831                       inc       [+31E8]
000142C9:i7504                           jne       file:000142CF
000142CB:iFF06EA31                       inc       [+31EA]
000142CF:i83BFEC3100                     cmp (w)   [bx+31EC],+00
000142D4:i7404                           je        file:000142DA
000142D6:iFF8FEC31                       dec       [bx+31EC]
000142DA:i83EB02                         sub (w)   bx,+02
000142DD:i73F0                           jnc       file:000142CF
000142DF:iA10232                         mov       ax,[+3202]
000142E2:i0106F631                       add       [+31F6],ax
000142E6:i7307                           jnc       file:000142EF
000142E8:i9C                             pushfw
000142E9:iFF1EF831                       call (d)  [+31F8]
000142ED:iEB04                           jmps      file:000142F3
000142EF:iB020                           mov       al,20
000142F1:iE620                           out       20,al
000142F3:i5B                             pop       bx
000142F4:i58                             pop       ax
000142F5:i1F                             pop       ds
000142F6:iCF                             iret16
Here is the above procedure assigned to Interrupt 8.

Code: Select all

seg009:7A02 set_other_timer proc far                ; CODE XREF: speaker_sound_stop+Fp
seg009:7A02                                         ; set_other_timer_2+Fp
seg009:7A02                 pushf
seg009:7A03                 cli
seg009:7A04                 sub     ax, ax
seg009:7A06                 mov     es, ax
seg009:7A08                 mov     word ptr es:20h, offset new_int_8_timer2 ; int 8
seg009:7A0F                 mov     es:22h, cs
seg009:7A14                 mov     al, byte_1DEEA
seg009:7A17                 out     43h, al         ; Timer 8253-5 (AT: 8254.2).
seg009:7A19                 mov     ax, word_1DEE2
seg009:7A1C                 mov     word_1DEE0, ax
seg009:7A1F                 out     40h, al         ; Timer 8253-5 (AT: 8254.2).
seg009:7A21                 xchg    al, ah
seg009:7A23                 out     40h, al         ; Timer 8253-5 (AT: 8254.2).
seg009:7A25                 popf
seg009:7A26                 retf
seg009:7A26 set_other_timer endp

Code: Select all

000142F7:i9C                             pushf
000142F8:iFA                             cli
000142F9:i2BC0                           sub       ax,ax
000142FB:i8EC0                           mov       es,ax
000142FD:i26C7062000C579                 mov       [es:+0020],79C5
00014304:i268C0E2200                     mov       [es:+0022],cs
00014309:iA00A32                         mov       al,[+320A]
0001430C:iE643                           out       43,al
0001430E:iA10232                         mov       ax,[+3202]
00014311:iA30032                         mov       [+3200],ax
00014314:iE640                           out       40,al
00014316:i86C4                           xchg      al,ah
00014318:iE640                           out       40,al
0001431A:i9D                             popf
0001431B:iCB                             retf
And here is the code that uses timer 1:

Code: Select all

First, set the timer:
seg003:04FE loc_4CEE:                               ; CODE XREF: sub_4CE8+62j
seg003:04FE                 cmp     kid_sword, 2
seg003:0503                 jnz     loc_4CFD
seg003:0505                 mov     wait_time1, 6 ; speed when fighting (smaller is faster)
seg003:050B                 jmp     short loc_4D03
seg003:050D ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
seg003:050D 
seg003:050D loc_4CFD:                               ; CODE XREF: sub_4CE8+Bj
seg003:050D                 mov     wait_time1, 5 ; speed when not fighting (smaller is faster)
seg003:0513 
seg003:0513 loc_4D03:                               ; CODE XREF: sub_4CE8+13j
...
Then wait until it becomes zero: (Busy waiting)
seg003:0555 loc_4D45:                               ; CODE XREF: sub_4CE8+64j
seg003:0555                 cmp     wait_time1, 0
seg003:055A                 jz      loc_4CEE
seg003:055C                 jmp     short loc_4D45

Code: Select all

00004EEE:i803E644302                     cmp       [+4364],02
00004EF3:i7508                           jne       file:00004EFD
00004EF5:iC706EE310600                   mov       [+31EE],0006 ; speed when fighting (smaller is faster)
00004EFB:iEB06                           jmps      file:00004F03
00004EFD:iC706EE310500                   mov       [+31EE],0005 ; speed when not fighting (smaller is faster)
...
00004F45:i833EEE3100                     cmp (w)   [+31EE],+00
00004F4A:i74A2                           je        file:00004EEE
00004F4C:iEBF7                           jmps      file:00004F45
Here comes the interesting part:
Search: C7 06 EE 31 06 00 EB 06 C7 06 EE 31 05 00
If you change the bolded parts to something smaller, the game will become faster. 00 00 will make it unplayably faster.
If you change only one of them, then only the fighting becomes faster, or only the non-fighting parts.
When the game runs faster this way, the remaining time also decreases faster. (Try it!)

So to sum up things:
- Timing is based on timer interrupts, which are not affected by the speed of the CPU or DOSBox cycles. (However, if the game runs very slow, the timer may already have reached zero when the busy waiting loop is reached. In this case the game will be slower than intended.)
- Speed is a bit different (slower) when fighting. And it seems to be done on purpose!
- Time also passes slower when you're fighting!

Also, did you know that time stops if the kid dies? You can see this best if there is less than one minute left.
User avatar
Jakim
Beylerbey
Beylerbey
Posts: 55
Joined: March 5th, 2011, 2:27 pm
Location: Poland
Contact:

Re: How PoP counts time (technical)

Post by Jakim »

That is was I looking for. Thanks, I'm going to check it out. I was sure you will know the answer :D.
Also, did you know that time stops if the kid dies? You can see this best if there is less than one minute left.
Yeah. However, I don't plan to die ;).
User avatar
Norbert
The Prince of Persia
The Prince of Persia
Posts: 5745
Joined: April 9th, 2009, 10:58 pm

Re: How PoP counts time (technical)

Post by Norbert »

David wrote: August 4th, 2013, 11:19 amSearch: C7 06 EE 31 06 00 EB 06 C7 06 EE 31 05 00
And...
For 1.3:
C7 06 C8 34 06 00 EB 06 C7 06 C8 34 05 00
For 1.4:
C7 06 EC 32 06 00 EB 06 C7 06 EC 32 05 00

I'll add these (and the chomper speed) to the EXE screen of apoplexy 3.3.
Post Reply