• Extracting ROM

    Alexander R Prussa day ago 0 comments

    I "needed" to figure out how disk i/o worked. While I could keep on using Ghidra on the system test and OS files, I felt that to make real progress on reverse-engineering, I'd need to pull the 64K ROM. I already pulled short bits and pieces with my memory-to-screen dumper, transcribing it manually or with AI, and disassembling, but it was a slow and annoying process. 

    It was time for something quite different. I probably could have physically taken the unit apart and pulled the data off the ROM chip. But it's scary to do that. The oscilloscope board is above the computer board, and above both is the scary power supply with a big warning. I had to dig through there once when I shorted something in setting up my Gotek and had to reinsulate the ribbon cable (it burned through the insulation on one wire but the wire was intact).

    The alternative I settled on was QR codes. Now that I could run C code on the device, I could put a chunk of ROM into a QR code, take a photo of the screen, and decode on my PC. 

    The first challenge was finding a QR code library for C that I could run on the device itself. I tried some and they were slow or crashy on large QR codes. I was abotu to give up, until I came across the Nayuki library. It worked great on the device.

    The second challenge was decoding a big sequence of QR code screen photographs programmatically. It turns out that most of the standard QR code python libraries weren't able to handle the combination between the logic analyzer's little CRT screen and the large QR codes I was using. I don't know if it was the scan lines, the specific contrast issues, the screen curvature, or the Moire. I ended up going with the QReader library which uses machine learning to preprocess the QR code. As a result it takes several settings. But it works. For most of the images.

    I ended up sending 512 bytes of ROM at a time, base64-encoded (I couldn't get binary-encoded QR codes to decode properly using QReader). QR code generation on the logic analyzer was slow, and I added a bit of a pause. As a result, each QR code was shown for about 10 seconds at a time. I had a Sony A7RII camera pointed at the screen, with an external timer taking a picture every two seconds, in case not every picture worked. The setup took about a thousand pictures. I downsized them with Imagemagick, and ran them through QReader to reconstruct a ROM binary. All code is here.

    At some point the cat walked between the camera and the screen, but didn't stay long enough to mess up all the pictures of that particular QR code. I had one hiccup: I accidentally deleted the photos mid-way through the run, and had to re-take the rest. 

    But then I had a nice 64K ROM file, which I was able to run through Ghidra.

  • Developing in C

    Alexander R Pruss01/25/2026 at 23:02 0 comments

    The hardest part of developing in C for the 68000 is finding a compiler. The amount of Googling was insane. I came across some projects to compile gcc as a 68k cross compiler, but they were Linuxy. I tried under Cygwin and failed. And then I found a MingW build on sourceforge. I combined this with Tom Storey's bare metal toolkit, modified crt0.S and platform.ld to load all code into RAM at 0x00984500, and it was off to the races. Again, there was some python scripting to add the HP's executable file header which I was slowly reverse-engineering (latest version here). I hadn't done programming on old hardware with a modern compiler before, and---wow!--gcc is really, really good with full optimization. 

    I wrote various simple functions to interact with the hp's code: this is now my libhp165x library. It is satisfying to be able to write code in a modern environment and produce binaries that are less than 35K in size.

    Finally, I started porting my 2002 Wiztris falling blocks game (here is the current code). It was very satisfying to get it to work. I had to remove the high score code from the initial version, however, because I didn't know how to do disk i/o.

  • Beginnings of reverse engineering

    Alexander R Pruss01/25/2026 at 22:50 0 comments

    With the chunking of the files discovered and the executable file origin known, I was able to make a start on disassembling pieces of the PVTEST_ self-test code with Ghidra. Messages shown by the test code to the user were useful, as looking at references in the code to them I was able to located PVTEST_ code that writes text to the screen. Eventually, I found that the screen buffer is at 0x600000. It turns out, I could have also found this from the (probably non-working) MAME code for related units that I later found.

    Finding an easy to use assembler took a bit, and I finally settled on EASy68K. It produces Motorola S-Records, and I wrote a python script that copied the binary data from the S-Record file to the beginning of the PVTEST_ code. I could then put the modified code into a .LIF file, and then convert that to an .HFE file for the Gotek Then I could just launch my code by going to the self-test in the oscilloscope's I/O menu, and switching disks in my Gotek drive. It was awkward, and there were a lot of reboots.

    Writing to the screen turned out to be more confusing than I expected. Writing zero bits did nothing. Writing one bits did different things at different times. It turned out from the Ghidra disassembly (I could also have learned this from the MAME code) that there were different writing modes controlled by a write-only hardware register at 0x0200100. For instance, if it's in mode 0xFE00, then writing 1's produces white pixels, while in mode 0xFF00, writing 1's produces black pixels. Discovering the intricacies of the screen i/o would still be for the future, but at least I was able to do basic output--put text on screen, and draw pixels.

    Next was input. I eventually found a ROM get key routine that the code called (0xEB38). With input and output I was in theory able to do a lot.

    I wrote various small bits of assembly test code, including a lot of code to dump memory locations to the screen. (The latest version is here. You can specify which address to go to, and press RUN to toggle whether the display is static or dynamic.) I was able to learn where in RAM the ROM code stored the current key and various information about the spinner state. (See my ever-evolving "messy notes".)

  • Beginnings of reverse engineering

    Alexander R Pruss01/25/2026 at 22:50 0 comments

    With the chunking of the files discovered and the executable file origin known, I was able to make a start on disassembling pieces of the PVTEST_ self-test code with Ghidra. Messages shown by the test code to the user were useful, as looking at references in the code to them I was able to located PVTEST_ code that writes text to the screen. Eventually, I found that the screen buffer is at 0x600000. It turns out, I could have also found this from the (probably non-working) MAME code for related units that I later found.

    Finding an easy to use assembler took a bit, and I finally settled on EASy68K. It produces Motorola S-Records, and I wrote a python script that copied the binary data from the S-Record file to the beginning of the PVTEST_ code. I could then put the modified code into a .LIF file, and then convert that to an .HFE file for the Gotek Then I could just launch my code by going to the self-test in the oscilloscope's I/O menu, and switching disks in my Gotek drive. It was awkward, and there were a lot of reboots.

    Writing to the screen turned out to be more confusing than I expected. Writing zero bits did nothing. Writing one bits did different things at different times. It turned out from the Ghidra disassembly (I could also have learned this from the MAME code) that there were different writing modes controlled by a write-only hardware register at 0x0200100. For instance, if it's in mode 0xFE00, then writing 1's produces white pixels, while in mode 0xFF00, writing 1's produces black pixels. Discovering the intricacies of the screen i/o would still be for the future, but at least I was able to do basic output--put text on screen, and draw pixels.

    Next was input. I eventually found a ROM get key routine that the code called (0xEB38). With input and output I was in theory able to do a lot.

    I wrote various small bits of assembly test code, including a lot of code to dump memory locations to the screen. (The latest version is here. You can specify which address to go to, and press RUN to toggle whether the display is static or dynamic.) I was able to learn where in RAM the ROM code stored the current key and various information about the spinner state. (See my ever-evolving "messy notes".)

    I also realized that I didn't need to do the awkward and long-drawn out process of booting up the unit and launching my code from the self-test menu option. I could simply make a Gotek virtual disk where my code was called SYSTEM_, and it would happily boot the code. Nor did my code need to be overlaid on the existing PVTEST_ code. It could be stand-alone and could be small so it would load fast (though it needed some zeroes that I erroneously thought was some kind of padding at the end).

  • HP 165x LIF files and chunking

    Alexander R Pruss01/23/2026 at 03:33 0 comments

    It was time to download and fire up Ghidra, and run it on PVTEST_ to get to know what the code is like. I ran it in 68000 mode. 

    There were some oddities with the disassembly. And a crucial piece of information was missing: I didn't know at what memory location the code was loaded, the origin of the assembly file (I had a pretty good guess of where the file header ended and where the code began--there was a JMP instruction that looked like a start). Looking at the disassembly, I had an idea. The code accessed various data addresses via instructions like

    movea.l #0x9aa84c,A0

    and

    pea (0x9a5939).l

    I noticed that often these addresses were pretty much in order. Which makes sense, in that in earlier pieces of code you tend to access earlier data. There was a good chance that these referred to data in the program itself. I had a clever (I think) idea that I would look at sequences of human-readable null-delimited ASCII strings, of which there were many, and look for code in the disassembly that accessed memory locations whose spacing equalled the size of these strings. With the help of dumping what looked like the right instructions from the Ghidra disassembly I eventually found some instructions and corresponding strings that seemed to match. This would let me know what memory address the strings were loaded at, and hence I could calculate the address the whole code got loaded at.

    Except it didn't quite work.

    Moreover, there were some weird things. Occasionally text strings were interrupted by a short piece of junk. And occasionally disassembly would break down weirdly.

    I had also been looking at the way the oscilloscope saved data. I noticed that it saved some of the data in 256 byte chunks (which happens to be the LIF block size), each starting with 0x00 0xFE. I guessed that these two bytes indicated the length of the data to come (254 bytes). And then I guessed that maybe the executable files were similarly divided into 256 byte chunks, and what I thought was junk was a chunk header. It sure looked that way.

    The chunking setup also allowed the system to have files whose length wasn't a multiple of block size, even though the LIF directory only listed file sizes in block size multiples. For the last header might be a number smaller than 0x00 0xFE.

    I wrote a quick and dirty unchunker and ran it the code. Suddenly everything started fitting into place. Looking at the spacing of memory accesses and lengths of strings, I was able to figure out that the code is loaded at 0x00984500. Yay!

    Eventually, I incorporated the unchunking and chunking into my lifutils.py code. Indeed, once I managed to extract the ROM code (a future story), I learned that the ROM disk code automatically chunks files being written and unchunks them on read, so we should really think of the chunking headers as a special feature of the LIF file system as found on the HP 165x units.

  • Data transfer

    Alexander R Pruss01/23/2026 at 03:17 0 comments

    The HP 165x analyzers use the LIF file system, like many HP computers. The HXC-firmware Gotek uses HFE files. (I tried RAW files on the flashdrive, but they didn't work.) But it's possible to convert between HFE files to RAW format with the HXC floppy emulator software, and RAW format is just a series of sectors, which should make a perfectly fine LIF image.

    hxcfe -finput:FILE.HFE -conv:RAW_LOADER -foutput:FILE.LIF

     You can then extract files from the FILE.LIF volume using the well-established lifutils. And in theory you can change the FILE.LIF volume, and turn it back to an HFE file on your Gotek's flashdrive with hxcfe again. 

    Except there are some hiccups. There is no layout file for HP 165x analyzers included with hxcfe. My first attempt was by converting the HFE file to XML, and then it was pretty easy to write a python converter from LIF to XML, which hxcfe can convert to HFE. 

    Eventually, I made an HP layout file. The latest version is here (it's improved from the original because it includes special track 79 stuff which has nonstandard sectors that the units use to check if they want to boot from the floppy). So you can now convert from LIF to HFE:

    hxcfe -uselayout:hp165x79.xml -finput:FILE.LIF -conv:HXC_HFE -foutput:FILE.HFE

     But there are still difficulties. While I was able to extract a SYSTEM_ file from the main floppy and a PVTEST_ (PV = Performance Verification, apparently) file from the self-test flloppy, lifutils refused to write them back for multiple reasons. First, the LIF volumes were lacking some metadata. Second, the file IDs for HP 165x executable files, namely C001, are not supported by lifutils. I could (and did) modify lifutils to get around one or both of these issues, but it was time to write a LIF tool more specialized to the HP 165x setting.

    So I wrote a little lifutils.py python utility (latest here) thanks to this great writeup. I had to do be careful about the special track 79 stuff. Now I could write data back and forth. In particular, I could change a text stringin SYSTEM_ with a binary editor and see the changes show up on the oscilloscope screen. Hacking is on!

    It later turned out that I was missing one crucial ingredient about how the file system worked. But that's for the next installment.

  • The Gotek

    Alexander R Pruss01/17/2026 at 14:26 0 comments

    I've always been worried about aging magnetic media. Late last year, my faithful oscilloscope didn't boot up. Fortunately, I had multiple copies of the system discs (the OS lets you make copies of a disc), so I was able to get it back up, and I made a new copy. But I was worried. I finally got a Gotek to replace the aging Sony MP-F52W-30 drive, made an adapter to interface the HP's Sony-compatible cable with the Gotek (there are some instructions here and more details here). Getting the disc change connection working was the hardest. It turns out that while my SFRKC30.AT3 Gotek looks from a distance like an SFRKC30.AT2 Gotek, it exposes the PA14 pin in the same place as the older SFRC922, and so on the AT3 one should follow the older unit's instructions.

    I was now able to use HFE files (which are a complicated file format that handles all sorts of non-standard discs) on a flash drive to boot the oscilloscope. Yay! To this day (January 17, 2027), I still haven't been able to get direct image files to work, though.

  • The beginning

    Alexander R Pruss01/17/2026 at 14:13 0 comments

    About a decade ago, I saw an HP 1653B Oscilloscope and Logic Analyzer on Craigslist for $50, including probes, pods, diskettes and three volumes of manuals. I was going to buy it, but when the seller heard about my hobby interests, he just gave it to me for free. Thank you!

    The copyright date on the ROM code is 1987. It's a great unit. Reliable and nice. I've used it as an oscilloscope (but not logic analyzer) for many projects. There are full manuals here.

    The system has a 3.5" floppy drive which it boots from. I've always been afraid of the floppies failing.

    Some years back, I made a Greaseweazle out of a Blue Pill and an old PC floppy drive. This let me transfer the system disc to my PC:

    gw read --format ibm.scan --tracks=c=0-79:h=0-1 outputfile.hfe::bitrate=500

    The HFE file can then be converted to a raw file with SAMDisk

    SAMDisk copy outputfile.hfe outputfile.raw

     The file system is LIF and I was able to use lifutils to extra the SYSTEM_ operating system file from the raw files. I tried to disassemble it with Ghidra in order to run my code on it, but failed (more on that later).