Zikzak - crummiest 8bit console/computer ever .. but I'm making it!


<damn, my keyboard 'n' key is unreliable; partdon mistyping more than usual below :)>

The below is useless, but if it amuses you, read on; thats why you're here, right?

Fuuuuuuu', okay, got my ps2 keyboard code ported to the zikzak eZ80 side. goddamn .. that was one of those weeks, where you stare at the same code and piece of hardware for a long, long time, and .. theres just enough clues in your testing and trials, to make you think its one of a dozen things.. but its not. (And with some bizarre behaviour, it started making me think.. bad solder joint? bad docs? buggy compiler optimizer munching my code? etc..).

But finally, finally got it.

Its really hard to debug interrupt handling code imho; I mean -- its _fast_ (the clock here is 10khz signal coming in), and you can't do much .. you can't use serial to print out anything (its too fast), and even logging to a circular buffer in RAM (think print statements, but into RAM, and into a buffer of say the last 100 messages or something) .. but that gets annoying fast; you want to break in and see the messages, but again.. you can't touch the system mid-stream to inmspect what happened, and adding a rediculous amoutn of logging is just annoying and disruptive. So in the end, the debug technique I came up with was to just add a bunch of output signals .. I've got a 4 channel scope after all, so watch the PS/2 keyboard CLK and DATA signals coming in, and watch my signals going out. ie: In my interupt handler .. put a output toggle as first thing, so that every time my interupt handler got hit, my output signal woudl toggle.. high/low/high/low; throw that on the scope and fid out all sorts of weirdness.. it toggling super fast, all the time; or after a reset, but not after keyboard was struck, etc. Using various kinds of interupts.. are they beig reset right? is the cpu just fooked? is my code fooked?

Took me way longer than it shoudl have, but once I got good debugging output signals into my head and onto the scope, it all came together pretty well. The other debug techniques just were't cutting it, or were themselves interfering.

In the end .. with some interupt types (such as rising or falling edge) you have to reset them (so the cpu knows it can interupt you again, pretty much.) For others, you don't need to reset them. Anyway, I was changing typesd willy nilly, so had to generalize my code.. pick which gpio's to use, whifh interupt type, what reset modes, all that.. since it was driving me nuts, make it easy to change, rule out solder joints etc by moving things around. Man. Anyway, with the eZ80 and falling edge interupt, you have to reset it by writing to the data port, and specify the edge type (falling or rising) with the data port. (Stupid, no?) .. i their C wrappers for setting up interupts (you can just tweak registers, but they offer convenience functions, that I shall now call INconvenience functions.) .. So you call the inconvenience function to set a falling edge interupt - and I need to dig out the code it does, cause it sucks - so that things are right. You have to reset the register to 1 (either before or after).. so its best to do this:

- set to 1 (reset the port)

- set to falling edge <- doesn't actually do anything? sets up most of the registers, but not actually the falling/rising edge one

- set to 0 (to signal you want falling edge)

Then in your interupt handler, you have to..

- set to 1 (reset)

- set back to 0 (to signal falling edge)

This isn't spelt out in the specs, and it varies by which chip you are using I'm using the F93 variation; the F91 is different, for instance.)

One would think settig to falling edge, would in fact, set it to falling edge. But it doesn't.

Note that this is the data register, which is the one that you can read to find out what incoming signals are at, etc. Note that if your ps/2 keyboard changes the signal, and causes a falling edge interupt, you _must_ set it to falling edge again,since the keyboard is pulling it up and down; when keyboard pulls it up, fine; but in your interupt handler, on its way out if you don't reset it to 1 and then 0, then the cpu is now using the value supplied by keyboard as the rising or falling edge indicator, say. ie: so an outside object is now setting your iterupt handler type? I dunno. Seems to be the case anyway.. or it is at least flaky.

This all leads me to.. I didn't quite grok that se tto 1, then set to 0, .. I didn't relize I had to set it to 0 every stinking time, because stupid-Zilog. Also, I had a bug.. I had a 'return' in just the wrong place, in a really devious locatio that seemed fully right, but in fact was causing it to jump back before forcig the falling edge interupt to 0 .. so in some odd cases, it was leaving it at whatever it was coming in (sometimes risig edge, a 1 value).

Therein was my madness.. the docs were not clear, and it varies by chip and interupt mode and cpu mode, and I also had a code bug.

Anyway, so now I have a working interupt based ps2 keyboard handler test code for zikzak.

I suppose I should make another lame little video, so you can see me typing on a keyboard and having it show up on the screen in glorious 8bit; maybe I should make a dmeo that emits a soud tone based on the key hit, and changes screen colour, or something; that'd be very Commodore 64 of me :)

