Beckman DU600 Reverse Engineering

Reverse engineering process for a 68332 based system

Similar projects worth following
Late 2014, I found a Beckman DU600 spectrophotometer in the trash. I took a quick look at the back and saw a VGA port, some serial ports, and a parallel port, so I decided to take it. Upon disassembly, I saw that the motherboard of the unit was based around the motorola 68332 microprocessor. It had 1MB onboard ROM, 512K ROM in an addon card (with sockets on that card to increase to 1MB), 256KB battery backed SRAM onboard, 1MB battery backed SRAM on a card, an Epson RTC chip, 1MB of DRAM, 512KB of video RAM for the onboard SVGA controller (Cirrus Logic CL-GD5429), discrete logic for a printer port (IEEE 1284), 1 onboard DUART, 1 more DUART on a daughtercard, and an AT keyboard controller chip (a real live NEC 8741, similar to the intel i8042 used in IBM PC/AT's). In addition to all of this (as if the system wasn't capable enough already) there's plenty of motor drivers, special stepper motor control circuitry, general purpose I/O lines, at least one DAC and a dual ADC.

I have been reverse engineering the hardware of this 68332 based system in order to run my own software on it. What that software will be specifically, I don't exactly know yet.

This is a link to all of the pictures:

Beckman DU600 pictures

Note: many of these pictures are quite large, up to about 5MB

Here is copy and paste from the text file I use to document anything I reverse engineer from the hardware:

default setup of address space
this could change if software changes it

$00 0000 - CSBOOT, 1MB, onboard boot ROM
$10 0000 - CS0, 1MB, card 1 & 2, addon ROM card
$40 0000 - CS1, 1MB, card 3 & 4, onboard DRAM
$5C 0000 - CS8, 256KB, card 8, onboard SRAM
$60 0000 - CS2, 1MB, card 5 & 6, SRAM card
$B0 0000 - CS7, 1MB, 8 bit port, videochip
$D0 0000 - CS5, 16KB, I/O
$E0 0000 - CS9, 2KB, 8 bit port
$F0 0000 - CS10, 64KB, 8 bit port, I/O
$FF E000 - TPURAM, 2K
$FF F000 - internal CPU on-chip registers

CS5 controls U210, a 74AC138 for address decode
CS9 goes to a pin on J3, and nowhere else as far as I can tell.
CS10 goes to U25, a 74AC138 for address decode. also goes to pin on JA1 for daughterboard.

hardware I/O addresses:

CS5:0-7FF - UART1
This is the base address for the 68C681 on the mainboard. The base set of registers is repeated to fill the block of addresses.

CS5:802 - LEDS - doesn't clear IRQ
CS5:800 - PPORT - clears IRQ
This write address controls the parallel port output pins and the 4 onboard LEDs. Bit 15, 14, 13, and 12 control CR 50, 51, 52, and 53 respectively. Bit 11 controls the /SELECT_PRINTER line. Bit 10 controls the RESET line. Bit 9 controls the /LF line. Bit 8 controls the /STROBE line. The lower 8 bits control the data outputs. This address is repeated in the range 800-FFF everytime ADDR2 is 0. If any address in this range is read or written which has ADDR1=0 then the /ACK interrupt is also cleared.

TODO: figure out how to read the status lines for the parallel port, should be U57

CS5:1800-1FFF - J4

CS5:2000-27FF - RTC
Base address for Epson RTC registers

CS5:2000-2800 - EEPROM

This address is used to write U719 (74AC573), an 8 bit latch. The actual chip seems to have the data lines hooked up backwards from the numbering in the datasheet, and it's attached to the high bits of the CPU data bus. The output of this latch is enabled or disabled by TPU channel 15. A 0 on the TPUCH15 pin on the 68332 enables output from this latch. Bit 6 and 7 appear to be unused. Bit 5 controls the DRAM controller /DISRFSH line. Bit 4 controls the DRAM controller /ML line. Bit 3 and 2 go to the PSU connector. Bit 1 and bit 0 seem to mask interrupt sources from the power supply board. A 1 masks the interrupts. The interrupts share a single interrupt latch flip flop and use IRQ1. They are cleared by a reset or reading this address.

