Project-28 - A somewhat Minimal 6502 system
Posted: Thu Jan 30, 2025 2:14 pm
I've written about this in the past, but wanted to give an update as I've been working on it recently and made a brief mention of it recently in another thread...
This project was a sort of (personal) challenge to a comment I made about my own variant of TinyBasic. I said something along the lines of "I felt it might be a good fit for a minimal 4K system"... So I set about doing it as my winter 2023 project - and it went well. I had it done by the Solstice, a PCB made and tested and a bodge wire fitted!) and interfaced it to a quick hack of a piece of stripboard with 18 LEDs on making a sort of flashing tree decoration.
https://unicorn.drogon.net/6507-tree.mp4
So what is it:
It's a small SBC (95mm²) with a 6507 CPU on-board, with 4KB of usable RAM and 4KB ROM. The ROM holds my own TinyBasic interpreter with serial IO routines. There is an on-board button which can be read as well as 4 LEDs which are under program control. The board also features a W65C22 VIA for GPIO. The button, LEDs and VIA have instructions built in to the on-board TinyBasic interpreter to save you using peek & poke to access them.
The 6507 can only address 8KB with its 13 pin address bus so a split of 4KB ROM and 4KB RAM works well.
The "ROM" is actually a 32KB EEPROM arranged as banks of 4KB. The CPU can only see one bank at a time and the setup is that one bank is used for the code of the TinyBasic interpreter and the other 7 are used to save/load programs from.
This is the Revision 1 board - I have recently changed the layout slightly and fixed the bodge wire I needed for this board and will be testing this soon. Note that the RAM is physically under the EEPROM. CPU, RAM and EEPROM are all 28-pin chips, hence the name: Project-28.
The board has 4 on-board LEDs that you can use as well as a button for one-bit input.
The CPU is a 6507 clocked at 2Mhz. It's been very stable so-far, but I'm not sure I can get many more Rockwell parts and I'll be testing UMC parts soon to see if they also run at 2Mhz.
The TinyBasic plus bit-banged serial IO and the code to access the button, LEDs and handle loading and saving Basic program to/from the EEPROM all fit into 4KB. It was quite a challenge even though it's a "classic" TinyBasic in that it uses an IL (Intermediate Language - a sort of virtual machine) to save space - there was still a lot of 6502 code to be written, squeezed, optimised and so on. One challenge is that the code to access the EEPROM needs to run from RAM, so it has to be copied into RAM, called, do the bank switching, save/load/dir and return back to the main code. (And due to size considerations, this code is now split into 2 parts, so 2 segments of code to copy and run in RAM).
The Basic is fairly straightforward for a TinyBasic with the addition of DO...UNTIL as well as FOR...NEXT. GOTO and GOSUB takes an expression so you can use that to GOTO 10+N for example. There are the usual TinyBasic 26 variables which are treated as 16-bit 2s compliment signed integers. Overflow is checked on all arithmetic operations. Additionally there is one more variable: @ which can be used as a general purpose variable, but has the side-effect of defining the field width of printed (decimal) numbers. Hexadecimal numbers are supported and can be input and printed too.
Even though the board runs at 2Mhz it's not blindingly fast. It's slower than most 6502 Basics of the era due to it's using an IL. the IL is a sort of virtual machine interpreted by the 6502 which then interprets and runs the Basic program. Turtles all the way down, as they say.
There are additional commands to manipulate the VIA using a wiring-like interface where it's considered to be a 16-pin device. You can read and write individual pins and change their modes in a manner similar to the wiring interface used in Arduino systems. Also to to read the button and read/write the on-board LEDs...
... and so there are also peek and poke commands (both byte and word) if you want to manipulate it directly for e.g. 8-bit port access.
Some commands are different to what you might expect in a Basic - unless you've had experience of Acorn Atom or BBC Basic. Hex numbers are supported but have to be prefixed with the ampersand (&) character rather than the usual $ or even 0x characters. (Dollar $ is the string prefix)
Prefixing a variable with the twiddle (~) symbol in a PRINT instruction will cause it to be printed as a 4-digit hexadecimal number. e.g. PRINT ~66 will show 0042 on the terminal. There is an additional output instruction: VDU. This takes a list of number or variables and outputs them as their single byte ASCII code. e.g. VDU 12,10,10,7 may clear the screen and move the cursor down 2 lines and ring the bell on some terminals.
Peek and Poke are called "indirection operators" and rather than the keyword peek or poke are the characters ? (for byte indirection) or ! for word indirection. e.g. to poke the value $AA into the VIA port A: ?&1F1 = &AA similarly to read port V into variable Z: Z = ?&1F0
(The VIA occupies addresses $01F0 through $01F0F in the memory map, so stack is initialised to $EF rather than the traditional $FF)
Strings are supported but string space allocation and manipulation is left as an exercise to the user. Saying $A will treat variable A as a pointer, so:
Strings can be copied and printed but you need to allocate space yourself. See the manual for more.
Programs can be saved into the EEPROM - there are 7 save slots and these are just numbers. If you have a line zero in your program which is a REM statement then that line is printed with the DIR command.
(Also demonstrates the CH or chain command - loads and runs a program but does not clear the variables while RUN normally does a clear).
There is a turnkey or autostart feature - if the program in slot 1 has the magic string: !BOOT after the REM statement then it will be loaded and run at power on time. This is how I was running my tree lights program with it powered from a USB power bank.
Using the wiring commands and LED: (I have a 10 LED bargraph display hooked up, so ...)
What now?
Well, my plan is to offer it as a kit to solder up yourself and have a bit of minimalistic fun. you have 4K of RAM for your Basic program (3K more than the Sinclair ZX80!) so don't waste it. (Technically, 3328 bytes as Page 0, 1 and 2 are used by the system and Basic program start at $300) The full specification of it all is on my website - see the link below.
And realistically, if I sell 10 of these I'll be happy. This isn't my retirement project...
Comments here are welcome - maybe even suggestions. This year has been sent mostly on code golfing trying to squeeze the already full 4K code further so I can fit in the EEPROM protection code and the VIA GPIO code. This has come as a slight speed reduction, but that's the way it is.
Cheers,
-Gordon
This project was a sort of (personal) challenge to a comment I made about my own variant of TinyBasic. I said something along the lines of "I felt it might be a good fit for a minimal 4K system"... So I set about doing it as my winter 2023 project - and it went well. I had it done by the Solstice, a PCB made and tested and a bodge wire fitted!) and interfaced it to a quick hack of a piece of stripboard with 18 LEDs on making a sort of flashing tree decoration.
https://unicorn.drogon.net/6507-tree.mp4
So what is it:
It's a small SBC (95mm²) with a 6507 CPU on-board, with 4KB of usable RAM and 4KB ROM. The ROM holds my own TinyBasic interpreter with serial IO routines. There is an on-board button which can be read as well as 4 LEDs which are under program control. The board also features a W65C22 VIA for GPIO. The button, LEDs and VIA have instructions built in to the on-board TinyBasic interpreter to save you using peek & poke to access them.
The 6507 can only address 8KB with its 13 pin address bus so a split of 4KB ROM and 4KB RAM works well.
The "ROM" is actually a 32KB EEPROM arranged as banks of 4KB. The CPU can only see one bank at a time and the setup is that one bank is used for the code of the TinyBasic interpreter and the other 7 are used to save/load programs from.
This is the Revision 1 board - I have recently changed the layout slightly and fixed the bodge wire I needed for this board and will be testing this soon. Note that the RAM is physically under the EEPROM. CPU, RAM and EEPROM are all 28-pin chips, hence the name: Project-28.
The board has 4 on-board LEDs that you can use as well as a button for one-bit input.
The CPU is a 6507 clocked at 2Mhz. It's been very stable so-far, but I'm not sure I can get many more Rockwell parts and I'll be testing UMC parts soon to see if they also run at 2Mhz.
The TinyBasic plus bit-banged serial IO and the code to access the button, LEDs and handle loading and saving Basic program to/from the EEPROM all fit into 4KB. It was quite a challenge even though it's a "classic" TinyBasic in that it uses an IL (Intermediate Language - a sort of virtual machine) to save space - there was still a lot of 6502 code to be written, squeezed, optimised and so on. One challenge is that the code to access the EEPROM needs to run from RAM, so it has to be copied into RAM, called, do the bank switching, save/load/dir and return back to the main code. (And due to size considerations, this code is now split into 2 parts, so 2 segments of code to copy and run in RAM).
The Basic is fairly straightforward for a TinyBasic with the addition of DO...UNTIL as well as FOR...NEXT. GOTO and GOSUB takes an expression so you can use that to GOTO 10+N for example. There are the usual TinyBasic 26 variables which are treated as 16-bit 2s compliment signed integers. Overflow is checked on all arithmetic operations. Additionally there is one more variable: @ which can be used as a general purpose variable, but has the side-effect of defining the field width of printed (decimal) numbers. Hexadecimal numbers are supported and can be input and printed too.
Even though the board runs at 2Mhz it's not blindingly fast. It's slower than most 6502 Basics of the era due to it's using an IL. the IL is a sort of virtual machine interpreted by the 6502 which then interprets and runs the Basic program. Turtles all the way down, as they say.
There are additional commands to manipulate the VIA using a wiring-like interface where it's considered to be a 16-pin device. You can read and write individual pins and change their modes in a manner similar to the wiring interface used in Arduino systems. Also to to read the button and read/write the on-board LEDs...
- IF BTN THEN ... :REM If Button is being pushed
- LED = 9 : REM Set LEDs to 1001 (9 in binary)
- LED = LED + 1 : REM increment the 'count' on the LEDs.
- PM P,M : REM PinMode, set pin P to mode M (1 for output, 0 for input)
- X = DR P : REM DigitalRead pin P
- DW P,V : REM DigitalWrite pin P with value V (0 or 1)
... and so there are also peek and poke commands (both byte and word) if you want to manipulate it directly for e.g. 8-bit port access.
Some commands are different to what you might expect in a Basic - unless you've had experience of Acorn Atom or BBC Basic. Hex numbers are supported but have to be prefixed with the ampersand (&) character rather than the usual $ or even 0x characters. (Dollar $ is the string prefix)
Prefixing a variable with the twiddle (~) symbol in a PRINT instruction will cause it to be printed as a 4-digit hexadecimal number. e.g. PRINT ~66 will show 0042 on the terminal. There is an additional output instruction: VDU. This takes a list of number or variables and outputs them as their single byte ASCII code. e.g. VDU 12,10,10,7 may clear the screen and move the cursor down 2 lines and ring the bell on some terminals.
Peek and Poke are called "indirection operators" and rather than the keyword peek or poke are the characters ? (for byte indirection) or ! for word indirection. e.g. to poke the value $AA into the VIA port A: ?&1F1 = &AA similarly to read port V into variable Z: Z = ?&1F0
(The VIA occupies addresses $01F0 through $01F0F in the memory map, so stack is initialised to $EF rather than the traditional $FF)
Strings are supported but string space allocation and manipulation is left as an exercise to the user. Saying $A will treat variable A as a pointer, so:
Code: Select all
A = TOP : REM Start of free RAM after the program
$A = "Hello"
B = ?(A+2) : Rem get 3rd character of A
VDU B : REM output the letter lPrograms can be saved into the EEPROM - there are 7 save slots and these are just numbers. If you have a line zero in your program which is a REM statement then that line is printed with the DIR command.
Code: Select all
>DIR
1:
2:
3:
4:
5:
6: Hello
7:
>CH 6
Hello, World!
>LIST
0 REM Hello
10 PRINT "Hello, World!"
20 ENDThere is a turnkey or autostart feature - if the program in slot 1 has the magic string: !BOOT after the REM statement then it will be loaded and run at power on time. This is how I was running my tree lights program with it powered from a USB power bank.
Using the wiring commands and LED: (I have a 10 LED bargraph display hooked up, so ...)
Code: Select all
10 REM Blinky Lights
15 REM first 10 pins output
20 FOR P = 0 TO 9 : PM P,1 : NEXT P
40 FOR P = 0 TO 9
45 LED = P
50 DW P, 1
60 FOR J = 1 TO 20 : NEXT J : REM DELAY
70 DW P, 0
80 NEXT P
85 IF BTN END
90 GOTO 40Well, my plan is to offer it as a kit to solder up yourself and have a bit of minimalistic fun. you have 4K of RAM for your Basic program (3K more than the Sinclair ZX80!) so don't waste it. (Technically, 3328 bytes as Page 0, 1 and 2 are used by the system and Basic program start at $300) The full specification of it all is on my website - see the link below.
And realistically, if I sell 10 of these I'll be happy. This isn't my retirement project...
Comments here are welcome - maybe even suggestions. This year has been sent mostly on code golfing trying to squeeze the already full 4K code further so I can fit in the EEPROM protection code and the VIA GPIO code. This has come as a slight speed reduction, but that's the way it is.
Cheers,
-Gordon