Thanks Jeff! Indeed, shoring up the power network stabilized the modules wonderfully -- I'm running solidly at 12.5875 MHz as desired with no issues.
Here is my latest progress, a font editor module (used already to create a decent 5x7 font for all the printable ASCII characters):
FONT EDITOR
The next module I built is the font editor. Since I can now program the W65C02S by byte editing the NVRAM, it made sense to go with minimal hardware and put most of the logic into software. The finished solderless version as of 2023-08-23 looks like this while in use:
Attachment:
PXL_20230824_072549657.jpg [ 1.91 MiB | Viewed 6036 times ]
I tried to make the user interface as intuitive and self-explanatory as possible. The use of grayscale for highlighting what you are editing means you can get a feel for what the button presses do quickly. My son, with no instructions, was able to figure it out and make a heart shape in 5 minutes of playing around.
The software interface from the font editor module to/from the CPU consists of:
Output latch at $C000 that drives a bar of LEDs (8 of 10 are used). For the font editing, this shows the ASCII value of the character being displayed and edited.
Output latch at $C001 that drives the 8 row anodes of the 8x8 LED font matrix display. We typically activate (set high) just one row at a time, with the row scan clocking at 12.3 kHz. The whole array is thus scanned at 1537.5 Hz, and with 3-bit (8 levels) grayscale via PWM we end up with full grayscale scanning effectively at 192.2 Hz.
Output latch at $C002 that drives the column cathodes (bit set means LED on).
Input buffer at $C003 that allows reading of bit 10 of the clock module in the high bit and four push button states in the lower four bits (the remaining bits are unused for now). The buttons are:
Bit 0: down button
Bit 1: up button
Bit 2: right button
Bit 3: left button
The hardware interface of the font module to previous modules is:
The clock module bit 10 signal,
the 8 CPU data R/W lines,
the CPU RWB line,
the active-low write enable line (combination of RWB and CPU clock), and
four memory-mapped I/O address decode lines for $C000-3.
The hardware specific to the font editor module is:
3x 74F374 8-bit latches
3x 74F240 inverter/buffers
1x TBD62783APG 8-bit anode driver
8x 330Ω current-limiting resistors to avoid burning out the 8x8 LED matrix
1x SIP bank of 8 330Ω resistors connected together on one end to limit current to the LED bar.
1x 74F32 quad positive OR gates to generate the latch clock signals from the write enable and respective address selection lines.
1x 74F00 quad NAND gates to generate the output enable of one of the `240s to output to the CPU data lines, based on the $C003 address decode selection line and the CPU RWB line.
A 10 LED bar (currently using only 9 of them)
An 8x8 LED matrix
4x push buttons
I’ll work on putting out some schematics.
After shoring up the power bus wiring, I’m now running stably at 12.5875MHz with all five modules hooked together: clock/counter, hexadecimal keypad scanner, NVRAM byte editor, CPU module, and now the font editor.
To keep my sanity, I modularized the code into moderately small subroutines, since I am only using a byte editor to code in machine language right now, and don’t have the ability to add or remove code from a routine except at the end of it, and thus end up rewriting the whole thing if there is an error or modification needed.
The memory state for the font editor consists of:
$00,$01: low and high byte of a 16-bit loop counter for timing. One application loop takes 81.4 usec (12.3 kHz). 256 loops takes 20.82 msec, and the full 65536 loops before wrapping takes 5.333 seconds.
$02: the ASCII value of the character being edited.
$03: indices of the row and selection of ASCII/row/column being edited at the moment.
Bits 0-2: index of row being edited
Bits 3-6: 4-bit code of what is being edited
0000: up/down add/subtracts $10 from the ASCII value.
0001: up/down add/subtracts 1 from the ASCII value
0010: up/down moves the row being edited up/down.
0011: leftmost/bit 7 pixel column for row is turned on/off with the up/down buttons.
0100-1010: Bits 6 through 0 are similarly edited using the up/down buttons.
Bit 7: always 0
$04-$07: up/down state and timers for buttons 0-3 respectively.
$08-$0B: The auto-repeat countdown timer for buttons 0-3 respectively.
$E000-$E7FF: the font itself, with 8 bytes per character (8x8 bit mask), 2kB total for 256 character bitmaps.
The font-editing code is broken up into the following routines:
Generic main loop that does general 6502 reset initialization (stack pointer to $1FF, $00 into status register), calls the application-specific initialization routine, and then in a forever loop calls the application per-loop work routine.
Font-editor init routine: clears $00-$07 to zeros, a reasonable initial state, then calls the clock sync routine so that the first-loop timing relative to the clock edges is the same as the Nth loop, in case that matters.
Get column mask in A from $03 edit selection variable.
A = A >> Y
A = 1 << Y
Wait for the clock line to change (used for synchronization/timing).
$10,$11 = address low,high of first byte of font memory for ASCII value in $02
Font editor per-loop work:
Handle key press events
Handle button indexed by X for X=3,2,1,0, generating key press events on first down press with debouncing, and for key press auto repeats for long button holds.
Handle button X key press event. The left/right buttons navigate what is being edited, and the up/down buttons change state on whatever is being edited.
Update display. The ASCII value is put in the LED bar using $C000, the active row in the 8x8 LED matrix is set using $C001, and the highlighted font row is output using $C002.
Highlight font row bits in A based on frame number modulo 8, to get grayscale visual cues as to what is being edited.
Increment loop count $10,$11
Clock sync
Overall the font editor code comes in at under 768 bytes, and lives at $F000-$F2FF. Boy, that was a lot of tedious hexadecimal data entry! I’m really looking forward to getting a more proper display, full keyboard input, and assembler going.