Category Archives: Reverse-Engineering

Reverse Engineering a 35 mm Animation Camera

Last week I visited Labor Berlin, an independent film laboratory dedicated to experimentation with motion picture film. They do everything film-related, from making their own chemical solutions to filming, processing, and screening, in all kinds of films (8 mm, 16 mm, 35 mm…). It is pretty much a hackerspace, but for film-related stuff.

One piece of equipment they had there was a Crass Tricktisch. It’s a film animation table and an animation camera, made by Crass GmbH.

Crass logo

The table and camera were given to Labor Berlin by L’Abominable, an independent French film laboratory just like them.

Basically, you have a table to draw your animation, a camera looking at the table from above, and a controller that lets you take single pictures, move the film forwards, backwards, and do some funky stuff like cross-fade.

Crass Tricktisch

They had a couple of cameras to use with the table, one super 16, and another 35 mm. The super 16 camera was connected to the animation table and they had already used it a few times, but the 35 mm camera had never been tested by them, since the connector was different, and wouldn’t attach to the table.

The Kamera

I got curious about how hard it would be to adapt the 35 mm camera to fit the connector, or vice-versa. We searched around in the Internet and found this website with some documentation about the table and the camera. These documents were written in the 60’s, 70’s, and some even in the 90’s. Part of the documents appear to be original technical manuals, others are hand-drawn, and others appear to be previous attempts at reverse-engineering. There was also a more official-looking manual at the laboratory with some nice drawings (like the image of the table above).

Kameraschaltung

The electrical schematics are a bit old-fashioned and they’re written in German, so I thought it would be easier to reverse-engineer the whole thing from the start than to try and understand German =).

I picked up a screwdriver and started opening the camera, taping each screw to a piece of paper with a description of where the screw came from. From my previous reverse-engineering endeavours, I know very well that it is important to document where each extracted part comes from. I don’t have a photographic memory, nor much of a good short term memory, so I usually end up with many pieces left after reassembly.

First view inside

After opening the first side, I could see the connector and the motor. The schematic had the description for the connector, but in all the manuals there were about 3 or 4 different description for cameras, so I didn’t know which one was correct. I opened up the whole camera, followed all the wires (even some tricky wires that turned out to be just stubs), tested all the pins, and came up with this schematic:

schematics

The camera uses one main motor to turn everything. It’s incredible how they managed to do everything mechanically. The same motor takes care of pulling the film, activating the shutter (in 3 different selectable speeds!), activating the film break system (so that the film is stopped while the shutter is open), counting the number of frames, and probably other things that I can’t remember right now.

The shutter uses a sliding window to give different exposure times, and there are two micro-switches directly related to the shutter. One of them changes state when the shutter is about to open, and the other changes state when the shutter is about to close.

The sliding shutter window uses an electrical circuit connected to a mechanical system to give the proper exposure time. There’s a complex mechanical system that automatically changes the exposure time, in order to do fade-ins and fade-outs. I spent quite some time trying to understand all of it, but then just gave up, and decided I wouldn’t want to use it anyways.

Electro-mechanical shutter system
The electro-mechanical cross-fade system

I was interested in understanding how the motor was controlled. A label in the motor read:

dunkermotor
Typ: SY 52 x 60 - 2
Nr: 36124
24V 3000 U/min

There are three wires going into the motor, so it’s obviously a three-phase motor (also that’s what it says in the schematic). There didn’t seem to be any switches to turn the motor on or off in the camera itself. So it must have been the animation table that controlled the camera through the connector.

I plugged in the (working) super 16 camera into the table, and unscrewed the connector casing. The connections were (apparently) the same as in the 35 mm camera. When the camera was stopped, the multimeter accused anything from ~19 VAC to ~34 VAC between each two terminals of the motor, which seemed odd. Nothing was moving, so I just assumed it was some stray voltage with no actual power. I thought about touching it with my fingers to make sure there was no power, but then I paused for a second, thought some more, and reminded myself that a 30 VAC electrical shock can be quite annoying. With the camera running, the multimeter accused about 24.7 VAC between each two terminals of the motor. So now I knew, it was the animation table that switched the motor on and off, not the camera itself.

Tricktisch
Der Tricktisch – The Animation Table

I didn’t feel like opening the animation table to see the circuit inside, since it seemed quite simple: at some point, the motor is switched on by just feeding it three-phase power.

Crappy picture closeup on animation table knobs
Crappy picture closeup on animation table knobs

There is one knob on the animation table that reads “Rückwärts / Vorwärts”, which means “reverse / forward”. If we switch that and turn the camera on again, it magically turns the motor in the other direction. The film runs backwards, the shutter runs backwards, everything runs backwards. How does the table change the motor direction? In a three-phase motor, it’s just a matter of switching any two phases. We’ll see more about this in another post.

