Inside the Einstein: The Disc, a TatungBytes course: XtalDOS listing the first practice disc image, on a rainbow sunburst
← Back to Modules
module
17

Files Bigger Than 16K

Introduction

S16's type stopped STORY.TXT in the middle of line 335, because it read only the file's first directory entry, and one entry holds at most 16K. STORY.TXT is 20,000 bytes. This section reads the rest - and with it, every file an Einstein disc can hold.

Extents

A directory entry has room for eight block numbers: 8 x 2K = 16K. A bigger file gets more than one entry, each describing the next 16K, and all carrying the same name. Each one is an extent, and byte 12 numbers them: 0 for the first 16K, 1 for the next, 2 for the next.

Every extent but the last is full: 128 records, eight blocks. The last holds whatever is left. STORY.TXT has two:

STORY.TXT's two entries, their blocks, and how they join into one file

To read the whole file, read each extent's blocks and keep its own records, in extent order, and join them. The order comes from byte 12, not from where the entries happen to sit in the directory, so the tool looks for extent 0, then extent 1, and so on - as many as there are entries with that name. If one of them is not there, the file is damaged, and the tool says so rather than handing back a file with a hole in it.

Nothing Yet

The search needs a way to say "not found yet". Python's None is a value meaning "nothing": set entry = None before looking, and if it is still None afterwards - tested with is None - nothing matched.

The code

This section replaces S16's read_file. Delete the old one and put this in its place, after find_file:

def read_file(disc, name):
    entries = find_file(disc, name)
    data = b""
    for extent in range(len(entries)):
        entry = None
        for e in entries:
            if e[12] == extent:
                entry = e
        if entry is None:
            sys.exit(f"{name} is damaged: its extent {extent} is missing")
        part = b""
        for block in entry_blocks(entry):
            part = part + read_block(disc, block)
        data = data + part[:entry[15] * 128]
    return data

Nothing else changes: type calls read_file, so it now reads big files too. That is one of the rewards of putting each job in its own function.

(The whole program: s17-edisc.py.)

Run it

python3 edisc.py type FILES.DSK STORY.TXT

What you should see - 409 lines this time. Where S16 stopped, the story now runs straight on:

Line 334 of STORY.TXT, which needs two extents.
Line 335 of STORY.TXT, which needs two extents.
Line 336 of STORY.TXT, which needs two extents.
Line 337 of STORY.TXT, which needs two extents.

and it ends:

Line 408 of STORY.TXT, which needs two extents.
Line 409

The file was made 20,000 bytes long, which happens to fall in the middle of line 409.

Change one thing

  • In read_file, change range(len(entries)) to range(1). Which section's behaviour do you get back?
  • Change part[:entry[15] * 128] to just part. Type STORY.TXT again: the ending is the same. Why? (Think about where the 1A is, and where the extra bytes come.) What would go wrong for a file that is not text, such as NUMBERS.BIN, which has no 1A to stop at?
  • Make a copy of FILES.DSK in which STORY.TXT's first entry is deleted - set the status byte of slot 1 to E5, using what you learned in S5. What does type say now? And list?

When it goes wrong

SymptomCause
STORY.TXT is damaged: its extent 0 is missingOne of the file's entries is not in use - here slot 1 was set to E5. The rest of the file is still there, but not all of it

Summary

A file over 16K has one directory entry per 16K, all with its name, numbered 0, 1, 2 ... in byte 12: its extents. read_file now finds each extent by number, reads its blocks, keeps its records, and joins them in order, refusing a file with an extent missing. edisc.py can read any file on an Einstein disc.

Next

S18, extract - copying a file off the disc onto your computer, byte for byte.

Get the Newsletter

New guides, disk images and community finds, roughly once a quarter. No spam, we promise, this isn't Tatung's marketing department.
Your subscription could not be saved. Please try again.
Your subscription has been successful.

Newsletter

Subscribe to our newsletter and stay updated.

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.