; PROGRAM: Error And Shell Editor ; AUTHOR: Paul Pomerleau and Jay Sage ; DATE: October 23, 1987 ; VERSION: 2.0 ; Copyright 1987, Paul Pomerleau ; This program provides a history shell and error handler for ZCPR3.3 ; A complex line editor is used to edit command lines. ; Run EASEDOC.COM to get a listing of Key-Command Bindings. ; This file assembles with SLR's Z80ASM ; ===================================================== ; ; D E F I N I T I O N S S E C T I O N ; ; ===================================================== version equ 20 betastage equ ' ' no equ 0 yes equ 0ffh cr equ 0dh lf equ 0ah bell equ 07h tab equ 09h bdos equ 0005h bios equ 0000h secbuf equ 0080h sectop equ 0100h killlen equ 300 maxsec equ 8 public cout,ccout,print,pstr,crlf extrn acase2,sua,gua ; SYSLIB extrn getmsg,getcst,putcst,putzex ; Z3LIB extrn z3init,getquiet extrn erradr,getcl1,z3log extrn getwhl,getduok,dutdir extrn getsh,qshell,shpush,shpop,putreg,getreg extrn qerror,getsrun,haltsub extrn z33chk,stopxsub,subon ; Z33LIB extrn getzrun,stopzex,haltzex extrn getefcb,parse2 ; ========================================================== ; ; S T A N D A R D P R O G R A M H E A D E R ; ; ========================================================== ENTRY: jp START defb 'Z3ENV' defb 3 ; Type-3 environment ENVADDR: dw 0f300h dw ENTRY defb version ; To go to the Error Handler, just ; go to START with error flag set. ; ====================================================== ; ; C O N F I G U R A T I O N A R E A ; ; ====================================================== NAME: db 'EASE VAR' ; Command history file WIDTH: dw 80 ; Length of line TOOSHORT: dw 02 ; Don't store in history ; if shorter than... GOBEGFLG: db yes BEEPFLG: db yes SMARTSAV: db yes ; No causes penny pinching. USEMEM: db no ; Use memory (vs. registers) MEMLOC: dw 47h ; Memory location (3 free ones) STARTREG: ; for EASE to use always. db 7 ; First register to use USE6: db no ; Set to TRUE to use ; BDOS 6 for input. TABLE: db [[LASTCASE - VECTOR] / 3] ; Number of cases dw BEEP ; Default case ring bell VECTOR: db 'Q' dw SHIFTED ; Meta Key db 'D' dw FCHR ; Right Char db 'S' dw BCHR ; Left Char db 'E' dw UP ; Up line db 'X' dw DOWN ; Down line db 'A' dw MBWORD ; Left word db 'F' dw MFWORD ; Right word db 'S'+80h dw GOBOLN ; Start of line db 'D'+80h dw GOEOLN ; End of line db 'G' dw FDEL ; Del char right db 'H' dw DELCHR ; Del char left db 127 dw DELCHR ; Del char left db 'T' dw FDWORD ; Del word right db 127 + 80h dw BDWORD ; Del word left db 'R' dw CMDKILL ; Kill to semi-colon db 'Y'+80h dw DELTOEND ; Delete to end of line db 'Y' dw DELLIN ; Delete line db 'U' dw UNDO ; Reinsert deleted text db 'B' dw BACKLINE ; Back in history shell db 'N' dw NEXTLINE ; Forward in history shell db 'O' dw BSEARCH ; Search for first char db 'V' dw TOGLIN ; Toggle insert CCOMP: db 'I' dw COMPLETE ; Complete filename db 'P' dw QINSERT ; Insert any char db 'W' dw REPLOT ; Redraw line db 'C' dw WARM ; Warm Boot db 'M' dw DONE ; End edit db '_'+80h dw QUITSH ; End EASE LASTCASE: PUNC: db ',.:!#%^&<>[]{}()_+-=`~/\|; ',tab PUNCLEN equ $ - PUNC SEP: db ' ,=/<>|',tab SEPLEN equ $ - SEP ; =================================================== ; ; M A I N C O D E S E C T I O N ; ; =================================================== RESTORE_STACK: ld sp,(SAVED_STACK) ret START: ld hl,(envaddr) ; Get environment address call z3init ; Initialize library routines call z33chk ret nz call SETCPM3 ld (SAVED_STACK),sp ld hl,STACK ld sp,hl ld hl,RESTORE_STACK push hl ld a,(5dh) push af call GETSEC ; Get the sector number call EASERUN jr z,FPFINE ld hl,BUFFER ld (POS),hl call SETFILE call RECOVER call PUTSEC FPFINE: ld c,19h ; Get current disk call ADOS inc a ld (OLDDRIVE),a pop af ld (5dh),a call qerror ; See if error handler invocation jp z,errorh ; If so, branch to error processing call qshell jp z,RUNSH ; Yes, don't install ;============================================================================= ; ; I N S T A L L A T I O N C O D E ; ;============================================================================= ; Program was invoked manually, so we need to set it up shell and error handler. ;---------------------------------------- ; Subtask 1 -- determine whether to use a DU or a DIR prefix ; ; The program can examine the ZCPR33 option bytes to determine what features ; are supported (DU and/or DIR forms, which one first, wheel control over DU ; use, etc.). For now we will just assume that a DU prefix will be used and ; will omit coding this block. ;---------------------------------------- ; Get user option: if null, do both. If E, then install the error handler ; else install the shell. ld a,(5dh) cp 'C' jp z,RUNSHNP call header ld a,(5dh) cp ' ' ; Error handler and shell jr z,BOTH cp 'E' ; Only error handler jr z,ERRONLY jr ISHELL ; Only the shell BOTH: call ISHELL ;---------------------------------------- ; Subtask 2 -- build error handling command line including directory prefix ; using data from the external FCB. We use the fact that the drive and user ; where the program was actually found along the path are stored in the ; command file control block. The user number is kept in the usual place; ; the drive is kept in the following byte. The drive is in the range 1..16 ; (unless the command is resident, in which case the drive byte is 0). ERRONLY: call getmsg ; Get pointer to error command line ld de,10h ; ..in message buffer add hl,de call PUT_NAME_TO_HL call getquiet ret nz call CLPRINT dc ' Error Handler' jr P_CLST ISHELL: ld hl,BUFFER call PUT_NAME_TO_HL call SHPUSH ; Store the shell name jr nz,SHBAD ; Push to deep? Then abort push hl call SETFILE call RECOVER ; Use existing file call PUTSEC pop hl call getquiet ret nz PRWELC: call CLPRINT dc ' Shell' P_CLST: call PRINT dc ': ' jp PSTR ; PRINT the string there and quit SHBAD: call CLPRINT SHERR: dc 'Shell Error' ret RECOVER: ld hl,SEC ld bc,BUFFER-SEC-1 call ZERO call SET call OPEN ret z ; jr z,DELSET DOFNO: call FNOWRITE jr nz,DOFNO call FNOWRITE jr nz,DOFNO jp BNOWRITE DELSET: ld c,19 ; Delete call FBDOS SET: xor a ld (SEC),a ; First sector ld hl,SECBUF + 1 ; +1==So no long pause -- at least at first. ld (FP),hl jp PUTSEC PUT_NAME_TO_HL: push hl ; Save pointer for way below ex de,hl ; Switch pointer into DE call getefcb ; Get address of the command FCB inc hl ; Advance pointer to name of program ; Get drive user from Z33's FCB. push hl ld bc,13 ; Offset to drive number add hl,bc ; HL now points to the drive number ; Here we get the drive where the program was found. Since we know that this ; is not a resident program, there is no need to check for a zero value. ld a,(hl) ; Get it and add a,'A'-1 ; ..convert to a letter ld (de),a ; Save in error command line inc de ; Increment command line pointer dec hl ; Back up to user number ld a,(hl) ; Get it and call mafdc ; ..convert to decimal in command line ld a,':' ; Put in the colon ld (de),a inc de cont1: pop hl ; Restore the pointer to the command name ld bc,8 ; Copy 8 characters of name ldir ; ..into error command line xor a ; Store terminating null ld (de),a pop hl ret ;============================================================================= ; ; E R R O R H A N D L I N G C O D E ; ;============================================================================= ; This is the main entry point for error handling errorh: ld a,(BEEPFLG) ; Did the user want a Beep? or a call nz,BEEP ;---------------------------------------- ; Subtask 1 -- Display program signon message task1: call header ;---------------------------------------- ; Subtask 2 -- Display system status ; This task determines whether ZEX and/or SUBMIT are running. Input ; redirection from either of them is turned off while error handling is ; performed (so user can provide the input). task2: call stopzex ; Stop ZEX input redirection call CLPRINT dc lf,tab,'(ZEX ' call getzrun ; Find out if ZEX is running ld e,a ld (ZEXORSUB),a call ponoff ; Print on/off call subon jr z,task2a ; Branch if submit not supported call PRINT dc ', SUBMIT ' ; See if submit is supported by the command processor call stopxsub ; Stop XSUB input redirection call getsrun ; Get submit running flag call ponoff ; PRINT on/off or e ld (ZEXORSUB),a ; See if wheel byte is on or off task2a: call PRINT dc ', WHEEL ' call getwhl call ponoff ld a,')' call COUT ;---------------------------------------- ; Subtask 3 -- Determine source of the error (internal or external) and ; display that information. task3: call CLPRINT dc tab call getcst ; Get command status flag bit 3,a ; See if external command bit is set jr nz,external ; Branch if external error call PRINT ; "IN"ternal dc 'In' jr task3a external: call PRINT ; "EX"ternal dc 'Ex' task3a: call PRINT ; "TERNAL ERROR" dc 'ternal error #' call xcmdoff ; Clear the external command bit (and ECP bit) ;---------------------------------------- ; Subtask 4 -- Determine the error return code and display information about ; the nature of the error. This section of the code can be expanded to cover ; more error types as they are defined. task4: call getmsg ld a,(hl) ; Get the error return code push af ; Save for use below call pafdc ; Display the number call PRINT dc ' -- ' pop af ; Get error code back cp 22 jr c,OKERR ld a,4 OKERR: dec a ld b,a ld hl,DUCHANGE or a jr z,PRINTERR SKIPERR: ld a,(hl) inc hl and 10000000b jr z,SKIPERR djnz SKIPERR PRINTERR: call PSTR call CRLF ;---------------------------------------- ; Subtask 5 -- Display bad command line ; ; In the final code, much more elaborate error processing would be performed ; here (or more likely, the code here will be used as a framework for existing ; error handlers). task5: call erradr ; Get pointer to bad command line push hl ; Save for reuse below ld de,BUFFER scan: ; Find end of this command ld a,(hl) ld (de),a or a ; See if end of command line buffer jr z,GOTEND cp ';' ; See if at command separator jr z,task5a inc hl ; Point to next character inc de jr scan ; Continue scanning task5a: push af push hl FILLREST: inc hl inc de ld a,(hl) ld (de),a or a jr nz,FILLREST pop hl pop af GOTEND: OKT5A: ld a,(hl) ld (hl),0 ; Mark end of string ld (delimptr),hl ; Save ptr to bad command's delimiter ld (delim),a ; Store delimiter push af ld a,(ZEXORSUB) or a jp z,ERREDIT call CLPRINT dc tab,'Bad Command:',tab pop af pop hl ; Restore pointer to beginning of command push af ; Save delimiting character call PSTR ; Display the bad command pop af or a jr z,task6 ; If no rest of line, get out without output push hl ; Save pointer to rest of command line call CLPRINT dc tab,'Rest of Line:',tab pop hl task5b: dec hl ; Pt back to bad command delimiter ld (hl),a ; Put semicolon back inc hl call PSTR ; PRINT rest of command line ; AND RETURN! ;---------------------------------------- ; Subtask 6 -- Deal with the bad command ; This is where the real error handling is performed. Here we just flush ; the entire command line and abort any submit job, but in a real error ; handler, several other functions would be performed. With normal command ; lines (ZEX and SUBMIT not running), the user has the following three basic ; choices: fix the bad command, skip the bad command, or abort the entire ; command line. If ZEX is running, there is an additional choice that should ; be available: abort the entire ZEX script. Similarly, if SUBMIT is running, ; the user must be given the option to abort the entire submit job. ; This code implements all of the above with the additional feature ; that if the bad command is the last on the line, the option to skip ; to next command is not presented as it would be meaningless. task6: call CLPRINT dc cr,lf,tab,'(E)dit/(A)bort' ld a,(delim) ; Get bad command delimiter or a jr z,task6a ; No trailing commands; skip next option call PRINT dc '/(C)ontinue' task6a: call PRINT dc ': ' task6c: call capin ; get response ld b,a ; Save for a moment ld a,(delim) ; Get command delimiter again or a ld a,b ; Response back in A jr z,task6b ; Don't allow 's' choice if no trailing command cp 'C' ; Continue? jr z,skip task6b: cp 'A' ; Abort? jr z,abort cp 'E' ; Edit? jp z,edit call beep ; Bad input jr task6c ;----------------------------------------------------------------------------- ; Skip over bad command and resume with next in line skip: call getcl1 ; Pt to command line buffer ld de,(delimptr) ; DE pts to bad command's delimiter inc de ; Now pointing to next command ld (hl),e ; Stuff address in inc hl ; ..first two bytes ld (hl),d ; ..of multiple command line buffer call PRINT defb 'Continuing...',1 ret ; Resume command execution with next command ;----------------------------------------------------------------------------- ; Abort (flush) command line abort: call abortmsg call getzrun ; See if ZEX is running jr z,abort2 ; Branch if not ; Deal with running ZEX script call CLPRINT dc tab,'Abort ZEX script (Y/N)? ' call getyesno ; Get user's answer jr nz,abort1 ; Branch if negative response call haltzex ; Abort ZEX call abortmsg jr abort2 abort1: call abort3 ; Deal with running SUBMIT job abort2: call getsrun ; Is a submit job running ret z ; If not, return to command processor call CLPRINT dc tab,'Abort SUBMIT job (Y/N)? ' call getyesno ; Get user's answer jr nz,abort3 ; Branch if negative response call haltsub ; Abort SUBMIT abortmsg: call PRINT defb 'Aborted',1 ret abort3: call PRINT defb ' No',1 ret ; Back to command processor ;----------------------------------------------------------------------------- ; XCMDOFF -- turn off external command flag ; This routine turns off the external command bit and the ECP bit in the ; command status flag. xcmdoff: call getcst ; Get the command status flag and 11110011b ; ..the error handler on return jp putcst ; Put new value back and return ;----------------------------------------------------------------------------- ; GETYESNO -- get yes/no answer from user ; Only 'Y' or 'y' accepted as affirmative answers. Routine returns Z if ; affirmative, NZ otherwise. getyesno: call capin ; Get user response cp 'Y' ret ;----------------------------------------------------------------------------- ; PONOFF -- PRINT ON or OFF in message ; ; If the Z flag is set on entry, 'OFF' is displayed; otherwize 'ON' is ; displayed. ponoff: jr z,poff call PRINT dc 'ON' ret poff: call PRINT dc 'OFF' ret ; ------------------------------ ; header -- PRINT program name and version ; header: call CLPRINT defb 'Z33 Error And Shell Editor, Vers. ' defb version / 10 + '0' defb '.' defb version mod 10 + '0' dc betastage ret ; ------------------------------ ; CLPRINT -- PRINT CR LF and then following string. CLPRINT: call CRLF jp PRINT ;----------------------------------------------------------------------------- ; Prompt -- PRINT a DU/DIR prompt. ; PROMPT: call QPROMPT ; PRINT the DU:DIR ld a,(INSFLG) INSPROMPT: or a ld a,'>' ; For insert jr nz,INSOK ld a,'}' ; For no insert INSOK: call COUT ld a,'>' jp COUT QPROMPT: call QERROR jr nz,NOTERR call PRINT dc '[Error] ' NOTERR: call getmsg ld bc,2eh add hl,bc ld c,(hl) inc hl ld b,(hl) call getduok jr z,NODU ld a,b add a,'A' ; Make it a letter call COUT ; Write it ld a,c call pafdc ; Write it as a number NODU: call dutdir ; Get the NDR ret z push hl call getduok ld a,':' call nz,COUT ; And the colon to separate pop hl ld b,8 ; Eight chars max NAMELOOP: ld a,(hl) ; Get the first char cp ' ' ; Is it the last ret z ; YUP. done call COUT ; Write it inc hl djnz NAMELOOP ; Repeat ret ; ----------------------------------------------------------- ; FillLine ; Check length of the new, edited, command line. If it will ; fit, copy it to the Z3 multiple command line buffer and ; return to the CPR to execute it. Otherwise, display error ; message and branch back to the editor. GETLEN: ld hl,BUFFER push hl xor a ld bc,killlen + 2 cpir ; Find the ZERO dec hl pop de push hl sbc hl,de ; Get the length ld b,h ld c,l pop hl ret FILL: xor a call putcst ; No errors call getcl1 ; Get Z3 command line addr in hl, ; ..length in a cp c ; Compare with length of new line jr c,FILLERR ; Branch if new line too long ld a,b ; High order byte of length should be 0 or a jr nz,FILLERR ; Branch if not push hl ; Save Z3CL ld de,4 ; Offset to first character in buffer add hl,de ex de,hl ; First char address in DE pop hl ; Z3CL address in HL ld (hl),e ; Store ptr to first command inc hl ; At Z3CL ld (hl),d ld hl,BUFFER ; Get back pointer to new command push hl inc bc ; Adjust length to include trailing null ldir ; Copy to system command line call EASERUN pop hl ret nz ; Return if it was an error ld a,(SAVFLG) or a ret z ; Didn't change from read: Don't save push hl call GETLEN ; Get length again ld hl,(TOOSHORT) sbc hl,bc pop hl ret nc ; Too short to save push hl call OPEN ; Open 'er up call GETSEC ; Get the intitial position call RRAND ; Read in the current record xor a call WFFILE ; Put the null to space the lines pop hl FILLWRITE: ld a,(hl) ; Loop to put in the whole line push hl call WFFILE ; Put in the next char of the line pop hl ld a,(hl) inc hl or a jr nz,FILLWRITE call BNOWRITE call PUTSEC xor a call WFFILE ; Write final spacer null xor a call WFFILE ; Put the null to space the lines FLUSH: call WRAND CLOSE: ld c,16 ; Close the file FBDOS: ld de,FCB jp BDOS ; Go back to CPR to execute it FILLERR: ; To long for CCP to digest call CLPRINT defb bell,tab defb 'Too long!',1 jp EDIT2 EASERUN: ld b,0 call GET cp 'E' ret GET: ld a,(USEMEM) ; B = Location 0..2 or a jr z,GREGS push hl push de ld d,0 ld e,b ld hl,(MEMLOC) add hl,de ld a,(hl) pop de pop hl ret GREGS: ld a,(STARTREG) push bc add b ld b,a call getreg pop bc ret PUT: ld c,a ; A = Data, B = location 0..2 ld a,(USEMEM) or a jr z,PREGS push hl push de ld d,0 ld e,b ld hl,(MEMLOC) add hl,de ld (hl),c pop de pop hl ret PREGS: ld a,(STARTREG) push bc add b ld b,a ld a,c call putreg pop bc ret GETSEC: ld b,1 call GET ld (SEC),a inc b call GET ld h,0 ld l,a ld (FP),hl ; Put the File Pointer to saved FP ret PUTSEC: ld a,'E' ld b,0 call PUT ld a,(SEC) call INCPUT ld de,(FP) ; Get SEC & FP ld a,e INCPUT: inc b jp PUT ;----------------------------------------------------------------------------- ; Edit -- He, He, He... ; ERREDIT: pop af pop hl jr EDITNOE EDIT: call PRINT db 'Edit',1 ; PRINT the 'E' for edit choice EDITNOE: call CRLF ld bc,BUFFER - ENDFLG - 1 ; Setup for zeroing out jr ESETUP ;-------------------------- ; Run Shell (RUNSH) -- clear out the buffers and go to the editor. RUNSH: RUNSHNP: call GETSEC ld a,1 ; Z3 Cmd line for ZEX call putzex ; Make ZEX think we are Z3 ld bc,BUFFER - ENDFLG ESETUP: call SETUP EDIT2: call OUTPUT1 ELOOP: xor a ld (NOOUT+1),a ; OK for output call GETKEY ld b,a ld a,(SHIFT) ; Get shift mask or b push af xor a ld (SHIFT),a ; Clear shift mask pop af cp ' ' jr c,CONTROL ; Yes, it's a command cp 127 jr nc,CONTROL call SAVECMD call INSERT ; No, it's just a letter jr ELOOP CONTROL: ld hl,ELOOP ; Return to... push hl ld de,TABLE ; Go to proper command call UCASE call SAVECMD call acase2 SAVECMD: push af ld a,(JUSTCMD) ld (LASTCMD),a pop af ld (JUSTCMD),a ret SHIFTED: ld a,10000000b ; Make shift mask set high bit ld (SHIFT),a ret DELCHR: call DPOS ; Back up and delete forward ret z call DELETE jp SHOWTOEND FCHR: call FWRAP ; Go ahead a char and wrap if EOL jp IPOS FWRAP: ld hl,(POS) ; Check for forward wrap ld a,(hl) or a ret nz pop hl jp GOBOLN BCHR: call BWRAP ; Go back and wrap if BOL jp DPOS BWRAP: ld hl,(POS) ; Check for back wrap dec hl ld a,(hl) or a ret nz pop hl jp GOEOLN MBWORD: call BWRAP ; Word forward with wrap BWORD: call DPOS ; Word forward without ret z inc de call PUNCCP ; Looking for punctuation jr z,BWORD ; Skip punct BWORD2: call DPOS ret z inc de call PUNCCP ; Go till we hit punct jr nz,BWORD2 dec de jp IPOS PUNCCP: ld hl,PUNC ; Check for punctuation ld bc,PUNCLEN cpir ret FDWORD: ld de,0 ; Delete forward a word call FWORD ; Word forward push de FDWBACK: ld a,d or e jr z,FDWENDBACK dec de call DPOS ; Back up same number of Chars jr FDWBACK FDWENDBACK pop de ; Delete same number of chars jr BDWRD1 BDWORD: ld de,0 ; Delete a word backwards call BWORD BDWRD1: call SETKILL BDWRD2: ld a,d or e jp z,BDWRDEND dec de push de push hl call DELETE ; Delete same number pop hl inc hl pop de jr BDWRD2 BDWRDEND: ld (hl),0 jp SHOWTOEND MFWORD: call FWRAP ; Forward word with wrap FWORD: call IPOS ret z inc de call PUNCCP ; Skip until punct jr nz,FWORD FWORD2: call IPOS ret z inc de call PUNCCP ; Skip punct jr z,FWORD2 dec de jp DPOS ; Back up one UP: ld hl,(WIDTH) ; Back 80 chars BACKUP: ld a,h or l ret z dec hl push hl call DPOS ; back up that many pop hl ret z cp ' ' jr nc,BACKUP ; Control chars count double ld a,h or l ret z dec hl jr BACKUP DOWN: ld hl,(WIDTH) ; Go down a line DOWNLOOP: ld a,h or l ret z dec hl push hl call IPOS ; Forward 'til hl = zero pop hl ret z cp ' ' ; Control chars count double jr nc,DOWNLOOP ld a,h or l ret z dec hl jr DOWNLOOP COMPLETE: xor a cpl ld (NOOUT+1),a ; NO output ld hl,LASTCMD ld a,(CCOMP) cp (hl) jr z,COMP2OK call ISOLATE_NAME call GET_FCB COMPL3: push af xor a ld (NOOUT+1),a pop af jp z,NOT_FOUND call BACK_UP_AND_REDISPLAY ret COMP2OK: call GET_NEXT ld hl,(POS) jr COMPL3 ISOLATE_NAME: ld hl,(POS) push hl COMPLOOP: call DPOS jr z,COMPDONE ld hl,SEP ld bc,SEPLEN cpir jr z,COMP2DONE jr COMPLOOP COMP2DONE: call IPOS COMPDONE: ld hl,(POS) pop de push de ld a,(de) push af xor a ld (de),a ld de,TBUF ld (STARTNAME),hl MLOOP: ld a,(hl) call UPCASE ld (de),a inc de inc hl cp ':' jr nz,NOCOLON ld (STARTNAME),hl NOCOLON: or a jr nz,MLOOP ld hl,STARDOTSTAR dec de ld bc,4 ldir pop af pop hl ld (hl),a ret GET_FCB: push hl ld hl,TBUF push hl ld de,5ch call parse2 ld a,(5ch+15) or a jr nz,MISSION_ABORT pop de ; DMA at TBUF call DMA ld de,5ch call PUTUD call DZ3LOG ld a,(5ch+13) ld (SEARCH_USER),a ld c,17 call ADOS NEXT_NAME: inc a jr z,NOGO push af call SETDMA call GETUD pop af or a NOGO: pop hl ret MISSION_ABORT: xor a pop hl pop hl ret GET_NEXT: push hl ld de,TBUF call DMA ld de,5ch call PUTUD ld a,(SEARCH_USER) ld (5ch+13),a call DZ3LOG ld c,18 call ADOS jr NEXT_NAME NOT_FOUND: xor a ld (JUSTCMD),a ld (POS),hl jp beep BACK_UP_AND_REDISPLAY: push af ld de,(STARTNAME) ld (POS),hl jr DEL_CHK_LOOP DEL_LOOP: push de call DELCHR pop de DEL_CHK_LOOP: ld hl,(POS) or a sbc hl,de jr nz,DEL_LOOP pop af dec a add a,a add a,a add a,a add a,a add a,a ld e,a ld d,0 ld hl,TBUF + 1 add hl,de ld b,8 FNLOOP: ld a,(hl) and 1111111b cp ' ' push hl push bc call nz,INSERT pop bc pop hl inc hl djnz FNLOOP FIRSTNAME: push hl ld a,'.' call INSERT pop hl ld b,3 TYPLOOP: ld a,(hl) and 1111111b cp ' ' jr z,LASTNAME push hl push bc call INSERT pop bc pop hl inc hl djnz TYPLOOP LASTNAME: call SHOWTOEND ret WARM: call DONEOUT jp 0 DONE: pop hl ; Save it and run it call DONEOUT call GETLEN ; Get the length in BC ld a,(BUFFER) cp ';' ; Comment? jr z,JRUNSH or a ; Empty? jp nz,FILL ; Stuff the buffer JRUNSH: call qerror ret z call qshell ret nz call CRLF jp RUNSHNP FDEL: call DELETE ; Delete forward a char ret z jp SHOWTOEND ; Redisplay QINSERT: call GETKEY ; Get a key and insert it -- whatever it is or a ret z jp INSERT TOGLIN: ; And Diagnostic Key (sometimes...) ; ld b,0 ; call GET ; call pa2hc ; call print ; db ', ',0 ; ld b,1 ; call GET ; call pa2hc ; call print ; db ', ',0 ; ld b,2 ; call GET ; call pa2hc ; call print ; db 13,10,'FP:',0 ; ld a,(FP) ; call pa2hc ; call print ; db ', SEC:',0 ; ld a,(SEC) ; call pa2hc ; call print ; db ', BACK:',0 ; ld a,(BACKFLG) ; call pa2hc ; call print ; db ', END:',0 ; ld a,(ENDFLG) ; call pa2hc ; call print ; db ' ',0 ; ret ld hl,INSFLG ; Toggle the insert flag ld a,(hl) cpl ld (hl),a ld hl,(POS) push hl push af call GOBOLN call BACK call BACK ; Back up to the begining and back to ICHAR pop af call INSPROMPT pop de TOGLP: ld hl,(POS) ; Go fwd to where we were sbc hl,de ret z call IPOS jr TOGLP CMDKILL: call SETKILL CKL: push hl call DELETE ; Delete to a semi-colon pop hl inc hl cp ';' jr z,CKDONE or a ; Or a NULL jr nz,CKL CKDONE: ld (hl),0 jp SHOWTOEND ; Redisplay DELLN1: call GOBOLN jr NSDELTOEND DELLIN: call GOBOLN ; Go to start DELTOEND: call SETKILL NSDELTOEND: call CLRTOEND ; Wipe everything on screen ld hl,(POS) ld (hl),0 ; Put a null at the start ret GOEOLN: call IPOS ; Move to end of line jr nz,GOEOLN ret GOBOLN: call DPOS ; Move to start of line jr nz,GOBOLN ret REPLOT: ld hl,(POS) ; RePRINT entire line push hl call GOEOLN ; Go to end and give us a CR LF REPLT1: call CRLF call OUTPUT ; Redisplay pop de GOTOPOS: or a call IPOS ; Move fwd to old position sbc hl,de ret z jr c,GOTOPOS GP2: call DPOS ret z sbc hl,de ; Done? ret z jr GP2 CAPIN: call cin ; Fall through to UPCASE UCASE: push bc ; Upcase A push af and 80h ld b,a pop af and 7fh ; Keep high bit call UCASE2 ; Real upcase or b pop bc ret UCASE2: cp ' ' ; Standard blah blah upcase funct jr nc,NOTCTL add '@' NOTCTL: cp 'a' ret c ; Not a lowercase cp 'z'+1 ret nc ; Not a lowercase sub ' ' ; Yes, a lowercase ret UPCASE: ; More standard upcase cp 'z' + 1 ret nc cp 'a' ret c sub a,'a'-'A' ret BSEARCH: call OPEN ; Check for open, etc. ld hl,(FP) ; Get our locations ld a,(SEC) push af push hl ld a,(ENDFLG) ; Did we hit the end? or a jr z,BBDONE ld a,(BACKFLG) ; Did we back up right before this? or a jr nz,BBDONE call BNOWRITE jr z,BSRCHNO1 BBACK: call BNOWRITE ; Find the NULL jr nz,BBACK BBDONE: ld bc,BUFFER push bc BSRCHL: call BNOWRITE ; Is this the end? BSRCHL2: call BNOWRITE jr z,BSRCHNO call FNOWRITE ; No, Place us properly BSRCL1: call BNOWRITE jr nz,BSRCL1 call FNOWRITE ld c,0 pop de push de BSRCHECKING: inc c call UCASE ld b,a ld a,(de) ld hl,(POS) sbc hl,de jr z,BSRGOTIT inc de call UCASE ; Make all upcase cp b jr nz,BSRGOTIT ; Same? push de push bc call FNOWRITE pop bc pop de jr BSRCHECKING BSRGOTIT: push af ld b,c BSR_BACKING_UP: push bc call BNOWRITE pop bc djnz BSR_BACKING_UP pop af jr nz,BSRCHL2 call NSDELTOEND xor a call BESET ; We went back -- Indicate it cpl ld de,(POS) ld (NOOUT+1),a pop bc pop hl pop af push de call GETLINE ; Get a whole line in pop hl ld (POS),hl call NOSAV ; xor a ; Getline always ends with a null ld (NOOUT+1),a jp SHOWTOEND BSRCHNO: pop bc BSRCHNO1: call BEEP ; Not found do the beep thing PUTBACK: pop hl pop af ld (FP),hl ; Restore our pointers ld (SEC),a RRAND: ld c,21h call DORAND ; Read that first one back in or a ret z ZBUF: push de ld hl,SECBUF ; Zero out our record ld bc,127 call ZERO pop de ret QUITSH: call qerror ; Pop the shell stack ; ret z ; Unless we're in the error handler. pop hl call nz,SHPOP call EASERUN ld a,0 ld b,0 call z,PUT DONEOUT: call GOEOLN ; Go to the end and CR ld a,13 JCOUT: jp COUT BEEP: ld a,bell jr JCOUT BACKLINE: ; Go to previous command line call OPEN ld a,(ENDFLG) or a call z,EZER ; Till we get to start BGET: call BNOWRITE call z,FNOWRITE jr z,BEEP ; No command line before ld hl,TBUF ld (hl),0 inc hl BINSERT: ; Insert the line backing up ld (hl),a inc hl push hl call BNOWRITE pop hl jr nz,BINSERT push hl ld hl,BACKFLG ; Did we just go backwards? ld a,(hl) ld (hl),0ffh or a pop hl jr z,BGET push hl call DELLN1 ; Kill existing line pop hl dec hl BIN2: ld a,(hl) dec hl or a jr z,GOBEGINOPTION ; Put her there push hl call INSERT pop hl jr BIN2 GOBEGINOPTION: push af ld a,(GOBEGFLG) or a call nz,GOBOLN call NOSAV pop af ret NEXTLINE: ld a,(ENDFLG) ; Are we at the end? or a ret z call OPEN ; Open the sucker if it ain't already GETLINE: FGET: call FNOWRITE jr z,CHKTOP ; At the end? push af call NOTTOP ; Set flag pop af ld hl,TBUF FINSERT: ld (hl),a inc hl push hl call FNOWRITE ; Bring 'em in pop hl jr nz,FINSERT ld (hl),0 ld hl,BACKFLG ; Did we go back? ld a,(hl) ld (hl),0 or a jr nz,FGET call DELLN1 ; Delete the line ld hl,TBUF FIN2: ld a,(hl) inc hl or a jr z,GOBEGINOPTION push hl call INSERT ; Push the chars in pop hl jr FIN2 CHKTOP: call DELLN1 ; Clean out existing line call BNOWRITE xor a jr ESET ; Fix End Flag NOTTOP: xor a cpl jr ESET EZER: xor a ; Set the lot of them cpl BESET: ld (BACKFLG),a ESET: ld (ENDFLG),a ret ; --------------------------------------- ; Support routines for the commands above ; SETSCAN: push af ; Delete line and quiet terminal call DELLN1 xor a cpl ld (NOOUT+1),a pop af ret INSERT: call DOSAV ld e,a ; Put the char in ld a,(INSFLG) or a jr nz,YAINS ld hl,(POS) ld a,(hl) or a ld a,e jr nz,OVERWRITE ; Do we insert? YAINS: xor a ld b,a push de call MOVEUP ; Push them up pop de jp z,BEEP ; No room ld a,e OVERWRITE: ld hl,(POS) ld (hl),a call IPOS ; Skip over it (PRINTing it) jp SHOWTOEND ; Redisplay DELETE: call DOSAV ld de,(POS) ; Kill a char quiet-like ld a,(de) or a ret z push af ld hl,DELETED inc (hl) cp ' ' jr nc,NOINC2 ; Increment DELETED as neccessary inc (hl) NOINC2: call MOVEDOWN pop af or a ret SETKILL: push de ld hl,(POS) ld de,KILL push de ld bc,killlen ; Stop before NULL in KILL ldir ; Move it pop hl pop de ret OUTPUT: call crlf ; New line OUTPUT1: call PROMPT ; Redisplay prompt OUTPUT2: ld hl,BUFFER ; And command line ld (POS),hl jp SHOWTOEND CLRTOEND: ld hl,(POS) ; Wipe out command line from cursor to right ld de,0 call DOSAV CLRLOOP: ld a,(hl) ; Loop until NULL or a jr z,NOWBACK cp ' ' jr nc,CLR2 inc de call SPACE ; Two for Control Chars CLR2: call SPACE ; Overwrite it inc hl inc de jr CLRLOOP NOWBACK: ld a,d or e ret z dec de call BACK ; And return to old location jr NOWBACK IPOS: ld hl,(POS) ; Get current char and PRINT it ld a,(hl) or a ret z ; Return zero if NULL push af inc hl ld (POS),hl push bc ld b,a ld a,(NOOUT + 1) ; Silence? or a ld a,b pop bc call z,CCOUT pop af ret DPOS: ld hl,(POS) ; Back up (^H) dec hl ld a,(hl) or a ret z push af ld (POS),hl cp ' ' call c,BACK ; Two for Control Char call BACK pop af ret SHOWTOEND: call PRINTHL ; Show line to end jr nz,SHOWLP ld hl,DELETED ; With spaces for deleted text ld a,(hl) or a jr z,SHOWLP SHW1: push af call SPACE pop af dec a jr nz,SHW1 SHW2: call BACK dec (hl) jr nz,SHW2 SHOWLP: ld a,d or e ret z dec de call DPOS jr SHOWLP PRINTHL: ld de,0 ; Show text at HL to NULL PHLOOP: call IPOS ret z inc de KILLFLG: ld a,0 ; Check for UNDO or a ret nz push bc ld c,11 ; Check for console in call ADOS pop bc or a jr z,PHLOOP call cin ; Check for previous input ld (GETKEY+1),a cp ' ' jr c,PHLOOP cp 127 ; If it's an input, don't redisplay ret nz jr PHLOOP GETKEY: ld b,0 xor a ld (GETKEY+1),a ; Save the previous key ld a,b or a call z,cin ret MOVEUP: ld hl,(POS) ; Move the text up to accept new char ld a,' ' UPLOOP: ld b,(hl) ld (hl),a inc hl ld a,b or a jr nz,UPLOOP ld (hl),a ld de,BUFFER + killlen sbc hl,de ; Until the end jr z,MOVEDOWN ; No good, move it back or 1 ret MOVEDOWN: ; Move a char out -- Delete ld hl,(POS) ld d,h ld e,l DNLOOP: inc hl ld a,(hl) ; Pull them down till we find a NULL ld (de),a or a inc de jr nz,DNLOOP ret UNDO: ld hl,KILL ; Insert Kill buffer xor a cpl ld (KILLFLG + 1),a call UNDO1 xor a ld (KILLFLG + 1),a jp SHOWTOEND UNDO1: ld a,(hl) ; insert at HL until NULL inc hl or a ret z push hl call INSERT pop hl jr UNDO1 WFFILE: call RCHECK ; Forward a char in the file with write ld de,(FP) ld (de),a ld hl,SECTOP-1 sbc hl,de jr nz,FNOWRITE call WRAND FNOWRITE: ; Forward a char call RCHECK ld de,(FP) inc de ld hl,SECTOP sbc hl,de jr nz,NOREAD ld hl,SEC inc (hl) call RRAND ld de,SECBUF NOREAD: ld a,(de) ld (FP),de or a ret WBFILE: call RCHECK ; Back a char in file with write ld de,(FP) ld (de),a ld hl,SECBUF sbc hl,de jr nz,BNOWRITE call WRAND BNOWRITE: ; Back a char call RCHECK or a ld de,(FP) dec de ld hl,SECBUF-1 sbc hl,de jr nz,NOREAD ld hl,SEC dec (hl) ; Should never get to be ZERO... Honest. jr nc,BNOZERO ld (hl),0 ; But, what the hell, it's free. BNOZERO: call RRAND ld de,SECTOP-1 jr NOREAD WRAND: ld c,22h ; Write random call DORAND or a ret z jp nz,WMERR DORAND: ld a,(SEC) ; Put random record in right place in FCB ld hl,FCB+33 ; Set the DMA and call BDOS ld (hl),a inc hl xor a ld (hl),a inc hl ld (hl),a push de call SETDMA call FBDOS pop de ret SETDMA: push bc ; Set the DMA to the SECBUF ld de,SECBUF call DMA pop bc ret DMA: ld c,26 jp ADOS BCD2: push bc ; Format two digit numbers ld c,0ffh BCD2A: inc c sub 10 jr nc,BCD2A ; Put them in BCD (??) form add a,10 ld b,a ld a,c rlca rlca rlca rlca or b pop bc ret PAFDC: ld d,0 ; PRINT them out MAFDC: push af ; Put them in a memory location call BCD2 push af rra rra rra rra and 1111b call nz,DEPUT ; Output tens pop af call DEPUT ; Output ones pop af ret DEPUT: and 1111b add '0' inc d dec d ; Check for output jp z,COUT ld (de),a ; Else memory stuff inc de ret SETCPM3: ld a,(USE6) or a jr nz,SETCINFN6 ld hl,CINFN1 ; .. set vector to ld (cinjmp),hl ; .. function 6 routine ld c,12 ; if CP/M Plus call BDOS cp 30h ret c SETCINFN6: ld hl,CINFN6 ; .. set vector to ld (cinjmp),hl ; .. function 6 routine ret ; cinjmp equ $+1 CIN: jp 0 ;vector to console input routine ; ; ; Bdos console in, fn 1--no echo. ; CINFN1: push hl push de push bc ld hl,(1) ; patch bios conout ld de,9 add hl,de ld e,(hl) ; save bios byte (might not be JP) ld (hl),0c9h ; .. to RET to prevent echo ld c,1 ; get char from bdos call ADOS ld (hl),e ; restore bios conout byte pop bc pop de pop hl ret ; ; Bdos console in, fn 6--no echo. ; CINFN6: push de push bc CINF60: ld c,6 ld e,0ffh call ADOS or a jr z,CINF60 ;..wait for input pop bc pop de ret RCHECK: push af ; Have we read already? ld a,(RDFLG) or a jr nz,RCK1 ; Yes cpl ld (RDFLG),a ; No, now yes push hl push bc call RRAND ; And read pop bc pop hl RCK1: pop af ret SETFILE: ; Make FCB ok w/respect to Z33 FCB ld hl,FCB ld bc,35 ; Zero out FCB and DMA. call ZERO ld hl,SECBUF ld bc,80h call ZERO ld hl,NAME ld de,FCB+1 ld bc,11 ldir call getefcb ld bc,14 ; Offset to drive number add hl,bc ; HL now points to the drive number ; Here we get the drive where the program was found. Since we know that this ; is not a resident program, there is no need to check for a zero value. ld a,(hl) ; Get it and ld de,FCB ld (de),a dec hl ; Back up to user number ld a,(hl) ; Get it and ld (FCB+13),a ; put it in our FCB ld de,FCB jp z3log OPEN: call EASERUN ret nz ; Don't perform any file operations! ld hl,OPENFLG ; Open the file ld a,(hl) or a ret nz ld (hl),0ffh OKFP: call SETFILE ld c,15 ; Open call FBDOS inc a ret nz YANEW: call DELSET ld c,22 ; Make call FBDOS inc a jr z,WMERR call ZBUF call SETDMA xor a ret SETUP: ld hl,INSFLG ; Fill and Zap ld (hl),0ffh inc hl ld (hl),0ffh ; BACKFLG inc hl jp ZERO ; Zero out to exhaustion of bc NOSAV: push af xor a SAVSTO: push bc ld b,a ld a,(SMARTSAV) or b ld (SAVFLG),a pop bc pop af ret DOSAV: push af xor a cpl jr SAVSTO ZERO: ld d,h ; Fill and area with zeros ld e,l inc de ld (hl),0 ldir ret WMERR: call GOEOLN ; Write and error message call crlf ld hl,DISKFULL call PSTR ld a,'!' ld (OPENFLG),a call COUT pop hl jp REPLOT DZ3LOG: ld a,(de) or a ld a,(OLDDRIVE) jp nz,z3log ld (de),a jp z3log PUTUD: push bc push af call GUA ld (SUSER),a ld c,19h call ADOS ld (SDRIVE),a pop af pop bc ret GETUD: push de push bc push af ld a,(SUSER) call SUA ld a,(SDRIVE) call SETDRIVE pop af pop bc pop de ret SETDRIVE: ld e,a ld c,0eh ADOS: push bc push de push hl call BDOS pop hl pop de pop bc ret PRINT: ex (sp),hl ; PRINT a string call PSTR ex (sp),hl ret PSTR: push af ; PRINT a string at HL ld a,(hl) inc hl or a jr z,PSTR1 cp 1 jr z,DCRLF ; If 1, then end with a CRLF push af and 01111111b call COUT pop af and 10000000b jr nz,PSTR1 pop af jr PSTR DCRLF: call CRLF PSTR1: pop af ret CCOUT: push af ; cp 8 ; For valid CCOUT, these are needed... ; jr z,OK ; But who's gonna know? ; cp 13 ; jr z,OK ; cp 10 ; jr z,OK cp ' ' jr nc,OK push af ld a,'^' call COUT pop af add '@' OK: call COUT pop af ret SPACE: ld a,' ' ; PRINT a space jr COUT BACK: ld a,8 ; PRINT a ^H COUT: push af ; Output push bc push de push hl ld e,a NOOUT: ld a,0 ; Being quiet or a ld c,6 call z,pbdos ; And the real PRINT pop hl pop de pop bc CPOP: pop af ret PBDOS: ld a,e ld hl,SPOS cp 8 jr z,BACKP cp 13 jr z,ZEROP cp 7 jr z,NOIP cp 10 jr z,NOIP cp 9 jr z,TABCHR inc (hl) NOIP: jp BDOS ; BIOS COUT ZEROP: ld (hl),1 BACKP: dec (hl) jr NOIP TABCHR: ld a,' ' ; Expand Tab call COUT ld a,7 and (hl) ret z jr TABCHR CRLF: call PRINT ; PRINT a CRLF dc cr,lf ret ;============================================================================= ; C O N S T A N T S ;============================================================================= DUCHANGE: dc 'Illegal attempt to change directory' BADDU: dc 'Invalid directory' BADPW: dc 'Incorrect password' UNKNOWN: dc 'Unknown error' BADFORM: dc 'Bad command name (file type or wild card used)' BADECP: dc 'Command not found (even by ECP)' BADCMD: dc 'Requested load file not found on disk' AMBIG: dc 'Ambiguous or missing file name' BADNUM: dc 'Bad numerical expression' NOFILE: dc 'Source file not found' DISKFULL: dc 'Disk full' OVERFLOW: dc 'TPA overflow' DUPLICATE: dc 'Duplicate file specifications' dc 'Segment too large' dc 'Bad segment address' dc 'Command line overflow' dc 'Duplicate file specification' dc '7' dc '8' ; 17...19 not yet in use dc '9' dc 'Can''t load rsx' dc 'Can''t remove rsx' STARDOTSTAR: db '*.*',0 ; B U F F E R S ; ------------- STACK equ entry + 1310h SAVED_STACK equ stack + 2 FCB equ saved_stack + 2 OLDDRIVE equ fcb + 36 STARTNAME equ olddrive + 1 SEARCH_USER equ startname + 2 LASTCMD equ search_user + 1 JUSTCMD equ lastcmd + 1 SUSER equ justcmd + 1 SDRIVE equ suser + 1 SEC equ sdrive + 1 FP equ sec + 2 POS equ fp + 2 DELIMPTR equ pos + 2 DELIM equ delimptr + 2 ZEXORSUB equ delim + 2 INSFLG equ zexorsub + 1 BACKFLG equ insflg + 1 ENDFLG equ backflg + 1 RDFLG equ endflg + 1 OPENFLG equ rdflg + 1 SHIFT equ openflg + 1 SAVFLG equ shift + 1 SPOS equ savflg + 1 DELETED equ spos + 1 TBUF equ deleted + 2 KILL equ tbuf + killlen + 2 BUFFER equ kill + killlen + 2 DEFAULT_TOP_OF_BUFFER equ buffer + killlen end