Matchbox sized 6502 / Z80 / 6809 Co Pro

emulators, hardware and classic software for atom + system machines
Post Reply
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Matchbox sized 6502 / Z80 / 6809 Co Pro

Post by hoglet »

Hi all,

Yesterday I received a small package from a very generous donor (thanks Ed....):
IMG_0698.JPG
It's a GODIL GOP XC3S200 module:
http://www.oho-elektronik.de/pics/UM_XC3S200.pdf

This thing is truly tiny, and is an amazingly neat bit of hardware. It could easily be turned into a complete Atom.

It's soon going to be a 6502 second processor. I just need to make a small PCB to connect the 24 pin DIL socket to a 40 pin IDC socket. The plan is to initially test this on the BBC Model B, and then have a go at connecting it to the Atom.

On Tuesday night I started cutting and pasting VHDL from the Atom FPGA project to make a second processor with just 4 components:
- a DCM (clock thingy)
- the T65 6502 core (4MHz initially)
- a ROM
- Ed/Richards Tube Verilog interface

Here's the VHDL in GitHub:
https://github.com/hoglet67/CoPro6502/b ... ro6502.vhd

There's a small amount of additional logic to implement the boot mode, where code executes initially from ROM, copies itself to RAM, and then on the first tube access starts running from RAM.

Last night I had a go at simulating it, which was a lot of fun, and found several bugs that would have been very hard to debug on real hardware. I've managed to simulate it for about 10ms, which is enough to see it copying itself into RAM, and then disabling boot mode.

At the moment, it's hanging on the first OSWRCH call, because the tube chip isn't initialized, so it's waiting forever to be allowed to send the "Acorn TUBE 6502 64K" message.

In the next post, I'll post JHG's excellent commented disassembly of the 6502 Tube client code, which will serve as a useful reference for the expected shenanigans of the next few days.

I'm going to have a go at trying to get the tube interface properly initialized in the simulation, and see if I can get the code to run any further.

More later....

Dave
Last edited by hoglet on Fri Nov 07, 2014 4:18 pm, edited 4 times in total.
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Re: Matchbox sized 6502 Co Processor

Post by hoglet »

Commented 6502 Tube Client Code thanks to JGH:
http://mdfs.net/Software/Tube/6502/

The whole thing is only 2KB.

This needs to run from RAM, as the NMI vector at FFFA/FFFB is dynamice updated :shock: (by the code at FD65). There is also another piece of self-modifying code at F860).

