Testing the Ruby 6502
After making it on breadboard, I was already relatively confident that it would work on stripboard and my initial testing went well. I could access the shared RAM from the ATmega, write test patterns into it, read them back and the initial bring-up of the 6502 went well. I wrote a tiny program to take the reset vector and loop to itself which let me probe some data and address lines to see that it was all working, and it was, so next step was to make it do something more useful …
Traditionally…
Most old (and old-style!) 8-bit micros had some sort of “monitor” program that ran at boot time.An early example is WozMon. This would let you edit memory, maybe load programs from a punched tape or audio tape and provide a way for programs to print and take keyboard input. The Apple 1 worked like this.
Often it was hidden away and all it really was was a set of basic input/output subroutines (what we might now call a BIOS) which were used by the main BASIC ROM to communicate with the outside world.
The Apple II, PET (and Vic20/C64), TRS-80, ZX Spectrum, and many others all worked in a similar manner to this
The Apple II did have another trick up its sleeve… It had a full-blown “monitor” that could let you display and alter and move memory, disassemble 6502 instructions, a mini one-line at a time assembler as well as a few more features like some floating point subroutines and a pseudo 16-bit interpreter.
The BBC Micro was slightly different again – come 1981 and some ideas had changed, so it had what we might now recognise as an operating system with well-defined ways to call functions inside the OS to perform text input and output, graphics, sound, disk (and network) filing systems and so on. There was a command-line interface to this Machine OS (MOS), but for the most part, you’d turn the computer on, and half a second later, like most of the other micros of the day, a beep then you were in BASIC. The command-line interface was commonly accessed from BASIC (and other languages) by typing a star (*) then the command, so the phrase “star command” was popular.
The main alternative to this style of computer startup was CP/M. More popular on 8080 and Z80 systems. This loaded from disk and provided a set of basic input and output subroutines (ie. BIOS) and a command-line driven interface. Almost all commands were then loaded from disk into RAM to be executed. Many modern day systems work like this.
What do I do for Ruby?
After initial bring-up testing and writing some basic routines to talk back to the ATmega host processor via the shared memory interface, I started with an Apple II style of monitor. I wrote a somewhat simplified version which let me do things like dump and modify memory, memory tests, poke short bits of code and execute them. I made this with a well-defined interface for character IO and a few utility routines, so that other programs could use it for keyboard and screen input and output and so on.
This worked (and still works!) well. I can use it to run ehBasic and Applesoft BASIC and I’ve also had Apple Integer BASIC running under it too, as well as a few of my own programs. I also wrote a new implementation of Woz’s Sweet16 for it too – to make it go a little faster and remove a (re)location issue it has. (I have plans for a project that will need some 16-bit data manipulation so I thought this might be handy)
This is an example of it in operation:
GMon v1.0. Ready Copyright (c) 2018, Gordon Henderson % 0.ff // Dump RAM from $0000 to $00FF 0000: 4C 3C C4 4C 3A CB 00 00 | L< L: | 0008: 00 00 4C 99 D1 22 00 6B | L " k | 0010: 00 00 00 04 00 00 00 00 | | 0018: 00 00 00 00 00 00 00 00 | | 0020: 00 00 00 00 00 00 00 00 | | 0028: 00 00 00 00 00 00 00 00 | | 0030: 00 00 FF DD 00 00 00 00 | | 0038: 00 00 00 00 00 00 00 00 | | 0040: 00 00 00 00 00 00 00 00 | | 0048: 00 00 00 00 00 00 00 00 | | 0050: 00 F0 55 52 00 07 50 C3 | UR P | 0058: 00 00 00 00 00 00 38 CE | 8 | 0060: 0A DD 78 00 00 00 03 01 | x | 0068: 08 65 09 6C 09 6C 09 00 | e l l | 0070: C0 00 C0 00 C0 C8 FF C8 | | 0078: 00 62 00 00 00 00 08 00 | b | 0080: 00 01 82 00 00 00 00 00 | | 0088: 88 00 05 00 6E 09 00 03 | n | 0090: 4C 01 00 00 6C 08 65 08 | L l e | 0098: 00 00 00 00 00 90 00 00 | | 00A0: F0 00 00 00 00 84 F0 00 | | 00A8: 00 00 00 00 00 0C C3 65 | e | 00B0: 09 E6 B8 D0 02 E6 B9 AD | | 00B8: 08 02 C9 3A B0 0A C9 20 | : | 00C0: F0 EF 38 E9 30 38 E9 D0 | 8 08 | 00C8: 60 80 4F C7 52 00 00 00 | ` O R | 00D0: 00 00 00 00 00 00 00 00 | | 00D8: 00 00 00 00 00 00 00 00 | | 00E0: 00 00 00 00 00 00 00 00 | | 00E8: 00 00 00 00 00 00 00 00 | | 00F0: 00 01 00 00 00 00 00 00 | | 00F8: F8 00 00 00 00 00 00 00 | | % ? Unknown command: "?" % 400:a9 47 20 03 f0 60 // Enter small program to print 'G' % 400.40f 0400: A9 47 20 03 F0 60 60 B9 | G `` | 0408: 00 FF 99 00 04 C8 D0 F7 | | % 400g Go @0400 G%
Load up Applesoft:
-> Applesoft ... Loading @ $C000. OK: End @ $E880 Remember to set CAPS-LOCK to use BASIC ]PRINT 1/3 .333333333 ]
At this point I typed in a program (actually copy & paste as I don’t have any Applesoft filing operations working yet)
]LIST 10 MAXITER = 20 20 LET C$ = " .,'~!^:;[/<&?oxOX# " 30 FOR Y = - 21 TO 21 40 FOR X = - 21 TO 21 50 CREAL = X / 11 70 CIMAG = Y / 11 80 ZREAL = CREAL 90 ZIMAG = CIMAG 95 CT = 1 100 ZM = ZREAL * ZREAL 105 ZN = ZIMAG * ZIMAG 107 ZL = ZM + ZN 110 IF ZL > 4 THEN GOTO 170 120 Z2 = ZM - ZN + CREAL 130 ZIMAG = ZREAL * ZIMAG * 2 + CIMAG 140 ZREAL = Z2 150 CT = CT + 1 160 IF CT < MAXITER THEN GOTO 100 170 PRINT MID$ (C$,1 + CT,1); 180 NEXT X 185 PRINT "" 190 NEXT Y 200 END ]RUN ...............,,,,,,,,,,,,,............... ............,,,,,,,,,,,,,,,,,,,............ ..........,,,,,,,,,,,,,,,,,,,,,,,.......... .........,,,,,,,,,,,,,,,,,,,,,,,,,......... ........,,,,,,,,,,,,,,,,,,,,,,,,,,,........ ......,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,...... .....,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..... .....,,,,,,'''''''''',,,,,,,,,,,,,,,,,..... ....,,,,'''''''''''~'''',,,,,,,,,,,,,,,.... ...,,,,'''''''''~~! !~~'',,,,,,,,,,,,,,,... ..,,,'''''''''~~~!![[ ~~~'',,,,,,,,,,,,,,.. ..,,'''''''''~~~!!:/O:!~~~'',,,,,,,,,,,,,.. .,,''''''''~~~~!!^< ^!!~''',,,,,,,,,,,,,. .,''''''''~~~~!^:;< o;^^!~''',,,,,,,,,,,,. .''''''''~~~!!; < O/[ !'''',,,,,,,,,,,. ,'''''''~!!!!^; <!~''',,,,,,,,,,,, ''''''~^^!!^^; [^~'''',,,,,,,,,,, '''~~!!<;;[:;o <~'''',,,,,,,,,,, '~~~!!^;#O /# :~~''',,,,,,,,,,, ~~~!!!; :~~''',,,,,,,,,,, ~~!^:;OO ^~~''',,,,,,,,,,, ;!~~'''',,,,,,,,,, ~~!^:;OO ^~~''',,,,,,,,,,, ~~~!!!; :~~''',,,,,,,,,,, '~~~!!^;#O /# :~~''',,,,,,,,,,, '''~~!!<;;[:;o <~'''',,,,,,,,,,, ''''''~^^!!^^; [^~'''',,,,,,,,,,, ,'''''''~!!!!^; <!~''',,,,,,,,,,,, .''''''''~~~!!; < O/[ !'''',,,,,,,,,,,. .,''''''''~~~~!^:;< o;^^!~''',,,,,,,,,,,,. .,,''''''''~~~~!!^< ^!!~''',,,,,,,,,,,,,. ..,,'''''''''~~~!!:/O:!~~~'',,,,,,,,,,,,,.. ..,,,'''''''''~~~!![[ ~~~'',,,,,,,,,,,,,,.. ...,,,,'''''''''~~! !~~'',,,,,,,,,,,,,,,... ....,,,,'''''''''''~'''',,,,,,,,,,,,,,,.... .....,,,,,,'''''''''',,,,,,,,,,,,,,,,,..... .....,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..... ......,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,...... ........,,,,,,,,,,,,,,,,,,,,,,,,,,,........ .........,,,,,,,,,,,,,,,,,,,,,,,,,......... ..........,,,,,,,,,,,,,,,,,,,,,,,.......... ............,,,,,,,,,,,,,,,,,,,............ ...............,,,,,,,,,,,,,............... ]
And that works well. I ran a few versions of this under Applesoft and ehBasic and use it as a sort of system soak test.
IO IO, it’s off to work I go …
The final thing to test was my single bit of IO decoding. I wrote a program, connected an oscilloscope to it and the scope trace wiggled as I expected it to. (Actually, not first time, I went through an iteration or 2 of my GAL programming to get it right). I hooked the signal up to an 8-bit latch with the inputs connected to the 8-bit address bus, then I could light up some LEDs.
This video is incrementing a counter each time it finishes a simple memory test:
Blinkenlights are always a good thing and a traditional Larson Scanner was written. (In Sweet 16)
Here is the code:
jsr strout .byte "Larson 16",13,10,0 goSweet16 set r12,$0100 ; Sweet16 stack pointer set r2,$00FF ; Larson data end marker value set r8,IO_REGISTER ; Update LEDs loop0: set r1,larsonData ; Address of the data table loop1: ld r8 ; Copy r8 - address of LEDs into r7 because ... st r7 ld @r1 ; Loads and increments r1 cpr r2 bz loop0 ; Reached the end st @r7 ; ... store increments bs delay br loop1 delay: set r3,$0800 sub1a: dcr r3 bnz sub1a rs .setcpu "65c02"
And data:
; larsonData: ; Bit patterns for a 3-bit wide scan, there and back again. ;******************************************************************************** .export larsonData larsonData: .byte %00000000 .byte %00000001 .byte %00000011 .byte %00000111 .byte %00011100 .byte %00111000 .byte %01110000 .byte %11100000 .byte %11000000 .byte %10000000 .byte %00000000 .byte %00000000 .byte %10000000 .byte %11000000 .byte %11100000 .byte %01110000 .byte %00111000 .byte %00011100 .byte %00001110 .byte %00000111 .byte %00000011 .byte %00000001 .byte %00000000 .byte $ff
But I wanted more…
After the Apple II, I would consider the BBC Micro to be my next favourite 8-bit micro system. Although they really tie for top place for many reasons. It was easy to say (back in the day) that the Beeb was better, faster, etc. but it also had 3 or 4 years advantage – and back then, 3 years was a long time and technology was changing really fast!
Anyway, after another “how hard can it be” moment I started to look at getting BBC Basic running. Fortunately a lot of hard work has already been done by others on this and Jonathan Harston in particular (See: http://mdfs.net/) so it was relatively easy. I had to write just 3 Acorn MOS “OSBYTE” calls to make a text-only BBC BASIC run.
Well, not quite – there was slightly more to it than that – mostly to do with IRQ/BRK handling, however I can now run BBC BASIC on Ruby and I’ll write about that soon..
Although before I close this one, just a note to say that once it was on stripboard and I’d written the code to do some memory tests and BASICs I decided to see how fast I could get it to run at… the W65C02 is rated to 14Mhz and I had been hoping I might get it up to 8Mhz – I did use some good fast memory but otherwise I didn’t really put much effort into the stipboard layout, and so once it was happy at 1Mhz, I tried 2Mhz, then 4 and 8Mhz crystals. At 8Mhz it was just as stable as 1Mhz and as I didn’t have any more crystals, I decided for a bit of fun to go for broke and tried the 16Mhz crystal I was using for the ATmega and just run a wire over the board. (See the green wire with a knot in it in the video above) I was quite amazed that it worked. And it continues to work just fine at 16Mhz although to save wires flying all over the board, I’m currently running it at 8Mhz.