Files
erkyrath.infocom-zcode-terps/ibm/boszip.asm
Andrew Plotkin b642da811e Initial commit.
2023-11-16 18:19:54 -05:00

4362 lines
116 KiB
NASM

TITLE ZIP Z-LANGUAGE INTERPRETER IBM/MS-DOS 2.0 VERSION
PAGE 58,132
.LIST
; Modification History -
;---------------------------------------------------
;
; 1) REWORKED FOR NEW SETUP FILE USAGE
; 2) FIXED NON-WORKING STACK SAVE AND RESTORE FILE
; 3) FIX RANDOM BUG - 6/25/84
; 4) FIXED STATUS LINE (LONG ROOM DESCRIPTION) - 07/24/84
; 5) Fixed status line bug on restart 22-Oct-84 - PHG "G"
; 6) REWRITE OF ZIP TO USE ALL MEMORY 1-NOV-84 - PHG "H"
; 7) Combination of IBM and MSZIP with new copy protection
; for all IBM compatible machines. 11-Mar-85 - PHG
; Minor versions: I -- original to test
; J -- fixed status line and more
; k -- enhanced printer timeout for IBM
; l -- mcrlf added to save, commenting
; m -- fixed status line for TI-PRO
; n -- added insert game disk on save
; and paging problems. Also reworked
; scripting.
; o -- fixed script on restart, no script on
; more, and minor save problems
; q -- fixed disk switch for verify
;
; r -- fixed status line bugs and refixed
; disk switch on restart.
; s -- restart bug on Tandy (their incompati-
; bility and more on restart
; t -- removed extra CR from opening screen bet
; ween last line of text and prompt.
; u -- Fixed mchri not to echo character on
; more or insert game disks.
; v -- fixed broken restore or failed save on
; low memory configuration, error in fit
; calculation, /k was fixed to work any
; where on the cmdline.
; w -- added /P for PCjr support, fixed still
; broken end of memory calculation caused
; by forgetting to count prelod blocks as
; used memory blocks. Also fixed opread
; so that it would flush words read greater
; than 59.
; x -- fix script checks from cmp to test.
; to fix wishbringer. RELEASE AS H
; J z -- stab at verify/paging bug, found in
; newzpc when setting curtab to zero since
; page is preloaded, didn't set zpcseg to
; 0 as well. RELEASE AS J
; J 1 -- fixed script bug on failed restore
; K 1 -- fixed save/restore bug. Removed srblks
; 2 -- fixed fix above which was still broken.
; 3 -- j1 was broken by k2 edit. fixed here
; 4 -- above
; 5 -- fixed restart to set segs to 0
; 6 -- stab at fixing screwy dos disk flush on
; create.
; 7 -- fix screen op to not clear window first
; 8 -- set min memory to 32k
; L RELEASE VERSION -- minmem set to 24K
;
; M 1 -- fix read in of last virtual block for full game
; 2 -- fixed a cross segment boundary get in nxtbyt
; N 1 -- fixed zip to not NEED setup.inf
; 2 -- added boss key function to MCHRI
SUBTTL STACK AND DATA SEGMENTS INITIALIZATION
ZVERSN EQU "N" ;ZIP VERSION NUMBER
ZEDIT EQU 0 ; EDIT NUMBER
ZMVERS EQU 3 ;Z-MACHINE VERSION NUMBER
LSTACK EQU 512 ;LENGTH OF USER STACK(MUST BE 1 PAGE FOR NOW)
DEBUG EQU 0
PVERS1 EQU 0 ;POSITION OF ZVERSION VERSION BYTE
PVERS2 EQU 1 ;ZVERSION MODE BYTE
PZRKID EQU 2 ;ZORKID
PENDLD EQU 4 ;ENDLOD
PSTART EQU 6 ;START
PVOCTB EQU 8 ;VOCAB
POBJTB EQU 10 ;OBJECT
PGLOTB EQU 12 ;GLOBALS
PPURBT EQU 14 ;PURBOT
PFLAGS EQU 16 ;USER FLAG WORD
PSERNM EQU 18 ;SERIAL NUMBER (6 BYTES)
PWRDTB EQU 24 ;WORDS
PLENTH EQU 26 ;GAME LENGTH
PCHKSM EQU 28 ;GAME CHECKSUM
PADCHR EQU 5 ;PADDING CHARACTER
SSVER EQU "A"-10 ; ADD THIS TO THE VERTICAL SETUP HEIGHT
SSHOR EQU -6 ; ADD THIS TO THE HORIZONTAL SETUP WIDTH
SSLNT EQU 3 ; LENGTH OF SETUP FILE
CRESET EQU 0H ;MS-DOS FUNCTION CALLS WITH INT 21H
CCONIN EQU 1H
CPROUT EQU 5H
CCONIO EQU 6H
CNOECHO EQU 7H ; (7) FIX MCHRI TO HAVE NO ECHO
CRDLIN EQU 0AH
CDRESET EQU 0DH ; (7o) DISK RESET
CSELDSK EQU 0EH ; (7n) SELECT DISK
CFOPEN EQU 0FH
CFCLOS EQU 10H
CFDELE EQU 13H
CFMAKE EQU 16H
CURDSK EQU 19H ; (7n) CURRENT DISK
CSDMAO EQU 1AH
CRDRND EQU 21H
CWRRND EQU 22H
CPRSNM EQU 29H
; FUNCTION CALLS ADDED FOR VERSION "H"
; UPGRADE TO DOS 2.0 AND ABOVE
CFCREAZ EQU 3CH
CFOPENZ EQU 3DH
CFCLOSZ EQU 3EH
CRDRNDZ EQU 3FH
CWRRNDZ EQU 40H
CFDELEZ EQU 41H
CFSEEK EQU 42H
CSETBLK EQU 4AH ; FOR DETERMINING MEM SIZE
SCROLLUP EQU 6 ; (7) FOR VIDEO BIOS CALL
DOSVER EQU 30H ; (7) REQUEST FOR DOS VERSION
BIOSEG EQU 0F800H ; (7) STRINGS ARE FOUND AT F000:E0++
BIOSOFF EQU 0000H ; (7)
COLCUR EQU 0607H ; (7) CURSOR SCAN LINES FOR COLOR
SETCUR EQU 1 ; (7) VIDEO FUNCTION TO SET CURSOR SIZE
COLATR EQU 17H ; (7) COLOR ATTRIBUTE IS WHITE ON BLUE
SCRBIT EQU 100000B ; (7) 5 BIT TURNS ON SCREEN SPLITTING
TOPSCR EQU 0100H ; (7) COORD'S OF TOP OF WINDOW
STDPRT EQU 4 ; (7) STANDARD PRINTER DEVICE
PRTMSK EQU 0A1H ; (7) PRINTER INI STATUS MASK
SCRMSK EQU 0FEFFH ; (7) WORD TO MASK SCRIPT BIT IN MODE WORD
RDYBIT EQU 1 ; (7) WAS THE PRINTER READY
MINMEM EQU (24*1024)/16 ; (7) MINIMUM MEMORY IN PARAGRAPHS
MAXLIN EQU 78 ; (7r) MAXIMUM INPUT LINE LENGTH
; ALL SEGS ORIGINALLY POINT TO CSEG. THE ORG 100H IS STANDARD FOR
; PRODUCING A .COM FILE. THE ES SEGMENT IS ADJUSTED DYNAMICALLY IN THE
; SYSINI ROUTINE TO ALLOW GAME SEGMENTS TO START AT OFFSET ZERO.
; SEE COMMENTS IN SYSINI FOR GAME SEGMENT ALLOCATION AND CALCULATION.
CSEG SEGMENT PARA PUBLIC
ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:CSEG
ORG 100H
;
PUBLIC MSZIP
MSZIP PROC
JMP START ; SKIP OVER ZIP DATA
;
; ALL DATA SAVE THE PAGE TABLE (PAGTAB) ARE DEFINED BETWEEN THE ABOVE JMP
; AND THE STARTING DESTINATION.
;
;VARIBLE DEFINITIONS:
PUBLIC START,GAMFILE,SAVFILE,GAMHNDL,SAVHNDL,HANDLE,SEEKF
PUBLIC LASTDRV,LSTDFLG,DRVFLG,LASTSAV,SSBUF,DEFDRV,CURDRV
;GTBLKS
;************************************************************************
; DO NOT MOVE THESE VARIABLES. DEFINE NOTHING ABOVE THEM. CREATE
; IS DEPENDENT UPON THIS FIXED VARIABLE SETUP FOR GAMFILE AND
; SAVFILE.
;************************************************************************
;
GAMFILE DB "WITNESS.DAT",0 ; PATCHED BY CREATE
DB 53 DUP (0) ; POTENTIAL LENGTH OF FILENAME
SAVFILE DB "WITNESS.SAV",0
DB 53 DUP (0) ; POTENTIAL LENGTH OF FILENAME
LASTSAV DB 64 DUP (0)
;
GAMESIZ DW ? ; GAME SIZE IN 512 BYTE BLOCKS
GAMHNDL DW ? ; FOR STORAGE OF FILE HANDLES
SAVHNDL DW ?
SSFILE DB "SETUP.INF",0
SSBUF DW SSLNT/2 DUP (0)
SKPDRV DB 0 ; (7n) DON'T OUTPUT DRIVE NAME
DEFDRV DB ? ; BYTE FOR DEFAULT DRIVE
CURDRV DB ? ; BYTE FOR NEW DRIVE (SAVES)
DRVFLG DB 0 ; FLAG WHETHER DRIVE SPECIFIED ON SAVE
LASTDRV DB ? ; TEMPORARY STORAGE FOR SAVE AND
LSTDFLG DB ? ; RESTORE FAILURE
HANDLE DW ? ; GENERAL HANDLE FOR GTBLKS ROUTINE
SEEKF DB 1 ; FLAG FOR WHETHER OR NOT TO SEEK
PRTBUF DB ? ; (7) BUFFER FOR PRINTER OUTPUT
PUBLIC DSKDIR
DSKDIR DB 0 ;0 FOR READ, 1 FOR WRITE
; SCREEN DEFINITIONS AND ANSI STRINGS
PUBLIC SCRATR,COLFLG,SCP,RCP,SCPL,SLPP,STINIT,STRESET,SBLINE
PUBLIC SELINE,SPINIT,STINIT,CTRESET,CBLINE,CELINE,CLS,CLSC
SCRATR DB 7 ;(7) BLACK BACKGROUND FOR DEFAULT
COLFLG DB 0 ;(7) ARE WE USING COLOR
SCPL DB 80 ; WIDTH OF THE SCREEN IN COLUMNS (DEFAULTS)
SLPP DB 25 ; LENGTH OF THE SCREEN IN ROWS
SCP DB 3 ; (7) ANSI SAVE CURSOR POSITION
DB 27,'[s'
RCP DB 3 ; (7) ANSI RESTORE CURSOR POSITION
DB 27,'[u'
STINIT DB 16
DB 27,"[2J",27,"[0m",27,"[01;01H"
STRESET DB 4
DB 27,"[0m"
SBLINE DB 15
DB 27,"[37;7m",27,"[01;01H"
SELINE DB 12
DB 27,"[0m",27,"["
SELINE1 DB "25;01H"
SPINIT DB 0
CTINIT DB 20
DB 27,"[37;44m",27,"[2J",27,"[01;01H"
CTRESET DB 0
CBLINE DB 16
DB 27,"[31;47m",27,"[01;01H"
CELINE DB 16
DB 27,"[37;44m",27,"["
CELINE1 DB "25;01H"
CPINIT DB 0
CLS DB 12
DB 27,"[2J",27,"["
CLS1 DB "25;01H"
CLSC DB 20
DB 27,"[37;44m",27,"[2J",27,"["
CLS1C DB "25;01H"
;
PUBLIC WINDOW1,COLWIN1,WINDOW0,COLWIN0,RADIX
WINDOW1 DB 8
DB 27,'[02;01H'
COLWIN1 DB 16
DB 27,'[02;01H',27,'[34;42m'
WINDOW0 DB 4
DB 27,'[0m'
COLWIN0 DB 8
DB 27,'[37;44m'
RADIX DB 10 ; THE DEFAULT RADIX FOR THE SCREEN
;SCRIPTING STUFF
PUBLIC GAMEIN,SCRHLD,SCRFLG,PRNNRDY
GAMEIN DB 0
SCRHLD DB 0 ; (7n) IS SCRIPTING TEMPORARILY OFF
SCRFLG DB 0
PRNNRDY DB " %Printer not ready: Abort or Retry? ",80H
;USL STUFF
SLFLG DB 1 ;STATUS LINE ASSUMED PRESENT
PMFLAG DB 0 ;AM/PM
PUBLIC SLSTR,SLTAB,SLSTAB,SLTTAB
SLSTR DW 0 ;TOP OF SCREEN STRING
SLTAB DW 0 ;TABLE USED BY OPUSL
; STATUS LINE DESCRIPTOR TABLE. CHOSEN BASED ON SETUP.INF AND MODE
; WORD. CHOICE OF STATUS LINE RESULTS WITH STATUS LINE STRING POINTER
; IN SLSTR AND TABLE DESCRIBING STATUS LINE IN SLTAB.
;
SLSTAB DW SLS40
DW SLS40T
DW SLS80
DW SLS80T
SLTTAB DW SLT40
DW SLT40T
DW SLT80
DW SLT80T
; THE FOLLOWING DATA STRUCTURES ARE TABLES AND STRINGS THAT ARE
; DEFINED TO CREATE AND MAINTAIN A WIDE VARIETY OF STATUS LINES.
; THE STRUCTURE OF THE DATA IS FIRST A STRING WHERE THE ROOM DESCRIPTION
; AND THE SCORE/MOVES OR TIME WILL BE WRITTEN. (A POINTER INTO THIS
; STRING IS CREATED IN TSETUP BASED ON TERMINAL WIDTH)
;
; THE STRING IS FOLLOWED BY A TABLE 12 WORDS IN LENGTH. THOSE WORDS
; CAN BE BROKEN UP INTO 3 GROUPS OF 4 WORDS. THERE IS ONE GROUP FOR
; EACH OF ROOM DESCRIPTION, SCORE AND MOVES (OR HOURS AND MINUTES).
; THE FIRST WORD INDICATES WHEN THE ENTRY WAS LAST UPDATED, THE SECOND
; IS A POINTER TO THE ROUTINE THAT OUTPUTS THE QUANTITY, AND THE THIRD
; AND FOURTH ARE THE BOUNDARIES OF THE FIELD IN WHICH THE QUANTITY IS
; WRITTEN.
;
; OPUSL AND TSETUP ARE THE ONLY ROUTINES THAT "KNOW" ABOUT THESE DATA
; STRUCTURES
;40 COLUMN, SCORE
PUBLIC SLS40,SLS40T
SLS40 DB 40 DUP(32),"S:",5 DUP(32),"M:",6 DUP(32),0,80H
SLS40T DW -1 ; LAST UPDATE
DW OPPRND ; ADDR OF PRINT ROOM DESC
DW 1 ; STARTING COLUMN
DW 25 ; ENDING COLUMN
DW -1
DW OPPRNN
DW 28
DW 30
DW -1
DW OPPRNN
DW 35
DW 40
;40 COLUMN, TIME
PUBLIC SLT40,SLT40T
SLT40 DB 40 DUP(32),"Time:",10 DUP(32),0,80H
SLT40T DW -1
DW OPPRND
DW 1
DW 24
DW -1
DW OPPRNH
DW 31
DW 33
DW -1
DW OPPRNM
DW 34
DW 40
;80 COLUMN, SCORE
PUBLIC SLS80,SLS80T
SLS80 DB 110 DUP(32),"Score:", 10 DUP(32),"Moves:",7 DUP(32),0,80H
SLS80T DW -1
DW OPPRND
DW 1
DW 27
DW -1
DW OPPRNN
DW 57 ; (7) ADJUST SCORE AND MOVES UNITS
DW 61 ; (7) BACK ONE FOR DA
DW -1
DW OPPRNN
DW 73 ; (7)
DW 80 ; (7)
;80 COLUMN, TIME
PUBLIC SLT80,SLT80T
SLT80 DB 120 DUP(32),"Time:", 13 DUP(32),0,80H
SLT80T DW -1
DW OPPRND
DW 1 ; (7) CHANGED FROM 3 FOR CONSISTENCY
DW 29 ; (7) UNDER DIRECTION BY DA
DW -1
DW OPPRNH
DW 66 ; (7) ADJUST BACKWARDS BY 3 FOR DA
DW 68 ; (7)
DW -1
DW OPPRNM
DW 69 ; (7)
DW 80 ; (7)
PUBLIC RSEED1,RSEED2
;OPRAND
RSEED1 DW ? ;SEED1 FOR RANDOM NUMBERS
RSEED2 DW ? ;SEED2 FOR RANDOM NUMBERS
RTEMP DW ? ;TEMP FOR RANDOM ROUTINE
PUBLIC RDWSTR,RDBOS,RDEOS,RDRET,RDNWDS,WRDOFF
;READ
RDWSTR DW 4 DUP(0) ;WORD STRING BUFFER FOR ZWORD
RDBOS DW 0 ;BEGINNING OF STRING POINTER
RDEOS DW 0 ;END OF STRING POINTER
RDRET DW 0 ;RETURN TABLE POINTER
RDNWDS DB 0 ;NUMBER OF WORDS READ
;PUTSTR
WRDOFF DW 0 ;OFFSET INTO WORD TABLE FOR CURRENT SET
PUBLIC CHRPTR,ENDBUF
;PUTCHR
CHRPTR DW 0 ;POINTS TO NEXT CHARACTER POSITION
ENDBUF DW 0 ;POINTS JUST PAST END OF OUTPUT BUFFER (0)
;GETNUM
STATUS DW 0 ;STATUS-LINE-REQUESTED FLAG
PUBLIC IRBRKS,TIMEMD,ZORKID,ENDLOD,VOCTAB,OBJTAB
PUBLIC GLOTAB,WRDTAB,PURBOT,ESIBKS,VWLEN,VWORDS,VOCBEG
PUBLIC OUTBUF,INBUF,RBRKS,BUFFERS,PAGES,INITTBL,SEGEND
;ZIPBGN
IRBRKS DB " ",9,13,12,".,?",0 ;INITIAL SET OF READ BREAK CHARS
TIMEMD DW 0 ;TIME(VERSUS SCORE)-MODE-FOR-STATUS-LINE FLAG
ZORKID DW 0 ;UNIQUE GAME & VERSION IDENTIFIER
ENDLOD DW 0 ;ENDLOD BLOCK NUMBER
SEGEND DW 0 ; (6) ENDLOD THAT DOESN'T GET DIDDLED
VOCTAB DW 0 ;SAVE VOCABULARY TABLE POINTER
OBJTAB DW 0 ;OBJECT TABLE POINTER
GLOTAB DW 0 ;GLOBAL TABLE POINTER
WRDTAB DW 0 ;WORD TABLE POINTER
PURBOT DW 0 ;PURE CODE POINTER
ESIBKS DW 0 ;END OF SELF-INSERTING BREAK CHARACTERS
VWLEN DW 0 ;NUMBER OF BYTES IN A VOCABULARY WORD ENTRY
VWORDS DW 0 ;NUMBER OF WORD ENTRIES IN VOCABULARY
VOCBEG DW 0 ;BEGINNING OF ACTUAL VOCABULARY
OUTBUF DB 81 DUP(?) ;OUTPUT BUFFER
INBUF DB MAXLIN+2 DUP(?) ;INPUT BUFFER
RBRKS DB 32 DUP(?) ;STRING OF READ BREAK CHARACTERS
BUFFERS DW 0 ;(6) NUMBER OF 512 BYTE BUFFERS FOR PAGING
PAGES DW 0 ;SWAPPING AREA
CHRFLG DB 0
INITTBL DB 254,0,0,0
; CMDLIN WAS CREATED FOR CHANGING CERTAIN PARAMETERS ON THE COMMAND LINE
; CURRENTLY THERE ARE SWITCHES (C,M,W). BITS ARE SET IN SCANCMD AND
; PROCESSED IN SYSINI OR SSETUP.
;
PUBLIC CMDLIN,MEMORY
CMDLIN DB 0 ; (7n) 16=IBM PARALLEL PRINTER
; (7) 8=SCROLL SET
; (7) 4=MEMORY SET
; (7) 2=COLOR SET
; (7) 1=MONOCHROME SET
MEMORY DW 0 ; (7) MEMORY SIZE SET ON CMDLIN
PUBLIC ZLOCS,ZPC1,ZPC2,ARGBLK,ZPCSEG,ZPCFLG
;RESTRT
ZLOCS DW 0 ;POINTER TO LOCALS
ZPC1 DW 0 ;ZPC BLOCK-POINTER
ZPC2 DW 0 ;ZPC BYTE-POINTER
ZPCSEG DB 0 ;(6) GAME SEGMENT WHERE ZPC IS
ZPCFLG DB 0 ; (7n) ZPC PAGE IS MUNGED?
ARGBLK DW 4 DUP(?) ;ARGUMENT BLOCK FOR EXTENDED OPERATIONS
PUBLIC CURPAG,CURBLK,CURTAB,CURSEG
;NEWZPC
CURPAG DW 0 ;CURRENT PAGE (WHERE ZPC IS) POINTER
CURBLK DW 0 ;CURRENT BLOCK, USUALLY SAME AS ZPC1
CURTAB DW 0 ;CURRENT PAGE TABLE POINTER +1
PUBLIC RTIME1,RTIME2,LPAGE,LPLOC,LPTAB,GAMESEG,SEG1,FITS
;GETPAG
RTIME1 DB 0 ;REFERENCE TIME, 1 1/2 WORDS USED
RTIME2 DW 0
LPAGE DW 0 ;LAST REFERENCED PAGE NUMBER
LPLOC DW 0 ;AND ITS CORE LOCATION
LPTAB DW 0 ;AND ITS TABLE POINTER
GAMESEG DW ? ;(6) FIRST (OR ZERO) GAME SEGMENT
SEG1 DW ? ;(6) SECOND GAMESEG (GAMESEG+64K)
CURSEG DB 0 ;(6) SEGMENT (0/1) FOR CURRENT PAGE
FITS DB 0 ;(6) FLAG FOR GAME ALL IN MEMORY
; SPLIT AND SCREEN VARS
PUBLIC SCRNFLG,SPLCOL,SPLTFLG
SCRNFLG DB 0 ; (7) WINDOW THAT WE ARE WRITING IN
SPLCOL DB 27H ; (7) GREEN BACKGROUD FOR WINDOW 1
SPLTFLG DB 0 ; (7) IS THE SCREEN SPLIT
;OPERATION TABLES: ;ZERO ARGUMENT OPERATIONS
PUBLIC ZEROOP,ONEOP,EXTOP
ZEROOP DW OPRTRU ;176
DW OPRFAL ;177
DW OPPRNI ;178
DW OPPRNR ;179
DW OPNOOP ;180
DW OPSAVE ;181
DW OPREST ;182
DW OPRSTT ;183
DW OPRSTA ;184
DW OPFSTA ;185
DW OPQUIT ;186
DW OPCRLF ;187
DW OPUSL ;188
DW OPVERI ;189
DW 0 ;190
DW 0 ;191
;ONE ARGUMENT OPERATIONS
ONEOP DW OPQZER ;128
DW OPQNEX ;129
DW OPQFIR ;130
DW OPLOC ;131
DW OPPTSI ;132
DW OPINC ;133
DW OPDEC ;134
DW OPPRNB ;135
DW 0 ;136
DW OPREMO ;137
DW OPPRND ;138
DW OPRETU ;139
DW OPJUMP ;140
DW OPPRIN ;141
DW OPVALU ;142
DW OPBCOM ;143
;TWO ARGUMENT AND EXTENDED ARGUMENT OPERATIONS
EXTOP DW 0 ;0
DW OPQEQU ;1
DW OPQLES ;2
DW OPQGRT ;3
DW OPQDLE ;4
DW OPQIGR ;5
DW OPQIN ;6
DW OPBTST ;7
DW OPBOR ;8
DW OPBAND ;9
DW OPQFSE ;10
DW OPFSET ;11
DW OPFCLE ;12
DW OPSET ;13
DW OPMOVE ;14
DW OPGET ;15
DW OPGETB ;16
DW OPGETP ;17
DW OPGTPT ;18
DW OPNEXT ;19
DW OPADD ;20
DW OPSUB ;21
DW OPMUL ;22
DW OPDIV ;23
DW OPMOD ;24
DW 0 ;25
DW 0 ;26
DW 0 ;27
DW 0 ;28
DW 0 ;29
DW 0 ;30
DW 0 ;31
DW OPCALL ;224
DW OPPUT ;225
DW OPPUTB ;226
DW OPPUTP ;227
DW OPREAD
;228
DW OPPRNC ;229
DW OPPRNN ;230
DW OPRAND ;231
DW OPPUSH ;232
DW OPPOP ;233
DW OPSPLT ;234
DW OPSCRN ;235
DW 0 ;236
DW 0 ;237
DW 0 ;238
DW 0 ;239
DW 0 ;240
DW 0 ;241
DW 0 ;242
DW 0 ;243
DW 0 ;244
DW 0 ;245
DW 0 ;246
DW 0 ;247
DW 0 ;248
DW 0 ;249
DW 0 ;250
DW 0 ;251
DW 0 ;252
DW 0 ;253
DW 0 ;254
DW 0 ;255
;(7) COPY PROTECTION STUFF
PUBLIC COMPATS,COMP1
COMPATS DW 2 ; (7) NUMBER OF STRINGS IN THE LIST
COMP1 DB 'COMPAQ Co$'
DB 'COPR. IBM$' ; (7) EACH STRING MUST BE 9 CHARS
; DB 'Tandy Cor$' ; (7v) ADD TANDY
;MCRLF
PUBLIC SCROLL,TOPLIN,MORLIN
; (7) SCROLL IS SEEMINGLY BACKWARDS BUT TOO INTERWOVEN TO
; GO AROUND AND FIX NOW.
; 0 = IBM Compatible 100% (use windowed scrolling)
; 1 = MS-DOS, no windowed scrolling
;
SCROLL DB 1
TOPLIN DW TOPSCR ; (7) WORD FOR CH,CL FOR UPPER RIGHT
MORE DB "**MORE** ",80H
EMORE DB 13," ",13,80H
MORLIN DW 0 ; (7) COUNT OF LINES SCROLLED WITHOUT PAUSE
;STRUCTURE AND RECORD DEFINITIONS:
;OBJECT OPERATIONS
OBJECT STRUC
FLAGS1 DW ?
FLAGS2 DW ?
PARENT DB ?
SIBLING DB ?
CHILD1 DB ?
PROPS DW ?
OBJECT ENDS
PROPID RECORD PROPSIZE:3,PROPNUM:5
;STRING DEFINITIONS
;STATION IDENTIFICATION
PUBLIC IDSTR
IDSTR DB "IBM/MS-DOS 2.0 Interpreter Version ",ZVERSN,ZEDIT,0
;TERMINAL SETUP
SSMSG1 DB "Cannot open Setup File.",0
SSMSG2 DB "Cannot read Setup File.",0
SSMSG3 DB "Cannot close Setup File.",0
PUBLIC SAV0,SAV1,SAV2,SAV3,ERR1,ERR2,ERR3,ERR4,ERR5,ERR6
;SAVE/RESTORE
SAV0 DB "Insert save disk then enter file name.",0
SAV1 DB "(Default is ",80H
SAV2 DB "): ",80H
SAV3 DB "Insert game disk then strike any key to continue.",0
ERR1 DB "SAVE file not found",0
ERR3 DB "Bad file name syntax",0
ERR4 DB "Unable to access file",0
ERR5 DB "No room on diskette for SAVE file",0
ERR6 DB "Read of SAVE file failed",0
;READ
ERR2 DB "Too many words typed, flushing: ",80H
PUBLIC FTL2,FTL4,FTL5,FTL6,FTL7,FTL9
;OPNEXT/OPPUTP
FTL2 DB "No such property",0
;ZIPBGN
FTL4 DB "Wrong game or version",0
;NXTINS
FTL5 DB "Illegal operation",0
;FINDPG
FTL6 DB "No free pages",0
;GTBLKS
FTL7 DB "Game file read error",0
;SYSINI
FTL9 DB "Game file not found",0
FTL10 DB 'Unauthorized copy',0
FTL11 DB 'Wrong DOS version. Must be 2.0 or higher',0
FTL12 DB 'Insufficient memory to play game',0
;Fatal error header
FATHDR DB "Fatal error: ",80H
;ZSTR CHARACTER CONVERSION VECTOR
PUBLIC ZCHRS
ZCHRS DB "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
DB " 0123456789.,!?_#'"
DB '"/\-:()'
PUBLIC STK_TOP,STKBOT,ZSTK_TP
; STACK SETUP
DW 200H DUP(?)
STK_TOP LABEL WORD
STKBOT DW LSTACK DUP(?)
ZSTK_TP LABEL WORD
;
SUBTTL MACROS
PAGE +
GTAWRD MACRO D,S ;;GET ABSOLUTE WORD
MOV D&X,ES:&S
XCHG D&H,D&L
ENDM
PTAWRD MACRO D,S ;;PUT ABSOLUTE WORK
XCHG S&H,S&L
MOV ES:&D,S&X
ENDM
MOVM MACRO D,S,R ;;MOVE MEMORY TO MEMORY
MOV R,S
MOV D,R
ENDM
PUSHZ MACRO S ;;PUSH ONTO Z STACK
XCHG SP,DI
PUSH S
XCHG SP,DI
ENDM
POPZ MACRO D ;;POP FROM Z STACK
XCHG SP,DI
POP D
XCHG SP,DI
ENDM
POPZT MACRO D ;;GET TOP OF Z STACK
MOV D,SS:[DI]
ENDM
PUSHZT MACRO S ;;PUT TOP OF Z STACK
MOV SS:[DI],S
ENDM
PRINT MACRO STR ;;PRINT A STRING, POINTER IS ARGUMENT
PUSH AX
MOV AX,OFFSET STR
CALL MPRNT
POP AX
ENDM
FATAL MACRO ERR ;;PRINT FATAL ERROR AND DIE
CALL MCRLF
MOV AX,OFFSET FATHDR
CALL MPRNT
MOV AX,OFFSET ERR
CALL MPRNT
JMP FINISH
ENDM
SUBTTL SYSTEM INITIALIZATION
PAGE +
PUBLIC START
START: MOV SP,OFFSET STK_TOP
MOV DI,OFFSET ZSTK_TP
JMP ZIPBGN ;JUMP TO BEGINNING OF ZIP CODE
;
PUBLIC OPSPLT,OPSCRN
OPSCRN PROC
MOV SCRNFLG,AL ; (7) TOGGLE SCREEN CURRENTLY ON
TEST AL,AL ; (7) WHICH DO WE WANT
JZ SCRN0 ; (7) SELECT BOTTOM, SCROLLING WINDOW
CALL SAVECUR
SCRN1: MOV BX,OFFSET WINDOW1 ; (7) HOME THE CURSOR
TEST COLFLG,1 ; (7) SHOULD WE SET THE COLOR
JZ SCRN2 ; (7) NOPE
MOV BX,OFFSET COLWIN1 ; (7) CLEAR SCREEN WITH COLOR
SCRN2: CALL MSPRT ; (7) WITH ANSI LOCATE
RET
SCRN0: CALL RESTCUR ; (7) RESTORE THE CURSOR POSITION
MOV BX,OFFSET WINDOW0
TEST COLFLG,1 ; (7) SHOULD WE CHANGE THE COLOR
JZ SCRN3 ; (7) NOPE...
MOV BX,OFFSET COLWIN0
SCRN3: CALL MSPRT ; (7) RESTORE COLOR
RET
OPSCRN ENDP
OPSPLT PROC
XOR AH,AH
CMP AL,0
JNZ SPL1 ; (7) YES, FIGURE WINDOW SIZE
MOV SPLTFLG,0 ; (7) TURN OFF SPLIT SCREEN
MOV TOPLIN,TOPSCR ; (7) RESTORE TOPLIN TO WHOLE SCR
MOV MORLIN,0 ; (7) RESET THE MORE LINE COUNT
CALL RESTCUR
RET
SPL1: MOV SPLTFLG,1 ; (7) TURN ON SPLIT SCREEN
CMP AL,SLPP ; (7) ARE THERE THIS MANY LINES
JLE SPL2
MOV AL,SLPP ; (7) USE NO MORE THAN THE SCREEN
DEC AL ; (7) LEAVE A SCROLL LINE
SPL2: XCHG AH,AL ; (7) GET #LINES IN TOP HALF
ADD TOPLIN,AX ; (7) THIS FIXES WINDOW 0 SCROLL SIZE
PUSH AX ; (K7) FIX CLEAR ON SPLIT SCREEN
CALL SAVECUR ; (7) SAVE THE CURSOR POSITION
MOV BH,SCRATR ; (7) GET ATTRIBUTE INTO BH FOR CLS
TEST COLFLG,1 ; (7) COLOR BLANK OR VANILLA BLANK
JZ SPL3 ; (7) VANILLA...
MOV BH,SPLCOL ; (7) USE THE GREEN CLEAR
SPL3: POP AX ; (K7) RESTORE AX
MOV DH,AH ; (7) BLANK WINDOW #1
; DEC DH ; (7) LESS ONE LINE (REMOVED (K7))
MOV AH,SCROLLUP ; (7) BLANK THE SCREEN
MOV AL,0 ; (7) ZERO MEANS ENTIRE WINDOW
MOV CX,TOPSCR ; (7) FROM UPPER LEFT
MOV DL,SCPL ; (7) TO LOWER RIGHT
DEC DL ; (7v) COORDINATES ARE ZERO BASED
INT 10H ; (7) CALL VIDEO BIOS ROUTINE TO CLS
CALL RESTCUR ; (7) RESTORE CURSOR POSITION
RET
OPSPLT ENDP
PUBLIC SAVECUR
SAVECUR PROC
MOV BX,OFFSET SCP ; (7) ANSI SAVE CURSOR
CALL MSPRT
RET
SAVECUR ENDP
PUBLIC RESTCUR
RESTCUR PROC
MOV BX,OFFSET RCP ; (7) ANSI RESTORE CURSOR
CALL MSPRT
RET
RESTCUR ENDP
PUBLIC FEEP
FEEP PROC
MOV AX,7
CALL MTTYO ;BELL
RET
FEEP ENDP
;
; VERIFY
;
; OP VERIFY HAS BEEN GIVEN A SPLIT PERSONALITY. THE CASE THAT THE ENTIRE
; GAME FITS INTO MEMORY CALLS ON OPVR2$ WHICH READS THE GAME IN TWO 64K
; READS. THE SECOND ES SEGMENT IS USED SO THAT THE SECOND READ WILL RESTORE
; THE GAME TO ITS ORIGINAL STATE. 64K IS READ IN AND CHECKSUMED AND THEN
; THE REMAINDER OF THE GAME IS READ BACK IN AND CHECKSUMED.
;
; THE ORIGINAL VERIFY FORCES THE PRELOAD TO BE LOADED AGAIN BY SETTING THE
; ENDLOD POINTER TO ZERO. THIS TRICKS GETBYT INTO THINKING THAT NO PAGES
; ARE LOCKED ALREADY IN CORE AND FORCES A CALL TO THE PAGING ROUTINES.
;
;
PUBLIC OPVERI
OPVERI PROC
CALL GAMOPEN ; (7q) PUT THE DISK BACK IN
PRINT IDSTR
CMP FITS,1 ; (6) WHOLE GAME IN???
JNZ OPVR1$
JMP OPVR2$
OPVR1$: MOV AX,ES:[PLENTH] ; (7q) GET END BYTE ADDR
XCHG AH,AL ; (7q) FLOP BYTES
MOV ZPCFLG,1 ; (7n) SET MUNGED ZPC FLAG
PUSH SI
PUSH DI
PUSH ENDLOD
CALL BSPLIT
MOV SI,AX
MOV DI,BX
CALL TBLINI ; (7n) REINITIALIZE THE PAGE TABLE
MOV BX,CURTAB
DEC BX
MOV AX,CURBLK
MOV [BX],AX
MOV AX,0FFFFH
INC BX
MOV [BX],AX
MOV BYTE PTR 2[BX],AL
MOV AX,0
MOV BX,64
MOV DX,0
MOV ENDLOD,0
OPVR1: PUSH SI
PUSH DI
PUSH DX
CALL GETBYT ; GET A BYTE FROM SEG1
POP DX
POP DI
POP SI
ADD DX,CX ; SUM IT
CMP AX,SI
JNE OPVR1
CMP BX,DI
JNE OPVR1
MOV AX,ES:[PCHKSM] ; REMOVED SEG OVERRIDE
XCHG AH,AL
POP ENDLOD
POP DI
POP SI
; CALL TBLINI ; (7n) RESET THE TABLE YET AGAIN
OPVR4: CMP AX,DX
JE OPVR2
JMP PFALSE
OPVR2: JMP PTRUE
;
; VERIFY FOR FITS==1 OR ALL OF GAME IS IN MEMORY
;
OPVR2$: PUSH SI
PUSH DI
PUSH CX
PUSH DS ; (6) ADDRESS SEGMENT 1
MOV BX,GAMHNDL ; (6) READ FROM GAME FILE
MOV SI,GAMESEG ; (6) THROUGH DS
MOV DS,SI ; (6) SO THAT WE OVERLAY GAME
MOV CX,0 ; (6) MOVE FILE PTR TO BEGINNING
MOV DX,64 ; (6) SKIP FIRST 64 BYTES
MOV AL,0 ; (6) METHOD OF SEEKING
MOV AH,CFSEEK ; (6) SEEK FUNCTION
INT 21H ; (6) DO IT
MOV CX,03FC0H ; (7n) READ 16K AT A TIME
MOV DX,0C000H ; (6) OFFSET INTO SEGMENT
MOV AH,CRDRNDZ ; (6) READ FUNCTION
INT 21H ; (6) DO IT
CMP AX,CX ; (6) DID WE GET IT ALL
JNE OPVR5 ; (6) DIE PAINFULLY
XOR DX,DX ; (7n) ZERO THE CHECKSUM
CALL CHKSUM ; (6) DO A CHKSUM
;
OPVR6: MOV DI,DX ; (7n) SAVE CHECK SUM
MOV DX,0C000H ; (7n) BUFFER SPACE
MOV CX,4000H ; (7n) READ 16K EACH TIME
MOV AH,CRDRNDZ ; (7n) DO A RANDOM READ
INT 21H ; (7n) DO IT
CMP AX,CX ; (7n) LAST READ?
JNE OPVR8
MOV DX,DI ; (7n) RESTORE CHECKSUM
CALL CHKSUM
JMP OPVR6
OPVR8: MOV AX,ES:[PLENTH] ; (7n) GET FILE LENGTH IN WORDS
XCHG AH,AL ; (7n) INTO AX
MOV DX,0 ; (7n) ZERO THIS FOR DIVIDE
MOV CX,2000H ; (7n) DIVIDE BY NUMBER OF BYTES/2
DIV CX ; (7n) TO GET REMAINDER
SHL DX,1 ; (7n) MULT BY 2 TO GET BYTES
MOV AX,DX ; (7n) DX SHOULD HAVE REM BYTES
MOV DX,DI ; (7n) RESTORE CHECKSUM
CALL CHKSUM
;
PUSH DX
MOV CX,0 ; (7n) SEEK TO 48K INTO FILE
MOV DX,0C000H ; (7n) TO GET OVERWRITTEN PART
MOV AH,CFSEEK ; (7n) FUNCTION IS SEEK
MOV AL,0 ; (7n) TO OFFSET FROM BEGINNING
INT 21H ; (7n) DO IT
MOV DX,0C000H ; (7n) NOW REFILL SEG WITH ORIG
MOV CX,4000H ; (7n) READ 16K IN
MOV AH,CRDRNDZ ; (7n) FUNCTION READ
INT 21H
POP DX
POP DS ; (6) RESTORES
POP CX
POP DI
POP SI
MOV AX,ES:[PCHKSM] ; (6) GET CHKSUM
XCHG AH,AL
JMP OPVR4
;
CHKSUM: MOV CX,AX ; (7n) GET NUMBER ACTUALLY READ
XOR AX,AX ; (6) ZERO THIS
MOV SI,0C000H ; (7n) 48K INTO THE BUFFER
OPVR3: LODSB ; (6) GET A BYTE
ADD DX,AX ; (6) ADD IT ALL UP
LOOP OPVR3
RET
;
OPVR5: FATAL FTL7 ; GAME FILE READ ERROR
OPVERI ENDP
;
; SAVE AND RESTORE HAVE BEEN MODIFIED TO USE DOS 2.0 FILE NAMES. THIS
; ALLOWS THE USER TO SAVE AND RESTORE TO ANY DISK DRIVE AND ANY SUBDIRECTORY.
; FILE NAMES ARE READ FROM THE CONSOLE AND TRANSFERRED INTO THE SAVFILE
; VARIABLE WITH NO PARSING OTHER THAN A CHECK FOR A DISK DRIVE SPECIFIER.
; IF THE NAME IS TYPED INCORRECTLY, THEN DOS RETURNS AN ERROR WHICH WE
; REPORT ACCORDINGLY.
;
; RESTORE
;
PUBLIC OPREST
OPREST PROC
MOV DSKDIR,0 ;INDICATE RESTORE (READ)
JMP OSV0$
OPREST ENDP
;
; SAVE
;
PUBLIC OPSAVE
OPSAVE PROC
MOV DSKDIR,1 ;INDICATE SAVE (WRITE)
;
;COMMON ENTRYPOINT FOR SAVE AND RESTORE
;
PUBLIC OSV0$,OSVD1,OSVD2,OSVD3,OSVD4A,OSVPSX,OSVOPW
PUBLIC OSVOPDF,OSVPS,OSVT0,OSV4X,OSV4Y
; PUBLIC OSVCC,OSV1C,OSV2C
PUBLIC OSV4,OSVPRR
; DO A DISK RESET IN CASE THEY WANT TO CHANGE DISKS
; UNFORTUNATELY WE HAVE TO ASSUME A 1 DRIVE SYSTEM AT THIS POINT
OSV0$: CMP FITS,1 ; (n) ARE WE ALL THERE
JZ OSV0A ; (n) YES, NO CLOSE
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...
; PRINT "FILENAME (DEFAULT IS drv:filename.ext):"
; PRINT "FILENAME (DEFAULT IS drv:"
OSV0A: PRINT SAV0 ;PRINT "INSERT SAVE DISK THEN ENTER FILE NAME."
PRINT SAV1 ;PRINT "FILE NAME (DEFAULT IS "
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 MTTYO ;PRINT DRIVE B, C, OR D
MOV AL,":" ;PRINT ":"
CALL MTTYO
; PRINT "filename"
OSVD1: PUSH SI ; (6) SAVE THIS
MOV SI,OFFSET SAVFILE ; (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 MTTYO ; (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 " ): "
PRINT SAV2 ;PRINT " ): "
CMP SCPL,50 ;ARE MAX CHARS/LINE >50?
JG OSVD4A ;....YES
CALL MCRLF ;....NO. PRINT A <cr><lf>
; GET LOSER'S SAVE FILENAME FROM THE KEYBOARD
OSVD4A: MOV BX,OFFSET INBUF ;^ KEYBOARD INPUT BUFFER
MOV AL,63 ;(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 SAVFILE ; (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: TEST SCRFLG,1 ; (7p) IS SCRIPTING ON?
JZ NOSCR ; (7p) NO SCRIPTING
CALL SCRSAVE ; (7p) SCRIPT THE SAVE FILE NAME
NOSCR: MOV SI,OFFSET SAVFILE ; (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,SAVFILE ; (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 SAVFILE ; 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 SAVFILE ; (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
;
; THE BELOW CODE GRIMLY EXPLAINS THE FORMAT OF A SAVE FILE. BASICALLY,
; FROM WHAT I CAN TELL, THE LOCALS AND VITALS ARE PUSHED ONTO THE ZSTACK
; AS WELL AS THE ZPC AND THE ZSTACK POINTER. THIS INFORMATION IS FIRST
; WRITTEN TO DISK. THEN THE REST OF PURBOT IS WRITTEN TO DISK. THIS CODE
; ALSO WORKS FOR A RESTORE. SO THE SAVE FILE CONSISTS OF FIRST ZIP INFO
; SAVED ON DISK FOLLOWED BY THE IMPURE PART OF THE GAME CODE.
;
OSVPS: XCHG DI,SP
PUSH ZPC1 ; PUSH VITALS ONTO ZSTACK
PUSH ZPC2
PUSH ZLOCS
PUSH ZORKID
SUB SP,OFFSET STKBOT ; (7v) RELATIVISE THE POINTER
MOV STKBOT,SP ; PUT DEST. INDEX INTO STACKBOTTOM
MOV SP,DI ; RESTORE SP
MOV SAVHNDL,AX ; SAVE FILE HANDLE
PUSH ES
MOV AX,SS ; FLOP STACK SEG AND EXTRA SEG
MOV ES,AX ; ES<=SS
MOV DX,OFFSET STKBOT ; CORE LOCATION
MOV CX,400H ; (6) DO TRANSFER HERE
MOV BX,SAVHNDL ; (6) GET HANDLE
MOV AH,CRDRNDZ ; SETUP FOR A READ
ADD AH,DSKDIR ; MAKE IT A WRITE IF NECESSARY
INT 21H ;
CMP AX,400H ; DID WE GET IT ALL
PUSHF ;SAVE RETURN CODE
MOV DI,SP ;DI ^ CURRENT ZSTACK POSITION
MOV SP,STKBOT ;SP <= ZSTACK POINTER
ADD SP,OFFSET STKBOT ; (7v) RELATIVISE THIS VALUE
POP AX ; POP OFF AND COMPARE FOR ZORKID
CMP AX,ZORKID ;ID OK?
JE OSVT0 ;....YES
MOV GAMEIN,0 ; (7v) INDICATE THAT THE GAME IS LOST
FATAL FTL4 ;....NO. FATAL ERROR
OSVT0: POP ZLOCS ;RESTORE LOCALS AND ZIP PROGRAM COUNTERS
POP ZPC2
POP ZPC1
XCHG DI,SP ;RESTORE MACHINE SP AND ZSTACK POINTER
POPF ;RESTORE REGS
POP ES
JNE OSV4Y ;TRANSFER WAS UNSUCCESSFUL
;
; THE OPEN/CREATE FILE WAS SUCCESSFUL, AND SO WAS THE BLOCK TRANSFER
; SO TRANSFER THE REST
;
OSV4X: MOV CX,ES:[PPURBT] ; (K3) MAYBE THIS IS WRONG
XCHG CH,CL ; (K3) XCHANGE BYTES
MOV AH,CRDRNDZ ; (K1) FUNCTION IS READ (OR WRITE)
ADD AH,DSKDIR ; (K1) ADD IN WRITE STUFF
MOV BX,SAVHNDL ; (K1) READ IT FROM HERE
MOV DX,0 ; (K1) READ IT RIGHT IN TO SEG0
PUSH DS ; (K1) SAVE DS
PUSH ES ; (K1) FLOP BYTES
POP DS ; (K1) ES IS NOW DOS ADDRESSIBLE
INT 21H ; (K1) DO THE SAVE/RESTORE
POP DS ; (K1) RESTORE DS
CMP AX,CX ; (K1) DID WE GET IT ALL?
OSV4Y: PUSHF ;SAVE DOS RETURN CODE
MOV AH,CFCLOSZ ; (6) CLOSE THE FILE
MOV BX,SAVHNDL ; (6) WITH THIS HANDLE
INT 21H
POPF ;RESTORE DOS RETURN CODE
JNE OSV4 ;ERROR EXIT
;
;ELSE EVERYTHING WORKED SUCCESSFULLY
;AND WE CAN GO BACK TO WHAT WE WERE
;DOING..........
;
MOV AH,SCRFLG ; (J2) FIX SCRFLG
XOR AL,AL ; (J2)
MOV WORD PTR ES:[PFLAGS],AX ; (J1) SCRIPT SHOULD MATCH SCRFLG
CMP FITS,1 ;(n) do not need to replace disk if in mem
JZ n1
CALL GAMOPEN ; (7s) OPEN GAME FILE
MOV DL,CURDRV ; (7n) RETURN TO SAVE DISK
MOV AH,CSELDSK ; (7n) AND SELECT IT FOR OPEN
INT 21H
N1: CALL NEWZPC ;GET A NEW PC
JMP PTRUE
;
; **** ERROR **** [Unable to Read File]
;
OSV4: 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,SAVHNDL ; (6) GET HANDLE FOR CLOSE
INT 21H
;
; **** ERROR **** [Diskette Full]
;
MOV DX,OFFSET SAVFILE ; (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
OPSAVE ENDP
;
; ERROR EXIT R/W operations
;
OSVPER PROC
CALL MPRNT ;PRINT ERROR MESSAGE
CALL ZRESTOR ; (7o) RESTORE OLD SAVE NAME
CMP FITS,1 ; (7v) HAVE WE CLOSED THE GAME FILE
JZ OSVPR1 ; (7v) SKIP REOPEN
CALL GAMOPEN ; (7v) OPEN IT
OSVPR1: MOV AL,SCRFLG ;REENABLE SCRIPTING
XCHG AH,AL
XOR AL,AL ; (J1) FIX SCRIPT ON BAD RESTART (RANDOM)
MOV ES:[PFLAGS],AX
JMP PFALSE
OSVPER ENDP
;
PUBLIC SAVESAV,ZRESTOR,SCRSAVE
SCRSAVE PROC
PUSH AX ; (7p) SAVES
PUSH SI
PUSH DX
MOV SI,OFFSET SAVFILE ; (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 SAVFILE ; (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 SAVFILE ; (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
;
; OP RESTART
;
PUBLIC OPRSTT
OPRSTT PROC
MOV BP,CHRPTR
MOV BYTE PTR DS:[BP],0 ;FORCE OUT THE BUFFER
PRINT OUTBUF
JMP RESTRT ;JUMP TO RESTART ADDRESS
OPRSTT ENDP
PUBLIC OPQUIT
OPQUIT PROC
JMP FINISH
OPQUIT ENDP
; THIS OPCODE IS A PSEUDO-OP. IT IS NEVER CALLED BY THE GAME BUT INSTEAD
; BY OPREAD. IT IS HEAVILY TIED TO THE STATUS LINE DATA STRUCTURES.
PUBLIC OPUSL
OPUSL PROC
CMP SLFLG,0
JNE OPUSL1
RET
OPUSL1: PUSH DI
PUSH CHRPTR
MOV BX,SLSTR
MOV CHRPTR,BX
MOV AX,16
MOV DI,SLTAB
MOV CX,3
USLLP: PUSH AX
CALL GETVAR
MOV DX,[DI+4]
CALL USLSET
PUSH CX
PUSH DI
CALL WORD PTR [DI+2]
POP DI
MOV DX,[DI+6]
CALL USLSPC
USLN: ADD DI,8
POP CX
USL1: POP AX
INC AX
LOOP USLLP
MOV AX,"$"
CALL PUTCHR
CALL MSOUT
POP CHRPTR
POP DI
RET
USLSET: MOV BX,SLSTR
MOV CHRPTR,BX
ADD CHRPTR,DX
RET
USLSPC: MOV CX,CHRPTR
SUB CX,SLSTR
SUB DX,CX
MOV CX,DX
JLE USLSP1
USLSPL: MOV AX,20H
CALL PUTCHR
LOOP USLSPL
USLSP1: RET
OPUSL ENDP
SUBTTL ARITHMETIC OPERATIONS
PAGE +
PUBLIC OPADD,OPSUB,OPMUL,OPDIV,OPMOD
;ADD
OPADD PROC
ADD AX,BX ;ADD OPR1 AND OPR2
JMP PUTVAL ;RETURN THE VALUE
OPADD ENDP
;SUB
OPSUB PROC
SUB AX,BX ;SUBTRACT OPR2 FROM OPR1
JMP PUTVAL ;RETURN THE VALUE
OPSUB ENDP
;MULTIPLY AX BY BX
OPMUL PROC
IMUL BX ;MULTIPLY OPR1 BY OPR2,IGNORING OVERFLOW(DX)
JMP PUTVAL ;RETURN THE VALUE
OPMUL ENDP
;DIVIDE AX BY BX
OPDIV PROC
CWD ;CLEAR HIGH WORD AND EXTEND SIGN FOR DIVIDE
IDIV BX ;DIVIDE OPR1 BY OPR2
JMP PUTVAL ;RETURN THE VALUE
OPDIV ENDP
;MOD
OPMOD PROC
CWD ;CLEAR HIGH WORD AND EXTEND SIGN FOR DIVIDE
IDIV BX ;DIVIDE OPR1 BY OPR2
MOV AX,DX ;WE WANT REMAINDER
JMP PUTVAL ;RETURN THE VALUE
OPMOD ENDP
;RANDOM BY DAN
PUBLIC OPRAND
OPRAND PROC
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
JMP PUTVAL
OPRAND ENDP
;LESS?
PUBLIC OPQLES,OPQGRT,OPBTST,OPBOR,OPBCOM,OPBAND
OPQLES PROC
CMP AX,BX ;IS OPR1 LESS THAN OPR2?
JL JPT ;YES, PREDICATE TRUE
JPF: JMP PFALSE ;NO, PREDICATE FALSE
JPT: JMP PTRUE
OPQLES ENDP
;GRTR?
OPQGRT PROC
CMP AX,BX ;IS OPR1 GREATER THAN OPR2?
JG JPT ;YES, PREDICATE TRUE
JMP JPF ;NO, PREDICATE FALSE
OPQGRT ENDP
SUBTTL LOGICAL OPERATIONS
PAGE +
;BTST
OPBTST PROC
NOT AX ;TURN OFF BITS IN OPR2 THAT ARE ON IN OPR1
AND BX,AX
JE JPT ;SUCCESS IF OPR2 COMPLETELY CLEARED
JMP JPF
OPBTST ENDP
;BOR
OPBOR PROC
OR AX,BX ;LOGICAL OR
JMP PUTVAL ;RETURN THE VALUE
OPBOR ENDP
;BCOM
OPBCOM PROC
NOT AX ;LOGICAL COMPLEMENT
JMP PUTVAL ;RETURN THE VALUE
OPBCOM ENDP
;BAND
OPBAND PROC
AND AX,BX ;LOGICAL AND
JMP PUTVAL ;RETURN THE VALUE
OPBAND ENDP
SUBTTL GENERAL PREDICATES
PAGE +
PUBLIC OPQEQU,OPQZER
;EQUAL?
OPQEQU PROC
NOP ;TELL CALLER TO USE ARGUMENT BLOCK
MOV BX,ARGBLK ;GET OPR1
CMP BX,ARGBLK[2] ;IS OPR1 EQUAL TO OPR2?
JE OQE1$ ;YES
CMP AX,3 ;NO, IS THERE A THIRD OPERAND?
JL OQE2$ ;NO
CMP BX,ARGBLK[4] ;YES, IS IT EQUAL TO OPR1?
JE OQE1$ ;YES
CMP AX,4 ;NO, IS THERE A FOURTH?
JL OQE2$ ;NO
CMP BX,ARGBLK[6] ;YES, IS IT EQUAL TO OPR1?
JNE OQE2$ ;NO
OQE1$: JMP PTRUE ;PREDICATE TRUE IF EQUAL
OQE2$: JMP PFALSE ;PREDICATE FALSE IF NOT
OPQEQU ENDP
;ZERO?
OPQZER PROC
CMP AX,0 ;IS OPR ZERO?
JNE OQZ1$ ;NO, PREDICATE FALSE
JMP PTRUE ;YES, PREDICATE TRUE
OQZ1$: JMP PFALSE
OPQZER ENDP
SUBTTL OBJECT OPERATIONS
PAGE +
PUBLIC OPMOVE,OPREMO,OPQFSE,OPFSET
;MOVE (OBJ1 INTO OBJ2)
OPMOVE PROC
PUSH AX ;PROTECT OPRS FROM REMOVE CALL
PUSH BX
CALL OPREMO ;REMOVE OBJ1 FROM WHEREVER IT IS
POP DX ;OBJ2
MOV AX,DX
CALL OBJLOC ;FIND ITS LOCATION
MOV BX,AX ;MOVE TO BASE
POP CX ;OBJ1
MOV AX,CX
CALL OBJLOC ;FIND ITS LOCATION
MOV BP,AX ;MOVE TO BASE
MOV ES:[BP].PARENT,DL ; PUT OBJ2 INTO OBJ1'S LOC SLOT
MOV DH,ES:[BX].CHILD1 ; GET CONTENTS OF OBJ2'S FIRST SLOT
MOV ES:[BX].CHILD1,CL ; MAKE OBJ1 FIRST CONTENT OF OBJ2
CMP DH,0 ;WERE THERE ANY OTHER CONTENTS?
JE OMV1$ ;NO
MOV ES:[BP].SIBLING,DH ; YES, CHAIN ONTO OBJ1'S SIBLING SLOT
OMV1$: RET
OPMOVE ENDP
;REMOVE (OBJ FROM ITS PARENT)
OPREMO PROC
MOV CX,AX ;SAVE OBJ FOR LATER
CALL OBJLOC ;FIND ITS LOCATION
MOV BX,AX ;MOVE TO BASE
MOV CH,ES:[BX].PARENT ; GET ITS PARENT
CMP CH,0 ;DOES IT HAVE A PARENT?
JE ORM3$ ;IF NOT, WE'RE DONE
MOV AL,CH ;PARENT
CALL OBJLOC ;FIND PARENT'S LOCATION
MOV BP,AX ;MOVE TO BASE
MOV DL,ES:[BP].CHILD1 ; GET PARENT'S FIRST CONTENT
CMP DL,CL ;IS IT OBJ?
JNE ORM1$ ;NO
MOVM ES:[BP].CHILD1,ES:[BX].SIBLING,AL ;YES, CHANGE SLOT TO
;OBJ'S SIBLING
JMP ORM2$ ;AND RETURN
ORM1$: MOV AL,DL ;CURRENT SIBLING
CALL OBJLOC ;FIND ITS LOCATION
MOV BP,AX ;MOVE TO BASE
MOV DL,ES:[BP].SIBLING ;GET NEXT SIBLING IN CHAIN
CMP DL,CL ;IS IT OBJ?
JNE ORM1$ ;NO, CONTINUE LOOP
MOVM ES:[BP].SIBLING,ES:[BX].SIBLING,AL ;YES, CHANGE IT TO
;OBJ'S SIBLING
ORM2$: MOV ES:[BX].PARENT,0 ;OBJ NOW HAS NO PARENT
MOV ES:[BX].SIBLING,0 ;OR SIBLING
ORM3$: RET
OPREMO ENDP
;FSET? (IS FLAG SET IN OBJ?)
OPQFSE PROC
CALL OBJLOC ;FIND OBJ'S LOCATION
CMP BX,16 ;SECOND WORD FLAG?
JL OQF1$ ;NO
SUB BX,16 ;YES, SUBTRACT 16 FROM FLAG NUMBER
INC AX ;AND USE SECOND FLAG WORD
INC AX
OQF1$: MOV CX,BX ;MOVE TO COUNT
MOV BX,AX ;MOVE TO BASE
GTAWRD A,[BX] ;GET THE FLAG WORD
MOV DX,8000H ;SHIFT A BIT TO PROPER POSITION
SHR DX,CL
TEST AX,DX ;IS THIS BIT SET IN FLAG WORD?
JE OQF2$ ;NO, PREDICATE FALSE
JMP PTRUE ;YES, PREDICATE TRUE
OQF2$: JMP PFALSE
OPQFSE ENDP
;FSET (SET A FLAG IN OBJ)
OPFSET PROC
CALL OBJLOC ;FIND OBJ'S LOCATION
CMP BX,16 ;SECOND WORD FLAG?
JL OFS1$ ;NO
SUB BX,16 ;YES, SUBTRACT 16 FROM FLAG NUMBER
INC AX ;AND USE SECOND FLAG WORD
INC AX
OFS1$: MOV CX,BX ;MOVE TO COUNT
MOV BX,AX ;MOVE TO BASE
GTAWRD A,[BX] ;GET THE FLAG WORD
MOV DX,8000H ;SHIFT A BIT TO PROPER POSITION
SHR DX,CL
OR AX,DX ;SET THIS BIT IN FLAG WORD
PTAWRD [BX],A ;STORE THE NEW FLAG WORD
RET
OPFSET ENDP
PUBLIC OPFCLE,OPLOC,OPQFIR,OPQNEX,OPQIN
;FCLEAR (CLEAR A FLAG IN OBJ)
OPFCLE PROC
CALL OBJLOC ;FIND OBJ'S LOCATION
CMP BX,16 ;SECOND WORD FLAG?
JL OFC1$ ;NO
SUB BX,16 ;YES, SUBTRACT 16 FROM FLAG NUMBER
INC AX ;AND USE SECOND FLAG WORD
INC AX
OFC1$: MOV CX,BX ;MOVE TO COUNT
MOV BX,AX ;MOVE TO BASE
GTAWRD A,[BX] ;GET THE FLAG WORD
MOV DX,7FFFH ;SHIFT A BIT TO PROPER POSITION
ROR DX,CL
AND AX,DX ;CLEAR THIS BIT IN FLAG WORD
PTAWRD [BX],A ;STORE THE NEW FLAG WORD
RET
OPFCLE ENDP
;LOC (RETURN CONTAINER OF OBJ)
OPLOC PROC
CALL OBJLOC ;FIND OBJ'S LOCATION
MOV BX,AX ;MOVE TO BASE
MOV AL,ES:[BX].PARENT ; GET LOC SLOT
JMP BYTVAL ;RETURN THE BYTE VALUE
OPLOC ENDP
;FIRST? (RETURN FIRST SLOT OF OBJ, FAIL IF NONE)
OPQFIR PROC
CALL OBJLOC ;FIND OBJ'S LOCATION
MOV BX,AX ;MOVE TO BASE
MOV AL,ES:[BX].CHILD1 ; GET FIRST SLOT
PUSH AX ;SAVE IT
CALL BYTVAL ;RETURN THE BYTE VALUE
POP AX ;RESTORE IT
CMP AL,0 ;WAS IT ZERO?
JE JPF1 ;YES, PREDICATE FALSE
JPT1: JMP PTRUE ;NO, PREDICATE TRUE
JPF1: JMP PFALSE
OPQFIR ENDP
;NEXT? (RETURN THE NEXT (SIBLING) SLOT OF OBJ, FAIL IF NONE)
OPQNEX PROC
CALL OBJLOC ;FIND OBJ'S LOCATION
MOV BX,AX ;MOVE TO BASE
MOV AL,ES:[BX].SIBLING ; GET SIBLING SLOT
PUSH AX ;SAVE IT
CALL BYTVAL ;RETURN THE BYTE VALUE
POP AX ;RESTORE IT
CMP AL,0 ;WAS IT ZERO?
JE JPF1 ;YES, PREDICATE FALSE
JMP JPT1 ;NO, PREDICATE TRUE
OPQNEX ENDP
;IN? (IS OBJ1 CONTAINED IN OBJ2?)
OPQIN PROC
CALL OBJLOC ;FIND OBJ1'S LOCATION
XCHG AX,BX ;MOVE TO BASE
CMP ES:[BX].PARENT,AL ; IS OBJ1'S PARENT OBJ2?
JE JPT1 ;YES, PREDICATE TRUE
JMP JPF1 ;NO, PREDICATE FALSE
OPQIN ENDP
PUBLIC OPGETP
;GETP (GET SPECIFIED PROPERTY OF OBJ, DEFAULT IF NONE)
OPGETP PROC
MOV DX,BX ;PROPERTY
CALL OBJLOC ;FIND OBJ'S LOCATION
MOV BP,AX ;MOVE TO BASE
GTAWRD B,[BP].PROPS ;GET LOCATION OF ITS PROPERTY TABLE
MOV AL,ES:[BX] ; LENGTH OF SHORT DESCRIPTION IN WORDS
SUB AH,AH ;CLEAN OFF ANY HIGH-ORDER BYTE
SHL AX,1 ;CONVERT TO BYTES
ADD BX,AX ;ADJUST POINTER TO SKIP IT
INC BX ;ALSO SKIP LENGTH BYTE
JMP OGP2$ ;SKIP NEXT LINE FIRST TIME THROUGH LOOP
OGP1$: CALL NXTPRP ;POINT TO NEXT PROPERTY
OGP2$: MOV AL,ES:[BX] ; GET PROPERTY IDENTIFIER
AND AL,MASK PROPNUM ;CLEAN OFF LENGTH BITS
CMP AL,DL ;COMPARE PROPERTY NUMBER WITH DESIRED ONE
JG OGP1$ ;IF GREATER, LOOP (TABLE SORTED IN REVERSE)
JL OGP3$ ;IF LESS, NO SUCH PROPERTY HERE
MOV AL,ES:[BX] ;GOT IT, NOW FIND LENGTH OF PROPERTY
INC BX ;POINT TO PROPERTY VALUE
AND AL,MASK PROPSIZE;GET LENGTH BITS
MOV CL,PROPSIZE
SHR AL,CL
CMP AL,0 ;BYTE VALUE?
JNE OGP5$ ;NO
MOV AL,ES:[BX] ;GET THE BYTE
JMP BYTVAL ;AND RETURN IT
OGP3$: DEC DX ;POINT INTO DEFAULT PROPERTY TABLE
SHL DX,1
MOV BX,DX
OGP4$: ADD BX,OBJTAB ;GET THE WORD
OGP5$: GTAWRD A,[BX]
JMP PUTVAL ;AND RETURN IT
OPGETP ENDP
;PUTP (CHANGE VALUE OF A PROPERTY, ERROR IF BAD NUMBER)
PUBLIC OPPUTP,OPP2$
OPPUTP PROC
PUSH CX ;SAVE NEW VALUE
MOV DX,BX ;PROPERTY
CALL OBJLOC ;FIND OBJ'S LOCATION
MOV BP,AX ;MOVE TO BASE
GTAWRD B,[BP].PROPS ;GET LOCATION OF ITS PROPERTY TABLE
MOV AL,ES:[BX] ; LENGTH OF SHORT DESCRIPTION IN WORDS
SUB AH,AH ;CLEAN OFF ANY HIGH-ORDER BYTE
SHL AX,1 ;CONVERT TO BYTES
ADD BX,AX ;ADJUST POINTER TO SKIP IT
INC BX ;ALSO SKIP LENGTH BYTE
JMP OPP2$ ;SKIP NEXT LINE FIRST TIME THROUGH LOOP
OPP1$: CALL NXTPRP ;POINT TO NEXT PROPERTY
OPP2$: MOV AL,ES:[BX] ; GET PROPERTY IDENTIFIER
AND AL,MASK PROPNUM ;CLEAN OFF LENGTH BITS
CMP AL,DL ;COMPARE PROPERTY NUMBER WITH DESIRED ONE
JE OPP3$ ;IF EQUAL, GOT IT
JG OPP1$ ;IF GREATER, LOOP (TABLE SORTED IN REVERSE)
FATAL FTL2 ;OTHERWISE, FATAL ERROR
OPP3$: MOV AL,ES:[BX] ;NOW FIND LENGTH OF PROPERTY
INC BX ;POINT TO PROPERTY VALUE
AND AL,MASK PROPSIZE;GET LENGTH BITS
MOV CL,PROPSIZE
SHR AL,CL
CMP AL,0 ;BYTE VALUE?
POP AX ;RESTORE NEW VALUE
JNE OPP4$ ;ZERO MEANS BYTE VALUE
MOV ES:[BX],AL ;STORE THE NEW BYTE
RET ;AND RETURN
OPP4$: PTAWRD [BX],A ;STORE THE NEW WORD VALUE
RET
OPPUTP ENDP
;NEXTP (RETURN NUMBER OF NEXT PROP FOLLOWING GIVEN PROB IN OBJ)
PUBLIC OPNEXT,ONX2$
OPNEXT PROC
MOV DX,BX ;PROPERTY
CALL OBJLOC ;FIND OBJ'S LOCATION
MOV BP,AX ;MOVE TO BASE
GTAWRD B,[BP].PROPS ;GET ITS LOCATION
MOV AL,ES:[BX] ; LENGTH OF SHORT DESCRIPTION IN WORDS
SUB AH,AH ;CLEAN OFF ANY HIGH-ORDER BYTE
SHL AX,1 ;CONVERT TO BYTES
ADD BX,AX ;ADJUST POINTER TO SKIP IT
INC BX ;ALSO SKIP LENGTH BYTE
CMP DX,0 ;WERE WE GIVEN ZERO AS PROP?
JE ONX4$ ;YES, GO RETURN FIRST PROPERTY NUMBER
JMP ONX2$ ;NO, SKIP NEXT LINE FIRST TIME THROUGH LOOP
ONX1$: CALL NXTPRP ;POINT TO NEXT PROPERTY
ONX2$: MOV AL,ES:[BX] ; GET PROPERTY IDENTIFIER
AND AL,MASK PROPNUM ;CLEAN OFF LENGTH BITS
CMP AL,DL ;COMPARE PROPERTY NUMBER WITH DESIRED ONE
JE ONX3$ ;IF EQUAL, GOT IT
JG ONX1$ ;IF GREATER, LOOP (TABLE SORTED IN REVERSE)
FATAL FTL2 ;OTHERWISE, FATAL ERROR
ONX3$: CALL NXTPRP ;POINT TO NEXT PROPERTY
ONX4$: MOV AL,ES:[BX] ; GET PROPERTY IDENTIFIER
AND AL,MASK PROPNUM ;EXTRACT PROPERTY NUMBER
JMP PUTVAL ;AND RETURN IT
OPNEXT ENDP
SUBTTL TABLE OPERATIONS
PAGE +
PUBLIC OPGET,OPGETB,OPPUT,OPGTPT
;GET (GET THE ITEM'TH WORD FROM TABLE)
OPGET PROC
SHL BX,1 ;CONVERT ITEM TO BYTE COUNT
ADD AX,BX ;INDEX INTO TABLE
CALL BSPLTB ;SPLIT THE POINTER
CALL GETWRD ;GET THE WORD
MOV AX,CX
JMP PUTVAL ;AND RETURN IT
OPGET ENDP
;GETB (GET THE ITEM'TH BYTE FROM TABLE)
OPGETB PROC
ADD AX,BX ;INDEX INTO TABLE
CALL BSPLTB ;SPLIT THE POINTER
CALL GETBYT ;GET THE BYTE
MOV AX,CX
JMP BYTVAL ;AND RETURN IT
OPGETB ENDP
;PUT (REPLACE THE ITEM'TH WORD IN TABLE)
OPPUT PROC
SHL BX,1 ;CONVERT ITEM TO BYTE COUNT
ADD BX,AX ;INDEX INTO TABLE
PTAWRD [BX],C ;STORE THE WORD
RET
OPPUT ENDP
;PUTB (REPLACE ITEM'TH BYTE IN TABLE)
OPPUTB PROC
ADD BX,AX ;INDEX INTO TABLE
MOV ES:[BX],CL ; STORE BYTE
RET
OPPUTB ENDP
;GETPT (GET POINTER TO PROPERTY TABLE FOR GIVEN PROP)
OPGTPT PROC
MOV DX,BX ;PROPERTY
CALL OBJLOC ;FIND OBJ'S LOCATION
MOV BP,AX ;MOVE TO BASE
GTAWRD B,[BP].PROPS ;GET LOCATION OF ITS PROPERTY TABLE
MOV AL,ES:[BX] ;LENGTH OF SHORT DESCRIPTION IN WORDS
SUB AH,AH ;CLEAN OFF ANY HIGH-ORDER BYTE
SHL AX,1 ;CONVERT TO BYTES
ADD BX,AX ;ADJUST POINTER TO SKIP IT
INC BX ;ALSO SKIP LENGTH BYTE
JMP OGT2$ ;SKIP NEXT LINE FIRST TIME THROUGH LOOP
OGT1$: CALL NXTPRP ;POINT TO NEXT PROPERTY
OGT2$: MOV AL,ES:[BX] ;GET PROPERTY IDENTIFIER
AND AL,MASK PROPNUM ;CLEAN OFF LENGTH BITS
CMP AL,DL ;COMPARE PROPERTY NUMBER WITH DESIRED ONE
JG OGT1$ ;IF GREATER, LOOP (TABLE SORTED IN REVERSE)
JE OGT3$ ;FOUND THE PROPERTY
SUB AX,AX ;RETURN ZERO FOR NO SUCH PROPERTY
JMP OGT4$
OGT3$: INC BX ;POINT TO PROPERTY VALUE
MOV AX,BX ;AND RETURN IT
OGT4$: JMP PUTVAL
OPGTPT ENDP
PUBLIC OPPTSI
;PTSIZE (RETURN SIZE OF PROPERTY TABLE)
OPPTSI PROC
MOV BX,AX ;TABLE POINTER
MOV AL,ES:[BX-1] ;GET PROPERTY INDENTIFIER
AND AL,MASK PROPSIZE;EXTRACT LENGTH BITS
SUB AH,AH
MOV CL,PROPSIZE
SHR AX,CL
INC AX ;ADJUST TO ACTUAL LENGTH
JMP PUTVAL ;RETURN IT
OPPTSI ENDP
SUBTTL VARIABLE OPERATIONS
PAGE +
PUBLIC OPVALU,OPSET,OPPUSH,OPPOP
;VALUE (GET VALUE OF VAR)
OPVALU PROC
CALL GETVAR ;GET THE VALUE
JMP PUTVAL ;AND RETURN IT
OPVALU ENDP
;SET (VAR TO VALUE)
OPSET PROC
JMP PUTVAR ;STORE THE VALUE
OPSET ENDP
;PUSH (A VALUE ONTO THE STACK)
OPPUSH PROC
PUSHZ AX ;PUSH THE VALUE
RET
OPPUSH ENDP
;POP (A VALUE OFF THE STACK INTO VAR)
OPPOP PROC
POPZ BX ;POP A VALUE
JMP PUTVAR ;AND STORE IT
OPPOP ENDP
PUBLIC OPINC,OPDEC
;INC (INCREMENT VAR)
OPINC PROC
MOV CX,AX ;VARIABLE
CALL GETVAR ;GET VAR'S VALUE
INC AX ;INCREMENT IT
OPINC1: MOV BX,AX ;VALUE
MOV AX,CX ;VARIABLE
JMP PUTVAR ;STORE NEW VALUE
OPINC ENDP
;DEC (DECREMENT VAR)
OPDEC PROC
MOV CX,AX ;VARIABLE
CALL GETVAR ;GET VAR'S VALUE
DEC AX ;DECREMENT IT
JMP OPINC1 ;STORE NEW VALUE
OPDEC ENDP
PUBLIC OPQIGR,OPQDLE
;IGRTR? (INCREMENT VAR & TEST IF GREATER THAN VAL)
OPQIGR PROC
PUSH AX ;SAVE VARIABLE
CALL GETVAR ;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 ;VALUE
POP AX ;RESTORE VARIABLE
CALL PUTVAR ;STORE NEW VALUE
CMP CX,0 ;TEST FLAG
JE OQI1$ ;FALSE, PREDICATE FALSE
JMP PTRUE ;ELSE, PREDICATE TRUE
OQI1$: JMP PFALSE
OPQIGR ENDP
;DLESS? (DECREMENT VAR & TEST IF LESS THAN VAL)
OPQDLE PROC
PUSH AX ;SAVE VARIABLE
CALL GETVAR ;GET VAR'S VALUE
DEC AX ;DECREMENT IT
SUB CX,CX ;SET FLAG FALSE
CMP AX,BX ;NEW VALUE LESS THAN VAL?
JGE OPQIG1 ;NO, PREDICATE FALSE
JMP OPQIG0 ;YES, PREDICATE TRUE
OPQDLE ENDP
SUBTTL I/O OPERATIONS
PAGE +
PUBLIC GETLIN,GETLLP,GETLLC,GETLEM
;for the boss key, include the boss file
INCLUDE iboss.asm
GETLIN PROC
PUSH BX
MOV SCRHLD,1 ; (7n) HOLD SCRIPTING
PUSH SI
MOV CL,ES:[SI]
MOV BX,OFFSET INBUF
MOV BYTE PTR [BX],CL
CMP CL,MAXLIN ; (7r) DON'T EXCEED BUFFER LENGTH
JBE GTLN0 ; (7r) FIX IF ABOVE
MOV BYTE PTR [BX],MAXLIN ; (7r) WITH OUR NEW MAXIMUM
GTLN0: MOV DX,BX
MOV AH,CRDLIN
INT 21H
TEST SCROLL,1 ;(7) WHAT KIND OF SCROLLING
JNZ GTLN1
MOV AH,SCROLLUP ;(7) USE A VIDEO BIOS SCROLL
MOV AL,1 ;(7) ONLY 1 LINE
MOV CX,TOPLIN ;(7) ALWAYS FREEZE TOP LINE
MOV DH,SLPP ;(7) GET SCREEN LENGTH
MOV DL,SCPL ;(7) GET THE WIDTH OF THE SCREEN
DEC DL ; (7v) COORDINATES ARE ZERO BASED
MOV BH,SCRATR ;(7) GET THE SCREEN ATTRIBUTE
INT 10H ;(7) CALL THE VIDEO
JMP GTLN2 ;(7) SKIP LINE FEED OUTPUT
GTLN1: MOV AH,CCONIO
MOV DL,10
INT 21H
GTLN2: POP DX
MOV BX,OFFSET INBUF
MOV CL,BYTE PTR [BX+1]
SUB CH,CH
INC BX
CMP CL,0
JE GETLEM
GETLLP: INC SI
INC BX
MOV AL,BYTE PTR [BX]
CALL boss
JNC GETLLC1 ;boss returns Carry Set if boss chr
MOV AL," "
GETLLC1:
CMP AL,"A"
JL GETLLC
CMP AL,"Z"
JG GETLLC
ADD AL,32
GETLLC: MOV ES:[SI],AL
LOOP GETLLP
GETLEM: INC SI
SUB AL,AL
MOV ES:[SI],AL
MOV SCRHLD,0 ; (7n) TURN OFF SCRIPT HOLD
POP BX
RET
GETLIN ENDP
PUBLIC PRTOUT
PRTOUT PROC
PUSH AX
PUSH BX
PUSH CX
PUSH DX ; (7) SAVE THIS, USING STD PRT
CMP SCROLL,1 ; (7) ARE WE COMPATIBLE
JZ POUT1 ; (7) NO
TEST CMDLIN,16 ; (7n) DO WE WANT IBM PRINTER
JZ POUT1 ; (7n) GUESS NOT
POUT0: MOV AH,0 ; (7) FUNCTION TO PRINT
MOV DX,0 ; (7) TO THE FIRST PRINTER
INT 17H ; (7) TRY IT
TEST AH,RDYBIT ; (7) TIME OUT?
JZ POUT2 ; (7) WORKED, RETURN
CALL PRNRDY ; (7)
JC POUT2
JMP POUT0 ; (7) RETRY
POUT1: MOV DL,AL ; (7n) GET CHARACTER TO PRINT
MOV AH,CPROUT ; (7n) PRINT IT
INT 21H
POUT2: POP DX ; (7n) FORGET THIS AND BREAK...
POP CX
POP BX
POP AX
RET
PRTOUT ENDP
PUBLIC PRNRDY,SCRCHK
PRNRDY PROC
PUSH AX
MOV SCRHLD,1 ; (7n) HOLD OFF SCRIPTING
PRINT PRNNRDY ; (7) ASK USER ABOUT ACTION
RDY1: CALL MCHRI ; (7) GET A RESPONSE
CALL MTTYO ; (7u) ECHO THE CHARACTER
AND AL,5FH ; (7) UPPPER CASIFY
CMP AL,'A' ; (7) SHOULD WE ABORT
JNZ RDY2 ; (7) ATTEMPTING RETRY
MOV SCRFLG,0 ; (7) TURN OFF SCRIPTING
AND ES:[PFLAGS],SCRMSK ; (7) AND IN THE MODE WORD
MOV SCRHLD,0 ; (7n) TURN OFF BAN ON SCRIPTING
STC ; (7) SET CARRY FOR NO RETRY
PUSHF ; (7) SAVE THESE
JMP RDY3 ; (7) CR LF ON EXIT
RDY2: CMP AL,'R' ; (7) RETRY???
JNZ RDY1 ; (7) WRONG CHARACTER
CLC ; (7) CLEAR CARRY FOR RETRY
PUSHF ; (7) SAVE FLAGS
RDY3: CALL MCRLF ; (7) DO A <CR><LF>
MOV SCRHLD,0 ; (7n) TURN OFF BAN ON SCRIPTING
POPF ; (7) RESTORE FLAGS AND
POP AX ; (7) CHARACTER TO PRINT
RET
PRNRDY ENDP
SCRCHK PROC
PUSH AX
PUSH BX
PUSH DX
GTAWRD A,[PFLAGS]
TEST AL,1 ;CHECK IF SCRIPTING IS REQUESTED
JZ SCRNN$
TEST SCRFLG,1 ; (7) NEW STATE?
JNZ SCR0L$ ; (7x) NO, CONTINUE
MOV SCRFLG,1 ; (7n) TURN ON SCRIPT FLAG
CMP SCROLL,0 ; (7) SHOULD WE BE DOING THIS?
JNZ SCR0L$ ; (7) NOPE...
TEST CMDLIN,16 ; (7n) DID WE REALLY REQUEST IBM
JZ SCR0L$
SCR_1$: MOV DX,0 ; (7) TRY TO INIT THE PRINTER
MOV AH,1
INT 17H ; (7) SO THAT WE WILL TIME OUT FAST
TEST AH,PRTMSK ; (7) TEST FOR TIME OUT
JZ SCR0L$ ; (7) NO PROBLEM, WE'RE FINE
CALL PRNRDY ; (7) INFORM USER OF PROBLEM
JNC SCR_1$
JMP SCRNN$ ; (7) TURN OFF SCRIPTING
SCR0L$: SUB CX,CX
MOV BP,DX
INC BP
SCR1L$: MOV AL,ES:[BP]
INC BP
CMP BP,SI
JLE SCR2L$
CALL PRTCRL
JMP SCREX$
SCR2L$: CALL PRTOUT
INC CX
CMP CL,SCPL
JNE SCR1L$
CALL PRTCRL
JC SCREX$
SUB CX,CX
JMP SCR1L$
SCRNN$: MOV SCRFLG,0
SCREX$: POP DX
POP BX
POP AX
RET
SCRCHK ENDP
PUBLIC PRTCRL
PRTCRL PROC
MOV AL,13 ;FINISH UP WITH CRLF
CALL PRTOUT
MOV AL,10
CALL PRTOUT
RET
PRTCRL ENDP
;READ (A LINE OF INPUT & PARSE IT, LINE BUF IN ES:AX,
;RETURN BUF IN ES:BX)
PUBLIC OPREAD,ORD1$,ORDNS$,ORD8$,ORD9$,ORD10$,ORD1A$,ORD11$,ORD12$
PUBLIC ORD13$,ORD14$,ORD15$,ORD16$,ORD17$,ORD18$,ORD19$,ORD20$
PUBLIC ORD21$,ORD22$,ORD23$
OPREAD PROC
PUSH AX ;SAVE LINE BUF
PUSH AX
PUSH BX
CALL OPUSL ;UPDATE STATUS LINE
POP BX
POP AX
MOV BP,CHRPTR ;NEXT CHARACTER POSITION
MOV BYTE PTR DS:[BP],80H ;(6)DON'T END OUTPUT, IF ANY, WITH NEW LINE
ORD1$: PRINT OUTBUF ;FORCE OUT ANY QUEUED TEXT
MOV CHRPTR,OFFSET OUTBUF ;RESET CHARACTER POINTER
POP SI ;INPUT BUFFER POINTER
MOV MORLIN,0 ;RE-INITIALIZE MORE COUNT FROM HERE
CALL GETLIN ;GET SOME CHARACTERS
CALL SCRCHK
ORDNS$: PUSH DI
MOV RDBOS,DX ;INITIALIZE RDBOS
MOV RDEOS,SI ;AND RDEOS
MOV RDRET,BX ;STORE RET POINTER
MOV RDNWDS,0 ;NO WORDS SO FAR
INC DX ;SKIP LENGTH BYTE
MOV DI,BX ;THIS WILL BE WORD ENTRY POINTER
INC DI ;SKIP MAX WORDS & NWORDS BYTES
INC DI
ORD8$: MOV CX,OFFSET RDWSTR;HERE FOR NEXT WORD, POINT TO WORD STRING
MOV BX,DX ;AND SAVE BEGINNING OF WORD POINTER
ORD9$: CMP DX,RDEOS ;END OF STRING?
JNE ORD10$ ;NO
CMP CX,OFFSET RDWSTR;YES, WAS A WORD FOUND?
JNE ORD15$ ;YES, WE STILL HAVE TO LOOKUP WORD
JMP ORD23$ ;NO, WE'RE DONE
ORD10$: MOV BP,DX ;GET NEXT CHARACTER FROM BUFFER
MOV AL,ES:[BP]
CMP AL,"A"
JL ORD1A$
CMP AL,"Z"
JG ORD1A$
ADD AL,32 ;LOWERCASIFY ALPHABETICS
ORD1A$: INC DX
MOV SI,OFFSET RBRKS ;LIST OF READ BREAK CHARACTERS
ORD11$: INC SI
CMP AL,[SI-1] ;SEARCH LIST FOR THIS ONE
JE ORD12$ ;FOUND IT
CMP BYTE PTR [SI],0 ;END OF LIST?
JNE ORD11$ ;NO, CONTINUE SEARCH
CMP CX,OFFSET RDWSTR[6] ;YES, NOT A BREAK, WORD STRING FULL?
JE ORD9$ ;YES, LOOP UNTIL END OF WORD
MOV BP,CX ;NO, TACK THIS CHARACTER ONTO STRING
MOV DS:[BP],AL
INC CX
JMP ORD9$ ;AND LOOP
ORD12$: CMP CX,OFFSET RDWSTR;WORD READ BEFORE THIS BREAK?
JNE ORD14$ ;YES
CMP SI,ESIBKS ;NO, BUT IS IT A SELF-INSERTING BREAK?
JBE ORD13$ ;YES
INC BX ;NO, UPDATE BEGINNING OF WORD TO SKIP BREAK
JMP ORD9$ ;AND RETURN TO LOOP TO FIND A WORD
ORD13$: MOV BP,CX ;STORE THE BREAK IN WORD STRING
MOV DS:[BP],AL
INC CX
JMP ORD15$ ;AND GO FOR THE WORD
ORD14$: DEC DX ;UNREAD TERMINATING BREAK IN CASE IT WAS SI
ORD15$: INC RDNWDS ;INCREMENT FOUND WORD COUNT
MOV BP,BX ;GREATER THAN MAX ALLOWED?
MOV BX,RDRET
MOV BL,ES:[BX]
CMP BL,59 ; (7w) GAME FIX
JBE ORD15A ; (7w) FIX NUMBER OF TOKENS ALLOWED
MOV BL,59 ; (7w) OTHERWISE WE OVERRUN A TABLE
ORD15A: CMP RDNWDS,BL
MOV BX,BP
JLE ORD16$ ;NO
PRINT ERR2 ;YES, INFORM LOSER
MOV AX,BX ;BEGINNING OF THIS WORD
MOV BP,RDEOS ;SAVE BYTE AFTER EOS
MOV BL,ES:[BP]
MOV BYTE PTR ES:[BP],0 ; ZERO IT TO MAKE STRING ASCIZ
PUSH BX ; (7w) SAVE THIS ADDRESS
MOV BX,DS ; (7w) FIGURE WHERE STRING IS IN DS
NEG BX ; (7w) SUBTRACT DS FROM ES
ADD BX,GAMESEG ; (7w) BX HAS PARAGRAPHS OF DIFFERENCE
MOV CL,4 ; (7w) CONVERT TO AN OFFSET
SHL BX,CL ; (7w) TO ADD IN WITH AX
ADD AX,BX ; (7w) NOW DS:AX SHOULD EQUAL PREV ES:AX
POP BX ; (7w) RESTORE BX
CALL MPRNT ;PRINT IT
MOV ES:[BP],BL ; AND RESTORE OLD BYTE
DEC RDNWDS ;REMEMBER THAT WE FLUSHED THIS WORD
JMP ORD23$ ;AND WE'RE DONE
ORD16$: MOV AX,BX ;CALCULATE NUMBER OF CHARACTERS IN WORD
NEG AX
ADD AX,DX
MOV ES:[DI+2],AL ;(6)SAVE THE NUMBER IN RET TABLE
SUB BX,RDBOS ;BYTE OFFSET FOR BEGINNING OF WORD
MOV ES:[DI+3],BL ;(6)STORE IT, TOO
MOV BP,CX ;MAKE WORD STRING ASCIZ
MOV BYTE PTR DS:[BP],0 ;(6) REMOVED SEG OVERRIDE (DS)
MOV AX,OFFSET RDWSTR;POINT TO IT
CALL ZWORD ;AND CONVERT TO (2-WORD) ZWORD
PUSH DX ;SAVE CHAR & WORD ENTRY POINTERS
PUSH DI
MOV DI,AX ;FIRST ZWORD WORD
MOV SI,VWORDS ;NUMBER OF VOCABULARY WORDS
MOV AX,SI
DEC AX ;WE WANT TO POINT TO LAST WORD
MUL VWLEN ;MULTIPLY BY WORD LENGTH IN BYTES
ADD AX,VOCBEG ;ADD POINTER TO BEGINNING TO FIND LAST WORD
MOV CX,AX ;POINTER TO LAST WORD
MOV DX,DI ;FIRST ZWORD WORD
MOV DI,BX ;SECOND ZWORD WORD
MOV BX,VWLEN ;CALCULATE INITIAL OFFSET FOR BINARY SEARCH
SAR SI,1
ORD17$: SAL BX,1
SAR SI,1
CMP SI,0
JNE ORD17$
MOV SI,VOCBEG ;BEGINNING OF WORD TABLE
ADD SI,BX ;ADD CURRENT OFFSET(HALF OF POWER-OF-2 TABLE)
PUSH AX ;SAVE
MOV AX,VWLEN ;AVOID FENCEPOST BUG FOR EXACT POWER-OF-2 TBL
SUB SI,AX
POP AX ;RESTORE
ORD18$: SAR BX,1 ;NEXT OFFSET WILL BE HALF OF PREVIOUS ONE
GTAWRD A,[SI] ;GET FIRST HALF OF CURRENT ZWORD
CMP DX,AX ;COMPARE DESIRED ONE TO IT
JA ORD19$ ;GREATER, WE'LL HAVE TO MOVE UP
JB ORD20$ ;LESS, WE'LL HAVE TO MOVE DOWN
MOV BP,SI ;SAME, GET SECOND HALF
INC BP
INC BP
GTAWRD A,[BP]
CMP DI,AX ;COMPARE DESIRED WORD WITH IT
JA ORD19$ ;GREATER, WE'LL HAVE TO MOVE UP
JB ORD20$ ;LESS, WE'LL HAVE TO MOVE DOWN
JMP ORD22$ ;SAME, WE'VE FOUND IT, RETURN IT
ORD19$: ADD SI,BX ;TO MOVE UP, ADD CURRENT OFFSET
CMP SI,CX ;HAVE WE MOVED PAST END OF TABLE?
JBE ORD21$ ;NO
MOV SI,CX ;YES, POINT TO END OF TABLE INSTEAD
JMP ORD21$
ORD20$: SUB SI,BX ;TO MOVE DOWN, SIMPLY SUBTRACT OFFSET
ORD21$: CMP BX,VWLEN ;IS OFFSET RESOLUTION BELOW ONE WORD?
JGE ORD18$ ;NO, CONTINUE LOOP
SUB SI,SI ;YES, WORD NOT FOUND, RETURN ZERO
ORD22$: POP DI ;RESTORE WORD ENTRY POINTER
MOV DX,SI ;POINTER TO WORD FOUND
XCHG DH,DL
MOV ES:[DI],DX ;(6) STORE IT
POP DX ;RESTORE CHAR POINTER
ADD DI,4 ;UPDATE POINTER FOR NEXT WORD ENTRY
JMP ORD8$ ;GO FOR IT
ORD23$: INC RDRET ;DONE, STORE NUMBER OF WORDS FOUND
MOV BP,RDRET
MOVM ES:[BP],RDNWDS,DL ; REMOVED ES OVERRIDE
POP DI ;RESTORE USER STACK POINTER
RET ;AND RETURN
OPREAD ENDP
;PRINTC (PRINT CHAR WHOSE ASCII VALUE IS GIVEN)
PUBLIC OPPRNC,OPPRNN
OPPRNC PROC
JMP PUTCHR ;QUEUE THE CHARACTER FOR PRINTING
OPPRNC ENDP
;PRINTN (PRINT A NUMBER)
OPPRNN PROC
MOV BX,AX ;NUMBER TO PRINT
CMP BX,0
JNE OPN1$ ;NON-ZERO
MOV AX,"0" ;SPECIAL CASE ZERO
JMP PUTCHR
OPN1$: JG OPN2$ ;POSITIVE?
MOV AX,"-" ;NO, PRINT MINUS SIGN
CALL PUTCHR
NEG BX ;AND MAKE IT POSITIVE
OPN2$: SUB CX,CX ;COUNT OF DIGITS ON STACK
JMP OPN4$ ;START WITH GREATER-THAN-10 TEST
OPN3$: MOV AX,BX ;EXTRACT A DIGIT
MOV BP,10
CWD
IDIV BP
PUSH DX ;PUSH IT
INC CX ;BUMP COUNT
MOV BX,AX ;GET QUOTIENT
OPN4$: CMP BX,10 ;MORE DIGITS TO EXTRACT?
JGE OPN3$ ;YES, GO LOOP
MOV AX,BX ;NO, GET LAST (FIRST) DIGIT
JMP OPN6$ ;ALREADY IN PLACE
OPN5$: POP AX ;POP NEXT DIGIT
OPN6$: ADD AX,"0" ;ASCIIZE IT
CALL PUTCHR ;QUEUE IT
DEC CX ;REDUCE DIGIT COUNT
JGE OPN5$ ;LOOP IF SOME LEFT
RET ;ELSE, RETURN
OPPRNN ENDP
PUBLIC OPPRIN,OPPRNB
;PRINT (THE STRING POINTED TO BY ES:AX)
OPPRIN PROC
CALL BSPLIT ;SPLIT THE BLOCK & WORD NUMBERS
JMP PUTSTR ;PRINT THE STRING
OPPRIN ENDP
;PRINTB (PRINT THE STRING POINTED TO BY THE BYTE-POINTER ES:AX)
OPPRNB PROC
CALL BSPLTB ;SPLIT THE BLOCK & BYTE NUMBERS
JMP PUTSTR ;PRINT THE STRING
OPPRNB ENDP
;PSEUDO-INSTRUCTIONS FOR HOURS/MINUTES HERE FOR STATUS LINE
OPPRNH PROC
MOV PMFLAG,0
CMP AL,12
JL OPH0$
MOV PMFLAG,1
OPH0$: CMP AL,12
JLE OPH1$
SUB AL,12 ;HOUR SLOT IS 24 HOUR TIME
OPH1$: CMP AL,0
JNE OPHZ$
MOV AL,12
OPHZ$: CMP AL,9
JG OPH2$
PUSH AX
MOV AL,32
CALL PUTCHR ;OUTPUT SPACE FOR HOUR LESS THAN 10
POP AX
OPH2$: CALL OPPRNN
MOV AL,":"
JMP PUTCHR ;AND COLON
OPPRNH ENDP
OPPRNM PROC
CMP AL,9
JG OPM1$
PUSH AX
MOV AL,"0"
CALL PUTCHR
POP AX
OPM1$: CALL OPPRNN
MOV AL,32
CALL PUTCHR
MOV AL,"a"
CMP PMFLAG,0
JE OPM2$
MOV AL,"p"
OPM2$: CALL PUTCHR
MOV AL,"m"
JMP PUTCHR
OPPRNM ENDP
PUBLIC OPPRND,OPPRNI,OPPRNR
;PRINTD (PRINT OBJ'S SHORT DESCRIPTION)
OPPRND PROC
CALL OBJLOC ;FIND OBJ'S LOCATION
ADD AX,7 ;PROPERTY TABLE POINTER
MOV BP,AX
GTAWRD A,[BP] ;GET IT
INC AX ;POINT TO STRING
CALL BSPLTB ;SPLIT POINTER
JMP PUTSTR ;AND PRINT THE STRING
OPPRND ENDP
;PRINTI (PRINT THE STRING FOLLOWING THIS INSTRUCTION)
OPPRNI PROC
MOV AX,ZPC1 ;GET POINTER TO STRING
MOV BX,ZPC2
CALL PUTSTR ;AND PRINT IT
MOV ZPC1,AX ;UPDATE ZPC
MOV ZPC2,BX
JMP NEWZPC
OPPRNI ENDP
;PRINTR (PRINTI FOLLOWED BY RTRUE)
OPPRNR PROC
CALL OPPRNI ;DO A PRINTI
CALL OPCRLF ;A CRLF
JMP OPRTRU ;AND AN RTRUE
OPPRNR ENDP
PUBLIC OPCRLF
;CRLF (DO A NEWLINE)
OPCRLF PROC
JMP NEWLIN ;DO A NEWLINE
OPCRLF ENDP
SUBTTL CONTROL OPERATIONS
PAGE +
PUBLIC OPCALL
;CALL (A FUNCTION WITH OPTIONAL ARGUMENTS), # OF ARGS IN AX
OPCALL PROC
NOP ;TELL CALLER TO USE ARGUMENT BLOCK
MOV DX,AX ;NUMBER OF ARGUMENTS TO CALL
MOV AX,ARGBLK ;FUNCTION TO CALL
CMP AX,0
JNE OCL1$ ;ZERO?
SUB AX,AX ;YES, SIMPLY RETURN A ZERO
JMP PUTVAL
OCL1$: PUSHZ ZPC1 ;OTHERWISE, SAVE OLD ZPC
PUSHZ ZPC2
MOV CX,ZLOCS ;AND OLD LOCAL POINTER
; SUB CX,STKBOT ;BUT RELATIVIZE IT IN CASE OF SAVE
PUSHZ CX ;AND SAVE IT
CALL BSPLIT ;SPLIT FUNCTION POINTER
MOV ZPC1,AX ;MAKE IT THE NEW ZPC
MOV ZPC2,BX
CALL NEWZPC ;UPDATE ZPC STUFF
MOV ZLOCS,DI ;LOCALS WILL START AT NEXT STACK SLOT
SUB ZLOCS,2
CALL NXTBYT ;NUMBER OF LOCALS
MOV BX,AX
MOV BP,OFFSET ARGBLK[2] ;POINT TO FIRST OPTIONAL ARG
OCL2$: DEC BX ;ANY MORE LOCALS?
JL OCL4$ ;NO, WE'RE DONE
CALL NXTWRD ;YES, GET THE NEXT LOCAL DEFAULT VALUE
DEC DX ;ANY MORE OPTIONALS GIVEN?
JLE OCL3$ ;NO
PUSHZ [BP] ;(6) YES, USE ITS VALUE
ADD BP,2
JMP OCL2$ ;AND CONTINUE LOOP
OCL3$: PUSHZ AX ;OTHERWISE, USE DEFAULT
JMP OCL2$ ;AND LOOP
OCL4$: RET
OPCALL ENDP
PUBLIC OPRETU
;RETURN (FROM CURRENT FUNCTION CALL)
OPRETU PROC
MOV DI,ZLOCS ;RESTORE OLD TOP OF STACK
POPZ DX ;DUMMY POP [WHY?]
POPZ ZLOCS ;AND OTHER VALUES
; MOV DX,STKBOT ;RE-ABSOLUTIZE THIS ONE
; ADD ZLOCS,DX
POPZ ZPC2
POPZ ZPC1
PUSH AX ;VALUE TO RETURN
CALL NEWZPC ;UPDATE ZPC STUFF
POP AX
JMP PUTVAL ;RETURN THE VALUE
OPRETU ENDP
PUBLIC OPRTRU,OPRFAL,OPJUMP,OPRSTA,OPFSTA,OPNOOP
;RTRUE
OPRTRU PROC
MOV AX,1 ;RETURN A 1
JMP OPRETU
OPRTRU ENDP
;RFALSE
OPRFAL PROC
SUB AX,AX ;RETURN A 0
JMP OPRETU
OPRFAL ENDP
;JUMP (TO A NEW LOCATION)
OPJUMP PROC
ADD ZPC2,AX ;ADD OFFSET TO CURRENT ZPC
SUB ZPC2,2 ;ADJUST IT
JMP NEWZPC ;NORMALIZE IT & UPDATE ZPC STUFF
OPJUMP ENDP
;RSTACK (RETURN STACK)
OPRSTA PROC
POPZ AX ;POP A VALUE
JMP OPRETU ;AND RETURN IT
OPRSTA ENDP
;FSTACK (FLUSH A VALUE OFF THE STACK)
OPFSTA PROC
POPZ DX ;FLUSH ONE
RET
OPFSTA ENDP
;NOOP (NO OPERATION)
OPNOOP PROC
RET ;DO NOTHING
OPNOOP ENDP
SUBTTL LOW LEVEL FUNCTIONS
PAGE +
;GET A BYTE, BLOCK-POINTER ES:AX, BYTE-POINTER ES:BX, RESULT IN CX,
;UPDATE AX & BX TO REFLECT BYTE GOTTEN
;
; GETBYT UNDERWENT SERIOUS RE-WRITING AND EVALUATION IN THE UPDATE TO USE
; ALL OF MEMORY. THE ORIGINAL ROUTINE WAS KEPT BUT CHANGES WERE MADE IN
; TWO SETS: THE FIRST TO HANDLE THE CASE IN WHICH ALL OF THE GAME IS IN
; MEMORY; AND THE SECOND IN WHICH THE GAME BUFFERS EXTEND PAST 64K AND THUS
; REQUIRING A SEGMENT SWITCH TO RETRIEVE THE BYTE.
;
; FOR THE FIRST CASE, THE BLOCK PTR IS USED TO DETERMINE THE SEGMENT IN
; WHICH THE BLOCK RESIDES, THE GAME SEGMENT IS SET ACCORDINGLY, AND THE
; BYTE IS FETCHED.
;
; THE SECOND CASE WILL CALL GETPAG TO DETERMINE WHERE THE PAGE IS LOCATED IN
; CORE. CALLING GETPAG WILL SET A VARIABLE CALLED CURSEG TO 0 OR 1 TO INDICATE
; THAT THE PAGE WE ARE TOUCHING IS IN THE FIRST OR SECOND SEGMENT. GETPAG
; RETURNS A VALID POINTER ASSUMING THAT THIS ROUTINE WILL SET ES ACCORDING TO
; CURSEG. IF CURSEG IS 0, GAMESEG (THE FIRST SEGMENT [OR ZEROTH]) WILL
; CONTAIN THE BLOCK, AND IF CURSEG IS 1, SEG1 (GAMESEG + 64K) WILL CONTAIN
; THE BLOCK.
;
; NOTE THAT ES ALWAYS CONTAINS THE VALUE GAMESEG UNLESS WE NEED TO FETCH
; OR PUT SOMETHING IN THE SECOND SEGMENT. AT THAT POINT ES IS SET TO SEG1
; ONLY FOR THAT OPERATION.
;
PUBLIC GETBYT,FETCH,GTY1A$,GTSEG1,GTY1$,GTY1B$,GTY2$,GTY3$
GETBYT PROC
PUSH SI ; SAVE THIS BEAR
PUSH AX ;SAVE BLOCK-POINTER
CMP FITS,1 ; (6) IS ALL OF THE GAME RESIDENT
JZ FETCH ; (6) IT'S THERE, GO GET IT
CMP AX,ENDLOD ;IS THIS A PRELOADED LOCATION?
JGE GTY1$ ;NO
FETCH: CMP AX,128 ; (6) CHECK SEGMENT FOR BLOCK
JGE GTSEG1 ; (6) GET BYTE FROM SEGMENT 1
GTY1A$: MOV CL,9 ;YES, RECONSTRUCT POINTER
SHL AX,CL ;SHIFT BLOCK POINTER BY 9
OR AX,BX ;ADD IN THE OFFSET
XCHG AX,BX
MOV CL,ES:[BX] ;GET THE BYTE
JMP GTY2$ ;CLEAR UNWANTED BYTE & RETURN IT
GTSEG1: MOV SI,SEG1 ; (6) GET SECOND GAME SEG
MOV ES,SI ; (6) ADDRESS SEG1
JMP GTY1A$ ; (6) USE SEG0 LOGIC
GTY1$: CALL GETPAG ;FIND THE PROPER PAGE
ADD AX,BX ;POINT TO DESIRED BYTE
XCHG AX,BX
CMP CURSEG,1 ;(6) CHECK SEGMENT THAT WERE LOOKING IN
JNZ GTY1B$ ;(6) ITS IN 0, GET IT NORMALLY
MOV SI,SEG1 ;(6) ITS NOT, SET UP ES TO SEG1
MOV ES,SI ;(6) GETPAG RETURNED RIGHT POINTER
GTY1B$: MOV CL,ES:[BX] ;GET IT
GTY2$: SUB CH,CH ;CLEAR UNWANTED BYTE & RETURN IT
MOV BX,AX
MOV SI,GAMESEG ; (6) MAKE SURE GAME SEG IS RESTORED
MOV ES,SI ; (6) TO SEG 0
POP AX ;RESTORE BLOCK-POINTER
POP SI ; RESTORE THIS
INC BX ;UPDATE POINTER
CMP BX,200H ;END-OF-PAGE?
JNE GTY3$ ;NO
SUB BX,BX ;YES, CLEAR BYTE-POINTER
INC AX ;AND UPDATE BLOCK-POINTER
GTY3$: RET
GETBYT ENDP
;GET A WORD, BLOCK POINTER ES:AX, BYTE-POINTER ES:BX, RESULT IN CX
PUBLIC GETWRD
GETWRD PROC
PUSH DX ;SAVE
CALL GETBYT ;GET HIGH-ORDER BYTE
PUSH CX ;SAVE IT
CALL GETBYT ;GET LOW-ORDER BYTE
POP DX ;GET OTHER BYTE
MOV CH,DL ;POSITION IT
POP DX ;RESTORE
RET
GETWRD ENDP
;GET THE NEXT BYTE, RETURN IT IN AX
; THIS ROUTINE ALSO WENT UNDER SIGNIFICANT REVISION TO ACCOMODATE MORE
; PAGING SPACE. TWO CASES HAD TO BE HANDLED: ALL OF GAME IN MEMORY AND
; MORE THAN 64K OF PAGING SPACE. THE FIRST CASE REQUIRED THAT THE VARIABLE
; ZPCSEG BE SET IN NEWZPC TO INDICATE IN WHICH SEGMENT OUR ZPC BLOCK COULD
; BE FOUND. THE SECOND CASE REQUIRES THAT ZPCSEG BE SET ONLY WHEN GETPAG
; IS CALLED FROM NEWZPC AND THE PAGE THAT IT GOT WAS PUT IN THE SECOND GAME
; SEGMENT.
;
PUBLIC NXTBYT,NXBA$,NXB1$
NXTBYT PROC
PUSH SI ;(6) PRESERVE THIS ONE TOO
PUSH BX ;SAVE
TEST ZPCFLG,1 ;(7n) HAS THIS PAGE SEEN FOUL PLAY?
JZ NXB0$ ; (7n) NOPE
CALL NEWZPC ; (7n) FIX INDEX INTO CURRENT PAGE
NXB0$: MOV BX,ZPC2 ;BYTE POINTER
ADD BX,CURPAG ;INDEX INTO CURRENT PAGE
CMP ZPCSEG,0 ;(6) WHERE ARE WE?
JZ NXBA$ ;(6) SEG0, ACT NORMALLY
MOV SI,SEG1 ;(6) GET SEG1 VALUE
MOV ES,SI ;(6) INTO ES
NXBA$: MOV AL,BYTE PTR ES:[BX] ; GET BYTE
PUSH AX ; AND SAVE
;PUSH ES:[BX] ;SAVE BYTE -- AT KILLER CODE ON FFFF
MOV SI,GAMESEG ;(6) GET BASE GAME SEG
MOV ES,SI ;(6) BACK INTO ES
INC ZPC2 ;UPDATE PC
CMP ZPC2,200H ;END-OF-PAGE?
JL NXB1$ ;NO
CALL NEWZPC ;YES, UPDATE PAGE
NXB1$: POP AX ;RETRIEVE BYTE
SUB AH,AH ;CLEAR UNWANTED BYTE
POP BX ;RESTORE
POP SI
RET ;AND RETURN IT
NXTBYT ENDP
;GET THE NEXT WORD, RETURN IT IN AX
PUBLIC NXTWRD
NXTWRD PROC
PUSH BX ;SAVE
CALL NXTBYT ;GET HIGH-ORDER BYTE
PUSH AX ;SAVE IT
CALL NXTBYT ;GET LOW-ORDER BYTE
POP BX ;GET HIGH-ORDER BYTE
MOV AH,BL ;POSITION IT
POP BX ;RESTORE
RET ;RETURN THE WORD
NXTWRD ENDP
;GET AN ARGUMENT GIVEN ITS TYPE IN AX
PUBLIC GETARG
GETARG PROC
DEC AX ;EXAMINE ARGUMENT
JL NXTWRD ;0 MEANT LONG IMMEDIATE
JE NXTBYT ;1 MEANT SHORT IMMEDIATE
CALL NXTBYT ;2 MEANT VARIABLE, GET THE VAR
CMP AX,0 ;STACK?
JNE GETVAR ;NO, JUST GET THE VAR'S VALUE
POPZ AX ;YES, POP THE STACK
RET
GETARG ENDP
;GET VALUE OF A VARIABLE, VAR IN AX, VALUE RETURNED IN AX
PUBLIC GETVAR
GETVAR PROC
CMP AX,0 ;STACK?
JNE GTV1$ ;NO
POPZT AX ;YES, GET TOP-OF-STACK
RET
GTV1$: PUSH BP ;SAVE
CMP AX,16 ;LOCAL?
JGE GTV3$ ;NO
DEC AX ;YES, POINT TO PROPER STACK ELEMENT
SHL AX,1
MOV BP,ZLOCS
SUB BP,AX
MOV AX,[BP] ;AND GET IT
GTV2$: POP BP ;RESTORE
RET
GTV3$: SUB AX,16 ;GLOBAL, POINT TO PROPER GLOBAL TABLE ELEMENT
SHL AX,1
ADD AX,GLOTAB
MOV BP,AX ;AND GET IT
GTAWRD A,[BP]
JMP GTV2$
GETVAR ENDP
;UPDATE VALUE OF A VARIABLE, VAR IN AX, NEW VALUE IN BX
PUBLIC PUTVAR
PUTVAR PROC
CMP AX,0 ;STACK?
JNE PTV1$ ;NO
PUSHZT BX ;YES, UPDATE TOP-OF-STACK
RET
PTV1$: CMP AX,16 ;LOCAL?
JGE PTV2$ ;NO
PUSH BP ;SAVE
DEC AX ;YES, POINT TO PROPER STACK ELEMENT
SHL AX,1
MOV BP,ZLOCS
SUB BP,AX
MOV [BP],BX ;AND UPDATE IT
POP BP ;RESTORE
RET
PTV2$: SUB AX,16 ;GLOBAL, POINT TO PROPER GLOBAL TABLE ELEMENT
SHL AX,1
ADD AX,GLOTAB
XCHG AX,BX ;AND UPDATE IT
PTAWRD [BX],A
RET
PUTVAR ENDP
;RETURN VAL IN AX TO LOCATION SPECIFIED BY NEXTBYTE
;DESTROYS BX, BUT IS USUALLY CALLED AT END OF TOP-LEVEL FUNCTION
PUBLIC BYTVAL
BYTVAL PROC
SUB AH,AH ;THIS ENTRY FOR BYTE VALUE TO CLEAR HIGH BYTE
JMP PUTVAL
BYTVAL ENDP
PUBLIC PUTVAL
PUTVAL PROC
MOV BX,AX ;NORMAL ENTRY
CALL NXTBYT ;GET VAR TO USE
CMP AX,0 ;STACK?
JNE PUTVAR ;NO, GO STORE VALUE
PUSHZ BX ;YES, PUSH ONTO STACK
RET ;AND RETURN
PUTVAL ENDP
;PREDICATE HANDLERS TRUE & FALSE
;DESTROYS REGISTERS, BUT ARE ONLY CALLED FROM END OF TOP-LEVEL FCNS
PUBLIC PFALSE,PTRUE
PFALSE PROC
SUB BX,BX ;PREDICATE WAS FALSE, CLEAR FLAG
JMP PPRED
PFALSE ENDP
PTRUE PROC
MOV BX,1 ;PREDICATE WAS TRUE, SET FLAG
JMP PPRED
PTRUE ENDP
PUBLIC PPRED
PPRED PROC
CALL NXTBYT ;GET FIRST (OR ONLY) PREDICATE JUMP BYTE
TEST AX,80H ;NORMAL POLARITY PREDICATE?
JE PPR1$ ;NO, LEAVE FLAG ALONE
INC BX ;YES, INCREMENT FLAG
PPR1$: TEST AX,40H ;ONE-BYTE JUMP OFFSET?
JE PPR2$ ;NO
AND AX,0FF3FH ;YES, CLEAR SPECIAL BITS
JMP PPR3$
PPR2$: AND AX,0FF3FH ;CLR SPECIAL BITS FROM HIGH-ORDER OFFSET BYTE
MOV CX,AX ;HIGH-ORDER BYTE
CALL NXTBYT ;GET LOW-ORDER BYTE
MOV AH,CL ;MOVE IN HIGH-ORDER BITS
TEST AX,2000H ;IS NUMBER NEGATIVE (14-BIT 2'S COMP NUMBER)?
JE PPR3$ ;NO
OR AX,0C000H ;YES, MAKE 16-BIT NUMBER NEGATIVE
PPR3$: DEC BX ;TEST FLAG
JE PPR6$ ;WAS 1, THAT MEANS DO NOTHING
CMP AX,0 ;ZERO JUMP?
JNE PPR4$ ;NO
JMP OPRFAL ;YES, THAT MEANS DO AN RFALSE
PPR4$: DEC AX ;ONE JUMP?
JNE PPR5$ ;NO
JMP OPRTRU ;YES, THAT MEANS DO AN RTRUE
PPR5$: DEC AX ;ADJUST OFFSET
ADD ZPC2,AX ;ADD TO PC
JMP NEWZPC ;AND UPDATE ZPC STUFF
PPR6$: RET
PPRED ENDP
;SPLIT BYTE-POINTER IN AX TO BLOCK-POINTER IN AX & BYTE OFFSET IN BX
PUBLIC BSPLTB
BSPLTB PROC
MOV BX,AX
XCHG AL,AH ;EXTRACT BLOCK BITS
SHR AX,1
AND AX,7FH ;CLEAR UNWANTED BITS
AND BX,1FFH ;CLEAR ALL BUT BYTE OFFSET BITS
RET
BSPLTB ENDP
;SPLIT WORD-POINTER IN AX TO BLOCK-POINTER IN AX & BYTE-OFFSET IN BX
PUBLIC BSPLIT
BSPLIT PROC
MOV BX,AX
MOV AL,AH ;EXTRACT BLOCK BITS
SUB AH,AH ;CLEAR UNWANTED BITS
SUB BH,BH ;CLEAR ALL BUT WORD OFFSET BITS
SHL BX,1 ;CONVERT TO BYTE OFFSET
RET
BSPLIT ENDP
SUBTTL OBJECT HACKERS
PAGE +
PUBLIC OBJLOC,NXTPRP
;GIVEN OBJ NUMBER IN AX, RETURN OBJ LOCATION IN AX
OBJLOC PROC
SUB AH,AH ;CLEAR UNWANTED BITS
PUSH BX ;MULTIPLY BY 9 THE LAZY WAY
MOV BX,AX
SHL AX,1
SHL AX,1
SHL AX,1
ADD AX,BX
POP BX ;RESTORE
ADD AX,OBJTAB ;INDEX INTO OBJECT TABLE
ADD AX,53 ;SKIPPING DEFAULT PROPERTY TABLE
RET
OBJLOC ENDP
;GIVEN POINTER TO A PROPERTY IN BX!, UPDATE IT TO POINT TO NEXT PROP
NXTPRP PROC
PUSH AX ;SAVE
PUSH CX ;SAVE
MOV AL,ES:[BX] ; GET PROPERTY IDENTIFIER
AND AL,MASK PROPSIZE;EXTRACT PROPERTY LENGTH (MINUS 1)
MOV CL,PROPSIZE
SHR AL,CL
SUB AH,AH
ADD BX,AX ;ADD IT TO OLD POINTER
ADD BX,2 ;ADJUST FOR EXTRA LENGTH BYTE PLUS IDENTIFIER
POP CX ;RESTORE
POP AX ;RESTORE
RET
NXTPRP ENDP
SUBTTL STRING FUNCTIONS
PAGE +
;OUTPUT A ZSTR, BLOCK-POINTER IN AX, BYTE-POINTER IN BX
;RETURN UPDATED POINTER
PUBLIC PUTSTR
PUTSTR PROC
PUSH SI ;SAVE
PUSH CX ;SAVE
PUSH DX ;SAVE
PUSH DI ;SAVE
PUSH BP ;SAVE
SUB DX,DX ;TEMP CS STARTS AT 0
SUB DI,DI ;PERM CS STARTS AT 0
PTS1$: CALL GETWRD ;GET NEXT STRING WORD
MOV SI,CX
PUSH AX ;SAVE POINTER & COPY OF STRING WORD
PUSH BX
PUSH SI
MOV CX,3 ;3 BYTES IN WORD
PTS2$: PUSH SI ;SAVE CURRENT BYTE (IN LOW-ORDER POSITION)
MOV BP,CX ;SHIFT TO NEXT BYTE
MOV CL,5
SAR SI,CL
MOV CX,BP
LOOP PTS2$ ;LOOP UNTIL DONE
MOV CX,3 ;RETRIEVE THE 3 BYTES
PTS3$: POP SI ;GET NEXT BYTE
AND SI,1FH ;CLEAR UNWANTED BITS
CMP DX,0 ;IN WORD MODE?
JGE PTS4$ ;NO {WAS BPL, CHECK}
SAL SI,1 ;YES, CALCULATE WORD OFFSET
ADD SI,WRDTAB ;POINT INTO WORD TABLE
ADD SI,WRDOFF ;USING PROPER 32-WORD BLOCK
GTAWRD A,[SI] ;POINT TO WORD STRING
CALL BSPLIT ;SPLIT IT
CALL PUTSTR ;AND PRINT IT
JMP PTS15$ ;CONT. WHERE WE LEFT OFF WITH TEMP CS RESET
PTS4$: CMP DX,3 ;CS 3 SELECTED (ASCII MODE)?
JL PTS6$ ;NO, NORMAL CS
JNE PTS5$ ;NO, BUT WE ARE IN ASCII MODE
XCHG DL,DH ;SHIFT SOME BITS HIGH TO MAKE NUMBER LARGE
OR DX,SI ;SAVE HIGH-ORDER ASCII BITS HERE
JMP PTS16$ ;GO GET NEXT BYTE
PTS5$: AND DX,3 ;EXTRACT PREVIOUSLY SAVED HIGH-ORDER BITS
MOV BP,CX ;POSITION THEM
MOV CL,5
SAL DX,CL
MOV CX,BP
OR DX,SI ;OR IN LOW-ORDER BITS
MOV AX,DX
JMP PTS14$ ;GO PRINT THE CHARACTER
PTS6$: CMP SI,6 ;SPECIAL CODE?
JL PTS9$ ;YES, SPACE, WORD, OR SHIFT
CMP DX,2 ;MIGHT ALSO BE SPECIAL IF IN CS 2
JNE PTS8$ ;BUT WE'RE NOT
CMP SI,7 ;CRLF?
JE PTS7$ ;YES
JG PTS8$ ;NO, NOT ASCII MODE, EITHER?
INC DX ;YES IT IS, SWITCH TO ASCII MODE
JMP PTS16$ ;AND GO GET NEXT BYTE
PTS7$: CALL NEWLIN ;CRLF REQUESTED, DO A NEWLINE
JMP PTS15$
PTS8$: MOV AX,DX ;NORMAL CHARACTER, GET CS
MOV BP,26 ;CALCULATE OFFSET FOR THIS CS
MUL BP
ADD AX,SI ;ADD IN CHARACTER OFFSET (+6)
SUB AX,6 ;CHARACTER OFFSET
MOV BX,OFFSET ZCHRS ;GET THE CHARACTER FROM CONVERSION VECTOR
XLAT ZCHRS
JMP PTS14$ ;GO PRINT IT
PTS9$: CMP SI,0 ;IS IT A SPACE?
JNE PTS10$ ;NO
MOV AX," " ;YES, GO PRINT A SPACE
JMP PTS14$
PTS10$: CMP SI,3 ;IS IT A WORD?
JG PTS11$ ;NO, MUST BE A SHIFT
OR DX,8000H ;SWITCH TO WORD MODE FOR NEXT BYTE
DEC SI ;CALCULATE WORD-TABLE BLOCK OFFSET
MOV BP,CX ;64 BYTES IN A BLOCK
MOV CL,6
SHL SI,CL
MOV CX,BP
MOV WRDOFF,SI ;SAVE IT AND LOOP
JMP PTS16$
PTS11$: SUB SI,3 ;CALCULATE NEW CS
CMP DX,0 ;TEMPORARY SHIFT (FROM CS 0)?
JNE PTS12$ ;NO
MOV DX,SI ;YES, JUST SAVE NEW TEMP CS
JMP PTS16$
PTS12$: CMP SI,DX ;IS THIS THE CURRENT CS?
JE PTS13$ ;YES, DO A PERM SHIFT TO IT
SUB DX,DX ;OTHERWISE, PERM SHIFT TO CS 0
PTS13$: MOV DI,DX ;TEMP & PERM CS'S ARE SAME NOW
JMP PTS16$
PTS3A$: JMP PTS3$ ;DUMMY FOR NON-SHORT LOOP
PTS14$: CALL PUTCHR ;PRINT THE CHARACTER
PTS15$: MOV DX,DI ;RESET TEMP CS TO PERM CS
PTS16$: LOOP PTS3A$ ;NEXT BYTE
POP SI ;RESTORE POINTERS & ORIGINAL STRING WORD
POP BX
POP AX
CMP SI,0 ;END-OF-STRING?
JL PTS1A$ ;YES, CLEAN UP & RETURN UPDATED POINTER
JMP PTS1$ ;NO, GET NEXT WORD
PTS1A$: POP BP ;RESTORES
POP DI
POP DX
POP CX
POP SI
RET
PUTSTR ENDP
;GIVEN AN ASCII CHARACTER IN AX, RETURN THE CHARACTER SET # IN AX
PUBLIC CHRCS
CHRCS PROC
CMP AX,0 ;IS THIS A NULL?
JNE CCS1$ ;NO
MOV AX,3 ;YES, RETURN DUMMY CS NUMBER
RET
CCS1$: PUSH BX ;SAVE
MOV BX,OFFSET ZCHRS ;POINT TO CONVERSION VECTOR
CCS2$: INC BX ;FOUND THE CHARACTER?
CMP AL,[BX-1]
JE CCS3$ ;YES
CMP BYTE PTR [BX],0 ;NO, END OF STRING?
JNE CCS2$ ;NO, CONTINUE LOOP
MOV AX,2 ;YES, CALL IT CS 2
JMP CCS5$
CCS3$: SUB BX,OFFSET ZCHRS ;FIND CHARACTER POSITION
SUB AX,AX ;START WITH CS 0
CCS4$: SUB BX,26 ;EVERY 26 CHARACTERS IS A NEW CS
JLE CCS5$ ;DONE
INC AX ;INCREMENT CS # & CONTINUE LOOP
JMP CCS4$
CCS5$: POP BX
RET
CHRCS ENDP
;GIVEN AN ASCII CHARACTER IN AX, RETURN ZSTR BYTE VALUE IN AX
PUBLIC CHRBYT
CHRBYT PROC
PUSH BX ;SAVE
MOV BX,OFFSET ZCHRS ;POINT TO CHARACTER CONVERSION TABLE
CHB1$: INC BX ;FOUND THE CHARACTER?
CMP AL,[BX-1]
JE CHB2$ ;YES
CMP BYTE PTR [BX],0 ;NO, END OF STRING?
JNE CHB1$ ;NO, CONTINUE LOOP
SUB AX,AX ;YES, RETURN ZERO FOR FAILURE
JMP CHB4$
CHB2$: SUB BX,OFFSET ZCHRS-5 ;ADJUST POINTER SO FIRST CHARACTER IS 6
MOV AX,BX
CHB3$: CMP AX,32 ;SUBTRACT MULTIPLES OF 26 UNTIL BASE CODE
JL CHB4$
SUB AX,26
JMP CHB3$
CHB4$: POP BX ;RESTORE
RET
CHRBYT ENDP
;CONVERT UP TO 6 ASCIZ CHARS POINTED TO BY DS:AX
;TO A 2-WORD ZSTR RETURNED IN AX & BX
PUBLIC ZWORD
ZWORD PROC
PUSH SI ;SAVES
PUSH CX
PUSH DX
PUSH DI
PUSH BP
MOV SI,AX ;CHARACTER STRING POINTER
SUB DI,DI ;CS STARTS AT 0
MOV CX,6 ;MAKE 6 ZSTR BYTES
ZWD1$: INC SI ;GET NEXT CHARACTER
MOV BL,[SI-1]
CMP BL,0
JNE ZWD3$ ;NOT END-OF-STRING
MOV AX,OFFSET PADCHR;AT END-OF-STRING, PAD WITH PAD CHARACTER
ZWD2$: PUSH AX ;SAVE A PAD BYTE
LOOP ZWD2$ ;LOOP UNTIL DONE
JMP ZWD6$ ;THEN GO FORM ZWORD
ZWD3$: MOV AX,BX
CALL CHRCS ;FIND THE CS NUMBER FOR THIS CHAR
CMP AX,0 ;CS 0?
JE ZWD4$ ;YES
ADD AX,3 ;NO, CALCULATE TEMP SHIFT BYTE
PUSH AX ;SAVE THE SHIFT BYTE
DEC CX ;REDUCE BYTE COUNT
JE ZWD6$ ;DONE
ZWD4$: MOV AX,BX ;FIND THE PROPER BYTE VALUE FOR THIS CHAR
CALL CHRBYT
CMP AX,0 ;IN NORMAL CS'S?
JNE ZWD5$ ;YES
MOV AX,6 ;NO, USE ASCII SHIFT
PUSH AX
DEC CX ;DONE YET?
JE ZWD6$ ;YES
MOV AX,BX ;NO, SAVE HIGH-ORDER ASCII BITS
MOV BP,CX
MOV CL,5
SAR AX,CL
MOV CX,BP
PUSH AX
DEC CX ;DONE YET?
JE ZWD6$ ;YES
AND BX,1FH ;NO, SAVE LOW-ORDER ASCII BITS
MOV AX,BX
ZWD5$: PUSH AX ;SAVE THIS BYTE
LOOP ZWD1$ ;LOOP UNTIL ZWORD FULL
ZWD6$: MOV BP,SP ;BUILD ZWORD WORDS FROM 6 SAVED BYTES
MOV AX,[BP+10]
MOV CL,5
SHL AX,CL
OR AX,[BP+8]
SHL AX,CL
OR AX,[BP+6]
MOV BX,[BP+4]
SHL BX,CL
OR BX,[BP+2]
SHL BX,CL
OR BX,[BP]
OR BX,8000H ;SET END-OF-STRING BIT IN SECOND WORD
ADD SP,12 ;FLUSH STACK
POP BP ;RESTORES
POP DI
POP DX
POP CX
POP SI
RET
ZWORD ENDP
SUBTTL TERMINAL I/O
PAGE +
;QUEUE CHARACTER IN AX FOR OUTPUT
PUBLIC PUTCHR
PUTCHR PROC
PUSH BP
MOV BP,CHRPTR
CMP BP,ENDBUF
JNE PTC7
PUSH AX
PUSH BX
PUSH SI
MOV BX,ENDBUF
MOV SI,OFFSET OUTBUF
PTC1: DEC BX
CMP BYTE PTR [BX]," "
JE PTC3
CMP BX,SI
JNE PTC1
PTC2: PRINT OUTBUF
MOV CHRPTR,SI
MOV BP,SP
CMP BYTE PTR 4 [BP]," "
JNE PTC6
MOV WORD PTR 4 [BP],0
JMP PTC6
PTC3: CMP BX,SI
JE PTC2
MOV BYTE PTR [BX],0
PRINT OUTBUF
MOV AX,ENDBUF
INC BX
PTC4: CMP BX,AX
JE PTC5
MOV BP,AX
MOV AL,[BX]
MOV [SI],AL
MOV AX,BP
INC BX
INC SI
JMP PTC4
PTC5: MOV CHRPTR,SI
PTC6: POP SI
POP BX
POP AX
CMP AX,0
JE PTC8
PTC7: MOV BP,CHRPTR
MOV [BP],AL ; (6) REMOVED DS OVERRIDE
INC CHRPTR
PTC8: POP BP
RET
PUTCHR ENDP
;GO TO NEW LINE, OUTPUTTING CURRENT BUFFER
NEWLIN PROC
PUSH BX ;SAVE
MOV BX,CHRPTR ;END LINE AT CURRENT POINT
MOV BYTE PTR [BX],0
MOV CHRPTR,OFFSET OUTBUF ;RESET CHARACTER POINTER
POP BX ;RESTORE
PRINT OUTBUF ;AND OUTPUT LINE
RET
NEWLIN ENDP
SUBTTL TOP LEVEL STUFF
PAGE +
; READ THE SETUP FILE
; SSETUP HAS UNDER GONE TO SETS OF REVISIONS. REVISION 6 IS RESPONSIBLE
; FOR ALL OF THE DOS 2.0 FILE HANDLING PATCHES. REVISION 7 IS RESPONSIBLE
; FOR SETTING UP COLOR OR MONOCHROME BASED ON THE VIDEO SETTINGS AND WHAT
; WAS SPECIFIED ON THE COMMAND LINE.
;
PUBLIC SSETUP
SSETUP PROC NEAR
MOV DX,OFFSET SSFILE ; (6) USE ASCIZ FILENAME
MOV AH,CFOPENZ ; TRY OPENING THE SETUP FILE
MOV AL,0 ; (6) OPEN FILE FOR READING
INT 21H
JC SSET1 ; IF OPEN FAIL, COMPLAIN
MOV BX,AX ; (6) PUT HANDLE IN BX
MOV AH,CRDRNDZ ; (6) READ INTO THE BUFFER
MOV DX,OFFSET SSBUF ; (6) BUFFER TO READ INTO
MOV CX,SSLNT ; (6) READ (CX) BYTES
INT 21H
CMP AL,SSLNT ; (6) DID WE GET ALL THE BYTES
JNE SSET2 ; IF READ FAIL, COMPLAIN
PUSH BX ; (6) SAVE THE HANDLE
MOV BX,DX ; (6) GET OFFSET TO BUFFER
MOV AL,[BX]
SUB AL,SSVER
DEC AL ; FOR A 25 LINE SCREEN, SLPP HAS 24 IN IT
MOV SLPP,AL
MOV AL,[BX+1]
SUB AL,SSHOR
MOV SCPL,AL
MOV SLFLG,1 ; SET IT TO ANSI
MOV AL,[BX+2]
CMP AL,"N" ; IS IT ASCII
JNE SSET4 ; NO, THEN LEAVE IT ALONE
MOV SLFLG,0 ; YES, THEN SET IT TO ASCII
MOV SCROLL,1 ; (7) NO SCROLLING IF NO ANSI.SYS
JMP SSET6
SSET4: TEST SCROLL,1 ; (7) SHOULD WE CONSIDER COLOR
JNZ SSET6 ; (7u) I GUESS NOT
MOV AH,15 ; (7) CHECK VIDEO STATE
INT 10H ; (7) TO SEE IF IN A COLOR MOOD
CMP AL,1 ; (7) THIS IS 40x25 COLOR
JNZ SSET5
SSET4A: TEST CMDLIN,1 ; (7) WAS THIS SET ON CMD LINE
JNZ SSET6 ; (7) YES, CANNOT OVERRIDE
CMP SCROLL,1 ; (7u) NO COLOR WITHOUT SCROLL CAPABILITY
JZ SSET6 ;
MOV COLFLG,1 ; (7) SET COLOR FLAG
MOV SCRATR,COLATR ; (7) SET VIDEO ATTRIBUTE
JMP SSET6
SSET5: CMP AL,3 ; (7) THIS IS 80x25 COLOR
JZ SSET4A
TEST CMDLIN,2 ; (7) DID THEY SET COLOR FROM CMD LINE
JNZ SSET4A ; (7) TURN IT ON
SSET6: POP BX ; (6) RESTORE THE HANDLE
MOV AH,CFCLOSZ ; (6) CLOSE THE SETUP FILE
INT 21H
JC SSET3 ; (6) IF CANNOT CLOSE FILE, COMPLAIN
RET
SSET1: DEC SLPP ; (N1) SET TO 24 TO WORK RIGHT ANYWAY!
PRINT SSMSG1
RET
SSET2: PRINT SSMSG2
RET
SSET3: PRINT SSMSG3
RET
SSETUP ENDP
;INITIALIZATION
PUBLIC TSET4
TSETUP PROC
MOV BX,OFFSET SELINE1
TEST COLFLG,1 ; (7) ARE WE USING COLOR
JZ TSET0 ; (7) NOPE, ...
MOV BX,OFFSET CELINE1 ; (7) FIX COLOR IF NECESSARY
TSET0: MOV AL,SLPP ; NUMBER OF LINES
SUB AH,AH ; ASCIIFY THIS NUMBER INTO AX
INC AX ; FOR SLPP = 24, THE CURSOR IS AT 25
DIV RADIX ; SO THAT WE CAN PUT THE CURSOR
ADD AH,"0" ; THERE WHEN WE FINISH THE STATUS
ADD AL,"0" ; LINE OUTPUT IN MSOUT
MOV [BX],AX ; DO THE PATCH
MOV BX,OFFSET CLS1 ; (7t) PATCH CLEAR SCREEN
MOV [BX],AX ; (7t) SO THAT IT RESTORE CURPOS
MOV BX,OFFSET CLS1C ; (7t) PATCH IT IN COLOR TOO
MOV [BX],AX
MOV BX,OFFSET OUTBUF ; NO DETERMINE END OF OUTPUT BUFFER
MOV CL,SCPL
SUB CH,CH
ADD BX,CX
DEC BX ; (7v) SHORTEN BY ONE TO AVOID HARD
MOV ENDBUF,BX ; WRAP
MOV BYTE PTR [BX],0
MOV BX,OFFSET SLSTAB ; NOW, CHOSE A STATUS LINE
CMP TIMEMD,0 ; CHECK FOR TIME MODE
JE TSET1 ; NOPE, USE OTHER STATUS LINE
ADD BX,8 ; YES, GET OFFSET TO TIME STAT LINES
TSET1: CMP CX,55 ; CX HAS WIDTH OF SCREEN IN COLUMNS
JL TSET2
MOV AX,60 ; MAKE AN INDEX INTO ROOM DESC
SUB CX,80 ; SCALE WITHIN 80 - WIDTH
MOV SI,4 ; SKIP SCORE PTR'S IN TABLE
JMP TSET3 ; FIX VALUES IN THE TABLE WITH CX
TSET2: MOV AX,15 ; MAKE AN INDEX INTO ROOM DESC BUFFER
SUB CX,40 ; SCALE WITHIN 40 - WIDTH
SUB SI,SI ; SI INDEXES INTO SCORE v. TIME
TSET3: SUB AX,CX ; BACKUP THE PTR IN BUFFER
ADD AX,[BX][SI] ; GET OFFSET TO BUFFER BEGINNING
MOV BX,2[BX][SI] ; GET OFFSET TO TABLE BEGINNING
TEST CX,CX ; SEE IF CX IS LESS THAN ZERO
JG TSET4 ; SKIP FIRST PATCH IF POSITIVE
ADD 6[BX],CX ; THESE PATCHES ADJUST THE RANGES FOR
;+EACH OF THE STATUS LINE FIELD ACCORDING
;+TO THE CALCULATED LENGTH OF THE STATUS
;+LINE
TSET4: ADD 12[BX],CX ; PATCH CX INTO THESE TABLE OFFSETS
ADD 14[BX],CX ; CX HAS DIFFERENCE BET TERM W & 80
ADD 20[BX],CX
ADD 22[BX],CX
TSET5: MOV SLSTR,AX ; SAVE STATUS LINE BUFFER ADDR IN HERE
MOV SLTAB,BX ; AND STATUS LINE TABLE ADDR IN HERE
CALL MINIT
RET
TSETUP ENDP
PUBLIC ZIPBGN,STR5$,STR8$,STR9$,STR10$,STR11$,STR12$,STR12A
PUBLIC STR12B,STR13$,STR14$,STR15$,STR17$
ZIPBGN PROC
CALL SYSINI ;DO ANY SYSTEM INITIALIZATION
CALL HERALD ;put up the herald file
STR5$: SUB AX,AX ;BLOCK 0
SUB BX,BX ;PUT AT BEGINNING OF GAME SEGMENT
CALL GETBLK ;GET THE BLOCK
MOV GAMEIN,1 ; (7q) INDICATE RELIABILITY OF SCRIPT BIT
CMP BYTE PTR ES:[PVERS1],ZMVERS ;PROPER Z-MACHINE VERSION?
JNE STR8$ ;NO
TEST BYTE PTR ES:[PVERS2],1 ;YES,PROPER MODE BITS?
JE STR9$ ;YES
STR8$: FATAL FTL4 ;SOMETHING WRONG, DIE
STR9$: TEST BYTE PTR ES:[PVERS2],2 ;TIME MODE REQUESTED?
JE STR10$ ;NO
INC TIMEMD ;YES, SET TIME-MODE FLAG
STR10$: MOV AL,BYTE PTR ES:[PVERS2]
CMP SLFLG,0
JNE STR11$
OR AL,10H ;TURN ON THE NO STATUS LINE BIT
MOV BYTE PTR ES:[PVERS2],AL
STR11$: TEST SCROLL,1 ; (7) CAN WE SPLIT THE SCREEN
JNZ STR11A ; (7) GUESS NOT...
MOV AL,BYTE PTR ES:[PVERS2]
OR AL,SCRBIT ; (7) TURN ON BIT FOR SPLIT
MOV BYTE PTR ES:[PVERS2],AL ;(7) SAVE IT
STR11A: GTAWRD A,[PZRKID] ;UNIQUE GAME & VERSION INDENTIFIER
MOV ZORKID,AX
GTAWRD A,[PENDLD] ;GET ENDLOD POINTER
TEST AX,1FFH ;ROUND UP TO NEXT BLOCK
JE STR12$
AND AX,0FE00H
ADD AX,200H
STR12$: MOV CX,AX
MOV BP,CX ;EXTRACT ENDLOD BLOCK
MOV CL,9
SAR BP,CL
MOV CX,BP
MOV ENDLOD,CX ;SAVE ENDLOD BLOCK NUMBER
MOV AX,128 ;(6) FIGURE BOUNDRY FOR SEG1
SUB AX,CX ;(6) SUBTRACT AMOUNT IN PRELOD
SHL AX,1 ;(6) MULTIPLY IT BY #BYTES PER
SHL AX,1 ;(6) ENTRY IN PAGTAB FOR SCALING
MOV SEGEND,AX ;(6) USED TO COMPARE TO PAGTAB TO LOCATE
;(6) SEGMENT IN WHICH GAMEBLOCK RESIDES
DEC CX ;NUMBER OF BLOCKS LEFT TO LOAD
CMP FITS,1 ;(6) READ THE WHOLE GAME IN
JNE STR12A ;(6) NO, DON'T READ IT ALL IN
MOV CX,127 ;(6) TWO 64K READS
MOV AX,1 ;(6) STARTING WITH BLK 1
MOV LPTAB,1 ;(6) STARTING LOCATION
CALL GTBLKS
MOV CX,127 ;(6) READ ALL BUT 1/2K
MOV LPTAB,128 ;(6) READ INTO SEG1
MOV AX,128 ;(6) THE LAST 64K BLKS
CALL GTBLKS
JNZ STR12$$ ;(8) READ LAST BLOCK?
MOV CX,1 ;(8) READ ONE MORE BLOCK
MOV AX,255 ;(8) LAST BLOCK NUMBER
MOV LPTAB,255 ;(8) PAGE LOCATION
CALL GTBLKS ;(8) GET IT
STR12$$:MOV LPTAB,0
JMP STR12B
STR12A: MOV AX,1 ;STARTING WITH BLOCK 1
MOV BX,OFFSET PAGTAB ; (6) GET A BUFFER ADDRESS
ADD BX,4 ; (6) SECOND ENTRY
MOV LPTAB,BX ; (6) SAVE THIS CALCULATED ADDR
CALL GTBLKS ;GET THE BLOCKS
STR12B: GTAWRD A,[PVOCTB] ;VOCAB LOCATION
MOV VOCTAB,AX ;SAVE VOCABULARY TABLE POINTER
GTAWRD A,[POBJTB] ;GET OBJECT TABLE POINTER
MOV OBJTAB,AX
GTAWRD A,[PGLOTB] ;GET GLOBAL TABLE POINTER
MOV GLOTAB,AX
GTAWRD A,[PWRDTB] ;GET WORD TABLE POINTER
MOV WRDTAB,AX
GTAWRD A,[PPURBT] ;GET PURE CODE POINTER
TEST AX,1FFH ;ROUND UP TO NEXT BLOCK
JE STR13$
ADD AX,200H
STR13$: MOV CL,9 ;EXTRACT BLOCK NUMBER
SAR AX,CL
AND AX,7FH ;CLEAR UNWANTED BITS
MOV PURBOT,AX ;SAVE IT
MOV BX,OFFSET RBRKS ;START OF READ BREAK CHARACTER TABLE
MOV SI,VOCTAB ;VOCAB TABLE POINTER
MOV CL,BYTE PTR ES:[SI] ;1ST BYTE IN VOCTAB IS # OF SIBREAKS
SUB CH,CH ;CLEAR HIGH BYTE
INC SI
STR14$: MOVM [BX],ES:[SI],AL ;TRANSFER THEM
INC BX
INC SI
LOOP STR14$
MOV ESIBKS,BX ;REMEMBER END OF SI BREAKS
MOV BP,OFFSET IRBRKS;ALWAYS END WITH INITIAL BREAK CHARACTERS
STR15$: MOVM [BX],[BP],AL ;(6)TRANSFER THEM
INC BX
INC BP
CMP AL,0
JNE STR15$
MOV AL,ES:[SI] ;GET VOCABULARY ENTRY LENGTH
SUB AH,AH
MOV VWLEN,AX ;AND STORE IT AWAY
INC SI
GTAWRD A,[SI] ;GET NEXT WORD IN TABLE
MOV VWORDS,AX ;NUMBER OF WORD ENTRIES IN VOCABULARY
ADD SI,2 ;MOVE TO NEXT WORD
MOV VOCBEG,SI ;BEGINNING OF ACTUAL VOCABULARY
MOV SI,ENDLOD ;GET # PAGES IN ENDLOD
MOV CL,9
SHL SI,CL ;THIS IS FIRST LOCATION (ES:) OF PAGING
MOV PAGES,SI ;SAVE THIS TO USE AS AN OFFSET
CALL MTIME
STR17$: JMP START1
ZIPBGN ENDP
;RESTART EXECUTION HERE
PUBLIC RESTRT,RESTRT1,START1
RESTRT PROC
CALL GAMOPEN ; (7q) OPEN THE GAME FILE
MOV MORLIN,0 ; (7s) RESET MORE COUNT
SUB AX,AX ;REREAD ALL OF THE IMPURE STUFF
MOV CURSEG,0 ; (K5) SET SEG FLAGS BACK TO SEG0
MOV ZPCSEG,0
MOV LPTAB,0 ; (6) FILL THE FIRST BIT OF STUFF
PUSH PAGES ; (6) SAVE THIS VARIABLE
MOV PAGES,0 ; (6) RESET IT FOR SMALL MEMORY READ
MOV CX,PURBOT
CALL GTBLKS
POP PAGES ; RESET IT AGAIN
MOV AH,SCRFLG ; (7o) WERE WE SCRIPTING
XOR AL,AL ; (7o) ZERO THIS
MOV WORD PTR ES:[PFLAGS],AX ; (7o) RESTORE SCRIPTING STATE
TEST SCROLL,1 ; (7) CAN WE SCROLL?
JNZ RESTRT1 ; (7) NEVER COULD, SKIP RE-INITS
OR BYTE PTR ES:[PVERS2],SCRBIT ; (7) TURN ON SPLIT
MOV TOPLIN,TOPSCR ; (7) FIX SCREEN VARIABLES ON RESTART
MOV SCRNFLG,0 ; (7) WHOLE SCREEN, ONLY 1
MOV SPLTFLG,0 ; (7) NO SPLIT SCREEN
JMP RESTRT1 ; PATCH TO FIX STATUS LINE BUG (5)/PHG
START1:
CALL TSETUP ;SETUP TERMINAL/STATUS LINE
RESTRT1:CALL CLRSCR ;CLEAR THE REMAINDER OF SCREEN
; MOV AL,BYTE PTR ES:[PVERS2]
; ADD AL,8 ;TANDY BIT
; MOV BYTE PTR ES:[PVERS2],AL
MOV SP,OFFSET STK_TOP ;INITIALIZE OUR STACK POINTER
MOV DI,OFFSET ZSTK_TP ;INITIALIZE USER STACK POINTER
MOV CHRPTR,OFFSET OUTBUF ;INITIALIZE OUTPUT CHARACTER POINTER
MOV ZLOCS,DI ;LOCALS WOULD START AT FIRST SLOT,
SUB ZLOCS,2 ;IF THERE WERE ANY
GTAWRD A,[PSTART] ;GET STARTING LOCATION
CALL BSPLTB ;SPLIT BLOCK & BYTE POINTERS
MOV ZPC1,AX ;INITIALIZE ZPC BLOCK-POINTER
MOV ZPC2,BX ;INITIALIZE ZPC BYTE-POINTER
CALL NEWZPC ;GET PAGE TO EXECUTE
JMP NXTINS
RESTRT ENDP
;MAIN INSTRUCTION INTERPRETATION LOOP
PUBLIC NXTINS,NXI0A$,NXI0B$,NXI0C$,NXI1$,NXI2$,NXI3$,NXI4$,NXI5$
PUBLIC NXI6$,NXI7$,NXI8$,NXI8A$,NXI9$,NXI10$,NXI11$,NXI12$,NXI14$,NXI15$
NXTINS PROC
CALL NXTBYT ;GET THE OPERATION BYTE
MOV DX,AX ;SAVE A COPY
CMP AX,80H ;IS IT A 2OP?
JL NXI9$ ;YES
CMP AX,0B0H ;NO, IS IT A 1OP?
JGE NXI0A$ ;NO
JMP NXI12$ ;YES
NXI0A$: CMP AX,0C0H ;IS IT A 0OP?
JG NXI0B$ ;NO
JMP NXI13$ ;YES
NXI0B$: AND AX,3FH ;IT'S EXTENDED, GET THE OPCODE
SHL AX,1 ;MAKE IT A WORD OFFSET
MOV BP,AX ;GET THE OPERATOR POINTER
MOV SI,[BP+EXTOP] ; (6) REMOVED DS OVERRIDE
CMP SI,0
JNE NXI0C$ ;OPERATION EXISTS
JMP NXI14$ ;NO SUCH OPERATION
NXI0C$: CALL NXTBYT ;GET THE ARGUMENT BYTE
MOV CX,4 ;SPLIT IT INTO 4 2-BIT MODE BYTES
NXI1$: PUSH AX
SAR AX,1
SAR AX,1
LOOP NXI1$
MOV CX,4 ;RETRIEVE THE 4 BYTES IN PROPER ORDER
SUB DX,DX
MOV BX,OFFSET ARGBLK;FIRST ARGUMENT SLOT
NXI2$: POP AX ;GET NEXT MODE BYTE?
AND AX,3 ;CLEAR OFF UNWANTED BITS
CMP AX,3 ;NO MORE ARGUMENTS?
JE NXI4$ ;YES, FLUSH ANY OTHER MODE BYTES
CALL GETARG ;ELSE, GET THE NEXT ARGUMENT
MOV [BX],AX ;SAVE IT IN ARGUMENT BLOCK
ADD BX,2
INC DX ;REMEMBER NUMBER OF ARGS GOTTEN
NXI3$: LOOP NXI2$ ;LOOP FOR REST OF MODE BYTES
JMP NXI5$
NXI4$: DEC CX ;DETERMINE NUMBER OF REMAINING MODE BYTES
SHL CX,1 ;WORD COUNT
ADD SP,CX ;FLUSH THEM
NXI5$: XCHG CX,DX
MOV AX,CX ;NUMBER OF ARGS GOES HERE FOR OPERATOR TO USE
CMP BYTE PTR [SI],90H ;(6)DOES OPERATOR WANT ARGBLK POINTER?
JE NXI8A$ ;YES, CALL OPERATOR NOW
DEC CX ;NO, IT WANTS ARGS IN REGISTERS
JL NXI8A$ ;NO ARGS, JUST CALL OPERATOR
JE NXI8$ ;1 ARG, GET IT
SUB CX,2
JL NXI7$ ;2 ARGS
JE NXI6$ ;3 ARGS
MOV DX,ARGBLK[6] ;ELSE, 4 ARGS, GET 4TH
NXI6$: MOV CX,ARGBLK[4] ;GET 3RD
NXI7$: MOV BX,ARGBLK[2] ;GET 2ND
NXI8$: MOV AX,ARGBLK ;GET FIRST ARG
NXI8A$: JMP NXI15$ ;AND CALL OPERATOR
NXI9$: AND AX,1FH ;2OP, EXTRACT OPERATION BITS
SHL AX,1 ;MAKE IT A WORD OFFSET
MOV BP,AX ;FIND POINTER TO OPERATOR ROUTINE
MOV SI,[BP+EXTOP] ;(6) REMOVED DS OVERRIDE
CMP SI,0
JE NXI14$ ;NO SUCH OPERATION
MOV AX,1 ;ASSUME FIRST ARG IS AN IMMEDIATE
TEST DX,40H ;IS IT INSTEAD A VARIABLE?
JE NXI10$ ;NO
INC AX ;YES, CHANGE MODE
NXI10$: CALL GETARG ;GET THE FIRST ARG
PUSH AX ;SAVE IT
MOV AX,1 ;ASSUME SECOND ARG IS AN IMMEDIATE
TEST DX,20H ;IS IT INSTEAD A VARIABLE?
JE NXI11$ ;NO
INC AX ;YES, CHANGE MODE
NXI11$: CALL GETARG ;GET THE SECOND ARG
MOV BX,AX ;POSITION IT
POP AX ;RECOVER FIRST ARG
CMP BYTE PTR [SI],90H ;(6)DOES ROUTINE WANT ARGUMENT BLOCK?
JNE NXI15$ ;NO, GO CALL IT
MOV ARGBLK,AX ;YES, MOVE ARGS TO ARGBLK
MOV ARGBLK[2],BX
MOV AX,2 ;ALWAYS 2 ARGS
JMP NXI15$ ;NOW CALL OPERATOR
NXI12$: AND DX,0FH ;1OP, EXTRACT OPERATION BITS
SHL DX,1 ;MAKE IT A WORD OFFSET
MOV BP,DX ;GET OPERATOR ROUTINE POINTER
MOV SI,[BP+ONEOP] ;(6) REMOVED DS OVERRIDE
CMP SI,0
JE NXI14$ ;ILLEGAL OPERATION
SAR AX,1 ;EXTRACT MODE BITS
SAR AX,1
SAR AX,1
SAR AX,1
AND AX,3
CALL GETARG ;GET THE ARGUMENT
JMP NXI15$ ;AND CALL OPERATOR
NXI13$: AND AX,0FH ;0OP, EXTRACT OPERATION BITS
SHL AX,1 ;MAKE IT A WORD OFFSET
MOV BP,AX ;GET OPERATOR ROUTINE POINTER
MOV SI,[BP+ZEROOP] ; (6) REMOVED DS OVERRIDE
CMP SI,0
JNE NXI15$ ;IT'S A LEGAL OPERATION
NXI14$: FATAL FTL5 ;OTHERWISE, COMPLAIN
NXI15$: CALL SI ;CALL THE OPERATOR ROUTINE
JMP NXTINS ;AND LOOP FOR NEXT INSTRUCTION
NXTINS ENDP
SUBTTL PAGING ROUTINES
PAGE +
;NORMALIZE ZPC & GET PROPER PAGE
; NEWZPC HAS BE EXTENDED TO UPDATE THE ZPCSEG VARIABLE. IT SETS THIS
; VARIABLE DIFFERENTLY ACCORDING TO PAGING SCHEME. IF THE GAME FITS IN
; MEMORY, ZPCSEG IS SET ACCORDING TO THE 17TH BIT OF THE ZPC, OTHERWISE
; IT IS SET WHEN CURSEG IS SET BY THE GETPAG ROUTINE INDICATING THAT
; THE CURRENT BLOCK IN THE SECOND ES SEGMENT.
;
PUBLIC NEWZPC,NWZ1$,NWZ2$,NWZ3$,NWZ4$,NWZ5$
NEWZPC PROC
PUSH SI ;SAVES
PUSH BP
PUSH BX
PUSH CX
PUSH DX
SUB AX,AX ;USE DOUBLE-WORD ARITHMETIC
MOV BX,ZPC1 ;GET BLOCK-POINTER
MOV BH,BL ;POSITION IT
SUB BL,BL
SHL BX,1
ADC AX,0
MOV SI,ZPC2 ;GET BYTE-OFFSET
XCHG SI,AX ;DOUBLE-WORDIFY IT (MIGHT BE NEGATIVE)
CWD
XCHG SI,AX
ADD BX,SI ;ADD IT TO OTHER DOUBLE WORD
ADC AX,DX
MOV DX,BX
AND DX,1FFH ;EXTRACT BYTE-OFFSET
MOV ZPC2,DX ;SAVE IT
MOV CL,9 ;EXTRACT BLOCK-POINTER
SAR BX,CL
AND BX,7FH
TEST AX,1 ;TEST 17TH BIT
JE NWZ1A$ ;WAS 0
OR BX,80H ;WAS 1, SET PROPER BIT IN BLOCK-POINTER
TEST FITS,1 ;(6) DO WE HAVE ALL OF THE GAME IN MEMORY
JZ NWZ1$ ; (6) BECAUSE PAGES ARE NOT CONTINUOUS
MOV ZPCSEG,1 ; WERE IN THE UPPER GAME SEG
JMP NWZ1$
NWZ1A$: TEST FITS,1 ;(6) SHOULD WE SET THIS HERE
JZ NWZ1$ ;(6) NO
MOV ZPCSEG,0 ;(6) SET SEGMENT TO BASE SEG
NWZ1$: MOV ZPC1,BX ;SAVE IT
TEST ZPCFLG,1 ;(7n) DO WE HAVE THE RIGHT BLOCK
JNZ NWZ1Q$ ;(7n) NO, SKIP THIS SKIP
CMP BX,CURBLK ;HAS IT CHANGED?
JE NWZ5$ ;NO
NWZ1Q$: MOV CURBLK,BX ;YES, REMEMBER NEW BLOCK
MOV AX,CURTAB ;IS OLD PAGE IN PAGING SPACE?
CMP AX,0
JE NWZ2$ ;NO
MOV BP,AX ;YES, STORE CURRENT REF TIME FOR OLD PAGE
MOVM [BP],RTIME1,CL ;(6) REMOVED DS OVERRIDE
MOVM [BP+1],RTIME2,CX ;(6) REMOVED DS OVERRIDE
INC AX
NWZ2$: CMP FITS,1 ;(6) IS THE WHOLE GAME THERE
JZ NWZ3$ ;(6) YES, GO DO WHATEVER
CMP BX,ENDLOD ;NEW PAGE ALREADY IN CORE?
JL NWZ3$ ;YES
MOV AX,BX ;NO, GET NEW PAGE
CALL GETPAG
MOV BL,CURSEG ;(6) SET ZPCSEG TO SAME AS CURSEG
MOV ZPCSEG,BL ;(6) SO THAT NXTBYT WORKS
MOV BX,AX
MOV AX,LPTAB ;GET NEW PAGE TABLE POINTER
INC AX ;POINT TO REF SLOT
MOV CURTAB,AX ;SAVE THIS POINTER FOR LATER
MOV BP,AX ;STORE HIGHEST RTIME TO KEEP PAGE FOR US
MOV BYTE PTR [BP],-1 ;(6) REMOVED DS OVERRIDE
MOV WORD PTR [BP+1],-1 ;(6) REMOVED DS OVERRIDE
INC AX
JMP NWZ4$
NWZ3$: MOV CL,9 ;CALCULATE PAGE ADDRESS
SHL BX,CL
TEST FITS,1
JNZ NWZ3A$ ; (7z) SKIP DURING WHOLE MEMORY EDITION
MOV ZPCSEG,0 ; (7z) RESET THIS TO SEG0
NWZ3A$: MOV CURTAB,0 ;CLEARING POINTER MEANS PAGE IS PRELOADED
NWZ4$: MOV CURPAG,BX ;UPDATE PAGE POINTER
NWZ5$: MOV ZPCFLG,0 ; (K3) RESET THIS FLAG
POP DX ;RESTORES
POP CX
POP BX
POP BP
POP SI
RET ;AND RETURN
NEWZPC ENDP
;GET THE PAGE WHOSE NUMBER IS IN AX, RETURN A POINTER TO IT IN AX
; GETPAG AND FINDPG WERE MODIFIED TO ACCOMMODATE THE EXTENDED PAGING
; SCHEMES. SEGEND, CURSEG AND PAGTAB ARE SIGNIFICANT VARIABLES IN THESE
; MODIFICATIONS. SEGEND IS USED TO COMPARE TO THE CURRENT LOCATION IN
; PAGTAB WHICH IN TURN INDICATES IN WHICH SEGMENT THE NEW BLOCK IS LOCATED.
; CURSEG IS SET TO TELL THE OUTSIDE WORLD WHICH SEGMENT THE LAST GETPAG
; TOUCHED. NOTE THAT BEING IN THE SECOND SEGMENT (SEG1) REQUIRES THAT
; ADDRESS CALCULATIONS DO NOT ADD IN PAGES (THE VARIABLE MARKING THE END
; OF PRELOAD). THIS REQUIREMENT IS NECESSARY BECAUSE THERE IS NO PRELOAD
; IN THE SECOND SEGMENT.
;
PUBLIC GETPAG,GTP1$,GTP2$,GTP2A$,GTP2B,GTP2C,GTP3$,GTP4$
GETPAG PROC
CMP AX,LPAGE ;IS THIS THE SAME PAGE AS LAST REFERENCED?
JNE GTP1$ ;NO
MOV AX,LPLOC ;YES, WE ALREADY HAVE LOCATION
RET ;RETURN IT
GTP1$: MOV LPAGE,AX ;SAVE NEW PAGE NUMBER
PUSH CX ;SAVES
PUSH BX
ADD RTIME2,1 ;UPDATE REFERENCE TIME (COUNT)
ADC RTIME1,0
MOV BX,OFFSET PAGTAB;PAGE INFORMATION TABLE
GTP2$: INC BX
CMP AL,[BX-1] ;SEARCH FOR DESIRED BLOCK
JNE GTP3$ ;NOT IT
CMP AX,CURBLK ;IS THIS THE CURRENT CODE PAGE?
JE GTP2A$ ;DON'T UPDATE REFERENCE TIME
MOVM [BX],RTIME1,CL ;FOUND IT, UPDATE ITS REFERENCE TIME
MOVM [BX+1],RTIME2,CX
GTP2A$: DEC BX ;BACKUP TO BEGINNING OF TABLE ENTRY
PUSH BX ; (6) SAVE THIS BEFORE SCALING IT
SUB BX,OFFSET PAGTAB;(6) BASIFY THE PAGTABLE ENTRY
CMP BX,SEGEND ;(6) CHECK IF PAGE IS IN 2ND SEGMENT
POP BX ; (6) RESTORE BX
JL GTP2B ;(6) NO, FUNCTION AS USUAL
MOV CURSEG,1 ;(6) YES, SET SEGMENT NUMBER FLAG
JMP GTP2C ;(6) SKIP SETTING TO ZERO
GTP2B: MOV CURSEG,0 ;(6) SET SEGMENT NUMBER TO 0
GTP2C: MOV LPTAB,BX ;SAVE IT
SUB BX,OFFSET PAGTAB;CALCULATE ADDRESS OF PAGE
TEST CURSEG,1 ;(6) DO A DIFFERENT CALC FOR SEG1
JZ GTP2D ;
SUB BX,SEGEND ; (6) FIGURE OFFSET INTO SEG1
GTP2D: MOV CL,7
SHL BX,CL
TEST CURSEG,1 ; (6) SKIP ADDING IN PAGES IF SEG1
JNZ GTP4$
ADD BX,PAGES
JMP GTP4$ ;AND RETURN PAGE POINTER
GTP3$: ADD BX,3 ;SKIP REFERENCE TIME
CMP WORD PTR [BX],-1;END OF TABLE?
JNE GTP2$ ;NO, CONTINUE SEARCH
CALL FINDPG ;YES, FIND A PAGE TO LOAD INTO
PUSH AX ;PAGE POINTER
MOV LPTAB,BX ;SAVE PAGE TABLE POINTER
MOV AX,LPAGE ;SAVE NEW BLOCK NUMBE
MOV [BX],AL
MOVM [BX+1],RTIME1,CL;AND CURRENT REF TIME
MOVM [BX+2],RTIME2,CX
POP BX ;PAGE POINTER
CALL GETBLK ;GET THE BLOCK
GTP4$: MOV AX,BX ;RETURN PAGE POINTER
MOV LPLOC,AX ;AND SAVE IT FOR LATER
POP BX ;RESTORES
POP CX
RET
GETPAG ENDP
;FIND A GOOD PAGE, RETURN PAGE POINTER IN AX & PAGTAB POINTER IN BX
; SEE COMMENTS UNDER GETPAG
PUBLIC FINDPG,FNP1$,FNP2$,FNP3$,FNP4$
FINDPG PROC
PUSH CX
MOV BX,OFFSET PAGTAB
MOV CX,-1 ;FAKE BEST-CASE REFERENCE COUNT
MOV DX,CX
INC BX ;SKIP BLOCK NUMBER FOR NOW
FNP1$: INC BX ;IS THIS REF TIME WORSE THAN CURRENT WORST?
CMP [BX-1],CL
JA FNP3$ ;NO
JB FNP2$ ;YES
CMP [BX],DX ;MAYBE, COMPARE LOW-ORDER WORDS, WORSE?
JAE FNP3$ ;NO
FNP2$: MOV CL,[BX-1] ;YES, SAVE ITS REF COUNT
MOV DX,[BX]
MOV AX,BX ;AND LOCATION (+2)
FNP3$: ADD BX,3 ;SKIP SECOND WORD
CMP BYTE PTR [BX-1],-1 ;LOOP UNTIL END OF TABLE
JNE FNP1$
INC CX ;WAS A PAGE REALLY FOUND?
JE FNP4$ ;NO, GROSS BUG!
SUB AX,2 ;YES, CALCULATE CORE LOCATION OF PAGE
MOV BX,AX
SUB AX,OFFSET PAGTAB
CMP AX,SEGEND ; (6) CHECK SEGMENT
JL FNP5$ ; (6) IN SEG0, SET CURSEG
MOV CURSEG,1 ; (6) SET CURSEG TO SEG1
SUB AX,SEGEND ; (6) SCALE DOWN THE PAGE PTR
JMP FNP6$
FNP5$: MOV CURSEG,0
FNP6$: MOV CL,7
SHL AX,CL
TEST CURSEG,1 ; (6) DON'T ADD IN PAGES IF IN SEG1
JNZ FNP7$
ADD AX,PAGES
FNP7$: POP CX ;RESTORES
RET
FNP4$: FATAL FTL6
FINDPG ENDP
SUBTTL HIGH LEVEL IBMPC DEPENDENT STUFF
;SYSTEM INITIALIZATION
; SYSINI HAS BEEN GREATLY MODIFIED TO INCLUDE DOS 2.0 FILE HANDLING,
; COMMAND LINE ARGUMENTS, COPY PROTECTION, AND DYNAMIC ALLOCATION OF
; PAGING SPACE.
;
; THE COPY PROTECTION SCHEME WORKS AS FOLLOWS: A NUMBER OF COMPUTERS ARE
; DEFINED AS COMPATIBLE WITH IBM IN THE DATA DECLARATIONS. INCLUDED FOR
; EACH IS THE FIRST 9 LETTERS OF THE COPYRIGHT NOTICE THAT EXISTS IN THE
; ROM. IF ANY OF THESE STRINGS IS FOUND IN THE ROM, COPY PROTECTION IS
; CHECKED, OTHERWISE IT IS NOT. IBM COMPATIBILITY MEANS TOTAL ROM BIOS
; COMPATIBILITY SUCH THAT WINDOWED SCROLLING AND SPECIAL DISK I/O MAY BE
; PERFORMED. NOT FINDING ANY OF THESE STRINGS RESULTS IN THE ZIP FUNCTIONING
; AS IF IT WERE ON A GENERIC MS-DOS BASED MACHINE.
;
; COMMAND LINE ARGUMENTS SET BITS IN THE VARIABLE CMDLIN IN THE ROUTINE
; SCANCMD. CMDLIN THEN AFFECTS VARIOUS PARAMETERS AND VARIABLES EITHER IN
; SYSINI OR SSETUP.
;
; THE PAGTAB IS SET UP ACCORDING TO AVAILABLE MEMORY. THERE EXISTS A WORD
; AT OFFSET 2 IN THE PSP (PROGRAM SEGMENT PREFIX, OR WHERE CS POINTS) WHICH
; INDICATES THE TOP OF PHYSICAL MEMORY. PAGTAB REPRESENTS THE LAST WORD USED
; BY THE ZIP. THE CODE SEGMENT REPRESENTS THE AMOUNT OF MEMORY TAKEN UP BY
; EVERYTHING UNDER THE ZIP SUCH AS THE OPERATING SYSTEM AND DEVICE DRIVERS.
; IF WE CONVERT PAGTAB TO PARAGRAPHS, THEN ALL OF THESE NUMBERS ARE IN
; PARAGRAPHS. SO:
;
; TOP_OF_MEM - (CS + PAGTAB) --> AVAILABLE MEMORY IN PARAGRAPHS
;
; AVAILABLE MEMORY IS CONVERTED INTO THE NUMBER OF BUFFERS AVAILABLE. FROM
; THIS NUMBER WE MUST SUBTRACT 1 OR 2 BUFFERS TO ALLOW FOR THE PAGTAB TO
; BE BUILT. KNOWING AVAILABLE BUFFERS TELLS US THE LENGTH OF OUR PAGTAB
; SINCE OUR PAGTAB IS 4 BYTES/BUFFER. AT THE NEXT PARAGRAPH BOUNDARDY BEYOND
; THE PAGTAB WE HAVE OUR FIRST GAME SEGMENT WHICH WILL BE MOVED TO ES AND
; STORED IN THE VARIABLE GAMESEG. ADDING 64K (IN PARAGRAPHS) TO GAMESEG
; WE ARRIVE AT OUT LOCATION FOR SEG1, THE SECOND GAME SEGMENT. THE PAGTAB
; IS THEN INITIALIZED.
;
PUBLIC SYSINI,SYS1,INITLP,SYSFTL
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
FATAL FTL11 ; (7) DIE APPROPRIATELY
GOODOS: 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
CALL SCANCMD ; (7) GET CMD LINE VARS
MOV AX,BIOSEG ; (7) GET SEG OF BIOS
MOV ES,AX ; (7) MAKE ADDRESSIBLE
MOV BX,COMPATS ; (7) GET NUMBER OF COMPATIBLES
MOV AX,OFFSET COMP1 ; (7) CHECK FOR COPYRIGHT
SYS$: MOV DX,9 ; (7) LENGTH FOR MATCHING STRING
MOV DI,BIOSOFF ; (7) OFFSET INTO BIOS TO CHECK
MOV CX,7000H ; (7) SEARCH THIS MANY BYTES
CALL CHK ; (7) LOOK FOR COPYRIGHT NOTICE
JNC GOTONE ; (7) JMP ON A MATCH
ADD AX,10 ; (7) ADVANCE TO NEXT STRING
DEC BX ; (7) DO WE HAVE ANOTHER STRING
JNZ SYS$ ; (7) YES, LOOK AT IT
JMP NOCMD ; (7) SKIP COPY PROTECT CHECK
GOTONE: MOV SCROLL,0 ; (7) USE WINDOWED SCROLLING
CALL CHKCOP ; (7) CHECK COPY PROTECTION
JNC NOCMD ; (7) CONTINUE ON
FATAL FTL10
NOCMD: MOV AH,CFOPENZ ; (6) ASCIZ STRINGS
MOV AL,0 ; (6) OPEN FOR READING
MOV DX,OFFSET GAMFILE
INT 21H
JNC SYSA$
JMP SYSFTL
SYSA$: MOV GAMHNDL,AX ; (6) SAVE THE FILE HANDLE
MOV BX,AX ; (7n) GET FILE HANDLE INTO BX
XOR CX,CX ; (7n) ZERO THESE
XOR DX,DX ; (7n) TO FIND EOF
MOV AH,CFSEEK ; (7n) SEEK TO END OF FILE
MOV AL,2 ; (7n) MOVE FILE PTR TO EOF
INT 21H ; (7n) DO IT
MOV CL,4 ; (7n) CONVERT AX TO PARAGRAPHS
SHR AX,CL ; (7n) TO SAVE AS FILESIZE
TEST DX,DX ; (7n) DO WE HAVE A 17TH BIT?
JZ SYSB$ ; (7n) NOPE
OR AH,10H ; (7n) TURN ON THAT BIT
SYSB$: MOV CL,5 ; (7n) REDUCE TO NUMBER OF 512 BLKS
SHR AX,CL ; (7n) FOR COMPARISON TO BUFFERS
MOV GAMESIZ,AX ; (7n) SAVE THIS SIZE FOR FITS
XOR CX,CX ; (7w) MOVE POINTER TO BOF
MOV DX,4 ; (7w) WE WANT THE WORD AT OFFSET 4
MOV AH,CFSEEK ; (7w) SEEK THERE
MOV AL,0 ; (7w) METHOD: OFFSET FROM BOF
INT 21H ; (7w) BX HAS FILE HANDLE
MOV AH,CRDRNDZ ; (7w) READ IN THE FIRST
MOV CX,2 ; (7w) READ THE WORD OF ENDLOD
MOV DX,OFFSET SSBUF ; (7w) ANY BUFFER BIG ENOUGH WILL DO
INT 21H ; (7w) READ IT
MOV AX,SSBUF ; (7w) GET THE WORD
XCHG AH,AL ; (7w) FLOP THE BYTES
TEST AX,1FFH ; (7w) ROUND UP TO NEXT BLOCK
JE SYSC$
AND AX,0FE00H ; (7w) TURN OF LOW BITS
ADD AX,200H ; (7w) ADD A BLOCK
SYSC$: MOV CL,9 ; (7w) CONVERT TO NUMBER OF BLOCKS
SAR AX,CL
MOV DX,AX ; (7w) AND SAVE NUMBER IN DX
MOV BX,2 ; (6) GET TOP OF MEMORY
MOV BX,CS:[BX] ; (6) FROM OUT OF THE PSP
MOV AX,CS ; (6) GET CODE SEG
SUB BX,AX ; (6) SUB CSEG FROM TOPMEM
MOV CL,4 ; (6) SHIFT COUNT
MOV AX,OFFSET PAGTAB ; (6) USE OFFSET TO FIGURE PARAG'S
SHR AX,CL ; (6) REDUCE THIS OFFSET TO PARAS
INC AX ; (6) SKIP ONE PARAGRAPH
AND AX,0FE0H ; (7z) BLOCKIFY THIS NUMBER
ADD AX,20H ; (7z) OTHERWISE LOSE A PAGE
SUB BX,AX ; (6) YIELDS PARAS AVAILABLE
TEST CMDLIN,4 ; (7) SET ON CMDLIN?
JZ SYS2
CMP BX,MEMORY ; (7) REASONABLE REQUEST
JBE SYS2 ; (7) NO, NOT REALLY THERE
MOV BX,MEMORY ; (7) USE USER SETTING
SYS2: CMP BX,MINMEM ; (7) AT LEAST 48K?
JAE SYS3 ; (7) YES
FATAL FTL12 ; (7) DIE APPROPRIATELY
SYS3: MOV CL,5 ; (6) DIVIDE BY 32 TO GET BUFFERS
SHR BX,CL ; (6) TO GET #BUFFERS
SUB BX,DX ; (7w) SUBTRACT ALL PRELOD BLOCKS
DEC BX ; (6) SUBTRACT 1 BUFFER FOR PAGTBL
CMP BX,128 ; (6) ABOVE 80H, WE NEED ANOTHER TBL
JL SYS1 ; (6) WE HAVE THIS MANY OR FEWER
DEC BX ; (6) TAKE AWAY ANOTHER PAGE
CMP BX,255 ; (6) THIS IS THE MOST WE WILL NEED
JLE SYS1 ; (6) DON'T MODIFY BX <= 255
MOV FITS,1 ; (6) FLAG THAT GAME FITS IN MEMORY
MOV BX,255 ; (6) DON'T ALLOW MORE THAN THIS
SYS1: PUSH BX ; (7w) SAVE THIS FOR FIDDLING
ADD BX,DX ; (7w) ADD IN THE BLOCKS PREV SBTRCTED
CMP BX,GAMESIZ ; (7n) CHECK FOR LUCKY FIT
POP BX ; (7w) AND RESTORE ACTUAL #BUFFERS
JBE SYSD$ ; (7n) IF BX IS GREATER, NO GO
MOV FITS,1 ; (7n) IT DOES FIT AFTERALL!!!
SYSD$: MOV BUFFERS,BX ; (6) SAVE THIS NUMBER
SHL BX,1 ; (6) GET NUMBER OF BYTES IN PAGTBL
SHL BX,1 ; (6) 4 BYTES WIDE * BX BYTES LONG
INC BX ; (6) ADD 1 FOR TERMINATOR BYTE
ADD BX,OFFSET PAGTAB ; (6) CALC TABLE LOCATION
MOV CX,4 ; (6) REDUCE #BYTES IN TABLE = PARAS
SHR BX,CL ; (6) TO DYNAMICALLY ALLOCATE SPACE
MOV AX,DS ; (6) GET A COPY OF CURRENT SEG
ADD BX,AX ; (6) AND CALCULATE THE NEW SEG
ADD BX,1 ; (6) ADD ONE PARAGRAPH FOR SAFETY
MOV GAMESEG,BX ; (6) THIS WILL BE USED FOR ES:GAME
ADD BX,1000H ; (6) ADD 64K TO CALC 2ND SEG
MOV SEG1,BX ; (6) THIS IS SEG0+64K
CALL TBLINI ; (7n) CALL TABLE INITIALIZATION
CALL SSETUP
RET
SYSFTL: FATAL FTL9
SYSINI ENDP
; THIS ROUTINE INITIALIZES THE PAGE TABLE
;
TBLINI PROC
PUSH SI ; SAVES
PUSH DI
PUSH AX
PUSH BX
PUSH CX
MOV DI,OFFSET PAGTAB ; (6) GET BEGINNING ADDR
MOV CX,WORD PTR BUFFERS ; (6) GET NUMBER OF ENTRIES
MOV AX,DS ; (7)GET DATA SEG
MOV ES,AX ; (7) FIX ES
CLD ;(6) AUTO INCREMENT
INITLP: MOV SI,OFFSET INITTBL ; (6) POINT TO STRING TO INITIALIZE
PUSH CX ; (6) SAVE REP COUNT
MOV CX,4 ; (6) GET INNER LOOP COUNT
REP MOVSB ; (6) TRANSFER THE STRING TO TBL
POP CX ; (6) RESTORE COUNT
LOOP INITLP ; (6) AND FINISH TABLE INITIALIZING
MOV WORD PTR [DI],-1 ; (6) DEPOSIT A -1 TO INDICATE END
MOV BX,GAMESEG ;
MOV ES,BX ; (6) SET THE EXTRA SEG TO GAMESEG
POP CX ; RESTORES
POP BX
POP AX
POP DI
POP SI
RET
TBLINI 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
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
PRINT SAV3 ; (7o) OTHERWISE PROMPT
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
POP DX
RET
GAMOPEN 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
; THIS ROUTINE CHECKS THE COPY PROTECTION
CHKCOP PROC
CLC ; FOR NOW, EVERYTHING WORKS
RET
CHKCOP ENDP
; THIS ROUTINE SETS SOME VARIABLES ACCORDING TO THE CMD LINE
; CURRENTLY THE 6 /PARAMETERS ARE SUPPORTED. THE FIRST THREE,
; /P INITIALIZE PRINTER THROUGH INT 17
; /M MONOCHROME
; /C COLOR
; /W WINDOWED SCROLLING
; /K<n> AMOUNT OF MEMORY IN K
; ARE INTENDED FOR THE USER. A FOURTH WILL NOT BE DOCUMENTED IS
; /G<gamefile.ext> WHICH WILL TEMPORARILY PATCH THE ZIP TO RUN THE
; GAME SPECIFIED.
;
PUBLIC SCANCMD,SCAN,SCAN0,SCAN1,SCAN2,SCAN3,SCAN4
PUBLIC SCAN5,SCAN6,SCAN7,SCAN8
SCANCMD PROC
CLD ; (7) AUTO INCREMENT
MOV SI,80H ; (7) GET # OF BYTES
LODSB ; (7) INTO AL
TEST AL,AL ; (7) ANY?
JNZ SCAN1
SCAN0: RET ; (7) NONE THERE
SCAN1: DEC AL ; (7) CARRIAGE RETURN DOESN'T COUNT
XOR DX,DX ; (7) ZERO THIS
MOV DL,AL ; (7) SAVE NUMBER OF BYTES
ADD DX,82H ; (7) ADDR OF LAST BYTE+1 ON LINE
INC SI ; (7) SKIP INITIAL SPACE
SCAN: CMP SI,DX ; (7) END OF CMD LINE?
LODSB ; (7) GET A DELIMITER
JZ SCAN0 ; (7) YEP, RETURN
CMP AL,'/' ; (7) WAS IT THE DELIMITER
JNZ SCAN ; (7) GET NEXT VALID CHAR
LODSB ; (7) GOT DELIMITER, GET BYTE
AND AL,5FH ; (7) UPPERCASIFY
CMP AL,'M' ; (7) DO THEY WANT MONOCHROME
JNZ SCAN2 ; (7) NOPE, TRY NEXT
OR CMDLIN,1 ; (7) SET FLAG IN CMDLIN VARIABLE
JMP SCAN
SCAN2: CMP AL,'W' ; (7) WINDOWED SCROLLING?
JNZ SCAN3
MOV SCROLL,0 ; (7) TURN ON SCROLLING
OR CMDLIN,8 ; (7) TURN ON 8 BIT
JMP SCAN
SCAN3: CMP AL,'G' ; (7) GAME FILE NAME?
JNZ SCAN5 ; (7) NOPE, CONTINUE ON...
MOV DI,OFFSET GAMFILE ; (7) MAKE THIS THE GAME
SCAN4: MOVSB ; (7) TRANSFER A BYTE
CMP DX,SI ; (7) THE END?
JNZ SCAN4
MOV AL,0 ; (7) DROP IN A NULL
STOSB ; (7) TO END THE STRING
RET
SCAN5: CMP AL,'C' ; (7) DO THEY WANT COLOR
JNZ SCAN6 ; (7) NOPE, TRY NEXT
OR CMDLIN,2 ; (7) SET FLAG IN CMDLIN VARIABLE
JMP SCAN
SCAN6: CMP AL,'K' ; (7) SET MEMORY SIZE
JNZ SCAN9 ; (7) NOPE LOOK ONWARD
MOV BX,0 ; (7) ZERO THIS
SCAN7: CMP DX,SI ; (7) THE END?
JNZ SCAN8 ; (7) NOPE
SCAN7A: OR CMDLIN,4 ; (7) TURN ON MEMORY SET BIT
MOV CL,6 ; (7w) CONVERT K TO PARAGRAPHS
SHL BX,CL ; (7) I THINK???
MOV MEMORY,BX ; (7) SAVE AMOUNT OF K SPECIFIED IN 512 BLKS
DEC SI ; (7v) BACKUP ONE
JMP SCAN
SCAN8: LODSB ; (7) GET FIRST BYTE
CMP AL,0D ; (7v) CR MEANS END OF STRING
JZ SCAN7A ; (7v) SO QUIT HERE
CMP AL,' ' ; (7v) SPACE MEANS NEXT STRING
JZ SCAN7A ; (7v) SO QUIT
CMP AL,'/' ; (7v) SLASH MEANS NEXT DELIMITER
JZ SCAN7A ; (7v) SO QUIT
XOR AH,AH ; (7) ZERO THIS
AAA ; (7) SCALE THE NUMBER DOWN
XCHG AX,BX ; (7) GET NUMBER (BX) INTO AC
PUSH DX ; (7) SAVE ENDPTR
MUL RADIX ; (7) MULTIPLY NUMBER BY TEN
ADD BX,AX ; (7) ADD IN NEW NUMBER
POP DX ; (7) RESTORE END PTR
JMP SCAN7
SCAN9: CMP AL,'P' ; (7w) DO WE NEED TO INITIALIZE THE PRINTER
JZ SCAN10 ; (7w) YES, DO IT
JMP SCAN
SCAN10: PUSH DX ; (7w) SAVE THIS
MOV DX,0 ; (7w) INITIALIZE FIRST PRINTER
MOV AH,1 ; (7w) FUNCTION NUMBER FOR INIT
INT 17H ; (7w) ASSUME IBM PC BECAUSE THEY USED /P
POP DX ; (7w) RESTORE END POINTER
JMP SCAN ; (7w) NOW GET BACK TO WORK
SCANCMD ENDP
CLRSCR PROC
CMP SLFLG,0 ; (7n) ANSI IN PLACE?
JZ CLR1
MOV BX,OFFSET CLS
CMP COLFLG,1 ; (7t) TURN ON COLOR WITH CLEAR SCREEN
JNZ CLR0
MOV BX,OFFSET CLSC ; (7t) TURN POINT TO COLOR ANSI CODE
CLR0: CALL MSPRT
RET
CLR1: MOV CL,SLPP ; (7n) GET NUMBER OF LINES
XOR CH,CH ; (7n) ZERO THE TOP
CLR2: CALL MCRLF ; (7n) ROLL THE SCREEN IF NO ANSI
MOV MORLIN,0 ; (7n) RESET TO AVOID MORE
LOOP CLR2
RET
CLRSCR ENDP
;GET A GAME FILE BLOCK, BLOCK NUMBER IN AX, CORE LOCATION IN ES:BX
PUBLIC GETBLK
GETBLK PROC
PUSH CX ;SAVE
MOV CX,1 ;CALL GTBLKS FOR 1 BLOCK ONLY
CALL GTBLKS
POP CX ;RESTORE
RET
GETBLK ENDP
;GET A SERIES OF GAME FILE BLOCKS,
;FIRST BLOCK NUMBER IN AX, CORE LOCATION IN ES:BX # OF BLOCKS IN CX
; GTBLKS UNDERWENT A MAJOR OVERHAUL. IT WAS CHANGED TO SUPPORT 2.0 FILE
; NAMES AND FILE HANDLES. THE FIRST PASS OF THE OVERHAUL WAS TO SUPPORT
; FITTING THE WHOLE GAME INTO MEMORY. THIS BASTARDIZATION OF THE ORIGINAL
; WAS FURTHER CONTORTED WHEN TRYING TO SUPPORT 128K AND LESS MEMORY CONFIG-
; URATIONS.
;
; THE ORIGINAL ARGUMENTS HAVE NOT BEEN LEFT WHOLLY IN TACT. AX IS STILL THE
; FIRST BLOCK NUMBER AND CX CONTAINS THE NUMBER OF BYTES TO READ IF ALL OF
; THE GAME IS IN MEMORY OR BLOCKS IF THE GAME DOESN'T ALL FIT. ES:BX NO
; LONGER HAS THE CORE LOCATION. CALLS TO THIS PROCEDURE MAY STILL PUT THE
; CORE LOCATION IN BX BUT IT IS IGNORED. CORE LOCATION IS CALCULATED ON THE
; FLY BASED ON THE ENTRY IN PAGTAB (WHICH IN TURN SETS THE SEGMENT IN WHICH
; THE READ IS DONE.)
;
; THE STARTING BLOCK NUMBER IS USED TO MOVE THE FILE PTR (2.0) FROM THE
; BEGINNING OF THE FILE TO THE PROPER GAME BLOCK.
;
; IN THE CASE OF ALL OF THE GAME IN MEMORY, THE SEGMENT IS CHOSEN BASED ON
; THE PAGE'S INTENDED BUFFER INDICATED BY THE VARIABLE LPTAB. BUFFER NUMBERS
; 128 AND ABOVE GO IN TO THE SECOND SEGMENT. THE GAME IS READ IN IN THE
; BEGINNING IN THREE OR FOUR READS AND THIS ROUTINE IS NOT TOUCHED AGAIN.
;
; IN THE CASE OF A SMALL MEMORY SYSTEM, THE SEGMENT TO READ INTO IS CHOSEN
; BASED ON LPTAB, CURSEG AND SEGEND (SEE GETPAG FOR EXPLANATION).
;
PUBLIC GTBLKS,GTBKI$,GTBLP,GETIT,GTBLKB,GTBOUT,GTB2,GTB3
GTBLKS PROC
MOV DSKDIR,0 ;READ MODE
PUSH AX
MOV AX,GAMHNDL ; (6) GET THIS HANDLE
MOV HANDLE,AX ; (6) INTO THE GENERAL HANDLE
POP AX ; (6) AND RESTORE THIS
GTBKI$: PUSH BP
PUSH DX
PUSH BX
PUSH SI
GTBLP: MOV DX,CX ; (6) SAVE NUMBER OF BLOCKS
MOV CX,9 ; (6) MULTIPLY BY 512
SHL DX,CL ; (6) DO IT
SHL AX,CL ; (6) CONVERT BLK# TO OFFSET
MOV CX,0 ; (6) ZERO THIS
ADC CX,0 ; (6) DID WE CARRY OUT ABOVE
MOV BX,HANDLE ; (6) GET FILE HANDLE
MOV SI,DX ; (6) SAVE NUMBER OF BYTES FOR READ
TEST SEEKF,1 ; (6) SHOULD WE REALLY?
JZ NOSEEK
MOV DX,AX ; (6) GET LOW ORDER #BYTES FOR SEEK
MOV AH,CFSEEK
MOV AL,0 ; (6) USE OFFSET FROM BEGINNING METHOD
INT 21H ; (6) SEEK
NOSEEK: MOV DX,LPTAB ; (6) FIGURE OUT BUFFER NUMBER
TEST FITS,1 ; (6) DO WE HAVE ALL THE GAME
JNZ NOFIX ; (6) IF NOT, FIX LPTAB (SCALE IT DOWN)
CMP DX,0 ; (6) ARE WE JUST STARTING UP
JZ NOFIX
SUB DX,OFFSET PAGTAB ; (6) BASIFY THE TABLE NUMBER
SHR DX,1
SHR DX,1 ; (6) DIVIDE TABLE ENTRY BY TABLE WIDTH(4)
MOV CL,9 ; (6) DIVIDE BY 512
TEST CURSEG,1 ; (6) WHICH SEG ARE WE IN
JNZ NOFIX ; (6) SKIP PAGE ADDITION
MOV AX,PAGES ; (6) GET AMOUNT OF PRELOD
SHR AX,CL ; (6) AND SKIP THAT AMOUNT OF BUFFER SPC
ADD DX,AX ; (6) ADD THAT INTO BUFFER NUMBER
NOFIX: MOV CX,SI ; (6) RESTORE #BYTES TO READ
MOV AH,CRDRNDZ ; (6) READ RANDOM GAME RECORD
CMP DSKDIR,0 ; (6) ARE WE READING?
JZ GTREAD
MOV AH,CWRRNDZ ; (6) NO, DO A WRITE INSTEAD
GTREAD: TEST FITS,1 ; (6) IS BUFFERING CONTINUOUS
JNZ GTBLK1$ ; (6) FIGURE BUFFER FROM DX
TEST CURSEG,1 ; (6) WHAT SEGMENT ARE WE IN
JZ GETIT ; (6) FUNCTION NORMALLY
MOV SI,SEGEND ; (6) GET SEGMENT ENDING POINT
SHR SI,1 ;(6) DIVIDE IT BY WIDTH OF PAGTAB
SHR SI,1 ; (6) TO USE AS AN INDEX INTO SEG1
SUB DX,SI ; (6) BASIFY BUFFER NUMBER FOR SEG1
JMP GTBLK2$ ; (6) USE SEG1 WITH SCALED DX
GTBLK1$:CMP DX,128
JL GETIT
SUB DX,128 ; (6) SUBTRACT TO BASIFY
GTBLK2$:PUSH DS
MOV SI,SEG1 ; (6) GET SEG1 INTO DS
MOV DS,SI ; (6) SET UP DS
JMP GTBLKB
GETIT: PUSH DS
MOV SI,ES
MOV DS,SI
GTBLKB: PUSH CX ; (6) SAVE NUMBER OF BLOCKS
MOV CL,9
SHL DX,CL ; (6) MAKE A NEW PAGE-PTR
POP CX ; (6) RESTORE COUNT OF BYTES
INT 21H ; (6) CALL DOS
CMP AX,CX ; (6) SET FLAGS
POP DS
MOV SEEKF,1 ; (6) RESET SEEK FLAG
GTBOUT: POP SI
POP BX
POP DX
POP BP
RET
GTB2: PUSH AX ; (6) SAVE THIS
MOV AX,GAMHNDL ; (6) GET HANDLE
CMP HANDLE,AX ; (6) COMPARE THEM
JE GTB3
STC
JMP GTBOUT
GTB3: FATAL FTL7
GTBLKS ENDP
SUBTTL IBMPC SYSTEM ROUTINES
PAGE +
PUBLIC MCHRI,MTTYO,MCRLF,MSOUT,MSPRT,MINIT,MPRNT,MTIME,FINISH
;READ A CHARACTER INTO AX, WAITING UNTIL ONE IS AVAILABLE, NO ECHO
MCHRI PROC
PUSH CX
PUSH DX
CMP CHRFLG,0
JNZ MCHR1
MOV CHRFLG,1
MCHR1: MOV AH,CNOECHO ; (7u) USE NO ECHO
INT 21H
SUB AH,AH
call boss ; check for boss key
POP DX
POP CX
RET
MCHRI ENDP
;PRINT THE CHARACTER IN AL, FOREGROUND IN AH
MTTYO PROC
PUSH AX
PUSH BX ;SAVES
PUSH CX
PUSH DX
PUSH BP
PUSH AX
MOV AH,CCONIO
MOV DL,AL
INT 21H
POP AX
TEST SCRFLG,1 ; (7x) IS SCRIPTING REQUESTED?
JZ MTYO1 ; (7n) IF NOT THEN DON'T BOTHER
CMP SCRHLD,1 ; (7n) IS SCRIPTING SUSPENDED?
JZ MTYO1
CALL PRTOUT
MTYO1: POP BP
POP DX
POP CX
POP BX
POP AX
RET
MTTYO ENDP
;PRINT A CARRIAGE RETURN/LINE FEED WITH MORE MODE
MCRLF PROC
PUSH AX ;SAVES
PUSH BX
PUSH CX
PUSH DX
PUSH BP
TEST SCROLL,1 ;(7) WHAT KIND OF SCROLLING
JNZ MCR$
TEST SCRNFLG,1 ; (7) ARE WE IN SCREEN #1
JNZ MCR$ ; (7) YES, USE OLD SCROLL
MOV AH,SCROLLUP ;(7) USE A VIDEO BIOS SCROLL
MOV AL,1 ;(7) ONLY 1 LINE
MOV CX,TOPLIN ;(7) ALWAYS FREEZE TOP LINE
MOV DH,SLPP ;(7) GET SCREEN LENGTH
MOV DL,SCPL ;(7) GET THE WIDTH OF THE SCREEN
DEC DL ; (7v) COORDINATES ARE ZERO BASED
MOV BH,SCRATR ;(7) GET THE SCREEN ATTRIBUTE
INT 10H ;(7) CALL THE VIDEO
MCR$: MOV AH,CCONIO
MOV DL,13
INT 21H
TEST SCRNFLG,1 ; (7) ARE WE WRITING TO WINDOW 1
JNZ MCRA$
TEST SCROLL,1 ;(7) NO LINE FEED ON WINDOWED SCROLL
JZ NOLF
MCRA$: MOV AH,CCONIO
MOV DL,10
INT 21H
NOLF: TEST SCRNFLG,1 ;(7) NO MORE FOR WINDOW 1
JNZ MCR1 ;(7) SKIP THIS GARBAGE
INC MORLIN ;INCREMENT NUMBER OF LINES OUTPUT
MOV AL,SLPP
SUB AH,AH
DEC AX ;(7n) SO THAT STATUS LINE DOESN'T OVERWRITE
MCRB$: MOV DX,TOPLIN ;(7) GET SCROLLING WINDOW TOPLINE
XCHG DH,DL ;(7) SCROLL LINE IN DL
SUB AL,DL ;(7) THIS MANY LINES NOW
CMP MORLIN,AX
JL MCR1
MOV MORLIN,0
MOV SCRHLD,1 ; (7o) SUSPEND SCRIPT
PRINT MORE
CALL MCHRI
PRINT EMORE
MOV SCRHLD,0 ; (7o) RESUME SCRIPT
MCR1: TEST SCRFLG,1 ; (7x) CHANGED TO TEST
JZ MCR2
CALL PRTCRL
MCR2: POP BP
POP DX
POP CX
POP BX
POP AX
RET
MCRLF ENDP
;OUTPUT STATUS LINE HERE
MSOUT PROC
MOV BX,OFFSET SBLINE
TEST COLFLG,1 ;(7) ARE WE IN COLOR MODE
JZ MSOUT1
MOV BX,OFFSET CBLINE ;(7) USE COLOR THROUGH ANSI
MSOUT1: CALL MSPRT
MOV AH,09H
MOV DX,SLSTR
INT 21H
MOV BX,OFFSET SELINE
TEST COLFLG,1 ;(7) ARE WE IN COLOR MODE
JZ MSOUT2
MOV BX,OFFSET CELINE ;(7) USE COLOR THROUGH ANSI
MSOUT2: CALL MSPRT
RET
MSOUT ENDP
MSPRT PROC
MOV CL,BYTE PTR [BX]
SUB CH,CH
CMP CL,0
JE MSPRT1
MSPLP: INC BX
MOV DL,BYTE PTR [BX]
MOV AH,06H
PUSH BX
INT 21H
POP BX
LOOP MSPLP
MSPRT1: RET
MSPRT ENDP
MINIT PROC
CMP SLFLG,0
JNE MINIT1
RET
MINIT1: MOV BX,OFFSET STINIT
TEST COLFLG,1 ;(7) ARE WE IN COLOR MODE
JZ MINIT2 ;(7)
MOV BX,OFFSET CTINIT ;(7)
MINIT2: CALL MSPRT
MOV BX,OFFSET SELINE
TEST COLFLG,1 ;(7) ARE WE IN COLOR MODE
JZ MINIT3 ;(7)
MOV BX,OFFSET CELINE ;(7)
MINIT3: CALL MSPRT
RET
MINIT ENDP
;PRINT A STRING, POINTER (TO DATA SEGMENT) IN AX, WHITE FOREGROUND
MPRNT PROC
CMP GAMEIN,1 ; (7q) DO NOT CHECK THIS UNLESS THE GAME IS IN
JNZ MPR0
PUSH AX
MOV AX,ES:[PFLAGS] ; (7n) SCRIPTING?
XCHG AH,AL ; (7n) SWAP BYTES
MOV SCRFLG,AL ; (7n) TURN IT ON, TURN IT ON AGAIN!
POP AX
MPR0: PUSH BX ;SAVE BX
MOV BX,AX ;STRING POINTER
MPR1: MOV AL,[BX] ;GET NEXT CHARACTER
CMP AL,0 ;END OF LINE, WITH CRLF?
JE MPR2 ;YES
CMP AL,80H ;END OF LINE, NO CRLF?
JE MPR3 ;YES
CALL MTTYO ;PRINT CHARACTER
INC BX ;POINT TO NEXT CHARACTER
JMP MPR1 ;REPEAT
MPR2: CALL MCRLF ;PRINT A CRLF
MPR3: POP BX ;RESTORE BX
RET
MPRNT ENDP
;GET TIME
MTIME PROC
PUSH CX
PUSH DX
MOV AH,2CH
INT 21H
MOV RSEED1,CX ; HOURS & MINUTES
MOV RSEED2,DX ; SECONDS & 100THS
POP DX
POP CX
RET
MTIME ENDP
FINISH PROC
CALL MCRLF
MOV BX,OFFSET STRESET
CALL MSPRT
MOV AH,4CH
INT 21H
FINISH ENDP
EVEN
;**********************************************************************
; DO NOT PLACE ANY VARIABLES OR CODE BEYOND THIS POINT! THE
; ZIP DYNAMICALLY ALLOCATES A PAGE TABLE AND PAGING SPACE BEYOND
; THIS LABEL.
;**********************************************************************
PUBLIC PAGTAB
PAGTAB LABEL BYTE
MSZIP ENDP
CSEG ENDS
END MSZIP