;*************************************************************************** ; ; C P / M 3 . 1 ( + ) ; ; Reconstructed from disk file image by SCG31. ; All labels and comments Copyright (c) 1984 by C.C. Software. ; Instructions Copyright (c) by Digital Research. ; ; CP/M is a trademark of Digital Research, Inc. ; ;*************************************************************************** ; ; Define external memory references. ; ABT$CHR EQU 3 ;Control-C used as an abort character. TAB EQU 9 ;Tab character. LF EQU 10 ;Line feed. CR EQU 13 ;Carriage return characte. CPM$DRIVE EQU 4 ;Current drive and user number storage byte. CPM EQU 5 ;Main entry point for the BDOS functions. TP$DRIVE EQU 50H ;Transiant program loaded from this drive. PASSWD1 EQU 51H ;Address of password for first file. NCHARS1 EQU 53H ;Number of characters in PASSWD1. PASSWD2 EQU 54H ;Address of password fo second file. NCHARS2 EQU 56H ;Number of characters in PASSWD2. DFT$FCB EQU 5CH ;First default FCB. SECOND$FCB EQU 6CH ;Second default FCB. DFT$BUFFER EQU 80H ;Default daata i/o buffer. ; ; File Control Block (FCB) definitions. ; DR EQU 0 ;Drive byte code. F1 EQU 1 ;File name, byte 1. F2 EQU 2 ;File name, byte 2. F3 EQU 3 ;File name, byte 3. F4 EQU 4 ;File name, byte 4. F5 EQU 5 ;File name, byte 5. F6 EQU 6 ;File name, byte 6. F7 EQU 7 ;File name, byte 7. F8 EQU 8 ;File name, byte 8. T1 EQU 9 ;File type, byte 1. T2 EQU 10 ;File type, byte 2. T3 EQU 11 ;File type, byte 3. EX EQU 12 ;File extent byte. S1 EQU 13 S2 EQU 14 RCNT EQU 15 ;Record count, this extent. D0 EQU 16 ;Data block storage. D1 EQU 17 D2 EQU 18 D3 EQU 19 D4 EQU 20 D5 EQU 21 D6 EQU 22 D7 EQU 23 D8 EQU 24 D9 EQU 25 D10 EQU 26 D11 EQU 27 D12 EQU 28 D13 EQU 29 D14 EQU 30 D15 EQU 31 REC EQU 32 ;Current record number. R0 EQU 33 ;Random record number, byte 0. R1 EQU 34 ;Random record number, byte 1. R2 EQU 35 ;Random record number, byte 2. ; ; Include the following lines if the loader is separated from ; the CCP which needs to reference these locations. ; ; PUBLIC SETUP,SET$NEXT,REMOVE,RELOCATE ; PUBLIC CHECKMEM,SCB$BASE,COMMON$BASE,RSX$BASE ; ; ; The ccp is a combination of a relocatable loader (in RSX format) ; and an absolute command processor. The first part is the loader. ; This will be relocated to highest ram (by itself) where it will ; process the "load overlay" bdos function #59. ; TPA$BASE: JP INIT ;Jump to initialize code. RSX$INIT:NOP ;Changed into a jump after relocation. NOP NOP JP XBDOS GO$BDOS:JP 6 ;Jump to next RSX (address filled by the loader). PREVIOUS:DEFW 7 ;Preious RSX address in chain. DEFB 0 ;Remove flag (0=dont remove). DEFB 0 ;Non-bank flag (always load). DEFB 'LOADER ' ;Name of loader RSX procedure. DEFB 255 ;Loader signal flag (last RSX in chain). DEFW 00000H ; ;*************************************************************************** ; ; System loader. This resides in highest ram to process the load-overlay ; function. This is a special RSX procedure and can load a COM or PRL ; type file and associated RSX procedures (relocating as necessary). ; ;*************************************************************************** ; ; Filter BDOS calls. Look for LOAD OVERLAY function (#59) and process ; this ourselves. ; ; This represents the system loader. The CCP will move this into ; highest ram memory and there it resides until the next warm boot. ; The code from 100 hex to 394 hex is a standard PRL type file in ; RSX format (the relocation bit map immediately follows the code). ; XBDOS: LD A,C CP 59 ;Load overlay (#59)? JP NZ,GO$BDOS ;Nope, just let the BDOS handle it. POP BC ;Okay, get return address. PUSH BC LD HL,0 ADD HL,SP ;Place it on our stack. LD SP,TMPSTACK LD (OLDSTACK),HL;Save users stack pointer. PUSH BC EX DE,HL LD (FCB$ADR),HL ;Store the address of the fcb to load. LD A,H ;If zero, then remove all RSX's. OR L PUSH AF CALL Z,REMOVE POP AF CALL NZ,LOADER ;Otherwise, load this new procedure. POP DE LD HL,TPA$BASE ;Check for an RSX header (pg-19). LD A,(HL) CP 0C9H ;Yes if just a return is present. JP Z,LOAD$NEXT LD A,D ;Check return address. DEC A ;0100 hex? OR E JP NZ,XRET LD A,(TPA$BASE+13);Yes... OR A JP NZ,XRET LD HL,(GO$BDOS+1);Reset the tpa space for this extra procedure. LD (6),HL LD (RSX$BASE),HL CALL SET$TPA XRET: LD HL,(OLDSTACK);Restore users stack pointer. LD SP,HL XOR A ;Clear result registers and return. LD L,A LD H,A RET ; ; Set error return code. ; LOAD$ERR:LD DE,0FEH ; ; Return location. Enter here with return code in (de). ; LDR$RET:LD HL,(OLDSTACK) LD SP,HL POP HL PUSH HL DEC H ;Is there a valid return address? LD A,H ;Note that 0100 would not be valid. OR L EX DE,HL LD A,L LD B,H RET NZ ; ; Load error. Program will not fit into memory. ; TOOBIG: LD C,9 ;Can not load program (not enough memory). LD DE,BADLOAD CALL CPM JP 0 ; ; Load an RSX procedure. ; LOAD$RSX: INC HL ;Extract length of RSX. LD C,(HL) INC HL LD B,(HL) LD A,(COMMON$BASE);Check common memory base (banked system?). OR A JP Z,INSTALL INC HL ;Yes, do we load this RSX? INC (HL) JP Z,SKIP$RSX ; ; Load RSX into highest memory. ; INSTALL:PUSH DE CALL CHECKMEM ;Make sure it will fit. POP HL CALL RELOCATE ;Move it into position and fit addresses. CALL SETUP ;Setup pointers to next RSX. SKIP$RSX:POP HL ; ; Load next RSX procedure. ; LOAD$NEXT:LD DE,16 ;Get address of PRL portion of proedure. ADD HL,DE PUSH HL LD E,(HL) INC HL LD D,(HL) ;Is there another RSX to handle? LD A,E OR D JP NZ,LOAD$RSX ;Yes... CALL RSX$INIT ;Done, some kind of initialization? LD A,(TPA$BASE+100H);Look for a null COM file. CP 0C9H ;They start with a RET instruction. JP NZ,LOAD01 LD HL,(SCB$BASE);No COM file, set flag bit #1 LD L,0B3H ;such that the CCP does not remove this LD A,(HL) ;when we return. OR 2 LD (HL),A LOAD01: LD HL,(TPA$BASE+1);The PRL file starts at 200, move it down LD B,H ;to 100 hex. LD C,L ;Byte count. LD HL,200H ;Source. LD DE,100H ;Destination. CALL BLKMOVE ;Move it. JP XRET ;All done. ; ; Setup the chain bytes (and flag bytes) for an RSX procedure. ; Each RSX has address pointers to the procedures in front and in ; back. ; SETUP: LD HL,(6) ;Set the first 6 bytes (this was the LD L,0 ;the serial number area. LD BC,6 ;Move 6 bytes from (hl) to (de). CALL BLKMOVE LD E,018H ;Clear loader flag byte. LD (DE),A LD E,CR ;Set address of previous RSX (if any). LD (DE),A DEC DE LD A,7 LD (DE),A LD L,E ;Fill in previous address to point to LD E,0BH ;this RSX. LD (HL),E INC HL LD (HL),D SET$NEXT: EX DE,HL ;Fill in pointer to next RSX (or the BDOS LD (HL),D ;if this is the last one). DEC HL LD (HL),6 SET$BASE:LD L,6 ;Set low address to point back to current LD (6),HL ;RSX procedure. LD (RSX$BASE),HL SET$TPA:LD DE,SCBPBLDR ;Store base of TPA in BDOS table (SCB). ACC$SCB:LD C,49 JP CPM ; ; Remove all completed RSX procedures. Note that only the ; lowest ones can actually be removed from the memory list. ; REMOVE: LD HL,(6) LD B,H REMOVE1:LD H,B ;Check the LOADER flag. LD L,018H INC (HL) ;<>0 and we reached the last one. DEC (HL) RET NZ LD L,0BH ;Get page of next RSX. LD B,(HL) LD L,0EH ;Check the REMOVE flag byte. LD A,(HL) ;=0 and we will not remove it. OR A JP Z,REMOVE1 LD L,0CH ;Okay, get address of previous RSX. LD E,(HL) INC HL LD D,(HL) LD A,B ;Make it point to the next one (this LD (DE),A ;effectively removes the present RSX). DEC DE LD A,6 LD (DE),A INC DE ;Make the next RSX point backward to the LD H,B ;presious one. This completes the pointer LD L,0CH ;chain. LD (HL),E INC HL LD (HL),D LD A,D ;Is this the bottom one? OR A PUSH BC CALL Z,SET$BASE ;Yes, set the upper TPA address. POP BC JP REMOVE1 ; ; File loader. This will read the file into memory and then relocate ; it (PRL files only). Note that this will determine the maximum ; number of sectors that can be read each time to speed the loading. ; LOADER: PUSH HL LD DE,GET$DMA ;Get current DMA address. CALL ACC$SCB EX DE,HL POP HL PUSH HL LD BC,REC ;Set record number in fcb to 0. ADD HL,BC LD (HL),0 INC HL LD C,(HL) ;Get load address. INC HL LD H,(HL) LD L,C DEC H ;Less than 100 hex? INC H JP Z,LOAD$ERR ;Yes, this is not allowed. PUSH HL PUSH DE PUSH HL CALL SET$ONE ;Get current multi-sector count POP HL PUSH AF ;and save it so we can restore this value LD E,080H ;when we finish. Prepare for large blocks. LD$NXT01:LD A,(7) ;Compute max number of sectors to be read DEC A ;at any one time. SUB H JP C,SMALL$SIZE ;Only 1 page available? INC A CP 64 ;Space >64 pages (16k) JP NC,LD$NXT03 RLCA ;Nope, compute max number of sectors to LD E,A ;read. LD A,L OR A ;Load address on a page boundry? JP Z,LD$NXT03 LD B,2 ;Nope, adjust for this address. DEC A JP M,LD$NXT02 DEC B ;Only one sector off. LD$NXT02:LD A,E ;Reducce the secor count in (e) by (b). SUB B JP Z,SMALL$SIZE ;Only a small area available? LD E,A ;Reset sector count. LD$NXT03:PUSH DE PUSH HL CALL SET$MULTI ;Set sector count to (e) and read the file. POP HL PUSH HL CALL RDNEXT ;Read next record(s). POP HL POP DE PUSH AF ;Save status. LD A,E ;Update memory pointer by (e) sectors. INC A RRA ADD A,H LD H,A LD (FSIZE),HL ;Save next available address. POP AF JP Z,LD$NXT01 ;Continue until end or error occurs. LD$NXT04:POP BC ;Note that we pushed (a) above. DEC A ;Set zero on end of file. LD E,B ;Reset multi-sector count to what user had CALL SET$MULTI ;prior to loading this last file. LD C,26 ;Set DMA address to what we had originally. POP DE PUSH AF ;Save read status flag. CALL CPM POP AF LD HL,(LDR$STAT);Get error return code. EX DE,HL JP NZ,LDR$RET ;Return on a read error. POP DE ;Read was okay, check for a PRL file. POP HL LD BC,9 ;Check extension, return if not PRL. ADD HL,BC LD A,(HL) AND 07FH CP 'P' ;A "P"? RET NZ INC HL LD A,(HL) AND 07FH CP 'R' ;An "R"? RET NZ INC HL LD A,(HL) AND 07FH SUB 'L' ;Then an "L"? RET NZ LD A,E ;Check load address, it must be a page boundry. OR A JP NZ,LOAD$ERR LD H,D ;All was okay. Relocate this file LD L,E ;from low memory to high and change all INC HL ;needed addresses via bit map. LD C,(HL) INC HL LD B,(HL) LD L,E ; ; Relocation routine. Memory from (hl)+100 will be moved ; to memory at (de), (bc) number of bytes. Immediately ; following the original code is bit map table. Starting ; from bit 7 to 0, each on bit represents a byte that must ; be adjusted (the upper half of an address). This is a cute ; routine. ; RELOCATE: INC H ;Bump source address by 100h. PUSH DE PUSH BC CALL BLKMOVE ;Move the code now. POP BC POP DE PUSH DE LD E,D ;Compute relocation offset value. DEC E PUSH HL LD H,E ;(h) is the offset value to use. ; ; Relocate the code that (de) points to. The address ; of the bit map table is on the stack. The code is (bc) ; bytes long. ; LD E,0 ;Point to start of code. RELMEM: LD A,B ;More bytes to relocate? OR C JP Z,RELM03 DEC BC ;Yes, need to get next byte from bit map? LD A,E AND 7 JP NZ,RELM01 EX (SP),HL ;Yes, move next byte into register (l). LD A,(HL) ;Note that pointer to bit map is on the stack. INC HL EX (SP),HL LD L,A RELM01: LD A,L ;Check next bit. RLA LD L,A JP NC,RELM02 ;Need to relocate this byte? LD A,(DE) ;Yes, offset is in (h). ADD A,H LD (DE),A RELM02: INC DE ;Move on to next byte. JP RELMEM RELM03: POP DE ;Restore (de) only, note that (bc) is zero, POP DE ;(h) is offset. RET ; ; Load file via normal DMA buffer area (there was not enough room ; in memory for fast procedure). ; SMALL$SIZE: CALL SET$ONE ;Set multi-sector count to (1). LD HL,DFT$BUFFER;Read into memory here. CALL RDNEXT JP NZ,LD$NXT04 ;End of file or error. LD HL,0FEH ;Sorry, file won't fit. LD (LDR$STAT),HL JP LD$NXT04 ; ; Check for enough memory below the BDOS to hold a program ; that is (bc) bytes long. Return with (de) containing the ; start of free memory that is available. Note that if ; there is not enough memory, a message is printed and a ; normal return is not executed. Besides room for the code, ; extra space must be available (15 pages minimum or what is ; specified with the file). ; CHECKMEM: LD A,(7) DEC A DEC BC SUB B ;Get lowest page requested. INC BC CP 15 ;Must be above ourselves of course. JP C,TOOBIG LD HL,(FSIZE) ;Check additional space required. CP H JP C,TOOBIG LD D,A ;Set (de) to the number of available LD E,0 ;pages of ram. RET ; ; Move a block of memory from (HL) to (DE) that is (BC) ; bytes long. ; BLKMOVE:LD A,B ;Test for done. OR C RET Z DEC BC ;Move next byte and adjust registers. LD A,(HL) LD (DE),A INC DE INC HL JP BLKMOVE ; ; Set the multi sector count to (1) and also get the previous ; value into (a). ; SET$ONE:LD E,1 ; ; Set multi-sector count to (e) and get previous value into (a). ; SET$MULTI:LD HL,(SCB$BASE);Value is stored in the sysstem tables. LD L,0E6H LD A,(HL) ;Get previous value. LD (HL),E ;And set new value. RET ; ; Read the next record from file into memory area ; pointed to by (DE). Return with zero flag set if okay. ; RDNEXT: EX DE,HL ;Set DMA address from M. LD C,26 PUSH HL CALL CPM LD C,20 ;Read next record from file. LD HL,(FCB$ADR) ;Get FCB of file. EX DE,HL CALL CPM LD (LDR$STAT),HL POP DE OR A RET Z LD E,H RET BADLOAD:DEFB CR,LF,'Cannot load Program 221282 COPYR ',39,'82 DRI ' DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0,0 SCB$BASE:DEFB 0,0 COMMON$BASE:DEFB 0 GET$DMA:DEFB 60,0 SCBPBLDR:DEFB 98,254 ; ; The following 37 bytes contain a bit map for the code ; from 100 to 394. For every bit that is set, the corresponding ; byte must be relocated. In addition, this area is used as ; data storage space after the code is relocated (saves space). ; ; Note: The contents of this area are set by RMAC and if any changes ; are made to the loader code, this area has to change also. The ; loader section can be separated from the CCP portion and assembled ; independantly. Then use DDT to combine the sections together again. ; RSX$BASE: DEFB 0,128 FSIZE: DEFB 0,0 FCB$ADR:DEFB 128,72 OLDSTACK:DEFB 65,16 LDR$STAT:DEFB 4,18,36,18,64,8,0,16,0,136,68,72,0,32,4,128 DEFB 0,9,0,32,0,0,1,32,0,0,16,0,0,17,18,0,0,65 TMPSTACK:DEFB 0,16,64,130,8,33,0,34,8,1,16,0,0,0,32,1,0,4 DEFB 8,1,2,8,36,18,0,36,64,0,132,0,2,4,0,0,0,0,0,0 DEFB 0,0,0 ; ; End of bit-map data. Fill remainder of page with EOF (1AH). ; DEFB 26,26,26,26,26,26,26,26,26,26,26,26,26,26,26,26 DEFB 26,26,26,26,26,26,26,26,26 ; ;*************************************************************************** ; ; Console Command Processor (CCP) for CP/M 3.1(+) ; ; This will handle all console interaction, load and execute user ; programs and built-in commands. Submit jobs and program chain ; functions are also processed here. ; ;*************************************************************************** ; ; ; Include the following lines if the loader is separated from ; the CCP which needs to reference these locations. ; ; EXTERNAL SETUP,SET$NEXT,REMOVE,RELOCATE ; EXTERNAL CHECKMEM,SCB$BASE,COMMON$BASE,RSX$BASE ; ; Start of the CCP program. Start off with a fake PRL prefix ; of 26 bytes. ; ; ORG 200H ;Include this line for separate loader. CCP: DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0 DEFB 0,0,0,0,0,0,0,0,0,0,0,0,0 ; ; Start of the command file processor. This is absolute code (normal ; COM type file). ; INIT: LD SP,OURSTK ;Use our own stack area. LD HL,MAINRET ;Install our own return address. PUSH HL LD DE,GET$SCB ;Get the address of the SCB table in the BDOS. LD C,49 CALL CPM LD (SCB$BASE),HL LD L,0FAH ;Set base address of common memory. This LD A,(HL) ;is zero if in non-banked mode. LD (COMMON$BASE),A LD L,099H ;Get base page of BDOS. LD A,(HL) LD (BASEPAGE),A LD A,(7) SUB (HL) JP NZ,INIT01 LD BC,0294H ;See if there is enough room to move the CALL CHECKMEM ;loader into high mem. LD H,E ;Okay, clear (hl). Note (de) is addr of LD L,E ;available mem (e)=0. CALL RELOCATE ;Move and relocate the loader code. LD HL,(6) ;Install the first 6 bytes from the base of LD L,E ;the BDOS to the start of the loader code LD C,6 ;(just extends the BDOS area). CALL MOVE$C ;Move (c) bytes from (hl) to (de). LD E,0BH ;Reset the jump address at relative byte (e) CALL SET$NEXT ;to BDOS+6 and reset the TPA base address. ; ; Initialize our table parameters and flag bytes. ; INIT01: LD C,98 ;Return all free blocks (unclosed files) CALL CPM ;to the system for re-use. LD B,0B6H ;Get relative parameter #182 from table. CALL GET$PARM ;Get some system parameters. INC A ;Get console screen width (zero relative). RRCA ;Make absolute, divide by 16. This becomes the RRCA ;number of directory names that can be printed RRCA ;per line. RRCA AND 0FH LD DE,DIRSIZE LD (DE),A LD L,0B8H ;Get lines per screen parameter. LD A,(HL) DEC A INC DE LD (DE),A XOR A ;Set "page" output mode. INC DE LD (DE),A LD A,'$' ;Add a delimitor. INC DE LD (DE),A LD L,0D3H ;Set this as the standard delimitor also. LD (HL),A LD L,0E6H LD (HL),1 ;Set multi-sector count to (1). INC HL XOR A ;Set BDOS error mode to (0). LD (HL),A LD L,0CFH ;Set console mode to (1). LD (HL),1 INC HL LD (HL),A ;Set RSX input mode to true. LD L,0A1H LD (HL),31H ;Set BDOS version number. LD L,0B4H ;Do we have to reset the disks? LD A,(HL) AND 020H ;Check bit 5. LD C,13 PUSH HL CALL NZ,CPM ;Yes, then do it. POP HL LD L,0B3H ;Do we have to remove all completed RSX's? LD A,(HL) AND 2 ;Check bit 1. PUSH HL CALL Z,REMOVE ;Yes, then do this also. POP HL LD A,(HL) ;And say that they have been removed. AND 0FDH LD (HL),A AND 040H ;Check bit 6, if set then the default PUSH HL ;user number will be used. Otherwise the LD L,0B0H ;previous number will be used. LD BC,USR$NUM LD D,H LD E,0E0H ;Get default user number. LD A,(DE) LD (BC),A ;Install it. LD A,(HL) ;Get previous user number. JP NZ,INIT02 LD (BC),A ;Use it instead. INIT02: LD (DE),A ;Reset default user number. INC BC LD E,0DAH ;Get current disk. LD A,(DE) JP NZ,INIT03 LD A,0FFH ;Use special drive (search chain?). INIT03: LD (BC),A ;Save disk number. DEC HL INC BC ;Get disk to access. LD A,(HL) LD (BC),A LD (DE),A ;Set previous disk. LD L,0ECH ;Get disk for temp files. INC BC LD A,(HL) ;Save it. LD (BC),A POP HL LD A,(HL) ;Check for a program chain request. AND 80H JP Z,INIT05 LD HL,DFT$BUFFER;Yes, next command must already be here. ; ; Fill input buffer from a previous chain request or use the ; standard auto-start program name. ; INIT04: LD DE,IN$BUFF+1 LD C,127 ;Set data length. LD A,C LD (DE),A INC DE CALL MOVE$C ;Move new buffer into place. JP EXECUTE ;Process this line. INIT05: LD L,0B5H ;Do we exucte built-in command? LD A,(HL) AND 2 JP NZ,CMDRET LD A,(HL) ;Yes, but only once. OR 2 LD (HL),A LD (AS$MODE),A LD HL,AUTO$START;This is the command (what sets it?). JP INIT04 AUTO$START:DEFB 'PROFILE.S',0 ; ; Command error return location. Turn off various other processing ; (submit or next command on single line). ; CMDRET: CALL SET$BC CALL CRLF ; ; Main command return location. Check for more submit data. ; MAINRET:LD HL,MAINSTK LD SP,HL XOR A LD (ROW$NUM),A ;Set row number to zero. LD HL,MAINRET ;Establish return location (normal). PUSH HL CALL SET$BC DEC HL ;Processing a submit file? LD A,(HL) AND 1 ;Test bit 0. JP Z,GET$COMMAND LD DE,IN$BUFF+1 ;Nope, get next line from user. CALL SETDMA ;Okay, set dma address. LD C,15 ;Open temp submit file ($$$.SUB). CALL TMP$FILE LD C,11 ;Check keyboard, quit on ^C. CALL Z,TMP$FILE ;Note that this just checks console. JP NZ,SUB$EOF ;Delete file and continue. LD HL,TEMP$SUB+R2;Clear random record number (upper 2 bytes). LD (HL),A DEC HL LD (HL),A DEC HL PUSH HL LD A,(TEMP$SUB+RCNT);Decrement record number. DEC A LD (HL),A LD C,33 ;Read next record (if any remain). CALL P,TMP$FILE POP HL DEC (HL) ;Did we complete this file? LD C,19 CALL M,TMP$FILE ;Yes, delete it. OR A ;Good read? ; ; Done reading, if zero flag is set then we will process this last ; line of data. Otherwise, we had an error and we will delete the ; temp file and ask for the next command from the console. ; SUB$EOF:PUSH AF ;Save error code. LD C,99 ;Truncate the temp file. CALL TMP$FILE POP AF JP Z,EXECUTE ;Did we have an error? LD BC,0B301H ;Yes, clear the submit file flag bit CALL CLR$BITS LD C,19 ;and delete the temp file. CALL TMP$FILE ; ; GEt the next command line from the console. ; GET$COMMAND: LD A,(USR$NUM) ;Print user number if not zero. OR A CALL NZ,DECOUT CALL PRTLOG ;Print drive name (A-P). LD A,'>' ;Add prompt (>). CALL PAGE$OUT LD DE,0B1BAH ;Extract address of continued input data. CALL MOVE2 OR A ;Set zero flag if nothing is present. PUSH AF LD BC,0B480H CALL NZ,CLR$BITS ;Clear bit 7 of (B4) if more input is available. CALL GETLINE ;Get input line from console. CALL CLR$BC ;Now clear bits 6,7 of (B4). POP AF CALL NZ,CONTINUE ;Adjust system table parameters. ; ; Execute command line from buffer. ; EXECUTE: CALL GET$MODE ;Use current or previous page mode? JP NZ,EXEC01 LD L,0C9H ;Get previous page mode byte. LD A,(HL) DEC HL LD (HL),A ;Reinstate this mode. EXEC01: LD L,0C8H ;Set page mode flag per BDOS direction. LD A,(HL) LD (PAGE$FLG),A CALL SCANLINE ;Scan input line, make UC and look for "[". RET Z ;Nothing entered? LD DE,USR$BLK CALL CHKFNAME ;Separate drive name and user number. LD A,(FCB+T1) ;Any extension entered? CP 020H JP NZ,EXEC04 ;Yes, go try to execute. LD HL,USR$BLK LD A,(HL) ;If a user number or drive was specified, INC HL ;just execute the program given. Otherwise OR (HL) ;we will check for a built-in command first. INC HL LD A,(HL) JP NZ,EXEC05 LD HL,CMDTBL ;Check for a built-in command. LD DE,FCB+1 ;(FCB+1 is start of name). LD A,(FCB+3) CP 021H ;Is name at least 3 chars long? CALL NC,COMPARE ;Yes, search for it. JP NZ,EXEC02 ;Match not found? LD A,(OPTIONS) ;Okay, command #(b) found, were any options OR A ;specified in initial command line? LD A,B LD HL,(INP$PTR) ;Save current pointer address incase of an LD (TEMP$PTR),HL;error. Then we can write bad file name. LD HL,ADRTBL JP Z,TBL$JUMP ;Nope, we can execute it then. CP 4 ;Options specified, is it command # 0-3? JP C,GET$ERR ;Yes, just execute command file of same name. LD HL,FCB+4 JP NZ,EXEC02 ;Was it #4 (DIRS)? LD (HL),' ' ;Yes, change name to DIR for loading. EXEC02: LD BC,0B418H CALL TST$BC ;Test relative byte (b4) anded with (18). JP Z,EXEC04 LD B,8 ;Set default extension. SUB B JP Z,EXEC03 LD B,0 EXEC03: PUSH BC ;Set ext, (a)=0 COM, (a)=8 SUB, (a)=16 PRL. CALL SET$EXT CALL PROCESS POP AF CALL SET$EXT EXEC04: CALL PROCESS ;Process command (only returns on an error). JP WHAT? ;Illegal extension given. EXEC05: CP ' ' JP NZ,EXEC02 CALL MORE LD A,(USR$BLK) SUB 1 JP C,LOGINDSK ; ; Set user number from (a). ; SET$USR:LD (USR$NUM),A ;Save number for us LD B,0B0H ;and for the system. CALL SET$PARM CALL SET$USER ;Put into SCB for other programs also. ; ; Login the specified disk if it is not the default disk. ; LOGINDSK:LD A,(FCB) ;Get disk drive. DEC A ;Default reference? RET M ;Yes, ignore. PUSH AF ;Nope, select it. CALL SELDSK POP AF LD (LOGDRIVE),A ;Then save it for ourselves. LD B,0AFH JP SET$PARM ;Also put into system tables. ; ; Built-in command table. All entries must end with a space. ; The end of the table is with a null. ; CMDTBL: DEFB 'DIR ' DEFB 'TYPE ' DEFB 'ERASE ' DEFB 'RENAME ' DEFB 'DIRSYS ' DEFB 'USER ' DEFB 0 ; ; Address table for above commands. These must be in the ; same order. ; ADRTBL: DEFW DIR,TYPE DEFW ERASE,RENAME DEFW SYSDIR,USER ; ; Drive print functions. ; PRTDRIVE:LD A,(DFT$FCB) ;Get drive name from fcb. PRTDRV$A:DEC A ;Use loged-in drive or (a)? JP P,PRTDR1 PRTLOG: LD A,(LOGDRIVE) ;Get loged-in drive. PRTDR1: ADD A,'A' ;Make (A) into drive name. JP CONSOLE ; ;*************************************************************************** ; ; Directory commands, the first entry (DIR) prints non- ; system names and the second entry SYSDIR prints all ; names. ; ;*************************************************************************** ; DIR: LD C,0 ;Clear system flag bit 7. LD DE,SYSFILES ;Load address of warning message. JP PRTDIR SYSDIR: LD C,080H ;Set system flag bit 7. LD DE,NONSYS ;Load address of warning message. PRTDIR: PUSH DE CALL PRINTDIR ;Print full directory. POP DE JP Z,NONE ;No files printed? LD A,L ;End last line if required. CP B CALL NC,CRLF LD HL,SKIPED ;Did we skip over any filenames? DEC (HL) INC (HL) RET Z DEC (HL) ;Yes, reset flag and display appropriate JP PRT$CRLF ;message. ; ; Print directory now in pages. Note that (de) points to a ; warning message to be displayed if some file names are ; not listed. Register (c), bit 7 indicates what type of ; files will be printed (0=normal, 1=system). ; Return with zero flag set if no names are listed at all. ; PRINTDIR:PUSH BC CALL DFTDMA CALL PARSEINP ;Parse input file descriptor. LD DE,DFT$FCB+1 ;All blanks? LD A,(DE) CP ' ' LD B,11 ;Set string length. CALL Z,FILLSTR ;Yes, fill field with "?". CALL MORE ;A second argument is not allowed. CALL GETFIRST ;Look for first name. POP BC RET Z ;None? LD A,(DIRSIZE) ;Get # of directory names per line allowed. LD L,A LD B,A INC B PRTD01: PUSH HL ;Look at system bit. LD HL,10 ADD HL,DE LD A,(HL) POP HL AND 080H CP C ;Do we show this file? JP Z,PRTD02 LD A,1 ;No, say we skiped some names. LD (SKIPED),A JP PRTD03 PRTD02: DEC B ;Reached end of line? CALL Z,CRLFX ;Yes, start new line and then print drive name. LD A,B CP L CALL Z,PRTDRIVE ;Put drive name at start of line. LD A,':' ;Add colon now. CALL CONSOLE CALL SPACE CALL PRTFNAME ;Print filename and extension. CALL SPACE PRTD03: PUSH BC ;Move on to the next name. PUSH HL CALL CHKCON ;We will quit on any key. CALL GETNEXT ;Lookup next name. POP HL POP BC JP NZ,PRTD01 ;More names to print? INC A ;No, clear zero flag and return. RET ; ;*************************************************************************** ; ; TYPE command processor. ; ;*************************************************************************** ; TYPE: LD HL,CMDRET ;Set return address. PUSH HL CALL PARSEINP ;Parse input file name. LD A,07FH ;Set buffer length to 128 bytes for TYPE. LD (BUFPNTR),A LD C,15 ;Open the file now. CALL GETFILE ;Note, this just prints 'file reqd' if none ; ;found. TYPE01: CALL CHKCON ;Check console and abort on any key. CALL FREAD ;Read next byte from file. RET NZ ;Done if none left or end-of-file reached. CP 01AH RET Z CALL PAGE$OUT ;Print next character and continue. JP TYPE01 ; ;*************************************************************************** ; ; USER command processing. Set desired user number. ; ;*************************************************************************** ; USER: LD DE,ASKUSER ;Ask for user number. CALL INP$NUM CALL DEC2BIN ;Convert number to binary, return in (a). RET Z ;Nothing entered? JP SET$USR ;Set new user number and return. ; ;*************************************************************************** ; ; Erase a specified file (or files). If an ambigeous name ; was entered, ask for confirmation. ; ;*************************************************************************** ; ERASE: CALL PARSEINP ;Parse input file name. JP Z,DEL$FILE ;No file given (note zero flag setting)? CALL WILDS ;Any wild cards used in name? JP NZ,DEL$FILE LD DE,ERASE? ;Yes, request confirmation first. CALL PRINT LD HL,(LINE$PTR);Print file specification up to a space. LD C,' ' CALL LINEOUT LD DE,YES$NO CALL PRTCHK ;Print '(Y/N)' and get a single char response. CALL CRLF LD A,L ;If 'Y' was typed, then erase the file(s). AND 05FH ;Note that this converts (a) to upper case CP 'Y' ;only when we are interested in a single RET NZ ;letter comparison. 'Y' typed? OR A ;Yes, clear zero flag. DEL$FILE:LD C,19 ;Set 'delete file' function. Note status of JP GETFILE ;of zero flag. ; ;*************************************************************************** ; ; RENAME command processor. ; ;*************************************************************************** ; RENAME: CALL PARSEINP ;Parse input file specification. PUSH AF ;Note zero set if none (save this). LD HL,16 ;Point to second name. ADD HL,DE EX DE,HL PUSH DE PUSH HL LD C,16 ;Move it into fcb+16 CALL MOVE$C CALL PARSEINP ;Parse it also. POP HL POP DE CALL DRIVECHK ;Check drives specified (must be the same). LD C,23 ;Set "rename" function. POP AF ; ; Execute file bdos fuction (c). Note that if the zero flag ; is set, then "file required" will be printed instead. ; GETFILE:PUSH AF ;Save zero flag. CALL NZ,MORE ;Anything remaning on input line is an error. POP AF LD DE,DFT$FCB LD B,0FFH LD H,1 ;Set (h) to error byte. CALL NZ,ACCESS ;Access the file according to (c). RET NZ ;Return if successful. DEC H ;File not found? JP M,NONE LD HL,(TEMP$PTR);Print file name then... LD (INP$PTR),HL GET$ERR:CALL PROCESS CALL PRTFNAME LD DE,REQD ;..."required" message. JP PRT$ERR ; ; Check that the specified drives (including defaults) have ; been specified consistantly (the same drive referenced). ; DRIVECHK:LD A,(DE) ;Get first drive. CP (HL) ;Same as second? RET Z ;Yes, then all is okay. OR A ;Nope, was second un-specified? RET Z ;Yes, this is okay too. INC (HL) ;Nope, but was the other specified? DEC (HL) JP NZ,WHAT? ;Nope. This is an error. LD (HL),A ;Okay, make both the same. RET ; ;*************************************************************************** ; ; General purpose subroutine and processing section. ; ;*************************************************************************** ; ; Search up to 11 bytes for a wild card character (?). Set ; the zero flag if one was found. ; WILDS: LD B,11 ;Set search limit to 11. ; ; Search file specification (de) for wild cards. Only look ; through (b) bytes. Set the zero flag if found. ; XWILDS: INC DE ;Get next byte. LD A,(DE) CP '?' ;A wild card? RET Z ;Yes, return on zero. DEC B ;Not yet, more to look at? JP NZ,XWILDS DEC B ;None found, clear zero flag and RET ;return. ; ; Input a user number from the console if one was not ; included with the USER command. ; INP$NUM:CALL NONBLANK ;Was a number entered? RET NZ ;Yes, just use it. CALL PRINT ;Print 'enter user #' message. CALL GETLINE ;Read line in response. JP SCANLINE ;Scan this line for special chars and return. ; ; File accessing error messages. ; NONE: LD DE,NO$FILE ;A file was not found with indicated name. PRT$ERR:CALL PRT$CRLF ;Print message (de) and carriage return. JP MAINRET ;Error is fatal, just return. ; ; Define SUBMIT file command processor to handle .SUB files. ; SUBMIT: DEFB 0,'SUBMIT COM' ; ; A file was specified with a .SUB extension. Enter the ; name SUBMIT.COM in front to simulate a submit job. ; SUB$FILE:LD A,(DE) ;Save drive to re-select. LD B,0ABH CALL SET$PARM LD HL,SUBMIT ;Move submit file name into place. LD C,12 CALL MOVE$C LD HL,IN$BUFF+1 ;Add separating space. LD (HL),' ' INC HL ;Pretend this was typed (in case of errors). ; ; Process the command line. Assume that a file name was ; given that we should try to load. ; LD (INP$PTR),HL PROCESS:LD DE,FCB+T1 ;Check extension against our table. LD HL,EXT$TBL CALL COMPARE RET NZ ;Not found, just an error. LD DE,USR$BLK ;Was user number specified? LD A,(DE) OR A RET NZ INC DE ;Nope, check first byte of FCB. If no drive LD A,(DE) ;was specified, enable drive search chain. LD C,A PUSH BC LD C,0 ;Flag to disable search. OR A JP NZ,DRVS03 ;Nope, use loged in drive only. LD BC,0E704H ;Access search chain(4 items max). LD A,(LOGDRIVE) ;Get default drive. INC A LD H,A LD L,1 ;One drive only. DRV$SRCH:INC B DEC C ;More drives to search for? LD A,C PUSH HL CALL P,GET$PARM ;If yes, then extract next into (a). POP HL OR A ;Done with chain? JP M,NO$MORE JP Z,DRVS01 ;Nope, but use default drive instead? CP H ;Nope, check to see if we are going to use JP NZ,DRVS02 ;the default drive anyway. ; DRVS01: LD A,H ;Use default drive. DEC L ;Only once though.k JP M,DRV$SRCH ; ; Look for the file on drive (a). (de) contains the address of the ; FCB. ; DRVS02: LD (DE),A DRVS03: PUSH BC PUSH HL CALL FOPEN ;Open the file now. POP HL POP BC JP Z,DRV$SRCH ;Can't? LD BC,0B403H ;Okay, do we print message to this effect? CALL TST$BC JP Z,EXT$JUMP LD A,(DE) ;Yes, print "d:filename (user)". CALL PRTDRV$A ;Print drive name. LD A,03AH ;Print colon next. CALL CONSOLE PUSH DE ;Print filename. CALL PRTFNAME POP DE PUSH DE LD HL,8 ;Check the 'f8' bit. If it is set, then ADD HL,DE ;"(user 0)" is also printed. This indicates LD A,(HL) ;that the file was found on user zero and AND 080H ;will become read-only. LD DE,USER0 CALL NZ,PRINT CALL CRLF POP DE ;Restore fcb pointer. ; ; Jump to section to handle files with one of the supported ; extension types. ; EXT$JUMP:POP AF LD HL,EXT$ADR ;Address of procedure for each type. ; ; Table directed procedures. Enter here with the relative ; table address in (a) and the beginning of the table in (hl). ; This will jump to the indicated entry (note that no range ; checking is performed). ; TBL$JUMP:ADD A,A ;Convert entry number to byte count (*2). CALL ADDHL PUSH DE LD E,(HL) ;Extract address from table. INC HL LD D,(HL) EX DE,HL POP DE JP (HL) ;Execute routine now. ; ; Table of possible extensions. Each must end in a space. The ; table must end with a null byte. ; EXT$TBL:DEFB 'COM ' DEFB 'SUB ' DEFB 'PRL ' DEFB 0 ;End of table. ; ; Addresses to handle the above file types. ; EXT$ADR:DEFW COM$PRL DEFW SUB$FILE DEFW COM$PRL ; NO$MORE:POP BC LD A,C LD (DE),A RET ; ; Set the default extension type to that speccified in (a). ; (a)=0 COM, (a)=8 SUB, (a)=16 PRL. ; SET$EXT:RRCA ;Divide by 2 (change into byte count). LD HL,EXT$TBL ;Get start of extension table. CALL ADDHL ;Index into table. LD DE,FCB+T1 ;Move extension into place now (3 bytes). LD C,3 JP MOVE$C ; ; Process a COM or PRL type file. ; COM$PRL:LD HL,100H LD (PARSE$FCB),HL LD HL,(BUFPNTR) DEC H LD L,0C0H PUSH HL LD A,(DE) ;Save drive that program comes from. LD (TP$DRIVE),A EX DE,HL LD C,35 CALL MOVE$C LD HL,AS$MODE INC (HL) LD HL,(INP$PTR) ;Move input line down to 80h. DEC HL LD DE,81H EX DE,HL LD (INP$PTR),HL CALL MOVSTRNG LD (DFT$BUFFER),A;Add character count. CALL PARSEINP ;Parse input filename. LD (PASSWD1),HL ;Store address of first password. LD A,B LD (NCHARS1),A ;And it length. LD DE,SECOND$FCB;Parse second filename (if present). CALL PARSEIT LD (PASSWD2),HL ;Store address of second password. LD A,B LD (NCHARS2),A ;And its length. LD HL,DSK$NUM LD A,(HL) OR A CALL P,SELDSK ;Select specified disk. LD A,(USR$NUM) ;And user number. CALL SET$USER ADD A,A ;Move user number into high nybble. ADD A,A ADD A,A ADD A,A LD L,0DAH ;Add in current drive. OR (HL) LD (CPM$DRIVE),A;Store here for CP/M 2.2 compatibility. POP DE LD HL,(BASEPAGE-1) XOR A LD L,A LD SP,HL ;Setup stack and return locations. LD H,A PUSH HL ;Setup return addresses, first 0000 INC H PUSH HL ;then 0100 hex. LD (DFT$FCB+REC),A;Set record number in FCB to zero. LD B,0CFH CALL SET$PARM ;Set console mode to normal (low byte=0). LD L,090H ;Interesting, this sets the SCB -9,10,11,12 LD (HL),A ;to zero. What is here? INC HL LD (HL),A INC HL LD (HL),A INC HL LD (HL),A LD L,0B3H ;Check to see if we are progress of chaining. LD A,(HL) AND 080H JP NZ,BEGIN ;Yes, leave return code alone. LD L,0ACH ;Nope, clear return code. LD (HL),A INC HL LD (HL),A BEGIN: LD A,(HL) ;Clear the chain flag bit. AND 03FH LD (HL),A LD C,59 ;Call the loaded to load the program and JP CPM ;start execution. ; ; Output (a) to the console in "page" mode. If this fills ; the screen, we will wait for a key before continuing. ; PAGE$OUT:CP LF ;Line feed? JP NZ,CONOT2 LD HL,SCRNSIZE LD A,(HL) ;Get screen row limit. INC HL INC (HL) ;Count this last line. SUB (HL) ;Reached limit? JP NZ,LINEFEED LD (HL),A ;Yes, reset current row. INC HL LD A,(HL) ;Do we wait between pages? OR A LD DE,WAITING CALL Z,PRTCHK ;Print wait message? CP ABT$CHR ;Do we abort? JP Z,CMDRET ;Yes, do it then. LD E,CR ;Okay, move to the start of this line. CALL CONOT3 ;And continue with the listing. LINEFEED:LD A,LF ;Output one line feed character. ; ; Output (a) to the console without further interogation. ; CONOT2: LD E,A CONOT3: LD C,2 ;Just output (E) to the console now. JP CPM ; ; Print message and get a single char in response. ; PRTCHK: CALL PRINT ;Print message and check keyboard. LD C,1 JP CPM ; ; Print message pointed to by (de).It must end with a '$'. ; PRINT: LD C,9 ;Print string function. JP CPM ; ; Get a line of data from the console. This executes the ; buffered read function such that normal correction keys ; will be recognized. ; GETLINE:LD HL,IN$BUFF ;Input a line from the console. LD (HL),0E7H ;Set max line length to 231. EX DE,HL LD C,10 ;Read a line into buffer pointed to by (de). CALL CPM LD HL,IN$BUFF+1 ;Check number of chars in line just entered. LD A,(HL) INC HL CALL ADDHL ;Add trailing null byte. LD (HL),0 JP CRLF ;Output end of line. ; ; Check the console for a key. If one is ready, then just ; abort current operation (jump to return address). The ; zero flag is set if nothing entered (any one care?). ; CHKCON: CALL CONSTAT ;Check console, abort on any key. RET Z JP CMDRET ; ; Interogate the console keyboard. Return with zero set ; if nothimng is ready. Otherwise return with character ; in register (a) with zero cleared. ; CONSTAT:LD C,11 ;Check console keyboard. CALL BDOS ;This will set flags on (a) before returning. RET Z ;Just return on zero (nothing ready). LD C,1 ;Okay, get character and return with it in (a). JP BDOS ;Note that this will clear the zero flag if ; ;char is not null. ; ; Set the default DMA address to 80 hex. ; DFTDMA: LD DE,DFT$BUFFER ; ; Set the data transfer address (or DMA address) to (de). ; SETDMA: LD C,26 JP CPM ; ; Select the drive as specified in register (a). ; SELDSK: LD E,A LD C,14 JP CPM ; ; Set the current user number to (a). This directly accesses ; the System Control Block (SCB). ; SET$USER:LD B,0E0H JP SET$PARM ; ; Open the file specified at FCB. The proper drive will ; be loged in. ; FOPEN: LD BC,15 ;Set b=0 (special?), c=15 (open file function). LD DE,FCB ;Get fcb address. ; ; Access the file specified by (de). The access function ; must be in (c). Register (b) is used as drive mask byte ; possibly to get/set/ignore time and date stamps? ; ACCESS: LD HL,REC ;Clear record number byte in fcb. ADD HL,DE LD (HL),0 PUSH BC PUSH DE LD A,(DE) ;Get drive specified in fcb. AND B ;And with specified mask byte (reason?). DEC A ;Select this drive if it is not the current CALL P,SELDSK ;drive (0). LD DE,TEMPDMA ;Set the dma address in case file i/o is CALL SETDMA ;involved in the specified function. POP DE POP BC PUSH DE LD HL,(SCB$BASE) LD L,0E7H LD (HL),B ;Save (b) in system tables before access. PUSH HL CALL CPM ;Perform desired file function. POP DE XOR A ;Reset system table byte (function?). LD (DE),A LD A,(LOGDRIVE) ;Reset loged in drive. LD E,0DAH ;Put in BDOS table (SCB). LD (DE),A PUSH HL CALL DFTDMA ;Reset dma address. POP HL ;Restore (hl) from return from access function. INC L ;Depending on access function, this may be an error flag. POP DE RET ; ; Look for a file in the directory. Either search ; for the first name or the next name. ; Return with the zero flag set if not found. ; GETFIRST:LD C,011H JP GET01 GETNEXT:LD C,18 GET01: LD DE,DFT$FCB ;Lookup file in directory. CALL CPM INC A RET Z ;Not there? DEC A ;Point to extent entry. ADD A,A ADD A,A ADD A,A ADD A,A ADD A,A LD HL,80H CALL ADDHL EX DE,HL XOR A ;Clear zero flag. DEC A RET RDNXTREC:XOR A LD (BUFPNTR),A LD C,20 ;Read next record. LD DE,DFT$FCB ; ; Call bdos function in (c) and set flags on (a) upon return. ; BDOS: CALL CPM OR A RET ; ; Access the submit temporary file "$$$.sub". ; TMP$FILE:LD DE,TEMP$SUB JP BDOS ; ; Scan the input line for special characters. Note that this ; stops on a semi-colon or exclamation mark and if an ; option lead-in is found ([), a flag is set. ; SCANLINE:CALL CLR$BC EX DE,HL XOR A ;Clear "option included" flag byte. LD (OPTIONS),A LD HL,IN$BUFF+2 ;Get next non-blank character. CALL NBLANK1 EX DE,HL CP ';' ;Ignore lines that start with a semi-colon. RET Z CP '!' ;Ignore leading exclamation point also. JP Z,SCAN01 CP ':' ;Is first character a colon? JP NZ,SCAN02 LD L,0ACH ;Yes, check return code. INC (HL) INC (HL) JP Z,SCAN01 ;0fe hex mean no error. INC HL INC (HL) ;A high byte id 0ff hex is an error. RET Z ;So skip this command line. SCAN01: INC DE ;Skip this character. SCAN02: EX DE,HL LD (INP$PTR),HL SCAN03: LD A,(HL) CP '[' ;Start of option? JP NZ,SCAN04 LD (OPTIONS),A ;Yes, set flag byte non-zero. SCAN04: CP 'a' ;Convert (A) to upper case. JP C,SCAN05 CP '{' JP NC,SCAN05 SUB 020H LD (HL),A SCAN05: CP '!' ;Multiple command separator? CALL Z,MULTIPLE ;Yes, move the remainder of the line into INC HL ;a safe area. OR A JP NZ,SCAN03 ; ; Find location of argument after command. Set zero flag if ; there is none. This will skip intervening blanks and tabs. ; NONBLANK:LD HL,(INP$PTR) ;Get pointer into command line. NBLANK1:LD (INP$PTR),HL ;Save current pointer. LD (LINE$PTR),HL LD A,(HL) ;Get next character. OR A ;End of line? RET Z CP ' ' ;Nope, a space? JP Z,NBLANK2 ;Yes, skip to next char. CP TAB ;How about a tab? RET NZ ;Nope, return with this character. NBLANK2:INC HL ;Bump to next character. JP NBLANK1 ; ; Multiple command line found, create a special RSX procedure to ; hold the remaining data (just below the lowest existing RSX). ; Then tell the BDOS about it (so we can find it next time). ; MULTIPLE: LD E,L LD D,H INC DE LD A,(DE) ;Check for 2 in a row. CP '!' PUSH AF PUSH HL CALL Z,MOVSTRNG ;Yes, just eliminate the first one. POP HL POP AF RET Z ;And return, we will get back here shortly. LD (HL),0 ;Place a null in its place. EX DE,HL LD HL,(6) ;Get low RSX address. DEC H ;Move to one page before it. LD L,018H ;Put the remaining line from here to the end. LD (HL),A PUSH HL MULTI01:INC HL ;Moveline while replacing all marks with INC DE ;carriage returns. The BDOS will return the LD A,(DE) ;next line up to (cr). LD (HL),A CP '!' ;Separator? JP NZ,MULTI02 LD (HL),CR ;Yes, change to a (cr). MULTI02:OR A ;End of line? JP NZ,MULTI01 LD (HL),CR ;Yes, append a (cr). INC HL LD (HL),A ;Then a null byte. LD L,6 LD (HL),0C3H ;Put jump instruction from xx06 to xx09 INC HL ;(this RSX performs no other action). LD (HL),9 INC HL LD (HL),H INC HL LD (HL),0C3H ;At nine, insert another jump instruction. LD L,0EH LD (HL),A ;Zero REMOVE flag so this is not removed. LD L,A ;Point to start of procedure EX DE,HL CALL SETUP ;let SETUP fix all address pointers. LD HL,(SCB$BASE);Put address of this data line into the LD L,0B1H ;SCB tables for the BDOS to use. POP DE INC DE LD (HL),E INC HL LD (HL),D LD L,0AEH ;Set flag saying the information is present. LD (HL),D XOR A ;Set end-of-line indication. RET ; ; Process a continuation line. This will check for an abort request ; (^C) or end of data (in which case the RSX is marked as removable). ; CONTINUE: LD DE,0BAB1H ;Move address of next character. CALL MOVE2 OR A ;High byte zero? LD DE,0BCB1H CALL Z,MOVE2 ;Yes, then move data from here instead. PUSH HL CALL CONSTAT ;Check for an abort request. POP HL LD L,0B1H JP NZ,CMD$END ;Yes, clear flags to stop processing. LD E,(HL) ;Get address of remainder of line. INC HL LD D,(HL) INC (HL) ;Is there any? DEC (HL) DEC HL JP Z,CMD$END ;Nope, clear flags. LD A,(DE) ;Yep, check next character for end mark OR A ;(null). RET NZ ; ; Remove the RSX associated with the multiple command processing. ; CMD$END:XOR A ;Clear the address pointer. LD (HL),A INC HL LD (HL),A LD L,0AEH ;Get address of REMOVE flag. LD H,(HL) LD L,0EH DEC (HL) ;Turn 0 into 0ff hex and then remove it. JP REMOVE ; ; Parse the input file specifications. Note that this routine is ; very strange. It is not consistant with the fine code throughout ; the rest of the CCP and loader. In particular are the references ; to 0B28 hex. I don't know what this is all about. It seems like ; nonsense. ; PARSEINP:LD DE,DFT$FCB ;Parse next filename and put fcb here. PARSEIT:CALL NONBLANK ;Skip over to first non-blank. PUSH AF ;Save zero flag (z=none). CALL PARSE POP AF RET PARSE: LD (INP$PTR),HL ;Save input pointer temporailly. LD (LINE$PTR),HL PUSH DE LD DE,INP$PTR ;Now let the bdos parse the filename. LD C,152 CALL CPM POP DE LD A,H ;(hl) will be zero if end of string found. OR L LD B,(HL) ;Get next non-blank char (maybe). INC HL ;(hl) is returned as ffff if error occured. JP NZ,PARSE01 LD HL,0B28H ;What??? PARSE01:LD A,H ;Did an error occur? OR L JP NZ,PARSE02 LD HL,0B28H ;Yes, but what is this statement doing here? CALL WHAT? ;Print error message. PARSE02:LD A,B ;Check terminating character. This is a CP '.' ;terminator and must be skipped if it is JP NZ,PARSE03 ;not a period. DEC HL ;Don't skip. PARSE03:LD (INP$PTR),HL ;Update address for next time. LD C,16 LD HL,PARSE$FCB+2 PUSH DE CALL MOVE$C ;Move FCB into users place (first 16 bytes). LD DE,TEMPDMA ;Move password here. LD C,10 CALL MOVE$C POP DE LD A,(HL) ;Get length of password. LD HL,0 ;Set in case there was none. OR A LD B,A JP Z,PARSE05 ;Why not just RET Z here? Very strange... LD HL,(LINE$PTR);Okay,look for start of passord. PARSE04:LD A,(HL) ;It follows a semi-colon. CP ';' INC HL JP NZ,PARSE04 PARSE05:RET ; ; Check file name specified for a leading user number and ; drive name. These must be seen within the first 4 bytes. ; (de) points to a 2 byte data storage area for parameters. ; CHKFNAME:PUSH DE ;Save pointer to data area. XOR A LD (DE),A ;Clear user number storage spot. INC DE LD (DE),A ;And drive name spot. INC DE CALL NONBLANK ;Skip over leading spaces or tabs. LD HL,(INP$PTR) POP DE PUSH DE LD B,4 ;Search up to 4 bytes for a colon or a null. CKNAM01:LD A,(HL) CP ':' ;A colon? JP Z,CKNAM04 OR A ;A null (end-of-line)? JP Z,CKNAM02 DEC B INC HL JP NZ,CKNAM01 ;Only search (b) bytes. CKNAM02:POP DE ;No drive name found, clear any suspected user XOR A ;number. LD (DE),A LD HL,(INP$PTR) CKNAM03:INC DE LD A,(DE) PUSH AF CALL PARSE POP AF LD (DE),A RET CKNAM04:LD HL,(INP$PTR) LD A,(HL) CKNAM05:CP 030H ;Look for leading user number. JP C,CKNAM06 CP 03AH JP NC,CKNAM06 CALL CVRTUSR ;Extract user number. POP DE PUSH DE LD A,(DE) ;Already seen one? OR A JP NZ,CKNAM02 ;Yes, error. LD A,B ;Nope, store it (plus one). INC A LD (DE),A JP CKNAM07 CKNAM06:CP 041H ;Look for possible drive name. JP C,CKNAM02 CP 051H JP NC,CKNAM02 POP DE PUSH DE INC DE LD A,(DE) ;Already seen one? OR A JP NZ,CKNAM02 LD A,(HL) ;Nope, convert to binary drive and store. SUB 040H LD (DE),A INC HL CKNAM07:LD A,(HL) ;Does colon follow name (required)? CP 03AH JP NZ,CKNAM05 ;Nope, error. INC HL ;Okay, continue scan. POP DE JP CKNAM03 ; ; Move 2 bytes from system parameters, relavive byte (d) ; into relative byte (e). ; MOVE2: LD HL,(SCB$BASE) LD L,D LD D,H LD C,2 ; ; Move a block of memory (c) bytes long from (hl) to (de). ; MOVE$C: LD A,(HL) LD (DE),A INC HL INC DE DEC C JP NZ,MOVE$C RET ; ; Move a string from (DE) to M and count bytes ; moved in (C). String ends in a null. ; MOVSTRNG:LD C,0 MOVEIT: LD A,(DE) ;Move next byte. LD (HL),A OR A ;Last byte moved? LD A,C RET Z ;Yes, return byte count in (A). INC HL ;Bump pointers. INC DE INC BC JP MOVEIT ; ; Read one byte from file (fcb is at 5c hex). Set zero ; flag if data in (a) is okay. Clear zero on eof or err. ; FREAD: XOR A LD HL,BUFPNTR ;Bump buffer pointer. Need to read in the next record? INC (HL) CALL M,RDNXTREC ;Okay to continue? OR A RET NZ ;Nope, return with error code (zero cleared). LD A,(BUFPNTR) ;Okay, extract next byte. LD HL,80H CALL ADDHL XOR A ;Clear zero flag (data is valid). LD A,(HL) RET GET$MODE:LD BC,0B440H ; ; Test parameter in relative byte (b) by masking it with ; register (c). Return with (hl) pointing to it. ; TST$BC: LD HL,(SCB$BASE) LD L,B LD A,(HL) AND C RET SET$BC: LD BC,0B4A0H CALL TST$BC LD A,C OR (HL) LD (HL),A RET CLR$BC: LD BC,0B4A0H CLR$BITS:CALL TST$BC LD A,C CPL AND (HL) LD (HL),A RET ; ; Set/get relative parameter (b) from/to (a). ; SET$PARM:LD HL,(SCB$BASE) LD L,B LD (HL),A RET GET$PARM:LD HL,(SCB$BASE) LD L,B LD A,(HL) RET PRT$CRLF:CALL PRINT ;Print message but does this set (l) to ; ;anything??? CRLFX: LD B,L ; ; Output a carriage return and line feed to the console. ; CRLF: LD A,CR ;Note registers are preserved. CALL CONSOLE LD A,LF JP CONSOLE ; ; Output (A) as a two digit decimal number. ; DECOUT: SUB 10 ;Less than 10? JP C,DECOT2 LD E,'0' ;No, compute left digit. DECOT1: INC E SUB 10 JP NC,DECOT1 PUSH AF CALL CONOT3 POP AF DECOT2: ADD A,'9'+1 ;Make (A) ascii and print JP PAGE$OUT ; ; Output string pointed to by (hl) on the console. ; It ends with either a null or same character as (C). ; LINEOUT:LD A,(HL) ;Terminator reached? OR A RET Z CP C RET Z CALL CONSOLE ;Nope, continue printing. INC HL JP LINEOUT MORE: CALL NONBLANK RET Z WHAT?: LD HL,AS$MODE ;If bits 6,7 <> 0 then this error will be LD A,(HL) ;skipped one time only. OR A LD (HL),0 ;Always becomes zero. RET NZ ;... just skip this error. LD HL,(LINE$PTR);Print file name. LD C,' ' ;Output up until a space. CALL LINEOUT LD A,'?' ;Follow by this. CALL PAGE$OUT JP CMDRET ;And return. ; ; Convert the next characters from ascii digits to a binary number. ; DEC2BIN:CALL NONBLANK LD HL,(INP$PTR) LD (LINE$PTR),HL RET Z LD A,(HL) ;Is this a decimal digit? CP '0' JP C,WHAT? CP '9'+1 JP NC,WHAT? CALL CVRTUSR ;Yes, convert number. LD (INP$PTR),HL ;Save pointer into line. OR 1 ;Clear carry and zero flags. LD A,B ;Get number into (a) and return. RET ; ; Convert the ascii decimal number pointed to by (hl) into binary. ; Return result in (b) and (hl) pointing to next character. ; CVRTUSR:LD B,0 ;Clear result. CVUSR1: LD A,(HL) ;Get next character. SUB '0' ;Convert to binary. RET C ;Non-digit? CP 10 RET NC PUSH AF ;Save for later. LD A,B ;Multiply previous sum by 10. ADD A,A ADD A,A ADD A,B ADD A,A LD B,A POP AF ;Pop new digit. INC HL ;Bump pointer. ADD A,B ;Add in new digit. LD B,A ;Put result back into (b). CP 16 ;Greater than 16? JP C,CVUSR1 JP WHAT? ;Yes, error. ; ; Print filename and extension. (de) points to fcb. ; PRTFNAME:INC DE ;Skip drive byte. LD H,8 ;Print 8 char name. CALL PRINT$H CALL SPACE ;Followed by a space then the 3 char extension. LD H,3 ;Set length count. ; ; Print (h) characters from (de) on the console. ; PRINT$H:LD A,(DE) AND 07FH ;Strip bit 7 always. CALL CONSOLE INC DE DEC H JP NZ,PRINT$H RET ; ; Output a single space to the console. ; SPACE: LD A,' ' ; ; Output (A) to the console and preserve all ; registers. ; CONSOLE:PUSH BC PUSH DE PUSH HL CALL PAGE$OUT POP HL POP DE POP BC RET ; ; Add (hl) and (a). Return result in (hl). ; ADDHL: ADD A,L ;Add into low byte. LD L,A RET NC ;Carry generated? INC H ;Yes, increment high byte. RET ; ; Fill string (DE) with "?". It is (B) characters long. ; FILLSTR:LD A,'?' ;Set fill character. FILLST1:LD (DE),A ;Stuff one more. INC DE DEC B JP NZ,FILLST1 ;Continue for (b) characters. OR A ;Clear zero flag then return. RET ; ; Compare the name from (de) with the table of names at (hl). ; The table ends with a null byte and each name ends with a ; space character. Blank chars are not compared. ; Set the zero flag on a match and return match number in (a). ; COMPARE:LD BC,0FFH ;Set (b)=0, (c)=-1 as counters. COMP01: PUSH DE PUSH HL COMP02: LD A,(DE) AND 07FH CP ' '+1 ;Check this byte? JP C,COMP03 CP (HL) ;Yes, match? JP NZ,COMP04 COMP03: INC DE ;So far so good.Move on to next byte. INC C ;Update string length count. LD A,' ' ;End of string? CP (HL) INC HL JP NZ,COMP02 POP HL ;Yes, move (c) byte string into place now. POP DE CALL MOVE$C LD A,B ;Get match number into (a) and return with RET ;zero flag set. COMP04: LD A,' ' ;Compare failed, look for end of string. COPM05: CP (HL) INC HL JP NZ,COPM05 POP DE POP DE INC B ;Bump to next name. LD C,0FFH LD A,(HL) SUB 1 ;End of names? Note that this will clear the JP NC,COMP01 ;zero flag. RET ; ; Data storage area. ; ASKUSER:DEFB 'Enter User #: $' NO$FILE:DEFB 'No File$' REQD: DEFB ' required$' ERASE?: DEFB 'ERASE $' YES$NO: DEFB ' (Y/N)? $' WAITING:DEFB cr,lf,cr,lf,'Press RETURN to Continue $' USER0: DEFB ' (User 0)$' SKIPED: DEFB 0 NONSYS: DEFB 'NON-' SYSFILES:DEFB 'SYSTEM FILE(S) EXIST$' AS$MODE:DEFB 0,0,0 ;Auto-start mode flag. GET$SCB:DEFB 3AH,0 INP$PTR:DEFB 0,0 DEFW PARSE$FCB+2 USR$NUM:DEFB 0 DSK$NUM:DEFB 0 LOGDRIVE:DEFB 0 TEMP$SUB:DEFB 1,'$$$ SUB' DEFB 0 ; ; End of initialized code area. The remaining labels are defined ; by address only (no contents assumed). ; DIRSIZE EQU $+23 SCRNSIZE EQU $+24 ROW$NUM EQU $+25 PAGE$FLG EQU $+26 LINE$PTR EQU $+27 TEMP$PTR EQU $+29 BUFPNTR EQU $+31 BASEPAGE EQU $+32 OPTIONS EQU $+33 TEMPDMA EQU $+34 USR$BLK EQU $+44 FCB EQU $+45 PARSE$FCB EQU $+78 IN$BUFF EQU $+116 MAINSTK EQU $+427 OURSTK EQU $+429 ; END