What does this all mean in relation to the motor and the connector? It means that turning the camera is just a matter of feeding it three-phase power directly through the connector. To test this, we decided to use the super 16 camera to jump start the 35 mm camera.

Jump starting 35 mm camera
The three white wires are coming from the super 16 camera

Since at first we didn’t have the right tools to do this jump start, the wires were very delicately positioned in order to just touch the connectors. At any moment a wire could slip and cause a short circuit. Luckily, that didn’t happen.

We connected a continuity tester from a multimeter to the “shutter open” switch, so that we could test that the switches were working properly. In this video you can see the result of all the hard work we did:

 

Now we know how the camera works. We could just build a new connector that fits into the animation table and use the 35 mm camera. But, since we know how the camera works, we can do much more! We have control over the basic operation of the camera. We know how to move the film back and forth, and we know when the shutter opens and closes. We did all that just by connecting a few wires. We don’t need the animation table to do all the work for us. We can recreate a stand-alone circuit that controls the camera in any weird way that we can think about. Long exposures, multiple exposures, sensor-activated filming, time-lapse photography…

So, what’s the next step? Replacing the animation table by an Arduino and a simple electronic circuit =).

Using MESS as the system’s printer

What was the main reason I started MESSing with the ActionPrinter 2000? To extract the characters’ bitmaps so that I could optimize image->ASCII conversion to the printer’s specific font.

As a fun side-project I decided to configure MESS with CUPS as a normal printer on my system to see how well it would work.

I started off by implementing the Centronics communication used by the printer in MESS. This way, it was possible to boot any device with a Centronics interface connected to the printer, as if I was actually running a device and the printer itself. For this, I booted a simple 486 with an MS-DOS 6.22 floppy disk inside MESS.

486

The parallel port communication with the printer was a little bit tricky.

ActionPrinter 2000 Centronics Interface Overview

When a strobe pulse is detected, the E05A30 gate array sets the BUSY signal. When the CPU is done reading the data and it’s time to unset the BUSY signal, the firmware does the following:

  1. Read the control register (which contains the ACK and BUSY signal) to a CPU register;
  2. Unsets the ACK signal in the CPU register and outputs everything to the control register;
  3. Unsets the BUSY signal in the CPU register and outputs everything to the control register;
  4. Sets the ACK signal in the CPU register and outputs everything to the control register.

The printer’s manual had conflicting information about which component was supposed to take care of setting/resetting the BUSY signal. Another printer’s manual (the LX-1050+, which uses the exact same gate array), had even more conflicting information about the Interface overview.

Besides that, the 486 I was using with the printer would ignore the ACK signal from the printer. The result was that between steps 3 and 4, the BUSY signal was unset, so the PC would ignore the ACK signal and send a new character. But step 4 would reset the BUSY signal again, since it had read the control register to a CPU register, and did not detect that the BUSY signal had changed! The PC would then send another character, so the printer had just “lost” a character somewhere in the middle of all that.

After quite some time trying to find the problem and discussing this issue with smf on IRC, he told me some very wise words:

<@smf> never assume that the programmer knew what he was doing

Indeed, the firmware would make much more sense if we assumed that the programmer who wrote it made a few mistakes. I had already seen a couple of nasty bugs in the firmware, such as these ones:

/* 7851 */
int cr_not_full_step(void)
{
    /* there is a bug in this function, it searches 5 items
     * into an array which only contains 4 items. The last
     * item (0x34) is part of the first instruction in the
     * next function:
     * 7864: 34 4A 10           LXI     HL,$104A
     */
    uint8_t full_steps[5] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x34 };
    uint8_t cur_step = [0xC008];
    for (int i = 0; i < 5; i++)
        if (cur_step == full_steps[i])
            return 0;
    return 1;
}

 

/* 00CA */
uint16_t mul16(uint16_t EA, uint8_t A)
{
    /* uses EA, BC, and A */
    uint8_t h = in16 >> 8;
    uint8_t l = in16 & 0xff;
    uint16_t mh = (uint16_t) in8 * h;
    uint16_t ml = (uint16_t) in8 * l;
    uint16_t r = 0;
    if (mh & 0xff00) {
        /* check is wrong, we don't care */
        carry = 1;
    } else {
        r =   mh;
        r <<= 8;
        r +=  ml;
    }
    return r; /* EA */
}

 

And so I added a little check to the gate array that made it impossible to reset the BUSY signal before the character had been read. The PC inside MESS was now capable of talking to the printer. I got this as a result of “dir > LPT1“:

