Files
erkyrath.infocom-zcode-terps/ibm/zstrings.ezp
Andrew Plotkin b642da811e Initial commit.
2023-11-16 18:19:54 -05:00

413 lines
12 KiB
Plaintext

SUBTTL STRING FUNCTIONS
PAGE +
;OUTPUT A ZSTR, BLOCK-POINTER IN AX, BYTE-POINTER IN BX
;RETURN UPDATED POINTER
PUBLIC PUTSTR
PUTSTR PROC
PUSH SI ;SAVE
PUSH CX ;SAVE
PUSH DX ;SAVE
PUSH DI ;SAVE
PUSH BP ;SAVE
SUB DX,DX ;TEMP CS STARTS AT 0
SUB DI,DI ;PERM CS STARTS AT 0
PTS1$: CALL GETWRD ;GET NEXT STRING WORD
MOV SI,CX
PUSH AX ;SAVE POINTER & COPY OF STRING WORD
PUSH BX
PUSH SI
MOV CX,3 ;3 BYTES IN WORD
PTS2$: PUSH SI ;SAVE CURRENT BYTE (IN LOW-ORDER POSITION)
MOV BP,CX ;SHIFT TO NEXT BYTE
MOV CL,5
SAR SI,CL
MOV CX,BP
LOOP PTS2$ ;LOOP UNTIL DONE
MOV CX,3 ;RETRIEVE THE 3 BYTES
PTS3$: POP SI ;GET NEXT BYTE
AND SI,1FH ;CLEAR UNWANTED BITS
CMP DX,0 ;IN WORD MODE?
JGE PTS4$ ;NO {WAS BPL, CHECK}
SAL SI,1 ;YES, CALCULATE WORD OFFSET
ADD SI,WRDTAB ;POINT INTO WORD TABLE
ADD SI,WRDOFF ;USING PROPER 32-WORD BLOCK
GTAWRD A,[SI] ;POINT TO WORD STRING
CALL BSPLIT ; SPLIT IT
CALL PUTSTR ;AND PRINT IT
JMP PTS15$ ;CONT. WHERE WE LEFT OFF WITH TEMP CS RESET
PTS4$: CMP DX,3 ;CS 3 SELECTED (ASCII MODE)?
JL PTS6$ ;NO, NORMAL CS
JNE PTS5$ ;NO, BUT WE ARE IN ASCII MODE
XCHG DL,DH ;SHIFT SOME BITS HIGH TO MAKE NUMBER LARGE
OR DX,SI ;SAVE HIGH-ORDER ASCII BITS HERE
JMP PTS16$ ;GO GET NEXT BYTE
PTS5$: AND DX,3 ;EXTRACT PREVIOUSLY SAVED HIGH-ORDER BITS
MOV BP,CX ;POSITION THEM
MOV CL,5
SAL DX,CL
MOV CX,BP
OR DX,SI ;OR IN LOW-ORDER BITS
MOV AX,DX
JMP PTS14$ ;GO PRINT THE CHARACTER
PTS6$: CMP SI,6 ;SPECIAL CODE?
JL PTS9$ ;YES, SPACE, WORD, OR SHIFT
CMP DX,2 ;MIGHT ALSO BE SPECIAL IF IN CS 2
JNE PTS8$ ;BUT WE'RE NOT
CMP SI,7 ;CRLF?
JE PTS7$ ;YES
JG PTS8$ ;NO, NOT ASCII MODE, EITHER?
INC DX ;YES IT IS, SWITCH TO ASCII MODE
JMP PTS16$ ;AND GO GET NEXT BYTE
PTS7$: CALL NEWLIN ;CRLF REQUESTED, DO A NEWLINE
JMP PTS15$
PTS8$: MOV AX,DX ;NORMAL CHARACTER, GET CS
MOV BP,26 ;CALCULATE OFFSET FOR THIS CS
MUL BP
ADD AX,SI ;ADD IN CHARACTER OFFSET (+6)
SUB AX,6 ;CHARACTER OFFSET
MOV BX,OFFSET ZCHRS ;GET THE CHARACTER FROM CONVERSION VECTOR
XLAT ZCHRS
JMP PTS14$ ;GO PRINT IT
PTS9$: CMP SI,0 ;IS IT A SPACE?
JNE PTS10$ ;NO
MOV AX," " ;YES, GO PRINT A SPACE
JMP PTS14$
PTS10$: CMP SI,3 ;IS IT A WORD?
JG PTS11$ ;NO, MUST BE A SHIFT
OR DX,8000H ;SWITCH TO WORD MODE FOR NEXT BYTE
DEC SI ;CALCULATE WORD-TABLE BLOCK OFFSET
MOV BP,CX ;64 BYTES IN A BLOCK
MOV CL,6
SHL SI,CL
MOV CX,BP
MOV WRDOFF,SI ;SAVE IT AND LOOP
JMP PTS16$
PTS11$: SUB SI,3 ;CALCULATE NEW CS
CMP DX,0 ;TEMPORARY SHIFT (FROM CS 0)?
JNE PTS12$ ;NO
MOV DX,SI ;YES, JUST SAVE NEW TEMP CS
JMP PTS16$
PTS12$: CMP SI,DX ;IS THIS THE CURRENT CS?
JE PTS13$ ;YES, DO A PERM SHIFT TO IT
SUB DX,DX ;OTHERWISE, PERM SHIFT TO CS 0
PTS13$: MOV DI,DX ;TEMP & PERM CS'S ARE SAME NOW
JMP PTS16$
PTS3A$: JMP PTS3$ ;DUMMY FOR NON-SHORT LOOP
PTS14$: CALL PUTCHR ;PRINT THE CHARACTER
PTS15$: MOV DX,DI ;RESET TEMP CS TO PERM CS
PTS16$: LOOP PTS3A$ ;NEXT BYTE
POP SI ;RESTORE POINTERS & ORIGINAL STRING WORD
POP BX
POP AX
CMP SI,0 ;END-OF-STRING?
JL PTS1A$ ;YES, CLEAN UP & RETURN UPDATED POINTER
JMP PTS1$ ;NO, GET NEXT WORD
PTS1A$: POP BP ;RESTORES
POP DI
POP DX
POP CX
POP SI
RET
PUTSTR ENDP
;GIVEN AN ASCII CHARACTER IN AX, RETURN THE CHARACTER SET # IN AX
PUBLIC CHRCS
CHRCS PROC
CMP AX,0 ;IS THIS A NULL?
JNE CCS1$ ;NO
MOV AX,3 ;YES, RETURN DUMMY CS NUMBER
RET
CCS1$: PUSH BX ;SAVE
MOV BX,OFFSET ZCHRS ;POINT TO CONVERSION VECTOR
CCS2$: INC BX ;FOUND THE CHARACTER?
CMP AL,[BX-1]
JE CCS3$ ;YES
CMP BYTE PTR [BX],0 ;NO, END OF STRING?
JNE CCS2$ ;NO, CONTINUE LOOP
MOV AX,2 ;YES, CALL IT CS 2
JMP CCS5$
CCS3$: SUB BX,OFFSET ZCHRS ;FIND CHARACTER POSITION
SUB AX,AX ;START WITH CS 0
CCS4$: SUB BX,26 ;EVERY 26 CHARACTERS IS A NEW CS
JLE CCS5$ ;DONE
INC AX ;INCREMENT CS # & CONTINUE LOOP
JMP CCS4$
CCS5$: POP BX
RET
CHRCS ENDP
;GIVEN AN ASCII CHARACTER IN AX, RETURN ZSTR BYTE VALUE IN AX
PUBLIC CHRBYT
CHRBYT PROC
PUSH BX ;SAVE
MOV BX,OFFSET ZCHRS ;POINT TO CHARACTER CONVERSION TABLE
CHB1$: INC BX ;FOUND THE CHARACTER?
CMP AL,[BX-1]
JE CHB2$ ;YES
CMP BYTE PTR [BX],0 ;NO, END OF STRING?
JNE CHB1$ ;NO, CONTINUE LOOP
SUB AX,AX ;YES, RETURN ZERO FOR FAILURE
JMP CHB4$
CHB2$: SUB BX,OFFSET ZCHRS-5 ;ADJUST POINTER SO FIRST CHARACTER IS 6
MOV AX,BX
CHB3$: CMP AX,32 ;SUBTRACT MULTIPLES OF 26 UNTIL BASE CODE
JL CHB4$
SUB AX,26
JMP CHB3$
CHB4$: POP BX ;RESTORE
RET
CHRBYT ENDP
;CONVERT UP TO 9 ASCIZ CHARS POINTED TO BY DS:AX
;TO A 3-WORD ZSTR RETURNED IN AX & BX & CX
PUBLIC ZWORD
ZWORD PROC
PUSH SI ;SAVES
PUSH DX
PUSH DI
PUSH BP
MOV SI,AX ;CHARACTER STRING POINTER
SUB DI,DI ;CS STARTS AT 0
MOV CX,9 ;MAKE 9 ZSTR BYTES
ZWD1$: INC SI ;GET NEXT CHARACTER
MOV BL,[SI-1]
CMP BL,0
JNE ZWD3$ ;NOT END-OF-STRING
MOV AX,OFFSET PADCHR;AT END-OF-STRING, PAD WITH PAD CHARACTER
ZWD2$: PUSH AX ;SAVE A PAD BYTE
LOOP ZWD2$ ;LOOP UNTIL DONE
JMP ZWD6$ ;THEN GO FORM ZWORD
ZWD3$: MOV AX,BX
CALL CHRCS ;FIND THE CS NUMBER FOR THIS CHAR
CMP AX,0 ;CS 0?
JE ZWD4$ ;YES
ADD AX,3 ;NO, CALCULATE TEMP SHIFT BYTE
PUSH AX ;SAVE THE SHIFT BYTE
DEC CX ;REDUCE BYTE COUNT
JE ZWD6$ ;DONE
ZWD4$: MOV AX,BX ;FIND THE PROPER BYTE VALUE FOR THIS CHAR
CALL CHRBYT
CMP AX,0 ;IN NORMAL CS'S?
JNE ZWD5$ ;YES
MOV AX,6 ;NO, USE ASCII SHIFT
PUSH AX
DEC CX ;DONE YET?
JE ZWD6$ ;YES
MOV AX,BX ;NO, SAVE HIGH-ORDER ASCII BITS
MOV BP,CX
MOV CL,5
SAR AX,CL
MOV CX,BP
PUSH AX
DEC CX ;DONE YET?
JE ZWD6$ ;YES
AND BX,1FH ;NO, SAVE LOW-ORDER ASCII BITS
MOV AX,BX
ZWD5$: PUSH AX ;SAVE THIS BYTE
LOOP ZWD1$ ;LOOP UNTIL ZWORD FULL
ZWD6$: MOV BP,SP ;BUILD ZWORD WORDS FROM 6 SAVED BYTES
MOV AX,[BP+16] ; (A0) MORE ON STACK NOW
MOV CL,5
SHL AX,CL
OR AX,[BP+14] ; (A0)
SHL AX,CL
OR AX,[BP+12] ; (A0)
MOV BX,[BP+10] ; (A0)
SHL BX,CL
OR BX,[BP+8] ; (A0)
SHL BX,CL
OR BX,[BP+6] ; (A0)
MOV DX,[BP+4] ; (A0) GET LAST WORD INTO DX
SHL DX,CL ; (A0)
OR DX,[BP+2] ; (A0)
SHL DX,CL ; (A0)
OR DX,[BP] ; (A0)
OR DX,8000H ;SET END-OF-STRING BIT IN SECOND WORD
MOV CX,DX ; (A0) SAVE THIRD WORD IN REG
ADD SP,18 ; (A0) FLUSH STACK
POP BP ;RESTORES
POP DI
POP DX
POP SI
RET
ZWORD ENDP
SUBTTL TERMINAL I/O
PAGE +
;
; QUEUE CHARACTER IN AX FOR OUTPUT
;
PUBLIC PUTCHR,PTC$,PTC0,PTC00,PTC0A,PTC0B,PTC0C,PTC0B
PUBLIC PTC0C,PTC0C1,PTC0D,PTC0E,PTC0F,PTC0G,PTC1,PTC2
PUBLIC PTC3,PTC4,PTC4A,PTC5,PTC6,PTC7,PTC8
PUTCHR PROC
TEST BUFBIT,1 ; (A0) ARE WE BUFFERING OUTPUT
JNZ PTC0 ; ....YES
CMP RDIR,1 ; (A0) SEND CHAR TO A TABLE?
JZ PTC$ ; ....YES
; {CASE} DIRECT OUTPUT
JMP MTTYO ; ....NO. OUTPUT TO THE CONSOLE DEVICE
; {CASE} OUTPUT TO A TABLE
PTC$: PUSH BX ; (A0) USE AS A PTR
MOV BX,RTABLE2 ; (A0) GET OFFSET INTO TABLE
MOV ES:[BX],AL ; (A0) "PUTB" THE CHAR
INC RTABLE2 ; (A0) INCREMENT THE OFFSET
INC RDIROUT ; (A0) INC THE COUNT OF CHAR'S OUTPUT
POP BX
RET
; {CASE} BUFFERED OUTPUT
PTC0: PUSH BP
MOV BP,CHRPTR ;GET THE CURRENT POSITION IN THE BUFFER
CMP BP,ENDBUF ;ARE WE AT THE END OF THE BUFFER?
; JNE PTC7 ; (A13) ....NO
JNE PTC00 ; (A13) ....NO
JMP PTC0G ; (A13) ....YES
; WE'RE NOT AT THE END OF THE BUFFER
PTC00: CMP CHRCNT,0 ; (A13) HAVE WE FILLED THE OUTPUT LINE?
JE PTC0A ; (A13) ....YES
JMP PTC7 ; (A13) ....NO
; THE OUTPUT LINE IS FULL, BUT THE BUFFER ISN'T
PTC0A: PUSH AX ; (A13) SAVE THE CHARACTER
PUSH BX ; (A13) AND SOME REGISTERS
PUSH SI
; LOOK BACK THROUGH THE OUTPUT BUFFER FOR A SPACE CHARACTER
; IN ORDER TO BREAK UP THE LINE AT THE LAST COMPLETE WORD
MOV BX,CHRPTR ; (A13) START AT CHRPTR
MOV SI,OFFSET OUTBUF ; (A13) UNTIL REACHING THE BUFFER HEAD
PTC0B: DEC BX ; (A13) BACKUP
CMP BYTE PTR [BX]," " ; (A13) HAVE WE FOUND A SPACE CHAR?
JE PTC0D ; (A13) ....YES
CMP BX,SI ; (A13) NO. ARE WE AT THE BUFFER HEAD?
JNE PTC0B ; (A13) ....NO. KEEP TRYING
; PRINT THE BUFFER FOLLOWED BY A CRLF.
PTC0C: MOV BYTE PTR [BX],0 ; (A13) WE WANT A CRLF
PRINT OUTBUF ; (A13) PRINT THE BUFFER
MOV CHRPTR,SI ; (A13) RESET TO THE BUFFER START
MOV CHRCNT,BUFLEN ; (A13) RESET THE OUTPUT LINE COUNTER
MOV BP,SP ; (A13)
CMP BYTE PTR 4 [BP]," " ; (A13) IS THE NEW CHARACTER A SPACE?
JNE PTC0C1 ; (A13) ....NO. LEAVE IT ALONE
MOV WORD PTR 4 [BP],0 ; (A13) ....YES. MAKE IT A NUL
PTC0C1: JMP PTC6
; WE FOUND A SPACE IN THE OUTPUT BUFFER
PTC0D: CMP BX,SI ; (A13) WAS IT AT THE BUFFER HEAD?
JE PTC0C ; (A13) ....YES
MOV BYTE PTR [BX],0 ; (A13) ....NO. MAKE IT A NUL
; PRINT THE BUFFER
PRINT OUTBUF ; (A13)
; INITIALIZE REGISTERS FOR THE BLOCK MOVE
MOV AX,CHRPTR ; (A13) POINT TO THE NEXT POSITION
INC BX ; (A13) AND THE 1ST NON OUTPUTED CHAR
; MOVE THE CHARACTERS THAT HAVEN'T BEEN OUTPUTED TO THE BEGINNING
; OF THE OUTPUT BUFFER
PTC0E: CMP BX,AX ; (A13) DONE?
JE PTC0F ; (A13) ....YES
MOV BP,AX ; (A13) SAVE AX
MOV AL,[BX] ; (A13) GET THE CURRENT CHARACTER
MOV [SI],AL ; (A13) MOVE IT
MOV AX,BP ; (A13) RESTORE AX
INC BX ; (A13) NEXT SOURCE
INC SI ; (A13) NEXT DESTINATION
JMP PTC0E ; (A13) LOOP UNTIL WE'VE MOVED THEM ALL
PTC0F: MOV AX,ENDBUF ; (A13) COMPUTE CHARACTERS REMAINING
SUB AX,SI ; (A13) IN THE OUTPUT LINE
MOV CHRCNT,AX ; (A13) AND SAVE IT
JMP PTC5 ; (A13)
; {SUBCASE} THE OUTPUT BUFFER IS FULL
PTC0G: PUSH AX ;SAVE THE CHARACTER
PUSH BX ; AND SOME REGISTERS
PUSH SI
; LOOK BACK THROUGH THE OUTPUT BUFFER FOR A SPACE CHARACTER
; IN ORDER TO BREAK UP THE LINE AT THE LAST COMPLETE WORD
MOV BX,ENDBUF ;START AT ENDBUF
MOV SI,OFFSET OUTBUF ;UNTIL REACHING THE BUFFER HEAD
PTC1: DEC BX ;BACKUP
CMP BYTE PTR [BX]," " ;HAVE WE FOUND A SPACE CHAR?
JE PTC3 ;....YES
CMP BX,SI ;....NO. ARE WE AT THE BUFFER HEAD?
JNE PTC1 ;....NO. KEEP TRYING
; SOMEHOW WE REACHED THE BUFFER HEAD WITHOUT FINDING A SINGLE SPACE
; CHARACTER IN THE ENTIRE BUFFER. ALL WE CAN DO IS TO ASSUME THAT
; THE ENTIRE STRING IS TO BE PRINTED FOLLOWED BY A CRLF.
PTC2: PRINT OUTBUF ;PRINT THE BUFFER FOLLOWED BY CRLF
MOV CHRPTR,SI ;RESET TO THE START OF THE BUFFER
MOV BP,SP
CMP BYTE PTR 4 [BP]," " ;LOOK AT THE CHARACTER JUST RECEIVED
JNE PTC6 ;IF IT'S NOT A SPACE, LEAVE IT ALONE
MOV WORD PTR 4 [BP],0 ; ELSE, MAKE IT A NUL
JMP PTC6
; WE FOUND A SPACE IN THE OUTPUT BUFFER
PTC3: CMP BX,SI ;WAS IT AT THE BUFFER HEAD?
JE PTC2 ;....YES
MOV BYTE PTR [BX],0 ;....NO. MAKE IT A NUL
; PRINT THE BUFFER
PRINT OUTBUF
; INITIALIZE REGISTERS FOR THE BLOCK MOVE
MOV AX,ENDBUF ;POINT TO THE END OF THE BUFFER
INC BX ;POINT TO THE 1ST NON OUTPUTED CHAR
; MOVE THE CHARACTERS THAT HAVEN'T BEEN OUTPUTED TO THE BEGINNING
; OF THE OUTPUT BUFFER
PTC4: CMP BX,AX ;HAVE WE REACHED THE END OF THE BUFFER?
JE PTC4A ;....YES
MOV BP,AX ;SAVE AX
MOV AL,[BX] ;GET THE CURRENT CHARACTER
MOV [SI],AL ;MOVE IT
MOV AX,BP ;RESTORE AX
INC BX ;NEXT SOURCE
INC SI ;NEXT DESTINATION
JMP PTC4 ;LOOP UNTIL WE'VE MOVED THEM ALL
PTC4A: MOV AX,ENDBUF ; (A13) COMPUTE CHARACTERS REMAINING
SUB AX,SI ; (A13) IN THE OUTPUT LINE
MOV CHRCNT,AX ; (A13) AND SAVE IT
PTC5: MOV CHRPTR,SI ;NEXT CHARACTER INPUT POSITION
PTC6: POP SI ;RECOVER THOSE REGISTERS
POP BX
POP AX ; AND THE CURRENT CHARACTER
CMP AX,0 ;EOL?
JE PTC8 ;....YES. IGNORE IT
PTC7: MOV BP,CHRPTR ;GET THE INPUT POSITION
MOV [BP],AL ;STORE THE CHARACTER
INC CHRPTR ;NEXT POSITION
DEC CHRCNT ; (A13) AND ONE LESS IN OUTPUT LINE
PTC8: POP BP
RET
PUTCHR ENDP
;GO TO NEW LINE, OUTPUTTING CURRENT BUFFER
NEWLIN PROC
PUSH BX ;SAVE
MOV BX,CHRPTR ;END LINE AT CURRENT POINT
MOV BYTE PTR [BX],0
MOV CHRPTR,OFFSET OUTBUF ;RESET CHARACTER POINTER
MOV CHRCNT,BUFLEN ; (A13) RESET THE OUTPUT LINE COUNTER
POP BX ;RESTORE REGISTER
PRINT OUTBUF ; AND OUTPUT LINE
RET
NEWLIN ENDP