Executive Summary:

The long believed to be inaccessible for mortals 6500/1 firmware of the VC-1520 device has been retrieved!

Full Story:

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":

FF802225353323262F3F37A71E1D3EBD141D3C3546070ACB14354739190B1D3D2EA3044D1C0D0B1B1D36474535B7420B 0D1F2D2B07051323C72AAF3223151D2FBF2233454D3FAF044D0C452CA5242D08C92033352523B308C922253533A302DD 044D3F1F0D05133345CD1C2F2312B30C1F3F4D4B03C30C1F3F4D4B3929384745331385323F0907C704133345493B0B0F CF081B3B49453313050D1F3FCD024B4F8F180B0D1F3F4D4B3947453313050719B9041333454D3F1F0D0B1939CB141727 25151A1D2D2B9B1225271715252A2D1D1BAB4219CF16471ACB12499F0C1F3F4D4B39292724A3363B1B1535474B3D1D0B 0513C3020B2F4B4308C9020F3F4D4B39083947453383443313050D1F3FCD020F3F4D45338342030F4F3889020F4F08B9 4C3F1F0D05134349A9020F4E4308C91233222F1EBF04132335BF020F4E0718C30E03C3020F2B292B4FC3020F0C454EC3 040D1F3F4D45331385020F3F4D4B398926433213050D1F3F4D45B3020F3F4D4B390918C3041333454739190B0D1F3FCD 222F0ECF0E05133345CF0E072347CF0E0327282743CF02054D4F0E0D45C322294D4F0E0DA90E4F4D0503C322030FAF5C 4D3B3523130517335328C912333F9F222D082DC924092D08D908F934231305071929370A2B3935C30E032335392B8B34 231305091B2BB93A1B09051333BF0637392B1B090513B3222F3F18B902112133392B1B09071525B7020F0A2B39B32229 2AAD02112133393ABD32170E0304BB1E13A3020B081B2923283B49C3020B091B2B39B304091B2B393523138504253739 2B0B8140313B1B090715B50A1913182BBB041323352717091B2BB90A2B1E13A30A0333BB0A072347CB0A051325272533 45CB024B0AC30A09254A49810A4B03C330BF70810239638306193933121939CB020F5F538378A23F9AA9BF8580A2FF86 818682E88683A9E0858F8AA23D9500CAD0FB20670FA90485154A851CA28B20E50FC615D0F7A90C851520E00FA582100E C615D0F5CA8681204D0F8583F0F6A23E20F00F20A10FA25F20630DA51ED0F4C61C20640F204D0F8583200A0CA58110F1 0A300520A10FF0EC0A300BA9E08506A9018507209A0F20160C10E120040C20000C20410F20AA0BA5812907186924C52C F00720100C30FB10C020160CB0FBA960852C20160C100320AA0BA52C29F0C960D0E0A52C29070AA8B96A0B48B9690B48 C004900420D10C98601E0C8C0C780B810B870B8B0B910B940A2903851E20A90FF01B2903851C101585211011290F8522 100B29014A6A8518900320AA0BA52DF0F94CF40AA52D49FFF05520160CB0FB20100C2920D0F9852DA20920160CB010CA D0F820040CC62DA207CAD0FD20100CA208A5802902F0FAA5802902D0FAA58049FF0A662CCAD0EA20160C90FB20040CA2 14CAD0FDA52D1003200A0CA52CC90D60A920D002A9400580D00AA9DF25808580A9BF25808580A580C580D0FA4A6A6020 AA0BF05BC90AF057C98DF05CC9FFD002A9DEC922D002E6194518852C297F38E920B00EA5192901851AF02DA52C297F09 20C940B023C9209006A62C1002691F852C20230F900320640F20610D461A900820810FA25C20630DA52DF0A34CF40A20 640F8619861AF0F0208C0FF0EB20AA0B861FC949D00320770FC948D00320810FC94DF00EC952F00AC944F004C94AD01E E61F4820D10C8406850720D10C84088509682904F00520EA0DF00320D70D4C9D0BA9038515204B0D20A40BF057C92EF0 4EC92DD002C61420540DB0EC4806122613A612A4130612261306122613188A6512851298651385136818651285129002 E613C615F01920A40BF01920540D90CCC945D003204B0DC920F009C92CF00520A40BD0ECA412A513A614F00E489849FF A86849FFC8D00318690160A90085148512851360AA38E9309004C90A90028A3860A62CE8A9088517A0008416B1161003 CAF008E616D0F5E617D0F120770FA001B116851B4AAA29078508A900850785092A851F8A4A4A4A29078506A61BE8F00E A521F00AA408A506490785088406A61CF00B0606260706082609CAD0F520D70DE616D002E617A51B10B4C9FFF008A9FF 851BA9E0D0AE60A20218B506750E9506B507750F9507CACAF0EFA20238B506F50A9506B507F50B9507CACAF0EFA202B4 06B50795261007203C0D95079406CACAF0ED38A508E506A509E507A90085232A2AAAB50785034A8501B50685026A8500 8A4902AA852AB5068504B5078505A5060507D007A5080509D02D60A5080509F01338A500E5048500A501E50585019008 A52AF013E625D01118A50065028500A50165038501E625E627A202B5263003200C0FB40AB50B1003203C0D489838E9E7 68E903B0088AD00C20230F9007261F38661F300EB525F016B5263004F629F629D629B525F008B506D002D607D606B526 1003200C0FCACAF0B2A51F10052901851F4AF010A622F010A523D0248623A5202901F004A2FED002A2FDE420F012E0FD F00320470F8682862020470FA9FF8582202D0F85258527C6234C3E0EB425F0090AB007F60AD002F60B60B50AD002D60B D60A6038A50AE9E0A50BE90160A5292903A8B95C0F48A52B2903A86819600F8583A966A00AD00AA987A012D004A976A0 2C85858488A58F10FCA90060090A060590A06050208C0FA61CE8A9050ACAD0FC850C860D209A0FA204B509950DCAD0F9 60A204B50D9505CAD0F9F00EA90085068507A50C8508A50D8509A900851F4CEA0DE61EA51E2903851EA51DC51EF03EA5 0A48A50B4820E40E208C0FA23C20E50F20E00F20E00FA23C20F00FE61DA51D2903851DC51ED0E46885076885064C920F 20EE0FA21EC629202D0FCAD0F860A21EE629202D0FCAD0F860AA950A950A950A

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.

Font Preview

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
Not much of a surprise that the reaction of the community (selected comments only) was quite enthusiastic:

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.

Technical details:

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.
Once the above two are reliably solved, data sheet information for the 6500/1 says that

"[...] a program can be loaded into RAM allowing the contents of the instruction ROM to be dumped to any port [...]"

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:
	... EA EA EA EA EA EA EA A9 00 ...
	... ex -- ex -- ex -- ex -- ex ...
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...
Sure it must be possible but Greg King suggested a far easier approach. While more of a hack than "industrial solution", this has proven to dramatically lower the number of potential issues and led directly to the successful end. Instead of three further stages the first supplied program is both a "downloader" and "dumper" in one. Although at a cost of speed and the need to recompile the code for each page to be dumped.

Jim wrote in the preamble comment to the first AVR program:

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..

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 #define directive 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:

  1. RES to GND
  2. 8 CLKext cycles (4 internal - using 4 "send_data()" calls)
  3. RES to 10V
  4. 8 CLKext cycles (4 internal - using 4 "send_data()" calls)
  5. Feed the 6502 "loader" code in a loop, passing "dumper" as data bytes
  6. Send JMP to "dumper" code (JMP $0000)
  7. RES to 5V
  8. 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...

Downloads: Links: