Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
master
Switch branches/tags
Go to file
@bitcrazed
Latest commit 09ad7bc Feb 10, 1983 History
1 contributor

Users who have contributed to this file

; [ This translation created 10-Feb-83 by Version 4.3 ]
.RADIX 8 ; To be safe
CSEG SEGMENT PUBLIC 'CODESG'
ASSUME CS:CSEG
INCLUDE OEM.H
TITLE GWSTS - GW-BASIC Common Statement Support
COMMENT *
--------- --- ---- -- ---------
COPYRIGHT (C) 1982 BY MICROSOFT
--------- --- ---- -- ---------
*
;General Feature Switches (Not OEM Switches)
;
KANADT=0 ;Japanese date format("[yy]yy/mm/dd")
STKEYF=1D ;Start number of string function keys
IBMCSR=IBMLIK ;IBM comp. cursor interface
FKEYCR=74O ;CR character for F-KEY display line
GWLEV2=0 ;Version 2.0 of GW BASIC-86
;OEM Switches (ONLY INCLUDE IF ABSOLUTELY NECESSARY)
;
MCI=0
TETRA=0
MELCO=0
ZENITH=0
;Definition of scroll types
; Choice of scroll type is by switch SCROLT.
; Switches defined here are used to implement a specific SCROLT type.
; If other scroll types are needed then additional SCROLT types should be
; defined here.
INVLIN=SCROLT ;Invisible (function key) Line
FKFSRL=(SCROLT-1) AND 1 ;Clear fkeys/full scroll/rewrite fkeys
;Local Switches
;
KEYFSW=0 ;No KEY Function
INTHND=SCP ;MSDOS Ctl-C interrupt handler
CLRFMT=(MELCO-1) AND (ZENITH-1) ;New COLOR parameter format
FKEYCR=27D ;IBM CR FKey display line graphic
.SALL
.RADIX 10
EXTRN CHRGTR:NEAR,SYNCHR:NEAR,SNERR:NEAR,FCERR:NEAR,GETBYT:NEAR
EXTRN USERR:NEAR
IF CPM86
CPMXIO MACRO DFUN
MOV CL,LOW OFFSET DFUN
INT 340O ;CPM86 system call
ENDM
ENDIF
DOSIO MACRO DFUN
MOV AH,LOW OFFSET DFUN
INT 33 ;MS-DOS system call
ENDM
GDAT=42D ;MS-DOS Get Date Function
SDAT=43D ;MS-DOS Set Date Function
GTIM=44D ;MS-DOS Get Time Function
STIM=45D ;MS-DOS Set Time Function
PUBLIC PATCHG
PATCHG: DB 500D DUP(?) ;GW patch space
PAGE
SUBTTL CLS,LOCATE,WIDTH (of screen),LCOPY
PUBLIC CLS,LOCATE,GWWID,LCOPYS,COLOR,GETLIN,SCRENF,SCREEN
DSEG SEGMENT PUBLIC 'DATASG'
ASSUME DS:DSEG
EXTRN LINCNT:WORD,LINLEN:WORD,CSRY:WORD,CSRX:WORD,BUF:WORD
DSEG ENDS
EXTRN GETFBC:NEAR
EXTRN SCRSTT:NEAR,SCRATR:NEAR
EXTRN SETCLR:NEAR,SWIDTH:NEAR
EXTRN LCPY:NEAR
COMMA=","
OPAREN="("
CPAREN=")"
QUOTE=34D
BKSPC=8D
CR=13D
LF=10D
PAGE
;CLS: CLear Screen issues an escape sequence to clear
; the CRT. Sequences are ANSII standard whereas the machine
; default is not. CLS resets the graphics cursor position.
;ENTRY - none
;EXIT - none
;USES - none
;
EXTRN CLRSCN:NEAR
CLS: CALL SCNINT ;Test for optional parameter
CMC
PUSHF
PUSH AX
CALL EOSCHK ;Test for end of statement
POP AX
POPF
PUSH BX
CALL CLRSCN
POP BX
RET
PAGE
;LOCATE: Parse the following syntax:
; LOCATE [Y] [, [X] [, [CURSOR] [, [START] [, [STOP] ]]]
;
EXTRN SCNPOS:NEAR
LOCATE: CALL SCNINT ;Get optional Y parameter
JNB YLCPRM ;Parameter present
CALL GTLINE ;Get the current screen position
YLCPRM: MOV DL,AL
OR AL,AL ;Test for LOCATE 0
JZ GOFCER
SUB DL,BYTE PTR KEYSW ;Increment if PF-keys are displayed
CMP DL,BYTE PTR LINCNT ;Check for parameter range
JA GOFCER
PUSH AX ;Save new Y location
CALL SCNINT ;Get optional X parameter
JNB XLCPRM ;Parameter present
CALL SCNPOS ;Get the current screen position
MOV AL,DH ;Default to current cursor position
XLCPRM: MOV DL,AL
DEC DL ;Dissallow LOCATE ,0
CMP DL,BYTE PTR LINLEN ;Check for parameter range
JAE GOFCER
PUSH AX ;Save new X
CALL SCNINT ;Cursor on/off - 0=off else on
EXTRN CSRATR:NEAR
MOV AH,LOW 377O ;Ensure non-zero
JNB LOCPR1 ;Parameter 1 found
XOR AH,AH ;Flag as a default
LOCPR1: PUSH AX ;Push first parameter and flag
CALL SCNINT ;Get next parameter
MOV AH,LOW 377O ;Ensure non-zero flag
JNB LOCPR2 ;Parameter 2 found
XOR AH,AH ;Flag as a default
LOCPR2: PUSH AX ;Push second parameter and flag
CALL SCNINT ;Get next parameter
MOV AH,LOW 377O ;Ensure non-zero flag
JNB LOCPR3 ;Parameter 3 found
XOR AH,AH ;Flag as a default
LOCPR3: PUSH AX ;Push third parameter
CALL EOSCHK ;Check for end of statement
MOV DX,BX ;Save text pointer
POP CX
POP BX
POP AX ;Recover three parameters
PUSH DX ;Save text pointer
CALL CSRATR ;Set Cursor Attribute (OEM routine)
JB GFCERR ;Declare error from CSRATR
POP BX ;Text pointer
SETLOC: MOV AX,BX
POP CX
POP BX
PUSH AX
MOV BH,CL
EXTRN SCNLOC:NEAR
MOV AX,BX
CALL SCNLOC ;position cursor at line [AL], col [AH]
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN CSRTYP:WORD
DSEG ENDS
EXTRN CSRDSP:NEAR
MOV DX,AX ;Load cursor position
MOV AL,LOW 3D ;Signal for user cursor
CALL CSRDSP ;Set cursor
POP BX ;Restore text pointer
RET
GOFCER: JMP FCERR
PAGE
;GWWID: Parsing for WIDTH [X] [, [Y]]
;ENTRY - WIDTH LPRINT is not a possibility at this point.
;EXIT - AL = X param
; BX = text pointer
;
GWWID: CALL SCNINT ;Get X dimension
JNB XPRAM ;X param found
MOV AL,BYTE PTR LINLEN ;Use current as default
XPRAM: PUSH AX ;Save for RET to WIDTH
PUSH AX ;Save for GWWID use
CALL SCNINT ;Get Y dimension
JNB YPRAM ;Y param found
MOV AL,BYTE PTR LINCNT ;Use current as default
YPRAM: PUSH AX
CALL EOSCHK ;Must be at end of statement
POP AX
CMP AL,BYTE PTR LINCNT ;Set CC's for Y dimension change
LAHF
MOV CX,AX
POP AX
CMP AL,BYTE PTR LINLEN ;Set CC's for X dimension change
LAHF
AND AH,CH ;Set CC's for X OR Y change
SAHF
JZ GWWIDX ;No change - done
PUSH BX ;Save text pointer
PUSH AX ;save Width
PUSH CX ;save Height
CALL SWIDTH ;Machine dependent set logic
POP CX
POP AX
JB GFCERR ;Error detected within SWIDTH
POP BX ;Restore text pointer
GWWIDX: POP AX ;Return X dimension for WIDTH
RET
GFCERR: JMP FCERR
PAGE
;LCOPY: Copy the screen to the line printer.
;ENTRY - BX = text pointer
;EXIT - BX = text pointer
LCOPYS: MOV DL,LOW 0 ;default parm is 0
JZ NOPARM ;branch if end-of-statement
CALL GETBYT ;[DL]=parm
NOPARM: CALL EOSCHK ;Check for unwanted parameters
PUSH BX ;Save text pointer
CALL LCPY
JB GFCERR ;Error detected in low level routine
POP BX
RET
PAGE
SUBTTL COLOR,GETLIN,SCREEN (function and statement)
;COLOR: Set the foreground, background, and boarder attributes.
; SYNTAX - COLOR [FOR] [,BACK [,BOARD]]
; Where - FOR = Foreground attribute
; BACK = Background attribute
; BOARD = Boarder attribute
;
COLOR: CALL GTPRMS ;Get arbitrary number of int. parms.
COLOR1: PUSH BX ;Save text pointer
MOV BX,DI ;Get parameter buffer pointer
CALL SETCLR ;Check colors for validity
JB GFCERR ;Error detected by low level routine
POP BX
RET
PAGE
;GETLIN: Obtain the current cursor line number.
;EXIT - FAC = cursor line number
; BX preserved
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN LINLEN:WORD,LINCNT:WORD,KEYSW:WORD
DSEG ENDS
GETLIN: CALL GTLINE
PUSH BX
XOR BX,BX
MOV BL,AL
CALL MAKINT
POP BX
CALL CHRGTR
RET
;GTLINE: Get the line number of the character position to which the next
;character would be written.
;Entry - none
;Exit - [AL] = line number
;Uses - AH
;
GTLINE: MOV AL,BYTE PTR CSRY ;BX = cursor line number
MOV AH,BYTE PTR CSRX
CMP AH,BYTE PTR LINLEN
JBE GETLN0 ;BRIF will not wrap before next char is output
MOV AH,BYTE PTR LINCNT ;AL=last valid line number
DEC AH ;Scroll always occurs on line [LINCNT]-1
CMP AL,AH
JAE GETLN0 ;BRIF wrap will cause scroll
INC AL ;Else wrap will cause line number increment
GETLN0: RET
PAGE
;SCRENF: Obtain character from screen.
SCRENF:
CALL CHRGTR ;Eat the SCREEN token since SCREEN is
;defined as a statement and the function
;is dispatched to by EVAL.
CALL SYNCHR
DB OFFSET OPAREN ;Check for "("
CALL SCNINT ;Get Y parameter
JB FCERGO ;Parameter not present
DEC AL
CMP AL,BYTE PTR LINCNT ;Check range [1,LINCNT]
JAE FCERGO ;Out of range
INC AL
PUSH AX ;Y param.
CALL SCNINT ;Check for X param
JB FCERGO ;Error - no X param
DEC AL
CMP AL,BYTE PTR LINLEN ;Check range [1,LINLEN]
JAE FCERGO ;Out of range
INC AL
PUSH AX ;X param.
CMP AH,LOW OFFSET CPAREN ;Check terminator
JE SCRCHR ;End of params - go get char.
CALL SCNINT
JB FCERGO ;Error - no CPAREN and no Z param.
CMP AH,LOW OFFSET CPAREN ;Must now have CPAREN
JNE FCERGO
CMP AL,LOW 00O ;Is Z zero?
JE SCRCHR ;Yes - go get screen character
POP AX ;Retrieve X param
POP CX ;Retrieve Y param
PUSH BX ;Save text pointer
MOV BX,CX ;Call SCRATR with AX=X,BX=Y
CALL SCRATR ;Get screen attributes
JMP SHORT SCRENX
FCERGO: JMP FCERR
SCRCHR: POP AX ;Retrieve col number
POP CX ;Retrieve row number
PUSH BX ;Save the text pointer
EXTRN SCRINP:NEAR
PUSH DX
MOV DL,CL
MOV DH,AL
STC ;Indicate call is from SCREEN function
CALL SCRINP ;[AX]=Read char at (DH,DL)
MOV BX,AX ;return result in BX
POP DX
SCRENX: CALL MAKINT ;Set FAC
POP BX ;Retrieve text pointer
DEC BX
CALL CHRGTR
RET
PAGE
;SCREEN: This statement has no standard syntax. It is handled
; by parsing single byte integers until end of statement.
; Parameters may be null (appearance of a comma before an
; expression is encountered).
; SCRSTT (machine dependent) is called to process parameters
; which are in a list which is headed by a one word parameter
; count. The remaining list entries are two bytes long.
; The first byte is 0 if the SCREEN parameter was null.
; The second byte is the parameter value if it is nonnull
; or meaningless (if the parameter was null).
SCREEN: CALL GTPRMS ;Get single byte integer parms
PUSH BX
MOV BX,DI
CALL SCRSTT ;Process params
JB FCERGO ;Error detected in low level routine
POP BX ;Restore text pointer
RET
GTPRMS: MOV DI,OFFSET BUF ;Parameters stored in BUF
XOR CX,CX ;Initialize parameter count
INC DI ;Reserve parameter count location
SCRLOP: INC CX ;Count the param
PUSH CX
PUSH DI
CALL SCNINT ;Look for a parameter
POP DI
POP CX
JZ STTEND ;End of statement encountered
JNB PRMFND ;Parameter found (AH = separator)
XOR AX,AX ;Indicate a null parameter
PRMFND: XCHG AH,AL
MOV WORD PTR 0[DI],AX ;Load parameter to list
INC DI ;Next list entry
INC DI
JMP SHORT SCRLOP ;Go get next param
STTEND: JB NOPRM
MOV BYTE PTR 0[DI],LOW 255D ;Set param. exists flag
MOV BYTE PTR 1[DI],AL
INC CX
NOPRM: DEC CX
MOV DI,OFFSET BUF ;Reset list index
MOV BYTE PTR 0[DI],CL ;Head list with param count
RET
PAGE
SUBTTL PUT & GET (Distinguish Disk from Graphics)
PUBLIC PUT,GET
EXTRN DPUTG:NEAR,GPUTG:NEAR
;PUT: This code parses enough of the PUT/GET statement to
;GET: distinguish between the graphics and disk versions of
; these commands.
; The accepted technique is to search for a "(". This
; does not always allow for file number expressions which begin
; with "(".
;ENTRY: [BX] points to the character following the token.
;EXIT - Exit is made by jumping to the appropriate PUT/GET
; code.
; [BX] - restored to entry value before call of PUT/GET code.
PUT: MOV CX,1 ;Set PUT flag
JMP PARSE
GET: XOR CX,CX ;Set GET flag
PARSE: PUSH CX ;Save indication of PUT or GET
PUSH BX ;Save text pointer
CMP AL,LOW "(" ;Test for "("
JE GRPVER ;branch if graphics version
CMP AL,LOW "@" ;test for relative GET/PUT
JNE DSKVER ;Disk code may have no "(" or "@"
GRPVER: POP BX ;Restore text pointer
POP AX
JMP GPUTG ;Go to graphics PUT/GET
DSKVER: POP BX ;Restore text pointer
POP AX ;Get PUT/GET flag
JMP DPUTG
PAGE
SUBTTL Parsing Routines for GWSTS
PUBLIC SCNINT,EOSCHK
;SCNINT: Test for an optional integer parameter.
; If a comma or EOL is discovered assume parameter is missing.
; Otherwise evaluate the parameter.
;EXIT - [AL] = parameter value
; C set - no parameter found
; C reset - parameter found
;USES - ALL
SCNINT: DEC BX
CALL CHRGTR
JZ NOMORE ;EOL - Param null
CMP AL,LOW OFFSET COMMA
JZ OMITD ;Comma found. Param null.
CALL GETBYT ;Evaluate parameter
PUSH AX ;Save parameter
DEC BX ;Prepare to test expression terminator
CALL CHRGTR
JZ TRMOK ;EOL caused termination - OK
CMP AL,LOW OFFSET COMMA
JZ TRMCOM ;Comma caused termination - OK
CMP AL,LOW OFFSET CPAREN ;CPAREN caused termination - OK
JZ TRMCOM
POP AX ;Retrieve param.
JMP FCERR ;All other terminators not OK
TRMCOM: INC BX ;Move over comma
TRMOK: MOV CL,AL ;Save terminator
POP AX ;Retrieve parameter value
MOV AH,CL ;Return with AH = terminator
CLC ;Flag param. found
RET
OMITD: INC BX
NOMORE: MOV AH,AL ;Save terminator
MOV AL,LOW 0D ;Set param value to 0, save flags
STC ;Flag param. not found
RET
PAGE
;EOSCHK: Detect garbage beyond end of statement
;ENTRY - BX = text pointer
;EXIT - AL = 0 & all other registers preserved or
; - Exit on error through FCERR
;
EOSCHK:
DEC BX ;Back up text pointer
EOSCH1:
CALL CHRGTR ;Get next character (skipping blanks)
JZ EOSCKX ;End of statement
JMP SNERR ;Not EOS - error
EOSCKX: RET
PAGE
SUBTTL Graphics Support Specific to the 8086
PUBLIC LINLP3
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN MINDEL:WORD,MAXDEL:WORD,MINUPD:WORD,MAXUPD:WORD
DSEG ENDS
EXTRN SETC:NEAR
;LINLP3: Inner loop of line code.
LINLP3: CALL SETC ;SET CURRENT POINT
ADD DX,WORD PTR MINDEL ;ADD SMALL DELTA TO SUM
CMP DX,WORD PTR MAXDEL ;TIME TO UPDATE MINOR?
JB LINLOP ;NO, UPDATE MAJOR AND CONTINUE
SUB DX,WORD PTR MAXDEL ;UPDATE SUM FOR NEXT POINT
CALL WORD PTR MINUPD+1 ;ADVANCE MINOR AXIS
LINLOP: CALL WORD PTR MAXUPD+1 ;UPDATE MAJOR AXIS
LOOP LINLP3 ;CONTINUE UNTIL COUNT EXHAUSTED
RET
PAGE
SUBTTL VARPT2 - VARPTR$ Function
PUBLIC VARPT2
EXTRN PTRGTN:NEAR
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN VALTYP:WORD,DSCPTR:WORD
DSEG ENDS
;VARPTR$(x)
; Called after VARPTR sees next char is "$"
; Returns 3 byte string as follows:
; byte 0: type of x
; byte 1: low-adr of varptr(x)
; byte 2: high-adr of varptr(x)
; Primary use is so BASCOM can handle DRAW "X"+VARPTR$(A$)
;
VARPT2:
CALL CHRGTR ;get byte after "$"
CALL SYNCHR
DB OFFSET "(" ;EAT LEFT PAREN
CALL PTRGTN ;GET ADDRESS OF VARIABLE
CALL SYNCHR
DB OFFSET ")" ;EAT RIGHT PAREN
OR DX,DX ;MAKE SURE NOT UNDEFINED VAR
JNZ VARRT2 ;SET CC'S. ZERO IF UNDEF
JMP FCERR ;ALL OVER IF UNDEF (DONT WANT
;USER POKING INTO ZERO IF HE'S
;TOO LAZY TO CHECK
VARRT2:
PUSH BX ;Save text pntr
PUSH DX ;Save Var addr
MOV AL,BYTE PTR VALTYP
PUSH AX ;Save type
MOV AL,LOW 3
CALL STRINI ;Get a 3 byte string
MOV BX,WORD PTR DSCPTR ;Descriptor in [BX]
POP WORD PTR 0[BX] ;Store Type in Byte 1
INC BX
POP WORD PTR 0[BX] ;Store addr in Bytes 2-3.
JMP PUTNEW ;Desc in FAC & ret. ([BX] on stack).
PAGE
SUBTTL PLAY/SOUND statements
;
; PLAY - MUSIC MACRO LANGUAGE
;
PUBLIC PLAYS
EXTRN DONOTE:NEAR
EXTRN MACLNG:NEAR,MCLXEQ:NEAR,FETCHR:NEAR,DECFET:NEAR,VALSC2:NEAR
EXTRN FETCHZ:NEAR
;Low-Level routine required:
; DONOTE(AL: voice (0=forground, 1=back), CX:frequency, DX:duration (1=18.7ms))
; queues note for execution, saves all regs.
;
PLAYS: MOV DX,OFFSET PLYTAB ;POINT TO PLAY COMMAND TABLE
JMP MACLNG
PLYTAB: DB "A" ;THE NOTES A-G
DW OFFSET PLYNOT
DB "B"
DW OFFSET PLYNOT
DB "C"
DW OFFSET PLYNOT
DB "D"
DW OFFSET PLYNOT
DB "E"
DW OFFSET PLYNOT
DB "F"
DW OFFSET PLYNOT
DB "G"
DW OFFSET PLYNOT
DB "M" ;Music Meta Command
DW OFFSET PLYMET
DB OFFSET "N"+128D ;PLAY NUMERIC NOTE
DW OFFSET PLYNUM
DB OFFSET "O"+128D ;OCTAVE
DW OFFSET POCTAV
DB OFFSET "P"+128D ;PAUSE
DW OFFSET PPAUSE
DB OFFSET "T"+128D ;TEMPO
DW OFFSET PTEMPO
DB OFFSET "L"+128D ;LENGTH
DW OFFSET PLYLEN
DB "X" ;EXECUTE STRING
DW OFFSET MCLXEQ
DB 00 ;END OF TABLE
; TABLE OF INDEXES INTO NOTTAB FOR EACH NOTE
; VALUE OF 255 MEANS NOTE NOT ALLOWED.
NOTXLT: DB OFFSET 9D*2 ;A- (G#)
DB OFFSET 10D*2 ;A
DB OFFSET 11D*2 ;A#
DB OFFSET 12D*2 ;B
DB 255D ;NO C- OR B#
DB OFFSET 1D*2 ;C
DB OFFSET 2D*2 ;C#
DB OFFSET 3D*2 ;D
DB OFFSET 4D*2 ;D#
DB OFFSET 5D*2 ;E
DB 255D ;NO E# OR F-
DB OFFSET 6D*2 ;F
DB OFFSET 7D*2 ;F#
DB OFFSET 8D*2 ;G
DB OFFSET 9D*2 ;G#
; TABLE OF NOTE FREQUENCIES
; THESE ARE THE FREQUENCIES IN HERTZ OF THE TOP OCTAVE (6)
; DIVIDED DOWN BY POWERS OF TWO TO GET ALL OTHER OCTAVES
;
NOTTAB: DW 4186D ;C
DW 4435D ;C#
DW 4699D ;D
DW 4978D ;D#
DW 5274D ;E
DW 5588D ;F
DW 5920D ;F#
DW 6272D ;G
DW 6645D ;G#
DW 7040D ;A
DW 7459D ;A#
DW 7902D ;B
PLYLEN: JNB PLGOFC ;ERROR IF NO ARG
CMP DL,LOW 65D ;ALLOW ONLY UP TO 64
JNB PLGOFC ;FC ERROR IF TOO BIG
OR DL,DL ;DON'T ALLOW ZERO
JZ PLGOFC ;FC ERROR IF ZERO
MOV BYTE PTR NOTELN,DL ;STORE NOTE LENGTH
RET
PTEMPO: CMP DL,LOW 32D ;ALLOW ONLY 32 - 255
JB PLGOFC ;FC ERROR IF TOO SMALL
MOV BYTE PTR BEATS,DL ;Store Beats per minute
RET
NCFCER:
PPAUSE: JNB PLGOFC ;ERROR IF NO ARG
XOR CX,CX ;PASS FREQ OF 0
CMP DL,LOW 65D ;ALLOW ONLY 1-64
JNB PLGOFC ;FC ERROR IF TOO BIG
OR DL,DL ;SEE IF ZERO
JZ PLYRET ;RETURN IF SO - NO PAUSE
JMP PPAUS2 ;[DX]=PAUSE LENGTH
POCTAV: JNB PLGOFC ;ERROR IF NO ARG
CMP DL,LOW 7 ;ALLOW ONLY OCTAVES 0..6
JNB PLGOFC ;FC ERROR IF TO BIG
MOV BYTE PTR OCTAVE,DL
PLYRET: RET
PLYNUM: JNB PLGOFC ;ERROR IF NO ARG
MOV AL,DL ;GET NOTE NUMBER INTO [AL]
OR AL,AL ;SEE IF ZERO (PAUSE)
JZ PLYNO3 ;DO THE PAUSE
CMP AL,LOW 85D ;ALLOW ONLY 0..84
JNB PLGOFC ;FC ERROR IF TOO BIG
CBW ;CLEAR HI BYTE FOR DIVIDE
DEC AX ;MAP TO 0..83
MOV DL,LOW 12D ;DIVIDE BY 12
DIV DL
MOV DH,AL ;OCTAVE TO [DH]
MOV AL,AH ;NOTE NUMBER IS REMAINDER
INC AL ;ADD ONE
ADD AL,AL ;DOUBLE TO MAKE INDEX
JMP SHORT PLYNU3 ;PLAY NOTE [AL], OCTAVE [DH]
PLGOFC: JMP FCERR ;GIVE FUNCTION CALL ERROR
PLYNOT: SUB CL,LOW OFFSET "A"-1 ;MAP TO 1..7
ADD CL,CL ;MAP TO 2..14 (THIS ASSUMES SHARP)
CALL FETCHR ;GET NEXT CHARACTER
JZ PLYNO2 ;END OF STRING - NO SHARP OR FLAT
CMP AL,LOW "#" ;CHECK FOR POSSIBLE SHARP
JZ PLYSHP ;SHARP IT THEN
CMP AL,LOW "+" ;"+" ALSO MEANS SHARP
JZ PLYSHP
CMP AL,LOW "-" ;"-" MEANS FLAT
JZ PLYFLT
CALL DECFET ;PUT CHAR BACK IN STRING.
JMP SHORT PLYNO2 ;TREAT AS UNMODIFIED NOTE.
PLYFLT: DEC CL ;DECREMENT TWICE TO FLAT IT
PLYNO2: DEC CL ;MAP BACK TO UNSHARPED
PLYSHP: MOV AL,CL ;INTO [AL] FOR XLAT
MOV BX,OFFSET NOTXLT ;POINT TO TRANSLATE TABLE
?CSLAB: ; Code segment dummy label
XLAT BYTE PTR ?CSLAB ;TRANSLATE INTO NOTE TABLE INDEX
OR AL,AL ;SEE IF LEGAL NOTE
JS PLGOFC ;NOTE'S OK IF NOT .GT. 127
;
; ENTER HERE WITH NOTE TO PLAY IN [AL]
; NOTE 0 IS PAUSE, 2,4,6,8..10,12 ARE A-G AND FRIENDS.
;
PLYNO3:
MOV DH,BYTE PTR OCTAVE ;GET OCTAVE INTO [DH] FOR LATER MATH
PLYNU3:
PUSH AX ;Save Note
PUSH DX ;Save Octave
MOV AL,BYTE PTR NOTELN
MOV BYTE PTR NOTE1L,AL ;One note duration = Note length
CALL FETCHR
JZ PLYNU4 ;Brif end of string
CALL VALSC2 ;See if possible number
CMP DL,LOW 65D ;If was .gt. 64
JNB PLGOFC ; then error
OR DL,DL ;Any Length?
JZ PLYNU4 ;Brif not, just do note
MOV BYTE PTR NOTE1L,DL ;Store duration for this note
PLYNU4:
POP DX ;Get Octave
POP AX ;Restore Note
CBW ;FILL [AH] WITH ZEROS
MOV BX,AX ;TRANSFER TO BX FOR INDEXING
OR BX,BX ;SEE IF PAUSE (NOTE # 0)
JZ PLYNO4 ;IF PAUSE, PASS [BX]=0
MOV BX,WORD PTR NOTTAB-2[BX] ;FETCH FREQUENCY
MOV CL,LOW 6 ;CALCULATE 6-OCTAVE
SUB CL,DH ;FOR # OF TIMES TO SHIFT FREQ.
SHR BX,CL ;DIVIDE BY 2^(6-OCTAVE)
ADC BX,0 ;ADD IN CARRY TO ROUND UP
PLYNO4:
MOV CX,BX ;FREQUENCY INTO [CX] FOR DONOTE
MOV DL,BYTE PTR NOTE1L ;Get this note's length
PPAUS2:
MOV AL,BYTE PTR BEATS ;GET BEATS PER UNIT TIME
MUL DL ;CALC NOTE LENGTH * BEATS
PUSH CX ;SAVE [CX] WHILE WE DIVIDE
MOV CX,AX ;CALC TIME CONST/(BEATS * NOTE LENGTH)
MOV DX,1 ;[DX:AX]=96000 (4*60*400.0) and will
MOV AX,73400O ; cause DONOTE [DX]=1 to play 2.5 milliseconds
DIV CX ; (in other words [DX]=400 will play 1 second)
POP CX ;RESTORE FREQUENCY
OR AX,AX ;IF DURATION IS ZERO, GET OUT.
JZ PLYNO8
PUSH CX ;Save Freq
PLYDOT:
PUSH AX ;Save duration
CALL FETCHR
JZ PLYDOX ;Brif EOS
CMP AL,LOW "." ;Note duration extender?
JNZ PLYDO2 ;Brif not
POP AX ;Get duration
MOV CX,3
MUL CX
SHR AX,1 ;Duration = Duration * 1.5
SHR DX,1 ;Ovf/2
OR DX,DX ;Still too big?
JZ PLYDOT ;Itterate if not
JMP FCERR ; else complain..
PLYDO2:
CALL DECFET ;Put char back
PLYDOX:
POP AX ;Duration
POP CX ;Get freq
OR CX,CX
JZ PLYNO9 ;Brif Pause
CMP BYTE PTR MSCALE,LOW 1
JZ PLYNO9 ;Brif Legatto
PUSH AX ;Save Duration
PUSH CX ;Save Frequency
MOV CL,BYTE PTR MSCALE ;Using scale for shift count
MOV BX,3 ;Stecatto multiplier
CMP CL,LOW 2
JZ PLYNO6 ;Brif Stecatto
MOV BX,7 ; else Normal
PLYNO6:
MUL BX ;Duration * 7/8 or 3/4
SHR AX,CL
OR AX,AX
JNZ PLYNO7 ;If zero
INC AX ; then make 1
PLYNO7:
POP CX ;Get Freq
CALL PLYNO9 ;Send note
POP AX ;Original duration
MOV CL,BYTE PTR MSCALE
SHR AX,CL ;pause after note is 1/8 or 1/4
XOR CX,CX ;Freq = 0 for pause
OR AX,AX ;Pause = 0?
JNZ PLYNO9 ;Brif not
PLYNO8:
RET ; else do nothing
PLYNO9:
MOV DX,AX ;DONOTE wants [CX]=freq, [DX]=duration.
JMP SHORT DOSND ;Play freq [CX] for time [DX]
PLYMER:
JMP FCERR
; PLYMET - Process Music Meta Commands.
PLYMET:
CALL FETCHZ ;Get Meta action or error
MOV CL,LOW 1 ;Factor for Legatto (1/1): MSCALE=1
CMP AL,LOW "L"
JZ PLYDUR ;Brif Legatto (Full note)
INC CL ;Factor for Stecatto (3/4): MSCALE=2
CMP AL,LOW "S"
JZ PLYDUR ;Brif Stecatto (3/4)
INC CL ;Factor for Normal (7/8): MSCALE=3
CMP AL,LOW "N"
JZ PLYDUR ;Brif Normal (7/8)
XOR CL,CL ;MMODE=0 for Forground
CMP AL,LOW "F"
JZ PLYMOD ;Brif Foreground Music
INC CL ;MMODE=1 for Background
CMP AL,LOW "B"
JNZ PLYMER ;Brif not Background Music
PLYMOD:
MOV BYTE PTR MMODE,CL ;Store Music Mode (0=FG, 1=BG)
RET
PLYDUR:
MOV BYTE PTR MSCALE,CL ;Store Duration Scaling factor
RET
;SNDINI is called to set OCTAVE, BEATS, NOTELN, NOTE1L, MSCALE, and MMODE
;to appropriate initial settings. SNDINI is called at CLEARC and during
;initialization.
;Entry - none
;Exit - all registers preserved
;
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN NOTELN:WORD,NOTE1L:WORD,BEATS:WORD,OCTAVE:WORD,MSCALE:WORD
EXTRN MMODE:WORD
DSEG ENDS
PUBLIC SNDINI
SNDINI: MOV BYTE PTR BEATS,LOW 120D
MOV BYTE PTR MSCALE,LOW 3D
MOV BYTE PTR MMODE,LOW 0D
MOV BYTE PTR NOTELN,LOW 4D
MOV BYTE PTR NOTE1L,LOW 4D
MOV BYTE PTR OCTAVE,LOW 4D
CALL SNDRST ;Turn off sound
RET
;SNDRST is called to reset background music. It is called during
; initialization from INIT and during the processing of CTL-C
; from POLKEY
; Entry - none
; Exit - All registers preserved
;
PUBLIC SNDRST
SNDRST: PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSHF
MOV AL,LOW 255D
CALL DONOTE ;Disable background music, init music queue
POPF
POP DX
POP CX
POP BX
POP AX
RET
PUBLIC BEEPS,BEEP,SOUNDS
EXTRN DONOTE:NEAR,FRQINT:NEAR,FRCSNG:NEAR,GETIN2:NEAR
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN FAC:WORD
DSEG ENDS
BEEP:
BEEPS: MOV CX,800D ; 800 Hz
MOV DX,100D ; .. for 1/4 second.
XOR AL,AL ;[AL]=Music Mode (0=Forground)
JMP SHORT JDNOTE
DOSND: MOV AL,BYTE PTR MMODE ;[AL]=Music Mode (0=Forground, 1=background)
JDNOTE: CALL DONOTE ;start new sound.
JNB DNOTOK ;No errors detected by DONOTE
JMP FCERR ;Function call error detected
DNOTOK:
JMP POLKEY ;Allow CTL-C to interrupt and return
; SOUND - Make SOUNDs with the speaker.
;
; Syntax: SOUND x,y
;
; Where: x is the Frequency in Hertz.
; y is the Duration in Clock ticks. (currently 18.2/sec).
;
; Frequency must be at least 37 Hz.
; If Duration is 0, then just turn off current sound...
;
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN $FACLO:WORD,$FACM1:WORD
DSEG ENDS
EXTRN $FMULS:NEAR
SOUNDS:
CALL GETIN2 ;Get frequency.
CMP DX,37D
JB SNDFCE ;Must be at least 37 Hz..
PUSH DX ;Save frequency
CALL SYNCHR
DB OFFSET ","
CALL FRMEVL ;Get duration.
CALL EOSCHK ;Syntax Error if not end-of-statement
PUSH BX ;Text pointer
CALL FRCSNG ;Make Single Precision
MOV BX,OFFSET FAC ;Point at Exponent
CMP BYTE PTR 0[BX],LOW 0 ;Will turn sound off if 0.
JNZ SOUNL4 ; Brif not, start new sound.
POP BX ;Text pointer
POP DX ;Frequency (not used)
JMP SHORT SNDRST ;Turn off sound, initialize the queue
SOUNL4:
CMP BYTE PTR 0[BX],LOW 221O ;Duration .gt. 65535?
JNB SNDFCE ;Brif so, too big for *32
PUSH BX ;Save FAC address
PUSH WORD PTR $FACM1 ;Push FAC on the stack
PUSH WORD PTR $FACLO
MOV BX,22D ;Mult by ^D22
CALL MAKINT
CALL FRCSNG ;Get s.p. ^D22
POP DX ;Get low mantissa bits
POP BX ;Exp sign and high mantissa bits
CALL $FMULS ;MULTIPLY
POP BX ;FAC address
CMP BYTE PTR 0[BX],LOW 221O ;Overflow?
JB SOUNL5 ;Brif not
MOV WORD PTR -1[BX],110177O ; else
MOV WORD PTR -3[BX],177400O ; force to 65535
SOUNL5:
CALL FRQINT ;Convert back to Integer
MOV DX,BX ; in [DX]
POP BX ;Text pointer
POP CX ;[CX]=Frequency, [DX]=Duration
JMP SHORT DOSND ;play the note
SNDFCE:
JMP FCERR ; Complain
PAGE
SUBTTL General Event Trapping Code
PUBLIC ONGOTP,SETGSB
EXTRN STPTRP:NEAR,ONTRP:NEAR,OFFTRP:NEAR,REQTRP:NEAR,FRETRP:NEAR
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN $ON:WORD,$OFF:WORD,$STOP:WORD
DSEG ENDS
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN TRPTBL:WORD
DSEG ENDS
;Event flags can have one or more of the following bits set:
; They are defined in BIMISC.MAC
;
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN T_ON:WORD ;1 event trapping on
DSEG ENDS
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN T_STOP:WORD ;2 event trapping stopped (remembers but doesn't report)
DSEG ENDS
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN T_REQ:WORD ;4 event trap requested (this event has happend)
DSEG ENDS
;To support EVENT TRAPPING, The following switches should be defined
; in the machine dependant Switch File:
;
; NMKEYT = number of soft keys
; NMCOMT = number of COMmunications ports
; NMPENT = number of light pens (0 or 1)
; NMSTRT = number of joysticks
; NUMTRP = total of all of the above
; ONGOSB should be 1
;
;To support EVENT TRAPPING, The following variables should be defined
; in the machine dependant RAM module:
;
; ONGSBF: BLOCK 1 ;some-event happend flag (see NEWSTT)
; TRPTBL: BLOCK 3*NUMTRP
; ;event flags and GOSUB line ptrs
;
PENOFF=0 ;offset for PEN event id's
KEYOFF=PENOFF+NMPENT ;offset for KEY event id's
COMOFF=KEYOFF+NMKEYT ;offset for COM event id's
STROFF=COMOFF+NMCOMT ;offset for STRIG event id's
PUBLIC CHKINT
EXTRN POLKEY:NEAR,POLLEV:NEAR
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN AUTFLG:WORD,SEMFLG:WORD,SAVTXT:WORD,SAVSTK:WORD,CURLIN:WORD
DSEG ENDS
;CHKINT is called from BASIC's NEWSTT loop to see if any trappable
; condition has occured. It traps active function keys, COM input,
; light pen interrupts, joystick triggers, CTL-C, CTL-S, and
; it queues vanilla keys for CHSNS. For efficiency, it calls POLLEV
; which looks at flag which gets set by BIOS when some interrupt occurs.
; This routine would not be necessary in a stand-alone environment since
; BASIC would manage its own interrupts.
;
CHKINT:
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN MSDCCF:WORD ;MSDOS Ctl-C flag
DSEG ENDS
TEST BYTE PTR MSDCCF,LOW 255D ;Test for MSDOS-received Ctl-C
JNZ CHKIN1
CALL POLLEV ;Test for occurance of trapable events
JZ CHKINX ;Exit - no trapable event
CHKIN1: PUSH BX
PUSH CX
PUSH DX
CALL POLKEY ;trap Function keys, CTL-C, CTL-S
EXTRN POLCOM:NEAR
CALL POLCOM ;trap COM interrupts
NOACOM:
CALL POLPEN ;trap PEN interrupts
CALL POLSTR ;trap STRIG interrupts
POP DX
POP CX
POP BX
CHKINX: RET
;SEVSTT - Set Event Status
; Common code for parsing event-id ON/OFF/STOP
; Entry - [CL]=number of legal events for device
; [CH]=event-id offset for device
;
SEVSTT: PUSH CX ;save maximum and offset for class
CALL SYNCHR
DB OFFSET "("
CALL GETBYT ;[AL]=class-relative event-id
PUSH AX ;save it
CALL SYNCHR
DB OFFSET ")"
PUSH AX ;save [AL]=$ON, $OFF, or $STOP
CALL EOSCH1 ;skip past $ON, $OFF, or $STOP
;error if not end of statement
POP DX ;restore $ON, $OFF, or $STOP
POP AX ;[AL]=class-relative event-id
POP CX ;restore maximum, offset for class
CMP CH,LOW OFFSET STROFF ;Special check for STRIG(x)
JNZ SEVST1 ;Brif not
ROR AL,1 ;Divide by 2 so get 0,1,2 or 3
JMP SHORT SEVST2
SEVST1:
OR AL,AL
JZ SEVALL ;branch if parm was 0
DEC AL ;make parm 0-relative
SEVST2:
PUSH DX ;save $ON, $OFF, or $STOP
CMP AL,CL ;compare with maximum
JGE FCERR1 ;branch if not in range
ADD AL,CH ;add in offset to get event id
CALL EVADR ;[DX]=adr of event flag
POP AX ;[AL]=ON, OFF, or STOP
;fall into SET1EV
;SET1EV - Set One Event
; Entry - [DX]=adr of event flag
; [AL]=$ON, $OFF, or $STOP
;
SET1EV: CALL EVSET ;set event flags to ON, OFF, or STOP
RET
FCERR1: JMP FCERR
SNERR1: JMP SNERR
;Set status for all events of a class
; Entry - [DL]=$ON, $OFF, or $STOP token
; [CH]=class event-id offset
; [CL]=number of events in class
;
SEVALL: PUSH DX ;save $ON, $OFF, or $STOP
MOV AL,CH ;[AL]=class offset
XOR CH,CH ;[CX]=number of entries in class
CALL EVADR ;[DX] points to 1st event flag
POP AX ;[AL]=ON, OFF, or STOP
SEVAL1: CALL EVSET ;set event for key 9-[B]
ADD DX,3 ;[DX] points to next entry
LOOPNE SEVAL1
RET
;EVSET changes the status of 1 event.
; Entry - [AL]=$ON, $OFF, or $STOP
; [DX] points to event flag
; Exit - All registers preserved.
;
EVSET: PUSH DX ;save caller's registers
PUSH CX
PUSH BX
PUSH AX
MOV BX,DX ;[BX]=adr of event flag
CMP AL,LOW OFFSET $ON
JZ EVON
CMP AL,LOW OFFSET $OFF
JZ EVOFF
CMP AL,LOW OFFSET $STOP
JNZ SNERR1
EVSTP: CALL STPTRP ;stop event pointed to by [BX]
JMP SHORT EVONX
EVON: CALL ONTRP ;enable event pointed to by [BX]
JMP SHORT EVONX
EVOFF: CALL OFFTRP ;disable event pointed to by [BX]
EVONX: POP AX ;restore all caller's registers
POP BX
POP CX
POP DX
RET
;EVADR transforms event-id to event-flag-pointer.
; Entry - [AL] = event id
; Exit - [DX]=adr of event flag, [AX] is used, all other regs preserved
;
EVADR: XOR AH,AH ;[AX] = event id
MOV DX,AX
ADD AX,AX
ADD AX,DX ;[AX] = 3*event id
ADD AX,OFFSET TRPTBL
MOV DX,AX
RET
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN $KEY2B:WORD,$PEN2B:WORD,$STR2B:WORD,$COM2B:WORD
DSEG ENDS
EXTRN DERDNA:NEAR
;ONGOTP routine is called from ON GOTO code within BINTRP
; Do syntax checks for event traps and return.
; Carry set indicates it was not a trap gosub definition.
; else carry is clear
;
; Syntax: ON [Event] GOSUB line no.
;
; WHERE: Event is one of following:
; COM(x)
; PEN
; STRIG(x) x = 0 for Trigger A, or 2 for Trigger B.
; KEY(x) x = 1..NMKEYT-1
;
; If one of the Event verbs does not follow the ON
; keyword, then go back to Level 1 "ONGOTO" to
; process ON ### GOSUB... or GOTO.
;
ONGOTP: PUSH BX ;Text pointer in case not us.
XOR CH,CH ;Assume one byte token
CMP AL,LOW 375O ;check for 2-byte token
JB NT2BTK ;branch if not FF, FE or FD
MOV CH,AL ;[CH] = 1st byte
CALL CHRGTR ;[AL] = 2nd byte of 2-byte token
MOV AH,CH ;[AH] = 1st byte
NT2BTK:
CMP AX,OFFSET $KEY2B
MOV CX,OFFSET (400O*KEYOFF)+NMKEYT
JZ ONFUN ;Brif ON KEY...
CMP AX,OFFSET $COM2B
MOV CX,OFFSET (400O*COMOFF)+NMCOMT
JZ ONFUN ;Brif ON COM...
CMP AX,OFFSET $PEN2B
MOV CX,OFFSET 400O*PENOFF
JNZ NTONPN ;BRIF not ON PEN...
CALL CHRGTR ;get next token
JMP SHORT ONSTM ;Brif ON PEN...
NTONPN:
CMP AX,OFFSET $STR2B
MOV CX,OFFSET (400O*STROFF)+NMSTRT
JZ ONFUN
STC ;set carry - indicates not event
POP BX ;Restore Entry Text pointer
JMP SHORT ONGOTX
FCERR2: JMP FCERR
JERDNA: JMP DERDNA ;Device unavailable error if PEN
ONFUN:
CALL GETSUB ;Get Event no. in (x).
DEC AL ;Want base 0. (If not STRIG(x))
CMP CH,LOW OFFSET STROFF ;Special check for STRIG(x)
JNZ ONFUN1 ;Brif not
INC AL ;Restore STRIG #
ROR AL,1 ;Divide by 2 so get 0,1,2 or 3
ONFUN1:
CMP AL,CL ;Value [AL] .gt. MAX [CL]?
JNB FCERR2 ;If so, then Ill fun error.
MOV CL,AL ;Save Event index in [CL]
ONSTM:
ADD CH,CL ;[CH]=event-id
MOV CL,LOW 1 ;[CL]=maximum number of line#'s in gosub
POP DX ;discard saved text pointer
DEC BX ;backup for CHRGET
OR AL,AL ;clear carry - indicates it was event
ONGOTX: MOV AL,BYTE PTR 0[BX] ;Restore Token
RET
GETSUB: PUSH CX
CALL CHRGTR ;skip current token
GETSU2: CALL SYNCHR
DB OFFSET "("
CALL GETBYT ;get event-class index
PUSH AX ;save it
CALL SYNCHR
DB OFFSET ")"
POP AX
POP CX
RET
;Set gosub entry
; Entry - [AL]=trap id, [DX]=line pointer
; Exit - [AX], [FLAGS] used, All other registers preserved.
;
SETGSB: PUSH SI
PUSH DX ;save gosub line pointer
CALL EVADR ;[DX] points to event flag
MOV SI,DX ;[SI] points to event flags
POP DX ;restore GOSUB line pointer
INC SI ;[SI] points to gosub line ptr entry
MOV WORD PTR 0[SI],DX ;put trap adr in table for this entry
POP SI
RET
;EVTRP is called to signal that an event has occured.
; Entry - [AL] = event id
; Exit - If NZ, the event is not being trapped (not ON)
; AX is used. All other registers are preserved.
;
EVTRP: PUSH DX
PUSH CX
PUSH BX
MOV BX,WORD PTR CURLIN
INC BX
JZ NOT_ON ;branch if in direct mode (no event trapping)
CALL EVADR ;[DX] points to event flag
MOV BX,DX ;[BX] points to event flag
MOV AL,BYTE PTR 0[BX] ;[AL]=event flag
AND AL,LOW OFFSET T_ON ;NZ if trapping enabled for key
JZ NOT_ON ;branch if event is not on
PUSHF
CALL REQTRP ;Trap enabled, Issue Request
POPF
NOT_ON: POP BX
POP CX
POP DX
RET
;EVCLR is called to reset an event
; Entry - [AL] = event id
; Exit - Flags, AX are used. All other registers are preserved.
;
EVCLR: PUSH DX
PUSH CX
PUSH BX
CALL EVADR ;[DX] points to event flag
MOV BX,DX ;[BX] points to event flag
CALL FRETRP ;Reset Trap Request
JMP SHORT NOT_ON
;TSTCEV is called to Test-and-Clear an event.
; Entry - [AL] = 0-relative event index
; Exit - [BX] = -1 if event had occured
; = 0 if event had not occured
; bit T.REQ of TRPTBL+3*index is cleared
;
TSTCEV:
CALL EVADR ;[DX] [points to event flag
MOV BX,DX ;[BX] points to event flag
MOV AL,BYTE PTR 0[BX] ;[AL] = current event state
PUSH AX
CALL FRETRP ;Reset Trap Request (clear T.REQ)
POP AX
XOR BX,BX ;prepare to return negative result
AND AL,LOW OFFSET T_REQ
JE TSTEVX
DEC BX ;[BX]=-1 (indicates event has occured)
TSTEVX: RET
PAGE
SUBTTL COM Statement and Event Trapping
PUBLIC COMS
PUBLIC COMTRP
;COM statement
;
COMS: MOV CX,OFFSET (400O*COMOFF)+NMCOMT
JMP SEVSTT ;branch to common code
;COMTRP is called by COM interrupt service routine to signal trapping.
; Entry - [AL]=0-relative com channel id.
; Exit - NZ is true if key trapping is enabled.
; All other regs preserved.
;
COMTRP: PUSH AX
ADD AL,LOW OFFSET COMOFF ;[AL]=event id
CALL EVTRP ;Signal the occurance of a COM EVENT
POP AX
RET
PAGE
SUBTTL SOFT KEY Statement and Event Trapping
PUBLIC KEYS
EXTRN LINPRT:NEAR,OUTDO:NEAR
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN $ON:WORD,$OFF:WORD,$LIST:WORD
DSEG ENDS
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN STRTAB:WORD,KEYSW:WORD,FKCNUM:WORD
DSEG ENDS
EXTRN SCROUT:NEAR,CLREOL:NEAR
PUBLIC KEYTRP
EXTRN MAKINT:NEAR
;SET KEY STATUS - KEY(n) (status)
;
KEYSTT: MOV CX,OFFSET (400O*KEYOFF)+NMKEYT
JMP SEVSTT ;branch to common code
;KEYTRP is called whenever CHGET receives a SOFTKEY from the keyboard.
; Entry - [AL] = Soft key id (0..NMKEYT-1)
; Exit - If NZ, key was trapped and should not be expanded.
; Otherwise, CHGET should expand key.
; All other registers are preserved.
;
KEYTRP: PUSH AX
ADD AL,LOW OFFSET KEYOFF ;[AL] = 0-relative event id
CALL EVTRP ;Signal the occurance of a KEY EVENT
POP AX
RET
EXTRN FKYADV:NEAR
;KEY Statement
;
KEYS:
CMP AL,LOW "("
JZ KEYSTT ;set key status
CMP AL,LOW OFFSET $ON
JZ KEYON ;Brif Enable Soft Key Display Line.
CMP AL,LOW OFFSET $OFF
JZ KEYOF ;Brif Disable Soft Key Display line.
CMP AL,LOW OFFSET $LIST
JZ KEYLSI ;Brif LIST Soft Keys.
CALL GETBYT ; else Key defn, get key number.
OR AL,AL
JZ KEYFCE
SUB AL,LOW OFFSET STKEYF ;Get zero relative key number
CMP AL,LOW OFFSET NMKEYF
JNB KEYFCE ;Must be STKEYF - NMKEYF+STKEYF, else error.
MOV DX,16D
MUL DL ; (16 * Key number).
MOV DX,AX
ADD DX,OFFSET STRTAB ;Index into Soft Key table
PUSH DX ;Save addr
CALL SYNCHR
DB OFFSET ","
CALL FRMEVL
PUSH BX ;Save Text pntr.
CALL FRESTR ;Get String Descriptor
MOV CL,BYTE PTR 0[BX]
CMP CL,LOW 15D
JB KEY1
MOV CL,LOW 15D ;String may be 0 to 15 chars.
KEY1:
INC BX
MOV SI,WORD PTR 0[BX] ;Get addr of string
POP BX ;Text pntr
POP DI ;STRTAB addr
PUSH BX
MOV CH,LOW 0
CLD
;Move new softkey
REP MOVSB ;to Softkey table
MOV BYTE PTR 0[DI],CH ;Terminate entry with 0.
MOV AL,BYTE PTR KEYSW ;Are the key definitions being
OR AL,AL ;displayed?
JZ NODSPK ;No, don't call DSPKEY since it would
;erase the bottom line of the screen.
CALL KEYDSP ;Yes, update the display.
NODSPK: POP BX ;Text pntr
RET
KEYFCE:
JMP FCERR ;complain..
KEYLSI: JMP SHORT KEYLST
KEYON: CALL SKEYON
JMP SHORT KEYOXX ;non-zero = ON
KEYOF: MOV AH,LOW 1D ;Prepare to inc scroll limit
MOV AL,LOW 0 ;zero = OFF
CALL KEYOX
KEYOXX: CALL CHRGTR ;over ON/OFF token.
RET
PUBLIC SKEYON
SKEYON: MOV AH,LOW -1D ;Prepare to dec scroll limit
MOV AL,LOW 377O
KEYOX: ;AH=scroll limit diff., AL=new KEYSW
CMP AL,BYTE PTR KEYSW ;State change?
MOV BYTE PTR KEYSW,AL
JZ KEYXX ;Brif same, do nothing
CMP BYTE PTR KEYSW,LOW 255D ;Test if change to ON
JNZ KEYOX1 ;Change is to OFF - do not call FKYADV
CALL FKYADV
KEYOX1:
CALL KEYDSP ;On, Display on 25th line.
KEYXX: RET
;List Function Keys
;
KEYLST:
PUSH BX
MOV SI,OFFSET STRTAB ;List 10 Special Function
MOV CX,OFFSET NMKEYF+(STKEYF*400O)
KEYLS0:
PUSH SI
MOV AL,LOW "F"
CALL OUTDO
PUSH CX
MOV BL,CH
MOV BH,LOW 0
CALL LINPRT ;Display the Key number.
MOV AL,LOW " "
CALL OUTDO
POP CX
POP SI
PUSH SI
PUSH CX
KEYLS1:
CLD
LODSB
OR AL,AL
JZ KEYLS2 ;Brif end of String.
CALL KEYLSP
JMP SHORT KEYLS1
KEYLS2:
MOV AL,LOW 13D ;Output carriage return
CALL OUTDO
MOV AL,LOW 10D ;Output a line feed
CALL OUTDO
POP CX
POP SI
ADD SI,16D ;Next key address
INC CH ;Next key number
DEC CL
JNZ KEYLS0
POP BX
JMP KEYOXX
KEYLSP:
PUSH SI
CMP AL,LOW 13D ;check for Carriage-Return
JNZ KEYLSQ
MOV AL,LOW 33O
KEYLSQ: CALL OUTDO
POP SI
RET
PAGE
SUBTTL KEYON, KEYOFF, and KEYDSP
PUBLIC KEYDSP,TKEYOF
EXTRN FKYFMT:NEAR
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN FKCNUM:WORD
DSEG ENDS
;TKEYOF is called to turn function key display off
;
TKEYOF: MOV BYTE PTR KEYSW,LOW 0 ;turn function key display switch off
; KEYDSP - Display Softkeys on last line of Screen.
REVNMS=1 ;Key Numbers are normal video, contents are rev-video
KEYDSP: PUSH DX
MOV DH,LOW 1D ;Set for col = 1
MOV DL,BYTE PTR LINCNT ;Set for last line
MOV AL,BYTE PTR KEYSW
OR AL,AL ;Key on or off?
JNZ KEYDS0 ;Softkey display switch on
CALL CLREOL ;Clear from (DH,DL) to EOL
POP DX
RET
KEYDS0:
EXTRN SETCSR:NEAR
MOV BYTE PTR CSRTYP,LOW 0D ;Set off mode cursor
CALL SETCSR ;Turn the cursor off
PUSH BX
KEYDS1: CALL GETFMT ;Get function key display format
KNXTST: PUSH AX ;Save Key disp no.
CMP AH,LOW "0" ;Single digit case?
JZ SINDIG ;Print only one digit
XCHG AH,AL
CALL KEYDCH ;Display first digit
XCHG AH,AL
SINDIG: CALL KEYDCH ;Display last digit
PUSH SI
MOV CL,BYTE PTR FKCNUM ;Count of chars per fun. key (set by GETFMT
CALL XFGBG ;Swap Forground & background colors
KNXTCH: ;Write the next key character
PUSH CX
CLD
LODSB
OR AL,AL ;End of string?
PUSHF
PUSH SI
JNZ KEYDS4 ;No, Display char
XOR AL,AL ;else blank
KEYDS4:
CALL KEYDCH ;Display char, adv cursor
POP SI
POPF
JNZ KEYDS5 ;Brif not EOS
DEC SI
KEYDS5:
POP CX
DEC CL
JNZ KNXTCH ;Loop for next character
CALL XFGBG ;Swap Forground & background colors
CALL KEYDB ;Follow with blank
POP SI
POP AX
CALL KEYADV ;Advance to next key
DEC CH
JNZ KNXTST ;Loop for next key string
KEYDSX: POP BX ;Retrieve cursor position
POP DX
MOV BYTE PTR CSRTYP,LOW 3D ;Set user mode cursor
CALL SETCSR ;Turn on cursor
RET
KEYDB: XOR AL,AL ;For Blank at end of Key field
KEYDCH:
PUSH AX
PUSH BX
OR AL,AL ;Separating keys?
JNZ KEYDC1 ;Brif not.
MOV AL,LOW " " ;else write space
KEYDC1: CMP AL,LOW OFFSET CR ;CR?
JNZ KEYNCR ;Not CR.
MOV AL,LOW OFFSET FKEYCR ;Subs Greater-Than-Sign
KEYNCR: CMP AL,LOW OFFSET LF ;Line feed?
JNZ KEYNLF ;Not line feed
MOV AL,LOW 74O ;Substitute Greater-Than-Sign
KEYNLF: PUSH CX
MOV AH,LOW 0
CALL SCROUT ;Write the character at (DH,DL)
INC DH
POP CX
POP BX
POP AX ;Restore key number
RET
PAGE
;Get function key display format
;
GETFMT: PUSH BX
CALL FKYFMT ;OEM routine
MOV CX,WORD PTR 0[BX] ;CH=key count, CL=Chrs/key
PUSH CX
MOV BYTE PTR FKCNUM,CL
MOV SI,OFFSET STRTAB ;SI=address of first fkey in table
MOV AL,BYTE PTR 2[BX]
CBW
PUSH AX ;Save number of first function key
DEC AL ;Set to zero relative
MOV CL,LOW 4D ;Multiply by 16 (bytes/key)
SHL AX,CL ;AX = index of first display key
ADD SI,AX
POP AX
CALL INTOCH ;Get key number to character code
CMP AH,LOW "0"
JZ ONEDIG ;Only one digit
DEC BYTE PTR FKCNUM ;Adjust function key format for two digits
ONEDIG: CALL KADNRM ;Normalize key address
POP CX
POP BX
RET
;INTOCH: Translate integer AL to characters in AX.
; Integers must be in the range (100,0].
; Radix is 10.
;USES - none
;
INTOCH: PUSH CX
XOR AH,AH
MOV CL,LOW 10D ;Load radix
DIV CL
ADD AX,30060O ;3030H forms character codes
XCHG AH,AL ;AH represents significant digit
POP CX
RET
;KEYADV - Advance to next key
;Entry - AX = key number characters
; SI = index into STRTAB (key code table)
;
KEYADV: ADD SI,16D ;Move to next key table entry
INC AL
CMP AL,LOW "9"
JLE KADNRM
MOV AL,LOW "0"
KADNRM:
CMP SI,OFFSET STRTAB+NMKEYF*16D
JB KADNMX
MOV SI,OFFSET STRTAB ;Wrap around to the first function key
;Print function key 10 number as 0
; except when it is the first key
CMP AH,LOW "1" ;Only true if 1st function key is key 10
JNZ KADNMX ; all other wraps are for 1 digit only
MOV AH,LOW "0"
MOV AL,LOW "1"
INC BYTE PTR FKCNUM ;Re-adjust format
KADNMX: RET
PAGE
SUBTTL Swap Forground & Background Colors
;Swap Forground & Background Colors (Toggle Reverse Video Mode)
;
EXTRN GETFBC:NEAR,SETFBC:NEAR ;Get and set forground/background attributes
XFGBG: CLC ;Signal text attributes
CALL GETFBC ;Get forground/background attributes
XCHG AX,BX
CALL SETFBC ;Set forground/background attributes
RET
PAGE
SUBTTL PEN Statement and Event Trapping
PUBLIC PENS,PENF
EXTRN MAKINT:NEAR
; Dispatch PEN statement depending upon following clauses:
;
; PEN ON Enable PEN Trapping.
; PEN OFF Disable PEN Trapping.
; PEN STOP Suspend PEN Trapping.
;
; Attempts to read Light pen when off
; result in "Illegal Function Call" Error.
;
PENS:
JZ SNERR2 ;Syntax error if end-of-statement
PUSH AX ;save $ON/$OFF/$STOP
CALL EOSCH1 ;Syntax Error if not End-Of-Statement
MOV DX,OFFSET TRPTBL+(3*PENOFF) ;[DX]=adr of event flag
POP AX ;[AL]=$ON, $OFF, or $STOP
CMP AL,LOW OFFSET $STOP
JE NTONOF ;branch if not ON/OFF
PUSH AX
SUB AL,LOW OFFSET $ON
JE PENS1 ;branch if "PEN ON"
MOV AL,LOW 1 ;better be OFF
PENS1: SUB AL,LOW 2 ;Map (ON, OFF) to (254, 255)
PUSH BX ;preserve text pointer (destroyed by RDPEN)
CALL RDPEN ;enable/disable light pen interrupts
POP BX
POP AX ;restore [AL] = $ON/$OFF
NTONOF:
JMP SET1EV ;Set Event Flag
SNERR2: JMP SNERR
FCERR3: JMP FCERR
;PEN Function:
; Syntax: x=PEN(n)
; n=0: Return -1 if pen was down since last poll, else 0.
; n=1: Return X Graphics Coordinate where pen was last activated.
; n=2: Return Y Graphics Coordinate where pen was last activated.
; n=3: Return -1 if pen is currently down, 0 if currently up.
; n=4: Return last known valid X Graphics Coordinate.
; n=5: Return last known valid Y Graphics Coordinate.
; n=6: Return character row where pen was last activated.
; n=7: Return character column where pen was last activated.
; n=8: Return last known character row.
; n=9: Return last known character column.
;
EXTRN RDPEN:NEAR
PENF:
CALL ONESUB ;[AL] = pen function
PUSH BX ;save text pointer
CMP AL,LOW 10
JB PENOK
JMP FCERR3 ;Error if Parm exceeds 9
PENOK:
OR AL,AL
JNE NPEN0 ;branch if not PEN(0)
;Whenever a PEN interrupt occurs, it is detected by POLPEN (called by CHKINT).
; PEN(0) tells whether a PEN event has occured since the last PEN(0).
; It determines this by testing-and-clearing the bit set by POLPEN.
; NOTE: PEN(0) always returns false when event trapping has been enabled
; (by PEN ON statement)
;
MOV AL,LOW OFFSET PENOFF
CALL TSTCEV ;test and clear event [AL]
JMP SHORT PENRET ;return -1 in BX if event has occured
NPEN0:
CALL RDPEN
PENRET: CALL MAKINT ;Return [BX] as signed integer
POP BX ;restore text pointer
RET
;POLPEN is called by CHKINT at beginning of every BASIC statement (NEWSTT).
; If a PEN interrupt has occured, it sets the appropriate bit in TRPTBL
; which will cause the BASIC program's pen service routine (ON PEN GOSUB)
; to be invoked.
; Exit - AX, BX, CX, DX can be used (restored by CHKINT).
; All other registers are preserved.
;
POLPEN:
XOR AL,AL ;See if lightpen has interrupted
CALL RDPEN
OR BX,BX
JE NOPENI ;branch if no lightpen interrupt
MOV AL,LOW OFFSET PENOFF
CALL EVTRP ;Signal the occurance of a PEN EVENT
NOPENI: RET
;Parse "(n)" and return n in [AL]
ONESUB:
;In non-IBMTOK versions, EVAL doesn't parse the argument
EXTRN INTFR2:NEAR
PUSH DX
CALL INTFR2 ;[DX] = function to be performed (argument)
JNE FCERRI ;branch if [DX] is not [0..255]
MOV AL,DL ;return argument in [AL]
POP DX ;restore caller's [DX]
RET
PAGE
SUBTTL STRIG Statement and Event Trapping
PUBLIC STRIGS,STRIGF,STICKF
EXTRN RDSTIK:NEAR,RDTRIG:NEAR
;STRIG statement
;
; Syntax:
;
; In IBM BASIC, STRIG ON enables trigger trapping while STRIG OFF
; disables trigger event trapping.
; It is parsed in GW-BASIC for syntax compatibility only.
;
; STRIG(n) ON Enable STRIG(n) Trapping.
; STRIG(n) OFF Disable STRIG(n) Trapping.
; STRIG(n) STOP Suspend STRIG(n) Trapping.
;
; WHERE:
; (n) is Trigger 0 for joystick trigger #1,
; 2 for joystick trigger #2,
; 4 for joystick trigger #3, etc.
;
STRIGS:
CMP AL,LOW "("
JNE STRIG1 ;branch if STRIG ON or STRIG OFF
MOV CX,OFFSET (400O*STROFF)+NMSTRT
JMP SEVSTT ;branch to common code
STRIG1:
CMP AL,LOW OFFSET $ON
JE STROK
CMP AL,LOW OFFSET $OFF
JE STROK
JMP SNERR ;SYNTAX ERROR if not $ON or $OFF or (n)
STROK: JMP EOSCH1 ;SYNTAX ERROR if not end-of-statement
;STRIG Function:
;
; Syntax:
;
; x=STRIG(n)
; n=0: return -1 if button 1 was pressed since last STRIG(0), else 0.
; n=1: return -1 if button 1 is currently pressed, else 0.
; n=2: return -1 if button 2 was pressed since last STRIG(0), else 0.
; n=3: return -1 if button 2 is currently pressed, else 0.
; etc.
;
STRIGF:
CALL ONESUB ;Parse "(n)", [AL] = n
PUSH BX ;save text pointer
MOV AH,AL ;Map AL to AH: (0,1,2,...) to (1,0,1,...)
INC AH
AND AH,LOW 1 ;[AH] = 1 for latched, 0 for current
SHR AL,1 ;[AL] = 0 relative joystick trigger id
CMP AL,LOW OFFSET NMSTRT
JB STRGOK
FCERRI: JMP FCERR ;branch if illegal trigger id
STRGOK:
OR AH,AH
JE NSTR0 ;brif current (not latched) was requested
PUSH AX
ADD AL,LOW OFFSET STROFF ;see if STRIG(n) ON has been executed
CALL EVADR ;if not, POLSTR will not call RDTRIG for
MOV BX,DX ; this trigger, so we must call it directly
TEST BYTE PTR 0[BX],LOW OFFSET T_ON
POP AX
JE NSTR0 ;branch if event is not enabled
;Whenever a STRIG interrupt occurs, it is detected by POLSTR (called by CHKINT).
; STRIG(0) tells whether a STRIG event has occured since the last STRIG(0).
; It determines this by testing-and-clearing the bit set by POLSTR.
; NOTE: STRIG(0) always returns false when event trapping has been enabled
; (by STRIG(n) ON statement)
;
ADD AL,LOW OFFSET STROFF ;[AL]=0 relative event index
CALL TSTCEV ;test and clear event [AL]
JMP SHORT STRRET ;return -1 in BX if event has occured
NSTR0: CALL RDTRIG ;[AL] = 0/1 for not-pressed/pressed
CBW ;[AX] = 0/1 for not-pressed/pressed
NEG AX ;[AX] = 0/-1 for not-pressed/pressed
MOV BX,AX
STRRET: CALL MAKINT ;return [BX] as signed integer
POP BX ;restore text pointer
RET
;STICK Function:
;
; Syntax:
;
; x=STICK(n)
; n=0: return x coordinate for joystick 1.
; n=1: return y coordinate for joystick 1.
; n=2: return x coordinate for joystick 2.
; n=3: return y coordinate for joystick 2.
; etc.
;
STICKF:
CALL ONESUB ;AL=stick id
PUSH BX ;save text pointer
CALL RDSTIK ;[BX] = stick coordinate
JAE STKOK
JMP FCERR ;branch if bad parameter
STKOK:
CALL MAKINT ;return [BX] as signed integer
POP BX ;restore text pointer
RET
;POLSTR is called by CHKINT at beginning of every BASIC statement (NEWSTT).
; If a STRIG interrupt has occured, it sets the appropriate bit in TRPTBL
; which will cause the BASIC program's pen service routine (ON STRIG(N) GOSUB)
; to be invoked.
; Exit - AX, BX, CX, DX can be used (restored by CHKINT).
; All other registers are preserved.
;
POLSTR:
XOR AX,AX ;[AL] = joystick trigger #0 (for RDTRIG)
INC AH ;[AH] = latched (not current) flag
STRILP:
PUSH AX ;save current trigger id
ADD AL,LOW OFFSET STROFF
CALL EVADR ;[DX] points to event mask
MOV BX,DX ;[BX] points to event mask
TEST BYTE PTR 0[BX],LOW OFFSET T_ON ;see if STRIG(n) ON has been done
POP AX
PUSH AX
JE STRI1 ;don't call RDTRIG if event not enabled
CALL RDTRIG ;[AL]=0/1 if trig is not-pressed/pressed
OR AL,AL
JE STRI1 ;brif this trigger has not interrupted
POP AX ;restore [AL] = joystick trigger id
PUSH AX
ADD AL,LOW OFFSET STROFF ;[AL] = global event id
CALL EVTRP ;Signal the occurance of a TRIGGER EVENT
STRI1: POP AX
INC AL
CMP AL,LOW OFFSET NMSTRT
JB STRILP ;brif there are more triggers to poll
RET
SUBTTL DATE - Get/Set Date.
PUBLIC DATES,DATEF
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN DAYSPM:WORD
DSEG ENDS
;DATE$="[M]M/[D]D/[YY]YY" or "[M]M-[D]D-[YY]YY"USA Date format
;
DATES: CALL PRSDAT ;CX=year, DH=month, DL=day
JMP SETDAT ;set system date
;X$=DATE$ returns "YYYY-MM-DD" if KANABS&KANADT else "MM-DD-YYYY"
;
DATEF:
CALL CHRGTR ;skip DATE$
PUSH BX ;Save Text pointer
MOV AL,LOW 10D
CALL STRINI ;Get space for 10 char string
PUSH DX ;save adr of string
CALL GETDAT ;CX=year, DH=month, DL=day
POP BX ;[BX]=adr of string
SUB CX,1900D ;Reduce year by two digits
CMP CL,LOW 100D ;See if in 20th century
MOV CH,LOW 19D ;Setup 20th century in case
JB DATEF2 ;Brif so.
SUB CL,LOW 100D ;subtract into next century
INC CH ;21st century
DATEF2:
MOV AL,DH
CALL PUTCHR ;Store ascii month
MOV AL,LOW "-"
CALL PUTCH2 ;put separater
MOV AL,DL
CALL PUTCHR ;Store ascii day
MOV AL,LOW "-"
CALL PUTCH2 ;put separater
MOV AL,CH
CALL PUTCHR ;Store ascii century
MOV AL,CL
CALL PUTCHR ;Store ascii year.
JMP PUTNEW ;Put result and ret (Txt ptr on stack).
PUTCHR:
AAM ;Convert to unpacked BCD
XCHG AL,AH
OR AX,30060O ;Add "0" bias to both digits.
CALL PUTCH2
MOV AL,AH
PUTCH2:
MOV BYTE PTR 0[BX],AL ;store char in string
INC BX
RET
;PRSDAT parses a string containing
; "[YY]YY/MM/DD" if KANABS&KANADT else "MM/DD/[YY]YY"
; Exit - CX=year, DH=month, DL=day,
; [BX]=new text pointer. All other regs preserved.
;
PRSDAT:
CALL SYNCHR
DB OFFSET EQULTK ;Must be DATE$ = string
CALL FRMEVL
PUSH BX ;Save Text pointer
CALL FRESTR
MOV CL,BYTE PTR 0[BX] ;Save string len in [CL]
CMP CL,LOW 1 ;String must not be null
JB DATERR ;Brif null str
MOV SI,WORD PTR 1[BX] ;[SI] has addr of string.
MOV BL,CL ;Working reg for string len.
CALL GNUM8 ;[AX]=month
MOV DH,AL ;[DH] = month
CALL DATSEP ;skip / or -
CALL GNUM8 ;[AL]=day of month
MOV DL,AL ;[DL] = day
CALL DATSEP ;skip / or -
CALL GNUM16 ;[AX]=year (16 BITS)
CMP AX,1978D
JNB DATE2 ;branch if .GE. 1978
CMP AX,100D
JNB DATERR ;error if between 100 and 1977
CMP AX,78D
JNB DATE1 ;add 1900 if .GE. 78
ADD AX,100D ;add 2000 if .LE. 77
DATE1: ADD AX,1900D
DATE2:
CMP AX,2100D
JNB DATERR ;branch if year too large
MOV CX,AX ;CX=year
POP BX ;Text pointer
RET ;Exit.
;DATSEP checks for a date separator (- or /) and returns if found.
;
DATSEP:
OR BL,BL
JZ DATERR ;Error if string empty.
MOV AL,BYTE PTR 0[SI]
CMP AL,LOW "/"
JZ DIGITX
CMP AL,LOW "-"
JZ DIGITX ;branch if found
JMP SHORT DATERR
GNUM8: CALL GNUM16 ;[AX]=16-bit number
OR AH,AH
JNZ DATERR ;error if larger than 255
RET
GNUM16: PUSH CX ;save caller's [CX], [DX]
PUSH DX
MOV AX,0 ;initialize accumulator
GNUML: CALL DIGIT ;[CX]=0..9
JB GNUMX ;branch if not legal digit
MOV DX,10D
MUL DX ;[AX]=[AX]*10
JO DATERR ;branch if overflow
ADD AX,CX ;add in new digit
JMP SHORT GNUML
GNUMX: POP DX
POP CX
RET2: RET
DIGIT: CMP BL,LOW 1 ;End-of-string?
JB RET2 ;Brif END-OF-STRING
MOV CL,BYTE PTR 0[SI]
SUB CL,LOW "0"
JB RET2 ;branch if illegal digit
CMP CL,LOW 10D
CMC
JB RET2 ;branch if illegal digit
MOV CH,LOW 0 ;[CX]=digit
DIGITX: DEC BL ;Length -1
INC SI ;[SI] points to next byte in string
RET
DATERR: JMP FCERR
IFE IBMTOK OR CPM86
;DYOFYR converts YEAR, MONTH, DAY to binary DAY-OF-YEAR
; Entry - [CX]=binary year (19xx/20xx)
; [DH]=binary month (1..12)
; [DL]=binary day-of-month (1..31)
; Exit - [BX]=binary day of year (0..364/365)
; [CX] is preserved, all other registers are destroyed.
;
DYOFYR: SUB CX,1978D ;[CX]=year - 1978
DEC DL ;[DL]=day of month - 1
DEC DH ;[DH]=month - 1
MOV BL,DL
XOR BH,BH ;[BX]=day accumulator=day-of-month - 1
MOV AH,BH
MOV AL,DH ;[AX]=month - 1
MOV SI,AX ;[SI]=month - 1
CALL SETFEB ;DAYSPM(2)=28 or 29
CMP DH,LOW 12D
JNB DATERR ;error if month is too large
CMP DL,BYTE PTR DAYSPM[SI]
JNB DATERR ;error if day-of-month too large
OR SI,SI
MONTHL: JNZ MONTHS
RET
MONTHS: MOV AL,BYTE PTR DAYSPM-1[SI]
ADD BX,AX ;days=days+DAYSPM(month)
DEC SI
JMP SHORT MONTHL
;SETFEB sets DAYSPM(2) to 28 or 29 depending on year
;
SETFEB: CALL CKLEAP ;[AX]=1 if [CX]=leap-year
ADD AL,LOW 28D ;[AL]=29 if leap, 28 if not
MOV BYTE PTR DAYSPM+1,AL ;DAYSPM(2)=28 or 29
RET
;CKLEAP returns with [AX]=1 if [CX]+1978 is a leap year, else [AX]=0.
;
CKLEAP: MOV AH,LOW 0
MOV AL,CL
AND AL,LOW 3
SUB AL,LOW 2 ;[AX]=0 if leap year
JZ CKLEA1 ;branch if it is leap-year
MOV AL,LOW 377O
CKLEA1: INC AL
RET
ENDIF
IF CPM86
;SETDAT sets the system clock's date.
; Entry - [CX]=year (19xx/20xx)
; [DH]=month (1..12)
; [DL]=binary day-of-month (1..31)
; Exit - [BX] preserved. All other registers destroyed.
;
SETDAT: PUSH BX ;save BX
CALL GDTIME ;get current date/time into DATIME
CALL DYOFYR ;[BX]=day of year (0..364/365)
;[CX]=year - 1978
OR CX,CX ;test year
JZ YEARSX ;branch if 1978
JMP SHORT YEARS1
YEARSL: CALL CKLEAP ;[AX]=1 if CX is leap-year
ADD BX,AX ;days=days+1 if leap-lear
YEARS1: ADD BX,365D ;days=days+365
LOOPNZ YEARSL
YEARSX: MOV WORD PTR DATIME,BX ;DATIME=count of days since 1/1/1978
CALL SDTIME ;set current date/time from DATIME
POP BX ;restore text pointer
RET
;GETDAT returns with [CX]=year, DH=month, DL=day-of-month.
; Exit - BX, AX are used.
;
GETDAT: CALL GDTIME ;get current date/time into DATIME
MOV DX,WORD PTR DATIME ;[DX]=no of days since JAN 1,1978
MOV CX,0 ;years=0
FNDYR: CALL CKLEAP ;[AX]=1 if leap-year
ADD AX,365D ;[AX]=366 if leap-year
CMP DX,AX
JB GOTYR ;branch if CX=year
SUB DX,AX ;days=days-365 or 366
INC CX ;year=year+1
JMP SHORT FNDYR
GOTYR: CALL SETFEB ;set DAYSPM(2)=28 or 29
MOV BX,0
MOV AH,BH
FNDMON: MOV AL,BYTE PTR DAYSPM[BX] ;[AX]=days in month BX
INC BX
CMP DX,AX
JB GOTMON ;branch if BX is month
SUB DX,AX
JMP SHORT FNDMON
GOTMON: MOV DH,BL ;[DH]=month (1..12)
INC DL ;[DL]=day of month (1..31)
ADD CX,1978D ;[CX]=year
RET
ENDIF
SETDAT: PUSH BX
DOSIO SDAT ;Give Date to MS-DOS.
OR AL,AL ;Date OK?
JNZ DATERR ;Brif not.
POP BX
RET
;GETDAT returns with [CX]=year, DH=month, DL=day-of-month.
; Exit - BX, AX are used.
;
GETDAT: DOSIO GDAT ;Get Date from MS-DOS
RET
PAGE
SUBTTL TIME - Get/Set Time.
PUBLIC TIMES,TIMEF
EXTRN STRINI:NEAR,PUTNEW:NEAR,FCERR:NEAR
EXTRN CHRGTR:NEAR,SYNCHR:NEAR,GETYPR:NEAR,FRMEVL:NEAR,FRESTR:NEAR
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN DATIME:WORD,EQULTK:WORD
DSEG ENDS
; TIME$=[H]H[:[M]M[:[S]S[:[T]T]]]
;
TIMES: CALL PRSTIM ;CH=hour, CL=min, DH=sec, DL=.01sec
JMP SETTIM ;set system time
;X$=TIME$ returns "HH:MM:SS"
;
TIMEF:
CALL CHRGTR ;skip TIME$ (it was CPI'ed in FRMEVL)
PUSH BX ;Save Text pointer
MOV AL,LOW 8D
CALL STRINI ;Get space for 8 char string
PUSH DX ;Save addr of string
CALL GETTIM ;CH=hour, CL=min, DH=sec
POP BX ;Restore addr of String
MOV AL,CH
CALL PUTCHR ;Store ascii hours
MOV AL,LOW ":"
CALL PUTCH2
MOV AL,CL
CALL PUTCHR ;Store ascii minutes
MOV AL,LOW ":"
CALL PUTCH2
MOV AL,DH
CALL PUTCHR ;Store ascii seconds.
JMP PUTNEW ;Put result and ret (Txt ptr on stack).
EXTRN FMULT:NEAR,CONIA:NEAR,FRCSNG:NEAR,FADD:NEAR,PUSHF:NEAR
;PRSTIM parses a string containing "HH[:MM[:SS[.TT]]]
; Exit - CH=hours, CL=minutes, DH=seconds, DL=.01 secs,
; [BX]=new text pointer. All other regs preserved.
;
PRSTIM:
CALL SYNCHR
DB OFFSET EQULTK ;Must be TIME$ = string
CALL FRMEVL
PUSH BX ;Save Text pointer
CALL FRESTR
MOV CL,BYTE PTR 0[BX] ;Save string len in [CL]
CMP CL,LOW 1 ;String must not be null
JB TIMERR ;Brif null str
MOV SI,WORD PTR 1[BX] ;[SI] has addr of string.
MOV BL,CL ;Working reg for string len.
CALL GNUM8 ;[AX]=hours
CMP AL,LOW 24D
JNB TIMERR
MOV CH,AL ;[CH] = hours
CALL TIMSEP
CALL GNUM8
CMP AL,LOW 60D
JNB TIMERR
MOV CL,AL ;[CL] = minutes
CALL TIMSEP
CALL GNUM8
CMP AL,LOW 60D
JNB TIMERR
MOV DH,AL ;[DH] = seconds.
CALL TIMSEP
CALL GNUM8
CMP AL,LOW 100D
JNB TIMERR
MOV DL,AH ;[DL] = 100ths.
POP BX ;Text pointer
RET ;Exit.
TIMSEP:
OR BL,BL
JZ TIMSXX
DEC BL
CLD ;Set to increment
LODSB
CMP AL,LOW ":"
JZ TIMSXX
CMP AL,LOW "."
JNZ TIMERR
TIMSXX:
RET
TIMERR: JMP FCERR
IF CPM86
SETTIM: CALL GDTIME ;get current date/time into DATIME
PUSH BX ;save text pointer
MOV BX,OFFSET DATIME+2 ;BX points to hours digit of buffer
MOV AL,CH
CALL BINBCD ;DT.HRS=BINBCD(CH)
MOV AL,CL
CALL BINBCD ;DT.MIN=BINBCD(CL)
MOV AL,DH
CALL BINBCD ;DT.SEC=BINBCD(DH)
CALL SDTIME ;set current date/time from DATIME
POP BX ;restore text pointer
RET
;BINBCD converts [AL] to 2-digit BCD and stores the result at [BX]
; Exit - BX is incremented
;
BINBCD:
PUSH DX ;save caller's [DX]
MOV AH,LOW 0
MOV DL,LOW 10D
DIV DL ;[AL]=[AX]/10, [AH]=remainder
ADD AL,AL ;[AL]=1st digit * 2
ADD AL,AL ; * 4
ADD AL,AL ; * 8
ADD AL,AL ; * 16
ADD AL,AH ; + second digit
POP DX ;restore caller's [DX]
JMP PUTCH2
GETTIM: CALL GDTIME ;get current date/time into DATIME
MOV BX,OFFSET DATIME+2 ;BX points to hours digit of buffer
CALL BCDBIN
MOV CH,AL ;[CH]=BCDBIN(DT.HRS)
CALL BCDBIN
MOV CL,AL ;[CL]=BCDBIN(DT.MIN)
CALL BCDBIN
MOV DH,AL ;[DH]=BCDBIN(DT.SEC)
RET
;GDTIME sets DATIME to the current date-time.
; Exit - All registers preserved.
;
GDTIME: PUSH ES
PUSH AX
PUSH BX
PUSH CX
PUSH DX
CLD ;Because of Melco BIOS Bug
MOV DX,OFFSET DATIME
CPMXIO 155D ;CPM86 system call
JMP SHORT SDTIMX
;SDTIME sets the system clock to DATIME
; Exit - All registers preserved.
;
SDTIME: PUSH ES
PUSH AX
PUSH BX
PUSH CX
PUSH DX
MOV DX,OFFSET DATIME
CPMXIO 104D ;CPM86 system call
SDTIMX: POP DX
POP CX
POP BX
POP AX
POP ES
RET
;BCDBIN converts [[BX]] from 2-digit BCD to binary and
; returns it in [AL]
; Exit - BX is incremented, AH, DL are destroyed.
;
BCDBIN: MOV AL,BYTE PTR 0[BX]
INC BX
MOV AH,AL ;[AH]=copy of input parameter
AND AL,LOW 360O ;[AL]=D1*16 (most significant digit)
ROR AL,1 ;[AL]=D1*8
MOV DL,AL ;[DL]=D1*8
ROR AL,1 ;[AL]=D1*4
ROR AL,1 ;[AL]=D1*2
ADD AL,DL ;[AL]=D1*10
AND AH,LOW 17O ;[AH]=D2
ADD AL,AH ;[AL]=10*D1+D2=binary result
RET
ENDIF
SETTIM: PUSH BX
DOSIO STIM ;Give Time to MS-DOS.
OR AL,AL ;Date OK?
JNZ TIMERR ;Brif not.
POP BX
RET
GETTIM: DOSIO GTIM ;Get Time from MS-DOS
RET
SUBTTL Error Handlers for Features not supported in a version
EXTRN DERDNA:NEAR
JMP DERDNA ;Device unavailable error
PUBLIC PALETE
PALETE:
JMP SNERR
DSEG SEGMENT PUBLIC 'DATASG'
EXTRN ERRADV:WORD
DSEG ENDS
EXTRN ERROR:NEAR
PUBLIC TIMER
TIMER:
PUBLIC ERDEV
ERDEV:
PUBLIC IOCTL
IOCTL:
PUBLIC CHDIR
CHDIR:
PUBLIC MKDIR
MKDIR:
PUBLIC RMDIR
RMDIR:
PUBLIC SHELL
SHELL:
PUBLIC ENVIRON
ENVIRON:
PUBLIC VIEW
VIEW:
PUBLIC WINDOW
WINDOW:
PUBLIC PMAP
PMAP:
ADVERR:
MOV DL,LOW OFFSET ERRADV
JMP ERROR
CSEG ENDS
END