mirror of
https://github.com/erkyrath/infocom-zcode-terps.git
synced 2026-01-11 23:43:24 +00:00
7569 lines
175 KiB
NASM
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
|