home *** CD-ROM | disk | FTP | other *** search
- UNIT CRTi; {version 1.00 of 03/03/88}
-
- {Copyright (c) 1988 by Carley Phillips. Placed in the PUBLIC DOMAIN.}
-
- {
- Many of the message threads on Compuserve's BPROGA DL 2 involve the use (and
- mis-use) of the CRT unit provided with Turbo 4. These routines were written
- to take some of the mystery out of what CRT routines do and do not do.
-
- This unit is an input-only replacement for Borland's CRT. This means that:
- 1. only the input and the misc functions (e.g. Sound) are implemented;
- 2. the parts which have to do with screen output are omitted; and
- 3. you have access to the source so you can change what you don't like.
-
- This allows your programs to avoid USEing CRT at all if your video output
- is done with Qwik40, FastWrite, or other packages. If you use CRTi anywhere,
- then make sure none of your units use CRT because the conflicts which may
- result may be very difficult to debug.
-
- Note that the equivalent of a Turbo variable (which they should have made
- public) is contained in HadControlBreak. This is the variable set by the
- control-break handler if CheckBreak is true. Normally Turbo tests this
- during ReadKey and during screen output. Your program can test this at
- any time, however, such as in your replacement screen routines or during
- a long mathematical calculation.
-
- There are two new public routines here:
- 1. a keyboard flushing routine; and
- 2. an abort routine you can call if your program finds HadControlBreak true.
-
- I will update these routines if anyone notifies me of bugs or of any way in
- which these routines do not provide satisfactory duplicates of Borland
- functionality.
-
- Comments, suggestions, bug reports, etc. should be sent on Compuserve (via
- EasyPlex since I'm not necessarily on every few days) to
-
- Carley Phillips, 76630,3312.
- }
-
- {*****************************************************************************}
- INTERFACE
-
- var
- CheckBreak : boolean; {false means ignore ^Break}
- SaveInt1B : pointer; {where old interrupt 1B address is saved}
- HadControlBreak : boolean; {set by ^Break handler if CheckBreak true}
-
- procedure Delay (mSec : word);
- {
- Direct replacement for CRT.Delay. Delays mSec number of milliseconds.
-
- Note that, like Borland's CRT unit, a delay count is determined during
- initialization. This means that if you change your machine speed after
- starting a program, the delay values will no longer be what you intended them
- to be.
- }
-
- procedure Sound (Hz : word);
- {
- Direct replacement for CRT.Sound. Starts a Hz frequency tone until it is
- turned off by NoSound.
- }
-
- procedure NoSound;
- {
- Direct replacement for CRT.NoSound. Stops a tone started with Sound.
- }
-
- function Keypressed : boolean;
- {
- Direct replacement for CRT.Keypressed. If you like, you can also check for
- a control break here by un-commenting the indicated lines.
- }
-
- function ReadKey : char;
- {
- Direct replacement for CRT.ReadKey.
- }
-
- procedure FlushKeyboard;
- {
- NEW public routine to flush the BIOS keyboard buffer. That is, unless the
- user is extraordinarily lucky with his timing of new input, Keypressed will
- be false immediatedly following FlushKeyboard. Normally one flushes the
- keyboard buffer in situations such as displaying an error message and then
- waiting on the user to press a key. If the buffer is not flushed, then the
- user may have typed ahead enough that the error message will be on the screen
- for too short a time to be read.
- }
-
- procedure ControlBreakAbort;
- {
- NEW public abort routine called when ReadKey detects that an allowed ^Break has
- been entered. Borland does something similar, but you are free to replace this
- with other code or even an external routine. Alternatively, you can use this
- routine from your code if you find that HadControlBreak has become true.
- }
-
- {*****************************************************************************}
- IMPLEMENTATION
-
- var
- Cpm : word; {counts per msec; initialized by InitDelay; used by Delay}
- Scan: byte; {saves scan code for Keypressed and ReadKey}
- ExitSave : pointer; {place to save old ExitProc}
-
- {*****************************************************************************}
- procedure Delay (mSec : word);
- begin
- Inline( {Assembly by Inline 03/03/88 22:28}
- $8B/$56/<MSEC/ { mov DX,[BP+<mSec];get number of msec}
- $09/$D2/ { or DX,DX ;check it}
- $74/$17/ { jz quit ;quit if 0 delay}
- $BF/>CPM/ { mov DI,>Cpm ;setup addr for subr}
- $8B/$05/ { mov AX,[DI] ;delay count for 1msec}
- $89/$C1/ {onemsec: mov CX,AX ;init delay subr counter}
- $E8/$06/$00/ { call subr ;call delay subr}
- $4A/ { dec DX ;count that we've done 1}
- $75/$F8/ { jnz onemsec ;loop for each msec}
- $E9/$07/$00/ { jmp quit ;skip over subroutine}
- $3A/$05/ {subr: cmp AL,[DI] ;check if constant chngd}
- $75/$02/ { jnz out ;it never will change}
- $E2/$FA/ { loop subr ;count and loop for msec}
- $C3 {out: ret ;return from local subr}
- ); {quit: ;label for end of inline}
- end;
-
- {*****************************************************************************}
- procedure Sound (Hz : word);
- begin
- Inline( {Assembly by Inline 03/03/88 22:28}
- $8B/$4E/<HZ/ { mov CX,[BP+<Hz] ;get frequency}
- $B8/$DD/$34/ { mov AX,$34DD ;load lsh of 1,193,181}
- $BA/$12/$00/ { mov DX,$0012 ;load msh of 1,193,181}
- $39/$CA/ { cmp DX,CX ;check frequency}
- $73/$1A/ { jnb quit ;quit if too small}
- $F7/$F1/ { div CX ;calculate timer divider}
- $89/$C1/ { mov CX,AX ;save quotient}
- $E4/$61/ { in AL,$61 ;get sense data}
- $A8/$03/ { test AL,$03 ;check for two lsb}
- $75/$08/ { jnz set ;skip if on}
- $0C/$03/ { or AL,$03 ;turn on two lsb}
- $E6/$61/ { out $61,AL ;turn them on}
- $B0/$B6/ { mov AL,$B6 ;load control value}
- $E6/$43/ { out $43,AL ;output to timer control}
- $88/$C8/ {set: mov AL,CL ;get lsh of quotient}
- $E6/$42/ { out $42,AL ;output to timer2}
- $88/$E8/ { mov AL,CH ;get msh of quotient}
- $E6/$42 { out $42,AL ;output to timer2}
- ); {quit: ;label for end of inline}
- end;
-
- {*****************************************************************************}
- procedure NoSound;
- begin
- Inline( {Assembly by Inline 03/03/88 22:28}
- $E4/$61/ { in AL,$61 ;get sense data}
- $24/$FC/ { and AL,$FC ;turn off two lsb}
- $E6/$61 { out $61,AL ;put it back}
- ); {quit: ;label for end of inline}
- end;
-
- {*****************************************************************************}
- function KeyPressed : boolean;
- begin
- (*
- if HadControlBreak then {un-comment this if you want to check ^Break here}
- ControlBreakAbort;
- *)
- Inline( {Assembly by Inline 03/03/88 22:28}
- $80/$3E/>SCAN/$00/ { cmp Byte Ptr[>Scan],$00;check scan code}
- $75/$08/ { jnz yes ;not 0 = we saved one}
- $B4/$01/ { mov AH,$01 ;check for char}
- $CD/$16/ { int $16 ;using BIOS}
- $B0/$00/ { mov AL,$00 ;assume we had none}
- $74/$02/ { jz quit ;Z flag means we didn't}
- $B0/$01/ {yes: mov AL,$01 ;load "true"}
- $88/$46/$FF); {quit: mov [BP-01],AL ;save final value}
- end;
-
- {*****************************************************************************}
- function ReadKey : char;
- begin
- Inline( {Assembly by Inline 03/03/88 22:28}
- $B0/$00/ { mov AL,0 ;load a zero}
- $86/$06/>SCAN/ { xchg [>Scan],AL ;xchg with scan code}
- $08/$C0/ { or AL,AL ;check for saved code}
- $75/$12/ { jnz saveit ;not 0 = we had one}
- $30/$E4/ { xor AH,AH ;read char}
- $CD/$16/ { int $16 ;using BIOS}
- $08/$C0/ { or AL,AL ;check "char" we got}
- $75/$0A/ { jnz saveit ;not 0 = got normal char}
- $88/$26/>SCAN/ { mov [>Scan],AH ;save extended scan code}
- $08/$E4/ { or AH,AH ;check scan code}
- $75/$02/ { jnz saveit ;not = 0 means not ^Brk}
- $B0/$03/ { mov al,$03 ;use "normal" ^C instead}
- $88/$46/$FF); {saveit: mov [BP-1],AL ;save final char}
- if HadControlBreak then
- ControlBreakAbort;
- end;
-
- {*****************************************************************************}
- procedure FlushKeyboard;
- begin
- Inline( {Assembly by Inline 03/03/88 22:28}
- $B4/$01/ {flush: mov AH,$01 ;check for char}
- $CD/$16/ { int $16 ;using BIOS}
- $74/$06/ { jz empty ;Z flag means none there}
- $30/$E4/ { xor AH,AH ;read char}
- $CD/$16/ { int $16 ;using BIOS}
- $EB/$F4/ { jmp flush ;loop until no more}
- $C6/$06/>SCAN/$00); {empty: mov Byte Ptr [>Scan],0;clear saved code}
- end;
-
- {*****************************************************************************}
- procedure ControlBreakAbort;
- begin
- HadControlBreak := false;
- FlushKeyboard;
- Inline( {Assembly by Inline 03/03/88 22:28}
- $BB/$07/$00/ { mov BX,$0007 ;pg 0; graphics fg color}
- $B8/$07/$0E/ { mov AX,$0E07 ;load a BEL}
- $CD/$10/ { int $10 ;output using BIOS}
- $B8/$5E/$0E/ { mov AX,$0E5E ;load a '^'}
- $CD/$10/ { int $10 ;output using BIOS}
- $B8/$43/$0E/ { mov AX,$0E43 ;load a 'C'}
- $CD/$10/ { int $10 ;output using BIOS}
- $B8/$0D/$0E/ { mov AX,$0E0D ;load a CR}
- $CD/$10/ { int $10 ;output using BIOS}
- $B8/$0A/$0E/ { mov AX,$0E0A ;load a LF}
- $CD/$10/ { int $10 ;output using BIOS}
- $CD/$23 { int $23 ;control-C interrupt}
- {;Note that DOS manual says only DOS should do this.}
- {;However, Turbo issues this interrupt directly in}
- {;these same circumstances. It is probably ok since}
- ); {;Turbo has hooked this interrupt.}
- end;
-
- {*****************************************************************************}
- {this is not public}
- procedure InitDelay;
- {
- Right after loading, this routine executes, for 1 timer tick (55 msec) a delay
- subroutine which is identical to that contained in the public Delay routine.
- From the count generated, it is possible to calculate what the count would have
- been for 1 millisecond and save this count in Cpm for later use by Delay.
- Note that, like Borland's CRT unit, this count is determined during
- initialization. This means that if you change your machine speed after
- starting a program, the delay values will no longer be what you intended them
- to be.
- }
- begin
- Inline( {Assembly by Inline 03/03/88 22:28}
- $1E/ { push DS ;save DS}
- $B8/$40/$00/ { mov AX,$0040 ;BIOS data segment}
- $8E/$D8/ { mov DS,AX ;into DS}
- $BF/$6C/$00/ { mov DI,$6C ;offset for lsh of timer}
- $8A/$05/ { mov AL,[DI] ;get lsb of timer}
- $3A/$05/ {sync: cmp AL,[DI] ;check if timer changed}
- $74/$FC/ { jz sync ;loop until it changes}
- $8A/$05/ { mov AL,[DI] ;get new timer value}
- $B9/$FF/$FF/ { mov CX,$FFFF ;initialize large count}
- $E8/$12/$00/ { call subr ;call delay subr}
- $1F/ { pop DS ;restore DS}
- $89/$C8/ { mov AX,CX ;count to lsh of dividnd}
- $F7/$D0/ { not AX ;AX=count for 55 msec}
- $31/$D2/ { xor DX,DX ;clear msh of dividend}
- $B9/$37/$00/ { mov CX,55 ;msec per timer tick}
- $F7/$F1/ { div CX ;compute count for 1 msec}
- $A3/>CPM/ { mov [>Cpm],AX ;save count for Delay}
- $E9/$07/$00/ { jmp quit ;skip over subroutine}
- $3A/$05/ {subr: cmp AL,[DI] ;check if timer changed}
- $75/$02/ { jnz out ;quit after 1 timer tick}
- $E2/$FA/ { loop subr ;count and loop}
- $C3 {out: ret ;return from local subr}
- ); {quit: ;label for end of inline}
- end;
-
- {*****************************************************************************}
- {this is not public}
- procedure ControlBreakHandler (Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP: word);
- interrupt;
- {
- Initialization code sets this up as the interrupt handler for a $1B (control-
- break interrupt. All it does is set a flag for checking by other routines.
- }
- begin
- HadControlBreak := CheckBreak;
- end;
-
- {*****************************************************************************}
- {this is not public}
- procedure InitControlBreak;
- {
- Initialization routine to install ControlBreakHandler as the interrupt $1B
- handler. The old vector is saved in the public SaveInt1B just as in CRT.
- This could be done totally in Turbo (without inline) but would require this
- unit to USE the DOS unit.
- }
- var
- OurAdr : pointer;
- begin
- OurAdr := @ControlBreakHandler;
- Inline( {Assembly by Inline 03/03/88 22:28}
- $B8/$1B/$35/ { mov AX,$351B ;setup for interrupt 1B}
- $CD/$21/ { int $21 ;get old int vector}
- $89/$1E/>SAVEINT1B/ { mov [>SaveInt1B],BX;save offset}
- $8C/$C3/ { mov BX,ES ;get segment}
- $89/$1E/>SAVEINT1B+2/ { mov [>SaveInt1B+2],BX;save segment}
- $8B/$56/<OURADR/ { mov DX,[BP+<OurAdr] ;get our offset}
- $8B/$46/<OURADR+2/ { mov AX,[BP+<OurAdr+2];get our segment}
- $1E/ { push DS ;save DS}
- $8E/$D8/ { mov DS,AX ;move segment for call}
- $B8/$1B/$25/ { mov AX,$251B ;setup for interrupt 1B}
- $CD/$21/ { int $21 ;set new int vector}
- $1F); { pop DS ;restore DS}
- end;
-
- {*****************************************************************************}
- {this is not public}
- {$F+} procedure ExitHandler; {$F-}
- {
- Exit procedure to de-install ControlBreakHandler.
- This could be done totally in Turbo (without inline) but would require this
- unit to USE the DOS unit.
- }
- begin
- Inline( {Assembly by Inline 03/03/88 22:28}
- $8B/$16/>SAVEINT1B/ { mov DX,[>SaveInt1B];get orig offset}
- $A1/>SAVEINT1B+2/ { mov AX,[>SaveInt1B+2];get orig segment}
- $1E/ { push DS ;save DS}
- $8E/$D8/ { mov DS,AX ;move segment for call}
- $B8/$1B/$25/ { mov AX,$251B ;setup for interrupt 1B}
- $CD/$21/ { int $21 ;restore old int vector}
- $1F); { pop DS ;restore DS}
- ExitProc := ExitSave; {when we leave, chain to next exit procedure}
- end;
-
- {*****************************************************************************}
- begin {CRTi initialization}
- CheckBreak := true; {default break-checking flag to True}
- Scan := 0; {initialize saved scan code}
- HadControlBreak := false; {initialize flag which says we had break}
- InitDelay; {determine the proper count for use by Delay}
- InitControlBreak; {set up control-break handler}
- ExitSave := ExitProc; {save old exit procedure}
- ExitProc := @ExitHandler; {put our exit procedure in the chain}
- end. {CRTi initialization}
-