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