• Please review our updated Terms and Rules here

Big80 - My attempt at implementing an FPGA TRS-80 Model 1

bradr

Member
Joined
Feb 7, 2020
Messages
15
Location
Sydney, Australia
Hey All,

I thought I'd share some info on a project I've been working on - it's yet another FPGA implementation of a TRS-80 that I started as a holiday hacking project over the Christmas break. Although the main goal of this project was to improve my FPGA development skills, it's turned our pretty well and I think worth sharing. My original target for this was to implement just enough of a TRS-80 Model 1 to be able to load games from cassette and play them - which it can do, but I'm now thinking about extending it.

Here it is in action with my son playing Defense Command using a Playstation controller that's emulating a TrisStick.


So far it implements a TRS80 Model 1 with Level II BASIC and 48k RAM. It's implemented on a Mimas V2 FPGA development board which is an almost perfect board for project's like this (I did a review of it here).

Video controller output is via standard VGA connector and supports green or amber screen, and optional scan line emulation (see here). Output is 800x600 with correct TRS-80 2:3 aspect ratio for pixels.

Keyboard input uses a PS2 keyboard via a PMod connector to the Mimas and can work in either topographical mode (where keys match the approximate position on the TRS-80 keyboard) or typing mode (where characters are remapped to make using a PC keyboard seem normal eg: Shift+2 gives an @ symbol unlike TRS-80).

It supports loading .cas files from an SD card and renders the raw bytes from the .cas file to an audio stream which is fed to the TRS-80's cassette input. Similarly for recording the FPGA hardware parses the generated audio stream and writes the raw bytes to the SD card in .cas file format. Currently the SD card needs to be specially formatted with a custom file-system where each file is numbered and the tape image is selected by using the buttons on the FPGA board and the selected tape is shown on the 7-segment display. This is less than convenient but I have plans to improve it (see below).

Since the CPU core and the cassette render/parsing logic is all driven from the same clock, I can overclock everything while loading and saving and get a "turbo tape mode" which reduces a 3+ minute load to about 8 seconds. This overclocking happens automatically when the cassette motor relay is turned on - so it's pretty seamless: the tape loads in turbo mode and then things revert to normal speed.

I've also implemented an auto-cassette mode the detects when cassette record or play operation starts (it tells the difference by monitoring port I/O activity) and automatically starts the virtual cassette recorder - so you don't need to manually start it yourself. See here for turbo mode, recording and auto start/stop demo.

That's what's currently implemented but I'm now working on some improvements. The biggest pain with it in it's current state is the need for a specially formatted SD card and it would be much easier if it could be FAT formatted as this would make copying .cas files to/from it much easier. Implementing FAT support in FPGA logic isn't feasible but there's a couple of options that I've considered:

1. Run the FAT/SD support on a separate microcontroller
2. Run a second soft-core processor on the FPGA and have it perform the FAT work.

I don't like the idea of a separate microcontroller because I'd prefer keep it all on the one board. A second processor is probably doable but I'd prefer not waste the space on the FPGA so instead I've decided to hijack the TRS-80's Z80 via NMI and switch to a separate address space and run all "system control" functions there. I've got a basic proof of concept for this working but still a lot of work to do.

This system control software (aka syscon) will also need a UI so I've also implemented a simple 32x16 character video overlay that will be used to select tapes and eventually disk images:

syscon-overlay.jpg

Anyway, it's been a fun project so far. If you're interested, source code and operating instructions are available here:

https://github.com/toptensoftware/big80

and more in-depth articles here:

https://www.toptensoftware.com/blog/tag/big80/

Interested in feedback :)

Brad
 
A second processor is probably doable but I'd prefer not waste the space on the FPGA so instead I've decided to hijack the TRS-80's Z80 via NMI and switch to a separate address space and run all "system control" functions there. I've got a basic proof of concept for this working but still a lot of work to do.

I'll be curious to see where you go with this idea. Just the other day I was thinking again about an idea for a crude modification to a minimal single-board CP/M computer (using a real Z-80, not an FPGA) to add some TRS-80 compatibility; the design in question uses a serial terminal so the rudimentary seed I came up with was to implement both the screen and the keyboard as areas in regular RAM and using a periodic NMI handler to poke keypresses received over serial into the memory-keyboard matrix and push screen updates back out.

Having just done the most minimal research it looks like making this work would require either patching the Level II ROM or, as you suggested, switching to an alternate address space. Out of the box the TRS-80 has NMI connected to the reset switch, so jumping to 0066h triggers a test to figure out if the machine has a disk controller present and behave differently depending on the result.

(Although, hrm, according to "ROM Routines Documented" by The Alternate Source with the Model III ROM the reset vector actually goes through RAM so in principle it could be hijacked without modifying the ROM code directly...)
 
the rudimentary seed I came up with was to implement both the screen and the keyboard as areas in regular RAM and using a periodic NMI handler to poke keypresses received over serial into the memory-keyboard matrix and push screen updates back out.

I think this could work for keyboard, but for video isn't it going to be too slow? 1K video ram over say 115200 baud serial will only get you about 11fps, but maybe that's all you need.

The other problem you might have is the address space switching for the NMI which I'm doing in (FPGA) hardware. It works like this:

1. When something that the syscon needs to know about happens (serial i/o, sd disk operation complete, keyboard input etc...) raise a "syscon" NMI.
2. When a syscon NMI is pending the hardware monitors the bus for a read at address 0x0066 with m1_n low. This detects that the Z80 is about to execute the NMI handler and is the point at which I page out the TRS-80 address space and page in the syscon - effectively a high bit flip on the address lines to external RAM.
3. The NMI runs
4. To exit and return from syscon to TRS80, the syscon software outputs to a special port that means "request to leave hijack mode". Once the hardware sees that, it monitors for a memory read that returns the op code for either "RETM" or "JP (HL)" and after that byte has been read, the TRS80 can be paged back in and the syscon out.

The trick with all that is to switch the address space at the right time - right when the NMI is about to execute and exactly after the last RETM instruction has executed. The JP(HL) check is for the initial boot where the whole thing starts in hijack mode, loads the TRS80 ROM from SD Card and then needs to switch out of hijack mode and jump to 0x0000 to boot the TRS80.

The other advantage I have with doing this in the FPGA is I can overclock the Z80 while in syscon hijack mode. With the Z80 overclocked to 40Mhz during syscon NMI there is practically no noticeable slowdown to the TRS80.

Anyway, my point is you're probably not going to mod an existing board to do all that.
 
I think this could work for keyboard, but for video isn't it going to be too slow? 1K video ram over say 115200 baud serial will only get you about 11fps, but maybe that's all you need.

Yeah, it definitely wouldn't work for playing video games or anything, it was mostly just a proof of concept idea. (I also wasn't thinking of anything as complicated as swapping address spaces, just hijacking the existing NMI handler. The Model I memory map has that 2k of empty space between the top of Level II and where I/O starts, there's room there for plopping some code.) A really lame idea I had for slightly accelerating things was to map a latch that would be triggered by any write between the 15k and 16k mark so the screen update code would *only* be run if the latch had been flipped since the last cycle, indicating that a screen update happened.
 
this is all pretty cool! good luck with the floppy implementation!

Thank you.

I've implemented a WD1773 FDC before for my Microbee FPGA project, but it just mapped sectors directly to flat images on the SD card so it didn't work with copy protected images. This time I'm going for something much more accurate so I'm doing deep dive on how the FDC works.

For anyone who's interested I've just put up a blog post about how all these recent (.cas from FAT SD card) changes wor including some details on hijacking the Z-80 to run the software for it. See here.
 
Back
Top