; > CommonIO ; Common host interface routines for hosts that do not implement BBC API functionality. ; 09-Apr-2024 Added *BASIC command ; OSCLI - Execute command ; ======================= ; Built-in *commands ; ------------------ .IO_CLIcmds ;.UX_CLIcmds equb &FF equb "basic",&8E equb "chdir",&86 equb "cd" ,&86 equb "esc" ,&8C equb "fx" ,&80 equb "help" ,&82 equb "load" ,&88 equb "quit" ,&84 equb "save" ,&8A ;"run",&90 equb 0 align .IO_CLIaddrs equw IO_CLIhost ; *fx equw CLIhelp ; *help equw IO_QUIT0 ; *quit equw CLIchdir ; *chdir, *cd equw CLIload ; *load equw CLIsave ; *save equw CLIesc ; *esc equw CLIbasic ; *basic ; OSCLI - Execute command ; ======================= ; On entry, r0=>command string ; On exit, r0=return status ; .IO_OSCLI mov r1,-(sp) ; Save R1 .IO_CLIlp1 movb (r0)+,r1 cmpb r1,#ASC"*" ; Skip stars beq IO_CLIlp1 cmpb r1,#ASC" " ; Skip spaces beq IO_CLIlp1 cmpb r1,#13 beq IO_CLInull ; Null string cmpb r1,#ASC"|" beq IO_CLInull ; Comment cmpb r1,#ASC"/" beq IO_CLIexternal ; */filename dec r0 ; Point back to non-space non-star char mov r0,-(sp) ; Save address after '*'s and ' 's mov r2,-(sp) ; Save R2 mov #IO_CLIcmds,r2 ; Point to command table .IO_CLIlp2 tstb (r2)+ bpl IO_CLIlp2 ; Look for start of table entry tstb (r2) ; End of command table? bne IO_CLIcmd ; Not end, check if matching command mov (sp)+,r2 ; Restore R2 mov (sp)+,r0 ; r0=>command string .IO_CLIexternal cmpb (r0)+,#ASC" " ; Skip any spaces after */ beq IO_CLIexternal dec r0 jmp IO_CLIslash ; End of table or */, try passing to system() .IO_CLIcmd mov 2(sp),r0 ; Get start of command line .IO_CLIlp3 movb (r0)+,r1 ; Get command line character bis #&20,r1 ; Force to lower case cmpb (r2)+,r1 ; Compare with table beq IO_CLIlp3 ; Characters match, check next dec r0 ; Point to non-matching command char dec r2 ; Point to non-matching table char cmpb (r0),#ASC"A" ; Check last character tested bcc IO_CLIlp2 ; Not end of command string, try next command movb (r2),r1 bpl IO_CLIlp2 ; Not end of table entry, try next one mov (sp)+,r2 ; Restore R2 .IO_CLIlp4 cmpb (r0)+,#ASC" " ; Skip trailing spaces beq IO_CLIlp4 dec r0 ; Command matched ; r0=> or after command ; r1=&80+n command number ; sp=>R0, R1 bic #&FF80,r1 ; Drop bit 7 and bit 0 mov IO_CLIaddrs(r1),r1 jsr pc,(r1) ; Errors never return ;bvs IO_CLIerror ; Restore and return with error tst (sp)+ ; Drop start of command .IO_CLInull mov (sp)+,r1 ; Restore R1 clr r0 ; Return R0=Ok rts pc ;.IO_CLIerror ;tst (sp)+ ; Drop start of command ;mov (sp)+,r1 ; Restore R1 ;setv ; Set V ;rts pc ; Common commands ; =============== ; *help ; ----- .CLIhelp mov #StartupMessage,r1 jsr pc,PrintR1 #ifdef RT11VER jsr pc,PrintInline equs "RT11 Host v" equb ((RT11VER >> 8) AND 15)+48 equb "." equb ((RT11VER >> 4) AND 15)+48 equb (RT11VER AND 15)+48 equb 13,0 align #endif .IO_CLIhost #if OSNAME$="Unix" #ifndef NOEMT mov 2(sp),r0 ; Get R0=>command emt 1 ; Pass on to host #endif #endif rts pc ; *esc (on|off) ; ------------- .CLIesc cmpb (r0)+,#ASC"@" ; Check for 'O'... bcs CLIescOn movb (r0),r1 ; Check for 'oN'.. or 'oF'... ; %1110 or %0110 .CLIescOn rol r1 ; Move to bit 4 add #FLG_ESCAPE,r1 ; Toggle bit 4 bic #-FLG_ESCAPE-1,r1 ; Keep bit 4, N/F bicb #FLG_ESCAPE,SV_SYS ; Remove bit 4 from flags bisb r1,SV_SYS ; Insert into system flags rts pc ; *basic ; ------ .CLIbasic adr SV_STRING,r1 mov r0,r5 jsr pc,InsAddLine ; Copy any command line mov SV_MEMTOP,r1 ; Get memory limits mov SV_MEMBOT,r0 jmp Restart ; Restart ; *quit ; ----- ;.CLIquit ;jmp IO_QUIT0 ; *load ; ----- .CLIload jsr pc,ScanAddrsR0 dec r0 bmi CLILoad1 ; *load name beq CLILoad1 ; *load name addr .jmpBadAddress jmp errBadAddress .CLILoad1 movb r0,MOS_BUF+6 ; &FF=use file's addr or &00=use supplied addr mov #&FF,r0 ; R0=LOAD .CLIFile mov #MOS_BUF,r1 jmp IO_FILE ; Error will be caught before return ; *save ; ----- .CLIsave jsr pc,ScanAddrsR0 cmp r0,#2 ; *save name bcs jmpBadAddress ; *save name addr cmp r0,#5 bcc jmpBadAddress ; *save too many addresses clr r0 ; R0=SAVE br CLIFile ; Scan a list of file addresses ; ----------------------------- .ScanHex2 mov #MOS_BUF+2,r1 .ScanHex ; Would this be smaller to do manually? mov r0,-(sp) mov r1,-(sp) jsr pc,CheckHexNext bcs jmpBadAddress ; ;; Call BASIC's evaluator ;; R0= first hex digit ;; R1=4 -> 4 bits per digit ;; R3=0 -> accumulator ;; R4=0 -> accululator ;; R5=> first digit, already checked ;mov #4,r1 ;clr r3 ;clr r4 ;jsr pc,EvalHexGo ;; R3/R4=hex value ;; R5=>first non-hex character ; ; Call BASIC's evaluator ; R3=0 -> accumulator ; R4=0 -> accululator ; R5=> first digit, already checked clr r3 ; Clear accumulator clr r4 dec r5 ; Back up R5 to first digit jsr pc,EvalHexGo ; R0/R1/R2 corrupted ; R3/R4=hex value ; R5=>first non-hex character ; mov (sp)+,r1 mov r4,(r1)+ mov r3,(r1) mov (sp)+,r0 .ScanHexSpc cmpb (r5)+,#ASC" " beq ScanHexSpc dec r5 cmpb (r5),#13 rts pc .ScanAddrsR0 mov r0,r1 ; r0=>command line .ScanAddrs mov r5,-(sp) mov r4,-(sp) mov r3,-(sp) mov r2,-(sp) mov r1,r5 ; r1=>command line mov r1,MOS_BUF ; filename jsr pc,SkipWord clr r0 cmpb (r5),#13 beq ScanAddrsDone ; No addresses inc r0 jsr pc,ScanHex2 ; BUF+2 = load mov r4,MOS_BUF+6 ; BUF+6 = exec mov r3,MOS_BUF+8 mov r4,MOS_BUF+10 ; BUF+10= start mov r3,MOS_BUF+12 cmpb (r5),#13 beq ScanAddrsDone ; One address inc r0 mov #MOS_BUF+14,r1 cmpb (r5),#ASC"+" bne ScanHexEnd inc r5 jsr pc,ScanHexSpc jsr pc,ScanHex ; BUF+14=length add MOS_BUF+10,r4 adc r3 add MOS_BUF+12,r3 mov r4,MOS_BUF+14 mov r3,MOS_BUF+16 cmpb (r5),#13 br ScanHexEnd2 .ScanHexEnd jsr pc,ScanHex ; BUF+14=end .ScanHexEnd2 beq ScanAddrsDone ; Two address inc r0 mov #MOS_BUF+6,r1 jsr pc,ScanHex ; BUF+6 = exec beq ScanAddrsDone ; Three address inc r0 jsr pc,ScanHex2 ; BUF+2 = load beq ScanAddrsDone ; Four address inc r0 ; Five or more addresses .ScanAddrsDone mov (sp)+,r2 mov (sp)+,r3 mov (sp)+,r4 mov (sp)+,r5 rts pc .SkipWord cmpb (r5)+,#ASC"!" bcc SkipWord dec r5 br ScanHexSpc ; Support routines ; ================ ; Get word from control block at r2 and add r3 to it, return r2=r2+4 ; ------------------------------------------------------------------ .FetchAddStore jsr pc,FetchWord add r3,r1 adc r0 ; Continue into StoreWord ; Store &r0:r1 to unaligned buffer at r2, r1=lo r0=hi, corrupts r0/r1, return r2=r2+4 ; ----------------------------------------------------------------------------------- .StoreWord movb r1,(r2)+ swab r1 movb r1,(r2)+ movb r0,(r2)+ swab r0 movb r0,(r2)+ rts pc ; Fetch &r0:r1 from unaligned buffer at r2, r1=lo r0=hi, return r2=r2+4 ; --------------------------------------------------------------------- .FetchWord jsr pc,FetchHalfWord mov r0,-(sp) jsr pc,FetchHalfWord mov (sp)+,r1 rts pc ; Get 16-bit half-word from unaligned r2 to r0, return r2=r2+2 ; ------------------------------------------------------------ .FetchHalfWord movb (r2)+,r1 bic #&FF00,r1 movb (r2)+,r0 bic #&FF00,r0 swab r0 bis r1,r0 rts pc ; Read line of text ; ----------------- ; On entry, R1=>address of memory to read string to ; On exit, R2=length of string ; CC=ok, CS=Escape pressed ; ; Could do in-line cursor movement ; .IO_WORD0 mov r1,-(sp) ; Save pointer to control block mov r3,-(sp) ; Save R3 mov r1,r2 ; r2=>control block jsr pc,FetchWord ; Get pointer to text buffer to r1 clr r2 ; Zero number of characters read .RdLnLoop jsr pc,IO_RDCH ; Get a character bcs RdLnEsc ; Escape state cmp r0,#13 beq RdLnCR ; - End of line cmp r0,#10 beq RdLnCR ; - End of line cmp r0,#21 beq RdLnU ; Ctrl-U - delete line cmp r0,#&C7 beq RdLnDel ; - del a character cmp r0,#127 beq RdLnDel ; - del a character cmp r0,#8 beq RdLnDel ; - del a character cmp r0,#ASC" " ;bcs RdLnLoop ; Ignore other control characters bcs RdLnIgnore ; Just echo other control characters cmp r2,#240 bcc RdLnLoop ; No more room for characters movb r0,(r1)+ ; Put character into memory inc r2 ; Inc. number of characters .RdLnIgnore jsr pc,IO_WRCH ; Output the character br RdLnLoop ; Go back for another ; .RdLnU mov r2,r3 ; We want to delete all characters br RdLnDelete .RdLnDel mov #1,r3 ; We only want to delete one char .RdLnDelete mov #&2008,r0 ; Output by backspacing .RdLnDeleteLp tst r2 ; Check line length beq RdLnLoop ; Length=0, jump back to main loop jsr pc,IO_WRCH ; swab r0 jsr pc,IO_WRCH ; swab r0 jsr pc,IO_WRCH ; again ;mov #8,r0 ; Output by backspacing ;jsr pc,IO_WRCH ;mov #32,r0 ;jsr pc,IO_WRCH ;mov #8,r0 ;jsr pc,IO_WRCH dec r1 ; Back address pointer dec r2 ; Dec character counter dec r3 ; Dec. number of DELs to do bne RdLnDeleteLp ; Loop for each to delete br RdLnLoop ; Go back into ReadLine loop ; .RdLnCR jsr pc,IO_NEWL ; Returns with r0= movb r0,(r1) ; Put terminator in clr r0 ; Restore R0, clear C .RdLnEsc .RdLnExit mov (sp)+,r3 ; Restore R3 mov (sp)+,r1 ; Get buffer address back, clear V rts pc ; r0=0, r1=buffer, r2=length