; File TIME.S includes the SIO2PC routines related to using ; and programming the timer chip, and timing routines in general ; ; ; Includes: ; ACC_DLY; TIMER_0; TIME_LAG; PRG_T0 ; MYVEC8; FIXV8; COLSEV8; ONE_TIK; TIMING ; SETUPV8; DO_TIME ; The following notes copied from NEW_TIME.ASM ; Notes: The 8254 chip is clocked at 1.193 Mhz. The period is ; 840 nanoseconds. Timer 2 is gated by bit 0 of port 61h. Timer 2 ; produces the sound through the speaker. Bit 1 of port 61h gates ; the timer's output to the speaker (1 = enable sound). ; Port # dir: COMMENT ; 040h R/W Counter 0 data. Counter 0 is sys. timer; IRQ 0 ; 041h R/W Counter 1 data. Used for refresh, 15 usec. period ; 042h R/W Counter 2 data. Gated by 61h, b0, used for speaker. ; 043h W Control word. COMMENT\ CONTROL WORD FORMAT: (PORT 043h) BIT 7 BIT 6 VALUE 1 1 Read back command, see below 0Ch 0 1 Select counter 1 40h 1 0 Select counter 2 80h 0 0 Select counter 0 00H BIT 5 BIT 4 0 0 Counter latch command 00h 0 1 Read/Write LSB only 10h 1 0 Read/Write MSB only 20h 1 1 Read/Write LSB, then MSB 30h Note: the remaining bits are don't care for Counter Latch command, and have different meanings (see below) for Read Back command. Otherwise, they are for mode programming as given below: BIT 3 BIT 2 BIT 1 VALUE 0 0 0 MODE 0 00h 0 0 1 MODE 1 02h X 1 0 MODE 2 04h X 1 1 MODE 3 06h 1 0 0 MODE 4 08h 1 0 1 MODE 5 09h BIT 0 is 0 for binary counter (normal) and 1 for BCD counter. Direct speaker control via PORT 061h: bit 0: set (1) for normal control by timer 2 bit 0: clear (0) for direct control bit 1: set to turn speaker on (cone out) when bit 0 is 0 bit 1: clear to turn speaker off (cone in) when bit 0 is 0 Ref. Using Assembly Language page 386 ENDOFCOMMENT\ ; A routine to waste just a few machine cycles, mainly for ; slow ports, between successive IN's and OUT's. TIME_LAG: NOP NOP NOP NOP JMP >L1 L1: RET ; A routine to delay until VARBL has changed value (one tick) ; or 55 msec. ONE_TIK: PUSH AX MOV AX,VARBL CMP VARBL,0; It won't change if it's zero JNE >L1 POP AX RET L1: CMP AX,VARBL JNE L1 POP AX RET ; the above routine has a resolution of 1 tick. The one below ; has more resolution: ; A post rev 3.14 addition ONETICK: PUSH AX, CX MOV CX, 4 L1: MOV BX, 08000h CALL TIMER_0 LOOP L1 POP CX, AX RET ; ; A routine to redirect the timer 0 interrupt to my routine ; This routine demonstrates the following: ; 1) Getting an interrupt vector ; 2) Re-directing an interrupt ; This routine is useful as a timer to time a certain number of timer ; ticks, each being 55 ms. After re-setting the interrupt, the user ; puts the number of ticks desired into WORD VARBL. The interrupt ; routine decrements the word until it is 0, and stops decrementing. ; The user can just monitor VARBL until it becomes zero. The normal ; functions of the timer 0 routine (INT 08) are still enabled. Some ; counts are: ; 10 SECONDS: 182 COUNTS ; 5 91 ; 3 55 ; 2 36 ; 1 18 ; .5 9 ; 110 mS 2 ; My interrupt routine: MYVEC8: STI PUSH DS PUSH CS POP DS ; *** NOTE TECHNIQUE FOR "CALLING" ORIGINAL INT ROUTINE AS ; THOUGH IT WERE A SUBROUTINE... **** PUSHF; See Bible, pg 247; simulate interrupt... CALL FAR [CONTIN]; let old handler go first then IRET here CMP VARBL,0; If it's already 0, don't change it JE >L1 DEC VARBL L1: POP DS IRET SETUPV8: MOV AL,08; Get INT 08 address MOV AH, 035h; Get interrupt vector function INT 021h; Now ES:BX holds interrupt vector MOV H_CONTIN,ES MOV CONTIN,BX ; Now change the interrupt vector to point to my routine: MOV AL,08; Set INT 08 address MOV AH,025h; Set interrupt DOS function MOV DX, OFFSET MYVEC8 PUSH CS POP DS INT 021h; Change the interrupt RET ; Now, to close, I'll restore the original vector: ; Note below:, I had to load DX first, because DS is the ref. ; segment for the MOV instruction! FIXV8: MOV AH,025h MOV AL,08 MOV DX, CONTIN MOV BX, H_CONTIN MOV DS,BX INT 021h PUSH CS; Get DS back to program segment POP DS; just because I like it that way... RET ; CLOSEV8 will be used to fix INT 08 vector from the INT 024h ; handler. It does the job without DOS because you're not supposed ; to call high DOS functions while in INT 024h handler. I'm relying ; on having interrupts turned off to keep me out of trouble. Note: ; don't try to use TICKS macro after turning off this function! ; NOTE: if there are problems, turn off timer interrupts directly at ; chip. CLOSEV8: CLI PUSH ES PUSH BX PUSH AX XOR BX,BX; Put 0 into BX, int table is in segment 0000 MOV ES,BX MOV BX,020h; Location of INT 8 vector MOV AX, CONTIN MOV ES:[BX],AX MOV AX,H_CONTIN MOV ES:[BX+2],AX STI POP AX POP BX POP ES RET ; ******************************************************************* ; Timing routines for timer #2 imported from NEW_TIME.ASM as part of ; rev. 4.08. Intent is to use timer 2 when timer 0 is busy with ; VARBL related timing. ; ******************************************************************* COMMENT\ ; A subroutine to check timer 2 till it times out, then return ; Since reading the out pin didn't work, I'll try reading the counter ; till it's less than 256. ONE_SHOT: MOV AL,0 MOV ERROR,AL MOV WERROR,0; IF COUNT DOESN'T CHANGE L1: MOV DX,043h MOV AL,11101000b; Latch status of counter 2 OUT DX,AL NOP INC ERROR; count loop iterations NOP MOV DX,042h IN AL,DX ; Get status, b7 is the out pin status ; IN AL,DX ; Get latched count, low byte. ; MOV AH,AL ; IN AL,DX ; now get high byte ; XCHG AL,AH ; Put in correct order... ; CMP AH,0 ; High byte down to 0? AND AL,080h ; b7 will be low until counter times out JZ L1 RET EOUT: MOV ERROR,0 ; Return error level 0 for no count change. RET ; A routine to set timer #2 to mode 1 and load with count = 0FFFFh MODE1: MOV DX,042h ; Clear out any latched junk: IN AL,DX NOP NOP NOP JMP >L1 L1: IN AL,DX MOV DX,061h IN AL,DX AND AL,0FEh; Clear bit 1 of control port to turn off timer. NOP NOP OUT DX,AL MOV DX,043h MOV AL,10110010b; Counter 2, 16 bit, mode 1. OUT DX,AL MOV DX,042h MOV AL,0FFh OUT DX,AL NOP JMP >L1 L1: OUT DX,AL ; Now, a loop using the one shot mode (1); MOV CX,182; 182 X 55 ms = 10 seconds DO_IT: MOV DX,061h IN AL,DX NOP NOP JMP >L1 L1: OR AL,1 OUT DX,AL; Set b1 on to trigger one-shot NOP NOP NOP NOP NOP JMP >L1 L1: NOP CALL ONE_SHOT NOP NOP NOP NOP MOV DX,061h IN AL,DX AND AL,0FEh; Clear b1 to reset one-shot OUT DX,AL LOOP DO_IT MOV AL,ERROR MOV AH,4Ch ; DOS end program function INT 021h END ENDOFCOMMENT\ ; TIMING ADJUST SUBROUTINE ; This subroutine lets the user input his own values for ; various serial bus timing loops. This is in hopes of curing ; 'ole Joe Woyak's timing problem once and for all. The calling ; program has cleared the screen and will restore it on return. TIMING: L2: CALL SCURRENT; Put current values into strings CALL CLR_SCRN PRINTL TMENU ABT: CALL GET_TORK CALL TO_UPPER; 3.20 ; CMP AL,'U'; "U" is a special: Update Menu ; JNE >L1 ;L2: CALL SCURRENT ; CALL CLR_SCRN ; PRINTL TMENU ; JMP ABT L1: CMP AL, 'G' ; 4.09 - diagnostic function JNE > P2 CALL DIAGS JMP > L7 P2: CMP AL, 'D' JNE > L3 CALL D1050 JMP > L7 L3: CMP AL, 'C' ; Change disk config: rev. 3.01 JNE > L1 AEC: CALL DISK_CONFG JMP L2; 3.20 L1: CMP AL, 'T' ; rev 3.06 addition JNE >L1 L6: CALL T_RI_SENSE JMP L2 L1: SUB AL,'1' CMP AL,9; REV 2.10, WAS 8 BEFORE JB >L1 JMP >L7 ; Quit if other than 1 thru 8 L1: XOR AH,AH MOV DI,AX SHL DI,1 PUSH DI; DI now points to word 0 - 8 PRINTL ENTER4 CALL GET4H; Hex number is returned in BX POP DI JC >L7 ; Carry means ESC pressed MOV [T1+DI],BX; Good #, so put into timing database ; PRINTL ST8 JMP L2 L7: RET DISK_CONFG: PRINTL CUR_DSTS MOV DI, 0 MOV CX, 4; 4 DISKS TO CHECK AEB: PUSH CX CMP B[WRITTN+DI],' '; Does this disk exist? JE AED LEA DX, [S_DISK + DI] WRITE_SCREEN 8 MOV AL, 'Y' TEST B[DSK_FLAGS+DI],2; B1 is "NO CONFG" JE >L1 MOV AL,'N' L1: CALL PRINT1; one byte to screen CALL LINE_FEED AED: POP CX ADD DI, BLOCK_SIZE LOOP AEB PRINTL ENT_NCH CALL GET_TORK CALL SET_BP; Carry clear if disk exists JC >L1 XOR B[DSK_FLAGS+BP],2; toggle configurability status CALL LINE_FEED JMP DISK_CONFG L1: RET ; This subroutine takes a hex # in AX and puts the 4 ASCII hex ; digits to the field "HEX_PLACE" HEX_IT: HEXTEX HEX_PLACE RET ; This routine takes the 16 bit numbers found in T1 thru T7, ; converts them to ASCII equivalents, and puts the ASCII strings ; into the strings ST1 thru ST9, getting them ready for printing. SCURRENT: MOV CX,8; counter, 8 items (BEFORE 2.10 WAS 7) L1: MOV BX,CX; BX will be index to specific WORD value DEC BX; do for 0 thru 6 SHL BX,1; Multiply by 2 to point to WORD MOV AX,[T1+BX]; Get actual word in AX CALL HEX_IT; Convert to string @ HEX_PLACE PUSH CX MOV DI,[STROFS+BX]; Get string offset pointer into DI MOV CX,4; Number of bytes to move PUSH CS POP ES MOV SI,OFFSET HEX_PLACE CLD REP MOVSB POP CX LOOP L1 RET ; A routine to program timer 0 to what it should already be: ; mode 3, count = 0 (2^16) ; Rev 3.18 change: was setting to MODE 2, now MODE 3 ... PRG_T0: MOV DX,043h; Address control port MOV AL,00110110xB; timer 0, 16 bit, mode 3, binary 3.18 OUT DX,AL MOV AL,0; now, output the count (0000) MOV DX,040h; Timer 0 port OUT DX,AL CALL TIME_LAG OUT DX,AL RET ; A routine to delay the number of 420 ns cycles as are stored ; in WORD @ T7. Used for delay between accesses to UART ACC_DLY: CMP T7,0 ; Just return if zero JE >L1 PUSH BX MOV BX,T7 CALL TIMER_0 POP BX L1: RET ; TIMER_0 ; rev 4.10 is converting TIMER_0 to actually use timer #2 ... ; ; A routine which waits until timer 0 has decremented the number ; of counts found in register BX. Each count takes about 420 nSec. ; Note: don't try this with BX = 0FFFFh; the CX < BX check is bound ; to always be true. In fact, I recommend that you don't even get ; close to 0FFFF, say within 1000, unless your computer is pretty ; fast, because "rollunder" will cause you to miss the event. ; Note that prior to 3.18, I thought each count was 840 nSec, but I've ; found out that the counter is counting by TWOS! ; 3.18 Changed this routine so it always set the timer up with a count ; of 0 and restarted. For some reason, this caused problems with PUTSEC ; (SIO2PC receiving sectors from the Atari). So 3.19 restores the old ; TIMER_0 method. ;SET DB 1 TIMER_0: PUSH AX PUSH DX PUSH CX COMMENT\ MOV DX,043h MOV AL,00000000xB; Latch counter 0 OUT DX,AL MOV DX,040h NOP IN AL,DX NOP NOP MOV CL,AL IN AL,DX NOP NOP MOV CH,AL ; CX now holds the initial count L1: MOV DX,043h MOV AL,0 OUT DX,AL MOV DX,040h NOP NOP IN AL,DX ; Get LSB MOV AH,AL NOP NOP IN AL,DX ; Get MSB XCHG AL,AH ; Correct order of LSB, MSB PUSH CX SUB CX,AX ; How many counts have passed? CMP CX,BX POP CX JB L1 ; Loop if CX less than BX ENDCOMMENT\ ;COMMENT\ rev 3.18 stuff taken out below, then fixed & put back 3.19 ; Note: it's necessary to turn bit 1 of PORT 61 on to allow input to ; timer 2. This isn't just for the speaker, it's needed for the counting ; function. It's done in the INIT part of SIO2PC. MOV DX, 043h MOV AL, 10110110xB; program timer 2 to mode 3 (b7 was 0) OUT DX, AL MOV AL, 0 MOV DX, 042h ; was 040h OUT DX, AL ; programmed count = 0 (65535) NOP NOP JMP >B1 B1: OUT DX, AL MOV CX, 0 SUB CX, BX ; CX now holds target count L1: MOV DX, 043h ; send counter latch command MOV AL, 080h ; was 0 OUT DX, AL ; now read the count (low, then high) MOV DX, 042h JMP >B2 B2: IN AL, DX MOV AH, AL NOP JMP >B3 B3: IN AL, DX XCHG AL, AH AND AX, AX JE L1 ; CMP CMND, 'W' ; JE > B6 ; CMP CMND, 'P' ; JNE > B5 ;B6: CMP SET,0 ; JE > B5; first time thru?? (3.19, debugging) ; PUSH SI ; this part for debugging ; MOV SI, T_COUNT ; MOV KEEP[SI],AX ; MOV KEEPBX[SI],BX ; MOV SET, 0 ; POP SI B5: CMP CX, AX ; 3.19 The BIG problem with 3.18 was testing the logic backwards ... ; I first changed from JG to JA to no avail, but finally realized ; that it needed to be JB, meaning branch if CX < AX and continue ; looping and timing ... JB L1; 3.19--was JG, but JG is for signed, JA for unsigned ... ;ENDCOMMENT\ POP CX POP DX POP AX ; CMP BX, 10 ; JB BREAK ; CMP CMND, 'W' ; JE > B7 ; CMP CMND, 'P' ; JNE > B4 ;B7: INC T_COUNT ; INC T_COUNT ; CMP T_COUNT, 20 ; JMP >B4 ;BREAK: NOP ;B4: MOV SET,0 RET ; DO_TIME ; This is a crude timer used to guard against getting stuck in a loop. ; Typically, at entry into the loop, you will put 0 into the word ; variable COUNT_TIME. Then, each time through, call this routine. If ; it returns with zero flag true, break out of the loop. Also, at entry ; into the loop, put the loop error ID character to variable B[ERR_CHAR]. ; If this routine times out, it will put the character to field ; STAT_EROR on the DEBUG status line. ; The time it takes is 65000*cycle time*# of cycles and is at least a ; few milliseconds, and could be several seconds in a big loop with a ; slow clock. COMMENT\ THIS ROUTINE HAS CAUSED ME PROBLEMS WITH FAST COMPUTERS (PENTIUMS), SO I'M GETTING RID OF IT!!! DO_TIME: DEC W[COUNT_TIME]; yes, DEC does condition ZF. JNZ >L1 CALL PUT_ERCH CMP AX,AX; Make sure ZF is still set L1: RET ENDOFCOMMENT\ ; BEEP gives a beep over the console speaker BEEP: CMP SOUND, 0 JE > L3 PUSH DX, AX MOV DX, 061h ; TTL control port IN AL, DX ; Now, program counter 2 for speaker. AND AL,0FEh; Set off bit 0 OUT DX,AL MOV DX,043h MOV AL,10110110xb; #2, 16 bit count, mode 3, binary OUT DX, AL MOV DX, 042h; Address counter #2 MOV AX, BEEPCNT ; 16 bit count OUT DX, AL XCHG AL, AH OUT DX, AL MOV DX, 061h; Now, turn on the gate IN AL, DX OR AL, 3; bits 0 and 1 on OUT DX, AL ; Hear anything? TICKS BEEPTIME MOV DX,061h IN AL, DX AND AL,0FFh - 2 ; Turn it off!!!!! (speaker output) OR AL, 1 ; Turn it on!! (gating input to counter #2) OUT DX, AL POP AX, DX L3: RET ; CLICK energizes the speaker coil for about 1 ms, then turns it off CLICK: CMP SOUND, 0 JE > L1 ; PUSH AX, BX, DX ; MOV DX, 063h ; IN AL, DX ; OR AL, 2 ; Make port 61h "input" ; OUT DX, AL MOV DX, 061h IN AL, DX ; AND AL, 0FFh - 1 ; clear bit 0 OR AL, 2; set bit 1 ; PUSH AX ; MOV DX, 063h ; IN AL, DX ; AND AL, 0FFh - 2 ; Make port 61h "output" ; OUT DX, AL ; POP AX ; MOV DX, 061h OUT DX, AL MOV BX, 2300 CALL TIMER_0 AND AL, 0FFh - 2; clear bit 1 OUT DX, AL POP DX, BX, AX L1: RET ; CLICK1 is a click meant to sound different from CLICK CLICK1: CMP SOUND, 0 JE > L2 PUSH AX, BX, DX MOV DX, 061h IN AL, DX OR AL, 2; set bit 1 OUT DX, AL MOV BX, 1000 ; time with cone out CALL TIMER_0 AND AL, 0FFh - 2; clear bit 1 OUT DX, AL ; cone in MOV BX, 500 ; dead time CALL TIMER_0 MOV DX, 061h IN AL, DX OR AL, 2; set bit 1 OUT DX, AL MOV BX, 1000 ; time with cone out CALL TIMER_0 AND AL, 0FFh - 2; clear bit 1 OUT DX, AL ; cone in POP DX, BX, AX L2: RET ; SNDONOFF turns the sound effects ON and OFF SNDONOFF: CMP SOUND, 0 ; sound OFF?? JE > L1 PSTATUS SOUNDOFF, ATTR9 MOV SOUND, 0 JMP > L2 L1: MOV SOUND, 0FFh PSTATUS SOUNDON, ATTR9 L2: TICKS 22 RET