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

429 lines
12 KiB
Plaintext

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