Did you notice that the lines are not very well aligned vertically? This happens because, up until that point, I would assume that the stepper motor positions would be exactly what the last step was set to by the firmware. This is not really the case, since the motor is not being used one step at a time. It does two steps at a time, and the printhead fires twice at different moments while the motor is still moving. The firmware is very well designed, in such a way that all this movement is taken into consideration when the printhead is moving either from left to right or from right to left. It even considers the 300 microseconds it takes for the printhead to hit the paper! I implemented all this dynamics into MESS and got perfect vertical alignment (funnily enough, even better than the actual printer, which is 20+ years old by now).

Anyways, now I knew how to make the printer receive data inside MESS. But what about receiving data from outside of MESS?

I created a Dummy Centronics driver in MESS that would just read from a named pipe and feed that to the printer. At the other end of the named pipe I could print whatever I wanted from my host system with “cat file > /dev/lp0“, such as this Lorem Ipsum:

Would it be possible to add this printer to CUPS and use it with any other program? Of course. It’s quite easy too:

$ sudo mkfifo /dev/lp0
$ sudo chmod 666 /dev/lp0 # bad idea but simple solution
$ sudo chown root.lp /dev/lp0
$ sudo lpadmin -p ap2000 -E -v parallel:/dev/lp0 -P Generic-ESC_P_Dot_Matrix_Printer-epson.ppd

The printer needs this PPD file so that CUPS knows how to convert anything to the only language the ActionPrinter 2000 understands (ESC/P). At first I had configured the device as a raw printer, so CUPS would happily send PostScript commands to the printer, which would just print them as plain text.

Now I was able to go to LibreOffice and print using the ap2000 printer:

And even GIMP!:

The printer’s resolution is 120×72 dpi. The pixels are not square: every 10 pixels horizontally are the same size as 6 pixels vertically.

MESSing with the ActionPrinter 2000

We receive all kinds of junk and old electronics at Tarrafa Hackerspace. One day, we received an Epson ActionPrinter 2000.

Epson ActionPrinter 2000

The ribbon was dry, but the printer still worked, so we bought a new ribbon and started having fun printing out ASCII-art stuff.

The ActionPrinter 2000 offers a few different kinds of fonts and modes: Draft, Near Letter Quality (Roman and Sans Serif), condensed mode, 12 characters per inch, 10 characters per inch, subscript, superscript… It occurred to me that the font used for the ASCII-art conversion programs was hardcoded, and probably didn’t relate to the one used in the ActionPrinter 2000.

So I was interested in mapping out the character bitmaps to get a better ASCII-art conversion. My first thought was: “let’s get a magnifying glass and map the characters dot by dot!”.

Mapping the dots using magnifying glass

Sure, that worked, but it was time consuming and not the smartest way… There should be some better way. To print out the dots, at some point there must be an electrical signal being sent to the printhead for each dot. So, what if I intercepted that signal and used it to map out the characters?

I opened up the printer and looked for the printhead connector. But then I found a 27c256 EPROM inside, which likely contained the firmware. Hey, the firmware has all the characters inside it somewhere, that’s better than fiddling with the printhead. I got the 27c256’s datasheet and used an Arduino Mega to dump its contents.

Dumping the 27c256 EPROM with an Arduino Mega

After reading through the dump for quite some time, I found some bitstreams that looked like characters, but they weren’t very well organized, so I couldn’t make sense of their structure. Then I remembered that the last time I had visited Garoa Hackerspace, it was Retroprogramming night, and Felipe “Juca” Sanches was talking about emulating old hardware using MAME/MESS. Well, what if I emulated the firmware and recorded the data before it was sent to the printhead?

So I started fiddling around with MESS. It already had some incomplete drivers for the Epson LX-800 and Epson EX-800 printers. I used them as a basis to get my head around the MESS codebase and add support for the ActionPrinter 2000. It shouldn’t be so hard, right? (whenever I ask “right?”, the answer is “wrong”).

So I added the ActionPrinter 2000 firmware and fired up MESS. The processor hanged. I spent some time looking for buttons or switches being read by MESS, maybe just tweaking them would get past the hang. That wasn’t the case, it would still hang and there was no way out of it. Now, if only I had a debugger to see what was wrong… Oh, MESS has a debugger.

MAME/MESS debugger

So, the ActionPrinter 2000 uses an uPD7810 processor. I needed to get its datasheet to understand the assembly. After stepping through the assembly for quite some time, I realized the uPD7810 emulation in MESS was lacking a few functions. There was no ADC support, which was necessary to read the input voltage and some switches. If the printer’s input voltage was too low, the printer would not boot.

So I implemented ADC support, and the firmware would just hang again a few instructions later. What was wrong this time? An interrupt wasn’t being set inside the processor. Who should be setting that interrupt?

