10 REM > Mini65/src
   20 REM Source for Tiny 6502 Single-port Tube Client
   30 REM ============================================
   40 REM This code may be freely reused, with acknowledgements
   50 :
   60 REM Tiny 6502 Tube Client in 512 bytes
   70 REM Implements OSWRCH and OSRDCH via a single I/O port.
   80 :
   90 :
  100 REM Boilerplate
  110 REM ===========
  120 IF HIMEM>&FFFF:SYS "OS_GetEnv"TOA$:IFLEFT$(A$,5)<>"B6502":OSCLI"B6502"+MID$(A$,INSTR(A$," "))
  130 ON ERROR REPORT:PRINT" at line ";ERL:END
  140 DEFFNif(A%):IFA%:z%=-1:=opt% ELSE z%=P%:=opt%
  150 DEFFNendif:IFz%=-1:=opt% ELSE z%=P%-z%:P%=P%-z%:O%=O%-z%:=opt%
  160 DEFFNelse:IFz%=-1:z%=P%:=opt% ELSE z%=P%-z%:P%=P%-z%:O%=O%-z%:z%=-1:=opt%
  170 DIM mcode% &1FF, L% &40
  180 :
  190 :
  200 REM Code to enter after RESET
  210 REM =========================
  220 MAIN=&FFB9:REM Replace with address to jump to
  230 MEMBOT%=&0800:REM Bottom of user memory
  240 MEMTOP%=&8000:REM Top of user memory
  250 RDLINE%=TRUE
  260 :
  270 :
  280 REM Name and version
  290 REM ================
  300 ver$="0.10":date$=" (11 Nov 2016)"
  310 load%=&FE00:name$="Mini65"
  320 :
  330 :
  340 REM System vectors
  350 REM ==============
  360 USERV=&200:BRKV =&202:IRQ1V=&204:IRQ2V=&206:CLIV =&208:BYTEV=&20A
  370 WORDV=&20C:WRCHV=&20E:RDCHV=&210:FILEV=&212:ARGSV=&214:BGetV=&216
  380 BPutV=&218:GBPBV=&21A:FINDV=&21C:FSCV =&21E:EVNTV=&220
  390 LPTR=&F8:TEXT=&FA:IRQA=&FC:FAULT=&FD:ESCFLG=&FF
  400 :
  410 REM I/O values, these are suitable for a 6850
  420 REM =========================================
  430 TxStatus=&FEF0:TxRDY=2:TxData=&FEF1:TxInit=&13:RxStop=&55:REM &13=Reset ACIA, &55=8N1, clock/16, RTS high
  440 RxStatus=&FEF0:RxRDY=1:RxData=&FEF1:RxInit=&55:RxCont=&15:REM &55=RTS high, &15=RTS low
  450 :
  460 REM Serial Tube system values
  470 REM =========================
  480 esc=&9B
  490 :
  500 :
  510 FOR P=0 TO 1:opt%=P*3+4
  520   P%=load%:O%=mcode%
  530   [OPT opt%
  540   .LF800
  550   JMP RESET
  560   .PrBanner
  570   JSR PrText
  580   .BANNER
  590   EQUB 13:EQUS "Tiny 6502 Kernel ":EQUS ver$
  600   EQUB 13:EQUB 0
  610   RTS
  620   :
  630   \ Tube Client Startup Code
  640   \ ========================
  650   .RESET
  660   SEI:LDX #&35               :\ Disable interupts
  670   .RESETlp
  680   LDA LFF00,X:STA &0200,X    :\ Copy default vectors
  690   DEX:BPL RESETlp:TXS        :\ Clear stack
  700   LDA #TxInit:STA TxStatus   :\ Initialise I/O port
  710   LDA #RxInit:STA RxStatus
  720   LDA #&00:STA ESCFLG:CLI
  730   JSR PrBanner:JSR OSNEWL
  740   LDA #&01:JMP OSCOLD        :\ Enter main program
  750   :
  760   .osBYTE
  770   CMP #&83:BCC osBYTEexit
  780   CMP #&85:BCS osBYTEexit
  790   LDX #0:LDY #MEMBOT% DIV 256
  800   ROR A:BCS osBYTEok
  810   LDY #MEMTOP% DIV 256
  820   .osBYTEok
  830   ROL A
  840   .osBYTEexit
  850   RTS
  860   :
  870   OPT FNif(RDLINE%)           :\ Perform RDLINE locally
  880   .osWORD
  890   STX LPTR+0:STY LPTR+1     :\ LPTR=>control block
  900   TAY:BNE Word00Exit        :\ OSWORD 0, jump to read line
  910   LDA (LPTR),Y:TAX:INY
  920   LDA LPTR,Y:STA LPTR+1
  930   STX LPTR+0:DEY            :\ (LPTR)=>string buffer, Y=0
  940   .Word00Lp1
  950   JSR OSRDCH:BCS Word00Exit :\ Wait for character
  960   CMP #&7F:BNE Word00Char   :\ Not Delete
  970   .Word00Delete
  980   TYA:BEQ Word00Lp1         :\ Nothing to delete
  990   LDA #&7F:JSR OSWRCH       :\ VDU 127
 1000   .Word00Back
 1010   DEY:JMP Word00Lp1         :\ Dec. counter, loop back
 1020   .Word00Char
 1030   CMP #&08:BEQ Word00Delete :\ BS is also Delete
 1040   CMP #&15:BNE Word00Ins    :\ Not Ctrl-U
 1050   TYA:BEQ Word00Lp1         :\ Nothing to delete
 1060   LDA #&7F
 1070   .Word00Lp2
 1080   JSR OSWRCH:DEY            :\ Delete characters
 1090   BNE Word00Lp2
 1100   BEQ Word00Lp1             :\ Jump back to start
 1110   .Word00Ins
 1120   STA (LPTR),Y              :\ Store character
 1130   CMP #&0D:CLC:BEQ Word00cr :\ Return - finish
 1140   CMP #&20:BCS Word00ctrl   :\ Control character
 1150   INY:BEQ Word00Back        :\ Too long, dec and go back
 1160   .Word00ctrl
 1170   JSR OSWRCH                :\ Print character
 1180   .Word00max
 1190   JMP Word00Lp1             :\ Loop for more
 1200   .Word00cr
 1210   JSR OSNEWL:CLC            :\ Return with CC, Y=length
 1220   .Word00Exit
 1230   RTS
 1240   OPT FNelse
 1250   :
 1260   \ Default error handler
 1270   \ =====================
 1280   .ErrorHandler
 1290   JSR OSNEWL
 1300   LDX FAULT+0:LDY FAULT+1
 1310   INX:BNE P%+3:INY           :\ XY=>error string
 1320   JSR PRSTRNG                :\ Print error string
 1330   .HALT
 1340   JMP HALT                   :\ And stop
 1350   :
 1360   \ Interrupt handlers
 1370   \ ==================
 1380   .IRQHandler
 1390   STA IRQA:PLA:PHA           :\ Save A, get flags from stack
 1400   AND #&10:BNE BRKHandler    :\ If BRK, jump to BRK handler
 1410   JMP (IRQ1V)                :\ Continue via IRQ1V handler
 1420   .IRQ1Handler
 1430   JMP (IRQ2V)                :\ Pass on to IRQ2V
 1440   .BRKHandler
 1450   TXA:PHA                    :\ Save X
 1460   TSX:LDA &0103,X:CLD        :\ Get address from stack
 1470   SEC:SBC #&01:STA FAULT+0
 1480   LDA &0104,X
 1490   SBC #&00:STA FAULT+1       :\ &FD/E=>after BRK opcode
 1500   PLA:TAX:LDA IRQA           :\ Restore X, get saved A
 1510   CLI:JMP (BRKV)             :\ Restore IRQs, jump to Error Handler
 1520   .IRQ2Handler
 1530   LDA IRQA                   :\ Restore A
 1540   .NMIHandler
 1550   RTI                        :\ Return from interupt
 1560   :
 1570   \ OSRDCH - Wait for character from input stream
 1580   \ =============================================
 1590   \ On exit, A =char, Cy=Escape flag
 1600   .osRDCH
 1610   JSR ReadData:BCC osRDCH    :\ Wait for character
 1620   BNE osRDCH2
 1630   .osRDCH1
 1640   JSR ReadData:BCC osRDCH1   :\ Wait for character
 1650   .osRDCH2
 1660   CMP #27:BEQ osRDCH3        :\ Exit with CS if Escape
 1670   .ReadDataNone
 1680   CLC                        :\ CC if not Escape
 1690   .osRDCH3
 1700   RTS
 1710   :
 1720   \ OSWRCH - Send character to output stream
 1730   \ ========================================
 1740   \ On entry, A =character
 1750   .osWRCH                    :\ WRCH is simply SendByte
 1760   .SendByte
 1770   JSR SendData               :\ Send byte
 1780   CMP #esc:BNE SendByteDone  :\ If not esc, done
 1790   \                          :\ Fall through to send again
 1800   :
 1810   :
 1820   \ *** LOW-LEVEL I/O CODE ***
 1830   \ ==========================
 1840   :
 1850   \ Send a raw byte of data
 1860   \ -----------------------
 1870   .SendData
 1880   PHA
 1890   .SendWait
 1900   LDA TxStatus               :\ Get Status
 1910   AND #TxRDY:BEQ SendWait    :\ Wait until data can be sent
 1920   PLA:STA TxData             :\ Send data
 1930   .SendByteDone
 1940   RTS
 1950   :
 1960   \ Read raw data
 1970   \ -------------
 1980   \ On exit, P =CC, no data
 1990   .ReadData
 2000   LDA RxStatus:AND #RxRDY    :\ Get RxStatus
 2010   BNE ReadDataNone           :\ Exit if no data present
 2020   LDA RxData:CMP #esc:SEC:RTS:\ CS=Data present, EQ/NE=esc
 2030   :
 2040   :
 2050   DEFAULT VECTOR TABLE
 2060   \ ====================
 2070   .LFF00
 2080   EQUW NullReturn      :\ &200 - USERV
 2090   EQUW ErrorHandler    :\ &202 - BRKV
 2100   EQUW IRQ1Handler     :\ &204 - IRQ1V
 2110   EQUW IRQ2Handler     :\ &206 - IRQ2V
 2120   EQUW NullReturn      :\ &208 - CLIV
 2130   EQUW osBYTE          :\ &20A - BYTEV
 2140   OPT FNif(RDLINE%)
 2150   EQUW osWORD        :\ &20C - WORDV
 2160   OPT FNelse
 2170   EQUW NullReturn    :\ &20C - WORDV
 2180   OPT FNendif
 2190   EQUW osWRCH          :\ &20E - WRCHV
 2200   EQUW osRDCH          :\ &210 - RDCHV
 2210   EQUW NullReturn      :\ &212 - FILEV
 2220   EQUW NullReturn      :\ &214 - ARGSV
 2230   EQUW NullReturn      :\ &216 - BGetV
 2240   EQUW NullReturn      :\ &218 - BPutV
 2250   EQUW NullReturn      :\ &21A - GBPBV
 2260   EQUW NullReturn      :\ &21C - FINDV
 2270   EQUW Unsupported     :\ &21E - FSCV
 2280   EQUW NullReturn      :\ &220 - EVNTV
 2290   EQUW Unsupported     :\ &222 - UPTV
 2300   EQUW Unsupported     :\ &224 - NETV
 2310   EQUW Unsupported     :\ &226 - VduV
 2320   EQUW Unsupported     :\ &228 - KEYV
 2330   EQUW Unsupported     :\ &22A - INSV
 2340   EQUW Unsupported     :\ &22C - RemV
 2350   EQUW Unsupported     :\ &22E - CNPV
 2360   EQUW NullReturn      :\ &230 - IND1V
 2370   EQUW NullReturn      :\ &232 - IND2V
 2380   EQUW NullReturn      :\ &234 - IND3V
 2390   :
 2400   .PrHexXY
 2410   TYA:JSR PrHex:TXA
 2420   .PrHex
 2430   PHA:LSR A:LSR A:LSR A:LSR A
 2440   JSR PrNybble:PLA
 2450   .PrNybble
 2460   AND #15:CMP #10:BCC PrDigit:ADC #6
 2470   .PrDigit
 2480   ADC #ASC"0":JMP OSWRCH
 2490   :
 2500   .PrText
 2510   PLA:STA TEXT+0:PLA:STA TEXT+1 :\ TEXT=>embedded string
 2520   JSR PrString2:JMP (TEXT)      :\ Print string and jump back to code
 2530   :
 2540   .PrString
 2550   STX TEXT+0:STY TEXT+1      :\ TEXT=>string at YX
 2560   .PrStringLp
 2570   LDY #&00:LDA (TEXT),Y      :\ Get character
 2580   BEQ PrString2:JSR OSASCI   :\ Print character if not &00
 2590   .PrString2
 2600   INC TEXT+0:BNE LFEA6:INC TEXT+1 :\ Increment address
 2610   .LFEA6
 2620   TAY:BNE PrStringLp         :\ Loop back if not &00
 2630   :
 2640   .ScanHex
 2650   .InitError
 2660   .Unsupported
 2670   .NullReturn
 2680   RTS
 2690   :
 2700   \ Standard Tube entry points
 2710   \ ==========================
 2720   EQUS STRING$((&FF95-P%)AND255,CHR$255)
 2730   .LFF95   :JMP NullReturn       :\ &FF95
 2740   .OSCOLD  :JMP MAIN             :\ &FF98  Start main program
 2750   .PRSTRNG :JMP PrString         :\ &FF9B  Print zero-terminated text at YX, returns A=0, Y corrupted
 2760   .LFF9E   :JMP NullReturn       :\ &FF9E
 2770   .SCANHEX :JMP ScanHex          :\ &FFA1  Scan hex string at (&F8), returned in &F0/1
 2780   .DISKACC :JMP NullReturn       :\ &FFA4
 2790   .DISKCCP :JMP NullReturn       :\ &FFA7
 2800   .PRHEX   :JMP PrHex            :\ &FFAA  Print A in hex, A corrupted
 2810   .PR2HEX  :JMP PrHexXY          :\ &FFAD  Print YX in hex, A corrupted
 2820   .USERINT :JMP NullReturn       :\ &FFB0
 2830   .PRTEXT  :JMP PrText           :\ &FFB3  Print zero-terminated inline text, returns A=0, Y corrupted
 2840   .VECDEF  :EQUB &36:EQUW LFF00  :\ &FFB6
 2850   .OSQUIT  :JMP MAIN             :\ &FFB9  Quit current program
 2860   .OSERROR :JMP NullReturn       :\ &FFBC
 2870   .OSINIT  :JMP InitError        :\ &FFBF  Initialise MOS error handler, A corrupted
 2880   .DISKRST :JMP NullReturn       :\ &FFC2
 2890   .LFFC5   :JMP NullReturn       :\ &FFC5
 2900   .NVRDCH  :JMP osRDCH           :\ &FFC8
 2910   .NVWRCH  :JMP osWRCH           :\ &FFCB
 2920   :
 2930   .OSFIND  :JMP (FINDV)          :\ &FFCE
 2940   .OSGBPB  :JMP (GBPBV)          :\ &FFD1
 2950   .OSBPUT  :JMP (BPutV)          :\ &FFD4
 2960   .OSBGET  :JMP (BGetV)          :\ &FFD7
 2970   .OSARGS  :JMP (ARGSV)          :\ &FFDA
 2980   .OSFILE  :JMP (FILEV)          :\ &FFDD
 2990   :
 3000   .OSRDCH  :JMP (RDCHV)          :\ &FFE0
 3010   .OSASCI  :CMP #&0D:BNE OSWRCH  :\ &FFE3
 3020   .OSNEWL  :LDA #&0A:JSR OSWRCH  :\ &FFE7
 3030   .OSWRCR  :LDA #&0D             :\ &FFEC
 3040   .OSWRCH  :JMP (WRCHV)          :\ &FFEE
 3050   .OSWORD  :JMP (WORDV)          :\ &FFF1
 3060   .OSBYTE  :JMP (BYTEV)          :\ &FFF4
 3070   .OS_CLI  :JMP (CLIV)           :\ &FFF7
 3080   :
 3090   .NMIV    :EQUW NMIHandler      :\ &FFFA  NMI Vector
 3100   .RESETV  :EQUW RESET           :\ &FFFC  RESET Vector
 3110   .IRQV    :EQUW IRQHandler      :\ &FFFE  IRQ Vector
 3120 ]:NEXT
 3130 IF O%>L%:PRINT"ERROR: Code overrun"
 3140 PRINT "Saving ";name$;
 3150 OSCLI "Save "+name$+" "+STR$~mcode%+" "+STR$~O%+" "+STR$~load%+" "+STR$~load%
 3160 PRINT