This time I am finally sitting down and (starting) to write the operating system for the Cat-644. (external link to hackday.io) The hardware is a 20 Mhz 8-bit homebrew computer built around an AtMega 644 microcontroller, and features VGA video output. Retrochallenge main page
I had VGA and video working together a few days ago. I took the 'cheat' route and just had a pin change event blank out an entire scanline. It worked perfectly. At a 30 Khz screen, if the keyboard is up to 15 khz, and the clock has a 50% duty cycle, the routine should be able to sample the bits just fine if it looks once per scanline. And it worked just fine.
I tried to handle the keypress without blanking out the screen. I did this on the cat1 test program when it was all in C:
It did not work in this case. Then I remembered that the sampling has to be at the same time every scanline. If sometimes I sample at the beginning (for a blank line), or at the end (for an active line), the important bit transition may have been missed. Basically if the ps2 pin clock is 50% duty cycle, if I don't sample evenly, something will be missed. So, instead I changed it to sample at the beginning of the scnaline cycle, at hsync, exactly at the same clock every scanline. This also didn't work! So I went back to the simple version that blanks out a scanline. (Thanks git!). And I ran by sample-and-save code immediately after my load-and-act code, and it still didn't work. Why the hell not? Well I have two versions of the sampling code here, both almost identical, and in the AVR simulator do the exact same time. One works, one does not! I even padded the working one with a NOP to make the io timings the same!
in zl, io(PCIFR) //read pin change flags andi zl, 1<<PCIF0 //isolate the flag we are looking for nop //skip a step to line up with not-working version out io(PCIFR), zl //write the flag back. If it was 0, this does nothing. If this was 1, writing a 1 here clears the flag in zh, io(KEY_PIN) //read the ps2 port andi zh, KEY_DATA | KEY_CLK //isolate the clock and data bits (bits 6 and 7) or zh, zl //add in the changed flag (bit 0) out io(GPIOR0), zh //store in GPIOR0, which is later read to get the captured ps/2 port snapshot, and the 'changed' flagNOT WORKING:
in zl, io(PCIFR) //read the pin change flags sbi io(PCIFR),PCIF0 //read the flags, set bit 0, and write back //technically this might clear other flags, But no other flags in this register are being used, and should be zero //also, clearing an already clear flag should do anything. It doesn't in the atmel simulator! andi zl, 1<<PCIF0 //isolate the pin change flag in zh, io(KEY_PIN) //read ps2 port andi zh, KEY_DATA | KEY_CLK //isolate the clock and data bits or zh, zl //add in the changed flag out io(GPIOR0), zh //store in GPIOR0
Now if you look at the not-working version there is a slight race condition. It is possible that after reading the pin change flag, the change can occur right before the next instruction SBI, and I'll have a pin change state that I haven't captured. But, this race condition is only vulnerable for 2 instruction cycles (.1 us out of every scanline), and if every keypress is magically hitting this, despite me plugging the keyboard in at a random time, that is very very unlucky. Or the pin change circuit of the AVR has large delay in it between a pin change and the flag appearing. Or writing a 1 to a non-set pin change flag causes some errant behavior, maybe suppressing the next up-and-coming pin change? The simulator doesn't seem to have any odd behavior here.
I plan to apply this fix to the complicated version, and then move on to the SD card.