thecodingidiot.com

The 2600 HardwareThe RAM

The RAM

The CPU is fetching and executing code from ROM. The last piece is RAM. Without it, most of the 6502 instruction set is crippled. The stack — the mechanism the CPU uses to call subroutines and return from them — lives at 01000100–01FF in RAM. The zero page, 00000000–00FF, is the fastest addressing mode on the 6502. JSR, RTS, PHA, PLA, and any instruction that writes to memory all require RAM. With ROM alone you have a read-only machine that can loop, branch, and compare, but nothing else.

Address decode for RAM

The RAM decode is the mirror of the ROM decode. The 62256 maps to the lower half of the address space, 00000000–7FFF. A15 is 0 for all addresses in that range. The 62256's CE# pin is active low and it needs to go low when A15 is low — which means CE# can connect directly to A15. No gate required. When A15 is 0, CE# is 0, and the RAM is selected. When A15 is 1, CE# is 1, and the RAM is deselected.

The RAM also needs OE# (Output Enable) and WE# (Write Enable) signals. These come from the CPU's RWB pin (pin 34 — read/write bar). When the CPU is reading (RWB = 1), OE# must go low to make the RAM drive the data bus. That is another inverter: connect both inputs of a second NAND gate on the 74HC00 to CPU RWB, and wire the gate output to RAM OE#.

When the CPU is writing (RWB = 0), WE# must go low to latch the byte into RAM. Wire CPU RWB directly to RAM WE#. When RWB is 0 the CPU is writing; WE# = 0 selects the write mode. When RWB is 1 the CPU is reading; WE# = 1 disables write mode.

Wiring the RAM

Seat the 62256 on the second breadboard beside the AT28C256. They share the same address bus and data bus connections, so most of the wiring is a matter of tapping into the existing lines. Add a 100nF decoupling capacitor next to the 62256.

Connect the address lines. The 62256 has the same 15-address-pin footprint as the AT28C256, and the same pins carry the same signals:

RAM A0–A14 -> CPU A0–A14 (same as ROM A0–A14)

Connect the data bus:

RAM D0–D7 -> CPU D0–D7 (same lines as ROM D0–D7)

Both chips share the address and data buses. Only CE# determines which one is active at any given moment. Because the ROM and RAM occupy non-overlapping address ranges, they will never both assert CE# at the same time.

Connect the control pins:

RAM CE# -> CPU pin 25 (A15) directly
RAM OE# -> second NAND gate output (74HC00, both inputs tied to CPU pin 34 / RWB)
RAM WE# -> CPU pin 34 (RWB) directly

Modified test program

Update ~/f03b-practice/test.s to write a known value to RAM and read it back. The result — success or failure — will be visible on the address LEDs as two different halting addresses:

  .org $8000
 
reset:
  lda #$aa
  sta $0000
  lda $0000
  cmp #$aa
  beq success
 
fail:
  jmp fail
 
success:
  jmp success
 
  .org $fffc
  .word reset
  .word reset

The program writes AAtoRAMaddressAA to RAM address 0000, reads it back into the accumulator, and compares it with $AA. beq success branches to the success loop if the comparison matched (the zero flag is set). If it did not match, execution falls through to the fail loop.

fail and success are both infinite loops — jmp fail and jmp success — but they sit at different addresses in the ROM. The address lines will show one pattern if the CPU is halted at fail and a different pattern if it is halted at success. That difference is visible on the LEDs.

Assemble and burn:

vasm6502_oldstyle -Fbin -dotdir test.s -o test.bin
minipro -p AT28C256 -w test.bin

Replace the AT28C256 in the circuit with the freshly programmed chip.

Verification

Apply power. The CPU resets, executes the test program, and halts in one of the two loops.

The success loop is at a higher ROM offset than fail because it appears later in the source. After assembling, look at the map: jmp fail assembles to $8000 + a few bytes, and jmp success assembles to a slightly higher address. The exact addresses depend on the instruction lengths of the preceding code, but the two patterns on A0–A7 will be distinct.

To interpret the result: if the LED pattern is completely stable (no flickering — just a fixed combination of lit and unlit), the CPU has halted in one of the two loops. If it is cycling rapidly through many patterns, the CPU is not executing the expected code — check the wiring before reading the LEDs.

To determine which loop: connect an LED to A15 as a sanity check (it should be lit, confirming the CPU is in ROM at all times). Then compare the A0–A7 pattern against the address printed by vasm in its listing output. Run the assembler with a listing flag to see the addresses:

vasm6502_oldstyle -Fbin -dotdir -L test.lst test.s -o test.bin

The listing file test.lst will show the address of every instruction. Find the address of jmp fail and jmp success, convert the low byte to binary, and compare with the LED pattern.

If the CPU halts at success, RAM read and write are both working. If it halts at fail, the comparison failed — the value read back from RAM did not match what was written. The most likely cause is a wiring problem on the data bus between the CPU and the RAM, or on OE# or WE#. Check that RAM OE# is driven by the NAND gate output, not tied directly to GND (which would cause bus contention when the ROM and RAM try to drive the data bus simultaneously).

Bus contention

One failure mode to be aware of when both ROM and RAM are wired: bus contention. This happens when both chips try to drive the data bus at the same moment. The address decode must guarantee that exactly one of ROM CE# and RAM CE# is low at any given time. With the circuit as described — ROM CE# = NOT(A15), RAM CE# = A15 — they are complements of each other. When A15 is 1, ROM is selected, RAM is not. When A15 is 0, RAM is selected, ROM is not. They cannot both be selected simultaneously.

If you accidentally wire RAM CE# to NOT(A15) instead of A15 directly, both chips will be selected for every address, and both will try to drive the data bus. The result is a logic conflict: one chip drives a pin high while the other drives it low, and the chips fight each other through near-zero impedance. This draws excessive current and will heat the chips. If anything on the board gets warm within the first few seconds of power-up, remove power and check the CE# wiring.

up next

Writing 6502 Assembly

Writing 6502 Assembly