jeff
 
In case anyone is curious.. the first thing I did was make a key-logger for the PS/2 keyboard protocol; ie: tap away, and show the 'scan code' (raw value) and the char code (display value) to serial, so that a keyboard map could be found. (You can google one up of course, but I wanted to know the protocol so needed to find the protocol packets as well.)

Turns out ps/2 keyboard is the simplest protocol around; here it is (keyboard->PC side anyway) i its entirety:

// PS/2 keyboard protocol
//
// If a normal key is PRESSED, its scancode comes across the bus
// If a normal key is RELEASED, we receive dec 240 (0xF0) and then the scancode.
// For a repeat, the PRESS event, then more PRESS events, and then the release event.
//
// Therefore, typical normal key lifecycle is:
//  ...... scancode ...... 240 scancode ......     <- 3 character packets
//         < press>        < release  >
//
// Special keys:
// dec 224 (0xE0) comes first, to escape the next value as a special key
// Special press: 224 scancode
// Special release: 224 240 scancode
//
// Retarded keys:
// Print screen and Pause do a bunch of crazy stuff

The handler for this is about ... 30 lines of C code :)

The second thing I did was make the piano and fart music generator; so it wil walk up the stairway of notes (A, A sharp, B, ...), and if you hit a key on the keyboard, it'll play that ote if its one (A, B, C, D...), or a farty effect noise ('laser shot' style warble), if not a normal noise.

Then it was bed time...

... 8 bit hacking your own OS; its fun. I know its dorky as all hell, but its fun :)

jeff
 
Ah, the fun of outrunning the docs.  I've a python project currently where one of my test functions causes the python interpreter to segfault four out of five times.  I've tried binary searching for a problematic line, and I thought I had it down to two pages of code that would regularly segfault, but I rebooted and then it stopped crashing :(

I guess I'll have to dive into the python C code and debug that, but that's a lot of learning to learn my way around that codebase.  It's only a unit test that does some things that real users should never need to do, but it's not that peculiar, and I know that somewhere down the line I'm likely to hit the same use case accidentally, and bring the house down.  But I spent months first staring at the code and the not staring and the code because I couldn't see anything else to trying, so I commented out the test.

Actually writing this has made me download gdb and try running my code through it.  Unfortunately, it never seems to crash when running under the debugger, so I guess it's some sort of race condition.  Maybe I could just code a pause into my test though - that's not ideal as I'm aiming for all my unit tests to run quicky for rapid iterability, but that suite currently runs in 1.5s on my PC, and my line in the sand is 5s, so I've got time to play with.
 
I wonder if there are any build options for CPython, to include some extra debugging options? Or maybe link in somethign like efence that helps force faults on buffer under/over-runs?

Yeah, tracking down stuff like that, especially a meta level (not in your code, but in the interp itself) is a right bitch :/

I'm stoked right now, from recent successes; will retest my VGA colours to make sure thats working, and I think all the base tests will have been covered.. then time to roll it all into a 'bios style' built in test menu so any given test can be brought up on need... and then start thinking about in-field reflashing and all that.

Hacking.. its in the blood, right? :)

jeff
 
Yeah, I could rebuild python with extra symbols, but I'm not sure that will help if it doesn't fail in the expected way even using the smaller non-debug executable.  But curiously, when run via gdb despite not containing any debug symbols, python spits out thread start and end lines for all the threads I spawn.

This whole project just started out because I couldn't get GTK or KDE running at all well on my P3 server, but I wanted to rip my CDs using it because it's got the most reliable CD drive installed, so I started writing a cli-based CD ripper.  To make it faster, I paralellised the IO-bound rip operation and the CPU-bound encode operation, then I thought I may as well break out the multiprocessing functionality into a reusable library.  Since then I've just been writing test code, and documenting the library, which has made me think of a number of ways I could make it easier to use, so hacking in new features at the same time.

The latest idea was to make sending and reciving IPC messages need less boilerplate code, by automatically spawning one thread to check for messages, and one to run your code.  Consequently, my unit tests now spawn a lot of short-lived threads.  It was all working fine though until I realised there was a danger of race conditions between those threads, and any shared data the user's message handling code and mainline code touches, so I added some threading.lock guards around stuff and let users get their own lock if the need it.  But my unit test that gets one from my class and not only checks it's the right thing, but tests that it actually does stop guarded code running at the same time works fine if run in isolation, but seems to cause a segfault in a later test if run as part of the suite.

