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

527 lines
14 KiB
Plaintext

SUBTTL GAMEOPS
PAGE
;
; VERIFY
;
; OP VERIFY HAS BEEN GIVEN A SPLIT PERSONALITY. THE CASE THAT THE ENTIRE
; GAME FITS INTO MEMORY CALLS ON OPVR2$ WHICH READS THE GAME IN TWO 64K
; READS. THE SECOND ES SEGMENT IS USED SO THAT THE SECOND READ WILL RESTORE
; THE GAME TO ITS ORIGINAL STATE. 64K IS READ IN AND CHECKSUMED AND THEN
; THE REMAINDER OF THE GAME IS READ BACK IN AND CHECKSUMED.
;
; THE ORIGINAL VERIFY FORCES THE PRELOAD TO BE LOADED AGAIN BY SETTING THE
; ENDLOD POINTER TO ZERO. THIS TRICKS GETBYT INTO THINKING THAT NO PAGES
; ARE LOCKED ALREADY IN CORE AND FORCES A CALL TO THE PAGING ROUTINES.
;
;
PUBLIC OPVERI
OPVERI PROC
CALL GAMOPEN ; (7q) PUT THE DISK BACK IN
PRINT IDSTR
CMP FITS,1 ; (6) WHOLE GAME IN???
JNZ OPVR1$
JMP OPVR2$
OPVR1$: MOV AX,ES:[PLENTH] ; (7q) GET END BYTE ADDR
XCHG AH,AL ; (7q) FLOP BYTES
MOV ZPCFLG,1 ; (7n) SET MUNGED ZPC FLAG
PUSH SI
PUSH DI
PUSH ENDLOD
CALL BSPLITQ
MOV SI,AX
; SUB BX,2 ; (A7) A FUDGE FACTOR (B1 wrong ehb)
MOV DI,BX
CALL TBLINI ; (7n) REINITIALIZE THE PAGE TABLE
MOV BX,CURTAB
SUB BX,2 ; (A0) BACKUP TO BLK NUMBER
MOV AX,CURBLK
MOV [BX],AX
MOV AX,0FFFFH ; (A0) DWORDIFY REF TIMES
MOV [BX+2],AX ; (A0)
MOV [BX+4],AX ; (A0)
MOV AX,0
MOV BX,64
MOV DX,0
MOV ENDLOD,0
OPVR1: PUSH SI
PUSH DI
PUSH DX
CMP LSTGGD,1 ; (A16) DON'T STRUGGLE
JNE OPVRA$
CALL GETLST ; (A16) GET LAST GETBYT VALUE
JMP OPVR$$
OPVRA$: CALL GETBYT ; GET A BYTE FROM SEG1
OPVR$$: POP DX
POP DI
POP SI
ADD DX,CX ; SUM IT
CMP AX,SI
JNE OPVR1
CMP BX,DI
JNE OPVR1
MOV AX,ES:[PCHKSM] ; REMOVED SEG OVERRIDE
XCHG AH,AL
POP ENDLOD
POP DI
POP SI
OPVR4: CMP AX,DX
JE OPVR2
JMP PFALSE
OPVR2: JMP PTRUE
;
; VERIFY FOR FITS==1 OR ALL OF GAME IS IN MEMORY
;
OPVR2$: PUSH SI
PUSH DI
PUSH CX
PUSH DS ; (6) ADDRESS SEGMENT 1
MOV BX,GAMHNDL ; (6) READ FROM GAME FILE
MOV SI,GAMESEG ; (6) THROUGH DS
MOV DS,SI ; (6) SO THAT WE OVERLAY GAME
MOV CX,0 ; (6) MOVE FILE PTR TO BEGINNING
MOV DX,64 ; (6) SKIP FIRST 64 BYTES
MOV AL,0 ; (6) METHOD OF SEEKING
MOV AH,CFSEEK ; (6) SEEK FUNCTION
INT 21H ; (6) DO IT
MOV CX,03FC0H ; (7n) READ 16K AT A TIME
MOV DX,0C000H ; (6) OFFSET INTO SEGMENT
MOV AH,CRDRNDZ ; (6) READ FUNCTION
INT 21H ; (6) DO IT
CMP AX,CX ; (6) DID WE GET IT ALL
JNE OPVR5 ; (6) DIE PAINFULLY
XOR DX,DX ; (7n) ZERO THE CHECKSUM
CALL CHKSUM ; (6) DO A CHKSUM
;
OPVR6: MOV DI,DX ; (7n) SAVE CHECK SUM
MOV DX,0C000H ; (7n) BUFFER SPACE
MOV CX,4000H ; (7n) READ 16K EACH TIME
MOV AH,CRDRNDZ ; (7n) DO A RANDOM READ
INT 21H ; (7n) DO IT
CMP AX,CX ; (7n) LAST READ?
JNE OPVR8
MOV DX,DI ; (7n) RESTORE CHECKSUM
CALL CHKSUM
JMP OPVR6
OPVR8: MOV AX,ES:[PLENTH] ; (7n) GET FILE LENGTH IN WORDS
XCHG AH,AL ; (7n) INTO AX
MOV DX,0 ; (7n) ZERO THIS FOR DIVIDE
MOV CX,1000H ; (A7) 4K quadbytes per 16K bytes
DIV CX ; (7n) number of quadbytes out of 4K
SHL DX,1 ; (A7) shift twice to get bytes
SHL DX,1 ; (7n) in partial block of 16K
; SUB DX,2 ; (A7) FUDGE FACTOR (B0 Bull!-ehb)
MOV AX,DX ; (7n) DX SHOULD HAVE REM BYTES
MOV DX,DI ; (7n) RESTORE CHECKSUM
CALL CHKSUM
;
PUSH DX
MOV CX,0 ; (7n) SEEK TO 48K INTO FILE
MOV DX,0C000H ; (7n) TO GET OVERWRITTEN PART
MOV AH,CFSEEK ; (7n) FUNCTION IS SEEK
MOV AL,0 ; (7n) TO OFFSET FROM BEGINNING
INT 21H ; (7n) DO IT
MOV DX,0C000H ; (7n) NOW REFILL SEG WITH ORIG
MOV CX,4000H ; (7n) READ 16K IN
MOV AH,CRDRNDZ ; (7n) FUNCTION READ
INT 21H
POP DX
POP DS ; (6) RESTORES
POP CX
POP DI
POP SI
MOV AX,ES:[PCHKSM] ; (6) GET CHKSUM
XCHG AH,AL
JMP OPVR4
;
CHKSUM: MOV CX,AX ; (7n) GET NUMBER ACTUALLY READ
XOR AX,AX ; (6) ZERO THIS
MOV SI,0C000H ; (7n) 48K INTO THE BUFFER
JCXZ OPVR7 ; (B0) check for 0 before loop
OPVR3: LODSB ; (6) GET A BYTE
ADD DX,AX ; (6) ADD IT ALL UP
LOOP OPVR3
OPVR7: RET
;
OPVR5: FATAL FTL7 ; GAME FILE READ ERROR
OPVERI ENDP
;
; SAVE AND RESTORE HAVE BEEN MODIFIED TO USE DOS 2.0 FILE NAMES. THIS
; ALLOWS THE USER TO SAVE AND RESTORE TO ANY DISK DRIVE AND ANY SUBDIRECTORY.
; FILE NAMES ARE READ FROM THE CONSOLE AND TRANSFERRED INTO THE SAVFILE
; VARIABLE WITH NO PARSING OTHER THAN A CHECK FOR A DISK DRIVE SPECIFIER.
; IF THE NAME IS TYPED INCORRECTLY, THEN DOS RETURNS AN ERROR WHICH WE
; REPORT ACCORDINGLY.
;
; RESTORE
;
PUBLIC OPREST
OPREST PROC
MOV DSKDIR,0 ;INDICATE RESTORE (READ)
JMP OSV0$
OPREST ENDP
;
; SAVE
;
PUBLIC OPSAVE
OPSAVE PROC
MOV DSKDIR,1 ;INDICATE SAVE (WRITE)
;
;COMMON ENTRYPOINT FOR SAVE AND RESTORE
;
PUBLIC OSV0$,OSVD1,OSVD2,OSVD3,OSVD4A,OSVPSX,OSVOPW
PUBLIC OSVOPDF,OSVPS,OSVT0,OSV4X,OSV4Y
; PUBLIC OSVCC,OSV1C,OSV2C
PUBLIC OSV4,OSVPRR
; DO A DISK RESET IN CASE THEY WANT TO CHANGE DISKS
; UNFORTUNATELY WE HAVE TO ASSUME A 1 DRIVE SYSTEM AT THIS POINT
OSV0$: CMP FITS,1 ; (n) ARE WE ALL THERE
JZ OSV0A ; (n) YES, NO CLOSE
MOV AH,CFCLOSZ ; (7s) CLOSE THE GAME FILE
MOV BX,GAMHNDL ; (7s) FOR REINSERTION
INT 21H
MOV AH,CDRESET ; (K6) WILL RESET FLUSH BUFFERS?
INT 21H ; (K6) HOPE SO...
; PRINT "FILENAME (DEFAULT IS drv:filename.ext):"
; PRINT "FILENAME (DEFAULT IS drv:"
OSV0A: PRINT SAV0 ;PRINT "INSERT SAVE DISK THEN ENTER FILE NAME."
PRINT SAV1 ;PRINT "FILE NAME (DEFAULT IS "
TEST DRVFLG,1 ; HAS USER SPECIFIED A DRIVE
JNZ OSVD1 ; YES, USE USER DEFAULT DRIVE
MOV AL,CURDRV ;AL <= DRIVE NO.
ADD AL,65 ;....NO. ADD 65 TO GET ASCII CHARACTER
CALL MTTYO ;PRINT DRIVE B, C, OR D
MOV AL,":" ;PRINT ":"
CALL MTTYO
; PRINT "filename"
OSVD1: PUSH SI ; (6) SAVE THIS
MOV SI,OFFSET SAVFILE ; (6) POINTER TO SAVE FILENAME (6)
MOV CX,SI ; (6) SAVE POINTER
CLD ; (6) SET DIR INCREMENT
OSVD2: LODSB ; (6) GET A BYTE INTO AL
TEST AL,AL ; (6) CHECK IF NULL
JZ OSVD3 ; (6) EXIT THIS LOOP
CALL MTTYO ; (6) OUTPUT IT
JMP OSVD2
OSVD3: SUB SI,CX ; (6) CALCULATE NUMBER OF CHARS
MOV CX,SI ; (6) GET NUMBER BACK INTO CX
POP SI ; (6) RESTORE THIS
; PRINT " ): "
PRINT SAV2 ;PRINT " ): "
CMP SCPL,50 ;ARE MAX CHARS/LINE >50?
JG OSVD4A ;....YES
CALL MCRLF ;....NO. PRINT A <cr><lf>
; GET LOSER'S SAVE FILENAME FROM THE KEYBOARD
OSVD4A: MOV BX,OFFSET INBUF ;^ KEYBOARD INPUT BUFFER
MOV AL,63 ;(6) COULD BE A WHOLE PATH
MOV BYTE PTR [BX],AL ;SET UP BUFFER TO TAKE 15 CHARACTERS
MOV DX,BX ;DX ^ BUFFER
MOV AH,CRDLIN ;READ A LINE FROM THE KEYBOARD
INT 21H
; PARSE THE FILENAME
CALL SAVESAV ; (7o) PRESERVE THE SAVE NAME
PUSH SI
PUSH DI
PUSH CX ; SAVE THIS
PUSH ES
PUSH DS
POP ES
LEA SI,INBUF[1] ; POINT TO NUM CHARS READ
LODSB ; GET NUM
TEST AL,AL ; ANY CHARS READ
JZ LOOK
MOV CL,AL ; STORE IT
XOR CH,CH ; CLEAR TOP HALF OF COUNTER
MOV DI,OFFSET SAVFILE ; (6) DI ^FCB
REPZ MOVSB ; TRANSFER THE STRING IF ANY
MOV AL,0 ; (6) GET A NULL
STOSB ; (6) DROP IT IN AT END OF STRING
LOOK: TEST SCRFLG,1 ; (7p) IS SCRIPTING ON?
JZ NOSCR ; (7p) NO SCRIPTING
CALL SCRSAVE ; (7p) SCRIPT THE SAVE FILE NAME
NOSCR: MOV SI,OFFSET SAVFILE ; (6) IS THERE A ':'
INC SI
LODSB ; (6) CHECK IT
CMP AL,':' ; (6) IF SO, FIX DEFDRV
JNZ NODRV ; (6) NOPE...
MOV DRVFLG,1 ; (6) SET FLAG
MOV AL,SAVFILE ; (7n) GET DRIVE LETTER
AND AL,5FH ; (7n) UPPERCASIFY
SUB AL,'A' ; (7n) BASIFY
CMP AL,CURDRV ; (7n) DON'T CHANGE IF UNNECESSARY
JZ DRV
MOV DL,AL ; (7n) SET UP FOR CHGDSK
MOV AH,CSELDSK ; (7n) SELECT DISK IN DL
INT 21H
MOV AH,CURDSK ; (7n) CHECK IF IT WORKED
INT 21H
MOV CURDRV,AL ; (7n) SAVE IT
JMP DRV
USEDEF: MOV SKPDRV,1 ; (7n) DON'T OUTPUT A DRIVE
NODRV: MOV DRVFLG,0
DRV: POP ES
POP CX
POP DI ;RESTORE'M
POP SI
CALL MCRLF ; (7) SCROLL PROPERLY
; OPEN THE FILE IF OP RESTORE
CMP DSKDIR,1 ;OP SAVE?
JE OSVOPW ;....YES
MOV DX,OFFSET SAVFILE ; 6) ....NO. OP RESTORE. DX ^ FCB
MOV AH,CFOPENZ ; (6) SELECT DOS OPEN FUNCTION
MOV AL,0 ; (6) FILE OPEN FOR READING
INT 21H ;OPEN THE FILE
JC OSVPSX ; (6) ....NO. FILE NOT FOUND
JMP OSVPS ;....YES
;
; **** ERROR **** [File Not Found]
;
OSVPSX: MOV AX,OFFSET ERR1 ;^ "SAVE FILE NOT FOUND" MSG
JMP OSVPER ;ERROR EXIT
; CREATE THE FILE IF OP SAVE
OSVOPW: MOV DX,OFFSET SAVFILE ; (6) ^ FCB
MOV CX,0 ; (6) NO SPECIAL ATTRIBUTE
MOV AH,CFCREAZ ; (6) SELECT DOS CREATE FUNCTION
INT 21H
JC OSVOPDF
JMP OSVPS ;....YES
;
; **** ERROR **** [Directory Full]
;
OSVOPDF:MOV AX,OFFSET ERR4 ;....NO. ^ "NO ROOM IN DIRECTORY" MSG
JMP OSVPER ;HOP TO ERROR EXIT
;
; WE'VE GOT AN OPEN FILE (WHICH WE CREATED IF IT WAS A SAVE FUNCTION)
; AND ARE READY TO EITHER READ (RESTORE) OR WRITE (SAVE) AS REQUIRED
;
; THE BELOW CODE GRIMLY EXPLAINS THE FORMAT OF A SAVE FILE. BASICALLY,
; FROM WHAT I CAN TELL, THE LOCALS AND VITALS ARE PUSHED ONTO THE ZSTACK
; AS WELL AS THE ZPC AND THE ZSTACK POINTER. THIS INFORMATION IS FIRST
; WRITTEN TO DISK. THEN THE REST OF PURBOT IS WRITTEN TO DISK. THIS CODE
; ALSO WORKS FOR A RESTORE. SO THE SAVE FILE CONSISTS OF FIRST ZIP INFO
; SAVED ON DISK FOLLOWED BY THE IMPURE PART OF THE GAME CODE.
;
OSVPS: XCHG DI,SP
PUSH ZPC1 ; PUSH VITALS ONTO ZSTACK
PUSH ZPC2
PUSH ZLOCS
PUSH ZORKID
SUB SP,OFFSET STKBOT ; (7v) RELATIVISE THE POINTER
MOV STKBOT,SP ; PUT DEST. INDEX INTO STACKBOTTOM
MOV SP,DI ; RESTORE SP
MOV SAVHNDL,AX ; SAVE FILE HANDLE
PUSH ES
MOV AX,SS ; FLOP STACK SEG AND EXTRA SEG
MOV ES,AX ; ES<=SS
MOV DX,OFFSET STKBOT ; CORE LOCATION
MOV CX,LSTACK*2 ; (6) DO TRANSFER HERE
MOV BX,SAVHNDL ; (6) GET HANDLE
MOV AH,CRDRNDZ ; SETUP FOR A READ
ADD AH,DSKDIR ; MAKE IT A WRITE IF NECESSARY
INT 21H ;
CMP AX,400H ; DID WE GET IT ALL
PUSHF ;SAVE RETURN CODE
MOV DI,SP ;DI ^ CURRENT ZSTACK POSITION
MOV SP,STKBOT ;SP <= ZSTACK POINTER
ADD SP,OFFSET STKBOT ; (7v) RELATIVISE THIS VALUE
POP AX ; POP OFF AND COMPARE FOR ZORKID
CMP AX,ZORKID ;ID OK?
JE OSVT0 ;....YES
MOV GAMEIN,0 ; (7v) INDICATE THAT THE GAME IS LOST
FATAL FTL4 ;....NO. FATAL ERROR
OSVT0: POP ZLOCS ;RESTORE LOCALS AND ZIP PROGRAM COUNTERS
POP ZPC2
POP ZPC1
XCHG DI,SP ;RESTORE MACHINE SP AND ZSTACK POINTER
POPF ;RESTORE REGS
POP ES
JNE OSV4Y ;TRANSFER WAS UNSUCCESSFUL
;
; THE OPEN/CREATE FILE WAS SUCCESSFUL, AND SO WAS THE BLOCK TRANSFER
; SO TRANSFER THE REST
;
OSV4X: MOV CX,ES:[PPURBT] ; (K3) MAYBE THIS IS WRONG
XCHG CH,CL ; (K3) XCHANGE BYTES
MOV AH,CRDRNDZ ; (K1) FUNCTION IS READ (OR WRITE)
ADD AH,DSKDIR ; (K1) ADD IN WRITE STUFF
MOV BX,SAVHNDL ; (K1) READ IT FROM HERE
MOV DX,0 ; (K1) READ IT RIGHT IN TO SEG0
PUSH DS ; (K1) SAVE DS
PUSH ES ; (K1) FLOP BYTES
POP DS ; (K1) ES IS NOW DOS ADDRESSIBLE
INT 21H ; (K1) DO THE SAVE/RESTORE
POP DS ; (K1) RESTORE DS
CMP AX,CX ; (K1) DID WE GET IT ALL?
OSV4Y: PUSHF ;SAVE DOS RETURN CODE
MOV AH,CFCLOSZ ; (6) CLOSE THE FILE
MOV BX,SAVHNDL ; (6) WITH THIS HANDLE
INT 21H
POPF ;RESTORE DOS RETURN CODE
JNE OSV4 ;ERROR EXIT
;
;ELSE EVERYTHING WORKED SUCCESSFULLY
;AND WE CAN GO BACK TO WHAT WE WERE
;DOING..........
;
MOV AH,SCRFLG ; (J2) FIX SCRFLG
OR AH,STSBIT ; (A9) TURN ON STATUS LINE REFRESH BIT
XOR AL,AL ; (J2)
MOV WORD PTR ES:[PFLAGS],AX ; (J1) SCRIPT SHOULD MATCH SCRFLG
CMP FITS,1 ;(n) do not need to replace disk if in mem
JZ n1
CALL GAMOPEN ; (7s) OPEN GAME FILE
MOV DL,CURDRV ; (7n) RETURN TO SAVE DISK
MOV AH,CSELDSK ; (7n) AND SELECT IT FOR OPEN
INT 21H
N1: CALL NEWZPC ;GET A NEW PC
MOV AX,2 ; (A0) RETURN 1 FOR SAVE OR 2 FOR RESTORE
SUB AL,DSKDIR ; (A0) SUBTRACT ONE FOR SAVE
JMP PUTVAL ; (A0)
;
; **** ERROR **** [Unable to Read File]
;
OSV4: MOV AX,OFFSET ERR6 ;^ "Read of SAVE file failed."
CMP DSKDIR,1 ;OP SAVE?
JNE OSVPRR ;....NO
MOV AH,CFCLOSZ ; (6) ....YES. CLOSE IT
MOV BX,SAVHNDL ; (6) GET HANDLE FOR CLOSE
INT 21H
;
; **** ERROR **** [Diskette Full]
;
MOV DX,OFFSET SAVFILE ; (7n) DELETE THE PIECE OF A FILE
MOV AH,CFDELEZ ; (7n) SO AS NOT TO CONFUSE
INT 21H
MOV AX,OFFSET ERR5 ;^ "No room on diskette for SAVE file."
OSVPRR: JMP OSVPER
OPSAVE ENDP
;
; ERROR EXIT R/W operations
;
OSVPER PROC
CALL MPRNT ;PRINT ERROR MESSAGE
CALL ZRESTOR ; (7o) RESTORE OLD SAVE NAME
CMP FITS,1 ; (7v) HAVE WE CLOSED THE GAME FILE
JZ OSVPR1 ; (7v) SKIP REOPEN
CALL GAMOPEN ; (7v) OPEN IT
OSVPR1: MOV AH,SCRFLG ; (A13) RESTORE SCRIPTING STATE
OR AH,STSBIT ; (A9) TURN ON STATUS LINE REFRESH BIT
XOR AL,AL ; (J1) FIX SCRIPT ON BAD RESTART (RANDOM)
MOV ES:[PFLAGS],AX
MOV AX,0 ; (A0) RETURN A FALSE
JMP PUTVAL ; (A0)
OSVPER ENDP
;
PUBLIC SAVESAV,ZRESTOR,SCRSAVE
SCRSAVE PROC
PUSH AX ; (7p) SAVES
PUSH SI
PUSH DX
MOV SI,OFFSET SAVFILE ; (7p) POINTER TO SAVE
SCRS0: LODSB ; (7p) GET A CHARACTER
TEST AL,AL ; (7p) LOOK FOR A NULL
JNZ SCRS1 ; (7p) DIE ON A NULL
POP DX
POP SI
POP AX
RET ; (7p) RETURN
SCRS1: MOV DL,AL ; (7p) GET CHARACTER INTO DL
MOV AH,CPROUT ; (7p) FOR DOS"IE" OUTPUT
INT 21H ; (7p) PRINT IT
JMP SCRS0
SCRSAVE ENDP
SAVESAV PROC
PUSH DI ; (7o) SAVES
PUSH SI
PUSH AX
PUSH ES
MOV AX,DS ; (7o) SET UP REGS
MOV ES,AX
MOV AL,CURDRV ; (7o) SAVE CURRENT DRIVE SPEC
MOV LASTDRV,AL ; (7o) IN CASE OF FAILURE
MOV AL,DRVFLG ; (7o) TEST DRIVE FLAG
MOV LSTDFLG,AL ; (7o) IN CASE OF FAILURE
MOV SI,OFFSET SAVFILE ; (7o) SETUP SOURCE AND DESTINATION
MOV DI,OFFSET LASTSAV
COPYLP: LODSB ; (7o) GET A BYTE
STOSB ; (7o) STORE A BYTE
TEST AL,AL ; (7o) STOP ON A NUL
JNZ COPYLP ; (7o) CONTINUE THE COPY
POP ES
POP AX ; (7o) RESTORES
POP SI
POP DI
RET
SAVESAV ENDP
;
;
ZRESTOR PROC
PUSH DI ; (7o) SAVES
PUSH SI
PUSH AX
PUSH ES
MOV AX,DS ; (7o) SET UP REGS
MOV ES,AX
MOV AL,LSTDFLG ; (7o) GET LAST DRIVE FLAG
MOV DRVFLG,AL ; (7o) AND RESTORE IT
MOV AL,LASTDRV ; (7o) GET ORIGINAL DRIVE SPEC
CMP AL,CURDRV ; (7o) HAS IT CHANGED
JZ ZRES1
MOV DL,AL ; (7o) SELECT DISK
MOV AH,CSELDSK ; (7o) TO CORRECT
INT 21H
MOV CURDRV,DL ; (7o) UPDATE THIS BABY
ZRES1: MOV DI,OFFSET SAVFILE ; (7o) SETUP SOURCE AND DESTINATION
MOV SI,OFFSET LASTSAV
ZRESLP: LODSB ; (7o) GET A BYTE
STOSB ; (7o) STORE A BYTE
TEST AL,AL ; (7o) STOP ON A NUL
JNZ ZRESLP ; (7o) CONTINUE THE COPY
POP ES
POP AX ; (7o) RESTORES
POP SI
POP DI
RET
ZRESTOR ENDP
;
; OP RESTART
;
PUBLIC OPRSTT
OPRSTT PROC
MOV BP,CHRPTR
MOV BYTE PTR DS:[BP],0 ;FORCE OUT THE BUFFER
PRINT OUTBUF
JMP RESTRT ;JUMP TO RESTART ADDRESS
OPRSTT ENDP
PUBLIC OPQUIT
OPQUIT PROC
JMP FINISH
OPQUIT ENDP