
S12 showed you the directory's slots: 32 bytes each, a name you could read, and a lot of numbers you could not. Everything the Einstein knows about a file is in those 32 bytes - there is nowhere else for it to be. This section reads every one of them, and teaches edisc.py to do the same.

| Byte | What it holds |
|---|---|
| 0 | Status: 00 for a slot in use, E5 for a slot that is free |
| 1-8 | Name: up to eight characters, padded with spaces (20) |
| 9-11 | Extension: up to three characters, padded with spaces. The top bit of byte 9 has a second job - see Locked files |
| 12 | Extent: which piece of the file this entry describes - 0 for the first |
| 13-14 | Not used |
| 15 | Records: how many 128-byte pieces of the file this entry holds - at most 128, which is 16K |
| 16-31 | Block numbers: where on the disc the file's data is |
Names are stored in capitals by convention, and a name shorter than eight letters is filled out with spaces, which is why the dump shows STORY and then three 20s. There is no dot on the disc: the dot is something you type, and something edisc.py puts back when it shows a name.
Extent and records only make full sense with files bigger than 16K, which need more than one entry. STORY.TXT is one: its first entry (slot 1) has extent 0 and 128 records - 16K, a full entry - and its second (slot 2) has extent 1 and 1D, 29 records, for the rest. S17 puts them together.
Block numbers say where the data is. The disc's file space is handed out in blocks of 2K, numbered from 1; S15 explains how a block number becomes four sectors. An entry has room for eight block numbers, two bytes each, low byte first - so 02 00 03 00 04 00 ... means blocks 2, 3, 4, and so on. 00 00 means "no block here". Eight blocks of 2K is 16K: that is why one entry holds at most 16K.
A file can be locked, so the Einstein will not let it be changed or deleted by accident. The lock is a single bit: the top bit (the one worth 128, 0x80) of byte 9, the first letter of the extension. README on FILES.DSK is locked, so its byte 9 is not 54 (T) but D4 - 54 with the top bit set. That is why S12's dump showed .XT. XtalDOS shows a locked file with a *:
1:*README .TXT
Python's & works on the bits of two numbers, keeping only the bits set in both. So:
entry[9] & 0x80 is 128 if the lock bit is set, and 0 if not - a test.entry[9] & 0x7F keeps the other seven bits and clears the top one: 0xD4 & 0x7F is 0x54, T again.The Einstein's own DOS stores every block number in two bytes, as above. But a few real Einstein discs - written by something else - store each block number in one byte: sixteen of them, 01 02 03 00 00 .... A tool that reads all discs must handle both, and it can tell which it has, entry by entry: no block number on an Einstein disc is ever bigger than 255, so in two-byte form the second byte of every pair - bytes 17, 19, 21 and so on - is always 00. If every one of those is zero, the entry is two-byte; if any is not, it is one-byte.
The course download has a disc whose one file is stored the one-byte way, EIGHTBIT.DSK. Look at it in XtalDOS:
0:DIR 1:
1: EIGHT .TXT
4k Size, 186k Free, 190k Total
The file holds 5,000 bytes in three blocks, 6K - but XtalDOS reads its 01 02 03 00 as the two-byte numbers 0201 and 0003: block 513, which does not exist, and block 3. Ask it to DISP 1:EIGHT.TXT and it fails with Not Ready 1: - it is looking for block 513. (Answer N to Try Again(Y/N)?.) The Einstein's own DOS only understands two-byte numbers; your tool will understand both.
Three helpers, after read_directory. entry_text turns bytes into printable text (using show_byte from S6). entry_name builds NAME.EXT from bytes 1-11, clearing the lock bit, and rstrip() removes the padding spaces from the end of a piece of text. entry_blocks reads the block numbers whichever way they are stored:
def entry_text(raw):
text = ""
for b in raw:
text = text + show_byte(b)
return text
def entry_name(entry):
name = entry_text(entry[1:9]).rstrip()
ext = entry_text(bytes([entry[9] & 0x7F]) + entry[10:12]).rstrip()
return name + "." + ext
def entry_blocks(entry):
field = entry[16:32]
sixteen_bit = True
for i in range(1, 16, 2):
if field[i] != 0:
sixteen_bit = False
blocks = []
if sixteen_bit:
for i in range(0, 16, 2):
block = int.from_bytes(field[i:i + 2], "little")
if block != 0:
blocks.append(block)
else:
for block in field:
if block != 0:
blocks.append(block)
return blocks
range(1, 16, 2) is 1, 3, 5 ... 15: the second byte of each pair.
Then the command, after show_directory. continue inside a loop means "skip the rest of the body and go on to the next one":
def show_entries(disc):
directory = read_directory(disc)
for slot in range(64):
entry = directory[slot * 32:slot * 32 + 32]
if entry[0] == 0xE5:
continue
locked = ""
if entry[9] & 0x80:
locked = " locked"
print(f"Slot {slot:2}: {entry_name(entry):12} status {entry[0]:02X}"
f" extent {entry[12]} records {entry[15]:3} blocks {entry_blocks(entry)}{locked}")
if entry[9] & 0x80: works because Python treats 0 as false and any other number as true. And one more command line, before the else::
elif command == "entries":
show_entries(disc)
(The whole program: s13-edisc.py.)
Run it
python3 edisc.py entries FILES.DSK
python3 edisc.py entries EIGHTBIT.DSK
What you should see
Slot 0: README.TXT status 00 extent 0 records 1 blocks [1] locked
Slot 1: STORY.TXT status 00 extent 0 records 128 blocks [2, 3, 4, 5, 6, 7, 8, 9]
Slot 2: STORY.TXT status 00 extent 1 records 29 blocks [10, 11]
Slot 3: NUMBERS.BIN status 00 extent 0 records 24 blocks [12, 13]
Slot 0: EIGHT.TXT status 00 extent 0 records 40 blocks [1, 2, 3]
Every byte accounted for. README is locked, in block 1. STORY.TXT is two entries and ten blocks. And EIGHT.TXT is three blocks, 1, 2 and 3 - which your tool read correctly and the Einstein's DOS did not.
entry_name, take out & 0x7F. What does entries FILES.DSK show for README now?entry_blocks always use the two-byte branch (set sixteen_bit = True after the loop). What does it give for EIGHT.TXT - and which number from XtalDOS's DIR does that explain?print(len(entry_blocks(entry)) * 2) as the last line inside show_entries' loop. Add up the numbers it prints for FILES.DSK. Where have you seen the total?A directory entry is status (0 in use, E5 free), an eight-character name and three-character extension padded with spaces, an extent number, a count of 128-byte records, and up to eight block numbers - two bytes each as the Einstein's DOS writes them, one byte each on a few discs written by other things, told apart by whether the pairs' second bytes are all zero. The top bit of byte 9 is the lock. edisc.py entries decodes them all.
S14, list - turning entries into a list of files, the way DIR does, with sizes worked out and the free space at the bottom.
This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.