home *** CD-ROM | disk | FTP | other *** search
-
- (* Sound and Delay routines for Fitted Software Tools Modula-2 compiler.
-
- The Delay, Sound, and NoSound procedures are functionally
- identical to their Turbo Pascal namesakes.
-
- These routines directly address the Intel 8253 Programmable
- Interval Timer (PIT) chip. Therefore these routines should run
- faithfully on all PC-compatible systems, regardless of the CPU
- or clock speed. I wrote these procedures using a a 12MHz 0ws
- '286, and tested them on an 8MHz 8088.
-
- Your shareware contribution appreciated by the author:
-
- G.S.Vigneault,
- Box 7169, Station A,
- Toronto, Ontario,
- Canada M5W 1X8
-
- Send comments to...
- UUCP: gregsv@eastern.uucp
- greg.vigneault@canrem.uucp
- ZLink: ACCESSCOMM
- *)
-
- MODULE PIT;
-
- FROM InOut IMPORT Write, WriteLn, WriteLongCard, WriteString;
- FROM Keyboard IMPORT GetKeyCh, KeyPressed;
- FROM SYSTEM IMPORT ASSEMBLER;
-
-
- (*------------------------------------*)
-
- PROCEDURE Set8253( count : CARDINAL );
- (* The 8253 counter #0 is the heartbeat of the DOS clock. The BIOS loads
- counter #0 with 0, mode 3. Some hi-res timing routines may leave it in
- mode 2, which will affect these procedures. Use Set8253(0) at the very
- beginning of your program to load counter#0 the same way that the BIOS
- does at power up. (Sloppy TSRs might still interfere.) *)
- BEGIN
- ASM
- MOV CX, count (* count down from this *)
- MOV DX,40H (* 8253 counter#0 port *)
-
- CLI (* do not disturb *)
- MOV AL,36H (* counter 0, mode 3, RL=11 *)
- OUT 43H,AL (* to 8253 control reg *)
- MOV AL,CL
- OUT DX,AL (* write counter 0, LSB *)
- MOV AL,CH
- OUT DX,AL (* write counter 0, MSB *)
- STI
-
- MOV ES,DX (* far data segment *)
- MOV AX,ES:[006CH] (* get BIOS timer word *)
- wait: CMP AX,ES:[006CH] (* wait for next tick *)
- JE wait (* to synchronize *)
- END
- END Set8253;
-
- (*------------------------------------*)
-
- PROCEDURE ReadTimer( VAR count : LONGCARD );
- (* Returns a 32-bit representation of 8253 counter#0. By calling this
- routine multiple times you may determine the elapsed time between each
- call, approximately in _microseconds_ (µs). *)
- BEGIN
- ASM
- MOV DX,40H (* 8253 count#0 port *)
- MOV ES,DX (* far data segment *)
- CLI (* do not disturb *)
- MOV AL,06H (* counter0, RL=00 to latch *)
- OUT 43H,AL (* control reg: mode 3 *)
- IN AL,DX (* get counter#0 LSB *)
- MOV BL,AL (* save it *)
- IN AL,DX (* get counter#0 MSB *)
- STI (* interrupts okay now *)
- MOV BH,AL (* BX = counter#0 *)
- MOV AX,ES:[006CH] (* get counter#0 overflow *)
- NEG BX (* downcounter to upcounter *)
- LES DI, count (* pointer to longcard *)
- MOV ES:[DI],BX (* lower16 = counter#0 *)
- MOV ES:[DI+2],AX (* upper16 = BIOS word *)
- END
- END ReadTimer;
-
- (*------------------------------------*)
-
- PROCEDURE Delay( ms : CARDINAL );
- (* Delays (pauses) approximately ms milliseconds before returning. I find
- this to be accurate to 0.2%. You may fine-tune this procedure to your
- system by adjusting the original "MOV DI,1990" load value below. *)
- BEGIN
- ASM
- MOV CX,ms (* get the requested ms wait *)
- JCXZ exit (* if nothing to do *)
- MOV DI,1990 (* this many is ~1ms *)
- MOV DX,40H (* counter 0 reg port *)
-
- wrap: CLI
- MOV AL,06H (* counter0, RL=00 to latch *)
- OUT 43H,AL (* control reg: mode 3 *)
- IN AL,DX (* get counter0 LSB *)
- MOV AH,AL (* save it *)
- IN AL,DX (* get counter0 MSB *)
- STI (* because of 8253 design *)
- XCHG AL,AH (* AX = counter 0 *)
- CMP AX,DI (* too near end of count? *)
- JBE wrap (* wait (less than 1ms) *)
- MOV SI,AX (* save ref *)
-
- wait: CLI (* _must_ read 2 bytes *)
- MOV AL,06H (* counter0, RL=00 to latch *)
- OUT 43H,AL (* control reg: mode 3 *)
- IN AL,DX (* get counter0 LSB *)
- MOV BL,AL (* save it *)
- IN AL,DX (* get counter0 MSB *)
- STI (* interrupts okay now *)
- MOV BH,AL (* now BX = counter 0 *)
- MOV AX,SI (* get ref val *)
- SUB AX,BX (* calc difference *)
- CMP AX,DI (* waited long enough? *)
- JB wait (* wait for 1ms *)
-
- LOOP wrap (* loop for another 1ms *)
- exit:
- END
- END Delay;
-
- (*------------------------------------*)
-
- PROCEDURE Sound( Freq : CARDINAL );
- (* tone of Freq frequency will continue to play until NoSound is called *)
- BEGIN
- Freq := SHORT( 1193182L DIV LONG( Freq ));
-
- ASM
- MOV DX,043H (* 8253 control reg port *)
- MOV AL,0B6H (* RL=11, counter#2, mode 3 *)
- OUT DX,AL
-
- DEC DX (* 42H = 8253 counter#2 *)
- MOV AX, Freq
- CLI (* do not disturb! *)
- OUT DX,AL (* Freq LSB *)
- MOV AL,AH
- OUT DX,AL (* Freq MSB *)
- STI (* due to 8253 design *)
-
- MOV DX,061H (* 8255 port B *)
- IN AL,DX (* D0=counter#2 gate enable *)
- OR AL,3 (* D1=speaker enable *)
- OUT DX,AL (* set bits D0 and D1 *)
- END
- END Sound;
-
- (*------------------------------------*)
-
- PROCEDURE NoSound;
- (* stops Sound by disabling speaker, and stopping 8253 counter 2 *)
- BEGIN
- ASM
- MOV DX,061H (* 8255 port B *)
- IN AL,DX
- AND AL,0FCH (* bits D0 and D1 off *)
- OUT DX,AL
- END
- END NoSound;
-
- (*------------------------------------*)
-
- PROCEDURE Tone( Freq, dur : CARDINAL);
- (* plays Freq tone for dur jiffies (1 PC jiffy = 1/18 sec ) *)
- BEGIN
- Freq := SHORT( 1193182L DIV LONG( Freq ));
-
- ASM
- MOV DX,043H (* 8253 control reg *)
- MOV AL,0B6H
- OUT DX,AL
-
- DEC DX (* 42H = 8253 counter 2 *)
- MOV AX, Freq (* freq divider *)
- CLI (* do not disturb! *)
- OUT DX,AL (* counter#2 LSB *)
- MOV AL,AH
- OUT DX,AL (* counter#2 MSB *)
- STI (* due to 8253 design *)
-
- MOV DX,061H (* 8255 Port B *)
- IN AL,DX
- OR AL,3 (* enable counter2 and spkr *)
- OUT DX,AL
-
- SUB AH,AH (* get system jiffy count *)
- INT 1AH (* via BIOS *)
- ADD DX, dur (* calc requested delay val *)
- MOV BX,DX (* save *)
- cont:
- INT 1AH (* get current jiffies *)
- CMP DX,BX (* are we there yet? *)
- JNE cont (* loop til we are *)
-
- MOV DX,061H (* port B bits *)
- IN AL,DX
- AND AL,0FCH (* disable counter2 and spkr *)
- OUT DX,AL
- END
- END Tone;
-
- (*------------------------------------*)
-
- PROCEDURE GetVideoMode( VAR vmode : CARDINAL );
- BEGIN
- ASM
- MOV AH,0FH (* get video mode fn *)
- INT 10H (* via BIOS *)
- SUB AH,AH (* make it a word *)
- LES DI, vmode (* pointer to the variable *)
- MOV ES:[DI],AX (* update vmode *)
- END
- END GetVideoMode;
-
- (*------------------------------------*)
-
- PROCEDURE SetVideoMode( Vmode : CARDINAL );
- BEGIN
- ASM
- MOV AX, Vmode (* get requested vid mode *)
- SUB AH,AH (* set video mode fn *)
- INT 10H (* via BIOS *)
- END
- END SetVideoMode;
-
- (*------------------------------------*)
-
- PROCEDURE ClearScreen;
- (* text mode clear screen *)
- BEGIN
- ASM
- SUB CX,CX (* upper left x1,y1 := 0 *)
- MOV DL,79 (* x2 lower right *)
- MOV DH,24 (* y2 lower right *)
- MOV BH,7 (* attribute *)
- SUB AL,AL (* to blank entire window *)
- MOV AH,6 (* using scroll *)
- INT 10H (* via BIOS *)
- END
- END ClearScreen;
-
- (*------------------------------------*)
-
- PROCEDURE GotoXY( Xcord, Ycord : CARDINAL );
- (* to place the text mode cursor *)
- BEGIN
- ASM
- MOV DX, Xcord
- MOV AX, Ycord
- MOV DH,AL
- SUB BH,BH (* display page 0 *)
- MOV AH,2 (* set cursor position *)
- INT 10H (* via BIOS *)
- END
- END GotoXY;
-
- (*----------------------------------------------------------------------*)
-
- VAR
- ch : CHAR;
- frequency,
- OldVmode,
- duration : CARDINAL;
- time1, time2 : LONGCARD;
-
- BEGIN (* main *)
-
- Set8253( 0 ); (* make sure it's the BIOS default *)
-
- GetVideoMode( OldVmode ); (* save current video mode *)
- SetVideoMode( 2 ); (* set 80x25 B&W text *)
-
- ClearScreen; (* blank the display *)
-
- Sound( 440 );
-
- FOR duration := 1 TO 5
- DO
- ReadTimer( time1 ); (* to time the Delay proc *)
- Delay(1000); (* 1s = 1000ms = 1000000µs *)
- ReadTimer( time2 ); (* for elapsed microseconds *)
- WriteString("[Timed Delay(1000ms) of ");
- WriteLongCard( time2-time1, 7 ); (* delta ReadTimer *)
- WriteString("µs (microseconds)]");
- WriteLn;
- WriteLn;
- END;
-
- NoSound;
-
- WriteLn;
- WriteString("Press any key to continue...");
- REPEAT (* wait *) UNTIL KeyPressed();
-
- ClearScreen;
-
- GotoXY( 37, 1 );
- WriteString("ORGAN");
- GotoXY( 3, 3 );
- WriteString(" Use the keys in rows Q..P and A..' to play notes");
- GotoXY( 3, 5 );
- WriteString(" Use 1..9 keys to set note duration (in 0.054945 sec).");
- GotoXY( 3, 7 );
- WriteString(" Press X to EXIT.");
- WriteLn;
- WriteLn;
-
- duration := 5;
-
- LOOP
- frequency := 0;
- GetKeyCh(ch);
- IF ch <> 0C THEN
- ch := CAP(ch);
- CASE ch OF
- "Q" : frequency := 440; |
- "W" : frequency := 466; |
- "E" : frequency := 494; |
- "R" : frequency := 523; |
- "T" : frequency := 554; |
- "Y" : frequency := 587; |
- "U" : frequency := 622; |
- "I" : frequency := 659; |
- "O" : frequency := 698; |
- "P" : frequency := 740; |
- "A" : frequency := 784; |
- "S" : frequency := 831; |
- "D" : frequency := 880; |
- "F" : frequency := 932; |
- "G" : frequency := 988; |
- "H" : frequency := 1046;|
- "J" : frequency := 1109;|
- "K" : frequency := 1175;|
- "L" : frequency := 1245;|
- ";" : frequency := 1329;|
- "'" : frequency := 1397;|
- "1".."9"
- : duration := ORD(ch)-48;|
- "X" : EXIT
- ELSE ;
- END (*case*)
- END; (*if*)
-
- IF frequency >= 100 THEN
- Write(ch);
- Tone( frequency, duration )
- END
- END; (*loop*)
-
- SetVideoMode( OldVmode ) (* restore original video mode *)
-
- END PIT.
-
-