Code: Select all

   10 REM >Client/src
   20 REM Source for 6502 Tube Client
   30 REM As supplied with External 6502 Second Processor
   40 REM Code copyright Acorn Computer
   50 REM Commentary copyright J.G.Harston
   60 :
   70 IF PAGE>&8000:LOADATN "OS_GetEnv"TOA$:IFLEFT$(A$,5)<>"B6502":OSCLI"B6502"+MID$(A$,INSTR(A$," "))
   80 :
   90 load%=&F800:DIM mcode% &900
  100 :
  110 USERV=&200: BRKV=&202:IRQ1V=&204:IRQ2V=&206
  120  CLIV=&208:BYTEV=&20A:WORDV=&20C:WRCHV=&20E
  130 RDCHV=&210:FILEV=&212:ARGSV=&214:BGetV=&216
  140 BPutV=&218:GBPBV=&21A:FINDV=&21C: FSCV=&21E
  150 EVNTV=&220: UPTV=&222: NETV=&224: VduV=&226
  160  KEYV=&228: INSV=&22A: RemV=&22C: CNPV=&22E
  170 IND1V=&230:IND2V=&232:IND3V=&234
  180 :
  190 ERRBUF=&236:INPBUF=&236
  200 :
  210 :
  220 REM Memory addresses:
  230 REM &EE/F = PROG   - Current program
  240 REM &F0/1 = NUM    - hex accumulator
  250 REM &F2/3 = MEMTOP - top of memory
  260 REM &F4/5 = address of byte transfer address, NMIAddr or ADDR
  270 REM &F6/7 = ADDR   - Data transfer address
  280 REM &F8/9 = String pointer, OSWORD control block
  290 REM &FA/B = CTRL   - OSFILE, OSGBPB control block, PrText string pointer
  300 REM &FC   = IRQ A store
  310 REM &FD/E => last error
  320 REM &FF   = Escape flag
  330 :
  340 FOR P=0TO1
  350 P%=load%:O%=mcode%
  360 [OPT P*3+4
  370 .RESET
  380 LDX #&00
  390 .LF802
  400 LDA &FF00,X:STA &FF00,X    :\ Copy entry block to RAM
  410 DEX:BNE LF802
  420 LDX #&36
  430 .LF80D
  440 LDA LFF80,X:STA USERV,X    :\ Set up default vectors
  450 DEX:BPL LF80D
  460 TXS:LDX #&F0               :\ Clear stack
  470 .LF819
  480 LDA &FDFF,X:STA &FDFF,X    :\ Copy &FE00-&FEEF to RAM, avoiding
  490 DEX:BNE LF819              :\  Tube registers at &FEFx
  500 LDY #RESET AND 255:STY &F8 :\ Point to start of ROM
  510 LDA #RESET DIV 256:STA &F9
  520 .LF82A                     :\ Copy rest of ROM to RAM
  530 LDA (&F8),Y:STA (&F8),Y    :\ Copy a page to RAM
  540 INY:BNE LF82A              :\ Loop for 256 bytes
  550 INC &F9:LDA &F9            :\ Inc. address high byte
  560 CMP #&FE:BNE LF82A         :\ Loop from &F800 to &FDFF
  570 LDX #&10
  580 .LF83B
  590 LDA LF859,X:STA &0100,X    :\ Copy jump code to &100
  600 DEX:BPL LF83B
  610 LDA &EE:STA &F6            :\ Copy &EE/F to &F6/7
  620 LDA &EF:STA &F7
  630 LDA #&00:STA &FF           :\ Clear Escape flag
  640 STA &F2:LDA #&F8:STA &F3   :\ Set memtop to start of ROM at &F800
  650 JMP &0100                  :\ Jump via low memory to page ROM out
  660 
  670 \ Executed in low memory to page ROM out
  680 \ --------------------------------------
  690 .LF859
  700 LDA TubeS1:CLI             :\ Check Tube R1 status to page ROM out
  710 .LF85D
  720 JMP LF860                  :\ Jump to initilise I/O with banner
  730 
  740 .LF860
  750 JSR PrText                 :\ Display startup banner
  760 EQUB 10:EQUS "Acorn TUBE 6502 64K"
  770 EQUB 10:EQUB 10:EQUB 13:EQUB 0
  780 NOP
  790 LDA #CmdOSLoop AND 255     :\ Next time RESET is soft entered,
  800 STA LF85D+1                :\ banner not printed
  810 LDA #CmdOSLoop DIV 256
  820 STA LF85D+2
  830 JSR WaitByte               :\ Wait for Acknowledge
  840 CMP #&80:BEQ EnterCode     :\ If &80, jump to enter code
  850                            :\ Otherwise, enter command prompt loop
  860 
  870 \ Minimal Command prompt
  880 \ ======================
  890 .CmdOSLoop
  900 LDA #ASC"*":JSR OSWRCH     :\ Print '*' prompt
  910 LDX #LF95D AND 255
  920 LDY #LF95D DIV 256
  930 LDA #&00:JSR OSWORD        :\ Read line to INPBUF
  940 BCS CmdOSEscape
  950 LDX #INPBUF AND 255
  960 LDY #INPBUF DIV 256        :\ Execute command
  970 JSR OS_CLI:JMP CmdOSLoop   :\  and loop back for another
  980 .CmdOSEscape
  990 LDA #&7E:JSR OSBYTE        :\ Acknowledge Escape state
 1000 BRK:EQUB 17:EQUS "Escape":BRK
 1010 
 1020 
 1030 \ Enter Code pointer to by &F6/7
 1040 \ ==============================
 1050 \ Checks to see if code has a ROM header, and verifies
 1060 \ it if it has
 1070 .EnterCode
 1080 LDA &F6:STA &EE:STA &F2    :\ Set current program and memtop
 1090 LDA &F7:STA &EF:STA &F3    :\  to address beng entered
 1100 LDY #&07:LDA (&EE),Y       :\ Get copyright offset
 1110 CLD:CLC:ADC &EE:STA &FD
 1120 LDA #&00:ADC &EF:STA &FE   :\ &FD/E=>copyright message
 1130 \
 1140 \ Now check for &00,"(C)"
 1150 LDY #&00:LDA (&FD),Y:BNE LF8FA     :\ Jump if no initial &00
 1160 INY:LDA (&FD),Y:CMP #&28:BNE LF8FA :\ Jump if no '('
 1170 INY:LDA (&FD),Y:CMP #&43:BNE LF8FA :\ Jump if no 'C'
 1180 INY:LDA (&FD),Y:CMP #&29:BNE LF8FA :\ Jump if no ')'
 1190 \
 1200 \ &00,"(C)" exists
 1210 LDY #&06:LDA (&EE),Y               :\ Get ROM type
 1220 AND #&4F:CMP #&40:BCC NotLanguage  :\ b6=0, not a language
 1230 AND #&0D:BNE Not6502Code           :\ type<>0 and <>2, not 6502 code
 1240 .LF8FA
 1250 LDA #&01:JMP (&00F2)               :\ Enter code with A=1
 1260 \
 1270 \ Any existing error handler will probably have been overwritten
 1280 \ Set up new error handler before generating an error
 1290 .NotLanguage
 1300 LDA #ErrorHandler AND 255:STA BRKV+0 :\ Claim error handler
 1310 LDA #ErrorHandler DIV 256:STA BRKV+1
 1320 BRK:EQUB 0:EQUS "This is not a language":EQUB 0
 1330 
 1340 .Not6502Code
 1350 LDA #ErrorHandler AND 255:STA BRKV+0 :\ Claim error handler
 1360 LDA #ErrorHandler DIV 256:STA BRKV+1
 1370 BRK:EQUB 0:EQUS "I cannot run this code":EQUB 0
 1380 
 1390 .ErrorHandler
 1400 LDX #&FF:TXS               :\ Clear stack
 1410 JSR OSNEWL:LDY #&01
 1420 .LF94D
 1430 LDA (&FD),Y:BEQ LF957      :\ Print error string
 1440 JSR OSWRCH:INY:BNE LF94D
 1450 .LF957
 1460 JSR OSNEWL:JMP CmdOSLoop   :\ Jump to command prompt
 1470 
 1480 \ Control block for command prompt input
 1490 \ --------------------------------------
 1500 .LF95D
 1510 EQUW INPBUF                :\ Input text to INPBUF at &236
 1520 EQUB &CA                   :\ Up to &CA characters
 1530 EQUB &20:EQUB &FF          :\ Min=&20, Max=&FF
 1540 
 1550 
 1560 \ MOS INTERFACE
 1570 \ =============
 1580 \
 1590 \
 1600 \ OSWRCH - Send character to output stream
 1610 \ ========================================
 1620 \ On entry, A =character
 1630 \ On exit,  A =preserved
 1640 \
 1650 \ Tube data  character  --
 1660 \
 1670 .osWRCH
 1680 BIT TubeS1                 :\ Read Tube R1 status
 1690 NOP:BVC osWRCH             :\ Loop until b6 set
 1700 STA TubeR1:RTS             :\ Send character to Tube R1
 1710 
 1720 
 1730 \ OSRDCH - Wait for character from input stream
 1740 \ =============================================
 1750 \ On exit, A =char, Cy=Escape flag
 1760 \
 1770 \ Tube data  &00  --  Carry Char
 1780 \
 1790 .osRDCH
 1800 LDA #&00:JSR SendCommand   :\ Send command &00 - OSRDCH
 1810 .WaitCarryChar             :\ Wait for Carry and A
 1820 JSR WaitByte:ASL A         :\ Wait for carry
 1830 .WaitByte
 1840 BIT TubeS2:BPL WaitByte    :\ Loop until Tube R2 has data
 1850 LDA TubeR2                 :\ Fetch character
 1860 .NullReturn
 1870 RTS
 1880 
 1890 
 1900 \ Skip Spaces
 1910 \ ===========
 1920 .SkipSpaces1
 1930 INY
 1940 .SkipSpaces
 1950 LDA (&F8),Y:CMP #&20:BEQ SkipSpaces1
 1960 RTS
 1970 
 1980 
 1990 \ Scan hex
 2000 \ ========
 2010 .ScanHex
 2020 LDX #&00:STX &F0:STX &F1   :\ Clear hex accumulator
 2030 .LF98C
 2040 LDA (&F8),Y                :\ Get current character
 2050 CMP #&30:BCC LF9B1         :\ <'0', exit
 2060 CMP #&3A:BCC LF9A0         :\ '0'..'9', add to accumulator
 2070 AND #&DF:SBC #&07:BCC LF9B1:\ Convert letter, if <'A', exit
 2080 CMP #&40:BCS LF9B1         :\ >'F', exit
 2090 .LF9A0
 2100 ASL A:ASL A:ASL A:ASL A    :\ *16
 2110 LDX #&03                   :\ Prepare to move 3+1 bits
 2120 .LF9A6
 2130 ASL A:ROL &F0:ROL &F1      :\ Move bits into accumulator
 2140 DEX:BPL LF9A6              :\ Loop for four bits, no overflow check
 2150 INY:BNE LF98C              :\ Move to next character
 2160 .LF9B1
 2170 RTS
 2180 
 2190 
 2200 \ Send string to Tube R2
 2210 \ ======================
 2220 .SendString
 2230 STX &F8:STY &F9            :\ Set &F8/9=>string
 2240 .SendStringF8
 2250 LDY #&00
 2260 .LF9B8
 2270 BIT TubeS2:BVC LF9B8       :\ Wait for Tube R2 free
 2280 LDA (&F8),Y:STA TubeR2     :\ Send character to Tube R2
 2290 INY:CMP #&0D:BNE LF9B8     :\ Loop until <cr> sent
 2300 LDY &F9:RTS                :\ Restore Y from &F9 and return
 2310 
 2320 
 2330 \ OSCLI - Execute command
 2340 \ =======================
 2350 \ On entry, XY=>command string
 2360 \ On exit,  XY= preserved
 2370 \
 2380 .osCLI
 2390 PHA:STX &F8:STY &F9        :\ Save A, &F8/9=>command string
 2400 LDY #&00
 2410 .LF9D1
 2420 JSR SkipSpaces:INY
 2430 CMP #ASC"*":BEQ LF9D1      :\ Skip spaces and stars
 2440 AND #&DF:TAX               :\ Ignore case, and save in X
 2450 LDA (&F8),Y                :\ Get next character
 2460 CPX #ASC"G":BEQ CmdGO      :\ Jump to check '*GO'
 2470 CPX #ASC"H":BNE osCLI_IO   :\ Not "H---", jump to pass to Tube
 2480 CMP #ASC".":BEQ CmdHELP    :\ "H.", jump to do *DELETEHIMEM
 2490 AND #&DF                   :\ Ignore case
 2500 CMP #ASC"E":BNE osCLI_IO   :\ Not "HE---", jump to pass to Tube
 2510 INY:LDA (&F8),Y            :\ Get next character
 2520 CMP #ASC".":BEQ CmdHELP    :\ "HE.", jump to do *DELETEHIMEM
 2530 AND #&DF                   :\ Ignore case
 2540 CMP #ASC"L":BNE osCLI_IO   :\ Not "HEL---", jump to pass to Tube
 2550 INY:LDA (&F8),Y            :\ Get next character
 2560 CMP #ASC".":BEQ CmdHELP    :\ "HEL.", jump to do *DELETEHIMEM
 2570 AND #&DF                   :\ Ignore case
 2580 CMP #ASC"P":BNE osCLI_IO   :\ Not "HELP---", jump to pass to Tube
 2590 INY:LDA (&F8),Y            :\ Get next character
 2600 AND #&DF                   :\ Ignore case
 2610 CMP #ASC"A":BCC CmdHELP    :\ "HELP" terminated by non-letter, do *DELETEHIMEM
 2620 CMP #ASC"[":BCC osCLI_IO   :\ "HELP" followed by letter, pass to Tube
 2630 
 2640 \ *Help - Display help information
 2650 \ --------------------------------
 2660 .CmdHELP
 2670 JSR PrText                 :\ Print help message
 2680 EQUB 10:EQUB 13:EQUS "6502 TUBE 1.10"
 2690 EQUB 10:EQUB 13
 2700 NOP                        :\ Continue to pass '*DELETEHIMEM' command to Tube
 2710 
 2720 
 2730 \ OSCLI - Send command line to host
 2740 \ =================================
 2750 \ On entry, &F8/9=>command string
 2760 \
 2770 \ Tube data  &02 string &0D  --  &7F or &80
 2780 \
 2790 .osCLI_IO
 2800 LDA #&02:JSR SendCommand   :\ Send command &02 - OSCLI
 2810 JSR SendStringF8           :\ Send command string at &F8/9
 2820 .osCLI_Ack
 2830 JSR WaitByte               :\ Wait for acknowledgement
 2840 CMP #&80:BEQ LFA5C         :\ Jump if code to be entered
 2850 PLA:RTS                    :\ Restore A and return
 2860 
 2870 
 2880 \ *GO - call machine code
 2890 \ -----------------------
 2900 .CmdGO
 2910 AND #&DF                   :\ Ignore case
 2920 CMP #ASC"O":BNE osCLI_IO   :\ Not '*GO', jump to pass to Tube
 2930 JSR SkipSpaces1            :\ Move past any spaces
 2940 JSR ScanHex:JSR SkipSpaces :\ Read hex value and move past spaces
 2950 CMP #&0D:BNE osCLI_IO      :\ More parameters, pass to Tube to deal with
 2960 TXA:BEQ LFA5C              :\ If no address given, jump to current program
 2970 LDA &F0:STA &F6            :\ Set program start to address read
 2980 LDA &F1:STA &F7
 2990 
 3000 .LFA5C
 3010 LDA &EF:PHA:LDA &EE:PHA    :\ Save current program
 3020 JSR EnterCode
 3030 PLA:STA &EE:STA &F2        :\ Restore current program and
 3040 PLA:STA &EF:STA &F3        :\  set address top of memory to it
 3050 PLA:RTS
 3060 
 3070 .CheckAck
 3080 BEQ osCLI_Ack
 3090 
 3100 
 3110 \ OSBYTE - Byte MOS functions
 3120 \ ===========================
 3130 \ On entry, A, X, Y=OSBYTE parameters
 3140 \ On exit,  A  preserved
 3150 \           If A<&80, X=returned value
 3160 \           If A>&7F, X, Y, Carry=returned values
 3170 \
 3180 .osBYTE
 3190 CMP #&80:BCS ByteHigh      :\ Jump for long OSBYTEs
 3200 \
 3210 \ Tube data  &04 X A    --  X
 3220 \
 3230 PHA:LDA #&04
 3240 .LFA7A
 3250 BIT TubeS2:BVC LFA7A       :\ Wait for Tube R2 free
 3260 STA TubeR2                 :\ Send command &04 - OSBYTELO
 3270 .LFA82
 3280 BIT TubeS2:BVC LFA82       :\ Wait for Tube R2 free
 3290 STX TubeR2:PLA             :\ Send single parameter
 3300 .LFA8B
 3310 BIT TubeS2:BVC LFA8B       :\ Wait for Tube R2 free
 3320 STA TubeR2                 :\ Send function
 3330 .LFA93
 3340 BIT TubeS2:BPL LFA93       :\ Wait for Tube R2 data present
 3350 LDX TubeR2:RTS             :\ Get return value
 3360 
 3370 .ByteHigh
 3380 CMP #&82:BEQ Byte82        :\ Read memory high word
 3390 CMP #&83:BEQ Byte83        :\ Read bottom of memory
 3400 CMP #&84:BEQ Byte84        :\ Read top of memory
 3410 \
 3420 \ Tube data  &06 X Y A  --  Cy Y X
 3430 \
 3440 PHA:LDA #&06
 3450 .LFAAB
 3460 BIT TubeS2:BVC LFAAB       :\ Wait for Tube R2 free
 3470 STA TubeR2                 :\ Send command &06 - OSBYTEHI
 3480 .LFAB3
 3490 BIT TubeS2:BVC LFAB3       :\ Wait for Tube R2 free
 3500 STX TubeR2                 :\ Send parameter 1
 3510 .LFABB
 3520 BIT TubeS2:BVC LFABB       :\ Wait for Tube R2 free
 3530 STY TubeR2                 :\ Send parameter 2
 3540 PLA
 3550 .LFAC4
 3560 BIT TubeS2:BVC LFAC4       :\ Wait for Tube R2 free
 3570 STA TubeR2                 :\ Send function
 3580 CMP #&8E:BEQ CheckAck      :\ If select language, check to enter code
 3590 CMP #&9D:BEQ LFAEF         :\ Fast return with Fast BPUT
 3600 PHA                        :\ Save function
 3610 .LFAD5
 3620 BIT TubeS2:BPL LFAD5       :\ Wait for Tube R2 data present
 3630 LDA TubeR2:ASL A:PLA       :\ Get Carry
 3640 .LFADF
 3650 BIT TubeS2:BPL LFADF       :\ Wait for Tube R2 data present
 3660 LDY TubeR2                 :\ Get return high byte
 3670 .LFAE7
 3680 BIT TubeS2:BPL LFAE7       :\ Wait for Tube R2 data present
 3690 LDX TubeR2                 :\ Get return low byte
 3700 .LFAEF
 3710 RTS
 3720 
 3730 .Byte84:LDX &F2:LDY &F3:RTS   :\ Read top of memory from &F2/3
 3740 .Byte83:LDX #&00:LDY #&08:RTS :\ Read bottom of memory
 3750 .Byte82:LDX #&00:LDY #&00:RTS :\ Return &0000 as memory high word
 3760 
 3770 
 3780 \ OSWORD - Various functions
 3790 \ ==========================
 3800 \ On entry, A =function
 3810 \           XY=>control block
 3820 \
 3830 .osWORD
 3840 STX &F8:STY &F9            :\ &F8/9=>control block
 3850 TAY:BEQ RDLINE             :\ OSWORD 0, jump to read line
 3860 PHA:LDY #&08
 3870 .LFB09
 3880 BIT TubeS2:BVC LFB09       :\ Loop until Tube R2 free
 3890 STY TubeR2                 :\ Send command &08 - OSWORD
 3900 .LFB11
 3910 BIT TubeS2:BVC LFB11       :\ Loop until Tube R2 free
 3920 STA TubeR2                 :\ Send function
 3930 TAX:BPL WordSendLow        :\ Jump with functions<&80
 3940 LDY #&00:LDA (&F8),Y       :\ Get send block length from control block
 3950 TAY:JMP WordSend           :\ Jump to send control block
 3960 
 3970 .WordSendLow
 3980 LDY WordLengthsLo-1,X      :\ Get send block length from table
 3990 CPX #&15:BCC WordSend      :\ Use this length for OSWORD 1 to &14
 4000 LDY #&10                   :\ Send 16 bytes for OSWORD &15 to &7F
 4010 .WordSend
 4020 BIT TubeS2:BVC WordSend    :\ Wait until Tube R2 free
 4030 STY TubeR2                 :\ Send send block length
 4040 DEY:BMI LFB45              :\ Zero or &81..&FF length, nothing to send
 4050 .LFB38
 4060 BIT TubeS2:BVC LFB38       :\ Wait for Tube R2 free
 4070 LDA (&F8),Y:STA TubeR2     :\ Send byte from control block
 4080 DEY:BPL LFB38              :\ Loop for number to be sent
 4090 .LFB45
 4100 TXA:BPL WordRecvLow        :\ Jump with functions<&80
 4110 LDY #&01:LDA (&F8),Y       :\ Get receive block length from control block
 4120 TAY:JMP WordRecv           :\ Jump to receive control block
 4130 
 4140 .WordRecvLow
 4150 LDY WordLengthsHi-1,X      :\ Get receive length from table
 4160 CPX #&15:BCC WordRecv      :\ Use this length for OSWORD 1 to &14
 4170 LDY #&10                   :\ Receive 16 bytes for OSWORD &15 to &7F
 4180 .WordRecv
 4190 BIT TubeS2:BVC WordRecv    :\ Wait for Tube R2 free
 4200 STY TubeR2                 :\ Send receive block length
 4210 DEY:BMI LFB71              :\ Zero of &81..&FF length, nothing to receive
 4220 .LFB64
 4230 BIT TubeS2:BPL LFB64       :\ Wait for Tube R2 data present
 4240 LDA TubeR2:STA (&F8),Y     :\ Get byte to control block
 4250 DEY:BPL LFB64              :\ Loop for number to receive
 4260 .LFB71
 4270 LDY &F9:LDX &F8:PLA        :\ Restore registers
 4280 RTS
 4290 
 4300 
 4310 \ RDLINE - Read a line of text
 4320 \ ============================
 4330 \ On entry, A =0
 4340 \           XY=>control block
 4350 \ On exit,  A =undefined
 4360 \           Y =length of returned string
 4370 \           Cy=0 ok, Cy=1 Escape
 4380 \
 4390 \ Tube data  &0A block  --  &FF or &7F string &0D
 4400 \
 4410 .RDLINE
 4420 LDA #&0A:JSR SendCommand   :\ Send command &0A - RDLINE
 4430 LDY #&04
 4440 .LFB7E
 4450 BIT TubeS2:BVC LFB7E       :\ Wait for Tube R2 free
 4460 LDA (&F8),Y:STA TubeR2     :\ Send control block
 4470 DEY:CPY #&01:BNE LFB7E     :\ Loop for 4, 3, 2
 4480 LDA #&07:JSR SendByte      :\ Send &07 as address high byte
 4490 LDA (&F8),Y:PHA            :\ Get text buffer address high byte
 4500 DEY
 4510 .LFB96
 4520 BIT TubeS2:BVC LFB96       :\ Wait for Tube R2 free
 4530 STY TubeR2                 :\ Send &00 as address low byte
 4540 LDA (&F8),Y:PHA            :\ Get text buffer address low byte
 4550 LDX #&FF:JSR WaitByte      :\ Wait for response
 4560 CMP #&80:BCS RdLineEscape  :\ Jump if Escape returned
 4570 PLA:STA &F8:PLA:STA &F9    :\ Set &F8/9=>text buffer
 4580 LDY #&00
 4590 .RdLineLp
 4600 BIT TubeS2:BPL RdLineLp    :\ Wait for Tube R2 data present
 4610 LDA TubeR2:STA (&F8),Y     :\ Store returned character
 4620 INY:CMP #&0D:BNE RdLineLp  :\ Loop until <cr>
 4630 LDA #&00:DEY:CLC:INX       :\ Return A=0, Y=len, X=00, Cy=0
 4640 RTS
 4650 :
 4660 .RdLineEscape
 4670 PLA:PLA:LDA #&00           :\ Return A=0, Y=len, X=FF, Cy=1
 4680 RTS
 4690 
 4700 
 4710 \ OSARGS - Read info on open file
 4720 \ ===============================
 4730 \ On entry, A =function
 4740 \           X =>data word in zero page
 4750 \           Y =handle
 4760 \ On exit,  A =returned value
 4770 \           X  preserved
 4780 \           Y  preserved
 4790 \
 4800 \ Tube data  &0C handle block function  --  result block
 4810 \
 4820 .osARGS
 4830 PHA:LDA #&0C:JSR SendCommand :\ Send command &0C - OSARGS
 4840 .LFBD2
 4850 BIT TubeS2:BVC LFBD2       :\ Loop until Tube R2 free
 4860 STY TubeR2                 :\ Send handle
 4870 LDA &03,X:JSR SendByte     :\ Send data word
 4880 LDA &02,X:JSR SendByte
 4890 LDA &01,X:JSR SendByte
 4900 LDA &00,X:JSR SendByte
 4910 PLA:JSR SendByte           :\ Send function
 4920 JSR WaitByte:PHA           :\ Get and save result
 4930 JSR WaitByte:STA &03,X     :\ Receive data word
 4940 JSR WaitByte:STA &02,X
 4950 JSR WaitByte:STA &01,X
 4960 JSR WaitByte:STA &00,X
 4970 PLA:RTS                    :\ Get result back and return
 4980 
 4990 
 5000 \ OSFIND - Open of Close a file
 5010 \ =============================
 5020 \ On entry, A =function
 5030 \           Y =handle or XY=>filename
 5040 \ On exit,  A =zero or handle
 5050 \
 5060 \ Tube data  &12 function string &0D  --  handle
 5070 \            &12 &00 handle  --  &7F
 5080 \
 5090 .osFIND
 5100 PHA:LDA #&12:JSR SendCommand :\ Send command &12 - OSFIND
 5110 PLA:JSR SendByte             :\ Send function
 5120 CMP #&00:BNE OPEN            :\ If <>0, jump to do OPEN
 5130 PHA:TYA:JSR SendByte         :\ Send handle
 5140 JSR WaitByte:PLA:RTS         :\ Wait for acknowledge, restore regs and return
 5150 .OPEN
 5160 JSR SendString               :\ Send pathname
 5170 JMP WaitByte                 :\ Wait for and return handle
 5180 
 5190 
 5200 \ OSBGet - Get a byte from open file
 5210 \ ==================================
 5220 \ On entry, H =handle
 5230 \ On exit,  A =byte Read
 5240 \           H =preserved
 5250 \           Cy set if EOF
 5260 \
 5270 \ Tube data  &0E handle --  Carry byte
 5280 \
 5290 .osBGET
 5300 LDA #&0E:JSR SendCommand   :\ Send command &0E - OSBGET
 5310 TYA:JSR SendByte           :\ Send handle
 5320 JMP WaitCarryChar          :\ Jump to wait for Carry and byte
 5330 
 5340 
 5350 \ OSBPut - Put a byte to an open file
 5360 \ ===================================
 5370 \ On entry, A =byte to write
 5380 \           Y =handle
 5390 \ On exit,  A =preserved
 5400 \           Y =preserved
 5410 \
 5420 \ Tube data  &10 handle byte  --  &7F
 5430 \
 5440 .osBPUT
 5450 PHA:LDA #&10:JSR SendCommand :\ Send command &10 - OSBPUT
 5460 TYA:JSR SendByte             :\ Send handle
 5470 PLA:JSR SendByte             :\ Send byte
 5480 PHA:JSR WaitByte:PLA:RTS     :\ Wait for acknowledge and return
 5490 
 5500 
 5510 \ Send a byte to Tube R2
 5520 \ ======================
 5530 .SendCommand
 5540 .SendByte
 5550 BIT TubeS2:BVC SendByte    :\ Wait for Tube R2 free
 5560 STA TubeR2:RTS             :\ Send byte to Tube R2
 5570 
 5580 
 5590 \ OSFILE - Operate on whole files
 5600 \ ===============================
 5610 \ On entry, A =function
 5620 \           XY=>control block
 5630 \ On exit,  A =result
 5640 \           control block updated
 5650 \
 5660 \ Tube data  &14 block string <cr> function  --  result block
 5670 \
 5680 .osFILE
 5690 STY &FB:STX &FA              :\ &FA/B=>control block
 5700 PHA:LDA #&14:JSR SendCommand :\ Send command &14 - OSFILE
 5710 LDY #&11
 5720 .LFC5F
 5730 LDA (&FA),Y:JSR SendByte   :\ Send control block
 5740 DEY:CPY #&01:BNE LFC5F     :\ Loop for &11..&02
 5750 DEY:LDA (&FA),Y:TAX
 5760 INY:LDA (&FA),Y:TAY        :\ Get pathname address to XY
 5770 JSR SendString             :\ Send pathname
 5780 PLA:JSR SendByte           :\ Send function
 5790 JSR WaitByte:PHA           :\ Wait for result
 5800 LDY #&11
 5810 .LFC7E
 5820 JSR WaitByte:STA (&FA),Y   :\ Get control block back
 5830 DEY:CPY #&01:BNE LFC7E     :\ Loop for &11..&02
 5840 LDY &FB:LDX &FA            :\ Restore registers
 5850 PLA:RTS                    :\ Get result and return
 5860 
 5870 
 5880 \ OSGBPB - Multiple byte Read and write
 5890 \ =====================================
 5900 \ On entry, A =function
 5910 \           XY=>control block
 5920 \ On exit,  A =returned value
 5930 \              control block updated
 5940 \
 5950 \ Tube data  &16 block function  --   block Carry result
 5960 \
 5970 .osGBPB
 5980 STY &FB:STX &FA              :\ &FA/B=>control block
 5990 PHA:LDA #&16:JSR SendCommand :\ Send command &16 - OSGBPB
 6000 LDY #&0C
 6010 .LFC9A
 6020 LDA (&FA),Y:JSR SendByte   :\ Send control block
 6030 DEY:BPL LFC9A              :\ Loop for &0C..&00
 6040 PLA:JSR SendByte           :\ Send function
 6050 LDY #&0C
 6060 .LFCA8
 6070 JSR WaitByte:STA (&FA),Y   :\ Get control block back
 6080 DEY:BPL LFCA8              :\ Loop for &0C..&00
 6090 LDY &FB:LDX &FA            :\ Restore registers
 6100 JMP WaitCarryChar          :\ Jump to get Carry and result
 6110 
 6120 
 6130 .Unsupported
 6140 BRK:EQUB 255:EQUS "Bad":EQUB 0
 6150 
 6160 
 6170 \ OSWORD control block lengths
 6180 \ ============================
 6190 .WordLengthsLo
 6200 EQUB &00:EQUB &05:EQUB &00:EQUB &05
 6210 EQUB &02:EQUB &05:EQUB &08:EQUB &0E
 6220 EQUB &04:EQUB &01:EQUB &01:EQUB &05
 6230 EQUB &00:EQUB &01:EQUB &20:EQUB &10
 6240 EQUB &0D:EQUB &00:EQUB &04:EQUB &80
 6250 .WordLengthsHi
 6260 EQUB &05:EQUB &00:EQUB &05:EQUB &00
 6270 EQUB &05:EQUB &00:EQUB &00:EQUB &00
 6280 EQUB &05:EQUB &09:EQUB &05:EQUB &00
 6290 EQUB &08:EQUB &18:EQUB &00:EQUB &01
 6300 EQUB &0D:EQUB &80:EQUB &04:EQUB &80
 6310 
 6320 
 6330 \ Interrupt Handler
 6340 \ =================
 6350 .InterruptHandler
 6360 STA &FC:PLA:PHA            :\ Save A, get flags from stack
 6370 AND #&10:BNE BRKHandler    :\ If BRK, jump to BRK handler
 6380 JMP (IRQ1V)                :\ Continue via IRQ1V handler
 6390 
 6400 .IRQ1Handler
 6410 BIT TubeS4:BMI LFD3F       :\ If data in Tube R4, jump to process errors and transferes
 6420 BIT TubeS1:BMI LFD18       :\ If data in Tube R1, jump to process Escape and Events
 6430 JMP (IRQ2V)                :\ Pass on to IRQ2V
 6440 
 6450 .BRKHandler
 6460 TXA:PHA                    :\ Save X
 6470 TSX:LDA &0103,X            :\ Get address from stack
 6480 CLD:SEC:SBC #&01:STA &FD
 6490 LDA &0104,X:SBC #&00:STA &FE :\ &FD/E=>after BRK opcode
 6500 PLA:TAX:LDA &FC            :\ Restore X, get saved A
 6510 CLI:JMP (BRKV)             :\ Restore IRQs, jump to Error Handler
 6520 
 6530 
 6540 \ Interrupt generated by data in Tube R1
 6550 \ --------------------------------------
 6560 .LFD18
 6570 LDA TubeR1:BMI LFD39       :\ b7=1, jump to set Escape state
 6580 TYA:PHA:TXA:PHA            :\ Save registers
 6590 JSR LFE80:TAY              :\ Get Y parameter from Tube R1
 6600 JSR LFE80:TAX              :\ Get X parameter from Tube R1
 6610 JSR LFE80                  :\ Get event number from Tube R1
 6620 JSR LFD36:PLA:TAX:PLA:TAY  :\ Dispatch event, restore registers
 6630 LDA &FC:RTI                :\ Restore A, return from interrupt
 6640 .LFD36
 6650 JMP (EVNTV)
 6660 .LFD39
 6670 ASL A:STA &FF              :\ Set Escape flag from b6
 6680 LDA &FC:RTI                :\ Restore A, return from interrupt
 6690 
 6700 
 6710 \ Interrupt generated by data in Tube R4
 6720 \ --------------------------------------
 6730 .LFD3F
 6740 LDA TubeR4:BPL LFD65       :\ b7=0, jump for data transfer
 6750 CLI
 6760 .LFD45
 6770 BIT TubeS2:BPL LFD45       :\ Wait for data in Tube R2
 6780 LDA TubeR2
 6790 LDA #&00:STA ERRBUF:TAY    :\ Store BRK opcode in error buffer
 6800 JSR WaitByte:STA ERRBUF+1  :\ Get error number
 6810 .LFD59
 6820 INY:JSR WaitByte           :\ Store bytes fetched from Tube R2
 6830 STA ERRBUF+1,Y:BNE LFD59   :\ Loop until final zero
 6840 JMP ERRBUF                 :\ Jump to error block to generate error
 6850 
 6860 \ Data transfer initiated by IRQ via Tube R4
 6870 \ ------------------------------------------
 6880 .LFD65
 6890 STA NMIV+0:TYA:PHA         :\ Save transfer type, save Y
 6900 LDY NMIV+0                 :\ Get transfer type back
 6910 LDA LFE70,Y:STA NMIV+0     :\ get NMI routine address from table
 6920 LDA LFE78,Y:STA NMIV+1     :\ and point NMIV to it
 6930 LDA LFE60,Y:STA &F4        :\ Point &F4/5 to transfer address field
 6940 LDA LFE68,Y:STA &F5
 6950 .LFD83
 6960 BIT TubeS4:BPL LFD83       :\ Wait until Tube R4 data present
 6970 LDA TubeR4                 :\ Get called ID from Tube R4
 6980 CPY #&05:BEQ LFDE7         :\ If 'TubeRelease', jump to exit
 6990 TYA:PHA:LDY #&01           :\ Save transfer type
 7000 .LFD93
 7010 BIT TubeS4:BPL LFD93       :\ Wait for Tube R4 data present
 7020 LDA TubeR4                 :\ Fetch and disgard address byte 4
 7030 .LFD9B
 7040 BIT TubeS4:BPL LFD9B       :\ Wait for Tube R4 data present
 7050 LDA TubeR4                 :\ Fetch and disgard address byte 3
 7060 .LFDA3
 7070 BIT TubeS4:BPL LFDA3       :\ Wait for Tube R4 data present
 7080 LDA TubeR4:STA (&F4),Y:DEY :\ Fetch address byte 2, store in address
 7090 .LFDAE
 7100 BIT TubeS4:BPL LFDAE       :\ Wait for Tube R4 data present
 7110 LDA TubeR4:STA (&F4),Y     :\ Fetch address byte 1, store in address
 7120 BIT TubeR3:BIT TubeR3      :\ Read from Tube R3 twice
 7130 .LFDBE
 7140 BIT TubeS4:BPL LFDBE       :\ Wait for Tube R4 data present
 7150 LDA TubeR4:PLA             :\ Get sync byte from Tube R4
 7160 CMP #&06:BCC LFDE7         :\ Exit if not 256-byte transfers
 7170 BNE LFDEC                  :\ Jump with 256-byte read
 7180 
 7190 \ Send 256 bytes to Tube via R3
 7200 \ -----------------------------
 7210 LDY #&00
 7220 .LFDCF
 7230 LDA TubeS3:AND #&80:BPL LFDCF:\ Wait for Tube R3 free
 7240 .NMI6Addr
 7250 LDA &FFFF,Y:STA TubeR3     :\ Fetch byte and send to Tube R3
 7260 INY:BNE LFDCF              :\ Loop for 256 bytes
 7270 .LFDDF
 7280 BIT TubeS3:BPL LFDDF       :\ Wait for Tube R3 free
 7290 STA TubeR3                 :\ Send final sync byte
 7300 .LFDE7
 7310 PLA:TAY:LDA &FC:RTI        :\ Restore registers and return
 7320 
 7330 \ Read 256 bytes from Tube via R3
 7340 \ -------------------------------
 7350 .LFDEC
 7360 LDY #&00
 7370 .LFDEE
 7380 LDA TubeS3:AND #&80:BPL LFDEE:\ Wait for Tube R3 data present
 7390 LDA TubeR3                 :\ Fetch byte from Tube R3
 7400 .NMI7Addr
 7410 STA &FFFF,Y:INY:BNE LFDEE  :\ Store byte and loop for 256 bytes
 7420 BEQ LFDE7                  :\ Jump to restore registers and return
 7430 
 7440 \ Transfer 0 - Transfer single byte to Tube
 7450 \ -----------------------------------------
 7460 .NMI0
 7470 PHA                        :\ Save A
 7480 .NMI0Addr
 7490 LDA &FFFF:STA TubeR3       :\ Get byte and send to Tube R3
 7500 INC NMI0Addr+1:BNE LFE0F   :\ Increment transfer address
 7510 INC NMI0Addr+2
 7520 .LFE0F
 7530 PLA:RTI                    :\ Restore A and return
 7540 
 7550 \ Transfer 1 - Transfer single byte from Tube
 7560 \ -------------------------------------------
 7570 .NMI1
 7580 PHA:LDA TubeR3             :\ Save A, get byte from Tube R3
 7590 .NMI1Addr
 7600 STA &FFFF                  :\ Store byte
 7610 INC NMI1Addr+1:BNE LFE20   :\ Increment transfer address
 7620 INC NMI1Addr+2
 7630 .LFE20
 7640 PLA:RTI                    :\ Restore A and return
 7650 
 7660 \ Transfer 2 - Transfer two bytes to Tube
 7670 \ ---------------------------------------
 7680 .NMI2
 7690 PHA:TYA:PHA:LDY #&00       :\ Save registers
 7700 LDA (&F6),Y:STA TubeR3     :\ Get byte and send to Tube R3
 7710 INC &F6:BNE LFE32:INC &F7  :\ Increment transfer address
 7720 .LFE32
 7730 LDA (&F6),Y:STA TubeR3     :\ Get byte and send to Tube R3
 7740 INC &F6:BNE LFE3D:INC &F7  :\ Increment transfer address
 7750 .LFE3D
 7760 PLA:TAY:PLA:RTI            :\ Restore registers and return
 7770 
 7780 \ Transfer 3 - Transfer two bytes from Tube
 7790 \ -----------------------------------------
 7800 .NMI3
 7810 PHA:TYA:PHA:LDY #&00       :\ Save registers
 7820 LDA TubeR3:STA (&F6),Y     :\ Get byte from Tube R3 and store
 7830 INC &F6:BNE LFE51:INC &F7  :\ Increment transfer address
 7840 .LFE51
 7850 LDA TubeR3:STA (&F6),Y     :\ Get byte from Tube R3 and store
 7860 INC &F6:BNE LFE5C:INC &F7  :\ Increment transfer address
 7870 .LFE5C
 7880 PLA:TAY:PLA:RTI            :\ Restore registers and return
 7890 
 7900 \ Data transfer address pointers
 7910 \ ------------------------------
 7920 .LFE60
 7930 EQUB (NMI0Addr+1) AND 255:EQUB (NMI1Addr+1) AND 255
 7940 EQUB &00F6 AND 255       :EQUB &00F6 AND 255
 7950 EQUB &00F6 AND 255       :EQUB &00F6 AND 255
 7960 EQUB (NMI6Addr+1) AND 255:EQUB (NMI7Addr+1) AND 255
 7970 .LFE68
 7980 EQUB (NMI0Addr+1) DIV 256:EQUB (NMI1Addr+1) DIV 256
 7990 EQUB &00F6 DIV 256       :EQUB &00F6 DIV 256
 8000 EQUB &00F6 DIV 256       :EQUB &00F6 DIV 256
 8010 EQUB (NMI6Addr+1) DIV 256:EQUB (NMI7Addr+1) DIV 256
 8020 
 8030 \ Data transfer routine addresses
 8040 \ -------------------------------
 8050 .LFE70
 8060 EQUB NMI0 AND 255   :EQUB NMI1 AND 255
 8070 EQUB NMI2 AND 255   :EQUB NMI3 AND 255
 8080 EQUB NMI_Ack AND 255:EQUB NMI_Ack AND 255
 8090 EQUB NMI_Ack AND 255:EQUB NMI_Ack AND 255
 8100 .LFE78
 8110 EQUB NMI0 DIV 256   :EQUB NMI1 DIV 256
 8120 EQUB NMI2 DIV 256   :EQUB NMI3 DIV 256
 8130 EQUB NMI_Ack DIV 256:EQUB NMI_Ack DIV 256
 8140 EQUB NMI_Ack DIV 256:EQUB NMI_Ack DIV 256
 8150 
 8160 
 8170 \ Wait for byte in Tube R1 while allowing requests via Tube R4
 8180 \ ============================================================
 8190 .LFE80
 8200 BIT TubeS1:BMI LFE94       :\ If data in Tube R1, jump to fetch it
 8210 .LFE85
 8220 BIT TubeS4                 :\ Check if data present in Tube R4
 8230 BPL LFE80                  :\ If nothing there, jump back to check Tube R1
 8240 LDA &FC                    :\ Save IRQ's A store in A register
 8250 PHP:CLI:PLP                :\ Allow an IRQ through to process R4 request
 8260 STA &FC:JMP LFE80          :\ Restore IRQ's A store and jump back to check R1
 8270 .LFE94
 8280 LDA TubeR1:RTS             :\ Fetch byte from Tube R1 and return
 8290 
 8300 
 8310 \ Print embedded string
 8320 \ =====================
 8330 .PrText
 8340 PLA:STA &FA:PLA:STA &FB    :\ &FA/B=>embedded string
 8350 LDY #&00
 8360 .LFEA0
 8370 INC &FA:BNE LFEA6:INC &FB  :\ Increment address
 8380 .LFEA6
 8390 LDA (&FA),Y:BMI LFEB0      :\ Get character, exit if >&7F
 8400 JSR OSWRCH:JMP LFEA0       :\ Print character and loop back for more
 8410 .LFEB0
 8420 JMP (&00FA)                :\ Jump back to code after string
 8430 
 8440 
 8450 \ Null NMI code
 8460 \ -------------
 8470 .NMI_Ack
 8480 STA TubeR3:RTI             :\ Store to TubeR3 to acknowlege NMI
 8490 
 8500 
 8510 \ Spare space
 8520 \ ===========
 8530 EQUS STRING$(&FEF0-P%,CHR$255)
 8540 
 8550 
 8560 \ I/O Space
 8570 \ =========
 8580 EQUS STRING$(8,CHR$0)
 8590 
 8600 \ Tube I/O Registers
 8610 \ ==================
 8620 .TubeS1 :\ &FEF8 :EQUB 0
 8630 .TubeR1 :\ &FEF9 :EQUB 0
 8640 .TubeS2 :\ &FEFA :EQUB 0
 8650 .TubeR2 :\ &FEFB :EQUB 0
 8660 .TubeS3 :\ &FEFC :EQUB 0
 8670 .TubeR3 :\ &FEFD :EQUB 0
 8680 .TubeS4 :\ &FEFE :EQUB 0
 8690 .TubeR4 :\ &FEFF :EQUB 0
 8700 
 8710 
 8720 \ Spare space
 8730 \ ===========
 8740 .LFF00
 8750 EQUS STRING$(&FF80-P%,CHR$255)
 8760 
 8770 
 8780 \ DEFAULT VECTOR TABLE
 8790 \ ====================
 8800 .LFF80
 8810 EQUW Unsupported      :\ &200 - USERV
 8820 EQUW ErrorHandler     :\ &202 - BRKV
 8830 EQUW IRQ1Handler      :\ &204 - IRQ1V
 8840 EQUW Unsupported      :\ &206 - IRQ2V
 8850 EQUW osCLI            :\ &208 - CLIV
 8860 EQUW osBYTE           :\ &20A - BYTEV
 8870 EQUW osWORD           :\ &20C - WORDV
 8880 EQUW osWRCH           :\ &20E - WRCHV
 8890 EQUW osRDCH           :\ &210 - RDCHV
 8900 EQUW osFILE           :\ &212 - FILEV
 8910 EQUW osARGS           :\ &214 - ARGSV
 8920 EQUW osBGET           :\ &216 - BGetV
 8930 EQUW osBPUT           :\ &218 - BPutV
 8940 EQUW osGBPB           :\ &21A - GBPBV
 8950 EQUW osFIND           :\ &21C - FINDV
 8960 EQUW Unsupported      :\ &21E - FSCV
 8970 EQUW NullReturn       :\ &220 - EVNTV
 8980 EQUW Unsupported      :\ &222 - UPTV
 8990 EQUW Unsupported      :\ &224 - NETV
 9000 EQUW Unsupported      :\ &226 - VduV
 9010 EQUW Unsupported      :\ &228 - KEYV
 9020 EQUW Unsupported      :\ &22A - INSV
 9030 EQUW Unsupported      :\ &22C - RemV
 9040 EQUW Unsupported      :\ &22E - CNPV
 9050 EQUW NullReturn       :\ &230 - IND1V
 9060 EQUW NullReturn       :\ &232 - IND2V
 9070 EQUW NullReturn       :\ &234 - IND3V
 9080 
 9090 .VECDEF :\ &FFB6 :EQUB &36:EQUW LFF80
 9100 .OSXXXX :\ &FFB9 :JMP Unsupported
 9110 .OSXXXX :\ &FFBC :JMP Unsupported
 9120 .OSXXXX :\ &FFBF :JMP Unsupported
 9130 .OSXXXX :\ &FFC2 :JMP Unsupported
 9140 .OSXXXX :\ &FFC5 :JMP Unsupported
 9150 .NVRDCH :\ &FFC8 :JMP osRDCH
 9160 .NVWRCH :\ &FFCB :JMP osWRCH
 9170 
 9180 .OSFIND :\ &FFCE :JMP (FINDV)
 9190 .OSGBPB :\ &FFD1 :JMP (GBPBV)
 9200 .OSBPUT :\ &FFD4 :JMP (BPutV)
 9210 .OSBGET :\ &FFD7 :JMP (BGetV)
 9220 .OSARGS :\ &FFDA :JMP (ARGSV)
 9230 .OSFILE :\ &FFDD :JMP (FILEV)
 9240 
 9250 .OSRDCH :\ &FFE0 :JMP (RDCHV)
 9260 .OSASCI :\ &FFE3 :CMP #&0D:BNE OSWRCH
 9270 .OSNEWL :\ &FFE7 :LDA #&0A:JSR OSWRCH
 9280 .OSWRCR :\ &FFEC :LDA #&0D
 9290 .OSWRCH :\ &FFEE :JMP (WRCHV)
 9300 .OSWORD :\ &FFF1 :JMP (WORDV)
 9310 .OSBYTE :\ &FFF4 :JMP (BYTEV)
 9320 .OS_CLI :\ &FFF7 :JMP (CLIV)
 9330 
 9340 .NMIV   :\ &FFFA :EQUW NMI0             :\ NMI Vector
 9350 .RESETV :\ &FFFC :EQUW RESET            :\ RESET Vector
 9360 .IRQV   :\ &FFFE :EQUW InterruptHandler :\ IRQ Vector
 9370 ]:NEXT
 9380 OSCLI"Save Client "+STR$~mcode%+" "+STR$~O%+" "+STR$~load%+" "+STR$~load%
 9390 ON ERROR ON ERROR OFF:END
 9400 *Quit