I guess I didn't actually need to do any of this to achieve my originally stated goal, but each step seemed a good idea at the time.  Gotta keep hacking!
 
I suppose another thing to add to my todo list for the main pcb .. (besides apparently squishing even more into the already cramped space, or increasing the pcb size again..) -- add both a mixer and amplifier for audio.

Currently (zikzak sbc rev3) just as the 'line out' (for each of the 3 audio channels) as pinheaders.. no mixing, no amp; you can actually just jumper over to a headphone (wrap the wires around the jack say) and it'll work, but really you shouldn't do that as typical speakers are _very_ low resistance (essentially a short nearly) so create a huge power draw. Turns out my headphones are 35ohm (not much, but enough..), and my speakers are 5K (self amplifying), so don't fry the pcb, but not good for it. I'd intended to add an external amp circuit, but for 'finalizing' the board, should build it right in.

A mixing circuit should be easy.. really just another resistor based DAC, nice and simple.

Amp .. I've got some LM386 amps which should do the trick.. never done that yet before, so I'll breadboard it up, and see how it performs, and if good.. will schematic that up and add it to the next PCB revision.

Lots to do..
 
Protip for the LM386 amps: They really need the 12 volt that they are capable of handling. Don't even bother trying to run them on 5/6 volts, they'll start distorting  terribly by the time you got something audible from the speaker.
 
Really? I was planning on a 5V feed to it.. Hmm

i do have a straight up Vcc line from power carried around so I could route that through, and require Vcc then to be 12V brick say.. But I do most development by just powering from 5 and 3.3 (from usb and down).. 

I'll experiment and see; will put a breadboard together to test over the next couple days :)  (halloween, no free time tonight!)
 
Here is a basic palette test; I think I need to fiddle with resistor values or diode values on the brightness DACs.. The voltage blend seems off but no chance yet to throw that on the scope this build.

image.jpeg
 
okay, I put together a very quick test of hardware 'channel mixer', and amp/volume.

The ay-3-891x just spits out 3 channels of lineout, so I tie them together pretty much, with a resistor divider off to ground. The rest feeds into the volume pot (currently a big knob, but on pcb I'll see about using a thumbwheel once I get some parts from ebay in a few weeks), which in turn I'm feeding into a 'minimum parts' arrangement of lm386. (If anyone wants schematic, let me know.. very rudimentary.)

lm386 I'm currently running off +5V just to see, and it seems to be working okay. I could run Vcc in instead, but so far I've not 'well defined' Vcc's value .. as long as its a few V higher than % (say 8+ .. I've used 9V batteries for instance), good enough for the voltage regulators to do their work; lately I'm just using usb +5V and a regulator down to 3.3, so no Vcc 9+ or the like. I'd like to keep that, since running off usb is just so bloody handy... so if lm386 can run on 5V, thats great. It is tempting to make a jumper so I can pick on board which power feeds lm386, but thats goofy..

Anyway, so, yeah .. currently still planning to keep power pcb separate (a little 1" square pcb of its own, say).. but maybe I'll move the power stuff onto this board if I have room (doubt it.. tight already and didn't even add jacks..)... but the amp and mixer circuit is nothing, so should fit on the pcb nicely.

jeff
 
Sort of -- the AY family has 3 channels of output, but internally it has the noise channel you can work with. But thats a modulator, not a direct output.

FWIW, I've minimized components on the lm386 and it works pretty well; no gain or anything and it'll work fine :)

This is a very WIP piece, so excuse the messy layout :)

zik80-r4-audio-001.png
 
Hmm, does tying the channels together like that on output work?  I guess not having any resistors in the path apart from your volume pot would mean that if it works, sufficient current is going to reach the amp though.  Just surprised you don't need any sort of resistor matrix to avoid upsetting any logic the the other side of the IC's pins.
 
That bit is right out of the datasheet so I'm assuming its good; really, my video DAC does something similar.. as long as they're output pins, they're mixing their signals together, so its okay .. but yeah, you would think you have some resistor in each of the channel outputs, or even a signal diode to limit flowback, or something. (in my video dac, for the intensity bits, its just an r2r ladder .. but needed diodes to limit the intensity bits from mixing back on each other into a big mess. No damage to the output pins, just messy signal.)
 
ahright, finally got a pile of parts in the post from all around the world. PS/2 keyboard right-angle pcb-mount connectors, DC003 DC barrel jack right angles, VGA right angles, headphone right angles, etc and so on.


Time to start measure all the pin layouts and package sizes, designing the components into my parts library, and remaking the pcb layout to fit. Take awhile, but will be painful/very-fun work :)


jeff
 
Back
Top