Tag Archives: kodak

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 …

Kodak pcd Film Scanner 2000 teardown

Sometimes when you look for one thing you find another. I should really be working on the pipe organ electronics. There is no Dickens fair and my Tale of two cities puppets are waiting in the wings. But there are ever so many interesting things in this world and they are not going anywhere and I have enough projects to keep me for 500 years …

And now a distraction on top of a distraction …

While looking for surplus parts for the MIT/BYU Holo monitor; I found a Kodak pcd film scanner 2000. Something I have wanted since the 1990s. I tried to get one of these back in 2014 and missed out on the auction. The unit came as-is for parts with no cables and software.

Over the decades I have collected as much information as I could on the Kodak PhotoCD system. Probably one of the most unpopular products ever. Eventually leading to the Bankruptcy of Kodak in 2013. For this reason I find the system an amusing diversion.

In the mid 1990s I worked for Apple Imaging. Part of the Postscript test group. Among my responsibilities was creating a color test suite for the apple color laser printer. Kodak was partnering with Apple at the time. I was given the PhotoCd disk to use as reference images.

I also used PhotoCD for my vacation photos, which were taken in 3D with a stereo Realist camera. Transferring the photographs to digital made viewing easier. The PhotoCD mastering system used a Sun Workstation to drive this scanner. There is no indication Kodak ever placed drivers for the Film Scanner 2000 online.

Ted Felix has a comprehensive website detailing the structure of the PhotoCD file format. Over the decades reading these files has become well documented. As a last gasp to save PhotoCD, Kodak packaged the workstation software as a program called Build-it.

Build-it was made for both mac and PC. For some reason Kodak opted to write the drivers as Photoshop plug ins rather than a stand alone driver.

Photo CD was supposed to be for deep time digital archiving with a lifespan of 100 years. It had an active life of about 12. Still there is a bit of stuff out there that was captured in those 12 or so years.

First though I need to see what in in this 60 pound box of ” parts.” To see what the hardware entailed.

Tearing down the Film scanner 2000

The first step in getting the scanner to work is to do a complete tear down. For the most part the scanner was fairly clean. Evidence it has been in storage for some time, there was a light deposit of dust in exposed places.

Removing the cover shows the air filtration system for the lamp housing and exhaust chimney. Also visible is the line scanner camera on the right. Much of the far space is taken by the power supply. Electronics are in a card cage behind the lamp assembly and in the near foreground.

When the air filter assembly is removed, It can be seen that this is pretty much a standard slide projector lamp housing. A good thing as this one is burned out.

The iTek scanner used three of these and had a 20 minute boot up calibration cycle. In the 1990s these black body radiators were considered standard illuminates. In reading the archived literature film is considered a chemical process.

With the 250 watt Xenon halogen lamp removed we can see a filter wheel. Some collimating lenses and a rather large light pipe inside the scanning frame, center.

Removing the lower electronics assemblies reveal the three ADC chips. Unlike the RFS series scanners, Which have a rotating tri color wheel, this unit has a 3 color CCD.

Removing the cover from the card cage, The micro controller contains 4 cards. It is based on an Intel 80C196 chip. The iTek interface used an i860.

Pulling the processor card we can see that this is not a consumer item, as there are segmented LEDs and some sort of 26 pin connector debug port on each card. The boards are 6 to 8 layers. The other three boards are the stepper motor drivers, more processor support chips, and the SCIS interface.

CD plugins for Kodak scanners have updatable firmware files. There are several chips that look like E proms and memory. If this hardware is retained and the SCSI command set can not be determined from existing drivers it may be possible to dump this code to see what the functions are.

A more practical solution would be to replace the processor card with a more modern equivalent, that can directly support Ethernet, such as the STMF429. The motherboard connectors are .1 pitched and keyed. This would make replacing the processor easy, and less time consuming than reverse engineering the obsolete SCSI firmware.

With the electronics out of the way we can take a better look at the filter wheel. This contains 2 dichroic filters. There is also an optical flag for home position.

The scanner was full of dust bunnies. So I will probably nick name it Rabbit. The light pipe is open to air on one end. In this photo some of the dust can be seen behind the sealed side.

The light pipe comes apart. The backlights are replaceable items. Inside the light pipe It looks like some sort of science fiction set. There is a center post that blocks the direct path from the light pipe. This is just visible in the upper left behind a back light Fresnel screen. The back lights are filament bulbs rather than LEDs. White LEDs were pretty pricy in the mid 1990s. CFLs a bit more popular with backlit lcd displays.

Removing the cover from the camera shows two more PCB boards. The camera connects to the ADCs through RF cables. Removing the CCD heat sink screws does not release the CCD PBB. The CCD seems to be sealed to the actual camera (Camera originating from the Latin word for black chamber or box.) It is not practical to remove the CCD to find any manufactures part numbers.

The Camera assembly rides on a spring loaded focusing axis. There are several optical flags. In front of a camera is a solenoid driven aperture plate. A calibration plate shows that these units are of laboratory quality.

A modern cell phone camera does have more resolution. The advantage of the line scanner is to reduce the distortion inherent in the grid.

Sadly Google is broken (It now serves advertisements for useless consumer products, which are bad for the economy, rather than technical information that can be used for deep time (or even recent time archiving.))

With the camera removed, What remains is the mechanically dampened cross slide. There are several solenoids and a FPC ribbon connecting this to different aperture plates. This unit only came with the slide mounting aperture. The film strip scanning unit being sold separately.

in Summery

Since I wanted this to scan more non standard film frames such as produced by a stereo 3D camera, I was going to make my own aperture plate anyway. Perhaps some time I will find the film scanning unit in another online surplus store. If you have one for sale or trade contat me as “PcdScan at [ delectra domain]”

When the film scanner was re-assembled, and a new lamp installed, the scanner was powered on for the first time. The scanner booted up, then ran some sort of self calibration on the scan axis, the focus axis and the filter wheel.

A hex dump of the Kodak pcd 4050 driver, photoshop plug in showed that it seems to contain the whole of the PhotoCD imaging workstation (Piw) code much like the Buil-it app. The pcd 4050 driver is also native powerpc. The RFS 3570 driver dumps as a FAT plugin. Adobe has archived the Photoshop plug in API, so it is easy to read the driver code.

The online version of the driver is dd0656.hqx. (1.1MB created 9/8/1999 11:44:55AM.) Over the decades I have downloaded this many times. The read me implies this is an update to an earlier version It may not contain all of the libraries. When loaded into Photoshop, the plug in gave an an error “file not found.”

Using a PEF viewer, it can be seen that the code was written in C++ by a popular 1990s IDE (code warrior)

The driver code was built with debug mangled C names on most of the functions. There are two shared libraries what may be missing that relate to the photo CD system. One is called PiwColorTransformLib and the other is simply called ‘sba.’ It is actually the Scene Balance Algorithm, that I am interested in.

When my stereo pairs were scanned they were scanned with a full aperture that included parts of the surrounding image. This makes correlation when doing photogrameritry on them awkward. At Apple we said of the Kodak engineers; The Kodak motto was “Don’t make it right, make it bright.”

This project has been many decades in the making (pretty much a quarter of a century.) So I expect to keep chipping away at it. If there are any others out there who are interested or may have old offline Kodak Pcd documents libraries, firmware or drivers to share. I have set up a special email address which is “PcdScan .at. [ delectra domain]” (this is encoded, but the email address should be clear where the text in brackets is the domain of this blog.