Where can I ask for help with building & sourcing components?

The best place is on the SpritesMind forum.


Can I manufacture these and sell them?

Yes, of course you can. And no, I don't want any royalties. I only ask that you abide by the spirit of the copyleft licensing: you can make whatever proprietary modifications you like to your own personal UMDK carts, but if you give/sell a modded cart to someone else, you must provide them with all the source code modifications you've used so they can build upon your work, as you have built upon mine. Furthermore, you must grant them the freedom to use, replicate, modify and redistribute your work under the same terms.


How does the debugger work?

The debugger uses the GDB remote serial protocol, which is just a small set of commands like:

  • Read 16 bytes of target memory at address 0x123456
  • Set register D7 to 0xCAFEBABE
  • Set breakpoint at 0x654321
  • Continue execution

Originally the remote serial protocol was designed to work over a serial (RS-232) link, but it can also be made to work over TCP/IP, which is what I did. So I wrote the gdb-bridge (actually "gdb-proxy" would be a better name), which listens for incoming GDB session connections on a TCP/IP port, and when the connection arrives it waits for GDB to issue commands. Some commands can be executed directly by just reading & writing the SDRAM[1], whereas others require a message-based conversation with the monitor program which is loaded when the MegaDrive boots[2].

Breakpoints are implemented by writing the illegal instruction opcode to the breakpoint address, and replacing the illegal instruction vector with the monitor address (0x400000). When the 68000 hits the illegal instruction, it throws an exception and thereby returns control to the monitor.

Single-stepping is implemented using the trace feature, which is a bit in the 68000 status register which when set will execute only one instruction in user mode, then throw a trace exeption, which also brings control back to the monitor.

The debugger has read/write access to the 68000 registers because the first thing the monitor does when it starts is to save the registers to a known region of SDRAM, where they can be read (and updated) by GDB. When the monitor exits, the last thing it does is to restore the 68000's state from that region of SDRAM (picking up any changes applied in the meantime by GDB).

[1] My SDRAM controller in the FPGA is able to interleave accesses by the host with accesses by the MegaDrive. This makes the SDRAM effectively dual-port.

[2] This conversation is achieved using an arbitrary binary message-passing scheme, where the host writes a command-code and a payload to an agreed region of SDRAM, and then triggers the command by writing to a semaphore. The host then polls that semaphore waiting for the MegaDrive to update it with "I've finished", whereupon the host reads the response payload from the same agreed region of SDRAM.


How does the bus-cycle tracing work?

Whenever the MegaDrive reads or writes to memory or one of its hardware registers, the address and the data read or written is available on the cartridge slot, so the UMDK cart can snoop on accesses to any[1] memory region. The snoop results in nine bytes of data, including:

  • A 20ns timestamp
  • Whether the source was the CPU or some sort of DMA
  • Whether it was a read or a write (high byte, low byte or both bytes)
  • The 23-bit address
  • The 16-bit data read or written

The FPGALink-implemented USB interface is capable of doing 42MiB/s which is several times faster than what is required for bus tracing a MegaDrive, but the USB output pipe is fairly bursty, so on each bus-cycle the nine bytes of trace data are added to a wide, deep FIFO which acts to smooth out the USB burstiness. And when the trace data gets to the host side it is written to disk in chunks 64KiB at a time.

[1] In fact, some cycles cannot be snooped for some reason, like reads from the MegaDrive's onboard RAM. Writes are visible, reads are not.


Read more »