2023-11-16 18:19:54 -05:00

7569 lines
175 KiB
NASM

TITLE DIP/8086
PAGE 58,132
.LIST
;----------------------------------------------------------------------
; DIP/8086 Version 1.0
; D-CODE INTERPRETER PROGRAM
; FOR IBM-PC UNDER MS-DOS
;----------------------------------------------------------------------
; INFOCOM, INC.
; 125 CAMBRIDGE PARK DRIVE
; CAMBRIDGE, MA 02140
; COMPANY PRIVATE -- NOT FOR DISTRIBUTION
;----------------------------------------------------------------------
; M O D I F I C A T I O N H I S T O R Y
;----------------------------------------------------------------------
; Date | Programer | Description
;----------------------------------------------------------------------
DEBUG EQU 0
GMAJOR EQU 'C'
GMINOR EQU 0
; C PHG RENAMED GAME.DAT TO FOOBLITZ.DAT ... RELEASE VERSION
;
; B7 PHG FIXED SPEED-UP IN DRAW FOR LOW MEMORY.
;
; B6 PHG FIXED EYE-DROPPER (SEG CROSS IN GTGFST) AND VERIFY.
; PATCHES ARE STILL B5
; B5 PHG SECOND ROUND OF SPEED UP -- DRAW LOOP
;
; B4 PHG SPEED-UP: AFTER NARROWING DOWN PROBLEMS TO SPECIFIC
; AREAS OF CODE, OPTIMIZATION WAS ATTEMPTED
;
; B3 PHG ADDED TRACE TRAP (CONDITIONAL ASSEMBLY) TO FIND
; OUT WHERE THIS THING WASTES THE MOST TIME
;
; 9/09/85 | PHG | ADDED SAVE AND RESTORE (W/STACK AND
; | I-FILE IN SAVE) AND VERIFY
; 8/29/85 | PHG | FIXED PAGING BUG AND ENHANCED
; 7/18/85 | PHG | ADDED SUPPORT FOR NO GAME PORT
; | ADAPTER
; 6/5/85 | R. LAY | MODIFIED {GINPUT} TO ACCEPT EITHER
; | | KRAFT JOYSTICK BUTTON
; 5/28/85 | R. LAY | IMPLIMENTED {LOAD} AND {DUMP}
; 5/15/85 | R. LAY | MODIFIED {SENSE} AND SOUND ROUTINES
; | | FOR IBM AT COMPATABILITY
; 5/1/85 | R. LAY | 128K VERSION COMPLETE
; 3/4/85 | R. LAY | 64K VERSION FROZEN. START OF 128K.
; 12/31/84 | R. LAY | START 64K VERSION, NO PAGING.
SUBTTL NOTES
PAGE
;----------------------------------------------------------------------
;
; I M P L E M E N T A T I O N N O T E S
;
;----------------------------------------------------------------------
;
; VIRTUAL MEMORY ALLOCATION
;
; Virtual memory exists as a Data File on the game disk. Although two
; data files are created by the implimentor: GFILE.DAT and IFILE.DAT,
; (usually named filename.DIP and filename.PIC) these are concantenated
; while in the DOS command mode by issuing the command:
; CONCAT0 filename, if GFILE.DAT (or filename.DIP) file size is even
; CONCAT1 filename if GFILE.DAT file size is odd.
;
; Note that this scheme allows for easy inclusion of new data files through the
; use of the batch facilities. There is one caution: the filename portion of
; the data files must be the same. This may be accomplished by renaming one or
; both of the files or avoided by modifing the batch files.
;
; Each of the original data files consist of two parts: Preload and Pureload.
; For documentation purposes, these are called GPRE, GPURE, IPRE, and IPURE.
;
SUBTTL EQUATES
PAGE
;----------------------------------------------------------------------
; E Q U A T E S
;----------------------------------------------------------------------
; -----------
; ERROR CODES
; -----------
; 00 -- GAME PRELOAD TOO BIG
; 01 -- IMAGE PRELOAD TOO BIG
; 02 -- UNDEFINED X-OP
; 03 -- UNDEFINED 0-OP
; 04 -- UNDEFINED 1-OP
; 05 -- UNDEFINED 2-OP
; 06 -- G-STACK UNDERFLOW
; 07 -- G-STACK OVERFLOW
; 08 -- DIVISION BY ZERO
; 09 -- PURITY VIOLATION (PUT/PUTB)
; 0A -- DISK ACCESS RANGE
; 0B -- DISK ACCESS
; 0C -- NO CALL ADDRESS
; 0D -- UNDEFINED SOUND
; 0E -- PURITY VIOLATION (SETI/SWAPI)
; 0F -- ACTIVE ICON TABLE OVERFLOW
; --------------
; COMMON EQUATES
; --------------
FALSE EQU 0 ;STANDARD VALUES
TRUE EQU 0FFH
LO EQU 0
HI EQU 1
; -----------------------
; FILE CONTROL STRUCTURES
; -----------------------
RECSIZ EQU 512 ;512 BYTES/RECORD
; ----------------------------
; MS-DOS INT 21 FUNCTION CALLS
; ----------------------------
CRESET EQU 0H ;PROGRAM TERMINATE
CCONIN EQU 1H ;WAIT FOR CONSOLE INPUT WITH ECHO
CPROUT EQU 5H ;PRINTER OUTPUT
CCONIO EQU 6H ;CONSOLE INPUT NO ECHO NO WAIT
CNOECHO EQU 7H ;INPUT NO ECHO
CRDLIN EQU 0AH ;CONSOLE LINE INPUT
CDRESET EQU 0DH ;DISK RESET
CFCREAZ EQU 3CH ;CREATE FILE
CFOPENZ EQU 3DH ;OPEN FILE
CFCLOSZ EQU 3EH ;CLOSE FILE
CRDRNDZ EQU 3FH ;READ RANDOM RECORD
CWRRNDZ EQU 40H ;WRITE RANDOM RECORD
CFDELEZ EQU 41H ;DELETE FILE
CFSEEK EQU 42H ;SEEK
CSETBLK EQU 4AH
CSELDSK EQU 0EH ; SELECT DISK
CURDSK EQU 19H
; -----------------
; MEMORY ALLOCATION
; -----------------
LSTACK EQU 512 ;SIZE OF THE GSTACK AND MACHINE STACK IN BYTES
LMOUTB EQU 80 ;MAX LENGTH OF OUTBUF, EXCLUDING TERMINAL 0
LDOUTB EQU 80 ;DEFAULT LENGTH OF OUTBUF
; ---------------------
; G-CODE HEADER OFFSETS
; ---------------------
GHEADER EQU 64 ; LENGTH OF G-FILE HEADER IN BYTES
GVERS EQU 0 ; (BYTE) G-MACHINE VERSION
GMODE EQU 1 ; (BYTE) MODE BYTE
GID EQU 2 ; (WORD) GAME ID
GEND EQU 4 ; (WORDPTR) BEGINNING OF NON-PRELOADED CODE
GSTART EQU 6 ; (WORDPTR ODD) EXECUTION START ADDRESS
GPUR EQU 8 ; (WORDPTR) BEGINNING OF PURE G-CODE
GGLOB EQU 10 ; (WORDPTR) START OF GLOBAL VARIABLE TABLE
GSER EQU 12 ; (6 BYTES) ASCII SERIAL NUMBER
GLEN EQU 18 ; (WORD) LENGTH OF G-PROGRAM IN WORDS
GCHECK EQU 20 ; (WORD) G-CODE CHECKSUM
GINTVER EQU 22 ; INTERPRETER VERSION LETTER AND NUMBER
; ---------------------
; I-FILE HEADER OFFSETS
; ---------------------
IHEADER EQU 8 ; LENGTH OF I-FILE HEADER IN BYTES
ILEN EQU 0 ; (WORD) I-FILE LENGTH IN BYTES
IEND EQU 2 ; (WORD) END OF I-FILE PRELOAD
ICHECK EQU 4 ; (WORD) I-FILE CHECKSUM WORD
IBLKS EQU 6 ; (BYTE) # BLOCKSETS IN FILE
IICONS EQU 7 ; (BYTE) # ICONS IN FILE
; ---------------
; GENERAL EQUATES
; ---------------
ATTIME EQU 004FH ; TIME STANDARD FOR "FAST" PROCESSOR
NSNDS EQU 4 ; THERE ARE FOUR SOUND EFFECTS
JIFCNT EQU 800H ; NUMBER USED TO COMPENSATE DELAY FOR IBM AT'S
VID_SG EQU 0B800H ; VIDEO MEMORY LOCATION
BIOSDATA EQU 40H ; BIOS DATA AREA SEG
KB_FLAG EQU 17H ; BIOS DATA AREA OFFSET TO KEY FLAGS
NUMLOCK EQU 20H ; NUMLOCK BIT
BIOS EQU 0F000H ; BIOS SEGMENT
BIOSOFF EQU 0E000H ; BIOS OFFSET
ID_OFF EQU 0FFFEH ; OFFSET IN BIOS TO MACHINE ID
JR_ID EQU 0FDH ; ID BYTE FOR PCjr
DOSVER EQU 30H ; GET DOS VERSION NUMBER
SUBTTL MAIN PROGRAM
PAGE
;----------------------------------------------------------------------
; M A I N P R O G R A M
;----------------------------------------------------------------------
IF1 %OUT PASS1
ELSEIF %OUT PASS2
ENDIF
CSEG SEGMENT PARA PUBLIC
ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:CSEG
ORG 100H
;
PUBLIC MSDIP
MSDIP PROC
JMP START ;SKIP OVER DIP DATA
SUBTTL DATA DEFINITIONS
PAGE
;---------------------------------------------------------------------
; D A T A D E F I N I T I O N S
;---------------------------------------------------------------------
; ---------------
; TRADITIONAL I/0
; ---------------
; FILE CONTROL STRUCTURES
GAMFILE DB 'FOOBLITZ.DAT',0 ;ACIIZ FILE NAME DEFAULT DRIVE
DB 49 DUP (0) ;POTENTIAL LENGTH OF FILENAME
GAMHNDL DW 0 ;GAME FILE HANDLE
DATFILE DB 'GAME.SAV',0 ; ASCIIZ FILE NAME
DB 53 DUP (0) ;POTENTIAL LENGTH OF FILENAME, NO DEFAULT
DATHNDL DW 0 ;GAME FILE HANDLE
LASTSAV DB 'GAME.SAV',0 ; ASCIIZ FILE NAME
DB 53 DUP (0) ;POTENTIAL LENGTH OF FILENAME, NO DEFAULT
NAMBUF DB 20 DUP (0) ;FILE NAME BUFFER
; DRIVE STUFF
LASTDRV DB ? ; LAST LOGGED DRIVE
DSKDIR DB -1 ; SAVE OR RESTORE?
LSTDFLG DB ? ; LAST DRIVE FLAG
DEFDRV DB 0 ; DEFAULT (ORIGINAL) DRIVE
DRVFLG DB 0 ; USER SPECIFIED DRIVE?
CURDRV DB 0 ; WHERE WE ARE LOGGED CURRENTLY
SKPDRV DB 0 ; SKIP DRIVE PRINT OUT
; KEYBOARD HANDLING
KYFLGS DB ? ; ORIGINAL KEYBOARD FLAGS
XLKEY DB 37H,38H,39H,0,34H,0,36H,0,31H,32H,33H
;MESSAGES
ERRTAB DW OFFSET FERR0
DW OFFSET FERR1
DW OFFSET FERR2
DW OFFSET FERR3
DW OFFSET FERR4
DW OFFSET FERR5
DW OFFSET FERR6
DW OFFSET FERR7
DW OFFSET FERR8
DW OFFSET FERR9
DW OFFSET FERR10
DW OFFSET FERR11
DW OFFSET FERR12
DW OFFSET FERR13
DW OFFSET FERR14
DW OFFSET FERR15
DW OFFSET FERR16
DW OFFSET FERR17
DW OFFSET FERR18
;
FERR0 DB 20,"GAME PRELOAD TOO BIG$"
FERR1 DB 21,"IMAGE PRELOAD TOO BIG$"
FERR2 DB 14,"UNDEFINED X-OP$"
FERR3 DB 14,"UNDEFINED 0-OP$"
FERR4 DB 14,"UNDEFINED 1-OP$"
FERR5 DB 14,"UNDEFINED 2-OP$"
FERR6 DB 17,"G-STACK UNDERFLOW$"
FERR7 DB 16,"G-STACK OVERFLOW$"
FERR8 DB 16,"DIVISION BY ZERO$"
FERR9 DB 23,"PURITY VIOLATION (PUTB)$"
FERR10 DB 18,"LSEEK OUT OF RANGE$"
FERR11 DB 19,"GAME FILE NOT FOUND$"
FERR12 DB 15,"NO CALL ADDRESS$"
FERR13 DB 15,"UNDEFINED SOUND$"
FERR14 DB 23,"PURITY VIOLATION (SETI)$"
FERR15 DB 27,"ACTIVE ICON TABLE OVERFLOW$"
FERR16 DB 25,"CANNOT SET GRAPHICS MODE$"
FERR17 DB 21,"INCORRECT DOS VERSION$"
FERR18 DB 24,"INVALID GAME OR VERSION$"
;
ERRM DB 13,"FATAL ERROR: $"
XITMSG DB " END OF SESSION $"
SAV0 DB " Insert SAVE disk then enter file name.$"
SAV1 DB " (Default is $"
SAV2 DB "): $"
SAV3 DB " Insert game disk then strike any key to continue.$"
ERR1 DB " SAVE file not found$"
ERR4 DB " Unable to access SAVE file$"
ERR5 DB " No room on diskette for SAVE file$"
ERR6 DB " Read of SAVE file failed$"
WAITSTR DB " The game is loading,",13,10
DB " please wait...$"
STRIKE DB " Strike any key to continue...$"
; I/O BUFFERS
PUBLIC OUTBUF,INBUF,XFRBUF
OUTBUF DB 40 DUP(?) ;OUTPUT BUFFER
INBUF DB 80 DUP(?) ;INPUT BUFFER
XFRBUF DB RECSIZ DUP (?) ;TRANSFER BUFFER USED BY {LOAD} AND {SAVE}
; SPECIAL CONTROL
PUBLIC IBMAT,DLYCNT,TIMER
PRTOFF DW ? ; (B5) PRINT SCREEN ENTRY POINTS
PRTSEG DW ?
PCJR DB 0 ; JR FLAG FOR TIMING
IBMSTR DB "COPR. IBM$" ; IBM IDENTIFICATION STRING
IBMAT DB (?) ;IS THIS AN IBM AT (TRUE OR FALSE)?
DLYCNT DW (?) ;DELAY REQUEST IN 1/10 SEC
TIMER DB (?) ;1/100 SEC COUNT FROM REAL TIME CLOCK
; USED IN DETERMINING 1/10 SEC DELAY
IF DEBUG
public itrcnt
itrcnt dw 2 dup (0)
public ipcount
ipcount dd 256 dup (0) ;pc position count -trace trap
public isavecs,ishift,ioffset,itop
isavecs dw 0 ;saved code seg paragraph
itrapcs dw 0
ishift dw 0 ;4 ;8 ;3
ioffset dw 0 ;mloop ;0 ;mloop
itop dw OFFSET MEMBUF ;imend ;-1 ;imend
t_out db 'tracer.out',0
;
;
ENDIF
PAGE
; -----------------------
; OP-CODE DISPATCH TABLES
; -----------------------
; 0-OP DISPATCH TABLE
OPT0 DW GNOOP ; B0
DW GRTRUE ; B1
DW GRFALS ; B2
DW GRSTAK ; B3
DW GFSTAK ; B4
DW GQUIT ; B5
DW GCOPYP ; B6
DW GVERP ; B7
NOPS0 EQU ($-OPT0)/2 ; # OF VALID 0-OPS
; 1-OP DISPATCH TABLE
OPT1 DW GPUSH ; 80
DW GPOP ; 81
DW GVALUE ; 82
DW GINC ; 83
DW GDEC ; 84
DW GZEROP ; 85
DW GBNOT ; 86
DW GJUMP ; 87
DW GRET ; 88
NOPS1 EQU ($-OPT1)/2 ; # OF VALID 1-OPS
; 2-OP DISPATCH TABLE
OPT2 DW BADOP2 ; 0 -- UNDEFINED
DW GADD ; 1
DW GSUB ; 2
DW GMUL ; 3
DW GDIV ; 4
DW GMOD ; 5
DW GBAND ; 6
DW GBIOR ; 7
DW GBXOR ; 8
DW GBITSP ; 9
DW GEQP ; A
DW GLESSP ; B
DW GDLESP ; C
DW GGRTRP ; D
DW GIGRTP ; E
DW GSET ; F
DW GGET ; 10
DW GGETB ; 11
NOPS2 EQU ($-OPT2)/2 ; # VALID 2-OPS
; X-OP DISPATCH TABLE
OPTX DW GCALL ; E0
DW GPUT ; E1
DW GPUTB ; E2
DW GINPUT ; E3
DW GSHOWI ; E4
DW GSETI ; E5
DW GSWAPI ; E6
DW GSOUND ; E7
DW GRAND ; E8
DW GCLEAR ; E9
DW GSHOWN ; EA
DW GWIND ; EB
DW GITER ; EC
DW GLOAD ; ED
DW GDUMP ; EE
DW GRESTR ; EF
DW GSAVE ; F0
NOPSX EQU ($-OPTX)/2 ; # VALID X-OPS
PAGE
; -----------------
; PROGRAM VARIABLES
; -----------------
; GCODE PROGRAM COUNTER DEFINITION
PUBLIC GPC,GPC2,GPC1,GPC0
GPC LABEL WORD
GPC2 DB 0 ;LSB OF GPC
GPC1 DB 0 ;MSB OF GPC
GPC0 DB 0 ;BIT 0 OF BYTE IS BIT 0 OF GPC
; VIRTUAL MEMORY POINTER DEFINITION
PUBLIC VMP,VMP2,VMP1,VMP0,VMSAV,VMSAV0
VMP LABEL WORD
VMP2 DB 0 ;LSB OF VMP
VMP1 DB 0 ;MSB OF VMP
VMP0 DB 0 ;BIT 0 OF BYTE IS BIT 0 OF VMP
VMSAV DW 0
VMSAV0 DB 0
; VALUES USED IN COLDSTART
PUBLIC GPRBLK,IPRBLK,GCODE,GPEND,ICODE,ISTRT,IPURE,ZORKID
GPRBLK DB 0 ;TOTAL NUMBER OF G-PRELOAD BLOCKS
IPRBLK DB 0 ;TOTAL NUMBER OF I-PRELOAD BLOCKS
GCODE DW 0 ;(WORD) VIRTUAL START ADDRESS OF G-CODE
GPEND DB 0 ;(BYTE) LAST VIRTUAL BLOCK OF G-PRELOAD
ICODE DW 0 ;(WORD) ABSOLUTE START ADDRESS OF I-PRELOAD
ISTRT DB 0 ;(BYTE) 1ST. V-PAGE OF I-PRELOAD
IPURE DW 0 ;(WORD) ABSOLUTE START ADDRESS OF I-PURELOAD
ZORKID DW ? ; GAME ID
; TABLE ADDRESSES
PUBLIC GLOCS,GLOBAL
GLOCS DW 0 ;OFFSET TO THE START OF THE DIP LOCAL TABLE
GLOBAL DW 0 ;(WORD) ABSOLUTE GLOBAL TABLE ADDR
; OPCODE SUPPORT
PUBLIC NARGS,ARG1,ABYTE,ADEX
NARGS DB 0 ;(BYTE) CURRENT NUMBER OF ARGUMENTS
ARG1 DW 0 ;(WORD) ARGUMENT 1 REGISTER
ARG2 DW 0 ;(WORD) ARGUMENT 2 REGISTER
ARG3 DW 0 ;(WORD) ARGUMENT 3 REGISTER
ARG4 DW 0 ;(WORD) ARGUMENT 4 REGISTER
ABYTE DB 0 ;(BYTE) X-OP ARGUMENT BYTE
ADEX DB 0 ;(BYTE) X-OP ARGUMENT INDEX
ADEX2 DB 0 ;(BYTE) "CALL" ARG-BYTE INDEX
PUBLIC CTYPES,CARGS
CTYPES DB 16 DUP(?) ;GCALL 'MODE BYTE' LIST
CARGS DW 16 DUP(?) ;GCALL ARGUMENTS LIST
PUBLIC OPCODE,VALUE,I,J,OLDGSP
OPCODE DB 0 ;(BYTE) OPCODE SAVE REGISTER
VALUE DW 0 ;(WORD) VALUE RETURN REGISTER
I DW 0 ;(WORD) GENERAL PURPOSE INDEX REGISTER #1
J DW 0 ;(WORD) GENERAL PURPOSE INDEX REGISTER #2
OLDGSP DW 0 ;(WORD) OLD G-STACK POINTER
; RANDOM
RSEED1 DW 0 ;VARIABLES USED FOR RANDOM NUMBER GENERATION
RSEED2 DW 0
RTEMP DW 0
; INPUT CONTROL
PUBLIC STICKA,KEYSAV,JOYSTK
STICKA DB 0 ;JOYSTICK FLAGS BYTE
KEYSAV DB 0 ;SENSE RETURN CODE
JOYSTK DB 0 ;JOYSTICK PRESENCE
; SOUND CONTROL STRUCTURES
STABLE DW SND1,SND2,SND3,SND4
PAGE
; ---------------------------
; GRAPHICS CONTROL STRUCTURES
; ---------------------------
; TABLE ADDRRESSES
PUBLIC BTAB,ITAB
BTAB DW 0 ;(WORD) ABSOLUTE BLOCKSET TABLE ADDRESS
ITAB DW 0 ;(WORD) ABSOLUTE ICON TABLE ADDRESS
; ICON DATA
PUBLIC NBLOKS,NICONS,IADR1,IADR10,IX1,IY1,XDEX1,YDEX1,BSET
NBLOKS DB 0 ;(BYTE) # BLOCKSETS IN I-FILE
NICONS DB 0 ;(BYTE) # ICONS IN I-FILE
IADR1 DW 0 ;(WORD) ABS ADDR OF ICON #1
IADR10 DB 0 ;(BYTE) LS BIT OF ABS ADDR OF ICON #1
IADR2 DW 0 ;(WORD) ABS ADDR OF ICON #2
IADR20 DB 0 ;(BYTE) LS BIT OF ABS ADDR OF ICON #2
IX1 DB 0 ;(BYTE) XSIZE OF ICON #1
IX2 DB 0 ;(BYTE) XSIZE OF ICON #2
IY1 DB 0 ;(BYTE) YSIZE OF ICON #1
IY2 DB 0 ;(BYTE) YSIZE OF ICON #2
XDEX1 DB 0 ;(BYTE) X-INDEX #1
XDEX2 DB 0 ;(BYTE) X-INDEX #2
YDEX1 DB 0 ;(BYTE) Y-INDEX #1
YDEX2 DB 0 ;(BYTE) Y-INDEX #2
BSET DB 0 ;(BYTE) BLOCKSET ID (1-255)
BSADR DW 0 ;(WORD) ABS ADDR OF CURRENT BLOCKSET
BSIZE DB 0 ;(BYTE) # BLOCKS IN CURRENT BLOCKSET
TOX DB 0 ;(BYTE) X-SAVE FOR "DUMP"
BLOCK DB 8 DUP (?) ;(8 BYTES) IMAGE BLOCK BUFFER
PUBLIC BLOCK,I_EOP,G_EOP
I_EOP DW ? ; (B5) ICON END OF PAGE
G_EOP DW ? ; (B5) GRAPHICS EOP
; MASKING
PUBLIC MSKFLG,MBLOCK
MBLOCK DB 8 DUP (?) ;(8 BYTES) MASK BLOCK BUFFER
MSKFLG DB 0 ;(BYTE) FLAG FOR MASKING 00=DISABLED
PUBLIC NEGATE,BLKLEN,XPSAV
XPSAV DW 0 ;(WORD) X-POSITION SAVE
XCURS DB 0 ;(BYTE) GAME CURSOR X-POS
YCURS DB 0 ;(BYTE) GAME CURSOR Y-POS
NEGATE DB 0 ;(BYTE) BLOCK NEGATE FLAG
BLKLEN DB 0 ;(BYTE) # BLOCKS PER BLOCK
; ITERATIONS
ITERS DB 0 ;(BYTE) # OF ICON ITERATIONS
DOITER DB 0 ;(BYTE) CURRENT ITERATION
ITPNT DB 0 ;(BYTE) POINTER INTO CURRENT ICON TABLE
NAICN DB 0 ;(BYTE) NUMBER OF ACTIVE ICONS
ITICN DB 0 ;(BYTE) COUNTER FOR ITERATE ROUTINE
; WINDOWS
PUBLIC XPOS,YPOS
XPOS DW 0 ;(WORD) ICON X-POSITION
YPOS DW 0 ;(WORD) ICON Y-POSITION
WINDX1 DB 0 ;(BYTE) LEFT CLIP VALUE
WINDX2 DB 0 ;(BYTE) RIGHT CLIP VALUE
WINDY1 DB 0 ;(BYTE) TOP CLIP VALUE
WINDY2 DB 0 ;(BYTE) BOTTOM CLIP VALUE
TOPCUT DB 0
SIDCUT DB 0
MDXCUT DB 0
WINDH DB 0
WINDW DB 0
IXSKIP DB 0
PUBLIC VDPADR
VDPADR DW 0 ;OFFSET INTO VIDEO RAM
PAGE
;
; This table is used to translate a Byte of graphics data into a Word of
; IBM graphics data.
;
; The IBM has a screen composed of 320 x 200 pixels. For the sake of
; convenience we use only 320 x 192. In Graphics Mode 4, the IBM utilizes
; 2 Bits/pixel.
;
; BIT 7 6 5 4 3 2 1 0
; _____________________________________
; | C1 C0 | C1 C0 | C1 C0 | C1 C0 |
; +--------+--------+--------+--------+
;
; With the color pallete and background color we've selected, eack Bit pair
; can represent 1 of 4 possible colors.
;
; C1 | C0 | COLOR
; ---+----+-----------
; 0 | 0 | BLACK
; 0 | 1 | CYAN
; 1 | 0 | MAGENTA
; 1 | 1 | WHITE
;
; Since the IBM has twice the horizontal resolution of the GRAPHIC DATA, we
; have to double each pixel. This causes the loss of cyan and magenta.
; Since the GRAPHIC DATA achieves color through archiving, the following
; scheme suffices.
;
; Given: A Byte of graphics data representing 8 pixels eg.:
;
; 00011011B (01BH)
;
; BIT 7 6 5 4 3 2 1 0
; 0 0 0 1 1 0 1 1
; PIXEL 1 2 3 4 5 6 7 8
;
; Becomes a Word of IBM graphics data representing 8 pixels eg.:
;
; 00000011 11001111B (03CFH)
;
; BIT F E | D C | B A | 9 8 | 7 6 | 5 4 | 3 2 | 1 0
; 0 0 | 0 0 | 0 0 | 1 1 | 1 1 | 0 0 | 1 1 | 1 1
; PIXEL 1 2 3 4 5 6 7 8
;
;
; Because all color is produced by artifacting, and the ATARI effect is
; different than the IBM we also right shift the word around 2 bits in
; advance for phase shifting purposes
;
PAGE
; GRAPHICS TRANSLATION TABLE
PUBLIC GRXTBL
GRXTBL DW 0000H,0C000H,0003H,0C003H
DW 000CH,0C00CH,000FH,0C00FH
DW 0030H,0C030H,0033H,0C033H
DW 003CH,0C03CH,003FH,0C03FH
DW 00C0H,0C0C0H,00C3H,0C0C3H
DW 00CCH,0C0CCH,00CFH,0C0CFH
DW 00F0H,0C0F0H,00F3H,0C0F3H
DW 00FCH,0C0FCH,00FFH,0C0FFH
DW 0300H,0C300H,0303H,0C303H
DW 030CH,0C30CH,030FH,0C30FH
DW 0330H,0C330H,0333H,0C333H
DW 033CH,0C33CH,033FH,0C33FH
DW 03C0H,0C3C0H,03C3H,0C3C3H
DW 03CCH,0C3CCH,03CFH,0C3CFH
DW 03F0H,0C3F0H,03F3H,0C3F3H
DW 03FCH,0C3FCH,03FFH,0C3FFH
DW 0C00H,0CC00H,0C03H,0CC03H
DW 0C0CH,0CC0CH,0C0FH,0CC0FH
DW 0C30H,0CC30H,0C33H,0CC33H
DW 0C3CH,0CC3CH,0C3FH,0CC3FH
DW 0CC0H,0CCC0H,0CC3H,0CCC3H
DW 0CCCH,0CCCCH,0CCFH,0CCCFH
DW 0CF0H,0CCF0H,0CF3H,0CCF3H
DW 0CFCH,0CCFCH,0CFFH,0CCFFH
DW 0F00H,0CF00H,0F03H,0CF03H
DW 0F0CH,0CF0CH,0F0FH,0CF0FH
DW 0F30H,0CF30H,0F33H,0CF33H
DW 0F3CH,0CF3CH,0F3FH,0CF3FH
DW 0FC0H,0CFC0H,0FC3H,0CFC3H
DW 0FCCH,0CFCCH,0FCFH,0CFCFH
DW 0FF0H,0CFF0H,0FF3H,0CFF3H
DW 0FFCH,0CFFCH,0FFFH,0CFFFH
DW 3000H,0F000H,3003H,0F003H
DW 300CH,0F00CH,300FH,0F00FH
DW 3030H,0F030H,3033H,0F033H
DW 303CH,0F03CH,303FH,0F03FH
DW 30C0H,0F0C0H,30C3H,0F0C3H
DW 30CCH,0F0CCH,30CFH,0F0CFH
DW 30F0H,0F0F0H,30F3H,0F0F3H
DW 30FCH,0F0FCH,30FFH,0F0FFH
DW 3300H,0F300H,3303H,0F303H
DW 330CH,0F30CH,330FH,0F30FH
DW 3330H,0F330H,3333H,0F333H
DW 333CH,0F33CH,333FH,0F33FH
DW 33C0H,0F3C0H,33C3H,0F3C3H
DW 33CCH,0F3CCH,33CFH,0F3CFH
DW 33F0H,0F3F0H,33F3H,0F3F3H
DW 33FCH,0F3FCH,33FFH,0F3FFH
DW 3C00H,0FC00H,3C03H,0FC03H
DW 3C0CH,0FC0CH,3C0FH,0FC0FH
DW 3C30H,0FC30H,3C33H,0FC33H
DW 3C3CH,0FC3CH,3C3FH,0FC3FH
DW 3CC0H,0FCC0H,3CC3H,0FCC3H
DW 3CCCH,0FCCCH,3CCFH,0FCCFH
DW 3CF0H,0FCF0H,3CF3H,0FCF3H
DW 3CFCH,0FCFCH,3CFFH,0FCFFH
DW 3F00H,0FF00H,3F03H,0FF03H
DW 3F0CH,0FF0CH,3F0FH,0FF0FH
DW 3F30H,0FF30H,3F33H,0FF33H
DW 3F3CH,0FF3CH,3F3FH,0FF3FH
DW 3FC0H,0FFC0H,3FC3H,0FFC3H
DW 3FCCH,0FFCCH,3FCFH,0FFCFH
DW 3FF0H,0FFF0H,3FF3H,0FFF3H
DW 3FFCH,0FFFCH,3FFFH,0FFFFH
PAGE
;
; Y-AXIS TRANSLATION TABLE
;
; This table is used to translate the starting row location (0-23) of
; a BLOCK to an offset from the VIDEO_RAM base address.
PUBLIC YAXTBL
; Y Axis (X)translation TaBLe
YAXTBL DW 1FB0H,20F0H,2230H,2370H
DW 24B0H,25F0H,2730H,2870H
DW 29B0H,2AF0H,2C30H,2D70H
DW 2EB0H,2FF0H,3130H,3270H
DW 33B0H,34F0H,3630H,3770H
DW 38B0H,39F0H,3B30H,3C70H
PAGE
; -------------------------
; PAGING CONTROL STRUCTURES
; -------------------------
PUBLIC FSIZE,BUFFERS,SEG0,SEG1,BUF,LRU,LOC,LADD,VPAGEN,OLDMRU,MEMMAP
FSIZE DB 0 ;NUMBER OF 512 BYTE BLOCKS IN GAME.DAT FILE
BUFFERS DB 0 ;NUMBER OF 512 BYTE BLOCKS IN PAGING AREA
SEG0 DW ? ;FIRST (OR ZERO) GAME SEGMENT
SEG1 DW ? ;SECOND GAME SEGMENT (SEG0 + 64K)
BUF DB 0 ;INDEX INTO MEMBUF
LRU DB 0 ;LEAST RECENTLY USED PAGE
LOC DB 0 ;HIGH ORDER ABSOLUTE ADDRESS BITS
LADD DW 0 ;LOW ORDER ABSOLUTE ADDRESS BITS
VPAGEN DB 0 ;VIRTUAL PAGE NUMBER
OLDMRU DB 0 ;PREVIOUS MRU BUFFER NUMBER
MEMMAP DB 512 DUP(?) ;PAGING MEMORY MAP
; -----------------
; STACK DEFINITIONS
; -----------------
PUBLIC MSTK_TP,MSTK_BT,GSTK_TP,GSTK_BT
EVEN
MSTK_BT DW LSTACK DUP(?) ;MACHINE STACK BOTTOM
MSTK_TP LABEL WORD ;MACHINE STACK TOP
GSTK_BT DW LSTACK DUP(?) ;G-CODE INTERPRETER STACK BOTTOM
GSTK_TP LABEL WORD ;G-CODE INTERPRETER STACK TOP
%OUT CODE
SUBTTL SYSTEM INITIALIZATION
PAGE
;----------------------------------------------------------------------
; S Y S T E M I N I T I A L I Z A T I O N
;----------------------------------------------------------------------
PUBLIC START
START: MOV SP,OFFSET MSTK_TP ;SETUP STACK PTR (SP)
MOV DI,OFFSET GSTK_TP ;SETUP GSTACK PTR (DI)
; JUMP TO BEGINNING OF DIP
JMP DIPBGN
SUBTTL 0-OP EXECUTORS
PAGE
;----------------------------------------------------------------------
; S U B R O U T I N E S
;----------------------------------------------------------------------
;---------------------------------------
; OPCODE EXECUTION PROCEDURES
;---------------------------------------
;---------------
; 0-OP EXECUTORS
;---------------
; ----
; NOOP
; ----
; NOOP (NO OPERATION)
PUBLIC GNOOP
GNOOP PROC
RET ;DO NOTHING
GNOOP ENDP
; -----
; RTRUE
; -----
; DO A "RETURN 1", WHERE 1 IS COMMONLY INTERPRETED AS TRUE.
PUBLIC GRTRUE,GRT2
GRTRUE PROC
MOV AX,1 ;RETURN A 1
GRT2: MOV ARG1,AX
JMP GRET ;RETURN VIA GRET
GRTRUE ENDP
; ------
; RFALSE
; ------
; DO A "RETURN 0", WHERE 0 IS COMMONLY INTERPRETED AS FALSE.
PUBLIC GRFALS
GRFALS PROC
SUB AX,AX ;RETURN A 0
JMP GRT2
GRFALS ENDP
; ------
; RSTACK
; ------
; "RETURN" WITH VALUE FROM GSTACK
PUBLIC GRSTAK
GRSTAK PROC
CALL POPVAL ;GET VALUE INTO AX
JMP GRT2
GRSTAK ENDP
; ------
; FSTACK
; ------
; FLUSH THE TOP VALUE OFF THE GSTACK
PUBLIC GFSTAK
GFSTAK PROC
JMP POPAXG
GFSTAK ENDP
; -----------------------
; POP AX FROM THE GSTACK
; -----------------------
PUBLIC POPAXG,GSTKU
POPAXG PROC
XCHG SP,DI ;USE THE REAL SP
POP AX ;POP THE STACK
XCHG SP,DI ;SWAP BACK
PUSH AX ;SAVE REGISTER
MOV AX,OFFSET GSTK_TP ;CHECK IF WE HAVE AN UNDERFLOW
CMP DI,AX ;
JA GSTKU ; IF WE DO IT'S FATAL
POP AX ; ELSE, RESTORE THE REGISTER
RET
; *** ERROR #6: G-STACK UNDERFLOW ***
GSTKU: POP AX
MOV AL,6
JMP GERROR
POPAXG ENDP
; ----
; QUIT
; ----
; THE GAME SHOULD DIE PEACEFULLY
; (MACHINE DEPENDENT)
PUBLIC GQUIT
GQUIT PROC
JMP FINISH
GQUIT ENDP
; -----
; COPYP
; -----
; *** UNABLE TO FIND IN THE DOCUMENTATION ***
PUBLIC GCOPYP
GCOPYP PROC
JMP PREDS
GCOPYP ENDP
; ----
; VERP
; ----
; *** UNABLE TO FIND IN THE DOCUMENTATION ***
PUBLIC GVERP,GVER1,GVER2,GVER3,GVER4,GVER5,GVER6,GVER7,GVER8
PUBLIC GVER$,GVER$$,GVERA,GVERB,GVERC,GVERD,GVERE
GVERP PROC
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH BP
PUSH DS
PUSH ES
; RESET THE FILE POINT TO BEGINNING OF THE FILE (LESS THE HEADER)
MOV BX,GAMHNDL ; FILE HANDLE
MOV CX,0
MOV DX,64 ; OFFSET IN THE FILE
MOV AX,4200H ; SEEK
INT 21H
MOV VMP,0 ; GET CHECK SUM VALUE
MOV VMP0,0
MOV AX,GCHECK ; OFFSET IN HEADER
CALL ADDVMP
CALL GW@VMP ; GET THE WORD THERE
MOV J,AX ; SAVE IN GENERAL VAR J
;
MOV AX,ICODE
MOV VMP,AX ; GET I-FILE CHECK SUM
MOV VMP0,0
MOV AX,ICHECK ; OFFSET IN HEADER
CALL ADDVMP
CALL GW@VMP ; GET THE WORD THERE
MOV I,AX ; SAVE I-CHECKSUM IN VARIABLE I
MOV BP,0 ; ZERO THIS BABY TO HOLD CURRENT CHECKSUMS
;
MOV VMP,0 ; GET LENGTH OF G-FILE
MOV VMP0,0
MOV AX,GLEN ; OFFSET IN HEADER
CALL ADDVMP
CALL GW@VMP ; GET THE WORD THERE
MOV DI,AX ; SAVE IT
SUB DI,GHEADER/2 ; SUBTRACT THE NUMBER OF BYTES IN HEADER
; FIGURE OUT HOW MUCH SPACE WE HAVE BETWEEN THE END OF PRELOAD
; AND THE END OF THE FIRST 64K BOUNDARY. WE WILL USE THAT SPACE
; AS A BUFFER FOR VERIFY. FIRST WE MUST ZERO OUT THE MEMMAP THAT
; INDICATE IF THOSE PAGES ARE IN CORE AND RESET THERE MEMBUF
; DESCRIPTORS. WE MUST ALSO PLAY WITH THE LRU POINTERS TO NOT
; SCREW UP THE MEMORY MAP.
MOV DX,SEG0 ; START OUT USING 0 SEG
MOV BL,BUFFERS ; HOW MANY POTENTIAL VERIFY BUFFERS?
CMP BL,080H ; NO MORE THAN 64K
JB GVER1
SUB BL,80H ; REDUCE IT 64K OR LESS
MOV DX,SEG1 ; SWITCH TO 1ST SEG
MOV BH,0 ; ZERO TOP HALF
PUSH BX ; SAVE NUMBER OF BUFFERS
JMP GVERE ; CLEAN OUT MEMBUFS
; ZERO BUFFERS IN SEG 0
GVER1: MOV AH,GPRBLK ; GET TOTAL NUMBER OF PRELOAD BLOCKS
ADD AH,IPRBLK ; TO FIGURE BUFFER BOUNDARY
SUB BL,AH ; FIGURE --> BUFFERS - PRELOAD
PUSH BX
MOV CX,BX ; NUMBER OF BUFFERS DESC'S TO CLEAN OUT
MOV BX,OFFSET MEMBUF+1 ; GET ADDRESS OF FIRST BUFFER
; MEMBUF ZEROING LOOP
;
GVERA: MOV BYTE PTR [BX],0 ; SET NO PAGE IN MEMORY FOR THIS BUFFER
ADD BX,4 ; NEXT BUFFER
LOOP GVERA ; CLEAN 'EM ALL OUT
MOV CX,0FFH ; NOW, CLEAN OUT THE MEMORY MAP
MOV BX,0 ; START AT THE BEGINNING
GVERB: TEST MEMMAP[BX],1 ; PRELOAD?
JZ GVERC ; NOPE, ZERO IT
GVERD: ADD BX,2 ; YES, SO SKIP THIS ONE
LOOP GVERB ; AND CHECK THE NEXT
JMP GVERE
GVERC: CMP DX,SEG0 ; WHICH SEG?
JE GVERC$
JMP GVERD$
GVERC$: CMP MEMMAP[BX+1],7FH ; IF BUFFER NUMBER < 127, THEN 0 IT
JA GVERD
GVERD$: MOV MEMMAP[BX+1],0 ; INDICATE PAGE NOT IN CORE
JMP GVERD ; INCREMENT AND CONTINUE
;
GVERE: POP BX
MOV CL,9
SHL BX,CL ; CONVERT BUFFERS INTO BYTES
MOV CX,BX ; SAVE NUMBER OF BYTES IN BUFFER
MOV ES,DX ; SET ES TO ADDRESS PROPER SEG
CMP DX,SEG0 ; WHICH SEG?
JNE GVERF
MOV DH,GPRBLK ; FIGURE BUFFER STARTING POINT
ADD DH,IPRBLK ; BY ADDING ALL PRELOAD BLOCKS
MOV DL,0 ; CLEAR OUT BOTTOM GARBAGE
SHL DX,1 ; CONVERT WORDS TO BYTE OFFSET
JMP GVERG
GVERF: MOV DX,0
;
GVERG: MOV SI,0
MOV BX,GAMHNDL ; GET FILE HANDLE
PUSH DS ; MAKE DS ADDRESS GAME SEG
PUSH ES
POP DS
GVER2: MOV AH,CRDRNDZ ; READ THE G-FILE PART
INT 21H
CALL CHKSUM ; PERFORM CHECK SUM ON BUFFER
CMP SI,DI ; HAVE WE CHECK THE WHOLE THING?
JE GVER3
JMP GVER2
;
GVER3: POP DS ; RESTORE DS
CMP BP,J ; CORRECT?
JE GVER4
JMP GVER$
; SEEK TO START OF I-FILE IN DAT FILE
GVER4: PUSH CX ; SAVE BUFFER SIZE
PUSH DX ; AND LOCATION
MOV DX,DI ; GET LENGTH OF G-FILE
ADD DX,GHEADER/2 ; ADD IN PREVIOUSLY SUBBED HEADER
ADD DX,IHEADER/2 ; ALSO SKIP I-FILE HEADER
MOV CX,0 ; CLEAR OUT FOR SEEK
INC DX ; (B6) MAKE UP FOR KERMIT FROM DEC20
GVER4A: SHL DX,1 ; CONVERT WORD LENGTH TO BYTES
ADC CX,0 ; GET 64K BIT IF THERE IS ONE
MOV AX,4200H ; SEEK
INT 21H ; DO IT
POP DX
POP CX
; NOW FILE POINTER IS READER TO READ I-PURE FOR VERIFY
; FIGURE OUT HOW MANY BYTES IN I-FILE
PUSH ES ; SAVE SEG BEING USED
MOV AX,ICODE
MOV VMP,AX ; GET LENGTH OF I-FILE OUT OF I-HEADER
MOV VMP0,0 ; SET VMP
MOV AX,ILEN
CALL ADDVMP
CALL GW@VMP ; GET THE WORD
MOV DI,AX ; SAVE LENGTH
SUB DI,IHEADER/2 ; CHECK FILE LESS HEADER
MOV BP,0 ; ZERO THIS BABY AGAIN
MOV SI,0
POP ES ; RESTORE SEG BEING USED
PUSH DS
PUSH ES
POP DS ; MAKE DS ADDRESS THE GAME SEG
;
GVER5: MOV AH,CRDRNDZ ; READ THE I FILE
INT 21H ; DO IT
CALL CHKSUM ; PERFORM CHECK SUM ON BUFFER
CMP SI,DI ; HAVE WE CHECK THE WHOLE THING?
JE GVER6
JMP GVER5
GVER6: POP DS ; RESTORE DS
CMP BP,I ; COMPARE CHECKSUMS
JE GVER7
JMP GVER$
GVER7: CLC ; INDICATE SUCCESS
GVER8: PUSHF
CMP FSIZE,0 ; (B5) DID WE USE THE HIGH SEG
JE GVER9
MOV AX,4200H ; (B5) SEEK FROM BEGINNING OF FILE
MOV CX,1 ; (B5) TO 64K LOCATION
PUSH DS ; (B6) SET TO UPPER
MOV DX,SEG1 ; (B6) SEGMENT FOR RELOAD
MOV DS,DX ; (B6) RESTORE DX
MOV DX,0
INT 21H
MOV DX,0 ; (B5) READ IN SECOND 64K
MOV CX,0FFFFH ; (B5) THAT MUCH
MOV AH,CRDRNDZ ; (B5) READ FUNCTION
INT 21H
POP DS
GVER9: POPF
POP ES ; RESTORES
POP DS
POP BP
POP DI
POP SI
POP DX
POP CX
POP BX
JC GVER$$ ; FAIL ON CARRY
JMP PREDS ; OTHERWISE SUCCESS
GVER$: STC ; COMMON ENTRY FOR FAILURE
JMP GVER8 ; DO RESTORES
GVER$$: JMP PREDF ; INDICATE FAILURE
GVERP ENDP
PUBLIC CHKSUM
CHKSUM PROC NEAR
PUSH AX ; SAVES
PUSH BX
PUSH CX
PUSH DS
PUSH ES
POP DS
MOV BX,SI ; GET NUMBER OF WORDS CHECKED SO FAR
MOV CX,AX ; GET NUMBER OF BYTES TO CHECK
MOV SI,DX ; ADDRESS OF BUFFER
MOV AH,0 ; ZERO TOP HALF
CHKS1: LODSB ; GET A BYTE
ADD BP,AX ; ADD THE BYTE
TEST SI,1 ; ODD BYTE?
JNZ CHKS2
INC BX
CMP BX,DI
JE CHKS3
CHKS2: LOOP CHKS1
CHKS3: MOV SI,BX ; RESTORE SI TO COUNT OF WORDS
POP DS
POP CX
POP BX
POP AX
RET
CHKSUM ENDP
SUBTTL 1-OP EXECUTORS
PAGE
;---------------
; 1-OP EXECUTORS
;---------------
; ----
; PUSH
; ----
;PUSH [ARG1] ONTO THE GSTACK
PUBLIC GPUSH
GPUSH PROC
MOV AX,ARG1
; AND FALL THROUGH .............
GPUSH ENDP
; -----------------------
; PUSH AX ONTO THE GSTACK
; -----------------------
PUBLIC PSHAXG,GSTKR
PSHAXG PROC
XCHG SP,DI ;USE THE REAL SP
PUSH AX ;PUSH IT ON THE STACK
XCHG SP,DI ;SWAP BACK
PUSH AX ;SAVE REGISTER
MOV AX,OFFSET GSTK_BT ;CHECK IF WE HAVE AN OVERFLOW
CMP DI,AX ;
JB GSTKR ; IF WE DO IT'S FATAL
POP AX ; ELSE, RESTORE THE REGISTER
RET
; *** ERROR #7: G-STACK OVERFLOW ***
GSTKR: POP AX
MOV AL,7
JMP GERROR
PSHAXG ENDP
; ---
; POP
; ---
;POP A VALUE OFF THE GSTACK INTO VARIABLE [ARG1]
PUBLIC GPOP
GPOP PROC
CALL POPVAL
JMP DOPUT
GPOP ENDP
; -----
; VALUE
; -----
;RETURN VALUE OF VARIABLE [ARG1]
PUBLIC GVALUE
GVALUE PROC
MOV AX,ARG1 ;GET VARIABLE ID
SUB AH,AH
CALL VARGET ;GET THE VALUE INTO [VALUE]
JMP PUTVAL ;AND RETURN IT
GVALUE ENDP
; ----
; INC
; ----
;INCREMENT VARIABLE [ARG1]
PUBLIC GINC
GINC PROC
MOV AX,ARG1 ;GET VARIABLE ID
SUB AH,AH
CALL VARGET ;GET VAR'S VALUE
INC VALUE ;INCREMENT IT
JMP DOPUT
GINC ENDP
; ---
; DEC
; ---
;DECREMENT VARIABLE [ARG1]
PUBLIC GDEC,DOPUT
GDEC PROC
MOV AX,ARG1
SUB AH,AH
CALL VARGET ;GET VAR'S VALUE
DEC VALUE ;DECREMENT IT
DOPUT: MOV AX,ARG1 ;RESTORE VARIABLE ID
SUB AH,AH
JMP VARPUT ;STORE NEW VALUE
GDEC ENDP
; -----
; ZERO?
; -----
;[ARG1]=0?
PUBLIC GZEROP,PYES
GZEROP PROC
MOV AX,ARG1
CMP AX,0 ;IS OPR ZERO?
JZ PYES ;YES, PREDICATE TRUE
JMP PREDF ;NO, PREDICATE FALSE
PYES: JMP PREDS
GZEROP ENDP
; ---
; NOT
; ---
;COMPLEMENT [ARG1]
PUBLIC GBNOT
GBNOT PROC
MOV AX,ARG1
NOT AX ;LOGICAL COMPLEMENT
JMP VEXIT ;RETURN THE VALUE
GBNOT ENDP
; ----
; JUMP
; ----
;JUMP TO G-ADDRESS [ARG1]
PUBLIC GJUMP
GJUMP PROC
MOV AX,ARG1 ;MOVE [ARG1] TO [VALUE]
MOV VALUE,AX
JMP PREDB3 ;A BRANCH THAT ALWAYS SUCCEEDS
GJUMP ENDP
; ---
; RET
; ---
;RETURN FROM "CALL" WITH VALUE [ARG1]
PUBLIC GRET,GRET1,GRET2
GRET PROC
; RESTORE THE GSTACK PTR
MOV DI,OLDGSP ;RESTORE OLD TOP OF THE GSTACK
; RESTORE LOCALS
; GET THE NUMBER OF LOCALS
CALL POPAXG ;POP # OF LOGALS FROM GSTACK INTO AX
XCHG AX,CX ; XFER INTO CX TO BE USED AS A LOOP COUNTER
; IF NUMBER OF LOCALS=0 THEN SKIP LOCAL RESTORATION
OR CX,CX ;ANY LOCALS?
JZ GRET2 ;....NO. SKIP LOCAL RESTORATION
;
; LOCAL RESTORATION
;
; INITIALIZE VMP
MOV AX,GLOCS ;SET UP VMP TO POINT TO THE LOCAL TABLE
MOV VMP,AX
MOV VMP0,0
ADD VMP,CX ;POINT PAST THE LAST LOCAL
; RESTORE ALL [CX] PUSHED LOCALS
GRET1: DEC VMP ;BACK UP TO THE CURRENT LOCAL
CALL POPAXG ;POP A LOCAL FROM THE GSTACK
CALL PW@VMP ;RESTORE THE LOCAL
LOOP GRET1 ; LOOP UNTIL WE HAVE THEM ALL
; RESTORE THE GPC
GRET2: CALL POPAXG ;GET THE GPC BIT FROM GSTACK
MOV GPC0,AL ; AND SAVE IT
CALL POPAXG ;GET GPC FROM GSTACK
MOV GPC,AX ; AND SAVE IT
; RESTORE THE OLD GSP
CALL POPAXG ;GET OLD GSP FROM GSTACK
MOV OLDGSP,AX ; AND SAVE IT
; GET THE VALUE TO RETURN
MOV AX,ARG1
JMP VEXIT
GRET ENDP
; -----------------
; VALUE RETURN EXIT
; -----------------
; ENTRY: VALUE IN AX
PUBLIC VEXIT
VEXIT PROC
MOV VALUE,AX
JMP PUTVAL ;RETURN THE VALUE
VEXIT ENDP
SUBTTL 2-OP EXECUTORS
PAGE
;---------------
; 2-OP EXECUTORS
;---------------
; --------
; BAD 2-OP
; --------
; *** ERROR #5: ILLEGAL 2-OP ***
PUBLIC BADOP2
BADOP2 PROC
MOV AL,5
JMP GERROR
BADOP2 ENDP
; ---
; ADD
; ---
;RETURN [ARG1] + [ARG2]
PUBLIC GADD
GADD PROC
MOV AX,ARG1
MOV BX,ARG2
ADD AX,BX ;ADD OPR1 AND OPR2
JMP VEXIT ;RETURN THE VALUE
GADD ENDP
; ---
; SUB
; ---
;RETURN [ARG1] - [ARG2]
PUBLIC GSUB
GSUB PROC
MOV AX,ARG1
MOV BX,ARG2
SUB AX,BX ;SUBTRACT OPR2 FROM OPR1
JMP VEXIT ;RETURN THE VALUE
GSUB ENDP
; ---
; MUL
; ---
;RETURN [ARG1] * [ARG2]
PUBLIC GMUL
GMUL PROC
MOV AX,ARG1
MOV BX,ARG2
IMUL BX ;MULTIPLY OPR1 BY OPR2,IGNORING OVERFLOW(DX)
JMP VEXIT ;RETURN THE VALUE
GMUL ENDP
; ---
; DIV
; ---
;RETURN QUOTIENT OF [ARG1] / [ARG2]
PUBLIC GDIV
GDIV PROC
MOV AX,ARG1
MOV BX,ARG2
CWD ;CLEAR HIGH WORD AND EXTEND SIGN FOR DIVIDE
IDIV BX ;DIVIDE OPR1 BY OPR2
JMP VEXIT ;RETURN THE VALUE
GDIV ENDP
; ---
; MOD
; ---
;RETURN REMAINDER OF [ARG1] / [ARG2]
PUBLIC GMOD
GMOD PROC
MOV AX,ARG1
MOV BX,ARG2
CWD ;CLEAR HIGH WORD AND EXTEND SIGN FOR DIVIDE
IDIV BX ;DIVIDE OPR1 BY OPR2
MOV AX,DX ;WE WANT REMAINDER
JMP VEXIT ;RETURN THE VALUE
GMOD ENDP
; ----
; BAND
; ----
;RETURN [ARG1] "AND" [ARG2]
PUBLIC GBAND
GBAND PROC
MOV AX,ARG1
MOV BX,ARG2
AND AX,BX ;LOGICAL AND
JMP VEXIT ;RETURN THE VALUE
GBAND ENDP
; ----
; BIOR
; ----
;RETURN [ARG1] "OR" [ARG2]
PUBLIC GBIOR
GBIOR PROC
MOV AX,ARG1
MOV BX,ARG2
OR AX,BX ;LOGICAL OR
JMP VEXIT ;RETURN THE VALUE
GBIOR ENDP
; ----
; BXOR
; ----
;RETURN [ARG1] "XOR" [ARG2]
PUBLIC GBXOR
GBXOR PROC
MOV AX,ARG1
MOV BX,ARG2
XOR AX,BX ;LOGICAL XOR
JMP VEXIT ;RETURN THE VALUE
GBXOR ENDP
; -----
; BITS?
; -----
;IS EVERY "ON" BIT IN [ARG1] ALSO "ON" IN [ARG2]
PUBLIC GBITSP,GBITS0
GBITSP PROC
MOV AX,ARG1
MOV BX,ARG2
NOT AX ;TURN OFF BITS IN OPR2 THAT ARE ON IN OPR1
AND BX,AX
JE GBITS0 ;SUCCESS IF OPR2 COMPLETELY CLEARED
JMP PBAD
GBITS0: JMP PGOOD
GBITSP ENDP
; ------
; EQUAL?
; ------
;DOES [ARG1] = [ARG2] (OR [ARG3] OR [ARG4])?
PUBLIC GEQP,GEQP1,GEQP2
GEQP PROC
MOV CL,BYTE PTR NARGS ;GET NUMBER OF ARGUMENTS
SUB CH,CH
DEC CX ;WE WANT TO COMPARE WITH ALL BUT ARG1
SUB BX,BX ;RESET OFFSET
MOV AX,WORD PTR ARG1 [BX] ;GET ARG1
GEQP1: INC BX ;POINT TO NEXT ARG
INC BX ; REMEMBER, IT'S A WORD
CMP AX,WORD PTR ARG1 [BX] ;DOES ARG1 = ARG [BX]?
JE GEQP2 ;....YES
LOOP GEQP1 ;....NO. TRY NEXT ARG
JMP PBAD ;PREDICATE FALSE IF WE RAN OUT OF ARGS
GEQP2: JMP PGOOD
GEQP ENDP
;
; PREDICATE HANDLERS
;
; USED TO VECTOR LOCAL SHORT JUMPS
PUBLIC PGOOD
PGOOD PROC
JMP PREDS
PGOOD ENDP
PUBLIC PBAD
PBAD PROC
JMP PREDF
PBAD ENDP
; -----
; LESS?
; -----
;IS [ARG1] < [ARG2]
PUBLIC GLESSP,GLESS0
GLESSP PROC
MOV AX,ARG1
MOV BX,ARG2
CMP AX,BX ;IS OPR1 LESS THAN OPR2?
JL GLESS0 ;YES, PREDICATE TRUE
JMP PBAD ;NO, PREDICATE FALSE
GLESS0: JMP PGOOD
GLESSP ENDP
; ------
; DLESS?
; ------
;DECREMENT VAR [ARG1]; SUCCEED IF < [ARG2]
PUBLIC GDLESP,GDLES0,GDLES1
GDLESP PROC
MOV AX,ARG1 ;GET VARIABLE ID
PUSH AX ;SAVE VARIABLE ID
CALL VARGET ;GET VAR'S VALUE
DEC AX ;DECREMENT IT
SUB CX,CX ;SET FLAG FALSE
MOV BX,ARG2 ;GET VALUE TO COMPARE TO
CMP AX,BX ;IS VAR [ARG1] < ARG2?
JGE GDLES0 ;NO
INC CX ;YES, CHANGE FLAG TO TRUE
GDLES0: MOV BX,AX ;GET VALUE
MOV VALUE,BX ;STORE IT IN [VALUE]
POP AX ;RESTORE VARIABLE ID
CALL VARPUT ;STORE [VALUE] AT VAR
CMP CX,0 ;TEST FLAG
JE GDLES1 ;FALSE, PREDICATE FALSE
JMP PGOOD ;ELSE, PREDICATE TRUE
GDLES1: JMP PBAD
GDLESP ENDP
; -----
; GRTR?
; -----
;IS [ARG1] > [ARG2]
PUBLIC GGRTRP,GGRTR0
GGRTRP PROC
MOV AX,ARG1
MOV BX,ARG2
CMP AX,BX ;IS OPR1 GREATER THAN OPR2?
JG GGRTR0 ;YES, PREDICATE TRUE
JMP PBAD ;NO, PREDICATE FALSE
GGRTR0: JMP PGOOD
GGRTRP ENDP
; ------
; IGRTR?
; ------
;INCREMENT [ARG1]; SUCCEED IF > [ARG2]
PUBLIC GIGRTP,OPQIG0,OPQIG1,OQI1$
GIGRTP PROC
MOV AX,ARG1
MOV BX,ARG2
PUSH AX ;SAVE VARIABLE
CALL VARGET ;GET VAR'S VALUE
INC AX ;INCREMENT IT
SUB CX,CX ;SET FLAG FALSE
CMP AX,BX ;NEW VALUE GREATER THAN VAL?
JLE OPQIG1 ;NO
OPQIG0: INC CX ;YES, CHANGE FLAG TO TRUE
OPQIG1: MOV BX,AX ;GET VALUE
MOV VALUE,BX ;STORE IT IN [VALUE]
POP AX ;RESTORE VARIABLE ID
CALL VARPUT ;STORE [VALUE] AT VAR
CMP CX,0 ;TEST FLAG
JE OQI1$ ;FALSE, PREDICATE FALSE
JMP PGOOD ;ELSE, PREDICATE TRUE
OQI1$: JMP PBAD
GIGRTP ENDP
; ---
; SET
; ---
;SET VARIABLE [ARG1] EQUAL TO [ARG2]
PUBLIC GSET
GSET PROC
MOV AX,ARG2 ;GET VALUE TO SAVE
MOV VALUE,AX ;PUT IT IN [VALUE]
MOV AX,ARG1 ;GET VARIABLE NO.
JMP VARPUT ;STORE THE VALUE IN THE VARIABLE
GSET ENDP
; ---
; GET
; ---
;RETURN ITEM [ARG2] IN WORD-TABLE [ARG1]
PUBLIC GGET
GGET PROC
MOV AX,ARG1 ;GET WORD TABLE BASE ADDRESS
ADD AX,ARG2 ;INDEX INTO WORD TABLE
MOV VMP,AX ;PUT ADDRESS INTO VIRTUAL MEMORY POINTER
MOV VMP0,0 ;IT MUST BE ON AN EVEN BOUNDRY
CALL GW@VMP ;GET THE WORD. RETURNED IN AX
MOV VALUE,AX ;PUT IT IN [VALUE]
JMP PUTVAL ;AND RETURN IT IN WORD TABLE [ARG1]
GGET ENDP
; ----
; GETB
; ----
;RETURN ITEM [ARG2] IN BYTE-TABLE [ARG1]
PUBLIC GGETB
GGETB PROC
MOV AX,ARG1 ;TABLE BASE ADDRESS (WORDPOINTER)
MOV VMP,AX ;SET UP BASE ADDRESS
MOV VMP0,0
MOV AX,ARG2 ;GET BYTE POINTER
CALL ADDVMP ;ADD BYTE OFFSET TO BASE ADDRESS
CALL GB@VMP ;GET THE BYTE
JMP PUTBYT ;AND RETURN IT VIA PUTBYT
GGETB ENDP
SUBTTL X-OP EXECUTORS
PAGE
;---------------
; X-OP EXECUTORS
;---------------
; ----
; CALL
; ----
; INSTRUCTION FORMAT:
;
; OPCALL, MODE BYTE1, .... MODE BYTEn, ARG1, .... ARGn
;
;
; MODE BYTE:
;
; There are no mode bits in the opcode itself. All of the mode bits appear
; in the byte(s) following the opcode. Each mode byte is capable of supporting
; up to four arguments. Thus a mode byte is composed of up to 4 half nibble
; mode control codes.
;
; (Mode control code will be abbreviated as MCC.) A mode byte may contain
; 4 MCC's labeled from left to right MCC1, MCC2, MCC3, and MCC4.
;
; MODE BYTE FORMAT:
;
; bits: 7 6 5 4 3 2 1 0
; +------+------+------+------+
; | MCC1 | MCC2 | MCC3 | MCC4 |
; +------+------+------+------+
;
;
; MODE CONTROL CODE FORMAT:
;
; bits: 1 0 | Operand
; ----|---------------------
; 0 0 | long immediate
; 0 1 | immediate
; 1 0 | variable
; 1 1 | no more operands
;
;
;
; AT ENTRY [NARGS] = 0
PUBLIC GCALL,GCL,GCL0,GCL1,GCL2,GCL3,GCL4,GCL5
PUBLIC GCL6,GCL7,GCL8,DOCALL,CALL1,CALL2,CALL3,CALL4
GCALL PROC
;
; FILL CTYPES ARRAY WITH 'NO MORE ARGS'
; INITIALIZE REGISTERS
MOV CX,16 ;NUMBER OF POSSIBLE ARGUMENTS
MOV AL,0C0H ;USE THE 'NO MORE ARGS' CODE
SUB BX,BX ;RESET BYTE INDEX
; FILL THE CURRENT CELL
GCL: MOV BYTE PTR CTYPES [BX],AL
; UPDATE INDEX
INC BX ;POINT TO THE NEXT CELL
; LOOP TILL DONE
LOOP GCL ; AND DO IT 16 TIMES TOTAL
;
; UNPACK MODE BYTES
;
MOV ADEX,4 ;INITIALIZE BYTE INDEX
MOV ADEX2,4 ;INITIALIZE ARG BYTE COUNTER
GCL0: CALL GB@GPCI ;GRAB AN ARGUMENT BYTE
MOV ABYTE,AL ;SAVE IT
JMP GCL2 ;DON'T SHIFT THE MODE BYTE THE FIRST TIME
GCL1: MOV AL,ABYTE ;RESTORE THE MODE BYTE
SHL AL,1 ;SHIFT AND
SHL AL,1 ;SHIFT AGAIN
MOV ABYTE,AL ;SAVE MODE BYTE FOR NEXT TIME
GCL2: AND AL,0C0H ;MASK OUT THE GARBAGE
CMP AL,0C0H ;LAST ARGUMENT?
JZ GCL3 ;....YES. TIME TO DECODE THE ARGS
;ELSE ADD THIS ARGUMENT TO THE ARG-TYPE LIST
MOV BL,NARGS ; SET UP INDEX
MOV BH,0
MOV BYTE PTR CTYPES [BX],AL
INC NARGS ; AND INCREMENT THE NUMBER OF ARGUMENTS
DEC ADEX ;IS THIS ARG BYTE EMPTY?
JNZ GCL1 ;....NO. GET NEXT ARG
MOV ADEX,4 ;ELSE RESET THE ARGUMENT INDEX
DEC ADEX2 ;HAVE WE DONE 4 ARG BYTES?
JNZ GCL0 ;....NO. GET ANOTHER
; [CTYPES] HAS LIST OF ARG TYPES
; [NARGS] HAS NUMBER OF ARGUMENTS
; GPC POINTS TO ARG1 (THE FUNCTION ADDRESS)
GCL3: MOV ADEX,0 ;RESET THE ARGUMENT INDEX
MOV ADEX2,0 ;AND STORAGE INDEX
MOV AL,NARGS ;CONTINUE ONLY IF
CMP AL,0 ;[NARGS] <> 0
JNZ GCL4
; *** ERROR #12: NO CALL ADDRESS ***
MOV AL,12
JMP GERROR
; SAVE THE GCALL ARGUMENTS IN THE CARGS ARRAY WHILE USING THE MODE
; CONTROL CODES (LOCATED IN THE CTYPES ARRAY) TO OBTAIN THEM.
GCL4:
MOV BL,ADEX ;GET ARG INDEX
MOV BH,0 ;WIPE OUT THE MSB
CMP BL,NARGS ;OUT OF ARGS YET?
JZ GCL8 ;....YES
INC ADEX ;UPDATE ARG INDEX TO NEXT ARG
; GET THE TYPE BYTE FOR CURRENT ARG IN AL
MOV AL,BYTE PTR CTYPES [BX]
CMP AL,0 ;LONG IMMEDIATE?
JNZ GCL5 ;....NO
; LONG IMMEDIATE
CALL GETLNG ;....YES, GET IT
JMP GCL7
GCL5: CMP AL,40H ;SHORT IMMEDIATE?
JNZ GCL6 ;....NO
; SHORT IMMEDIATE
CALL GETSHT ;....YES, GET IT
JMP GCL7
; VARIABLE
GCL6: CALL GETVAR ;MUST BE A GET VARIABLE
; ARGUMENT IS NOW IN AX
; MOVE VALUE (IN AX) TO [CARGS]
GCL7: MOV BL,ADEX2 ;GET THE STORAGE INDEX
MOV BH,0
MOV WORD PTR CARGS [BX],AX
;SAVE IT IN CARGS
INC ADEX2 ; AND INCREMENT THE INDEX
INC ADEX2 ; TWICE, CAUSE WE SAVING WORDS
JMP GCL4 ;LOOP BACK FOR ANOTHER ARGUMENT
; ARGUMENTS IN [CARGS], # OF ARGUMENTS IN [NARGS]
GCL8: MOV AX,CARGS
OR AX,AX ;DOES CALL ADDRESS=0?
JNZ DOCALL ;....NO. CONTINUE
JMP PUTBYT ;ELSE RETURN THE ZERO IN AL
;
; AND NOW THE ACTUAL CALL
;
DOCALL: MOV AX,OLDGSP ;PUSH [OLDGSP] ON THE GSTACK
CALL PSHAXG
MOV AX,GPC ;PUSH [GPC] ON THE GSTACK
CALL PSHAXG
MOV AL,GPC0 ;PUSH GPC BIT ON THE GSTACK
CALL PSHAXG
;SET GPC TO THE FUNCTION ADDRESS
MOV AX,CARGS ;GET THE FUNCTION ADDRESS
MOV GPC,AX ;PUT IT IN THE GPC
MOV GPC0,0 ; ALL FUNCTIONS START ON WORD BOUNDRIES
;PROCESS THE FUNCTION'S LOCALS
CALL GB@GPCI ;GET # OF LOCALS IN FUNCTION
MOV ADEX,AL ;SAVE HERE FOR REFERENCE
MOV ADEX2,AL ;SAVE HERE FOR COUNTING
; ANY LOCALS?
OR AL,AL ;IF # LOCALS = 0
JZ CALL2 ; THEN JUMP AHEAD
; PUT THE FUNCTION'S LOCALS IN THE LOCAL TABLE
; WHILE SAVING THE PRE-EXISTING LOCALS ON THE GSTACK
; SET UP LOOP COUNTER
MOV CL,AL ;PUT NUMBER OF LOCALS IN LOOP COUNTER
SUB CH,CH
MOV AX,GLOCS ;POINT VMP AT THE BASE ADDRESS OF THE
MOV VMP,AX ; LOCAL TABLE
MOV VMP0,0
CALL1: CALL GW@VMP ;GET OLD LOCAL
CALL PSHAXG ; SAVE IT ON THE GSTACK
CALL GETLNG ;GET FUNCTION'S LOCAL & POINT TO NEXT
CALL PW@VMPI ;PUT IT IN THE LOCAL TABLE AND POINT TO
; THE NEXT TABLE ENTRY
LOOP CALL1
CALL2: DEC NARGS ;ANY ARGS LEFT?
JZ CALL4 ;....NO
; PASS UP TO 15 [CARGS] TO [LOCALS]
SUB BX,BX ;INITIALIZE LOOP INDEX
MOV AX,GLOCS ;POINT VMP AT THE BASE ADDRESS OF THE
MOV VMP,AX ; LOCAL TABLE
MOV VMP0,0
CALL3: MOV AX,WORD PTR CARGS+2 [BX] ;GET ARGUMENT
CALL PW@VMPI ;ASSIGN ARG TO A LOCAL
INC BX ;UPDATE INDEX
INC BX ;IT'S A WORD PTR
DEC NARGS ;OUT OF ARGS YET?
JNZ CALL3 ;....NO
CALL4: MOV AL,ADEX ;GET NO. OF LOCALS
MOV AH,0 ;MASK OUT THE MSB
CALL PSHAXG ; AND SAVE ON THE GSTACK
MOV OLDGSP,DI ;SAVE THE GSTACK POINTER
JMP MLOOP ;AND GO BACK TO MAIN LOOP
GCALL ENDP
PAGE
; ---
; PUT
; ---
;
; SET ITEM [ARG2] IN WORD-TABLE [ARG1] EQUAL TO [ARG3]
;
PUBLIC GPUT
GPUT PROC
MOV AX,ARG1 ;GET WORD TABLE BASE ADDRESS
ADD AX,ARG2 ;INDEX INTO WORD TABLE
MOV VMP,AX ;PUT ADDRESS INTO VIRTUAL MEMORY POINTER
MOV VMP0,0 ;IT MUST BE ON AN EVEN BOUNDRY
MOV AX,ARG3 ;GET THE VALUE
CALL PW@VMP ; AND WRITE IT TO VIRTUAL
RET
GPUT ENDP
; ----
; PUTB
; ----
;
; SET ITEM [ARG2] IN BYTE-TABLE [ARG1] EQUAL TO [ARG3 (LSB)]
;
PUBLIC GPUTB
GPUTB PROC
MOV AX,ARG1 ;TABLE BASE ADDRESS (WORDPOINTER)
MOV VMP,AX ;SET UP BASE ADDRESS
MOV VMP0,0
MOV AX,ARG2 ;GET BYTE POINTER
CALL ADDVMP ;ADD BYTE OFFSET TO BASE ADDRESS
MOV AX,ARG3 ;GET VALUE
CALL PB@VMPI ;WRITE OUT THE BYTE
RET
GPUTB ENDP
PAGE
; -----
; INPUT
; -----
; IMPORTS:
; [ARG1], [ARG2] optional
;
; IF [NARGS] INDICATES THAT THERE IS A [ARG2], THEN [ARG2]
; IS A WORDPOINTER ADDRESS TO AN ACTIVE ICON TABLE. IN THIS
; CASE, ITERATION IS PERFORMED.
;
; [ARG1] IS DECODED AS FOLLOWS:
; 0 : IMMEDIATELY POLL THE KEYBOARD AND JOYSTICK ONCE
; -int : PAUSE FOR [-int]/60 SEC.
; int : POLL THE KEYBOARD FOR int/60 SEC. OR UNTIL A
; KEYPRESS IS DETECTED
;
; EXPORTS: [VALUE] via PUTBYT [AL]
; WHERE AL IS ENCODED AS FOLLOWS:
; 8FH : NO KEYPRESS (NO INPUTS DETECTED)
; +int : 7-BIT ASCII CHARACTER
; -int : JOYSTICK DATA *
;
; *note See DIP SPECIFICATION for JOYSTICK DATA rules
PUBLIC GINPUT
GINPUT PROC
MOV KEYSAV,8FH ;KEYSAV <= NO KEY FOUND
MOV AX,ARG1 ;GET ARGUMENT
MOV VALUE,AX ;MOVE [ARG1] INTO [VALUE]
CMP NARGS,1 ;IF ONLY 1 ARG
JE DONTIT ; THEN DON'T ITERATE
JMP ITRATE ; ELSE, ITERATE
DONTIT: JMP NOIT
GINPUT ENDP
NOIT PROC
MOV AX,VALUE ;RECOVER THE SILLY ARG
OR AX,AX ;TEST IT AGAIN
JS DELAY ;IF NEGATIVE, THIS IS A DELAY REQUEST
JNZ TICK ;IF NON-ZERO, THEN ENTER SENSE LOOP
NOIT1: CALL SENSE ; ELSE, READ JUST ONCE
MOVED: JMP PUTBYT ;RETURN [AL]
;
; PURPOSE: POLL THE KEYBOARD FOR int/60 SEC. OR UNTIL A
; KEYPRESS IS DETECTED
;
TICK: CMP PCJR,1 ; DON'T DOUBLE VALUE IF jr
JE TICK1 ; SO SKIP IT
SHL VALUE,1 ; DOUBLE THE WAIT VALUE
TICK1: CALL SENSE ;GET A READING
CMP AL,10001111B ;ANY ACTIVITY?
JNE MOVED ;....YES. RETURN IT.
CMP IBMAT,FALSE ; ARE WE A FAST ONE
JE TICK3
MOV CX,JIFCNT
TICK2: LOOP TICK2 ; WASTE SOME TIME
TICK3: DEC VALUE ;DECREMENT COUNT, DOES COUNT = 0?
JNZ TICK1 ;....NO, LOOP BACK AND TRY AGAIN
;THE CLOCK EXPIRED WITHOUT A READING
NOREAD: JMP PUTBYT
;
; PROGRAMABLE DELAY
;
; PURPOSE: POLL THE KEYBOARD FOR [-int]/60 SEC. REPORT THE
; FIRST DETECTED KEYPRESS IF ANY.
;
; ENTER: AX=[-INT]
;
DELAY: NEG VALUE ;CHANGE [-INT] TO [+INT]
CMP PCJR,1 ; DON'T SHIFT FOR PCjr
JE DELALP ; SKIP IT
SHL VALUE,1 ;DOUBLE DELAY TIME FOR RICK'S MISCALC
DELALP: CALL SENSE ;GET A READING
CMP KEYSAV,8FH ;ANY PREVIOUS ACTIVITY?
JNE DELA1 ;....YES
MOV KEYSAV,AL ;....NO, SAVE THE CURRENT READING
DELA1: CMP IBMAT,FALSE ;IS THIS A SLOW MACHINE?
JE DELA3 ;....YES
MOV CX,JIFCNT ;IT'S A FAST MACHINE (AN IBM AT)
DELA2: LOOP DELA2 ; SO USE A DELAY LOOP TO COMPENSATE
DELA3: DEC VALUE ;DECREMENT COUNT, DOES COUNT = 0?
JNZ DELALP ;....NO, LOOP BACK AND TRY AGAIN
;THE CLOCK EXPIRED
MOV AL,KEYSAV ;RETURN THE KEYCODE
JMP PUTBYT
NOIT ENDP
; ---------------------------
; CHECK KEYBOARD AND JOYSTICK
; ---------------------------
PUBLIC SENSE
SENSE PROC
; CHECK KEYBOARD FIRST
MOV AH,6 ;SELECT DIRECT CONSOLE INPUT
MOV DL,0FFH ; WITHOUT ECHO
INT 21H ;MSDOS FUNCTION CALL
JZ STICK ;IF NO KEYPRESS, THEN CHECK STICK
; ELSE WE GOT A KEYSTROKE
CMP AL,0DH ; (B5) CARRIAGE RETURN?
JE SENS2
CMP AL,0 ; EXTENDED ASCII?
JNE SENS1 ; NOPE
MOV AH,CNOECHO ; READ THE EXTENDED CODE
INT 21H ; GET THE CHAR
SUB AL,71 ; BASIFY THE HOME KEY
CMP AL,10 ; UPPER LIMIT
JA STICK ; DON'T RETURN ANYTHING
MOV BX,OFFSET XLKEY ; TRANSLATE THE KEY
XLAT ; LOOK UP IN TABLE
SENS2: PUSH AX ; SAVE CHAR AND TURN ON NUMLOCK
PUSH ES
MOV AX,BIOSDATA ; SEG ADDRESS BIOS DATA AREA
MOV ES,AX
MOV BX,KB_FLAG ; OFFSET TO BIOS DATA FOR KEYS
OR BYTE PTR ES:[BX],NUMLOCK ; TURN ON NUMLOCK
POP ES
POP AX
SENS1: AND AL,01111111B ;7-BIT ASCII ONLY
RET
; CHECK THE JOYSTICK
STICK: TEST JOYSTK,1 ; IS THERE REALLY ONE THERE?
JNZ STICK1 ; ITS THERE, ONWARD
PUSH CX
MOV CX,JIFCNT-200H ; NUMBER OF INSTRUCTIONS CYCLES NOT EXECUTED
STICK$: LOOP STICK$ ; TAKE AS MUCH TIME AS WE WOULD HAVE
POP CX
MOV AL,8FH ; RETURN CENTER POSITION
RET
STICK1: MOV DX,201H ;DX ^ GAME PORT
MOV AL,10001111B ;INITIALIZE JOYSTICK ACTIVITY BYTE
MOV STICKA,AL ; TO INDICATE NO KEYPRESSES
;*********** PATCH *************************
;RET ;TO DISABLE JOYSTICK
;*******************************************
;GET JOYSTICK X-POSITION
MOV AH,1 ;SELECT X-AXIS
CALL POSITN
MOV BL,STICKA ;GET THE JOYSTICK FLAGS
CMP AL,60H ;IS THE JOYSTICK TO THE RIGHT?
JB STK0 ;....NO
XOR BL,00001000B ;TURN OFF THE RIGHT BIT (NEGATIVE LOGIC)
JMP STK1 ;IF IT'S TO THE RIGHT, IT CAN'T BE TO THE LEFT
STK0: CMP AL,30H ;IS THE JOYSTICK TO THE LEFT?
JAE STK1 ;....NO
XOR BL,00000100B ;TURN OFF THE LEFT BIT
STK1: MOV STICKA,BL ;SAVE JOYSTICK FLAGS
MOV AH,2 ;SELECT Y POSITION
CALL POSITN ;GET JOYSTICK POSITION
MOV BL,STICKA ;GET THE JOYSTICK FLAGS
CMP AL,60H ;IS THE JOYSTICK DOWN?
JB STK2 ;....NO
XOR BL,00000010B ;TURN OFF THE DOWN BIT (NEGATIVE LOGIC)
JMP STK3 ;IF IT'S DOWN, IT CAN'T BE UP
STK2: CMP AL,30H ;IS THE JOYSTICK UP?
JAE STK3 ;....NO
XOR BL,00000001B ;TURN OFF THE UP BIT
STK3: MOV STICKA,BL
IN AL,DX ;READ SWITCH INPUTS
MOV AH,AL ;SAVE IT IN [AH]
AND AL,10H ;IS BUTTON 1 DEPRESSED?
JNZ STK3A ;....NO
MOV BL,STICKA ;....YES
OR BL,00010000B ;TURN ON THE BUTTON BIT
STK3A: AND AH,20H ;IS BUTTON 2 DEPRESSED?
JNZ STK4 ;....NO
MOV BL,STICKA ;....YES
OR BL,00010000B ;TURN ON THE BUTTON BIT
STK4: MOV AL,BL ;RETURN THE VALUE IN AL
RET
SENSE ENDP
;
; GET JOYSTICK ANALOG POSITION
;
;
; AXIS SELECTED WITH AH
; 1= X AXIS
; 2= Y AXIS
;
; A DIFFERENT ROUTINE IS PROVIDED FOR IBM AND IBM AT
POSITN PROC
PUSH CX
IN AL,61H ;GET PORT DATA
PUSH AX ; AND SAVE IT
CLI ;CLEAR INTERRUPTS
SUB CX,CX ;SET LOOP CONTROL VALUE = 0
OUT DX,AL ;START THE TIMING
POSX1: IN AL,DX ;GET STATE
TEST AL,AH ;WE'RE INTERESTED IN THE BIT IN AH
LOOPNE POSX1 ;LOOP UNTIL THE BIT COMES BACK ON
; IS THIS AN IBM AT?
CMP IBMAT,TRUE
JNE POSX2
SAR CX,1
POSX2: SUB AX,AX
SUB AX,CX ;DETERMINE COUNT
CMP AX,255
JBE POSX3
MOV AX,255
;EQUALIZATION
POSX3: PUSH AX
; IS THIS AN IBM AT?
CMP IBMAT,TRUE
JNE POSX4
SHL CX,1
POSX4: AND CX,01FFH
EQ1: IN AL,DX
TEST AL,AH
LOOP EQ1
POP CX
POP AX ;RESTORE INTERRUPT STATUS
OUT 61H,AL ; THE PORT VALUE
STI ;ENABLE INTERRUPTS
MOV AX,CX ;RESTORE JOYSTICK VALUE
POP CX
RET
POSITN ENDP
PAGE
; --------------------
; DUMP BLOCK TO SCREEN
; --------------------
; ENTRY: BLOCK TO PUT ON SCREEN IN [BLOCK] (TOP BYTE IN [BLOCK + 0])
; X-COORDINATE (0-39) IN [AH], Y-COORDINATE (0-23) IN [AL]
;
PUBLIC DUMP
DUMP PROC
PUSH DI
CMP AH,39 ; (B4) SHIFT FIX
JNE DMP1
JMP DMPFIX
DMP1: MOV BX,VID_SG ;POINT AT THE VIDEO BUFFER
MOV ES,BX
; CALCULATE OFFSET FROM VIDEO_RAM BASE ADDRESS TO STORE THE FIRST BYTE
SUB BX,BX ;ZERO MSB
XCHG BL,AL ;BX <= ROW ADDRESS (0-23)
; AL IS NOW "0"
SHL BX,1 ;MAKE BX A WORD INDEX
XCHG AH,AL ;GET COLUMN CONTRIBUTION IN AL
SHL AX,1 ; x2 SINCE IBM HAS 80 COLUMNS
ADD AX,WORD PTR YAXTBL[BX] ;ADD ROW CONTRIBUTION
; TO COLUMN CONTRIBUTION
MOV DI,AX ; (B4) USE REG
MOV SI,OFFSET BLOCK ; (B4) USE REG
MOV CX,4 ;INITIALIZE LOOP COUNTER
SUB DI,1FAFH ; (B4) FIRST PASS FUDGE FACTOR
JMP DLOOP1 ; (B4) SKIP ACTUAL FIX OFFSET
; TRANSLATE BYTE IN [AL] INTO A DOUBLED BIT WORD IN [AX]
DLOOP: SUB DI,1FB2H ; (B4) FIX OFFSET
DLOOP1: XOR AH,AH ; (B4) ZERO TOP HALF
LODSB ; (B4) GET BLOCKSET BYTE
SHL AX,1 ; (B4) BYTE TO WORD
XCHG BP,AX ; (B4) INTO AN INDEX REG
MOV AX,GRXTBL[BP] ; (B4) GET SHIFTED WORD FROM TBL
;PHASE SHIFT
;MOVE THE WORD INTO VIDEO RAM
STOSB ; (B4) STORE MIDDLE BYTE
MOV AL,AH ;SWAP
AND AX,3FC0H ;MASK
MOV DH,ES:[DI-2] ; (B4) GET IMAGE ALREADY ON SCREEN
MOV DL,ES:[DI] ; (B4)
AND DX,0C03FH ;MASK IT
OR AX,DX ;COMBINE SOURCE AND DEST TO
MOV ES:[DI-2],AH ; (B4) ACHIEVE PHASE SHIFT ON LEFT
STOSB ; (B4) AND ON RIGHT
; ODD BYTE IN BLOCK
ADD DI,1FFEH ; FIX OFFSET
; TRANSLATE BYTE IN [AL] INTO A DOUBLED BIT WORD IN [AX]
XOR AH,AH ; (B4) ZERO TOP HALF
LODSB ; (B4) GET BLOCKSET BYTE
SHL AX,1 ; (B4) BYTE TO WORD
XCHG BP,AX ; (B4) INTO AN BASE REG
MOV AX,GRXTBL[BP] ; (B4) GET SHIFTED WORD FROM TBL
;PHASE SHIFT
;MOVE THE WORD INTO VIDEO RAM
STOSB ; (B4) STORE MIDDLE BYTE
MOV AL,AH ;SWAP
AND AX,3FC0H ;MASK
MOV DH,ES:[DI-2] ; (B4) GET IMAGE ALREADY ON SCREEN
MOV DL,ES:[DI] ; (B4)
AND DX,0C03FH ;MASK IT
OR AX,DX ;COMBINE SOURCE AND DEST TO
MOV ES:[DI-2],AH ; (B4) ACHIEVE PHASE SHIFT ON LEFT
STOSB ; (B4) AND ON RIGHT
LOOP DLOOP
POP DI
RET
DUMP ENDP
DMPFIX: MOV BX,VID_SG ;POINT AT THE VIDEO BUFFER
MOV ES,BX
SUB BX,BX ;ZERO MSB
XCHG BL,AL ;BX <= ROW ADDRESS (0-23)
SHL BX,1 ;MAKE BX A WORD INDEX
XCHG AH,AL ;GET COLUMN CONTRIBUTION IN AL
SHL AX,1 ; x2 SINCE IBM HAS 80 COLUMNS
ADD AX,WORD PTR YAXTBL[BX] ;ADD ROW CONTRIBUTION
MOV DI,AX ; (B4) USE REG
MOV SI,OFFSET BLOCK ; (B4) USE REG
MOV CX,4 ;INITIALIZE LOOP COUNTER
SUB DI,1FAFH ; (B4) FIRST PASS FUDGE FACTOR
JMP DFLP1 ; (B4) SKIP ACTUAL FIX OFFSET
SUB DI,1FAFH ; (B4) FIRST PASS FUDGE FACTOR
JMP DFLP1 ; (B4) SKIP ACTUAL FIX OFFSET
DFLP: SUB DI,1FB1H ; (B4) FIX OFFSET
DFLP1: XOR AH,AH ; (B4) ZERO TOP HALF
LODSB ; (B4) GET BLOCKSET BYTE
SHL AX,1 ; (B4) BYTE TO WORD
XCHG BP,AX ; (B4) INTO AN INDEX REG
MOV AX,GRXTBL[BP] ; (B4) GET SHIFTED WORD FROM TBL
STOSB ; (B4) STORE MIDDLE BYTE
MOV AL,AH ;SWAP
AND AX,3FC0H ;MASK
MOV DH,ES:[DI-2] ; (B4) GET IMAGE ALREADY ON SCREEN
MOV DL,ES:[DI] ; (B4)
AND DX,0C03FH ; (B4) MASK IT
OR AX,DX ;COMBINE SOURCE AND DEST TO
MOV ES:[DI-2],AH ; (B4) ACHIEVE PHASE SHIFT ON LEFT
ADD DI,1FFFH ; FIX OFFSET
XOR AH,AH ; (B4) ZERO TOP HALF
LODSB ; (B4) GET BLOCKSET BYTE
SHL AX,1 ; (B4) BYTE TO WORD
XCHG BP,AX ; (B4) INTO AN BASE REG
MOV AX,GRXTBL[BP] ; (B4) GET SHIFTED WORD FROM TBL
STOSB ; (B4) STORE MIDDLE BYTE
MOV AL,AH ;SWAP
AND AX,3FC0H ;MASK
MOV DH,ES:[DI-2] ; (B4) GET IMAGE ALREADY ON SCREEN
MOV DL,ES:[DI] ; (B4)
AND DX,0C03FH ;MASK IT
OR AX,DX ;COMBINE SOURCE AND DEST TO
MOV ES:[DI-2],AH ; (B4) ACHIEVE PHASE SHIFT ON LEFT
LOOP DFLP
POP DI
RET
PAGE
; ------------------------------
; DUMP BLOCK TO SCREEN WITH MASK
; ------------------------------
; ENTRY: BLOCK TO PUT ON SCREEN IN [BLOCK] (TOP BYTE IN [BLOCK + 0])
; X-COORDINATE (0-39) IN [AH], Y-COORDINATE (0-23) IN [AL]
;
; IF THE MASK BIT IS A "1" THEN THE SCREEN SHOWS THROUGH
; IF THE MASK BIT IS A "0" THEN THE ICON IS DISPLAYED
;
PUBLIC DMPMSK
DMPMSK PROC
PUSH BX ;SAVE AFFECTED REGISTER(S)
PUSH CX
PUSH DX
PUSH ES
MOV BX,VID_SG ;POINT AT THE VIDEO BUFFER
MOV ES,BX
; CALCULATE OFFSET FROM VIDEO_RAM BASE ADDRESS TO STORE THE FIRST BYTE
SUB BX,BX ;ZERO MSB
XCHG BL,AL ;BX <= ROW ADDRESS (0-23)
; AL IS NOW "0"
SHL BX,1 ;MAKE BX A WORD INDEX
XCHG AH,AL ;GET COLUMN CONTRIBUTION IN AL
SHL AX,1 ; x2 SINCE IBM HAS 80 COLUMNS
ADD AX,WORD PTR YAXTBL[BX] ;ADD ROW CONTRIBUTION
; TO COLUMN CONTRIBUTION
MOV VDPADR,AX ;SAVE STARTING MEMORY OFFSET
SUB SI,SI ;INITIALIZE INDEX REGISTER
MOV CX,4 ;INITIALIZE LOOP COUNTER
DMPMLP: MOV BL,BLOCK[SI] ;SELECT ICON BYTE
MOV BH,MBLOCK[SI] ;SELECT MASK BYTE
SUB VDPADR,1FB0H ;FIX OFFSET
CALL M1DISP ;TRANSLATE, MASK AND MOVE IT TO VRAM
INC SI ; NEXT BYTES
MOV BL,BLOCK[SI] ;SELECT ICON BYTE
MOV BH,MBLOCK[SI] ;SELECT MASK BYTE
ADD VDPADR,2000H ;FIX OFFSET
CALL M1DISP ;TRANSLATE, MASK AND MOVE IT TO VRAM
INC SI ; NEXT BYTES
LOOP DMPMLP
POP ES ;RESTORE AFFECTED REGISTER(S)
POP DX
POP CX
POP BX
RET
DMPMSK ENDP
PUBLIC M1DISP
M1DISP PROC
PUSH CX
PUSH BX ;SAVE MASK BYTE [IN BH]
; TRANSLATE BLOCK BYTE IN [BL] INTO A DOUBLED BIT WORD IN [AX]
SUB BH,BH
SHL BX,1
MOV DX,GRXTBL[BX] ;BLOCK WORD
POP BX ;RECOVER MASK BYTE
XCHG BH,BL ;PUT IT IN BL
SUB BH,BH
SHL BX,1
MOV CX,GRXTBL[BX] ;MASK WORD
PUSH DX ;SAVE BLOCK WORD
; GET THE VIDEO WORD FROM VIDEO RAM
MOV BX,VDPADR ;GET BASE ADDRESS
MOV AX,ES:[BX]
XCHG AL,AH
AND AX,3FFFH
MOV DL,ES:[BX+2]
AND DL,0C0H
OR AH,DL
POP DX ;DX <= BLOCK WORD
;CX <= MASK WORD
;AX <= VIDEO WORD
;BLOCK <= ((VIDEO XOR BLOCK) AND MASK) XOR BLOCK
XOR AX,DX
AND AX,CX
XOR AX,DX
; STORE THE WORD IN VIDEO RAM
MOV BX,VDPADR ;GET BASE ADDRESS
;PHASE SHIFT
;MOVE THE WORD INTO VIDEO RAM
MOV ES:[BX+1],AL
MOV AL,AH
AND AX,3FC0H
MOV DH,ES:[BX]
MOV DL,ES:[BX+2]
AND DX,0C03FH
OR DX,AX
MOV ES:[BX],DH
MOV ES:[BX+2],DL
POP CX
RET
M1DISP ENDP
%OUT DMPMSK DONE
PAGE
; ------
; GSHOWN
; ------
; DISPLAY NEGATIVE ICON [ARG3] AT X=[ARG1], Y=[ARG2]
PUBLIC GSHOWN
GSHOWN PROC
MOV NEGATE,0FFH ;ENABLE NEGATE
JMP SHOWI ; AND DO A SHOWI
GSHOWN ENDP
; ------
; GSHOWI
; ------
; DISPLAY POSITIVE ICON [ARG3] AT X=[ARG1], Y=[ARG2]
PUBLIC GSHOWI
GSHOWI PROC
MOV NEGATE,0 ;DISABLE NEGATE
; AND FALL THROUGH .............
GSHOWI ENDP
; -----
; SHOWI
; -----
; CODE COMMON TO GSHOWI AND GSHOWN
; DISPLAY ICON [ARG3] AT X=[ARG1], Y=[ARG2]
; IF [ARG4] IS GIVEN, IT POINTS TO THE MASK TO BE USED
PUBLIC SHOWI
SHOWI PROC
MOV MSKFLG,0 ;DISABLE MASKING UNTIL FURTHER NOTICE
CMP NARGS,3
JE NOMASK ;IF ONLY 3 ARGS, THEN DON'T MASK
; {CASE} MASK
MOV AX,ARG4 ;GET MASK INFO
CALL ISU ; THIS ENTRYPOINT WILL PROVIDE:
; ICON[ARG4] DATA V-ADDR IN [IADR2]
; X-SIZE IN [IX2]
; Y-SIZE IN [IY2]
; ICON[ARG3] DATA V-ADDR IN [IADR1]
; X-SIZE IN [IX1] AND [XDEX1]
; Y-SIZE IN [IY1] AND [YDEX1]
; ASSIGNED BLOCKSET IN [BSET]
MOV AL,IX1 ;IF WIDTH AND HEIGHT ARE NOT THE SAME
CMP AL,IX2 ; THEN DO NOTHING
JNE DRAWEX
MOV AL,IY1
CMP AL,IY2
JNE DRAWEX
DEC MSKFLG ;ELSE SET MSKFLG [0FFH]
JB ITENT ; YES, THIS IS AN UNCONDITIONAL JUMP
; {CASE} NO MASKING
; SET UP ICON [ARG3]
NOMASK: MOV AX,ARG3 ;GET THE V-ADDRESS OF THE ICON
CALL GETI ; AND SET IT UP
; ICON DATA V-ADDR IN [IADR1]
; X-SIZE IN [IX1] AND [XDEX1]
; Y-SIZE IN [IY1] AND [YDEX1]
; ASSIGNED BLOCKSET IN [BSET]
; AND FALL THROUGH .............
SHOWI ENDP
PUBLIC ITENT,DRAWEX,DRAWI1,DRAWI2,DRAWI3,DRAWI4
PUBLIC DRAWI5,DRAWE2,DRAWIA,DRAWI8,DRAWI9,DRAWIB
PUBLIC DRAWIC,DRAWID,DRAW,DRAW1,DRAW2A,CLPRI2
ITENT PROC
MOV AL,0 ; INITIALIZE VARIABLES
MOV TOPCUT,AL
MOV SIDCUT,AL
MOV MDXCUT,AL
; DETERMINE IF THE ICON CAN BE LEGALLY DRAWN
; FROM THE STANDPOINT OF IT'S Y-POSITION
MOV AX,ARG2 ;GET ICON Y-POSITION
OR AH,AH ; IF MSB=0
JZ DRAWI1 ; THEN ICON STARTS ON WINDOW PAGE
CMP AH,0FFH ; IF ICON Y-POS MSB <> 0 AND <> 0FFH
JNE DRAWEX ; THEN ICON WILL NOT BE DISPLAYED
; {CASE 1} ICON STARTS ABOVE WINDOW PAGE (MSB = 0FFH)
; DETERMINE TOP CLIP
; FIND ICON Y1 - WINDOW Y1
MOV BL,WINDY1 ;GET UPPER EDGE OF WINDOW
SUB BH,BH ;WORDIFY
NEG AX ;SINCE IT'S (-) WE GET TWO'S COMPLIMENT
ADD AX,BX ; AND ADD TO GET THE ABSOLUTE DISTANCE
OR AH,AH
JZ DRAWI2 ;IF DISTANCE LE. 255 THEN CONTINUE
; ELSE DEPART
DRAWEX: RET
; {CASE 2} ICON STARTS ON WINDOW PAGE (MSB = 00H)
; DETERMINE IF A TOPCLIP IS NECESSARY
DRAWI1: MOV AL,WINDY1 ;FIND WINDOW Y1 - ICON Y1
MOV BL,BYTE PTR ARG2
SUB AL,BL ;IF (-) THEN ICON STARTS BELOW TOP EDGE
JNS DRAWI2 ; OF WINDOW
; {CASE 2A} THE ICON TOPEDGE LIES ON OR BELOW THE WINDOW TOPEDGE
; NO TOPCUT IS NECESSARY
; [WINDY2] = LAST LINE OF THE WINDOW TO DISPLAY
DRAWI3: MOV AL,WINDY2 ;FIND WINDY2 - ICON Y1 [BL]
INC AL
SUB AL,BL ;GET DISTANCE
JS DRAWEX ;EXIT IF (-) THE WHOLE ICON IS BELOW THE WINDOW
; COMPARE SIZE OF THE DISPLAYABLE AREA ((WINDY2 + 1) - ICON Y1)
; AND THE ICON HEIGHT
CMP AL,IY1 ;IF SIZE OF DISPLAYABLE AREA GE.ICON HEIGHT
JAE DRAWI4 ; THEN DON'T BOTHER CHANGING ICON HEIGHT
MOV IY1,AL ; ELSE, SUBSTITUTE SIZE OF THE DISPLAYABLE
JMP DRAWI4 ; AREA FOR THE ICON HEIGHT (BOTTOM CUT)
DRAWI2: MOV TOPCUT,AL ;SAVE AMOUNT TO CUT FROM TOP OF ICON
;DO THE TOPCUT
DRAWI4: MOV AL,IY1 ;GET HEIGHT OF ICON
SUB AL,TOPCUT ;DO TOPCUT
JBE DRAWEX ;NO ICON LEFT TO DRAW SO EXIT
MOV YDEX1,AL
SUB AL,WINDH ;IF THE ICON IS SMALLER THAN THE WINDOW
JB DRAWI5 ; THEN DISPLAY ALL OF IT
MOV AL,WINDH ; ELSE, DISPLAY A WINDOW'S WORTH OF ICON
MOV YDEX1,AL
; MULTIPLY TOPCUT AND ICON WIDTH, ADD PRODUCT TO ICON POINTER
; IN ORDER TO POINT AT THE START OF THE FIRST DISPLAYED ROW
DRAWI5: MOV AL,BYTE PTR ARG2 ;CHANGE ICON STARTING YPOS
ADD AL,TOPCUT ;TO REFLECT TOPCUT
MOV BYTE PTR YPOS,AL
MOV AL,TOPCUT
MOV BL,IX1
MUL BL ;AX=TOPCUT * ICON WIDTH
PUSH AX ;SAVE TOPCUT
CALL AIADR1 ;IADR1 <= IADR1 + TOPCUT
POP AX ;RESTORE TOPCUT
CMP MSKFLG,0 ;IS MASKING DISABLED?
JE NOMSK1 ;....YES
CALL AIADR2 ;....NO. IADR2 <= IADR2 + TOPCUT
;COMPUTE THE DISPLAYED WIDTH AND THE IXSKIP
NOMSK1: MOV AL,IX1 ;KLUDGE
MOV MDXCUT,AL
MOV AX,ARG1 ;GET ICON X-COORDINATE
OR AH,AH ;IF MSB = 0
JZ DRAWI8 ; THEN ICON STARTS ON WINDOW PAGE
CMP AH,0FFH ; ELSE, IF MSB <> 0FFH
JNE DRAWE2 ; THEN WE MIGHT AS WELL GO HOME
; {CASE} ICON STARTS BEFORE WINDOW PAGE (MSB = 0FFH)
; DETERMINE LEFT CLIP
; FIND ICON X1 - WINDOW X1
MOV BL,WINDX1 ;GET LEFT EDGE OF WINDOW
SUB BH,BH
NEG AX ;SINCE IT'S NEG, WE CHANGE THE SIGN
ADD AX,BX ; AND GET THE ABSOLUTE DISTANCE
OR AH,AH
JZ DRAWI9 ;IF d <= 255 THEN CONTINUE
DRAWE2: RET
DRAWIA: MOV AL,WINDX2
INC AL
SUB AL,BYTE PTR ARG1 ;GET DISTANCE
JS DRAWE2
CMP AL,IX1 ;COMPARE WITH WIDTH
JAE DRAWIB
MOV IX1,AL
JMP DRAWIB
DRAWI8: MOV AL,WINDX1
MOV BL,BYTE PTR ARG1
SUB AL,BL
JS DRAWIA
DRAWI9: MOV SIDCUT,AL
;DO THE SIDCUT
DRAWIB: MOV AL,IX1 ;GET WIDTH
SUB AL,SIDCUT
JBE DRAWE2
MOV XDEX1,AL
SUB AL,WINDW
JB DRAWIC
MOV AL,WINDW
MOV XDEX1,AL
DRAWIC: MOV AL,SIDCUT
SUB AH,AH
PUSH AX ;SAVE SIDECUT
CALL AIADR1 ;IADR1 <= IADR1 + SIDCUT
POP AX ;RESTORE SIDCUT
CMP MSKFLG,0 ;IS MASKING DISABLED?
JE DRAWID ;....YES
CALL AIADR2 ;....NO. IADR2 <= IADR2 + SIDCUT
DRAWID: MOV AL,MDXCUT
SUB AL,XDEX1
MOV IXSKIP,AL
MOV AL,XDEX1
MOV IX1,AL ;SET COUNTER REFRESH VALUE
MOV AL,BYTE PTR ARG1
ADD AL,SIDCUT
MOV BYTE PTR XPOS,AL
MOV BYTE PTR XPSAV,AL
;GET THE REQUIRED BLOCKSET DATA
MOV AL,BSET ;USE THE BLOCKSET ID
; GET THE ADDRESS OF THE CURRENT BLOCKSET
MOV BX,BTAB ;GET THE BASE ADDRESS OF THE BPT
MOV VMP,BX ;SET UP VMP
MOV VMP0,0
SUB AH,AH ;AL CONTAINS BLOCKSET ID
DEC AL ;ZERO ALIGN IT
ADD VMP,AX ;ADD WORD INDEX TO BASE ADDRESS
CALL GW@VMP ;GET THE BLOCKSET POINTER
MOV VMP,AX ;SET UP VMP
MOV VMP0,0
CALL GW@VMPI
MOV BLKLEN,AH ;# BYTES PER BLOCK
MOV BSIZE,AL ;# OF BLOCKS
MOV BX,VMP ;MOVE V-ADDR OF BLOCKSET DATA
MOV BSADR,BX ;INTO [BSADR]
;CHECK FOR MASKING
CMP MSKFLG,0 ;IS MASKING DISABLED?
JE DRAW ;....YES. USE NORMAL DRAW ROUTINE
JMP DOMASK ; ELSE, GO DO THE MASK DRAW ROUTINE
;
; DRAW THE ICON ITERATION AT [IADR1]
;
; DRAW A ROW OF ICON BLOCKS
DRAW: PUSH DI ; (B5) SAVE GSTACK
CALL SETIADR ; (B5) SET UP SI AND GET BLOCK ID
CALL GTGBLK ;GET GRAPHICS BLOCK [AL] INTO BLOCK
CMP FSIZE,0 ; (B5) SET UP DI ACCORDING TO REAL MEMORY
JE DRW0
DRW: MOV DI,BSADR ; (B5) GET ADDRESS OF THE BLOCK SET
SHL DI,1 ; (B5) CONVERT TO A REAL ADDR
DRW0: CMP FSIZE,0 ; (B5) CALCULATE END-OF-PAGE
JNE DRW1 ; (B5) FITS - EOP AT END OF SEG
MOV AX,SI ; (B5) GET I-PAGE
AND AX,0FE00H ; (B5) ISOLATE BLOCK BITS
ADD AX,200H ; (B5) ADD PAGE LENGTH TO GET EOP
MOV I_EOP,AX ; (B5) SAVE IT
JMP DRW2
DRW1: MOV I_EOP,0
MOV G_EOP,0 ; (B5) SET EOPAGES TO END OF SEGMENT
DRW2: MOV AH,BYTE PTR XPOS ; (B5) GET XPOSITION
MOV AL,BYTE PTR YPOS ; (B5) AND Y
MOV CL,YDEX1 ; (B5) SET UP Y INDEX
MOV BL,XDEX1 ; (B5) SET UP X INDEX
JMP DRW3
FIX_I: CMP FSIZE,0 ; (B5) DECIDE WHICH TYPE OF FIX
JNE FXI3
MOV BYTE PTR XPOS,AH ; (B5) SAVE VARS
MOV BYTE PTR YPOS,AL
MOV YDEX1,CL
MOV XDEX1,BL
MOV AX,IADR1 ; (B5) GET STARTING ADDRESS
AND AX,7F00H ; (B5) ISOLATE BLOCK BITS
ADD AX,100H ; (B5) AND ADVANCE TO NEXT BLOCK
MOV IADR10,0 ; (B7) SET THIS
TEST SI,1 ; (B7) EVEN?
JZ FXI2
MOV IADR10,1 ; (B7) NOPE, ODD
FXI2: AND SI,1FEH ; (B5) ISOLATE WRAP VALUE
SHR SI,1 ; (B7) ALIGN WITH IADR1 IN AX
OR AX,SI ; (B5) AND MAKE THEM COUNT HERE
MOV IADR1,AX ; (B5) SAVE VIRTUAL PAGE
CALL SETIADR ; (B5) GET ICON ID AND RESET SI
CALL GTGBLK ; (B5) GET A GRAPHIC BLOCK
JMP DRW0
FXI3: ADD BP,1000H
JMP DRW$
DRAW$: POPF
JC FIX_I
CMP FSIZE,0 ; (B5) NO END OF PAGE IF FITS
JNE DRW$
CMP SI,I_EOP ; (B5) CHECK END CONDITIONS
JAE FIX_I
DRW$: XCHG AX,DX ; (B5) SAVE X,Y
PUSH ES ; (B5) NO, SAVE IT
MOV ES,BP ; (BP) AND MAKE ADDRESSIBLE
MOV AL,ES:[SI] ; (B5) GET BYTE
POP ES
PUSH ES ; (B5) SAVE IN CASE OF CHANGE
CALL GTGFST ; (B5) GET IT DIRECTLY UNLESS SEG WRAP
POP ES
XCHG AX,DX ; (B5) RESTORE X,Y
; FALL THROUGH INTO DUMP LOOP
DRW3: PUSH AX
PUSH BX
PUSH CX
PUSH BP
PUSH SI
PUSH ES
CALL DUMP ; (B5) DUMP SAVES DI
POP ES
POP SI
POP BP
POP CX
POP BX
POP AX
; UPDATE THE COLUMN COUNT
DRAW1: DEC BL ;DECREMENT THE COLUMN COUNTER
JZ CLPRI2 ; IF WE'RE OUT OF COLUMNS, DO NEXT ROW
INC AH ; ELSE, INCREMENT X COORDINATE
; UPDATE THE BLOCK ID POINTER
DRAW2A: INC SI ; (B5) NEXT ICON BYTE
PUSHF ; (B5) SAVE FLAGS FOR ZERO
JMP DRAW$ ; AND DRAW THE NEXT ICON BYTE
; NEXT ROW
; REFRESH THE COLUMN COUNTER
CLPRI2: MOV BL,IX1
; REFRESH THE X-COORDINATE
MOV AH,BYTE PTR XPSAV
; SKIP BLOCK #'S THAT AREN'T DRAWN AND UPDATE POINTER
MOV DL,IXSKIP
SUB DH,DH
INC DX
;IADR1 <= IADR1 + IXSKIP + 1
ITEN1: ADD SI,DX ; (B5) ADD IN SKIP VALUE
PUSHF
; UPDATE THE ROW COUNTER
INC AL ;INCREMENT THE Y-COORDINATE
DEC CL ;DECREMENT ROW COUNTER
JNZ DRAW$ ; LOOP UNTIL ALL ROWS ARE DONE
POPF
POP DI
RET
ITENT ENDP
PUBLIC SETIADR
SETIADR PROC
MOV AX,IADR1 ;POINT [VMP] TO CURRENT BLOCK ID
MOV VMP,AX
MOV AL,IADR10
MOV VMP0,AL
CALL GB@VMP ;GET THE BLOCK ID INTO AL
MOV BP,ES ; (B5) SAVE SEG
MOV CL,LOC ; (B5) GET LAST EFFECTIVE ADDRESS
XOR CH,CH ; (B5) DO CONVERSION
SHL CX,1 ; (B5) DOUBLE VPAGE
MOV BX,LADD ; (B5) GET LOW ADDRESS BITS
OR BH,CL ; (B5) OR IN HIGH BITS
MOV SI,BX ; (B5) SAVE ICON ADDRESS
RET
SETIADR ENDP
;
; DRAW THE ICON ITERATION AT [IADR1] WITH MASK AT [IADR2]
;
DOMASK PROC
MOV AX,IADR2 ;POINT [VMP] TO THE NEXT MASK BLOCK ID
MOV VMP,AX
MOV AL,IADR20
MOV VMP0,AL
MOV AL,NEGATE ;SAVE NEGATE STATUS
PUSH AX
MOV NEGATE,0 ;NEVER NEGATE THE MASK
CALL GB@VMP ;GET MASK BLOCK ID
CALL GTGBLK ;GET MASK GRAPHICS BLOCK [AL] INTO BLOCK
; COPY [BLOCK] INTO [MASK]
MOV CX,8 ;LOAD LOOP COUNTER
SUB BX,BX ;RESET INDEX
B2MLP: MOV AL,BLOCK[BX]
MOV MBLOCK[BX],AL
INC BX
LOOP B2MLP
MOV AX,IADR1 ;POINT [VMP] TO CURRENT ICON BLOCK ID
MOV VMP,AX
MOV AL,IADR10
MOV VMP0,AL
POP AX ;RESTORE NEGATE
MOV NEGATE,AL
CALL GB@VMP ;GET THE BLOCK ID INTO AL
CALL GTGBLK ;GET GRAPHICS BLOCK [AL] INTO BLOCK
MOV AH,BYTE PTR XPOS ;GET THE X-COORDINATE
MOV AL,BYTE PTR YPOS ; AND THE Y-COORDINATE
; DRAW [BLOCK] AGAINST [MASK] AT X,Y [AX]
CALL DMPMSK
; UPDATE THE COLUMN COUNT
DEC XDEX1 ;DECREMENT THE COLUMN COUNTER
JZ CLPRIA ; IF WE'RE OUT OF COLUMNS, DO NEXT ROW
INC BYTE PTR XPOS ; ELSE, INCREMENT X COORDINATE
; UPDATE THE ICON BLOCK ID POINTER
XOR IADR10,1 ;CHANGE THE STATE OF BIT0
JNZ DM1 ; CONTINUE IF CHANGING FROM A "0" TO A "1"
INC IADR1 ;ELSE BUMP THE REST OF IADR1
; UPDATE THE MASK BLOCK ID POINTER
DM1: XOR IADR20,1 ;CHANGE THE STATE OF BIT0
JNZ DM2 ; CONTINUE IF CHANGING FROM A "0" TO A "1"
INC IADR2 ;ELSE BUMP THE REST OF IADR2
DM2: JMP DOMASK
; NEXT ROW
; REFRESH THE COLUMN COUNTER
CLPRIA: MOV AL,IX1
MOV XDEX1,AL
; REFRESH THE X-COORDINATE
MOV AL,BYTE PTR XPSAV
MOV BYTE PTR XPOS,AL
; SKIP BLOCK #'S THAT AREN'T DRAWN AND UPDATE THE POINTERS
MOV AL,IXSKIP
SUB AH,AH
INC AX ; +1 TO UPDATE THE POINTERS
PUSH AX ;SAVE IXSKIP + 1
CALL AIADR1 ;IADR1 <= IADR1 + IXSKIP + 1
POP AX ;RESTORE IXSKIP + 1
CALL AIADR2 ;IADR1 <= IADR1 + IXSKIP + 1
; UPDATE THE ROW COUNTER
INC BYTE PTR YPOS ;INCREMENT THE Y-COORDINATE
DEC YDEX1 ;DECREMENT ROW COUNTER
JZ DOMA1 ;EXIT WHEN DONE
JMP DOMASK ; ELSE, LOOP
DOMA1: RET
DOMASK ENDP
PAGE
; ----
; SETI
; ----
; COPY ICON [ARG3] INTO ICON [ARG4] WITH
; TOP LEFT CORNER AT X=[ARG1], Y=[ARG2];
; RETURN "0" IF RANGE ERROR, "1" IF OKAY
PUBLIC GSETI,SI0,SI1,SI3
GSETI PROC
CALL ISETUP
;GET STATS OF SOURCE AND DESTINATION
CALL DOFIT ;WILL THE SOURCE FIT IN THE DESTINATION?
JNC SIA ;CARRY CLEAR IF OK
JMP RET0 ;ELSE RETURN A ZERO
; [GPC] WILL BE TEMPORARILY USED FOR AN AUTOINCREMENTING
; VIRTUAL MEMORY POINTER
SIA: MOV AX,GPC ;PUSH [GPC] ON THE STACK
PUSH AX
MOV AL,GPC0 ;PUSH GPC BIT ON THE STACK
PUSH AX
; VMP POINTS TO THE FIRST BYTE OF THE SOURCE ICON
; POINT [GPC] AT THE FIRST BYTE OF THE SOURCE ICON
MOV AX,VMP
MOV GPC,AX
MOV AL,VMP0
MOV GPC0,AL
; POINT [VMP] AT THE CURRENT BYTE OF THE DESTINATION SUB-ICON
SI0: MOV AX,IADR2
MOV VMP,AX
MOV AL,IADR20
MOV VMP0,AL
SI1: CALL GB@GPCI ;GET A BYTE FROM THE SOURCE
CALL PB@VMPI ;PUT IT IN THE DESTINATION
DEC XDEX1 ;OUT OF SOURCE X'S YET?
JNZ SI1 ;....NO. MOVE ANOTHER BYTE
MOV AL,IX1 ;REFRESH COLUMN COUNTER
MOV XDEX1,AL
MOV AL,BYTE PTR IX2 ;GET X-SIZE OF DESTINATION
SUB AH,AH ;ADD TO BASE ADDRESS OF SUB-ICON
CALL AIADR2 ;IADR2 <= IADR2 + [AX]
;TO GET ADDRESS OF NEXT SUB-ROW
SI3: DEC YDEX1 ;OUT OF Y'S YET?
JNZ SI0 ;....YES. RESET X-INDEX
; AND LOOP TILL EMPTY
; RESTORE GPC
POP AX
MOV GPC0,AL
POP AX
MOV GPC,AX
MOV AL,1 ;RETURN A 1 FOR SUCCESS
JMP PUTBYT
GSETI ENDP
PAGE
; -----
; SWAPI
; -----
; SAME AS "SETI" EXCEPT ICON [ARG4] SUB-DATA IS COPIED
; BACK INTO ICON [ARG3]
PUBLIC GSWAPI,GSW0,GSW1,GSW2,PRERR2
GSWAPI PROC
CALL ISETUP ;SETUP ICON
; SOURCE ICON MUST BE IN PRELOAD
MOV AX,IADR1 ;GET ICON ADDRESS
CMP AX,IPURE ;IS SOURCE IN I-PRELOAD?
JB GSW0 ;....YES. SO CONTINUE
; *** ERROR #14: PURITY VIOLATION (SETI/SWAPI) ***
PRERR2: MOV AL,14
JMP GERROR
; SOURCE ICON MUST BE NO LARGER THAN THE DESTINATION ICON
GSW0: CALL DOFIT ;WILL IT FIT IN THE DESTINATION
JNC GSW1 ;CARRY IS CLEAR IF OK
JMP RET0 ;ELSE RETURN A ZERO
; SWAP ICONS
; [GPC] WILL BE TEMPORARILY USED FOR AN AUTOINCREMENTING
; VIRTUAL MEMORY POINTER
GSW1: MOV AX,GPC ;PUSH [GPC] ON THE STACK
PUSH AX
MOV AL,GPC0 ;PUSH GPC BIT ON THE STACK
PUSH AX
; SET UP OUTER LOOP
; POINT [GPC] AT THE SOURCE ICON
GSW2: MOV AX,IADR1
MOV GPC,AX
MOV AL,IADR10
MOV GPC0,AL
; POINT [VMP] AT THE DESTINATION ICON
MOV AX,IADR2
MOV VMP,AX
MOV AL,IADR20
MOV VMP0,AL
; INNER LOOP
GSW3: CALL GB@GPC ;GET A BYTE FROM THE SOURCE
MOV BL,AL ;SAVE IT TEMPORARILY
CALL GB@VMP ;GET DESTINATION BYTE
CALL PB@GPCI ;PUT IT IN THE SOURCE
; AND INCREMENT GPC
MOV AL,BL ;RETRIEVE ORIGINAL SOURCE BYTE
CALL PB@VMPI ;PUT IT IN THE DESTINATION
; AND INCREMENT THE VMP PTR
; TEST INNER LOOP
DEC XDEX1 ;OUT OF SOURCE X'S YET?
JNZ GSW3 ;....NO. MOVE ANOTHER BYTE
; REFRESH INNER LOOP VARIABLES
MOV AL,IX1 ;REFRESH COLUMN COUNTER
MOV XDEX1,AL
; UDATE SOURCE AND DESTINATION ADDRESSES
MOV AL,BYTE PTR IX2 ;ADD X-SIZE OF DESTINATION
SUB AH,AH ; TO BASE ADDRESS OF SUB-ICON
CALL AIADR2 ;TO GET ADDRESS OF NEXT ROW
MOV AL,BYTE PTR IX1 ;ADD X-SIZE OF SOURCE
SUB AH,AH ; TO BASE ADDRESS OF SOURCE
CALL AIADR1 ;TO GET ADDRESS OF NEXT ROW
; TEST OUTER LOOP
DEC BYTE PTR YDEX1 ;OUT OF Y'S YET?
JNZ GSW2 ; NO, RESET ROW INDEX AND LOOP
;RESTORE THE GPC
POP AX
MOV GPC0,AL
POP AX
MOV GPC,AX
;SET UP RETURN VALUE
MOV AL,1 ;RETURN A 1 FOR SUCCESS
JMP PUTBYT
GSWAPI ENDP
PAGE
; -----
; SOUND
; -----
; PLAY SOUND [ARG1] (1-127)
PUBLIC GSOUND
GSOUND PROC
MOV AX,ARG1
OR AX,AX
JZ SNDERR ;RANGE ERROR
JS SNDERR ;RANGE ERROR
CMP AX,NSNDS + 1
JAE SNDERR ;SOUND NOT DEFINED
DEC AX
SHL AX,1 ;MAKE IT A WORD OFFSET
MOV BP,AX ;GET SOUNDLIST PTR
MOV SI,DS:[BP+STABLE]
CALL SI ;CALL THE SOUND ROUTINE
RET
GSOUND ENDP
PUBLIC SNDERR
SNDERR PROC
; *** ERROR #13: ILLEGAL SOUND EFFECT ***
; MOV AL,0DH
; JMP GERROR
; CHANGE MADE PER LIM'S REQUEST
CALL SND1
CALL SND4
CALL SND1
CALL SND4
RET
SNDERR ENDP
;
; ACTUAL SOUND EFFECTS
;
PUBLIC SND1
;BOOP
SND1 PROC
MOV CX,500 ;FREQ
MOV DX,7 ;DURATION * .05 SEC
JMP TONE
SND1 ENDP
PUBLIC SND2
;CLICK
SND2 PROC
MOV CX,3000 ;FREQ
MOV DX,1 ;DURATION * .05 SEC
JMP TONE
SND2 ENDP
PUBLIC SND3
;TING
SND3 PROC
MOV CX,2600 ;FREQ
MOV DX,5 ;DURATION * .05 SEC
JMP TONE
SND3 ENDP
PUBLIC SND4
;RAZZ
SND4 PROC
MOV CX,120 ;FREQ
MOV DX,20 ;DURATION * .05 SEC
JMP TONE
SND4 ENDP
PAGE
; ----
; RAND
; ----
; RETURN A RANDOM VALUE BETWEEN 1 AND [ARG1]
PUBLIC GRAND
GRAND PROC
MOV AX,ARG1
SUB AH,AH ; HACK RANDOM TO ONE BYTE
MOV RTEMP,AX ; SAVE ARGUMENT FOR LATER
MOV AX,RSEED1
MOV BX,RSEED2
MOV RSEED2,AX
CLC
RCR AX,1
RCR BX,1
XOR RSEED1,BX ;PUT XOR'D NUBMER INTO RSEED1
MOV AX,RSEED1 ;THIS IS RANDOM VALUE
AND AX,0EFFFH ;CHOP HIGH BIT FOR DIVIDE
SUB DX,DX ;CLEAR MOST SIGN. PORTION OF 32 BIT WORD
DIV RTEMP ;DIVIDE WORD BY WORD
MOV AX,DX ;MOVE REMAINDER INTO AX
INC AX ;MAKE SURE NO ZERO
MOV VALUE,AX ;SAVE IT IN VALUE
JMP PUTVAL
GRAND ENDP
PAGE
; -----
; CLEAR
; -----
;
; CLEAR SCREEN TO WHITE IF [ARG1] IS NEGATIVE
; CLEAR SCREEN TO BLACK IF [ARG1] IS POSITIVE
;
PUBLIC GCLEAR,GCLR1,GCLR2,GCLR3
GCLEAR PROC
PUSH DI ;SAVE AFFECTED REGISTER(S)
PUSH CX
PUSH ES
MOV AX,VID_SG ;POINT AT THE VIDEO BUFFER
MOV ES,AX
MOV AX,ARG1 ;GET THE ARGUMENT
CMP AX,0 ; (B5) DO BLACK DIFFERENTLY
JE GCLR4
MOV BP,AX ; GET WORD
AND BP,3FH ; MASK OFF HI NIBBLE FOR BIT SHIFT PROBLEM
MOV DI,0 ;SET UP INDEX
MOV BX,1E00H ;SET UP LOOP COUNTER
; PAINT THE SCREEN WITH AX
GCLR1: XCHG AX,BP ; GET MASK BYTE
STOSB
XCHG AX,BP
MOV CX,79 ; DO A LINE - 1 AT A TIME
REPZ STOSB
SUB BX,80 ; SUBTRACT FROM TOTAL
JNZ GCLR1
MOV DI,2000H
MOV BX,1E00H
GCLR2: XCHG AX,BP
STOSB
XCHG AX,BP
MOV CX,79 ; (B4) DO A LINE
REPZ STOSB
SUB BX,80
JNZ GCLR2
;CLEANUP WHEN COMPLETE
GCLR3: POP ES ;RESTORE THOSE REGISTERS
POP CX
POP DI
RET
GCLR4: MOV DI,0
MOV CX,0F00H ; (B5) DO WHOLE SCREEN
REPZ STOSW
MOV DI,2000H ; (B5) ODD BYTES
MOV CX,0F00H ; (B5) COUNT
REPZ STOSW
JMP GCLR3
GCLEAR ENDP
PAGE
; ------
; WINDOW
; ------
; SET THE WINDOW LIMITS TO THE RECTANGLE WHOSE UPPER LEFT SCREEN
; COORDINATE IS AT (locx1, locy1) AND LOWER RIGHT SCREEN COORDINATE
; IS AT (locx2, locy2).
PUBLIC GWIND,WISK0,WISK1,WISK2,WISK3,WISK4
PUBLIC WISK5,WISK6,WISK7,WISK8
GWIND PROC
SUB BX,BX ;RESET INDEX
;TEST IF - OR > 255
WISK0: MOV AX,WORD PTR ARG1[BX] ;GET COORDINATE
OR AH,AH ;DOES MSB=0?
JZ WISK1 ;....YES. COORDINATE IS EITHER + OR 0
SUB AX,AX ;ELSE IT'S NEGATIVE
MOV WORD PTR ARG1[BX],0 ; CLIP IT TO 0
;IT'S EITHER + OR 0
WISK1: PUSH BX ;SAVE INDEX
AND BX,2 ;IS IT AN X OR Y COORDINATE?
POP BX ;RESTORE INDEX
JZ WISK2 ;IT'S AN X-COORDINATE
;Y COORDINATE CLIPPING
CMP AX,23 ;IS THE Y-COORDINATE ON SCREEN?
JBE WISK3 ;....YES
MOV ARG1[BX],23 ;....NO. CLIP IT
JMP WISK3
;X COORDINATE CLIPPING
WISK2: CMP AX,39 ;IS THE X-COORDINATE ON SCREEN?
JBE WISK3 ;....YES
MOV ARG1[BX],39 ;....NO. CLIP IT
WISK3: INC BL ;UPDATE INDEX TO POINT
INC BL ;AT THE NEXT ARG
CMP BL,8 ;HAVE WE DONE ALL OF THE ARGS
JNZ WISK0 ;....NO. KEEP LOOPING
; SWITCH X-COORDINATES IF REQUIRED
WISK4: MOV AL,BYTE PTR ARG1 ;GET X1
MOV AH,BYTE PTR ARG3 ;GET X2
CMP AL,AH ;IF X1 < X2
JL WISK5 ; THEN DON'T SWAP
XCHG AL,AH ;SWAP X1 AND X2
WISK5: MOV BYTE PTR WINDX1,AL ;SAVE X1
MOV BYTE PTR WINDX2,AH ;SAVE X2
;COMPUTE WINDOW WIDTH
SUB AH,AL ;WINDW = WINDX2 - WINDX1 + 1
INC AH
MOV WINDW,AH
; SWITCH Y-COORDINATES IF REQUIRED
WISK6: MOV AL,BYTE PTR ARG2 ;GET Y1
MOV AH,BYTE PTR ARG4 ;GET Y2
CMP AL,AH ;IF Y1 > Y2
JL WISK7 ; THEN DON'T SWAP
XCHG AL,AH ;SWAP Y1 AND Y2
WISK7: MOV BYTE PTR WINDY1,AL ;SAVE Y1
MOV BYTE PTR WINDY2,AH ;SAVE Y2
;COMPUTE WINDOW HEIGHT
WISK8: SUB AH,AL ;WINDH = WINDY2 - WINDY1 + 1
INC AH
MOV WINDH,AH
RET
GWIND ENDP
PAGE
; -----
; GITER
; -----
;
; INITIALIZE ITERATION TABLE AT WORD ADDRESS ARG1
;
GITER PROC
MOV AX,ARG1 ;POINT TO TABLE BASE ADDRESS
MOV VMP,AX
MOV VMP0,0
CALL GB@VMPI ;[BYTE0] NUMBER OF ENTRIES IN TABLE
MOV ITICN,AL
MOV AL,1 ;[BYTE1] SET CURRENT ENTRY=1
CALL PB@VMPI
; WE'RE POINTING TO THE DATA STRUCTURE OF THE FIRST ENTRY
; AND SUCCEEDING ENTRIES
GITER1: CALL GW@VMPI ;GET THE ICON WORD-PTR ADDRESS IN [AX]
; SAVE VIRTUAL MEMORY POINTER
MOV BX,VMP
PUSH BX
MOV BL,VMP0
PUSH BX
CALL GETI ;GET THE ICON INFORMATION
; RESTORE VIRTUAL MEMORY POINTER
POP BX
MOV VMP0,BL
POP BX
MOV VMP,BX
; SKIP ICON'S HORIZONTAL AND VERTICAL POSITION
ADD VMP,2 ;TWO WORDS
; SET UP REMAINING DATA
SUB AL,AL ;NEGATE=0 FOR POSITIVE IMAGE
CALL PB@VMPI
MOV AL,1 ;CURRENT ITERATION=1
CALL PB@VMPI
MOV AL,BSET ;SAVE BLOCKSET IDENTIFIER
CALL PB@VMPI
MOV AL,ITERS ;SAVE NUMBER OF ITERATIONS
CALL PB@VMPI
MOV AL,IX1 ;SAVE WIDTH
CALL PB@VMPI
MOV AL,IY1 ;SAVE HEIGHT
CALL PB@VMPI
DEC ITICN ;ANY MORE ENTRIES?
JNZ GITER1 ;....YES
RET
GITER ENDP
;------------------------
PUBLIC GRESTR
GRESTR PROC
MOV DSKDIR,0 ;INDICATE RESTORE (READ)
JMP OSV0$
GRESTR ENDP
;
; SAVE
;
PUBLIC GSAVE
GSAVE PROC
MOV DSKDIR,1 ;INDICATE SAVE (WRITE)
;
;COMMON ENTRYPOINT FOR SAVE AND RESTORE
;
; DO A DISK RESET IN CASE THEY WANT TO CHANGE DISKS
; UNFORTUNATELY WE HAVE TO ASSUME A 1 DRIVE SYSTEM AT THIS POINT
OSV0$: MOV AH,CFCLOSZ ; (7s) CLOSE THE GAME FILE
MOV BX,GAMHNDL ; (7s) FOR REINSERTION
INT 21H
MOV AH,CDRESET ; (K6) WILL RESET FLUSH BUFFERS?
INT 21H ; (K6) HOPE SO...
MOV AH,2 ; SET CURSOR POSITION
MOV DX,100H ; COORD'S ARE 0,0
MOV BH,0 ; ACTIVE PAGE 0
INT 10H ; TO PREVENT INVISIBLE CURSOR MOVEMENT
; PRINT "FILENAME (DEFAULT IS drv:filename.ext):"
; PRINT "FILENAME (DEFAULT IS drv:"
OSV0A: MOV AX,OFFSET SAV0 ;PRINT "INSERT SAVE DISK THEN ENTER FILE..."
CALL MPRNT ; WITH CRLF
MOV AX,OFFSET SAV1 ;PRINT "FILE NAME (DEFAULT IS "
CALL NPRNT ; WITH NO CRLF
TEST DRVFLG,1 ; HAS USER SPECIFIED A DRIVE
JNZ OSVD1 ; YES, USE USER DEFAULT DRIVE
MOV AL,CURDRV ;AL <= DRIVE NO.
ADD AL,65 ;....NO. ADD 65 TO GET ASCII CHARACTER
CALL PRTOUT ;PRINT DRIVE B, C, OR D
MOV AL,":" ;PRINT ":"
CALL PRTOUT
; PRINT "filename"
OSVD1: PUSH SI ; (6) SAVE THIS
MOV SI,OFFSET DATFILE ; (6) POINTER TO SAVE FILENAME (6)
MOV CX,SI ; (6) SAVE POINTER
CLD ; (6) SET DIR INCREMENT
OSVD2: LODSB ; (6) GET A BYTE INTO AL
TEST AL,AL ; (6) CHECK IF NULL
JZ OSVD3 ; (6) EXIT THIS LOOP
CALL PRTOUT ; (6) OUTPUT IT
JMP OSVD2
OSVD3: SUB SI,CX ; (6) CALCULATE NUMBER OF CHARS
MOV CX,SI ; (6) GET NUMBER BACK INTO CX
POP SI ; (6) RESTORE THIS
; PRINT " ): "
MOV AX,OFFSET SAV2 ;PRINT " ): "
CALL NPRNT ; WITH NO CRLF
; GET LOSER'S SAVE FILENAME FROM THE KEYBOARD
OSVD4A: MOV BX,OFFSET INBUF ;^ KEYBOARD INPUT BUFFER
MOV AL,15 ;(6) COULD BE A WHOLE PATH
MOV BYTE PTR [BX],AL ;SET UP BUFFER TO TAKE 15 CHARACTERS
MOV DX,BX ;DX ^ BUFFER
MOV AH,CRDLIN ;READ A LINE FROM THE KEYBOARD
INT 21H
; PARSE THE FILENAME
CALL SAVESAV ; (7o) PRESERVE THE SAVE NAME
PUSH SI
PUSH DI
PUSH CX ; SAVE THIS
PUSH ES
PUSH DS
POP ES
LEA SI,INBUF[1] ; POINT TO NUM CHARS READ
LODSB ; GET NUM
TEST AL,AL ; ANY CHARS READ
JZ LOOK
MOV CL,AL ; STORE IT
XOR CH,CH ; CLEAR TOP HALF OF COUNTER
MOV DI,OFFSET DATFILE ; (6) DI ^FCB
REPZ MOVSB ; TRANSFER THE STRING IF ANY
MOV AL,0 ; (6) GET A NULL
STOSB ; (6) DROP IT IN AT END OF STRING
LOOK: MOV SI,OFFSET DATFILE ; (6) IS THERE A ':'
INC SI
LODSB ; (6) CHECK IT
CMP AL,':' ; (6) IF SO, FIX DEFDRV
JNZ NODRV ; (6) NOPE...
MOV DRVFLG,1 ; (6) SET FLAG
MOV AL,DATFILE ; (7n) GET DRIVE LETTER
AND AL,5FH ; (7n) UPPERCASIFY
SUB AL,'A' ; (7n) BASIFY
CMP AL,CURDRV ; (7n) DON'T CHANGE IF UNNECESSARY
JZ DRV
MOV DL,AL ; (7n) SET UP FOR CHGDSK
MOV AH,CSELDSK ; (7n) SELECT DISK IN DL
INT 21H
MOV AH,CURDSK ; (7n) CHECK IF IT WORKED
INT 21H
MOV CURDRV,AL ; (7n) SAVE IT
JMP DRV
USEDEF: MOV SKPDRV,1 ; (7n) DON'T OUTPUT A DRIVE
NODRV: MOV DRVFLG,0
DRV: POP ES
POP CX
POP DI ;RESTORE'M
POP SI
CALL MCRLF ; (7) SCROLL PROPERLY
; OPEN THE FILE IF OP RESTORE
CMP DSKDIR,1 ;OP SAVE?
JE OSVOPW ;....YES
MOV DX,OFFSET DATFILE ; 6) ....NO. OP RESTORE. DX ^ FCB
MOV AH,CFOPENZ ; (6) SELECT DOS OPEN FUNCTION
MOV AL,0 ; (6) FILE OPEN FOR READING
INT 21H ;OPEN THE FILE
JC OSVPSX ; (6) ....NO. FILE NOT FOUND
JMP OSVPS ;....YES
;
; **** ERROR **** [File Not Found]
;
OSVPSX: MOV AX,OFFSET ERR1 ;^ "SAVE FILE NOT FOUND" MSG
JMP OSVPER ;ERROR EXIT
; CREATE THE FILE IF OP SAVE
OSVOPW: MOV DX,OFFSET DATFILE ; (6) ^ FCB
MOV CX,0 ; (6) NO SPECIAL ATTRIBUTE
MOV AH,CFCREAZ ; (6) SELECT DOS CREATE FUNCTION
INT 21H
JC OSVOPDF
JMP OSVPS ;....YES
;
; **** ERROR **** [Directory Full]
;
OSVOPDF:MOV AX,OFFSET ERR4 ;....NO. ^ "NO ROOM IN DIRECTORY" MSG
JMP OSVPER ;HOP TO ERROR EXIT
;
; WE'VE GOT AN OPEN FILE (WHICH WE CREATED IF IT WAS A SAVE FUNCTION)
; AND ARE READY TO EITHER READ (RESTORE) OR WRITE (SAVE) AS REQUIRED
OSVPS: MOV DATHNDL,AX ; SAVE FILE HANDLE FOR DUMP/LOAD
XCHG DI,SP
MOV AH,GPC2 ; SAVE GPC
MOV AL,GPC1
PUSH AX
MOV AL,GPC0 ; SAVE LOW BIT
PUSH AX ; OH, WHAT A WASTE
PUSH GLOCS ; SAVE LOCALS TABLE POINTER
PUSH OLDGSP ; RICK'S OLD G-STACK POINTER
PUSH ZORKID ; SAVE GAME ID
; SUB SP,OFFSET GSTK_BT ; (7v) RELATIVISE THE POINTER
; MOV GSTK_BT,SP ; PUT DEST. INDEX INTO STACKBOTTOM
mov ax,sp
sub ax,offset gstk_bt ; make sp relative, but not in SP
mov gstk_bt,ax ; the other way is dangerous
MOV SP,DI ; RESTORE SP
PUSH ES
MOV AX,SS ; FLOP STACK SEG AND EXTRA SEG
MOV ES,AX ; ES<=SS
MOV DX,OFFSET GSTK_BT ; CORE LOCATION
MOV CX,LSTACK*2 ; (6) DO TRANSFER HERE
MOV BX,DATHNDL ; (6) GET HANDLE
MOV AH,CRDRNDZ ; SETUP FOR A READ
ADD AH,DSKDIR ; MAKE IT A WRITE IF NECESSARY
INT 21H ;
CMP AX,CX ; DID WE GET IT ALL
PUSHF ;SAVE RETURN CODE
MOV DI,SP ;DI ^ CURRENT ZSTACK POSITION
; MOV SP,GSTK_BT ;SP <= ZSTACK POINTER
; ADD SP,OFFSET GSTK_BT ; (7v) RELATIVISE THIS VALUE
mov ax,gstk_bt
add ax,offset gstk_bt
mov sp,ax
POP AX ; POP OFF AND COMPARE FOR ZORKID
CMP AX,ZORKID ;ID OK?
JE OSVT0 ;....YES
xchg di,sp
popf
pop es
MOV AL,18 ; SAVE ERROR
JMP GERROR ;....NO. FATAL ERROR
OSVT0: POP OLDGSP ; RESTORE OLD G-STACK POINTER FOR RETURN
POP GLOCS ;RESTORE LOCALS AND ZIP PROGRAM COUNTERS
POP AX ; RESTORE LOW BIT
MOV GPC0,AL ; FROM THIS WORD
POP AX ; RESTORE GPC
MOV GPC2,AH
MOV GPC1,AL
XCHG DI,SP ;RESTORE MACHINE SP AND ZSTACK POINTER
POPF ;RESTORE REGS
POP ES
; BEGIN SAVE OF G-FILE PRELOAD
;
MOV BX,DATHNDL ; GET HANDLE
MOV AH,DSKDIR ; GET DIRECTION
PUSH DS ; SAVE IT
MOV CX,SEG0 ; GET GAME SEG
MOV DS,CX ; ADDRESS IT
PUSH BX ; USE THIS AS INDEX
MOV BX,GEND ; DO <GET <LENGTH>>
MOV CX,[BX] ; GET LENGTH WORD
POP BX ; RESTORE HANDLE
XCHG CH,CL ; BYTE SWAP
SHL CX,1 ; CONVERT TO BYTES
MOV DX,0 ; STARTS AT BEGINNING OF THE SEG
ADD AH,CRDRNDZ ; START OUT WITH READ
INT 21H ; READ IT
JMP OSVPS2
;
;
OSVPS2: JC OSV4 ; SKIP ON ERROR
; SUCESSFUL TRANSFER OF THE G-FILE PRELOAD.
; TRANSFER I-FILE PRELOAD
PUSH BX ; SAVE HANDLE
PUSH DS ; SAVE BUFFER SEG
MOV AX,CS ; GET DATA SEG
MOV DS,AX ; SO THAT WE CAN DO REAL ADDRESSING
MOV AX,ICODE ; GET INFO OUT OF I-PRE
MOV VMP,AX ; SET UP VIRTUAL MEMORY POINTER
MOV VMP0,0 ; TO GET
MOV AX,IEND ; THE END OF I-PRELOAD
CALL ADDVMP ; ADD IN OFFSET
CALL GW@VMP ; GET THE WORD
MOV DX,LADD ; GET THE LOW ADDRESS BITS
MOV BL,LOC ; GET BLOCK NUMBER
MOV BH,0 ; ZERO TOP HALF
SHL BX,1 ; CONVERT TO ADDRESS
OR DH,BL ; TURN ON BLOCK BITS
SUB DX,IEND ; GET START BYTE ADDRESS
MOV CX,AX ; GET NUMBER OF WORDS IN I-PRE
SHL CX,1 ; CONVERT TO BYTES
MOV AH,CRDRNDZ ; GET FUNCTION
ADD AH,DSKDIR ; ADD IN DIRECTION
POP DS ; RESTORE BUFFER SEG
POP BX ; RESTORE HANDLE
INT 21H ; READ/WRITE THE I-FILE
JC OSV4
; SUCCESS IN GENERAL -- CLOSE FILES
POP DS ; HANDLE IS STILL IN BX
MOV AH,CFCLOSZ ; CLOSE THE SAVE FILE
INT 21H ; THRU DOS
CALL GAMOPEN ; (7s) OPEN GAME FILE
MOV AH,2 ; SET CURSOR POSITION
MOV DX,100H ; COORD'S ARE 0,0
MOV BH,0 ; ACTIVE PAGE 0
INT 10H ; TO PREVENT INVISIBLE CURSOR MOVEMENT
MOV DL,CURDRV ; (7n) RETURN TO SAVE DISK
MOV AH,CSELDSK ; (7n) AND SELECT IT FOR OPEN
INT 21H
MOV DSKDIR,-1 ; RESET SAVE/RESTORE FLAG
MOV ARG1,0 ; (B7) CLEAR OUT THE SCREEN
CALL GCLEAR ; (B7) IN CASE OF ERRORS
JMP PGOOD
;
; **** ERROR **** [Unable to Read File]
;
OSV4: POP DS
MOV AX,OFFSET ERR6 ;^ "Read of SAVE file failed."
CMP DSKDIR,1 ;OP SAVE?
JNE OSVPRR ;....NO
MOV AH,CFCLOSZ ; (6) ....YES. CLOSE IT
MOV BX,DATHNDL ; (6) GET HANDLE FOR CLOSE
INT 21H
;
; **** ERROR **** [Diskette Full]
;
MOV DX,OFFSET DATFILE ; (7n) DELETE THE PIECE OF A FILE
MOV AH,CFDELEZ ; (7n) SO AS NOT TO CONFUSE
INT 21H
MOV AX,OFFSET ERR5 ;^ "No room on diskette for SAVE file."
OSVPRR: JMP OSVPER
GSAVE ENDP
;
; ERROR EXIT R/W operations
;
OSVPER PROC
CALL MPRNT ;PRINT ERROR MESSAGE
CALL ZRESTOR ; (7o) RESTORE OLD SAVE NAME
CALL GAMOPEN ; (7v) OPEN IT
MOV AH,2 ; SET CURSOR POSITION
MOV DX,100H ; COORD'S ARE 0,0
MOV BH,0 ; ACTIVE PAGE 0
INT 10H ; TO PREVENT INVISIBLE CURSOR MOVEMENT
MOV ARG1,0 ; (B7) CLEAR OUT THE SCREEN
CALL GCLEAR ; (B7) IN CASE OF ERRORS
MOV DSKDIR,-1 ; RESET SAVE/RESTORE FLAG
JMP PBAD
OSVPER ENDP
;
PUBLIC SAVESAV,ZRESTOR,SCRSAVE
SCRSAVE PROC
PUSH AX ; (7p) SAVES
PUSH SI
PUSH DX
MOV SI,OFFSET DATFILE ; (7p) POINTER TO SAVE
SCRS0: LODSB ; (7p) GET A CHARACTER
TEST AL,AL ; (7p) LOOK FOR A NULL
JNZ SCRS1 ; (7p) DIE ON A NULL
POP DX
POP SI
POP AX
RET ; (7p) RETURN
SCRS1: MOV DL,AL ; (7p) GET CHARACTER INTO DL
MOV AH,CPROUT ; (7p) FOR DOS"IE" OUTPUT
INT 21H ; (7p) PRINT IT
JMP SCRS0
SCRSAVE ENDP
SAVESAV PROC
PUSH DI ; (7o) SAVES
PUSH SI
PUSH AX
PUSH ES
MOV AX,DS ; (7o) SET UP REGS
MOV ES,AX
MOV AL,CURDRV ; (7o) SAVE CURRENT DRIVE SPEC
MOV LASTDRV,AL ; (7o) IN CASE OF FAILURE
MOV AL,DRVFLG ; (7o) TEST DRIVE FLAG
MOV LSTDFLG,AL ; (7o) IN CASE OF FAILURE
MOV SI,OFFSET DATFILE ; (7o) SETUP SOURCE AND DESTINATION
MOV DI,OFFSET LASTSAV
COPYLP: LODSB ; (7o) GET A BYTE
STOSB ; (7o) STORE A BYTE
TEST AL,AL ; (7o) STOP ON A NUL
JNZ COPYLP ; (7o) CONTINUE THE COPY
POP ES
POP AX ; (7o) RESTORES
POP SI
POP DI
RET
SAVESAV ENDP
;
;
ZRESTOR PROC
PUSH DI ; (7o) SAVES
PUSH SI
PUSH AX
PUSH ES
MOV AX,DS ; (7o) SET UP REGS
MOV ES,AX
MOV AL,LSTDFLG ; (7o) GET LAST DRIVE FLAG
MOV DRVFLG,AL ; (7o) AND RESTORE IT
MOV AL,LASTDRV ; (7o) GET ORIGINAL DRIVE SPEC
CMP AL,CURDRV ; (7o) HAS IT CHANGED
JZ ZRES1
MOV DL,AL ; (7o) SELECT DISK
MOV AH,CSELDSK ; (7o) TO CORRECT
INT 21H
MOV CURDRV,DL ; (7o) UPDATE THIS BABY
ZRES1: MOV DI,OFFSET DATFILE ; (7o) SETUP SOURCE AND DESTINATION
MOV SI,OFFSET LASTSAV
ZRESLP: LODSB ; (7o) GET A BYTE
STOSB ; (7o) STORE A BYTE
TEST AL,AL ; (7o) STOP ON A NUL
JNZ ZRESLP ; (7o) CONTINUE THE COPY
POP ES
POP AX ; (7o) RESTORES
POP SI
POP DI
RET
ZRESTOR ENDP
PUBLIC GAMOPEN
; THIS PROCEDURE OPENS THE GAME FILE AFTER CLOSING ALL FILES.
; IF THE GAME FILE IS NOT ON THE DISK, IT PROMPTS FOR REINSERTION
GAMOPEN PROC
PUSH DX
PUSH CX
MOV CX,0 ; (B5) INDICATE FIRST PASS
MOV DL,DEFDRV ; (7o) GET DRIVE WHERE GAME IS
MOV AH,CSELDSK ; (7o) TO MAKE OPEN WIN
INT 21H ; (7o) DO IT
MOV AH,CFCLOSZ ; (7s) TRY CLOSING INSTEAD OF RESET
MOV BX,GAMHNDL ; (7s) OF THE GAME FILE
INT 21H ; (7o) TO MAKE SURE THIS WINS
GOPEN0: MOV AH,CFOPENZ ; (6) ASCIZ STRINGS
MOV AL,0 ; (6) OPEN FOR READING
MOV DX,OFFSET GAMFILE ; (7o) OPEN THE FILE AGAIN
INT 21H
JNC GOPEN1 ; (7o) IF SUCCESS, CONTINUE
INC CX
MOV AX,OFFSET SAV3 ; (7o) OTHERWISE PROMPT
CALL MPRNT
CALL MCHRI ; (7o) AND WAIT FOR A CHARACTER
JMP GOPEN0 ; (7o) AND LOOP
GOPEN1: MOV GAMHNDL,AX ; (7o) SAVE GAME HANDLE
MOV DL,CURDRV ; (7v) RESTORE SAVE DRIVE
MOV AH,CSELDSK ; (7v) SO THAT WE ARE NOT CONFUSED
INT 21H ; (7v) THANKS MAX
CMP CX,0 ; (B5) CHECK FOR PAUSE
JNE GOPEN2 ; (B5) NOT NECESSARY
MOV AX,OFFSET STRIKE ; (B5) STRIKE ANY KEY MESSAGE
CALL NPRNT ; (B5) PRINT IT
CALL MCHRI ; (B5) AND WAIT
GOPEN2: POP CX ; (B5) AND RESTORE THIS ONE
POP DX
RET
GAMOPEN ENDP
MCHRI PROC
MOV AH,CNOECHO
INT 21H
RET
MCHRI ENDP
;------------------------
PAGE
; ----
; LOAD
; ----
;
; PURPOSE: THIS ROUTINE LOADS THE SPECIFIED AMOUNT OF DATA INTO A
; SPECIFIED BLOCK OF PRELOAD. IF ARG3 IS PROVIDED, THEN
; THIS IS A TRANSFER FROM DISK. ELSE, IT IS AN I/O PORT
; OPERATION.
;
; INPUTS: ARG1=TABLE VIRTUAL ADDRESS
; ARG2=LENGTH IN WORDS
; ARG3=FILENAME POINTER (OPTIONAL)
;
; OUTPUTS: DATA IS TRANSFERED TO PRELOAD, IF THE OPERATION IS
; ILLEGAL OR FAILS FOR ANY REASON, A "0" IS RETURNED
; VIA {RET0}. ELSE A "1" IS RETURNED VIA {RET1}.
;
; REGISTERS DESTROYED: ALL
;
PUBLIC GLOAD
GLOAD PROC
CMP NARGS,3 ;IS THIS A FILE TRANSFER REQUEST?
JE GLD0 ;....YES
JMP LDIO ;....NO, IT'S AN I/O TRANSFER
;
; FILE TRANSFER REQUEST
;
; ASSUME THAT THE DEFAULT DISK IS TO BE USED
; CLOSE THE GAMEFILE
GLD0: MOV AH,CFCLOSZ ;CLOSE THE GAME FILE FOR REINSERTION
MOV BX,GAMHNDL ;IN CASE THIS IS A 1 DRIVE SYSTEM,
INT 21H ; OR THE DEFAULT DRIVE IS SPECIFIED
MOV AH,CDRESET ;AND FLUSH
INT 21H
; PARSE THE FILENAME
MOV AX,ARG3 ;SET UP VMP TO POINT TO THE FILENAME
MOV VMP,AX
MOV VMP0,0
CALL GW@VMPI ;GET THE LENGTH OF THE FILENAME
MOV CX,AX ;IN CX FOR USE AS A LOOP COUNTER
MOV BX,OFFSET DATFILE ;^ FCB
;GET THE FILENAME INTO THE FCB
GLD1: CALL GB@VMPI ;GET A CHARACTER
MOV [BX],AL ;PUT IT IN THE FCB
INC BX ;NEXT CHARACTER
LOOP GLD1 ; GET THE REST
MOV BYTE PTR [BX],0 ;DROP IN A NUL AT THE END OF THE NAME
PUSH DI ;SAVE SOME REGISTERS
PUSH DS
POP ES ;BOTH ES & DS POINT AT THE DATA SEGMENT
; OPEN THE DATA FILE
MOV AH,CFOPENZ ;SELECT OPEN FILE
MOV AL,0 ;SELECT READ
MOV DX,OFFSET DATFILE ;POINT AT THE ASCIIZ STRING
INT 21H ;MDOS CALL
JNC GLD2
JMP LDERR1
GLD2: MOV DATHNDL,AX ;SAVE THE FILE HANDLE
; START THE ACTUAL TRANSFER OPERATION
RESTENT:CMP ARG2,0 ;DO WE HAVE ANY DATA?
JNE GLDLP ;....YES
JMP LDERR3 ;....NO
GLDLP: SUB ARG2,256 ;IS THIS A FULL BLOCK?
JB GLD3 ;....NO
; READ A RECORD INTO DS:DX
MOV DX,OFFSET XFRBUF ;POINT AT THE TRANSFER BUFFER
MOV CX,RECSIZ ;READ A 512 BYTE RECORD (256 WORDS)
MOV BX,DATHNDL ;GET THE FILE HANDLE
MOV AH,CRDRNDZ ;SELECT READ RANDOM RECORD
INT 21H ;INVOKE FUNCTION CALL
CMP AX,CX ;DID WE GET A FULL RECORD?
JE GLDLPA ;....YES
JMP LDERR3 ;....NO
GLDLPA: MOV CX,RECSIZ ;GET RECORD SIZE (IN BYTES)
MOV BX,OFFSET XFRBUF ;^ TRANSFER BUFFER (SOURCE)
MOV AX,ARG1 ;SETUP VMP (DESTINATION)
MOV VMP,AX
MOV VMP0,0
GLDLP1: MOV AL,BYTE PTR [BX] ;GET THE BYTE
CALL PB@VMPI ;PUT IT IN VM AND AUTOINC
INC BX ;NEXT BYTE
LOOP GLDLP1
ADD ARG1,256 ;UPDATE DESTINATION POINTER
JMP GLDLP ;KEEP GOING UNTIL WE'VE DONE ALL THE
; FULL RECORDS
GLD3: ADD ARG2,256 ;SEE IF THERE'S A PARTIAL RECORD
JZ GLD5 ;....NO
MOV DX,OFFSET XFRBUF ;POINT AT THE TRANSFER BUFFER
MOV CX,ARG2 ;GET THE SIZE
SHL CX,1 ;BYTIFY IT
MOV BX,DATHNDL ;GET THE FILE HANDLE
MOV AH,CRDRNDZ ;SELECT READ RANDOM RECORD
INT 21H ;INVOKE FUNCTION CALL
CMP AX,CX ;DID WE GET ALL OF THE PARTIAL RECORD?
JE GLD4 ;....YES
JMP LDERR3 ;....NO
GLD4: MOV CX,ARG2 ;GET RECORD SIZE (IN WORDS)
SHL CX,1 ;BYTIFY
MOV BX,OFFSET XFRBUF ;^ TRANSFER BUFFER (SOURCE)
MOV AX,ARG1 ;SETUP VMP (DESTINATION)
MOV VMP,AX
MOV VMP0,0
GLDLP2: MOV AL,BYTE PTR [BX] ;GET THE BYTE
CALL PB@VMPI ;PUT IT IN VM AND AUTOINC
INC BX ;NEXT BYTE
LOOP GLDLP2
;CLOSE THE FILE
GLD5: CMP DSKDIR,-1 ;IF NOT -1, THEN SAVE OR RESTORE
JZ GLD5A
CLC ; CLEAR CARRY TO TELL S/R
RET
GLD5A: MOV BX,DATHNDL ;GET THE FILE HANDLE
MOV AH,CFCLOSZ ;CLOSE IT
INT 21H
; REOPEN THE GAME FILE
MOV AH,CFOPENZ
MOV AL,0 ;OPEN FOR READ
MOV DX,OFFSET GAMFILE
INT 21H
JNC GLD6
JMP DSKERR
GLD6: MOV GAMHNDL,AX ;SAVE THE FILE HANDLE
POP DI ;RESTORE GSTACK POINTER
JMP RET1 ;RETURN A "1"
LDERR0:
LDERR1:
LDERR2:
LDERR3:
CMP DSKDIR,-1 ; IF NOT, THEN SAVE/RESTORE
JZ GLD6A
STC ; WARN SAVE RESTORE
RET
;CLOSE THE DATA FILE
GLD6A: MOV BX,DATHNDL ;GET THE FILE HANDLE
MOV AH,CFCLOSZ ;CLOSE IT
INT 21H
; REOPEN THE GAME FILE
MOV AH,CFOPENZ
MOV AL,0 ;OPEN FOR READ
MOV DX,OFFSET GAMFILE
INT 21H
JNC GLD7
JMP DSKERR ;UNRECOVERABLE ERROR
GLD7: MOV GAMHNDL,AX ;SAVE THE FILE HANDLE
POP DI ;RESTORE GSTACK POINTER
JMP RET0
GLOAD ENDP
; ----
; LDIO
; ----
;
; PURPOSE: THIS ROUTINE LOADS THE SPECIFIED AMOUNT OF DATA INTO A
; SPECIFIED BLOCK OF PRELOAD. IT IS THE I/O PORT OPERATION
; SUBCASE OF {GLOAD}.
;
; INPUTS: ARG1=TABLE VIRTUAL ADDRESS
; ARG2=LENGTH (IN WORDS)
;
; OUTPUTS: DATA IS TRANSFERED TO PRELOAD, IF THE OPERATION IS
; ILLEGAL OR FAILS FOR ANY REASON, A "0" IS RETURNED
; VIA {RET0}. ELSE A "1" IS RETURNED VIA {RET1}.
;
; REGISTERS DESTROYED: ALL
;
PUBLIC LDIO
LDIO PROC
; SUB AX,AX ;SIGNAL A READ OPERATION
; CALL INITIO ;INITIALIZE I/O DEVICE
;
; MOV DX,OFFSET XFRBUF ;POINT AT THE TRANSFER BUFFER
; CMP ARG2,0 ;DO WE HAVE A LEGAL LENGTH?
; JE IOERR1 ;....NO
;
;LDIOLP: SUB ARG2,256 ;IS THIS A FULL RECORD?
; JB LDIO1 ;....NO
; MOV CX,RECSIZ ;READ 512 BYTES (256 WORDS)
; CALL IOREAD ; AND PUT THEM IN THE XFER BUFFER
; CMP AX,CX ;DID WE GET THE EXPECTED AMOUNT?
; JNE IOERR1 ;....NO
; MOV CX,256 ;GET RECORD SIZE (IN WORDS)
; CALL XFR2VM ;BLOCK MOVE THE XFER BUFFER TO VIRTUAL
; JMP LDIOLP ;CONTINUE UNTIL WE'VE DONE ALL THE
; ; FULL BLOCKS
;
;LDIO1: ADD ARG2,256 ;COMPUTE PARTIAL RECORD SIZE
; JZ LDIO2 ;NO PARTIAL RECORD
; MOV CX,ARG2 ;GET SIZE OF PARTIAL RECORD
; CALL IOREAD ; GET IT IN THE XFER BUFFER
; CMP AX,CX ;DID WE GET THE EXPECTED AMOUNT?
; JNE IOERR1 ;....NO
; MOV CX,ARG2 ;GET RECORD SIZE (IN WORDS)
; CALL XFR2VM ;....YES, TRANSFER IT TO VM
;LDIO2: JMP RET1 ;SIGNAL SUCCESS
IOERR1: JMP RET0 ;SIGNAL FAILURE
LDIO ENDP
;@@
IOREAD PROC
RET ;DUMMY
IOREAD ENDP
;@@
INITIO PROC
RET ;DUMMY
INITIO ENDP
; ------
; XFR2VM
; ------
;
; PURPOSE: THIS ROUTINE TRANSFERS [CX] BYTES OF DATA FROM THE TRANFER
; BUFFER TO VIRTUAL MEMORY.
;
; INPUTS: ARG1=TABLE VIRTUAL ADDRESS
; [CX]=NUMBER OF BYTES TO TRANSFER
;
; OUTPUTS: DATA IS TRANSFERED TO PRELOAD
;
; REGISTERS DESTROYED: ALL
;
PUBLIC XFR2VM
XFR2VM PROC
MOV BX,OFFSET XFRBUF ;^ TRANSFER BUFFER (SOURCE)
MOV AX,ARG1 ;SETUP VMP (DESTINATION)
MOV VMP,AX
MOV VMP0,0
XFVMLP: MOV AL,BYTE PTR [BX] ;GET THE BYTE
CALL PB@VMPI ;PUT IT IN VM AND AUTOINC
INC BX ;NEXT BYTE
LOOP XFVMLP
RET
XFR2VM ENDP
PAGE
; ----
; DUMP
; ----
;
; PURPOSE: THIS ROUTINE TRANSFERS A SPECIFIED AMOUNT OF DATA FROM
; A SUPPLIED VIRTUAL MEMORY ADDRESS. IF ARG3 IS PROVIDED,
; THEN THIS IS A TRANSFER TO DISK. ELSE, IT IS AN I/O PORT
; OPERATION.
;
; INPUTS: ARG1=TABLE VIRTUAL ADDRESS
; ARG2=LENGTH
; ARG3=FILENAME POINTER (OPTIONAL)
;
; OUTPUTS: DATA IS TRANSFERED FROM VIRTUAL MEMORY, IF THE
; OPERATION IS ILLEGAL OR FAILS FOR ANY REASON,
; A "0" IS RETURNED VIA {RET0}. ELSE A "1" IS
; RETURNED VIA {RET1}.
;
; REGISTERS DESTROYED: ALL
;
PUBLIC GDUMP
GDUMP PROC
; DETERMINE TRANSFER TYPE
CMP NARGS,3 ;IS THIS A FILE TRANSFER REQUEST?
JE GDM0 ;....YES
JMP DMIO ;....NO, IT'S AN I/O TRANSFER
;
; FILE TRANSFER REQUEST
;
; ASSUME THAT THE DEFAULT DISK IS TO BE USED
; CLOSE THE GAMEFILE
GDM0: MOV AH,CFCLOSZ ;CLOSE THE GAME FILE FOR REINSERTION
MOV BX,GAMHNDL ;IN CASE THIS IS A 1 DRIVE SYSTEM,
INT 21H ; OR THE DEFAULT DRIVE IS SPECIFIED
MOV AH,CDRESET ;AND FLUSH
INT 21H
; PARSE THE FILENAME
MOV AX,ARG3 ;SET UP VMP TO POINT TO THE FILENAME
MOV VMP,AX
MOV VMP0,0
CALL GW@VMPI ;GET THE LENGTH OF THE FILENAME
MOV CX,AX ;IN CX FOR USE AS A LOOP COUNTER
MOV BX,OFFSET DATFILE ;^ FCB
;GET THE FILENAME INTO THE FCB
GDM1: CALL GB@VMPI ;GET A CHARACTER
MOV [BX],AL ;PUT IT IN THE FCB
INC BX ;NEXT CHARACTER
LOOP GDM1 ; GET THE REST
MOV BYTE PTR [BX],0 ;DROP IN A NUL AT THE END OF THE NAME
PUSH DI ;SAVE SOME REGISTERS
PUSH DS
POP ES ;BOTH ES & DS POINT AT THE DATA SEGMENT
; OPEN THE DATA FILE
MOV AH,CFCREAZ ;SELECT CREATE FILE
SUB CX,CX ;SELECT READ/WRITE ATTRIBUTE
MOV DX,OFFSET DATFILE ;POINT AT THE ASCIIZ STRING
INT 21H ;MDOS CALL
JNC GDM2
JMP DMERR1
GDM2: MOV DATHNDL,AX ;SAVE THE FILE HANDLE
; START THE ACTUAL TRANSFER OPERATION
SAVENT: CMP ARG2,0 ;DO WE HAVE A LEGAL LENGTH?
JNE GDMLP ;....YES
JMP DMERR0 ;....NO
; PROCESS THE FULL RECORDS
GDMLP: SUB ARG2,256 ;IS THIS A FULL BLOCK?
JB GDM3 ;....NO
; TRANSFER A FULL RECORD FROM VIRTUAL MEMORY TO THE TRANSFER BUFFER
MOV CX,RECSIZ ;GET RECORD SIZE (IN BYTES)
MOV BX,OFFSET XFRBUF ;^ TRANSFER BUFFER (DESTINATION)
MOV AX,ARG1 ;SETUP VMP (SOURCE)
MOV VMP,AX
MOV VMP0,0
GDMLP1: CALL GB@VMPI ;GET THE BYTE AND AUTOINC
MOV BYTE PTR [BX],AL ;PUT IT IN XFRBUF
INC BX ;NEXT DESTINATION
LOOP GDMLP1
ADD ARG1,256 ;UPDATE SOURCE POINTER
; WRITE A FULL RECORD TO THE DISK
MOV DX,OFFSET XFRBUF ;POINT AT THE TRANSFER BUFFER
MOV CX,RECSIZ ;WRITE A 512 BYTE RECORD (256 WORDS)
MOV BX,DATHNDL ;GET THE FILE HANDLE
MOV AH,CWRRNDZ ;SELECT WRITE RANDOM RECORD
INT 21H ;INVOKE FUNCTION CALL
CMP AX,CX ;DID WE WRITE A FULL RECORD?
JE GDMLP ;....YES, TRY ANOTHER
JMP DMERR2 ;....NO
; PARTIAL RECORD
GDM3: ADD ARG2,256 ;SEE IF THERE'S A PARTIAL RECORD
JZ GDM4 ;....NO
; TRANSFER PARTIAL RECORD FROM VIRTUAL MEMORY TO THE TRANSFER BUFFER
MOV CX,ARG2 ;GET THE SIZE (IN WORDS)
SHL CX,1 ;BYTIFY
MOV BX,OFFSET XFRBUF ;^ TRANSFER BUFFER (DESTINATION)
MOV AX,ARG1 ;SETUP VMP (SOURCE)
MOV VMP,AX
MOV VMP0,0
GDMLP2: CALL GB@VMPI ;GET THE BYTE AND AUTOINC
MOV BYTE PTR [BX],AL ;PUT IT IN XFRBUF
INC BX ;NEXT DESTINATION
LOOP GDMLP2
; WRITE A PARTIAL RECORD TO THE DISK
MOV CX,ARG2 ;GET THE SIZE (IN WORDS)
SHL CX,1 ;BYTIFY
MOV DX,OFFSET XFRBUF ;POINT AT THE TRANSFER BUFFER
MOV BX,DATHNDL ;GET THE FILE HANDLE
MOV AH,CWRRNDZ ;SELECT WRITE RANDOM RECORD
INT 21H ;INVOKE FUNCTION CALL
CMP AX,CX ;DID WE WRITE ENOUGH?
JE GDM4 ;....YES
JMP DMERR2 ;....NO
;CLOSE THE DATA FILE
GDM4: CMP DSKDIR,-1 ; CHECK SAVE RESTORE FLAG
JZ GDM4A
CLC ; CLEAR TO SAVE/RESTORE
RET
;
GDM4A: MOV BX,DATHNDL ;GET THE FILE HANDLE
MOV AH,CFCLOSZ ;CLOSE IT
INT 21H
; REOPEN THE GAME FILE
MOV AH,CFOPENZ
MOV AL,0 ;OPEN FOR READ
MOV DX,OFFSET GAMFILE
INT 21H
JNC GDM5
JMP DSKERR
GDM5: MOV GAMHNDL,AX ;SAVE THE FILE HANDLE
POP DI ;RESTORE GSTACK POINTER
JMP RET1 ;RETURN A "1"
DMERR0:
DMERR1:
DMERR2:
CMP DSKDIR,-1 ; CHECK SAVE/RESTORE FLAG
JZ GDM5A
STC ; WARN SAVE RESTORE
RET
;CLOSE THE DATA FILE
GDM5A: MOV BX,DATHNDL ;GET THE FILE HANDLE
MOV AH,CFCLOSZ ;CLOSE IT
INT 21H
; REOPEN THE GAME FILE
MOV AH,CFOPENZ
MOV AL,0 ;OPEN FOR READ
MOV DX,OFFSET GAMFILE
INT 21H
JNC GDM6
JMP DSKERR ;UNRECOVERABLE ERROR
GDM6: MOV GAMHNDL,AX ;SAVE THE FILE HANDLE
POP DI ;RESTORE GSTACK POINTER
JMP RET0
GDUMP ENDP
; ----
; DMIO
; ----
;
; PURPOSE: THIS ROUTINE DUMPS A SPECIFIED AMOUNT OF DATA FROM
; PRELOAD TO THE I/O PORT.
;
; INPUTS: ARG1=TABLE VIRTUAL ADDRESS
; ARG2=LENGTH (IN WORDS)
;
; OUTPUTS: DATA IS TRANSFERED TO THE I/O PORT, IF THE OPERATION
; IS ILLEGAL OR FAILS FOR ANY REASON, A "0" IS RETURNED
; VIA {RET0}. ELSE A "1" IS RETURNED VIA {RET1}.
;
; REGISTERS DESTROYED: ALL
;
PUBLIC DMIO
DMIO PROC
; MOV AX,1 ;SIGNAL A WRITE OPERATION
; CALL INITIO ;INITIALIZE I/O DEVICE
;
; MOV DX,OFFSET XFRBUF ;POINT AT THE TRANSFER BUFFER
; CMP ARG2,0 ;DO WE HAVE A LEGAL LENGTH?
; JE IOERR2 ;....NO
;
;DMIOLP: SUB ARG2,256 ;IS THIS A FULL RECORD?
; JB DMIO1 ;....NO
; CALL VM2XFR ;BLOCK MOVE THE VM BLOCK TO XFER BUFFER
;
; MOV CX,RECSIZ ;WRITE 512 BYTES (256 WORDS)
; CALL IOWRITE ; AND PUT THEM IN THE XFER BUFFER
; CMP AX,CX ;DID WE WRITE THE EXPECTED AMOUNT?
; JNE IOERR2 ;....NO
; JMP DMIOLP ;CONTINUE UNTIL WE'VE DONE ALL THE
; ; FULL BLOCKS
;
;DMIO1: ADD ARG2,256
; MOV CX,ARG2 ;SETUP SIZE OF PARTIAL RECORD
; CMP CX,0 ;DO WE EVEN HAVE A PARTIAL RECORD?
; JE DMIO2 ;....NO
;
; CALL VM2XFR ;BLOCK MOVE THE VM BLOCK TO XFER BUFFER
; CALL IOWRITE ; AND PUT THEM IN THE XFER BUFFER
; CMP AX,CX ;DID WE WRITE THE EXPECTED AMOUNT?
; JNE IOERR2 ;....NO
;
;DMIO2: JMP RET1 ;SIGNAL SUCCESS
;
IOERR2: JMP RET0 ;SIGNAL FAILURE
DMIO ENDP
;@@
IOWRITE PROC
RET ;DUMMY
IOWRITE ENDP
; ------
; VM2XFR
; ------
;
; PURPOSE: THIS ROUTINE TRANSFERS [CX] BYTES OF DATA FROM VIRTUAL
; MEMORY TO THE TRANSFER BUFFER.
;
; INPUTS: ARG1=TABLE VIRTUAL ADDRESS
; [CX]=NUMBER OF BYTES TO TRANSFER
;
; OUTPUTS: DATA IS TRANSFERED TO XFRBUF
;
; REGISTERS DESTROYED: ALL
;
PUBLIC VM2XFR
VM2XFR PROC
MOV BX,OFFSET XFRBUF ;^ TRANSFER BUFFER (DESTINATION)
MOV AX,ARG1 ;SETUP VMP (SOURCE)
MOV VMP,AX
MOV VMP0,0
VMXFLP: CALL GB@VMPI ;GET THE BYTE AND AUTOINC
MOV BYTE PTR [BX],AL ;PUT IT IN XFRBUF
INC BX ;NEXT DESTINATION
LOOP VMXFLP
RET
VM2XFR ENDP
PAGE
;----------------------
; ITERATIONS
;----------------------
;
; EXECUTE THE ITERATION TABLE AT [ARG2]
; {FOR I=ITPNT TO ITICN
;
; DRAW THE ACTIVE ICON [ITERATION]
;
; UPDATE THE ICON'S ITERATION COUNTER
;
; TEST FOR INPUT
; IF INPUT THEN SAVE THE NEXT ACTIVE ICON ENTRY NUMBER
; AND PROCESS THE INPUT}
;
; NEXT I}
;
; IF DONE
; REINITIALIZE ITPNT <= 1
; BRANCH TO NOIT}.
PUBLIC ITRATE
ITRATE PROC
MOV AX,ARG2 ;POINT AT THE ITERATION TABLE
MOV VMP,AX
MOV VMP0,0
CALL GB@VMPI ;GET THE NUMBER OF ENTRIES
MOV ITICN,AL
MOV AL,VMP0 ;SAVE POINTER TO ITPNT
MOV VMSAV0,AL
MOV AX,VMP
MOV VMSAV,AX
CALL GB@VMPI ;GET THE ENTRY TO START WITH
MOV ITPNT,AL ; WE'RE POINTING AT THE FIRST WORD
; IN THE FIRST ENTRY
MOV AL,1 ;SKIP ENTRIES 1 TO ITPNT
ITLP2: CMP AL,ITPNT
JZ ITLOOP
ADD VMP,6 ;SIX WORDS PER ENTRY
INC AL
JMP ITLP2
; PROCESS THE DATA FOR THE CURRENT ACTIVE ICON
ITLOOP: MOV AX,VMP ;SAVE POINTER TO THE CURRENT
PUSH AX ; ACTIVE ICON'S DATA BASE
MOV AL,VMP0
PUSH AX
CALL GW@VMPI ;GET ICON BASE ADDRESS
MOV IADR1,AX
MOV IADR10,0
CALL GW@VMPI ;GET ICON HORIZONTAL POSITION
MOV ARG1,AX
CALL GW@VMPI ;GET ICON VERTICAL POSITION
MOV ARG2,AX
CALL GW@VMPI
MOV NEGATE,AH ;NEGATE
MOV BYTE PTR I,AL ;CURRENT ITERATION
CALL GW@VMPI
MOV BSET,AH ;BLOCKSET ID
MOV ITERS,AL ;# OF ITERATIONS
CALL GW@VMPI
MOV IX1,AH ;WIDTH
MOV IY1,AL ;HEIGHT
; UPDATE THE CURRENT ITERATION IN THE DATA STRUCTURE
MOV BL,BYTE PTR I
CMP BL,ITERS ;CURRENT ITERATION=NO. OF ITERATIONS?
JNE ITSKP2 ;....NO
MOV BL,0 ;....YES, SET IT TO 0 SO IT'LL BE 1
ITSKP2: INC BL ;NEXT POSSIBLE ITERATION
POP AX ;RESTORE POINTER TO THE
MOV VMP0,AL ; CURRENT ACTIVE ICON'S DATA BASE
POP AX
MOV VMP,AX
MOV AX,7 ;POINT TO THE CURRENT ITERATION
CALL ADDVMP
MOV AX,BX ;SAVE UPDATED VALUE
CALL PB@VMPI
; UPDATE POINTER TO THE NEXT ACTIVE ICON
ADD VMP,2 ;IT'S 4 MORE BYTES TO THE NEXT ACTIVE ICON
MOV AX,VMP ;SAVE POINTER TO THE
PUSH AX ; NEXT ACTIVE ICON'S DATA BASE
MOV AL,VMP0
PUSH AX
; POINT TO THE CURRENT ICON'S BLOCK I.D. DATA
MOV AL,IX1 ;GET LENGTH OF ONE ICON
MUL IY1
DEC BYTE PTR I ;ZERO ALIGN
MOV BL,BYTE PTR I
SUB BH,BH
MUL BX ;PRODUCT S/B IN AX
ADD IADR1,2 ;POINT PAST HEADER (2 WORDS)
CALL AIADR1 ;ADD IN OFFSET
; DRAW THE ITERATION
MOV MSKFLG,0 ;DISABLE MASKING
CALL ITENT ;DRAW THE ICON
;
; HAVE WE PROCESSED ALL THE ACTIVE ICONS?
;
MOV AL,ITPNT ;ARE WE DONE?
CMP AL,ITICN
JE ITEXI ;....YES
INC ITPNT ;....NO. POINT TO THE NEXT ICON
; INPUT PROCESSING
TEST BYTE PTR VALUE,80H ;IF INPUT WAS CALLED WITH A NEGATIVE
JNZ NONSNS ;INT THEN DON'T LOOK FOR INPUT
CALL SENSE ;ELSE, POLL THE KEYBOARD
CMP AL,8FH ;DO WE HAVE AN INPUT?
JNE ITSKP5 ;....YES
NONSNS: POP AX ;RESTORE POINTER TO THE
MOV VMP0,AL ; NEXT ACTIVE ICON'S DATA BASE
POP AX
MOV VMP,AX
JMP ITLOOP
; WE RECEIVED AN INPUT WHILE ITERATING
; SO, WE SAVE WHERE WE ARE AND PROCESS THE INPUT
ITSKP5: PUSH AX ;SAVE INPUT
MOV AL,VMSAV0 ;RESTORE POINTER TO ACTIVE TABLE HEADER
MOV VMP0,AL ; ITPNT ENTRY
MOV AX,VMSAV
MOV VMP,AX
MOV AL,ITPNT ;SAVE WHERE WE LEFT OFF
CALL PB@VMP
POP AX ;RESTORE SENSE
POP BX ;FLUSH VMP POINTER TO CURRENT ACTIVE ICON
POP BX
JMP PUTBYT
; WE ITERATED ALL THE ICONS IN THE ACTIVE TABLE
; SO, WE SET UP TO DO IT AGAIN LATER AND EXIT
ITEXI: POP BX ;FLUSH VMP0
POP BX ;FLUSH VMP
MOV AL,VMSAV0 ;RESTORE POINTER TO ACTIVE TABLE HEADER
MOV VMP0,AL ; ITPNT ENTRY
MOV AX,VMSAV
MOV VMP,AX
MOV AL,1 ;SET ITPNT ENTRY TO 1
CALL PB@VMP
JMP NOIT ; AND RESUME INPUT PROCESSING
ITRATE ENDP
PAGE
;--------------------------------------------------
; OPCODE EXECUTION PROCEDURES SUPPORT CODE
;--------------------------------------------------
; ------
; TSTPRE
; ------
;
; PURPOSE: THIS ROUTINE TESTS THE RANGE OF A SPECIFIED VIRTUAL MEMORY
; BLOCK TO DETERMINE IF IT IS ALL CONTAINED IN EITHER
; G-PRELOAD OR I-PRELOAD.
;
; INPUTS: [ARG1] VIRTUAL MEMORY STARTING ADDRESS (WORD POINTER)
; [ARG2] LENGTH IN WORDS
;
; OUTPUTS: CARRY CLEAR IF IN PRELOAD
; CARRY SET IF NOT IN PRELOAD
;
; REGISTERS DESTROYED: AX
;
PUBLIC TSTPRE
TSTPRE PROC
; TEST STARTING ADDRESS
MOV AX,ARG1 ;GET STARTING ADDRESS
CMP AH,GPEND ;DOES THE ADDRESS RESIDE BELOW THE
; FIRST BLOCK OF G-PURE?
JB TPR0 ;....YES, IT'S G-PRELOAD
CMP AX,ICODE ;IS IT GE. I-PRELOAD?
JB TPRERR ;....NO, IT'S NOT PRELOAD.
CMP AX,IPURE ;IS IT < IPURE
JB TPR1 ;....YES, IT'S I-PRELOAD
JMP TPRERR ;FAIL, IT'S NOT PRELOAD
; TEST LAST ADDRESS TO SEE IF IT'S IN G-PRELOAD TOO
TPR0: ADD AX,ARG2 ;ADD LENGTH
CMP AH,GPEND ;DOES THE ADDRESS RESIDE BELOW THE
; FIRST BLOCK OF G-PURE?
JB TPREOK ;....YES, IT'S GOOD
JMP TPRERR ;FAIL IF NOT IN G-PRELOAD
; TEST LAST ADDRESS TO SEE IF IT'S IN I-PRELOAD TOO
TPR1: ADD AX,ARG2 ;ADD LENGTH
CMP AX,IPURE ;IS IT < IPURE
JB TPREOK ;....YES, IT'S I-PRELOAD
TPRERR: STC ;SET CARRY FOR FAILURE
RET
TPREOK: CLC ;CLEAR CARRY FOR SUCCESS
RET
TSTPRE ENDP
PAGE
; --------------------------------
; POP GSTACK INTO [VALUE] AND [AX]
; --------------------------------
;
PUBLIC POPVAL
POPVAL PROC
CALL POPAXG ;POP A VALUE
MOV VALUE,AX ;SAVE IT IN VALUE
RET
POPVAL ENDP
; --------------------------
; UPDATE VALUE OF A VARIABLE
; --------------------------
; ENTRY: VALUE TO STORE IN [VALUE]
; VARIABLE ID IN AX
PUBLIC VARPUT,PTV1$,PTV2$
VARPUT PROC
CMP AX,0 ;LOCAL OR GLOBAL
JNE PTV1$ ;....YES.
MOV AX,VALUE ;....NO. STACK. GET VALUE
JMP PSHAXG ; AND PUSH IT ON THE GSTACK
;SAVE [VALUE] TO LOCAL OR GLOBAL
PTV1$: PUSH BX ;SAVE AFFECTED REGISTER(S)
MOV BX,GLOCS ;ASSUME IT'S A LOCAL
CMP AX,16 ;WAS IT A LOCAL?
JNB PTV2$ ;....NO
DEC AX ;....YES, ZERO ALIGN
JMP PTV3$
; IT WAS A GLOBAL
PTV2$: MOV BX,GLOBAL ;GET GLOBAL TABLE BASE ADDRESS
SUB AX,16 ;ZERO ALIGN
PTV3$: MOV VMP,BX ;PUT ADDRESS INTO VIRTUAL MEMORY POINTER
MOV VMP0,0 ;IT MUST BE ON AN EVEN BOUNDRY
ADD VMP,AX
MOV AX,VALUE ;GET THE VALUE
POP BX ;RESTORE AFFECTED REGISTER(S)
JMP PW@VMP ; AND WRITE IT TO VIRTUAL
VARPUT ENDP
; ----------------------------------
; GET A VARIABLE FROM THE MAINLOOP
; ----------------------------------
; IMPORTS: VARIABLE ID IN G-CODE
; EXPORTS: VALUE RETURNED IN AX AND [VALUE]
PUBLIC GETVAR
GETVAR PROC
CALL GB@GPCI ;GET VARIABLE ID
JMP VARGET
GETVAR ENDP
; --------------------------------------
; GET A VARIABLE FROM WITHIN AN OPCODE
; --------------------------------------
; IMPORTS: VARIABLE ID IN [AL]
; EXPORTS: VALUE RETURNED IN AX AND [VALUE]
PUBLIC VARGET,GTV1$,GTV2$
VARGET PROC
CMP AL,0 ;IF ID NE. 0
JNE GTV1$ ; THEN ACCESS A VARIABLE
JMP POPVAL ;ELSE, POP GSTACK INTO [AX] AND [VALUE]
; IS THE VARIABLE LOCAL OR GLOBAL
GTV1$: PUSH BX ;SAVE AFFECTED REGISTER(S)
MOV BX,GLOCS ;ASSUME IT'S A LOCAL
CMP AX,16 ;WAS IT?
JNB GTV2$ ;....NO
DEC AX ;....YES, ZERO ALIGN
JMP GTV3$
; HANDLE A GLOBAL VARIABLE
GTV2$: MOV BX,GLOBAL ;GET GLOBAL TABLE BASE ADDRESS
SUB AX,16 ;POINT TO TABLE ELEMENT
GTV3$: MOV VMP,BX ;PUT ADDRESS INTO VIRTUAL MEMORY POINTER
MOV VMP0,0 ;IT MUST BE ON AN EVEN BOUNDRY
ADD VMP,AX
CALL GW@VMP ;GET THE VARIABLE
MOV VALUE,AX ;PUT IT IN [VALUE]
POP BX ;RESTORE AFFECTED RESISTER(S)
RET
VARGET ENDP
; ---------------
; PREDICATE FAILS
; ---------------
;PREDICATE HANDLERS TRUE & FALSE
;DESTROYS REGISTERS, BUT ARE ONLY CALLED FROM END OF TOP-LEVEL FCNS
PUBLIC PREDF,PREDF1
PREDF PROC
CALL GB@GPCI ;GET FIRST BRANCH BYTE
OR AL,AL
JNS PREDF1 ;BRANCH IF BIT 7 CLEAR
JMP PREDNB ; ELSE IGNORE PREDICATE BRANCH
PREDF1: JMP PREDB
PREDF ENDP
; -----------------------
; IGNORE PREDICATE BRANCH
; -----------------------
PUBLIC PREDNB,PREDN0
PREDNB PROC
AND AL,01000000B ;TEST BIT 6
JNZ PREDN0 ;SHORT BRANCH IF SET
JMP GB@GPCI ;ELSE SKIP OVER 2ND BRANCH BYTE
PREDN0: RET
PREDNB ENDP
; ------------------
; PREDICATE SUCCEEDS
; ------------------
PUBLIC PREDS,PREDS0
PREDS PROC
CALL GB@GPCI ;GET FIRST BRANCH BYTE
OR AL,AL
JNS PREDS0 ;DON'T BRANCH IF BIT 7 IS CLEAR
JMP PREDB ; ELSE, DO A PREDICATE BRANCH
PREDS0: JMP PREDNB
PREDS ENDP
; --------------------------
; PERFORM A PREDICATE BRANCH
; --------------------------
; ENTRY: 1ST PRED BYTE IN [AL]
PUBLIC PREDB,PREDLB,DOB2,PREDB1,PREDB2,PRDB2A
PREDB PROC
SUB AH,AH ;MSB NOT USED
TEST AL,01000000B ;LONG OR SHORT BRANCH?
JZ PREDLB ;LONG IF BIT 6 IS CLEAR
; HANDLE A SHORT BRANCH
AND AL,00111111B ;FORM SHORT OFFSET
MOV WORD PTR VALUE,AX ;MSB OF OFFSET IS ZERO
JMP PREDB1 ;DO THE BRANCH
; HANDLE A LONG BRANCH
PREDLB: AND AL,00111111B ;FORM MSB OF OFFSET
TEST AL,00100000B ;CHECK SIGN OF 14-BIT VALUE
JZ DOB2 ;POSITIVE?
OR AL,11100000B ;....NO. EXTEND SIGN BIT
DOB2: MOV BL,AL ;PUT MSB OF OFFSET IN BL
CALL GB@GPCI ;GET LSB OF 14-BIT OFFSET
MOV AH,BL ; RECOVER THE MSB
MOV VALUE,AX ;SAVE THE BRANCH OFFSET IN VALUE
; TEST BRANCH OFFSET
PREDB1: OR AH,AH ;CHECK MSB OF OFFSET
JNZ PREDB3 ;DO BRANCH IF NZ
OR AL,AL ;IF LSB IS NZ
JNZ PREDB2 ;MAKE SURE IT ISN'T A "1"
JMP GRFALS ; ELSE, DO AN "RFALSE"
PREDB2: CMP AL,1 ;IS OFFSET= 1
JNZ PRDB2A ;....NO. DO THE BRANCH
JMP GRTRUE ; ELSE, DO AN "RTRUE"
PRDB2A: JMP PREDB3 ;EXECUTE THE "JUMP"
PREDB ENDP
;ENTRYPOINT FOR "JUMP"
PUBLIC PREDB3
PREDB3 PROC
MOV AX,WORD PTR VALUE ;GET THE OFFSET
SUB AX,2 ;CALC [VALUE] - 2
;CONVERT A 16-BIT OFFSET TO 17 BITS
SAR AX,1 ;PROCESS THE LS BIT, AND SIGN EXTEND
JNC PRDB3A ;SKIP IF "0"
; ELSE WE HAVE TO INCREMENT GPC
XOR GPC0,1 ;CHANGE THE STATE OF BIT0
JNZ PRDB3A ;CONTINUE IF CHANGING FROM "0" TO A "1"
INC GPC ;ELSE BUMP THE REST OF GPC
PRDB3A: ADD GPC,AX ;ADD IN THE REST OF THE OFFSET
RET
PREDB3 ENDP
; ------
; AIADR1
; ------
;
; ENTRY: IADR1
; [AX] = VALUE TO BE ADDED TO IADR1
;
; EXIT: IADR1 <= IADR1 + [AX]
;
AIADR1 PROC
SHR AX,1 ;THINK ABOUT IT
JNC AIAD1
XOR IADR10,1 ;CHANGE THE STATE OF BIT0
JNZ AIAD1 ; CONTINUE IF CHANGING FROM A "0" TO A "1"
INC IADR1 ;ELSE BUMP THE REST OF IADR1
AIAD1: ADD IADR1,AX
RET
AIADR1 ENDP
; ------
; AIADR2
; ------
;
; ENTRY: IADR2
; [AX] = VALUE TO BE ADDED TO IADR2
;
; EXIT: IADR2 <= IADR2 + [AX]
;
AIADR2 PROC
SHR AX,1 ;THINK ABOUT IT
JNC AIAD2
XOR IADR20,1 ;CHANGE THE STATE OF BIT0
JNZ AIAD2 ; CONTINUE IF CHANGING FROM A "0" TO A "1"
INC IADR2 ;ELSE BUMP THE REST OF IADR2
AIAD2: ADD IADR2,AX
RET
AIADR2 ENDP
; ------------
; RETURN A "0"
; ------------
PUBLIC RET0
RET0 PROC
MOV AL,0
JMP PUTBYT
RET0 ENDP
; ------------
; RETURN A "1"
; ------------
PUBLIC RET1
RET1 PROC
MOV AL,1
; AND FALL THROUGH .............
RET1 ENDP
; -----------------------
; RETURN A BYTE VIA [GPC]
; -----------------------
;
;RETURNS THE BYTE IN AL TO THE LOCATION SPECIFIED BY THE GPC
;DESTROYS BX, BUT IS USUALLY CALLED AT END OF TOP-LEVEL FUNCTION
PUBLIC PUTBYT
PUTBYT PROC
SUB AH,AH ;THIS ENTRY FOR BYTE VALUE TO CLEAR HIGH BYTE
MOV VALUE,AX
JMP PUTVAL
PUTBYT ENDP
; ---------------------------------------------------
; RETURN [VALUE] TO THE LOCATION SPECIFIED BY THE GPC
; ---------------------------------------------------
; ENTRY: [VALUE] <= THE VALUE TO RETURN
; [GPC] <= VARIABLE ID
;
; EXIT: [VALUE] IS STORED
; GPC <= GPC + 1
PUBLIC PUTVAL,PUTVL0,PUTVL1
PUTVAL PROC
CALL GB@GPCI ;GET VAR TO USE
CMP AX,0 ;STACK?
JNE PUTVL0 ;NO, GO STORE VALUE
; VARIABLE ID=0
; PUSH [VALUE] ONTO THE GSTACK
MOV AX,VALUE ;GET [VALUE]
JMP PSHAXG ;PUSH IT ONTO GSTACK
; VARIABLE ID <> 0
; SAVE [VALUE] TO LOCAL OR GLOBAL
PUTVL0: PUSH BX ;SAVE AFFECTED REGISTER(S)
MOV BX,GLOCS ;ASSUME IT'S A LOCAL
CMP AX,16 ;WAS IT?
JNB PUTVL1 ;....NO
DEC AX ;....YES, ZERO ALIGN
JMP PUTVL2
; HANDLE A GLOBAL VARIABLE
PUTVL1: MOV BX,GLOBAL ;GET GLOBAL TABLE BASE ADDRESS
SUB AX,16 ;POINT TO TABLE ELEMENT
PUTVL2: MOV VMP,BX ;PUT ADDRESS INTO VIRTUAL MEMORY POINTER
MOV VMP0,0 ;IT MUST BE ON AN EVEN BOUNDRY
ADD VMP,AX
MOV AX,VALUE ;GET THE VALUE
POP BX ;RESTORE AFFECTED REGISTER(S)
JMP PW@VMP ; AND WRITE IT TO VIRTUAL
PUTVAL ENDP
SUBTTL GRAPHIC OPCODE SUPPORT ROUTINES
PAGE
; -------------------------------------------
; GET GRAPHICS BLOCK [AL] IN CURRENT BLOCKSET
; -------------------------------------------
; ENTRY: BLOCK ID # (1-255) IN [AL]
; EXIT: BLOCK DATA IN [BLOCK]
PUBLIC GTGBLK,GBLL
GTGBLK PROC
PUSH BX
PUSH CX
;CALCULATE OFFSET FROM THE START OF BLOCKSET
MOV BX,BSADR ;GET BASE ADDRESS OF BLOCKSET
MOV VMP,BX ;SETUP VMP
MOV VMP0,0
XOR AH,AH ; (B5) CLEAN OUT TOP HALF
MOV CL,3
SHL AX,CL ; (B5) MULTIPLY BY COMMON BLOCK LEN
CALL ADDVMP ;SET UP VADDR OF FIRST BYTE IN BLOCK
;SETUP LOOP COUNTER AND INDEX
MOV CL,4
SUB CH,CH
MOV BX,OFFSET BLOCK
GBLL: CALL GW@VMPI ;GET A WORD
XOR AL,NEGATE ;INVERT BITS IF NEGATE=0FFH
XOR AH,NEGATE ;INVERT BITS IF NEGATE=0FFH
XCHG AH,AL ;SWAPIFY
MOV [BX],AX ; STORE [BLOCK (BX)]
INC BX ; AND [BLOCK (BX + 1)]
INC BX ;UPDATE DESTINATION
LOOP GBLL ;LOOP TILL ALL BYTES ARE DONE
POP CX
POP BX
RET
GTGBLK ENDP
PUBLIC GTGFST
GTGFST PROC
CMP FSIZE,0 ; (B5) WHOLE GAME IN?
JNE GTF0
JMP GTGBLK ; (B5) TAKE THE LONG WAY HOME
GTF0: PUSH BX
PUSH CX
PUSH DI
PUSH SI
;CALCULATE OFFSET FROM THE START OF BLOCKSET
MOV BX,0 ; (B5) FLAG TO INDICATE RESTORATION OF ES
JMP GTF1
; FIX AX TO BE BLOCK ID AND USE SLOW GET BLOCK BECAUSE OF SEG
; BOUNDARY CROSSING
GTFIX: PUSH ES
MOV CX,ES
ADD CX,1000H
MOV ES,CX ; (B5) SET UP FOR NEXT SEG
MOV BX,1 ; (B5) SET FLAG FOR SEG RESTORE
JMP GTF4 ; (B5) GET BLOCK FROM NEXT SEG
GTF1: MOV CL,3 ; (B5) SHIFT COUNT
XOR AH,AH ; (B5) MULTIPLY BY BLKLEN
SHL AX,CL ; (B5) DO IT
GTF2: ADD DI,AX ; (B5) ADD IN OFFSET
JC GTFIX
ADD DI,8 ; (B5) CHECK IF WE'LL CROSS SEG
JNC GTF3
MOV BX,2 ; (B5) USE BX AS A FLAG FOR THIS COND
GTF3: SUB DI,8 ; (B6) FIX DI
GTF4: XCHG SI,DI ; (B5) GET POINTER
MOV DI,OFFSET BLOCK ; (B5) GET BLOCK ADDRESS
;SETUP LOOP COUNTER AND INDEX
MOV CL,4
MOV CH,NEGATE
PUSH DS ; (B5) SAVE SEGS AND SWAP 'EM
PUSH ES
PUSH DS
PUSH ES
POP DS
POP ES
CMP BX,2 ; (B5) CHECK FOR WRAP INDICATOR
JNE GFLL
GFSL: LODSW ; (B5) GET A WORD
CMP SI,0 ; (B5) CHECK FOR WRAP
JNE GFSL1 ; (B5) NOT YET
MOV BX,DS ; (B5) IT DID, FIX SEG
ADD BX,1000H ; (B5) TO ADDRESS NEXT SEG
MOV DS,BX ; (B5) FINISH FIX UP
MOV BX,0 ; (B5) RESET BX
GFSL1: XOR AL,CH ; (B5) CONTINUE PROCESS
XOR AH,CH
STOSW ; (B5) FILL IN REST OF BLOCK
DEC CL ; (B5) AND DECREMENT LOOP CNTR
JNZ GFSL ; (B5) CONTINUE IF BYTES LEFT
JMP GFRES ; (B5) OR EXIT
GFLL: LODSW ; (B5) GET A WORD
XOR AL,CH ;INVERT BITS IF NEGATE=0FFH
XOR AH,CH ;INVERT BITS IF NEGATE=0FFH
STOSW ; (B5) STORE [BLOCK (BX)]
DEC CL
JNZ GFLL
GFRES: POP ES
POP DS
CMP BX,1 ; (B5) LOOK FOR RESTORE BIT
JNE GTF5
POP ES ; (B5) SEG CROSS, RESTORE ES
GTF5: POP SI
POP DI
POP CX
POP BX
RET
GTGFST ENDP
; ------------------------
; SETUP FOR SETI AND SWAPI
; ------------------------
PUBLIC ISETUP
ISETUP PROC
MOV AX,WORD PTR ARG4 ;GET WRD PTR ADDR OF DESTINATION ICON
CMP AX,IPURE ;IS DEST IN I-PRELOAD?
JB ISU ;....YES. SO CONTINUE
JMP PRERR2 ;ELSE PURITY VIOLATION!
; AND FALL THROUGH .............
ISETUP ENDP
; ----------------------------
; SETUP ENTRYPOINT FOR MASKING
; ----------------------------
PUBLIC ISU
ISU PROC
CALL GETI ;FETCH STATS OF DEST ICON
MOV AL,BYTE PTR IX1 ;COPY DEST ICON STATS
MOV BYTE PTR IX2,AL ;TO AUXILIARY STORAGE
MOV AL,BYTE PTR IY1
MOV BYTE PTR IY2,AL
MOV AX,WORD PTR IADR1
MOV WORD PTR IADR2,AX
MOV AL,IADR10
MOV IADR20,AL
MOV AX,ARG3 ;NOW GET THE SOURCE ICON STATS
; AND FALL THROUGH .............
ISU ENDP
; --------
; GET ICON
; --------
; ENTRY: V-ADDR OF ICON DATA DEFINITION IN [AX]
;
; EXIT: ICON DATA V-ADDR IN [IADR1]
; X-SIZE IN [IX1] AND [XDEX1]
; Y-SIZE IN [IY1] AND [YDEX1]
; ASSIGNED BLOCKSET IN [BSET]
PUBLIC GETI,NEWXY
GETI PROC
PUSH BX ;SAVE AFFECTED REGISTER(S)
MOV VMP,AX ;LOAD VIRTUAL MEMORY POINTER
MOV VMP0,0
CALL GW@VMPI ;THE FIRST WORD HAS
MOV BYTE PTR BSET,AH ; THE ASSIGNED BLOCKSET
MOV BYTE PTR ITERS,AL ; AND THE # OF ITERATIONS
CALL GW@VMPI ;THE SECOND WORD HAS
MOV BYTE PTR IX1,AH ; X-SIZE
MOV BYTE PTR IY1,AL ; AND Y-SIZE
MOV AX,VMP ;GET BASE V-ADDR
MOV WORD PTR IADR1,AX ;IN [IADR1]
MOV AL,VMP0
MOV IADR10,AL
; SETUP [XDEX1] & [YDEX1]
NEWXY: MOV AL,BYTE PTR IX1
MOV BYTE PTR XDEX1,AL
MOV AL,BYTE PTR IY1
MOV BYTE PTR YDEX1,AL
POP BX ;RESTORE AFFECTED REGISTER(S)
RET
GETI ENDP
; -----------------------------
; WILL SOURCE ICON FIT IN DEST?
; -----------------------------
; ENTRY: IX1 = X-SIZE OF SOURCE
; IY1 = Y-SIZE OF SOURCE
; IX2 = X-SIZE OF DESTINATION
; IY2 = Y-SIZE OF DESTINATION
; ARG1 = HORIZONTAL OFFSET OF THE SUB-IMAGE IN THE
; DESTINATION ICON THAT IS TO BE SWAPPED
; ARG2 = VERTICAL OFFSET OF THE SUB-IMAGE IN THE
; DESTINATION ICON THAT IS TO BE SWAPPED
;
; EXIT: CARRY CLEAR IF FIT OK, ELSE CARRY SET
; [IADR2] HAS ABSOLUTE ADDRESS OF SUB-ICON
PUBLIC DOFIT,NOFIT,FIT0,FIT1,FIT2,FIT3,FITEX
DOFIT PROC
MOV AL,BYTE PTR ARG1 ;GET X-POSITION
ADD AL,IX1 ;ADD TO THE X-SIZE OF SOURCE
CMP AL,IX2 ;COMPARE TO THE X-SIZE OF DESTINATION
JBE FIT0 ;OK IF LESS OR EQUAL
NOFIT: STC ; ELSE, SET CARRY
JMP FITEX ; AND EXIT
FIT0: MOV AL,BYTE PTR ARG2 ;GET Y-POSITION
MOV BL,AL ;SAVE IT FOR LATER
ADD AL,IY1 ;ADD THE Y-SIZE OF SOURCE
CMP AL,IY2 ;COMPARE TO Y-SIZE OF DESTINATION
JBE FIT1 ;OK IF LESS OR EQUAL
JMP NOFIT ; ELSE, SET CARRY AND EXIT
; MAKE [IADR2] POINT TO ADDR OF SUB ICON
FIT1: MOV AL,BYTE PTR ARG1 ;GET X-POSITION
OR AL,AL ;IF X-POS = 0
JZ FIT3 ; DON'T ADD X-OFFSET
; MOV AL,BYTE PTR ARG1 ;ADD THE X-COORDINATE OF SUB-ICON
SUB AH,AH ;TO BASE ADDRESS OF DEST ICON
CALL AIADR2 ;TO GET THE BASE ADDR OF SUB-ICON
JMP FIT3 ;ALWAYS SKIP THE 1ST Y-ITERATION
FIT2: MOV AL,BYTE PTR IX2 ;ADD THE X-SIZE OF DEST ICON
SUB AH,AH ;TO BASE ADDRESS OF SUB-ICON
CALL AIADR2 ;ONCE FOR EACH Y-COORDINATE
FIT3: DEC BL ;OUT OF Y-COORDINATES YET?
JNS FIT2 ; NO. KEEP ADDING
CLC ;CLEAR CARRY FOR SUCCESS
FITEX: RET
DOFIT ENDP
SUBTTL GPC VIRTUAL MEMORY ACCESS
PAGE
; ---------------------------------------------------------------
; The following subroutines are used to access Virtual Memory
; through the GPC.
;
; G-MACHINE PROGRAM COUNTER DEFINITION
; GPC GPC1 = MSB OF GPC GPC2 = LSB OF GPC
; GPC0 = BIT 0 OF BYTE IS BIT 0 OF GPC
; ----------------------------------------------------------------
; ------
; ADDGPC
; ------
;
; ENTRY: [AX] = VALUE TO BE ADDED TO GPC
; EXIT: GPC <= GPC + [AX]
;
ADDGPC PROC
SHR AX,1 ;THINK ABOUT IT
JNC ADDGP1
XOR GPC0,1 ;CHANGE THE STATE OF BIT0
JNZ ADDGP1 ; CONTINUE IF CHANGING FROM A "0" TO A "1"
INC GPC ;ELSE BUMP THE REST OF GPC
ADDGP1: ADD GPC,AX
RET
ADDGPC ENDP
; -----------------------------------------------
; {GB@GPC}
; GET A BYTE OF VIRTUAL MEMORY AT THE CURRENT GPC
; -----------------------------------------------
;
; EXIT: BYTE IN [AX]
;
PUBLIC GB@GPC
GB@GPC PROC
PUSH BX ;SAVE AFFECTED REGISTER(S)
PUSH CX
;CREATE AND SAVE THE LOW ADDRESS BITS
MOV AX,GPC ;GET GPC1 AND GPC2
MOV BL,GPC0 ;GET GPC BIT
SHL AX,1 ;SHIFT GPC2 AND
OR AL,BL ; APPEND THE GPC BIT
AND AX,01FFH ; AND ISOLATE THE LADD BITS
MOV LADD,AX ;SAVE LOW ADDRESS BITS
;CREATE AND SAVE THE VIRTUAL PAGE NUMBER
MOV AX,GPC ;GET VIRTUAL ADDRESS AGAIN
JMP GB@VM1 ; AND USE EXISTING CODE
GB@GPC ENDP
; -----------------------------
; {GW@GPC}
; GET A WORD AT THE CURRENT GPC
; -----------------------------
;
; EXIT: WORD IN [AX]
; GPC <= GPC + 2
;
PUBLIC GW@GPC
GW@GPC PROC
PUSH BX ;SAVE AFFECTED REGISTER(S)
PUSH CX
;SEE IF WE CAN LEGALLY CHEAT
CMP GPC2,0FFH ;CHEAT 510 TIMES OUT OF 512
JNE GW@GP3
CMP GPC0,1 ;WE STILL HAVE A 50/50 CHANCE
JNE GW@GP3 ;....YAY!
; RATS, WE HAVE TO DO IT THE HARD WAY
CALL GB@GPCI ;GET THE FIRST BYTE AND POINT TO THE NEXT
MOV BL,AL ;SAVE THE MSB IN BL
CALL GB@GPC ;GET THE LSB IN AL
MOV AH,BL ;PUT THE MSB IN AH
; DECREMENT THE GPC
XOR GPC0,1 ;CHANGE THE STATE OF BIT0
JZ GW@GP4 ;EXIT IF CHANGING FROM A "1" TO A "0"
DEC GPC ;ELSE DECREMENT THE REST OF THE GPC
GW@GP4: POP CX
POP BX
RET
;CREATE AND SAVE THE LOW ADDRESS BITS
GW@GP3: MOV AX,GPC ;GET GPC1 AND GPC2
MOV BL,GPC0 ;GET GPC BIT
SHL AX,1 ;SHIFT GPC2 AND
OR AL,BL ; APPEND THE GPC BIT
AND AX,01FFH ; AND ISOLATE THE LADD BITS
MOV LADD,AX ;SAVE LOW ADDRESS BITS
;CREATE AND SAVE THE VIRTUAL PAGE NUMBER
MOV AX,GPC ;GET VIRTUAL ADDRESS AGAIN
JMP GW@VM1 ; AND USE EXISTING CODE
GW@GPC ENDP
; -----------------------------------------------
; {GB@GPCI}
; GET A BYTE OF VIRTUAL MEMORY AT THE CURRENT GPC
; AND INCREMENT THE GPC
; -----------------------------------------------
;
; EXIT: BYTE IN [AX]
; GPC <= GPC + 1
;
PUBLIC GB@GPCI
GB@GPCI PROC
CALL GB@GPC ;GET THE BYTE
; AND FALL THROUGH .............
GB@GPCI ENDP
; -----------------
; {INCGPC}
; INCREMENT THE GPC
; -----------------
;
; EXIT: GPC <= GPC + 1
;
PUBLIC INCGPC
INCGPC PROC
;UPDATE THE VIRTUAL MEMORY POINTER
XOR GPC0,1 ;CHANGE THE STATE OF BIT0
JNZ INCGP1 ;EXIT IF CHANGING FROM A "0" TO A "1"
INC GPC ;ELSE BUMP THE REST OF THE GPC
INCGP1: RET
INCGPC ENDP
; -----------------------------------------------
; {GW@GPCI}
; GET A WORD OF VIRTUAL MEMORY AT THE CURRENT GPC
; GPC = GPC + 2
; -----------------------------------------------
;
; EXIT: WORD IN [AX]
; GPC <= GPC + 2
;
PUBLIC GW@GPCI
GW@GPCI PROC
CALL GW@GPC ;GET THE WORD
INC GPC ;SAME AS ADDING 2 TO THE ENTIRE GPC
RET
GW@GPCI ENDP
; -----------------------------
; {PB@GPC}
; PUT A BYTE AT THE CURRENT GPC
; -----------------------------
;
; ENTRY: [AL] = BYTE TO BE STORED
;
; EXIT: AL IS STORED AT THE SELECTED VIRTUAL ADDRESS
;
PUBLIC PB@GPC
PB@GPC PROC
PUSH BX ;SAVE AFFECTED REGISTER(S)
PUSH CX
PUSH AX ;SAVE THE VALUE
;CREATE AND SAVE THE LOW ADDRESS BITS
MOV AX,GPC ;GET GPC1 AND GPC2
MOV BL,GPC0 ;GET GPC BIT
SHL AX,1 ;SHIFT GPC2 AND
OR AL,BL ; APPEND THE GPC BIT
AND AX,01FFH ; AND ISOLATE THE LADD BITS
MOV LADD,AX ;SAVE LOW ADDRESS BITS
;CREATE AND SAVE THE VIRTUAL PAGE NUMBER
MOV AX,GPC ;GET VIRTUAL ADDRESS AGAIN
JMP PB@VM1 ; AND USE EXISTING CODE
PB@GPC ENDP
; -----------------------------
; {PW@GPC}
; PUT A WORD AT THE CURRENT GPC
; -----------------------------
;
; ENTRY: [AX] = WORD TO BE STORED
;
; EXIT: AX IS STORED AT THE SELECTED VIRTUAL ADDRESS
;
PUBLIC PW@GPC
PW@GPC PROC
PUSH BX ;SAVE AFFECTED REGISTER(S)
PUSH CX
;SEE IF WE CAN LEGALLY CHEAT
CMP GPC2,0FFH ;CHEAT 510 TIMES OUT OF 512
JNE PW@GP3
CMP GPC0,1 ;WE STILL HAVE A 50/50 CHANCE
JNE PW@GP3 ;....YAY!
; RATS, WE HAVE TO DO IT THE HARD WAY
XCHG AH,AL
MOV BL,AH
CALL PB@GPCI ;STORE THE BYTE AND POINT TO THE NEXT
MOV AL,BL
CALL PB@GPC
; DECREMENT THE GPC
XOR GPC0,1 ;CHANGE THE STATE OF BIT0
JZ PW@GP4 ;EXIT IF CHANGING FROM A "1" TO A "0"
DEC GPC ;ELSE DECREMENT THE REST OF THE GPC
PW@GP4: POP CX
POP BX
RET
PW@GP3: PUSH AX ;SAVE THE VALUE
;CREATE AND SAVE THE LOW ADDRESS BITS
MOV AX,GPC ;GET GPC1 AND GPC2
MOV BL,GPC0 ;GET GPC BIT
SHL AX,1 ;SHIFT GPC2 AND
OR AL,BL ; APPEND THE GPC BIT
AND AX,01FFH ; AND ISOLATE THE LADD BITS
MOV LADD,AX ;SAVE LOW ADDRESS BITS
;CREATE AND SAVE THE VIRTUAL PAGE NUMBER
MOV AX,GPC ;GET VIRTUAL ADDRESS AGAIN
;AND EXECUTE COMMON CODE
JMP PW@VM1
PW@GPC ENDP
; -----------------------------
; {PB@GPCI}
; PUT A BYTE AT THE CURRENT GPC
; AND INCREMENT THE GPC
; -----------------------------
;
; ENTRY: [AL] = BYTE TO BE STORED
;
; EXIT: AL IS STORED AT THE SELECTED VIRTUAL ADDRESS
; GPC <= GPC + 1
;
PUBLIC PB@GPCI
PB@GPCI PROC
CALL PB@GPC ;WRITE OUT THE BYTE
;UPDATE THE VIRTUAL MEMORY POINTER
JMP INCGPC
PB@GPCI ENDP
; -----------------------------
; {PW@GPCI}
; PUT A WORD AT THE CURRENT GPC
; GPC = GPC + 2
; -----------------------------
;
; ENTRY: [AX] = WORD TO BE STORED
;
; EXIT: AX IS STORED AT THE SELECTED VIRTUAL ADDRESS
; GPC <= GPC + 2
;
PUBLIC PW@GPCI
PW@GPCI PROC
CALL PW@GPC ;PUT THE WORD IN VIRTUAL MEMORY
INC GPC ;SAME AS ADDING 2 TO THE ENTIRE GPC
RET
PW@GPCI ENDP
; ---------------------------------------------------
; {GETSHT}
; GET A BYTE OF VIRTUAL MEMORY AT THE CURRENT GPC
; INCREMENT THE GPC
; AND PUT THE BYTE IN [VALUE]
; ---------------------------------------------------
;
; ENTRY: G-MACHINE PROGRAM COUNTER DEFINITION
; GPC1 = MSB OF GPC
; GPC2 = LSB OF GPC
; GPC0 = BIT 0 OF BYTE IS BIT 0 OF GPC
;
; EXIT: BTYE IN [AX] AND [VALUE]
; GPC <= GPC + 1
;
;
PUBLIC GETSHT
GETSHT PROC
CALL GB@GPCI ;GET THE BYTE IN [AL]
MOV VALUE,AX ; AND SAVE IT IN VALUE
RET
GETSHT ENDP
; ---------------------------------------------------
; {GETLNG}
; GET A WORD OF VIRTUAL MEMORY AT THE CURRENT GPC
; GPC = GPC + 2
; AND PUT THE WORD IN [VALUE]
; ---------------------------------------------------
;
; ENTRY: G-MACHINE PROGRAM COUNTER DEFINITION
; GPC1 = MSB OF GPC
; GPC2 = LSB OF GPC
; GPC0 = BIT 0 OF BYTE IS BIT 0 OF GPC
;
; EXIT: WORD IN [AX]
; GPC <= GPC + 2
;
; REGISTERS DESTROYED: AX
;
PUBLIC GETLNG
GETLNG PROC
CALL GW@GPCI ;GET THE WORD IN [AX]
MOV VALUE,AX ; AND SAVE IT IN VALUE
RET
GETLNG ENDP
SUBTTL VMP VIRTUAL MEMORY ACCESS
PAGE
; ---------------------------------------------------------------
; The following subroutines are used to access Virtual Memory
; through the VMP.
;
; VIRTUAL MEMORY POINTER DEFINITION
; VMP VMP1 = MSB OF VMP VMP2 = LSB OF VMP
; VMP0 = BIT 0 OF BYTE IS BIT 0 OF VMP
; ----------------------------------------------------------------
; ------
; ADDVMP
; ------
;
; ENTRY: [AX] = VALUE TO BE ADDED TO VMP
; EXIT: VMP <= VMP + [AX]
;
ADDVMP PROC
SHR AX,1 ;THINK ABOUT IT
JNC ADDVM1
XOR VMP0,1 ;CHANGE THE STATE OF BIT0
JNZ ADDVM1 ; CONTINUE IF CHANGING FROM A "0" TO A "1"
INC VMP ;ELSE BUMP THE REST OF VMP
ADDVM1: ADD VMP,AX
RET
ADDVMP ENDP
; -----------------------------------------------
; {GB@VMP}
; GET A BYTE OF VIRTUAL MEMORY AT THE CURRENT VMP
; -----------------------------------------------
;
; EXIT: BYTE IN [AX]
;
PUBLIC GB@VMP
GB@VMP PROC
;CREATE AND SAVE THE LOW ADDRESS BITS
PUSH BX ;SAVE AFFECTED REGISTER(S)
PUSH CX
MOV AX,VMP ;GET VMP1 AND VMP2
MOV BL,VMP0 ;GET VMP BIT
SHL AX,1 ;SHIFT VMP2 AND
OR AL,BL ; APPEND THE VMP BIT
AND AX,01FFH ; AND ISOLATE THE LADD BITS
MOV LADD,AX ;SAVE LOW ADDRESS BITS
;CREATE AND SAVE THE VIRTUAL PAGE NUMBER
MOV AX,VMP ;GET VIRTUAL ADDRESS AGAIN
; AND FALL THROUGH .............
GB@VMP ENDP
; --------------------------------
; CODE COMMON TO GB@VMP AND GB@GPC
; --------------------------------
GB@VM1 PROC
MOV VPAGEN,AH ; SAVE THE VIRTUAL PAGE NUMBER
;PAGE IN THE BLOCK OF VIRTUAL MEMORY
CALL GTVBLK ; AND MAKE LOC VALID
;RESTORE LADD AND MAKE A FULLY FORMED ADDRESS
MOV AL,LOC ;GET THE ABSOLUTE BLOCK NUMBER
SUB AH,AH
SHL AX,1 ;SHIFT THE SEGMENT BIT INTO BIT 8 AND A "0"
; INTO BIT 0, PUTTING THE HIGHER ADDRESS BITS
; IN [AL] AND THE SEGMENT BIT IN [AH]
MOV BX,LADD ;RESTORE THE LOWER ADDRESS BITS
OR BH,AL ;COMBINE THE LOWER AND HIGHER ADDRESS BITS
;DETERMINE THE PROPER SEGMENT
MOV CX,SEG0 ;PRE-POINT AT SEG0
OR AH,AH ;TEST SEGMENT BIT
JZ GB@VM2 ;IF "0" THEN IT'S SEG0
MOV CX,SEG1 ; ELSE POINT TO THE OTHER SEGMENT
GB@VM2: MOV ES,CX ;ES POINTS TO THE CORRECT SEGMENT
;GET THE BYTE
MOV AL,ES:[BX] ;GET THE BYTE
SUB AH,AH ;GET RID OF UNWANTED BYTE
POP CX ;RESTORE AFFECTED REGISTER(S)
POP BX
RET
GB@VM1 ENDP
; -----------------------------
; {GW@VMP}
; GET A WORD AT THE CURRENT VMP
; -----------------------------
;
; EXIT: WORD IN [AX]
; VMP <= VMP + 2
;
PUBLIC GW@VMP
GW@VMP PROC
PUSH BX ;SAVE AFFECTED REGISTER(S)
PUSH CX
;SEE IF WE CAN LEGALLY CHEAT
CMP VMP2,0FFH ;CHEAT 510 TIMES OUT OF 512
JNE GW@VM3
CMP VMP0,1 ;WE STILL HAVE A 50/50 CHANCE
JNE GW@VM3 ;....YAY!
; RATS, WE HAVE TO DO IT THE HARD WAY
CALL GB@VMPI ;GET THE BYTE AND POINT TO THE NEXT
MOV BL,AL
CALL GB@VMP ;GET THE SECOND BYTE
MOV AH,BL ;FORM THE WORD
; DECREMENT THE VMP
XOR VMP0,1 ;CHANGE THE STATE OF BIT0
JZ GW@VM4 ;EXIT IF CHANGING FROM A "1" TO A "0"
DEC VMP ;ELSE DECREMENT THE REST OF THE VMP
GW@VM4: POP CX
POP BX
RET
;CREATE AND SAVE THE LOW ADDRESS BITS
GW@VM3: MOV AX,VMP ;GET VMP1 AND VMP2
MOV BL,VMP0 ;GET VMP BIT
SHL AX,1 ;SHIFT VMP2 AND
OR AL,BL ; APPEND THE VMP BIT
AND AX,01FFH ; AND ISOLATE THE LADD BITS
MOV LADD,AX ;SAVE LOW ADDRESS BITS
;CREATE AND SAVE THE VIRTUAL PAGE NUMBER
MOV AX,VMP ;GET VIRTUAL ADDRESS AGAIN
; AND FALL THROUGH .............
GW@VMP ENDP
; --------------------------------
; CODE COMMON TO GW@VMP AND GW@GPC
; --------------------------------
GW@VM1 PROC
MOV VPAGEN,AH ; SAVE THE VIRTUAL PAGE NUMBER
;PAGE IN THE BLOCK OF VIRTUAL MEMORY
CALL GTVBLK ; AND MAKE LOC VALID
;RESTORE LADD AND MAKE A FULLY FORMED ADDRESS
MOV AL,LOC ;GET THE ABSOLUTE BLOCK NUMBER
SUB AH,AH
SHL AX,1 ;SHIFT THE SEGMENT BIT INTO BIT 8 AND A "0"
; INTO BIT 0, PUTTING THE HIGHER ADDRESS BITS
; IN [AL] AND THE SEGMENT BIT IN [AH]
MOV BX,LADD ;RESTORE THE LOWER ADDRESS BITS
OR BH,AL ;COMBINE THE LOWER AND HIGHER ADDRESS BITS
;DETERMINE THE PROPER SEGMENT
MOV CX,SEG0 ;PRE-POINT AT SEG0
OR AH,AH ;TEST SEGMENT BIT
JZ GW@VM2 ;IF "0" THEN IT'S SEG0
MOV CX,SEG1 ; ELSE POINT TO THE OTHER SEGMENT
GW@VM2: MOV ES,CX ;ES POINTS TO THE CORRECT SEGMENT
;GET THE WORD
MOV AX,ES:[BX] ;GET THE WORD
XCHG AH,AL ;SWAPIFY
POP CX ;RESTORE AFFECTED REGISTER(S)
POP BX
RET
GW@VM1 ENDP
; -----------------------------------------------
; {GB@VMPI}
; GET A BYTE OF VIRTUAL MEMORY AT THE CURRENT VMP
; AND INCREMENT THE VMP
; -----------------------------------------------
;
; EXIT: BYTE IN [AX]
; VMP <= VMP + 1
;
PUBLIC GB@VMPI
GB@VMPI PROC
CALL GB@VMP ;GET THE BYTE
; AND FALL THROUGH .............
GB@VMPI ENDP
; -----------------
; {INCVMP}
; INCREMENT THE VMP
; -----------------
;
; EXIT: VMP <= VMP + 1
;
PUBLIC INCVMP
INCVMP PROC
;UPDATE THE VIRTUAL MEMORY POINTER
XOR VMP0,1 ;CHANGE THE STATE OF BIT0
JNZ INCMP1 ;EXIT IF CHANGING FROM A "0" TO A "1"
INC VMP ;ELSE BUMP THE REST OF THE VMP
INCMP1: RET
INCVMP ENDP
; -----------------------------------------------
; {GW@VMPI}
; GET A WORD OF VIRTUAL MEMORY AT THE CURRENT VMP
; VMP = VMP + 2
; -----------------------------------------------
;
; EXIT: WORD IN [AX]
; VMP <= VMP + 2
;
PUBLIC GW@VMPI
GW@VMPI PROC
CALL GW@VMP ;GET THE WORD
INC VMP ;SAME AS ADDING 2 TO THE ENTIRE VMP
RET
GW@VMPI ENDP
; -----------------------------
; {PB@VMP}
; PUT A BYTE AT THE CURRENT VMP
; -----------------------------
;
; ENTRY: [AL] = BYTE TO BE STORED
;
; EXIT: AL IS STORED AT THE SELECTED VIRTUAL ADDRESS
;
PUBLIC PB@VMP
PB@VMP PROC
PUSH BX ;SAVE AFFECTED REGISTER(S)
PUSH CX
PUSH AX ;SAVE THE VALUE
;CREATE AND SAVE THE LOW ADDRESS BITS
MOV AX,VMP ;GET VMP1 AND VMP2
MOV BL,VMP0 ;GET VMP BIT
SHL AX,1 ;SHIFT VMP2 AND
OR AL,BL ; APPEND THE VMP BIT
AND AX,01FFH ; AND ISOLATE THE LADD BITS
MOV LADD,AX ;SAVE LOW ADDRESS BITS
;CREATE AND SAVE THE VIRTUAL PAGE NUMBER
MOV AX,VMP ;GET VIRTUAL ADDRESS AGAIN
; AND FALL THROUGH .............
PB@VMP ENDP
; --------------------------------
; CODE COMMON TO PB@VMP AND PB@GPC
; --------------------------------
PB@VM1 PROC
MOV VPAGEN,AH ; TO SAVE THE VIRTUAL PAGE NUMBER
;PAGE IN THE BLOCK OF VIRTUAL MEMORY
CALL GTVBLK ; AND MAKE LOC VALID
;RESTORE LADD AND MAKE A FULLY FORMED ADDRESS
MOV AL,LOC ;GET THE ABSOLUTE BLOCK NUMBER
SUB AH,AH
SHL AX,1 ;SHIFT THE SEGMENT BIT INTO BIT 8 AND A "0"
; INTO BIT 0, PUTTING THE HIGHER ADDRESS BITS
; IN [AL] AND THE SEGMENT BIT IN [AH]
MOV BX,LADD ;RESTORE THE LOWER ADDRESS BITS
OR BH,AL ;COMBINE THE LOWER AND HIGHER ADDRESS BITS
;DETERMINE THE PROPER SEGMENT
MOV CX,SEG0 ;PRE-POINT AT SEG0
OR AH,AH ;TEST SEGMENT BIT
JZ PB@VM2 ;IF "0" THEN IT'S SEG0
MOV CX,SEG1 ; ELSE POINT TO THE OTHER SEGMENT
PB@VM2: MOV ES,CX ;ES POINTS TO THE CORRECT SEGMENT
;STORE THE BYTE
POP AX ;RESTORE THE BYTE
MOV ES:[BX],AL
POP CX ;RESTORE AFFECTED REGISTER(S)
POP BX
RET
PB@VM1 ENDP
; -----------------------------
; {PW@VMP}
; PUT A WORD AT THE CURRENT VMP
; -----------------------------
;
; ENTRY: [AX] = WORD TO BE STORED
;
; EXIT: AX IS STORED AT THE SELECTED VIRTUAL ADDRESS
;
PUBLIC PW@VMP
PW@VMP PROC
PUSH BX ;SAVE AFFECTED REGISTER(S)
PUSH CX
;SEE IF WE CAN LEGALLY CHEAT
CMP VMP2,0FFH ;CHEAT 510 TIMES OUT OF 512
JNE PW@VM3
CMP VMP0,1 ;WE STILL HAVE A 50/50 CHANCE
JNE PW@VM3 ;....YAY!
; RATS, WE HAVE TO DO IT THE HARD WAY
XCHG AH,AL
MOV BL,AH
CALL PB@VMPI ;STORE THE BYTE AND POINT TO THE NEXT
MOV AL,BL
CALL PB@VMP
; DECREMENT THE VMP
XOR VMP0,1 ;CHANGE THE STATE OF BIT0
JZ PW@VM4 ;EXIT IF CHANGING FROM A "1" TO A "0"
DEC VMP ;ELSE DECREMENT THE REST OF THE VMP
PW@VM4: POP CX
POP BX
RET
PW@VM3: PUSH AX ;SAVE THE VALUE
;CREATE AND SAVE THE LOW ADDRESS BITS
MOV AX,VMP ;GET VMP1 AND VMP2
MOV BL,VMP0 ;GET VMP BIT
SHL AX,1 ;SHIFT VMP2 AND
OR AL,BL ; APPEND THE VMP BIT
AND AX,01FFH ; AND ISOLATE THE LADD BITS
MOV LADD,AX ;SAVE LOW ADDRESS BITS
;CREATE AND SAVE THE VIRTUAL PAGE NUMBER
MOV AX,VMP ;GET VIRTUAL ADDRESS AGAIN
; AND FALL THROUGH .............
PW@VMP ENDP
; --------------------------------
; CODE COMMON TO PW@VMP AND PW@GPC
; --------------------------------
PW@VM1 PROC
MOV VPAGEN,AH ; TO SAVE THE VIRTUAL PAGE NUMBER
;PAGE IN THE BLOCK OF VIRTUAL MEMORY
CALL GTVBLK ; AND MAKE LOC VALID
;RESTORE LADD AND MAKE A FULLY FORMED ADDRESS
MOV AL,LOC ;GET THE ABSOLUTE BLOCK NUMBER
SUB AH,AH
SHL AX,1 ;SHIFT THE SEGMENT BIT INTO BIT 8 AND A "0"
; INTO BIT 0, PUTTING THE HIGHER ADDRESS BITS
; IN [AL] AND THE SEGMENT BIT IN [AH]
MOV BX,LADD ;RESTORE THE LOWER ADDRESS BITS
OR BH,AL ;COMBINE THE LOWER AND HIGHER ADDRESS BITS
;DETERMINE THE PROPER SEGMENT
MOV CX,SEG0 ;PRE-POINT AT SEG0
OR AH,AH ;TEST SEGMENT BIT
JZ PW@VM2 ;IF "0" THEN IT'S SEG0
MOV CX,SEG1 ; ELSE POINT TO THE OTHER SEGMENT
PW@VM2: MOV ES,CX ;ES POINTS TO THE CORRECT SEGMENT
;STORE THE WORD
POP AX ;RESTORE THE WORD
XCHG AH,AL ;SWAPIFY
MOV ES:[BX],AX ;WRITE IT OUT
POP CX ;RESTORE AFFECTED REGISTER(S)
POP BX
RET
PW@VM1 ENDP
; -----------------------------
; {PB@VMPI}
; PUT A BYTE AT THE CURRENT VMP
; AND INCREMENT THE VMP
; -----------------------------
;
; ENTRY: [AL] = BYTE TO BE STORED
;
; EXIT: AL IS STORED AT THE SELECTED VIRTUAL ADDRESS
; VMP <= VMP + 1
;
PUBLIC PB@VMPI
PB@VMPI PROC
CALL PB@VMP ;WRITE OUT THE BYTE
;UPDATE THE VIRTUAL MEMORY POINTER
JMP INCVMP
PB@VMPI ENDP
; -----------------------------
; {PW@VMPI}
; PUT A WORD AT THE CURRENT VMP
; VMP = VMP + 2
; -----------------------------
;
; ENTRY: [AX] = WORD TO BE STORED
;
; EXIT: AX IS STORED AT THE SELECTED VIRTUAL ADDRESS
; VMP <= VMP + 2
;
PUBLIC PW@VMPI
PW@VMPI PROC
CALL PW@VMP ;PUT THE WORD IN VIRTUAL MEMORY
INC VMP ;SAME AS ADDING 2 TO THE ENTIRE VMP
RET
PW@VMPI ENDP
;------------------------------------------------
; E N D O F S U B R O U T I N E S
;------------------------------------------------
SUBTTL COLD START
PAGE
;----------------------------------------------------------------------
; C O L D S T A R T
;----------------------------------------------------------------------
PUBLIC DIPBGN,REST1,REST2
DIPBGN PROC
CALL SYSINI ;DO ANY SYSTEM INITIALIZATION
CALL MTIME ;INITIALIZE RANDOM NUMBER GENERATOR
MOV WORD PTR OLDGSP,DI ;INITIALIZE OLD GSTACK POINTER
; SET DEFAULT CLIPPING PARAMETERS
MOV WINDX1,0 ;FULL SCREEN
MOV WINDY1,0
MOV WINDX2,39
MOV WINDY2,23
MOV WINDH,24 ;WINDH = (WINDY2 - WINDY1) + 1
MOV WINDW,40 ;WINDW = (WINDX2 - WINDX1) + 1
;
; GET G-PRELOAD INTO THE BUFFER AREA
;
; INITIALIZE GCODE
MOV GCODE,0
; GET THE FIRST BLOCK OF G-PRELOAD
MOV VPAGEN,0 ;VIRTUAL BLOCK 0
CALL GTPBLK ;GET PRELOAD BLOCK
; GET THE GAME ID FOR SAVE/RESTORE AND POSTERITY
;
MOV VMP,0 ;SET UP VIRTUAL MEMORY ADDRESS POINTER
MOV VMP0,0 ; TO RETRIEVE [GEND]
MOV AX,GID
CALL ADDVMP
CALL GW@VMP ;GET THE END OF G-PRELOAD (WORDPTR)
MOV ZORKID,AX ; SAVE IT AWAY
; EXTRACT DATA FROM G-PRELOAD HEADER
; DETERMINE THE NUMBER OF PAGES IN G-PRELOAD
; GET THE VIRTUAL ADDRESS OF THE START OF G-PURELOAD
MOV VMP,0 ;SET UP VIRTUAL MEMORY ADDRESS POINTER
MOV VMP0,0 ; TO RETRIEVE [GEND]
MOV AX,GEND
CALL ADDVMP
CALL GW@VMP ;GET THE END OF G-PRELOAD (WORDPTR)
; SEE IF THE 1ST. VIRTUAL PAGE OF G-PURELOAD CONTAINS ANY G-PRELOAD
OR AL,AL ;IS THERE A PARTIAL PRE-LOAD RECORD?
JZ NOGPRT ;NOPE
INC AH ;YUP
; NO G-CODE PARTIAL RECORD
; DETERMINE THE TOTAL NUMBER OF PAGES IN G-PRELOAD
NOGPRT: MOV GPEND,AH ;SAVE IT, IT'S THE FIRST VIRTUAL BLOCK
; # OF G-PURELOAD
MOV GPRBLK,AH ; AND THE TOTAL NUMBER OF
; G-PRELOAD BLOCKS
; DO WE HAVE ENOUGH ROOM IN THE PAGING AREA FOR G-PRELOAD?
CMP BUFFERS,AH
JA GOTRM1 ;....YES
; *** ERROR #0: GAME PRELOAD IS TOO BIG ***
NOGRAM: MOV AL,0
JMP GERROR
; GET THE REST OF G-PRELOAD INTO THE BUFFER AREA
; USE GSTRT TO DETERMINE THE NUMBER OF G-PRELOAD BLOCKS TO
; TO BE LOADED
GOTRM1: MOV AL,GMAJOR ; SET INTERPRETER VERSION
MOV AH,GMINOR ; LETTER AND NUMBER
MOV WORD PTR ES:[GINTVER],AX ; STORE INTERPRETER VERSION
MOV CL,GPRBLK ;GET TOTAL NUMBER OF G-PRELOAD BLOCKS
DEC CL ;-1 SINCE WE'VE ALREADY DONE THE 1ST.
JZ GTIPRE ;LEAVE IF IT WAS THE ONLY PRELOAD BLOCK
SUB CH,CH ;INITIALIZE LOOP COUNTER
MOV VPAGEN,1 ;START WITH VIRTUAL BLOCK #1
; LOAD THE REMAINING G-PRELOAD LOOP
LGPRLP: PUSH CX ;SAVE LOOP COUNTER
CALL GTPBLK ;GET THE PRELOAD BLOCK
INC VPAGEN ;NEXT BLOCK
POP CX ;RESTORE LOOP COUNTER
LOOP LGPRLP ;CONTINUE UNTIL ALL OF G-PRELOAD IS IN
;
; GET I-PRELOAD INTO THE BUFFER AREA
;
; CALCULATE 1ST. VIRTUAL PAGE OF THE IFILE
GTIPRE: MOV VMP,0 ;GET THE LENGTH OF THE G-PROGRAM
MOV VMP0,0 ; IN WORDS
MOV AX,GLEN
CALL ADDVMP
CALL GW@VMP
;PATCH FOR PADDING PURPOSES
INC AX ;ICODE IS AT THE NEXT WORD ADDRESS
MOV ICODE,AX ;SAVE VIRTUAL STARTING ADDRESS
MOV ISTRT,AH ; AND STARTING VIRTUAL PAGE NUMBER
; GET THE FIRST BLOCK OF I-PRELOAD
MOV VPAGEN,AH ;SETUP THE VIRTUAL PAGE NUMBER
CALL GTPBLK ; AND GET THE FIRST BLOCK OF I-PRELOAD
; DETERMINE VIRTUAL ADDRESS OF IPURE
MOV AX,ICODE
MOV VMP,AX
MOV VMP0,0
MOV AX,IEND
CALL ADDVMP
CALL GW@VMP ;GET THE LENGTH OF I-PRELOAD IN WORDS
INC AX ;I-PURE STARTS AT THE NEXT EVEN ADDRESS
MOV BX,ICODE ;GET OFFSET TO THE START OF THE IFILE
ADD AX,BX ;ADD STARTING ADDRESS
; TO GET A GFILE RELATIVE ADDRESS
MOV IPURE,AX ;SAVE CORRECTED START OF I-PURE
;
; GET THE REST OF I-PRELOAD INTO THE BUFFER AREA
;
; DETERMINE THE NUMBER OF BLOCKS OF I-PRELOAD REMAINING
; DETERMINE THE TOTAL NUMBER OF I-PRELOAD BLOCKS
; USE THE STARTING I-PRELOAD VIRTUAL BLOCK NUMBER
; TO DETERMINE AN ADJUSTED SIZE OF I-PRELOAD
MOV BH,ISTRT ;GET VIRTUAL I-PRELOAD STARTING PAGE
SUB BL,BL ; PAGE ALIGN IT
MOV AX,IPURE ;GET THE RELATIVIZED STARTING
; ADDRESS OF IPURE
SUB AX,BX ;SUBTRACT ZERO ADJUSTED STARTING ADDR
; SEE IF THE 1ST. VIRTUAL PAGE OF IPURE CONTAINS ANY I-PRELOAD
OR AL,AL ;SEE IF THERE'S A PARTIAL RECORD
JZ NOIPRT ;NOPE
INC AH ;YUP
NOIPRT: MOV IPRBLK,AH ;SAVE TOTAL NUMBER OF I-PRELOAD BLOCKS
; DO WE HAVE ENOUGH ROOM IN THE PAGING AREA FOR I-PRELOAD?
ADD AH,GPRBLK ;ADD IN THE NUMBER OF G-PRELOAD BLOCKS
CMP BUFFERS,AH
JA NOIP1
JMP NOGRAM ;....NO
NOIP1: MOV CL,IPRBLK ;GET THE NUMBER OF I-PRELOAD BLOCKS
OR CL,CL ;SINCE IT'S POSSIBLE TO HAVE NONE
JZ INITAB ; WE HAVE TO CHECK FOR IT
DEC CL ;-1 SINCE WE'VE ALREADY DONE THE 1ST.
JZ INITAB ;LEAVE IF IT WAS THE ONLY PRELOAD BLOCK
SUB CH,CH ;INITIALIZE LOOP COUNTER
;INITIALIZE VALUES FOR I-PRELOAD LOOP
MOV AL,ISTRT ;GET VIRTUAL I-PRELOAD STARTING BLOCK #
INC AL ;ADD 1 SINCE WE'VE ALREADY LOADED THE
; FIRST BLOCK
MOV VPAGEN,AL ;SAVE STARTING BLOCK #
; LOAD REMAINING I-PRELOAD LOOP
LIPRLP: PUSH CX ;SAVE LOOP COUNTER
CALL GTPBLK ;GET THE BLOCK
INC VPAGEN ;NEXT BLOCK
POP CX ;RESTORE LOOP COUNTER
LOOP LIPRLP ;CONTINUE UNTIL ALL OF I-PRELOAD IS IN
; IF THE GAME FITS IN AVAILABLE MEMORY,
; READ THE WHOLE THING IN
CMP FSIZE,0 ; (B4) DOES THE GAME FIT?
JE SLURP1
MOV DL,GPRBLK ; (B4) SO START SLURPING
XOR DH,DH ; (B4) SCRAP TOP HALF
MOV CL,9 ; (B4) CONVERT TO BYTES
SHL DX,CL ; (B4) OF G-PRELOAD
MOV CX,0 ; (B4) SET UP FOR SEEK
MOV BX,GAMHNDL
MOV AX,4200H ; (B4) SEEK PAST B-O-F
INT 21H
PUSH DS ; (B4) USE GAME BUFFER
MOV CX,SEG0 ; (B4) MAKE ADDRESSIBLE THRU DS
MOV DS,CX
MOV DX,AX ; (B4) SAVE OFFSET
MOV AX,0H ; (B4) HOW MANY BYTES TO END OF SEG?
SUB AX,DX ; (B4) ONE SEG IN LENGTH
MOV CX,AX ; (B4) THAT'S HOW MANY TO READ FIRST
MOV AH,CRDRNDZ ; (B4) READ THAT AMOUNT
INT 21H
CMP AX,CX ; (B4) THAT COULD BE THE END...
JNE SLURP
MOV AX,DS ; (B4) MAKE NEXT SEG ADDRESSIBLE
ADD AX,1000H ; (B4) 64K ABOVE THE FIRST
MOV DS,AX
MOV CX,0FFFFH ; (B4) READ UP TO ANOTHER 64K
MOV DX,0 ; (B4) STARTING AT THE BEGINNING
MOV AH,CRDRNDZ ; (B4) READ IT ALL IN
INT 21H
SLURP: POP DS
PUSH DI
PUSH ES ; (B4) SAVES
PUSH DS ; (B4) MAKE DS ADDRESSIBLE THRU ES
POP ES
MOV AL,1 ; (B4) ALL PAGES WILL BE PRELOAD
MOV CX,0FFH
MOV DI,OFFSET MEMMAP ; (B4) FILL TABLE TO INDICATE ALL IN
SLURP2: MOV AH,CL ; (B4) FILL IN BUF NUMBER
SUB AH,0FFH ; (B4) FROM 0 - FF
NEG AH ; (B4) RECTIFY
STOSW ; (B4) STORE IT
LOOP SLURP2
POP ES
POP DI
SLURP1:
;
; INITIALIZE POINTERS TO VARIABLES
;
; GLOBAL TABLE [GLOBAL]
INITAB: MOV AX,GCODE
MOV VMP,AX
MOV VMP0,0
MOV AX,GGLOB
CALL ADDVMP
CALL GW@VMP ;GET POINTER TO GLOBAL TABLE
MOV GLOBAL,AX ;SAVE IT
; LOCAL TABLE [GLOCS]
SUB AX,0FH ;BACK UP 15 WORDS FOR LOCALS
MOV GLOCS,AX ;STORE PTR TO START OF LOCALS
;
; REPAIR PREDEFINED GLOBALS 16, 17, 18, AND 19
;
; GLOBAL 16 - NUMBER OF BLOCKSETS
MOV AX,ICODE
MOV GPC,AX
MOV GPC0,0
MOV AX,IBLKS
CALL ADDGPC
CALL GETSHT ;GET THE NUMBER OF BLOCKSETS IN VALUE
MOV NBLOKS,AL ;STORE IT IN NBLOKS
MOV AX,16 ; AND IN GLOBAL #16
CALL VARPUT
; GLOBAL 17 - POINTER TO BLOCKSET POINTER TABLE
MOV AX,ICODE ;GET OFFSET TO THE START OF IFILE
ADD AX,4 ;BPT IS 8-BYTES FURTHER DOWN
MOV BTAB,AX ;SAVE VIRTUAL ADDRESS OF BPT
MOV VALUE,AX ;PUT IT IN VALUE, SO WE CAN
MOV AX,17 ;STORE THE VIRTUAL ADDRESS OF THE
CALL VARPUT ; BLOCKSET POINTER TABLE IN GLOBAL #17
; GLOBAL 18 - NUMBER OF ICONS
MOV AX,ICODE
MOV GPC,AX
MOV GPC0,0
MOV AX,IICONS
CALL ADDGPC
CALL GETSHT ;GET THE NUMBER OF ICONS IN VALUE
MOV NICONS,AL ;SAVE NUMBER OF ICONS
MOV AX,18 ;STORE NICONS IN GLOBAL #18
CALL VARPUT
; GLOBAL 19 - PTR TO ICON PTR TABLE
MOV AL,NBLOKS ;GET NO. OF BLOCKSETS
SUB AH,AH
; SHL AX,1 ;WORDIFY IT TO GET SIZE OF BLOCKSET TBL
; SHR AX,1 ; AND DEWORDIFY 'CAUSE BPT IS A WRDPTR
MOV CX,BTAB ;GET THE BPT VIRTUAL ADDRESS
ADD AX,CX ;POINT PAST THE BPT
MOV ITAB,AX ;SAVE VIRTUAL ADDRESS OF IPT
MOV VALUE,AX ;PUT IT IN VALUE, SO WE CAN
MOV AX,19 ;STORE THE VIRTUAL ADDRESS OF
CALL VARPUT ; THE ICON POINTER TABLE IN GLOBAL #19
;
; TABLE PATCHING
;
; RELATIVIZE THE BPT TO THE START OF THE GFILE
; INITIALIZE LOOP COUNTER WITH THE NO. OF BLOCKSETS
MOV CL,NBLOKS
SUB CH,CH
; POINT TO THE BPT
MOV AX,BTAB
MOV VMP,AX
MOV VMP0,0
; GET THE OFFSET TO THE START OF THE IFILE
MOV DX,ICODE
REST1: PUSH CX ;SAVE LOOP COUNTER
PUSH DX ;SAVE OFFSET
CALL GW@VMP ;GET THE BLOCKSET POINTER
POP DX ;RESTORE OFFSET
PUSH DX ;SAVE OFFSET
ADD AX,DX ; RELATIVIZE IT
CALL PW@VMPI ;SAVE POINTER
POP DX ;RESTORE OFFSET
POP CX ;RESTORE LOOP COUNTER
LOOP REST1 ;CONTINUE TILL WE'VE DONE THEM ALL
; RELATIVIZE THE IPT TO THE START OF THE GFILE
; INITIALIZE LOOP COUNTER WITH THE NO. OF ICONS
MOV CL,NICONS
SUB CH,CH
; POINT TO THE IPT
MOV AX,ITAB
MOV VMP,AX
MOV VMP0,0
; GET THE OFFSET TO THE START OF THE IFILE
MOV DX,ICODE
REST2: PUSH CX ;SAVE LOOP COUNTER
PUSH DX ;SAVE OFFSET
CALL GW@VMP ;GET THE ICON POINTER
POP DX ;RESTORE OFFSET
PUSH DX ;SAVE OFFSET
ADD AX,DX ; RELATIVIZE IT
CALL PW@VMPI ;SAVE POINTER
POP DX ;RESTORE OFFSET
POP CX ;RESTORE LOOP COUNTER
LOOP REST2 ;CONTINUE TILL WE'VE DONE THEM ALL
;
; INITIALIZE THE INTERPRETER PROGRAM COUNTER
;
; GET THE EXECUTION STARTING ADDRESS FROM WITHIN THE G-FILE
MOV VMP,0
MOV VMP0,0
MOV AX,GSTART ;OFFSET TO G-PROGRAM STARTING ADDRESS
CALL ADDVMP ;FORM THE VMP ADDRESS
CALL GW@VMP ;GET STARTING ADDRESS
; LOAD THE GPC WITH THE START ADDRESS
MOV GPC,AX
MOV GPC0,1
JMP MLOOP ; AND JUMP INTO THE MAINLOOP
DIPBGN ENDP
SUBTTL MAIN LOOP
PAGE
;----------------------------------------------------------------------
; INSTRUCTION INTERPRETATION LOOP
;----------------------------------------------------------------------
PUBLIC MLOOP,DC0,DC1,DC2,OPEXT,TOPX,OPX1,OPX2,OPX3,OPX4
PUBLIC OPXNXT,DOXOP,DODIS,OP0,OP1,OP1A,OP1B,OP1EX,BADOP1
PUBLIC OP2,OP2A,OP2B,OP2C,OP2D,OP2EX,OP2F
MLOOP PROC
;
; INITIALIZE LOOP
;
MOV NARGS,0 ;CLEAR #ARGS
CALL GB@GPCI ;GET THE OPERATION BYTE
MOV OPCODE,AL ;SAVE A COPY
;debug
CMP GPC,1234H
JNE MLP1
JMP MLP1
;
; DETERMINE OPCODE TYPE
;
; IF OPCODE IS POSITIVE ( < 80H ) THEN IT'S A 2-OP (SHORT FORM)
MLP1: CMP AL,80H ;IS IT A 2-OP (SHORT FORM)?
JNB DC0 ;....NO.
JMP OP2 ;....YES, GO DO IT
; IF OPCODE = 0E0H THEN IT'S GCALL
; WHICH IS HANDLED AS A SPECIAL CASE
DC0: CMP AL,0E0H ;IS IT "CALL"?
JNE DC1 ;....NO.
JMP GCALL ;....YES.
; IF OPCODE < 0B0H THEN IT'S A 1-OP
DC1: CMP AL,0B0H ;NO, IS IT A 1-OP?
JNB DC2 ;....NO.
JMP OP1 ;....YES.
; IF OPCODE < 0C0H THEN IT'S A 0-OP
; ELSE IT'S A X-OP
DC2: CMP AL,0C0H ;IS IT AN 0-OP?
JNB OPEXT ;....NO. IT IS AN X-OP
JMP OP0 ;....YES.
; --------------
; HANDLE AN X-OP
; --------------
OPEXT: CALL GB@GPCI ;GRAB ARGUMENT BYTE
MOV BYTE PTR ABYTE,AL ;SAVE IT
SUB BX,BX
MOV BYTE PTR ADEX,BL ;INIT LOOP INDEX
JZ OPX1 ;UNCONDITIONAL JUMP (ENTER LOOP)
TOPX: MOV AL,BYTE PTR ABYTE ;GET ARG BYTE
SAL AL,1 ;SHIFT NEXT 2 ARG BITS
SAL AL,1 ;INTO BITS 7 & 6
MOV BYTE PTR ABYTE,AL ;HOLD FOR LATER
OPX1: AND AL,11000000B ;ISOLATE ARGUMENT BITS
JNZ OPX2 ;IS IT A LONG IMMEDIATE?
; 00 = LONG IMMEDIATE
CALL GETLNG
JMP OPXNXT
OPX2: CMP AL,01000000B ;IS IT A SHORT IMMEDIATE?
JNZ OPX3 ;....NO, KEEP GUESSING
; 01 = SHORT IMMEDIATE
CALL GETSHT
JMP OPXNXT
OPX3: CMP AL,10000000B ;LAST TEST. VARIABLE?
JNZ OPX4 ;....NO. NO MORE ARGUMENTS
; 10 = VARIABLE
CALL GETVAR
OPXNXT: MOV BL,ADEX ;RESTORE ARGUMENT INDEX
SUB BH,BH ;CLEAR OUT HIGH BYTE
MOV AX,WORD PTR VALUE ;GRAB VALUE
MOV WORD PTR ARG1 [BX],AX ;STORE IN ARGUMENT TABLE
INC NARGS ;UPDATE ARGUMENT COUNTER
INC BX ;UPDATE INDEX
INC BX ;TWICE
MOV BYTE PTR ADEX,BL ;SAVE INDEX
CMP BL,8 ;HAVE WE DONE 4 ARGUMENTS YET?
JB TOPX ;....NO, GET SOME MORE
;ALL X-OP ARGUMENTS READY
OPX4: MOV AL,OPCODE ;GET THE OPCODE BACK
SUB AH,AH
CMP AL,0E0H ;IS IT AN EXTENDED 2-OP?
JAE DOXOP ;....NO, IT'S A REAL X-OP
JMP OP2EX ; ELSE, TREAT IT LIKE A REAL 2-OP
; IS IT A VALID X-0P?
DOXOP: MOV BX,OFFSET OPTX ;^ TO START OF X-OP DISPATCH TABLES
AND AL,1FH ;ISOLATE OP ID BITS
CMP AL,NOPSX ;IS IT A LEGAL X-OP
JB DODIS ;....YES, DISPATCH IT
; *** ERROR #2: UNDEFINED X-OP ***
MOV AL,2
JMP GERROR ;NO SUCH OPERATION
; ---------------
; OPCODE DISPATCH
; ---------------
;ENTRY: MASKED OPCODE INDEX IN [AL], BASE ADDRESS OF THE OPCODE
;DISPATCH TABLE IN BX.
DODIS: SHL AX,1 ;MAKE IT A WORD OFFSET
ADD BX,AX ;ADD THE OFFSET TO THE BASE ADDRESS
MOV SI,DS:[BX] ;GET THE ADDRESS FROM THE DISPATCH TABLE
CALL SI ;CALL THE OP ROUTINE
JMP MLOOP ;AND LOOP FOR NEXT INSTRUCTION
; --------------
; HANDLE A 0-OP
; --------------
OP0: MOV BX,OFFSET OPT0 ;^ TO 0-OP DISPATCH TABLE
AND AL,0FH ;EXTRACT OPERATION BITS
CMP AL,NOPS0 ;OUT OF RANGE?
JB DODIS ;....NO, DISPATCH IT
; *** ERROR #3: UNDEFINED 0-OP ***
MOV AL,3
JMP GERROR
; -------------
; HANDLE A 1-OP
; -------------
OP1: AND AL,00110000B ;1OP. ISOLATE ARGUMENT BITS
JNZ OP1A
CALL GETLNG ;00 = LONG IMMEDIATE
JMP OP1EX
OP1A: CMP AL,00010000B ;SHORT IMMEDIATE?
JNZ OP1B ;....NO
CALL GETSHT ;....YES, 01 = SHORT IMMEDIATE
JMP OP1EX
OP1B: CMP AL,00100000B ;VARIABLE
JNZ BADOP1 ;....NO, UNDEFINED STATE
CALL GETVAR ;10 = VARIABLE
OP1EX: MOV AX,VALUE ;VALUE TO [ARG1], UPDATE COUNT
MOV WORD PTR ARG1,AX
INC NARGS
MOV BX,OFFSET OPT1 ;^ TO OP-1 DISPATCH TABLES
MOV AL,OPCODE ;RESTORE OPCODE
AND AX,00001111B ;ISOLATE OP ID BITS
CMP AL,NOPS1 ;IF WITHIN RANGE,
JB DODIS ;THE EXECUTE THE 1-OP
; *** ERROR #4: UNDEFINED 1-OP ***
BADOP1: MOV AL,4
JMP GERROR
; -------------
; HANDLE A 2-OP
; -------------
OP2: AND AX,01000000B ;2OP, ISOLATE 1st ARG BIT
JNZ OP2A ;SHORT IMMEDIATE?
CALL GETSHT ;....YES. 0 = SHORT IMMEDIATE
JMP OP2B
OP2A: CALL GETVAR ;1 = VARIABLE
OP2B: MOV AX,WORD PTR VALUE ;VALUE TO [ARG1], UPDATE COUNT
MOV WORD PTR ARG1,AX
INC NARGS
MOV AL,OPCODE ;RESTORE THE OPCODE BYTE
AND AX,00100000B ;ISOLATE 2nd ARG BIT
JNZ OP2C ;SHORT IMMEDIATE?
CALL GETSHT ;....YES, 0 = SHORT IMMEDIATE
JMP OP2D
OP2C: CALL GETVAR ;1 = VARIABLE
OP2D: MOV AX,WORD PTR VALUE ;MOVE VALUE TO [ARG2]
MOV WORD PTR ARG2,AX
INC NARGS ;UPDATE COUNT
;ENTRY FOR EXTENDED 2-OPS
OP2EX: MOV BX,OFFSET OPT2 ;^ DISPATCH TABLE
MOV AL,OPCODE ;RESTORE OPCODE BYTE
AND AX,1FH ;ISOLATE OP ID BITS
CMP AL,NOPS2 ;LEGAL 2-OP?
JAE OP2F ;ERROR IF OUT OF RANGE
JMP DODIS ;ELSE DISPATCH
; *** ERROR #5: UNDEFINED 2-OP ***
OP2F: MOV AL,5
JMP GERROR
MLOOP ENDP
SUBTTL HIGH LEVEL IBMPC DEPENDENT ROUTINES
PAGE
; ---------------------
; SYSTEM INITIALIZATION
; ---------------------
PUBLIC SYSINI,SYS1,DSKERR
SYSINI PROC
MOV AH,DOSVER ; (7) GET DOS VERSION #
INT 21H ; (7) MUST BE 2.0 OR HIGHER
CMP AL,2 ; (7) DOS 2.0+
JGE GOODOS
MOV AL,17
JMP GERROR
; DETERMINE PRESENCE OF JOYSTICK
GOODOS: CALL STICK1 ; READ THE JOYSTICK PORT (OR ETHER)
AND AL,0EFH ; MASK OFF BUTTON BITS
CMP AL,8AH ; 8AH IS BOTTOM RIGHT (NO JOYSTICK)
JZ NOSTK
MOV JOYSTK,1 ; THERE IS A STICK SO USE IT!
; OPEN THE GAME FILE
NOSTK: PUSH ES
PUSH BX
MOV AX,BIOSDATA ; SEG ADDRESS BIOS DATA AREA
MOV ES,AX
MOV BX,KB_FLAG ; OFFSET TO BIOS DATA FOR KEYS
MOV AL,ES:[BX] ; GET BYTE OF KEYBOARD STATE
MOV KYFLGS,AL ; SAVE THEM
OR BYTE PTR ES:[BX],NUMLOCK ; TURN ON NUMLOCK
POP BX
POP ES
MOV AH,CURDSK ; (6) GET DEFAULT DRIVE
INT 21H ; (6) FROM DOS
MOV DEFDRV,AL ; (6) SAVE DEFAULT DRIVE
MOV CURDRV,AL ; (6) SAVE DEFAULT DRIVE
MOV AH,CFOPENZ ;ASCIZ STRINGS
MOV AL,0 ;OPEN FOR READ
MOV DX,OFFSET GAMFILE
INT 21H
JNC SYSIN1
JMP DSKERR
; DETERMINE SPACE AVAILABLE
SYSIN1: MOV GAMHNDL,AX ;SAVE THE FILE HANDLE
; DETERMINE THE SIZE OF DIP.DAT
; MOVE READ/WRITE POINTER TO THE END OF THE FILE
SUB CX,CX ;SET CX:DX OFFSET = 0
SUB DX,DX
MOV BX,GAMHNDL ;GET FILE HANDLE
MOV AH,CFSEEK ;SELECT LSEEK FUNCTION
MOV AL,2 ;USE OFFSET FROM END METHOD
INT 21H ;INVOKE FUNCTION CALL
; DX:AX = FILE SIZE IN BYTES
; DETERMINE THE NUMBER OF 512 BYTE PARA'S
TEST AX,1FFH ;IS THERE A PARTIAL DISK PARAGRAPH?
JZ SYSIN2 ;....NO
ADD AX,200H ;ADD 1 TO THE NUMBER OF PARA'S
SYSIN2: SHR AX,1 ;DETERMINE NO. OF 512 BYTE PARA'S
SUB AL,AL
XCHG AH,AL
CMP DX,0
JZ SYSIN3
ADD AL,80H
SYSIN3: MOV FSIZE,AL ;SAVE IT
; DETERMINE THE AMOUNT OF MEMORY AVAILABLE FOR PAGING BUFFERS
MOV BX,2 ;GET TOP OF MEMORY
MOV BX,CS:[BX] ;FROM OUT OF THE PSP
MOV AX,CS ;GET CODE SEG
SUB BX,AX ;SUB CSEG FROM TOPMEM
MOV CL,4 ;SHIFT COUNT
MOV AX,OFFSET MEMBUF ;USE OFFSET TO FIGURE PARAG'S
SHR AX,CL ;REDUCE THIS OFFSET TO PARA'S
INC AX ;SKIP ONE PARAGRAPH
SUB BX,AX ;YIELDS PARA'S AVAILABLE
MOV CL,5 ;DIVIDE BY 32
SHR BX,CL ; TO GET THE NUMBER OF 512 BYTE BUFFERS
; REDUCE THE NUMBER OF BUFFERS AVAILABLE TO ACCOMODATE MEMBUFF
DEC BX ;SUBTRACT 1 BUFFER FOR MEMBUF
CMP BX,128 ;ABOVE 80H, WE NEED ANOTHER
JL SYS1 ;WE HAVE 128 OR FEWER BUFFERS AVAILABLE
DEC BX ;TAKE AWAY ANOTHER PAGE
CMP BX,255 ;THIS IS THE MOST WE WILL NEED
JLE SYS1 ;DON'T MODIFY IF BX LE. 255
MOV BX,255 ;DONT ALLOW MORE THAN 255
SYS1: MOV BUFFERS,BL ;SAVE THE NUMBER OF BUFFERS
; DYNAMICALLY CALCULATE THE START OF SEG0
SHL BX,1 ;GET NUMBER OF BYTES IN MEMBUF
SHL BX,1 ;IT'S 4 BYTES x BX BYTES LONG
ADD BX,OFFSET MEMBUF ;CALCULATE TABLE LOCATION
MOV CX,4 ;REDUCE NUMBER OF BYTES IN TABLE=PARA'S
SHR BX,CL ;TO DYNAMICALLY ALLOCATE SPACE
MOV AX,DS ;GET A COPY OF THE CURRENT SEG
ADD BX,AX ;AND CALCULATE THE NEW SEG
ADD BX,10H ;ADD ONE PARAGRAPH FOR SAFETY
MOV SEG0,BX ;THIS WILL BE USED FOR ES:GAME
ADD BX,1000H ;ADD 64K TO CALC 2ND SEG
MOV SEG1,BX ;THIS IS SEG0+64K
; INITILIZE PAGING TABLES
; INITIALIZE MEMMAP
SUB AX,AX ;PRELOAD FLAGS=RESET (0)
;MEMBUF INDICES=NOT CORE RESIDENT (0)
SUB BX,BX ;INITIALIZE ARRAY ROW POINTER
MOV CX,00FFH ;SET UP FOR 255 ENTRIES
INITMM: MOV MEMMAP[BX],AL ;RESET PRELOAD FLAG
MOV MEMMAP[BX+1],AH ;MARK MEMBUF INDICE = NOT CORE RESIDENT
ADD BX,2 ;NEXT OFFSET
LOOP INITMM ;INITIALIZE EACH ENTRY
; DETERMINE IF THE GAME "FITS"
MOV AL,BUFFERS
CMP AL,FSIZE
JNB SYSXIT ;IF THE GAME "FITS" FSIZE IS > 0
MOV FSIZE,0 ;ELSE FSIZE = 0
; INITIALIZE MEMBUF
MOV CL,BUFFERS ;NUMBER OF ENTRIES USED AS A LOOP CTR
SUB CH,CH
SUB BX,BX ;INITIALIZE OFFSET INTO TABLE
MOV DH,255 ;INITIAL VALUE FOR "PREVIOUS" POINTER
MOV DL,1 ;INITIAL VALUE FOR "NEXT" POINTER
MOV AL,0 ;INITIAL "LOC" (ABS SEG:PAGE VALUE)
INITMB: MOV MEMBUF[BX],AL ;SAVE LOC
MOV MEMBUF[BX+1],0 ;V-PAGE IS THE RESPONSIBILITY OF GTVBLK
MOV MEMBUF[BX+2],DH ;INITIALIZE "PREVIOUS"
MOV MEMBUF[BX+3],DL ; AND "NEXT" POINTERS
INC AL ;NEXT LOC
INC DH ;NEXT "PREVIOUS"
INC DL ;NEXT "NEXT"
ADD BX,4 ;NEXT TABLE ENTRY
LOOP INITMB ;REPAIR EACH MEMBUF ENTRY
;CONNECT HEAD AND TAIL
DEC BX ;BACK UP TO "NEXT" COLUMN OF LAST ENTRY
MOV DL,MEMBUF[BX] ;GET "NEXT"
DEC DL ;SUBTRACT 1 TO GET TAIL
MOV MEMBUF[BX],0 ;SPLICE TAIL TO HEAD
MOV MEMBUF+2,DL ;SPLICE HEAD TO TAIL
; INITIALIZE LRU BUFFER POINTER
MOV LRU,0
;
; EXIT POINT
;
; INITIALIZE SEGMENT POINTER TO THE START OF PAGING
SYSXIT: MOV BX,SEG0 ;
MOV ES,BX ;EXTRA SEG ^ THE START OF PAGING
CALL DISPRT
CALL CSETUP ;SET UP THE CONSOLE DEVICE
CALL CHKCPU ;SET THE IBMAT FLAG IF INDICATED
CALL INITSND ;INITIALIZE TIMER FOR SOUND
IF DEBUG
CALL TRACE ; CHECK TO SEE IF TRACER IS WANTED
ENDIF
RET
; **** ERROR #11 **** DISK DATA FILE HANDLING ERROR
; CLOSE THE FILE
DSKERR: MOV BX,GAMHNDL ;BX <= HANDLE
MOV AH,3EH ;REQUEST CLOSE
INT 21H ;MSDOS CALL
; AND PERFORM ERROR PROCESSING
MOV AL,0BH ;SET UP ERROR RETURN CODE
JMP GERROR ;AND GO TO ERROR EXIT
SYSINI ENDP
IF DEBUG
PUBLIC TRACE
TRACE PROC
MOV SI,80H ; GET COMMAND LINE
LODSB ; SEE COMMAND PARMS
CMP AL,2 ; LOOK FOR /T
JNB TRACE1
TRACE0: RET
TRACE1: MOV SI,83H ; LOOK AT BYTE
AND BYTE PTR [SI],5FH ; TURN OFF 32 BIT
CMP BYTE PTR [SI],'T' ; TRACE?
JNE TRACE0
CALL ITRACE ; YEP, INITIALIZE TRACER
RET
TRACE ENDP
; trace trap routines for the mme
; itrace toggles tracing & sets the interrupt. itrint traps the
; interrupt and counts the bucket for the current pc
;
public itrace ;trace trap enable
itrace proc near
push ax
push bx
push dx
mov dx,offset itrint
mov bx,ds ;save data segment
mov ax,cs
mov isavecs,ax ;for compare in int routine
mov ds,ax ;ds:dx is trap address
mov al,1 ;trace int #
mov ah,25H ;function # for set int vector
int 21H
mov ds,bx
pop dx
pop bx
pushf ;get the flags
pop ax
xor ax,400O ;complement the trace flag
push ax
popf
pop ax
ret
itrace endp
;
itrint proc near
push bp
mov bp,sp ;stack:saved bp(0), ip(2), cs(4), flags(6)
push bx
push cx
inc itrcnt ;wrong cs, just count it
jne itrint2
inc itrcnt+2
itrint2:mov bx,[bp+2] ;pick up ip
cmp bx,ioffset
jb itrint1 ;dont count below
cmp bx,itop
ja itrint1 ;dont count above
sub bx,ioffset
mov cx,ishift
shr bx,cl
xor bh,bh ;0:255 - high pc bits
add bx,bx ;byte address of a long
add bx,bx
inc ipcount[bx]
jne itrint1 ;check wrap
inc ipcount+2[bx]
itrint1:pop cx
pop bx
pop bp
iret
itrint endp
ptracer proc
mov dx,offset t_out
mov cx,0
mov ah,cfcreaz ; create the file tracer.out
int 21h
mov dx,offset itrcnt ; get address of vars
mov cx,256*32+4 ; number of bytes to write
mov bx,ax ; get handle from create
mov ah,40h ; write the data
int 21h
mov ah,3eh ; close the data file
int 21h
ret ; all done
ptracer endp
ENDIF
PUBLIC DISPRT
DISPRT PROC ; DISABLE PRINT SCREEN
PUSH ES
MOV AX,3505H ; (B5) GET INTERRUPT VECTOR FIVE
INT 21H ; (B5) (PRTSCR) FROM DOS
MOV PRTOFF,BX ; (B5) SAVE OFFSET
MOV PRTSEG,ES ; (B5) SAVE 'EM BOTH
MOV DX,OFFSET RETEOI
MOV AX,2505H ; (B5) SET INTERRUPT TO POINT TO IRET
INT 21H
POP ES
RET
DISPRT ENDP
PUBLIC ENAPRT
ENAPRT PROC ; (B5) REENABLE PRINT SCREEN
PUSH DS
MOV AX,2505H ; (B5) RESET PRTSCR
MOV DX,PRTOFF ; (B5) GET OFFSET
MOV BX,PRTSEG ; (B5) SET UP DS
MOV DS,BX ; (B5) TO POINT TO OLD ROUTINE
INT 21H
POP DS
RET
ENAPRT ENDP
RETEOI: IRET ; (B5) RETURN FROM PRINT SCREEN
; ----------------------
; CONSOLE INITIALIZATION
; ----------------------
PUBLIC CSETUP
CSETUP PROC
; INITIALIZE VIDEO
; SELECT MEDIUM RESOLUTION GRAPHICS
MOV AH,0
MOV AL,4
INT 10H
MOV AH,0FH ; SEE IF IT WORKED
INT 10H ; BY ASKING THE VIDEO
CMP AL,4 ; IT SHOULD BE AS SET
JE CSET$
MOV AL,10H ; NEW ERROR EXIT
JMP GERROR
; SET BACKGROUND COLOR
CSET$: MOV AH,11 ;REQUEST COLOR
MOV BL,0 ;SELECT BACKGROUND
MOV BH,0 ; 0=BLACK
INT 10H ;CALL BIOS
; SELECT COLOR PALETTE
MOV AH,11 ;REQUEST COLOR
MOV BL,1 ;SELECT PALETTE #1
MOV BH,1 ;0=BLACK, 1=CYAN, 2=MAGENTA, 3=WHITE
INT 10H ;CALL BIOS
; CLEAR THE SCREEN TO BLACK AND RESET THE ACTIVE ICON TABLE
MOV ARG1,0 ;LOAD ARG1 WITH A POSITIVE VALUE
CALL GCLEAR ;DO IT
CMP FSIZE,0 ; (B4) GAME FIT?
JNE CSET$$
RET
CSET$$: MOV AH,2 ; (B4) NOW PRINT A LOADING MESSAGE
MOV DX,800H ; (B4) SOMEWHERE ON THE SCREEN
MOV BH,0
INT 10H
MOV AX,OFFSET WAITSTR ; (B4) GAME IS LOADING...
CALL MPRNT
MOV AH,2 ; (B4) AND RESET THE CURSOR TO TOP
MOV DX,100H
MOV BH,0
INT 10H
RET
CSETUP ENDP
; -------------------------------------
; GET A BLOCK OF PRELOAD VIRTUAL MEMORY
; -------------------------------------
;
; ENTRY: [VPAGEN] = VIRTUAL BLOCK NUMBER (RANGE 0-255)
; [LRU] = NUMBER OF LEAST RECENTLY USED BUFFER
;
PUBLIC GTPBLK
GTPBLK PROC
CALL GTVBLK ;GET THE BLOCK
; USE [VPAGEN] TO SET [PREFLG]
MOV BL,VPAGEN ;GET VIRTUAL PAGE NUMBER
SUB BH,BH
SHL BX,1 ;CONVERT TO WORD POINTER
MOV MEMMAP[BX],1 ;SET PREFLG
CMP FSIZE,0
JZ GTPBL1
RET
; USE [BUF] TO UNSPLICE THE ALLOCATED BUFFER FROM THE CIRCULAR
; LINKED LIST. NOTE, IN THE CASE OF PRELOAD, BUF CONTAINS
; THE NUMBER OF THE JUST USED BUFFER, WHICH WAS THE LRU AND IS NOW
; THE MRU.
GTPBL1: MOV DL,BUF ;SAVE BUF FOR FIXING THE LRU
MOV BL,DL ;USE BUF TO CHANGE "NEXT" POINTER
SUB BH,BH ; IN PREVIOUS BUFFER
SHL BX,1 ;4 BYTES PER TABLE ENTRY
SHL BX,1
;GET "NEXT" [AH] AND "PREVIOUS" [AL]
MOV AX,WORD PTR MEMBUF[BX+2]
MOV BL,AL ;USE "PREVIOUS" TO
SUB BH,BH ; POINT TO THE 2ND MRU'S TABLE
SHL BX,1 ;4 BYTES PER TABLE ENTRY
SHL BX,1
MOV MEMBUF[BX+3],AH ;SET TO POINT FORWARD TO THE LRU
MOV BL,AH ;USE "NEXT" TO
SUB BH,BH ; POINT TO THE LRU'S TABLE
SHL BX,1 ;4 BYTES PER TABLE ENTRY
SHL BX,1
MOV MEMBUF[BX+2],AL ;SET TO POINT BACK TO 2ND MRU
; MAKING IT THE NEW MRU
RET
GTPBLK ENDP
; -----------------------------
; GET A BLOCK OF VIRTUAL MEMORY
; -----------------------------
;
; ENTRY: [VPAGEN] = VIRTUAL BLOCK NUMBER (RANGE 0-255)
; [LRU] = NUMBER OF LEAST RECENTLY USED BUFFER
;
; THROUGH USE OF A COMMON EXIT POINT
; EXIT: BUF (RANGE 0 THROUGH [BUFFERS]-1)
; LOC
;
PUBLIC GTVBLK
GTVBLK PROC
PUSH BX
; USE [VPAGEN] TO ACCESS [PREFLG] AND [BUF]
MOV BL,VPAGEN ;GET VIRTUAL PAGE NUMBER
SUB BH,BH
SHL BX,1 ;CONVERT TO WORD POINTER
MOV AL,MEMMAP[BX+1] ;GET BUF
MOV BUF,AL ;SAVE IT
TEST MEMMAP[BX],1 ;TEST PREFLG
; IS THIS SECTION LOADED PRELOAD?
JZ NOTPRE ;....NO
; LOADED PRELOAD
; BUF IS VALID
ISIN: MOV BL,BUF ;WE CAREFULLY SAW TO IT'S VALIDITY
MOV LOC,BL ;AND PUT IT WHERE THE SUN....
POP BX
RET
; {CASE} NOT LOADED PRELOAD
NOTPRE: CMP FSIZE,0 ;ARE WE PAGING?
JZ NOTPR1 ;....YES
; NOT PAGING
CMP BUF,0 ;IS IT ALREADY IN CORE?
; {CASE} IT IS ALREADY IN CORE
JNE ISIN ;....YES
; {CASE} IT NOT IN CORE
MOV AL,VPAGEN
MOV MEMMAP[BX+1],AL ;SET UP THE BUF ENTRY IN MEMMAP
MOV BUF,AL ; AND SET BUF = VPAGEN
PUSH CX ;SAVE SOME MORE REGISTERS
PUSH DX
JMP GTVBL2 ;AND BRING IT IN
NOTPR1: CMP BUF,0 ;IS IT ALREADY IN CORE?
JNE INCORE ;....YES
JMP NOTIN ;....NO. THEN BRING IT IN
; {CASE} IT IS IN CORE AND NOT PRELOAD
INCORE: MOV BL,BUF ;IS IT THE LRU?
CMP LRU,BL
JNE SPLICE ;....NO
; IT IS THE LRU PAGE, SO MAKE IT THE MRU
SUB BH,BH ;UPDATE THE LEAST RECENTLY USED BUFFER
SHL BX,1 ;4 BYTES PER ENTRY
SHL BX,1
MOV AL,MEMBUF[BX+3] ;GET "NEXT" POINTER
MOV LRU,AL ;IT'S THE NEW LRU, MAKING THIS BUFFER
; THE MRU
; BUF IS VALID
MOV BL,BUF
MOV OLDMRU,BL ;SAVE BUF AS OLD MRU
MOV LOC,BL ;AND LOC
POP BX
RET
; IT ISN'T THE LRU. WE HAVE TO MAKE IT THE MRU, IF IT ISN'T
; ALREADY. THE EXISTING LRU REMAINS UNCHANGED
SPLICE: MOV BL,BUF
CMP BL,OLDMRU ;IS THIS ALREADY THE MRU?
JNE SPLIC1 ;....NO
MOV LOC,BL ;AND SAVE BUF IN LOC
POP BX
RET
; STEP 1 - UNSPLICE THE "TOUCHED" BUFFER
SPLIC1: PUSH CX
PUSH DX
MOV OLDMRU,BL ;SAVE BUF AS OLD MRU
SUB BH,BH ;
SHL BX,1 ;4 BYTES PER ENTRY
SHL BX,1
MOV AX,WORD PTR MEMBUF[BX+2] ;GET "NEXT" AND "PREVIOUS"
PUSH BX
MOV BL,AL ;START WITH "PREVIOUS" BUFFER
SUB BH,BH ;
SHL BX,1 ;4 BYTES PER ENTRY
SHL BX,1
MOV MEMBUF[BX+3],AH ;POINT FORWARD TO "NEXT"
MOV BL,AH ;THEN "NEXT" BUFFER
SUB BH,BH ;
SHL BX,1 ;4 BYTES PER ENTRY
SHL BX,1
MOV MEMBUF[BX+2],AL ;POINT BACK TO "PREVIOUS"
;SPLICE IN THE NEW MRU
MOV DL,BUF
MOV BL,LRU ;GET THE "PREVIOUS" POINTER IN LRU
SUB BH,BH ; WHICH POINTS AT THE OLD MRU
SHL BX,1 ;4 BYTES PER ENTRY
SHL BX,1
MOV AL,MEMBUF[BX+2] ;GET "PREVIOUS" POINTER
MOV MEMBUF[BX+2],DL ;MAKE LRU POINT BACK TO BUF
MOV BL,AL ;GET THE "NEXT" POINTER FROM FORMER
SUB BH,BH ; MRU
SHL BX,1 ;4 BYTES PER ENTRY
SHL BX,1
MOV AH,MEMBUF[BX+3] ;GET "NEXT" POINTER
MOV MEMBUF[BX+3],DL ;MAKE IT POINT FORWARD TO BUF
; AND REPAIR BUF
POP BX
MOV WORD PTR MEMBUF[BX+2],AX
; BUF IS VALID
JMP GTVRET ;WE'RE DONE, USE COMMON EXIT
; {CASE} IT ISN'T IN CORE
; DEALLOCATE THE LEAST RECENTLY USED BUFFER
NOTIN: PUSH CX
PUSH DX
MOV BL,LRU ;GET LRU SO AS TO DEALLOCATE
SUB BH,BH ; THE LEAST RECENTLY USED BUFFER
SHL BX,1
SHL BX,1
MOV BL,MEMBUF[BX+1] ;GET THE OLD VIRTUAL PAGE NUMBER
; WE'LL USE IT TO INDEX INTO MEMMAP
SUB BH,BH ; AND SET IT'S BUF ENTRY
SHL BX,1 ; TO "0"
MOV MEMMAP[BX+1],0 ;THIS DEALLOCATES THE BUFFER FOR THE
; OLD VIRTUAL PAGE.
; ALLOCATE MEMBUF [LRU] TO THE NEW VIRTUAL PAGE
MOV BL,LRU ;GET LRU SO AS TO ALLOCATE
SUB BH,BH ; THE LEAST RECENTLY USED BUFFER
SHL BX,1
SHL BX,1
MOV AL,VPAGEN ;GET NEW VIRTUAL PAGE NUMBER
MOV MEMBUF[BX+1],AL ;STORE IT
; SETUP THE LOAD POINT FOR THE NEW VIRTUAL PAGE
PUSH BX ; SAVE BUFFER DESCRIPTOR
MOV BL,AL ;USE VPAGEN AS AN INDEX
SUB BH,BH ; INTO MEMBUF
SHL BX,1
MOV AL,LRU ;LRU IS THE BUFFER TO USE
MOV MEMMAP[BX+1],AL ; SO SAVE IT
MOV BUF,AL ; AND SAVE IT FOR COMMON EXIT
MOV OLDMRU,AL ;SAVE BUF AS OLD MRU
POP BX ; RESTORE BUFFER DESCRIPTOR
; AND MAKE THIS BUFFER THE MRU
; SHL BX,1 ;SO WE CAN USE IT AS A QUAD POINTER
MOV AL,MEMBUF[BX+3] ;GET "NEXT" POINTER
MOV LRU,AL ;IT'S THE NEW LRU
; MAKING THIS BUFFER THE MRU
; INITIALIZE REGISTERS TO PAGE IN A BLOCK OF VIRTUAL MEMORY
; GET DESTINATION IN ES:BX FORM
; FIXUP ES
GTVBL2: MOV BL,BUF ;GET DESTINATION BLOCK NUMBER
MOV CX,SEG0 ;PRE-POINT AT SEG0
OR BL,BL ;TEST SEGMENT BIT
JNS SEGAOK ;IF NO SIGN THEN IT'S SEG0
AND BL,7FH ; ELSE STRIP OFF THE SEGMENT BIT
MOV CX,SEG1 ; AND POINT TO THE OTHER SEGMENT
SEGAOK: MOV ES,CX ;ES POINTS TO THE CORRECT SEGMENT
; CONVERT THE DESTINATION BLOCK NUMBER TO A BYTE OFFSET
MOV CL,9 ;x 512
SHL BX,CL ;
; GET VIRTUAL BLOCK NUMBER IN AL
MOV AL,VPAGEN ;GET THE VIRTUAL PAGE NUMBER
;
; GET VIRTUAL BLOCK [AL] IN ES:BX
;
PUSH BX ;SAVE BX PORTION OF DESTINATION
; MOVE FILE READ/WRITE POINTER (LSEEK) TO POINT TO THE PROPER BLOCK
; CALCULATE OFFSET IN BYTES AND PLACE IN CX:DX
MOV CX,9 ;MULTIPLY BY 512 (RECSIZ)
SHL AX,CL ;TO CONVERT BLK# TO OFFSET
MOV CX,0 ;SET TO ZERO, BUT CHANGE
ADC CX,0 ;TO "1" IF OFFSET > 64K
MOV DX,AX ;GET LOW ORDER #BYTES FOR LSEEK
; MOVE READ/WRITE POINTER TO OFFSET IN CX:DX
; FROM THE BEGINNING OF THE FILE
MOV BX,GAMHNDL ;GET FILE HANDLE
MOV AH,CFSEEK ;SELECT LSEEK FUNCTION
MOV AL,0 ;USE OFFSET FROM BEGINNING METHOD
INT 21H ;INVOKE FUNCTION CALL
; READ THE SELECTED 512 BYTE RECORD INTO CORE LOCATION ES:BX
; TRANLATE THE ES:BX CORE LOCATION INTO DS:DX FORMAT
; RESTORE BX PORTION OF DESTINATION INTO DX
POP DX
; FIXUP SEGMENT PORTION OF THE DESTINATION
PUSH DS ;SAVE DS FOR LATER RESTORATION
PUSH ES ;PUSH EXTRA SEGMENT
POP DS ;IN ORDER TO PUT IT IN DS
; READ THE RECORD INTO DS:DX
MOV CX,RECSIZ ;READ A 512 BYTE RECORD
MOV AH,CRDRNDZ ;SELECT READ RANDOM RECORD
INT 21H ;INVOKE FUNCTION CALL
CMP AX,CX ;SET FLAGS
; RESTORE DATA SEGMENT
POP DS
; COMMON EXIT POINT
; EXIT: BUF (RANGE 0 THROUGH [BUFFERS]-1)
; LOC
;
GTVRET: MOV BL,BUF ;WE CAREFULLY SAW TO IT'S VALIDITY
MOV LOC,BL ;AND PUT IT WHERE THE SUN....
POP DX ;RESTORE AFFECTED REGISTER(S)
POP CX
POP BX
RET
GTVBLK ENDP
; --------------
; ERROR HANDLING
; --------------
; ENTRY: ERROR CODE IN [AL]
PUBLIC GERROR,GERR1
GERROR PROC
push ax
mov ah,2
mov dx,100H ; row 1, col 0
mov bh,0 ; screen 0
int 10H ; set cursor
pop ax
XOR AH,AH
SHL AX,1 ; INDEX
MOV BX,AX ; USE INDEX REG
MOV BX,ERRTAB[BX] ; GET PROPER ERROR MESSAGE
MOV AL,[BX] ; GET LENGTH
ADD AL,ERRM ; ADD LENGTH OF STARTING STRING
mov ch,al ; save total text length
MOV AH,40 ; GET WIDTH OF SCREEN
SUB AH,AL ; GET REMAINING SPACES
SHR AH,1 ; DIVIDE IN HALF
TEST AH,1 ; ODD?
JZ GERR0 ; NOPE
DEC AH ; YES, SUBTRACT 1
GERR0: MOV CL,AH ; GET COUNT OF SPACES
add CH,AH ; SAVE right edge=left spcs+text len
GERR1: CMP CL,0 ; DON'T PRINT "NO" SPACES
JE GERR2 ; SKIP PRINT
MOV AH,2 ; PRINT A CHAR
MOV DL,' ' ; A SPACE IN FACT
INT 21H ; DO IT
DEC CL
JMP GERR1
GERR2: MOV AX,OFFSET ERRM+1
CALL NPRNT ;PRINT IT (NO CRLF)
MOV AX,BX ; GET ERROR STRING
INC AX ; SKIP LENGTH BYTE
CALL NPRNT ; PRINT THE ERROR STRING
GERR3: CMP CH,40 ; FINISH THE LINE WITH SPACES
JE GERR4
MOV DL,' ' ; PRINT A SPACE
MOV AH,2
INT 21H
INC CH
JMP GERR3
GERR4: MOV AH,2
MOV DL,13
INT 21H
MOV DL,10
INT 21H
JMP FINISH ;EXIT CLEANLY
GERROR ENDP
;PRINT A CARIAGE RETURN / LINE FEED
PUBLIC MCRLF
MCRLF PROC
MOV AL,13
CALL PRTOUT
MOV AL,10
CALL PRTOUT
RET
MCRLF ENDP
;PRINT THE CHARACTER IN [AL]
PUBLIC PRTOUT
PRTOUT PROC
PUSH AX
PUSH BX
PUSH CX
MOV DL,AL
MOV AH,2
INT 21H
POP CX
POP BX
POP AX
RET
PRTOUT ENDP
;PRINT A STRING FOLLOWED BY <CR><LF>, PTR IN AX
PUBLIC MPRNT
MPRNT PROC
PUSH DX
MOV DX,AX
MOV AH,9
INT 21H
CALL MCRLF
POP DX
RET
MPRNT ENDP
;PRINT A STRING NOT FOLLOWED BY <CR><LF>, PTR IN AX
PUBLIC NPRNT,NPR1,NPR2
NPRNT PROC
PUSH BX ;SAVE BX
MOV BX,AX ;STRING PTR
NPR1: MOV AL,[BX] ;GET NEXT CHARACTER
CMP AL,"$" ;END OF THE LINE
JE NPR2 ;....YES
CALL PRTOUT ;....NO. PRINT THE CHARACTER
INC BX ;POINT TO THE NEXT CHARACTER
JMP NPR1 ;REPEAT UNTIL END OF THE LINE
NPR2: POP BX ;RESTORE BX
RET
NPRNT ENDP
;GET TIME TO BE USED IN NUDGING THE RANDOM NUMBER GENERATOR
PUBLIC MTIME
MTIME PROC
PUSH CX
PUSH DX
MOV AH,2CH
INT 21H
MOV RSEED1,CX ;HRS & MIN
MOV RSEED2,DX ;S's & 1/100 THS
POP DX
POP CX
RET
MTIME ENDP
;EXIT THE GAME PEACEFULLY AND RETURN TO DOS
PUBLIC FINISH
FINISH PROC
; INDICATE THE GAME IS OVER
MOV AH,2 ; SET CURSOR POSITION
MOV BH,0 ; FIRST PAGE
MOV DX,1800H ; ON LINE 25
INT 10H ; POSITION IT
MOV AX,OFFSET XITMSG ;POINT AT "END OF SESSION"
CALL NPRNT ;PRINT IT
CALL ENAPRT
; WAIT FOR ANY KEYPRESS
MOV AH,CNOECHO ; (B5) READ A CHAR
INT 21H
; GO BACK TO REGULAR TEXT MODE WITH A CLEAR SCREEN
; SELECT STANDARD MODE
MOV AH,0 ;REQUEST MODE SET
MOV AL,2 ;STANDARD MONOCHROME 80 X 25
INT 10H ;CALL BIOS FUNCTION
IF DEBUG
CALL PTRACER ; CALL 'C' OUTPUT OF TABLE
ENDIF
MOV AX,BIOSDATA ; FIX KEYBOARD
MOV ES,AX
MOV BX,KB_FLAG ; KEYBOARD FLAGS OFFSET
MOV AL,KYFLGS ; GET ORIGINAL STATE
MOV ES:[BX],AL ; STORE IT
; EXIT TO DOS
MOV AH,04CH
INT 21H
FINISH ENDP
; --------------------------------
; CHECK FOR FAST PROCESSOR (80286)
; --------------------------------
;
; PURPOSE: THE SOUND ROUTINES ARE IMPLIMENTED THROUGH THE USE OF
; SOFTWARE TIMING LOOPS. THEY ARE THEREFORE SUCCEPTABLE TO
; BEING CHANGED BY FASTER PROCESSORS.
;
; IN ORDER TO PROVIDE COMPENSATION FOR THIS EFFECT, THIS
; ROUTINE IS USED TO DETERMINE IF THE CPU IS "FAST" OR "SLOW".
;
; *** NOTE *** THIS ROUTINE IS TO BE CALLED DURING COLD START
;
; INPUTS: NONE
;
; OUTPUTS: [IBMAT] = TRUE IF THIS IS A FASTER CPU
; = FALSE IF THIS IS A NORMAL CPU
; REGISTERS DESTROYED:
; AX,BX,DX
;
; EXTERNAL REFERENCES: [IBMAT], [TIMER], TRUE, FALSE, ATTIME
;
PUBLIC CHKCPU,CHKCP1,CHKCP2
CHKCPU PROC
; INITIALIZE
MOV IBMAT,FALSE ;PREMARK THIS AS A "SLOW" CPU
SUB BX,BX ;INITIALIZE LOOP COUNTER
; TO DETERMINE SPEED
CALL SYNCH ;SYNCHRONIZE TIMER
MOV TIMER,DL ;SAVE 1/100 SEC COUNT
; PREPARE TO QUALIFY
CHKCP1: MOV AH,2CH ;GET THE TIME UNTIL IT CHANGES
INT 21H ; SO WE CAN USE THE COUNT IN BX
INC BX ; TO DECIDE IF THIS IS A FAST CPU
CMP DL,TIMER ;HAS THE CLOCK TICKED?
JZ CHKCP1 ;....NO. LOOP UNTIL IT DOES
; PHOTO FINISH
CMP BX,ATTIME ;COMPARE WITH TIME STANDARD
JL CHKCP2 ; IF BX IS LESS, THEN THE CPU IS SLOW
MOV IBMAT,TRUE ; ELSE IT'S FAST AND WE SO MARK IT
CHKCP2: PUSH ES
PUSH DI
MOV AX,BIOS ; BIOS SEGMENT
MOV ES,AX ; MAKE IT ADDRESSIBLE
MOV DI,BIOSOFF ; GET BIOS OFFSET
MOV AX,OFFSET IBMSTR ; SOURCE STRING "IBM COPR."
MOV DX,9 ; LENGTH OF STRING
MOV CX,200H ; WITHIN THE FIRST 1/2 K
CALL CHK ; CHK BIOS FOR IBM COPR.
JNC CHK_ID
CHKCP3: POP DI
POP ES
RET
CHK_ID: MOV DI,ID_OFF ; GET IBM ID BYTE OFFSET
CMP BYTE PTR ES:[DI],JR_ID ; IS IT A JUNIOR
JNE CHKCP3
MOV PCJR,1 ; SET FLAG FOR PCJR
JMP CHKCP3
CHKCPU ENDP
; GIVEN SOURCE PTR IN DS:AX, AND A DEST PTR IN ES:DI, CHK SEARCHES
; FOR THE STRING POINTED TO BY DS:AX IN THE FIRST (CX) BYTES OF
; THE STRING POINTED TO BY ES:DI
PUBLIC CHK
CHK PROC
CLD ; (7) CLEAR THE DIRECTION FLAG
PUSH SI ; (7) SAVE THIS
PUSH DX ; (7) SAVE THIS FOR RETRIES
MOV BP,SP ; (7) GET A FRAME POINTER
MOV SI,AX ; (7) SET UP FIRST OFFSET
LUP: CMP BYTE PTR [SI],'$' ; (7) END CHAR
JE MATCH ; (7) REACHING END==MATCH
CMPSB ; (7) COMPARE TWO BYTES
JNE RESET ; (7) RESET SOURCE IF MISMATCH
DEC DX
LOOP LUP ; (7) DECREMENT COUNTER AND TRY AGAIN
JMP RTFAIL ; (7) OUT OF CHARS, FAILED
RESET: MOV SI,AX ; (7) MOVE SOURCE PTR BACK INTO SI
MOV DX,[BP] ; (7) RESTORE NUMBER OF BYTES IN STR
LOOP LUP ; (7) TRY AGAIN
RTFAIL: STC ; (7) FAIL BY RETURNING CARRY SET
POP DX
POP SI ; (7) RESTORE SI
RET
MATCH: TEST DX,DX ; (7) MATCH OF ENTIRE STRING
JNZ RTFAIL
CLC ; (7) GOT A MATCH, RETURN CARRY CLEAR
POP DX
POP SI ; (7) RESTORE SI
RET
CHK ENDP
; ------------------------
; INITIALIZE SPEAKER TIMER
; ------------------------
;
; PURPOSE: THIS ROUTINE INITIALIZES THE PORTION OF THE 8253 TIMER
; CHIP USED BY THE SPEAKER SYSTEM. IN PARTICULAR, IT SETS
; UP CHANNEL 2 OF THE TIMER AS A SQUARE-WAVE GENERATOR.
;
; *** NOTE *** THIS ROUTINE IS TO BE CALLED DURING COLD START
;
; INPUTS: NONE
;
; OUTPUTS: TIMER 2
;
; REGISTERS DESTROYED: NONE
;
; EXTERNAL REFERENCES: NONE
;
PUBLIC INITSND
INITSND PROC
PUSH AX ;SAVE REGISTERS
MOV AL,0B6H ;TIMER CONTROL WORD
OUT 43H,AL ;SEND IT TO THE CONTROL PORT
POP AX ;RESTORE REGISTERS
RET
INITSND ENDP
; ----------------------
; SET THE TONE FREQUENCY
; ----------------------
;
; PURPOSE: THIS ROUTINE SELECTS THE FREQUENCY OF THE SQUARE-WAVE
; TONE TO THE SPEAKER.
;
; INPUTS: CX=FREQUENCY
;
; OUTPUTS: TIMER 2
;
; REGISTERS DESTROYED: NONE
;
; EXTERNAL REFERENCES: NONE
;
PUBLIC TONESET
TONESET PROC
PUSH AX ;SAVE REGISTERS
PUSH CX
MOV AL,CL ;GET LOWER BYTE
OUT 42H,AL ;SEND OUT VALUE TO TIMER
MOV AL,CH ;GET UPPER BYTE
OUT 42H,AL ;SEND OUT VALUE TO TIMER
POP CX ;RESTORE REGISTERS
POP AX
RET
TONESET ENDP
; ------------
; TURN ON TONE
; ------------
;
; PURPOSE: THIS ROUTINE TURNS ON THE TIMER AND SPEAKER TO PRODUCE
; A TONE. THE FREQUENCY OF THE TONE MUST HAVE ALREADY
; BEEN SENT TO THE TIMER.
;
; INPUTS: NONE
;
; OUTPUTS: TIMER 2, AND SPEAKER
;
; REGISTERS DESTROYED: NONE
;
; EXTERNAL REFERENCES: NONE
;
PUBLIC TONEON
TONEON PROC
PUSH AX ;SAVE REGISTERS
IN AL,61H ;GET SYSTEM PORT B CONTROL DATA
OR AL,3 ;TURN SPEAKER AND TIMER ON
OUT 61H,AL ;SEND OUT NEW VALUE
POP AX ;RESTORE REGISTERS
RET
TONEON ENDP
; ------------
; TURN OFF TONE
; ------------
;
; PURPOSE: THIS ROUTINE TURNS OFF THE TIMER AND SPEAKER
;
; INPUTS: NONE
;
; OUTPUTS: TIMER 2, AND SPEAKER
;
; REGISTERS DESTROYED: NONE
;
; EXTERNAL REFERENCES: NONE
;
PUBLIC TONEOFF
TONEOFF PROC
PUSH AX ;SAVE REGISTERS
IN AL,61H ;GET SYSTEM PORT B CONTROL DATA
AND AL,0FCH ;TURN SPEAKER AND TIMER OFF
OUT 61H,AL ;SEND OUT NEW VALUE
POP AX ;RESTORE REGISTERS
RET
TONEOFF ENDP
; -----------
; SOUND DELAY
; -----------
;
; PURPOSE: THIS ROUTINE DELAYS FOR A SPECIFIED NUMBER OF .05 SEC
; INTERVALS.
;
; INPUTS: [CX] = NUMBER OF .05 SEC TO DELAY
;
; OUTPUTS: NONE
;
; REGISTERS DESTROYED: NONE
;
; EXTERNAL REFERENCES: NONE
;
PUBLIC SNDDLY,SNDLY1,SNDLY2
SNDDLY PROC
PUSH AX ;SAVE REGISTERS
PUSH BX
PUSH CX
PUSH DX
CMP CX,0
JNE SNDLY1
; SHORTER THAN OTHERWISE POSSIBLE DURATION
MOV CX,JIFCNT ;<.05 SEC DELAY REQUEST, SO WAIT A JIFFY
SNDLY0: LOOP SNDLY0
JMP SNDLY3
CALL SYNCH ;SYNCHRONIZE TIMER
MOV TIMER,DL ;SAVE 1/100 SEC COUNT
; TIMING LOOP
SNDLY1: PUSH CX ;SAVE LOOP COUNTER
SNDLY2: MOV AH,2CH ;GET THE TIME UNTIL IT CHANGES
INT 21H ; MSDOS FUNCTION CALL
CMP DL,TIMER ;HAS THE CLOCK TICKED?
JZ SNDLY2 ;....NO. LOOP UNTIL IT DOES
POP CX ;RESTORE LOOP COUNTER
MOV TIMER,DL ;SAVE NEW COUNT
LOOP SNDLY1 ; AND LOOP
SNDLY3: POP DX ;RESTORE REGISTERS
POP CX
POP BX
POP AX
RET
SNDDLY ENDP
; -----
; SYNCH
; -----
;
; PURPOSE: THIS ROUTINE WAITS FOR A CHANGE IN THE 1/100 SEC TIMER, TO
; INSURE THAT WE'RE AT THE START OF A NEW TIME PERIOD.
;
; INPUTS: NONE
;
; OUTPUTS: [DL] = 1/100 SEC COUNT AT SYNCH TIME
;
; REGISTERS DESTROYED: DL
;
; EXTERNAL REFERENCES: NONE
;
PUBLIC SYNCH,SYNCH1
SYNCH PROC
PUSH AX ;SAVE REGISTERS
PUSH BX
PUSH CX
MOV AH,2CH ;GET THE TIME FROM THE REAL TIME CLOCK
INT 21H ;MSDOS FUNCTION CALL
MOV TIMER,DL ;SAVE 1/100 SEC COUNT
; SYNCHRONIZE
SYNCH1: MOV AH,2CH ;GET THE TIME AGAIN, SO WE CAN INSURE
INT 21H ; THAT WE ARE SYNCHRONIZED WITH THE
CMP DL,TIMER ; START OF A NEW TIMING PERIOD
JZ SYNCH1 ;LOOP UNTIL SYNCHRONIZED
POP CX ;RESTORE REGISTERS
POP BX
POP AX
RET
SYNCH ENDP
; ----
; FREQ
; ----
;
; PURPOSE: THIS ROUTINE CONVERTS A FREQUENCY TO THE NUMBER REQUIRED
; BY TONESET.
;
; INPUTS: [CX] = FREQUENCY IN HZ
;
; OUTPUTS: [CX] = CONVERTED VALUE
;
; REGISTERS DESTROYED: CX
;
; EXTERNAL REFERENCES: NONE
;
PUBLIC FREQ
FREQ PROC
PUSH AX ;SAVE REGISTERS
PUSH BX
PUSH DX
MOV DX,12H ;UPPER PART OF NUMERATOR
MOV AX,34DEH ;LOWER PART OF NUMERATOR
DIV CX ;DIVIDE BY FREQUENCY
MOV CX,AX ;QUOTIENT IS THE OUTPUT
POP DX ;RESTORE REGISTERS
POP BX
POP AX
RET
FREQ ENDP
; -----------
; MAKE A TONE
; -----------
;
; PURPOSE: THIS ROUTINE MAKES A TONE OF A GIVEN FREQUENCY AND LENGTH
;
; INPUTS: [CX] = FREQUENCY IN HZ
; [DX] = DURATION IN .05 SEC INTERVALS
;
; OUTPUTS: TO THE SPEAKER AND TIMER 2
;
; REGISTERS DESTROYED: NONE
;
; EXTERNAL REFERENCES: {TONESET}, {TONEON}, {TONEOFF}, {DELAY}
;
PUBLIC TONE
TONE PROC
PUSH AX ;SAVE REGISTERS
PUSH BX
PUSH CX
PUSH DX
; COMPUTE THE FREQUENCY AND SETUP THE TONE GENERATOR (TIMER 2)
CALL FREQ ;CONVERT THE FREQUENCY
CALL TONESET ;SET UP TIMER 2
; TURN ON THE TONE
CALL TONEON
; WAIT FOR THE DELAY TIME
MOV CX,DX
CALL SNDDLY
; TURN OFF THE TONE
CALL TONEOFF
POP DX ;RESTORE THE REGISTERS
POP CX
POP BX
POP AX
RET
TONE ENDP
EVEN
;
; START OF A DYNAMICALLY ALLOCATED PAGE CONTROL STRUCTURE
;
PUBLIC MEMBUF
MEMBUF LABEL BYTE
;
MSDIP ENDP
CSEG ENDS
END MSDIP