Panasonic KW W1000 word processor

johnw's picture

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()