All of the 3.3V parts for the new COLE-2 prototype have arrived, and I’ve begun the slow process of rewiring everything. I am hopeful that this rebuild will eliminate my stability problems from the previous version, and also possibly get up to a target speed of 6.25 MHz.
In addition to increased stability, this rebuild is allowing me to reconcile what I have built vs what I have drawn on the schematics. Up to this point the design in the schematics has been lagging the actual hardware, because I was making so many design changes. This time around I’m doing things right: schematic first, then build.
Before I started any wiring I experimented with various breadboard layouts and chip positioning. In the end I opted for six breadboards, oriented vertically and arranged left to right. This, plus some careful chip placement will make the wiring of the data and address lines much easier. And, with six full boards, I can now fit all of the planned features into this build and still have plenty of room left.
Here’s what the build looks like at the moment, with all of the power rails connected and about half of the data bus wiring done:
The six boards, from left to right, are:
The TinyFPA and VGA connection. I’ll be adding some 74LVC245 buffers here for the address & data bus connections soon.
The XC9572XL CPLD, which is replacing both of the GALs from the previous build.
The 65816 CPU (bottom), plus the 74LVC245 data bus buffer (top) and the 74LVC573 bank address latch (middle).
The first 512 KB static RAM (top) and VIA #1 (bottom)
The second 512 KB static RAM (top), VIA #2 (middle), and the ATmega328p (bottom; this may move)
The 512 KB flash ROM (top) and the 28L92 dual UART (bottom)
I don’t expect the rebuild to be done for at least a couple of weeks. I have learned that if I speed up the build I get impatient and end up doing sloppier work. So, I’m going to just do a little bit of wiring every day while I work on more interesting things, like the new VGA implementation. I think the end result will be much better off that way.
It’s summer, and a particularly brutal one here in Michigan. My home office is not air conditioned, so I’ve been working on my projects a lot less. I have not been totally idle however, and so I’ve decided to give a brief update.
“I know engineers; they love to change things!”
Dr. McCoy, Star Trek: The Motion Picture
Over the past few weeks I’ve grown very dissatisfied with the responsiveness of my current design. In fact, output to the VGA console is noticeably more sluggish than output to the serial port at 38.4k bps! This is largely due to the very slow interface between the CPU and the Propeller. I debated various solutions to this problem, and decided to re-implement the video and sound on a TinyFPGA.
You may recall that I’ve already dabbled with a custom VGA chip, and even had a partial prototype implemented on an Altera MAX V. The iCE40 chip on the TinyFPGA does not have a nearly as many I/O pins as the MAX V however. and so interfacing an external frame buffer is not feasible. What the chip does have, however, is 16 KB of dual-port block RAM directly on the chip. This simplifies the design considerably, at the expense of frame buffer size.
With only 16KB for the frame buffer, the graphics output will be less ambitious than my original goal. It will still have the 80×25 text mode, but graphics will be restricted to 160×120 in 16 colors. I may also add a 320×240 monochrome mode or a 320×200 4-color mode, depending on available logic resources (I do need some resources to implement sound at some point).
Finally, the new design will map the frame buffer directly into CPU memory, and run at full CPU bus speed. This will greatly improve the console’s performance, especially for scrolling.
The current prototype build is no longer as stable as it once was; the system will randomly reboot after an indeterminate amount of time. I have not spent much time trying to diagnose the issue, but I suspect some problem with noise on the reset line. Instead, I’ve decided to just tear the prototype down and rebuild it. The current build has had so much grafted onto it that the layout and wiring are an absolute mess, so this will be a great opportunity to clean things up and hopefully make the build more stable.
While I am rebuilding I will also be swapping out the GALs and one or two other pieces of discrete logic with a Lattice XC9572XL CPLD. This part comes in 44-pin QFP, which is still relatively easy to hand solder, and can also be easily adapted to a breadboard build. It’s also programmable from Linux with a Bus Pirate and a bit of work, so I can program the chip from the same machine where I do my other development work.
Since the XC9572XL is a 3.3V part I will need to drop the system voltage from 5V to 3.3V. This is actually going to make life easier, as I’ll no longer need logic level conversion to talk to the FPGA. It will still get me a maximum of 8 MHz, which matches up well with the speeds of my RAMs and flash ROM anyway.
Speaking of flash ROM, the one part of my build that is not 3.3V-compatible is the EEPROM. As it turns out, 3.3V parallel EEPROMs are not very plentiful, and don’t come in speeds faster than 200ns. So, for the new build, I’m going to switch to a flash ROM, specifically the SST39LF040. This will give the new build a whopping 512 KB of ROM operating at 70ns, which will be good to at least 7.1 MHz, and possibly the full 8 MHz.
The CPLD and flash ROM should be arriving later this week, so I am hoping to do some if not all of the rebuild over the weekend. Once the new build is done I’ll post another update with some pictures.
Now that the hardware is mostly finalized time to talk about the software stack. My design is modeled after CP/M (and by extension, MS-DOS), in which there is a ROM BIOS that provides a standardized view of the hardware. On top of that is the actual operating system, which uses the BIOS to access the hardware and provides more complex services like working with files and launching applications.
The “official” OS for this project will of course be a version of JR/OS from COLE-1. My hope is to make JR/OS easily portable to future boards (yes, COLE-3 is already in my head!) and possibly other peoples’ boards, such as the Neon816 or the Commander X16.
With that out of the way let’s start at the bottom of the stack and talk a bit about the BIOS.
The BIOS serves three main functions:
Initializing the hardware at power-on or reset
Providing standardized APIs for accessing the hardware.
Booting the operating system. This may consist of passing control to a ROM-resident copy of the operating system, or booting from external media such as an SD card.
The current incarnation of the COLE-2 ROM does not contain any OS code, nor does it implement booting. In fact, there’s not even any mass storage drivers yet.
The BIOS API
The exact list of services that will be provided by the BIOS is still a moving target, but it will for sure provide the following:
Attaching the console to an I/O device. COLE-2 supports both VGA and serial consoles
Reading/writing a byte to/from the current console device
Reading/writing a byte to/from a character device (such as a serial port)
Reading/writing a block to/from a block device (such as an SD card)
Making device-specific calls, in the manner of Unix’s ioctl() function. Common operations such as “set baud rate” will be standardized.
Enumeration of available device drivers
Registration of new device drivers
Making API Calls
All BIOS services are accessed via the 65816 COP instruction. The COP signature byte selects the API method to call; parameters are passed in the accumulator or on the stack. For example, here is how you would print the letter “A” to the console:
COP $02 ; SYS_CONSOLE_WRITE
All registers and CPU state are preserved, except the accumulator and the carry bit; they are used to pass data and error state back to the caller. System calls must be made in native mode but the registers can be in any combination of 8- or 16-bit mode.
The BIOS uses a dispatch table to transfer control to the selected API method after performing some house keeping. First, it saves all CPU registers onto the stack. Next, it sets the data bank register to the system bank (currently $00), and re-enables interrupts if they were enabled when COP was called (since COP disables them). It then sets the processor to 8-bit mode, sets the direct page to point to the bottom of the system call stack frame, and calls the API method using a JSL. Upon return from the API method, the BIOS will clean up any stack parameters, restore registers, and return control to the caller.
For those who never followed the brief history of COLE-1, JR/OS stands for Josh’s Retro Operating System. The slash in the name is an homage to GS/OS, the operating system of my teenage years.
Due to the short life of the COLE-1 project very little actual JR/OS code exists. What does exist though are some design goals:
FAT32 as its native file system, to make it easy to transfer files with other systems.
Support any block device that can provide 512-byte blocks
Boot from ROM or external storage
API provided via COP (BIOS will reserve signature bye values for OS use)
Initially single-process but with multi-process, protected memory support in mind for future versions
System memory management
Command shell for launching applications
I expect this to evolve heavily once I get into writing the actual JR/OS code.
The initial version of JR/OS will operate much like CP/M or MS-DOS in that only one application process will be running at any given time. However, even this first version will lay the groundwork for a future multi-process version. Applications will be expected to behave as if they are not the sole running program, and make use of OS services such as memory management to behave properly.
Applications will consist of one or more module files, each of which must fit within a single 64K bank. These module files will be built to run at address $0000 and will be loaded into the beginning of a free bank. Future JR/OS versions may relax this restriction by supporting relocatable object files.
For multi-module applications JR/OS will provide a module loader that loads a file and returns its load address. A given module will only be loaded once; further requests for the same file will return the address of the existing copy. This will be the basis of shared library support in a future JR/OS version.
Right now I’m busy working on getting the BIOS to something resembling its final state. Basic console and serial port support are already implemented; the next step is to get the SPI and SD card drivers ported over from COLE-1. At that point I’ll be able to get to work on JR/OS itself, probably in parallel with the first actual JR/OS application: the BASIC interpreter.
As promised I got the new, simplified keyboard interface working this weekend. This means that COLE-2 can now operate as a fully standalone computer; no serial console needed. Of course, it’s still not terribly useful, because the only thing in ROM is the system monitor, but it’s a good start!
Overall I am very happy with the new design. It’s more or less what I described in my last post, with one small exception. In the new version, pressing one of the toggle keys (CapsLock, NumLock, and ScrollLock) is still handled inside the AVR. When a key down on one of those keys is detected, it will toggle the LED state, and then send the BIOS either a key down or key up code, depending on the new state of the LED. In other words, as far as the BIOS is concerned, the toggle keys are just regular modifier keys, albeit ones with longer-than-normal key down times.
Mouse and game pad support are not yet implemented, though the new firmware is driving both PS/2 ports now. These features, and some improvements to support hot plug and device detection, are on my list for future development.
With the keyboard finally working I’m going to bite the bullet and start cleaning up the BIOS. Specifically, I need to nail down a formal API and drive model so that I can start working on the OS and the BASIC interpreter application. We’ll see how that goes. 🙂
Well it took about two weeks longer than I had planned but I finally have a PS/2 keyboard implementation!
My design places the bulk of the processing on the AVR itself; it decodes the variable-length PS/2 scan codes and translates them into single-byte key codes. The AVR sends all key-down events to the BIOS along with the modifier key state at the time. The key codes it sends are a super set of ASCII; codes with the high bit set are special keys that don’t have an ASCII equivalent (such as the Fn keys). It also maintains the keyboard LEDs when the user presses one of the toggle keys (Caps Lock, Scroll Lock, and Number Lock).
The Bad News
Unfortunately, my design is not very reliable.
The first problem is that I see odd garbage key presses occasionally. The AVR PS/2 receive code does not yet check the parity bit, so it’s quite possible these key presses are due to transmission glitches.
The bigger problem, however, are the hard freezes. After enough typing on the keyboard the system will eventually freeze until I reset the AVR. When this happens the AVR is still working; pressing any of the toggle keys such as Caps Lock will properly update the LEDs. That means the AVR is successfully receiving and decoding keys, and is able to send commands to the keyboard. The breakdown appears to be in the communication link to the rest of the system.
Back to the Drawing Board
My current implementation was an interesting experiment, but I feel like it may just be too fragile to ever be fully reliable. I need something simpler, even if it means making some sacrifices. So I’ve come up with a new design.
My new design is focusing on the keyboard, mouse, and game pads (yes, I’m moving those back to the AVR). Here’s a rough diagram:
The data link to the VIA has been reduced to a unidirectional link driven by a 74HC595 shift register. This frees up six pins and allows me to move the PS/2 clock lines onto the INT0 and INT1 lines (PD2 and PD3). These two lines give me more reliable detection of clock signal transitions. It also means there are enough free lines to move the game pad support back onto the AVR.
In this updated design the AVR will still decode the PS/2 scan codes into key codes, but it will no longer act on any of them; instead they will just be fed directly to the BIOS for further processing. Mouse data will come to the BIOS preceded by a special prefix byte, and the AVR will continuously scan the game pads and present those as key up/down codes as well.
BIOS to AVR Communication
If you’ve been paying attention you may be wondering what happens to the keyboard LEDs now, since all of the key state processing has moved to the BIOS and there’s no reverse comm channel in my diagram. I have two ideas for this; one is to use the VIA’s shift register, and the other is to use the second channel on the system UART.
Using the VIA shift register would give me a moderate-speed channel back to the AVR, over which I could then send commands to either the keyboard or mouse. I have no other plans for the shift register so this could make good use of some otherwise idle hardware. The draw back, however, is that it does not support any sort of framing or error checking so even one missed bit would throw the whole thing permanently of out sync. Since I’d be implementing the AVR side entirely in software I cannot rule out missing a bit here and there.
The UART channel is my preferred choice at the moment. I would be giving up the second serial port, but I would end up with a much more robust link. The framing bits would keep the link in sync, and the AVR has a hardware UART built-in so I wouldn’t even need to implement anything in software other than a handler for the data.
In all of this talk about the new design I’ve neglected to mention the hardware SPI functionality. I was fully prepared to sacrifice this feature in the name of simplification and reliability, but when I started thinking about hooking the AVR up to the UART I realized i can still have my SPI. Indeed this may actually be a better solution than my original design; I already have fully interrupt-driven serial support in the BIOS, and so long SD card block reads and writes can be streamed to and from the AVR entirely in the background. It’s sort of like a poor man’s DMA.
I’ve already implemented most of the hardware changes for this design (everything is done except the serial link). I’m also 75% of the way done with the AVR firmware changes. By this weekend I should be able to type on the keyboard again, hopefully with greater reliability than before.
After several frustrating days and a lot of captures from my logic analyzer I am happy to report that I finally have the AVR integrated with the system, along with the basics of PS/2 keyboard support!
I did have to make one small sacrifice, which is to move my as-yet-unimplemented game pad support from the AVR to the VIA. This was done to free up enough pins on the AVR to let me go back to an external 16 MHz resonator, as running on the internal 8 MHz clock was proving to be a bit too slow to keep up. Game pads don’t support interrupts anyway so this is not going to be an issue at all.
How it works
To begin let me explain the actual hardware connection between the VIA and the AVR (an ATmega 328p):
The connection is a bidirectional parallel bus between port A of VIA #1 and port D of the AVR. Data transfer handshaking is handled by the VIA’s built-in handshaking support., which uses the CA1 and CA2 lines to act as Data Ready and Data Acknowledge signals. When sending, the VIA generates Data Ready pulses on CA2; the AVR acknowledges the bytes by sending Data Acknowledge pulses on CA1. Receive mode works the same way except the meaning of the CA1 and CA2 lines is flipped; note that this means CA1 is always the input and CA2 the output.
Since the VIA and AVR are sharing a bidirectional data bus it is important to make sure only one device is trying to drive the bus at any given moment. This is controlled by the VIA using the DDIR line; it is high when the VIA is driving the bus and low otherwise.
Every transaction starts with a data packet from the VIA, followed by a response packet from the AVR. The packet format is very simple and consists of a packet type, payload length, and the payload:
Payload size, low byte first
+3 – +514
The payload, when present, can be up to 512 bytes in length; this number was chosen to allow a single SD card block to fit in a packet. The payload length can even be zero, in which case the entire packet is just three bytes.
A transaction starts when the VIA drives DDIR high; this signals that it’s about to start transmitting a packet. The VIA then transmits the packet in its entirety before pulling DDIR low again and going into receive mode. It then waits for the AVR to transmit the reply packet, after which both sides fall back to their idle state.
The AVR never sends data unless it is replying to a request from the VIA. It can, however, generate an interrupt by driving the CB1 input to the VIA low. A GET_STATUS request can then be sent to the AVR to determine the source of the interrupt.
The AVR Implementation
On the AVR side all of this is implemented as a state machine, driven almost entirely by pin change interrupts:
The only part that is not interrupt-driven is the transition from the RECEIVED state to the SENDING state. The actual packet processing is done in the AVR’s main loop, and the act of queuing the reply is what triggers that state transition.
There is one small difference between the diagram above and the actual implementation, and that is the handling of DDIR going high. In the code as implemented a DDIR low-high transition will ALWAYS transition to the RECEIVING state, regardless of the current state. I did this so that it is possible to recover easily from a botched transaction without needing to reset the AVR.
PS/2 Keyboard Support
With the VIA and AVR finally talking reliably I moved on to implementing the PS/2 keyboard driver. This was much easier and took me maybe half a day.
Like the MPU interface, the PS/2 driver is interrupt-driven, this time by the PS/2 clock signal, which is always generated by the device. I won’t take the time to diagram it here since there are quite a few examples on the net of how to do this, and my implementation is nothing special.
As it stands now I am able to send and receive bytes with a PS/2 keyboard; this means I can receive key up/down events as well as control the LEDs, configure the typematic key repeat, and other fun things. I need to implement two more pieces to make this a complete solution however:
Implement interrupts when the AVR has buffered data from the keyboard
Decoding of key scan codes into actual ASCII that the rest of the computer expects. I am leaning towards doing this on the ‘816 side.
These two tasks are my focus for at least the beginning of the week. I’ll post another status as soon as I’m able to type to the computer without using the serial port!
Progress for the past few days has been slower than I’d like, but hey, progress is progress right?
I’ve finally ironed out the remaining bugs in the hardware interface with the propeller. Previously I had thought things were stable, but when I attempted to read back the frame buffer in my scroll code I would get corrupted data, frequent crashes, and hard lockups. I spent two long nights trying to figure out what was wrong with my code; turns out it was a couple of hardware issue…
Bus Buffer Timing
The first problem was with the propeller’s data bus buffer, a 74LVC245 used for converting the 5V signals from the rest of the system down to 3.3V. When the propeller isn’t being accessed this buffer was being held in a high-Z state by a signal called /PROPBE generated by one of my GALs. I suspect that this signal, which was generated from the output of a second GAL, introduced enough extra lag that the buffer was not shutting off fast enough. Putting that signal back under direct propeller control solved the random crashes and data corruption, but not the lockups.
The second problem was with the /STP signal used for clock stretching. This signal had been giving me problems since day one due to voltage level differences. The obvious solution of using an ACT series part for the flip-flop did NOT work; despite the data sheet claiming the 74ACT74 is TTL-level compatible it would not recognize 3.3V on the /PRE input. So, I had the signal hooked up with a pull-up to 5V and was relying on the propeller to pull it low. As it turns out, this only sort of worked, and it was enough to cause the CPU clock to randomly start and stop at weird times, violating the timing specs and causing random lockups. I finally gave up and just tossed a 74HCT04 inverter in front of that signal, since I had a few on-hand, and voila! No more lockups.
The system is now rock-solid as far as I’ve been able to tell. I’ve had it running for more than 48 hours with zero issues. I may even swap out the crystal and see if I can push it up to 4 MHz; previously this made the system horrendously unstable, but with the /STP issue fixed it may finally work.
With the hardware issues sorted out I was finally able to get a basic console driver working, including scrolling. To celebrate I prettied up the boot screen:
The console code is still in that “hacky but at least it works” state. Some of this is due to the fact that I have been dragging my feet on implementing a real driver architecture in my BIOS. Fixing that is high on my priority list for ROM enhancements.
Another thing the console driver is sorely lacking is a robust set of control codes. I briefly considered implementing VT-100 for compatibility with the old serial console, but VT-100 is pretty complex and way overkill for my needs (and it would require a decimal parser in the console driver…yuck). Instead I’ve decided to go more Apple II-style using control characters. Right now the only thing implemented is Ctrl-A + byte to change the text attribute byte.
Not much to report here. I would like to code up a boot sound soon, but I have zero experience with the SID. I am hoping to find some code I can borrow for that.
Absolutely no progress here. I did briefly work on hooking up the PS/2 connection directly through the VIA, but abandoned it; I just didn’t want the VIA doing that much polling during interrupts. So, I’m back to my original plan of using an ATmega to handle the user input devices.
After much internal debate I think I’m going to offload SPI onto the ATmega instead of the propeller. The driving force here is that my current propeller interface just doesn’t really provide for a clean way to implement SPI. The shortage of I/O pins contributed as well.
The new plan is to designate the ATmega as the I/O coprocessor; it will handle SPI in addition to the keyboard, mouse, and game controllers. I have not yet decided HOW to do this, as this means the ATmega<->VIA interface will need to be bidirectional. I do have some ideas though.
This week’s number one priority is to get the ATmega interface up and running. Once that’s done I will implement the keyboard code and then finally be free of the serial console for now.
Time permitting I’d also like to finally start tackling the mess that is the BIOS. In fact I may make another post this week just talking about the BIOS design, as I’m finding that these posts help me think through my issues.
I said I’d post an update (with a picture) when I got the VGA console working, so here it is:
It’s very preliminary; the screen does not scroll yet, and input is still coming from the serial port (there’s no keyboard port yet), but it does work.
One thing that is rather apparent in the above screen shot is that the weird issues with some of the punctuation symbols. I’ll dig into that after I get scrolling working.
The image above was taken using my updated hardware design that I talked about in my last post, which uses clock stretching in lieu of the 65816 RDY pin to pause the CPU during propeller accesses. The previous design rarely lasted more than a few minutes without random serial port garbage, and always crashed after a few hours at most. This new design has so far gone nearly 48 hours glitch-free, and probably would have gone longer had I not powered it down for a ROM update.
With basic video output working my next task is to make the console scrolling work so that it’s truly usable. After that I’ll be getting the keyboard port implemented, which I’m still planning to do using a small ATmega ‘328. It’s going to connect through the VIA so it should be a lot less work than it was getting the propeller implemented.
As I mentioned in my previous post I am using a Parallax Propeller in my COLE-2 SBC project. The propeller is a neat little chip. I won’t go into a whole lot of details about it here, since that is well-covered elsewhere, but the basics are that it’s an 8-core (or “cog”) processor running at 80 MHz, and is very hobbyist-friendly. It’s a bit of an odd duck from a programming point of view, but once you get used to it you can do some amazing things with it. There are literally hundreds of open source modules available implementing all sorts of software-designed peripherals so you can do a lot while writing little to no code of you own, if you so choose.
What initially drew me to the propeller is its ability to generate video signals, both composite (NTSC or PAL) and analog VGA. It’s able to do this thanks to some custom hardware included in each cog that facilitates the generation of the proper timing and the shifting out of pixel data based on that timing. You can do this with as little as one cog, although multiple cogs working together will allow you to get higher resolutions and/or better color depth. The only limitations are that is is limited to 6-bit RRGGBB color, and there is only 32 KB of shared RAM (called “hub RAM”) available for holding your frame buffer and other shared data.
Once you have the propeller integrated into your design, however, you might as well put all eight cogs to use; the only cost incurred is adding the extra driver code and maybe some I/O pins (more on that later). With that in mind I decided to see how much I could pack into that single chip.
The Bus Interface
To start with I need a way to interface the propeller to the rest of the system. Many projects just do this over a serial link; the chip can bit-bang serial at modest bit rates, and I could easily have attached it to the second port of the UART. But serial can be a real bottleneck for graphics modes, so I wanted some sort of parallel interface. In an ideal world I would let the chip pretend to be a 32 KB RAM chip, but this would eat up every single available I/O pin and would probably be overkill for the modest graphics modes the chip is capable of producing. So, I decided to borrow from the 1970s-era TMS9918, which did all configuration and VRAM access through just two addressable registers. This means I only need 1 address input instead of 15.
To implement this I connected the propeller to 14 signals bus: D0-D7, Φ2, /IOSEL2 (from my address decoder GAL), RWB, RDY, and /IRQ. The RDY line would be held low to halt the CPU while the propeller responds to a bus request, since it’s not fast enough to keep up with the CPU at full speed. All of these signals would be fed through a pair of 74LVC245 buffers, because the propeller is a 3.3V part but the rest of my system is 5V. So far so good, or so I thought…
RDY and Waiting
As it turns out using RDY with the 65816 is not quite as straightforward as I had hoped, due to the way it multiplexes the bank address onto the data bus. The bank address is emitted during the first half of the CPU cycle, when Φ2 is low. The data bus is connected to a 74ACT573 latch, which is kept open (transparent) during Φ2 low, but which closes and captures the bank address when Φ2 goes high.
Normally this setup works fine, and in fact it’s the exact design recommended by WDC. The problem comes in when you start trying to use RDY. When RDY is pulled low, the CPU halts as soon as Φ2 transitions from high to low. The actual Φ2 clock, however, does not stop. If RDY is kept low long enough for Φ2 to go high again, the bank address latch will capture some random data bus data as the bank address, and when the CPU finally resumes it will likely access the wrong memory address.
For my test implementation I solved this by using some extra lines on a GAL to construct a latch enable signal that such that the latch remains closed as long as RDY is low. Unfortunately this seems to have made the system slightly unstable, even when the propeller is not being accessed (during which times it’s not even on the bus, as its data bus buffer is disabled until /IOSEL2 goes low while Φ2 is high.) My GALs are fast (7 ns parts), but it’s possible the extra delay is the causing the strange behavior.
For my next attempt I am going to try a different approach: halting the CPU’s Φ2 clock during the high phase using a circuit like this:
My current clock generator is the top half of that circuit, which means I already have the second half of the flip-flop available to use to add the bottom half; I just need to do the wiring. Once that’s done the propeller will pull /STP low instead of RDY, and my bank address latch will go back to being directly qualified by Φ2. I am hoping this will result in a stable system.
As a bonus I am hoping this new setup will solve another issue I have so far ignored; when first powering on, the propeller takes a few seconds to boot, during which it is not properly asserting RDY. This causes boots to randomly fail. With the new setup /STP will be low during this time so the CPU will not even try to boot until the propeller is up and running.
Writing the PASM code to implement the 65xx bus interface turned out to be much easier than I was expecting; I was able to hack out a working proof-of-concept implementation in a few hours. It isn’t even a lot of code; the basic code forming the main loop is just this:
mainloop waitpeq Pin_PHI2, Pin_CS_PHI2 'Wait for /CS to go low with PHI2 high
andn outa, Pin_RDY 'Pull RDY low
mov _in, ina 'Capture the input port
and _in, Pin_RS WZ,NR 'Check RS bit (0 = vram, 1 = registers)
and _in, Pin_RWB 'Mask RWB bit for later
if_e jmp #:vram
tjz _in, #write_register
:vram tjz _in, #write_vram
'' Common code for all ops; unhalts the CPU, waits for /CS to go high and then loops
or outa, Pin_RDY 'Unpause the CPU
waitpeq Pin_CS, Pin_CS 'Wait for /CS to go high again
andn dira, Pins_Data 'Set data bus pins to high-Z (input state)
jmp #mainloop 'Rinse and repeat
In the end I had a working setup in which reading the propeller on either I/O port would return a constantly incrementing byte, which I could also change by writing to either port. This allowed me to verify that the bus interface was working properly.
With the test code working I’ve started adding useful functionality. So far I’ve gotten the VRAM read and write working, and I’ve been able to successfully fill the screen with characters using assembly code running on the main CPU.
At the moment the video output is being driven by the “80×25 C0DF” driver from the waitvid.2048 repository. It generates 80×25 text using a 9×16 font; each character has an attribute byte associated with it that points to a 256-entry color palette. Each color entry in turn consists of a foreground/background color pair and a blink bit. The driver also supports two independent hardware cursors that can be a block or an underline, with or without blinking. The video buffer, color palette, and the font are all in hub RAM, so in theory they could all be made changeable by the main CPU.
I would like to offer the ability to switch to an alternate video driver (either a limited resolution bitmap, or perhaps a tiled driver with sprites), but as of yet I have not worked out how to accomplish this.
Since I have video the most natural choice for another thing to add is audio. As it turns out someone has written a Propeller module called SIDcog that emulates the C64 SID chip. It takes only a single cog to run, uses very little hub RAM (just a couple dozen bytes for registers) and only two I/O pins.
The SIDcog module is very simple to use; you tell it what pins to use for left/right audio, and it returns a pointer to a block of emulated SID registers in hub memory. Reading or writing those locations will affect the emulated SID just like it would a real one. So, in theory, once I’ve finished implementing the write_register function in my bus interface I will be able to play sound.
An SPI cog is the last piece I plan to add, since at that point I will be almost out of I/O pins. The propeller will handle the actual SPI transfers and signal the main CPU via interrupt once the transfer is complete. To allow reading or writing of entire SDcard blocks I plan to implement a small 512-byte buffer. I am not yet sure how this will be implemented at the bus interface side but I have some ideas.
At the moment I’m hard at work getting the bus interface fully implemented. My focus is on getting the video registers implemented enough that I can try redirecting the console output to VGA. Expect another post (with pictures) once that VGA boot screen is working!
NOTE: I’m going to experiment with turning on commenting, starting with this post. We’ll see how it goes.
The nice thing about creating things as a hobby is that you can switch to a new thing whenever you get bored or frustrated with the current thing. That is exactly what happened to me a few months ago: I started getting frustrated with the limited features I had available on COLE-1, so I decided to just jump ahead and start the next iteration. The result is this:
That ugly mess of wires is actually a functional 2 MHz 65816-based SBC, called, not surprisingly, COLE-2.
COLE-2 will be a substantial upgrade from its predecessor:
6 MHz 65C816 CPU (largely limited by EEPROM speeds)
1 MB of RAM
256 KB of ROM w/ full system monitor, OS, and 16-bit BASIC
ATmega328p for PS/2 keyboard/mouse and two NES-compatible game ports
Dual 65C22 Versatile Interface Adapters
SD card reader
A user port
A single expansion slot
When I started this project my goal was to produce another headless SBC similar to COLE-1, but with an expansion slot that would act as a platform for developing my custom CPLD-based video system. However it became apparent that I was trying to take too big of a leap there, and so I decided to add basic on-board video as an interim step.
My current prototype is scaled down from the final specs, since it’s implemented on solderless breadboards. It only runs at 2 MHz, and has only 512 KB of RAM, 32 KB of ROM, one VIA, and no ATmega. Much of this is just due to lack of space on the breadboard; as you can see I’m already spilling out onto additional boards, and all those long wire runs take their toll on stability, especially at higher speeds. My plan is to squeeze the ATmega in there somewhere to get a working keyboard port, and then move on to designing the PCB and building a full prototype.
In my next post I will dive into how I integrated the Propeller into my design.