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