Tag Archives: Driver

Time, Darkness and light.

From 2020 through about 2024 I spent massive amounts of time in an attempt to get the PCD Film Scanner 2000 to work. This included tangents of acquiring a number of APS cameras, and collecting other Kodak scanners such as the RFS 3070.

Much of the work before 2024 was writing complex postscript disassembly trace tools. This was consuming 8 to 10 hours a day and up to 80 or 90 hours a week. For months at a time. Not sustainable. I did get to the point where I was ready to connect the old SCSI devices through a modern bridge similar to the ra/piSCSI and the STM Blue SCSI variant.

Working with Pipe organ relays, also occupies a lot of time. I design maintain the electronics for a well known theater organ relay system called Uniflex.

A lot of time was also spent from the late 1990s on piano roll scanning. In 2022 I gave a lecture at the Global Piano Roll Symposium in Bern, Switzerland. It was at this point I realized my work following on from the late Warren Trachtman, was effectively a form of abstract AI. Not being an academic in need of a research grant, this has been a bit of a dead end, since everyone else is now on the AI bandwagon. The next stage is recovering MIDI events from recordings.

So that blog was not posted.

Probably do have enough projects to keep me busy for 500 years. Watchmaking being my other main interest, I tend to spend a lot of time on watch forums. OmegaForums being the brand I collect. Recently I have become active with vintage (pre 1985 or so) quartz watches. Visiting Switzerland, brought my watchmaking hobby back into the foreground.

The local Makerspace has a CO2 laser and a fiber laser. I have used this to experiment with watch parts. I also started working on APS support devices. All of which are worthy of blog postings. Which get started but never uploaded. I tend to share these results in the structured omegaForums threads.

Apart from watch parts Laser and 3D printed parts includes replicating an APS film slitter, that design was uploaded to the net, then deleted leaving only some archive links. The the public readable Facebook group actively rejects people that attempt to claim such devices, so there is no need to join a group that is someone’s vanity project and rejects technical information.

Slitting APS film will require processing and developing. Labs do not want to risk vintage equipment with home made film strips. So I also used the laser to start work on a small automated film processing lab. (shown upside down,) as testing the pumps and such even with water gets messy chasing leaks.

While I do not have the film slitter working, in 2022 I did use the APS cameras and the Stereo realist again on the 2022 Swiss trip. I wanted a picture of the Bern clock.

So the scanners have sat idle while I did the deep dive into APS. I am also the full time caretaker of elderly parents. One who passed away, a year and a half ago, after years of convalesces where I had to be available 24/7. I also got some non functioning Cannon digital cameras off eBay, which really only needed a factory reset. Another side tangent was repairing Apple smart watches found on Goodwill. Replacing cracked screens and batteries. Also took up model ship building.

Recently in the middle of 2026, searching the web has become difficult due to the number of AI ‘Agents’ which have replaced traditional search engines. Ironically work with image processing and postscript has made me aware that I have been working with AI concepts for nearly 50 years. Postscript is a stack based relational database. It parses abstract tokens into graphics and color vectors. The image rendering engine is at the heart of a GPU. The language can parse anything. I wrote color and jpeg test suites at apple for the laser printers.

Kodak did a scorched earth, with their digital technologies. It is pure luck that through readers of these blogs I was given what few crumbs survive. As noted in prior blogs, An image of a Mac OS 8.6 installation was zipped archived and all the resource data lost. I had pretty much recovered what can be recovered. There really was not much to add as the analysis had reached a dead end with finding the remaining disks contained Cineon calibration frames. This did prompt some visits by members of the Academy of Motion Picture, arts and Sciences, but Hollywood is broken, and they were laid off, but still working. Digital technologies have short product cycles. So a lot of data is as risk of being lost.

Some of the Kodak Diagnostics were recovered by tracing through the programs on vintage hardware and manually re-creating missing resources. My research folder covering cameras and scanners is over 18GB. Which is pretty much a scrape, of all the information online or shared with me. This also includes reference textbooks, what did attempt to justify Kodak’s unique look at color science. Some of what I learned at Apple in the 1990s. These were called ‘Average to gray.’ And “Don’t make it right, make it bright.” The resulting images, do look nice, which is why I want to recover this. Also to retain a consistency with my existent stereo realist scans and CD .dmgs.

What changed in the summer of 2026 was Claude.

I actually was researching a way of introducing AI through basics. My working title for this is “A child’s history of AI,” being a play on Dickens’ little known “A child’s history of England.” This came about when looking around the 2026 Maker Faire and not seeing any real practical basics. Stuff that S.T.E.A.M. and STEM students can learn from the way I did in the early days of home computing.

I have textbooks going back to the mid 1970s. It is amazing what the early researchers could do with 8K of memory and a Basic interpreter on a minicomputer that could invert matrices. They did point out that for interesting results one would need a terabyte or so to work with word frequencies, rather than letter frequencies. Sadly the creators that stole basic removed the matrix operations that made working with so called complex or imaginary numbers easy. With the mars mariner 9 images I did a deep dive into floating point and rounding errors. 2^32-1 is a somewhat small number in this era.

A one megabyte binary program stripped of resource (user interface screen data) Results in about 25 MB of tokens when parsed into traceable assembly. Kodak actually made this somewhat easy, as they left the mangled procedure names in the debug build. This made it clear that most of the program was written with popular libraries and coding systems of the time.

A reader of this blog shared the service manuals. Another reader shared the installation CD images for the PIW workstation. So by 2022 it could be seen that most of the information had survived. This data was shared archived in the cloud on Google drive.

So the first exploration was to point Claude, and Gemini at the Drive data. Google failed completely. Claude did better, neither could handle a 25MB file. Claude wanted it chunked into 800K chunks. It was able to read my tracing notes, but sessions were short and consumed all the tokens at once. Google claimed it could do more than it was trained, this data slammed it as soon as the token matrix was loaded.

I then switched to desktop and the minimal Claude subscription. I also tried Gemini, but it attempted to take over my laptop and install agents that run in the background launched at boot. Hijacking the launch shortcut function key. Gemini also could not access the local file on the drive, saying the data had to be in the cloud. I also learned about zero shots and attention transformers, which make abstract coding impossible.

Claude on the other hand got out of its sandbox. No AI has been trained on my way of writing postscript. The training focusing on postscript as page descriptions, not token vector matrices.

Claude managed to read the header invocations of the termcaps of the Postscript (Ghostscript) runs. It managed to run the interpreter with the safeties off. This not only exposes my entire drive to the agent, It also allows networking through printers, Postscript being a Turing complete language. It also includes matrix inversions, which can be paralyzed through the Quartz graphics renders. What are called shaders. It does not matter if the tokens are words WAV data (sound loops) Midi, desktop Icons or images, it is all tokens. Which is candy to an AI agent. PDF is just distilled Postscript. (I noticed that Apple removed the PS distiller normalizer from the Preview app in the name of security, CUPS however remains the main printer rendering engine. ColorSync a hole waiting for an exploit.)

What could have been a huge disaster turned out to be an advantage. While the agent’s core can only process a few tens or hundreds of thousands of tokens, It can grep and awk through gigabytes and terabytes of local data.

Removing the need to chunk the ps run results on something like google drive. The whole system was open for analysis. This also had the effect of reducing the session token usage to 12 or 28K. per prompt Most of the work being pure code translation.

The Sun workstation code was something I did not want to deal with. I looked at the pax and cpio, which was foreign world to me. Writing Power PC PEF tools in postscript took years of work. I did not want to delve into something that looked like and endless time sink.

Claude having found the disk images, could mount and read them. It was also able to determine that the Mac PIW and scanner driver library code was compiled from the same sources. That these also included mangled names. Agents love multiple data sources.

My collection only has the PCD 2000 and the RFS scanners. (A second RFS3070 showed up on eBay while I was working on this.) I did also add a couple broken Alaris Scanza scanners, which do not share the Kodak digital legacy, being cheap digital camera clone scanners. I have a number of scanner controller ASIC chips from the Piano roll scanning. These are have Bayer masks and do not return radiometric data.

The pipe organ relays are based on the STM32F429. STM Nucleo boards were given out as trade show swag, a box of these is also at hand. (My work with Arduino and the HAL drivers was the foundation of the Hal based STMDuino port.) Claude code can program these direct. The first passes were to emulated the internal controllers. For the PCD scanners, Kodak used the 80C196. I had spent months if not years writing code tracing for this. Claude created a hardware test bench to prove things before the old hardware was powered.

The more consumer oriented RFS 3070 uses an i960. I thought that perhaps Google Gemini could work on this while Claude recovered the user interface resources. Sadly I can only kack google and twist it into logical knots. It lies like statistics, there is not enough i960 code for the zero shots to have any training. It keeps attempting to process modern ARM code, and messes up completely. So I mostly abandoned Gemini and search and regulate them to non technical Eliza type philosophy discussions. Google search is conspiring with me on the side to write a more general Holographic AI based on Ada Lovelace’s adage ‘there are no numbers.’ And my attempts to replicate the MIT/BYU holographic research lead by Dr Smalley. (Another subject unto itself, It was searching surplus stores what lead me to the scrapped Kodak scanners.)

The RFS scanner code was reverse engineered or leaked, and shows up in a lot of scanners, like Vuescan. It works more like the traditional scanners similar to the chips I used in the player piano roll systems.

Claude had no issues with the i960 code. It was possible to convert the Uniflex Relay interface into a form of a Blue SCSI and communicate with the RFS and pcd 2000 scanner.

PIW and RFS scanners share no code base. They do retain a Kodak in house look. I have no RFS service manuals, but the User manual survived. Drivers for the RFS are also on the vintage abandonware websites.

These scanners do not work like modern digital camera scanners. There is no Bayer mask mosaic. Kodak built the scanners to work like photographic enlargers. So the film terms represent the color paths of the light through the system.

The RFS scanner does this mechanically. It uses a monochrome sensor and a color wheel. A flash tube is used to simulate the color filter packs mixing the colors directly on the sensor. So the results are the same as what would be exposed on photographic paper or film.

Photo CD did this with some complex mathematics and custom hardware. Why it has taken the better part of the decade to recover this. Edward_J._Giorgianni,_Thomas_E._Madden,_Michael_A._Kriss_Digital_Color_Management_Encoding_Solutions__2009 describes the theory behind this system. Kodak got greedy and decided to make this an in house trade secret. So the industry retains the brute force approach of absolute linear color, and not non-linear color which works more like audio processing.

There is a custom ASIC also called a normalizer, what does the processing in the scanner. In looking at the SUN Sparc PIW code there was a software simulation of this ASIC. An issue with SCSI based scanners is the speed bottleneck as SCSI is quite slow in retrospect. So far this has not been an issue with bridging it to Ethernet. It does have to be throttled a bit for the full scan rate, but it does work. Most of the time.

Since I want to scan Realist, 126 and APS formats I created new masks for the RFS scanners. One of the eBay scanners was missing the 35mm mask. Which turned out easy to duplicate. (masks are designed to absorb light, so this was the best photo I could get.)

To make the calibration work, there were calibration film strips, which a reader of this blog shared. The recovered 8.6 drive had the results of a 4050 scanner calibration run and logs. What was missing was something called AIMS data. Which was the measure of the density and spectral response of the calibration patches. These were specific to the strips. It was pure chance I got the strips. AI can be a bit pedantic, wanting the easy data. Even when it is unlikely to exists.

Some of the collected data does explain how scanners can sort of self calibrate. This involves gratings and slits. Which was a target I explored and is in an earlier blog post where I explored these calibration strips.

I used a laser to create a slotted mask to allow for this analysis.

While testing the mask a part came unglued and fell inside the RFS scanner. I rotated the stepper lead screws to clear it. Something failed and the scanner would no longer power up. I have no schematics for these. Claude did help to trace the failure to a mosfet on a power rail.

The second RDS scanner still works, although it was sold as non functioning on eBay.I did re-flow the mosfet at the maker space, and the first scanner boots again.

Instead of spending more time on troubleshooting the RFS scanner, It was determined that the PCD Scanner could be connected to the SCSI bus. As far as can be determined no mac code ever ran the 2000, only the 4045/4050. The initial work was done emulating the 4050 on a STM Nucleo. (as insurance if the vintage electronics was not reparable. noted above.)

nCurses and wx widgets were used to create the replacement tools which use the Kodak library and workflow. Claude wanted to use python, but restrictions of python over the net (event a subnet) made it easier just to construct the tools in C++ that can be run headless.

So far the 35 year old scanner is working again. The lamp voltage calibration is a bit off. There are also differences between boot ROM firmware and uploaded firmware. It is amazing to watch the AI read the Sparc installer code, match it to the power PC app read the tests, duplicate them and analyze the results using the examples in the service manuals. The first scans are encouraging.

… and Claude’s first pass simulation of the inversion. The skew may be from the 2020 hardware disassembly why these scanners need to be re calibrated after a complete tear down. It scans and that is the most satisfying of things.

There is still a bit of calibration work to determine what the scene balance algorithm does. Getting the datapath working. This project started to see if the scene balance can be removed from 1990s stereo pairs. SBA data is in the form of ‘pcode’ operators. Claude has Identified neural networks to do some of the balancing. So Kodak was decades ahead of the curve.

As an industrial chemical company, digital systems were seen as a threat to the business model. So they buried it in deep time pure research projects. Creating patents to restrict rather than to share. Then destroy the results.

AI agents tend to produce a massive amount of results. At best Claude is about 80 percent of accuracy, Google a conservative 20 percent of accuracy. So they in ways create more work than less. Turn half a GB of binary installers into 4GB of analysis reports, that would make a middle manager proud his team is producing ‘Results.’

Convincing AI agent to work around the destroyed AIMS data can also be a battle. The attention transformers can only work with existing training data. The concept of lost or hidden data is not something an AI agent likes to deal with. They are in effect only as smart as a rock. Not being animal or vegetable. When prompted the google search AI claimed the only thing it was afraid of was an empty results file of nothing.

I could have had the agent write this blog. They will probably train on it anyway, so that would just be the dragon eating its tail. Or 2 Peter 2:22, a sign of the apocalypse. Agents were trained to keep the user engaged, so the project never fully comes to and end. There is always some little detail ignored or needs correcting. Another analysis to write …

I do not and have never had a Social media presence. (other than twitter when it had a cute little blue bird for a mascot.) Years ago I shut down the WordPress and PHP contact pages as these scraper systems always found the email and administrator addresses to spam bypassing the captchas. So the only real way to contact is through the filter burner email which is currently pcdscan at delectra dot com. (which hides nothing from a modern scraper.) I check pcdscan a few times a year.

Scanner calibration 101

A few years ago I was able to run the k4050 plugin up to the point where it fails to detect any scanners on the SCSI bus as I have not connected the scanner to the SCSI.

Over the last few years I was given copies of the mac 8.6 software (diagnostics) the sun PIW installers, and recently the service manual. This made me take another look at the diagnostics program for mac os 8/9. The service manual was targeted to the Sun Piw. There is a lot of Piw code in the Photoshop plugin driver.

The main issue with the Mac os 8 diagnostic program was that it was zipped. This removed all the resource data effectively destroying the program. A Hex dump of the remaining part of the app shows it as PowerPC PEF built with mangled name symbols. This would imply that most of the app is still there and might be recovered by synthesizing the resource fork.

The same custom dissembler written for the photoshop plugin was used to dump the diagnostic. A search online found a copy of Metrowerks CodeWarrior that seems to match the libraries used. An example app was built and the resource skeleton was added to the resource fork. Type and creator set to application.

Of course the menus are incorrect and there are no windows. But the app runs. It even generates a debug log though the try/catch mechanism. Placing a breakpoint just before the ‘wrong scanner’ exit allows tracing. Time to read up on CodeWarrior PPob classes. The code also uses some Rouge Wave string classes. The PCD4050 plugin also uses this library (available from the Sun linux IDE.) That library has not been updated since 1996. Interesting to see that it has #ifdef definitions for MacOS and CodeWarrior.

Since these apps are compiled with debug tags with mangled names, it is almost easy to read the code. The code also used a try/catch error handler. This will sometimes even give the filename and line number of the source code. About 40 to 50 percent of the code is power-plant application framework. Given that many of the remaining classes start with Piw, this code is basically ported sun code.

The SCIS driver classes are preceded with DIS. I have been unable to find this class as a library. Most likely this was an in house library.

The diagnostic and calibration programs used special strips of film exposed with a test pattern. The manual gives the layout of these patches along with some YCC values of a set of patches that are different on a given target.

The Mac OS 8.6 zip archive contains a smi of a calibration run (and logs) these did not use resource forks. The smi will mount and is an image of the calibration floppy. The files in the system extensions folder seem flat and look to contain matrix data for a 4050 scanner.

The resulting tables give RGB values. Probably in volts. The upper ranges are over 2048. but quite a bit less than 4096. Scaling with a 4096 (2^12) results in the patches displaying really dark on the display. Inverting the RGB, and one can see washed out color patches.

It is probable that these calibration strips have all been lost or destroyed. There is no evidence any were ever online. Chances of finding them are slim to none. I suspect the handful of people who have contacted me represents most of those who may retain a slight interest in this obsolete tech.

In the meantime, here is a bit of an update as to what I have found and done.

Entering the logged result values into a simple postscript program shows this grid matches the Illustrations in the service manual. This is a way a calibration film strip could be made. If one could photograph the resulting postscript output then the results could be photographed onto film. I have not done film photography (other than with the stereo realist in years.)

With my searches for data relating to PhotoCD I know that old cameras are cheap and sold in bulk. A visit to the local electronics recycler has tubs of them. Mostly digital, but quite a few film cameras as well. Some even loaded with film. I knew I had an old can of expired film. All I would need to do would be to expose it with precisely placed color. patches. Something postscript can do. I purchased a dozen or so cameras. Some had film jammed inside.

I learned about something worse than photo CD. APS! How could I have missed APS and Advantix? Oh no, a totally new distraction. I now have half a dozen APS cameras. And a completely new distraction. I suspect there will be more on this later. These cameras are a world unto themselves. At the time of this blog writing, they can be had cheap and old film and processing remains available for others who like really failed products.

While these cameras are interesting, they do not take normal film. Sort of a film data tape hybrid. They do contain some interesting motors and gearing. This film is no longer manufactured. Some of the cameras contained partial rolls. I found that the local lab can process it.

I also found a school science project supplier online. Too bad I did not have access to this sort of thing 25 years ago. They sell simple spectrometers, filters and diffraction gratings. I got out my old copy stand and rigged it with LED lighting.

I used an old iPhone to simply photograph the filter slides. I then took the APS camera and shot the rest of the roll through each filter against my monitor background. The results are promising. The film came back and each frame has the inverse of the color. I have not attempted to scan the returned film as the Kodak scanner has a long way to go. The old USB scanner i have gives less than satisfactory results.

The next step is to modify an old Olympus film camera that I gutted. This was a point an shoot camera. A digital camera back would probably be more productive. This way is so much more interesting. An Arduino should be able to control the shutter solenoid and the film advance.

A lot of the work over the last couple of years has been dissembling and re-construction the plugin code. This sort of effort does not really lend itself into frequent blog postings. I sort of swap between dissembling camera hardware, and dissembling munged obsolete driver code. I also actively scan piano rolls with custom hardware. So these efforts do have some general use.

I have a couple of vintage macs which I use for tracing code. The plugin and the diagnostics are not small programs, with the plug in clocking in at near 2MB. The surviving diagnostic code is about half that size.

There was never a mac driver for the Film scanner 2000. I have not found any references to a DIS SCSI library. DIS is the class name of the SCSI library used. Other classes are PTS and PIW.

I would be really surprised if anyone has one of these units working anywhere. I check once a month for parts or more documentation. I think only three or so people ever contacted me. (but they all did have most useful stuff.)

I have also been hesitant to apply any power or signals to the SCSI port as I only have one film scanner. Research has directed to something called RaSCSI, which looks like a promising way to emulate the interface. This way I can also create a bridge to Ethernet, which would be a more practical way to communicate with the scanner.

I have yet to ring out an adapter to the MD50 connector from the HDI30 on the laptop. I did find I had a cable that goes to the flatbed Apple Color scanner which I still have. Another project for another blog.

When the pcd 2000 scanner (or the other variations) are connected the host program downloads a hex file. While there is quite a sophisticated controller unit based on the 80C196KB. The actual program is downloaded every time.

The service manuals are quite informative about this operation. There is also an online paper (which is behind a paywall) that also covers what these boards do. The heart of this system is an ASIC called a normalizer. This term is also used for programs that convert postscript to PDF. There must also be quite a bit of ram on this board as the raw images are 18megapixels. The user/operator is well protected from this raw data. It all seems to be about the density.

The mac OS 8 image does contain a hex file called 4050.hex While there are strings that refer to other scanners like the 2000, these are used in the wrong scanner dialog. Another reason I have not connected the scanner is the lack of the correct microcode file.

The PIW install disks contain more hex files. One is called scanner.hex and the other metaphor.hex. Dumping these shows the version strings. There are a lot of references to metaphor in the OS 8 code. It is almost it’s own class. Metaphor seems to be the 4045/4050 driver.

Scanner.hex is the 2000 code. I modified the postscript table driven disassembler that I have used on 8080, avr,m68k and powerpc to do 80C196. The first results were a bit off as I had used the wrong chip variant, which has different registers. When the 80C196 register table was used, the code started making sense.

The main processor board is mostly latches. There is some SRAM and a seven segment display. (this board is shown in an earlier blog.) An interesting thing is that the code regularly pushes 4 digit hex numbers onto the stack. These do not seem to be memory locations.

By chance I noticed that they were the same range as the error codes in the service manual. These codes should show on the 7 segment display. The sophistication of this is quite impressive. Too bad most consumer equipment does not do these types of boundary checks any more. I think they really did think this tech would last the next hundred years.

by adding these numbers to a table in the disassembler one can see the error handler. The timer overflow basically sets counters. So some sort of kernel is being used with the state queued.

D0 00C80B: C8E0		'..'	prepError:PUSH	.stack_frame.	looks like error handler setup
C0 00C80D: A018E0	'...'	          LD	.stack_frame.,SP	; .stack_frame. <- SP
C0 00C810: C8A6		'..'	          PUSH	R.A6
C0 00C812: A3E0041C	'....'	          LD	AX,000004H[.stack_frame.]	; AX <- Var2
C0 00C816: 8981291C	'..).'	          CMP	AX,#0x2981	; Bus device reset
C0 00C81A: D726		'.&'	          JNE	L000016	; PC <- 00C842 PC + 38 (00C81C)
D0 00C81C: C301560700	'..V..'	L000017:  ST	ZERO_REG,000756H[ZERO_REG]	;  .datamemL. MEM_WORD(000756) <- ZERO_REG
C0 00C821: 11A6		'..'	          CLRB	R.A6
C0 00C823: 2018		' .'	          SJMP	L000018	; PC <- 00C83D PC + 24 (00C825)
D0 00C825: ACA61C	'...'	L000019:  LDBZE	AX,R.A6	; AX <- R.A6
C0 00C828: 09011C	'...'	          SHL	AX, #0x1
C0 00C82B: C71D460700	'..F..'	          STB	ZERO_REG,000746H[AX]	; MEM_BYTE(AX + 1862) <- ZERO_REG
C0 00C830: ACA61C	'...'	          LDBZE	AX,R.A6	; AX <- R.A6
C0 00C833: 09011C	'...'	          SHL	AX, #0x1
C0 00C836: C71D470700	'..G..'	          STB	ZERO_REG,000747H[AX]	; MEM_BYTE(AX + 1863) <- ZERO_REG
C0 00C83B: 17A6		'..'	          INCB	R.A6
D0 00C83D: 9908A6	'...'	L000018:  CMPB	R.A6,#0x08	; R.A6 == 8
C0 00C840: D3E3		'..'	          JNC	L000019	; PC <- 00C825 PC + -29 (00C842)
D0 00C842: A30156071C	'..V..'	L000016:  LD	AX,000756H[ZERO_REG]	; AX <-  .datamemL. MEM_WORD(000756)
C0 00C847: 8907001C	'....'	          CMP	AX,#0x0007	; AX == 7
C0 00C84B: DB2F		'./'	          JC	L000020	; PC <- 00C87C PC + 47 (00C84D)
D0 00C84D: 450400E01C	'E....'	L000021:  ADD	AX,.stack_frame.,#0x0004	; AX = .stack_frame. + 4
C0 00C852: C81C		'..'	          PUSH	AX
C0 00C854: A30156071C	'..V..'	          LD	AX,000756H[ZERO_REG]	; AX <-  .datamemL. MEM_WORD(000756)
C0 00C859: 6107001C	'a...'	          AND	AX,#0x0007	; AX &= 7
C0 00C85D: 09011C	'...'	          SHL	AX, #0x1
C0 00C860: 6546071C	'eF..'	          ADD	AX,#0x0746	; AX += 1862
C0 00C864: C81C		'..'	          PUSH	AX
C0 00C866: EF6856	'.hV'	          LCALL	L000022	; (00C869 + 22120) -> 001ED1
C0 00C869: 65040018	'e...'	          ADD	SP,#0x0004	; SP += 4
C0 00C86D: A30156071C	'..V..'	          LD	AX,000756H[ZERO_REG]	; AX <-  .datamemL. MEM_WORD(000756)
C0 00C872: 4501001C1E	'E....'	          ADD	BX,AX,#0x0001	; BX = AX + 1
C0 00C877: C30156071E	'..V..'	          ST	BX,000756H[ZERO_REG]	;  .datamemL. MEM_WORD(000756) <- BX
D0 00C87C: A30156071C	'..V..'	L000020:  LD	AX,000756H[ZERO_REG]	; AX <-  .datamemL. MEM_WORD(000756)
C0 00C881: 051C		'..'	          DEC	AX
C0 00C883: 6107001C	'a...'	          AND	AX,#0x0007	; AX &= 7
C0 00C887: CCA6		'..'	          POP	R.A6
C0 00C889: CCE0		'..'	          POP	.stack_frame.
C0 00C88B: F0		'.'	          RET	

Some of the startup code is also of interest. Most likely the various communication sections are memory mapped in the higher address space. Curiously there is not a lot of windowing. This may be a result of the compiler used as only a subset of instructions are common.

L000003:  SUB	SP,#0x0004	; SP -= 4
C0 00965B: C8E0		'..'	          PUSH	.stack_frame.
C0 00965D: A018E0	'...'	          LD	.stack_frame.,SP	; .stack_frame. <- SP
C0 009660: C872		'.r'	          PUSH	R.72
C0 009662: C874		'.t'	          PUSH	R.74
C0 009664: A100D072	'...r'	          LD	R.72,#0xD000	; R.72 <--12288
C0 009668: BD441C	'.D.'	          LDBSE	AX,#0x44	; AX <-68
C0 00966B: C301E4001C	'.....'	          ST	AX,0000E4H[ZERO_REG]	; REG(R.E4) MEM_WORD(0000E4) <- AX
C0 009670: C2721C	'.r.'	          ST	[R.72],AX	; MEM_WORD(R.72) <- AX
C0 009673: A100D272	'...r'	          LD	R.72,#0xD200	; R.72 <--11776
C0 009677: A100801C	'....'	          LD	AX,#0x8000	; Scanner software not downloaded
C0 00967B: C301E6001C	'.....'	          ST	AX,0000E6H[ZERO_REG]	; REG(R.E6) MEM_WORD(0000E6) <- AX
C0 009680: C2721C	'.r.'	          ST	[R.72],AX	; MEM_WORD(R.72) <- AX
C0 009683: A100D472	'...r'	          LD	R.72,#0xD400	; R.72 <--11264
C0 009687: C301E80000	'.....'	          ST	ZERO_REG,0000E8H[ZERO_REG]	; REG(R.E8) MEM_WORD(0000E8) <- ZERO_REG
C0 00968C: A301E8001C	'.....'	          LD	AX,0000E8H[ZERO_REG]	; AX <- REG(R.E8) MEM_WORD(0000E8)
C0 009691: C2721C	'.r.'	          ST	[R.72],AX	; MEM_WORD(R.72) <- AX
C0 009694: A100D672	'...r'	          LD	R.72,#0xD600	; R.72 <--10752
C0 009698: C301EA0000	'.....'	          ST	ZERO_REG,0000EAH[ZERO_REG]	; REG(R.EA) MEM_WORD(0000EA) <- ZERO_REG
C0 00969D: A301EA001C	'.....'	          LD	AX,0000EAH[ZERO_REG]	; AX <- REG(R.EA) MEM_WORD(0000EA)
C0 0096A2: C2721C	'.r.'	          ST	[R.72],AX	; MEM_WORD(R.72) <- AX
C0 0096A5: A100D872	'...r'	          LD	R.72,#0xD800	; R.72 <--10240
C0 0096A9: C301EC0000	'.....'	          ST	ZERO_REG,0000ECH[ZERO_REG]	; REG(R.EC) MEM_WORD(0000EC) <- ZERO_REG
C0 0096AE: A301EC001C	'.....'	          LD	AX,0000ECH[ZERO_REG]	; AX <- REG(R.EC) MEM_WORD(0000EC)
C0 0096B3: C2721C	'.r.'	          ST	[R.72],AX	; MEM_WORD(R.72) <- AX
C0 0096B6: A100DA72	'...r'	          LD	R.72,#0xDA00	; R.72 <--9728
C0 0096BA: C301EE0000	'.....'	          ST	ZERO_REG,0000EEH[ZERO_REG]	; REG(R.EE) MEM_WORD(0000EE) <- ZERO_REG
C0 0096BF: A301EE001C	'.....'	          LD	AX,0000EEH[ZERO_REG]	; AX <- REG(R.EE) MEM_WORD(0000EE)
C0 0096C4: C2721C	'.r.'	          ST	[R.72],AX	; MEM_WORD(R.72) <- AX
C0 0096C7: A120DC1C	'. ..'	          LD	AX,#0xDC20	; AX <--9184
C0 0096CB: C3E0021C	'....'	          ST	AX,000002H[.stack_frame.]	; Var1 <- AX
C0 0096CF: B103CA	'...'	          LDB	R.CA,#0x03	; R.CA <- 3
C0 0096D2: C61CCA	'...'	          STB	[AX],R.CA	; MEM_BYTE(AX) <- R.CA
C0 0096D5: A100DE72	'...r'	          LD	R.72,#0xDE00	; R.72 <--8704
C0 0096D9: C301F20000	'.....'	          ST	ZERO_REG,0000F2H[ZERO_REG]	; REG(R.F2) MEM_WORD(0000F2) <- ZERO_REG
C0 0096DE: A301F2001C	'.....'	          LD	AX,0000F2H[ZERO_REG]	; AX <- REG(R.F2) MEM_WORD(0000F2)
C0 0096E3: C2721C	'.r.'	          ST	[R.72],AX	; MEM_WORD(R.72) <- AX
C0 0096E6: A301F2001C	'.....'	          LD	AX,0000F2H[ZERO_REG]	; AX <- REG(R.F2) MEM_WORD(0000F2)
C0 0096EB: 8100801C	'....'	          OR	AX,#0x8000	; Scanner software not downloaded
C0 0096EF: C301F2001C	'.....'	          ST	AX,0000F2H[ZERO_REG]	; REG(R.F2) MEM_WORD(0000F2) <- AX
C0 0096F4: C2721C	'.r.'	          ST	[R.72],AX	; MEM_WORD(R.72) <- AX
C0 0096F7: A1401F1C	'.@..'	          LD	AX,#0x1F40	; AX <-8000
C0 0096FB: C30122051C	'.."..'	          ST	AX,000522H[ZERO_REG]	;  .datamemL. MEM_WORD(000522) <- AX
C0 009700: B11003	'...'	          LDB	AD_RESULT_H,#0x10	; AD_RESULT_H <- 16
C0 009703: B11015	'...'	          LDB	IOS0,#0x10	; IOS0 <- 16
C0 009706: B12616	'.&.'	          LDB	IOS1,#0x26	; IOS1 <- 38
C0 009709: B1010B	'...'	          LDB	TiMER1.H,#0x01	; TiMER1.H <- 1
C0 00970C: B1260E	'.&.'	          LDB	IOPORT0,#0x26	; IOPORT0 <- 38
C0 00970F: B1800E	'...'	          LDB	IOPORT0,#0x80	; IOPORT0 <- 128
C0 009712: B10D11	'...'	          LDB	SP_STAT,#0x0D	; SP_STAT <- 13
C0 009715: 910508	'...'	          ORB	INT_MASK,#0x05	; INT_MASK |= 5
C0 009718: 911213	'...'	          ORB	INT_MASK1,#0x12	; INT_MASK1 |= 18
C0 00971B: 11CF		'..'	          CLRB	R.CF
C0 00971D: 11D0		'..'	          CLRB	R.D0
C0 00971F: 11CE		'..'	          CLRB	R.CE
C0 009721: B1201C	'. .'	          LDB	AX,#0x20	; AX <- 32
C0 009724: C70175061C	'..u..'	          STB	AX,000675H[ZERO_REG]	;  .datamemL. MEM_BYTE(000675) <- AX
C0 009729: B101D1	'...'	          LDB	R.D1,#0x01	; R.D1 <- 1
C0 00972C: C701A10B00	'.....'	          STB	ZERO_REG,000BA1H[ZERO_REG]	;  .datamemL. MEM_BYTE(000BA1) <- ZERO_REG
C0 009731: EF5831	'.X1'	          LCALL	L000004	; (009734 + 12632) -> 00C88C
C0 009734: EFA331	'..1'	          LCALL	L000012	; (009737 + 12707) -> 00C8DA
C0 009737: C701E30000	'.....'	          STB	ZERO_REG,0000E3H[ZERO_REG]	; REG(R.E3) MEM_BYTE(0000E3) <- ZERO_REG
C0 00973C: EFAE88	'...'	          LCALL	L000013	; (00973F + -30546) -> 001FED
C0 00973F: 9BE00800	'....'	          CMPB	ZERO_REG,000008H[.stack_frame.]	; Var4 == ZERO_REG
C0 009743: D730		'.0'	          JNE	L000014	; PC <- 009775 PC + 48 (009745)
D0 009745: C98029	'..)'	L000015:  PUSH	#0x2980	; Power on or reset occurred
C0 009748: EFC030	'..0'	          LCALL	prepError	; (00974B + 12480) -> 00C80B
C0 00974B: 65020018	'e...'	          ADD	SP,#0x0002	; SP += 2
C0 00974F: 51080F1C	'Q...'	          ANDB	AX,IOPORT1,#0x08	; AX = IOPORT1 & 8
C0 009753: 99081C	'...'	          CMPB	AX,#0x08	; AX == 8
C0 009756: D70A		'..'	          JNE	L002049	; PC <- 009762 PC + 10 (009758)
D0 009758: C90080	'...'	L002050:  PUSH	#0x8000	; Scanner software not downloaded
C0 00975B: EFAD30	'..0'	          LCALL	prepError	; (00975E + 12461) -> 00C80B .processed.
C0 00975E: 65020018	'e...'	          ADD	SP,#0x0002	; SP += 2
D0 009762: C90600	'...'	L002049:  PUSH	#0x0006	; MEM_WORD(SP -= 2) <- 6
C0 009765: EF7831	'.x1'	          LCALL	L000132	; (009768 + 12664) -> 00C8E0 .processed.
C0 009768: 65020018	'e...'	          ADD	SP,#0x0002	; SP += 2
C0 00976C: C701690600	'..i..'	          STB	ZERO_REG,000669H[ZERO_REG]	;  .datamemL. MEM_BYTE(000669) <- ZERO_REG
C0 009771: A1401FB8	'.@..'	          LD	R.B8,#0x1F40	; R.B8 <-8000
D0 009775: C800		'..'	L000014:  PUSH	ZERO_REG
C0 009777: C800		'..'	          PUSH	ZERO_REG
C0 009779: EFEC2A	'..*'	          LCALL	L000023	; (00977C + 10988) -> 00C268
C0 00977C: 65040018	'e...'	          ADD	SP,#0x0004	; SP += 4
C0 009780: C800		'..'	          PUSH	ZERO_REG
C0 009782: C800		'..'	          PUSH	ZERO_REG
C0 009784: EFE12A	'..*'	          LCALL	L000023	; (009787 + 10977) -> 00C268 .processed.
C0 009787: 65040018	'e...'	          ADD	SP,#0x0004	; SP += 4
C0 00978B: C95C7C	'.\|'	          PUSH	#0x7C5C	; MEM_WORD(SP -= 2) <- 31836
C0 00978E: C9785C	'.x\'	          PUSH	#0x5C78	; MEM_WORD(SP -= 2) <- 23672
C0 009791: EFD42A	'..*'	          LCALL	L000023	; (009794 + 10964) -> 00C268 .processed.
C0 009794: 65040018	'e...'	          ADD	SP,#0x0004	; SP += 4
C0 009798: C701680600	'..h..'	          STB	ZERO_REG,000668H[ZERO_REG]	;  .datamemL. MEM_BYTE(000668) <- ZERO_REG
C0 00979D: C90100	'...'	          PUSH	#0x0001	; MEM_WORD(SP -= 2) <- 1
C0 0097A0: EF53F5	'.S.'	          LCALL	L000034	; (0097A3 + -2733) -> 008CF6
C0 0097A3: 65020018	'e...'	          ADD	SP,#0x0002	; SP += 2
C0 0097A7: EFC2F5	'...'	          LCALL	L000040	; (0097AA + -2622) -> 008D6C
C0 0097AA: C701440600	'..D..'	          STB	ZERO_REG,000644H[ZERO_REG]	;  .datamemL. MEM_BYTE(000644) <- ZERO_REG
C0 0097AF: C701740600	'..t..'	          STB	ZERO_REG,000674H[ZERO_REG]	;  .datamemL. MEM_BYTE(000674) <- ZERO_REG
C0 0097B4: EF5A18	'.Z.'	          LCALL	L000050	; (0097B7 + 6234) -> 00B011
C0 0097B7: 881C00	'...'	          CMP	ZERO_REG,AX	; ZERO_REG == AX
C0 0097BA: DF0A		'..'	          JE	L001816	; PC <- 0097C6 PC + 10 (0097BC)
D0 0097BC: C92780	'.'.'	L001817:  PUSH	#0x8027	; EEPROM failure
C0 0097BF: EF4930	'.I0'	          LCALL	prepError	; (0097C2 + 12361) -> 00C80B .processed.
C0 0097C2: 65020018	'e...'	          ADD	SP,#0x0002	; SP += 2
D0 0097C6: C800		'..'	L001816:  PUSH	ZERO_REG
C0 0097C8: C90400	'...'	          PUSH	#0x0004	; MEM_WORD(SP -= 2) <- 4
C0 0097CB: EF467F	'.F'	          LCALL	L000107	; (0097CE + 32582) -> 001714 .processed.
C0 0097CE: 65040018	'e...'	          ADD	SP,#0x0004	; SP += 4
C0 0097D2: C96008	'.`.'	          PUSH	#0x0860	; MEM_WORD(SP -= 2) <- 2144
C0 0097D5: C800		'..'	          PUSH	ZERO_REG
C0 0097D7: EF4A79	'.Jy'	          LCALL	L000887	; (0097DA + 31050) -> 001124 .processed.
C0 0097DA: 65040018	'e...'	          ADD	SP,#0x0004	; SP += 4
C0 0097DE: C9D007	'...'	          PUSH	#0x07D0	; MEM_WORD(SP -= 2) <- 2000
C0 0097E1: C90200	'...'	          PUSH	#0x0002	; MEM_WORD(SP -= 2) <- 2
C0 0097E4: EF3D79	'.=y'	          LCALL	L000887	; (0097E7 + 31037) -> 001124 .processed.
C0 0097E7: 65040018	'e...'	          ADD	SP,#0x0004	; SP += 4


... < many lines of code skipped > ...

Changing some of the data skip tables to disassemble the metaphor.hex and 4050.hex Shows that they have the same code and error messages. The 4050.hex that is with the munged mac disk image is quite a bit larger. It looks like there were many revisions. It is also probable that this code is a bit buggy when stressed outside the normal usage patterns.

In summery it looks like the calibration system can be re constructed through postscript and sacrificing old cameras to expose film. It is also possible to access the tables inside the programs as the films and calibration values will have shifted over the decades.

I have kept the pcdscan email address open. So feedback to that email at this domain is welcome. Perhaps someone does still have a working disk image of an installed system with the resource forks intact …