User avatar
jms2
Posts: 3765
Joined: Mon Jan 08, 2007 6:38 am
Location: Derby, UK
Contact:

Re: Matchbox sized 6502 Co Processor

Post by jms2 »

Dave,

One of the things that I have been thinking about with respect to my similar project is how to simulate the VHDL code. The problem being that the second processor has to live in isolation from a host machine. Or do you run a simulation of a host machine as well..?
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Re: Matchbox sized 6502 Co Processor

Post by hoglet »

jms2 wrote:One of the things that I have been thinking about with respect to my similar project is how to simulate the VHDL code. The problem being that the second processor has to live in isolation from a host machine. Or do you run a simulation of a host machine as well..?
I guess it depends on how much you want to try to simulate. I've written a simple test bench that simulates a few writes to the tube registers from the host side, which is enough to initialize things and force a parasite reset:
https://github.com/hoglet67/CoPro6502/b ... tBench.vhd

This was enough to allow me to test the Co Processor 6502 seems to be able to execute code, and test the bootstrap code. But not really to test the data transfer.

To go further, you would need a real 6502 on the host side as well, executing real code.

My plan is to now try the real hardware, and only go back to the simulation when it doesn't work [-o< [-o< [-o<

Dave
User avatar
roland
Posts: 5148
Joined: Thu Aug 29, 2013 9:29 pm
Location: Born (NL)
Contact:

Re: Matchbox sized 6502 Co Processor

Post by roland »

That's really amazingly small.

Your advantage with the bbc is that most of the software is already available. On the Atom we have barely random access files ready :lol: and the rest is still unwritten...

There are many interesting projects going on here =D>
FPGAtom: 512 KB RAM, Real Time Clock and 64 colours
MAN WOMAN :shock:
User avatar
richardtoohey
Posts: 4075
Joined: Thu Dec 29, 2011 5:13 am
Location: Tauranga, New Zealand
Contact:

Re: Matchbox sized 6502 Co Processor

Post by richardtoohey »

roland wrote:There are many interesting projects going on here =D>
You can say that again! :shock:

I read the first one and go "that's interesting, must have a closer look to see what the clever people have done" and then the second and third and fourth projects come out and I've got no chance! :-s

Not that I need to get distracted by any other projects, got enough projects of my own that I've started and then put down. [-X

But great to see what you are all up to! :D
User avatar
flynnjs
Posts: 863
Joined: Tue Jul 06, 2010 10:33 pm
Contact:

Re: Matchbox sized 6502 Co Processor

Post by flynnjs »

Dave,
in your Tube code on github, are there any changes from the version that Ed uploaded to his site or is it just a clone?

I ask because I took Ed's out of date code and fixed a few bugs (it turned out Ed had also fixed these but not release) and I wondered if your insights into ULA reversing had lead to any further improvements.

