close-circle
Close

BIOS Update and Plans

A project log for Kestrel-2DX

The Kestrel-2DX, a specific embodiment of the Kestrel Computer Project (also on Hackaday), will help with new hardware bring-up.

Samuel A. Falvo II 6 days ago0 Comments

A quickie, since I'm getting ready for work as I type this.

Status Update

The Kestrel-2DX BIOS now has ASCII-capable keyboard functionality.  So, with character output and keyboard input now defined, I basically have a really dumb terminal at my disposal.

I need to wrap the functionality up into interfaces that are more convenient to use, however.  For example, console output requires that you manually turn the cursor off before printing or moving the cursor to a new location on the screen, then turn it back on again afterwards (worry not; cursor_off() and cursor_on() properly nest up to 65535 levels deep).  Likewise, keyboard input is handled using a kind of polling interface that would suit an evented software stack quite well, but is awkward for Unix-style interfaces.

So far, the software consumes 3688 bytes of ROM space, with another 3200 bytes consumed by PS/2-to-ASCII translation tables and the 8x8 fixed-width font, for a total space occupation of 6888 bytes as of this log post.

Future Plans/Ideas

Looking forward, I'm seriously thinking about migrating the design of the bootstrap firmware towards an event-driven architecture.  The code as written seems already well suited to this architecture.  In fact, in my dummy integration test code currently in ROM (which will undeniably change by the time you read this, as progress continues), there is an event loop which illustrates how to properly poll for keyboard input:

static void
ps2_playground(void) {
	uint16_t rawkey, code;
	int valid;
	char ascii;
	int shifted = 0, ctrled = 0;
	for(;;) {
		kia_get_raw(
			(uint16_t *)(&rawkey),
			(int *)(&valid)
		);
		if(valid) {
			code = rawkey & 0x7FFF;
			if((code == RAWKEY_CTRL_L) || (code == RAWKEY_CTRL_R)) {
				ctrled = (rawkey & RAWKEYF_RELEASE) == 0;
			}
			if((code == RAWKEY_SHIFT_L) || (code == RAWKEY_SHIFT_R)) {
				shifted = (rawkey & RAWKEYF_RELEASE) == 0;
			}
			kia_raw_to_ascii(rawkey, shifted, ctrled, &ascii, &valid);
			if(valid) {
				cursor_off();
				con_write_char(ascii);
				cursor_on();
			}
		}
	}
}

I have written keyboard code many times over the years (Kestrel-2 emulator, Kestrel-2 FPGA, Kestrel-3 emulator, etc.), and none have been so compact as this, especially considering how trivial it is to maintain shift state (required for proper ASCII conversion).  Funny to think that this code is not designed; it's not even unit tested.  It just fell out of the design naturally, and "just worked."

So, for the purposes of writing the first interactive shell for the Kestrel-2DX, I think the only things I need to work on is:

The another nice advantage of building a BIOS in an event-driven manner like this, which I believe I touched upon in an earlier blog post on the main Kestrel Computer Project page, is that it completely isolates the event handlers from whether or not I choose to use interrupts.  Currently, despite the CPU supporting them, I make no use of interrupts.  But, if/when I add timer support, interrupts will need to be supported.  Further, longer term, when I support SD card I/O, initial implementations will not use DMA; but I do intend on using DMA eventually, which again requires interrupts.  The programming interface should remain invariant under these changing conditions.

I know from experience that making the KIA interrupt-driven fundamentally alters how to write a keyboard driver and, if you insist on a Unix-style synchronous I/O model, not in a pleasant way.  You end up needing all manner of additional impedance matching constructs like FIFOs, and you need to worry about race conditions between interrupt handlers and FIFO consumers, etc.  Although not technically multi-threaded, you suddenly need to think in multi-threaded terms.  From there, it's not much of a leap to implementing a multitasking OS of some kind, which I feel I'm not quite ready for.  I can write one (and I have written many some decades ago), but with only 32KB of usable RAM, the pay-off for doing so just isn't there.

It'd be nice to isolate programs from significant changes; going with an evented architecture not only eliminates all that cruft, but in addition, it results in substantially smaller programs.  Just look at what was done with the Commodore 64/128 version of GEOS, to cite one real-world example.  The Contiki OS is another great example. 

In my case, 6888 bytes of 64-bit, non-compressed, RISC-V code compiled from C to implement 95% of a dumb terminal using a bitmapped display and a PS/2 keyboard for input is not a small achievement.  But, I will claim that, as I add required features to the BIOS image, its size will not grow substantially, thanks to its evented architecture.  I suspect it will culminate in a distinctly GEOS-like environment when I'm done.

Discussions