The video card¶
Superseded (D98, 2026-11-10; D103, 2026-11-12): this card, revision 0,
is being replaced by a card on Hitachi's HD63484 ACRTC, from YACC1-D's V3
design: revision 2. (Revision 1, a
6845 card, was a description only.) Revision 0 was
never ordered or built. The emulators and the kernel no longer model it
(D105, 2026-11-12): they model revision 2, and this card's two gate-level
runs against them (§6: vidcard_test, ps2_test) are gone. Its netlist
is gone too (D106, 2026-11-12): generators/card_video.py is now
revision 2's. Revision 0's generator, its 18 GALs' fuse files, its
gate-level helper (sim_video.py) and its place in the timing pass are in
the repository's history, last in commit 78473d7. Its schematic and
board are gone as well (D108, 2026-11-12): hardware/video-card/kicad now
holds revision 2's; revision 0's are in the history, last in commit
5d447f7. This page describes the card as it was.
Status: NETLIST DONE (2026-10-26), verified at gate level with the CPU,
MMU / data, memory and I/O cards, its picture included (§6); layout to do.
Any slot from 3 to 10, 9U × 340 mm (D49), through-hole (D21). D50, D52,
D53, D57, D67. The netlist is generators/card_video.py, the font
generators/gen_font.py.
88 ICs, 18 of them GALs; 207 parts (python3 generators/card_video.py --bom).
1. What it holds¶
VGA 640 × 480 at 60 Hz (a 25.175 MHz dot clock): 80 × 25 text in 8 × 16 cells from a font ROM, on the screen's lines 40–439, and a 640 × 480 one-bit overlay, each layer switched on and off; two PS/2 ports (keyboard and mouse). The console (D53).
Its RAM, 64 KiB at 0xFE00_0000 + n × 64 KiB (n = the window jumpers;
default 5, 0xFE05_0000), bytes, halfwords and words, at any time:
| Offset | What |
|---|---|
0x0000–0x0F9F |
text: 25 rows × 80 cells; a cell a halfword: the character, then its attribute (bit 0 inverse, 1 underline, 2 bright, 3 blink) |
0x1000–0xA5FF |
overlay: 480 lines × 80 bytes, bit 7 the leftmost dot (D67: first planned at 0x8000, where it does not fit) |
| the rest | RAM, not shown |
Its registers, bytes, in the window's top page (0xFFFF_DF00 by default):
| Offset | Read | Write |
|---|---|---|
+00 |
control | bit 0 TEXT, 1 OVERLAY, 2 XOR (else OR), 3 CURSOR |
+01 |
status: bit 0 VBLANK, 1 DE (a drawn dot's column and line) | taken, ignored |
+02 / +03 |
cursor row / column | the same |
+08 |
keyboard data (reading takes it; 0 when nothing waits) | a command byte to the keyboard |
+09 |
keyboard status: bit 0 FULL, 1 BUSY (a command being sent) | taken, ignored |
+0C / +0D |
the mouse's, as +08 / +09 |
|
+10 |
interrupt enables: bit 0 keyboard, 1 mouse | the same |
+F0 / +F1 |
card ID 3 / revision 0 | taken, ignored |
+F4 |
interrupt status (a word: bit 0 keyboard FULL, 1 mouse FULL) | taken, ignored |
Anything else in the window, or another size, gets no -ACK: a bus
error. Reset clears control, the cursor and the enables: a black screen.
The picture. A text dot is the font's, unless the cell blinks and the blink is off (32 frames, ~1.9 Hz); the underline lights scan line 15; inverse inverts the cell; the cursor (CURSOR on, its row and column) inverts scan lines 14–15 while the blink is on. Bright text is 0.7 V, the rest 0.5 V, on all three guns (white). The overlay ORs over the text, or with XOR inverts it.
2. Jumpers (at the card's edge, labelled)¶
Fitted = 0, open = 1:
| Jumpers | Meaning | Default |
|---|---|---|
WIN2 WIN1 WIN0 |
the window: registers at 0xFFFF_8F00 + n × 4 KiB, RAM at 0xFE00_0000 + n × 64 KiB |
5: WIN1 fitted |
L2 L1 L0 |
the interrupt level (both ports) | 4: L0, L1 fitted |
3. Blocks¶
| Block | Parts | ICs |
|---|---|---|
| Bus interface | 3 × 74HCT541 (A2–A15, BE, strobes, CLK, -CLK, -RES: one load a line); 74HCT688 (A31–A25); GALs VHI (A24–A16: the RAM's window, the device windows), VSEL (the access: RAM or register page), VBE (lanes, -ACK, the transceivers), VDEC (register reads), VWR (register writes); 4 × 74HCT245 (D ↔ the card's XD0–31); 74HC05 (-ACK, -WAIT, the PS/2 lines: open drain); pull-downs on XD0–15 |
15 |
| Registers | GAL VCTL (control, enables, the interrupt); GALs VRD0 / VRD1 (the lane 0 and lane 1 registers read); 74HCT32; 74HCT244 (the vector's window); 2 × 74HCT273 + 2 × 74HCT541 (the cursor, read back) | 8 |
| Timing | 25.175 MHz oscillator; 74ACT04 (DCLK, DCLKN); 6 × 74F163 (DC: the dot; HC: the character, 0–99; VC: the line, 0–524); 4 × 74F161 (the overlay's address); GALs HORZ, VERT (the decodes, sync), VTIM (the slots on DCLK's rise, the handshake), VSLOT (on its fall: whose address the RAM sees), VBUS (on -CLK: the handshake's bus side); 74HCT393 (frames: the blink) |
17 |
| RAM | 2 × CY7C199 (32K × 8, 15 ns: the even bytes, the odd bytes); 6 × 74ACT541 (the text's, the overlay's and the bus's addresses); GAL ROW5 + 2 × 74F283 (row × 80 + column); 4 × 74HCT541 (write buffers), 4 × 74HCT574 (read latches) | 19 |
| Display | 74HCT574 × 2 (CL: the character; AL: the attribute, the cursor's match, the column's place); SST39SF010A (the font); 3 × 74ACT574 (FL: its dots; OL0, OL1: the overlay bytes); 2 × 74F151 (a character's dot, bit 7 first); 2 × 74HCT688 (the cursor); GALs CELL (a character's attributes), VOUT (the dots, the syncs); 74ACT541 (the VGA drivers) | 13 |
| PS/2 | per port: GAL PS2K / PS2M, 74HCT163 (bits), 74HCT595 (receive), 74HCT597 (send); 74HCT14, 74HCT175 (the lines in), 74HC280 + 74HCT74 (the command's parity), 74HCT00 | 13 |
| Interrupts | 74LS145 (the level, open collector), 74HCT85 (the level acknowledged) | 2 |
| Front edge | the DE-15 (VGA) socket; two mini-DIN-6 (KEYBOARD, MOUSE), +5 V through a 0.5 A polyfuse | — |
4. Theory of operation¶
4.1 The raster¶
A character time is 8 dots (318 ns). DC counts the dots, HC the
characters of a line (0–99: 80 drawn, then 2 front porch, 12 sync, 6 back
porch: 800 dots, 31.47 kHz), VC the lines (0–524: 60 Hz). VC 0 is the
screen's line 40, the first text line, so that the text row is VC8:4 and
the scan line VC3:0; the overlay's line 0 is VC 485. After -RES
(synchronized: DRESN) the counters start at 0. HC and VC advance at dot 5 of
a character time (ENH), the overlay's address at dot 1 (OENA), so neither
changes while a slot uses it.
4.2 The RAM's four slots¶
The RAM is 16 bits wide: the even bytes in one CY7C199, the odd in the other, so a text cell (character and attribute) is one halfword. Each character time has four 2-dot slots. GAL VSLOT, on the dot clock's falling edges, puts a slot's address on the RAM (enabling one pair of 74ACT541s); GAL VTIM, on the rising edges, takes its data 1.5 dots later and lets the address go half a dot after that:
dot 0 1 2 3 4 5 6 7 0
address [text )[bus, low )[bus, high )[overlay )
data taken TLK ^ RLKA ^ RLKB ^ BCK ^
-WE (a write) [ ] [ ] (per byte lane)
- Text (dots 0.5–2.5): the cell at row × 80 + column ((VC8:4 × 5 + HC6:4) × 16 + HC3:0: GAL ROW5 and two 74F283s). CL takes the character, AL the attribute, the cursor's match (two 74HCT688s: the row against VC8:4, the column against HC) and whether this column is text / drawn. The character and the scan line address the font ROM; at the next character time's start (BCK) FL takes the dots.
- The bus (dots 2.5–6.5): the word's low halfword, then its high one.
- The overlay (dots 6.5–8.5): the halfword holding the column's byte
(the overlay's address counter, from
0x1000at the frame's start, one byte a drawn column); OL0 and OL1 take both bytes at BCK, and the address's bit 0 (in GAL CELL) enables the right one.
So a character's font and overlay bytes are both ready as its dots begin. Two 74F151s pick each dot (DC selects, bit 7 first). GAL CELL has the character's attributes as K (the glyph shows) and M (light or invert regardless): the text dot is (TD & K) ^ M, the overlay's OD & E. GAL VOUT ORs or XORs them and registers every dot with the syncs (HSR from GAL HORZ, VSR from GAL VERT), so the dots and the syncs leave together. A 74ACT541 drives the resistors: a dot is 0.5 V into 75 Ω (680 Ω), a bright one 0.7 V (with 1.5 kΩ more), the syncs TTL through 47 Ω.
4.3 The bus: the handshake¶
The card answers its RAM at once (-ACK) and holds -WAIT until
the dot clock's side has done the access, whatever the bus clock:
- REQ = the access and not DONEB (GAL VBUS). It is
-WAITitself, and crosses into the dot clock through two flip-flops (R1, R2 in GAL VTIM). - At dot 2 of a character time, with R2 and not DONE, GO starts the
access: the bus's two slots in this character time. A write: the RAM's
-OEoff and the write buffers (XD → RD) on through both slots,-WEfor a dot in each, per byte lane (BE0–3). A read: RLKA and RLKB clock the read latches (RD → XD), which drive the card's data bus for the whole cycle. - DONE (at dot 6) crosses back through one flip-flop clocked by
-CLK(DONEB, mid-cycle): REQ, so-WAIT, ends; the cycle ends at the next CLK. With REQ gone, DONE clears.
At 3 MHz an access waits one or two clocks (from 8 to 16 dots after
REQ). Back-to-back accesses work: DONEB is clear again by the middle of the
next cycle's first clock, before -WAIT is looked at. The registers
are on the bus's own clock and answer without waiting.
Register writes are taken at the cycle's end (D72): the cursor registers' and the PS/2 send registers' clocks fall in the bus clock's second half and rise with CLK as the cycle ends, so the address decode, the transceivers and the data have the whole cycle (at mid-cycle they did not, at the datasheets' worst case). The PS/2 GALs still see the command strobe (KCMDC / MCMDC) as a level in the second half.
4.4 PS/2¶
Each port is a GAL state machine on the bus clock (PS2K, PS2M), its clock and data lines read through a 74HCT14 and a 74HCT175 and pulled low through the 74HC05 (4.7 kΩ pull-ups).
- Receiving. A fall of the device's clock is CK2 & !CK1. A 74HCT163 counts them: the data bits (falls 2–9) shift into a 74HCT595; the 11th sets FULL, which loads the 595's output register and holds the clock low, so the device waits with its next byte. Reading the data register (KRD) clears FULL. The 595 drives lane 0 only while FULL: an empty port reads 0 (the pull-downs).
- Sending. A write to the data register loads the byte into a 74HCT597 (and its odd parity, from a 74HC280, into a 74HCT74) and sets BUSY. The GAL holds the clock low for five line ticks (TICK: HSYNC through GAL VBUS: 127–159 µs, ≥ 100 µs), then pulls data low (the start bit) and lets the clock go. The device clocks the frame: at each fall the GAL presents the next bit (the 597 shifts at falls 2–9, the parity behind the eight data bits), lets data go for the stop bit, and the 11th fall (the device's acknowledge) ends BUSY. The answer (FA) arrives as an ordinary byte.
- Interrupts (D52, D57): FULL and its enable, either port, pull the
card's level through the 74LS145. In the acknowledge cycle (the level
matching, 74HCT85) the vector is window × 16 + 0 (keyboard) or
+ 1 (mouse, if the keyboard is not asking):
0x50/0x51by default.
5. The font¶
generators/gen_font.py draws printable ASCII on an 8 × 8 grid (capitals
on rows 0–6, descenders on row 7), each row shown on two scan lines: the
look of an 8 × 8 font on a VGA screen. python3 generators/gen_font.py
writes video/font8x16.bin, the whole SST39SF010A (128 KiB: the font at
the start, FF after it); address = character × 16 + scan line.
6. Verification¶
- Its 18 GALs against their specs, exhaustively; the netlist's electrical checks.
- At gate level with the CPU, MMU / data, memory and I/O cards
(
generators/sim_sys.py --mem --io --video), the card whole: its own 25.175 MHz clock against the CPU card's 24 MHz (generators/sim_video.py); only the PS/2 devices are models, driving the sockets' lines. vidcard_test: the RAM in every width and lane (each access through the handshake), the registers, the status register three times as the raster moves, five refused accesses, then a screen. Every instruction's state and every cycle asp32uemu -C, which now models the card's timing exactly: the two clocks' schedule, the handshake's flip-flops and the raster (p32io.h,vid_hw_*). All 614 RAM accesses wait as many clocks in both.- The picture (
--frame PNG): after HALT the dot clock runs on for a frame; the VGA outputs, cut by the syncs, must be dot for dot the picture the card's RAM and registers describe (sim_video.expected), with HSYNC 96 dots every 800 and VSYNC 2 lines every 525, and nothing lit outside 640 × 480. ps2_test: the keyboard's bytes as they arrive, its vectored interrupt (0x80005004), the mouse's reset (FF: FA AA 00), a command with its data byte (ED 02: FA FA), the parity checked by the device. Self-checking on both emulators and the card; not cycle for cycle (on the card a byte takes a frame on the wire).
7. To check before layout¶
- The mini-DIN-6 footprint (Same Sky MD-60SM: as on YACC1's video card) in
hardware/lib/P32W.pretty. - The 25.175 MHz oscillator in a DIP-14 can (25.000 MHz works with most monitors, if that is what is in stock).
- Timing against the datasheets: the slots assume the 74ACT541's enable times and the CY7C199-15's access (≤ 45 ns of the 60); the dot-rate GALs are the -7 grade (ATF22V10C-7PX).
- Programming (TL866II+ or T48): the GALs' fuse files are
hardware/gal/video/*.jed(minipro -p ATF22V10C -w FILE), the fontvideo/font8x16.bin(minipro -p SST39SF010A -w ...): hardware/gal/README.md.