.TITLE ZRSX - RSX specific code for ZEMU. .IDENT /V1.37/ ;++ ; ZRSX - The ZEMU RSX specific code. ; (c) 2000, 2001, 2002, 2004, 2005 by Johnny Billquist ; ; History: ; 00-07-30 BQT Initial coding started. ; Y1.0 00-08-26 21:00 BQT First release. ; Y1.1 00-09-04 23:00 BQT Added timeout functionality. ; Y1.2 00-09-07 19:00 BQT Improved timeout reader. ; Y1.3 00-09-08 19:45 BQT Improved the search for games. ; Code will now try to be smart if no dir was ; specified. It then looks first in current ; dir, and then in [INFOCOM]. ; Y1.4 00-09-09 18:30 BQT Bugfix. The last valid address in task was ; incorrectly deduced from .LIMIT, which ; won't handle if we installed the task ; with an increment in place... ; V1.5 00-09-16 12:00 BQT Changed ZEXIT to ABORT. ; Made version official. ; V1.6 00-10-07 13:00 BQT Changed default directory handling to accept ; devices and logical names. ; The same is now also true for SAVE/RESTORE ; filenames. ; V1.7 00-10-08 18:00 BQT Corrected code to work as multiuser task. ; V1.8 00-10-27 14:45 BQT Added scripring file. ; V1.9 00-12-04 13:00 BQT Added handling of CSI character. ; V1.10 00-12-29 00:30 BQT Changed read function to use ast, and read all. ; It will also detect ^C. ; Added exit AST. ; V1.11 00-12-30 04:30 BQT Added a call to INPEND from INPINI, since ; we can actually get two calls to INPEND in ; a row, where the second one is without timer. ; V1.12 01-01-04 23:00 BQT Bugfix. Terminal must be in full duplex for ; ZEMU to work. ; Added GIN$ to drop privilege once game file ; is opened. This allows us to play game files ; we are not allowed to read. ; V1.13 01-01-08 02:00 BQT Bugfix. After the default file names, ; we need to ensure we are on an even address. ; V1.14 01-02-19 03:00 BQT Added /ID switch. ; V1.15 01-02-26 15:30 BQT Added one more param to SAVOPN. ; V1.16 01-03-13 02:00 BQT Moved the attributes to set on terminal to ; r/w section, since RT: requires them there. ; V1.17 01-05-30 02:00 BQT Bugfix. If an error occured, the AST handler ; didn't issue a new read. ; V1.18 01-09-20 01:00 BQT Added /LI and /HE switches. ; V1.19 02-12-29 00:30 BQT Added check to truncate game names to 9 ; characters if longer. ; Improved random seed generator. ; V1.20 03-08-14 20:00 BQT Changed cache handling. ; V1.21 03-08-16 19:00 BQT Added games in region. ; V1.22 04-08-31 11:10 BQT Added GAMINI for initializations just before ; starting play. ; V1.23 05-05-03 22:00 BQT Added call to SCRZAP at ^L ; V1.24 05-05-24 12:15 BQT Changed terminal input to be unsolicited asts. ; V1.25 05-07-26 01:00 BQT Improved INPINI to always update cursor ; position. ; V1.26 05-08-01 15:00 BQT Added /CO switch to enable color cabability. ; V1.27 05-08-31 14:30 BQT Small improvements in HELP text. ; V1.28 06-01-31 21:00 BQT Changed output QIO to QIOW, since program ; might overwrite buffer on output. ; V1.29 06-04-26 16:00 BQT Bugfix. Size of game is in F.HIBK, not ; F.EFBK. The latter is where EOF is, for ; appending, and it can point beyond existing ; blocks. ; HIBK can also point beyond EFBK, but we ; don't care about that, since we don't ; play with the RSX EOF pointers in ZEMU games. ; HIBK is always the last actually allocated ; block for the file. ; V1.30 06-05-03 19:15 BQT Added debug printouts. ; V1.31 07-10-11 03:00 BQT Changed protection word for regions. ; V1.32 07-10-17 13:30 BQT Added dynamic check if fastmap is available. ; V1.33 07-11-08 00:30 BQT Added SST vectors. ; V1.34 08-09-04 16:30 BQT Changed INPINI calling sequence. ; V1.35 09-04-23 14:30 BQT Bugfix. When using large memory model, memory ; mapping was done without privs, meaning it ; failed when it shouldn't. ; V1.36 09-04-24 13:50 BQT Improved common region handling. ; If task have privileges, it now creates the ; region owned by [1,54], and with [RD,D,,] ; If task don't have privs, the region is ; created owned by the task uic, and with ; protection taken from the file, but only ; copying the read bits. ; V1.37 09-05-06 11:30 BQT Added full wildcard handling for listing ; games. ;-- SAVEXT=^RZSG ; This extension is used if DSAV don't have one. SCREXT=^RLOG ; Extension for transcriptions. DEFPGS=1024. ; Default small page size DEFMIN=2 ; Default minimum APRs for large cache RBSIZ=64. ; Size of rcv buffer. .MCALL GCMLB$, GCML$, CSI$, CSI$1, CSI$4 ; Command handling. .MCALL CSI$SW, CSI$SV, CSI$ND .MCALL EXIT$S, EXST$S, EXTK$S, DIR$, GTSK$C ; Task and general stuff .MCALL QIOW$S, QIOW$C, QIOW$ ; I/O .MCALL WTSE$, WTLO$, CLEF$, SETF$ ; Event flags. .MCALL MRKT$, CMKT$C, GTIM$C ; Time. .MCALL SREX$C ; Exit AST. .MCALL SVTK$S ; SST vectors for task .MCALL ENAR$S, DSAR$S, ASTX$S ; AST handling. .MCALL FCSMC$ ; File handling. .MCALL GIN$S ; General information .MCALL RDBDF$, WDBDF$, RDBBK$, WDBBK$ ; Memory handling .MCALL GMCX$S, CRRG$S, ATRG$S, CRAW$S, MAP$ .MCALL TFEA$S FCSMC$ D.RSX=100000 .MACRO TSTIO SB,ADDR,?LBL CMPB SB,#IS.SUC BEQ LBL MOV SB+2,-(SP) MOV SB,-(SP) JSR PC,ADDR LBL: .ENDM TSTIO .PSECT $DPB$.,RO,D ; The DPBs of $C forms are read only. FSRSZ$ 2 CSI$ RDBDF$ WDBDF$ .INCLUDE /ZMAC/ .SBTTL R/W data area. .PSECT DATA,D,RW GCM: GCMLB$ ,ZEM,,CMDLUN CSI: .BLKB C.SIZE ; ARGBLK: .BLKW 10. VERBLK: .BLKW 2 DATBLK: .BLKW 10 ; ; "Simple" structure to extract file protection info. ; RATBUF: .WORD 0,RATPRO ; 2 args. File and attribute buffer ptr. RATPRO: .BYTE -2,2 ; Attribute buffer. Attribute and length. .WORD FILPRO ; Pointer to where to place info. .BYTE 0,0 ; End of list. FILPRO: .WORD 0 ; File protection word, finally. ; ZRDB: RDBBK$ ZWDB: WDBBK$ ;' SFNAM: ; Search filename temp storage. TSKPAR: .BLKW 20 ; File descriptors. FDB: FDBDF$ FDRC$A FD.RWM FDBK$A ,512.,,DBEFN,DBISB FDOP$A DBLUN,CSI+C.DSDS,DFNAM SAVFDB: FDBDF$ FDRC$A FD.RWM FDBK$A ,512.,,SAVEFN,SAVISB FDOP$A SAVLUN,CSI+C.DSDS,DFNAM2 SCRFDB: FDBDF$ FDAT$A R.VAR,FD.CR FDRC$A FDOP$A SCRLUN,CSI+C.DSDS,DFNAM3 FDBF$A SCREFN ; Time information buffer. MFNAM: ; Match filename temp storage TMBUF: .BLKW 10 ; Area for GTIM$ directive. ; Parse information MASK: .WORD 0 ; Mask telling which switches are present. PGSIZ: .WORD DEFPGS ; Default page size MINAPR: .WORD DEFMIN ; Minimum # of APRs for large cache type. SYSFLG: .WORD 0 ; Flag to set if system game. MAPF: .WORD 0 ; Flag telling if we're using fastmap. ; ; Default file names... ; GAMSIZ==FDB+F.HIBK+2 ; Game file size DFNAM2: NMBLK$ ; Default game name with dir and all. ; Also used as default name for SAVE/RESTORE GAMR50==DFNAM2+N.FNAM ; Game name in R50 GAMID==GAMR50 ; Game ID info (junk info in save file) DFNAM3: NMBLK$ ; Default script file name. ; ; I/O status blocks. ; DBISB: .BLKW 2 SAVISB: .BLKW 2 IOSB: .BLKW 2 FNDCNT: .WORD 0 ; Counter for found files. FMTPTR: .WORD 0 ; ; Data area for .BLK macro arguments... ; BLK: .WORD 0 ; In RSX, blocks are 32 bit values. ; therefore we have leading zero. BPAGE:: .WORD 0 ; These values are written BADDR:: .WORD 0 ; by ZMAC macros... BCNT:: .WORD 0 ; Therefore they must exist. ; ; Different variables. ; FREEPT: .WORD 0 ; Pointer to free area on memory. END:: .WORD 0 ; Pointer to end of existing memory. ; BUF:: .BLKB 256. ; Small buffer to expand strings in. RBUF: .BLKB RBSIZ ; Small buffer for received chars. RBUFE: RBR: .WORD RBUF ; Rcv char read ptr. RBW: .WORD RBUF ; Rcv char write ptr. RBLEN: .WORD 0 ; Rcv char length. ; IAPR: .WORD 0 ; Mask of I-space aprs in use. DAPR: .WORD 0 ; Mask of D-space aprs in use. APRMSK: .WORD 0 ; Mask of free APRs. CHEFLG: .WORD 0 ; Flag for large cache blocks. ; GBUF: .BYTE TC.ACR,0,TC.NBR,0,TC.FDX,0,TC.PTH,0,TC.NEC,0 GBL2=.-GBUF ; The attributes above are restored at finish. .BYTE TC.ANI ATERM: .BYTE 0 .BYTE TC.SCP SCP: .BYTE 0 .BYTE TC.SFC SFC: .BYTE 0 .BYTE TC.TTP TTP: .BYTE 0 .BYTE TC.WID WID: .BYTE 0 .BYTE TC.AVO AVO: .BYTE 0 .BYTE TC.SFC SOFT: .BYTE 0 .BYTE TC.LPP LPP: .BYTE 0 GLEN=.-GBUF RGS: .WORD 0 ; Flag for color available. .EVEN ; ; The next three strings must be in R/W memory, because CSI$1 writes ; to them in order to compress them. ; DDIR: GAMDIR ; Default directory. DDIRL=.-DDIR DSAV: SAVDIR ; Default save name. DSAVL=.-DSAV DSCR: SCPDIR ; Default scripting name. DSCRL=.-DSCR ; .EVEN ; Specific attributes we want set. ; These *should* be possible to have ; read-only. Unfortunately, the RH: driver ; wants them writeable... SBUF: .BYTE TC.FDX,1 ; Full duplex. .BYTE TC.ACR,0 ; No wrap. .BYTE TC.PTH,1 ; Pass through .BYTE TC.NEC,1 ; No echo .BYTE TC.NBR,1 ; No broadcast SLEN=.-SBUF ; .SBTTL Readonly data. .PSECT CONST,D,RO ; ; Switches defined for RSX version: ; ; /BL:n limits the memory usage to n pages of cache. ; This limit is located at address CLIM. ; /DE:n sets the debug flag for what messages to write. ; /IN:n sets the interpreter ID. ; /ID tells ZEMU version. ; /HE will give a short help. ; /LI will list all available games. ; /LO lists local games ; /SY lists system games ; /MM forces ZEMU to use small cache buffer size. ; /PS:n sets the small cache page size ; /MA:n sets the minimum APRs required for large cache ; /CO will set color capability for games. ; SWTAB: CSI$SW BL,1,MASK,SET,,SWVAL CSI$SW DE,2,MASK,SET,,SWVAL2 CSI$SW IN,4,MASK,SET,,SWVAL3 CSI$SW ID,10,MASK,SET CSI$SW HE,20,MASK,SET CSI$SW LI,40,MASK,SET CSI$SW LO,140,MASK,SET CSI$SW SY,240,MASK,SET CSI$SW MM,400,MASK,SET CSI$SW PS,400,MASK,SET,,SWVAL4 CSI$SW MA,1000,MASK,SET,,SWVAL5 CSI$SW CO,1,RGS,SET CSI$ND SWVAL: CSI$SV DECIMAL,CLIM,2 CSI$ND SWVAL2: CSI$SV NUMERIC,DBGFLG,2 CSI$ND SWVAL3: CSI$SV DECIMAL,INTERP,2 CSI$ND SWVAL4: CSI$SV DECIMAL,PGSIZ,2 CSI$ND SWVAL5: CSI$SV DECIMAL,MINAPR,2 CSI$ND ; INFO:: .LIMIT ; Task memory information. ; DFNAM: NMBLK$ ,DAT,,SY,0 ; Default game name. .SBTTL Constant DPBs defined at compile time. NOTIM: CLEF$ CLKEFN WAITX: WTLO$ 0,30 ; TIIEFN ! CLKEFN SETIO: SETF$ TIIEFN CLRIO: CLEF$ TIIEFN DOMAP: MAP$ ZWDB READ: QIOW$ IO.ATA,INLUN,CMDEFN,,,, .SBTTL SST vector table SSTVEC: .WORD S.ODD .WORD S.MPRO .WORD S.BPT .WORD S.IOT .WORD S.INST .WORD S.EMT .WORD S.TRAP SSTLEN=.-SSTVEC .SBTTL Modifiable DPBs defined at compile time. .PSECT DPB,D,RW IO: QIOW$ IO.WLB,TILUN,TIEFN MARK: MRKT$ CLKEFN,,1 ; .PSECT TEXT,D,RO BADFN: .ASCIZ /Bad filename specified./<15><12> BADFMT: .ASCIZ /The specified file have bad format./<15><12> FNF: .ASCIZ /File not found./<15><12> FOE: .ASCIZ /File protected./<15><12> VERFMT: .ASCII /ZEMU %R%R%N(c) 2003 by Johnny Billquist.%N/ .ASCIZ /Built on %Y %2Z./ HLPFMT: .ASCII /ZEMU %R%R%N(c) 2003 by Johnny Billquist.%N/ .ASCII /Built on %Y %2Z.%N%N/ .ASCII "/IN:n - Set interpreter ID to n.%N" .ASCII "/BL:n - Set memory limit to n pages.%N" .ASCII "/MA:n - Set minimum APRs required for memory cache.%N" .ASCII "/PS:n - Set pagesize for small page model (implies /MM).%N" .ASCII "/MM - Force small page model.%N" .ASCII "/CO - Set color available bit for game.%N" .ASCII "/ID - Show ZEMU version.%N" .ASCII "/HE - Give this help message.%N" .ASCII "/LI - List all available games.%N" .ASCII "/LO - List local games.%N" .ASCII "/SY - List system games." .BYTE 0 ; End of text. LFMT: .ASCIZ /-- Local games --/ GFMT: .ASCIZ /-- System games --/ HFMT: .ASCIZ /Game Release Serial Inform Z-Machine/ FFMT: .ASCIZ /%9<%I%9> %6<%M%6> %6E %4E %D/ ; F.M1: .ASCIZ /%N----- A bug have occured -----%N/ F.ODD: .ASCIZ /Odd address trap./ F.MPRO: .ASCII /Memory protect error.%N/ .ASCIZ /SR1: %P SR2: %P SR0: %P/ F.BPT: .ASCIZ /Breakpoint/ F.IOT: .ASCIZ "I/O trap" F.INST: .ASCIZ /Reserved instruction/ F.EMT: .ASCIZ /EMT: %O/ F.TRAP: .ASCIZ /TRAP: %O/ F.SST: .ASCII /%NR0 %P%N/ .ASCII /R1 %P%N/ .ASCII /R2 %P%N/ .ASCII /R3 %P%N/ .ASCII /R4 %P%N/ .ASCII /R5 %P%N/ .ASCII /SP %P%N/ .ASCII /PC %P%N/ .ASCII /PS %P%N/ .ASCIZ /-- Aborting ZEMU. Please send an error report./ .PSECT $$DBTS,D,RO,GBL,SAV ; Let TKB fill in some info here... $DBTS:: .PSECT CODE,I,RO .SBTTL SST handlers ; .ENABL LSB S.ODD: CLR -(SP) MOV #F.ODD,-(SP) BR 10$ S.MPRO: MOV #6,-(SP) MOV #F.MPRO,-(SP) BR 10$ S.BPT: CLR -(SP) MOV #F.BPT,-(SP) BR 10$ S.IOT: CLR -(SP) MOV #F.IOT,-(SP) BR 10$ S.INST: CLR -(SP) MOV #F.INST,-(SP) BR 10$ S.EMT: MOV #2,-(SP) MOV #F.EMT,-(SP) BR 10$ S.TRAP: MOV #2,-(SP) MOV #F.TRAP,-(SP) 10$: MOV R0,IOBUF2 MOV #IOBUF2+2,R0 MOV R1,(R0)+ MOV R2,(R0)+ MOV R3,(R0)+ MOV R4,(R0)+ MOV R5,(R0)+ MOV #BUF,R0 MOV #F.M1,R1 CALL $EDMSG MOV (SP)+,R1 MOV (SP)+,R5 MOV SP,R2 CALL $EDMSG ADD R5,SP MOV SP,IOBUF2+14 MOV (SP)+,IOBUF2+16 MOV (SP)+,IOBUF2+20 MOV #F.SST,R1 MOV #IOBUF2,R2 CALL $EDMSG MOV #BUF,R1 SUB R1,R0 QIOW$S #IO.WLB,#TILUN,#TIEFN,,,, JMP ABORT .DSABL LSB .SBTTL ZMSG ; ; ZMSG shall print a message on the users terminal. The string ; will have a CRLF appended. ; The function is used on errors. ; ; In: R1 -> NUL-terminated string. ; 2(SP) - A list of arguments for the string. ; ; No registers should be changed. ; ; The formatting used is that of $EDMSG in RSX. ; ; The string formatting is kindof like printf() in C, in that ; special codes in the string are replaced by the arguments ; pointed at in the argument list. ; ; Special formatting characters are: ; ; %D - signed decimal. ; %O - signed octal. ; %M - unsigned octal with leading zeroes. ; %P - unsigned octal. ; %U - unsigned decimal. ; %B - unsigned octal byte. Argument is address of byte. ; ZMSG:: CALL $SAVAL ; Save registers. MOV #BUF,R0 ; Buffer in R0. MOV SP,R2 ; Argument block. ADD #20,R2 ; CALL $EDMSG ; Format message. SUB #BUF,R0 ; Get length. QIOW$S #IO.WLB,#CMDLUN,#CMDEFN,,,,<#BUF,R0,#40> RETURN ; Done. .SBTTL PUTTXT ; ; PUTTXT is the routine to write a text to the screen. ; ; In: R0 - Address of text. ; R1 - Length of text. ; ; Note that the text is NUL terminated. ; The NUL is not accounted for in the length, though. ; .ENABL LSB PUTTXT:: .DBG #D.RSX,<"[PUTTXT: Address = %M, count = %D. characters]">,R0,R1 MOV R0,IO+Q.IOPL MOV R1,IO+Q.IOPL+2 DIR$ #IO RETURN .SBTTL GETRND - Give us a random value. ; ; This routine can use any trick in the book, and return a ; good random number in R0. It is used to initialize the ; random number generator. Suggested good values are from ; a clock. Preferrably ms. or so. ; .ENABL LSB GETRND:: GTIM$C TMBUF,CODE MOV TMBUF+10,R1 ; Get minutes MUL #60.,R1 ; Convert into seconds. ADD TMBUF+12,R1 ; Add seconds. MUL TMBUF+16,R1 ; Convert into ticks. ADD TMBUF+14,R1 ; Add ticks. MOV R1,R0 ; Get result in R0. .DBG #D.RSX,<"GETRND: returning %M as seed]">,R0 RETURN .SBTTL INPINI - Initiate input. ; ; Initiating input means we'll start a timer if we need one. The ; actual reading is active all the time, through an AST (except at ; the start). ; ; In: TMO - Timeout. ; .ENABL LSB INPINI:: .DBG #D.RSX,<"[INPINI: timout = %D. tenths of a second]">,TMO CALL INPEND ; Just in case... TST TMO ; Any timeout? BEQ 10$ ; No. MOV R0,-(SP) ; Save registers. MOV R1,-(SP) MOV TMO,R0 ; Get timeout. MUL TMBUF+G.TICP,R0 ; Multiply by ticks/second. DIV #10.,R0 ; Divide for 1/10 seconds. MOV R0,MARK+M.KTMG ; Save magnitude. DIR$ #MARK ; Start timer. MOV (SP)+,R1 ; Restore registers. MOV (SP)+,R0 10$: CALLR MCUR ; Place cursor. .SBTTL INPEND - End input. ; ; Stop input. In this case, it just means stop the timer (if one exist). ; ; No arguments. ; No return values. ; .ENABL LSB INPEND:: .DBG #D.RSX,<"[INPEND: timer cancelled]"> CMKT$C CLKEFN,,CODE ; Cancel possible timer. DIR$ #NOTIM ; The timer event flag should be clear. RETURN .SBTTL RAST - Receive character AST. ; ; AST entered for each character read. ; ; Input is on stack. ; No return values are possible. ; ; All characters recevied are placed in the input queue, where they can ; be read later. We also set an event flag, to indicate that we have ; recevied characters. ; RAST: CMPB (SP),#3 ; ^C? BEQ 1$ CMPB (SP),#14 ; ^L? BEQ 2$ CMP RBLEN,#RBSIZ ; Buffer full? BHIS 100$ ; Yes. MOVB (SP)+,@RBW ; No. Save char. INC RBLEN ; Bump size. INC RBW ; Bump write pointer. CMP RBW,#RBUFE ; Wrap? BLO 10$ MOV #RBUF,RBW ; Yes. 10$: DIR$ #SETIO ; Mark that data is available. ASTX$S ; Done. 1$: JMP ABORT ; At ^C we abort. 2$: CALL SCRZAP ; At ^L we zap screen and return. 100$: TST (SP)+ ; Clear stack. ASTX$S .SBTTL CHARNE - Read one character from keyboard, with no echo. ; ; Out: Carry clear: R0 - character read. ; Carry set: We had timeout. ; .ENABL LSB CHARNE:: DIR$ #WAITX,CODE ; Wait for read or timer. TST RBLEN ; Check if anything available. BEQ 100$ ; No. Then timer must have finished. MOV RBR,R0 ; Yes. Get pointer. MOVB (R0)+,-(SP) ; Save char. CMP R0,#RBUFE ; End of buffer? BLO 10$ ; No. MOV #RBUF,R0 ; Yes. 10$: MOV R0,RBR ; Save new pointer. DSAR$S ; No ASTs here... DEC RBLEN ; Decrement length of buffer. BNE 20$ ; Not empty... DIR$ #CLRIO ; Empty. Clear EFN. 20$: ENAR$S ; Okay to get new ASTs... CLR R0 ; Get char back. BISB (SP)+,R0 ; Get char. .DBG #D.RSX,<"[CHARNE: returning char %O]">,R0 RETURN ; Tada! (Carry is 0 here) 100$: DIR$ #MARK ; Setup another timer. .DBG #D.RSX,<"CHARNE: returning timeout]"> SEC ; Mark timeout. RETURN ; Done. .SBTTL GAMINI ; ; GAMINI is a routine called just before the game play begins. ; Here any final preparations can be done before the main loop ; runs. ; GAMINI:: GIN$S #GI.UPD,#ARGBLK,#5 ; Reset UIC to that of terminal. GIN$S #GI.SPR,#0 ; Drop privileges. RETURN .SBTTL ZINIT ; ; ZINIT is the game initializing routine. It should somehow query ; the user about which game to play, look up, and open. ; ; Also process any possible switches... ; .ENABL LSB ZINIT:: FINIT$ ; Initialize FCS. SREX$C ABORT,,CODE ; Where to go if someone tries to abort us... SVTK$S #SSTVEC,#SSTLEN ; Setup vectors. MOV R3,VERBLK ; Save ZEMU version. MOV R4,VERBLK+2 MOV #$DBTS,R0 ; Copy date of build MOV #DATBLK,R1 ; to this place. MOV #10,R2 1111$: MOV (R0)+,(R1)+ SOB R2,1111$ ; ; First we'll create the default file name block for game files, ; in case a game file is given without a directory, and we ; might have a default directory to search. ; CSI$1 #CSI,#DDIR,#DDIRL ; Start by expanding the default dir. CSI$4 ,OUTPUT MOV #FDB,R0 ; The FDB we use. MOV #DFNAM2,R1 ; Filename block to fill in. MOV #CSI+C.DSDS,R2 ; Data set descriptor. MOV #DFNAM,R3 ; Default file name. BISB #FL.AEX,F.FLG(R0) ; Locial name expansion already done. CALL .PARSE ; Parse this. ; ; Now find out how much, if any, memory is preallocated. ; GTSK$C TSKPAR,CODE ; Get task parameters. BCC 1$ ; ; If GTSK$ fails, we're in serious trouble. How could it fail??? ; 101$: .MSG <"Could not find out task size."> EXIT$S ; 1$: MOV TSKPAR+G.TSTS,END ; Save end. MOV INFO+2,FREEPT ; Setup free pointer. .DBG #D.RSX,<"[ZINIT: FREEPT = END = %M]">,FREEPT ; ; The "main" loop to find a game to play. ; Start by reading a command. ; ZLOOP: GCML$ #GCM ; Get command (game name). BCC 10$ ; Ok? CMPB #GE.EOF,G.ERR(R0) ; No. EOF? BEQ 5$ ; Yes. .MSG <"Error reading command."> 5$: EXIT$S ; ; The next step is to initialize the parser. ; 10$: MOV #CSI,R0 ; Get CSI pointer. CLR C.SWAD(R0) ; No switches on command. CLR MASK CSI$1 ,GCM+G.CMLD+2,GCM+G.CMLD ; Parse line. BCC 20$ ; Worked? .MSG <"Unable to parse game name."> BR ZLOOP ; ; After that, we'll parse the game name. ; 20$: TST C.CMLD(R0) ; Empty line? BEQ ZLOOP ; Yes. BITB #CS.EQU,C.STAT(R0) ; Output specified? BNE 21$ ; Yes. CSI$4 ,OUTPUT,#SWTAB ; Get input filename. ; (silly of RSX to call a single ; file output...) BCC 30$ ; Ok? .MSG <"Bad format for game name."> BR ZLOOP 21$: .MSG <"You cannot specify an output file."> BR ZLOOP ; ; When we come this far, we make some sanity checks, and then ; we try to open the game file. ; 30$: BIT #10,MASK ; /ID given? BEQ 301$ ; No. MOV #BUF,R0 ; We'll do our own format here. MOV #VERFMT,R1 MOV #VERBLK,R2 CALL $EDMSG SUB #BUF,R0 QIOW$S #IO.WLB,#CMDLUN,#CMDEFN,,,,<#BUF,R0,#40> JMP ZLOOP 301$: BIT #20,MASK ; /HE given? BEQ 302$ ; No. MOV #BUF,R0 ; We'll do our own format here. MOV #HLPFMT,R1 MOV #VERBLK,R2 CALL $EDMSG SUB #BUF,R0 QIOW$S #IO.WLB,#CMDLUN,#CMDEFN,,,,<#BUF,R0,#40> JMP ZLOOP 302$: BIT #40,MASK ; /LI given? BEQ 303$ ; No. ; ; Here we shall list all available games... ; BIT #200,MASK ; /LO given? BNE 3020$ ; No. MOV #LFMT,FMTPTR ; Header line. MOV #DFNAM,R3 ; Default name. CALL DOLIST 3020$: BIT #100,MASK ; /SY given? BNE 3021$ ; No. MOV #GFMT,FMTPTR ; Header line. MOV #DFNAM2,R3 ; Default directory please. CLR CSI+C.DSDS ; Remove user specified deivce. CLR CSI+C.DSDS+4 ; Remove user specified directory. CALL DOLIST 3021$: JMP ZLOOP ; 303$: BITB #CS.NMF,C.STAT(R0) ; Filename specified? BNE 31$ ; Yes. .MSG <"No game name specified."> JMP ZLOOP 31$: BITB #CS.WLD,C.STAT(R0) ; Wildcard? BEQ 32$ ; No. .MSG <"Wildcard game names not supported."> JMP ZLOOP 32$: BITB #CS.MOR,C.STAT(R0) ; More filenames given? BEQ 33$ ; No. .MSG <"Only one game at a time, please."> JMP ZLOOP 33$: CMP C.FILD(R0),#9. ; More than 9 chars in filename? BLOS 34$ MOV #9.,C.FILD(R0) ; Yes. Truncate to 9 chars. 34$: MOV #DFNAM,FDB+F.DFNB ; Default filename block. CLR SYSFLG ; Clear system flag. OPNS$R #FDB,,,#FD.RWM,,,OPERR ; All ok. Open file. ; ; Once we have the game open, we make some additional sanity checks, ; in order to try making sure that this really is a game file. ; CMPB F.RTYP(R0),#R.FIX ; Fixed records? BNE 40$ ; No. Not a good game file. TSTB F.RATT(R0) ; No attributes, please... BNE 40$ ; We had some. CMP F.RSIZ(R0),#512. ; We expect 512 byte records. BEQ 50$ ; It was. All is good! ; ; Not a game file... ; 40$: CLOSE$ ; This wasn't a good file! .MSG <"That didn't look like a game file!"> JMP ZLOOP ; ; We have a game file. Now we'll just do some final setups, and ; then we're on the way to adventure! ; 50$: MOV #IO.RAT,R1 ; Read attributes MOV #2,R2 ; Additional params. MOV #RATBUF,R3 ; CALL .XQIO GIN$S #GI.SPR,#0 ; Drop privilege. ; ; Now we shall create a default name block for the save file name. ; ; Setup filename block for save file... ; MOV #SAVEXT,FDB+F.FNB+N.FTYP ; Default file extension... CLR FDB+F.FNB+N.FVER ; File version is 0. CLR FDB+F.FNB+N.DID ; Directory is current dir. CLR FDB+F.FNB+N.DID+2 CLR FDB+F.FNB+N.DID+4 MOV #"SY,FDB+F.FNB+N.DVNM ; Default device is SY0: CLR FDB+F.FNB+N.UNIT ; ; Now expand the default string, which combined with the just ; created default name will give us the default filename block ; to use in SAVE/RESTORE filenames. ; CSI$1 #CSI,#DSAV,#DSAVL ; Start by expanding the default dir. CSI$4 ,OUTPUT MOV #SAVFDB,R0 ; The FDB we use. MOV #DFNAM2,R1 ; Filename block to fill in. MOV #CSI+C.DSDS,R2 ; Data set descriptor. MOV #FDB+F.FNB,R3 ; Default file name is game name. BISB #FL.AEX,F.FLG(R0) ; Locial name expansion already done. CALL .PARSE ; Parse this. ; ; Now expand the default string, which combined with the just ; created default name will give us the default filename block ; to use in scripting filename. ; MOV #SCREXT,FDB+F.FNB+N.FTYP ; Default file extension... CSI$1 #CSI,#DSCR,#DSCRL ; Start by expanding the default dir. CSI$4 ,OUTPUT MOV #SAVFDB,R0 ; The FDB we use. MOV #DFNAM3,R1 ; Filename block to fill in. MOV #CSI+C.DSDS,R2 ; Data set descriptor. MOV #FDB+F.FNB,R3 ; Default file name is game name. BISB #FL.AEX,F.FLG(R0) ; Locial name expansion already done. CALL .PARSE ; Parse this. ; DIR$ #NOTIM ; Make sure the read timer flag isn't ; set. DIR$ #CLRIO ; Make sure read EFN is clear. QIOW$C SF.GMC,TILUN,CMDEFN,,IOSB,,,CODE ; Get terminal characteristics. TSTIO IOSB,IOERR QIOW$C SF.SMC,TILUN,CMDEFN,,IOSB,,,CODE ; Set some terminal chars... TSTIO IOSB,IOERR DIR$ #READ ; And setup read. ; RETURN ; Done? ; ; Open error. Perhaps we should try another default name? ; OPERR: BITB #CS.DIF,CSI+C.STAT ; Did we have an explicit dir? BNE 110$ ; Yes. No use in trying... ; ; If we didn't find game, let's try with default spec. ; MOV #DFNAM2,F.DFNB(R0) ; Default filename block. INC SYSFLG ; Trying system game. OPNS$R ,,,#FD.RWM,,,100$ ; Open file. RETURN ; All ok. ; 100$: ADD #2,SP ; Drop return address. 110$: ADD #2,SP CMPB F.ERR(R0),#IE.PRI BEQ 120$ .MSG <"Game file not found."> JMP ZLOOP 120$: .MSG <"Game file protected."> JMP ZLOOP .DSABL LSB ; ; Move a filename to a temp buffer, at the same time updating ; the FNB ; ; In: R2 - Pointer to dataset descriptor ; R3 - Default FNB. ; R1 - FNB. ; MOVNAM: CALL $SAVAL ; Save all registers. MOV #SFNAM,R0 ; Point at where to write filename. ADD #10,R2 ; Point at filename dataset. MOV (R2)+,R4 ; Get length of filename string. MOV (R2),R2 ; Get pointer to filename. TST R4 ; Any file string given? BNE 10$ ; Yes. Start copy. BIS #NB.SNM,N.STAT(R1) ; No. Then we assume wildcard. MOVB #'*,(R0)+ BR 20$ ; Done. 10$: CMPB (R2),#'* ; Copy filename. Wildcard chars? BEQ 15$ ; Yes. CMPB (R2),#'% BEQ 15$ ; Yes. BR 16$ ; No. 15$: BIS #NB.SNM,N.STAT(R1) ; Yes. Wildcard. Tell so. 16$: MOVB (R2)+,(R0)+ ; Copy. SOB R4,10$ ; Until all done. 20$: CLRB (R0) ; Mark end of filename. BIS #NB.SVR,N.STAT(R1) ; And always wildcard file version. BIT #NB.TYP,N.STAT(R1) ; Did we get explicit type? BNE 30$ ; Yes. MOV N.FTYP(R3),N.FTYP(R1) ; No. Copy default explicitly. 30$: RETURN ; ; Move R50 filename to temp storage. ; ; In: R1 - Pointer to R50 filename ; MOVNR5: CALL $SAVAL ; Save all registers. MOV #MFNAM,R0 ; Destination pointer. MOV R1,R5 ; Save pointer. MOV #3,R4 ; Loop count. 10$: MOV (R5)+,R1 ; Get word. CALL $C5TA ; Convert. SOB R4,10$ ; Repeat. CLRB (R0) ; Mark end of string. MOV #9.,R1 ; Loop count. 20$: CMPB -(R0),#' ; Space? BNE 30$ ; No. Done. CLRB (R0) ; Yes. Drop it. SOB R1,20$ ; Loop. 30$: RETURN ; Done. ; ; Match filenames. ; ; In: SFNAM - Search filename ; MFNAM - Match filename ; ; Out: CS - No match ; NAMMAT: CALL $SAVAL ; Save all registers. MOV #SFNAM,R0 ; Search filename. MOV #MFNAM,R1 ; Match filename. 10$: TSTB (R0) ; End of search filename? BEQ 100$ ; Yes. CMPB (R0),#'* ; Wildcard? BEQ 90$ ; Yes. CMPB (R0),#'% ; Any? BEQ 80$ ; Yes. CMPB (R0)+,(R1)+ ; Normal char. Match? BEQ 10$ ; Yes. Do next. 20$: SEC ; Not equal. Match failed. RETURN ; ; Match any one char. ; 80$: INC R0 ; Skip match char. TSTB (R1)+ ; Skip any char. BNE 10$ ; Do next. BR 20$ ; End of string. Failed. ; ; Match wild. ; 90$: INC R0 ; Skip match char. 91$: MOV R0,-(SP) ; Save pointers. MOV R1,-(SP) CALL 10$ ; And try match rest. BCC 99$ ; Match succeeded. Done. MOV (SP)+,R1 ; Restore pointer. MOV (SP)+,R0 TSTB (R1) ; End of string and no match? BEQ 20$ ; Yes. We fail. INC R1 ; No. Advance. BR 91$ 99$: ADD #4,SP ; Success. Clean stack. RETURN ; Carry is known clear here. ; ; Done. ; 100$: TSTB (R1) ; End of search string also? BNE 20$ ; No. Fail. RETURN ; Yes. Done. ; ; List existing games. ; ; Input: FMTPTR - List header to print ; R3 - Default name block ; DOLIST: CALL $SAVAL ; Save all registers. MOV #FDB,R0 ; FDB to use. MOV #FDB+F.FNB,R1 ; FNB to fill in. MOV #CSI+C.DSDS,R2 ; Data set descriptor. CLR FNDCNT ; Clear counter. BISB #FL.AEX,F.FLG(R0) ; Local name expansion already done. CALL .PARSE ; Parse this. CALL MOVNAM ; Move filename string, also ; setting flags in FNB. LLOOP: MOV #FDB,R0 MOV #FDB+F.FNB,R1 CALL .FIND ; Find file. BCC 10$ ; Okay... JMP 100$ ; Nothing found. Stop. 10$: ; We have a possible game found. Open it, verify attributes and ; contents of first part of memory. ADD #N.FNAM,R1 ; Point at filename. CALL MOVNR5 ; Move R50 filename. CALL NAMMAT ; Try to match names. BCS LLOOP ; Not a match. Try next. OPNS$R #FDB,,,#FD.RWM,,,300$ ; Open file. CMPB F.RTYP(R0),#R.FIX ; Check if file looks good... BNE 50$ TSTB F.RATT(R0) BNE 50$ CMP F.RSIZ(R0),#512. BNE 50$ ; Attributes are good... MOV #IOBUF,R1 ; Point at I/O buffer. MOV #1,BPAGE ; Read first block (BLK,,BPAGE) READ$ ,R1,#512.,#BLK,,,,200$ ; Read first block. WAIT$ ,,,200$ TSTB (R1) ; Check that type isn't 0. BEQ 50$ ; Bad. CMPB (R1),#8. ; Check that type isn't > 8 BHI 50$ ; Bad. MOV #ARGBLK,R2 ; Setup argument block. MOV #MFNAM,(R2)+ ; Game name MOV 2(R1),(R2) ; Second word is release. SWAB (R2)+ MOV R1,(R2) ; Point at serial number string. ADD #22,(R2)+ ; (12h) MOV R1,(R2) ; Point at inform version string. ADD #74,(R2)+ ; (3Ch) MOVB (R1),(R2)+ ; Z-machine version. TST FNDCNT ; Have we printed header_ BNE 20$ ; Yes. MOV FMTPTR,R1 ; No. Start with that. CALL ZMSG MOV #HFMT,R1 ; It's actually two lines... CALL ZMSG 20$: MOV #BUF,R0 ; Setup for $EDMSG MOV #FFMT,R1 MOV #ARGBLK,R2 CALL $EDMSG SUB #BUF,R0 ; Get length of lines. QIOW$S #IO.WLB,#CMDLUN,#CMDEFN,,,,<#BUF,R0,#40> ; Print it. INC FNDCNT ; Bump find counter. 50$: CLOSE$ #FDB ; Close file. ; BIT #NB.SNM,FDB+F.FNB+N.STAT ; Explicit name? ; BEQ 100$ ; Yes. Done. JMP LLOOP ; No. Search for next. 100$: RETURN ; Done. 200$: CLOSE$ ; Error. Close file. BR 400$ 300$: ADD #2,SP ; Drop return address. CMPB #IE.PRI,F.ERR(R0) ; Priv error? BNE 400$ JMP LLOOP ; Yes. Do next file. 400$: .MSG <"Really bad error occured."> EXIT$S .SBTTL ZBLK - Read a page from the game file into memory. ; ; In: BADDR - Address ; BPAGE - Page number ; BCNT - Block size ; .ENABL LSB ZBLK:: .DBG #D.RSX,<"[ZBLK: BADDR = %P, BPAGE = %D., BCNT = %D. bytes]">,BADDR,BPAGE,BCNT MOV R0,-(SP) ; Save R0. INC BPAGE ; Make info block bias. READ$ #FDB,BADDR,BCNT,#BLK,,,,10$ ; Issue read. WAIT$ ,,,10$ ; Wait for operation to complete. MOV (SP)+,R0 ; Restore R0. RETURN ; Done. ; 10$: .MSG <"I/O error while reading game file. Aborting..."> .MSG <"Error code is %B(%B).">,#FDB+F.ERR,#FDB+F.ERR+1 .MSG <"Block = %D,%D, size = %D.">,BLK,BLK+2,BCNT JMP ABORT .DSABL LSB .SBTTL Transcription game functions. ; ; SCROPN - Create a scripting file. ; ; In: R0 - Points at a filename to use. ; R1 - Length of filename. ; ; Out: Carry set - Failure. ; ; Filename is terminated by a NUL, which isn't included in the ; length. ; SCROPN:: MOV R0,CSI+C.CMLD+2 MOV R1,CSI+C.CMLD CSI$1 #CSI ; Initialize analyzer. BCS 100$ ; Fail. BITB #CS.EQU,C.STAT(R0) ; Found a '='? BNE 100$ ; Yes. Fail. CSI$4 ,OUTPUT BCS 100$ ; Fail. BITB #CS.WLD,C.STAT(R0) ; Wildcard? BNE 100$ ; Yes. BITB #CS.MOR,C.STAT(R0) ; ',' found? BNE 100$ ; Yes. OPEN$W #SCRFDB,,,,,,90$ RETURN ; Done. ; 90$: ADD #2,SP 100$: MOV #BADFN,R0 ; Bad file name message. CALL SCRTXT ; Tell it through the normal screen. SEC RETURN ; ; SCRBLK - Write a line to the script (log) file. ; ; In: R0 - Pointer to buffer. ; R1 - Length of buffer. ; ; The line is terminated by a NUL character, which isn't included ; in the length. ; SCRBLK:: MOV R0,-(SP) ; Save registers. MOV R2,-(SP) MOV R0,R2 ; Move address to R2. PUT$ #SCRFDB,R2,R1 ; Output line. MOV (SP)+,R2 ; Restore registers. MOV (SP)+,R0 RETURN ; ; SCRCLO - Close the script (log) file. ; SCRCLO:: MOV R0,-(SP) CLOSE$ #SCRFDB MOV (SP)+,R0 RETURN .SBTTL Save game functions. ; ; SAVOPN - Create a save file. ; ; In: R0 - Points at a filename to use. ; R1 - Length of filename. ; R2 - Flag for overwrite old or create new file. ; (1 = Write new file. ; 0 = Open old file to modify) ; ; Out: Carry set - Failure. ; ; Filename is terminated by a NUL, which isn't included in the ; length. ; SAVOPN:: MOV R0,CSI+C.CMLD+2 MOV R1,CSI+C.CMLD CSI$1 #CSI ; Initialize analyzer. BCS 100$ ; Fail. BITB #CS.EQU,C.STAT(R0) ; Found a '='? BNE 100$ ; Yes. Fail. CSI$4 ,OUTPUT BCS 100$ ; Fail. BITB #CS.WLD,C.STAT(R0) ; Wildcard? BNE 100$ ; Yes. BITB #CS.MOR,C.STAT(R0) ; ',' found? BNE 100$ ; Yes. MOV #SAVFDB,R0 MOVB #R.FIX,F.RTYP(R0) CLRB F.RATT(R0) MOV #512.,F.RSIZ(R0) TST R2 ; Overwrite file? BNE 10$ OPEN$U ,,,#FD.RWM,,,20$ ; Yes. Open for update. RETURN 20$: ADD #2,SP ; Error opening for update, try write. 10$: OPEN$W ,,,#FD.RWM,,,90$ ; No. Open for write. RETURN ; Done. ; 90$: ADD #2,SP ; Error. Drop return. 100$: MOV #BADFN,R0 ; Error message. CALL SCRTXT ; Write it through normal screen. SEC RETURN ; ; SAVBLK - Write a block to the save file. ; ; In: R0 - Pointer to a 512 byte buffer to write. ; SAVBLK:: MOV R0,-(SP) MOV R1,-(SP) MOV R0,R1 WRITE$ #SAVFDB,R1 WAIT$ MOV (SP)+,R1 MOV (SP)+,R0 RETURN ; ; SAVCLO - Close the save file. ; SAVCLO:: ; ; RESCLO - Close restore file. ; RESCLO:: MOV R0,-(SP) CLOSE$ #SAVFDB MOV (SP)+,R0 RETURN .SBTTL Restore game from file. ; ; RESOPN - Open file to restore from. ; ; In: R0 - Points to filename to open. ; R1 - Length of filename. ; ; Out: Carry set - Failure. ; ; Filename is terminated by a NUL, which isn't included in the ; length. ; RESOPN:: MOV R0,CSI+C.CMLD+2 MOV R1,CSI+C.CMLD CSI$1 #CSI ; Initialize analyzer. BCS 100$ ; Fail. BITB #CS.EQU,C.STAT(R0) ; Found a '='? BNE 100$ ; Yes. Fail. CSI$4 ,OUTPUT BCS 100$ ; Fail. BITB #CS.WLD,C.STAT(R0) ; Wildcard? BNE 100$ ; Yes. BITB #CS.MOR,C.STAT(R0) ; ',' found? BNE 100$ ; Yes. OPEN$R #SAVFDB,,,#FD.RWM,,,90$ CMPB F.RTYP(R0),#R.FIX BNE 200$ TSTB F.RATT(R0) BNE 200$ CMP F.RSIZ(R0),#512. BNE 200$ RETURN ; Done. ; 90$: ADD #2,SP ; Drop return address. CMPB F.ERR(R0),#IE.PRI ; Was it privilege violation? BEQ 91$ ; Yes. MOV #FNF,R0 ; No. The we failed to find file. BR 110$ 91$: MOV #FOE,R0 ; File was protected... BR 110$ 100$: MOV #BADFN,R0 ; Bad file name. 110$: CALL SCRTXT ; Print message. SEC ; Indicate failure. RETURN 200$: CLOSE$ MOV #BADFMT,R0 CALL SCRTXT SEC RETURN ; ; RESBLK - Read a block from restore file. ; ; In: R0 - Points at a data area to place next 512 byte block. ; RESBLK:: MOV R0,-(SP) MOV R1,-(SP) MOV R0,R1 READ$ #SAVFDB,R1 WAIT$ MOV (SP)+,R1 MOV (SP)+,R0 RETURN .SBTTL ZEXIT - Exit program. ; ; Do all eventual cleanup here. ; ; This is called from all over the place. ; One might argue that we could start over after getting here, ; but currently we are expected to return to the OS... ; .ENABL LSB ZEXIT:: .DBG #D.RSX,<"Entered ZEXIT]"> CLOSE$ #FDB ; Close game file. QIOW$C SF.SMC,TILUN,CMDEFN,,,,,CODE ; Restore terminal mode. EXIT$S ; Return to OS. .SBTTL ZALLOC - Allocate a page of memory. ; ; Other code makes the assumption that pages are allocated ; linear and sequential, so that you can call ZALLOC twice ; to get larger space. ; ; In: R0 - Bytes to allocate. ; Out: R0 - Address of memory. ; ; If carry is set, we failed the allocate. The game will never call ; this routine agan after this error, so the state we leave in ; don't matter at that time. ; .ENABL LSB ZALLOC:: .DBG #D.RSX,<"[ZALLOC: Requested size = %D. bytes]">,R0 INC R0 ; Round off to words. BIC #1,R0 MOV FREEPT,-(SP) ; Save pointer to memory. ADD R0,FREEPT ; Set new free pointer. BCS 20$ ; We wrapped. New alloc went over the top. CMP FREEPT,END ; Check if we are inside alloc mem. BLOS 10$ ; We are. All is ok. MOV FREEPT,R0 ; Get free pointer. SUB END,R0 ; Find out how much memory we miss. ADD #77,R0 ; Round up. BIC #77,R0 ADD R0,END ; This is the new end. BCS 20$ ; Overflow... ASH #-6,R0 ; Divide by 64. BIC #176000,R0 ; Mask off bits we know should be 0. EXTK$S R0 ; Extend task. BCS 20$ ; Error extending... 10$: CLC MOV (SP)+,R0 ; Get address of new memory block. .DBG #D.RSX,<"[ZALLOC: Memory allocated at %P]">,R0 RETURN 20$: TST (SP)+ ; Clean stack. .DBG #D.RSX,<"[ZALLOC failed to allocate more memory]"> SEC ; Mark error. RETURN .SBTTL GETSCL - Get screen width. ; ; Screen info routines. ; ; Get screen width. ; ; Out: R0 - Width of screen. ; .ENABL LSB GETSCL:: CLR R0 BISB WID,R0 .DBG #D.RSX,<"[GETSCL: returning %D. columns]">,R0 RETURN .SBTTL GETSLN - Get screen length. ; ; Get screen length. ; ; Out: R0 - Length of screen. ; .ENABL LSB GETSLN:: CLR R0 BISB LPP,R0 .DBG #D.RSX,<"[GETSLN: returning %D. lines]">,R0 RETURN .SBTTL GETSTP - Get screen type. ; ; Get screen type. ; ; Out: R0 - Screen type. ; .ENABL LSB GETSTP:: MOV #UNKT,R0 ; Default is unknown... CMPB TTP,#T.VT52 BNE 10$ MOV #VT52,R0 ; Type is VT52. 10$: CMPB TTP,#T.V100 BNE 20$ MOV #VT100,R0 20$: CMPB TTP,#T.V102 BNE 30$ MOV #VT102,R0 30$: CMPB TTP,#T.V2XX BNE 40$ MOV #VT200,R0 40$: .DBG #D.RSX,<"[GETSTP: returning terminal type %D.]">,R0 RETURN .DSABL LSB ; ; Get if we have text attributes. ; GETAVO:: TSTB AVO BEQ 10$ SEC 10$: RETURN ; ; Get if we have color. ; GETCOL:: TSTB RGS BEQ 10$ SEC 10$: RETURN ; ; Get if we have font 3. ; GETSOF:: TSTB SOFT BEQ 10$ SEC 10$: RETURN ; ; Error handlers. ; CODE: MOV (SP)+,R0 .MSG <"Directive error at PC=%P $DSW=%O">,R0,$DSW EXST$S #2 ; IOERR: MOV (SP)+,R0 MOV (SP)+,R1 MOV (SP)+,R2 .MSG <"I/O error at PC=%P IOSB=%O,%O">,R0,R1,R2 EXST$S #2 .SBTTL CACHE functions ; ; CHESIZ - Get size of cache page ; ; This routine is called before starting to allocate cache memory, ; and so we can use it for any preparatory work needed for cache memory ; allocation. ; ; Out: R0 - Size ; .ENABL LSB CHESIZ:: GIN$S #GI.SPR,#1 ; Gain privilege. BCS 1001$ ; Couldn't get privs. Use small cache. ; ; We have privileges. Let's fool around some. ; GIN$S #GI.DEF,#454 ; Set UIC to [1,54] MOV #^C1,FILPRO ; Change protection to SY:R only 1001$: JSR R5,.SAVR1 ; Save registers. TFEA$S #T4$FMP ; Do we have fastmap? CMPB $DSW,#IS.SET BNE 1000$ ; No. No fastmap. MOV #-1,MAPF ; Yes. Save fastmap function pointer. .DBG #D.MAP,<"Using FASTMAP"> 1000$: BIT #400,MASK BNE 1$ ; /MM or /PS TST SYSFLG BEQ 1$ ; Not system game. Don't use large cache MOV GAMSIZ,R1 ; Get game size. CLR R0 ASHC #9.,R0 SUB DYNHI,R1 SBC R0 ASHC #-6,R0 CMP R1,#8192./64. ; Game less than one page? BLO 1$ ; Too small game BR 2$ ; We'll try large cache size. 1$: JMP STDCHE ; User small (standard) cache size. 2$: ; ; First we need to find out which APRs are free... ; .DBG #D.MAP,<"WS.MAP: %P, WS.SIS: %P, WS.UDS: %P">,#WS.MAP,#WS.SIS,#WS.UDS SUB #193.*2,SP ; Make space on stack. MOV SP,R0 ; Save pointer GMCX$S R0 ; Get current mapping 10$: TST (R0) ; Search for end of list. BMI 100$ ; End found. .DBG #D.MAP,<"APR: %P, Siz: %P, Sts: %P">,W.NAPR-1(R0),W.NLEN(R0),W.NSTS(R0) BIT #WS.MAP,W.NSTS(R0) ; Window mapped? BEQ 90$ ; No. BIT #WS.SIS,W.NSTS(R0) ; Yes. Supervisor mode? BNE 90$ ; Yes. Ignore it. MOV #IAPR,R1 ; No. Assume I-space. BIT #WS.UDS,W.NSTS(R0) ; D-space? BEQ 20$ ; No. MOV #DAPR,R1 ; Yes. 20$: MOV #1,R2 ; Bitmask. MOVB W.NAPR(R0),R3 ; Get base apr. ASH R3,R2 ; Make APR mask. 30$: .DBG #D.MAP,<"Setting bit %P in %P (%P)">,R2,R1,W.NLEN(R0) BIS R2,(R1) ; Mark APR as used. ASL R2 ; Move mask to next apr. SUB #<8192./64.>,W.NLEN(R0) ; Step forward in window. BGT 30$ ; Repeat if window covers next APR. 90$: ADD #20,R0 ; Next window. BR 10$ 100$: ADD #193.*2,SP ; Remove info from stack. .DBG #D.MAP,<"ISPACE: %P, DSPACE: %P">,IAPR,DAPR ; ; Now we know what APRs we can use. If DAPR.NE.0 we have D-space, and ; we should analyze that mask, otherwise we should use I-space. ; MOV #WS.UDS,ZWDB+W.NSTS ; Assume we want D-space. MOV DAPR,R0 ; Get mask. BNE 110$ ; There was something. MOV IAPR,R0 ; Nothing. Use I-space. CLR ZWDB+W.NSTS ; We want I-space. 110$: COMB R0 ; Invert all bits. MOV R0,APRMSK ; Save APR mask. .DBG #D.MAP,<"APRMSK is %P">,R0 ; ; We now have an APR-mask which we can use if we want to. ; R0 have a one for each free APR. ; Let's just count how many free APRs we have. ; CLR R1 ; Count of free APRs. 120$: INC R1 ; Count one APR. MOV R0,R2 ; Get mask in R2. NEG R2 ; Negate it. BIC R2,R0 ; This is neat. Clear one bit in the mask. BNE 120$ ; And if there still are bits set, repeat. ; ; R1 now have number of free APRs to use. ; .DBG #D.MAP,<"%D free APRs">,R1 CMP R1,MINAPR ; How many APRs do we require? BHIS 121$ JMP STDCHE ; We have fewer. Let's use small bufs. 121$: ; ; We have now decided to use memory mapped game. ; MOV GAMSIZ,R1 ; Get game size. CLR R0 ASHC #9.,R0 ; Make into bytes. SUB DYNHI,R1 ; Drop dynamic memory. SBC R0 ASHC #-6,R0 ; Get size in 64B blocks. MOV R1,ZRDB+R.GSIZ ; Size of region. MOV GAMR50,ZRDB+R.GNAM ; Name of region. MOV GAMR50+2,ZRDB+R.GNAM+2 CLR ZRDB+R.GPAR ; Partition name CLR ZRDB+R.GPAR+2 MOV #,ZRDB+R.GSTS MOV FILPRO,R0 ; Get file protection. BIS #167346,R0 ; Remove: WO:WED,GR:WED,OW:WED,SY:WE MOV R0,ZRDB+R.GPRO ; Set as region prot. ATRG$S #ZRDB ; Try to attach. CMP #IS.SUC,$DSW ; Ok? BEQ 140$ ; Yes. We're happy. CMP #IE.PNS,$DSW ; Any other error than region don't ; exist? BNE 139$ ; Yes. Fail. BIS #RS.WRT,ZRDB+R.GSTS ; Not ok. Try to create with write. CRRG$S #ZRDB ; Create region. CMP #IS.SUC,$DSW ; Result ok? BEQ 140$ 139$: .DBG #D.MAP,<"Failed to attach. %P">,$DSW JMP STDCHE 140$: .DBG #D.MAP,<"Create region ok."> MOV ZRDB+R.GID,ZWDB+W.NRID ; Save region ID. ; Check for creation here as well, just in case we had two users ; try this at about the same time. BIT #RS.CRR,ZRDB+R.GSTS ; Did we create region? BEQ 200$ ; Nope. .DBG #D.MAP,<"We are responsible for filling the region."> ; ; Next we need to create atleast one window, and map it ; in order to copy the game file... ; MOV APRMSK,R0 ; Get APR-mask MOV #-1,R1 ; R1 will have base address. 141$: INC R1 ; Next APR. ASR R0 ; Shift mask. BCC 141$ ; If APR not free, repeat. ; ; R1 now holds a base APR to use. ; .DBG #D.MAP,<"We'll use APR %D for filling.">,R1 MOVB R1,ZWDB+W.NAPR ; Window base APR MOV #8192./64.,ZWDB+W.NSIZ ; Size of window. CLR ZWDB+W.NOFF ; Offset. CLR ZWDB+W.NLEN ; Length. BIS #,ZWDB+W.NSTS ; More flags. CRAW$S #ZWDB ; Create address window and map it. CMP #IS.SUC,$DSW ; Okay? BNE 210$ ; No. .DBG #D.MAP,<"Created window ok."> ; ; Let's start filling it in. ; MOV DYNHI,R0 ; Block #. ASH #-9.,R0 ; First block of static mem. MOV ZRDB+R.GSIZ,R1 ; Size to copy. MOV #8192./64.,R2 ; Window size. MOV ZWDB+W.NBAS,R3 ; Get base address. 150$: MOV R1,R4 ; Copy rest of data. CMP R4,R2 ; If window is smaller... BLOS 160$ ; ... MOV R2,R4 ; ...we shall copy window size. 160$: MOV R4,R5 ; Get size to copy in R5. ASH #6,R5 ; Make into bytes. .BLK R3,R0,R5 ; Read data. ADD R4,ZWDB+W.NOFF ; Move map ahead. CLR ZWDB+W.NLEN DIR$ #DOMAP ASH #-9.,R5 ; Make size in blocks. ADD R5,R0 ; Next block... SUB R4,R1 ; Decrement data we need to copy. BNE 150$ ; ; Now we have copied all data. ; 200$: ;CLOSE$ #FDB ; Close game file. (Cannot do b.c. save/restore) MOV #8192.,R0 ; Cache size. MOV #-1,CHEFLG ; Set flag. RETURN ; Done. ; 210$: .MSG <"Failed to create address window. %P">,$DSW ; ; Small buffer cache. ; STDCHE: MOV PGSIZ,R0 ; Cache size. CLR CHEFLG ; Clear flag. RETURN ; ; CHEALL - Cache allocate ; ; Out: CS - No more cache memory available. ; CC - R0 - Block id. ; R1 - Base address ; .ENABL LSB CHEALL:: TST CHEFLG ; Large cache size? BNE 10$ ; Yes. .ALLOC PGSIZ ; No. MOV R0,R1 ; Physical address is also ID. RETURN ; Carry is preserved from .ALLOC ; 10$: MOV #1,R0 ; Mask. CLR R1 ; Base APR .DBG #D.MAP,<"Looking for free APRs."> 20$: BIT R0,APRMSK ; Is this APR free? BNE 30$ ; Yes. ASL R0 ; No. Check next bit. INC R1 ; And that is a new APR. CMP R1,#10 ; Beyond APR 7? BLO 20$ ; No. Loop. .DBG #D.MAP,<"No APR free."> SEC ; No more APRs available. Indicate. RETURN ; Done. 30$: .DBG #D.MAP,<"APR %D taken.">,R1 BIC R0,APRMSK ; APR taken. MOVB R1,ZWDB+W.NAPR ; Setup WDB. MOV #<8192./64.>,ZWDB+W.NSIZ ; Size of window. CLR ZWDB+W.NOFF ; Offset. CLR ZWDB+W.NLEN ; Length. BIS #WS.MAP,ZWDB+W.NSTS ; Flags. ; CLR ZWDB+W.NSTS ; Flags. CRAW$S #ZWDB ; Create address window. CMP #IS.SUC,$DSW ; Did we succeed? BNE 40$ ; No. MOV ZWDB+W.NBAS,R1 ; Base address. MOVB ZWDB+W.NID,R0 ; Id. TST MAPF ; Are we using fastmap? BEQ 39$ ; No. MOVB ZWDB+W.NAPR,R0 ; Yes. Use a different type of ID then. ASH #3,R0 ; Move into correct place for fastmap. BIT #WS.UDS,ZWDB+W.NSTS ; Do we use D-space? BEQ 39$ ; No. BIS #100,R0 ; Yes. Indicate in ID. 39$: .DBG #D.MAP,<"CHEALL returns with %P/%P">,R0,R1 RETURN 40$: .MSG <"Failed to create address window. %P">,$DSW JMP ABORT ; ; CHEUPD - Cache update ; ; In: R0 - Block id. ; R2,R3 - Virtual address ; ; Out: R0 - Offset to virtual address from virtual base. ; R2,R3 - Virtual base address. ; .ENABL LSB CHEUPD:: MOV R3,-(SP) ; Save offset. TST CHEFLG ; What model? BNE 10$ ; Large... ; ; Small... ; (Block id is the same as the physical address of the page) ; ASHC #-9.,R2 ; Shift down address to page. .BLK R0,R3,PGSIZ ; Read in data. ASHC #9.,R2 ; Get base address. MOV (SP)+,R0 ; Get offset... SUB R3,R0 RETURN ; Done. ; ; Large... ; 10$: .DBG #D.MAP,<"CHEUPD: %P -- %P,,%P">,R0,R2,R3 SUB DYNHI,R3 ; Drop dynamic memory from offset. SBC R2 ASHC #-6,R2 ; Offset is in 64B blocks. BIC #7,R3 ; And must be aligned on 512B. TST MAPF ; Using fastmap? BEQ 20$ ; No. ; FASTMAP MOV R1,-(SP) ; Yes. Save R1. MOV R3,R1 ; R1 shall be offset. MOV ZRDB+R.GSIZ,R2 ; Get end of region. SUB #200,R2 ; This is the highest offset we use... CMP R1,R2 ; Are we past that? BLOS 15$ ; No. MOV R2,R1 ; Yes. Use high limit instead. 15$: MOV R1,-(SP) ; Save offset. IOT ; Fast map. CMP #IS.SUC,R0 ; Checkl result. BNE 30$ MOV (SP)+,R3 ; Restore offset. CLR R2 MOV (SP)+,R1 ; Restore R1. BR 25$ ; NORMAL MAP 20$: MOV R0,ZWDB+W.NID ; Id. MOV R3,ZWDB+W.NOFF ; Offset. CLR ZWDB+W.NLEN ; Length to map. DIR$ #DOMAP ; Map window. CMP #IS.SUC,$DSW ; Okay? BNE 40$ ; No. ; 25$: ASHC #6,R2 ; Get base address. ADD DYNHI,R3 ADC R2 MOV (SP)+,R0 ; Get offset. SUB R3,R0 ; Make relative. .DBG #D.MAP,<"RETURN: %P -- %P,,%P">,R0,R2,R3 RETURN 30$: .MSG <"FASTMAP failed. %P %P(%P)">,R0,R1,ZRDB+R.GSIZ JMP ABORT 40$: .MSG <"MAP$ failed. %P %P(%P)">,$DSW,R3,ZRDB+R.GSIZ JMP ABORT ; .END