I had already started reverse-engineering the printer’s hardware, but now I knew it was important to map all hardware connections in MESS. There were many other integrated circuits: a gate array (E05A30), a RAM chip (2064C), a 256-bit EEPROM (93c06), a stepper motor driver (SLA7020M), and others…

The gate array that was partly implemented in MESS was the E05A03, not the E05A30. It’s a custom-made gate array, and works pretty much like a black box. There’s no way to find out what goes on inside if you don’t have access to the datasheets (which I didn’t). I created some skeleton code for the E05A30 gate array for MESS.

I found out the interrupt that wasn’t being set should have been set by the gate array. So I added an interrupt request right after the gate array started.

I got a few more instructions being run and the firmware would hang again. At this point it was getting complicated having to read the assembly all the time, so I started manually decompiling the firmware. It is a 32 KB firmware. It shouldn’t take too long, right? (remember: the answer is “wrong”)

With a bit of decompiled code, I got this snippet:

void reset_ram(uint8_t val)
{
	memset(0x9800, val, 0x2000);
	if (*((uint8_t *)0xb7ff) != 0xff) {
		killall();
		beep(4, 2);
		while(1); /* HANG */
	}
}

void _start()
{
	[...]
	reset_ram(0xff);
	reset_ram(0x00);
	[...]
}

That didn’t make much sense to me. The RAM was only 8 KB, starting at 0x8000 and ending at 0xa000. There should be nothing at 0xB7FF. Even if there was something, it was being set to 0xFF and then to 0x00, but the data was expected to stay at 0xFF. It might be something inside the gate array, I don’t know. I just created a fake memory device that worked just as was expected for the emulator to be happy.

I got some more instructions, but it entered an infinite loop. Hey, that’s better than hanging…

After much more debugging and decompiling, I realized that the printer was outputting some commands to the gate array in a loop. There were 8 commands being sent and there would be a check for a sensor in the board. I looked at the sensor in the printer, and it turned out to be the Home sensor, i.e. whether the printhead was back at position 0. The command sequence being sent looked very familiar, like the phase signals for a stepper motor. MESS already had stepper motors implemented. I just needed to adapt the command sequence, because it passed through the SLA7020M before reaching the motor. And then I got this:

:__________@_______________________________:
:_________@________________________________:
:________@_________________________________:
:_______@__________________________________:
:______@___________________________________:
:_____@____________________________________:
:____@_____________________________________:
:___@______________________________________:
:__@_______________________________________:
:_@________________________________________:
:@_________________________________________:
:_@________________________________________:
:__@_______________________________________:
:___@______________________________________:
:____@_____________________________________:
:_____@____________________________________:
:______@___________________________________:
:_______@__________________________________:
:________@_________________________________:
:_________@________________________________:
:__________@_______________________________:
:___________@______________________________:
:____________@_____________________________:
:_____________@____________________________:
:______________@___________________________:
:_______________@__________________________:
:________________@_________________________:
:_________________@________________________:
:__________________@_______________________:
:___________________@______________________:
:____________________@_____________________:

This is a printf() of the motor seeking home and going back to the middle of the page. The printer was finally starting to work! This was awesome!

But then, the printer would get into an infinite loop again. It was waiting for the Online button to be pressed, but it wasn’t being acknowledged by the processor. That’s when I realized that the button should trigger an interrupt, which wasn’t properly implemented in MESS. So I implemented the interrupt properly and pressed the Online button.

According to the printer’s User Manual, now the printer should be pulling paper. Indeed, I noticed a different command sequence being sent (for the Paper Feed stepper motor). Now I had both motors working.

What was the next step? Entering the input loop. I should be able to send commands to the printer and have it print them out to paper.

At this moment we take a little pause. We need to realize that the work up to here has already taken me many months. It may seem simple when being read in a blog post, but it was a lot of hard work.

So, instead of going for the input loop, I decided to use the printer’s self-test function. While running the self-test function, I got more commands being sent to the gate array. I suspected they were the printhead signals (the ones I was after right when I started this). Indeed, they seemed like characters, but there was a problem: the printhead wasn’t being fired. After some debugging, I noticed that the timer which would fire the printhead was incorrectly implemented in MESS, so I had to fix that.

Finally, I had a bunch of stuff being printed. I wrote a little script to organize that stuff into an image, and then I got this:

I finally got my characters.

The code can be found in my GitHub account, inside the MAME repository and the lx810l branch. It should be merged into MAME upstreams in a few days. To run the self-test, build the dummy_centronics subtarget from the lx810l-debug branch.

By the way, the characters matched the ones I got with the magnifying glass…