CS7:0-FFFFF - ISA bus/SVGA controller
The SVGA controller is mapped here. 0-7FFFF seems to be mapped to I/O reads and writes (not confirmed) while 80000-FFFFF seem to be mapped to memory reads and writes. This is necessary because the video chip is made to work on an ISA bus which has separate memory and I/O spaces while the 68K does not.

chip is setup for MCLK on MCLK pin
ISA bus
Symmetric DRAM
single WE, multiple CAS
6 MCLK RAS cycle
44.74431MHz MCLK
32K ROM BIOS at C0000-C7FFF
8 bit BIOS ROM
Internal MCLK
3C3h is video system sleep register

CS7:02000-02007, 02406-02407 - ATA registers
This is piggybacked into the pseudo ISA bus used for the VGA controller. Decoding is done using a 74ACT138 decoding the top 6 address lines (A15-A10).

CS10:0-3FF - UART2
This is the base address for the 68C681 on the daughtercard. The base set of registers is repeated to fill the block of addresses. This also reappears at 2000-23FF

CS10:400-7FF - KB controller
odd addresses are status/command, even addresses are data. These registers may be repeated at 2400-27FF

This address writes to U18 (74AC573) on the daughtercard. It's bits control various motor functions on the daughtercard. Bit 7 seems to be disconnected by a jumper....

Read more »


description of hardware on the board