I also heavily modified Ed's code to give a true synchronous interface to a 6502 core that didn't interface precisely like a physical one (all the timings are different).

What I want to do is cut a version that has that synchronous interface on the "best known good" drop of Tube code so that I can offer my monolithic tube PCB out to the masses. Tidying up the last few tube interface bugs was the last thing to do and I ran out of steam a bit.

Once I'm happy it's all working I'll pop whole design up for everyone (including open source PCB).

J
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Re: Matchbox sized 6502 Co Processor

Post by hoglet »

Hi all,

A bit more progress to report....

Today I made a small PCB to connect the GOP module to the Tube:
IMG_0700.JPG
IMG_0702.JPG
IMG_0716.JPG
The two headers are to allow easy connection of the signals to a logic analyzer for debugging.

As expected, it didn't work first time....

There was no (beebmaster) smoke, but the boot sequence was hanging, unless the DFS ROM was disabled.

After a bit of debugging with the logic analyzer, I could see lots of ram reads and writes, and the bootmode signal going low after 1ms. Then I noticed that the parasite IRQ line was being held low all the time, which didn't seem right.

Looking at how this is generated in Tube.v, it looks to me the logic was the wrong way around:

Code: Select all

   assign p_irq_b = ( (h_reg0_q_r[`I_IDX] & p_data_available_w[0]) | (h_reg0_q_r[`J_IDX] & p_data_available_w[3]) ) ? `P_INTERRUPT_OFF_D : 1'b0;
So I changed it to this:

Code: Select all

   assign p_irq_b = ( (h_reg0_q_r[`I_IDX] & p_data_available_w[0]) | (h_reg0_q_r[`J_IDX] & p_data_available_w[3]) ) ? 1'b0 : `P_INTERRUPT_OFF_D  ;
 
And low and behold, it lives!!!
IMG_0703.JPG
I don't have many second processor programs to test with, but I do have a disc of Level 9 adventure games:
IMG_0708.JPG
IMG_0707.JPG
Now, there are still several issues to work through:

1. When first powered up, it hangs at A, requiring a control Break:
IMG_0710.JPG
I suspect this is just because the Xilinx is taking longer to load it's configuration that the Beeb takes to boot.

2. Tube Elite doesn't work - it load the splash screen and the main program but then hangs:
IMG_0711.JPG
3. It doesn't work in a Master 128 - it is recognised and you get BASIC printed, and sometimes some garbage, but the > prompt never appears.
IMG_0712.JPG
The machine is not hung, because caps lock still works. But I can't get any further.

But this all seems very promising.

Ed, do any of the above issues sound familiar?

Dave
User avatar
BigEd
Posts: 6261
Joined: Sun Jan 24, 2010 10:24 am
Location: West Country
Contact:

Re: Matchbox sized 6502 Co Processor

Post by BigEd »

Great work Dave! My memory is fallible, but here's a timeline of some of our observations:
https://sites.google.com/site/beeb816/to-dos
which do tally up with some of yours. For sure when we experimented with different TTL families to buffer our GODIL Tube to the host machine, we got different kinds of failures, some of which were statistical. From our records:
Failure Types
FAIL (1) Tube recognized, banner message appears but hang following announcement of ROMs (can sometimes boot to '*' prompt)
FAIL (2) Tube not recognized at all and system comes up as if coprocessor absent.
FAIL (3) Unreliable boot to BASIC or '*' prompt
FAIL(4) Unreliable boot: ctrl break hangs, break unreliably boots to '*' (~80%)
It's odd that we got anything to work given your need to bugfix p_irq_b - it seems not to be fixed in our latest code drop at
https://sites.google.com/site/beeb816/file-cabinet
I can't explain that.

We experimented also with the optional pullup/pulldowns the FPGA can implement internally, and with the voltage standard it can adhere to (LVTTL or HCMOS33) - we found that no pullup, no pulldown, LVTTL was successful for the Beeb's external Tube interface and for the Master's internal copro interface. It seems we didn't try the Master's external interface - at least, we didn't record a result.

Great to see you got so far so quickly!

Hope this helps
Ed
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Re: Matchbox sized 6502 Co Processor

Post by hoglet »

Ed,

Regarding p_irq_b, if you look at the older version of the code on your site (tube.20100606T1943.tgz), the bug is not present:

Code: Select all

   assign p_irq_b = ( (h_reg0_q_r[`I_IDX] & p_data_available_w[0]) | (h_reg0_q_r[`J_IDX] & p_data_available_w[3]) ) ? 1'b0 : `P_INTERRUPT_OFF_D;
So it looks like a regression, rather than something that was always there.

Dave
User avatar
BigEd
Posts: 6261
Joined: Sun Jan 24, 2010 10:24 am
Location: West Country
Contact:

Re: Matchbox sized 6502 Co Processor

Post by BigEd »

That explains it! Thanks. :oops: :roll: :? #-o
User avatar
roland
Posts: 5148
Joined: Thu Aug 29, 2013 9:29 pm
Location: Born (NL)
Contact:

Re: Matchbox sized 6502 Co Processor

Post by roland »

Nice job, Dave. Again =D>
Today I made a small PCB to connect the GOP module to the Tube
Can't you make my new Atom board? That would be a lot quicker than makePCB :lol:

Greetings,
Roland
FPGAtom: 512 KB RAM, Real Time Clock and 64 colours
MAN WOMAN :shock:
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Re: Matchbox sized 6502 Co Processor

Post by hoglet »

roland wrote:Can't you make my new Atom board? That would be a lot quicker than makePCB :lol:
I'll have a go if you can do a single sided layout :lol:

Dave
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Re: Matchbox sized 6502 Co Processor

Post by hoglet »

I'm frustrated that it won't run Elite....

I've updated the T65 core to be a 65C02 and the Tube ROM to be the 65C102 version from JGH's site. But this made no difference (not that I really expected it to).

I've had a look with the logic analyzer, and the host seems to be very busy endlessly reading register 1 then register 0. What's interesting is that if I look at the parasite side, it's not writing to the tube at all. So it looks like there may be a problem here with 24 byte FIFO that is register 1.

This is normally used for OSWRCH from the parasite to the host, but it's possible that Elite uses it for something else, or that it is pushing much more data through this.

I think this is going to be very tricky to diagnose.....

Dave
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Re: Matchbox sized 6502 Co Processor

Post by hoglet »

I've just noticed that the 24 byte FIFO is actually only a 1 byte FIFO in Rich/Ed's tube design.

I wonder if Elite for some reason needs more that just 1 byte.

But that still wouldn't explain the behaviour I'm seeing on the host, continuously reading tube address 0 (register 1 status) and 1 (register 1 data).

What's weird is that if you look at the values being returned:
- Read address 0 -> 0x4F (register 1 status)
- Read address 1 -> 0x41 (register 1 data)

Looking at the Tube App Note, a 0x4F in register 1 status means:
- A1=0 - there is no data available in register 1
- F1=1 - register 1 is not full

If there is no data available, I don't understand why the host goes on to read the data register.

Dave
User avatar
flynnjs
Posts: 863
Joined: Tue Jul 06, 2010 10:33 pm
Contact:

Re: Matchbox sized 6502 Co Processor

Post by flynnjs »

Tomorrow, I'll do a diff on my code from Ed's and see if any of my fixes need to be merged in and if they might help solve any of your issues.
I've also been round the IRQ polarity loop, but some others too.

Jason
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Re: Matchbox sized 6502 Co Processor

Post by hoglet »

Hi Jason,

Sorry, I somehow managed to miss your post earlier.
flynnjs wrote:Dave,
in your Tube code on github, are there any changes from the version that Ed uploaded to his site or is it just a clone?

I ask because I took Ed's out of date code and fixed a few bugs (it turned out Ed had also fixed these but not release) and I wondered if your insights into ULA reversing had lead to any further improvements.
It was the latest version from his site.

I spotted the IRQ polarity bug this afternoon.

The only other thing I spotted during the reverse engineering was that the R3 N flag wasn't being correctly generated. See this post:
http://www.stardot.org.uk/forums/viewto ... =60#p93314

I've fixed this, but it made no difference to Elite.

If there is any other behaviour you are uncertain about, I have a full gate level simulation of the reverse engineered Tube design running.
flynnjs wrote:Tomorrow, I'll do a diff on my code from Ed's and see if any of my fixes need to be merged in and if they might help solve any of your issues.
I've also been round the IRQ polarity loop, but some others too.
That would be most helpful.

Are you able to run Tube Elite on your version?

In the mean time, I mighy have a go at a Z80 second processor using the T80 core:
http://opencores.org/project,t80
Looks like the boot mode logic is slightly more complicated, as it flips the ROM back in when handing an NMI interrupt.

Dave
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Re: Matchbox sized 6502 Co Processor

Post by hoglet »

Jason,

I hope you don't mind me posting this link to the OSHUG (Open Source Hardware User Group) talk you did at the end of last year about your 6502 FPGA Second Processor project:
https://www.youtube.com/watch?v=2hcSdu4DHD4
Well worth listening to.

Dave
User avatar
BigEd
Posts: 6261
Joined: Sun Jan 24, 2010 10:24 am
Location: West Country
Contact:

Re: Matchbox sized 6502 Co Processor

Post by BigEd »

Thanks for sharing Jason's talk Dave! I should clarify that Rich is responsible for virtually all the code in our Tube model, whereas I'm responsible for all mistakes in merging updates for the recent releases.

Note that John Kortink's ReTuLa also only has a single byte for the 24-byte FIFO. But I don't know whether or not it runs Elite. I do know he's said that the short FIFO should only cause a small performance loss - and we measured about 2% (see below.)

I found that "Stephen", a commenter on Mike Stirling's Beeb-on-FPGA article, also wrote an HDL Tube which did everything except run Elite. He said:
Can you try the following for me on your FPGA model.

Code: Select all

*FX 9,0
This seems to cause a hard interrupt lock up for me about a 10th of a second after i do it. This is whats breaking elite tube for me.
Dave, as you've been able to see the parasite-side Tube activity during the livelock, could you also see the address bus, enough to find out which bit of OS code is running on the parasite?

Cheers
Ed
The Sphere benchmark times were:

No coprocessor, Master 128 - 24.1s
Internal 65C102 Coprocessor @ 4Mhz, with original tube IC - 10.93s
Internal 65C102 Coprocessor @ 4Mhz, with our tube replacement FPGA - 11.18s

Code: Select all

10 TIME=0
15 MODE 4
20 CLS
30 S%=400
40 VDU29,640;512;
50 MOVE 0,0
60 FOR A=0 TO 126 STEP.25:DRAW S%*SINA,S%*COSA*SIN(A*.95):NEXT
70 PRINT TIME/100
Our current implementation has the following configuration:

Code: Select all

         Host-> Parasite    Parasite->host
Reg1       1 byte              1 byte
Reg2       1 byte              1 byte
Reg3       2 byte              2 byte
Reg4       1 byte              1 byte
So a little over 2% difference compared with the original which has a 24 byte FIFO in the p->h direction for Reg1.
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Re: Matchbox sized 6502 Co Processor

Post by hoglet »

BigEd wrote:Dave, as you've been able to see the parasite-side Tube activity during the livelock, could you also see the address bus, enough to find out which bit of OS code is running on the parasite?
In terms of debugging, I have the following pins available to me:
- 7 TP pins on the GOP module test connector (TP2..8)
- 6 spare pins on the DIP
This is probably just about enough to be able to see the relevant parasite address.

At the moment I'm tinkering with a Z80 core.

My plan was to wait for Jason to post his fixes, and see if they resolve the Elite issue, and if not, I'll dig in further.

Dave
User avatar
flynnjs
Posts: 863
Joined: Tue Jul 06, 2010 10:33 pm
Contact:

Re: Matchbox sized 6502 Co Processor

Post by flynnjs »

haha, I threw that talk together about a week after I got my first boot using Ed's Tube code and one of the cores I mentioned. It was after that that I discovered a number of other issues and I've not touched/fixed anything since!

Did anyone go to the last talk, October 2014? That was a sort of follow up, on OS processors and verification.

Thanks for clarifying how many mods you've made. I've got some down-time today so I'll explore my changes and post back later.
User avatar
BeebMaster
Posts: 7380
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Re: Matchbox sized 6502 Co Processor

Post by BeebMaster »

What is the blue tint in the screenshots? Is it deliberate? I rather like it!
Image
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Re: Matchbox sized 6502 Co Processor

Post by hoglet »

BeebMaster wrote:What is the blue tint in the screenshots? Is it deliberate? I rather like it!
It's just the way the monitor is when you are slightly off axis.

Dave
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Re: Matchbox sized 6502 Co Processor

Post by hoglet »

This morning I've been trying to put together a Z80 second processor using the same hardware.

I've got it to the point when it is recognised and outputs it's name during the boot sequence:
IMG_0717.JPG
But it's then hanging where I think it should be saying insert system disk.

At least, that's what BeebMasters's seems to say at this point:
http://www.beebmaster.co.uk/2P/AcornZ80-11.html

I'll look at the Client ROM to source to try to figure our where it's getting stuck.

If I remove the BBC Basic ROM, I can get to a * prompt.

Can anyone point me at a version of Z80 basic I can load into Sideways ROM?

Dave
User avatar
BigEd
Posts: 6261
Joined: Sun Jan 24, 2010 10:24 am
Location: West Country
Contact:

Re: Matchbox sized 6502 Co Processor

Post by BigEd »

Looks like there's a BBC BASIC ROM in BBCTube.zip as found at
http://mdfs.net/Software/BBCBasic/CPMTube/
courtesy of JGH and RIchard Russell.
User avatar
danielj
Posts: 9900
Joined: Thu Oct 02, 2008 5:51 pm
Location: Manchester
Contact:

Re: Matchbox sized 6502 Co Processor

Post by danielj »

Won't that still need CP/M to handle the low level I/O?

d.
User avatar
BigEd
Posts: 6261
Joined: Sun Jan 24, 2010 10:24 am
Location: West Country
Contact:

Re: Matchbox sized 6502 Co Processor

Post by BigEd »

As I understand it, this version has been patched to remove the dependency on CP/M, and to allow for a ROM version.

(See http://goo.gl/Mndt86)
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Re: Matchbox sized 6502 Co Processor

Post by hoglet »

BigEd wrote:As I understand it, this version has been patched to remove the dependency on CPM, and to allow for a ROM version.
OK, I've tried this Z80 Basic ROM and it still hangs during boot.

If I disable all language ROMs and do a control break, I get a * prompt.

If I then do a *HELP DFS, then it hangs half way through the expected output.

If I instead I do a *HELP XXX, then it completes (less output). But if I do it again, it hangs.

Looks like it is hanging after outputting about 60 characters.

I think I'll wait for Jason's fixed to the tube code, and see if these help.

Dave
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Re: Matchbox sized 6502 Co Processor

Post by hoglet »

OK, the hanging on *HELP is caused by the output to printer being enabled.

If I do a ^C, then *HELP never hangs.

Pressing Escape causes an immediate hang of the parasite, and the parasite IRQ line goes low and stays low.

So, possibly I have a problem with interrupt handling on the parasite.

Dave
User avatar
hoglet
Posts: 12665
Joined: Sat Oct 13, 2012 7:21 pm
Location: Bristol
Contact:

Re: Matchbox sized 6502 Co Processor

Post by hoglet »

I took a bit of a break this afternoon and have just come back, and immediately spotted a silly mistake in generating P_CS_B.

On correcting this, I'm getting a bit further, but it now seems there is a problem with NMI.

When NMI is asserted (0), I see the following:
- two writes to RAM (as the return address is pushed onto the stack)
- boot mode entered (as an instruction is read from 0x0066)
- boot mode exited (as an instuction is read from >= 0x8000)
- a single access to the tube

At this point, I would expect NMI to go back to (1), but it doesn't, and it seems to stay low for ever.

I'm guessing this is a tube bug. Or, I've somehow messed to the NMI handling, and the wrong address is being jumped to.

Jason, I'd really appreciate a copy of your fixed up tube code!

Dave
Post Reply

Return to “acorn atom and acorn system series”