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

3674 lines
81 KiB
NASM

.LIST
TITLE ZIP Z-LANGUAGE INTERPRETER IBMPC VERSION
PAGE 58,132
SUBTTL STACK AND DATA SEGMENTS INITIALIZATION
; CHANGES ON "D" VERSION
;
; 1) SPLIT AND SCREEN OPCODES WORK
; 2) RAGGED SCREEN FIXED
; 3) DELETE KEY COLOR PROBLEM FIXED
; 4) PRINTER PROBLEM FIXED
; 5) COLOR QUESTION ON RESTART FIXED
; 6) RANDOMNESS BUG FIXED - 06/25/84 - DAN HORN
ZVERSN EQU "D" ;ZIP VERSION NUMBER
ZMVERS EQU 3 ;Z-MACHINE VERSION NUMBER
LSTACK EQU 256 ;LENGTH OF USER STACK(MUST BE 1 PAGE FOR NOW)
ABS_SG SEGMENT AT 0H
ABS_SG ENDS
STK_SG SEGMENT PARA STACK
DW 100H DUP(?)
STK_TOP LABEL WORD
STKBOT DW LSTACK DUP(?)
ZSTK_TP LABEL WORD
STK_SG ENDS
DATA_SG SEGMENT PARA
;VARIBLE DEFINITIONS:
;SCREEN COLOR ATTRIBUTES
NRMATR DB 07H ;FOR B/W, NORMAL - IN COLOR, WHITE ON BLUE
USLATR DB 70H ;B/W AND COLOR, BLACK ON WHITE
TYPATR DB 07H ;FOR COLOR, YELLOW ON BLUE/ FOR B&W, ?
COLORQ DB " Do you want color (Y/N)? ",80H
COLFLG DB 0 ;NON ZERO IF THEY WANT COLOR
OLDCUR DW 0 ; TEMP FOR OLD CURSOR POSITION
;SCRIPTING STUFF
SCRFLG DB 0
PRNRDY DB " * Printer not ready: Abort or Retry? ",80H
;USL STUFF
USLMOD DB 0 ;FOR PUTCHR
PMFLAG DB 0 ;AM/PM
SLSTR DW 0 ;TOP OF SCREEN STRING
SLTAB DW 0 ;TABLE USED BY OPUSL
SLSTAB DW SLS40
DW SLS40T
DW SLS80
DW SLS80T
SLTTAB DW SLT40
DW SLT40T
DW SLT80
DW SLT80T
;40 COLUMN, SCORE
SLS40 DB 25 DUP(32),"S:",4 DUP(32),"M:",7 DUP(32),0,80H
SLS40T DW -1
DW OPPRND
DW 2
DW 24
DW -1
DW OPPRNN
DW 27
DW 30
DW -1
DW OPPRNN
DW 33
DW 36
;40 COLUMN, TIME
SLT40 DB 25 DUP(32),"Time:",10 DUP(32),0,80H
SLT40T DW -1
DW OPPRND
DW 2
DW 24
DW -1
DW OPPRNH
DW 31
DW 33
DW -1
DW OPPRNM
DW 34
DW 39
;80 COLUMN, SCORE
SLS80 DB 51 DUP(32),"Score:",8 DUP(32),"Moves:",9 DUP(32),0,80H
SLS80T DW -1
DW OPPRND
DW 2
DW 27
DW -1
DW OPPRNN
DW 58
DW 61
DW -1
DW OPPRNN
DW 73
DW 78
;80 COLUMN, TIME
SLT80 DB 60 DUP(32),"Time:",15 DUP(32),0,80H
SLT80T DW -1
DW OPPRND
DW 2
DW 27
DW -1
DW OPPRNH
DW 69
DW 71
DW -1
DW OPPRNM
DW 72
DW 77
;REBOOT AT END OF GAME
REBOOT DB " ** End of session **",0
REBOO1 DB "Strike any key to reboot",0
;VERIFY/INTERPRETER VERSION
INTMSG DB "IBM-PC Interpreter Version "
INTVER DB "D",80H
;SAVE/RESTORE
NDISKS DB 2
FUCK DB 0
SRTXT0 DB "Type backspace to abort",80H
SRTXT1 DB "Disk drive (A-D) (default = "
SRDRV DB "B"
DB "):",80H
SRTXT2 DB "Position (0-9) (default = "
SRPOS DB "0"
DB "):",80H
SRTXT3 DB "Insert SAVE disk, then hit a key:",80H
SRTXT4 DB "Insert game disk, then hit a key:",80H
;GTBLKS
DSKMOD DB 0 ;0 FOR GAME READ, 1 FOR SAVE/RESTORE
DSKDRV DB 0 ;CURRENT DRIVE
DSKDIR DB 0 ;0 FOR READ, 1 FOR WRITE
GTBCNT DW 0 ;REMAINING BLOCKS TO BE READ
GTBSTT DW 0 ;STARTING BLOCK
GTBCOR DW 0 ;STARTING CORE ADDRESS
;OPRAND
RSEED1 DW ? ;SEED FOR RANDOM NUMBERS
RSEED2 DW ? ;SEED TWO
RTEMP DW ? ;TEMP FOR RANDOM ROUTINES
;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
;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
;ZIPBGN
IRBRKS DB " ",9,13,12,".,?",0 ;INITIAL SET OF READ BREAK CHARS
TWIDTH DW 0 ;TERMINAL WIDTH
RWIDTH DW 0 ;REAL TERMINAL WIDTH
SCRN0 DB 1 ;START OF SCROLLING SCREEN
CURSCR DB 0 ;CURRENT SCREEN
LFTMAR DB 1 ;LEFT MARGIN DEFAULT = 1 (2 FOR 80-COLUMN)
MEMTOP DW 0 ;LAST AVAILABLE LOCATION
TIMEMD DW 0 ;TIME(VERSUS SCORE)-MODE-FOR-STATUS-LINE FLAG
ZORKID DW 0 ;UNIQUE GAME & VERSION IDENTIFIER
ENDLOD DW 0 ;ENDLOD BLOCK NUMBER
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
BKBUF DB 512 DUP(?) ;SCRATCH BUFFER FOR BACKUP
OUTBUF DB 81 DUP(?) ;OUTPUT BUFFER
RBRKS DB 32 DUP(?) ;STRING OF READ BREAK CHARACTERS
PAGTAB DB 64 DUP(254,0,0,0) ;PAGE INFORMATION TABLE
DB 255
PAGES DW 0 ;SWAPPING AREA
BUFPGS DW 0
CHRFLG DB 0
INIFLG DB 0
INIWID EQU 1
INIMEM EQU 2
INIRND EQU 4
;RESTRT
ZLOCS DW 0 ;POINTER TO LOCALS
ZPC1 DW 0 ;ZPC BLOCK-POINTER
ZPC2 DW 0 ;ZPC BYTE-POINTER
ARGBLK DW 4 DUP(?) ;ARGUMENT BLOCK FOR EXTENDED OPERATIONS
;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
;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
;BACKUP COPY STUFF
SECCNT DB 0
TRKCNT DB 3
BKDSK DB 0 ;DRIVE TO BACKUP ONTO
BKFRST DB 1
BKASK1 DB "You may make one backup copy of your",0H
BKASK2 DB "game. To do so you need a formatted",0H
BKASK3 DB "COPY disk. Also, neither MASTER",0H
BKASK4 DB "nor COPY disk may be write-protected.",0H
BKASK5 DB "Make COPY now? (Y/N) ",80H
BKYEP DB "[YES]",80H
BKNOPE DB "[NO]",80H
BKMC1 DB "Insert MASTER disk in drive A",0H
BKMC2 DB "Insert COPY disk in drive B",0H
BKMC3 DB "Hit any key to start backup",80H
BKMAST DB "Insert MASTER disk; hit any key",80H
BKCOPY DB "Insert COPY disk; hit any key",80H
BKWPRT DB "Disk write-protected; hit any key",80H
BKNRDY DB "Disk error; hit any key",80H
BKDONE DB "Backup completed.",0H
BKFAIL DB "Disk error; backup aborted.",0H
BKWPS1 DB "Please write-protect the disk in",0H
BKWPS2 DB "drive A; hit any key",80H
DSK_SV1 DW 0
DSK_SV2 DW 0
DSK_PRM DB 11001111B
DB 2
DB 37
DB 3
DB 4
DB 02AH
DB 0FFH
DB 050H
DB 0F6H
DB 25
DB 4
IDTBL DB 1,0,1,3
DB 1,0,2,3
DB 1,0,3,3
DB 1,0,4,3
;OPERATION TABLES:
;ZERO ARGUMENT OPERATIONS
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
;.CRLF
MORE DB "**MORE** ",80H
MORLIN DW 0
LINMAX DW 23
LINMXX EQU 23
;.GETTM
TIME DD ?
;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
;READ
ERR2 DB "Too many words typed, flushing: ",80H
;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
;Fatal error header
FATHDR DB "Fatal error: ",80H
;ZSTR CHARACTER CONVERSION VECTOR
ZCHRS DB "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
DB " 0123456789.,!?_#'"
DB '"/\-:()'
DATA_SG ENDS
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 .PRINT
POP AX
ENDM
FATAL MACRO ERR ;;PRINT FATAL ERROR AND DIE
CALL .CRLF
MOV AX,OFFSET FATHDR
CALL .PRINT
MOV AX,OFFSET ERR
CALL .PRINT
JMP FINISH
ENDM
CURGET MACRO
PUSH AX
SUB BH,BH
MOV AH,3
INT 10H
POP AX
ENDM
CURSET MACRO POS
PUSH AX
SUB BH,BH
MOV AH,2
IFNB<POS>
MOV DX,POS
ENDIF
INT 10H
POP AX
ENDM
SUBTTL SYSTEM INITIALIZATION
PAGE +
CODE_SG SEGMENT PARA
ASSUME CS:CODE_SG,DS:DATA_SG,ES:GAME_SG,SS:STK_SG
START PROC FAR
;NEVER, EVEN FOR AN INSTANT, CONSIDER CHANGING THESE NEXT INSTRUCTIONS
;THE GAME CREATOR MODIFIES THEM AND THEY MUST REMAIN HERE
MOVM DS,DATA_SG,AX
MOVM ES,GAME_SG,AX
MOVM SS,STK_SG,AX
;END OF MAGIC SECTION. HACK AND SLASH TO YOUR HEART'S CONTENT
MOV SP,OFFSET STK_TOP
MOV DI,OFFSET ZSTK_TP
JMP ZIPBGN ;JUMP TO BEGINNING OF ZIP CODE
START ENDP
FEEP PROC
MOV AX,7
CALL .TTYOUT ;BELL
RET
FEEP ENDP
OPVERI PROC
PRINT INTMSG
CALL .CRLF
MOV AX,ES:[PLENTH]
XCHG AH,AL
PUSH SI
PUSH DI
PUSH ENDLOD
CALL BSPLIT
MOV SI,AX
MOV DI,BX
MOV AX,0
MOV BX,64
MOV DX,0
MOV ENDLOD,0
OPVR1: PUSH SI
PUSH DI
PUSH DX
CALL GETBYT
POP DX
POP DI
POP SI
ADD DX,CX
CMP BX,0
JNZ OPVX
NOP
OPVX: CMP AX,SI
JNE OPVR1
CMP BX,DI
JNE OPVR1
MOV AX,ES:[PCHKSM]
XCHG AH,AL
POP ENDLOD
POP DI
POP SI
CMP AX,DX
JE OPVR2
JMP PFALSE
OPVR2: JMP PTRUE
OPVERI ENDP
OPREST PROC
MOV DSKDIR,0 ;INDICATE RESTORE
JMP OSV0$
OPREST ENDP
OPSAVE PROC
MOV DSKDIR,1 ;INDICATE WRITE
OSV0$: PRINT SRTXT0 ;WRITE "TYPE BACKSPACE..."
CALL .CRLF
PRINT SRTXT2 ;WRITE "POSITION (DEFAULT = X): "
OSV2A$: CALL .CHRIN
CMP AL,13
JNE OSV3$
MOV AL,SRPOS ;GET DEFAULT POSITION
OSV3$: MOV BL,AL
CMP AL,8
JNE OSV3X$
CALL .CRLF
JMP PFALSE
OSV3X$: SUB AL,"0"
CMP AL,9
JG OSV3A$
CMP AL,0
JGE OSV3B$
OSV3A$: CALL FEEP
JMP OSV2A$
OSV3B$: MOV SRPOS,BL
MOV AL,BL
MOV AH,NRMATR
CALL .TTYOUT
CALL .CRLF
PRINT SRTXT1 ;WRITE "DRIVE (DEFAULT = X): "
OSV0A$: CALL .CHRIN
CMP AL,13
JNE OSV1$
MOV AL,SRDRV ;GET DEFAULT DRIVE
OSV1$: CMP AL,8
JNE OSAB1$
CALL .CRLF
JMP PFALSE
OSAB1$: CMP AL,"a"
JL OSAB2$
SUB AL,32 ;UPPERCASE DRIVE LETTERS
OSAB2$: MOV BL,AL
SUB AL,"A" ;GET DOWN TO BASICS
CMP AL,3
JG OSV1A$ ;LOSING DRIVE NUMBER
CMP AL,0
JGE OSV1B$
OSV1A$: CALL FEEP ;EXPRESS DISPLEASURE
JMP OSV0A$
OSV1B$: MOV SRDRV,BL
MOV AL,BL
MOV AH,NRMATR
CALL .TTYOUT ;ECHO THE DRIVE LETTER
CALL .CRLF
PRINT SRTXT3 ;WRITE "Insert save disk..."
CALL .CHRIN
CMP AL,8 ;ABORT REQUESTED?
JNE OSV3Z$
CALL .CRLF
JMP PFALSE
OSV3Z$: MOV AL,SRPOS ;GET POSITION
SUB AL,"0"
SUB AH,AH
MOV BL,SRDRV
SUB BL,"A"
MOV DSKDRV,BL
XCHG DI,SP
PUSH ZPC1
PUSH ZPC2
PUSH ZLOCS
PUSH ZORKID
MOV STKBOT,SP ;SAVE THE WORLD
MOV SP,DI
MOV CL,5
SHL AX,CL ;CHANGE TO BLOCK
PUSH AX ;SAVE STARTING BLOCK #
PUSH ES ;SAVE ES
MOV DX,SS
MOV ES,DX ;FAKE ES TO BE STACK SEGMENT
MOV BX,OFFSET STKBOT
MOV CX,1
CALL SRBLKS ;READ/WRITE STACK
PUSHF
MOV DI,SP ;SAVE SP
MOV SP,STKBOT ;RETRIEVE SAVED USER STACK POINTER
POP AX
CMP AX,ZORKID ;CHECK GAME VERSIONS
JE OSVT0$
FATAL FTL4
OSVT0$: POP ZLOCS
POP ZPC2
POP ZPC1
XCHG DI,SP
POPF
POP ES
POP AX
JC OSV4$ ;DIE HERE IF FIRST READ/WRITE FAILED
INC AX ;NEXT BLOCK STARTS
SUB BX,BX ;START AT ES:0
MOV CX,PURBOT ;NUMBER OF PURE BLOCKS
CALL SRBLKS
PUSHF ;SAVE FLAGS FROM CALL
CALL .CRLF
CMP DSKDRV,0 ;CHECK FOR DRIVE 0
JNZ OSV3C$
PRINT SRTXT4 ;WRITE "Insert game disk..."
CALL .CHRIN ;WAIT UNTIL READY
CALL .CRLF
OSV3C$: CALL NEWZPC ;PAGE MAY HAVE CHANGED, LOSER!
POPF ;GET FLAGS BACK
JC OSV4$
MOV AL,SCRFLG ;GET CURRENT SCRIPT STATE
PTAWRD [PFLAGS],A ;AND MAKE RESTORED FLAGS REFLECT IT
JMP PTRUE
OSV4$: CALL .CRLF
JMP PFALSE
OPSAVE ENDP
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
OPQUIT PROC
JMP FINISH
OPQUIT ENDP
OPSCRN PROC
SUB AH,AH ; ONLY HAVE LOWER BYTE
MOV CURSCR,AL ; put screen # into current screen
CMP AX,0 ; was it 0
JNE OPSCR1 ; no then test #1
MOV DX,OLDCUR ; save old cursor position
CMP COLFLG,0 ; TEST IF COLOR ON
JZ DIP1
MOV NRMATR,17H ; give them color
JMP OPSCR2
DIP1: MOV NRMATR,07H ; GIVE THEN NOTHING
JMP OPSCR2
OPSCR1: CMP AX,1 ; was it 1
JNE OPSCR3
CURGET
MOV OLDCUR,DX ; no then ignore
CMP COLFLG,0 ; TEST IF COLOR ON
JZ DIP3
MOV NRMATR,75H ; give them color
JMP DIP4
DIP3: MOV NRMATR,07H ; GIVE THEM NOTHING
DIP4: MOV DH,1 ; put it at 1,1
MOV DL,LFTMAR ; UPPER LEFT-HAND CORNER
OPSCR2: MOV AH,2 ; set cursor
MOV BH,0 ; graphics page
INT 10H
OPSCR3: RET
OPSCRN ENDP
OPSPLT PROC
CMP AX,0 ; split zero
JE OPSPLU
CURGET
MOV OLDCUR,DX
SUB LINMAX,AX ; subtract max plus one
ADD AX,1
MOV SCRN0,AL ; USED IN SCROLL FOR CRLF
MOV CL,LFTMAR
MOV CH,01 ; UPPER LEFT CORNER
MOV DX,RWIDTH ; LOWER RIGHT ROW
SUB DL,LFTMAR
SUB DL,1
MOV DH,SCRN0 ; LOWER RIGHT COLUMN
MOV AL,0 ; blank entire screen
MOV AH,6 ; clear the screen
CMP COLFLG,0 ; IS IT COLOR?
JZ DAN1
MOV BH,2FH ; WHITE ON GREEN
JMP DAN2
DAN1: MOV BH,07H ; IF NO COLOR THEN BLACK ON WHITE
DAN2: INT 10H ; CLEAR TOP WINDOW
MOV DX,OLDCUR ; GET BACK CURSOR POSITION
MOV AH,2 ; set cursor
SUB BH,BH ; make sure it's zero
INT 10H
RET
OPSPLU: MOV SCRN0,1
MOV AX,LINMXX
MOV LINMAX,AX
MOV MORLIN,0 ;RESET MORE COUNT
RET
OPSPLT ENDP
OPUSL PROC
PUSH DI
MOV USLMOD,1
CURGET ;GET THE CURSOR LOCATION
PUSH DX ;SAVE IT FOR A RAINY DAY
MOV AX,16 ;FIRST GLOBAL (ROOM)
MOV DI,SLTAB ;USL TABLE IN FORCE
MOV CX,3 ;NUMBER OF THINGS WE PRINT
USLLP$: PUSH AX
CALL GETVAR ;GET THE GLOBAL
CMP AX,[DI] ;HAS IT CHANGED
JE USL1$ ;THANK GOD, NO
CURSET [DI+4] ;MOVE THE CURSOR TO THE RIGHT PLACE
PUSH CX
PUSH DI
CALL WORD PTR [DI+2] ;CALL THE PROPER ROUTINE
CURGET ;WHERE IS THE CURSOR NOW?
POP DI
SUB DX,[DI+6] ;WHERE IS THE END OF THE FIELD?
NEG DX
JLE USLN$ ;DONT PRINT ZERO OR LESS SPACES
MOV AL,DL
MOV AH,USLATR ;THIS IS REVERSE VIDEO
PUSH DI
CALL .SPACE ;PRINT THAT MANY SPACES
POP DI
USLN$: ADD DI,8 ;POINT TO NEXT TABLE ENTRY
POP CX ;RESTORE REGISTERS
USL1$: POP AX
INC AX ;POINT TO NEXT GLOBAL
LOOP USLLP$ ;DO ALL THREE THEN LEAVE
POP DX
CURSET DX ;IT MAY OR MAY NOT BE RAINING, BUT....
MOV USLMOD,0
POP DI
RET
OPUSL ENDP
SUBTTL ARITHMETIC OPERATIONS
PAGE +
;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
OPRAND PROC
SUB AH,AH
MOV RTEMP,AX
MOV AX,RSEED1
MOV BX,RSEED2
MOV RSEED2,AX
CLC
RCR AX,1
RCR BX,1
XOR RSEED1,BX
MOV AX,RSEED1
AND AX,0EFFFH
SUB DX,DX
DIV RTEMP
MOV AX,DX
INC AX ;MUST BE BETWEEN 1 AND N, INCLUSIVE
TEST INIFLG,INIRND
JNZ OPRN1$
JMP PUTVAL ;SIMPLY RETURN
OPRN1$: PUSH AX
CALL OPPRNN ;PRINT NUMBER
MOV AL,32
CALL PUTCHR
POP AX
JMP PUTVAL ;RETURN THE VALUE
OPRAND ENDP
;LESS?
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 +
;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 +
;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
;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
;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)
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)
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 +
;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
;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 +
;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
;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
;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 +
EOLCHR EQU 10 ;LINE-FEED IS END-OF-LINE CHARACTER
GETLIN PROC
PUSH BX
MOV AL,SCRFLG
PUSH AX
MOV SCRFLG,0 ;DEFEAT SCRIPTING DURING INPUT
MOV DX,SI ;HOLD ON TO STA
MOV CL,ES:[SI]
SUB CH,CH ;CX HAS MAX # CHARACTERS TO READ
INC SI ;SI HAS POINTER TO LINE BUFFER
GTLL$: CALL .CHRIN ;GET A CHARACTER
CMP AL,EOLCHR ;IS IT END OF LINE?
JE GTLX$
CMP AL,13
JNE GTL0$
GTLX$: CALL .CRLF
POP AX
MOV SCRFLG,AL ;RESET SCRIPT FLAG
POP BX
RET ;OUR WORK IS DONE
GTL0$: CMP AL,8 ;WAS IT A BACKSPACE
JNE GTL1$
MOV AX,SI
SUB AL,1
CMP DX,AX ;ARE WE AT BEGINNING OF BUFFER?
JNE GTL2$
GTLF$: MOV AX,7
CALL .TTYOUT ;FEEP AT HIM NOW
JMP GTLL$
GTLL1$: DEC SI ;MOVE BUFFER AND COUNT BACK (OR FORWARD)
INC CX
JMP GTLL$
GTL2$: PUSH DX
PUSH CX
CURGET ;READ THE CURSOR (DH = ROW, DL = COLUMN)
POP CX
CMP DL,LFTMAR ;ARE WE AT BEGINNING OF A LINE?
JNE GTL3$
SUB DH,1 ;PREVIOUS LINE
MOV AX,TWIDTH
MOV DL,AL ;END OF LINE
GTL3$: SUB DL,1 ;CURSET/GET ARE ZERO BASED
CURSET ;MOVE IT THERE
MOV AL,1
MOV AH,NRMATR ;NO ATTRIBUTES, ONE SPACE
CALL .SPACE ;WRITE THE SPACE
POP DX
JMP GTLL1$ ;LOOP BACK
GTL1$: JCXZ GTLF$ ;BUFFER FULL
MOV AH,TYPATR
CALL .TTYOUT ;ECHO THE CHARACTER
PUSH AX
PUSH DX
PUSH CX
CURGET ;FIND THE CURSOR
POP CX
MOV AX,TWIDTH
CMP DL,AL ;CHECK IF AT END OF LINE
JNZ GTL5$
CALL .CRLF ;MAKE A NICE CRLF AT END OF LINE
GTL5$: POP DX
POP AX
GTL4$: CMP AL,"A" ; CHECK UPPER CASE RANGE A-Z
JL GTLD$
CMP AL,"Z"
JG GTLD$
ADD AL,20H ;ADJUST FOR LOWER CASE
GTLD$: MOV ES:[SI],AL ; *** THIS PUTS CHAR. IN BUFFER ***
INC SI ;MOVE POINTER, COUNT FORWARD
DEC CL
JMP GTLL$
GETLIN ENDP
PRTLOS PROC
MOV AL,SCRFLG
PUSH AX
PUSH DX
MOV SCRFLG,0 ;TURN OFF SCRIPTING TO AVOID GETTING HUNG
PRINT PRNRDY
PRNL$: CALL .CHRIN
CMP AL,"A"
JE PRNA$
CMP AL,"a"
JE PRNA$
CMP AL,"R"
JE PRNR$
CMP AL,"r"
JE PRNR$
MOV AL,7
CALL .TTYOUT
JMP PRNL$
PRNA$: CLC
PRNEX$: PUSHF
CALL .CRLF
POPF
POP DX
POP AX
MOV SCRFLG,AL ;RESTORE SCRIPT FLAG
RET
PRNR$: STC
JMP PRNEX$
PRTLOS ENDP
PRTOUT PROC
PRTRT$: MOV AH,0 ;CODE FOR OUTPUT TO PRINTER PORT
MOV DX,0
INT 17H ;FORCE IT OUT
TEST AH,1 ;ERROR TIME OUT CODE
JZ PRTO1$ ;LOST ON TIME OUT
CALL PRTLOS
JC PRTRT$ ;RETRY IF SO ORDERED
GTAWRD A,[PFLAGS]
AND AX,0FFFEH ;TURN OFF THE SCRIPT BIT
PTAWRD [PFLAGS],A
MOV SCRFLG,0 ;AND UNSET THE FLAG
STC ;INDICATE LOSER WANTS ABORT
RET
PRTO1$: CLC ;INDICATE WIN
RET
PRTOUT ENDP
SCRCHK PROC
PUSH AX
PUSH BX
PUSH DX
GTAWRD A,[PFLAGS]
TEST AL,1 ;CHECK IF SCRIPTING IS REQUESTED
JZ SCRNN$
CMP SCRFLG,0 ;CHECK WHETHER THIS IS A NEW STATE
JNE SCR1$
SCRR1$: PUSH DX
MOV DX,0 ;FIRST PRINTER SLOT
MOV AH,1
INT 17H ;INITIALIZE PORT
TEST AH,0A1H ;CHECK FOR TIME OUT,BUSY, OUT OF PAPER
JNZ SCRR2$
POP DX
JMP SCR1$
SCRR2$: POP DX
CALL PRTLOS ;ASK USER WHAT TO DO NOW?
JC SCRR1$ ;RETRY
GTAWRD A,[PFLAGS]
AND AX,0FFFEH ;TURN OFF THE SCRIPT BIT
PTAWRD [PFLAGS],A
MOV SCRFLG,0 ;AND FLAG
SCREX$: POP DX
POP BX
POP AX
RET
SCR1$: MOV SCRFLG,1
SUB CX,CX ;COUNT OF CHARS PRINTED
MOV BP,DX ;START OF INPUT LINE
INC BP ;FIRST CHAR IS LENGTH OF BUFFER
SCR1L$: MOV AL,ES:[BP]
INC BP ;GET CHARACTER
CMP BP,SI ;END OF INPUT?
JLE SCR2L$
CALL PRTCRL
JMP SCREX$
SCR2L$: CALL PRTOUT ;OUTPUT ONE CHARACTER TO THE PRINTER
JC SCREX$ ;THIS MEANS SCRIPTING ABORTED
INC CX
CMP CX,TWIDTH
JNE SCR1L$
CALL PRTCRL
JC SCREX$
SUB CX,CX ;RESTART COUNT
JMP SCR1L$ ;GO FOR MORE
SCRNN$: MOV SCRFLG,0
JMP SCREX$
SCRCHK ENDP
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)
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 ;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 ;CHECK FOR SCRIPTING, SCRIPT INPUT IF ON
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 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
CALL .PRINT ;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 ;SAVE THE NUMBER IN RET TABLE
SUB BX,RDBOS ;BYTE OFFSET FOR BEGINNING OF WORD
MOV ES:[DI+3],BL ;STORE IT, TOO
MOV BP,CX ;MAKE WORD STRING ASCIZ
MOV BYTE PTR DS:[BP],0
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 ;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
POP DI ;RESTORE USER STACK POINTER
RET ;AND RETURN
OPREAD ENDP
;PRINTC (PRINT CHAR WHOSE ASCII VALUE IS GIVEN)
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
;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,0
JNE OPH00$
MOV AL,12
OPH00$: CMP AL,12
JLE OPH1$
SUB AL,12 ;HOUR SLOT IS 24 HOUR TIME
OPH1$: 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
;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
;CRLF (DO A NEWLINE)
OPCRLF PROC
JMP NEWLIN ;DO A NEWLINE
OPCRLF ENDP
SUBTTL CONTROL OPERATIONS
PAGE +
;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 DS:[BP] ;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
;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
;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 PROC
PUSH AX ;SAVE BLOCK-POINTER
CMP AX,ENDLOD ;IS THIS A PRELOADED LOCATION?
JGE GTY1$ ;NO
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
GTY1$: CALL GETPAG ;FIND THE PROPER PAGE
ADD AX,BX ;POINT TO DESIRED BYTE
XCHG AX,BX
MOV CL,ES:[BX] ;GET IT
GTY2$: SUB CH,CH ;CLEAR UNWANTED BYTE & RETURN IT
MOV BX,AX
POP AX ;RESTORE BLOCK-POINTER
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
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
NXTBYT PROC
PUSH BX ;SAVE
MOV BX,ZPC2 ;BYTE POINTER
ADD BX,CURPAG ;INDEX INTO CURRENT PAGE
PUSH ES:[BX] ;SAVE BYTE
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
RET ;AND RETURN IT
NXTBYT ENDP
;GET THE NEXT WORD, RETURN IT IN AX
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
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
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
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
BYTVAL PROC
SUB AH,AH ;THIS ENTRY FOR BYTE VALUE TO CLEAR HIGH BYTE
JMP PUTVAL
BYTVAL ENDP
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
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
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
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
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 +
;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 +
PADCHR EQU 5 ;ZSTR PADDING CHARACTER
;OUTPUT A ZSTR, BLOCK-POINTER IN AX, BYTE-POINTER IN BX
;RETURN UPDATED POINTER
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
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
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
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
PUTCHR PROC
PUSH BP
CMP USLMOD,0
JE PTC0$
PUSH AX ;SAVE EVERYTHING
PUSH BX
PUSH CX
PUSH SI
PUSH DI
SUB BH,BH
MOV AH,9
MOV CX,1
MOV BL,USLATR
INT 10H ;OUTPUT CHARACTER IN INVERSE VIDEO
MOV AH,3
INT 10H ;READ CURSOR
INC DX
MOV AH,2
INT 10H ;MOVE CURSOR FORWARD
POP DI
POP SI
POP CX
POP BX
POP AX
POP BP
RET
PTC0$: MOV BP,CHRPTR ;END OF BUFFER?
CMP BP,ENDBUF
JNE PTC7$ ;NO
PUSH AX ;YES, ENTER OUTPUT ROUTINE; SAVES
PUSH BX
PUSH SI
MOV BX,ENDBUF ;END OF BUFFER
MOV SI,OFFSET OUTBUF;BEGINNING OF BUFFER
PTC1$: DEC BX ;SEARCH FOR SPACE BACKWARDS FROM END
CMP BYTE PTR [BX]," "
JE PTC3$
CMP BX,SI ;STOP AT BEGINNING OF BUFFER
JNE PTC1$
PTC2$: PRINT OUTBUF ;NO SPACES, OUTPUT WHOLE LINE
MOV CHRPTR,SI ;RESTORE POINTER TO BEGINNING OF BUFFER
MOV BP,SP
CMP BYTE PTR [BP+4]," " ;DID A SPACE CAUSE BUFFER OVERFLOW?
JNE PTC6$ ;NO
MOV WORD PTR [BP+4],0 ;YES, FLUSH IT
JMP PTC6$ ;AND RETURN
PTC3$: CMP BX,SI ;DEGENERATE CASE WITH SPACE AT OUTBUF?
JE PTC2$ ;YES, OUTPUT WHOLE LINE
MOV BYTE PTR [BX],0 ;NO, OUTPUT UP TO SPACE
PRINT OUTBUF
MOV AX,ENDBUF ;END OF BUFFER
INC BX ;START GETTING CHARACTERS AFTER THE SPACE
PTC4$: CMP BX,AX ;AT END YET?
JE PTC5$ ;YES
MOV BP,AX ;SAVE AX
MOVM [SI],[BX],AL ;NO, COPY NEXT CHAR TO BEGINNING OF BUF
MOV AX,BP ;RESTORE AX
INC BX
INC SI
JMP PTC4$ ;AND CONTINUE
PTC5$: MOV CHRPTR,SI ;NEXT CHAR WILL GO AFTER COPIED STUFF
PTC6$: POP SI ;CLEAN UP
POP BX
POP AX
CMP AX,0 ;AND IGNORE A NULL CHARACTER
JE PTC8$
PTC7$: MOV BP,CHRPTR ;NOW STORE THE CHARACTER IN BUFFER
MOV DS:[BP],AL
INC CHRPTR ;UPDATE POINTER
PTC8$: POP BP ;RESTORE
RET ;AND RETURN
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
EOICHR EQU 15 ;CARRIAGE RETURN IS NORMAL END OF INPUT
SUBTTL TOP LEVEL STUFF
PAGE +
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 10Q ;VOCAB
POBJTB EQU 12Q ;OBJECT
PGLOTB EQU 14Q ;GLOBALS
PPURBT EQU 16Q ;PURBOT
PFLAGS EQU 20Q ;USER FLAG WORD
PSERNM EQU 22Q ;SERIAL NUMBER (6 BYTES)
PWRDTB EQU 30Q ;WORDS
PLENTH EQU 32Q ;GAME LENGTH
PCHKSM EQU 34Q ;GAME CHECKSUM
LMOUTB EQU 80 ;MAX LENGTH OF OUTBUF, EXCLUDING TERMINAL 0
LDOUTB EQU 80 ;DEFAULT LENGTH OF OUTBUF
LPAGES EQU 128 ;MAX NUMBER OF PAGES EXPECTED
LXBYTS EQU LSTACK+LMOUTB+(4*LPAGES)+2000Q ;LENGTH OF EXTRA BYTES NEEDED
;INITIALIZATION
TSETUP PROC
TEST INIFLG,INIWID ;CHECK FOR FORCED 40 COLUMN MODE
JZ TS0$
MOV TWIDTH,38 ;FORCE IT TO 40
TS0$: MOV BX,OFFSET OUTBUF ;START OF OUTPUT BUFFER
ADD BX,TWIDTH ;ADD LENGTH OF BUFFER
SUB BL,LFTMAR ; *** SUBTRACT THE LEFT MARGIN VALUE
MOV ENDBUF,BX ;THIS IS ENDBUF POINTER
MOV BYTE PTR [BX],0 ;BUFFER IS FOLLOWED BY A 0 FOR CONVENIENCE
MOV BX,OFFSET SLSTAB ;GET STATUS LINE TABLE
CMP TIMEMD,0 ;CHECK FOR SCORE/TIME MODE
JE TS1$
ADD BX,8 ;THE TIME MODE TABLE IS OFFSET FOUR WORDS
TS1$: MOV SI,0 ;OFFSET INTO PROPER TABLE
CMP TWIDTH,38 ;CHECK TERMINAL WIDTH
JE TS2$
ADD SI,4 ;OFFSET TO 80 COLUMN STUFF
TS2$: MOV AX,[BX][SI]
MOV SLSTR,AX
MOV CX,2[BX][SI]
MOV SLTAB,CX ;GET THE RIGHT POINTERS TO STRING/TABLE
MOV BL,USLATR ;INVERSE VIDEO, STRING ALREADY IN AX
MOV CX,0 ;AT 0,LFTMAR
CALL .PRINTA ;PRINT THE STATUS LINE
RET
TSETUP ENDP
ZIPBGN PROC
CALL SYSINI ;DO ANY SYSTEM INITIALIZATION
CALL BKCHK ;CHECK FOR MAKING BACKUP COPY
STR5$: CALL .GETMEM
MOV BX,ES
SUB AX,BX
MOV MEMTOP,AX ;LAST AVAILABLE PARAGRAPH LOCATION
SUB AX,AX ;BLOCK 0
SUB BX,BX ;PUT AT BEGINNING OF GAME SEGMENT
CALL GETBLK ;GET THE BLOCK
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
STR10$: GTAWRD A,[PZRKID] ;UNIQUE GAME & VERSION INDENTIFER
MOV ZORKID,AX
OR BYTE PTR ES:[PVERS2],0020H ;SET SPLIT BIT
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
DEC CX ;NUMBER OF BLOCKS LEFT TO LOAD
MOV AX,1 ;STARTING WITH BLOCK 1
MOV BX,200H ;LOCATION TO PUT THEM
CALL GTBLKS ;GET THE BLOCKS
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],DS:[BP],AL ;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
MOV SI,ENDLOD ;GET # PAGES IN ENDLOD
NEG SI
MOV DI,MEMTOP ;GET PARAGRAPHS OF MEMORY
MOV CL,5
SAR DI,CL ;CHANGE TO PAGES
ADD SI,DI ;NUMBER OF PAGES FOR PAGING
MOV BUFPGS,SI ;SAVE THIS NUMBER FOR FUTURE REFERENCE
CMP BUFPGS,63
JLE STR16$
MOV BUFPGS,63 ;OUR TABLE IS ONLY SO BIG....
MOV SI,63
STR16$: SHL SI,1
SHL SI,1 ;EACH PAGE HAS 4 BYTES OF INFO
MOV BX,OFFSET PAGTAB ;POINT INTO PAGE INFO TABLE
ADD SI,BX
MOV WORD PTR [SI],-1 ;MAKE THIS THE END OF TABLE MARK
CALL .GETTM ;GET THE CURRENT TIME-OF-DAY
MOVM RSEED1,TIME[2],AX;USE IT TO INITIALIZE RANDOM NUMBER SEED
MOVM RSEED2,TIME,AX ;BE CONSISTENT
STR17$: JMP START1
ZIPBGN ENDP
PRSPC PROC
PUSH AX
PUSH BX
PUSH CX
CALL OPPRNN
MOV AL,32
CALL PUTCHR
POP CX
POP BX
POP AX
RET
PRSPC ENDP
;RESTART EXECUTION HERE
RESTRT PROC
INT 19H ; REBOOT THE SUCKER FOR NOW
RET ; I'M SENTIMENTAL
START1: CALL WPCHK ;MAKE SURE GAME DISK IS WRITE-PRO
CALL TSETUP ;SETUP TERMINAL/STATUS LINE
CALL CLRSCR ;CLEAR THE REMAINDER OF THE SCREEN
MOV SP,OFFSET STK_TOP ;INIT OUR STACK POINTER
MOV DI,OFFSET ZSTK_TP ;INIT USER STACK POINTER
MOV CHRPTR,OFFSET OUTBUF ;INIT OUTPUT CHAR. POINTER
MOV ZLOCS,DI ; LOCALS WOULD START AT FIRST SLOT,
SUB ZLOCS,2 ; IF THERE WERE ANY
GTAWRD A,[PSTART]
CALL BSPLTB ;SPLIT BLOCK & BYTE POINTERS
MOV ZPC1,AX ;INIT ZPC BLOCK-POINTER
MOV ZPC2,BX ;INIT ZPC BYTE-POINTER
CALL NEWZPC ;GET PAGE TO EXECUTE
CMP INIFLG,5
JNE FUBAR$
MOV AX,CS
CALL PRSPC
MOV AX,DS
CALL PRSPC
MOV AX,ES
CALL PRSPC
MOV AX,SS
CALL PRSPC
MOV AX,ENDLOD
CALL PRSPC
MOV AX,PAGES
CALL PRSPC
MOV AX,BUFPGS
CALL PRSPC
MOV AX,MEMTOP
CALL PRSPC
FUBAR$: MOV FUCK,1
JMP NXTINS
RESTRT ENDP
;MAIN INSTRUCTION INTERPRETATION LOOP
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,DS:[BP+EXTOP]
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 CS:BYTE PTR [SI],90H ;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,DS:[BP+EXTOP]
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 CS:BYTE PTR [SI],90H ;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,DS:[BP+ONEOP]
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,DS:[BP+ZEROOP]
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 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 NWZ1$ ;WAS 0
OR BX,80H ;WAS 1, SET PROPER BIT IN BLOCK-POINTER
NWZ1$: MOV ZPC1,BX ;SAVE IT
CMP BX,CURBLK ;HAS IT CHANGED?
JE NWZ5$ ;NO
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 DS:[BP],RTIME1,CL
MOVM DS:[BP+1],RTIME2,CX
INC AX
NWZ2$: CMP BX,ENDLOD ;NEW PAGE ALREADY IN CORE?
JL NWZ3$ ;YES
MOV AX,BX ;NO, GET NEW PAGE
CALL GETPAG
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 DS:[BP],-1
MOV WORD PTR DS:[BP+1],-1
INC AX
JMP NWZ4$
NWZ3$: MOV CL,9 ;CALCULATE PAGE ADDRESS
SHL BX,CL
MOV CURTAB,0 ;CLEARING POINTER MEANS PAGE IS PRELOADED
NWZ4$: MOV CURPAG,BX ;UPDATE PAGE POINTER
NWZ5$: 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 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
MOV LPTAB,BX ;SAVE IT
SUB BX,OFFSET PAGTAB;CALCULATE ADDRESS OF PAGE
MOV CL,7
SHL BX,CL
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 NUMBER
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
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
MOV CL,7
SHL AX,CL
ADD AX,PAGES
POP CX ;RESTORES
RET
FNP4$: FATAL FTL6
FINDPG ENDP
SUBTTL HIGH LEVEL IBMPC DEPENDENT STUFF
;SYSTEM INITIALIZATION
SYSINI PROC
MOV AH,15
INT 10H ;CHECK VIDEO STATE
MOV BL,AH ;GET SCREEN WIDTH
SUB BH,BH ;AND ZERO HIGH END
MOV TWIDTH,BX ;SAVE IT AWAY
MOV RWIDTH,BX
SUB TWIDTH,2
INC LFTMAR ; FIX FOR OVERSCAN
CMP AL,7
JE SINIX$
TEST AL,1
JZ SINIX$
PRINT COLORQ ;ASK IF COLOR IS REALLY NEEDED
CALL .CHRIN ;READ A CHARACTER
CMP AL,"Y"
JE SINC$ ;REALLY WANTS COLOR IF Y OR y
CMP AL,"y"
JNE SINIX$
SINC$: MOV COLFLG,AL ; NON-ZERO = THEY WANT COLOR
MOV USLATR,74H ;MAKE STATUS LINE RED ON WHITE IN COLOR MODE
MOV NRMATR,17H ;WHITE ON BLUE FOR THE REST
MOV TYPATR,1FH ;INPUT WILL BE YELLOW ON BLUE
SINIX$: MOV AH,1
INT 16H ;CHECK KEYBOARD
JZ SINI0$ ;NO CHARACTER AVAILABLE
SUB AL,"0"
JL SINIF$
CMP AL,9
JG SINIF$
MOV INIFLG,AL ;SET UP INIT FLAG IF 0-9
SINIF$: MOV AH,0
INT 16H ;BE NICE AND READ THE CHARACTER
SINI0$: INT 11H ;DEVICE DETERMINATION
TEST AL,0C0H ;TEST BITS 7&6 FOR # DRIVES
JNZ SINI1$
MOV SRDRV,"A" ;MAKE DEFAULT DRIVE A FOR 1 DRIVE SYSTEM
MOV NDISKS,1
SINI1$: TEST INIFLG,INIMEM
JZ SIN1A$
MOV NDISKS,1
SIN1A$: TEST INIFLG,8 ;CHECK FOR COLOR SETUP
JZ SINI2$
MOV AL,"U"
CALL GETNUM ;READ NUMBER FOR USL ATTRIBUTE
MOV USLATR,AL
CALL .CRLF
MOV AL,"N"
CALL GETNUM
MOV NRMATR,AL
SINI2$: RET
SYSINI ENDP
CLRSCR PROC
MOV CH,1 ;CLEAR ACTIVE SCREEN AREA
MOV CL,0
MOV DX,TWIDTH
INC DL
CMP DL,40
JLE CLRSC1
INC DL
CLRSC1: MOV DH,18H
MOV AX,0600H
MOV BH,NRMATR
INT 10H ;CLEAR SCREEN, I HOPE
RET
CLRSCR ENDP
GETNUM PROC
MOV BH,0
MOV AH,7
CALL .TTYOUT ;WHAT A PROMPT!
CALL .CHRIN
MOV AH,7
CALL .TTYOUT ;ECHO
SUB AL,"0"
MOV BL,AL
CALL .CHRIN
MOV AH,7
CALL .TTYOUT ;ECHO
SUB AL,"0"
MOV CL,4
SHL BL,CL
ADD BL,AL
MOV AX,BX
RET
GETNUM ENDP
;GET A GAME FILE BLOCK, BLOCK NUMBER IN AX, CORE LOCATION IN ES:BX
GETBLK PROC
PUSH CX ;SAVE
MOV CX,1 ;CALL GTBLKS FOR 1 BLOCK ONLY
CALL GTBLKS
POP CX ;RESTORE
RET
GETBLK ENDP
GAMETRK EQU 6 ;FIRST GAME TRACK (0-39)
SRBLKS PROC
MOV DSKMOD,1 ;SAVE/RESTORE MODE
JMP GTBKI$
SRBLKS ENDP
PTBLKS PROC
MOV DSKDIR,1 ;SET FLAG FOR WRITE
MOV DSKDRV,0 ;ALWAYS FROM DRIVE 0
MOV DSKMOD,0 ;GAME MODE
JMP GTBKI$
PTBLKS ENDP
;GET A SERIES OF GAME FILE BLOCKS,
;FIRST BLOCK NUMBER IN AX, CORE LOCATION IN ES:BX # OF BLOCKS IN CX
GTBLKS PROC
MOV DSKDIR,0 ;READ MODE
MOV DSKDRV,0 ;ALWAYS FROM DRIVE 0
MOV DSKMOD,0 ;GAME MODE
GTBKI$: PUSH DX ;SAVES
PUSH BP
GTBX0$: MOV GTBCNT,CX ;SAVE NUMBER OF BLOCKS TO READ
MOV GTBSTT,AX ;SAVE FIRST BLOCK
MOV GTBCOR,BX ;SAVE STARTING LOCATION
MOV BP,8 ;8 BLOCKS PER TRACK
SUB DX,DX ;CLEAR HIGH WORD FOR DIVIDE
DIV BP ;DETERMINE TRACK AND SECTOR OFFSETS
XCHG AX,CX ;SECTOR OFFSET & NUMBER OF BLOCKS
CMP DSKMOD,0 ;CHECK FOR GAME VS. SAVE/RESTORE
JNE GTBXN$
ADD CX,GAMETRK ;ADD IN FIRST GAME TRACK
GTBXN$: MOV CH,CL ;POSITION TRACK NUMBER
INC DX ;CONVERT TO ONE-BASED SECTOR NUMBER
MOV CL,DL ;POSITION SECTOR NUMBER
MOV AH,DSKDRV ;ALWAYS DRIVE 0
MOV DL,9
SUB DL,CL ;DL NOW HAS MAXIMUM SECTORS LEFT THIS TRACK
CMP AL,DL ;SEE IF WE WANT MORE THAN THE MAX
JLE GTBX1$
MOV AL,DL ;MAKE IT THE MAX THEN
MOV DH,1 ;SIGNAL ANOTHER PASS NEEDED
GTBX1$: CALL .RDWRT ;PERFORM THE DISK OPERATION
JC GTB1$ ;READ/WRITE FAILED IF CARRY SET
CMP DH,1 ;TEST FOR ANOTHER PASS
JNZ GTBX2$
SUB DH,DH ;CLEAN UP SO THAT DL IS # SECTORS JUST HACKED
MOV AX,GTBSTT
ADD AX,DX ;START N SECTORS HIGHER
MOV CX,GTBCNT
SUB CX,DX ;AND N LESS SECTORS LEFT TO HACK
MOV BX,GTBCOR
PUSH CX
MOV CX,9 ;SHIFT #SECTORS BY 9 FOR NEW CORE ADDRESS
SHL DX,CL
POP CX
ADD BX,DX ;HERE'S WHERE TO READ INTO NEXT PASS
JMP GTBX0$
GTBX2$: CLC ;INDICATE DISK OPERATION WON
GTBX3$: POP BP ;RESTORES
POP DX
RET
GTB1$: CMP DSKMOD,0 ;CHECK WHETHER THIS IS GAME MODE
JZ GTB2$ ;YES, FATAL OUT
STC ;SET CARRY FLAG TO INDICATE LOSSAGE
JMP GTBX3$ ;RETURN NORMALLY
GTB2$: FATAL FTL7
GTBLKS ENDP
SUBTTL FRIGGING BACKUP COPY ROUTINE
PAGE +
WPCHK PROC
MOV DSKDIR,1 ; WRITE
MOV DSKDRV,0 ; DRIVE 0
MOV DSKMOD,1 ; ABSOLUTE SECTORS
MOV AX,2FH ; RANDOM SPOT
MOV BX,0
MOV CX,1 ; ONE BLOCK
CALL GTBKI$
CMP AH,3 ; HOPE IT FAILS WITH W/P FAILURE
JE WPCHK1 ; GOOD. WIN
PRINT BKWPS1 ; TELL USER TO W/P DISK
PRINT BKWPS2
CALL .CHRIN ; WAIT FOR CHARACTER
CALL .CRLF
JMP WPCHK ; AND TRY AGAIN
WPCHK1: MOV MORLIN,0
RET
WPCHK ENDP
;CHECK WHETHER BACKUP IS POSSIBLE
BKCHK PROC
CALL TSETUP
CALL BKSTAT ;GET STATUS BITS INTO AL
CMP AL,3 ;THIS IS MASTER/BACKUP ALLOWED
JE BKUP
RET
BKUP: PRINT BKASK1 ;ASK WHETHER BACKUP WANTED
PRINT BKASK2
PRINT BKASK3
PRINT BKASK4
PRINT BKASK5
CALL .CHRIN ;GET A CHARACTER
CMP AL,"!"
JE BKUPF
CMP AL,"Y"
JE BKUP1
CMP AL,"y"
JE BKUP1
PRINT BKNOPE
CALL .CRLF
MOV MORLIN,0
RET ;DOESN'T WANT TO, APPARENTLY...
BKUPF: MOV NDISKS,1
BKUP1: PRINT BKYEP
CALL .CRLF
MOV AL,NDISKS
CMP AL,1
JE BKUP1Z
MOV BKDSK,1 ;MAKE COPY DISK DRIVE 1 FOR >1 DRIVE SYSTEMS
BKUP1Z: CMP NDISKS,1
JE BKZZ
CALL BKIFST ;INSERT MASTER DISK
JMP BKZZ
BKZZ: MOV AX,0 ;SECTOR 0
MOV BX,0 ;LOCATION 0
MOV CX,1 ;ONE SECTOR (BOOTSTRAP)
MOV DSKDIR,0 ;READ
MOV DSKDRV,0
CALL GTBKI$
JNC BKUP1A
JMP BKABRT
BKUP1A: CALL BKICOP ;INSERT COPY DISK
MOV AX,0
MOV BX,0
MOV CX,1
MOV DSKDIR,1
MOVM DSKDRV,BKDSK,DL
CALL GTBKI$ ;WRITE BOOTSTRAP ONTO COPY
JNC BKUP1B
JMP BKABRT
BKUP1B: CALL BKIMST ;ASK TO INSERT MASTER
;CODE HERE FOR FORMATTING/COPYING INTERPRETER IS TAKEN
;FROM CREATE.ASM -- IT'S ALL MAGIC
CALL DSKHAK
MOV DX,3
SUB BX,BX
MOV CH,1
BKRIL: PUSH DX
MOV CL,1
PUSH CX
SUB DX,DX
MOV AX,0204H
INT 13H
JC BKRIF
POP CX
INC CH
ADD BX,1000H
POP DX
DEC DX
JNZ BKRIL
JMP BKWINT
BKRIF: POP DX
POP DX
JMP BKABRT
BKWINT: CALL DSKFIX
CALL BKICOP ;LOAD COPY DISK
CALL DSKHAK
; NOW WRITE IT OUT
MOV CX,0101H
FRML: MOV BX,OFFSET IDTBL
MOV SECCNT,4
FRMLL: MOV [BX],CH
ADD BX,4
DEC SECCNT
JNZ FRMLL
MOV BX,OFFSET IDTBL
MOV DH,0
MOV DL,BKDSK
PUSH ES
MOV AX,DS
MOV ES,AX
MOV AX,0504H
INT 13H ;ES MUST BE = TO DS FOR FORMAT
POP ES
ADD CH,1 ;PUT CHECK FOR FAILURE HERE
DEC TRKCNT
JNZ FRML
MOV TRKCNT,3
MOV BX,0
MOV CX,0101H
MOV DH,0
MOV DL,BKDSK
WLP: MOV AX,0304H
INT 13H
ADD BX,1000H
INC CH
DEC TRKCNT
JNZ WLP
CALL DSKFIX
MOV TRKCNT,4 ;READ MAX. 96 K (THIS WILL CHANGE)
MOV SECCNT,30H ;START OF GAME
BKUPGL: CALL BKIMST ;GET MASTER BACK INTO DRIVE
MOV DSKDIR,0 ;READ
MOV DSKDRV,0
MOV AL,SECCNT
SUB AH,AH ;FIRST SECTOR HERE
MOV BX,0
MOV CX,40H ;GET 32K (64 SECTORS)
CALL GTBKI$ ;READ SOME GAME STUFF
JNC BKUPG1
JMP BKABRT
BKUPG1: CMP TRKCNT,1
JNE BKUP2 ;CONTINUE
MOV DSKDIR,1 ;WRITE
MOV DSKDRV,0
MOV BX,OFFSET BKBUF
MOV CX,1
MOV BKBUF,1 ;PVERS1=0
PUSH ES
MOV AX,DS
MOV ES,AX
MOV AX,2EH
CALL GTBKI$ ;FIX SO IT CAN'T BE COPIED
POP ES
JNC BKUP2
JMP BKABRT
BKUP2: CALL BKICOP ;GET COPY NOW
MOV DSKDIR,1 ;WRITE
MOVM DSKDRV,BKDSK,DL
MOV AL,SECCNT
SUB AH,AH
MOV BX,0
MOV CX,40H
CALL GTBKI$ ;WRITE SOME GAME STUFF
JNC BKUP2A
JMP BKABRT
BKUP2A: SUB TRKCNT,1
JZ BKUP3 ;DONE WITH BACKUP
ADD SECCNT,40H
JMP BKUPGL
BKUP3: PRINT BKDONE
CALL BKIMST ;ENSURE MASTER IN PLACE
MOV MORLIN,0
RET
BKABRT: PRINT BKFAIL
CALL BKIMST ;ENSURE MASTER IN PLACE
MOV MORLIN,0
RET
BKCHK ENDP
DSKHAK PROC
MOV DI,DS
MOV SI,OFFSET DSK_PRM
PUSH DS
SUB AX,AX
MOV DS,AX
ASSUME DS:ABS_SG
MOV BX,78H
MOV CX,[BX]
MOV DX,[BX+2]
MOV [BX],SI
MOV [BX+2],DI
POP DS
ASSUME DS:DATA_SG
MOV DSK_SV1,CX
MOV DSK_SV2,DX
RET
DSKHAK ENDP
DSKFIX PROC
PUSH DS
MOV CX,DSK_SV1
MOV DX,DSK_SV2
SUB AX,AX
MOV DS,AX
ASSUME DS:ABS_SG
MOV BX,78H
MOV [BX],CX
MOV [BX+2],DX
POP DS
ASSUME DS:DATA_SG
RET
DSKFIX ENDP
;READ THE STATUS BLOCK FROM DISK
BKSTAT PROC
PUSH ES
MOV AX,DS
MOV ES,AX
BKSTRT: MOV DSKDIR,0 ;READ
MOV DSKMOD,1 ;ABSOLUTE SECTORS
MOV AX,2EH ;STATUS SECTOR
MOV BX,OFFSET BKBUF ;READ AT BKBUF (IN DS)
MOV CX,1 ;ONE SECTOR
CALL GTBKI$ ;READ IT
JC BKST1 ;FAILED?
CMP BKFRST,1
JE BKSEND
MOV DSKDIR,1 ;WRITE
MOV AX,2FH ;RANDOM SPOT
MOV BX,0
MOV CX,1
CALL GTBKI$ ;WRITE SOMETHING OR OTHER
JC BKST0 ;LOSER!
BKSEND: MOV BKFRST,0
POP ES
MOV AL,BKBUF ;GET FIRST BYTE
RET
BKST0: CMP AH,3 ;CHECK SPECIFICALLY FOR WRITE-PROTECT
JNE BKST1
PRINT BKWPRT ;TELL LOSER ABOUT WRITE PROTECTEDNESS
JMP BKST2
BKST1: PRINT BKNRDY ;TELL LOSER DISK ISN`T WINNING
BKST2: CALL .CHRIN ;GET SOME RANDOM CHARACTER
CALL .CRLF
JMP BKSTRT ;TRY, TRY AGAIN
BKSTAT ENDP
;ASK LOSER TO INSERT MASTER DISK, CHECK FOR MASTERHOOD
BKIFST PROC
PRINT BKMC1
PRINT BKMC2
PRINT BKMC3
CALL .CHRIN
CALL .CRLF
CALL BKIMST
CALL BKICOP
RET
BKIFST ENDP
BKIMST PROC
CMP BKDSK,0
JNE BKIMS1
BKIMSR: PRINT BKMAST ;PRINT MESSAGE
CALL .CHRIN ;GET SOME CHARACTER
CALL .CRLF
BKIMS1: MOV DSKDRV,0
CALL BKSTAT ;GET STATUS FROM DISK, ENSURE NOT W/P
CMP AL,3
JE BKIMSX ;IT'S NOT A MASTER DISK....
CMP AL,1
JE BKIMSX
JMP BKIMSR
BKIMSX: RET
BKIMST ENDP
;ASK LOSER TO INSERT COPY DISK, CHECK FOR COPYHOOD
BKICOP PROC
CMP BKDSK,0
JNE BKICP1
BKICPR: PRINT BKCOPY ;PRINT MESSAGE
CALL .CHRIN
CALL .CRLF
BKICP1: MOVM DSKDRV,BKDSK,DL
CALL BKSTAT
TEST AL,1
JNZ BKICPR ;IT'S NOT A COPY DISK....
RET
BKICOP ENDP
SUBTTL IBMPC SYSTEM ROUTINES
PAGE +
;READ A CHARACTER INTO AX, WAITING UNTIL ONE IS AVAILABLE, NO ECHO
.CHRIN PROC
CMP CHRFLG,0
JNZ .CHR1$
MOV CHRFLG,1
PUSH CX
PUSH DX
SUB AH,AH
INT 1AH ;READ THE SYSTEM TIME AT FIRST CHARACTER
XOR RSEED1,CX ;DO SOME MORE RANDOMIZING
XOR RSEED2,DX ;BE CONSISTENT
POP DX
POP CX
.CHR1$: SUB AH,AH ;SET UP FOR READ NEXT CHARACTER STRUCK
INT 16H ;CALL BIOS KEYBOARD I/O ROUTINE
SUB AH,AH ;CLEAR HIGH BYTE
RET
.CHRIN ENDP
;PRINT THE CHARACTER IN AL, FOREGROUND IN AH
.TTYOUT PROC
PUSH AX
PUSH BX ;SAVES
PUSH DX
PUSH BP
PUSH AX
MOV BL,AH ;FOREGROUND
MOV AH,14 ;SET UP FOR TELETYPE OUTPUT
SUB BH,BH ;PAGE 0
INT 10H ;CALL BIOS DISPLAY I/O ROUTINE
POP AX
CMP SCRFLG,0
JZ .TYO1$
CALL PRTOUT
.TYO1$: POP BP ;RESTORES
POP DX
POP BX
POP AX
RET
.TTYOUT ENDP
;PRINT THE CHARACTER IN AL, ATTRIBUTES IN AH, ROW IN BH, COLUMN IN BL
.CHROUT PROC
PUSH CX ;SAVES
PUSH DX
PUSH BP
PUSH AX ;SAVE CHARACTER AND ATTRIBUTES
MOV AH,2 ;SET UP FOR SET CURSOR POSITION
MOV DX,BX ;ROW AND COLUMN
SUB BH,BH ;PAGE 0
INT 10H ;CALL BIOS DISPLAY I/O ROUTINE
POP AX ;RESTORE CHARACTER AND ATTRIBUTES
MOV AH,9 ;SET UP FOR WRITE ATTRIBUTE/CHARACTER
SUB BH,BH ;PAGE 0
MOV BL,NRMATR ;NORMAL ATTRIBUTES
MOV CX,1 ;WRITE 1 CHARACTER
INT 10H ;CALL BIOS DISPLAY I/O ROUTINE
POP BP ;RESTORES
POP DX
POP CX
RET
.CHROUT ENDP
;PRINT SPACES, WITHOUT MOVING CURSOR, NUMBER IN AL, ATTRIBUTE IN AH
.SPACE PROC
PUSH BX ;SAVES
PUSH CX
PUSH BP
MOV BL,AH ;ATTRIBUTE
SUB AH,AH ;CLEAR HIGH BYTE
MOV CX,AX ;NUMBER OF SPACES
MOV AH,9 ;SET UP FOR WRITE ATTRIBUTE/CHARACTER
MOV AL,32 ;WRITE SPACES
SUB BH,BH ;PAGE 0
MOV BL,NRMATR ;****** THIS SHOULD HELP
INT 10H ;CALL BIOS DISPLAY I/O ROUTINE
POP BP ;RESTORES
POP CX
POP BX
RET
.SPACE ENDP
;MOVE TO FIRST COLUMN OF SCREEN BOTTOM
.CRBT PROC
PUSH AX ;SAVES
PUSH BX
PUSH DX
PUSH BP
MOV AH,2 ;SET UP FOR SET CURSOR POSITION
SUB BH,BH ;PAGE 0
MOV DL,LFTMAR ;MOVE TO FIRST COLUMN
MOV DH,24 ;MOVE TO BOTTOM ROW
INT 10H ;CALL BIOS DISPLAY I/O ROUTINE
POP BP ;RESTORES
POP DX
POP BX
POP AX
RET
.CRBT ENDP
;PRINT A CARRIAGE RETURN/LINE FEED, WITH MORE MODE (ASSUMING SCREEN
;BOTTOM)
.CRLF PROC
PUSH AX ;SAVES
PUSH BX
PUSH CX
PUSH DX
PUSH BP
CMP CURSCR,0
JE .CRLFN
CURGET ;HERE FOR SCREEN 1, PUT CURSOR ON NEXT LINE
ADD DH,1
MOV DL,2 ; ADJUST FOR SONAR IN SEA STALKER
.CRLF1: CURSET
JMP .CR1$
.CRLFN: INC MORLIN ;INCREMENT NUMBER OF LINES OUTPUT
MOV AH,6 ;SET UP FOR SCROLL PAGE UP
MOV AL,1 ;ONE LINE UP
MOV BH,NRMATR ;NORMAL DISPLAY ATTRIBUTE
MOV CL,0
MOV CH,SCRN0
MOV DX,TWIDTH
MOV DH,18H ;END AT LINE 24, COLUMN TWIDTH+1
INT 10H ;CALL BIOS DISPLAY I/O ROUTINE
CALL .CRBT ;MOVE TO BEGINNING OF NEW LINE
MOV AX,MORLIN
CMP AX,LINMAX ;FULL PAGE OUTPUT?
JL .CR1$ ;NOT YET
MOV MORLIN,0 ;RESET COUNTER
MOV AX,OFFSET MORE ;ADDRESS OF MORE MODE PROMPT STRING
MOV BL,USLATR ;INVERSE VIDEO
MOV CH,18H ;STARTING IN ROW 24, COLUMN LFTMAR
MOV CL,LFTMAR
CALL .PRINTA ;PRINT IT WITH ATTRIBUTES
CALL .CHRIN ;READ A CHARACTER TO CONTINUE
CALL .CRBT ;MOVE TO BEGINNING OF NEW LINE
MOV AL,9 ;9 SPACES
MOV AH,NRMATR ;NORMAL DISPLAY
CALL .SPACE ;PRINT THEM (CURSOR UNAFFECTED)
.CR1$: CMP SCRFLG,0
JE .CR2$
CALL PRTCRL ;CRLF TO PRINTER
.CR2$: POP BP ;RESTORES
POP DX
POP CX
POP BX
POP AX
RET
.CRLF ENDP
;PRINT A STRING, POINTER (TO DATA SEGMENT) IN AX, WHITE FOREGROUND
.PRINT PROC
PUSH BX ;SAVE BX
MOV BX,AX ;STRING POINTER
.PR1$: MOV AL,[BX] ;GET NEXT CHARACTER
CMP AL,0 ;END OF LINE, WITH CRLF?
JE .PR2$ ;YES
CMP AL,80H ;END OF LINE, NO CRLF?
JE .PR3$ ;YES
MOV AH,NRMATR ;WHITE FOREGROUND
CALL .TTYOUT ;PRINT CHARACTER
INC BX ;POINT TO NEXT CHARACTER
JMP .PR1$ ;REPEAT
.PR2$: CALL .CRLF ;PRINT A CRLF
.PR3$: POP BX ;RESTORE BX
RET
.PRINT ENDP
;PRINT A STRING, POINTER (TO DATA SEGMENT) IN AX, ATTRIBUTES IN BX,
;STARTING ROW IN CH, STARTING COLUMN IN CL
.PRINTA PROC
PUSH CX ;SAVE CX
PUSH DX ;SAVE DX
PUSH SI ;SAVE SI
PUSH DI ;SAVE DI
MOV SI,AX ;STRING POINTER
MOV DX,BX ;ATTRIBUTES
MOV DI,CX ;ROW AND COLUMN
.PA1$: MOV AL,[SI] ;GET NEXT CHARACTER
CMP AL,0 ;END OF LINE, WITH CRLF?
JE .PA2$ ;YES
CMP AL,80H ;END OF LINE, NO CRLF?
JE .PA3$ ;YES
MOV AH,DL ;ATTRIBUTES
MOV BX,DI ;ROW AND COLUMN
CALL .CHROUT ;PRINT CHARACTER
INC SI ;POINT TO NEXT CHARACTER
INC DI ;MOVE TO NEXT COLUMN
JMP .PA1$ ;REPEAT
.PA2$: CALL .CRLF ;PRINT A CRLF
.PA3$: POP DI ;RESTORE DI
POP SI ;RESTORE SI
POP DX ;RESTORE DX
POP CX ;RESTORE CX
RET
.PRINTA ENDP
;READ AL SECTORS FROM DRIVE AH STARTING AT TRACK CH, SECTOR CL
;TO MEMORY STARTING AT ES:BX
.RDWRT PROC
PUSH DX ;SAVES
PUSH BP
PUSH SI
CMP FUCK,0
JE .RDX$
CMP INIFLG,5
JNE .RDX$
CALL PRSPC
MOV AX,BX
CALL PRSPC
MOV AX,CX
CALL PRSPC
CALL .CRLF
.RDX$: MOV BP,4 ;TRY READING 4 TIMES BEFORE FAILING
MOV DL,AH ;DRIVE NUMBER
MOV AH,2 ;SET UP FOR DISK READ
CMP DSKDIR,0
JE .RD0$
ADD AH,1 ;WRITE IS CONVENIENTLY 3
.RD0$: MOV SI,AX ;SAVE SET UP DATA FOR RETRIES
SUB DH,DH ;HEAD 0
.RD1$: MOV AX,SI ;RESTORE SET UP DATA
INT 13H ;CALL BIOS DISKETTE I/O ROUTINE
JNC .RD2$ ;READ SUCCEEDED
DEC BP ;DECREMENT TRY COUNTER
JNE .RD1$ ;RETRY IF COUNTER NON-ZERO
CMP FUCK,0
JE .RDFL$
CMP INIFLG,5
JNE .RDFL$
CALL PRSPC
CALL .CRLF
.RDFL$: STC ;INDICATE READ FAILED
.RD2$: POP SI ;RESTORES
POP BP
POP DX
RET
.RDWRT ENDP
;GET MEMORY SIZE (IN PARAGRAPHS)
.GETMEM PROC
TEST INIFLG,INIMEM ;CHECK FOR FORCED MEMORY
JZ .GTM1$
MOV AX,0C00H ;48K (IN PARAGRAPHS)
RET
.GTM1$: INT 12H ;CALL BIOS MEMORY SIZE DETERMINATION ROUTINE
CMP AX,512 ;CHECK FOR >512K
JL .GTMX$
MOV AX,500
.GTMX$: SHL AX,1 ;CONVERT FROM K TO PARAGRAPHS
SHL AX,1
SHL AX,1
SHL AX,1
SHL AX,1
SHL AX,1
CMP AX,1000H ;MAXIMUM ALLOWED IS 64K (CROCK)
JLE .GTM2$
MOV AX,1000H
.GTM2$: RET
.GETMEM ENDP
;GET TIME
.GETTM PROC
PUSH AX ;SAVES
PUSH CX
PUSH DX
SUB AH,AH ;SET UP FOR READ TIME OF DAY
INT 1AH ;CALL BIOS TIME OF DAY ROUTINE
MOV TIME,CX ;STORE IN VARIABLE
MOV TIME[2],DX
POP DX ;RESTORES
POP CX
POP AX
RET
.GETTM ENDP
FINISH PROC
CALL .CRLF
PRINT REBOOT
PRINT REBOO1
CALL .CHRIN ;GET A CHARACTER
INT 19H ;REBOOT
FINISH ENDP
CODE_SG ENDS
GAME_SG SEGMENT PAGE AT 800H
GAME_SG ENDS
END START