TITLE DIP/8086 PAGE 58,132 .LIST ;---------------------------------------------------------------------- ; DIP/8086 Version 1.0 ; D-CODE INTERPRETER PROGRAM ; FOR IBM-PC UNDER MS-DOS ;---------------------------------------------------------------------- ; INFOCOM, INC. ; 125 CAMBRIDGE PARK DRIVE ; CAMBRIDGE, MA 02140 ; COMPANY PRIVATE -- NOT FOR DISTRIBUTION ;---------------------------------------------------------------------- ; M O D I F I C A T I O N H I S T O R Y ;---------------------------------------------------------------------- ; Date | Programer | Description ;---------------------------------------------------------------------- DEBUG EQU 0 GMAJOR EQU 'C' GMINOR EQU 0 ; C PHG RENAMED GAME.DAT TO FOOBLITZ.DAT ... RELEASE VERSION ; ; B7 PHG FIXED SPEED-UP IN DRAW FOR LOW MEMORY. ; ; B6 PHG FIXED EYE-DROPPER (SEG CROSS IN GTGFST) AND VERIFY. ; PATCHES ARE STILL B5 ; B5 PHG SECOND ROUND OF SPEED UP -- DRAW LOOP ; ; B4 PHG SPEED-UP: AFTER NARROWING DOWN PROBLEMS TO SPECIFIC ; AREAS OF CODE, OPTIMIZATION WAS ATTEMPTED ; ; B3 PHG ADDED TRACE TRAP (CONDITIONAL ASSEMBLY) TO FIND ; OUT WHERE THIS THING WASTES THE MOST TIME ; ; 9/09/85 | PHG | ADDED SAVE AND RESTORE (W/STACK AND ; | I-FILE IN SAVE) AND VERIFY ; 8/29/85 | PHG | FIXED PAGING BUG AND ENHANCED ; 7/18/85 | PHG | ADDED SUPPORT FOR NO GAME PORT ; | ADAPTER ; 6/5/85 | R. LAY | MODIFIED {GINPUT} TO ACCEPT EITHER ; | | KRAFT JOYSTICK BUTTON ; 5/28/85 | R. LAY | IMPLIMENTED {LOAD} AND {DUMP} ; 5/15/85 | R. LAY | MODIFIED {SENSE} AND SOUND ROUTINES ; | | FOR IBM AT COMPATABILITY ; 5/1/85 | R. LAY | 128K VERSION COMPLETE ; 3/4/85 | R. LAY | 64K VERSION FROZEN. START OF 128K. ; 12/31/84 | R. LAY | START 64K VERSION, NO PAGING. SUBTTL NOTES PAGE ;---------------------------------------------------------------------- ; ; I M P L E M E N T A T I O N N O T E S ; ;---------------------------------------------------------------------- ; ; VIRTUAL MEMORY ALLOCATION ; ; Virtual memory exists as a Data File on the game disk. Although two ; data files are created by the implimentor: GFILE.DAT and IFILE.DAT, ; (usually named filename.DIP and filename.PIC) these are concantenated ; while in the DOS command mode by issuing the command: ; CONCAT0 filename, if GFILE.DAT (or filename.DIP) file size is even ; CONCAT1 filename if GFILE.DAT file size is odd. ; ; Note that this scheme allows for easy inclusion of new data files through the ; use of the batch facilities. There is one caution: the filename portion of ; the data files must be the same. This may be accomplished by renaming one or ; both of the files or avoided by modifing the batch files. ; ; Each of the original data files consist of two parts: Preload and Pureload. ; For documentation purposes, these are called GPRE, GPURE, IPRE, and IPURE. ; SUBTTL EQUATES PAGE ;---------------------------------------------------------------------- ; E Q U A T E S ;---------------------------------------------------------------------- ; ----------- ; ERROR CODES ; ----------- ; 00 -- GAME PRELOAD TOO BIG ; 01 -- IMAGE PRELOAD TOO BIG ; 02 -- UNDEFINED X-OP ; 03 -- UNDEFINED 0-OP ; 04 -- UNDEFINED 1-OP ; 05 -- UNDEFINED 2-OP ; 06 -- G-STACK UNDERFLOW ; 07 -- G-STACK OVERFLOW ; 08 -- DIVISION BY ZERO ; 09 -- PURITY VIOLATION (PUT/PUTB) ; 0A -- DISK ACCESS RANGE ; 0B -- DISK ACCESS ; 0C -- NO CALL ADDRESS ; 0D -- UNDEFINED SOUND ; 0E -- PURITY VIOLATION (SETI/SWAPI) ; 0F -- ACTIVE ICON TABLE OVERFLOW ; -------------- ; COMMON EQUATES ; -------------- FALSE EQU 0 ;STANDARD VALUES TRUE EQU 0FFH LO EQU 0 HI EQU 1 ; ----------------------- ; FILE CONTROL STRUCTURES ; ----------------------- RECSIZ EQU 512 ;512 BYTES/RECORD ; ---------------------------- ; MS-DOS INT 21 FUNCTION CALLS ; ---------------------------- CRESET EQU 0H ;PROGRAM TERMINATE CCONIN EQU 1H ;WAIT FOR CONSOLE INPUT WITH ECHO CPROUT EQU 5H ;PRINTER OUTPUT CCONIO EQU 6H ;CONSOLE INPUT NO ECHO NO WAIT CNOECHO EQU 7H ;INPUT NO ECHO CRDLIN EQU 0AH ;CONSOLE LINE INPUT CDRESET EQU 0DH ;DISK RESET CFCREAZ EQU 3CH ;CREATE FILE CFOPENZ EQU 3DH ;OPEN FILE CFCLOSZ EQU 3EH ;CLOSE FILE CRDRNDZ EQU 3FH ;READ RANDOM RECORD CWRRNDZ EQU 40H ;WRITE RANDOM RECORD CFDELEZ EQU 41H ;DELETE FILE CFSEEK EQU 42H ;SEEK CSETBLK EQU 4AH CSELDSK EQU 0EH ; SELECT DISK CURDSK EQU 19H ; ----------------- ; MEMORY ALLOCATION ; ----------------- LSTACK EQU 512 ;SIZE OF THE GSTACK AND MACHINE STACK IN BYTES LMOUTB EQU 80 ;MAX LENGTH OF OUTBUF, EXCLUDING TERMINAL 0 LDOUTB EQU 80 ;DEFAULT LENGTH OF OUTBUF ; --------------------- ; G-CODE HEADER OFFSETS ; --------------------- GHEADER EQU 64 ; LENGTH OF G-FILE HEADER IN BYTES GVERS EQU 0 ; (BYTE) G-MACHINE VERSION GMODE EQU 1 ; (BYTE) MODE BYTE GID EQU 2 ; (WORD) GAME ID GEND EQU 4 ; (WORDPTR) BEGINNING OF NON-PRELOADED CODE GSTART EQU 6 ; (WORDPTR ODD) EXECUTION START ADDRESS GPUR EQU 8 ; (WORDPTR) BEGINNING OF PURE G-CODE GGLOB EQU 10 ; (WORDPTR) START OF GLOBAL VARIABLE TABLE GSER EQU 12 ; (6 BYTES) ASCII SERIAL NUMBER GLEN EQU 18 ; (WORD) LENGTH OF G-PROGRAM IN WORDS GCHECK EQU 20 ; (WORD) G-CODE CHECKSUM GINTVER EQU 22 ; INTERPRETER VERSION LETTER AND NUMBER ; --------------------- ; I-FILE HEADER OFFSETS ; --------------------- IHEADER EQU 8 ; LENGTH OF I-FILE HEADER IN BYTES ILEN EQU 0 ; (WORD) I-FILE LENGTH IN BYTES IEND EQU 2 ; (WORD) END OF I-FILE PRELOAD ICHECK EQU 4 ; (WORD) I-FILE CHECKSUM WORD IBLKS EQU 6 ; (BYTE) # BLOCKSETS IN FILE IICONS EQU 7 ; (BYTE) # ICONS IN FILE ; --------------- ; GENERAL EQUATES ; --------------- ATTIME EQU 004FH ; TIME STANDARD FOR "FAST" PROCESSOR NSNDS EQU 4 ; THERE ARE FOUR SOUND EFFECTS JIFCNT EQU 800H ; NUMBER USED TO COMPENSATE DELAY FOR IBM AT'S VID_SG EQU 0B800H ; VIDEO MEMORY LOCATION BIOSDATA EQU 40H ; BIOS DATA AREA SEG KB_FLAG EQU 17H ; BIOS DATA AREA OFFSET TO KEY FLAGS NUMLOCK EQU 20H ; NUMLOCK BIT BIOS EQU 0F000H ; BIOS SEGMENT BIOSOFF EQU 0E000H ; BIOS OFFSET ID_OFF EQU 0FFFEH ; OFFSET IN BIOS TO MACHINE ID JR_ID EQU 0FDH ; ID BYTE FOR PCjr DOSVER EQU 30H ; GET DOS VERSION NUMBER SUBTTL MAIN PROGRAM PAGE ;---------------------------------------------------------------------- ; M A I N P R O G R A M ;---------------------------------------------------------------------- IF1 %OUT PASS1 ELSEIF %OUT PASS2 ENDIF CSEG SEGMENT PARA PUBLIC ASSUME CS:CSEG,DS:CSEG,ES:CSEG,SS:CSEG ORG 100H ; PUBLIC MSDIP MSDIP PROC JMP START ;SKIP OVER DIP DATA SUBTTL DATA DEFINITIONS PAGE ;--------------------------------------------------------------------- ; D A T A D E F I N I T I O N S ;--------------------------------------------------------------------- ; --------------- ; TRADITIONAL I/0 ; --------------- ; FILE CONTROL STRUCTURES GAMFILE DB 'FOOBLITZ.DAT',0 ;ACIIZ FILE NAME DEFAULT DRIVE DB 49 DUP (0) ;POTENTIAL LENGTH OF FILENAME GAMHNDL DW 0 ;GAME FILE HANDLE DATFILE DB 'GAME.SAV',0 ; ASCIIZ FILE NAME DB 53 DUP (0) ;POTENTIAL LENGTH OF FILENAME, NO DEFAULT DATHNDL DW 0 ;GAME FILE HANDLE LASTSAV DB 'GAME.SAV',0 ; ASCIIZ FILE NAME DB 53 DUP (0) ;POTENTIAL LENGTH OF FILENAME, NO DEFAULT NAMBUF DB 20 DUP (0) ;FILE NAME BUFFER ; DRIVE STUFF LASTDRV DB ? ; LAST LOGGED DRIVE DSKDIR DB -1 ; SAVE OR RESTORE? LSTDFLG DB ? ; LAST DRIVE FLAG DEFDRV DB 0 ; DEFAULT (ORIGINAL) DRIVE DRVFLG DB 0 ; USER SPECIFIED DRIVE? CURDRV DB 0 ; WHERE WE ARE LOGGED CURRENTLY SKPDRV DB 0 ; SKIP DRIVE PRINT OUT ; KEYBOARD HANDLING KYFLGS DB ? ; ORIGINAL KEYBOARD FLAGS XLKEY DB 37H,38H,39H,0,34H,0,36H,0,31H,32H,33H ;MESSAGES ERRTAB DW OFFSET FERR0 DW OFFSET FERR1 DW OFFSET FERR2 DW OFFSET FERR3 DW OFFSET FERR4 DW OFFSET FERR5 DW OFFSET FERR6 DW OFFSET FERR7 DW OFFSET FERR8 DW OFFSET FERR9 DW OFFSET FERR10 DW OFFSET FERR11 DW OFFSET FERR12 DW OFFSET FERR13 DW OFFSET FERR14 DW OFFSET FERR15 DW OFFSET FERR16 DW OFFSET FERR17 DW OFFSET FERR18 ; FERR0 DB 20,"GAME PRELOAD TOO BIG$" FERR1 DB 21,"IMAGE PRELOAD TOO BIG$" FERR2 DB 14,"UNDEFINED X-OP$" FERR3 DB 14,"UNDEFINED 0-OP$" FERR4 DB 14,"UNDEFINED 1-OP$" FERR5 DB 14,"UNDEFINED 2-OP$" FERR6 DB 17,"G-STACK UNDERFLOW$" FERR7 DB 16,"G-STACK OVERFLOW$" FERR8 DB 16,"DIVISION BY ZERO$" FERR9 DB 23,"PURITY VIOLATION (PUTB)$" FERR10 DB 18,"LSEEK OUT OF RANGE$" FERR11 DB 19,"GAME FILE NOT FOUND$" FERR12 DB 15,"NO CALL ADDRESS$" FERR13 DB 15,"UNDEFINED SOUND$" FERR14 DB 23,"PURITY VIOLATION (SETI)$" FERR15 DB 27,"ACTIVE ICON TABLE OVERFLOW$" FERR16 DB 25,"CANNOT SET GRAPHICS MODE$" FERR17 DB 21,"INCORRECT DOS VERSION$" FERR18 DB 24,"INVALID GAME OR VERSION$" ; ERRM DB 13,"FATAL ERROR: $" XITMSG DB " END OF SESSION $" SAV0 DB " Insert SAVE disk then enter file name.$" SAV1 DB " (Default is $" SAV2 DB "): $" SAV3 DB " Insert game disk then strike any key to continue.$" ERR1 DB " SAVE file not found$" ERR4 DB " Unable to access SAVE file$" ERR5 DB " No room on diskette for SAVE file$" ERR6 DB " Read of SAVE file failed$" WAITSTR DB " The game is loading,",13,10 DB " please wait...$" STRIKE DB " Strike any key to continue...$" ; I/O BUFFERS PUBLIC OUTBUF,INBUF,XFRBUF OUTBUF DB 40 DUP(?) ;OUTPUT BUFFER INBUF DB 80 DUP(?) ;INPUT BUFFER XFRBUF DB RECSIZ DUP (?) ;TRANSFER BUFFER USED BY {LOAD} AND {SAVE} ; SPECIAL CONTROL PUBLIC IBMAT,DLYCNT,TIMER PRTOFF DW ? ; (B5) PRINT SCREEN ENTRY POINTS PRTSEG DW ? PCJR DB 0 ; JR FLAG FOR TIMING IBMSTR DB "COPR. IBM$" ; IBM IDENTIFICATION STRING IBMAT DB (?) ;IS THIS AN IBM AT (TRUE OR FALSE)? DLYCNT DW (?) ;DELAY REQUEST IN 1/10 SEC TIMER DB (?) ;1/100 SEC COUNT FROM REAL TIME CLOCK ; USED IN DETERMINING 1/10 SEC DELAY IF DEBUG public itrcnt itrcnt dw 2 dup (0) public ipcount ipcount dd 256 dup (0) ;pc position count -trace trap public isavecs,ishift,ioffset,itop isavecs dw 0 ;saved code seg paragraph itrapcs dw 0 ishift dw 0 ;4 ;8 ;3 ioffset dw 0 ;mloop ;0 ;mloop itop dw OFFSET MEMBUF ;imend ;-1 ;imend t_out db 'tracer.out',0 ; ; ENDIF PAGE ; ----------------------- ; OP-CODE DISPATCH TABLES ; ----------------------- ; 0-OP DISPATCH TABLE OPT0 DW GNOOP ; B0 DW GRTRUE ; B1 DW GRFALS ; B2 DW GRSTAK ; B3 DW GFSTAK ; B4 DW GQUIT ; B5 DW GCOPYP ; B6 DW GVERP ; B7 NOPS0 EQU ($-OPT0)/2 ; # OF VALID 0-OPS ; 1-OP DISPATCH TABLE OPT1 DW GPUSH ; 80 DW GPOP ; 81 DW GVALUE ; 82 DW GINC ; 83 DW GDEC ; 84 DW GZEROP ; 85 DW GBNOT ; 86 DW GJUMP ; 87 DW GRET ; 88 NOPS1 EQU ($-OPT1)/2 ; # OF VALID 1-OPS ; 2-OP DISPATCH TABLE OPT2 DW BADOP2 ; 0 -- UNDEFINED DW GADD ; 1 DW GSUB ; 2 DW GMUL ; 3 DW GDIV ; 4 DW GMOD ; 5 DW GBAND ; 6 DW GBIOR ; 7 DW GBXOR ; 8 DW GBITSP ; 9 DW GEQP ; A DW GLESSP ; B DW GDLESP ; C DW GGRTRP ; D DW GIGRTP ; E DW GSET ; F DW GGET ; 10 DW GGETB ; 11 NOPS2 EQU ($-OPT2)/2 ; # VALID 2-OPS ; X-OP DISPATCH TABLE OPTX DW GCALL ; E0 DW GPUT ; E1 DW GPUTB ; E2 DW GINPUT ; E3 DW GSHOWI ; E4 DW GSETI ; E5 DW GSWAPI ; E6 DW GSOUND ; E7 DW GRAND ; E8 DW GCLEAR ; E9 DW GSHOWN ; EA DW GWIND ; EB DW GITER ; EC DW GLOAD ; ED DW GDUMP ; EE DW GRESTR ; EF DW GSAVE ; F0 NOPSX EQU ($-OPTX)/2 ; # VALID X-OPS PAGE ; ----------------- ; PROGRAM VARIABLES ; ----------------- ; GCODE PROGRAM COUNTER DEFINITION PUBLIC GPC,GPC2,GPC1,GPC0 GPC LABEL WORD GPC2 DB 0 ;LSB OF GPC GPC1 DB 0 ;MSB OF GPC GPC0 DB 0 ;BIT 0 OF BYTE IS BIT 0 OF GPC ; VIRTUAL MEMORY POINTER DEFINITION PUBLIC VMP,VMP2,VMP1,VMP0,VMSAV,VMSAV0 VMP LABEL WORD VMP2 DB 0 ;LSB OF VMP VMP1 DB 0 ;MSB OF VMP VMP0 DB 0 ;BIT 0 OF BYTE IS BIT 0 OF VMP VMSAV DW 0 VMSAV0 DB 0 ; VALUES USED IN COLDSTART PUBLIC GPRBLK,IPRBLK,GCODE,GPEND,ICODE,ISTRT,IPURE,ZORKID GPRBLK DB 0 ;TOTAL NUMBER OF G-PRELOAD BLOCKS IPRBLK DB 0 ;TOTAL NUMBER OF I-PRELOAD BLOCKS GCODE DW 0 ;(WORD) VIRTUAL START ADDRESS OF G-CODE GPEND DB 0 ;(BYTE) LAST VIRTUAL BLOCK OF G-PRELOAD ICODE DW 0 ;(WORD) ABSOLUTE START ADDRESS OF I-PRELOAD ISTRT DB 0 ;(BYTE) 1ST. V-PAGE OF I-PRELOAD IPURE DW 0 ;(WORD) ABSOLUTE START ADDRESS OF I-PURELOAD ZORKID DW ? ; GAME ID ; TABLE ADDRESSES PUBLIC GLOCS,GLOBAL GLOCS DW 0 ;OFFSET TO THE START OF THE DIP LOCAL TABLE GLOBAL DW 0 ;(WORD) ABSOLUTE GLOBAL TABLE ADDR ; OPCODE SUPPORT PUBLIC NARGS,ARG1,ABYTE,ADEX NARGS DB 0 ;(BYTE) CURRENT NUMBER OF ARGUMENTS ARG1 DW 0 ;(WORD) ARGUMENT 1 REGISTER ARG2 DW 0 ;(WORD) ARGUMENT 2 REGISTER ARG3 DW 0 ;(WORD) ARGUMENT 3 REGISTER ARG4 DW 0 ;(WORD) ARGUMENT 4 REGISTER ABYTE DB 0 ;(BYTE) X-OP ARGUMENT BYTE ADEX DB 0 ;(BYTE) X-OP ARGUMENT INDEX ADEX2 DB 0 ;(BYTE) "CALL" ARG-BYTE INDEX PUBLIC CTYPES,CARGS CTYPES DB 16 DUP(?) ;GCALL 'MODE BYTE' LIST CARGS DW 16 DUP(?) ;GCALL ARGUMENTS LIST PUBLIC OPCODE,VALUE,I,J,OLDGSP OPCODE DB 0 ;(BYTE) OPCODE SAVE REGISTER VALUE DW 0 ;(WORD) VALUE RETURN REGISTER I DW 0 ;(WORD) GENERAL PURPOSE INDEX REGISTER #1 J DW 0 ;(WORD) GENERAL PURPOSE INDEX REGISTER #2 OLDGSP DW 0 ;(WORD) OLD G-STACK POINTER ; RANDOM RSEED1 DW 0 ;VARIABLES USED FOR RANDOM NUMBER GENERATION RSEED2 DW 0 RTEMP DW 0 ; INPUT CONTROL PUBLIC STICKA,KEYSAV,JOYSTK STICKA DB 0 ;JOYSTICK FLAGS BYTE KEYSAV DB 0 ;SENSE RETURN CODE JOYSTK DB 0 ;JOYSTICK PRESENCE ; SOUND CONTROL STRUCTURES STABLE DW SND1,SND2,SND3,SND4 PAGE ; --------------------------- ; GRAPHICS CONTROL STRUCTURES ; --------------------------- ; TABLE ADDRRESSES PUBLIC BTAB,ITAB BTAB DW 0 ;(WORD) ABSOLUTE BLOCKSET TABLE ADDRESS ITAB DW 0 ;(WORD) ABSOLUTE ICON TABLE ADDRESS ; ICON DATA PUBLIC NBLOKS,NICONS,IADR1,IADR10,IX1,IY1,XDEX1,YDEX1,BSET NBLOKS DB 0 ;(BYTE) # BLOCKSETS IN I-FILE NICONS DB 0 ;(BYTE) # ICONS IN I-FILE IADR1 DW 0 ;(WORD) ABS ADDR OF ICON #1 IADR10 DB 0 ;(BYTE) LS BIT OF ABS ADDR OF ICON #1 IADR2 DW 0 ;(WORD) ABS ADDR OF ICON #2 IADR20 DB 0 ;(BYTE) LS BIT OF ABS ADDR OF ICON #2 IX1 DB 0 ;(BYTE) XSIZE OF ICON #1 IX2 DB 0 ;(BYTE) XSIZE OF ICON #2 IY1 DB 0 ;(BYTE) YSIZE OF ICON #1 IY2 DB 0 ;(BYTE) YSIZE OF ICON #2 XDEX1 DB 0 ;(BYTE) X-INDEX #1 XDEX2 DB 0 ;(BYTE) X-INDEX #2 YDEX1 DB 0 ;(BYTE) Y-INDEX #1 YDEX2 DB 0 ;(BYTE) Y-INDEX #2 BSET DB 0 ;(BYTE) BLOCKSET ID (1-255) BSADR DW 0 ;(WORD) ABS ADDR OF CURRENT BLOCKSET BSIZE DB 0 ;(BYTE) # BLOCKS IN CURRENT BLOCKSET TOX DB 0 ;(BYTE) X-SAVE FOR "DUMP" BLOCK DB 8 DUP (?) ;(8 BYTES) IMAGE BLOCK BUFFER PUBLIC BLOCK,I_EOP,G_EOP I_EOP DW ? ; (B5) ICON END OF PAGE G_EOP DW ? ; (B5) GRAPHICS EOP ; MASKING PUBLIC MSKFLG,MBLOCK MBLOCK DB 8 DUP (?) ;(8 BYTES) MASK BLOCK BUFFER MSKFLG DB 0 ;(BYTE) FLAG FOR MASKING 00=DISABLED PUBLIC NEGATE,BLKLEN,XPSAV XPSAV DW 0 ;(WORD) X-POSITION SAVE XCURS DB 0 ;(BYTE) GAME CURSOR X-POS YCURS DB 0 ;(BYTE) GAME CURSOR Y-POS NEGATE DB 0 ;(BYTE) BLOCK NEGATE FLAG BLKLEN DB 0 ;(BYTE) # BLOCKS PER BLOCK ; ITERATIONS ITERS DB 0 ;(BYTE) # OF ICON ITERATIONS DOITER DB 0 ;(BYTE) CURRENT ITERATION ITPNT DB 0 ;(BYTE) POINTER INTO CURRENT ICON TABLE NAICN DB 0 ;(BYTE) NUMBER OF ACTIVE ICONS ITICN DB 0 ;(BYTE) COUNTER FOR ITERATE ROUTINE ; WINDOWS PUBLIC XPOS,YPOS XPOS DW 0 ;(WORD) ICON X-POSITION YPOS DW 0 ;(WORD) ICON Y-POSITION WINDX1 DB 0 ;(BYTE) LEFT CLIP VALUE WINDX2 DB 0 ;(BYTE) RIGHT CLIP VALUE WINDY1 DB 0 ;(BYTE) TOP CLIP VALUE WINDY2 DB 0 ;(BYTE) BOTTOM CLIP VALUE TOPCUT DB 0 SIDCUT DB 0 MDXCUT DB 0 WINDH DB 0 WINDW DB 0 IXSKIP DB 0 PUBLIC VDPADR VDPADR DW 0 ;OFFSET INTO VIDEO RAM PAGE ; ; This table is used to translate a Byte of graphics data into a Word of ; IBM graphics data. ; ; The IBM has a screen composed of 320 x 200 pixels. For the sake of ; convenience we use only 320 x 192. In Graphics Mode 4, the IBM utilizes ; 2 Bits/pixel. ; ; BIT 7 6 5 4 3 2 1 0 ; _____________________________________ ; | C1 C0 | C1 C0 | C1 C0 | C1 C0 | ; +--------+--------+--------+--------+ ; ; With the color pallete and background color we've selected, eack Bit pair ; can represent 1 of 4 possible colors. ; ; C1 | C0 | COLOR ; ---+----+----------- ; 0 | 0 | BLACK ; 0 | 1 | CYAN ; 1 | 0 | MAGENTA ; 1 | 1 | WHITE ; ; Since the IBM has twice the horizontal resolution of the GRAPHIC DATA, we ; have to double each pixel. This causes the loss of cyan and magenta. ; Since the GRAPHIC DATA achieves color through archiving, the following ; scheme suffices. ; ; Given: A Byte of graphics data representing 8 pixels eg.: ; ; 00011011B (01BH) ; ; BIT 7 6 5 4 3 2 1 0 ; 0 0 0 1 1 0 1 1 ; PIXEL 1 2 3 4 5 6 7 8 ; ; Becomes a Word of IBM graphics data representing 8 pixels eg.: ; ; 00000011 11001111B (03CFH) ; ; BIT F E | D C | B A | 9 8 | 7 6 | 5 4 | 3 2 | 1 0 ; 0 0 | 0 0 | 0 0 | 1 1 | 1 1 | 0 0 | 1 1 | 1 1 ; PIXEL 1 2 3 4 5 6 7 8 ; ; ; Because all color is produced by artifacting, and the ATARI effect is ; different than the IBM we also right shift the word around 2 bits in ; advance for phase shifting purposes ; PAGE ; GRAPHICS TRANSLATION TABLE PUBLIC GRXTBL GRXTBL DW 0000H,0C000H,0003H,0C003H DW 000CH,0C00CH,000FH,0C00FH DW 0030H,0C030H,0033H,0C033H DW 003CH,0C03CH,003FH,0C03FH DW 00C0H,0C0C0H,00C3H,0C0C3H DW 00CCH,0C0CCH,00CFH,0C0CFH DW 00F0H,0C0F0H,00F3H,0C0F3H DW 00FCH,0C0FCH,00FFH,0C0FFH DW 0300H,0C300H,0303H,0C303H DW 030CH,0C30CH,030FH,0C30FH DW 0330H,0C330H,0333H,0C333H DW 033CH,0C33CH,033FH,0C33FH DW 03C0H,0C3C0H,03C3H,0C3C3H DW 03CCH,0C3CCH,03CFH,0C3CFH DW 03F0H,0C3F0H,03F3H,0C3F3H DW 03FCH,0C3FCH,03FFH,0C3FFH DW 0C00H,0CC00H,0C03H,0CC03H DW 0C0CH,0CC0CH,0C0FH,0CC0FH DW 0C30H,0CC30H,0C33H,0CC33H DW 0C3CH,0CC3CH,0C3FH,0CC3FH DW 0CC0H,0CCC0H,0CC3H,0CCC3H DW 0CCCH,0CCCCH,0CCFH,0CCCFH DW 0CF0H,0CCF0H,0CF3H,0CCF3H DW 0CFCH,0CCFCH,0CFFH,0CCFFH DW 0F00H,0CF00H,0F03H,0CF03H DW 0F0CH,0CF0CH,0F0FH,0CF0FH DW 0F30H,0CF30H,0F33H,0CF33H DW 0F3CH,0CF3CH,0F3FH,0CF3FH DW 0FC0H,0CFC0H,0FC3H,0CFC3H DW 0FCCH,0CFCCH,0FCFH,0CFCFH DW 0FF0H,0CFF0H,0FF3H,0CFF3H DW 0FFCH,0CFFCH,0FFFH,0CFFFH DW 3000H,0F000H,3003H,0F003H DW 300CH,0F00CH,300FH,0F00FH DW 3030H,0F030H,3033H,0F033H DW 303CH,0F03CH,303FH,0F03FH DW 30C0H,0F0C0H,30C3H,0F0C3H DW 30CCH,0F0CCH,30CFH,0F0CFH DW 30F0H,0F0F0H,30F3H,0F0F3H DW 30FCH,0F0FCH,30FFH,0F0FFH DW 3300H,0F300H,3303H,0F303H DW 330CH,0F30CH,330FH,0F30FH DW 3330H,0F330H,3333H,0F333H DW 333CH,0F33CH,333FH,0F33FH DW 33C0H,0F3C0H,33C3H,0F3C3H DW 33CCH,0F3CCH,33CFH,0F3CFH DW 33F0H,0F3F0H,33F3H,0F3F3H DW 33FCH,0F3FCH,33FFH,0F3FFH DW 3C00H,0FC00H,3C03H,0FC03H DW 3C0CH,0FC0CH,3C0FH,0FC0FH DW 3C30H,0FC30H,3C33H,0FC33H DW 3C3CH,0FC3CH,3C3FH,0FC3FH DW 3CC0H,0FCC0H,3CC3H,0FCC3H DW 3CCCH,0FCCCH,3CCFH,0FCCFH DW 3CF0H,0FCF0H,3CF3H,0FCF3H DW 3CFCH,0FCFCH,3CFFH,0FCFFH DW 3F00H,0FF00H,3F03H,0FF03H DW 3F0CH,0FF0CH,3F0FH,0FF0FH DW 3F30H,0FF30H,3F33H,0FF33H DW 3F3CH,0FF3CH,3F3FH,0FF3FH DW 3FC0H,0FFC0H,3FC3H,0FFC3H DW 3FCCH,0FFCCH,3FCFH,0FFCFH DW 3FF0H,0FFF0H,3FF3H,0FFF3H DW 3FFCH,0FFFCH,3FFFH,0FFFFH PAGE ; ; Y-AXIS TRANSLATION TABLE ; ; This table is used to translate the starting row location (0-23) of ; a BLOCK to an offset from the VIDEO_RAM base address. PUBLIC YAXTBL ; Y Axis (X)translation TaBLe YAXTBL DW 1FB0H,20F0H,2230H,2370H DW 24B0H,25F0H,2730H,2870H DW 29B0H,2AF0H,2C30H,2D70H DW 2EB0H,2FF0H,3130H,3270H DW 33B0H,34F0H,3630H,3770H DW 38B0H,39F0H,3B30H,3C70H PAGE ; ------------------------- ; PAGING CONTROL STRUCTURES ; ------------------------- PUBLIC FSIZE,BUFFERS,SEG0,SEG1,BUF,LRU,LOC,LADD,VPAGEN,OLDMRU,MEMMAP FSIZE DB 0 ;NUMBER OF 512 BYTE BLOCKS IN GAME.DAT FILE BUFFERS DB 0 ;NUMBER OF 512 BYTE BLOCKS IN PAGING AREA SEG0 DW ? ;FIRST (OR ZERO) GAME SEGMENT SEG1 DW ? ;SECOND GAME SEGMENT (SEG0 + 64K) BUF DB 0 ;INDEX INTO MEMBUF LRU DB 0 ;LEAST RECENTLY USED PAGE LOC DB 0 ;HIGH ORDER ABSOLUTE ADDRESS BITS LADD DW 0 ;LOW ORDER ABSOLUTE ADDRESS BITS VPAGEN DB 0 ;VIRTUAL PAGE NUMBER OLDMRU DB 0 ;PREVIOUS MRU BUFFER NUMBER MEMMAP DB 512 DUP(?) ;PAGING MEMORY MAP ; ----------------- ; STACK DEFINITIONS ; ----------------- PUBLIC MSTK_TP,MSTK_BT,GSTK_TP,GSTK_BT EVEN MSTK_BT DW LSTACK DUP(?) ;MACHINE STACK BOTTOM MSTK_TP LABEL WORD ;MACHINE STACK TOP GSTK_BT DW LSTACK DUP(?) ;G-CODE INTERPRETER STACK BOTTOM GSTK_TP LABEL WORD ;G-CODE INTERPRETER STACK TOP %OUT CODE SUBTTL SYSTEM INITIALIZATION PAGE ;---------------------------------------------------------------------- ; S Y S T E M I N I T I A L I Z A T I O N ;---------------------------------------------------------------------- PUBLIC START START: MOV SP,OFFSET MSTK_TP ;SETUP STACK PTR (SP) MOV DI,OFFSET GSTK_TP ;SETUP GSTACK PTR (DI) ; JUMP TO BEGINNING OF DIP JMP DIPBGN SUBTTL 0-OP EXECUTORS PAGE ;---------------------------------------------------------------------- ; S U B R O U T I N E S ;---------------------------------------------------------------------- ;--------------------------------------- ; OPCODE EXECUTION PROCEDURES ;--------------------------------------- ;--------------- ; 0-OP EXECUTORS ;--------------- ; ---- ; NOOP ; ---- ; NOOP (NO OPERATION) PUBLIC GNOOP GNOOP PROC RET ;DO NOTHING GNOOP ENDP ; ----- ; RTRUE ; ----- ; DO A "RETURN 1", WHERE 1 IS COMMONLY INTERPRETED AS TRUE. PUBLIC GRTRUE,GRT2 GRTRUE PROC MOV AX,1 ;RETURN A 1 GRT2: MOV ARG1,AX JMP GRET ;RETURN VIA GRET GRTRUE ENDP ; ------ ; RFALSE ; ------ ; DO A "RETURN 0", WHERE 0 IS COMMONLY INTERPRETED AS FALSE. PUBLIC GRFALS GRFALS PROC SUB AX,AX ;RETURN A 0 JMP GRT2 GRFALS ENDP ; ------ ; RSTACK ; ------ ; "RETURN" WITH VALUE FROM GSTACK PUBLIC GRSTAK GRSTAK PROC CALL POPVAL ;GET VALUE INTO AX JMP GRT2 GRSTAK ENDP ; ------ ; FSTACK ; ------ ; FLUSH THE TOP VALUE OFF THE GSTACK PUBLIC GFSTAK GFSTAK PROC JMP POPAXG GFSTAK ENDP ; ----------------------- ; POP AX FROM THE GSTACK ; ----------------------- PUBLIC POPAXG,GSTKU POPAXG PROC XCHG SP,DI ;USE THE REAL SP POP AX ;POP THE STACK XCHG SP,DI ;SWAP BACK PUSH AX ;SAVE REGISTER MOV AX,OFFSET GSTK_TP ;CHECK IF WE HAVE AN UNDERFLOW CMP DI,AX ; JA GSTKU ; IF WE DO IT'S FATAL POP AX ; ELSE, RESTORE THE REGISTER RET ; *** ERROR #6: G-STACK UNDERFLOW *** GSTKU: POP AX MOV AL,6 JMP GERROR POPAXG ENDP ; ---- ; QUIT ; ---- ; THE GAME SHOULD DIE PEACEFULLY ; (MACHINE DEPENDENT) PUBLIC GQUIT GQUIT PROC JMP FINISH GQUIT ENDP ; ----- ; COPYP ; ----- ; *** UNABLE TO FIND IN THE DOCUMENTATION *** PUBLIC GCOPYP GCOPYP PROC JMP PREDS GCOPYP ENDP ; ---- ; VERP ; ---- ; *** UNABLE TO FIND IN THE DOCUMENTATION *** PUBLIC GVERP,GVER1,GVER2,GVER3,GVER4,GVER5,GVER6,GVER7,GVER8 PUBLIC GVER$,GVER$$,GVERA,GVERB,GVERC,GVERD,GVERE GVERP PROC PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI PUSH BP PUSH DS PUSH ES ; RESET THE FILE POINT TO BEGINNING OF THE FILE (LESS THE HEADER) MOV BX,GAMHNDL ; FILE HANDLE MOV CX,0 MOV DX,64 ; OFFSET IN THE FILE MOV AX,4200H ; SEEK INT 21H MOV VMP,0 ; GET CHECK SUM VALUE MOV VMP0,0 MOV AX,GCHECK ; OFFSET IN HEADER CALL ADDVMP CALL GW@VMP ; GET THE WORD THERE MOV J,AX ; SAVE IN GENERAL VAR J ; MOV AX,ICODE MOV VMP,AX ; GET I-FILE CHECK SUM MOV VMP0,0 MOV AX,ICHECK ; OFFSET IN HEADER CALL ADDVMP CALL GW@VMP ; GET THE WORD THERE MOV I,AX ; SAVE I-CHECKSUM IN VARIABLE I MOV BP,0 ; ZERO THIS BABY TO HOLD CURRENT CHECKSUMS ; MOV VMP,0 ; GET LENGTH OF G-FILE MOV VMP0,0 MOV AX,GLEN ; OFFSET IN HEADER CALL ADDVMP CALL GW@VMP ; GET THE WORD THERE MOV DI,AX ; SAVE IT SUB DI,GHEADER/2 ; SUBTRACT THE NUMBER OF BYTES IN HEADER ; FIGURE OUT HOW MUCH SPACE WE HAVE BETWEEN THE END OF PRELOAD ; AND THE END OF THE FIRST 64K BOUNDARY. WE WILL USE THAT SPACE ; AS A BUFFER FOR VERIFY. FIRST WE MUST ZERO OUT THE MEMMAP THAT ; INDICATE IF THOSE PAGES ARE IN CORE AND RESET THERE MEMBUF ; DESCRIPTORS. WE MUST ALSO PLAY WITH THE LRU POINTERS TO NOT ; SCREW UP THE MEMORY MAP. MOV DX,SEG0 ; START OUT USING 0 SEG MOV BL,BUFFERS ; HOW MANY POTENTIAL VERIFY BUFFERS? CMP BL,080H ; NO MORE THAN 64K JB GVER1 SUB BL,80H ; REDUCE IT 64K OR LESS MOV DX,SEG1 ; SWITCH TO 1ST SEG MOV BH,0 ; ZERO TOP HALF PUSH BX ; SAVE NUMBER OF BUFFERS JMP GVERE ; CLEAN OUT MEMBUFS ; ZERO BUFFERS IN SEG 0 GVER1: MOV AH,GPRBLK ; GET TOTAL NUMBER OF PRELOAD BLOCKS ADD AH,IPRBLK ; TO FIGURE BUFFER BOUNDARY SUB BL,AH ; FIGURE --> BUFFERS - PRELOAD PUSH BX MOV CX,BX ; NUMBER OF BUFFERS DESC'S TO CLEAN OUT MOV BX,OFFSET MEMBUF+1 ; GET ADDRESS OF FIRST BUFFER ; MEMBUF ZEROING LOOP ; GVERA: MOV BYTE PTR [BX],0 ; SET NO PAGE IN MEMORY FOR THIS BUFFER ADD BX,4 ; NEXT BUFFER LOOP GVERA ; CLEAN 'EM ALL OUT MOV CX,0FFH ; NOW, CLEAN OUT THE MEMORY MAP MOV BX,0 ; START AT THE BEGINNING GVERB: TEST MEMMAP[BX],1 ; PRELOAD? JZ GVERC ; NOPE, ZERO IT GVERD: ADD BX,2 ; YES, SO SKIP THIS ONE LOOP GVERB ; AND CHECK THE NEXT JMP GVERE GVERC: CMP DX,SEG0 ; WHICH SEG? JE GVERC$ JMP GVERD$ GVERC$: CMP MEMMAP[BX+1],7FH ; IF BUFFER NUMBER < 127, THEN 0 IT JA GVERD GVERD$: MOV MEMMAP[BX+1],0 ; INDICATE PAGE NOT IN CORE JMP GVERD ; INCREMENT AND CONTINUE ; GVERE: POP BX MOV CL,9 SHL BX,CL ; CONVERT BUFFERS INTO BYTES MOV CX,BX ; SAVE NUMBER OF BYTES IN BUFFER MOV ES,DX ; SET ES TO ADDRESS PROPER SEG CMP DX,SEG0 ; WHICH SEG? JNE GVERF MOV DH,GPRBLK ; FIGURE BUFFER STARTING POINT ADD DH,IPRBLK ; BY ADDING ALL PRELOAD BLOCKS MOV DL,0 ; CLEAR OUT BOTTOM GARBAGE SHL DX,1 ; CONVERT WORDS TO BYTE OFFSET JMP GVERG GVERF: MOV DX,0 ; GVERG: MOV SI,0 MOV BX,GAMHNDL ; GET FILE HANDLE PUSH DS ; MAKE DS ADDRESS GAME SEG PUSH ES POP DS GVER2: MOV AH,CRDRNDZ ; READ THE G-FILE PART INT 21H CALL CHKSUM ; PERFORM CHECK SUM ON BUFFER CMP SI,DI ; HAVE WE CHECK THE WHOLE THING? JE GVER3 JMP GVER2 ; GVER3: POP DS ; RESTORE DS CMP BP,J ; CORRECT? JE GVER4 JMP GVER$ ; SEEK TO START OF I-FILE IN DAT FILE GVER4: PUSH CX ; SAVE BUFFER SIZE PUSH DX ; AND LOCATION MOV DX,DI ; GET LENGTH OF G-FILE ADD DX,GHEADER/2 ; ADD IN PREVIOUSLY SUBBED HEADER ADD DX,IHEADER/2 ; ALSO SKIP I-FILE HEADER MOV CX,0 ; CLEAR OUT FOR SEEK INC DX ; (B6) MAKE UP FOR KERMIT FROM DEC20 GVER4A: SHL DX,1 ; CONVERT WORD LENGTH TO BYTES ADC CX,0 ; GET 64K BIT IF THERE IS ONE MOV AX,4200H ; SEEK INT 21H ; DO IT POP DX POP CX ; NOW FILE POINTER IS READER TO READ I-PURE FOR VERIFY ; FIGURE OUT HOW MANY BYTES IN I-FILE PUSH ES ; SAVE SEG BEING USED MOV AX,ICODE MOV VMP,AX ; GET LENGTH OF I-FILE OUT OF I-HEADER MOV VMP0,0 ; SET VMP MOV AX,ILEN CALL ADDVMP CALL GW@VMP ; GET THE WORD MOV DI,AX ; SAVE LENGTH SUB DI,IHEADER/2 ; CHECK FILE LESS HEADER MOV BP,0 ; ZERO THIS BABY AGAIN MOV SI,0 POP ES ; RESTORE SEG BEING USED PUSH DS PUSH ES POP DS ; MAKE DS ADDRESS THE GAME SEG ; GVER5: MOV AH,CRDRNDZ ; READ THE I FILE INT 21H ; DO IT CALL CHKSUM ; PERFORM CHECK SUM ON BUFFER CMP SI,DI ; HAVE WE CHECK THE WHOLE THING? JE GVER6 JMP GVER5 GVER6: POP DS ; RESTORE DS CMP BP,I ; COMPARE CHECKSUMS JE GVER7 JMP GVER$ GVER7: CLC ; INDICATE SUCCESS GVER8: PUSHF CMP FSIZE,0 ; (B5) DID WE USE THE HIGH SEG JE GVER9 MOV AX,4200H ; (B5) SEEK FROM BEGINNING OF FILE MOV CX,1 ; (B5) TO 64K LOCATION PUSH DS ; (B6) SET TO UPPER MOV DX,SEG1 ; (B6) SEGMENT FOR RELOAD MOV DS,DX ; (B6) RESTORE DX MOV DX,0 INT 21H MOV DX,0 ; (B5) READ IN SECOND 64K MOV CX,0FFFFH ; (B5) THAT MUCH MOV AH,CRDRNDZ ; (B5) READ FUNCTION INT 21H POP DS GVER9: POPF POP ES ; RESTORES POP DS POP BP POP DI POP SI POP DX POP CX POP BX JC GVER$$ ; FAIL ON CARRY JMP PREDS ; OTHERWISE SUCCESS GVER$: STC ; COMMON ENTRY FOR FAILURE JMP GVER8 ; DO RESTORES GVER$$: JMP PREDF ; INDICATE FAILURE GVERP ENDP PUBLIC CHKSUM CHKSUM PROC NEAR PUSH AX ; SAVES PUSH BX PUSH CX PUSH DS PUSH ES POP DS MOV BX,SI ; GET NUMBER OF WORDS CHECKED SO FAR MOV CX,AX ; GET NUMBER OF BYTES TO CHECK MOV SI,DX ; ADDRESS OF BUFFER MOV AH,0 ; ZERO TOP HALF CHKS1: LODSB ; GET A BYTE ADD BP,AX ; ADD THE BYTE TEST SI,1 ; ODD BYTE? JNZ CHKS2 INC BX CMP BX,DI JE CHKS3 CHKS2: LOOP CHKS1 CHKS3: MOV SI,BX ; RESTORE SI TO COUNT OF WORDS POP DS POP CX POP BX POP AX RET CHKSUM ENDP SUBTTL 1-OP EXECUTORS PAGE ;--------------- ; 1-OP EXECUTORS ;--------------- ; ---- ; PUSH ; ---- ;PUSH [ARG1] ONTO THE GSTACK PUBLIC GPUSH GPUSH PROC MOV AX,ARG1 ; AND FALL THROUGH ............. GPUSH ENDP ; ----------------------- ; PUSH AX ONTO THE GSTACK ; ----------------------- PUBLIC PSHAXG,GSTKR PSHAXG PROC XCHG SP,DI ;USE THE REAL SP PUSH AX ;PUSH IT ON THE STACK XCHG SP,DI ;SWAP BACK PUSH AX ;SAVE REGISTER MOV AX,OFFSET GSTK_BT ;CHECK IF WE HAVE AN OVERFLOW CMP DI,AX ; JB GSTKR ; IF WE DO IT'S FATAL POP AX ; ELSE, RESTORE THE REGISTER RET ; *** ERROR #7: G-STACK OVERFLOW *** GSTKR: POP AX MOV AL,7 JMP GERROR PSHAXG ENDP ; --- ; POP ; --- ;POP A VALUE OFF THE GSTACK INTO VARIABLE [ARG1] PUBLIC GPOP GPOP PROC CALL POPVAL JMP DOPUT GPOP ENDP ; ----- ; VALUE ; ----- ;RETURN VALUE OF VARIABLE [ARG1] PUBLIC GVALUE GVALUE PROC MOV AX,ARG1 ;GET VARIABLE ID SUB AH,AH CALL VARGET ;GET THE VALUE INTO [VALUE] JMP PUTVAL ;AND RETURN IT GVALUE ENDP ; ---- ; INC ; ---- ;INCREMENT VARIABLE [ARG1] PUBLIC GINC GINC PROC MOV AX,ARG1 ;GET VARIABLE ID SUB AH,AH CALL VARGET ;GET VAR'S VALUE INC VALUE ;INCREMENT IT JMP DOPUT GINC ENDP ; --- ; DEC ; --- ;DECREMENT VARIABLE [ARG1] PUBLIC GDEC,DOPUT GDEC PROC MOV AX,ARG1 SUB AH,AH CALL VARGET ;GET VAR'S VALUE DEC VALUE ;DECREMENT IT DOPUT: MOV AX,ARG1 ;RESTORE VARIABLE ID SUB AH,AH JMP VARPUT ;STORE NEW VALUE GDEC ENDP ; ----- ; ZERO? ; ----- ;[ARG1]=0? PUBLIC GZEROP,PYES GZEROP PROC MOV AX,ARG1 CMP AX,0 ;IS OPR ZERO? JZ PYES ;YES, PREDICATE TRUE JMP PREDF ;NO, PREDICATE FALSE PYES: JMP PREDS GZEROP ENDP ; --- ; NOT ; --- ;COMPLEMENT [ARG1] PUBLIC GBNOT GBNOT PROC MOV AX,ARG1 NOT AX ;LOGICAL COMPLEMENT JMP VEXIT ;RETURN THE VALUE GBNOT ENDP ; ---- ; JUMP ; ---- ;JUMP TO G-ADDRESS [ARG1] PUBLIC GJUMP GJUMP PROC MOV AX,ARG1 ;MOVE [ARG1] TO [VALUE] MOV VALUE,AX JMP PREDB3 ;A BRANCH THAT ALWAYS SUCCEEDS GJUMP ENDP ; --- ; RET ; --- ;RETURN FROM "CALL" WITH VALUE [ARG1] PUBLIC GRET,GRET1,GRET2 GRET PROC ; RESTORE THE GSTACK PTR MOV DI,OLDGSP ;RESTORE OLD TOP OF THE GSTACK ; RESTORE LOCALS ; GET THE NUMBER OF LOCALS CALL POPAXG ;POP # OF LOGALS FROM GSTACK INTO AX XCHG AX,CX ; XFER INTO CX TO BE USED AS A LOOP COUNTER ; IF NUMBER OF LOCALS=0 THEN SKIP LOCAL RESTORATION OR CX,CX ;ANY LOCALS? JZ GRET2 ;....NO. SKIP LOCAL RESTORATION ; ; LOCAL RESTORATION ; ; INITIALIZE VMP MOV AX,GLOCS ;SET UP VMP TO POINT TO THE LOCAL TABLE MOV VMP,AX MOV VMP0,0 ADD VMP,CX ;POINT PAST THE LAST LOCAL ; RESTORE ALL [CX] PUSHED LOCALS GRET1: DEC VMP ;BACK UP TO THE CURRENT LOCAL CALL POPAXG ;POP A LOCAL FROM THE GSTACK CALL PW@VMP ;RESTORE THE LOCAL LOOP GRET1 ; LOOP UNTIL WE HAVE THEM ALL ; RESTORE THE GPC GRET2: CALL POPAXG ;GET THE GPC BIT FROM GSTACK MOV GPC0,AL ; AND SAVE IT CALL POPAXG ;GET GPC FROM GSTACK MOV GPC,AX ; AND SAVE IT ; RESTORE THE OLD GSP CALL POPAXG ;GET OLD GSP FROM GSTACK MOV OLDGSP,AX ; AND SAVE IT ; GET THE VALUE TO RETURN MOV AX,ARG1 JMP VEXIT GRET ENDP ; ----------------- ; VALUE RETURN EXIT ; ----------------- ; ENTRY: VALUE IN AX PUBLIC VEXIT VEXIT PROC MOV VALUE,AX JMP PUTVAL ;RETURN THE VALUE VEXIT ENDP SUBTTL 2-OP EXECUTORS PAGE ;--------------- ; 2-OP EXECUTORS ;--------------- ; -------- ; BAD 2-OP ; -------- ; *** ERROR #5: ILLEGAL 2-OP *** PUBLIC BADOP2 BADOP2 PROC MOV AL,5 JMP GERROR BADOP2 ENDP ; --- ; ADD ; --- ;RETURN [ARG1] + [ARG2] PUBLIC GADD GADD PROC MOV AX,ARG1 MOV BX,ARG2 ADD AX,BX ;ADD OPR1 AND OPR2 JMP VEXIT ;RETURN THE VALUE GADD ENDP ; --- ; SUB ; --- ;RETURN [ARG1] - [ARG2] PUBLIC GSUB GSUB PROC MOV AX,ARG1 MOV BX,ARG2 SUB AX,BX ;SUBTRACT OPR2 FROM OPR1 JMP VEXIT ;RETURN THE VALUE GSUB ENDP ; --- ; MUL ; --- ;RETURN [ARG1] * [ARG2] PUBLIC GMUL GMUL PROC MOV AX,ARG1 MOV BX,ARG2 IMUL BX ;MULTIPLY OPR1 BY OPR2,IGNORING OVERFLOW(DX) JMP VEXIT ;RETURN THE VALUE GMUL ENDP ; --- ; DIV ; --- ;RETURN QUOTIENT OF [ARG1] / [ARG2] PUBLIC GDIV GDIV PROC MOV AX,ARG1 MOV BX,ARG2 CWD ;CLEAR HIGH WORD AND EXTEND SIGN FOR DIVIDE IDIV BX ;DIVIDE OPR1 BY OPR2 JMP VEXIT ;RETURN THE VALUE GDIV ENDP ; --- ; MOD ; --- ;RETURN REMAINDER OF [ARG1] / [ARG2] PUBLIC GMOD GMOD PROC MOV AX,ARG1 MOV BX,ARG2 CWD ;CLEAR HIGH WORD AND EXTEND SIGN FOR DIVIDE IDIV BX ;DIVIDE OPR1 BY OPR2 MOV AX,DX ;WE WANT REMAINDER JMP VEXIT ;RETURN THE VALUE GMOD ENDP ; ---- ; BAND ; ---- ;RETURN [ARG1] "AND" [ARG2] PUBLIC GBAND GBAND PROC MOV AX,ARG1 MOV BX,ARG2 AND AX,BX ;LOGICAL AND JMP VEXIT ;RETURN THE VALUE GBAND ENDP ; ---- ; BIOR ; ---- ;RETURN [ARG1] "OR" [ARG2] PUBLIC GBIOR GBIOR PROC MOV AX,ARG1 MOV BX,ARG2 OR AX,BX ;LOGICAL OR JMP VEXIT ;RETURN THE VALUE GBIOR ENDP ; ---- ; BXOR ; ---- ;RETURN [ARG1] "XOR" [ARG2] PUBLIC GBXOR GBXOR PROC MOV AX,ARG1 MOV BX,ARG2 XOR AX,BX ;LOGICAL XOR JMP VEXIT ;RETURN THE VALUE GBXOR ENDP ; ----- ; BITS? ; ----- ;IS EVERY "ON" BIT IN [ARG1] ALSO "ON" IN [ARG2] PUBLIC GBITSP,GBITS0 GBITSP PROC MOV AX,ARG1 MOV BX,ARG2 NOT AX ;TURN OFF BITS IN OPR2 THAT ARE ON IN OPR1 AND BX,AX JE GBITS0 ;SUCCESS IF OPR2 COMPLETELY CLEARED JMP PBAD GBITS0: JMP PGOOD GBITSP ENDP ; ------ ; EQUAL? ; ------ ;DOES [ARG1] = [ARG2] (OR [ARG3] OR [ARG4])? PUBLIC GEQP,GEQP1,GEQP2 GEQP PROC MOV CL,BYTE PTR NARGS ;GET NUMBER OF ARGUMENTS SUB CH,CH DEC CX ;WE WANT TO COMPARE WITH ALL BUT ARG1 SUB BX,BX ;RESET OFFSET MOV AX,WORD PTR ARG1 [BX] ;GET ARG1 GEQP1: INC BX ;POINT TO NEXT ARG INC BX ; REMEMBER, IT'S A WORD CMP AX,WORD PTR ARG1 [BX] ;DOES ARG1 = ARG [BX]? JE GEQP2 ;....YES LOOP GEQP1 ;....NO. TRY NEXT ARG JMP PBAD ;PREDICATE FALSE IF WE RAN OUT OF ARGS GEQP2: JMP PGOOD GEQP ENDP ; ; PREDICATE HANDLERS ; ; USED TO VECTOR LOCAL SHORT JUMPS PUBLIC PGOOD PGOOD PROC JMP PREDS PGOOD ENDP PUBLIC PBAD PBAD PROC JMP PREDF PBAD ENDP ; ----- ; LESS? ; ----- ;IS [ARG1] < [ARG2] PUBLIC GLESSP,GLESS0 GLESSP PROC MOV AX,ARG1 MOV BX,ARG2 CMP AX,BX ;IS OPR1 LESS THAN OPR2? JL GLESS0 ;YES, PREDICATE TRUE JMP PBAD ;NO, PREDICATE FALSE GLESS0: JMP PGOOD GLESSP ENDP ; ------ ; DLESS? ; ------ ;DECREMENT VAR [ARG1]; SUCCEED IF < [ARG2] PUBLIC GDLESP,GDLES0,GDLES1 GDLESP PROC MOV AX,ARG1 ;GET VARIABLE ID PUSH AX ;SAVE VARIABLE ID CALL VARGET ;GET VAR'S VALUE DEC AX ;DECREMENT IT SUB CX,CX ;SET FLAG FALSE MOV BX,ARG2 ;GET VALUE TO COMPARE TO CMP AX,BX ;IS VAR [ARG1] < ARG2? JGE GDLES0 ;NO INC CX ;YES, CHANGE FLAG TO TRUE GDLES0: MOV BX,AX ;GET VALUE MOV VALUE,BX ;STORE IT IN [VALUE] POP AX ;RESTORE VARIABLE ID CALL VARPUT ;STORE [VALUE] AT VAR CMP CX,0 ;TEST FLAG JE GDLES1 ;FALSE, PREDICATE FALSE JMP PGOOD ;ELSE, PREDICATE TRUE GDLES1: JMP PBAD GDLESP ENDP ; ----- ; GRTR? ; ----- ;IS [ARG1] > [ARG2] PUBLIC GGRTRP,GGRTR0 GGRTRP PROC MOV AX,ARG1 MOV BX,ARG2 CMP AX,BX ;IS OPR1 GREATER THAN OPR2? JG GGRTR0 ;YES, PREDICATE TRUE JMP PBAD ;NO, PREDICATE FALSE GGRTR0: JMP PGOOD GGRTRP ENDP ; ------ ; IGRTR? ; ------ ;INCREMENT [ARG1]; SUCCEED IF > [ARG2] PUBLIC GIGRTP,OPQIG0,OPQIG1,OQI1$ GIGRTP PROC MOV AX,ARG1 MOV BX,ARG2 PUSH AX ;SAVE VARIABLE CALL VARGET ;GET VAR'S VALUE INC AX ;INCREMENT IT SUB CX,CX ;SET FLAG FALSE CMP AX,BX ;NEW VALUE GREATER THAN VAL? JLE OPQIG1 ;NO OPQIG0: INC CX ;YES, CHANGE FLAG TO TRUE OPQIG1: MOV BX,AX ;GET VALUE MOV VALUE,BX ;STORE IT IN [VALUE] POP AX ;RESTORE VARIABLE ID CALL VARPUT ;STORE [VALUE] AT VAR CMP CX,0 ;TEST FLAG JE OQI1$ ;FALSE, PREDICATE FALSE JMP PGOOD ;ELSE, PREDICATE TRUE OQI1$: JMP PBAD GIGRTP ENDP ; --- ; SET ; --- ;SET VARIABLE [ARG1] EQUAL TO [ARG2] PUBLIC GSET GSET PROC MOV AX,ARG2 ;GET VALUE TO SAVE MOV VALUE,AX ;PUT IT IN [VALUE] MOV AX,ARG1 ;GET VARIABLE NO. JMP VARPUT ;STORE THE VALUE IN THE VARIABLE GSET ENDP ; --- ; GET ; --- ;RETURN ITEM [ARG2] IN WORD-TABLE [ARG1] PUBLIC GGET GGET PROC MOV AX,ARG1 ;GET WORD TABLE BASE ADDRESS ADD AX,ARG2 ;INDEX INTO WORD TABLE MOV VMP,AX ;PUT ADDRESS INTO VIRTUAL MEMORY POINTER MOV VMP0,0 ;IT MUST BE ON AN EVEN BOUNDRY CALL GW@VMP ;GET THE WORD. RETURNED IN AX MOV VALUE,AX ;PUT IT IN [VALUE] JMP PUTVAL ;AND RETURN IT IN WORD TABLE [ARG1] GGET ENDP ; ---- ; GETB ; ---- ;RETURN ITEM [ARG2] IN BYTE-TABLE [ARG1] PUBLIC GGETB GGETB PROC MOV AX,ARG1 ;TABLE BASE ADDRESS (WORDPOINTER) MOV VMP,AX ;SET UP BASE ADDRESS MOV VMP0,0 MOV AX,ARG2 ;GET BYTE POINTER CALL ADDVMP ;ADD BYTE OFFSET TO BASE ADDRESS CALL GB@VMP ;GET THE BYTE JMP PUTBYT ;AND RETURN IT VIA PUTBYT GGETB ENDP SUBTTL X-OP EXECUTORS PAGE ;--------------- ; X-OP EXECUTORS ;--------------- ; ---- ; CALL ; ---- ; INSTRUCTION FORMAT: ; ; OPCALL, MODE BYTE1, .... MODE BYTEn, ARG1, .... ARGn ; ; ; MODE BYTE: ; ; There are no mode bits in the opcode itself. All of the mode bits appear ; in the byte(s) following the opcode. Each mode byte is capable of supporting ; up to four arguments. Thus a mode byte is composed of up to 4 half nibble ; mode control codes. ; ; (Mode control code will be abbreviated as MCC.) A mode byte may contain ; 4 MCC's labeled from left to right MCC1, MCC2, MCC3, and MCC4. ; ; MODE BYTE FORMAT: ; ; bits: 7 6 5 4 3 2 1 0 ; +------+------+------+------+ ; | MCC1 | MCC2 | MCC3 | MCC4 | ; +------+------+------+------+ ; ; ; MODE CONTROL CODE FORMAT: ; ; bits: 1 0 | Operand ; ----|--------------------- ; 0 0 | long immediate ; 0 1 | immediate ; 1 0 | variable ; 1 1 | no more operands ; ; ; ; AT ENTRY [NARGS] = 0 PUBLIC GCALL,GCL,GCL0,GCL1,GCL2,GCL3,GCL4,GCL5 PUBLIC GCL6,GCL7,GCL8,DOCALL,CALL1,CALL2,CALL3,CALL4 GCALL PROC ; ; FILL CTYPES ARRAY WITH 'NO MORE ARGS' ; INITIALIZE REGISTERS MOV CX,16 ;NUMBER OF POSSIBLE ARGUMENTS MOV AL,0C0H ;USE THE 'NO MORE ARGS' CODE SUB BX,BX ;RESET BYTE INDEX ; FILL THE CURRENT CELL GCL: MOV BYTE PTR CTYPES [BX],AL ; UPDATE INDEX INC BX ;POINT TO THE NEXT CELL ; LOOP TILL DONE LOOP GCL ; AND DO IT 16 TIMES TOTAL ; ; UNPACK MODE BYTES ; MOV ADEX,4 ;INITIALIZE BYTE INDEX MOV ADEX2,4 ;INITIALIZE ARG BYTE COUNTER GCL0: CALL GB@GPCI ;GRAB AN ARGUMENT BYTE MOV ABYTE,AL ;SAVE IT JMP GCL2 ;DON'T SHIFT THE MODE BYTE THE FIRST TIME GCL1: MOV AL,ABYTE ;RESTORE THE MODE BYTE SHL AL,1 ;SHIFT AND SHL AL,1 ;SHIFT AGAIN MOV ABYTE,AL ;SAVE MODE BYTE FOR NEXT TIME GCL2: AND AL,0C0H ;MASK OUT THE GARBAGE CMP AL,0C0H ;LAST ARGUMENT? JZ GCL3 ;....YES. TIME TO DECODE THE ARGS ;ELSE ADD THIS ARGUMENT TO THE ARG-TYPE LIST MOV BL,NARGS ; SET UP INDEX MOV BH,0 MOV BYTE PTR CTYPES [BX],AL INC NARGS ; AND INCREMENT THE NUMBER OF ARGUMENTS DEC ADEX ;IS THIS ARG BYTE EMPTY? JNZ GCL1 ;....NO. GET NEXT ARG MOV ADEX,4 ;ELSE RESET THE ARGUMENT INDEX DEC ADEX2 ;HAVE WE DONE 4 ARG BYTES? JNZ GCL0 ;....NO. GET ANOTHER ; [CTYPES] HAS LIST OF ARG TYPES ; [NARGS] HAS NUMBER OF ARGUMENTS ; GPC POINTS TO ARG1 (THE FUNCTION ADDRESS) GCL3: MOV ADEX,0 ;RESET THE ARGUMENT INDEX MOV ADEX2,0 ;AND STORAGE INDEX MOV AL,NARGS ;CONTINUE ONLY IF CMP AL,0 ;[NARGS] <> 0 JNZ GCL4 ; *** ERROR #12: NO CALL ADDRESS *** MOV AL,12 JMP GERROR ; SAVE THE GCALL ARGUMENTS IN THE CARGS ARRAY WHILE USING THE MODE ; CONTROL CODES (LOCATED IN THE CTYPES ARRAY) TO OBTAIN THEM. GCL4: MOV BL,ADEX ;GET ARG INDEX MOV BH,0 ;WIPE OUT THE MSB CMP BL,NARGS ;OUT OF ARGS YET? JZ GCL8 ;....YES INC ADEX ;UPDATE ARG INDEX TO NEXT ARG ; GET THE TYPE BYTE FOR CURRENT ARG IN AL MOV AL,BYTE PTR CTYPES [BX] CMP AL,0 ;LONG IMMEDIATE? JNZ GCL5 ;....NO ; LONG IMMEDIATE CALL GETLNG ;....YES, GET IT JMP GCL7 GCL5: CMP AL,40H ;SHORT IMMEDIATE? JNZ GCL6 ;....NO ; SHORT IMMEDIATE CALL GETSHT ;....YES, GET IT JMP GCL7 ; VARIABLE GCL6: CALL GETVAR ;MUST BE A GET VARIABLE ; ARGUMENT IS NOW IN AX ; MOVE VALUE (IN AX) TO [CARGS] GCL7: MOV BL,ADEX2 ;GET THE STORAGE INDEX MOV BH,0 MOV WORD PTR CARGS [BX],AX ;SAVE IT IN CARGS INC ADEX2 ; AND INCREMENT THE INDEX INC ADEX2 ; TWICE, CAUSE WE SAVING WORDS JMP GCL4 ;LOOP BACK FOR ANOTHER ARGUMENT ; ARGUMENTS IN [CARGS], # OF ARGUMENTS IN [NARGS] GCL8: MOV AX,CARGS OR AX,AX ;DOES CALL ADDRESS=0? JNZ DOCALL ;....NO. CONTINUE JMP PUTBYT ;ELSE RETURN THE ZERO IN AL ; ; AND NOW THE ACTUAL CALL ; DOCALL: MOV AX,OLDGSP ;PUSH [OLDGSP] ON THE GSTACK CALL PSHAXG MOV AX,GPC ;PUSH [GPC] ON THE GSTACK CALL PSHAXG MOV AL,GPC0 ;PUSH GPC BIT ON THE GSTACK CALL PSHAXG ;SET GPC TO THE FUNCTION ADDRESS MOV AX,CARGS ;GET THE FUNCTION ADDRESS MOV GPC,AX ;PUT IT IN THE GPC MOV GPC0,0 ; ALL FUNCTIONS START ON WORD BOUNDRIES ;PROCESS THE FUNCTION'S LOCALS CALL GB@GPCI ;GET # OF LOCALS IN FUNCTION MOV ADEX,AL ;SAVE HERE FOR REFERENCE MOV ADEX2,AL ;SAVE HERE FOR COUNTING ; ANY LOCALS? OR AL,AL ;IF # LOCALS = 0 JZ CALL2 ; THEN JUMP AHEAD ; PUT THE FUNCTION'S LOCALS IN THE LOCAL TABLE ; WHILE SAVING THE PRE-EXISTING LOCALS ON THE GSTACK ; SET UP LOOP COUNTER MOV CL,AL ;PUT NUMBER OF LOCALS IN LOOP COUNTER SUB CH,CH MOV AX,GLOCS ;POINT VMP AT THE BASE ADDRESS OF THE MOV VMP,AX ; LOCAL TABLE MOV VMP0,0 CALL1: CALL GW@VMP ;GET OLD LOCAL CALL PSHAXG ; SAVE IT ON THE GSTACK CALL GETLNG ;GET FUNCTION'S LOCAL & POINT TO NEXT CALL PW@VMPI ;PUT IT IN THE LOCAL TABLE AND POINT TO ; THE NEXT TABLE ENTRY LOOP CALL1 CALL2: DEC NARGS ;ANY ARGS LEFT? JZ CALL4 ;....NO ; PASS UP TO 15 [CARGS] TO [LOCALS] SUB BX,BX ;INITIALIZE LOOP INDEX MOV AX,GLOCS ;POINT VMP AT THE BASE ADDRESS OF THE MOV VMP,AX ; LOCAL TABLE MOV VMP0,0 CALL3: MOV AX,WORD PTR CARGS+2 [BX] ;GET ARGUMENT CALL PW@VMPI ;ASSIGN ARG TO A LOCAL INC BX ;UPDATE INDEX INC BX ;IT'S A WORD PTR DEC NARGS ;OUT OF ARGS YET? JNZ CALL3 ;....NO CALL4: MOV AL,ADEX ;GET NO. OF LOCALS MOV AH,0 ;MASK OUT THE MSB CALL PSHAXG ; AND SAVE ON THE GSTACK MOV OLDGSP,DI ;SAVE THE GSTACK POINTER JMP MLOOP ;AND GO BACK TO MAIN LOOP GCALL ENDP PAGE ; --- ; PUT ; --- ; ; SET ITEM [ARG2] IN WORD-TABLE [ARG1] EQUAL TO [ARG3] ; PUBLIC GPUT GPUT PROC MOV AX,ARG1 ;GET WORD TABLE BASE ADDRESS ADD AX,ARG2 ;INDEX INTO WORD TABLE MOV VMP,AX ;PUT ADDRESS INTO VIRTUAL MEMORY POINTER MOV VMP0,0 ;IT MUST BE ON AN EVEN BOUNDRY MOV AX,ARG3 ;GET THE VALUE CALL PW@VMP ; AND WRITE IT TO VIRTUAL RET GPUT ENDP ; ---- ; PUTB ; ---- ; ; SET ITEM [ARG2] IN BYTE-TABLE [ARG1] EQUAL TO [ARG3 (LSB)] ; PUBLIC GPUTB GPUTB PROC MOV AX,ARG1 ;TABLE BASE ADDRESS (WORDPOINTER) MOV VMP,AX ;SET UP BASE ADDRESS MOV VMP0,0 MOV AX,ARG2 ;GET BYTE POINTER CALL ADDVMP ;ADD BYTE OFFSET TO BASE ADDRESS MOV AX,ARG3 ;GET VALUE CALL PB@VMPI ;WRITE OUT THE BYTE RET GPUTB ENDP PAGE ; ----- ; INPUT ; ----- ; IMPORTS: ; [ARG1], [ARG2] optional ; ; IF [NARGS] INDICATES THAT THERE IS A [ARG2], THEN [ARG2] ; IS A WORDPOINTER ADDRESS TO AN ACTIVE ICON TABLE. IN THIS ; CASE, ITERATION IS PERFORMED. ; ; [ARG1] IS DECODED AS FOLLOWS: ; 0 : IMMEDIATELY POLL THE KEYBOARD AND JOYSTICK ONCE ; -int : PAUSE FOR [-int]/60 SEC. ; int : POLL THE KEYBOARD FOR int/60 SEC. OR UNTIL A ; KEYPRESS IS DETECTED ; ; EXPORTS: [VALUE] via PUTBYT [AL] ; WHERE AL IS ENCODED AS FOLLOWS: ; 8FH : NO KEYPRESS (NO INPUTS DETECTED) ; +int : 7-BIT ASCII CHARACTER ; -int : JOYSTICK DATA * ; ; *note See DIP SPECIFICATION for JOYSTICK DATA rules PUBLIC GINPUT GINPUT PROC MOV KEYSAV,8FH ;KEYSAV <= NO KEY FOUND MOV AX,ARG1 ;GET ARGUMENT MOV VALUE,AX ;MOVE [ARG1] INTO [VALUE] CMP NARGS,1 ;IF ONLY 1 ARG JE DONTIT ; THEN DON'T ITERATE JMP ITRATE ; ELSE, ITERATE DONTIT: JMP NOIT GINPUT ENDP NOIT PROC MOV AX,VALUE ;RECOVER THE SILLY ARG OR AX,AX ;TEST IT AGAIN JS DELAY ;IF NEGATIVE, THIS IS A DELAY REQUEST JNZ TICK ;IF NON-ZERO, THEN ENTER SENSE LOOP NOIT1: CALL SENSE ; ELSE, READ JUST ONCE MOVED: JMP PUTBYT ;RETURN [AL] ; ; PURPOSE: POLL THE KEYBOARD FOR int/60 SEC. OR UNTIL A ; KEYPRESS IS DETECTED ; TICK: CMP PCJR,1 ; DON'T DOUBLE VALUE IF jr JE TICK1 ; SO SKIP IT SHL VALUE,1 ; DOUBLE THE WAIT VALUE TICK1: CALL SENSE ;GET A READING CMP AL,10001111B ;ANY ACTIVITY? JNE MOVED ;....YES. RETURN IT. CMP IBMAT,FALSE ; ARE WE A FAST ONE JE TICK3 MOV CX,JIFCNT TICK2: LOOP TICK2 ; WASTE SOME TIME TICK3: DEC VALUE ;DECREMENT COUNT, DOES COUNT = 0? JNZ TICK1 ;....NO, LOOP BACK AND TRY AGAIN ;THE CLOCK EXPIRED WITHOUT A READING NOREAD: JMP PUTBYT ; ; PROGRAMABLE DELAY ; ; PURPOSE: POLL THE KEYBOARD FOR [-int]/60 SEC. REPORT THE ; FIRST DETECTED KEYPRESS IF ANY. ; ; ENTER: AX=[-INT] ; DELAY: NEG VALUE ;CHANGE [-INT] TO [+INT] CMP PCJR,1 ; DON'T SHIFT FOR PCjr JE DELALP ; SKIP IT SHL VALUE,1 ;DOUBLE DELAY TIME FOR RICK'S MISCALC DELALP: CALL SENSE ;GET A READING CMP KEYSAV,8FH ;ANY PREVIOUS ACTIVITY? JNE DELA1 ;....YES MOV KEYSAV,AL ;....NO, SAVE THE CURRENT READING DELA1: CMP IBMAT,FALSE ;IS THIS A SLOW MACHINE? JE DELA3 ;....YES MOV CX,JIFCNT ;IT'S A FAST MACHINE (AN IBM AT) DELA2: LOOP DELA2 ; SO USE A DELAY LOOP TO COMPENSATE DELA3: DEC VALUE ;DECREMENT COUNT, DOES COUNT = 0? JNZ DELALP ;....NO, LOOP BACK AND TRY AGAIN ;THE CLOCK EXPIRED MOV AL,KEYSAV ;RETURN THE KEYCODE JMP PUTBYT NOIT ENDP ; --------------------------- ; CHECK KEYBOARD AND JOYSTICK ; --------------------------- PUBLIC SENSE SENSE PROC ; CHECK KEYBOARD FIRST MOV AH,6 ;SELECT DIRECT CONSOLE INPUT MOV DL,0FFH ; WITHOUT ECHO INT 21H ;MSDOS FUNCTION CALL JZ STICK ;IF NO KEYPRESS, THEN CHECK STICK ; ELSE WE GOT A KEYSTROKE CMP AL,0DH ; (B5) CARRIAGE RETURN? JE SENS2 CMP AL,0 ; EXTENDED ASCII? JNE SENS1 ; NOPE MOV AH,CNOECHO ; READ THE EXTENDED CODE INT 21H ; GET THE CHAR SUB AL,71 ; BASIFY THE HOME KEY CMP AL,10 ; UPPER LIMIT JA STICK ; DON'T RETURN ANYTHING MOV BX,OFFSET XLKEY ; TRANSLATE THE KEY XLAT ; LOOK UP IN TABLE SENS2: PUSH AX ; SAVE CHAR AND TURN ON NUMLOCK PUSH ES MOV AX,BIOSDATA ; SEG ADDRESS BIOS DATA AREA MOV ES,AX MOV BX,KB_FLAG ; OFFSET TO BIOS DATA FOR KEYS OR BYTE PTR ES:[BX],NUMLOCK ; TURN ON NUMLOCK POP ES POP AX SENS1: AND AL,01111111B ;7-BIT ASCII ONLY RET ; CHECK THE JOYSTICK STICK: TEST JOYSTK,1 ; IS THERE REALLY ONE THERE? JNZ STICK1 ; ITS THERE, ONWARD PUSH CX MOV CX,JIFCNT-200H ; NUMBER OF INSTRUCTIONS CYCLES NOT EXECUTED STICK$: LOOP STICK$ ; TAKE AS MUCH TIME AS WE WOULD HAVE POP CX MOV AL,8FH ; RETURN CENTER POSITION RET STICK1: MOV DX,201H ;DX ^ GAME PORT MOV AL,10001111B ;INITIALIZE JOYSTICK ACTIVITY BYTE MOV STICKA,AL ; TO INDICATE NO KEYPRESSES ;*********** PATCH ************************* ;RET ;TO DISABLE JOYSTICK ;******************************************* ;GET JOYSTICK X-POSITION MOV AH,1 ;SELECT X-AXIS CALL POSITN MOV BL,STICKA ;GET THE JOYSTICK FLAGS CMP AL,60H ;IS THE JOYSTICK TO THE RIGHT? JB STK0 ;....NO XOR BL,00001000B ;TURN OFF THE RIGHT BIT (NEGATIVE LOGIC) JMP STK1 ;IF IT'S TO THE RIGHT, IT CAN'T BE TO THE LEFT STK0: CMP AL,30H ;IS THE JOYSTICK TO THE LEFT? JAE STK1 ;....NO XOR BL,00000100B ;TURN OFF THE LEFT BIT STK1: MOV STICKA,BL ;SAVE JOYSTICK FLAGS MOV AH,2 ;SELECT Y POSITION CALL POSITN ;GET JOYSTICK POSITION MOV BL,STICKA ;GET THE JOYSTICK FLAGS CMP AL,60H ;IS THE JOYSTICK DOWN? JB STK2 ;....NO XOR BL,00000010B ;TURN OFF THE DOWN BIT (NEGATIVE LOGIC) JMP STK3 ;IF IT'S DOWN, IT CAN'T BE UP STK2: CMP AL,30H ;IS THE JOYSTICK UP? JAE STK3 ;....NO XOR BL,00000001B ;TURN OFF THE UP BIT STK3: MOV STICKA,BL IN AL,DX ;READ SWITCH INPUTS MOV AH,AL ;SAVE IT IN [AH] AND AL,10H ;IS BUTTON 1 DEPRESSED? JNZ STK3A ;....NO MOV BL,STICKA ;....YES OR BL,00010000B ;TURN ON THE BUTTON BIT STK3A: AND AH,20H ;IS BUTTON 2 DEPRESSED? JNZ STK4 ;....NO MOV BL,STICKA ;....YES OR BL,00010000B ;TURN ON THE BUTTON BIT STK4: MOV AL,BL ;RETURN THE VALUE IN AL RET SENSE ENDP ; ; GET JOYSTICK ANALOG POSITION ; ; ; AXIS SELECTED WITH AH ; 1= X AXIS ; 2= Y AXIS ; ; A DIFFERENT ROUTINE IS PROVIDED FOR IBM AND IBM AT POSITN PROC PUSH CX IN AL,61H ;GET PORT DATA PUSH AX ; AND SAVE IT CLI ;CLEAR INTERRUPTS SUB CX,CX ;SET LOOP CONTROL VALUE = 0 OUT DX,AL ;START THE TIMING POSX1: IN AL,DX ;GET STATE TEST AL,AH ;WE'RE INTERESTED IN THE BIT IN AH LOOPNE POSX1 ;LOOP UNTIL THE BIT COMES BACK ON ; IS THIS AN IBM AT? CMP IBMAT,TRUE JNE POSX2 SAR CX,1 POSX2: SUB AX,AX SUB AX,CX ;DETERMINE COUNT CMP AX,255 JBE POSX3 MOV AX,255 ;EQUALIZATION POSX3: PUSH AX ; IS THIS AN IBM AT? CMP IBMAT,TRUE JNE POSX4 SHL CX,1 POSX4: AND CX,01FFH EQ1: IN AL,DX TEST AL,AH LOOP EQ1 POP CX POP AX ;RESTORE INTERRUPT STATUS OUT 61H,AL ; THE PORT VALUE STI ;ENABLE INTERRUPTS MOV AX,CX ;RESTORE JOYSTICK VALUE POP CX RET POSITN ENDP PAGE ; -------------------- ; DUMP BLOCK TO SCREEN ; -------------------- ; ENTRY: BLOCK TO PUT ON SCREEN IN [BLOCK] (TOP BYTE IN [BLOCK + 0]) ; X-COORDINATE (0-39) IN [AH], Y-COORDINATE (0-23) IN [AL] ; PUBLIC DUMP DUMP PROC PUSH DI CMP AH,39 ; (B4) SHIFT FIX JNE DMP1 JMP DMPFIX DMP1: MOV BX,VID_SG ;POINT AT THE VIDEO BUFFER MOV ES,BX ; CALCULATE OFFSET FROM VIDEO_RAM BASE ADDRESS TO STORE THE FIRST BYTE SUB BX,BX ;ZERO MSB XCHG BL,AL ;BX <= ROW ADDRESS (0-23) ; AL IS NOW "0" SHL BX,1 ;MAKE BX A WORD INDEX XCHG AH,AL ;GET COLUMN CONTRIBUTION IN AL SHL AX,1 ; x2 SINCE IBM HAS 80 COLUMNS ADD AX,WORD PTR YAXTBL[BX] ;ADD ROW CONTRIBUTION ; TO COLUMN CONTRIBUTION MOV DI,AX ; (B4) USE REG MOV SI,OFFSET BLOCK ; (B4) USE REG MOV CX,4 ;INITIALIZE LOOP COUNTER SUB DI,1FAFH ; (B4) FIRST PASS FUDGE FACTOR JMP DLOOP1 ; (B4) SKIP ACTUAL FIX OFFSET ; TRANSLATE BYTE IN [AL] INTO A DOUBLED BIT WORD IN [AX] DLOOP: SUB DI,1FB2H ; (B4) FIX OFFSET DLOOP1: XOR AH,AH ; (B4) ZERO TOP HALF LODSB ; (B4) GET BLOCKSET BYTE SHL AX,1 ; (B4) BYTE TO WORD XCHG BP,AX ; (B4) INTO AN INDEX REG MOV AX,GRXTBL[BP] ; (B4) GET SHIFTED WORD FROM TBL ;PHASE SHIFT ;MOVE THE WORD INTO VIDEO RAM STOSB ; (B4) STORE MIDDLE BYTE MOV AL,AH ;SWAP AND AX,3FC0H ;MASK MOV DH,ES:[DI-2] ; (B4) GET IMAGE ALREADY ON SCREEN MOV DL,ES:[DI] ; (B4) AND DX,0C03FH ;MASK IT OR AX,DX ;COMBINE SOURCE AND DEST TO MOV ES:[DI-2],AH ; (B4) ACHIEVE PHASE SHIFT ON LEFT STOSB ; (B4) AND ON RIGHT ; ODD BYTE IN BLOCK ADD DI,1FFEH ; FIX OFFSET ; TRANSLATE BYTE IN [AL] INTO A DOUBLED BIT WORD IN [AX] XOR AH,AH ; (B4) ZERO TOP HALF LODSB ; (B4) GET BLOCKSET BYTE SHL AX,1 ; (B4) BYTE TO WORD XCHG BP,AX ; (B4) INTO AN BASE REG MOV AX,GRXTBL[BP] ; (B4) GET SHIFTED WORD FROM TBL ;PHASE SHIFT ;MOVE THE WORD INTO VIDEO RAM STOSB ; (B4) STORE MIDDLE BYTE MOV AL,AH ;SWAP AND AX,3FC0H ;MASK MOV DH,ES:[DI-2] ; (B4) GET IMAGE ALREADY ON SCREEN MOV DL,ES:[DI] ; (B4) AND DX,0C03FH ;MASK IT OR AX,DX ;COMBINE SOURCE AND DEST TO MOV ES:[DI-2],AH ; (B4) ACHIEVE PHASE SHIFT ON LEFT STOSB ; (B4) AND ON RIGHT LOOP DLOOP POP DI RET DUMP ENDP DMPFIX: MOV BX,VID_SG ;POINT AT THE VIDEO BUFFER MOV ES,BX SUB BX,BX ;ZERO MSB XCHG BL,AL ;BX <= ROW ADDRESS (0-23) SHL BX,1 ;MAKE BX A WORD INDEX XCHG AH,AL ;GET COLUMN CONTRIBUTION IN AL SHL AX,1 ; x2 SINCE IBM HAS 80 COLUMNS ADD AX,WORD PTR YAXTBL[BX] ;ADD ROW CONTRIBUTION MOV DI,AX ; (B4) USE REG MOV SI,OFFSET BLOCK ; (B4) USE REG MOV CX,4 ;INITIALIZE LOOP COUNTER SUB DI,1FAFH ; (B4) FIRST PASS FUDGE FACTOR JMP DFLP1 ; (B4) SKIP ACTUAL FIX OFFSET SUB DI,1FAFH ; (B4) FIRST PASS FUDGE FACTOR JMP DFLP1 ; (B4) SKIP ACTUAL FIX OFFSET DFLP: SUB DI,1FB1H ; (B4) FIX OFFSET DFLP1: XOR AH,AH ; (B4) ZERO TOP HALF LODSB ; (B4) GET BLOCKSET BYTE SHL AX,1 ; (B4) BYTE TO WORD XCHG BP,AX ; (B4) INTO AN INDEX REG MOV AX,GRXTBL[BP] ; (B4) GET SHIFTED WORD FROM TBL STOSB ; (B4) STORE MIDDLE BYTE MOV AL,AH ;SWAP AND AX,3FC0H ;MASK MOV DH,ES:[DI-2] ; (B4) GET IMAGE ALREADY ON SCREEN MOV DL,ES:[DI] ; (B4) AND DX,0C03FH ; (B4) MASK IT OR AX,DX ;COMBINE SOURCE AND DEST TO MOV ES:[DI-2],AH ; (B4) ACHIEVE PHASE SHIFT ON LEFT ADD DI,1FFFH ; FIX OFFSET XOR AH,AH ; (B4) ZERO TOP HALF LODSB ; (B4) GET BLOCKSET BYTE SHL AX,1 ; (B4) BYTE TO WORD XCHG BP,AX ; (B4) INTO AN BASE REG MOV AX,GRXTBL[BP] ; (B4) GET SHIFTED WORD FROM TBL STOSB ; (B4) STORE MIDDLE BYTE MOV AL,AH ;SWAP AND AX,3FC0H ;MASK MOV DH,ES:[DI-2] ; (B4) GET IMAGE ALREADY ON SCREEN MOV DL,ES:[DI] ; (B4) AND DX,0C03FH ;MASK IT OR AX,DX ;COMBINE SOURCE AND DEST TO MOV ES:[DI-2],AH ; (B4) ACHIEVE PHASE SHIFT ON LEFT LOOP DFLP POP DI RET PAGE ; ------------------------------ ; DUMP BLOCK TO SCREEN WITH MASK ; ------------------------------ ; ENTRY: BLOCK TO PUT ON SCREEN IN [BLOCK] (TOP BYTE IN [BLOCK + 0]) ; X-COORDINATE (0-39) IN [AH], Y-COORDINATE (0-23) IN [AL] ; ; IF THE MASK BIT IS A "1" THEN THE SCREEN SHOWS THROUGH ; IF THE MASK BIT IS A "0" THEN THE ICON IS DISPLAYED ; PUBLIC DMPMSK DMPMSK PROC PUSH BX ;SAVE AFFECTED REGISTER(S) PUSH CX PUSH DX PUSH ES MOV BX,VID_SG ;POINT AT THE VIDEO BUFFER MOV ES,BX ; CALCULATE OFFSET FROM VIDEO_RAM BASE ADDRESS TO STORE THE FIRST BYTE SUB BX,BX ;ZERO MSB XCHG BL,AL ;BX <= ROW ADDRESS (0-23) ; AL IS NOW "0" SHL BX,1 ;MAKE BX A WORD INDEX XCHG AH,AL ;GET COLUMN CONTRIBUTION IN AL SHL AX,1 ; x2 SINCE IBM HAS 80 COLUMNS ADD AX,WORD PTR YAXTBL[BX] ;ADD ROW CONTRIBUTION ; TO COLUMN CONTRIBUTION MOV VDPADR,AX ;SAVE STARTING MEMORY OFFSET SUB SI,SI ;INITIALIZE INDEX REGISTER MOV CX,4 ;INITIALIZE LOOP COUNTER DMPMLP: MOV BL,BLOCK[SI] ;SELECT ICON BYTE MOV BH,MBLOCK[SI] ;SELECT MASK BYTE SUB VDPADR,1FB0H ;FIX OFFSET CALL M1DISP ;TRANSLATE, MASK AND MOVE IT TO VRAM INC SI ; NEXT BYTES MOV BL,BLOCK[SI] ;SELECT ICON BYTE MOV BH,MBLOCK[SI] ;SELECT MASK BYTE ADD VDPADR,2000H ;FIX OFFSET CALL M1DISP ;TRANSLATE, MASK AND MOVE IT TO VRAM INC SI ; NEXT BYTES LOOP DMPMLP POP ES ;RESTORE AFFECTED REGISTER(S) POP DX POP CX POP BX RET DMPMSK ENDP PUBLIC M1DISP M1DISP PROC PUSH CX PUSH BX ;SAVE MASK BYTE [IN BH] ; TRANSLATE BLOCK BYTE IN [BL] INTO A DOUBLED BIT WORD IN [AX] SUB BH,BH SHL BX,1 MOV DX,GRXTBL[BX] ;BLOCK WORD POP BX ;RECOVER MASK BYTE XCHG BH,BL ;PUT IT IN BL SUB BH,BH SHL BX,1 MOV CX,GRXTBL[BX] ;MASK WORD PUSH DX ;SAVE BLOCK WORD ; GET THE VIDEO WORD FROM VIDEO RAM MOV BX,VDPADR ;GET BASE ADDRESS MOV AX,ES:[BX] XCHG AL,AH AND AX,3FFFH MOV DL,ES:[BX+2] AND DL,0C0H OR AH,DL POP DX ;DX <= BLOCK WORD ;CX <= MASK WORD ;AX <= VIDEO WORD ;BLOCK <= ((VIDEO XOR BLOCK) AND MASK) XOR BLOCK XOR AX,DX AND AX,CX XOR AX,DX ; STORE THE WORD IN VIDEO RAM MOV BX,VDPADR ;GET BASE ADDRESS ;PHASE SHIFT ;MOVE THE WORD INTO VIDEO RAM MOV ES:[BX+1],AL MOV AL,AH AND AX,3FC0H MOV DH,ES:[BX] MOV DL,ES:[BX+2] AND DX,0C03FH OR DX,AX MOV ES:[BX],DH MOV ES:[BX+2],DL POP CX RET M1DISP ENDP %OUT DMPMSK DONE PAGE ; ------ ; GSHOWN ; ------ ; DISPLAY NEGATIVE ICON [ARG3] AT X=[ARG1], Y=[ARG2] PUBLIC GSHOWN GSHOWN PROC MOV NEGATE,0FFH ;ENABLE NEGATE JMP SHOWI ; AND DO A SHOWI GSHOWN ENDP ; ------ ; GSHOWI ; ------ ; DISPLAY POSITIVE ICON [ARG3] AT X=[ARG1], Y=[ARG2] PUBLIC GSHOWI GSHOWI PROC MOV NEGATE,0 ;DISABLE NEGATE ; AND FALL THROUGH ............. GSHOWI ENDP ; ----- ; SHOWI ; ----- ; CODE COMMON TO GSHOWI AND GSHOWN ; DISPLAY ICON [ARG3] AT X=[ARG1], Y=[ARG2] ; IF [ARG4] IS GIVEN, IT POINTS TO THE MASK TO BE USED PUBLIC SHOWI SHOWI PROC MOV MSKFLG,0 ;DISABLE MASKING UNTIL FURTHER NOTICE CMP NARGS,3 JE NOMASK ;IF ONLY 3 ARGS, THEN DON'T MASK ; {CASE} MASK MOV AX,ARG4 ;GET MASK INFO CALL ISU ; THIS ENTRYPOINT WILL PROVIDE: ; ICON[ARG4] DATA V-ADDR IN [IADR2] ; X-SIZE IN [IX2] ; Y-SIZE IN [IY2] ; ICON[ARG3] DATA V-ADDR IN [IADR1] ; X-SIZE IN [IX1] AND [XDEX1] ; Y-SIZE IN [IY1] AND [YDEX1] ; ASSIGNED BLOCKSET IN [BSET] MOV AL,IX1 ;IF WIDTH AND HEIGHT ARE NOT THE SAME CMP AL,IX2 ; THEN DO NOTHING JNE DRAWEX MOV AL,IY1 CMP AL,IY2 JNE DRAWEX DEC MSKFLG ;ELSE SET MSKFLG [0FFH] JB ITENT ; YES, THIS IS AN UNCONDITIONAL JUMP ; {CASE} NO MASKING ; SET UP ICON [ARG3] NOMASK: MOV AX,ARG3 ;GET THE V-ADDRESS OF THE ICON CALL GETI ; AND SET IT UP ; ICON DATA V-ADDR IN [IADR1] ; X-SIZE IN [IX1] AND [XDEX1] ; Y-SIZE IN [IY1] AND [YDEX1] ; ASSIGNED BLOCKSET IN [BSET] ; AND FALL THROUGH ............. SHOWI ENDP PUBLIC ITENT,DRAWEX,DRAWI1,DRAWI2,DRAWI3,DRAWI4 PUBLIC DRAWI5,DRAWE2,DRAWIA,DRAWI8,DRAWI9,DRAWIB PUBLIC DRAWIC,DRAWID,DRAW,DRAW1,DRAW2A,CLPRI2 ITENT PROC MOV AL,0 ; INITIALIZE VARIABLES MOV TOPCUT,AL MOV SIDCUT,AL MOV MDXCUT,AL ; DETERMINE IF THE ICON CAN BE LEGALLY DRAWN ; FROM THE STANDPOINT OF IT'S Y-POSITION MOV AX,ARG2 ;GET ICON Y-POSITION OR AH,AH ; IF MSB=0 JZ DRAWI1 ; THEN ICON STARTS ON WINDOW PAGE CMP AH,0FFH ; IF ICON Y-POS MSB <> 0 AND <> 0FFH JNE DRAWEX ; THEN ICON WILL NOT BE DISPLAYED ; {CASE 1} ICON STARTS ABOVE WINDOW PAGE (MSB = 0FFH) ; DETERMINE TOP CLIP ; FIND ICON Y1 - WINDOW Y1 MOV BL,WINDY1 ;GET UPPER EDGE OF WINDOW SUB BH,BH ;WORDIFY NEG AX ;SINCE IT'S (-) WE GET TWO'S COMPLIMENT ADD AX,BX ; AND ADD TO GET THE ABSOLUTE DISTANCE OR AH,AH JZ DRAWI2 ;IF DISTANCE LE. 255 THEN CONTINUE ; ELSE DEPART DRAWEX: RET ; {CASE 2} ICON STARTS ON WINDOW PAGE (MSB = 00H) ; DETERMINE IF A TOPCLIP IS NECESSARY DRAWI1: MOV AL,WINDY1 ;FIND WINDOW Y1 - ICON Y1 MOV BL,BYTE PTR ARG2 SUB AL,BL ;IF (-) THEN ICON STARTS BELOW TOP EDGE JNS DRAWI2 ; OF WINDOW ; {CASE 2A} THE ICON TOPEDGE LIES ON OR BELOW THE WINDOW TOPEDGE ; NO TOPCUT IS NECESSARY ; [WINDY2] = LAST LINE OF THE WINDOW TO DISPLAY DRAWI3: MOV AL,WINDY2 ;FIND WINDY2 - ICON Y1 [BL] INC AL SUB AL,BL ;GET DISTANCE JS DRAWEX ;EXIT IF (-) THE WHOLE ICON IS BELOW THE WINDOW ; COMPARE SIZE OF THE DISPLAYABLE AREA ((WINDY2 + 1) - ICON Y1) ; AND THE ICON HEIGHT CMP AL,IY1 ;IF SIZE OF DISPLAYABLE AREA GE.ICON HEIGHT JAE DRAWI4 ; THEN DON'T BOTHER CHANGING ICON HEIGHT MOV IY1,AL ; ELSE, SUBSTITUTE SIZE OF THE DISPLAYABLE JMP DRAWI4 ; AREA FOR THE ICON HEIGHT (BOTTOM CUT) DRAWI2: MOV TOPCUT,AL ;SAVE AMOUNT TO CUT FROM TOP OF ICON ;DO THE TOPCUT DRAWI4: MOV AL,IY1 ;GET HEIGHT OF ICON SUB AL,TOPCUT ;DO TOPCUT JBE DRAWEX ;NO ICON LEFT TO DRAW SO EXIT MOV YDEX1,AL SUB AL,WINDH ;IF THE ICON IS SMALLER THAN THE WINDOW JB DRAWI5 ; THEN DISPLAY ALL OF IT MOV AL,WINDH ; ELSE, DISPLAY A WINDOW'S WORTH OF ICON MOV YDEX1,AL ; MULTIPLY TOPCUT AND ICON WIDTH, ADD PRODUCT TO ICON POINTER ; IN ORDER TO POINT AT THE START OF THE FIRST DISPLAYED ROW DRAWI5: MOV AL,BYTE PTR ARG2 ;CHANGE ICON STARTING YPOS ADD AL,TOPCUT ;TO REFLECT TOPCUT MOV BYTE PTR YPOS,AL MOV AL,TOPCUT MOV BL,IX1 MUL BL ;AX=TOPCUT * ICON WIDTH PUSH AX ;SAVE TOPCUT CALL AIADR1 ;IADR1 <= IADR1 + TOPCUT POP AX ;RESTORE TOPCUT CMP MSKFLG,0 ;IS MASKING DISABLED? JE NOMSK1 ;....YES CALL AIADR2 ;....NO. IADR2 <= IADR2 + TOPCUT ;COMPUTE THE DISPLAYED WIDTH AND THE IXSKIP NOMSK1: MOV AL,IX1 ;KLUDGE MOV MDXCUT,AL MOV AX,ARG1 ;GET ICON X-COORDINATE OR AH,AH ;IF MSB = 0 JZ DRAWI8 ; THEN ICON STARTS ON WINDOW PAGE CMP AH,0FFH ; ELSE, IF MSB <> 0FFH JNE DRAWE2 ; THEN WE MIGHT AS WELL GO HOME ; {CASE} ICON STARTS BEFORE WINDOW PAGE (MSB = 0FFH) ; DETERMINE LEFT CLIP ; FIND ICON X1 - WINDOW X1 MOV BL,WINDX1 ;GET LEFT EDGE OF WINDOW SUB BH,BH NEG AX ;SINCE IT'S NEG, WE CHANGE THE SIGN ADD AX,BX ; AND GET THE ABSOLUTE DISTANCE OR AH,AH JZ DRAWI9 ;IF d <= 255 THEN CONTINUE DRAWE2: RET DRAWIA: MOV AL,WINDX2 INC AL SUB AL,BYTE PTR ARG1 ;GET DISTANCE JS DRAWE2 CMP AL,IX1 ;COMPARE WITH WIDTH JAE DRAWIB MOV IX1,AL JMP DRAWIB DRAWI8: MOV AL,WINDX1 MOV BL,BYTE PTR ARG1 SUB AL,BL JS DRAWIA DRAWI9: MOV SIDCUT,AL ;DO THE SIDCUT DRAWIB: MOV AL,IX1 ;GET WIDTH SUB AL,SIDCUT JBE DRAWE2 MOV XDEX1,AL SUB AL,WINDW JB DRAWIC MOV AL,WINDW MOV XDEX1,AL DRAWIC: MOV AL,SIDCUT SUB AH,AH PUSH AX ;SAVE SIDECUT CALL AIADR1 ;IADR1 <= IADR1 + SIDCUT POP AX ;RESTORE SIDCUT CMP MSKFLG,0 ;IS MASKING DISABLED? JE DRAWID ;....YES CALL AIADR2 ;....NO. IADR2 <= IADR2 + SIDCUT DRAWID: MOV AL,MDXCUT SUB AL,XDEX1 MOV IXSKIP,AL MOV AL,XDEX1 MOV IX1,AL ;SET COUNTER REFRESH VALUE MOV AL,BYTE PTR ARG1 ADD AL,SIDCUT MOV BYTE PTR XPOS,AL MOV BYTE PTR XPSAV,AL ;GET THE REQUIRED BLOCKSET DATA MOV AL,BSET ;USE THE BLOCKSET ID ; GET THE ADDRESS OF THE CURRENT BLOCKSET MOV BX,BTAB ;GET THE BASE ADDRESS OF THE BPT MOV VMP,BX ;SET UP VMP MOV VMP0,0 SUB AH,AH ;AL CONTAINS BLOCKSET ID DEC AL ;ZERO ALIGN IT ADD VMP,AX ;ADD WORD INDEX TO BASE ADDRESS CALL GW@VMP ;GET THE BLOCKSET POINTER MOV VMP,AX ;SET UP VMP MOV VMP0,0 CALL GW@VMPI MOV BLKLEN,AH ;# BYTES PER BLOCK MOV BSIZE,AL ;# OF BLOCKS MOV BX,VMP ;MOVE V-ADDR OF BLOCKSET DATA MOV BSADR,BX ;INTO [BSADR] ;CHECK FOR MASKING CMP MSKFLG,0 ;IS MASKING DISABLED? JE DRAW ;....YES. USE NORMAL DRAW ROUTINE JMP DOMASK ; ELSE, GO DO THE MASK DRAW ROUTINE ; ; DRAW THE ICON ITERATION AT [IADR1] ; ; DRAW A ROW OF ICON BLOCKS DRAW: PUSH DI ; (B5) SAVE GSTACK CALL SETIADR ; (B5) SET UP SI AND GET BLOCK ID CALL GTGBLK ;GET GRAPHICS BLOCK [AL] INTO BLOCK CMP FSIZE,0 ; (B5) SET UP DI ACCORDING TO REAL MEMORY JE DRW0 DRW: MOV DI,BSADR ; (B5) GET ADDRESS OF THE BLOCK SET SHL DI,1 ; (B5) CONVERT TO A REAL ADDR DRW0: CMP FSIZE,0 ; (B5) CALCULATE END-OF-PAGE JNE DRW1 ; (B5) FITS - EOP AT END OF SEG MOV AX,SI ; (B5) GET I-PAGE AND AX,0FE00H ; (B5) ISOLATE BLOCK BITS ADD AX,200H ; (B5) ADD PAGE LENGTH TO GET EOP MOV I_EOP,AX ; (B5) SAVE IT JMP DRW2 DRW1: MOV I_EOP,0 MOV G_EOP,0 ; (B5) SET EOPAGES TO END OF SEGMENT DRW2: MOV AH,BYTE PTR XPOS ; (B5) GET XPOSITION MOV AL,BYTE PTR YPOS ; (B5) AND Y MOV CL,YDEX1 ; (B5) SET UP Y INDEX MOV BL,XDEX1 ; (B5) SET UP X INDEX JMP DRW3 FIX_I: CMP FSIZE,0 ; (B5) DECIDE WHICH TYPE OF FIX JNE FXI3 MOV BYTE PTR XPOS,AH ; (B5) SAVE VARS MOV BYTE PTR YPOS,AL MOV YDEX1,CL MOV XDEX1,BL MOV AX,IADR1 ; (B5) GET STARTING ADDRESS AND AX,7F00H ; (B5) ISOLATE BLOCK BITS ADD AX,100H ; (B5) AND ADVANCE TO NEXT BLOCK MOV IADR10,0 ; (B7) SET THIS TEST SI,1 ; (B7) EVEN? JZ FXI2 MOV IADR10,1 ; (B7) NOPE, ODD FXI2: AND SI,1FEH ; (B5) ISOLATE WRAP VALUE SHR SI,1 ; (B7) ALIGN WITH IADR1 IN AX OR AX,SI ; (B5) AND MAKE THEM COUNT HERE MOV IADR1,AX ; (B5) SAVE VIRTUAL PAGE CALL SETIADR ; (B5) GET ICON ID AND RESET SI CALL GTGBLK ; (B5) GET A GRAPHIC BLOCK JMP DRW0 FXI3: ADD BP,1000H JMP DRW$ DRAW$: POPF JC FIX_I CMP FSIZE,0 ; (B5) NO END OF PAGE IF FITS JNE DRW$ CMP SI,I_EOP ; (B5) CHECK END CONDITIONS JAE FIX_I DRW$: XCHG AX,DX ; (B5) SAVE X,Y PUSH ES ; (B5) NO, SAVE IT MOV ES,BP ; (BP) AND MAKE ADDRESSIBLE MOV AL,ES:[SI] ; (B5) GET BYTE POP ES PUSH ES ; (B5) SAVE IN CASE OF CHANGE CALL GTGFST ; (B5) GET IT DIRECTLY UNLESS SEG WRAP POP ES XCHG AX,DX ; (B5) RESTORE X,Y ; FALL THROUGH INTO DUMP LOOP DRW3: PUSH AX PUSH BX PUSH CX PUSH BP PUSH SI PUSH ES CALL DUMP ; (B5) DUMP SAVES DI POP ES POP SI POP BP POP CX POP BX POP AX ; UPDATE THE COLUMN COUNT DRAW1: DEC BL ;DECREMENT THE COLUMN COUNTER JZ CLPRI2 ; IF WE'RE OUT OF COLUMNS, DO NEXT ROW INC AH ; ELSE, INCREMENT X COORDINATE ; UPDATE THE BLOCK ID POINTER DRAW2A: INC SI ; (B5) NEXT ICON BYTE PUSHF ; (B5) SAVE FLAGS FOR ZERO JMP DRAW$ ; AND DRAW THE NEXT ICON BYTE ; NEXT ROW ; REFRESH THE COLUMN COUNTER CLPRI2: MOV BL,IX1 ; REFRESH THE X-COORDINATE MOV AH,BYTE PTR XPSAV ; SKIP BLOCK #'S THAT AREN'T DRAWN AND UPDATE POINTER MOV DL,IXSKIP SUB DH,DH INC DX ;IADR1 <= IADR1 + IXSKIP + 1 ITEN1: ADD SI,DX ; (B5) ADD IN SKIP VALUE PUSHF ; UPDATE THE ROW COUNTER INC AL ;INCREMENT THE Y-COORDINATE DEC CL ;DECREMENT ROW COUNTER JNZ DRAW$ ; LOOP UNTIL ALL ROWS ARE DONE POPF POP DI RET ITENT ENDP PUBLIC SETIADR SETIADR PROC MOV AX,IADR1 ;POINT [VMP] TO CURRENT BLOCK ID MOV VMP,AX MOV AL,IADR10 MOV VMP0,AL CALL GB@VMP ;GET THE BLOCK ID INTO AL MOV BP,ES ; (B5) SAVE SEG MOV CL,LOC ; (B5) GET LAST EFFECTIVE ADDRESS XOR CH,CH ; (B5) DO CONVERSION SHL CX,1 ; (B5) DOUBLE VPAGE MOV BX,LADD ; (B5) GET LOW ADDRESS BITS OR BH,CL ; (B5) OR IN HIGH BITS MOV SI,BX ; (B5) SAVE ICON ADDRESS RET SETIADR ENDP ; ; DRAW THE ICON ITERATION AT [IADR1] WITH MASK AT [IADR2] ; DOMASK PROC MOV AX,IADR2 ;POINT [VMP] TO THE NEXT MASK BLOCK ID MOV VMP,AX MOV AL,IADR20 MOV VMP0,AL MOV AL,NEGATE ;SAVE NEGATE STATUS PUSH AX MOV NEGATE,0 ;NEVER NEGATE THE MASK CALL GB@VMP ;GET MASK BLOCK ID CALL GTGBLK ;GET MASK GRAPHICS BLOCK [AL] INTO BLOCK ; COPY [BLOCK] INTO [MASK] MOV CX,8 ;LOAD LOOP COUNTER SUB BX,BX ;RESET INDEX B2MLP: MOV AL,BLOCK[BX] MOV MBLOCK[BX],AL INC BX LOOP B2MLP MOV AX,IADR1 ;POINT [VMP] TO CURRENT ICON BLOCK ID MOV VMP,AX MOV AL,IADR10 MOV VMP0,AL POP AX ;RESTORE NEGATE MOV NEGATE,AL CALL GB@VMP ;GET THE BLOCK ID INTO AL CALL GTGBLK ;GET GRAPHICS BLOCK [AL] INTO BLOCK MOV AH,BYTE PTR XPOS ;GET THE X-COORDINATE MOV AL,BYTE PTR YPOS ; AND THE Y-COORDINATE ; DRAW [BLOCK] AGAINST [MASK] AT X,Y [AX] CALL DMPMSK ; UPDATE THE COLUMN COUNT DEC XDEX1 ;DECREMENT THE COLUMN COUNTER JZ CLPRIA ; IF WE'RE OUT OF COLUMNS, DO NEXT ROW INC BYTE PTR XPOS ; ELSE, INCREMENT X COORDINATE ; UPDATE THE ICON BLOCK ID POINTER XOR IADR10,1 ;CHANGE THE STATE OF BIT0 JNZ DM1 ; CONTINUE IF CHANGING FROM A "0" TO A "1" INC IADR1 ;ELSE BUMP THE REST OF IADR1 ; UPDATE THE MASK BLOCK ID POINTER DM1: XOR IADR20,1 ;CHANGE THE STATE OF BIT0 JNZ DM2 ; CONTINUE IF CHANGING FROM A "0" TO A "1" INC IADR2 ;ELSE BUMP THE REST OF IADR2 DM2: JMP DOMASK ; NEXT ROW ; REFRESH THE COLUMN COUNTER CLPRIA: MOV AL,IX1 MOV XDEX1,AL ; REFRESH THE X-COORDINATE MOV AL,BYTE PTR XPSAV MOV BYTE PTR XPOS,AL ; SKIP BLOCK #'S THAT AREN'T DRAWN AND UPDATE THE POINTERS MOV AL,IXSKIP SUB AH,AH INC AX ; +1 TO UPDATE THE POINTERS PUSH AX ;SAVE IXSKIP + 1 CALL AIADR1 ;IADR1 <= IADR1 + IXSKIP + 1 POP AX ;RESTORE IXSKIP + 1 CALL AIADR2 ;IADR1 <= IADR1 + IXSKIP + 1 ; UPDATE THE ROW COUNTER INC BYTE PTR YPOS ;INCREMENT THE Y-COORDINATE DEC YDEX1 ;DECREMENT ROW COUNTER JZ DOMA1 ;EXIT WHEN DONE JMP DOMASK ; ELSE, LOOP DOMA1: RET DOMASK ENDP PAGE ; ---- ; SETI ; ---- ; COPY ICON [ARG3] INTO ICON [ARG4] WITH ; TOP LEFT CORNER AT X=[ARG1], Y=[ARG2]; ; RETURN "0" IF RANGE ERROR, "1" IF OKAY PUBLIC GSETI,SI0,SI1,SI3 GSETI PROC CALL ISETUP ;GET STATS OF SOURCE AND DESTINATION CALL DOFIT ;WILL THE SOURCE FIT IN THE DESTINATION? JNC SIA ;CARRY CLEAR IF OK JMP RET0 ;ELSE RETURN A ZERO ; [GPC] WILL BE TEMPORARILY USED FOR AN AUTOINCREMENTING ; VIRTUAL MEMORY POINTER SIA: MOV AX,GPC ;PUSH [GPC] ON THE STACK PUSH AX MOV AL,GPC0 ;PUSH GPC BIT ON THE STACK PUSH AX ; VMP POINTS TO THE FIRST BYTE OF THE SOURCE ICON ; POINT [GPC] AT THE FIRST BYTE OF THE SOURCE ICON MOV AX,VMP MOV GPC,AX MOV AL,VMP0 MOV GPC0,AL ; POINT [VMP] AT THE CURRENT BYTE OF THE DESTINATION SUB-ICON SI0: MOV AX,IADR2 MOV VMP,AX MOV AL,IADR20 MOV VMP0,AL SI1: CALL GB@GPCI ;GET A BYTE FROM THE SOURCE CALL PB@VMPI ;PUT IT IN THE DESTINATION DEC XDEX1 ;OUT OF SOURCE X'S YET? JNZ SI1 ;....NO. MOVE ANOTHER BYTE MOV AL,IX1 ;REFRESH COLUMN COUNTER MOV XDEX1,AL MOV AL,BYTE PTR IX2 ;GET X-SIZE OF DESTINATION SUB AH,AH ;ADD TO BASE ADDRESS OF SUB-ICON CALL AIADR2 ;IADR2 <= IADR2 + [AX] ;TO GET ADDRESS OF NEXT SUB-ROW SI3: DEC YDEX1 ;OUT OF Y'S YET? JNZ SI0 ;....YES. RESET X-INDEX ; AND LOOP TILL EMPTY ; RESTORE GPC POP AX MOV GPC0,AL POP AX MOV GPC,AX MOV AL,1 ;RETURN A 1 FOR SUCCESS JMP PUTBYT GSETI ENDP PAGE ; ----- ; SWAPI ; ----- ; SAME AS "SETI" EXCEPT ICON [ARG4] SUB-DATA IS COPIED ; BACK INTO ICON [ARG3] PUBLIC GSWAPI,GSW0,GSW1,GSW2,PRERR2 GSWAPI PROC CALL ISETUP ;SETUP ICON ; SOURCE ICON MUST BE IN PRELOAD MOV AX,IADR1 ;GET ICON ADDRESS CMP AX,IPURE ;IS SOURCE IN I-PRELOAD? JB GSW0 ;....YES. SO CONTINUE ; *** ERROR #14: PURITY VIOLATION (SETI/SWAPI) *** PRERR2: MOV AL,14 JMP GERROR ; SOURCE ICON MUST BE NO LARGER THAN THE DESTINATION ICON GSW0: CALL DOFIT ;WILL IT FIT IN THE DESTINATION JNC GSW1 ;CARRY IS CLEAR IF OK JMP RET0 ;ELSE RETURN A ZERO ; SWAP ICONS ; [GPC] WILL BE TEMPORARILY USED FOR AN AUTOINCREMENTING ; VIRTUAL MEMORY POINTER GSW1: MOV AX,GPC ;PUSH [GPC] ON THE STACK PUSH AX MOV AL,GPC0 ;PUSH GPC BIT ON THE STACK PUSH AX ; SET UP OUTER LOOP ; POINT [GPC] AT THE SOURCE ICON GSW2: MOV AX,IADR1 MOV GPC,AX MOV AL,IADR10 MOV GPC0,AL ; POINT [VMP] AT THE DESTINATION ICON MOV AX,IADR2 MOV VMP,AX MOV AL,IADR20 MOV VMP0,AL ; INNER LOOP GSW3: CALL GB@GPC ;GET A BYTE FROM THE SOURCE MOV BL,AL ;SAVE IT TEMPORARILY CALL GB@VMP ;GET DESTINATION BYTE CALL PB@GPCI ;PUT IT IN THE SOURCE ; AND INCREMENT GPC MOV AL,BL ;RETRIEVE ORIGINAL SOURCE BYTE CALL PB@VMPI ;PUT IT IN THE DESTINATION ; AND INCREMENT THE VMP PTR ; TEST INNER LOOP DEC XDEX1 ;OUT OF SOURCE X'S YET? JNZ GSW3 ;....NO. MOVE ANOTHER BYTE ; REFRESH INNER LOOP VARIABLES MOV AL,IX1 ;REFRESH COLUMN COUNTER MOV XDEX1,AL ; UDATE SOURCE AND DESTINATION ADDRESSES MOV AL,BYTE PTR IX2 ;ADD X-SIZE OF DESTINATION SUB AH,AH ; TO BASE ADDRESS OF SUB-ICON CALL AIADR2 ;TO GET ADDRESS OF NEXT ROW MOV AL,BYTE PTR IX1 ;ADD X-SIZE OF SOURCE SUB AH,AH ; TO BASE ADDRESS OF SOURCE CALL AIADR1 ;TO GET ADDRESS OF NEXT ROW ; TEST OUTER LOOP DEC BYTE PTR YDEX1 ;OUT OF Y'S YET? JNZ GSW2 ; NO, RESET ROW INDEX AND LOOP ;RESTORE THE GPC POP AX MOV GPC0,AL POP AX MOV GPC,AX ;SET UP RETURN VALUE MOV AL,1 ;RETURN A 1 FOR SUCCESS JMP PUTBYT GSWAPI ENDP PAGE ; ----- ; SOUND ; ----- ; PLAY SOUND [ARG1] (1-127) PUBLIC GSOUND GSOUND PROC MOV AX,ARG1 OR AX,AX JZ SNDERR ;RANGE ERROR JS SNDERR ;RANGE ERROR CMP AX,NSNDS + 1 JAE SNDERR ;SOUND NOT DEFINED DEC AX SHL AX,1 ;MAKE IT A WORD OFFSET MOV BP,AX ;GET SOUNDLIST PTR MOV SI,DS:[BP+STABLE] CALL SI ;CALL THE SOUND ROUTINE RET GSOUND ENDP PUBLIC SNDERR SNDERR PROC ; *** ERROR #13: ILLEGAL SOUND EFFECT *** ; MOV AL,0DH ; JMP GERROR ; CHANGE MADE PER LIM'S REQUEST CALL SND1 CALL SND4 CALL SND1 CALL SND4 RET SNDERR ENDP ; ; ACTUAL SOUND EFFECTS ; PUBLIC SND1 ;BOOP SND1 PROC MOV CX,500 ;FREQ MOV DX,7 ;DURATION * .05 SEC JMP TONE SND1 ENDP PUBLIC SND2 ;CLICK SND2 PROC MOV CX,3000 ;FREQ MOV DX,1 ;DURATION * .05 SEC JMP TONE SND2 ENDP PUBLIC SND3 ;TING SND3 PROC MOV CX,2600 ;FREQ MOV DX,5 ;DURATION * .05 SEC JMP TONE SND3 ENDP PUBLIC SND4 ;RAZZ SND4 PROC MOV CX,120 ;FREQ MOV DX,20 ;DURATION * .05 SEC JMP TONE SND4 ENDP PAGE ; ---- ; RAND ; ---- ; RETURN A RANDOM VALUE BETWEEN 1 AND [ARG1] PUBLIC GRAND GRAND PROC MOV AX,ARG1 SUB AH,AH ; HACK RANDOM TO ONE BYTE MOV RTEMP,AX ; SAVE ARGUMENT FOR LATER MOV AX,RSEED1 MOV BX,RSEED2 MOV RSEED2,AX CLC RCR AX,1 RCR BX,1 XOR RSEED1,BX ;PUT XOR'D NUBMER INTO RSEED1 MOV AX,RSEED1 ;THIS IS RANDOM VALUE AND AX,0EFFFH ;CHOP HIGH BIT FOR DIVIDE SUB DX,DX ;CLEAR MOST SIGN. PORTION OF 32 BIT WORD DIV RTEMP ;DIVIDE WORD BY WORD MOV AX,DX ;MOVE REMAINDER INTO AX INC AX ;MAKE SURE NO ZERO MOV VALUE,AX ;SAVE IT IN VALUE JMP PUTVAL GRAND ENDP PAGE ; ----- ; CLEAR ; ----- ; ; CLEAR SCREEN TO WHITE IF [ARG1] IS NEGATIVE ; CLEAR SCREEN TO BLACK IF [ARG1] IS POSITIVE ; PUBLIC GCLEAR,GCLR1,GCLR2,GCLR3 GCLEAR PROC PUSH DI ;SAVE AFFECTED REGISTER(S) PUSH CX PUSH ES MOV AX,VID_SG ;POINT AT THE VIDEO BUFFER MOV ES,AX MOV AX,ARG1 ;GET THE ARGUMENT CMP AX,0 ; (B5) DO BLACK DIFFERENTLY JE GCLR4 MOV BP,AX ; GET WORD AND BP,3FH ; MASK OFF HI NIBBLE FOR BIT SHIFT PROBLEM MOV DI,0 ;SET UP INDEX MOV BX,1E00H ;SET UP LOOP COUNTER ; PAINT THE SCREEN WITH AX GCLR1: XCHG AX,BP ; GET MASK BYTE STOSB XCHG AX,BP MOV CX,79 ; DO A LINE - 1 AT A TIME REPZ STOSB SUB BX,80 ; SUBTRACT FROM TOTAL JNZ GCLR1 MOV DI,2000H MOV BX,1E00H GCLR2: XCHG AX,BP STOSB XCHG AX,BP MOV CX,79 ; (B4) DO A LINE REPZ STOSB SUB BX,80 JNZ GCLR2 ;CLEANUP WHEN COMPLETE GCLR3: POP ES ;RESTORE THOSE REGISTERS POP CX POP DI RET GCLR4: MOV DI,0 MOV CX,0F00H ; (B5) DO WHOLE SCREEN REPZ STOSW MOV DI,2000H ; (B5) ODD BYTES MOV CX,0F00H ; (B5) COUNT REPZ STOSW JMP GCLR3 GCLEAR ENDP PAGE ; ------ ; WINDOW ; ------ ; SET THE WINDOW LIMITS TO THE RECTANGLE WHOSE UPPER LEFT SCREEN ; COORDINATE IS AT (locx1, locy1) AND LOWER RIGHT SCREEN COORDINATE ; IS AT (locx2, locy2). PUBLIC GWIND,WISK0,WISK1,WISK2,WISK3,WISK4 PUBLIC WISK5,WISK6,WISK7,WISK8 GWIND PROC SUB BX,BX ;RESET INDEX ;TEST IF - OR > 255 WISK0: MOV AX,WORD PTR ARG1[BX] ;GET COORDINATE OR AH,AH ;DOES MSB=0? JZ WISK1 ;....YES. COORDINATE IS EITHER + OR 0 SUB AX,AX ;ELSE IT'S NEGATIVE MOV WORD PTR ARG1[BX],0 ; CLIP IT TO 0 ;IT'S EITHER + OR 0 WISK1: PUSH BX ;SAVE INDEX AND BX,2 ;IS IT AN X OR Y COORDINATE? POP BX ;RESTORE INDEX JZ WISK2 ;IT'S AN X-COORDINATE ;Y COORDINATE CLIPPING CMP AX,23 ;IS THE Y-COORDINATE ON SCREEN? JBE WISK3 ;....YES MOV ARG1[BX],23 ;....NO. CLIP IT JMP WISK3 ;X COORDINATE CLIPPING WISK2: CMP AX,39 ;IS THE X-COORDINATE ON SCREEN? JBE WISK3 ;....YES MOV ARG1[BX],39 ;....NO. CLIP IT WISK3: INC BL ;UPDATE INDEX TO POINT INC BL ;AT THE NEXT ARG CMP BL,8 ;HAVE WE DONE ALL OF THE ARGS JNZ WISK0 ;....NO. KEEP LOOPING ; SWITCH X-COORDINATES IF REQUIRED WISK4: MOV AL,BYTE PTR ARG1 ;GET X1 MOV AH,BYTE PTR ARG3 ;GET X2 CMP AL,AH ;IF X1 < X2 JL WISK5 ; THEN DON'T SWAP XCHG AL,AH ;SWAP X1 AND X2 WISK5: MOV BYTE PTR WINDX1,AL ;SAVE X1 MOV BYTE PTR WINDX2,AH ;SAVE X2 ;COMPUTE WINDOW WIDTH SUB AH,AL ;WINDW = WINDX2 - WINDX1 + 1 INC AH MOV WINDW,AH ; SWITCH Y-COORDINATES IF REQUIRED WISK6: MOV AL,BYTE PTR ARG2 ;GET Y1 MOV AH,BYTE PTR ARG4 ;GET Y2 CMP AL,AH ;IF Y1 > Y2 JL WISK7 ; THEN DON'T SWAP XCHG AL,AH ;SWAP Y1 AND Y2 WISK7: MOV BYTE PTR WINDY1,AL ;SAVE Y1 MOV BYTE PTR WINDY2,AH ;SAVE Y2 ;COMPUTE WINDOW HEIGHT WISK8: SUB AH,AL ;WINDH = WINDY2 - WINDY1 + 1 INC AH MOV WINDH,AH RET GWIND ENDP PAGE ; ----- ; GITER ; ----- ; ; INITIALIZE ITERATION TABLE AT WORD ADDRESS ARG1 ; GITER PROC MOV AX,ARG1 ;POINT TO TABLE BASE ADDRESS MOV VMP,AX MOV VMP0,0 CALL GB@VMPI ;[BYTE0] NUMBER OF ENTRIES IN TABLE MOV ITICN,AL MOV AL,1 ;[BYTE1] SET CURRENT ENTRY=1 CALL PB@VMPI ; WE'RE POINTING TO THE DATA STRUCTURE OF THE FIRST ENTRY ; AND SUCCEEDING ENTRIES GITER1: CALL GW@VMPI ;GET THE ICON WORD-PTR ADDRESS IN [AX] ; SAVE VIRTUAL MEMORY POINTER MOV BX,VMP PUSH BX MOV BL,VMP0 PUSH BX CALL GETI ;GET THE ICON INFORMATION ; RESTORE VIRTUAL MEMORY POINTER POP BX MOV VMP0,BL POP BX MOV VMP,BX ; SKIP ICON'S HORIZONTAL AND VERTICAL POSITION ADD VMP,2 ;TWO WORDS ; SET UP REMAINING DATA SUB AL,AL ;NEGATE=0 FOR POSITIVE IMAGE CALL PB@VMPI MOV AL,1 ;CURRENT ITERATION=1 CALL PB@VMPI MOV AL,BSET ;SAVE BLOCKSET IDENTIFIER CALL PB@VMPI MOV AL,ITERS ;SAVE NUMBER OF ITERATIONS CALL PB@VMPI MOV AL,IX1 ;SAVE WIDTH CALL PB@VMPI MOV AL,IY1 ;SAVE HEIGHT CALL PB@VMPI DEC ITICN ;ANY MORE ENTRIES? JNZ GITER1 ;....YES RET GITER ENDP ;------------------------ PUBLIC GRESTR GRESTR PROC MOV DSKDIR,0 ;INDICATE RESTORE (READ) JMP OSV0$ GRESTR ENDP ; ; SAVE ; PUBLIC GSAVE GSAVE PROC MOV DSKDIR,1 ;INDICATE SAVE (WRITE) ; ;COMMON ENTRYPOINT FOR SAVE AND RESTORE ; ; DO A DISK RESET IN CASE THEY WANT TO CHANGE DISKS ; UNFORTUNATELY WE HAVE TO ASSUME A 1 DRIVE SYSTEM AT THIS POINT OSV0$: MOV AH,CFCLOSZ ; (7s) CLOSE THE GAME FILE MOV BX,GAMHNDL ; (7s) FOR REINSERTION INT 21H MOV AH,CDRESET ; (K6) WILL RESET FLUSH BUFFERS? INT 21H ; (K6) HOPE SO... MOV AH,2 ; SET CURSOR POSITION MOV DX,100H ; COORD'S ARE 0,0 MOV BH,0 ; ACTIVE PAGE 0 INT 10H ; TO PREVENT INVISIBLE CURSOR MOVEMENT ; PRINT "FILENAME (DEFAULT IS drv:filename.ext):" ; PRINT "FILENAME (DEFAULT IS drv:" OSV0A: MOV AX,OFFSET SAV0 ;PRINT "INSERT SAVE DISK THEN ENTER FILE..." CALL MPRNT ; WITH CRLF MOV AX,OFFSET SAV1 ;PRINT "FILE NAME (DEFAULT IS " CALL NPRNT ; WITH NO CRLF TEST DRVFLG,1 ; HAS USER SPECIFIED A DRIVE JNZ OSVD1 ; YES, USE USER DEFAULT DRIVE MOV AL,CURDRV ;AL <= DRIVE NO. ADD AL,65 ;....NO. ADD 65 TO GET ASCII CHARACTER CALL PRTOUT ;PRINT DRIVE B, C, OR D MOV AL,":" ;PRINT ":" CALL PRTOUT ; PRINT "filename" OSVD1: PUSH SI ; (6) SAVE THIS MOV SI,OFFSET DATFILE ; (6) POINTER TO SAVE FILENAME (6) MOV CX,SI ; (6) SAVE POINTER CLD ; (6) SET DIR INCREMENT OSVD2: LODSB ; (6) GET A BYTE INTO AL TEST AL,AL ; (6) CHECK IF NULL JZ OSVD3 ; (6) EXIT THIS LOOP CALL PRTOUT ; (6) OUTPUT IT JMP OSVD2 OSVD3: SUB SI,CX ; (6) CALCULATE NUMBER OF CHARS MOV CX,SI ; (6) GET NUMBER BACK INTO CX POP SI ; (6) RESTORE THIS ; PRINT " ): " MOV AX,OFFSET SAV2 ;PRINT " ): " CALL NPRNT ; WITH NO CRLF ; GET LOSER'S SAVE FILENAME FROM THE KEYBOARD OSVD4A: MOV BX,OFFSET INBUF ;^ KEYBOARD INPUT BUFFER MOV AL,15 ;(6) COULD BE A WHOLE PATH MOV BYTE PTR [BX],AL ;SET UP BUFFER TO TAKE 15 CHARACTERS MOV DX,BX ;DX ^ BUFFER MOV AH,CRDLIN ;READ A LINE FROM THE KEYBOARD INT 21H ; PARSE THE FILENAME CALL SAVESAV ; (7o) PRESERVE THE SAVE NAME PUSH SI PUSH DI PUSH CX ; SAVE THIS PUSH ES PUSH DS POP ES LEA SI,INBUF[1] ; POINT TO NUM CHARS READ LODSB ; GET NUM TEST AL,AL ; ANY CHARS READ JZ LOOK MOV CL,AL ; STORE IT XOR CH,CH ; CLEAR TOP HALF OF COUNTER MOV DI,OFFSET DATFILE ; (6) DI ^FCB REPZ MOVSB ; TRANSFER THE STRING IF ANY MOV AL,0 ; (6) GET A NULL STOSB ; (6) DROP IT IN AT END OF STRING LOOK: MOV SI,OFFSET DATFILE ; (6) IS THERE A ':' INC SI LODSB ; (6) CHECK IT CMP AL,':' ; (6) IF SO, FIX DEFDRV JNZ NODRV ; (6) NOPE... MOV DRVFLG,1 ; (6) SET FLAG MOV AL,DATFILE ; (7n) GET DRIVE LETTER AND AL,5FH ; (7n) UPPERCASIFY SUB AL,'A' ; (7n) BASIFY CMP AL,CURDRV ; (7n) DON'T CHANGE IF UNNECESSARY JZ DRV MOV DL,AL ; (7n) SET UP FOR CHGDSK MOV AH,CSELDSK ; (7n) SELECT DISK IN DL INT 21H MOV AH,CURDSK ; (7n) CHECK IF IT WORKED INT 21H MOV CURDRV,AL ; (7n) SAVE IT JMP DRV USEDEF: MOV SKPDRV,1 ; (7n) DON'T OUTPUT A DRIVE NODRV: MOV DRVFLG,0 DRV: POP ES POP CX POP DI ;RESTORE'M POP SI CALL MCRLF ; (7) SCROLL PROPERLY ; OPEN THE FILE IF OP RESTORE CMP DSKDIR,1 ;OP SAVE? JE OSVOPW ;....YES MOV DX,OFFSET DATFILE ; 6) ....NO. OP RESTORE. DX ^ FCB MOV AH,CFOPENZ ; (6) SELECT DOS OPEN FUNCTION MOV AL,0 ; (6) FILE OPEN FOR READING INT 21H ;OPEN THE FILE JC OSVPSX ; (6) ....NO. FILE NOT FOUND JMP OSVPS ;....YES ; ; **** ERROR **** [File Not Found] ; OSVPSX: MOV AX,OFFSET ERR1 ;^ "SAVE FILE NOT FOUND" MSG JMP OSVPER ;ERROR EXIT ; CREATE THE FILE IF OP SAVE OSVOPW: MOV DX,OFFSET DATFILE ; (6) ^ FCB MOV CX,0 ; (6) NO SPECIAL ATTRIBUTE MOV AH,CFCREAZ ; (6) SELECT DOS CREATE FUNCTION INT 21H JC OSVOPDF JMP OSVPS ;....YES ; ; **** ERROR **** [Directory Full] ; OSVOPDF:MOV AX,OFFSET ERR4 ;....NO. ^ "NO ROOM IN DIRECTORY" MSG JMP OSVPER ;HOP TO ERROR EXIT ; ; WE'VE GOT AN OPEN FILE (WHICH WE CREATED IF IT WAS A SAVE FUNCTION) ; AND ARE READY TO EITHER READ (RESTORE) OR WRITE (SAVE) AS REQUIRED OSVPS: MOV DATHNDL,AX ; SAVE FILE HANDLE FOR DUMP/LOAD XCHG DI,SP MOV AH,GPC2 ; SAVE GPC MOV AL,GPC1 PUSH AX MOV AL,GPC0 ; SAVE LOW BIT PUSH AX ; OH, WHAT A WASTE PUSH GLOCS ; SAVE LOCALS TABLE POINTER PUSH OLDGSP ; RICK'S OLD G-STACK POINTER PUSH ZORKID ; SAVE GAME ID ; SUB SP,OFFSET GSTK_BT ; (7v) RELATIVISE THE POINTER ; MOV GSTK_BT,SP ; PUT DEST. INDEX INTO STACKBOTTOM mov ax,sp sub ax,offset gstk_bt ; make sp relative, but not in SP mov gstk_bt,ax ; the other way is dangerous MOV SP,DI ; RESTORE SP PUSH ES MOV AX,SS ; FLOP STACK SEG AND EXTRA SEG MOV ES,AX ; ES<=SS MOV DX,OFFSET GSTK_BT ; CORE LOCATION MOV CX,LSTACK*2 ; (6) DO TRANSFER HERE MOV BX,DATHNDL ; (6) GET HANDLE MOV AH,CRDRNDZ ; SETUP FOR A READ ADD AH,DSKDIR ; MAKE IT A WRITE IF NECESSARY INT 21H ; CMP AX,CX ; DID WE GET IT ALL PUSHF ;SAVE RETURN CODE MOV DI,SP ;DI ^ CURRENT ZSTACK POSITION ; MOV SP,GSTK_BT ;SP <= ZSTACK POINTER ; ADD SP,OFFSET GSTK_BT ; (7v) RELATIVISE THIS VALUE mov ax,gstk_bt add ax,offset gstk_bt mov sp,ax POP AX ; POP OFF AND COMPARE FOR ZORKID CMP AX,ZORKID ;ID OK? JE OSVT0 ;....YES xchg di,sp popf pop es MOV AL,18 ; SAVE ERROR JMP GERROR ;....NO. FATAL ERROR OSVT0: POP OLDGSP ; RESTORE OLD G-STACK POINTER FOR RETURN POP GLOCS ;RESTORE LOCALS AND ZIP PROGRAM COUNTERS POP AX ; RESTORE LOW BIT MOV GPC0,AL ; FROM THIS WORD POP AX ; RESTORE GPC MOV GPC2,AH MOV GPC1,AL XCHG DI,SP ;RESTORE MACHINE SP AND ZSTACK POINTER POPF ;RESTORE REGS POP ES ; BEGIN SAVE OF G-FILE PRELOAD ; MOV BX,DATHNDL ; GET HANDLE MOV AH,DSKDIR ; GET DIRECTION PUSH DS ; SAVE IT MOV CX,SEG0 ; GET GAME SEG MOV DS,CX ; ADDRESS IT PUSH BX ; USE THIS AS INDEX MOV BX,GEND ; DO > MOV CX,[BX] ; GET LENGTH WORD POP BX ; RESTORE HANDLE XCHG CH,CL ; BYTE SWAP SHL CX,1 ; CONVERT TO BYTES MOV DX,0 ; STARTS AT BEGINNING OF THE SEG ADD AH,CRDRNDZ ; START OUT WITH READ INT 21H ; READ IT JMP OSVPS2 ; ; OSVPS2: JC OSV4 ; SKIP ON ERROR ; SUCESSFUL TRANSFER OF THE G-FILE PRELOAD. ; TRANSFER I-FILE PRELOAD PUSH BX ; SAVE HANDLE PUSH DS ; SAVE BUFFER SEG MOV AX,CS ; GET DATA SEG MOV DS,AX ; SO THAT WE CAN DO REAL ADDRESSING MOV AX,ICODE ; GET INFO OUT OF I-PRE MOV VMP,AX ; SET UP VIRTUAL MEMORY POINTER MOV VMP0,0 ; TO GET MOV AX,IEND ; THE END OF I-PRELOAD CALL ADDVMP ; ADD IN OFFSET CALL GW@VMP ; GET THE WORD MOV DX,LADD ; GET THE LOW ADDRESS BITS MOV BL,LOC ; GET BLOCK NUMBER MOV BH,0 ; ZERO TOP HALF SHL BX,1 ; CONVERT TO ADDRESS OR DH,BL ; TURN ON BLOCK BITS SUB DX,IEND ; GET START BYTE ADDRESS MOV CX,AX ; GET NUMBER OF WORDS IN I-PRE SHL CX,1 ; CONVERT TO BYTES MOV AH,CRDRNDZ ; GET FUNCTION ADD AH,DSKDIR ; ADD IN DIRECTION POP DS ; RESTORE BUFFER SEG POP BX ; RESTORE HANDLE INT 21H ; READ/WRITE THE I-FILE JC OSV4 ; SUCCESS IN GENERAL -- CLOSE FILES POP DS ; HANDLE IS STILL IN BX MOV AH,CFCLOSZ ; CLOSE THE SAVE FILE INT 21H ; THRU DOS CALL GAMOPEN ; (7s) OPEN GAME FILE MOV AH,2 ; SET CURSOR POSITION MOV DX,100H ; COORD'S ARE 0,0 MOV BH,0 ; ACTIVE PAGE 0 INT 10H ; TO PREVENT INVISIBLE CURSOR MOVEMENT MOV DL,CURDRV ; (7n) RETURN TO SAVE DISK MOV AH,CSELDSK ; (7n) AND SELECT IT FOR OPEN INT 21H MOV DSKDIR,-1 ; RESET SAVE/RESTORE FLAG MOV ARG1,0 ; (B7) CLEAR OUT THE SCREEN CALL GCLEAR ; (B7) IN CASE OF ERRORS JMP PGOOD ; ; **** ERROR **** [Unable to Read File] ; OSV4: POP DS MOV AX,OFFSET ERR6 ;^ "Read of SAVE file failed." CMP DSKDIR,1 ;OP SAVE? JNE OSVPRR ;....NO MOV AH,CFCLOSZ ; (6) ....YES. CLOSE IT MOV BX,DATHNDL ; (6) GET HANDLE FOR CLOSE INT 21H ; ; **** ERROR **** [Diskette Full] ; MOV DX,OFFSET DATFILE ; (7n) DELETE THE PIECE OF A FILE MOV AH,CFDELEZ ; (7n) SO AS NOT TO CONFUSE INT 21H MOV AX,OFFSET ERR5 ;^ "No room on diskette for SAVE file." OSVPRR: JMP OSVPER GSAVE ENDP ; ; ERROR EXIT R/W operations ; OSVPER PROC CALL MPRNT ;PRINT ERROR MESSAGE CALL ZRESTOR ; (7o) RESTORE OLD SAVE NAME CALL GAMOPEN ; (7v) OPEN IT MOV AH,2 ; SET CURSOR POSITION MOV DX,100H ; COORD'S ARE 0,0 MOV BH,0 ; ACTIVE PAGE 0 INT 10H ; TO PREVENT INVISIBLE CURSOR MOVEMENT MOV ARG1,0 ; (B7) CLEAR OUT THE SCREEN CALL GCLEAR ; (B7) IN CASE OF ERRORS MOV DSKDIR,-1 ; RESET SAVE/RESTORE FLAG JMP PBAD OSVPER ENDP ; PUBLIC SAVESAV,ZRESTOR,SCRSAVE SCRSAVE PROC PUSH AX ; (7p) SAVES PUSH SI PUSH DX MOV SI,OFFSET DATFILE ; (7p) POINTER TO SAVE SCRS0: LODSB ; (7p) GET A CHARACTER TEST AL,AL ; (7p) LOOK FOR A NULL JNZ SCRS1 ; (7p) DIE ON A NULL POP DX POP SI POP AX RET ; (7p) RETURN SCRS1: MOV DL,AL ; (7p) GET CHARACTER INTO DL MOV AH,CPROUT ; (7p) FOR DOS"IE" OUTPUT INT 21H ; (7p) PRINT IT JMP SCRS0 SCRSAVE ENDP SAVESAV PROC PUSH DI ; (7o) SAVES PUSH SI PUSH AX PUSH ES MOV AX,DS ; (7o) SET UP REGS MOV ES,AX MOV AL,CURDRV ; (7o) SAVE CURRENT DRIVE SPEC MOV LASTDRV,AL ; (7o) IN CASE OF FAILURE MOV AL,DRVFLG ; (7o) TEST DRIVE FLAG MOV LSTDFLG,AL ; (7o) IN CASE OF FAILURE MOV SI,OFFSET DATFILE ; (7o) SETUP SOURCE AND DESTINATION MOV DI,OFFSET LASTSAV COPYLP: LODSB ; (7o) GET A BYTE STOSB ; (7o) STORE A BYTE TEST AL,AL ; (7o) STOP ON A NUL JNZ COPYLP ; (7o) CONTINUE THE COPY POP ES POP AX ; (7o) RESTORES POP SI POP DI RET SAVESAV ENDP ; ; ZRESTOR PROC PUSH DI ; (7o) SAVES PUSH SI PUSH AX PUSH ES MOV AX,DS ; (7o) SET UP REGS MOV ES,AX MOV AL,LSTDFLG ; (7o) GET LAST DRIVE FLAG MOV DRVFLG,AL ; (7o) AND RESTORE IT MOV AL,LASTDRV ; (7o) GET ORIGINAL DRIVE SPEC CMP AL,CURDRV ; (7o) HAS IT CHANGED JZ ZRES1 MOV DL,AL ; (7o) SELECT DISK MOV AH,CSELDSK ; (7o) TO CORRECT INT 21H MOV CURDRV,DL ; (7o) UPDATE THIS BABY ZRES1: MOV DI,OFFSET DATFILE ; (7o) SETUP SOURCE AND DESTINATION MOV SI,OFFSET LASTSAV ZRESLP: LODSB ; (7o) GET A BYTE STOSB ; (7o) STORE A BYTE TEST AL,AL ; (7o) STOP ON A NUL JNZ ZRESLP ; (7o) CONTINUE THE COPY POP ES POP AX ; (7o) RESTORES POP SI POP DI RET ZRESTOR ENDP PUBLIC GAMOPEN ; THIS PROCEDURE OPENS THE GAME FILE AFTER CLOSING ALL FILES. ; IF THE GAME FILE IS NOT ON THE DISK, IT PROMPTS FOR REINSERTION GAMOPEN PROC PUSH DX PUSH CX MOV CX,0 ; (B5) INDICATE FIRST PASS MOV DL,DEFDRV ; (7o) GET DRIVE WHERE GAME IS MOV AH,CSELDSK ; (7o) TO MAKE OPEN WIN INT 21H ; (7o) DO IT MOV AH,CFCLOSZ ; (7s) TRY CLOSING INSTEAD OF RESET MOV BX,GAMHNDL ; (7s) OF THE GAME FILE INT 21H ; (7o) TO MAKE SURE THIS WINS GOPEN0: MOV AH,CFOPENZ ; (6) ASCIZ STRINGS MOV AL,0 ; (6) OPEN FOR READING MOV DX,OFFSET GAMFILE ; (7o) OPEN THE FILE AGAIN INT 21H JNC GOPEN1 ; (7o) IF SUCCESS, CONTINUE INC CX MOV AX,OFFSET SAV3 ; (7o) OTHERWISE PROMPT CALL MPRNT CALL MCHRI ; (7o) AND WAIT FOR A CHARACTER JMP GOPEN0 ; (7o) AND LOOP GOPEN1: MOV GAMHNDL,AX ; (7o) SAVE GAME HANDLE MOV DL,CURDRV ; (7v) RESTORE SAVE DRIVE MOV AH,CSELDSK ; (7v) SO THAT WE ARE NOT CONFUSED INT 21H ; (7v) THANKS MAX CMP CX,0 ; (B5) CHECK FOR PAUSE JNE GOPEN2 ; (B5) NOT NECESSARY MOV AX,OFFSET STRIKE ; (B5) STRIKE ANY KEY MESSAGE CALL NPRNT ; (B5) PRINT IT CALL MCHRI ; (B5) AND WAIT GOPEN2: POP CX ; (B5) AND RESTORE THIS ONE POP DX RET GAMOPEN ENDP MCHRI PROC MOV AH,CNOECHO INT 21H RET MCHRI ENDP ;------------------------ PAGE ; ---- ; LOAD ; ---- ; ; PURPOSE: THIS ROUTINE LOADS THE SPECIFIED AMOUNT OF DATA INTO A ; SPECIFIED BLOCK OF PRELOAD. IF ARG3 IS PROVIDED, THEN ; THIS IS A TRANSFER FROM DISK. ELSE, IT IS AN I/O PORT ; OPERATION. ; ; INPUTS: ARG1=TABLE VIRTUAL ADDRESS ; ARG2=LENGTH IN WORDS ; ARG3=FILENAME POINTER (OPTIONAL) ; ; OUTPUTS: DATA IS TRANSFERED TO PRELOAD, IF THE OPERATION IS ; ILLEGAL OR FAILS FOR ANY REASON, A "0" IS RETURNED ; VIA {RET0}. ELSE A "1" IS RETURNED VIA {RET1}. ; ; REGISTERS DESTROYED: ALL ; PUBLIC GLOAD GLOAD PROC CMP NARGS,3 ;IS THIS A FILE TRANSFER REQUEST? JE GLD0 ;....YES JMP LDIO ;....NO, IT'S AN I/O TRANSFER ; ; FILE TRANSFER REQUEST ; ; ASSUME THAT THE DEFAULT DISK IS TO BE USED ; CLOSE THE GAMEFILE GLD0: MOV AH,CFCLOSZ ;CLOSE THE GAME FILE FOR REINSERTION MOV BX,GAMHNDL ;IN CASE THIS IS A 1 DRIVE SYSTEM, INT 21H ; OR THE DEFAULT DRIVE IS SPECIFIED MOV AH,CDRESET ;AND FLUSH INT 21H ; PARSE THE FILENAME MOV AX,ARG3 ;SET UP VMP TO POINT TO THE FILENAME MOV VMP,AX MOV VMP0,0 CALL GW@VMPI ;GET THE LENGTH OF THE FILENAME MOV CX,AX ;IN CX FOR USE AS A LOOP COUNTER MOV BX,OFFSET DATFILE ;^ FCB ;GET THE FILENAME INTO THE FCB GLD1: CALL GB@VMPI ;GET A CHARACTER MOV [BX],AL ;PUT IT IN THE FCB INC BX ;NEXT CHARACTER LOOP GLD1 ; GET THE REST MOV BYTE PTR [BX],0 ;DROP IN A NUL AT THE END OF THE NAME PUSH DI ;SAVE SOME REGISTERS PUSH DS POP ES ;BOTH ES & DS POINT AT THE DATA SEGMENT ; OPEN THE DATA FILE MOV AH,CFOPENZ ;SELECT OPEN FILE MOV AL,0 ;SELECT READ MOV DX,OFFSET DATFILE ;POINT AT THE ASCIIZ STRING INT 21H ;MDOS CALL JNC GLD2 JMP LDERR1 GLD2: MOV DATHNDL,AX ;SAVE THE FILE HANDLE ; START THE ACTUAL TRANSFER OPERATION RESTENT:CMP ARG2,0 ;DO WE HAVE ANY DATA? JNE GLDLP ;....YES JMP LDERR3 ;....NO GLDLP: SUB ARG2,256 ;IS THIS A FULL BLOCK? JB GLD3 ;....NO ; READ A RECORD INTO DS:DX MOV DX,OFFSET XFRBUF ;POINT AT THE TRANSFER BUFFER MOV CX,RECSIZ ;READ A 512 BYTE RECORD (256 WORDS) MOV BX,DATHNDL ;GET THE FILE HANDLE MOV AH,CRDRNDZ ;SELECT READ RANDOM RECORD INT 21H ;INVOKE FUNCTION CALL CMP AX,CX ;DID WE GET A FULL RECORD? JE GLDLPA ;....YES JMP LDERR3 ;....NO GLDLPA: MOV CX,RECSIZ ;GET RECORD SIZE (IN BYTES) MOV BX,OFFSET XFRBUF ;^ TRANSFER BUFFER (SOURCE) MOV AX,ARG1 ;SETUP VMP (DESTINATION) MOV VMP,AX MOV VMP0,0 GLDLP1: MOV AL,BYTE PTR [BX] ;GET THE BYTE CALL PB@VMPI ;PUT IT IN VM AND AUTOINC INC BX ;NEXT BYTE LOOP GLDLP1 ADD ARG1,256 ;UPDATE DESTINATION POINTER JMP GLDLP ;KEEP GOING UNTIL WE'VE DONE ALL THE ; FULL RECORDS GLD3: ADD ARG2,256 ;SEE IF THERE'S A PARTIAL RECORD JZ GLD5 ;....NO MOV DX,OFFSET XFRBUF ;POINT AT THE TRANSFER BUFFER MOV CX,ARG2 ;GET THE SIZE SHL CX,1 ;BYTIFY IT MOV BX,DATHNDL ;GET THE FILE HANDLE MOV AH,CRDRNDZ ;SELECT READ RANDOM RECORD INT 21H ;INVOKE FUNCTION CALL CMP AX,CX ;DID WE GET ALL OF THE PARTIAL RECORD? JE GLD4 ;....YES JMP LDERR3 ;....NO GLD4: MOV CX,ARG2 ;GET RECORD SIZE (IN WORDS) SHL CX,1 ;BYTIFY MOV BX,OFFSET XFRBUF ;^ TRANSFER BUFFER (SOURCE) MOV AX,ARG1 ;SETUP VMP (DESTINATION) MOV VMP,AX MOV VMP0,0 GLDLP2: MOV AL,BYTE PTR [BX] ;GET THE BYTE CALL PB@VMPI ;PUT IT IN VM AND AUTOINC INC BX ;NEXT BYTE LOOP GLDLP2 ;CLOSE THE FILE GLD5: CMP DSKDIR,-1 ;IF NOT -1, THEN SAVE OR RESTORE JZ GLD5A CLC ; CLEAR CARRY TO TELL S/R RET GLD5A: MOV BX,DATHNDL ;GET THE FILE HANDLE MOV AH,CFCLOSZ ;CLOSE IT INT 21H ; REOPEN THE GAME FILE MOV AH,CFOPENZ MOV AL,0 ;OPEN FOR READ MOV DX,OFFSET GAMFILE INT 21H JNC GLD6 JMP DSKERR GLD6: MOV GAMHNDL,AX ;SAVE THE FILE HANDLE POP DI ;RESTORE GSTACK POINTER JMP RET1 ;RETURN A "1" LDERR0: LDERR1: LDERR2: LDERR3: CMP DSKDIR,-1 ; IF NOT, THEN SAVE/RESTORE JZ GLD6A STC ; WARN SAVE RESTORE RET ;CLOSE THE DATA FILE GLD6A: MOV BX,DATHNDL ;GET THE FILE HANDLE MOV AH,CFCLOSZ ;CLOSE IT INT 21H ; REOPEN THE GAME FILE MOV AH,CFOPENZ MOV AL,0 ;OPEN FOR READ MOV DX,OFFSET GAMFILE INT 21H JNC GLD7 JMP DSKERR ;UNRECOVERABLE ERROR GLD7: MOV GAMHNDL,AX ;SAVE THE FILE HANDLE POP DI ;RESTORE GSTACK POINTER JMP RET0 GLOAD ENDP ; ---- ; LDIO ; ---- ; ; PURPOSE: THIS ROUTINE LOADS THE SPECIFIED AMOUNT OF DATA INTO A ; SPECIFIED BLOCK OF PRELOAD. IT IS THE I/O PORT OPERATION ; SUBCASE OF {GLOAD}. ; ; INPUTS: ARG1=TABLE VIRTUAL ADDRESS ; ARG2=LENGTH (IN WORDS) ; ; OUTPUTS: DATA IS TRANSFERED TO PRELOAD, IF THE OPERATION IS ; ILLEGAL OR FAILS FOR ANY REASON, A "0" IS RETURNED ; VIA {RET0}. ELSE A "1" IS RETURNED VIA {RET1}. ; ; REGISTERS DESTROYED: ALL ; PUBLIC LDIO LDIO PROC ; SUB AX,AX ;SIGNAL A READ OPERATION ; CALL INITIO ;INITIALIZE I/O DEVICE ; ; MOV DX,OFFSET XFRBUF ;POINT AT THE TRANSFER BUFFER ; CMP ARG2,0 ;DO WE HAVE A LEGAL LENGTH? ; JE IOERR1 ;....NO ; ;LDIOLP: SUB ARG2,256 ;IS THIS A FULL RECORD? ; JB LDIO1 ;....NO ; MOV CX,RECSIZ ;READ 512 BYTES (256 WORDS) ; CALL IOREAD ; AND PUT THEM IN THE XFER BUFFER ; CMP AX,CX ;DID WE GET THE EXPECTED AMOUNT? ; JNE IOERR1 ;....NO ; MOV CX,256 ;GET RECORD SIZE (IN WORDS) ; CALL XFR2VM ;BLOCK MOVE THE XFER BUFFER TO VIRTUAL ; JMP LDIOLP ;CONTINUE UNTIL WE'VE DONE ALL THE ; ; FULL BLOCKS ; ;LDIO1: ADD ARG2,256 ;COMPUTE PARTIAL RECORD SIZE ; JZ LDIO2 ;NO PARTIAL RECORD ; MOV CX,ARG2 ;GET SIZE OF PARTIAL RECORD ; CALL IOREAD ; GET IT IN THE XFER BUFFER ; CMP AX,CX ;DID WE GET THE EXPECTED AMOUNT? ; JNE IOERR1 ;....NO ; MOV CX,ARG2 ;GET RECORD SIZE (IN WORDS) ; CALL XFR2VM ;....YES, TRANSFER IT TO VM ;LDIO2: JMP RET1 ;SIGNAL SUCCESS IOERR1: JMP RET0 ;SIGNAL FAILURE LDIO ENDP ;@@ IOREAD PROC RET ;DUMMY IOREAD ENDP ;@@ INITIO PROC RET ;DUMMY INITIO ENDP ; ------ ; XFR2VM ; ------ ; ; PURPOSE: THIS ROUTINE TRANSFERS [CX] BYTES OF DATA FROM THE TRANFER ; BUFFER TO VIRTUAL MEMORY. ; ; INPUTS: ARG1=TABLE VIRTUAL ADDRESS ; [CX]=NUMBER OF BYTES TO TRANSFER ; ; OUTPUTS: DATA IS TRANSFERED TO PRELOAD ; ; REGISTERS DESTROYED: ALL ; PUBLIC XFR2VM XFR2VM PROC MOV BX,OFFSET XFRBUF ;^ TRANSFER BUFFER (SOURCE) MOV AX,ARG1 ;SETUP VMP (DESTINATION) MOV VMP,AX MOV VMP0,0 XFVMLP: MOV AL,BYTE PTR [BX] ;GET THE BYTE CALL PB@VMPI ;PUT IT IN VM AND AUTOINC INC BX ;NEXT BYTE LOOP XFVMLP RET XFR2VM ENDP PAGE ; ---- ; DUMP ; ---- ; ; PURPOSE: THIS ROUTINE TRANSFERS A SPECIFIED AMOUNT OF DATA FROM ; A SUPPLIED VIRTUAL MEMORY ADDRESS. IF ARG3 IS PROVIDED, ; THEN THIS IS A TRANSFER TO DISK. ELSE, IT IS AN I/O PORT ; OPERATION. ; ; INPUTS: ARG1=TABLE VIRTUAL ADDRESS ; ARG2=LENGTH ; ARG3=FILENAME POINTER (OPTIONAL) ; ; OUTPUTS: DATA IS TRANSFERED FROM VIRTUAL MEMORY, IF THE ; OPERATION IS ILLEGAL OR FAILS FOR ANY REASON, ; A "0" IS RETURNED VIA {RET0}. ELSE A "1" IS ; RETURNED VIA {RET1}. ; ; REGISTERS DESTROYED: ALL ; PUBLIC GDUMP GDUMP PROC ; DETERMINE TRANSFER TYPE CMP NARGS,3 ;IS THIS A FILE TRANSFER REQUEST? JE GDM0 ;....YES JMP DMIO ;....NO, IT'S AN I/O TRANSFER ; ; FILE TRANSFER REQUEST ; ; ASSUME THAT THE DEFAULT DISK IS TO BE USED ; CLOSE THE GAMEFILE GDM0: MOV AH,CFCLOSZ ;CLOSE THE GAME FILE FOR REINSERTION MOV BX,GAMHNDL ;IN CASE THIS IS A 1 DRIVE SYSTEM, INT 21H ; OR THE DEFAULT DRIVE IS SPECIFIED MOV AH,CDRESET ;AND FLUSH INT 21H ; PARSE THE FILENAME MOV AX,ARG3 ;SET UP VMP TO POINT TO THE FILENAME MOV VMP,AX MOV VMP0,0 CALL GW@VMPI ;GET THE LENGTH OF THE FILENAME MOV CX,AX ;IN CX FOR USE AS A LOOP COUNTER MOV BX,OFFSET DATFILE ;^ FCB ;GET THE FILENAME INTO THE FCB GDM1: CALL GB@VMPI ;GET A CHARACTER MOV [BX],AL ;PUT IT IN THE FCB INC BX ;NEXT CHARACTER LOOP GDM1 ; GET THE REST MOV BYTE PTR [BX],0 ;DROP IN A NUL AT THE END OF THE NAME PUSH DI ;SAVE SOME REGISTERS PUSH DS POP ES ;BOTH ES & DS POINT AT THE DATA SEGMENT ; OPEN THE DATA FILE MOV AH,CFCREAZ ;SELECT CREATE FILE SUB CX,CX ;SELECT READ/WRITE ATTRIBUTE MOV DX,OFFSET DATFILE ;POINT AT THE ASCIIZ STRING INT 21H ;MDOS CALL JNC GDM2 JMP DMERR1 GDM2: MOV DATHNDL,AX ;SAVE THE FILE HANDLE ; START THE ACTUAL TRANSFER OPERATION SAVENT: CMP ARG2,0 ;DO WE HAVE A LEGAL LENGTH? JNE GDMLP ;....YES JMP DMERR0 ;....NO ; PROCESS THE FULL RECORDS GDMLP: SUB ARG2,256 ;IS THIS A FULL BLOCK? JB GDM3 ;....NO ; TRANSFER A FULL RECORD FROM VIRTUAL MEMORY TO THE TRANSFER BUFFER MOV CX,RECSIZ ;GET RECORD SIZE (IN BYTES) MOV BX,OFFSET XFRBUF ;^ TRANSFER BUFFER (DESTINATION) MOV AX,ARG1 ;SETUP VMP (SOURCE) MOV VMP,AX MOV VMP0,0 GDMLP1: CALL GB@VMPI ;GET THE BYTE AND AUTOINC MOV BYTE PTR [BX],AL ;PUT IT IN XFRBUF INC BX ;NEXT DESTINATION LOOP GDMLP1 ADD ARG1,256 ;UPDATE SOURCE POINTER ; WRITE A FULL RECORD TO THE DISK MOV DX,OFFSET XFRBUF ;POINT AT THE TRANSFER BUFFER MOV CX,RECSIZ ;WRITE A 512 BYTE RECORD (256 WORDS) MOV BX,DATHNDL ;GET THE FILE HANDLE MOV AH,CWRRNDZ ;SELECT WRITE RANDOM RECORD INT 21H ;INVOKE FUNCTION CALL CMP AX,CX ;DID WE WRITE A FULL RECORD? JE GDMLP ;....YES, TRY ANOTHER JMP DMERR2 ;....NO ; PARTIAL RECORD GDM3: ADD ARG2,256 ;SEE IF THERE'S A PARTIAL RECORD JZ GDM4 ;....NO ; TRANSFER PARTIAL RECORD FROM VIRTUAL MEMORY TO THE TRANSFER BUFFER MOV CX,ARG2 ;GET THE SIZE (IN WORDS) SHL CX,1 ;BYTIFY MOV BX,OFFSET XFRBUF ;^ TRANSFER BUFFER (DESTINATION) MOV AX,ARG1 ;SETUP VMP (SOURCE) MOV VMP,AX MOV VMP0,0 GDMLP2: CALL GB@VMPI ;GET THE BYTE AND AUTOINC MOV BYTE PTR [BX],AL ;PUT IT IN XFRBUF INC BX ;NEXT DESTINATION LOOP GDMLP2 ; WRITE A PARTIAL RECORD TO THE DISK MOV CX,ARG2 ;GET THE SIZE (IN WORDS) SHL CX,1 ;BYTIFY MOV DX,OFFSET XFRBUF ;POINT AT THE TRANSFER BUFFER MOV BX,DATHNDL ;GET THE FILE HANDLE MOV AH,CWRRNDZ ;SELECT WRITE RANDOM RECORD INT 21H ;INVOKE FUNCTION CALL CMP AX,CX ;DID WE WRITE ENOUGH? JE GDM4 ;....YES JMP DMERR2 ;....NO ;CLOSE THE DATA FILE GDM4: CMP DSKDIR,-1 ; CHECK SAVE RESTORE FLAG JZ GDM4A CLC ; CLEAR TO SAVE/RESTORE RET ; GDM4A: MOV BX,DATHNDL ;GET THE FILE HANDLE MOV AH,CFCLOSZ ;CLOSE IT INT 21H ; REOPEN THE GAME FILE MOV AH,CFOPENZ MOV AL,0 ;OPEN FOR READ MOV DX,OFFSET GAMFILE INT 21H JNC GDM5 JMP DSKERR GDM5: MOV GAMHNDL,AX ;SAVE THE FILE HANDLE POP DI ;RESTORE GSTACK POINTER JMP RET1 ;RETURN A "1" DMERR0: DMERR1: DMERR2: CMP DSKDIR,-1 ; CHECK SAVE/RESTORE FLAG JZ GDM5A STC ; WARN SAVE RESTORE RET ;CLOSE THE DATA FILE GDM5A: MOV BX,DATHNDL ;GET THE FILE HANDLE MOV AH,CFCLOSZ ;CLOSE IT INT 21H ; REOPEN THE GAME FILE MOV AH,CFOPENZ MOV AL,0 ;OPEN FOR READ MOV DX,OFFSET GAMFILE INT 21H JNC GDM6 JMP DSKERR ;UNRECOVERABLE ERROR GDM6: MOV GAMHNDL,AX ;SAVE THE FILE HANDLE POP DI ;RESTORE GSTACK POINTER JMP RET0 GDUMP ENDP ; ---- ; DMIO ; ---- ; ; PURPOSE: THIS ROUTINE DUMPS A SPECIFIED AMOUNT OF DATA FROM ; PRELOAD TO THE I/O PORT. ; ; INPUTS: ARG1=TABLE VIRTUAL ADDRESS ; ARG2=LENGTH (IN WORDS) ; ; OUTPUTS: DATA IS TRANSFERED TO THE I/O PORT, IF THE OPERATION ; IS ILLEGAL OR FAILS FOR ANY REASON, A "0" IS RETURNED ; VIA {RET0}. ELSE A "1" IS RETURNED VIA {RET1}. ; ; REGISTERS DESTROYED: ALL ; PUBLIC DMIO DMIO PROC ; MOV AX,1 ;SIGNAL A WRITE OPERATION ; CALL INITIO ;INITIALIZE I/O DEVICE ; ; MOV DX,OFFSET XFRBUF ;POINT AT THE TRANSFER BUFFER ; CMP ARG2,0 ;DO WE HAVE A LEGAL LENGTH? ; JE IOERR2 ;....NO ; ;DMIOLP: SUB ARG2,256 ;IS THIS A FULL RECORD? ; JB DMIO1 ;....NO ; CALL VM2XFR ;BLOCK MOVE THE VM BLOCK TO XFER BUFFER ; ; MOV CX,RECSIZ ;WRITE 512 BYTES (256 WORDS) ; CALL IOWRITE ; AND PUT THEM IN THE XFER BUFFER ; CMP AX,CX ;DID WE WRITE THE EXPECTED AMOUNT? ; JNE IOERR2 ;....NO ; JMP DMIOLP ;CONTINUE UNTIL WE'VE DONE ALL THE ; ; FULL BLOCKS ; ;DMIO1: ADD ARG2,256 ; MOV CX,ARG2 ;SETUP SIZE OF PARTIAL RECORD ; CMP CX,0 ;DO WE EVEN HAVE A PARTIAL RECORD? ; JE DMIO2 ;....NO ; ; CALL VM2XFR ;BLOCK MOVE THE VM BLOCK TO XFER BUFFER ; CALL IOWRITE ; AND PUT THEM IN THE XFER BUFFER ; CMP AX,CX ;DID WE WRITE THE EXPECTED AMOUNT? ; JNE IOERR2 ;....NO ; ;DMIO2: JMP RET1 ;SIGNAL SUCCESS ; IOERR2: JMP RET0 ;SIGNAL FAILURE DMIO ENDP ;@@ IOWRITE PROC RET ;DUMMY IOWRITE ENDP ; ------ ; VM2XFR ; ------ ; ; PURPOSE: THIS ROUTINE TRANSFERS [CX] BYTES OF DATA FROM VIRTUAL ; MEMORY TO THE TRANSFER BUFFER. ; ; INPUTS: ARG1=TABLE VIRTUAL ADDRESS ; [CX]=NUMBER OF BYTES TO TRANSFER ; ; OUTPUTS: DATA IS TRANSFERED TO XFRBUF ; ; REGISTERS DESTROYED: ALL ; PUBLIC VM2XFR VM2XFR PROC MOV BX,OFFSET XFRBUF ;^ TRANSFER BUFFER (DESTINATION) MOV AX,ARG1 ;SETUP VMP (SOURCE) MOV VMP,AX MOV VMP0,0 VMXFLP: CALL GB@VMPI ;GET THE BYTE AND AUTOINC MOV BYTE PTR [BX],AL ;PUT IT IN XFRBUF INC BX ;NEXT DESTINATION LOOP VMXFLP RET VM2XFR ENDP PAGE ;---------------------- ; ITERATIONS ;---------------------- ; ; EXECUTE THE ITERATION TABLE AT [ARG2] ; {FOR I=ITPNT TO ITICN ; ; DRAW THE ACTIVE ICON [ITERATION] ; ; UPDATE THE ICON'S ITERATION COUNTER ; ; TEST FOR INPUT ; IF INPUT THEN SAVE THE NEXT ACTIVE ICON ENTRY NUMBER ; AND PROCESS THE INPUT} ; ; NEXT I} ; ; IF DONE ; REINITIALIZE ITPNT <= 1 ; BRANCH TO NOIT}. PUBLIC ITRATE ITRATE PROC MOV AX,ARG2 ;POINT AT THE ITERATION TABLE MOV VMP,AX MOV VMP0,0 CALL GB@VMPI ;GET THE NUMBER OF ENTRIES MOV ITICN,AL MOV AL,VMP0 ;SAVE POINTER TO ITPNT MOV VMSAV0,AL MOV AX,VMP MOV VMSAV,AX CALL GB@VMPI ;GET THE ENTRY TO START WITH MOV ITPNT,AL ; WE'RE POINTING AT THE FIRST WORD ; IN THE FIRST ENTRY MOV AL,1 ;SKIP ENTRIES 1 TO ITPNT ITLP2: CMP AL,ITPNT JZ ITLOOP ADD VMP,6 ;SIX WORDS PER ENTRY INC AL JMP ITLP2 ; PROCESS THE DATA FOR THE CURRENT ACTIVE ICON ITLOOP: MOV AX,VMP ;SAVE POINTER TO THE CURRENT PUSH AX ; ACTIVE ICON'S DATA BASE MOV AL,VMP0 PUSH AX CALL GW@VMPI ;GET ICON BASE ADDRESS MOV IADR1,AX MOV IADR10,0 CALL GW@VMPI ;GET ICON HORIZONTAL POSITION MOV ARG1,AX CALL GW@VMPI ;GET ICON VERTICAL POSITION MOV ARG2,AX CALL GW@VMPI MOV NEGATE,AH ;NEGATE MOV BYTE PTR I,AL ;CURRENT ITERATION CALL GW@VMPI MOV BSET,AH ;BLOCKSET ID MOV ITERS,AL ;# OF ITERATIONS CALL GW@VMPI MOV IX1,AH ;WIDTH MOV IY1,AL ;HEIGHT ; UPDATE THE CURRENT ITERATION IN THE DATA STRUCTURE MOV BL,BYTE PTR I CMP BL,ITERS ;CURRENT ITERATION=NO. OF ITERATIONS? JNE ITSKP2 ;....NO MOV BL,0 ;....YES, SET IT TO 0 SO IT'LL BE 1 ITSKP2: INC BL ;NEXT POSSIBLE ITERATION POP AX ;RESTORE POINTER TO THE MOV VMP0,AL ; CURRENT ACTIVE ICON'S DATA BASE POP AX MOV VMP,AX MOV AX,7 ;POINT TO THE CURRENT ITERATION CALL ADDVMP MOV AX,BX ;SAVE UPDATED VALUE CALL PB@VMPI ; UPDATE POINTER TO THE NEXT ACTIVE ICON ADD VMP,2 ;IT'S 4 MORE BYTES TO THE NEXT ACTIVE ICON MOV AX,VMP ;SAVE POINTER TO THE PUSH AX ; NEXT ACTIVE ICON'S DATA BASE MOV AL,VMP0 PUSH AX ; POINT TO THE CURRENT ICON'S BLOCK I.D. DATA MOV AL,IX1 ;GET LENGTH OF ONE ICON MUL IY1 DEC BYTE PTR I ;ZERO ALIGN MOV BL,BYTE PTR I SUB BH,BH MUL BX ;PRODUCT S/B IN AX ADD IADR1,2 ;POINT PAST HEADER (2 WORDS) CALL AIADR1 ;ADD IN OFFSET ; DRAW THE ITERATION MOV MSKFLG,0 ;DISABLE MASKING CALL ITENT ;DRAW THE ICON ; ; HAVE WE PROCESSED ALL THE ACTIVE ICONS? ; MOV AL,ITPNT ;ARE WE DONE? CMP AL,ITICN JE ITEXI ;....YES INC ITPNT ;....NO. POINT TO THE NEXT ICON ; INPUT PROCESSING TEST BYTE PTR VALUE,80H ;IF INPUT WAS CALLED WITH A NEGATIVE JNZ NONSNS ;INT THEN DON'T LOOK FOR INPUT CALL SENSE ;ELSE, POLL THE KEYBOARD CMP AL,8FH ;DO WE HAVE AN INPUT? JNE ITSKP5 ;....YES NONSNS: POP AX ;RESTORE POINTER TO THE MOV VMP0,AL ; NEXT ACTIVE ICON'S DATA BASE POP AX MOV VMP,AX JMP ITLOOP ; WE RECEIVED AN INPUT WHILE ITERATING ; SO, WE SAVE WHERE WE ARE AND PROCESS THE INPUT ITSKP5: PUSH AX ;SAVE INPUT MOV AL,VMSAV0 ;RESTORE POINTER TO ACTIVE TABLE HEADER MOV VMP0,AL ; ITPNT ENTRY MOV AX,VMSAV MOV VMP,AX MOV AL,ITPNT ;SAVE WHERE WE LEFT OFF CALL PB@VMP POP AX ;RESTORE SENSE POP BX ;FLUSH VMP POINTER TO CURRENT ACTIVE ICON POP BX JMP PUTBYT ; WE ITERATED ALL THE ICONS IN THE ACTIVE TABLE ; SO, WE SET UP TO DO IT AGAIN LATER AND EXIT ITEXI: POP BX ;FLUSH VMP0 POP BX ;FLUSH VMP MOV AL,VMSAV0 ;RESTORE POINTER TO ACTIVE TABLE HEADER MOV VMP0,AL ; ITPNT ENTRY MOV AX,VMSAV MOV VMP,AX MOV AL,1 ;SET ITPNT ENTRY TO 1 CALL PB@VMP JMP NOIT ; AND RESUME INPUT PROCESSING ITRATE ENDP PAGE ;-------------------------------------------------- ; OPCODE EXECUTION PROCEDURES SUPPORT CODE ;-------------------------------------------------- ; ------ ; TSTPRE ; ------ ; ; PURPOSE: THIS ROUTINE TESTS THE RANGE OF A SPECIFIED VIRTUAL MEMORY ; BLOCK TO DETERMINE IF IT IS ALL CONTAINED IN EITHER ; G-PRELOAD OR I-PRELOAD. ; ; INPUTS: [ARG1] VIRTUAL MEMORY STARTING ADDRESS (WORD POINTER) ; [ARG2] LENGTH IN WORDS ; ; OUTPUTS: CARRY CLEAR IF IN PRELOAD ; CARRY SET IF NOT IN PRELOAD ; ; REGISTERS DESTROYED: AX ; PUBLIC TSTPRE TSTPRE PROC ; TEST STARTING ADDRESS MOV AX,ARG1 ;GET STARTING ADDRESS CMP AH,GPEND ;DOES THE ADDRESS RESIDE BELOW THE ; FIRST BLOCK OF G-PURE? JB TPR0 ;....YES, IT'S G-PRELOAD CMP AX,ICODE ;IS IT GE. I-PRELOAD? JB TPRERR ;....NO, IT'S NOT PRELOAD. CMP AX,IPURE ;IS IT < IPURE JB TPR1 ;....YES, IT'S I-PRELOAD JMP TPRERR ;FAIL, IT'S NOT PRELOAD ; TEST LAST ADDRESS TO SEE IF IT'S IN G-PRELOAD TOO TPR0: ADD AX,ARG2 ;ADD LENGTH CMP AH,GPEND ;DOES THE ADDRESS RESIDE BELOW THE ; FIRST BLOCK OF G-PURE? JB TPREOK ;....YES, IT'S GOOD JMP TPRERR ;FAIL IF NOT IN G-PRELOAD ; TEST LAST ADDRESS TO SEE IF IT'S IN I-PRELOAD TOO TPR1: ADD AX,ARG2 ;ADD LENGTH CMP AX,IPURE ;IS IT < IPURE JB TPREOK ;....YES, IT'S I-PRELOAD TPRERR: STC ;SET CARRY FOR FAILURE RET TPREOK: CLC ;CLEAR CARRY FOR SUCCESS RET TSTPRE ENDP PAGE ; -------------------------------- ; POP GSTACK INTO [VALUE] AND [AX] ; -------------------------------- ; PUBLIC POPVAL POPVAL PROC CALL POPAXG ;POP A VALUE MOV VALUE,AX ;SAVE IT IN VALUE RET POPVAL ENDP ; -------------------------- ; UPDATE VALUE OF A VARIABLE ; -------------------------- ; ENTRY: VALUE TO STORE IN [VALUE] ; VARIABLE ID IN AX PUBLIC VARPUT,PTV1$,PTV2$ VARPUT PROC CMP AX,0 ;LOCAL OR GLOBAL JNE PTV1$ ;....YES. MOV AX,VALUE ;....NO. STACK. GET VALUE JMP PSHAXG ; AND PUSH IT ON THE GSTACK ;SAVE [VALUE] TO LOCAL OR GLOBAL PTV1$: PUSH BX ;SAVE AFFECTED REGISTER(S) MOV BX,GLOCS ;ASSUME IT'S A LOCAL CMP AX,16 ;WAS IT A LOCAL? JNB PTV2$ ;....NO DEC AX ;....YES, ZERO ALIGN JMP PTV3$ ; IT WAS A GLOBAL PTV2$: MOV BX,GLOBAL ;GET GLOBAL TABLE BASE ADDRESS SUB AX,16 ;ZERO ALIGN PTV3$: MOV VMP,BX ;PUT ADDRESS INTO VIRTUAL MEMORY POINTER MOV VMP0,0 ;IT MUST BE ON AN EVEN BOUNDRY ADD VMP,AX MOV AX,VALUE ;GET THE VALUE POP BX ;RESTORE AFFECTED REGISTER(S) JMP PW@VMP ; AND WRITE IT TO VIRTUAL VARPUT ENDP ; ---------------------------------- ; GET A VARIABLE FROM THE MAINLOOP ; ---------------------------------- ; IMPORTS: VARIABLE ID IN G-CODE ; EXPORTS: VALUE RETURNED IN AX AND [VALUE] PUBLIC GETVAR GETVAR PROC CALL GB@GPCI ;GET VARIABLE ID JMP VARGET GETVAR ENDP ; -------------------------------------- ; GET A VARIABLE FROM WITHIN AN OPCODE ; -------------------------------------- ; IMPORTS: VARIABLE ID IN [AL] ; EXPORTS: VALUE RETURNED IN AX AND [VALUE] PUBLIC VARGET,GTV1$,GTV2$ VARGET PROC CMP AL,0 ;IF ID NE. 0 JNE GTV1$ ; THEN ACCESS A VARIABLE JMP POPVAL ;ELSE, POP GSTACK INTO [AX] AND [VALUE] ; IS THE VARIABLE LOCAL OR GLOBAL GTV1$: PUSH BX ;SAVE AFFECTED REGISTER(S) MOV BX,GLOCS ;ASSUME IT'S A LOCAL CMP AX,16 ;WAS IT? JNB GTV2$ ;....NO DEC AX ;....YES, ZERO ALIGN JMP GTV3$ ; HANDLE A GLOBAL VARIABLE GTV2$: MOV BX,GLOBAL ;GET GLOBAL TABLE BASE ADDRESS SUB AX,16 ;POINT TO TABLE ELEMENT GTV3$: MOV VMP,BX ;PUT ADDRESS INTO VIRTUAL MEMORY POINTER MOV VMP0,0 ;IT MUST BE ON AN EVEN BOUNDRY ADD VMP,AX CALL GW@VMP ;GET THE VARIABLE MOV VALUE,AX ;PUT IT IN [VALUE] POP BX ;RESTORE AFFECTED RESISTER(S) RET VARGET ENDP ; --------------- ; PREDICATE FAILS ; --------------- ;PREDICATE HANDLERS TRUE & FALSE ;DESTROYS REGISTERS, BUT ARE ONLY CALLED FROM END OF TOP-LEVEL FCNS PUBLIC PREDF,PREDF1 PREDF PROC CALL GB@GPCI ;GET FIRST BRANCH BYTE OR AL,AL JNS PREDF1 ;BRANCH IF BIT 7 CLEAR JMP PREDNB ; ELSE IGNORE PREDICATE BRANCH PREDF1: JMP PREDB PREDF ENDP ; ----------------------- ; IGNORE PREDICATE BRANCH ; ----------------------- PUBLIC PREDNB,PREDN0 PREDNB PROC AND AL,01000000B ;TEST BIT 6 JNZ PREDN0 ;SHORT BRANCH IF SET JMP GB@GPCI ;ELSE SKIP OVER 2ND BRANCH BYTE PREDN0: RET PREDNB ENDP ; ------------------ ; PREDICATE SUCCEEDS ; ------------------ PUBLIC PREDS,PREDS0 PREDS PROC CALL GB@GPCI ;GET FIRST BRANCH BYTE OR AL,AL JNS PREDS0 ;DON'T BRANCH IF BIT 7 IS CLEAR JMP PREDB ; ELSE, DO A PREDICATE BRANCH PREDS0: JMP PREDNB PREDS ENDP ; -------------------------- ; PERFORM A PREDICATE BRANCH ; -------------------------- ; ENTRY: 1ST PRED BYTE IN [AL] PUBLIC PREDB,PREDLB,DOB2,PREDB1,PREDB2,PRDB2A PREDB PROC SUB AH,AH ;MSB NOT USED TEST AL,01000000B ;LONG OR SHORT BRANCH? JZ PREDLB ;LONG IF BIT 6 IS CLEAR ; HANDLE A SHORT BRANCH AND AL,00111111B ;FORM SHORT OFFSET MOV WORD PTR VALUE,AX ;MSB OF OFFSET IS ZERO JMP PREDB1 ;DO THE BRANCH ; HANDLE A LONG BRANCH PREDLB: AND AL,00111111B ;FORM MSB OF OFFSET TEST AL,00100000B ;CHECK SIGN OF 14-BIT VALUE JZ DOB2 ;POSITIVE? OR AL,11100000B ;....NO. EXTEND SIGN BIT DOB2: MOV BL,AL ;PUT MSB OF OFFSET IN BL CALL GB@GPCI ;GET LSB OF 14-BIT OFFSET MOV AH,BL ; RECOVER THE MSB MOV VALUE,AX ;SAVE THE BRANCH OFFSET IN VALUE ; TEST BRANCH OFFSET PREDB1: OR AH,AH ;CHECK MSB OF OFFSET JNZ PREDB3 ;DO BRANCH IF NZ OR AL,AL ;IF LSB IS NZ JNZ PREDB2 ;MAKE SURE IT ISN'T A "1" JMP GRFALS ; ELSE, DO AN "RFALSE" PREDB2: CMP AL,1 ;IS OFFSET= 1 JNZ PRDB2A ;....NO. DO THE BRANCH JMP GRTRUE ; ELSE, DO AN "RTRUE" PRDB2A: JMP PREDB3 ;EXECUTE THE "JUMP" PREDB ENDP ;ENTRYPOINT FOR "JUMP" PUBLIC PREDB3 PREDB3 PROC MOV AX,WORD PTR VALUE ;GET THE OFFSET SUB AX,2 ;CALC [VALUE] - 2 ;CONVERT A 16-BIT OFFSET TO 17 BITS SAR AX,1 ;PROCESS THE LS BIT, AND SIGN EXTEND JNC PRDB3A ;SKIP IF "0" ; ELSE WE HAVE TO INCREMENT GPC XOR GPC0,1 ;CHANGE THE STATE OF BIT0 JNZ PRDB3A ;CONTINUE IF CHANGING FROM "0" TO A "1" INC GPC ;ELSE BUMP THE REST OF GPC PRDB3A: ADD GPC,AX ;ADD IN THE REST OF THE OFFSET RET PREDB3 ENDP ; ------ ; AIADR1 ; ------ ; ; ENTRY: IADR1 ; [AX] = VALUE TO BE ADDED TO IADR1 ; ; EXIT: IADR1 <= IADR1 + [AX] ; AIADR1 PROC SHR AX,1 ;THINK ABOUT IT JNC AIAD1 XOR IADR10,1 ;CHANGE THE STATE OF BIT0 JNZ AIAD1 ; CONTINUE IF CHANGING FROM A "0" TO A "1" INC IADR1 ;ELSE BUMP THE REST OF IADR1 AIAD1: ADD IADR1,AX RET AIADR1 ENDP ; ------ ; AIADR2 ; ------ ; ; ENTRY: IADR2 ; [AX] = VALUE TO BE ADDED TO IADR2 ; ; EXIT: IADR2 <= IADR2 + [AX] ; AIADR2 PROC SHR AX,1 ;THINK ABOUT IT JNC AIAD2 XOR IADR20,1 ;CHANGE THE STATE OF BIT0 JNZ AIAD2 ; CONTINUE IF CHANGING FROM A "0" TO A "1" INC IADR2 ;ELSE BUMP THE REST OF IADR2 AIAD2: ADD IADR2,AX RET AIADR2 ENDP ; ------------ ; RETURN A "0" ; ------------ PUBLIC RET0 RET0 PROC MOV AL,0 JMP PUTBYT RET0 ENDP ; ------------ ; RETURN A "1" ; ------------ PUBLIC RET1 RET1 PROC MOV AL,1 ; AND FALL THROUGH ............. RET1 ENDP ; ----------------------- ; RETURN A BYTE VIA [GPC] ; ----------------------- ; ;RETURNS THE BYTE IN AL TO THE LOCATION SPECIFIED BY THE GPC ;DESTROYS BX, BUT IS USUALLY CALLED AT END OF TOP-LEVEL FUNCTION PUBLIC PUTBYT PUTBYT PROC SUB AH,AH ;THIS ENTRY FOR BYTE VALUE TO CLEAR HIGH BYTE MOV VALUE,AX JMP PUTVAL PUTBYT ENDP ; --------------------------------------------------- ; RETURN [VALUE] TO THE LOCATION SPECIFIED BY THE GPC ; --------------------------------------------------- ; ENTRY: [VALUE] <= THE VALUE TO RETURN ; [GPC] <= VARIABLE ID ; ; EXIT: [VALUE] IS STORED ; GPC <= GPC + 1 PUBLIC PUTVAL,PUTVL0,PUTVL1 PUTVAL PROC CALL GB@GPCI ;GET VAR TO USE CMP AX,0 ;STACK? JNE PUTVL0 ;NO, GO STORE VALUE ; VARIABLE ID=0 ; PUSH [VALUE] ONTO THE GSTACK MOV AX,VALUE ;GET [VALUE] JMP PSHAXG ;PUSH IT ONTO GSTACK ; VARIABLE ID <> 0 ; SAVE [VALUE] TO LOCAL OR GLOBAL PUTVL0: PUSH BX ;SAVE AFFECTED REGISTER(S) MOV BX,GLOCS ;ASSUME IT'S A LOCAL CMP AX,16 ;WAS IT? JNB PUTVL1 ;....NO DEC AX ;....YES, ZERO ALIGN JMP PUTVL2 ; HANDLE A GLOBAL VARIABLE PUTVL1: MOV BX,GLOBAL ;GET GLOBAL TABLE BASE ADDRESS SUB AX,16 ;POINT TO TABLE ELEMENT PUTVL2: MOV VMP,BX ;PUT ADDRESS INTO VIRTUAL MEMORY POINTER MOV VMP0,0 ;IT MUST BE ON AN EVEN BOUNDRY ADD VMP,AX MOV AX,VALUE ;GET THE VALUE POP BX ;RESTORE AFFECTED REGISTER(S) JMP PW@VMP ; AND WRITE IT TO VIRTUAL PUTVAL ENDP SUBTTL GRAPHIC OPCODE SUPPORT ROUTINES PAGE ; ------------------------------------------- ; GET GRAPHICS BLOCK [AL] IN CURRENT BLOCKSET ; ------------------------------------------- ; ENTRY: BLOCK ID # (1-255) IN [AL] ; EXIT: BLOCK DATA IN [BLOCK] PUBLIC GTGBLK,GBLL GTGBLK PROC PUSH BX PUSH CX ;CALCULATE OFFSET FROM THE START OF BLOCKSET MOV BX,BSADR ;GET BASE ADDRESS OF BLOCKSET MOV VMP,BX ;SETUP VMP MOV VMP0,0 XOR AH,AH ; (B5) CLEAN OUT TOP HALF MOV CL,3 SHL AX,CL ; (B5) MULTIPLY BY COMMON BLOCK LEN CALL ADDVMP ;SET UP VADDR OF FIRST BYTE IN BLOCK ;SETUP LOOP COUNTER AND INDEX MOV CL,4 SUB CH,CH MOV BX,OFFSET BLOCK GBLL: CALL GW@VMPI ;GET A WORD XOR AL,NEGATE ;INVERT BITS IF NEGATE=0FFH XOR AH,NEGATE ;INVERT BITS IF NEGATE=0FFH XCHG AH,AL ;SWAPIFY MOV [BX],AX ; STORE [BLOCK (BX)] INC BX ; AND [BLOCK (BX + 1)] INC BX ;UPDATE DESTINATION LOOP GBLL ;LOOP TILL ALL BYTES ARE DONE POP CX POP BX RET GTGBLK ENDP PUBLIC GTGFST GTGFST PROC CMP FSIZE,0 ; (B5) WHOLE GAME IN? JNE GTF0 JMP GTGBLK ; (B5) TAKE THE LONG WAY HOME GTF0: PUSH BX PUSH CX PUSH DI PUSH SI ;CALCULATE OFFSET FROM THE START OF BLOCKSET MOV BX,0 ; (B5) FLAG TO INDICATE RESTORATION OF ES JMP GTF1 ; FIX AX TO BE BLOCK ID AND USE SLOW GET BLOCK BECAUSE OF SEG ; BOUNDARY CROSSING GTFIX: PUSH ES MOV CX,ES ADD CX,1000H MOV ES,CX ; (B5) SET UP FOR NEXT SEG MOV BX,1 ; (B5) SET FLAG FOR SEG RESTORE JMP GTF4 ; (B5) GET BLOCK FROM NEXT SEG GTF1: MOV CL,3 ; (B5) SHIFT COUNT XOR AH,AH ; (B5) MULTIPLY BY BLKLEN SHL AX,CL ; (B5) DO IT GTF2: ADD DI,AX ; (B5) ADD IN OFFSET JC GTFIX ADD DI,8 ; (B5) CHECK IF WE'LL CROSS SEG JNC GTF3 MOV BX,2 ; (B5) USE BX AS A FLAG FOR THIS COND GTF3: SUB DI,8 ; (B6) FIX DI GTF4: XCHG SI,DI ; (B5) GET POINTER MOV DI,OFFSET BLOCK ; (B5) GET BLOCK ADDRESS ;SETUP LOOP COUNTER AND INDEX MOV CL,4 MOV CH,NEGATE PUSH DS ; (B5) SAVE SEGS AND SWAP 'EM PUSH ES PUSH DS PUSH ES POP DS POP ES CMP BX,2 ; (B5) CHECK FOR WRAP INDICATOR JNE GFLL GFSL: LODSW ; (B5) GET A WORD CMP SI,0 ; (B5) CHECK FOR WRAP JNE GFSL1 ; (B5) NOT YET MOV BX,DS ; (B5) IT DID, FIX SEG ADD BX,1000H ; (B5) TO ADDRESS NEXT SEG MOV DS,BX ; (B5) FINISH FIX UP MOV BX,0 ; (B5) RESET BX GFSL1: XOR AL,CH ; (B5) CONTINUE PROCESS XOR AH,CH STOSW ; (B5) FILL IN REST OF BLOCK DEC CL ; (B5) AND DECREMENT LOOP CNTR JNZ GFSL ; (B5) CONTINUE IF BYTES LEFT JMP GFRES ; (B5) OR EXIT GFLL: LODSW ; (B5) GET A WORD XOR AL,CH ;INVERT BITS IF NEGATE=0FFH XOR AH,CH ;INVERT BITS IF NEGATE=0FFH STOSW ; (B5) STORE [BLOCK (BX)] DEC CL JNZ GFLL GFRES: POP ES POP DS CMP BX,1 ; (B5) LOOK FOR RESTORE BIT JNE GTF5 POP ES ; (B5) SEG CROSS, RESTORE ES GTF5: POP SI POP DI POP CX POP BX RET GTGFST ENDP ; ------------------------ ; SETUP FOR SETI AND SWAPI ; ------------------------ PUBLIC ISETUP ISETUP PROC MOV AX,WORD PTR ARG4 ;GET WRD PTR ADDR OF DESTINATION ICON CMP AX,IPURE ;IS DEST IN I-PRELOAD? JB ISU ;....YES. SO CONTINUE JMP PRERR2 ;ELSE PURITY VIOLATION! ; AND FALL THROUGH ............. ISETUP ENDP ; ---------------------------- ; SETUP ENTRYPOINT FOR MASKING ; ---------------------------- PUBLIC ISU ISU PROC CALL GETI ;FETCH STATS OF DEST ICON MOV AL,BYTE PTR IX1 ;COPY DEST ICON STATS MOV BYTE PTR IX2,AL ;TO AUXILIARY STORAGE MOV AL,BYTE PTR IY1 MOV BYTE PTR IY2,AL MOV AX,WORD PTR IADR1 MOV WORD PTR IADR2,AX MOV AL,IADR10 MOV IADR20,AL MOV AX,ARG3 ;NOW GET THE SOURCE ICON STATS ; AND FALL THROUGH ............. ISU ENDP ; -------- ; GET ICON ; -------- ; ENTRY: V-ADDR OF ICON DATA DEFINITION IN [AX] ; ; EXIT: ICON DATA V-ADDR IN [IADR1] ; X-SIZE IN [IX1] AND [XDEX1] ; Y-SIZE IN [IY1] AND [YDEX1] ; ASSIGNED BLOCKSET IN [BSET] PUBLIC GETI,NEWXY GETI PROC PUSH BX ;SAVE AFFECTED REGISTER(S) MOV VMP,AX ;LOAD VIRTUAL MEMORY POINTER MOV VMP0,0 CALL GW@VMPI ;THE FIRST WORD HAS MOV BYTE PTR BSET,AH ; THE ASSIGNED BLOCKSET MOV BYTE PTR ITERS,AL ; AND THE # OF ITERATIONS CALL GW@VMPI ;THE SECOND WORD HAS MOV BYTE PTR IX1,AH ; X-SIZE MOV BYTE PTR IY1,AL ; AND Y-SIZE MOV AX,VMP ;GET BASE V-ADDR MOV WORD PTR IADR1,AX ;IN [IADR1] MOV AL,VMP0 MOV IADR10,AL ; SETUP [XDEX1] & [YDEX1] NEWXY: MOV AL,BYTE PTR IX1 MOV BYTE PTR XDEX1,AL MOV AL,BYTE PTR IY1 MOV BYTE PTR YDEX1,AL POP BX ;RESTORE AFFECTED REGISTER(S) RET GETI ENDP ; ----------------------------- ; WILL SOURCE ICON FIT IN DEST? ; ----------------------------- ; ENTRY: IX1 = X-SIZE OF SOURCE ; IY1 = Y-SIZE OF SOURCE ; IX2 = X-SIZE OF DESTINATION ; IY2 = Y-SIZE OF DESTINATION ; ARG1 = HORIZONTAL OFFSET OF THE SUB-IMAGE IN THE ; DESTINATION ICON THAT IS TO BE SWAPPED ; ARG2 = VERTICAL OFFSET OF THE SUB-IMAGE IN THE ; DESTINATION ICON THAT IS TO BE SWAPPED ; ; EXIT: CARRY CLEAR IF FIT OK, ELSE CARRY SET ; [IADR2] HAS ABSOLUTE ADDRESS OF SUB-ICON PUBLIC DOFIT,NOFIT,FIT0,FIT1,FIT2,FIT3,FITEX DOFIT PROC MOV AL,BYTE PTR ARG1 ;GET X-POSITION ADD AL,IX1 ;ADD TO THE X-SIZE OF SOURCE CMP AL,IX2 ;COMPARE TO THE X-SIZE OF DESTINATION JBE FIT0 ;OK IF LESS OR EQUAL NOFIT: STC ; ELSE, SET CARRY JMP FITEX ; AND EXIT FIT0: MOV AL,BYTE PTR ARG2 ;GET Y-POSITION MOV BL,AL ;SAVE IT FOR LATER ADD AL,IY1 ;ADD THE Y-SIZE OF SOURCE CMP AL,IY2 ;COMPARE TO Y-SIZE OF DESTINATION JBE FIT1 ;OK IF LESS OR EQUAL JMP NOFIT ; ELSE, SET CARRY AND EXIT ; MAKE [IADR2] POINT TO ADDR OF SUB ICON FIT1: MOV AL,BYTE PTR ARG1 ;GET X-POSITION OR AL,AL ;IF X-POS = 0 JZ FIT3 ; DON'T ADD X-OFFSET ; MOV AL,BYTE PTR ARG1 ;ADD THE X-COORDINATE OF SUB-ICON SUB AH,AH ;TO BASE ADDRESS OF DEST ICON CALL AIADR2 ;TO GET THE BASE ADDR OF SUB-ICON JMP FIT3 ;ALWAYS SKIP THE 1ST Y-ITERATION FIT2: MOV AL,BYTE PTR IX2 ;ADD THE X-SIZE OF DEST ICON SUB AH,AH ;TO BASE ADDRESS OF SUB-ICON CALL AIADR2 ;ONCE FOR EACH Y-COORDINATE FIT3: DEC BL ;OUT OF Y-COORDINATES YET? JNS FIT2 ; NO. KEEP ADDING CLC ;CLEAR CARRY FOR SUCCESS FITEX: RET DOFIT ENDP SUBTTL GPC VIRTUAL MEMORY ACCESS PAGE ; --------------------------------------------------------------- ; The following subroutines are used to access Virtual Memory ; through the GPC. ; ; G-MACHINE PROGRAM COUNTER DEFINITION ; GPC GPC1 = MSB OF GPC GPC2 = LSB OF GPC ; GPC0 = BIT 0 OF BYTE IS BIT 0 OF GPC ; ---------------------------------------------------------------- ; ------ ; ADDGPC ; ------ ; ; ENTRY: [AX] = VALUE TO BE ADDED TO GPC ; EXIT: GPC <= GPC + [AX] ; ADDGPC PROC SHR AX,1 ;THINK ABOUT IT JNC ADDGP1 XOR GPC0,1 ;CHANGE THE STATE OF BIT0 JNZ ADDGP1 ; CONTINUE IF CHANGING FROM A "0" TO A "1" INC GPC ;ELSE BUMP THE REST OF GPC ADDGP1: ADD GPC,AX RET ADDGPC ENDP ; ----------------------------------------------- ; {GB@GPC} ; GET A BYTE OF VIRTUAL MEMORY AT THE CURRENT GPC ; ----------------------------------------------- ; ; EXIT: BYTE IN [AX] ; PUBLIC GB@GPC GB@GPC PROC PUSH BX ;SAVE AFFECTED REGISTER(S) PUSH CX ;CREATE AND SAVE THE LOW ADDRESS BITS MOV AX,GPC ;GET GPC1 AND GPC2 MOV BL,GPC0 ;GET GPC BIT SHL AX,1 ;SHIFT GPC2 AND OR AL,BL ; APPEND THE GPC BIT AND AX,01FFH ; AND ISOLATE THE LADD BITS MOV LADD,AX ;SAVE LOW ADDRESS BITS ;CREATE AND SAVE THE VIRTUAL PAGE NUMBER MOV AX,GPC ;GET VIRTUAL ADDRESS AGAIN JMP GB@VM1 ; AND USE EXISTING CODE GB@GPC ENDP ; ----------------------------- ; {GW@GPC} ; GET A WORD AT THE CURRENT GPC ; ----------------------------- ; ; EXIT: WORD IN [AX] ; GPC <= GPC + 2 ; PUBLIC GW@GPC GW@GPC PROC PUSH BX ;SAVE AFFECTED REGISTER(S) PUSH CX ;SEE IF WE CAN LEGALLY CHEAT CMP GPC2,0FFH ;CHEAT 510 TIMES OUT OF 512 JNE GW@GP3 CMP GPC0,1 ;WE STILL HAVE A 50/50 CHANCE JNE GW@GP3 ;....YAY! ; RATS, WE HAVE TO DO IT THE HARD WAY CALL GB@GPCI ;GET THE FIRST BYTE AND POINT TO THE NEXT MOV BL,AL ;SAVE THE MSB IN BL CALL GB@GPC ;GET THE LSB IN AL MOV AH,BL ;PUT THE MSB IN AH ; DECREMENT THE GPC XOR GPC0,1 ;CHANGE THE STATE OF BIT0 JZ GW@GP4 ;EXIT IF CHANGING FROM A "1" TO A "0" DEC GPC ;ELSE DECREMENT THE REST OF THE GPC GW@GP4: POP CX POP BX RET ;CREATE AND SAVE THE LOW ADDRESS BITS GW@GP3: MOV AX,GPC ;GET GPC1 AND GPC2 MOV BL,GPC0 ;GET GPC BIT SHL AX,1 ;SHIFT GPC2 AND OR AL,BL ; APPEND THE GPC BIT AND AX,01FFH ; AND ISOLATE THE LADD BITS MOV LADD,AX ;SAVE LOW ADDRESS BITS ;CREATE AND SAVE THE VIRTUAL PAGE NUMBER MOV AX,GPC ;GET VIRTUAL ADDRESS AGAIN JMP GW@VM1 ; AND USE EXISTING CODE GW@GPC ENDP ; ----------------------------------------------- ; {GB@GPCI} ; GET A BYTE OF VIRTUAL MEMORY AT THE CURRENT GPC ; AND INCREMENT THE GPC ; ----------------------------------------------- ; ; EXIT: BYTE IN [AX] ; GPC <= GPC + 1 ; PUBLIC GB@GPCI GB@GPCI PROC CALL GB@GPC ;GET THE BYTE ; AND FALL THROUGH ............. GB@GPCI ENDP ; ----------------- ; {INCGPC} ; INCREMENT THE GPC ; ----------------- ; ; EXIT: GPC <= GPC + 1 ; PUBLIC INCGPC INCGPC PROC ;UPDATE THE VIRTUAL MEMORY POINTER XOR GPC0,1 ;CHANGE THE STATE OF BIT0 JNZ INCGP1 ;EXIT IF CHANGING FROM A "0" TO A "1" INC GPC ;ELSE BUMP THE REST OF THE GPC INCGP1: RET INCGPC ENDP ; ----------------------------------------------- ; {GW@GPCI} ; GET A WORD OF VIRTUAL MEMORY AT THE CURRENT GPC ; GPC = GPC + 2 ; ----------------------------------------------- ; ; EXIT: WORD IN [AX] ; GPC <= GPC + 2 ; PUBLIC GW@GPCI GW@GPCI PROC CALL GW@GPC ;GET THE WORD INC GPC ;SAME AS ADDING 2 TO THE ENTIRE GPC RET GW@GPCI ENDP ; ----------------------------- ; {PB@GPC} ; PUT A BYTE AT THE CURRENT GPC ; ----------------------------- ; ; ENTRY: [AL] = BYTE TO BE STORED ; ; EXIT: AL IS STORED AT THE SELECTED VIRTUAL ADDRESS ; PUBLIC PB@GPC PB@GPC PROC PUSH BX ;SAVE AFFECTED REGISTER(S) PUSH CX PUSH AX ;SAVE THE VALUE ;CREATE AND SAVE THE LOW ADDRESS BITS MOV AX,GPC ;GET GPC1 AND GPC2 MOV BL,GPC0 ;GET GPC BIT SHL AX,1 ;SHIFT GPC2 AND OR AL,BL ; APPEND THE GPC BIT AND AX,01FFH ; AND ISOLATE THE LADD BITS MOV LADD,AX ;SAVE LOW ADDRESS BITS ;CREATE AND SAVE THE VIRTUAL PAGE NUMBER MOV AX,GPC ;GET VIRTUAL ADDRESS AGAIN JMP PB@VM1 ; AND USE EXISTING CODE PB@GPC ENDP ; ----------------------------- ; {PW@GPC} ; PUT A WORD AT THE CURRENT GPC ; ----------------------------- ; ; ENTRY: [AX] = WORD TO BE STORED ; ; EXIT: AX IS STORED AT THE SELECTED VIRTUAL ADDRESS ; PUBLIC PW@GPC PW@GPC PROC PUSH BX ;SAVE AFFECTED REGISTER(S) PUSH CX ;SEE IF WE CAN LEGALLY CHEAT CMP GPC2,0FFH ;CHEAT 510 TIMES OUT OF 512 JNE PW@GP3 CMP GPC0,1 ;WE STILL HAVE A 50/50 CHANCE JNE PW@GP3 ;....YAY! ; RATS, WE HAVE TO DO IT THE HARD WAY XCHG AH,AL MOV BL,AH CALL PB@GPCI ;STORE THE BYTE AND POINT TO THE NEXT MOV AL,BL CALL PB@GPC ; DECREMENT THE GPC XOR GPC0,1 ;CHANGE THE STATE OF BIT0 JZ PW@GP4 ;EXIT IF CHANGING FROM A "1" TO A "0" DEC GPC ;ELSE DECREMENT THE REST OF THE GPC PW@GP4: POP CX POP BX RET PW@GP3: PUSH AX ;SAVE THE VALUE ;CREATE AND SAVE THE LOW ADDRESS BITS MOV AX,GPC ;GET GPC1 AND GPC2 MOV BL,GPC0 ;GET GPC BIT SHL AX,1 ;SHIFT GPC2 AND OR AL,BL ; APPEND THE GPC BIT AND AX,01FFH ; AND ISOLATE THE LADD BITS MOV LADD,AX ;SAVE LOW ADDRESS BITS ;CREATE AND SAVE THE VIRTUAL PAGE NUMBER MOV AX,GPC ;GET VIRTUAL ADDRESS AGAIN ;AND EXECUTE COMMON CODE JMP PW@VM1 PW@GPC ENDP ; ----------------------------- ; {PB@GPCI} ; PUT A BYTE AT THE CURRENT GPC ; AND INCREMENT THE GPC ; ----------------------------- ; ; ENTRY: [AL] = BYTE TO BE STORED ; ; EXIT: AL IS STORED AT THE SELECTED VIRTUAL ADDRESS ; GPC <= GPC + 1 ; PUBLIC PB@GPCI PB@GPCI PROC CALL PB@GPC ;WRITE OUT THE BYTE ;UPDATE THE VIRTUAL MEMORY POINTER JMP INCGPC PB@GPCI ENDP ; ----------------------------- ; {PW@GPCI} ; PUT A WORD AT THE CURRENT GPC ; GPC = GPC + 2 ; ----------------------------- ; ; ENTRY: [AX] = WORD TO BE STORED ; ; EXIT: AX IS STORED AT THE SELECTED VIRTUAL ADDRESS ; GPC <= GPC + 2 ; PUBLIC PW@GPCI PW@GPCI PROC CALL PW@GPC ;PUT THE WORD IN VIRTUAL MEMORY INC GPC ;SAME AS ADDING 2 TO THE ENTIRE GPC RET PW@GPCI ENDP ; --------------------------------------------------- ; {GETSHT} ; GET A BYTE OF VIRTUAL MEMORY AT THE CURRENT GPC ; INCREMENT THE GPC ; AND PUT THE BYTE IN [VALUE] ; --------------------------------------------------- ; ; ENTRY: G-MACHINE PROGRAM COUNTER DEFINITION ; GPC1 = MSB OF GPC ; GPC2 = LSB OF GPC ; GPC0 = BIT 0 OF BYTE IS BIT 0 OF GPC ; ; EXIT: BTYE IN [AX] AND [VALUE] ; GPC <= GPC + 1 ; ; PUBLIC GETSHT GETSHT PROC CALL GB@GPCI ;GET THE BYTE IN [AL] MOV VALUE,AX ; AND SAVE IT IN VALUE RET GETSHT ENDP ; --------------------------------------------------- ; {GETLNG} ; GET A WORD OF VIRTUAL MEMORY AT THE CURRENT GPC ; GPC = GPC + 2 ; AND PUT THE WORD IN [VALUE] ; --------------------------------------------------- ; ; ENTRY: G-MACHINE PROGRAM COUNTER DEFINITION ; GPC1 = MSB OF GPC ; GPC2 = LSB OF GPC ; GPC0 = BIT 0 OF BYTE IS BIT 0 OF GPC ; ; EXIT: WORD IN [AX] ; GPC <= GPC + 2 ; ; REGISTERS DESTROYED: AX ; PUBLIC GETLNG GETLNG PROC CALL GW@GPCI ;GET THE WORD IN [AX] MOV VALUE,AX ; AND SAVE IT IN VALUE RET GETLNG ENDP SUBTTL VMP VIRTUAL MEMORY ACCESS PAGE ; --------------------------------------------------------------- ; The following subroutines are used to access Virtual Memory ; through the VMP. ; ; VIRTUAL MEMORY POINTER DEFINITION ; VMP VMP1 = MSB OF VMP VMP2 = LSB OF VMP ; VMP0 = BIT 0 OF BYTE IS BIT 0 OF VMP ; ---------------------------------------------------------------- ; ------ ; ADDVMP ; ------ ; ; ENTRY: [AX] = VALUE TO BE ADDED TO VMP ; EXIT: VMP <= VMP + [AX] ; ADDVMP PROC SHR AX,1 ;THINK ABOUT IT JNC ADDVM1 XOR VMP0,1 ;CHANGE THE STATE OF BIT0 JNZ ADDVM1 ; CONTINUE IF CHANGING FROM A "0" TO A "1" INC VMP ;ELSE BUMP THE REST OF VMP ADDVM1: ADD VMP,AX RET ADDVMP ENDP ; ----------------------------------------------- ; {GB@VMP} ; GET A BYTE OF VIRTUAL MEMORY AT THE CURRENT VMP ; ----------------------------------------------- ; ; EXIT: BYTE IN [AX] ; PUBLIC GB@VMP GB@VMP PROC ;CREATE AND SAVE THE LOW ADDRESS BITS PUSH BX ;SAVE AFFECTED REGISTER(S) PUSH CX MOV AX,VMP ;GET VMP1 AND VMP2 MOV BL,VMP0 ;GET VMP BIT SHL AX,1 ;SHIFT VMP2 AND OR AL,BL ; APPEND THE VMP BIT AND AX,01FFH ; AND ISOLATE THE LADD BITS MOV LADD,AX ;SAVE LOW ADDRESS BITS ;CREATE AND SAVE THE VIRTUAL PAGE NUMBER MOV AX,VMP ;GET VIRTUAL ADDRESS AGAIN ; AND FALL THROUGH ............. GB@VMP ENDP ; -------------------------------- ; CODE COMMON TO GB@VMP AND GB@GPC ; -------------------------------- GB@VM1 PROC MOV VPAGEN,AH ; SAVE THE VIRTUAL PAGE NUMBER ;PAGE IN THE BLOCK OF VIRTUAL MEMORY CALL GTVBLK ; AND MAKE LOC VALID ;RESTORE LADD AND MAKE A FULLY FORMED ADDRESS MOV AL,LOC ;GET THE ABSOLUTE BLOCK NUMBER SUB AH,AH SHL AX,1 ;SHIFT THE SEGMENT BIT INTO BIT 8 AND A "0" ; INTO BIT 0, PUTTING THE HIGHER ADDRESS BITS ; IN [AL] AND THE SEGMENT BIT IN [AH] MOV BX,LADD ;RESTORE THE LOWER ADDRESS BITS OR BH,AL ;COMBINE THE LOWER AND HIGHER ADDRESS BITS ;DETERMINE THE PROPER SEGMENT MOV CX,SEG0 ;PRE-POINT AT SEG0 OR AH,AH ;TEST SEGMENT BIT JZ GB@VM2 ;IF "0" THEN IT'S SEG0 MOV CX,SEG1 ; ELSE POINT TO THE OTHER SEGMENT GB@VM2: MOV ES,CX ;ES POINTS TO THE CORRECT SEGMENT ;GET THE BYTE MOV AL,ES:[BX] ;GET THE BYTE SUB AH,AH ;GET RID OF UNWANTED BYTE POP CX ;RESTORE AFFECTED REGISTER(S) POP BX RET GB@VM1 ENDP ; ----------------------------- ; {GW@VMP} ; GET A WORD AT THE CURRENT VMP ; ----------------------------- ; ; EXIT: WORD IN [AX] ; VMP <= VMP + 2 ; PUBLIC GW@VMP GW@VMP PROC PUSH BX ;SAVE AFFECTED REGISTER(S) PUSH CX ;SEE IF WE CAN LEGALLY CHEAT CMP VMP2,0FFH ;CHEAT 510 TIMES OUT OF 512 JNE GW@VM3 CMP VMP0,1 ;WE STILL HAVE A 50/50 CHANCE JNE GW@VM3 ;....YAY! ; RATS, WE HAVE TO DO IT THE HARD WAY CALL GB@VMPI ;GET THE BYTE AND POINT TO THE NEXT MOV BL,AL CALL GB@VMP ;GET THE SECOND BYTE MOV AH,BL ;FORM THE WORD ; DECREMENT THE VMP XOR VMP0,1 ;CHANGE THE STATE OF BIT0 JZ GW@VM4 ;EXIT IF CHANGING FROM A "1" TO A "0" DEC VMP ;ELSE DECREMENT THE REST OF THE VMP GW@VM4: POP CX POP BX RET ;CREATE AND SAVE THE LOW ADDRESS BITS GW@VM3: MOV AX,VMP ;GET VMP1 AND VMP2 MOV BL,VMP0 ;GET VMP BIT SHL AX,1 ;SHIFT VMP2 AND OR AL,BL ; APPEND THE VMP BIT AND AX,01FFH ; AND ISOLATE THE LADD BITS MOV LADD,AX ;SAVE LOW ADDRESS BITS ;CREATE AND SAVE THE VIRTUAL PAGE NUMBER MOV AX,VMP ;GET VIRTUAL ADDRESS AGAIN ; AND FALL THROUGH ............. GW@VMP ENDP ; -------------------------------- ; CODE COMMON TO GW@VMP AND GW@GPC ; -------------------------------- GW@VM1 PROC MOV VPAGEN,AH ; SAVE THE VIRTUAL PAGE NUMBER ;PAGE IN THE BLOCK OF VIRTUAL MEMORY CALL GTVBLK ; AND MAKE LOC VALID ;RESTORE LADD AND MAKE A FULLY FORMED ADDRESS MOV AL,LOC ;GET THE ABSOLUTE BLOCK NUMBER SUB AH,AH SHL AX,1 ;SHIFT THE SEGMENT BIT INTO BIT 8 AND A "0" ; INTO BIT 0, PUTTING THE HIGHER ADDRESS BITS ; IN [AL] AND THE SEGMENT BIT IN [AH] MOV BX,LADD ;RESTORE THE LOWER ADDRESS BITS OR BH,AL ;COMBINE THE LOWER AND HIGHER ADDRESS BITS ;DETERMINE THE PROPER SEGMENT MOV CX,SEG0 ;PRE-POINT AT SEG0 OR AH,AH ;TEST SEGMENT BIT JZ GW@VM2 ;IF "0" THEN IT'S SEG0 MOV CX,SEG1 ; ELSE POINT TO THE OTHER SEGMENT GW@VM2: MOV ES,CX ;ES POINTS TO THE CORRECT SEGMENT ;GET THE WORD MOV AX,ES:[BX] ;GET THE WORD XCHG AH,AL ;SWAPIFY POP CX ;RESTORE AFFECTED REGISTER(S) POP BX RET GW@VM1 ENDP ; ----------------------------------------------- ; {GB@VMPI} ; GET A BYTE OF VIRTUAL MEMORY AT THE CURRENT VMP ; AND INCREMENT THE VMP ; ----------------------------------------------- ; ; EXIT: BYTE IN [AX] ; VMP <= VMP + 1 ; PUBLIC GB@VMPI GB@VMPI PROC CALL GB@VMP ;GET THE BYTE ; AND FALL THROUGH ............. GB@VMPI ENDP ; ----------------- ; {INCVMP} ; INCREMENT THE VMP ; ----------------- ; ; EXIT: VMP <= VMP + 1 ; PUBLIC INCVMP INCVMP PROC ;UPDATE THE VIRTUAL MEMORY POINTER XOR VMP0,1 ;CHANGE THE STATE OF BIT0 JNZ INCMP1 ;EXIT IF CHANGING FROM A "0" TO A "1" INC VMP ;ELSE BUMP THE REST OF THE VMP INCMP1: RET INCVMP ENDP ; ----------------------------------------------- ; {GW@VMPI} ; GET A WORD OF VIRTUAL MEMORY AT THE CURRENT VMP ; VMP = VMP + 2 ; ----------------------------------------------- ; ; EXIT: WORD IN [AX] ; VMP <= VMP + 2 ; PUBLIC GW@VMPI GW@VMPI PROC CALL GW@VMP ;GET THE WORD INC VMP ;SAME AS ADDING 2 TO THE ENTIRE VMP RET GW@VMPI ENDP ; ----------------------------- ; {PB@VMP} ; PUT A BYTE AT THE CURRENT VMP ; ----------------------------- ; ; ENTRY: [AL] = BYTE TO BE STORED ; ; EXIT: AL IS STORED AT THE SELECTED VIRTUAL ADDRESS ; PUBLIC PB@VMP PB@VMP PROC PUSH BX ;SAVE AFFECTED REGISTER(S) PUSH CX PUSH AX ;SAVE THE VALUE ;CREATE AND SAVE THE LOW ADDRESS BITS MOV AX,VMP ;GET VMP1 AND VMP2 MOV BL,VMP0 ;GET VMP BIT SHL AX,1 ;SHIFT VMP2 AND OR AL,BL ; APPEND THE VMP BIT AND AX,01FFH ; AND ISOLATE THE LADD BITS MOV LADD,AX ;SAVE LOW ADDRESS BITS ;CREATE AND SAVE THE VIRTUAL PAGE NUMBER MOV AX,VMP ;GET VIRTUAL ADDRESS AGAIN ; AND FALL THROUGH ............. PB@VMP ENDP ; -------------------------------- ; CODE COMMON TO PB@VMP AND PB@GPC ; -------------------------------- PB@VM1 PROC MOV VPAGEN,AH ; TO SAVE THE VIRTUAL PAGE NUMBER ;PAGE IN THE BLOCK OF VIRTUAL MEMORY CALL GTVBLK ; AND MAKE LOC VALID ;RESTORE LADD AND MAKE A FULLY FORMED ADDRESS MOV AL,LOC ;GET THE ABSOLUTE BLOCK NUMBER SUB AH,AH SHL AX,1 ;SHIFT THE SEGMENT BIT INTO BIT 8 AND A "0" ; INTO BIT 0, PUTTING THE HIGHER ADDRESS BITS ; IN [AL] AND THE SEGMENT BIT IN [AH] MOV BX,LADD ;RESTORE THE LOWER ADDRESS BITS OR BH,AL ;COMBINE THE LOWER AND HIGHER ADDRESS BITS ;DETERMINE THE PROPER SEGMENT MOV CX,SEG0 ;PRE-POINT AT SEG0 OR AH,AH ;TEST SEGMENT BIT JZ PB@VM2 ;IF "0" THEN IT'S SEG0 MOV CX,SEG1 ; ELSE POINT TO THE OTHER SEGMENT PB@VM2: MOV ES,CX ;ES POINTS TO THE CORRECT SEGMENT ;STORE THE BYTE POP AX ;RESTORE THE BYTE MOV ES:[BX],AL POP CX ;RESTORE AFFECTED REGISTER(S) POP BX RET PB@VM1 ENDP ; ----------------------------- ; {PW@VMP} ; PUT A WORD AT THE CURRENT VMP ; ----------------------------- ; ; ENTRY: [AX] = WORD TO BE STORED ; ; EXIT: AX IS STORED AT THE SELECTED VIRTUAL ADDRESS ; PUBLIC PW@VMP PW@VMP PROC PUSH BX ;SAVE AFFECTED REGISTER(S) PUSH CX ;SEE IF WE CAN LEGALLY CHEAT CMP VMP2,0FFH ;CHEAT 510 TIMES OUT OF 512 JNE PW@VM3 CMP VMP0,1 ;WE STILL HAVE A 50/50 CHANCE JNE PW@VM3 ;....YAY! ; RATS, WE HAVE TO DO IT THE HARD WAY XCHG AH,AL MOV BL,AH CALL PB@VMPI ;STORE THE BYTE AND POINT TO THE NEXT MOV AL,BL CALL PB@VMP ; DECREMENT THE VMP XOR VMP0,1 ;CHANGE THE STATE OF BIT0 JZ PW@VM4 ;EXIT IF CHANGING FROM A "1" TO A "0" DEC VMP ;ELSE DECREMENT THE REST OF THE VMP PW@VM4: POP CX POP BX RET PW@VM3: PUSH AX ;SAVE THE VALUE ;CREATE AND SAVE THE LOW ADDRESS BITS MOV AX,VMP ;GET VMP1 AND VMP2 MOV BL,VMP0 ;GET VMP BIT SHL AX,1 ;SHIFT VMP2 AND OR AL,BL ; APPEND THE VMP BIT AND AX,01FFH ; AND ISOLATE THE LADD BITS MOV LADD,AX ;SAVE LOW ADDRESS BITS ;CREATE AND SAVE THE VIRTUAL PAGE NUMBER MOV AX,VMP ;GET VIRTUAL ADDRESS AGAIN ; AND FALL THROUGH ............. PW@VMP ENDP ; -------------------------------- ; CODE COMMON TO PW@VMP AND PW@GPC ; -------------------------------- PW@VM1 PROC MOV VPAGEN,AH ; TO SAVE THE VIRTUAL PAGE NUMBER ;PAGE IN THE BLOCK OF VIRTUAL MEMORY CALL GTVBLK ; AND MAKE LOC VALID ;RESTORE LADD AND MAKE A FULLY FORMED ADDRESS MOV AL,LOC ;GET THE ABSOLUTE BLOCK NUMBER SUB AH,AH SHL AX,1 ;SHIFT THE SEGMENT BIT INTO BIT 8 AND A "0" ; INTO BIT 0, PUTTING THE HIGHER ADDRESS BITS ; IN [AL] AND THE SEGMENT BIT IN [AH] MOV BX,LADD ;RESTORE THE LOWER ADDRESS BITS OR BH,AL ;COMBINE THE LOWER AND HIGHER ADDRESS BITS ;DETERMINE THE PROPER SEGMENT MOV CX,SEG0 ;PRE-POINT AT SEG0 OR AH,AH ;TEST SEGMENT BIT JZ PW@VM2 ;IF "0" THEN IT'S SEG0 MOV CX,SEG1 ; ELSE POINT TO THE OTHER SEGMENT PW@VM2: MOV ES,CX ;ES POINTS TO THE CORRECT SEGMENT ;STORE THE WORD POP AX ;RESTORE THE WORD XCHG AH,AL ;SWAPIFY MOV ES:[BX],AX ;WRITE IT OUT POP CX ;RESTORE AFFECTED REGISTER(S) POP BX RET PW@VM1 ENDP ; ----------------------------- ; {PB@VMPI} ; PUT A BYTE AT THE CURRENT VMP ; AND INCREMENT THE VMP ; ----------------------------- ; ; ENTRY: [AL] = BYTE TO BE STORED ; ; EXIT: AL IS STORED AT THE SELECTED VIRTUAL ADDRESS ; VMP <= VMP + 1 ; PUBLIC PB@VMPI PB@VMPI PROC CALL PB@VMP ;WRITE OUT THE BYTE ;UPDATE THE VIRTUAL MEMORY POINTER JMP INCVMP PB@VMPI ENDP ; ----------------------------- ; {PW@VMPI} ; PUT A WORD AT THE CURRENT VMP ; VMP = VMP + 2 ; ----------------------------- ; ; ENTRY: [AX] = WORD TO BE STORED ; ; EXIT: AX IS STORED AT THE SELECTED VIRTUAL ADDRESS ; VMP <= VMP + 2 ; PUBLIC PW@VMPI PW@VMPI PROC CALL PW@VMP ;PUT THE WORD IN VIRTUAL MEMORY INC VMP ;SAME AS ADDING 2 TO THE ENTIRE VMP RET PW@VMPI ENDP ;------------------------------------------------ ; E N D O F S U B R O U T I N E S ;------------------------------------------------ SUBTTL COLD START PAGE ;---------------------------------------------------------------------- ; C O L D S T A R T ;---------------------------------------------------------------------- PUBLIC DIPBGN,REST1,REST2 DIPBGN PROC CALL SYSINI ;DO ANY SYSTEM INITIALIZATION CALL MTIME ;INITIALIZE RANDOM NUMBER GENERATOR MOV WORD PTR OLDGSP,DI ;INITIALIZE OLD GSTACK POINTER ; SET DEFAULT CLIPPING PARAMETERS MOV WINDX1,0 ;FULL SCREEN MOV WINDY1,0 MOV WINDX2,39 MOV WINDY2,23 MOV WINDH,24 ;WINDH = (WINDY2 - WINDY1) + 1 MOV WINDW,40 ;WINDW = (WINDX2 - WINDX1) + 1 ; ; GET G-PRELOAD INTO THE BUFFER AREA ; ; INITIALIZE GCODE MOV GCODE,0 ; GET THE FIRST BLOCK OF G-PRELOAD MOV VPAGEN,0 ;VIRTUAL BLOCK 0 CALL GTPBLK ;GET PRELOAD BLOCK ; GET THE GAME ID FOR SAVE/RESTORE AND POSTERITY ; MOV VMP,0 ;SET UP VIRTUAL MEMORY ADDRESS POINTER MOV VMP0,0 ; TO RETRIEVE [GEND] MOV AX,GID CALL ADDVMP CALL GW@VMP ;GET THE END OF G-PRELOAD (WORDPTR) MOV ZORKID,AX ; SAVE IT AWAY ; EXTRACT DATA FROM G-PRELOAD HEADER ; DETERMINE THE NUMBER OF PAGES IN G-PRELOAD ; GET THE VIRTUAL ADDRESS OF THE START OF G-PURELOAD MOV VMP,0 ;SET UP VIRTUAL MEMORY ADDRESS POINTER MOV VMP0,0 ; TO RETRIEVE [GEND] MOV AX,GEND CALL ADDVMP CALL GW@VMP ;GET THE END OF G-PRELOAD (WORDPTR) ; SEE IF THE 1ST. VIRTUAL PAGE OF G-PURELOAD CONTAINS ANY G-PRELOAD OR AL,AL ;IS THERE A PARTIAL PRE-LOAD RECORD? JZ NOGPRT ;NOPE INC AH ;YUP ; NO G-CODE PARTIAL RECORD ; DETERMINE THE TOTAL NUMBER OF PAGES IN G-PRELOAD NOGPRT: MOV GPEND,AH ;SAVE IT, IT'S THE FIRST VIRTUAL BLOCK ; # OF G-PURELOAD MOV GPRBLK,AH ; AND THE TOTAL NUMBER OF ; G-PRELOAD BLOCKS ; DO WE HAVE ENOUGH ROOM IN THE PAGING AREA FOR G-PRELOAD? CMP BUFFERS,AH JA GOTRM1 ;....YES ; *** ERROR #0: GAME PRELOAD IS TOO BIG *** NOGRAM: MOV AL,0 JMP GERROR ; GET THE REST OF G-PRELOAD INTO THE BUFFER AREA ; USE GSTRT TO DETERMINE THE NUMBER OF G-PRELOAD BLOCKS TO ; TO BE LOADED GOTRM1: MOV AL,GMAJOR ; SET INTERPRETER VERSION MOV AH,GMINOR ; LETTER AND NUMBER MOV WORD PTR ES:[GINTVER],AX ; STORE INTERPRETER VERSION MOV CL,GPRBLK ;GET TOTAL NUMBER OF G-PRELOAD BLOCKS DEC CL ;-1 SINCE WE'VE ALREADY DONE THE 1ST. JZ GTIPRE ;LEAVE IF IT WAS THE ONLY PRELOAD BLOCK SUB CH,CH ;INITIALIZE LOOP COUNTER MOV VPAGEN,1 ;START WITH VIRTUAL BLOCK #1 ; LOAD THE REMAINING G-PRELOAD LOOP LGPRLP: PUSH CX ;SAVE LOOP COUNTER CALL GTPBLK ;GET THE PRELOAD BLOCK INC VPAGEN ;NEXT BLOCK POP CX ;RESTORE LOOP COUNTER LOOP LGPRLP ;CONTINUE UNTIL ALL OF G-PRELOAD IS IN ; ; GET I-PRELOAD INTO THE BUFFER AREA ; ; CALCULATE 1ST. VIRTUAL PAGE OF THE IFILE GTIPRE: MOV VMP,0 ;GET THE LENGTH OF THE G-PROGRAM MOV VMP0,0 ; IN WORDS MOV AX,GLEN CALL ADDVMP CALL GW@VMP ;PATCH FOR PADDING PURPOSES INC AX ;ICODE IS AT THE NEXT WORD ADDRESS MOV ICODE,AX ;SAVE VIRTUAL STARTING ADDRESS MOV ISTRT,AH ; AND STARTING VIRTUAL PAGE NUMBER ; GET THE FIRST BLOCK OF I-PRELOAD MOV VPAGEN,AH ;SETUP THE VIRTUAL PAGE NUMBER CALL GTPBLK ; AND GET THE FIRST BLOCK OF I-PRELOAD ; DETERMINE VIRTUAL ADDRESS OF IPURE MOV AX,ICODE MOV VMP,AX MOV VMP0,0 MOV AX,IEND CALL ADDVMP CALL GW@VMP ;GET THE LENGTH OF I-PRELOAD IN WORDS INC AX ;I-PURE STARTS AT THE NEXT EVEN ADDRESS MOV BX,ICODE ;GET OFFSET TO THE START OF THE IFILE ADD AX,BX ;ADD STARTING ADDRESS ; TO GET A GFILE RELATIVE ADDRESS MOV IPURE,AX ;SAVE CORRECTED START OF I-PURE ; ; GET THE REST OF I-PRELOAD INTO THE BUFFER AREA ; ; DETERMINE THE NUMBER OF BLOCKS OF I-PRELOAD REMAINING ; DETERMINE THE TOTAL NUMBER OF I-PRELOAD BLOCKS ; USE THE STARTING I-PRELOAD VIRTUAL BLOCK NUMBER ; TO DETERMINE AN ADJUSTED SIZE OF I-PRELOAD MOV BH,ISTRT ;GET VIRTUAL I-PRELOAD STARTING PAGE SUB BL,BL ; PAGE ALIGN IT MOV AX,IPURE ;GET THE RELATIVIZED STARTING ; ADDRESS OF IPURE SUB AX,BX ;SUBTRACT ZERO ADJUSTED STARTING ADDR ; SEE IF THE 1ST. VIRTUAL PAGE OF IPURE CONTAINS ANY I-PRELOAD OR AL,AL ;SEE IF THERE'S A PARTIAL RECORD JZ NOIPRT ;NOPE INC AH ;YUP NOIPRT: MOV IPRBLK,AH ;SAVE TOTAL NUMBER OF I-PRELOAD BLOCKS ; DO WE HAVE ENOUGH ROOM IN THE PAGING AREA FOR I-PRELOAD? ADD AH,GPRBLK ;ADD IN THE NUMBER OF G-PRELOAD BLOCKS CMP BUFFERS,AH JA NOIP1 JMP NOGRAM ;....NO NOIP1: MOV CL,IPRBLK ;GET THE NUMBER OF I-PRELOAD BLOCKS OR CL,CL ;SINCE IT'S POSSIBLE TO HAVE NONE JZ INITAB ; WE HAVE TO CHECK FOR IT DEC CL ;-1 SINCE WE'VE ALREADY DONE THE 1ST. JZ INITAB ;LEAVE IF IT WAS THE ONLY PRELOAD BLOCK SUB CH,CH ;INITIALIZE LOOP COUNTER ;INITIALIZE VALUES FOR I-PRELOAD LOOP MOV AL,ISTRT ;GET VIRTUAL I-PRELOAD STARTING BLOCK # INC AL ;ADD 1 SINCE WE'VE ALREADY LOADED THE ; FIRST BLOCK MOV VPAGEN,AL ;SAVE STARTING BLOCK # ; LOAD REMAINING I-PRELOAD LOOP LIPRLP: PUSH CX ;SAVE LOOP COUNTER CALL GTPBLK ;GET THE BLOCK INC VPAGEN ;NEXT BLOCK POP CX ;RESTORE LOOP COUNTER LOOP LIPRLP ;CONTINUE UNTIL ALL OF I-PRELOAD IS IN ; IF THE GAME FITS IN AVAILABLE MEMORY, ; READ THE WHOLE THING IN CMP FSIZE,0 ; (B4) DOES THE GAME FIT? JE SLURP1 MOV DL,GPRBLK ; (B4) SO START SLURPING XOR DH,DH ; (B4) SCRAP TOP HALF MOV CL,9 ; (B4) CONVERT TO BYTES SHL DX,CL ; (B4) OF G-PRELOAD MOV CX,0 ; (B4) SET UP FOR SEEK MOV BX,GAMHNDL MOV AX,4200H ; (B4) SEEK PAST B-O-F INT 21H PUSH DS ; (B4) USE GAME BUFFER MOV CX,SEG0 ; (B4) MAKE ADDRESSIBLE THRU DS MOV DS,CX MOV DX,AX ; (B4) SAVE OFFSET MOV AX,0H ; (B4) HOW MANY BYTES TO END OF SEG? SUB AX,DX ; (B4) ONE SEG IN LENGTH MOV CX,AX ; (B4) THAT'S HOW MANY TO READ FIRST MOV AH,CRDRNDZ ; (B4) READ THAT AMOUNT INT 21H CMP AX,CX ; (B4) THAT COULD BE THE END... JNE SLURP MOV AX,DS ; (B4) MAKE NEXT SEG ADDRESSIBLE ADD AX,1000H ; (B4) 64K ABOVE THE FIRST MOV DS,AX MOV CX,0FFFFH ; (B4) READ UP TO ANOTHER 64K MOV DX,0 ; (B4) STARTING AT THE BEGINNING MOV AH,CRDRNDZ ; (B4) READ IT ALL IN INT 21H SLURP: POP DS PUSH DI PUSH ES ; (B4) SAVES PUSH DS ; (B4) MAKE DS ADDRESSIBLE THRU ES POP ES MOV AL,1 ; (B4) ALL PAGES WILL BE PRELOAD MOV CX,0FFH MOV DI,OFFSET MEMMAP ; (B4) FILL TABLE TO INDICATE ALL IN SLURP2: MOV AH,CL ; (B4) FILL IN BUF NUMBER SUB AH,0FFH ; (B4) FROM 0 - FF NEG AH ; (B4) RECTIFY STOSW ; (B4) STORE IT LOOP SLURP2 POP ES POP DI SLURP1: ; ; INITIALIZE POINTERS TO VARIABLES ; ; GLOBAL TABLE [GLOBAL] INITAB: MOV AX,GCODE MOV VMP,AX MOV VMP0,0 MOV AX,GGLOB CALL ADDVMP CALL GW@VMP ;GET POINTER TO GLOBAL TABLE MOV GLOBAL,AX ;SAVE IT ; LOCAL TABLE [GLOCS] SUB AX,0FH ;BACK UP 15 WORDS FOR LOCALS MOV GLOCS,AX ;STORE PTR TO START OF LOCALS ; ; REPAIR PREDEFINED GLOBALS 16, 17, 18, AND 19 ; ; GLOBAL 16 - NUMBER OF BLOCKSETS MOV AX,ICODE MOV GPC,AX MOV GPC0,0 MOV AX,IBLKS CALL ADDGPC CALL GETSHT ;GET THE NUMBER OF BLOCKSETS IN VALUE MOV NBLOKS,AL ;STORE IT IN NBLOKS MOV AX,16 ; AND IN GLOBAL #16 CALL VARPUT ; GLOBAL 17 - POINTER TO BLOCKSET POINTER TABLE MOV AX,ICODE ;GET OFFSET TO THE START OF IFILE ADD AX,4 ;BPT IS 8-BYTES FURTHER DOWN MOV BTAB,AX ;SAVE VIRTUAL ADDRESS OF BPT MOV VALUE,AX ;PUT IT IN VALUE, SO WE CAN MOV AX,17 ;STORE THE VIRTUAL ADDRESS OF THE CALL VARPUT ; BLOCKSET POINTER TABLE IN GLOBAL #17 ; GLOBAL 18 - NUMBER OF ICONS MOV AX,ICODE MOV GPC,AX MOV GPC0,0 MOV AX,IICONS CALL ADDGPC CALL GETSHT ;GET THE NUMBER OF ICONS IN VALUE MOV NICONS,AL ;SAVE NUMBER OF ICONS MOV AX,18 ;STORE NICONS IN GLOBAL #18 CALL VARPUT ; GLOBAL 19 - PTR TO ICON PTR TABLE MOV AL,NBLOKS ;GET NO. OF BLOCKSETS SUB AH,AH ; SHL AX,1 ;WORDIFY IT TO GET SIZE OF BLOCKSET TBL ; SHR AX,1 ; AND DEWORDIFY 'CAUSE BPT IS A WRDPTR MOV CX,BTAB ;GET THE BPT VIRTUAL ADDRESS ADD AX,CX ;POINT PAST THE BPT MOV ITAB,AX ;SAVE VIRTUAL ADDRESS OF IPT MOV VALUE,AX ;PUT IT IN VALUE, SO WE CAN MOV AX,19 ;STORE THE VIRTUAL ADDRESS OF CALL VARPUT ; THE ICON POINTER TABLE IN GLOBAL #19 ; ; TABLE PATCHING ; ; RELATIVIZE THE BPT TO THE START OF THE GFILE ; INITIALIZE LOOP COUNTER WITH THE NO. OF BLOCKSETS MOV CL,NBLOKS SUB CH,CH ; POINT TO THE BPT MOV AX,BTAB MOV VMP,AX MOV VMP0,0 ; GET THE OFFSET TO THE START OF THE IFILE MOV DX,ICODE REST1: PUSH CX ;SAVE LOOP COUNTER PUSH DX ;SAVE OFFSET CALL GW@VMP ;GET THE BLOCKSET POINTER POP DX ;RESTORE OFFSET PUSH DX ;SAVE OFFSET ADD AX,DX ; RELATIVIZE IT CALL PW@VMPI ;SAVE POINTER POP DX ;RESTORE OFFSET POP CX ;RESTORE LOOP COUNTER LOOP REST1 ;CONTINUE TILL WE'VE DONE THEM ALL ; RELATIVIZE THE IPT TO THE START OF THE GFILE ; INITIALIZE LOOP COUNTER WITH THE NO. OF ICONS MOV CL,NICONS SUB CH,CH ; POINT TO THE IPT MOV AX,ITAB MOV VMP,AX MOV VMP0,0 ; GET THE OFFSET TO THE START OF THE IFILE MOV DX,ICODE REST2: PUSH CX ;SAVE LOOP COUNTER PUSH DX ;SAVE OFFSET CALL GW@VMP ;GET THE ICON POINTER POP DX ;RESTORE OFFSET PUSH DX ;SAVE OFFSET ADD AX,DX ; RELATIVIZE IT CALL PW@VMPI ;SAVE POINTER POP DX ;RESTORE OFFSET POP CX ;RESTORE LOOP COUNTER LOOP REST2 ;CONTINUE TILL WE'VE DONE THEM ALL ; ; INITIALIZE THE INTERPRETER PROGRAM COUNTER ; ; GET THE EXECUTION STARTING ADDRESS FROM WITHIN THE G-FILE MOV VMP,0 MOV VMP0,0 MOV AX,GSTART ;OFFSET TO G-PROGRAM STARTING ADDRESS CALL ADDVMP ;FORM THE VMP ADDRESS CALL GW@VMP ;GET STARTING ADDRESS ; LOAD THE GPC WITH THE START ADDRESS MOV GPC,AX MOV GPC0,1 JMP MLOOP ; AND JUMP INTO THE MAINLOOP DIPBGN ENDP SUBTTL MAIN LOOP PAGE ;---------------------------------------------------------------------- ; INSTRUCTION INTERPRETATION LOOP ;---------------------------------------------------------------------- PUBLIC MLOOP,DC0,DC1,DC2,OPEXT,TOPX,OPX1,OPX2,OPX3,OPX4 PUBLIC OPXNXT,DOXOP,DODIS,OP0,OP1,OP1A,OP1B,OP1EX,BADOP1 PUBLIC OP2,OP2A,OP2B,OP2C,OP2D,OP2EX,OP2F MLOOP PROC ; ; INITIALIZE LOOP ; MOV NARGS,0 ;CLEAR #ARGS CALL GB@GPCI ;GET THE OPERATION BYTE MOV OPCODE,AL ;SAVE A COPY ;debug CMP GPC,1234H JNE MLP1 JMP MLP1 ; ; DETERMINE OPCODE TYPE ; ; IF OPCODE IS POSITIVE ( < 80H ) THEN IT'S A 2-OP (SHORT FORM) MLP1: CMP AL,80H ;IS IT A 2-OP (SHORT FORM)? JNB DC0 ;....NO. JMP OP2 ;....YES, GO DO IT ; IF OPCODE = 0E0H THEN IT'S GCALL ; WHICH IS HANDLED AS A SPECIAL CASE DC0: CMP AL,0E0H ;IS IT "CALL"? JNE DC1 ;....NO. JMP GCALL ;....YES. ; IF OPCODE < 0B0H THEN IT'S A 1-OP DC1: CMP AL,0B0H ;NO, IS IT A 1-OP? JNB DC2 ;....NO. JMP OP1 ;....YES. ; IF OPCODE < 0C0H THEN IT'S A 0-OP ; ELSE IT'S A X-OP DC2: CMP AL,0C0H ;IS IT AN 0-OP? JNB OPEXT ;....NO. IT IS AN X-OP JMP OP0 ;....YES. ; -------------- ; HANDLE AN X-OP ; -------------- OPEXT: CALL GB@GPCI ;GRAB ARGUMENT BYTE MOV BYTE PTR ABYTE,AL ;SAVE IT SUB BX,BX MOV BYTE PTR ADEX,BL ;INIT LOOP INDEX JZ OPX1 ;UNCONDITIONAL JUMP (ENTER LOOP) TOPX: MOV AL,BYTE PTR ABYTE ;GET ARG BYTE SAL AL,1 ;SHIFT NEXT 2 ARG BITS SAL AL,1 ;INTO BITS 7 & 6 MOV BYTE PTR ABYTE,AL ;HOLD FOR LATER OPX1: AND AL,11000000B ;ISOLATE ARGUMENT BITS JNZ OPX2 ;IS IT A LONG IMMEDIATE? ; 00 = LONG IMMEDIATE CALL GETLNG JMP OPXNXT OPX2: CMP AL,01000000B ;IS IT A SHORT IMMEDIATE? JNZ OPX3 ;....NO, KEEP GUESSING ; 01 = SHORT IMMEDIATE CALL GETSHT JMP OPXNXT OPX3: CMP AL,10000000B ;LAST TEST. VARIABLE? JNZ OPX4 ;....NO. NO MORE ARGUMENTS ; 10 = VARIABLE CALL GETVAR OPXNXT: MOV BL,ADEX ;RESTORE ARGUMENT INDEX SUB BH,BH ;CLEAR OUT HIGH BYTE MOV AX,WORD PTR VALUE ;GRAB VALUE MOV WORD PTR ARG1 [BX],AX ;STORE IN ARGUMENT TABLE INC NARGS ;UPDATE ARGUMENT COUNTER INC BX ;UPDATE INDEX INC BX ;TWICE MOV BYTE PTR ADEX,BL ;SAVE INDEX CMP BL,8 ;HAVE WE DONE 4 ARGUMENTS YET? JB TOPX ;....NO, GET SOME MORE ;ALL X-OP ARGUMENTS READY OPX4: MOV AL,OPCODE ;GET THE OPCODE BACK SUB AH,AH CMP AL,0E0H ;IS IT AN EXTENDED 2-OP? JAE DOXOP ;....NO, IT'S A REAL X-OP JMP OP2EX ; ELSE, TREAT IT LIKE A REAL 2-OP ; IS IT A VALID X-0P? DOXOP: MOV BX,OFFSET OPTX ;^ TO START OF X-OP DISPATCH TABLES AND AL,1FH ;ISOLATE OP ID BITS CMP AL,NOPSX ;IS IT A LEGAL X-OP JB DODIS ;....YES, DISPATCH IT ; *** ERROR #2: UNDEFINED X-OP *** MOV AL,2 JMP GERROR ;NO SUCH OPERATION ; --------------- ; OPCODE DISPATCH ; --------------- ;ENTRY: MASKED OPCODE INDEX IN [AL], BASE ADDRESS OF THE OPCODE ;DISPATCH TABLE IN BX. DODIS: SHL AX,1 ;MAKE IT A WORD OFFSET ADD BX,AX ;ADD THE OFFSET TO THE BASE ADDRESS MOV SI,DS:[BX] ;GET THE ADDRESS FROM THE DISPATCH TABLE CALL SI ;CALL THE OP ROUTINE JMP MLOOP ;AND LOOP FOR NEXT INSTRUCTION ; -------------- ; HANDLE A 0-OP ; -------------- OP0: MOV BX,OFFSET OPT0 ;^ TO 0-OP DISPATCH TABLE AND AL,0FH ;EXTRACT OPERATION BITS CMP AL,NOPS0 ;OUT OF RANGE? JB DODIS ;....NO, DISPATCH IT ; *** ERROR #3: UNDEFINED 0-OP *** MOV AL,3 JMP GERROR ; ------------- ; HANDLE A 1-OP ; ------------- OP1: AND AL,00110000B ;1OP. ISOLATE ARGUMENT BITS JNZ OP1A CALL GETLNG ;00 = LONG IMMEDIATE JMP OP1EX OP1A: CMP AL,00010000B ;SHORT IMMEDIATE? JNZ OP1B ;....NO CALL GETSHT ;....YES, 01 = SHORT IMMEDIATE JMP OP1EX OP1B: CMP AL,00100000B ;VARIABLE JNZ BADOP1 ;....NO, UNDEFINED STATE CALL GETVAR ;10 = VARIABLE OP1EX: MOV AX,VALUE ;VALUE TO [ARG1], UPDATE COUNT MOV WORD PTR ARG1,AX INC NARGS MOV BX,OFFSET OPT1 ;^ TO OP-1 DISPATCH TABLES MOV AL,OPCODE ;RESTORE OPCODE AND AX,00001111B ;ISOLATE OP ID BITS CMP AL,NOPS1 ;IF WITHIN RANGE, JB DODIS ;THE EXECUTE THE 1-OP ; *** ERROR #4: UNDEFINED 1-OP *** BADOP1: MOV AL,4 JMP GERROR ; ------------- ; HANDLE A 2-OP ; ------------- OP2: AND AX,01000000B ;2OP, ISOLATE 1st ARG BIT JNZ OP2A ;SHORT IMMEDIATE? CALL GETSHT ;....YES. 0 = SHORT IMMEDIATE JMP OP2B OP2A: CALL GETVAR ;1 = VARIABLE OP2B: MOV AX,WORD PTR VALUE ;VALUE TO [ARG1], UPDATE COUNT MOV WORD PTR ARG1,AX INC NARGS MOV AL,OPCODE ;RESTORE THE OPCODE BYTE AND AX,00100000B ;ISOLATE 2nd ARG BIT JNZ OP2C ;SHORT IMMEDIATE? CALL GETSHT ;....YES, 0 = SHORT IMMEDIATE JMP OP2D OP2C: CALL GETVAR ;1 = VARIABLE OP2D: MOV AX,WORD PTR VALUE ;MOVE VALUE TO [ARG2] MOV WORD PTR ARG2,AX INC NARGS ;UPDATE COUNT ;ENTRY FOR EXTENDED 2-OPS OP2EX: MOV BX,OFFSET OPT2 ;^ DISPATCH TABLE MOV AL,OPCODE ;RESTORE OPCODE BYTE AND AX,1FH ;ISOLATE OP ID BITS CMP AL,NOPS2 ;LEGAL 2-OP? JAE OP2F ;ERROR IF OUT OF RANGE JMP DODIS ;ELSE DISPATCH ; *** ERROR #5: UNDEFINED 2-OP *** OP2F: MOV AL,5 JMP GERROR MLOOP ENDP SUBTTL HIGH LEVEL IBMPC DEPENDENT ROUTINES PAGE ; --------------------- ; SYSTEM INITIALIZATION ; --------------------- PUBLIC SYSINI,SYS1,DSKERR SYSINI PROC MOV AH,DOSVER ; (7) GET DOS VERSION # INT 21H ; (7) MUST BE 2.0 OR HIGHER CMP AL,2 ; (7) DOS 2.0+ JGE GOODOS MOV AL,17 JMP GERROR ; DETERMINE PRESENCE OF JOYSTICK GOODOS: CALL STICK1 ; READ THE JOYSTICK PORT (OR ETHER) AND AL,0EFH ; MASK OFF BUTTON BITS CMP AL,8AH ; 8AH IS BOTTOM RIGHT (NO JOYSTICK) JZ NOSTK MOV JOYSTK,1 ; THERE IS A STICK SO USE IT! ; OPEN THE GAME FILE NOSTK: PUSH ES PUSH BX MOV AX,BIOSDATA ; SEG ADDRESS BIOS DATA AREA MOV ES,AX MOV BX,KB_FLAG ; OFFSET TO BIOS DATA FOR KEYS MOV AL,ES:[BX] ; GET BYTE OF KEYBOARD STATE MOV KYFLGS,AL ; SAVE THEM OR BYTE PTR ES:[BX],NUMLOCK ; TURN ON NUMLOCK POP BX POP ES MOV AH,CURDSK ; (6) GET DEFAULT DRIVE INT 21H ; (6) FROM DOS MOV DEFDRV,AL ; (6) SAVE DEFAULT DRIVE MOV CURDRV,AL ; (6) SAVE DEFAULT DRIVE MOV AH,CFOPENZ ;ASCIZ STRINGS MOV AL,0 ;OPEN FOR READ MOV DX,OFFSET GAMFILE INT 21H JNC SYSIN1 JMP DSKERR ; DETERMINE SPACE AVAILABLE SYSIN1: MOV GAMHNDL,AX ;SAVE THE FILE HANDLE ; DETERMINE THE SIZE OF DIP.DAT ; MOVE READ/WRITE POINTER TO THE END OF THE FILE SUB CX,CX ;SET CX:DX OFFSET = 0 SUB DX,DX MOV BX,GAMHNDL ;GET FILE HANDLE MOV AH,CFSEEK ;SELECT LSEEK FUNCTION MOV AL,2 ;USE OFFSET FROM END METHOD INT 21H ;INVOKE FUNCTION CALL ; DX:AX = FILE SIZE IN BYTES ; DETERMINE THE NUMBER OF 512 BYTE PARA'S TEST AX,1FFH ;IS THERE A PARTIAL DISK PARAGRAPH? JZ SYSIN2 ;....NO ADD AX,200H ;ADD 1 TO THE NUMBER OF PARA'S SYSIN2: SHR AX,1 ;DETERMINE NO. OF 512 BYTE PARA'S SUB AL,AL XCHG AH,AL CMP DX,0 JZ SYSIN3 ADD AL,80H SYSIN3: MOV FSIZE,AL ;SAVE IT ; DETERMINE THE AMOUNT OF MEMORY AVAILABLE FOR PAGING BUFFERS MOV BX,2 ;GET TOP OF MEMORY MOV BX,CS:[BX] ;FROM OUT OF THE PSP MOV AX,CS ;GET CODE SEG SUB BX,AX ;SUB CSEG FROM TOPMEM MOV CL,4 ;SHIFT COUNT MOV AX,OFFSET MEMBUF ;USE OFFSET TO FIGURE PARAG'S SHR AX,CL ;REDUCE THIS OFFSET TO PARA'S INC AX ;SKIP ONE PARAGRAPH SUB BX,AX ;YIELDS PARA'S AVAILABLE MOV CL,5 ;DIVIDE BY 32 SHR BX,CL ; TO GET THE NUMBER OF 512 BYTE BUFFERS ; REDUCE THE NUMBER OF BUFFERS AVAILABLE TO ACCOMODATE MEMBUFF DEC BX ;SUBTRACT 1 BUFFER FOR MEMBUF CMP BX,128 ;ABOVE 80H, WE NEED ANOTHER JL SYS1 ;WE HAVE 128 OR FEWER BUFFERS AVAILABLE DEC BX ;TAKE AWAY ANOTHER PAGE CMP BX,255 ;THIS IS THE MOST WE WILL NEED JLE SYS1 ;DON'T MODIFY IF BX LE. 255 MOV BX,255 ;DONT ALLOW MORE THAN 255 SYS1: MOV BUFFERS,BL ;SAVE THE NUMBER OF BUFFERS ; DYNAMICALLY CALCULATE THE START OF SEG0 SHL BX,1 ;GET NUMBER OF BYTES IN MEMBUF SHL BX,1 ;IT'S 4 BYTES x BX BYTES LONG ADD BX,OFFSET MEMBUF ;CALCULATE TABLE LOCATION MOV CX,4 ;REDUCE NUMBER OF BYTES IN TABLE=PARA'S SHR BX,CL ;TO DYNAMICALLY ALLOCATE SPACE MOV AX,DS ;GET A COPY OF THE CURRENT SEG ADD BX,AX ;AND CALCULATE THE NEW SEG ADD BX,10H ;ADD ONE PARAGRAPH FOR SAFETY MOV SEG0,BX ;THIS WILL BE USED FOR ES:GAME ADD BX,1000H ;ADD 64K TO CALC 2ND SEG MOV SEG1,BX ;THIS IS SEG0+64K ; INITILIZE PAGING TABLES ; INITIALIZE MEMMAP SUB AX,AX ;PRELOAD FLAGS=RESET (0) ;MEMBUF INDICES=NOT CORE RESIDENT (0) SUB BX,BX ;INITIALIZE ARRAY ROW POINTER MOV CX,00FFH ;SET UP FOR 255 ENTRIES INITMM: MOV MEMMAP[BX],AL ;RESET PRELOAD FLAG MOV MEMMAP[BX+1],AH ;MARK MEMBUF INDICE = NOT CORE RESIDENT ADD BX,2 ;NEXT OFFSET LOOP INITMM ;INITIALIZE EACH ENTRY ; DETERMINE IF THE GAME "FITS" MOV AL,BUFFERS CMP AL,FSIZE JNB SYSXIT ;IF THE GAME "FITS" FSIZE IS > 0 MOV FSIZE,0 ;ELSE FSIZE = 0 ; INITIALIZE MEMBUF MOV CL,BUFFERS ;NUMBER OF ENTRIES USED AS A LOOP CTR SUB CH,CH SUB BX,BX ;INITIALIZE OFFSET INTO TABLE MOV DH,255 ;INITIAL VALUE FOR "PREVIOUS" POINTER MOV DL,1 ;INITIAL VALUE FOR "NEXT" POINTER MOV AL,0 ;INITIAL "LOC" (ABS SEG:PAGE VALUE) INITMB: MOV MEMBUF[BX],AL ;SAVE LOC MOV MEMBUF[BX+1],0 ;V-PAGE IS THE RESPONSIBILITY OF GTVBLK MOV MEMBUF[BX+2],DH ;INITIALIZE "PREVIOUS" MOV MEMBUF[BX+3],DL ; AND "NEXT" POINTERS INC AL ;NEXT LOC INC DH ;NEXT "PREVIOUS" INC DL ;NEXT "NEXT" ADD BX,4 ;NEXT TABLE ENTRY LOOP INITMB ;REPAIR EACH MEMBUF ENTRY ;CONNECT HEAD AND TAIL DEC BX ;BACK UP TO "NEXT" COLUMN OF LAST ENTRY MOV DL,MEMBUF[BX] ;GET "NEXT" DEC DL ;SUBTRACT 1 TO GET TAIL MOV MEMBUF[BX],0 ;SPLICE TAIL TO HEAD MOV MEMBUF+2,DL ;SPLICE HEAD TO TAIL ; INITIALIZE LRU BUFFER POINTER MOV LRU,0 ; ; EXIT POINT ; ; INITIALIZE SEGMENT POINTER TO THE START OF PAGING SYSXIT: MOV BX,SEG0 ; MOV ES,BX ;EXTRA SEG ^ THE START OF PAGING CALL DISPRT CALL CSETUP ;SET UP THE CONSOLE DEVICE CALL CHKCPU ;SET THE IBMAT FLAG IF INDICATED CALL INITSND ;INITIALIZE TIMER FOR SOUND IF DEBUG CALL TRACE ; CHECK TO SEE IF TRACER IS WANTED ENDIF RET ; **** ERROR #11 **** DISK DATA FILE HANDLING ERROR ; CLOSE THE FILE DSKERR: MOV BX,GAMHNDL ;BX <= HANDLE MOV AH,3EH ;REQUEST CLOSE INT 21H ;MSDOS CALL ; AND PERFORM ERROR PROCESSING MOV AL,0BH ;SET UP ERROR RETURN CODE JMP GERROR ;AND GO TO ERROR EXIT SYSINI ENDP IF DEBUG PUBLIC TRACE TRACE PROC MOV SI,80H ; GET COMMAND LINE LODSB ; SEE COMMAND PARMS CMP AL,2 ; LOOK FOR /T JNB TRACE1 TRACE0: RET TRACE1: MOV SI,83H ; LOOK AT BYTE AND BYTE PTR [SI],5FH ; TURN OFF 32 BIT CMP BYTE PTR [SI],'T' ; TRACE? JNE TRACE0 CALL ITRACE ; YEP, INITIALIZE TRACER RET TRACE ENDP ; trace trap routines for the mme ; itrace toggles tracing & sets the interrupt. itrint traps the ; interrupt and counts the bucket for the current pc ; public itrace ;trace trap enable itrace proc near push ax push bx push dx mov dx,offset itrint mov bx,ds ;save data segment mov ax,cs mov isavecs,ax ;for compare in int routine mov ds,ax ;ds:dx is trap address mov al,1 ;trace int # mov ah,25H ;function # for set int vector int 21H mov ds,bx pop dx pop bx pushf ;get the flags pop ax xor ax,400O ;complement the trace flag push ax popf pop ax ret itrace endp ; itrint proc near push bp mov bp,sp ;stack:saved bp(0), ip(2), cs(4), flags(6) push bx push cx inc itrcnt ;wrong cs, just count it jne itrint2 inc itrcnt+2 itrint2:mov bx,[bp+2] ;pick up ip cmp bx,ioffset jb itrint1 ;dont count below cmp bx,itop ja itrint1 ;dont count above sub bx,ioffset mov cx,ishift shr bx,cl xor bh,bh ;0:255 - high pc bits add bx,bx ;byte address of a long add bx,bx inc ipcount[bx] jne itrint1 ;check wrap inc ipcount+2[bx] itrint1:pop cx pop bx pop bp iret itrint endp ptracer proc mov dx,offset t_out mov cx,0 mov ah,cfcreaz ; create the file tracer.out int 21h mov dx,offset itrcnt ; get address of vars mov cx,256*32+4 ; number of bytes to write mov bx,ax ; get handle from create mov ah,40h ; write the data int 21h mov ah,3eh ; close the data file int 21h ret ; all done ptracer endp ENDIF PUBLIC DISPRT DISPRT PROC ; DISABLE PRINT SCREEN PUSH ES MOV AX,3505H ; (B5) GET INTERRUPT VECTOR FIVE INT 21H ; (B5) (PRTSCR) FROM DOS MOV PRTOFF,BX ; (B5) SAVE OFFSET MOV PRTSEG,ES ; (B5) SAVE 'EM BOTH MOV DX,OFFSET RETEOI MOV AX,2505H ; (B5) SET INTERRUPT TO POINT TO IRET INT 21H POP ES RET DISPRT ENDP PUBLIC ENAPRT ENAPRT PROC ; (B5) REENABLE PRINT SCREEN PUSH DS MOV AX,2505H ; (B5) RESET PRTSCR MOV DX,PRTOFF ; (B5) GET OFFSET MOV BX,PRTSEG ; (B5) SET UP DS MOV DS,BX ; (B5) TO POINT TO OLD ROUTINE INT 21H POP DS RET ENAPRT ENDP RETEOI: IRET ; (B5) RETURN FROM PRINT SCREEN ; ---------------------- ; CONSOLE INITIALIZATION ; ---------------------- PUBLIC CSETUP CSETUP PROC ; INITIALIZE VIDEO ; SELECT MEDIUM RESOLUTION GRAPHICS MOV AH,0 MOV AL,4 INT 10H MOV AH,0FH ; SEE IF IT WORKED INT 10H ; BY ASKING THE VIDEO CMP AL,4 ; IT SHOULD BE AS SET JE CSET$ MOV AL,10H ; NEW ERROR EXIT JMP GERROR ; SET BACKGROUND COLOR CSET$: MOV AH,11 ;REQUEST COLOR MOV BL,0 ;SELECT BACKGROUND MOV BH,0 ; 0=BLACK INT 10H ;CALL BIOS ; SELECT COLOR PALETTE MOV AH,11 ;REQUEST COLOR MOV BL,1 ;SELECT PALETTE #1 MOV BH,1 ;0=BLACK, 1=CYAN, 2=MAGENTA, 3=WHITE INT 10H ;CALL BIOS ; CLEAR THE SCREEN TO BLACK AND RESET THE ACTIVE ICON TABLE MOV ARG1,0 ;LOAD ARG1 WITH A POSITIVE VALUE CALL GCLEAR ;DO IT CMP FSIZE,0 ; (B4) GAME FIT? JNE CSET$$ RET CSET$$: MOV AH,2 ; (B4) NOW PRINT A LOADING MESSAGE MOV DX,800H ; (B4) SOMEWHERE ON THE SCREEN MOV BH,0 INT 10H MOV AX,OFFSET WAITSTR ; (B4) GAME IS LOADING... CALL MPRNT MOV AH,2 ; (B4) AND RESET THE CURSOR TO TOP MOV DX,100H MOV BH,0 INT 10H RET CSETUP ENDP ; ------------------------------------- ; GET A BLOCK OF PRELOAD VIRTUAL MEMORY ; ------------------------------------- ; ; ENTRY: [VPAGEN] = VIRTUAL BLOCK NUMBER (RANGE 0-255) ; [LRU] = NUMBER OF LEAST RECENTLY USED BUFFER ; PUBLIC GTPBLK GTPBLK PROC CALL GTVBLK ;GET THE BLOCK ; USE [VPAGEN] TO SET [PREFLG] MOV BL,VPAGEN ;GET VIRTUAL PAGE NUMBER SUB BH,BH SHL BX,1 ;CONVERT TO WORD POINTER MOV MEMMAP[BX],1 ;SET PREFLG CMP FSIZE,0 JZ GTPBL1 RET ; USE [BUF] TO UNSPLICE THE ALLOCATED BUFFER FROM THE CIRCULAR ; LINKED LIST. NOTE, IN THE CASE OF PRELOAD, BUF CONTAINS ; THE NUMBER OF THE JUST USED BUFFER, WHICH WAS THE LRU AND IS NOW ; THE MRU. GTPBL1: MOV DL,BUF ;SAVE BUF FOR FIXING THE LRU MOV BL,DL ;USE BUF TO CHANGE "NEXT" POINTER SUB BH,BH ; IN PREVIOUS BUFFER SHL BX,1 ;4 BYTES PER TABLE ENTRY SHL BX,1 ;GET "NEXT" [AH] AND "PREVIOUS" [AL] MOV AX,WORD PTR MEMBUF[BX+2] MOV BL,AL ;USE "PREVIOUS" TO SUB BH,BH ; POINT TO THE 2ND MRU'S TABLE SHL BX,1 ;4 BYTES PER TABLE ENTRY SHL BX,1 MOV MEMBUF[BX+3],AH ;SET TO POINT FORWARD TO THE LRU MOV BL,AH ;USE "NEXT" TO SUB BH,BH ; POINT TO THE LRU'S TABLE SHL BX,1 ;4 BYTES PER TABLE ENTRY SHL BX,1 MOV MEMBUF[BX+2],AL ;SET TO POINT BACK TO 2ND MRU ; MAKING IT THE NEW MRU RET GTPBLK ENDP ; ----------------------------- ; GET A BLOCK OF VIRTUAL MEMORY ; ----------------------------- ; ; ENTRY: [VPAGEN] = VIRTUAL BLOCK NUMBER (RANGE 0-255) ; [LRU] = NUMBER OF LEAST RECENTLY USED BUFFER ; ; THROUGH USE OF A COMMON EXIT POINT ; EXIT: BUF (RANGE 0 THROUGH [BUFFERS]-1) ; LOC ; PUBLIC GTVBLK GTVBLK PROC PUSH BX ; USE [VPAGEN] TO ACCESS [PREFLG] AND [BUF] MOV BL,VPAGEN ;GET VIRTUAL PAGE NUMBER SUB BH,BH SHL BX,1 ;CONVERT TO WORD POINTER MOV AL,MEMMAP[BX+1] ;GET BUF MOV BUF,AL ;SAVE IT TEST MEMMAP[BX],1 ;TEST PREFLG ; IS THIS SECTION LOADED PRELOAD? JZ NOTPRE ;....NO ; LOADED PRELOAD ; BUF IS VALID ISIN: MOV BL,BUF ;WE CAREFULLY SAW TO IT'S VALIDITY MOV LOC,BL ;AND PUT IT WHERE THE SUN.... POP BX RET ; {CASE} NOT LOADED PRELOAD NOTPRE: CMP FSIZE,0 ;ARE WE PAGING? JZ NOTPR1 ;....YES ; NOT PAGING CMP BUF,0 ;IS IT ALREADY IN CORE? ; {CASE} IT IS ALREADY IN CORE JNE ISIN ;....YES ; {CASE} IT NOT IN CORE MOV AL,VPAGEN MOV MEMMAP[BX+1],AL ;SET UP THE BUF ENTRY IN MEMMAP MOV BUF,AL ; AND SET BUF = VPAGEN PUSH CX ;SAVE SOME MORE REGISTERS PUSH DX JMP GTVBL2 ;AND BRING IT IN NOTPR1: CMP BUF,0 ;IS IT ALREADY IN CORE? JNE INCORE ;....YES JMP NOTIN ;....NO. THEN BRING IT IN ; {CASE} IT IS IN CORE AND NOT PRELOAD INCORE: MOV BL,BUF ;IS IT THE LRU? CMP LRU,BL JNE SPLICE ;....NO ; IT IS THE LRU PAGE, SO MAKE IT THE MRU SUB BH,BH ;UPDATE THE LEAST RECENTLY USED BUFFER SHL BX,1 ;4 BYTES PER ENTRY SHL BX,1 MOV AL,MEMBUF[BX+3] ;GET "NEXT" POINTER MOV LRU,AL ;IT'S THE NEW LRU, MAKING THIS BUFFER ; THE MRU ; BUF IS VALID MOV BL,BUF MOV OLDMRU,BL ;SAVE BUF AS OLD MRU MOV LOC,BL ;AND LOC POP BX RET ; IT ISN'T THE LRU. WE HAVE TO MAKE IT THE MRU, IF IT ISN'T ; ALREADY. THE EXISTING LRU REMAINS UNCHANGED SPLICE: MOV BL,BUF CMP BL,OLDMRU ;IS THIS ALREADY THE MRU? JNE SPLIC1 ;....NO MOV LOC,BL ;AND SAVE BUF IN LOC POP BX RET ; STEP 1 - UNSPLICE THE "TOUCHED" BUFFER SPLIC1: PUSH CX PUSH DX MOV OLDMRU,BL ;SAVE BUF AS OLD MRU SUB BH,BH ; SHL BX,1 ;4 BYTES PER ENTRY SHL BX,1 MOV AX,WORD PTR MEMBUF[BX+2] ;GET "NEXT" AND "PREVIOUS" PUSH BX MOV BL,AL ;START WITH "PREVIOUS" BUFFER SUB BH,BH ; SHL BX,1 ;4 BYTES PER ENTRY SHL BX,1 MOV MEMBUF[BX+3],AH ;POINT FORWARD TO "NEXT" MOV BL,AH ;THEN "NEXT" BUFFER SUB BH,BH ; SHL BX,1 ;4 BYTES PER ENTRY SHL BX,1 MOV MEMBUF[BX+2],AL ;POINT BACK TO "PREVIOUS" ;SPLICE IN THE NEW MRU MOV DL,BUF MOV BL,LRU ;GET THE "PREVIOUS" POINTER IN LRU SUB BH,BH ; WHICH POINTS AT THE OLD MRU SHL BX,1 ;4 BYTES PER ENTRY SHL BX,1 MOV AL,MEMBUF[BX+2] ;GET "PREVIOUS" POINTER MOV MEMBUF[BX+2],DL ;MAKE LRU POINT BACK TO BUF MOV BL,AL ;GET THE "NEXT" POINTER FROM FORMER SUB BH,BH ; MRU SHL BX,1 ;4 BYTES PER ENTRY SHL BX,1 MOV AH,MEMBUF[BX+3] ;GET "NEXT" POINTER MOV MEMBUF[BX+3],DL ;MAKE IT POINT FORWARD TO BUF ; AND REPAIR BUF POP BX MOV WORD PTR MEMBUF[BX+2],AX ; BUF IS VALID JMP GTVRET ;WE'RE DONE, USE COMMON EXIT ; {CASE} IT ISN'T IN CORE ; DEALLOCATE THE LEAST RECENTLY USED BUFFER NOTIN: PUSH CX PUSH DX MOV BL,LRU ;GET LRU SO AS TO DEALLOCATE SUB BH,BH ; THE LEAST RECENTLY USED BUFFER SHL BX,1 SHL BX,1 MOV BL,MEMBUF[BX+1] ;GET THE OLD VIRTUAL PAGE NUMBER ; WE'LL USE IT TO INDEX INTO MEMMAP SUB BH,BH ; AND SET IT'S BUF ENTRY SHL BX,1 ; TO "0" MOV MEMMAP[BX+1],0 ;THIS DEALLOCATES THE BUFFER FOR THE ; OLD VIRTUAL PAGE. ; ALLOCATE MEMBUF [LRU] TO THE NEW VIRTUAL PAGE MOV BL,LRU ;GET LRU SO AS TO ALLOCATE SUB BH,BH ; THE LEAST RECENTLY USED BUFFER SHL BX,1 SHL BX,1 MOV AL,VPAGEN ;GET NEW VIRTUAL PAGE NUMBER MOV MEMBUF[BX+1],AL ;STORE IT ; SETUP THE LOAD POINT FOR THE NEW VIRTUAL PAGE PUSH BX ; SAVE BUFFER DESCRIPTOR MOV BL,AL ;USE VPAGEN AS AN INDEX SUB BH,BH ; INTO MEMBUF SHL BX,1 MOV AL,LRU ;LRU IS THE BUFFER TO USE MOV MEMMAP[BX+1],AL ; SO SAVE IT MOV BUF,AL ; AND SAVE IT FOR COMMON EXIT MOV OLDMRU,AL ;SAVE BUF AS OLD MRU POP BX ; RESTORE BUFFER DESCRIPTOR ; AND MAKE THIS BUFFER THE MRU ; SHL BX,1 ;SO WE CAN USE IT AS A QUAD POINTER MOV AL,MEMBUF[BX+3] ;GET "NEXT" POINTER MOV LRU,AL ;IT'S THE NEW LRU ; MAKING THIS BUFFER THE MRU ; INITIALIZE REGISTERS TO PAGE IN A BLOCK OF VIRTUAL MEMORY ; GET DESTINATION IN ES:BX FORM ; FIXUP ES GTVBL2: MOV BL,BUF ;GET DESTINATION BLOCK NUMBER MOV CX,SEG0 ;PRE-POINT AT SEG0 OR BL,BL ;TEST SEGMENT BIT JNS SEGAOK ;IF NO SIGN THEN IT'S SEG0 AND BL,7FH ; ELSE STRIP OFF THE SEGMENT BIT MOV CX,SEG1 ; AND POINT TO THE OTHER SEGMENT SEGAOK: MOV ES,CX ;ES POINTS TO THE CORRECT SEGMENT ; CONVERT THE DESTINATION BLOCK NUMBER TO A BYTE OFFSET MOV CL,9 ;x 512 SHL BX,CL ; ; GET VIRTUAL BLOCK NUMBER IN AL MOV AL,VPAGEN ;GET THE VIRTUAL PAGE NUMBER ; ; GET VIRTUAL BLOCK [AL] IN ES:BX ; PUSH BX ;SAVE BX PORTION OF DESTINATION ; MOVE FILE READ/WRITE POINTER (LSEEK) TO POINT TO THE PROPER BLOCK ; CALCULATE OFFSET IN BYTES AND PLACE IN CX:DX MOV CX,9 ;MULTIPLY BY 512 (RECSIZ) SHL AX,CL ;TO CONVERT BLK# TO OFFSET MOV CX,0 ;SET TO ZERO, BUT CHANGE ADC CX,0 ;TO "1" IF OFFSET > 64K MOV DX,AX ;GET LOW ORDER #BYTES FOR LSEEK ; MOVE READ/WRITE POINTER TO OFFSET IN CX:DX ; FROM THE BEGINNING OF THE FILE MOV BX,GAMHNDL ;GET FILE HANDLE MOV AH,CFSEEK ;SELECT LSEEK FUNCTION MOV AL,0 ;USE OFFSET FROM BEGINNING METHOD INT 21H ;INVOKE FUNCTION CALL ; READ THE SELECTED 512 BYTE RECORD INTO CORE LOCATION ES:BX ; TRANLATE THE ES:BX CORE LOCATION INTO DS:DX FORMAT ; RESTORE BX PORTION OF DESTINATION INTO DX POP DX ; FIXUP SEGMENT PORTION OF THE DESTINATION PUSH DS ;SAVE DS FOR LATER RESTORATION PUSH ES ;PUSH EXTRA SEGMENT POP DS ;IN ORDER TO PUT IT IN DS ; READ THE RECORD INTO DS:DX MOV CX,RECSIZ ;READ A 512 BYTE RECORD MOV AH,CRDRNDZ ;SELECT READ RANDOM RECORD INT 21H ;INVOKE FUNCTION CALL CMP AX,CX ;SET FLAGS ; RESTORE DATA SEGMENT POP DS ; COMMON EXIT POINT ; EXIT: BUF (RANGE 0 THROUGH [BUFFERS]-1) ; LOC ; GTVRET: MOV BL,BUF ;WE CAREFULLY SAW TO IT'S VALIDITY MOV LOC,BL ;AND PUT IT WHERE THE SUN.... POP DX ;RESTORE AFFECTED REGISTER(S) POP CX POP BX RET GTVBLK ENDP ; -------------- ; ERROR HANDLING ; -------------- ; ENTRY: ERROR CODE IN [AL] PUBLIC GERROR,GERR1 GERROR PROC push ax mov ah,2 mov dx,100H ; row 1, col 0 mov bh,0 ; screen 0 int 10H ; set cursor pop ax XOR AH,AH SHL AX,1 ; INDEX MOV BX,AX ; USE INDEX REG MOV BX,ERRTAB[BX] ; GET PROPER ERROR MESSAGE MOV AL,[BX] ; GET LENGTH ADD AL,ERRM ; ADD LENGTH OF STARTING STRING mov ch,al ; save total text length MOV AH,40 ; GET WIDTH OF SCREEN SUB AH,AL ; GET REMAINING SPACES SHR AH,1 ; DIVIDE IN HALF TEST AH,1 ; ODD? JZ GERR0 ; NOPE DEC AH ; YES, SUBTRACT 1 GERR0: MOV CL,AH ; GET COUNT OF SPACES add CH,AH ; SAVE right edge=left spcs+text len GERR1: CMP CL,0 ; DON'T PRINT "NO" SPACES JE GERR2 ; SKIP PRINT MOV AH,2 ; PRINT A CHAR MOV DL,' ' ; A SPACE IN FACT INT 21H ; DO IT DEC CL JMP GERR1 GERR2: MOV AX,OFFSET ERRM+1 CALL NPRNT ;PRINT IT (NO CRLF) MOV AX,BX ; GET ERROR STRING INC AX ; SKIP LENGTH BYTE CALL NPRNT ; PRINT THE ERROR STRING GERR3: CMP CH,40 ; FINISH THE LINE WITH SPACES JE GERR4 MOV DL,' ' ; PRINT A SPACE MOV AH,2 INT 21H INC CH JMP GERR3 GERR4: MOV AH,2 MOV DL,13 INT 21H MOV DL,10 INT 21H JMP FINISH ;EXIT CLEANLY GERROR ENDP ;PRINT A CARIAGE RETURN / LINE FEED PUBLIC MCRLF MCRLF PROC MOV AL,13 CALL PRTOUT MOV AL,10 CALL PRTOUT RET MCRLF ENDP ;PRINT THE CHARACTER IN [AL] PUBLIC PRTOUT PRTOUT PROC PUSH AX PUSH BX PUSH CX MOV DL,AL MOV AH,2 INT 21H POP CX POP BX POP AX RET PRTOUT ENDP ;PRINT A STRING FOLLOWED BY , PTR IN AX PUBLIC MPRNT MPRNT PROC PUSH DX MOV DX,AX MOV AH,9 INT 21H CALL MCRLF POP DX RET MPRNT ENDP ;PRINT A STRING NOT FOLLOWED BY , PTR IN AX PUBLIC NPRNT,NPR1,NPR2 NPRNT PROC PUSH BX ;SAVE BX MOV BX,AX ;STRING PTR NPR1: MOV AL,[BX] ;GET NEXT CHARACTER CMP AL,"$" ;END OF THE LINE JE NPR2 ;....YES CALL PRTOUT ;....NO. PRINT THE CHARACTER INC BX ;POINT TO THE NEXT CHARACTER JMP NPR1 ;REPEAT UNTIL END OF THE LINE NPR2: POP BX ;RESTORE BX RET NPRNT ENDP ;GET TIME TO BE USED IN NUDGING THE RANDOM NUMBER GENERATOR PUBLIC MTIME MTIME PROC PUSH CX PUSH DX MOV AH,2CH INT 21H MOV RSEED1,CX ;HRS & MIN MOV RSEED2,DX ;S's & 1/100 THS POP DX POP CX RET MTIME ENDP ;EXIT THE GAME PEACEFULLY AND RETURN TO DOS PUBLIC FINISH FINISH PROC ; INDICATE THE GAME IS OVER MOV AH,2 ; SET CURSOR POSITION MOV BH,0 ; FIRST PAGE MOV DX,1800H ; ON LINE 25 INT 10H ; POSITION IT MOV AX,OFFSET XITMSG ;POINT AT "END OF SESSION" CALL NPRNT ;PRINT IT CALL ENAPRT ; WAIT FOR ANY KEYPRESS MOV AH,CNOECHO ; (B5) READ A CHAR INT 21H ; GO BACK TO REGULAR TEXT MODE WITH A CLEAR SCREEN ; SELECT STANDARD MODE MOV AH,0 ;REQUEST MODE SET MOV AL,2 ;STANDARD MONOCHROME 80 X 25 INT 10H ;CALL BIOS FUNCTION IF DEBUG CALL PTRACER ; CALL 'C' OUTPUT OF TABLE ENDIF MOV AX,BIOSDATA ; FIX KEYBOARD MOV ES,AX MOV BX,KB_FLAG ; KEYBOARD FLAGS OFFSET MOV AL,KYFLGS ; GET ORIGINAL STATE MOV ES:[BX],AL ; STORE IT ; EXIT TO DOS MOV AH,04CH INT 21H FINISH ENDP ; -------------------------------- ; CHECK FOR FAST PROCESSOR (80286) ; -------------------------------- ; ; PURPOSE: THE SOUND ROUTINES ARE IMPLIMENTED THROUGH THE USE OF ; SOFTWARE TIMING LOOPS. THEY ARE THEREFORE SUCCEPTABLE TO ; BEING CHANGED BY FASTER PROCESSORS. ; ; IN ORDER TO PROVIDE COMPENSATION FOR THIS EFFECT, THIS ; ROUTINE IS USED TO DETERMINE IF THE CPU IS "FAST" OR "SLOW". ; ; *** NOTE *** THIS ROUTINE IS TO BE CALLED DURING COLD START ; ; INPUTS: NONE ; ; OUTPUTS: [IBMAT] = TRUE IF THIS IS A FASTER CPU ; = FALSE IF THIS IS A NORMAL CPU ; REGISTERS DESTROYED: ; AX,BX,DX ; ; EXTERNAL REFERENCES: [IBMAT], [TIMER], TRUE, FALSE, ATTIME ; PUBLIC CHKCPU,CHKCP1,CHKCP2 CHKCPU PROC ; INITIALIZE MOV IBMAT,FALSE ;PREMARK THIS AS A "SLOW" CPU SUB BX,BX ;INITIALIZE LOOP COUNTER ; TO DETERMINE SPEED CALL SYNCH ;SYNCHRONIZE TIMER MOV TIMER,DL ;SAVE 1/100 SEC COUNT ; PREPARE TO QUALIFY CHKCP1: MOV AH,2CH ;GET THE TIME UNTIL IT CHANGES INT 21H ; SO WE CAN USE THE COUNT IN BX INC BX ; TO DECIDE IF THIS IS A FAST CPU CMP DL,TIMER ;HAS THE CLOCK TICKED? JZ CHKCP1 ;....NO. LOOP UNTIL IT DOES ; PHOTO FINISH CMP BX,ATTIME ;COMPARE WITH TIME STANDARD JL CHKCP2 ; IF BX IS LESS, THEN THE CPU IS SLOW MOV IBMAT,TRUE ; ELSE IT'S FAST AND WE SO MARK IT CHKCP2: PUSH ES PUSH DI MOV AX,BIOS ; BIOS SEGMENT MOV ES,AX ; MAKE IT ADDRESSIBLE MOV DI,BIOSOFF ; GET BIOS OFFSET MOV AX,OFFSET IBMSTR ; SOURCE STRING "IBM COPR." MOV DX,9 ; LENGTH OF STRING MOV CX,200H ; WITHIN THE FIRST 1/2 K CALL CHK ; CHK BIOS FOR IBM COPR. JNC CHK_ID CHKCP3: POP DI POP ES RET CHK_ID: MOV DI,ID_OFF ; GET IBM ID BYTE OFFSET CMP BYTE PTR ES:[DI],JR_ID ; IS IT A JUNIOR JNE CHKCP3 MOV PCJR,1 ; SET FLAG FOR PCJR JMP CHKCP3 CHKCPU ENDP ; GIVEN SOURCE PTR IN DS:AX, AND A DEST PTR IN ES:DI, CHK SEARCHES ; FOR THE STRING POINTED TO BY DS:AX IN THE FIRST (CX) BYTES OF ; THE STRING POINTED TO BY ES:DI PUBLIC CHK CHK PROC CLD ; (7) CLEAR THE DIRECTION FLAG PUSH SI ; (7) SAVE THIS PUSH DX ; (7) SAVE THIS FOR RETRIES MOV BP,SP ; (7) GET A FRAME POINTER MOV SI,AX ; (7) SET UP FIRST OFFSET LUP: CMP BYTE PTR [SI],'$' ; (7) END CHAR JE MATCH ; (7) REACHING END==MATCH CMPSB ; (7) COMPARE TWO BYTES JNE RESET ; (7) RESET SOURCE IF MISMATCH DEC DX LOOP LUP ; (7) DECREMENT COUNTER AND TRY AGAIN JMP RTFAIL ; (7) OUT OF CHARS, FAILED RESET: MOV SI,AX ; (7) MOVE SOURCE PTR BACK INTO SI MOV DX,[BP] ; (7) RESTORE NUMBER OF BYTES IN STR LOOP LUP ; (7) TRY AGAIN RTFAIL: STC ; (7) FAIL BY RETURNING CARRY SET POP DX POP SI ; (7) RESTORE SI RET MATCH: TEST DX,DX ; (7) MATCH OF ENTIRE STRING JNZ RTFAIL CLC ; (7) GOT A MATCH, RETURN CARRY CLEAR POP DX POP SI ; (7) RESTORE SI RET CHK ENDP ; ------------------------ ; INITIALIZE SPEAKER TIMER ; ------------------------ ; ; PURPOSE: THIS ROUTINE INITIALIZES THE PORTION OF THE 8253 TIMER ; CHIP USED BY THE SPEAKER SYSTEM. IN PARTICULAR, IT SETS ; UP CHANNEL 2 OF THE TIMER AS A SQUARE-WAVE GENERATOR. ; ; *** NOTE *** THIS ROUTINE IS TO BE CALLED DURING COLD START ; ; INPUTS: NONE ; ; OUTPUTS: TIMER 2 ; ; REGISTERS DESTROYED: NONE ; ; EXTERNAL REFERENCES: NONE ; PUBLIC INITSND INITSND PROC PUSH AX ;SAVE REGISTERS MOV AL,0B6H ;TIMER CONTROL WORD OUT 43H,AL ;SEND IT TO THE CONTROL PORT POP AX ;RESTORE REGISTERS RET INITSND ENDP ; ---------------------- ; SET THE TONE FREQUENCY ; ---------------------- ; ; PURPOSE: THIS ROUTINE SELECTS THE FREQUENCY OF THE SQUARE-WAVE ; TONE TO THE SPEAKER. ; ; INPUTS: CX=FREQUENCY ; ; OUTPUTS: TIMER 2 ; ; REGISTERS DESTROYED: NONE ; ; EXTERNAL REFERENCES: NONE ; PUBLIC TONESET TONESET PROC PUSH AX ;SAVE REGISTERS PUSH CX MOV AL,CL ;GET LOWER BYTE OUT 42H,AL ;SEND OUT VALUE TO TIMER MOV AL,CH ;GET UPPER BYTE OUT 42H,AL ;SEND OUT VALUE TO TIMER POP CX ;RESTORE REGISTERS POP AX RET TONESET ENDP ; ------------ ; TURN ON TONE ; ------------ ; ; PURPOSE: THIS ROUTINE TURNS ON THE TIMER AND SPEAKER TO PRODUCE ; A TONE. THE FREQUENCY OF THE TONE MUST HAVE ALREADY ; BEEN SENT TO THE TIMER. ; ; INPUTS: NONE ; ; OUTPUTS: TIMER 2, AND SPEAKER ; ; REGISTERS DESTROYED: NONE ; ; EXTERNAL REFERENCES: NONE ; PUBLIC TONEON TONEON PROC PUSH AX ;SAVE REGISTERS IN AL,61H ;GET SYSTEM PORT B CONTROL DATA OR AL,3 ;TURN SPEAKER AND TIMER ON OUT 61H,AL ;SEND OUT NEW VALUE POP AX ;RESTORE REGISTERS RET TONEON ENDP ; ------------ ; TURN OFF TONE ; ------------ ; ; PURPOSE: THIS ROUTINE TURNS OFF THE TIMER AND SPEAKER ; ; INPUTS: NONE ; ; OUTPUTS: TIMER 2, AND SPEAKER ; ; REGISTERS DESTROYED: NONE ; ; EXTERNAL REFERENCES: NONE ; PUBLIC TONEOFF TONEOFF PROC PUSH AX ;SAVE REGISTERS IN AL,61H ;GET SYSTEM PORT B CONTROL DATA AND AL,0FCH ;TURN SPEAKER AND TIMER OFF OUT 61H,AL ;SEND OUT NEW VALUE POP AX ;RESTORE REGISTERS RET TONEOFF ENDP ; ----------- ; SOUND DELAY ; ----------- ; ; PURPOSE: THIS ROUTINE DELAYS FOR A SPECIFIED NUMBER OF .05 SEC ; INTERVALS. ; ; INPUTS: [CX] = NUMBER OF .05 SEC TO DELAY ; ; OUTPUTS: NONE ; ; REGISTERS DESTROYED: NONE ; ; EXTERNAL REFERENCES: NONE ; PUBLIC SNDDLY,SNDLY1,SNDLY2 SNDDLY PROC PUSH AX ;SAVE REGISTERS PUSH BX PUSH CX PUSH DX CMP CX,0 JNE SNDLY1 ; SHORTER THAN OTHERWISE POSSIBLE DURATION MOV CX,JIFCNT ;<.05 SEC DELAY REQUEST, SO WAIT A JIFFY SNDLY0: LOOP SNDLY0 JMP SNDLY3 CALL SYNCH ;SYNCHRONIZE TIMER MOV TIMER,DL ;SAVE 1/100 SEC COUNT ; TIMING LOOP SNDLY1: PUSH CX ;SAVE LOOP COUNTER SNDLY2: MOV AH,2CH ;GET THE TIME UNTIL IT CHANGES INT 21H ; MSDOS FUNCTION CALL CMP DL,TIMER ;HAS THE CLOCK TICKED? JZ SNDLY2 ;....NO. LOOP UNTIL IT DOES POP CX ;RESTORE LOOP COUNTER MOV TIMER,DL ;SAVE NEW COUNT LOOP SNDLY1 ; AND LOOP SNDLY3: POP DX ;RESTORE REGISTERS POP CX POP BX POP AX RET SNDDLY ENDP ; ----- ; SYNCH ; ----- ; ; PURPOSE: THIS ROUTINE WAITS FOR A CHANGE IN THE 1/100 SEC TIMER, TO ; INSURE THAT WE'RE AT THE START OF A NEW TIME PERIOD. ; ; INPUTS: NONE ; ; OUTPUTS: [DL] = 1/100 SEC COUNT AT SYNCH TIME ; ; REGISTERS DESTROYED: DL ; ; EXTERNAL REFERENCES: NONE ; PUBLIC SYNCH,SYNCH1 SYNCH PROC PUSH AX ;SAVE REGISTERS PUSH BX PUSH CX MOV AH,2CH ;GET THE TIME FROM THE REAL TIME CLOCK INT 21H ;MSDOS FUNCTION CALL MOV TIMER,DL ;SAVE 1/100 SEC COUNT ; SYNCHRONIZE SYNCH1: MOV AH,2CH ;GET THE TIME AGAIN, SO WE CAN INSURE INT 21H ; THAT WE ARE SYNCHRONIZED WITH THE CMP DL,TIMER ; START OF A NEW TIMING PERIOD JZ SYNCH1 ;LOOP UNTIL SYNCHRONIZED POP CX ;RESTORE REGISTERS POP BX POP AX RET SYNCH ENDP ; ---- ; FREQ ; ---- ; ; PURPOSE: THIS ROUTINE CONVERTS A FREQUENCY TO THE NUMBER REQUIRED ; BY TONESET. ; ; INPUTS: [CX] = FREQUENCY IN HZ ; ; OUTPUTS: [CX] = CONVERTED VALUE ; ; REGISTERS DESTROYED: CX ; ; EXTERNAL REFERENCES: NONE ; PUBLIC FREQ FREQ PROC PUSH AX ;SAVE REGISTERS PUSH BX PUSH DX MOV DX,12H ;UPPER PART OF NUMERATOR MOV AX,34DEH ;LOWER PART OF NUMERATOR DIV CX ;DIVIDE BY FREQUENCY MOV CX,AX ;QUOTIENT IS THE OUTPUT POP DX ;RESTORE REGISTERS POP BX POP AX RET FREQ ENDP ; ----------- ; MAKE A TONE ; ----------- ; ; PURPOSE: THIS ROUTINE MAKES A TONE OF A GIVEN FREQUENCY AND LENGTH ; ; INPUTS: [CX] = FREQUENCY IN HZ ; [DX] = DURATION IN .05 SEC INTERVALS ; ; OUTPUTS: TO THE SPEAKER AND TIMER 2 ; ; REGISTERS DESTROYED: NONE ; ; EXTERNAL REFERENCES: {TONESET}, {TONEON}, {TONEOFF}, {DELAY} ; PUBLIC TONE TONE PROC PUSH AX ;SAVE REGISTERS PUSH BX PUSH CX PUSH DX ; COMPUTE THE FREQUENCY AND SETUP THE TONE GENERATOR (TIMER 2) CALL FREQ ;CONVERT THE FREQUENCY CALL TONESET ;SET UP TIMER 2 ; TURN ON THE TONE CALL TONEON ; WAIT FOR THE DELAY TIME MOV CX,DX CALL SNDDLY ; TURN OFF THE TONE CALL TONEOFF POP DX ;RESTORE THE REGISTERS POP CX POP BX POP AX RET TONE ENDP EVEN ; ; START OF A DYNAMICALLY ALLOCATED PAGE CONTROL STRUCTURE ; PUBLIC MEMBUF MEMBUF LABEL BYTE ; MSDIP ENDP CSEG ENDS END MSDIP