modelracing/dribling.cppDribbling has no tile maps and no sprite chip. Every dot on the screen is an 8-bit number assembled from six independent sources that run in lockstep with the beam, and that number is looked up in a small PROM to get the colour. This page takes that apart, one wire at a time.
Each checkbox is one input to the colour PROM's address. Untick one and that line is forced to 0 before the lookup, just as if you pulled the trace off the board.
Here is the whole idea in one picture. The frame you see is not stored anywhere. What exists are separate bit planes, each scanned at the same instant by the same horizontal and vertical counters. For any pixel, each plane contributes its bits, the bits sit side by side on the address pins of the colour PROM, and the PROM answers with a colour. Priority, transparency and "is this grass or a line or a player" are all decided by what is burned into that one table.
In the driver this is a single line of C. Six reads, six shifts, one OR:
int const b7 = BIT(m_proms[(x >> 3) | ((y >> 3) << 5)], 0); // 93453 PROM at 3C, one entry per 8×8 tile int const b6 = m_abca; // "ab. campo", 8255 port C bit 5 int const b5 = BIT(x, 3); // the 8H line of the pixel counter int const b4 = BIT(m_gfxroms[(x >> 3) | (y << 5)], x & 7); // 2532 EPROMs 3P + 3N: the pitch int const b3 = BIT(m_videoram[(x >> 3) | (y << 5)], x & 7); // CPU bitmap, 0x2000-0x3FFF int const b2_0 = m_colorram[(x >> 3) | ((y >> 2) << 7)] & 7; // colour RAM, 0xC000, one entry per 8×4 cell dst[x] = (b7 << 7) | (b6 << 6) | (b5 << 5) | (b4 << 4) | (b3 << 3) | b2_0;
The probe wanders the screen on its own. Point at the screen (or tap it) to steer it yourself. The loupe shows the neighbourhood with the two granularities that matter drawn on top: dashed lines are the 8×8 tiles of the field PROM, the finer ticks are the 8×4 cells of colour RAM.
The colour PROM is a 63S140 at 3E. Its outputs are active low and only four of them matter: one bit of red through 220 Ω, two bits of green through 820 Ω and 560 Ω, and one bit of blue through 220 Ω, straight to the monitor's RED, GREEN and BLUE pins. That gives 2 × 4 × 2 = 16 possible colours. MAME decodes it as r = (~prom[i] >> 0) & 1; g = (~prom[i] >> 1) & 3; b = (~prom[i] >> 3) & 1;
Memory on this board is far too slow to be read once per pixel at 5 MHz. So both bitmaps are fetched a byte at a time and pushed through 74LS166 parallel-to-serial shift registers. On the schematic you can find them along the bottom edge; one output is labelled VID (the CPU bitmap) and another CAMP, short for campo, the pitch. Every eighth pixel clock the registers load a new byte; in between they shift one bit out per clock. The address they load from is simply the counter value with the low three bits dropped: (x >> 3) | (y << 5), 32 bytes per scanline.
Watch the counter lamps below. The low three (1H, 2H, 4H) pick which bit is on its way out. 8H toggles every eight pixels, and that same wire goes directly into bit 5 of the colour address. In this illustration it is what alternates the grass shade in eight-pixel bands, for free, with no memory at all.
The video RAM is plain 1-bit-per-pixel memory, eight pixels per byte, bit 0 on the left. Drawing a player at an x that is a multiple of 8 is a straight copy. Anywhere else, every row of the sprite straddles two bytes and has to be shifted. A Z80 at 5 MHz doing that with rotate instructions for ten players and a ball every frame would run out of time, so the board does it in hardware.
The CPU writes a byte to I/O port 0x40. The board keeps the last two bytes written, DS (newest) and DR (previous). Three bits called SH0-2, set through the second 8255's port C, choose the shift amount. Reading port A of the first 8255 returns the pair shifted as one 16-bit window: (DS << SH) | (DR >> (8 - SH)). Drag the slider to move the sprite and watch the two bytes that land in video RAM.
The four steps are the port traffic MAME models (iowrite offset 0x40, dsr_r on 8255 #1 port A), written out as Z80 for clarity. They are not a disassembly of the game's own drawing routine.
The pitch lives in 8 KB of EPROM that the CPU never touches. Its layout mirrors video RAM exactly, same 32 bytes per line, same bit order, so the two can share one address counter and one kind of shift register. The CPU only has to maintain what moves. Erasing a player is clearing its bytes; there is no background to save and restore underneath, because the background is on another plane.
Priority costs nothing either. Where a player crosses a pitch line both VID and CAMP are 1 in the same pixel, and it is the colour PROM's contents that say the player wins. The same trick handles the field enable: with ab. campo low, every address with bit 6 clear can map to black, which blanks grass and lines together for attract and score screens without erasing anything.
The price is colour resolution. Video RAM is one bit deep, so a moving object's colour comes from colour RAM, one 3-bit code per 8×4 cell. The code only matters where VID is 1, so a cell can be generous around a player, but two players of different colours in the same cell have to share one code. Turn on the 8×4 grid on the monitor above and watch the cells travel with the players. The pitch, meanwhile, gets its colour from the fixed wires (bits 4 to 7) and needs no colour RAM at all.
Colour RAM writes are masked with offset & 0x1f9f in the driver. Address bits 5 and 6 are not decoded, which leaves 32 columns in bits 0 to 4 and the cell row in bits 7 to 12: exactly the (x >> 3) | ((y >> 2) << 7) the video side reads.