SUBTTL GETBYT - LOW LEVEL FUNCTIONS PAGE + ;GET A BYTE, BLOCK-POINTER ES:AX, BYTE-POINTER ES:BX, RESULT IN CX, ;UPDATE AX & BX TO REFLECT BYTE GOTTEN ; ; GETBYT UNDERWENT SERIOUS RE-WRITING AND EVALUATION IN THE UPDATE TO USE ; ALL OF MEMORY. THE ORIGINAL ROUTINE WAS KEPT BUT CHANGES WERE MADE IN ; TWO SETS: THE FIRST TO HANDLE THE CASE IN WHICH ALL OF THE GAME IS IN ; MEMORY; AND THE SECOND IN WHICH THE GAME BUFFERS EXTEND PAST 64K AND THUS ; REQUIRING A SEGMENT SWITCH TO RETRIEVE THE BYTE. ; ; FOR THE FIRST CASE, THE BLOCK PTR IS USED TO DETERMINE THE SEGMENT IN ; WHICH THE BLOCK RESIDES, THE GAME SEGMENT IS SET ACCORDINGLY, AND THE ; BYTE IS FETCHED. ; ; THE SECOND CASE WILL CALL GETPAG TO DETERMINE WHERE THE PAGE IS LOCATED IN ; CORE.CALLING GETPAG WILL SET A VARIABLE CALLED CURSEG TO 0 OR 1 TO INDICATE ; THAT THE PAGE WE ARE TOUCHING IS IN THE FIRST OR SECOND SEGMENT. GETPAG ; RETURNS A VALID POINTER ASSUMING THAT THIS ROUTINE WILL SET ES ACCORDING TO ; CURSEG. IF CURSEG IS 0, GAMESEG (THE FIRST SEGMENT [OR ZEROTH]) WILL ; CONTAIN THE BLOCK, AND IF CURSEG IS 1, SEG1 (GAMESEG + 64K) WILL CONTAIN ; THE BLOCK. ; ; NOTE THAT ES ALWAYS CONTAINS THE VALUE GAMESEG UNLESS WE NEED TO FETCH ; OR PUT SOMETHING IN THE SECOND SEGMENT. AT THAT POINT ES IS SET TO SEG1 ; ONLY FOR THAT OPERATION. ; PUBLIC GETBYT,FETCH,GTY1A$,GTSEG,GTY1$,GTY1B$,GTY2$,GTY3$ GETBYT PROC PUSH SI ; SAVE THIS BEAR PUSH AX ;SAVE BLOCK-POINTER CMP FITS,1 ; (6) IS ALL OF THE GAME RESIDENT JZ FETCH ; (6) IT'S THERE, GO GET IT CMP AX,ENDLOD ;IS THIS A PRELOADED LOCATION? JGE GTY1$ ;NO FETCH: CMP AX,128 ; (A0) FIRST SEG IS SPECIAL CASE JB GTY1A$ ; (A0) SEGS ALREADY SET UP RIGHT SUB AX,128 ; (A0) BASIFY BLOCK NUMBER FOR SEG MOV SI,SEG1 ; (A0) SET UP TO FIND SEG MOV CURSEG,1 ; (A0) INIT THIS FOR LOOP FETCH1: CMP AX,128 ; (6) CHECK SEGMENT FOR BLOCK JB GTSEG ; (A0) GO AHEAD AND GET IT SUB AX,128 ; (A0) IS IT IN THE NEXT SEG ADD SI,1000H ; (A0) INC SEG POINTER INC CURSEG ; (A0) INCREMENT OUR SEGMENT POINTER JMP FETCH1 GTY1A$: MOV CL,9 ;YES, RECONSTRUCT POINTER SHL AX,CL ;SHIFT BLOCK POINTER BY 9 OR AX,BX ;ADD IN THE OFFSET XCHG AX,BX JMP GTY1B$ ; (A16) GET BYTE (OR WORD) GTSEG: MOV ES,SI ; (6) ADDRESS SEG1 JMP GTY1A$ ; (6) USE SEG0 LOGIC GTY1$: CALL GETPAG ;FIND THE PROPER PAGE ADD AX,BX ;POINT TO DESIRED BYTE XCHG AX,BX CMP CURSEG,0 ;(A0) CHECK SEGMENT THAT WERE LOOKING IN JZ GTY1B$ ;(A0) ITS IN 0, GET IT NORMALLY PUSH AX ; (A0) SAVE IMPORT REGISTERS PUSH BX PUSH DX MOV DX,0 ; (A0) ZERO THIS FOR MUL MOV AX,1000H ; (A0) INITIAL SEG ADDR MOV BL,CURSEG ; (A0) GET CURRENT SEGMENT NUMBER MOV BH,0 ; (A0) ZERO TOP HALF MUL BX ; (A0) SEG NUMBER * GAMESEG ADD AX,GAMESEG ; (A0) GET GAMESEG AS OFFSET MOV SI,AX ; (A0) SAVE IN SI POP DX ; (A0) RESTORES POP BX POP AX MOV ES,SI ;(6) GETPAG RETURNED RIGHT POINTER GTY1B$: CMP BX,0FFFFH ; (A16) CAN'T READ A WORD ACROSS SEG JNE GTWRD1$ MOV CL,ES:[BX] ; GET BYTE JMP GTY2$ GTWRD1$:MOV CX,ES:[BX] ; GET A WORD AND SAVE A BYTE MOV LSTGET,CH ; SAVE NEXT SEQUENTIAL BYTE MOV LSTGGD,1 ; (A16) INDICATE GOOD GET NEXT BYTE GTY2$: SUB CH,CH ;CLEAR UNWANTED BYTE & RETURN IT MOV BX,AX MOV SI,GAMESEG ; (6) MAKE SURE GAME SEG IS RESTORED MOV ES,SI ; (6) TO SEG 0 POP AX ;RESTORE BLOCK-POINTER POP SI ; RESTORE THIS INC BX ;UPDATE POINTER CMP BX,200H ;END-OF-PAGE? JNE GTY3$ ;NO SUB BX,BX ;YES, CLEAR BYTE-POINTER INC AX ;AND UPDATE BLOCK-POINTER MOV LSTGGD,0 ; (A16) INDICATE NO NEXT BYTE GTY3$: RET GETBYT ENDP ; GET THE BYTE FOLLOWING THE LAST BYTE GOTTEN BY GETBYT ; PUBLIC GETLST GETLST PROC MOV CL,LSTGET ; (A16) GET THE SAVED BYTE MOV CH,0 ; (A16) ZERO TOP HALF MOV LSTGGD,0 ; (A16) RESET FLAG INC BX ; (A16) UPDATE POINTER CMP BX,200H ; (A16) END OF PAGE? JNE GTLST2 MOV BX,0 ; (A16) UPDATE BYTE POINTER INC AX ; (A16) AND PAGE POINTER GTLST2: RET GETLST ENDP ;GET A WORD, BLOCK POINTER ES:AX, BYTE-POINTER ES:BX, RESULT IN CX PUBLIC GETWRD GETWRD PROC PUSH DX ;SAVE CALL GETBYT ;GET HIGH-ORDER BYTE PUSH CX ;SAVE IT CMP LSTGGD,1 ; (A16) FAST GET? JNZ GETWRD1 CALL GETLST ; (A16) GET LAST BYTE JMP GETWRD2 GETWRD1:CALL GETBYT ;GET LOW-ORDER BYTE GETWRD2:POP DX ;GET OTHER BYTE MOV CH,DL ;POSITION IT POP DX ;RESTORE RET GETWRD ENDP ;GET THE NEXT BYTE, RETURN IT IN AX ; THIS ROUTINE ALSO WENT UNDER SIGNIFICANT REVISION TO ACCOMODATE MORE ; PAGING SPACE. TWO CASES HAD TO BE HANDLED: ALL OF GAME IN MEMORY AND ; MORE THAN 64K OF PAGING SPACE. THE FIRST CASE REQUIRED THAT THE VARIABLE ; ZPCSEG BE SET IN NEWZPC TO INDICATE IN WHICH SEGMENT OUR ZPC BLOCK COULD ; BE FOUND. THE SECOND CASE REQUIRES THAT ZPCSEG BE SET ONLY WHEN GETPAG ; IS CALLED FROM NEWZPC AND THE PAGE THAT IT GOT WAS PUT IN THE SECOND GAME ; SEGMENT. ; PUBLIC NXTBYT,NXBA$,NXB1$ NXTBYT PROC CMP LSTNGD,1 ; (A16) HAS THE ZPC BEEN PLAYED WITH SINCE? JNE NXTBYT1 ; YES JMP NXTLST ; (A16) NOPE, GET A BYTE QUICKLY NXTBYT1:PUSH SI ;(6) PRESERVE THIS ONE TOO PUSH BX ;SAVE TEST ZPCFLG,1 ;(7n) HAS THIS PAGE SEEN FOUL PLAY? JZ NXB0$ ; (7n) NOPE CALL NEWZPC ; (7n) FIX INDEX INTO CURRENT PAGE NXB0$: MOV BX,ZPC2 ;BYTE POINTER ADD BX,CURPAG ;INDEX INTO CURRENT PAGE CMP ZPCSEG,0 ;(6) WHERE ARE WE? JZ NXBA$ ;(6) SEG0, ACT NORMALLY PUSH BX ; (A0) SAVE THIS TO USE AS INDEX PUSH DX ; (A0) SAVE THIS 'CUZ IT GETS CLOBBERED XOR BH,BH ; (A0) STARTING FROM ZERO MOV BL,ZPCSEG ; (A0) THIS VAR HAS WHAT SEG WERE IN DEC BX ; (A0) SEG 1 MULS 1000*0+SEG1 MOV AX,1000H ; (A0) FIRST SEGMENT VALUE MUL BX ; (A0) MULTIPLY BY SEGMENT NUMBER ADD AX,SEG1 ; (A0) ADD IN ADDR OF SEG1 POP DX ; (A0) RESTORE IT POP BX ; (A0) RESTORE OFFSET MOV ES,AX ;(6) INTO ES NXBA$: CMP BX,0FFFFH ; (A16) CAN'T CROSS SEG BOUNDARY JNE NXBB$ MOV AL,ES:[BX] ; (A6) THIS COULD BE CROSSING A SEG JMP NXBC$ NXBB$: MOV AX,ES:[BX] ; (A16) GET A WORD AND SWAP BYTES MOV LSTNXT,AH ; (A16) SAVE HIGH BYTE FOR NEXT NXTBYT MOV LSTNGD,1 ; (A16) SET FLAG TO INDICATE QUICK NEXT NXBC$: PUSH AX ; (A6) SO PREVENT A CRASH MOV SI,GAMESEG ;(6) GET BASE GAME SEG MOV ES,SI ;(6) BACK INTO ES INC ZPC2 ;UPDATE PC CMP ZPC2,200H ;END-OF-PAGE? JL NXB1$ ;NO MOV LSTNGD,0 ; (A16) RESET FLAG, BYTE UNRELIABLE CALL NEWZPC ;YES, UPDATE PAGE NXB1$: POP AX ;RETRIEVE BYTE SUB AH,AH ;CLEAR UNWANTED BYTE POP BX ;RESTORE POP SI RET ;AND RETURN IT NXTBYT ENDP ; ; GET THE NEXT BYTE FOLLOWING PREVIOUS NXTBYT CALL PUBLIC NXTLST NXTLST PROC MOV AH,0 ; (A16) ZERO TOP HALF MOV AL,LSTNXT ; (A16) GET BYTE PUSH AX ; (A16) SAVE THE BYTE MOV LSTNGD,0 ; (A16) GO THRU THE BIGGIE NEXT TIME INC ZPC2 ; (A16) BUMP PC CMP ZPC2,200H ; (A16) END OF PAGE? JNE NXTL1 CALL NEWZPC NXTL1: POP AX RET NXTLST ENDP ;GET THE NEXT WORD, RETURN IT IN AX PUBLIC NXTWRD NXTWRD PROC PUSH BX ;SAVE CALL NXTBYT ;GET HIGH-ORDER BYTE PUSH AX ;SAVE IT CALL NXTBYT ;GET LOW-ORDER BYTE POP BX ;GET HIGH-ORDER BYTE MOV AH,BL ;POSITION IT POP BX ;RESTORE RET ;RETURN THE WORD NXTWRD ENDP ;GET AN ARGUMENT GIVEN ITS TYPE IN AX PUBLIC GETARG GETARG PROC DEC AX ;EXAMINE ARGUMENT JL NXTWRD ;0 MEANT LONG IMMEDIATE JE GETAR2 ;1 MEANT SHORT IMMEDIATE GETAR1: CALL NXTBYT ;2 MEANT VARIABLE, GET THE VAR CMP AX,0 ;STACK? JNE GETVAR ;NO, JUST GET THE VAR'S VALUE POPZ AX ;YES, POP THE STACK RET GETAR2: JMP NXTBYT GETARG ENDP ;GET VALUE OF A VARIABLE, VAR IN AX, VALUE RETURNED IN AX PUBLIC GETVAR GETVAR PROC CMP AX,0 ;STACK? JNE GTV1$ ;NO POPZT AX ;YES, GET TOP-OF-STACK RET GTV1$: PUSH BP ;SAVE CMP AX,16 ;LOCAL? JGE GTV3$ ;NO DEC AX ;YES, POINT TO PROPER STACK ELEMENT SHL AX,1 MOV BP,ZLOCS SUB BP,AX MOV AX,[BP] ;AND GET IT GTV2$: POP BP ;RESTORE RET GTV3$: SUB AX,16 ;GLOBAL, POINT TO PROPER GLOBAL TABLE ELEMENT SHL AX,1 ADD AX,GLOTAB MOV BP,AX ;AND GET IT GTAWRD A,[BP] JMP GTV2$ GETVAR ENDP ;UPDATE VALUE OF A VARIABLE, VAR IN AX, NEW VALUE IN BX PUBLIC PUTVAR PUTVAR PROC CMP AX,0 ;STACK? JNE PTV1$ ;NO PUSHZT BX ;YES, UPDATE TOP-OF-STACK RET PTV1$: CMP AX,16 ;LOCAL? JGE PTV2$ ;NO PUSH BP ;SAVE DEC AX ;YES, POINT TO PROPER STACK ELEMENT SHL AX,1 MOV BP,ZLOCS SUB BP,AX MOV [BP],BX ;AND UPDATE IT POP BP ;RESTORE RET PTV2$: SUB AX,16 ;GLOBAL, POINT TO PROPER GLOBAL TABLE ELEMENT SHL AX,1 ADD AX,GLOTAB XCHG AX,BX ;AND UPDATE IT PTAWRD [BX],A RET PUTVAR ENDP ;RETURN VAL IN AX TO LOCATION SPECIFIED BY NEXTBYTE ;DESTROYS BX, BUT IS USUALLY CALLED AT END OF TOP-LEVEL FUNCTION PUBLIC BYTVAL BYTVAL PROC SUB AH,AH ;THIS ENTRY FOR BYTE VALUE TO CLEAR HIGH BYTE JMP PUTVAL BYTVAL ENDP PUBLIC PUTVAL PUTVAL PROC MOV BX,AX ;NORMAL ENTRY CALL NXTBYT ;GET VAR TO USE CMP AX,0 ;STACK? JNE PUTVAR ;NO, GO STORE VALUE PUSHZ BX ;YES, PUSH ONTO STACK RET ;AND RETURN PUTVAL ENDP ;PREDICATE HANDLERS TRUE & FALSE ;DESTROYS REGISTERS, BUT ARE ONLY CALLED FROM END OF TOP-LEVEL FCNS PUBLIC PFALSE,PTRUE PFALSE PROC SUB BX,BX ;PREDICATE WAS FALSE, CLEAR FLAG JMP PPRED PFALSE ENDP PTRUE PROC MOV BX,1 ;PREDICATE WAS TRUE, SET FLAG JMP PPRED PTRUE ENDP PUBLIC PPRED PPRED PROC CALL NXTBYT ;GET FIRST (OR ONLY) PREDICATE JUMP BYTE TEST AX,80H ;NORMAL POLARITY PREDICATE? JE PPR1$ ;NO, LEAVE FLAG ALONE INC BX ;YES, INCREMENT FLAG PPR1$: TEST AX,40H ;ONE-BYTE JUMP OFFSET? JE PPR2$ ;NO AND AX,0FF3FH ;YES, CLEAR SPECIAL BITS JMP PPR3$ PPR2$: AND AX,0FF3FH ;CLR SPECIAL BITS FROM HIGH-ORDER OFFSET BYTE MOV CX,AX ;HIGH-ORDER BYTE CALL NXTBYT ;GET LOW-ORDER BYTE MOV AH,CL ;MOVE IN HIGH-ORDER BITS TEST AX,2000H ;IS NUMBER NEGATIVE (14-BIT 2'S COMP NUMBER)? JE PPR3$ ;NO OR AX,0C000H ;YES, MAKE 16-BIT NUMBER NEGATIVE PPR3$: DEC BX ;TEST FLAG JE PPR6$ ;WAS 1, THAT MEANS DO NOTHING CMP AX,0 ;ZERO JUMP? JNE PPR4$ ;NO JMP OPRFAL ;YES, THAT MEANS DO AN RFALSE PPR4$: DEC AX ;ONE JUMP? JNE PPR5$ ;NO JMP OPRTRU ;YES, THAT MEANS DO AN RTRUE PPR5$: DEC AX ;ADJUST OFFSET MOV LSTNGD,0 ; (A16) NXTBYT WILL BE LONG ADD ZPC2,AX ;ADD TO PC CMP ZPC2,200H ; (A15) DID WE CROSS A BOUNDARY? JB PPR6$ ; (A15) DO NEWZPC ONLY IF PAGE IS CROSSED JMP NEWZPC ;AND UPDATE ZPC STUFF PPR6$: RET PPRED ENDP ;SPLIT BYTE-POINTER IN AX TO BLOCK-POINTER IN AX & BYTE OFFSET IN BX PUBLIC BSPLTB BSPLTB PROC MOV BX,AX XCHG AL,AH ;EXTRACT BLOCK BITS SHR AX,1 AND AX,7FH ;CLEAR UNWANTED BITS AND BX,1FFH ;CLEAR ALL BUT BYTE OFFSET BITS RET BSPLTB ENDP ;SPLIT WORD-POINTER IN AX TO BLOCK-POINTER IN AX & BYTE-OFFSET IN BX PUBLIC BSPLIT BSPLIT PROC MOV BX,AX MOV AL,AH ;EXTRACT BLOCK BITS SUB AH,AH ;CLEAR UNWANTED BITS SUB BH,BH ;CLEAR ALL BUT WORD OFFSET BITS SHL BX,1 ;CONVERT TO BYTE OFFSET RET BSPLIT ENDP ;SPLIT QUAD-POINTER IN AX TO BLOCK-POINTER IN AX & BYTE-OFFSET IN BX PUBLIC BSPLITQ BSPLITQ PROC PUSH CX ; (A0) SAVE AND USE FOR SHIFT COUND MOV CL,7 ; (A0) ZIP BIT SHIFT WOULD LOSE BITS MOV BX,AX SHR AX,CL ; (A0) RIGHT SHIFT 7 TO GET BLOCK POINTER AND BX,7FH ;CLEAR ALL BUT WORD OFFSET BITS ADD BX,BX ADD BX,BX POP CX ; (A0) RESTORE CX RET BSPLITQ ENDP SUBTTL OBJECT HACKERS PAGE + PUBLIC OBJLOC,NXTPRP ;GIVEN OBJ NUMBER IN AX, RETURN OBJ LOCATION IN AX OBJLOC PROC PUSH BX ;MULTIPLY BY OBJECT LENGTH PUSH DX ; (A0) PRESERVE FROM SIGN EXTENSION MOV BX,OBJLEN ; (A0) GET LENGTH OF OBJECT FOR MULTIPLY MUL BX ; (A0) USE BX AS MULTIPICAND POP DX ; (A0) RESTORE DX POP BX ;RESTORE ADD AX,OBJTAB ;INDEX INTO OBJECT TABLE ADD AX,PROPDEF ; (A0) SKIPPING DEFAULT PROPERTY TABLE RET OBJLOC ENDP ;GIVEN POINTER TO A PROPERTY IN BX!, UPDATE IT TO POINT TO NEXT PROP NXTPRP PROC PUSH AX ;SAVE PUSH CX ;SAVE MOV AL,ES:[BX] ; GET PROPERTY IDENTIFIER INC BX ; (A0) ADVANCE PROP BYTE POINTER TEST AL,80H ; (A0) PROPERTY LENGTH GREATER THAN 2? JNZ NXTPR1 ; (A0) YES TEST AL,40H ; (A0) NO, PROPERTY LENGTH 2? JZ NXTPR2 ; (A0) NO, PROPERTY LENGTH 1 INC BX ; (A0) UPDATE THE POINTER NXTPR2: INC BX ; (A0) UPDATE THE POINTER POP CX POP AX RET ; NXTPR1: MOV AL,ES:[BX] ; (A0) GET SECOND PROPERTY LENGTH BYTE AND AL,MASK PROPNUM ; (A0) MASK OFF HIGH BITS INC BX ; (A0) UPDATE POINTER XOR AH,AH ; (A0) CLEAR THIS FOR SAFETY ADD BX,AX ; (A0) ADD IN LENGTH OF PROPERTY POP CX ;RESTORE POP AX ;RESTORE RET NXTPRP ENDP