TITLE ZIP Z-LANGUAGE INTERPRETER 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 ; SUBTTL STACK AND DATA SEGMENTS INITIALIZATION ZVERSN EQU "G" ;ZIP VERSION NUMBER ZMVERS EQU 3 ;Z-MACHINE VERSION NUMBER LSTACK EQU 512 ;LENGTH OF USER STACK(MUST BE 1 PAGE FOR NOW) 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 EOICHR EQU 15 ;CARRIAGE RETURN IS NORMAL END OF INPUT EOLCHR EQU 10 ;LINE FEED CHARACTER 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 CRDLIN EQU 0AH CFOPEN EQU 0FH CFCLOS EQU 10H CFDELE EQU 13H CFMAKE EQU 16H CSDMAO EQU 1AH CRDRND EQU 21H CWRRND EQU 22H CPRSNM EQU 29H GAME_SG SEGMENT PARA DB 0E000H DUP(?) GAME_SG ENDS ABS_SG SEGMENT AT 0H ABS_SG ENDS STK_SG SEGMENT PARA STACK DW 200H DUP(?) STK_TOP LABEL WORD STKBOT DW LSTACK DUP(?) ZSTK_TP LABEL WORD STK_SG ENDS DATA_SG SEGMENT PARA ;VARIBLE DEFINITIONS: ;GTBLKS GAMFCB DB 0,"WITNESS DAT",30 DUP(0) SAVFCB DB 0,"WITNESS SAV",30 DUP(0) SSFCB DB 0,"SETUP INF",30 DUP(0) DSKFCB DW 0 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 DSKBUF DB 512 DUP(0) ;DISK BUFFER FOR TRANSFERS SSBUF DB SSLNT DUP (0) ; SCREEN DEFINITIONS SCPL DB 78 SLPP DB 24 STINIT DB 16 DB 27,"[2J",27,"[0m",27,"[01;01H" STRESET DB 0 SBLINE DB 12 DB 27,"[7m",27,"[01;01H" SELINE DB 12 DB 27,"[0m",27,"[" SELINE1 DB "25;01H" SPINIT DB 0 RADIX DB 10 ; THE DEFAULT RADIX FOR THE SCREEN ;SCRIPTING STUFF SCRFLG DB 0 PRNRDY DB " * Printer not ready: Abort or Retry? ",80H ;USL STUFF SLFLG DB 1 ;STATUS LINE ASSUMED PRESENT 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 40 DUP(32),"S:",5 DUP(32),"M:",6 DUP(32),0,80H SLS40T DW -1 DW OPPRND DW 1 DW 25 DW -1 DW OPPRNN DW 28 DW 30 DW -1 DW OPPRNN DW 35 DW 40 ;40 COLUMN, TIME 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 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 58 DW 62 DW -1 DW OPPRNN DW 74 DW 80 ;80 COLUMN, TIME SLT80 DB 120 DUP(32),"Time:", 13 DUP(32),0,80H SLT80T DW -1 DW OPPRND DW 3 DW 29 DW -1 DW OPPRNH DW 69 DW 71 DW -1 DW OPPRNM DW 72 DW 80 ;OPRAND RSEED1 DW ? ;SEED1 FOR RANDOM NUMBERS RSEED2 DW ? ;SEED2 FOR RANDOM NUMBERS RTEMP DW ? ;TEMP FOR RANDOM ROUTINE ;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 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 OUTBUF DB 81 DUP(?) ;OUTPUT BUFFER INBUF DB 150 DUP(?) ;INPUT 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 ;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 SAVDS DW 0 ;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 ;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 0 ;234 DW 0 ;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 ;MCRLF MORE DB "**MORE** ",80H EMORE DB 13," ",13,80H MORLIN DW 0 ;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 IDSTR DB "MS-DOS 2.0 Interpreter Version G",0 ;TERMINAL SETUP SSMSG1 DB "Cannot open Setup File.",0 SSMSG2 DB "Cannot read Setup File.",0 SSMSG3 DB "Cannot close Setup File.",0 ;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 hit RETURN to continue.",0 ERR1 DB "SAVE file not found",0 ERR3 DB "Bad file name syntax",0 ERR4 DB "No room in directory for SAVE 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 ;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 ;Fatal error header FATHDR DB "Fatal error: ",80H ;ZSTR CHARACTER CONVERSION VECTOR ZCHRS DB "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" DB " 0123456789.,!?_#'" DB '"/\-:()' DBGFLG DB 0 INSCNT DW 0 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 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 + CODE_SG SEGMENT PARA ASSUME CS:CODE_SG,DS:DATA_SG,ES:GAME_SG,SS:STK_SG START PROC FAR MOVM DS,DATA_SG,AX MOVM ES,GAME_SG,AX MOVM SS,STK_SG,AX 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 MTTYO ;BELL RET FEEP ENDP ; ; VERIFY ; OPVERI PROC PRINT IDSTR 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 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 ; ; RESTORE ; OPREST PROC MOV DSKDIR,0 ;INDICATE RESTORE (READ) JMP OSV0$ OPREST ENDP ; ; SAVE ; OPSAVE PROC MOV DSKDIR,1 ;INDICATE SAVE (WRITE) ; ;COMMON ENTRYPOINT FOR SAVE AND RESTORE ; ; DO A DISK RESET IN CASE THEY WANT TO CHANGE DISKS ; UNFORTUNATELY WE HAVE TO ASSUME A 1 DRIVE SYSTEM AT THIS POINT OSV0$: MOV AH,0DH ;CLOSE ALL OPEN FILES INT 21H ; PRINT "FILENAME (DEFAULT IS drv:filename.ext):" ; PRINT "FILENAME (DEFAULT IS drv:" PRINT SAV0 ;PRINT "INSERT SAVE DISK THEN ENTER FILE NAME." PRINT SAV1 ;PRINT "FILE NAME (DEFAULT IS " MOV AL,SAVFCB ;AL <= DRIVE NO. CMP AL,0 ;DRIVE A? JE OSVD1 ;....YES ADD AL,64 ;....NO. ADD 64 TO GET ASCII CHARACTER CALL MTTYO ;PRINT DRIVE B, C, OR D MOV AL,":" ;PRINT ":" CALL MTTYO ; PRINT "filename" OSVD1: MOV CX,8 ;8 CHARACTERS MOV BX,OFFSET SAVFCB ;^ FCB OSVD3: INC BX ;^ NEXT CHARACTER MOV AL,BYTE PTR [BX] ;GET IT IN AL CMP AL,20H ;IS IT A " "? JE OSVD2 ;....YES. WE'RE DONE CALL MTTYO ;....NO. PRINT IT LOOP OSVD3 ;LOOP FOR NEXT CHARACTER ; PRINT ".ext" OSVD2: MOV AL,"." ;PRINT "." CALL MTTYO MOV CX,3 ;3 CHARACTERS IN EXTENSION MOV BX,OFFSET SAVFCB ;^ FCB ADD BL,8 ;CORRECT TO POINT AT ext-1 OSVD4: INC BX ;^ NEXT CHARACTER MOV AL,BYTE PTR [BX] ;GET IT IN AL CALL MTTYO ;PRINT IT LOOP OSVD4 ;LOOP UNTIL ALL THREE ARE PRINTED ; PRINT " ): " PRINT SAV2 ;PRINT " ): " CMP SCPL,50 ;ARE MAX CHARS/LINE >50? JG OSVD4A ;....YES CALL MCRLF ;....NO. PRINT A ; GET LOSER'S SAVE FILENAME FROM THE KEYBOARD OSVD4A: MOV BX,OFFSET INBUF ;^ KEYBOARD INPUT BUFFER MOV AL,15 ;X:FIRSTNAM.EXT 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 MOV AL,10 ;PRINT A CALL MTTYO ; PARSE THE FILENAME PUSH ES ;SAVE'M PUSH SI PUSH DI MOV SI,OFFSET INBUF ADD SI,2 ;SI ^ FIRST CHARACTER IN BUFFER MOV AX,DS ;ES<=DS MOV ES,AX MOV DI,OFFSET SAVFCB ;DI ^FCB MOV AH,CPRSNM ;SELECT DOS FUNCTION MOV AL,0EH ;USE DEFAULTS IN SAVFCB INT 21H ;PARSE FILE NAME (FCB AT SAVFCB) POP DI ;RESTORE'M POP SI POP ES ; OPEN THE FILE IF OP RESTORE CMP DSKDIR,1 ;OP SAVE? JE OSVOPW ;....YES MOV DX,OFFSET SAVFCB ;....NO. OP RESTORE. DX ^ FCB MOV AH,CFOPEN ;SELECT DOS OPEN FUNCTION INT 21H ;OPEN THE FILE CMP AL,0FFH ;FILE EXISTS? JE OSVPSX ;....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 SAVFCB ;^ FCB MOV AH,CFDELE ;DELETE FILE BEFORE OPENING INT 21H MOV DX,OFFSET SAVFCB ;^FCB MOV AH,CFMAKE ;SELECT DOS CREATE FUNCTION INT 21H CMP AL,0FFH ;SUCCESS? JNE OSVPS ;....YES ; ; **** ERROR **** [Directory Full] ; MOV AX,OFFSET ERR4 ;....NO. ^ "NO ROOM IN DIRECTORY" MSG JMP OSVPER ;HOP TO ERROR EXIT ; ;WE'VE GOT AN OPEN FILE (WHICH WE CREATED IF IT WAS A SAVE FUNCTION) ;AND ARE READY TO EITHER READ (RESTORE) OR WRITE (SAVE) AS REQUIRED ; OSVPS: XCHG DI,SP PUSH ZPC1 ; PUSH VITALS ONTO ZSTACK PUSH ZPC2 PUSH ZLOCS PUSH ZORKID MOV STKBOT,SP ; PUT DEST. INDEX INTO STACKBOTTOM MOV SP,DI ; RESTORE SP PUSH AX ; HAS RESULT OF FILE CREATE PUSH ES MOV AX,SS ; FLOP STACK SEG AND EXTRA SEG MOV ES,AX ; ES<=SS MOV AX,0 ; BLOCK NUMBER OF DISK MOV BX,OFFSET STKBOT ; CORE LOCATION MOV CX,2 ; TRANSFER CX*4 (128 BYTES PER)BLOCKS CALL SRBLKS ; DO THE BLOCK TRANSFER PUSHF ;SAVE RETURN CODE MOV DI,SP ;DI ^ CURRENT ZSTACK POSITION MOV SP,STKBOT ;SP <= ZSTACK POINTER POP AX ; POP OFF AND COMPARE FOR ZORKID CMP AX,ZORKID ;ID OK? JE OSVT0 ;....YES 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 POP AX JC OSV4Y ;TRANSFER WAS UNSUCCESSFUL ; ; THE OPEN/CREATE FILE WAS SUCCESSFUL, AND SO WAS THE BLOCK TRANSFER ; SO TRANSFER THE REST ; OSV4X: MOV AX,2 ;BLOCK NUMBER OF DISK SUB BX,BX ;START AT THE BEGINING MOV CX,PURBOT ;CX * 4 BLOCKS TO READ OR WRITE CALL SRBLKS OSV4Y: PUSHF ;SAVE DOS RETURN CODE MOV AH,CFCLOS ;CLOSE THE FILE MOV DX,OFFSET SAVFCB INT 21H OSVCC: MOV AL,SAVFCB ;AL <= DRIVE NO. CMP AL,0 ;DRIVE A? JNE OSV2C ;....NO OSV1C: MOV AH,0DH ;....YES. DO A DISK RESET BEFORE THEY INT 21H ;CHANGE DISKS PRINT SAV3 ;PRINT "INSERT GAME DISK" MSG CALL MCHRI ;WAIT FOR KEYPRESS CALL MCRLF ;PRINT OSV2C: MOV AH,CFOPEN ;OPEN THE GAME FILE MOV DX,OFFSET GAMFCB INT 21H CMP AL,0FFH JE OSV1C ;LOOP UNTIL THEY DO IT CALL NEWZPC ;GET A NEW PC POPF ;RESTORE DOS RETURN CODE JC OSV4 ;ERROR EXIT ; ;ELSE EVERYTHING WORKED SUCCESSFULLY ;AND WE CAN GO BACK TO WHAT WE WERE ;DOING.......... ; MOV AL,SCRFLG ;reenable scripting XCHG AH,AL MOV ES:[PFLAGS],AX 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,CFCLOS ;....YES. CLOSE IT MOV DX,OFFSET SAVFCB INT 21H ; ; **** ERROR **** [Diskette Full] ; 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 MOV AL,SCRFLG ;REENABLE SCRIPTING XCHG AH,AL MOV ES:[PFLAGS],AX JMP PFALSE OSVPER ENDP ; ; OP RESTART ; 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 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 + ;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 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 ;RANDOM ;OPRAND PROC ; MOV BX,AX ;SAVE OPR ; MOV AX,RSEED ;GET THE SEED ; MOV CX,257 ;TRANSFORM IT ; MUL CX ; ADD AX,11 ; MOV RSEED,AX ;SAVE NEW VALUE ; SUB DX,DX ;TRIM IT TO PROPER SIZE ; AND AX,0EFFFH ;CLEAR BIT TO PREVENT POSSIBLE OVERFLOW ; DIV BX ; MOV AX,DX ; INC AX ;MUST BE BETWEEN 1 AND N, INCLUSIVE ; JMP PUTVAL ;SIMPLY RETURN ;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 + GETLIN PROC PUSH BX MOV AL,SCRFLG PUSH AX MOV SCRFLG,0 PUSH SI MOV CL,ES:[SI] MOV BX,OFFSET INBUF MOV BYTE PTR [BX],CL MOV DX,BX MOV AH,CRDLIN INT 21H MOV AH,CCONIO MOV DL,10 INT 21H 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] 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 POP AX MOV SCRFLG,AL POP BX RET GETLIN ENDP PRTOUT PROC PUSH AX PUSH BX PUSH CX MOV DL,AL MOV AH,CPROUT INT 21H POP CX POP BX POP AX RET PRTOUT ENDP SCRCHK PROC PUSH AX PUSH BX PUSH DX GTAWRD A,[PFLAGS] TEST AL,1 ;CHECK IF SCRIPTING IS REQUESTED JZ SCRNN$ MOV SCRFLG,1 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 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 MOV INSCNT,0 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 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 ;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,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 ;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 + ;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 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 DS:[BP],AL 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 PROC NEAR MOV DX,OFFSET SSFCB MOV AH,CFOPEN ; TRY OPENING THE SETUP FILE INT 21H CMP AL,0FFH JE SSET1 ; IF OPEN FAIL, COMPLAIN MOV BX,OFFSET SSFCB MOV WORD PTR [BX+14],SSLNT MOV DX,OFFSET SSBUF MOV AH,CSDMAO ; POINT TO THE INPUT BUFFER INT 21H MOV DX,OFFSET SSFCB MOV AH,CRDRND ; READ INTO THE BUFFER INT 21H CMP AL,0 JNE SSET2 ; IF READ FAIL, COMPLAIN MOV BX,OFFSET SSBUF 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 SSET4: MOV DX,OFFSET SSFCB MOV AH,CFCLOS ; CLOSE THE SETUP FILE INT 21H CMP AL,0FFH JE SSET3 ; IF CANNOT CLOSE FILE, COMPLAIN RET SSET1: PRINT SSMSG1 RET SSET2: PRINT SSMSG2 RET SSET3: PRINT SSMSG3 RET SSETUP ENDP ;INITIALIZATION TSETUP PROC MOV BX,OFFSET SELINE1 MOV AL,SLPP SUB AH,AH INC AX ; FOR SLPP = 24, THE CURSOR IS AT 25 DIV RADIX ADD AH,"0" ADD AL,"0" MOV [BX],AX MOV BX,OFFSET OUTBUF MOV CL,SCPL SUB CH,CH ADD BX,CX MOV ENDBUF,BX MOV BYTE PTR [BX],0 MOV BX,OFFSET SLSTAB CMP TIMEMD,0 JE TSET1 ADD BX,8 TSET1: CMP CX,55 JL TSET2 MOV AX,60 SUB CX,80 MOV SI,4 JMP TSET3 TSET2: MOV AX,15 SUB CX,40 SUB SI,SI TSET3: SUB AX,CX ADD AX,[BX][SI] MOV BX,2[BX][SI] TEST CX,CX JG TSET4 ADD 6[BX],CX TSET4: ADD 12[BX],CX ADD 14[BX],CX ADD 20[BX],CX ADD 22[BX],CX MOV SLSTR,AX MOV SLTAB,BX CALL MINIT RET TSETUP ENDP ;INITIALIZATION TTSETUP PROC MOV BX,OFFSET OUTBUF MOV AL,SCPL SUB AH,AH ADD BX,AX MOV ENDBUF,BX MOV BYTE PTR [BX],0 MOV BX,OFFSET SLSTAB CMP TIMEMD,0 JE TSET1 ADD BX,8 TTSET1: MOV SI,0 CMP SCPL,39 JE TSET2 ADD SI,4 TTSET2: MOV AX,[BX][SI] MOV SLSTR,AX MOV CX,2[BX][SI] MOV SLTAB,CX CALL MINIT RET TTSETUP ENDP ZIPBGN PROC CALL SYSINI ;DO ANY SYSTEM INITIALIZATION STR5$: 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, 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$: 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 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 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 MTIME STR17$: JMP START1 ZIPBGN ENDP ;RESTART EXECUTION HERE RESTRT PROC SUB AX,AX ;REREAD ALL OF THE IMPURE STUFF SUB BX,BX MOV CX,PURBOT CALL GTBLKS JMP RESTRT1 ; PATCH TO FIX STATUS LINE BUG (5)/PHG START1: ; MOV AL,BYTE PTR ES:[PVERS2] ; ADD AL,8 ;TANDY BIT ; MOV BYTE PTR ES:[PVERS2],AL CALL TSETUP ;SETUP TERMINAL/STATUS LINE RESTRT1:CALL CLRSCR ;CLEAR THE REMAINDER OF SCREEN 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 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,CFOPEN MOV DX,OFFSET GAMFCB INT 21H CMP AL,0FFH JE SYSFTL CALL SSETUP RET SYSFTL: FATAL FTL9 SYSINI ENDP CLRSCR PROC RET CLRSCR 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 SRBLKS PROC MOV DSKFCB,OFFSET SAVFCB JMP GTBKI$ SRBLKS 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 DSKFCB,OFFSET GAMFCB GTBKI$: PUSH BX PUSH DX PUSH BP SHL AX,1 SHL AX,1 SHL CX,1 SHL CX,1 MOV GTBSTT,AX MOV GTBCOR,BX MOV GTBCNT,CX GTBLP: MOV AH,CSDMAO MOV DX,OFFSET DSKBUF INT 21H MOV AX,GTBSTT MOV BX,DSKFCB MOV [BX+33],AX MOV AH,CRDRND CMP DSKDIR,0 JE GTBKIR PUSH SI PUSH DI PUSH DS PUSH ES PUSH CX MOV CX,GTBCOR MOV SI,DS MOV DI,ES MOV ES,SI MOV DS,DI ; SWAP DS,ES MOV SI,CX MOV DI,OFFSET DSKBUF MOV CX,128 REP MOVSB POP CX POP ES POP DS POP DI POP SI MOV AH,CWRRND GTBKIR: MOV DX,DSKFCB INT 21H CMP AL,2 ;GOOD/0=successful read, 3=partial record zero padded JE GTB2 ;BAD/2=not enough space on disk, 1=no data available CMP AL,1 JE GTB2 CMP DSKDIR,0 JNE GTBKK PUSH SI PUSH DI PUSH CX MOV SI,OFFSET DSKBUF MOV DI,GTBCOR MOV CX,128 REP MOVSB POP CX POP DI POP SI GTBKK: DEC GTBCNT JZ GTBFIN ADD GTBCOR,128 MOV BX,GTBCOR INC GTBSTT JMP GTBLP GTBFIN: CLC GTBOUT: POP BP POP DX POP BX RET GTB2: CMP DSKFCB,OFFSET GAMFCB JE GTB3 STC JMP GTBOUT GTB3: FATAL FTL7 GTBLKS ENDP SUBTTL IBMPC SYSTEM ROUTINES PAGE + ;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,CCONIN INT 21H SUB AH,AH 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 CMP SCRFLG,0 JZ MTYO1 CALL PRTOUT MTYO1: POP BP POP DX POP CX POP BX POP AX RET MTTYO ENDP ;PRINT SPACES, WITHOUT MOVING CURSOR, NUMBER IN AL, ATTRIBUTE IN AH MSPC PROC RET MSPC ENDP ;PRINT A CARRIAGE RETURN/LINE FEED WITH MORE MODE MCRLF PROC PUSH AX ;SAVES PUSH BX PUSH CX PUSH DX PUSH BP INC MORLIN ;INCREMENT NUMBER OF LINES OUTPUT MOV AH,CCONIO MOV DL,13 INT 21H MOV AH,CCONIO MOV DL,10 INT 21H MOV AL,SLPP SUB AH,AH CMP MORLIN,AX JL MCR1 MOV MORLIN,0 MOV AL,SCRFLG PUSH AX MOV SCRFLG,0 PRINT MORE CALL MCHRI PRINT EMORE POP AX MOV SCRFLG,AL MCR1: CMP SCRFLG,0 JE 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 CALL MSPRT MOV AH,09H MOV DX,SLSTR INT 21H MOV BX,OFFSET SELINE 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 CALL MSPRT MOV BX,OFFSET SELINE CALL MSPRT RET MINIT ENDP ;PRINT A STRING, POINTER (TO DATA SEGMENT) IN AX, WHITE FOREGROUND MPRNT PROC 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 CODE_SG ENDS END START