plain - 8.67 kB - 12/23/2016 at 00:07


  • Discovering a New VGA Mode

    joe.zatarski12/23/2016 at 06:33 0 comments

    Given that the CL-GD5429 on the Beckman motherboard only has 512KB of memory, yet supports 1280x1024 modes (and 1600x1200 interlaced), I began to look into ways to support higher resolution modes about a year ago, sometime in early 2016. 1280x1024x4bpp uses 640KB of video memory. If I could find a way to reduce the bit depth to 1 or 2bpp, I would cut the memory usage down enough to support a 1280x1024 display in the limited 512KB of memory available to the 5429.

    Having some experience with the common modes supported on VGA, I knew that there were CGA compatibility modes using 1 and 2bpp color depths, so this is where I began my search. However, I quickly realized that these modes were not efficient with memory. They were designed purely for compatibility, and nothing else, effectively being 4bpp modes themselves that masked off 2 or 3 bits resulting in the 1 and 2bpp modes. This was a dead end.

    However, during my browsing of the 5429 technical reference manual, I noticed a pair of bits that sounded promising. These two were the 'Shift and Load 32' and 'Shift and Load 16' bits in the VGA Sequencer's clocking register (SR1).

    According to the Cirrus Logic technical reference, the Shift and Load 32 bit, "controls the Display Data Shifters in the Graphics Controller according to the following table:"

    SR1[4]SR1[2]Data Shifters Loaded
    00Every character clock
    01Every 2nd character clock
    1XEvery 4th character clock

    To me, these two fields sounded potentially like the answer to 1 and 2bpp modes. One 'character' on the VGA when doing graphics was 8 pixels. Due to the organization of VGA memory being (logically) 32 bits wide, 8 4-bit pixels normally fit into one word, meaning you would logically load the video shift registers once per character clock. Together, the '4' video shifters would shift out 4 bits at a time, one set of 4 for each pixel. However, in 2 or 1bpp modes, you would have 16 or 32 pixels per word respectively, equaling 2 or 4 characters worth of pixels each. So, in this case, you would load the data shifters once every 2 or 4 character clocks since one word now was equivalent to that number of characters worth of pixels. While the video shift registers are normally thought to be 4 independent 8-bit shift registers, I theorize (at least in these modes) that the shift registers actually feed into each other, producing a 32 bit shift register chain, with 'taps' every 8 bits for the bits which go to the attribute controller (which translates 4 bit values into the 8-bit color values going to the pallette DAC).

    However, this brings up another point: 4 bits still go to the attribute controller, even in these cases where only 1 or 2 bits should be used. These can cause odd colors to be produced as the bits shift through the shifters, and the farther right pixels act as more significant bits. For example, suppose we have a monochrome bit stream loaded into the shifters like this:


    8 pixel clocks later, the shifters will look like this:


    In the first example, the 4 bit attribute is 0110, while in the second it is 0011. For a 1bpp mode, we actually only care about the LSB of this nibble. Luckily, there are ways to remedy this in the attribute controller. The first way, is to use the 'Enable Color Plane' bits in the 'Color Plane Enable Register' (AR12). These bits allow you to AND mask off the other bits in the attributes. Loading with a value of 0001 selects just the LSB for 1bpp modes, and loading with a value of 0101 selects the two relevant bits for 2bpp modes. The other way to remedy this issue is to load the attribute controller palette registers such that the colors selected are independent of the bits which should not influence the color. For example, making all of the even attributes one color, while all the odd attributes are another color, effectively ignores all but the LSB, giving the same effect. Similarly, making colors 0000, 0010, 1000, and 1010 all the same...

    Read more »

  • Creating my own machine language monitor

    joe.zatarski12/23/2016 at 00:30 0 comments

    Some time after getting zBUG to run, I decided to write my own monitor. zBUG had some quirks that I didn't like about it, particularly the non-sanitizing input routines which had no way to make corrections, and interpreted otherwise invalid characters in unfortunate ways (such as 'G' in hexadecimal inputs having a value of 16). In my opinion, the zBUG code wasn't well suited to modification into a more usable monitor, so I decided to begin writing my own from scratch.

    The code for this monitor is here:

    My first goal was to get a simple command parser. This command parser would search an array of commands, comparing the command that was typed with strings stored in the monitor corresponding to the commands that are supported. Once the framework for the command parser was there, the next goal was to create a way to download s-records into the memory of the board. Afterwards, the next goal was to create a boot command that would simulate the boot process of the CPU32. These three features together allowed me to finally replace zBUG (which was currently in ROM) with the new monitor. As I added features, I could test them by assembling a RAM version of the monitor, downloading that into memory, and using the boot command. Now the new version of the monitor would be running. This way, I didn't have to erase and reprogram the old UV EPROMs every time I had a new version of the monitor, as this is a somewhat time consuming process.

    Development of this monitor has continued. I added support for command arguments to the command parser. Arguments are passed somewhat in the C style of an argc int (in register d7) and an *argv[] pointer to an array of string pointers (dynamically allocated on the stack) in a6, with each pointer pointing to one argument. When the command returns, the command parser removes the dynamically allocated stack variables.

    I also added handling and reporting of various exceptions, including full parsing of the CPU32 BERR exception stack frames.

    Now, I am currently working on a disassembler, after which I'll finally add single stepping support for running and debugging code.

    Future planned features include:

    PATA disk support

    virtualization support

    some sort of TRAP based API for various I/O functions

  • Adding 16 bit ISA support

    joe.zatarski12/22/2016 at 23:12 0 comments

    Today I will be adding 4 project logs, 3 of which are a bit overdue.

    For the first, sometime back in June of last year, I began to look into the simple ISA implementation used to interface the Cirrus Logic CL-GD5429 SVGA chip to the 68332. My intention was to find out if the limited subset of the ISA signals was enough to add a simple PATA interface without much trouble.

    What I discovered is that most of the ISA signals are created within U44, a GAL22V10. The pinout, as best as I could tell, is listed in the hardware description. Not all of the pins are directly used, but still carry signals, and are instead reused internally in the GAL. These have been marked NC, and little to no attempt was made to discover what exactly they do or whether they are useful.

    I don't remember exactly now, as my memory is a bit fuzzy, but I think a single transfer takes at least 4 cycles of the CPU clock before DSACK (depending on whether or not the device requests additional wait states). This means the ISA bus implementation is effectively running at 1/4 the speed of the CPU clock, or about 4MHz (given the CPU runs at approximately 16MHz), resulting in 4MT/s absolute maximum.

    However, during this endeavor, I noted that the video chip was only wired for 8 bit transfers! This not only cuts the potential performance in half, but it would make adding a PATA bus difficult since 16 bit transfers are a requirement of the PATA bus since the ATA-2 spec, if I'm not mistaken. (Of course, neither of these two things are actually issues if you're using the board as it was intended, in a Beckman DU600. Graphics speed was certainly no concern, and board layout simplicity was probably a bigger concern.)

    Maybe the order of these two things are actually reversed, but the result is the same: I set out to develop circuitry that would implement 16 bit ISA transfers, not only to improve graphics performance, but also to allow the possibility of adding a PATA bus at a later date.

    I began reading the ISA specs, and refreshing my memory on the way CPU32 dynamic bus sizing worked.

    The 68332 works on an asynchronous bus design: That is, the CPU reads or writes a memory location, and then waits for the device to acknowledge the transfer. This is a DSACK, or Data and Size ACKnowledge. The 68332, having a 16 bit external bus, allows 16 bit or 8 bit transfers per cycle. Whether the transfer was 8 or 16 bits is determined by the device being accessed. There are two DSACK signals, DSACK0 and DSACK1. By asserting one or the other DSACK line, the device can acknowledge an 8 bit, or a 16 bit transfer. Additionally, there are two SIZ output signals from the 68332, SIZ0 and SIZ1. Because the 68K line is 32 bits internally, the 68332 considers transfers up to 32 bits as a single transfer, even though they take two physical transfers on the 16 bit bus. The SIZ outputs indicate the number of bytes left to be transferred, 1, 2, 3, or 4 (0 means 4). The only way to get 3 is after a single byte transfer on a 4 byte operand transfer.

    During a 32 bit write, the 68K will always place the upper word on the bus first. During a 16 bit write, the 68K just places the entire word on the bus. Lastly, during an 8 bit write, the 68K actually duplicates the 8 bit data on both halves of the bus. This is done so that 16 bit devices can use the half of the bus they prefer (depending on ADDR0) but 8 bit devices will always have the data available on the upper half of the bus. During a 24 bit write (a special case of a partially completed 32 bit write), the 68K will duplicate the upper byte across the data bus, similar to the 8 bit write.

    During a 32 bit or 16 bit read, the 68K will always latch an entire word from the data bus, but depending on DSACK, may only use the upper byte if the transfer acknowledged was only 8 bit. During an 8 bit read, or a 24 bit read (again, special case of partially completed 32 bit transfer) the 68K will only use one byte of the data from the bus. If the transfer was 16 bits, the 68K...

    Read more »

  • shoutout to r/retrobattlestations

    joe.zatarski08/06/2015 at 04:20 0 comments

    Adding a pic to make this a valid entry into 68K week rewind at r/retrobattlestations.

  • Finishing Up Interrupt Stuff

    joe.zatarski06/07/2015 at 04:50 0 comments

    I have finished reverse engineering the interrupt acknowledge circuitry, mostly AVEC, DUARTs provide their own vector.

    copy and paste from my hardware notes text file:

    U29 is interrupt acknowledge decoder. For this to work, FC0, FC1, and ADDR19 must be enabled in place of CS3, 4, and 6.
    IRQA7 - AVEC
    IRQA6 - AVEC
    IRQA4 - JA1 pin 25, UART2 IACK
    IRQA3 - AVEC
    IRQA2 - AVEC
    IRQA1 - AVEC

    I took a look at the IRQ lines using a scope to confirm that IRQ3 was the issue (if you recall from the last post, the RTC periodic interrupt vector) and saw pulsing on IRQ3 at 64hz (makes sense) but also a constant asserted state on IRQ2 which I found interesting. It seems that due to the way the parallel port IRQ circuitry is designed, whether it starts up requesting an IRQ or not is dependant on the behavior of a flip flop which has its active low reset input tied high via a resistor during power on. Anyway, this can result in the parallel port interrupt being asserted after reset. This is a simple fix, I just have to write or read any address in the CS5:0800-0FFF range which has ADDR1 low and the IRQ clears.

    The RTC required a bit more reverse engineering. I started by finding reference documents on the particular RTC used, an Epson RTC72423. I saw that this RTC has two chip selects, one active high, the other active low. The active low chip select was driven by a 74AC138 which decoded the address to CS5:2000-27FF. The active high chip select was driven by the active low RESET line. This is to prevent the RTC from accidentally being written during a RESET or during power on. Next, I checked where the data lines connected: D0 to DATA8, D1 to DATA9, D2 to DATA10, and D3 to DATA11. This makes perfect sense for a big endian architecture of course, as a single byte read will give you the data of the RTC this way. I suspected the address lines would be attached A0 to ADDR1, A1 to ADDR2, and so on since CS5 is normally setup to be a 16 bit port (more on that in a later post maybe). However, this was not the case. A0 was connected to ADDR0, A1 to ADDR1, and so on. I realized at this point, that the RTC must take advantage of dynamic bus resizing to force the bus to 8 bits when it is accessed. Sure enough, the select line for the RTC also went to some circuitry which added an RC delay and asserted DSACK0, thus indicating to the 68332 that the access should be 8 bits wide.

    I examined these memory values in the monitor, and sure enough it looked like the RTC. I could see the seconds counting up, and the seconds carrying over to minutes. All that's left to do is to see if I can write some code to set the RTC, and more importantly, disable the periodic interupt (or just handle it).

    Also, I think the upper 4 bits of the data bus are just left floating during RTC accesses, but I'm not sure. Every read I did resulted in #$6 in the upper nibble. The chip enable line didn't seem to enable anything other than the RTC, but I didn't look too extensively.

  • What I have So Far

    joe.zatarski06/06/2015 at 05:02 0 comments

    Here is a summary of everything I've done so far:

    I have figured out quite a bit of the architecture of the thing. I know the addresses of the ROM, ROM card, DRAM, SRAM, SRAM card, EEPROM, both DUARTs, the parallel port, the keyboard controller, and the SVGA chip. Today, I also finished figuring out all of the external IRQ sources. Next is to figure out how the interupt acknowledge for all of them work. I suspect the DUARTs provide their own vector number, like normal, and all of the others likely autovector since the others are either discrete hardware or do not have built in functionality to provide it's own vector.

    The first demo I ran was to blink some LED's about 1 time per second. The second I printed a test message over a serial port to my DEC VT420. The third, I initialized the SVGA controller and showed a picture in 640x480 16 color mode. I wrote a 4th demo to play a bit of music utilizing the 68332's built in Time Processing Module (TPU) by instructing the TPU to produce PWM waveforms of the correct frequency and 50% duty cycle. Next I ported EhBASIC to it, and messed around with that for a while.

    Right now I'm working on porting a machine language monitor to it, zBug. Initially this was crashing right after it enabled interrupts. I edited the source and kept interrupts turned off, and sure enough, this fixed it. However, I decided it was finally time to trace out all of the IRQ lines. Here's what I got:

    IRQ7 - goes to J3 and U27 pin 13. appears to be used for expansion, possibly the optional floppy drive interface.
    IRQ6 - JA1 pin 26, KBD controller output buffer full interrupt, pin 35
    IRQ5 - UART1 interrupt request
    IRQ4 - JA1 pin 5, UART2 interrupt request
    IRQ3 - RTC periodic interrupt
    IRQ2 - U26 pin 4, parallel port /ACK interrupt. Indicates printer is ready for more data.
    IRQ1 - JA1 pin 6, causes U14 on daughterboard to latch, U26 pin 2, used for 3 interrupt sources on the PSU board, two maskable, the third not.

    IRQ3 is likely the one causing the crash. I bet it's firing shortly after the interrupts are turned on, vectoring to an unhandled interrupt, and then causing a crash. I'll finish up the IRQ stuff by figuring out the interrupt acknowledge stuff, then find the address of the RTC so I can turn off the periodic interrupt in my init routines and clear it.

View all 6 project logs

Enjoy this project?



Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates