Panasonic KW W1000 word processor
Panasonic KW W1000 word processor
Word processor functions but reports "No disk" when diskette is inserted.
To take the machine apart, turn it over, undo about 8 crosshead self-tapping screws underneath, turn it right way up again, remove the 2 knobs at the ends of the platen, and lift off the cover carefully (ribbon cable at right side).
Examine the diskette drive. A commonly reported fault, the drive belt is probably slack. I hoped to swap out the whole drive for a PC one (I have more than a dozen). However, the connection is not the same, it's a directly soldered ribbon cable rather than the standard connector. And the PC drives I have don't have belts, evidently that was a design flaw in early drives. So I returned the machine to the owner unfixed, for his priority was to recover the data, not fix the drive.
I successfully recovered the contents of the diskettes that contain the owner's family history. I found the raw data on the diskettes can be accessed on a PC with a 3.5" drive. It's not a standard format such as CP/M or MSDOS, as I'd hoped, though it bears some resemblance to FAT12, so I had to write a program to do it.
The diskettes are single sided, double density, each containing about 360KB. I don't know whether all PC diskette controllers are the same, but the following is what worked for me.
Install Linux on the hard drive, these instructions assume Ubuntu or Debian.
Get a root terminal. E.g. sudo -i
apt-get install fdutils
The most helpful page I found concerning the use of fdutils is http://www.fdutils.linux.lu/disk-id.html (this was drawn to my attention by Peter Haagerup, http://rc700.dk).
That fdutils page describes how to determine the characteristics of the diskette under test. I assume you will read that page to understand what the following commands do. For the Panasonic diskettes try
fdrawcmd readid 0 rate=2 need_seek track=0
Until I used rate=2 I was getting an error 40 indicated in the first output byte. Using rate=2 I got 0, confirming that double density is correct.
Try some other tracks, up to track 79:
fdrawcmd readid 0 rate=2 need_seek track=79
I confirmed that only one side is used:
fdrawcmd readid 4 rate=2 need_seek track=0
If the diskette is readable you should now be able to image it:
setfdprm /dev/fd0 360/720 cat /dev/fd0 >mydiskette.img
This should produce a file of 368,640 bytes containing the raw data from the diskette.
I had eight diskettes. Of these, seven were fully readable, but one was only readable from tracks 58 to 79. Perhaps this was due to head misalignment, or the diskette had been stored badly. If it had been more critical to recover the data from this diskette I would have tried installing other drives and/or adjusting the head alignment.
The diskette has a directory containing the filename, its size in bytes and its starting cluster. Files longer than a single cluster may be scattered across the diskette.
I've cobbled together a Python program to read an image and generate the files. I'm an experienced programmer, but I'm not familiar with Python, so this is a laughably amateurish piece of work, but, as they say in India "it does the needful"! At least, it did for me.
However, that was not the end of the journey. Each file starts with several hundred bytes of binary. I assume this area contains information such as the tab settings or the font, but I've made no attempt to reverse engineer it. I imagine the easiest way to attempt that would be to have a fully working Panasonic machine and create small files that could be compared.
After that first area is ASCII text. However the newline character is hex 0d, which is neither the Unix newline (0a) nor the two byte Windows newline (0d 0a). To help with this the program writes a second subdirectory with the files modified, making each '0d' a '0d 0a' sequence.
There are also occasional strange characters amongst the ASCII text. For example I suspect hex 0fd means the following text is centred. No doubt there are various such formatting characters to reflect the capabilities of the machine.
Do contact me if you're trying to read such diskettes or if you find the program at all useful. It will not only please me to hear of another user, but it might encourage me to tidy it up and write further notes.
import os import sys # GNU Public License version 3 # Copyright John Washington, mail: john at-symbol johnwash dot co another dot uk # This program is hastily cobbled together, if you think it may be useful to you # I'd be delighted to hear from you and tidy it up. fc = 'zz' # global to simplify the various extraction functions # holds the file contents def i1(addr): # returns 1 byte as an integer return ord(fc[addr]) def i2(addr): # returns 2 bytes as an integer (low, high), i.e. Intel ordering return ord(fc[addr]) + ord(fc[addr+1])*256 def i4(addr): # returns 4 bytes as an integer return ((ord(fc[addr+3]) * 256 + ord(fc[addr+2])) * 256) + ord(fc[addr+1]) * 256 + ord(fc[addr]) def s(addr, length): # returns a string starting at 'addr' return fc[addr:addr+length] def nextCluster(n): # The cluster map starts at hex 200 in the image. There's a 2nd copy at hex 600, but we ignore it. # Each 3 bytes contains 2 12-bit numbers (the cluster number) x = 0x200 + (n//2)*3 x1 = i1(x) x2 = i1(x+1) x3 = i1(x+2) if n % 2 == 0: result = (x2 & 15) * 256 + x1 else: result = (x2 >> 4) + (x3 * 16) # print ' n=', n, hex(x), hex(x1), hex(x2), hex(x3), hex(result) return result def listDir(): for i in xrange(0,112): #print i1(i+0x200) #print len(fc) d = i*32 + 0xA00 cluster = i2(d+26) fileLen = i4(d+28) if i1(d) <> 0 and i1(d) <> 0xE5: print s(d, 11), 'cluster1=', cluster, 'fileLen=', fileLen n = cluster b = 127 while True: n = nextCluster(n) print n if n == 0xFFF: break if n == 0: break if b == 0: break b = b - 1 def writeFiles(path): for i in xrange(0,112): #print i1(i+0x200) #print len(fc) d = i*32 + 0xA00 cluster = i2(d+26) fileLen = i4(d+28) if i1(d) <> 0 and i1(d) <> 0xE5: fname = s(d, 11).strip() print fname, 'cluster1=', cluster, 'fileLen=', fileLen fname = fname.replace(' ', '-') n = cluster b = 127 fname2 = path + '/' + fname with open(fname2, mode='wb') as f2: while True: x = fileLen if x > 1024: x = 1024 f2.write(s(n*0x400 + 0x1000, x)) n = nextCluster(n) fileLen = fileLen - 1024 print 'cluster=', n, 'x=', x if n == 0xFFF: break if n == 0: break if b == 0: break b = b - 1 with open(fname2, mode='rb') as f2: with open(fname2+'.txt', mode='wb') as f3: ss = f2.read() ss = ss.replace('\r', '\r\n') f3.write(ss) def listClusterMap(): for i in xrange(0, 20): n = nextCluster(i) print i, n def solveJigsaw(): global fc fileName = "famhist1" for fileName in sys.argv[1:]: with open(fileName, mode='rb') as f: # b is important -> binary fc = f.read() path = fileName + ".files" if not os.path.exists(path): os.mkdir(path) # list directory #listDir() writeFiles(path) # list cluster map #listClusterMap() def splitTracksIntoSectors(): # this was used for a disk that was unreadable before track 58 global fc for i in xrange(58, 80): fname = 'mast-fam-hist/track' + str(i) with open(fname, mode='rb') as f: fc = f.read() print 'f.read len=', len(fc) for j in xrange(1, 10): fname2 = fname + '-sector' + str(j) with open(fname2, mode='wb') as f2: k = (j-1)*512 sector = s(k, 512) print(i, j, k, len(fc), len(sector)) f2.write(sector) solveJigsaw() # splitTracksIntoSectors()