The long believed to be inaccessible for mortals 6500/1 firmware of the VC-1520 device has been retrieved!
On 2014-06-09 at 1202Z Gerrit Heitsch started a new thread on the cbm-hackers mailing list. This time he looked for a solution to dump the content of Amiga's keyboard controller, which he found being very similar to 6500/1. For those who are not aware - 6500/1 is a 6502 core based single chip computer, which controls for example the Commodore's 1520 printer/plotter. The initial conclusion was that nothing really changed since the previous thread, which I started almost two years earlier. Meaning - nobody really tried any of the discussed options. The main reason most probably being relatively high level of uncertainty about the details. Available technical information states clearly that ROM content can be obtained using a special testing procedure, and that every 6500/1 is equipped with "TEST LOGIC" for exactly such purposes
Special test logic provides a method for thoroughly testing the 6500/1. Applying a +10V signal to the RES line places the 6500/1 in the test mode. While in this mode, all memory fetches are made from Port PC. External test equipment can use this feature to test internal CPU logic and I/O. A program can be loaded into RAM allowing the contents of the instruction ROM to be dumped to any port for external verification. All 6500/1 microcomputers are tested by Commodore Semiconductor Group using this feature.
but the wording left a lot of room for interpretation on how this can actually be done. Or how the "external test equipment" should be constructed. Soci/Singularcrew suggested using a second 6502 based system to be used as the master device and run in unison. Gerrit Heitsch thought it would be most convenient to use an EPROM and a 4040 based circuit. Martin Hoffman-Vetter thought of using an AVR microcontroller to do both jobs (uploading the custom program into the 6500/1's RAM and downloading the ROM content). But those were ideas and concepts rather than solid plans. Other knowledgeable people joined the discussion, trying to work out the details, yet the thread seemed to lose momentum again. Despite this, some people felt that we were really close to finding a solution and continued to push for more details. Those more detailed thoughts eventually started to get posted. At some point, Jim Brain felt he collected enough information to give it a try. He took an ATmega32, which happened to be "at the top of his parts box", found additional necessary elements in his spare parts bin, and designed the following circuit
quickly implemented in the good hackers' style:
in the lower-right corner of the multi-project workbench!
He connected everything, wrote the necessary AVR code, run the first test and found out that it... doesn't work!. A few more advices, a few more tries and still no good news. Until late night (or maybe early morning is more appropriate wording here) he realised that the fuse settings on the AVR he used, caused JTAG to be turned on, which in turn caused incorrect opcodes being sent to the 6500/1. After correcting this mistake, on 2014-07-04 at 0816Z correct data started to flow off the Commodore's SOC, probably for the very first time outside of MOS/CSG's production facilities.
The result of Jim's hard work was a "mere":
But this innocent looking string of hexadecimal digits is what many people have been waiting for! Inside the surprisingly few bytes above, some currently unnamed authors implemented a whole control of the printer/plotter's hardware, full serial IEC communication stack, and on top of those they still managed to squeeze a fairly complete font with 96 vector characters in! An example of software engineering craftsmanship at the level we don't see all too often. If your browser supports SVG images, you can peruse the font below in its full, vector-based glory.
Extracting the above content from insides of the controller chip is a milestone achievement, which:
- Makes analysing and preserving the 1520 firmware possible.
- Allows insightful explanation of all the inner workings of the 1520 device.
- Enables creating a 100% accurate 1520 emulation e. g. in VICE.
- Opens clear path to analyse, archive and preserve ROM content of many other devices controlled by variants of the 6500/1 SOC.
- Enables creating a programmable, 100% compatible replacement for the "unobtainable" 6500/1 part for all the devices depending on it
◦ Absolutely awesome stuff Jim.
◦ I guess it's exactly for the moments like this we all indulge in this kind of stuff.
◦ Brilliant work there.
The process got broken into several stages, each with its own set of problems to be solved.
- Reset the CPU - This part is probably the easiest. Push down the RES line and you are done. Keep it LOW as long as you prepare yourself for the next step. Release it when ready. Not much to see here - any GPIO output with enough sink capability will do, but...
- Change the mode of operation into "test mode" - Now a bit more tricky exercise. No GPIO will give us a way to select between three different states and their respective voltages. A bit of a circuit had to be devised and connected to the RES line. You can see it on the right-hand side of the diagram above.
But how do we do it? We have to remember that when the line is released (to +5), CPU starts executing its RESET procedure. When we turn it into test mode, we need to be ready to supply PORT C with bits to be further executed or we risk jamming the processor. But the question is what the CPU is expecting in order to continue at the very moment we pull the line to 10V? We don't know what it is doing at that moment. Whether it is fetching a command or a data / address information. Having the above in mind, something "safe" for the processor has to be provided so that no matter what it is doing, it will neither crash nor wander off into lalaland when interpreting supplied data as command or vice-versa. By supplying dummy but "safe" bits for some time, we should be able to "synchronise" the CPU to a known state, when we can start feeding it our real commands. The first what comes to mind is to feed the processor with a string of $EA (NOP) bytes but as Marko Mäkelä clearly pointed out, it takes 2 cycles to execute NOP command and we can run into a situation like:
"[...] a program can be loaded into RAM allowing the contents of the instruction ROM to be dumped to any port [...]"
... EA EA EA EA EA EA EA A9 00 ... ... ex -- ex -- ex -- ex -- ex ...Where:
ex = executed
-- = Byte supplied as operand or "dummy" cycle (when the CPU executes the command).
And we never reach the A9 and never load the Accumulator with our intended value. To get around that we needed a sequence, which makes SURE that a certain byte will be executed as a command. Marko suggested a clean solution to this problem, which is to use a stream of 2-cycle instructions (like $EA) followed by a terminating sequence of one 3-cycle instruction trailed by a 2-cycle one. Since with a long enough sequence of initial NOPs, there are only 2 alternatives:
... EA EA EA EA 24 EA EA A9 00 ... ... ex -- ex -- ex -- -- ex -- ... ... -- ex -- ex -- ex -- ex -- ...either way, $A9 will be executed as command and the Accumulator will be loaded with $00! Marko thought of using some of the stack related commands like PHA or PHP. Those are 3-cycle commands but they are "invasive". In the sense that they affect other things beside the processor flags. A "non-invasive" 3-cycle command is surely better. That's what Gerrit eventually supplied with the zero page BIT command ($24), effectively doing nothing in our case but taking three cycles to complete. Working this solution out completed the first two stages. Now, if we wanted to follow the instructions given by Commodore Semiconductor Group we would need to:
- Supply a "downloader" program to the I/O port C and make the CPU execute our externally supplied binary code.
- Upload the actual "dumper" program this way.
- In the end make the CPU jump from the "downloader" to the "dumper" program and execute it further on. Obviosly, turning off the TEST mode in the very appropriate moment...
Jim wrote in the preamble comment to the first AVR program:
According to his own words, removing two factors from the trial and error equation made the odds look much better and only because of that he eventually decided to bite the bullet. But then, once this basic process has been mastered, equipped with much more experience, he was able to approach the problem "by the book" with ease. On that second shot he rewrote the AVR software and added a small 6502 "dumper" assembly code in order to make the whole dump in single pass. Both versions are included in the main.c file (part of the "Extraction tools" below). They are selected using a
The code takes a brute force approach to pulling memory contents from the 6500/1. Based on a plan by Greg King to simply pull data directly from the ROM, with no need to create a program that runs in the 6500/1, the code holds RESET low for a suitable time, puts the CPU into TEST mode, and tries to synchronize the opcode stream using a set of opcodes provided by Gerrit Heitsch, modified based on empirical testing. Once synchronization happens, it instructs the internal CPU to load one memory location into the .A register, dropping out of TEST during the cycle when the load from ROM occurs. It then stores the data to PORTA, JAMS the CPU, and the AVR reads the data from PORTA, sends it out via the AVR UART, and then repeats the process for the next memory location..
#definedirective before compiling. 6500 is clocked at 250kHz, which seems to be the effective minimum. Although the 6502 limit should be 100kHz (remember - the internal clock inside the 6500/1 is half the frequency of the supplied one) but the tests have shown that 250kHz is needed to ensure stable operation. The ATMega runs at 16MHz and its serial port is configured to 57600bps. The operation sequence looks like this:
- RES to GND
- 8 CLKext cycles (4 internal - using 4 "send_data()" calls)
- RES to 10V
- 8 CLKext cycles (4 internal - using 4 "send_data()" calls)
- Feed the 6502 "loader" code in a loop, passing "dumper" as data bytes
- Send JMP to "dumper" code (JMP $0000)
- RES to 5V
- Watch the strobe and sip off the data, passing it to UART
There is still one uncleared thing about the loop in point (5). It seems that additional toggling between the normal and TEST mode inside this loop is required. We don't know why at the moment but you can find it in the C program file...