Wednesday, 4 March 2026

Reverse engineering Microbee Basic

TLDR version: I am reverse engineering Microbee Basic. My disassembled code is on my google drive. It's getting pretty complete, to the point where I can move code around, reassemble, and things still work.

Over the years, I've occasionally delved into the inner workings of the various Microbee Basics, generally for a specific purpose, like wanting to figure out how the cassette data is structured for restoring tapes, or more recently figuring out how the bee reads it's keyboard. With the creation of the original SuperPAK coreboard, I had a brief look to see how the PAK command worked, with the aim of seeing if we could know which PAK ROM we were from the PAK call (spoiler, it's in HL).

during a recent holiday, I made the decision to upgrade basic for the Freebee 4MB Pak Cart. I had a few goals, in increasing order of complexity:

  • Extend PAK beyond 256 * 8K = 2MB - Pak Cart has 4MB of Flash available, so I wanted to be able to type "PAK 320" and have that work.
  • Add commands to erase and copy PAKs. "DELETE D" should erase the whole 512K chip in the PAK D slot. "DELETE 63" should erase PAK 63. "COPYPAK 275 TO 63" should copy the contents of PAK 275 to PAK 63. COPYPAK D to A should copy the whole 512K PAK Cart from D to A.
  • Create a CP/M like directory structure on PAKs, so that I could have a PAK block with the directory on that PAK Cart, and be able to list it with "DIR A", run a program (potentially spanning multiple PAKs) with "RUN BLAH.M" where BLAH.M is the name of a program that can be viewed on the currently selected PAK Cart, and of course "LOAD BLAH.B" would check if BLAH.B exists on the currently selected PAK before trying to load from tape.

So as you can see it's getting increasingly ambitious, and to get things working we need to both find space in the basic ROMs to store our code, plus figure out enough of how the basic works that we can add our routines.

Finding the space is a doddle. Basic 5.29e, the premium BASIC, uses a bank select scheme to add a whole 8K ROM to the usual 16K basic. Examining this extended ROM shows that only 2K is used for the premium graphics routines, leaving us with 6K to play with. In fact the way that the ROM is implemented on the FreeBee Pak Cart coreboard, I can have an arbitrary dividing line between code that stays put (ROM B) and code that banks in and out (ROM A and C), as I just use a 32K EPROM with a lot of duplication. So I could squeeze all the staying put code into a 2K chunk, for example, and have 2 * 14K banks, for a total of 30K (2 * 14K + 2K) of available code space.

So now onto figuring out how it works so that I can make modifications. This is done through disassembly, using every possible hint to first figure out what's code and what's data, and then what routines do what in the code, and then working our way through all the routines to nut them out. Along the way we correct the disassembly stuff-ups where it's interpreted data as code, and to give meaningful names to routines and meaningful labels for jumps, with comments. Along the way we make sure our increasingly commented disassembly is correct by assembling it and performing a binary diff with the original ROM.

So let's start with the binary code for Basic 5.29e. It's a 24K file, stored as BASIC A, in memory from 8000h to 9FFFh when LV5 is 0 (ie at boot), followed by BASIC B, always in memory from A000h to BFFFh, followed by BASIC C, in memory from 8000h to 9FFF when LV5 is 1. So with our disassembler (I use Z80DASM, which is part of Z80ASM), we type:

z80dasm --origin=0x8000 --labels --output=basic.asm basic.bin

This gives us a very long file that looks like this (very first section):

; z80dasm 1.2.0
; command line: z80dasm --origin=0x08000 --labels --output=basic.asm basic.bin

	org 08000h

l8000h:
	jp l84c6h
	jp l84c6h
	jp la3e3h
	jp la3cbh
	jp la626h
	jp lacafh
l8012h:
	jp lab6dh
sub_8015h:
	jp laae6h
sub_8018h:
	jp lab26h
	jp lab17h
sub_801eh:
	jp l83d7h
l8021h:
	jp l8517h
sub_8024h:
	jp lad98h
l8027h:
	jp laf9eh
	jp la801h
	jp la7ceh
	jp lb035h
	jp lb040h
	jp lb04ch
	jp lb057h
	jp lb0a8h
	jp l80ebh
	jp l80bch
	jp l809bh
	jp l845fh
	jp l8433h
	jp l83c1h

The first section above is a "jump table". This is common in code of the era. You want to be able to advertise functions within your code, but you know that when you edit and reassemble the code things will move, so you start with a list of jumps to important functions, that way people can call the jump, which in turn goes to the function, which can then return to theirt code. While the function itself might move around, the jump never does.

So now we become detectives. There are some memory maps available that give us a bit of a hint about what's where in the basic. There's a reasonably good one in "Wildcards" by Ash, Burt, and Nallawalla. Let's use their insights to name the stuff in the jump table and add comments to everything:


;#############################################################################################
; Start of BASIC ROM A
;#############################################################################################

		org 08000h

		jp RESETTOHERE		; Start of BASIC
		jp RESETTOHERE		; BASIC warm start
		jp WAITMBEEKEY		; DGOS Wait for keyboard input - A register
		jp GETKEYIFANY		; DGOS Scan keyboard
		jp MBEEVDUFROMB		; DGOS Display character in B register
		jp GIVEPIOARM		; DGOS Give PIO an arm
l8012h:		jp CASSBYTEIN		; DGOS Get byte from cassette in A
sub_8015h:	jp CASSBLOCKIN		; DGOS Get block from cassette
sub_8018h:	jp CASSBYTEOUT		; DGOS Cassette byte out A
		jp CASSBLOCKOUT		; DGOS Cassette block out
sub_801eh:	jp RUNPROG		; Auto-execute address for saving BASIC program
l8021h:		jp BASWARMSTART		; Warm start for restoring Reset jump
sub_8024h:	jp HIRESINIT		; HIRES initialisation
l8027h:		jp LORESINIT		; LORES initialisation
		jp SETINVERSE		; INVERSE initialisation
		jp SETUNDERLIN		; UNDERLINE initialisation
		jp SETDOT		; SET dot: X = HL, Y = DE
		jp RESETDOT		; RESET dot returns Z if OK
		jp INVERTDOT		; INVERT dot
		jp TESTDOT		; Test for dot - NZ if set/error
		jp PLOTLINE		; PLOT a line
		jp GETCHAR		; Redirected input A
		jp PUTCHAR		; Redirected output A
		jp LPUTCHAR		; Redirected print output A
		jp WRMSTRCLRVAR		; Jump to BASIC with CLEAR
		jp READYMODE		; Jump to BASIC command level
		jp JMPBASFRPAK		; Jump to BASIC after NET or PAK

Every time we change a label, we do a global find and replace on the label, that way the routine gets labelled, as does every single call to the routine in the code. We use a method for labels that makes them obvious. I like to use all caps (shouty much), and I'm not afraid to use labels up to 16 characters. Yes, it means I have to hit TAB a lot, but so be it. Readability is _everything_.

Talking about readability, let's put lots of super-obvious breaks in the code. I like the ;############ sequence going all the way across the line. It makes a very obvious divider.

So the obvious next place to look is the first jump, where BASIC goes on power-up:


RESETTOHERE:
	di
l84c7h:
	ld sp,00080h
	call sub_a3c9h
	ld hl,lba6ah
l84d0h:
	ld a,(hl)
	or a
	jr z,l84dch
	ld b,a
	inc hl
	ld c,(hl)
	inc hl
	otir
	jr l84d0h

Cool, our first learning is already there from the find and replace. First thing the routine does is disable interrupts, then initialise the stack pointer to a very low address in memory. This kinda makes sense - we don't know how much memory the machine has on power up, and we know (from the memory map) that memory from 0000-0080 is a scratch pad, so we don't mind trashing that with our stack. Let's follow the first call to see what that does:


sub_a3c9h:
	reti

It's just a return from interrupt. This ensures that if there's a device that's triggered an interrupt prior to the reset, it's cleared so it's in a known state. As before, every time we learn something, we comment and label:


;#############################################################################################
; RESETTOHERE
;   input:	None.
;   output:	Performs a complete initialisation of the system.
;   affects:	Everything.
;#############################################################################################

RESETTOHERE:	di			; Disable interrupts
		ld sp,00080h		; We don't know how much RAM we have yet, so
					; Initialise stack pointer to low memory
		call RETURNINT		; Perform a reti
		ld hl,lba6ah
l84d0h:		ld a,(hl)
		or a
		jr z,l84dch
		ld b,a
		inc hl
		ld c,(hl)
		inc hl
		otir
		jr l84d0h

Now the next bit is a loop, with an exit in the jr z,l84dch bit. Looks like we get a byte from a table at lba6ah, if that's not zero, we use it as a counter, then we get the next byte from the table into C. The OTIR outputs the data pointed to by HL to the port pointed to by C, so this routine is clearly used for initialising devices from a table. These insights go into the code. First the table at lba6ah:


lba6ah:
	dec b
	ld bc,00f80h
	rla
	add a,e
	add a,e
	dec b
	inc bc
	adc a,d
	rst 38h
	sbc a,c
	or a
	ld a,a
	nop
	nop

As a disassembly, this looks really nonsensical, as it's not instructions, it's data. So let's go back to the binary ROM and open this bit up in a HEX editor to see what it is, noting that the HEX editor sees our code as starting at 0000h not 8000h, so a bit of address math is needed to find the section:


3A60: 08 FF 7F 00 00 00 00 00 00 C9 05 01 80 0F 17 83 
3A70: 83 05 03 8A FF 99 B7 7F 00 00 08 B6 C8 A3 C8 A3

Our first byte, which is used as a counter, is at BA6Ah. This is just 5. Next byte is a port address, Port 01. So we're sending five bytes (80 0F 17 83 83) to port 01. Going to the bee port map, Port 01 is the control register for PIO port A. So this code looks like it's initialising PIO port A. If we download the instruction manual for the PIO, we can follow it through. The next bit does much the same for Port 03, which is PIO port B. So let's remove the chunk of meaningless code at BA6Ah and substitute our data:


;#############################################################################################
; PORTINITDATA - data used to initialise PIO
;#############################################################################################

PORTINITDATA:	db 5, PIOACONTROL	; Initialise PIO A with five bytes
		db 080h			; Set interrupt vector 080h
 		db 00Fh			; Set port to output mode
 		db 017h, 083h, 083h	; Configure interrupt mask

 		db 5, PIOBCONTROL	; Initialise BIO B with five bytes
		db 08Ah			; Set interrupt vector 08Ah
 		db 0FFh			; Set port to control mode
		db 10011001b		; Set port b bit 0 (TAPEIN) to input
					; Set port b bit 1 (TAPEOUT) to output
					; Set port b bit 2 (RS232 CLK) to output
					; Set port b bit 3 (RS232 CTS) to input
					; Set port b bit 4 (RS232 RXD) to input
					; Set port b bit 5 (RS232 TXD) to output
					; Set port b bit 6 (SPEAKER) to output
					; Set port b bit 7 (VSYNC) to input
 		db 0B7h, 01111111b	; Set interrupt mask & enable interrupts
					; for bit transitions on bit 7 (VSYNC)
		db 0, 0			; Signify end of PORTINITDATA

Note I've started labelling the ports as something meaningful - PIOACONTROL rather than 001h. So somewhere up the top of our code we need to equate our label for PIOACONTROL to 001h. Let's put everything we know about the bee hardware ports into a file HARDWARE.asm, and include that:


;#############################################################################################
; Hardware Constants - memory organisation
;#############################################################################################

SCRATCH:	equ 00000h		; Basic Scratch Area
BASIC:		equ 08000h		; Start of Basic ROM

;#############################################################################################
; Hardware Constants - ports
;#############################################################################################

PIOADATA:	equ 000h		; PIO Port A data
PIOACONTROL:	equ 001h		; PIO Port A control
PIOBDATA:	equ 002h		; PIO Port B data
PIOBCONTROL:	equ 003h		; PIO Port B control

We'll add to this file as we learn more. Now that we've nutted out our table, we comment the code that uses it:


;#############################################################################################
; RESETTOHERE
;   input:	None.
;   output:	Performs a complete initialisation of the system.
;   affects:	Everything.
;#############################################################################################

RESETTOHERE:	di			; Disable interrupts
		ld sp,CTCV1		; We don't know how much RAM we have yet, so
					; Initialise stack pointer to low memory
		call RETURNINT		; Perform a reti
		ld hl,PORTINITDATA	; Point to port initialisation data
PORTINITLOOP:	ld a,(hl)		; Get counter for otir
		or a			; Set flags
		jr z,PORTINITFIN	; Zero - finished initialising ports
		ld b,a			; Load byte counter
		inc hl
		ld c,(hl)		; Get address of IO port from table
		inc hl			; point to data that gets sent to IO port
		otir			; send it
		jr PORTINITLOOP		; go get data for next device

Yay! We've worked our first bit out. This is essentially the process we follow for the whole ROM. Following calls and jumps, figuring out what's data and what's code, and then commenting and labelling so that it makes sense to us, not just to the CPU.

I've been doing this for maybe two months now pretty intensively. I've used a lot of great resources to figure stuff out: Wildcards, the Microbee Technical manual, and the Microbee Basic Software Hacker's Handbook, by Nigel Cottrill. I have commented maybe 70% of the code, and made some amazing insights along the way. For example, I've learned that most of the code, the core routines for Basic, are common to the Super-80; another machine that was developed at around the same time. That's because they both use "BASIC ETC" as a base, and they both just added their own IO routines to BASIC ETC and went from there.

It's also pretty clear that later writers of code did not have source code from earlier versions, as they became increasingly afraid to move code. The messiest bit was when they went from version 5.00 to 5.11, adding code to do colour. This code is an absolute shambles. The author didn't understand at all how the basic was structured. Rather than adding a keyword for colour alongside all the other keywords, they instead mangled the routines that tokenise the input line, searching for "COLO" in the line and substituting POKE commands inline. So then they needed to also mangle the code that detokenises for list so it does the opposite. Again, never moving code, instead having sudden jumps or calls to patches.

So as I mentioned at the top, I have a really good disassembly going, and I'm sharing it. You can assemble this code and it will give an exact byte-for-byte duplicate of Basic 5.29e. I've also got a highly modified version that I'm adding lots of stuff to for my Pak Cart Freebee. 9600 baud serial, 2400 baud cassette, more PAK, cleaned up colour commands, and much of the spaghetti untangled to free up masses of space. Here it is.

Sunday, 4 January 2026

A PAK Coreboard for Freebee using Cartridges.

I follow Natalie the Nerd on Bluesky, who does the most amazing things with Gameboy hardware recreations, including (I kid you not) making the lego gameboy actually work. Recently she posted about her shenanigans with 3D printing cartridge cases for a retro console, and it got me to thinking that cartridges would be cool for FreeBee too. They could be a neat way to share code between machines, a bit more accessible than the Compact Flash stuff has been (can just copy them on an EPROM programmer or similar without regard to card format), and (this was what really got me going) they could be an easy way to extend FreeBee, both for memory _and_ I/O.

So I went looking for edge connectors to base my cartridge design off, and found the EDAC 0.1" pitch 34 way connector. This would have been just the thing to plug your 5.25" floppy drive in with back in the day. Including it's ears, it's 63.5mm long, and with 34 pins it has enough to support a 32 pin 39SF040 512K Flash memory, with a couple of pins left over for I/O addressing.

So memory wise four cart slots fit across the back of the board. I can do another four 32 pin Flash chips on the board, plus one chip for Basic, plus a RAM. Each of the Flash chips is a 512K 39SF040, giving me the possibility of 2MB on the board and 2MB plugged in via carts.

I departed a little way from previous coreboards and dropped Microbee compatibility. The Freebee main board does reset, and includes 32K of RAM, so let's use that to simplify the RAM decode and overall logic on this board. The RAM is 128K, and any 32K bank may be dropped in to the upper 32K address space, pushing aside ROMs (Basic and PAK) and video memory when it does so. This gives us 160kB of RAM total, which is more than you'll ever need.

The memory map looks something like:

  • 0000-7FFF: Mainboard RAM.
  • 8000-AFFF: Basic ROM when Port4b7 is reset, with the added bonus of LV5, which allows 32K here. When Port4b7 is set, we get RAM here.
  • B000-CFFF: Pak memory, determined bu Port A and Port4b1
  • D000-EFFF: RAM, regardless. There's no NET ROM.
  • F000-FFFF: Video memory when Port4b6 is reset, otherwise RAM.

The carts get a chip select for their PAK ROM space, and for I/O they get a decoded address space of 16 I/O positions, wich each one on a separate range. This means that I/O will be in different places depending on where it's plugged in, but is the only way to avoid conflict.

The simplest cart is just a 512K x 8 Flash chip. It really couldn't be easier.

Boards have arrived. I used to (professionally) do hard gold for edge connector contacts. I ordered ENIG (electroless nickle-gold) for these. Will be interesting to see how durable it is.

And assembled, running microbee premium basic using my ROM emulator so I can play with the ROM:

I added write protect switches to the carts, and also to the coreboard, so I can protect the PAK flash.

Tuesday, 9 December 2025

An LCD for Freebee

I've got this idea for making a coreboard that plugs in to freebee that contains it's own LCD controller. I can buy (from Core electronics) a lovely little 10" 1280x800 LCD, with a converter board that takes a VGA input. If I do 2 pixels per bee pixel horizontally, and 3 pixels vertically, I get a 640 x 266 display, which is terribly close to the 80x24 bee mode (640 x 264).

I bought one of these (panel is a B101EW05), and this is what it looks like on freebee, via a GBS-8200 CGA-VGA upscaler. It's not too shabby:

the data sheet for the panel says it works with dot-clocks from 64-85 MHz, so at (say) 80 MHz, halving horizontal resolution, I only have to generate pixels at 40 MHz. That's doable with 74AHC...

Here's a little board I knocked up to drive it. I use an ICE40HX1K FPGA, which has 64kbit of dual port RAM, which can be used as a line buffer, plus a PLL for generating a 6x dot clock. I've put an LVDS serialiser in as well, so it'll drive the panel directly. Yeah, not a lot of through hole parts in this one.

And built. This is not like most boards I do lately, featuring TQFP and TSSOP parts, 0603 resistors and caps, and some fiendish SMD inductors that don't have leads at all. Now to get to writing Verilog code for the FPGA.

Thursday, 26 June 2025

A Z180 Freebee

When I was young, I read byte. I was a weird kid. One of my favourite columns was Steve Garcia's Circuit Cellar, because he had a really good hardware focus. A machine I thought was incredibly cool was a HD64180 single-board-computer that he featured in the September 1985 edition.

Now I've got the Freebee going quite nicely, I'd like to continue exploring where I can go with it. Top of the list is to get it to run ROMWBW, which is an unencumbered CP/M install. My brother has a cool Z180 board that runs ROMWBW, so I figure we can make a machine that is zero effort to adapt ROMWBW to, plus has the same degree of Microbee compatibility as the Freebee, plus has more modern I/O, making it easier to play with.

So the spec is:

  • Z180 CPU in 68 pin PLCC package, running 3.375MHz and up to 27 MHz. 3.375 MHz is needed for Bee compatibility.
  • 512K on-board flash ROM to store ROMWBW and whatever else, plus a 512K SRAM (not all used, as I need 64K for a potential Microbee coreboard and video RAM.
  • Shared serial port with Z180 serial and Microbee PIO bit-banged serial, allowing use with both ROMWBW and Microbee software.
  • SD card interface using Z180 synchronous serial, for full ROMWBW compatibility.
  • The whole video and keyboard logic from Freebee Premium.
  • A USB host mass-storage interface based on the CH376. Some of the guys on MSPP have done a FAT based driver for this, and it's very cool.

The CH376 is SMD, and SD card receptacles are also. I think that's a good excuse to swap most of the logic to SMD as well. I'll stick with the easiest SMD parts I can.

The CPU does a lot of heavy lifting. It has a wait state generator that simplifies a lot of things, plus a very cool memory management unit that allows me to put whatever I want wherever I want it in the Z80 address space. In a hardware sense, I simply have ROM from 00000-7FFFF, RAM from 80000-EFFFF, and video memory from FF000-FFFFF. I gate the MREQ signal for the coreboard such that it is only active from F0000-FFFFF, maining a Microbee or Freebee coreboard can simply live alongside the enormous on-board memory. I also do all the decode for banked video RAM from the DRAM/CF coreboard on the mainboard, this way I don't need lots of confusing jumpers. Also there's no boot mode any more. The machine starts up with ROM throughout the Z80 address space, and you have to program the MMU if you want something different.

So some schematics. These are very early, and not yet annotated. I have a lot of work to go yet to put this on a board, so this stuff _will_ change.

Sunday, 11 May 2025

Moar Video Memory

Brad and uBee on MSPP ganged up on me. The Microbee Premium is capable of running a full 32K of PCG RAM, if you unsolder the two 0.6" wide 6264s it came with and substitute 4 0.3" wide ones. They reckoned that FreeBee Fremium should be able to do that too. uBee even has code that displays pictures that totally needs it.

I tried to fob them off, saying there wasn't room on the board for the decode logic for the extra address line. And besides, bigger RAMs are all 0.6" wide. So Brad totally shamed me by designing a daughter board that does it, using a single 74AHC00 for decode and a 0.3" wide 64K x 8 cache RAM. Okay, I can take a hint! We need to grow the video RAM chip to 32 pins, which is straightforward, but the hard bit is we need to squeeze in an extra chip (a 74AHC00) to decode the A15 line for the RAM.

So the key to squeezing this in is realising that inner layer pads and outer layer pads don't need to be the same size and shape. Easy solderability favours generous elliptical pads on the outer layers, but the inner layer pads can be minimum size and round. TBy using a 50 thou inner layer pad, and 10 thou tracks and spaces, we can squeeze two inner-layer traces between pins. This resulted in a week or so of advanced maze game, adding in the extra chip for decode and doing a huge shuffle to get it to route. I didn't stop there, I kept going to reduce the length of many lines, and get stuff out from under the keyboard. This way, if I want to cut the keyboard off, I can.

Importantly, this will still work with a 28 pin 32K RAM. Just insert it so the ground pin is in the right place and it'll be happy, just like a stock Premium!

After a little more finessing, I've got a board that I can chop in half to seperate the keyboard. Now if the keyboard is done in two layers (still four for the main computer), then it gets quite a bit cheaper to make, and as an added bonus you can position the keyboard however you like.

During assembly. It's smol. I use a lot of rosin flux, but that's okay because I make my own.

The split off keyboard has a huge advantage over the single-board Freebee, in that the keyboard PCB can be mounted above the main board. This allows us to get the positioning of the keyboard PCB _just so_, so that Cherry keyswitches and keycaps (which are a lot shorter than the Microbee ones) neatly poke through a Microbee case. Dunno what to do about the spacebar though. 7u is the looongest I could find, which is 1u too short! When I do find an 8u spacebar, I won't have to rebuild the whole computer to make it work. I can just redo the keyboard itself. Of course a second advantage is that you can connect it to the main board using a cable, should you wish.

And having built the board, a quick test before plugging a keyboard in (still waiting on parts for the keyboard. It's the smollest bee yet.

Here it is with a keyboard installed, showing how the two board setup works much nicer in a Microbee case using standard Cherry keyswitches. I've been testing this version pretty exhaustively over the last few days - it works with all my coreboards (SuperPAK, CF, SRAM, DRAM), and works with every bit of software I've thrown at it. It's the most capable bee I've ever used, and has exactly zero bodge wires. It's rather fitting that as I've assembled it (missing the cursor keys to fit in the 32K IC case), and without a coreboard, it's functionally equivalent to the 32K colour machine I had when I was a child.

Friday, 9 May 2025

Freebees in the wild

I've been encouraging people building Freebees to post pictures of their machines. I derive great satisfaction from seeing the customisations people put into their creations. Knowing that people are having fun with stuff that I've designed is, well, a real blast. And if others see what's happening they'll be egged on to join in and have a go themselves.

So with permission of people, I'm going to make this post a tribute to the various machines that are out there. If you want me to feature yours, just drop me a line on my email - suzyfromoz@gmail.com - with a picture and description and I'll add it. If you have a blog or whatever where you feature it I can bung a link in for that too.

So in no particular order:

Brizza's Freebee Fremium, with Compact Flash coreboard. Brizza designed a cool 3D printed lower case for his that keeps it off the desk, while providing instant access to the top of the machine:

Brizza is a sucker for punishment, and also built a standard Freebee:

Cheshirenoir's Freebee Fremium prototype. Cheshire isn't above adding bodge wires to make a board work, so bravely built one of the initial Fremium prototypes up, dealing with a dodgy CRTC and annoyingly fast RAM along the way, then putting it in a cool laser cut case:

Glen's Freebee is unique. He built a version of the production board just before the power switch was added. Glen discovered the fast RAM issues on the initial Freebee, that can be worked around by using slower video RAM, and have since been resolved in Freebee Fremium (but not in original Freebee - I must revisit the design when I have a chance). Check out the cool 3D printed keyboard frame and custom keycaps:

Dirk https://8bitsisenough.blogspot.com is brave enough to go with lavender keycaps on his Freebee Fremium build. His blog has some great pointers on sourcing components:

Saturday, 15 March 2025

A second stab at a ROM Emulator, using a Teensy 4.0

After assembling the ROM Emulator using an ATMega and some RAM, I had issues getting it going. I could connect to the emulator using a terminal, and could change bytes on the ROM, but the software to download an image didn't work.

I put it to one side and did other stuff - notably getting MCLZ8 running on Freebee. I found the process of working within the Arduino IDE to control MCLZ8 as a debugger extremely enjoyable. I implemented commands to examine and change memory, do port I/O, examine and change the Z80 registers, start and stop the machine, and even do breakpoints. Many of the good stuff from NICE, running in a Teensy plugged into Freebee.

So then I saw that Microcore Labs also had an EPROM emulator, running in a Teensy 4. Unlike the ATMega EPROM emulator, we're not dependent on some code running on the host to transfer a file to the emulator. We just convert a binary file to a C array, paste it into the code for the emulator under the Arduino IDE, and hit the compile and upload button.

So I knocked out a better interposer board to go under the Teensy based on the Microcore Labs one, and had a play.

Great news is it all works! I can run an emulated Z80 on Freebee with emulated ROM as well.

There are, however, a couple of points. Firstly I had to overclock the Teensy 4.0 to 900 MHz or better to get it to run reliably with the emulated Z80. Note this is probably due to me fiddling with the Z80 emulation to get _that_ to run at 3.375 HHz reliably. I skip a wait for rising clock on the opcode fetch, as the Teensy was often not making it, meaning the teensy was very slow. By doing that though I suspect I occasionally get very fast opcode fetches, which the relatively slow emulated ROM can't reliably make. Oh well, overclock the hell out of it to make it work.

Second point is that the Arduino IDE doesn't like to simultaneously talk to multiple Teensys. This doesn't seem to affect the code upload - just select the Teensy appropriately, but it does affect console. I need console to drive my debugger. I found a cool tool - TYCommander, which gives me a console independently of the Arduino IDE which lets me control the emulated Z80 while the emulated ROM is also plugged in.

Links:

Wednesday, 26 February 2025

Keep it simple, stupid! A straightforward boot ROM for Freebee

I've been inflicting my buggy boot code with monitor etc on people, which is making their experiences in building Freebee more difficult. It's exacerbated by not distributing basic with the code, as that's copyright Microbee. I had one person wondering why it didn't do anything when he powered it up. After some faffing around, I asked "so did you patch basic in?" and it turned out he hadn't done that.

So I've generated a much simpler ROM that just copies the fonts in, then checks for the presence of basic (just looks for a C3h at 8000h). If it doesn't find that, it displays a helpful message as follows:

I shall put the new code, with a binary (not including basic!) in with the other design files.

Saturday, 1 February 2025

MCLZ8 on Freebee

So it turns out you can emulate a Z80 using a Teensy 4.1. Better yet, with the aid of a few voltage translators on a little interposer board, you can plug the teensy into your Z80 machine and run using the teensy as the processor.

This is kinda huge - the sky's the limit for the debug ability that comes from being able to interrogate everything that's happening at processor level over a dead-basic USB connection.

Luckily, Freebee doesn't need no stinkin' coreboard to run as a basic 32K bee, so the rather tall Teensy + interposer board is no impediment. However the interposer board as layed out on the Microcore Labs website was a bit bulky, so I redid it so it fits neatly under the Teensy. This means a third board is also needed to get back to the normal 0.6" pin pitch for the Z80. I guess I could have used an SMD connector for both Teensy and Z80 socket, but it's easier with through-hole here.

Then we download the MCLZ8 firmware, install the Arduino IDE + Teensy loader, compile the source, and upload the code to the Teensy. And just like that, we have a Z80 hardware emulator in software.

Thursday, 2 January 2025

An STD Bus Video Card that does CGA and EGA resolutions

With the latest release of Freebee Fremium, I added circuitry to invert VSYNC. This is useful to convince a multisync monitor (or upscaler) that you're doing EGA timing (16.67ms vsync, 45.76 µs hsync) with 350 vertical lines. On Freebee I can't just up and change dot clock to do full EGA resolution though, as the one crystal is used for both CRTC and CPU timing, and swapping the CPU to a different crystal is likely to hit it with a very short clock and crash it.

Independently, my brother Doug (yes, the madness runs in the family) has been working hard on STD bus cards. He's created a design for a Z180 CPU card, which even runs ROMWBW (hey Doug, send me a link to a blurb on your CPU card). I reckon it'd be a lot of fun to combine the two - graphics and STD bus. It's sort of reverting to the microbee roots, where it was a collection of S100 cards, but this time STD bus (as it's smaller) and still plenty big to put what we want on the cards.

My idea is for a simple three slot STD bus backplane, housed in a small Hammond enclosure (like the one for my floppy drive that I show in various places on my blog). One slot would be for a video card, one for a CPU + menory, and the last slot for other I/O (floppy disk etc). I'm getting away from the strict microbee compatibility, but who cares. In my mind as long as I can run CP/M and patch other microbee code to make it work, that's good enough.

So on to this design. It's a refactoring of my current Freebee Fremium video hardware, but I've added the ability to swap dot clocks between 13.5 MHz (or 14.31818 MHz if you have no interest in microbee stuff) for CGA, and 16.257 MHz for EGA. I've rolled all the various kludge bank selects (colour, char ROM etc.) into one port, and changed the port addressing such that the whole lot sits in four contiguous port addresses, then used a '688 to allow the card to sit anywhere in port space.

Addressing with STD bus is a bit harder. STD bus was developed when 64K was huge, like S100. The developers came up with a signal, MEMEX, that allows for expansion beyond 64K, but were pretty unimaginative as to how that's used. They envisaged it driven from a port bit on the CPU card to select a different 64K bank. The video card needs 4K of address space from F000-FFFF, and does a lot of bank switching in this spot to allow access to all the various memory in it's 32K RAM, so would work with that.

Alas more modern STD bus CPU cards generally have memory on board, and often large amounts, like 512K. They do their own bank switching on the card, and ignore MEMEX. After all, why would you need off-card memory when you've got 512K on the CPU card. This holds true until you want to build a video controller, you numpties. So the MEMEX line is problematic. I drive MEMEX either high or low (jumper selectable) when the video RAM is banked in to allow a memory card to deselect it's own RAM. I also allow MEMEX to be used as an input to my card (another jumper), and only enable the RAM if MEMEX is high in this case. If your card doesn't respect MEMEX than you'll need to make some changes before you can use this.

I knocked the board layout over in a couple of days of design frenzy. My productivity with Kicad always amazes me. It's such a wonderful PCB tool. Granted, much of the grunt-work was already done by copying and pasting from the Freebee design, but still.

It was a bit of a squeeze though. STD bus cards are 6.5" long and 4.5" wide, but once you respect the edge connector and card guide allowances, you've actually got about 6" by 4.2" to work with. I squeezed some 43 ICs into this space. Along the way I had to drop spacing between rows to 0.15", and revert to modern round pads. I also went with (sacre bleau!) surface mount decoupling caps (and resistors), so it's got a bit of a late eighties feel to it. So no rounded tracks either. I actually briefly toyed with six layers to get the thing to route, but eventually got it down to four. The difference in price from four to six layers is a factor of two, so it's sensible to stick to four if at all possible.

And schematics:

Design files are on my Google Drive.