Assembly Disc Access Primer, a TatungBytes course: DISKMON reading the hidden sector IDs of the course's own practice disc, on a rainbow sunburst
← Back to Modules
module
17

A File, by Hand

Introduction

In S7 and S8 the BDOS found POEM.TXT and fed it to you a record at a time. Here is what it did, done by DISKMON with nothing but MOS calls.

Name to Entry

FINDSET reads the drive and name, builds the name into the FCB (only bytes 1-11 matter - they line up with an entry's bytes 1-11), and reads the directory. FINDEXT looks through the 64 entries for one that is in use, has the right extent number in byte 12, and the same eleven name bytes (with bit 7 cleared, so a locked file still matches). L asks for extent 0, then 1, and so on until there are no more.

Entry to Blocks to Sectors

Each entry has up to eight block numbers, two bytes each, low byte first; a 0 means no more. For each block, BLKSEC works out each of its four sectors from S3's rule: logical sector 4 × block + i; track = logical ÷ 10 + 2; sector = the remainder. DIV10 does the dividing.

Only What Is There

The entry's byte 15 says how many records of the extent are used. L keeps the count in RLEFT and shows only that many: four records from a full sector, fewer from the last. And TYPEOUT stops at the 1Ah that ends the text.

The Code

After the line JP Z,CMDD, add:

        CP 'L'
        JP Z,CMDL

After the last line of PNAME (RET), add:

; L d name: find a file in drive d's directory and type it, following
; its blocks to the sectors - what the BDOS's open and read did for you.
CMDL:   CALL FINDSET
        XOR A
        LD (EXTENT),A
CMDL1:  CALL FINDEXT
        JR NZ,CMDL9
        LD A,(IX+15)            ; records in this extent
        LD (RLEFT),A
        LD B,8
        PUSH IX
        POP HL
        LD DE,16
        ADD HL,DE               ; HL -> the block numbers
CMDL2:  PUSH BC
        LD E,(HL)
        INC HL
        LD D,(HL)
        INC HL
        PUSH HL
        LD A,D
        OR E
        JR Z,CMDL8              ; block 0: no more blocks
        LD B,4                  ; four sectors to a block
        LD C,0
CMDL3:  PUSH BC
        PUSH DE
        LD A,(RLEFT)
        OR A
        JR Z,CMDL7
        CALL BLKSEC             ; block DE, sector C -> STRK, SSEC
        CALL READSEC
        JP NZ,DISCERR
        LD A,(RLEFT)            ; up to four records from this sector
        LD B,4
        CP B
        JR NC,CMDL4
        LD B,A
CMDL4:  LD A,(RLEFT)
        SUB B
        LD (RLEFT),A
        LD HL,0
        LD DE,128
CMDL5:  ADD HL,DE
        DJNZ CMDL5              ; HL = 128 x records
        LD B,H
        LD C,L
        LD HL,SECBUF
        CALL TYPEOUT
        JR C,CMDL6              ; 1Ah: the text has ended
        POP DE
        POP BC
        INC C
        DJNZ CMDL3
        POP HL
        POP BC
        DJNZ CMDL2
        JR CMDLX
CMDL6:  JP CRMAIN
CMDL7:  POP DE
        POP BC
CMDL8:  POP HL
        POP BC
CMDLX:  LD HL,EXTENT
        INC (HL)
        JR CMDL1
CMDL9:  LD A,(EXTENT)
        OR A
        LD HL,NOTFM
        JP Z,DONE
        JP CRMAIN

; FINDSET: drive and name from the line; read the directory.
FINDSET: CALL GETNUM
        JP C,WHAT
        LD (SDRV),A
        LD DE,FCB
        CALL GETNAME
        JP C,WHAT
        CALL READDIR
        JP NZ,DISCERR
        RET

; FINDEXT: IX -> the entry for FCB's name with extent EXTENT. Z if found.
FINDEXT: LD IX,DIRBUF
        LD B,64
FE1:    LD A,(IX+0)
        CP 0E5H
        JR Z,FE3
        LD A,(EXTENT)
        CP (IX+12)
        JR NZ,FE3
        PUSH BC
        PUSH IX
        POP HL
        INC HL
        LD DE,FCB+1
        LD B,11
FE2:    LD A,(HL)
        AND 7FH                 ; ignore the locked bit
        LD C,A
        LD A,(DE)
        CP C
        JR NZ,FE4
        INC HL
        INC DE
        DJNZ FE2
        POP BC
        XOR A                   ; Z: found
        RET
FE4:    POP BC
FE3:    LD DE,32
        ADD IX,DE
        DJNZ FE1
        OR 1                    ; NZ: not there
        RET

; BLKSEC: block DE's sector C (0-3) -> STRK, SSEC.
; Logical sector 4 x block + C, counted from track 2 sector 0;
; track = logical / 10 + 2, sector = logical mod 10.
BLKSEC: EX DE,HL
        ADD HL,HL
        ADD HL,HL
        LD B,0
        ADD HL,BC
        CALL DIV10
        LD (SSEC),A
        LD A,L
        ADD A,2
        LD (STRK),A
        RET

After the line RECM: DEFB ' RECORDS ',0, add:

EXTENT: DEFB 0
RLEFT:  DEFB 0

Run it on a copy of PRIMER.DSK:

L 1 POEM.TXT

What you should see

> L 1 POEM.TXT
THE DISC
Forty tracks go round and round,
ten sectors on each one;
the directory shows what can be found,
and blocks say where they've gone.

Ask the DOS and it will look
the name up for you, line by line;
or read the sectors like a book
and all the work is yours and mine.

>

The same poem T showed in S8, read without the BDOS.

Change One Thing

  • Make L show only extent 0 of a file. What does L 1 ROOMS.DAT stop at then - and how many records is that?
  • Remove the RLEFT checks so that every sector of every block is shown. On which file would you see the difference?

Summary

Directory entry, block numbers, tracks and sectors, record count: DISKMON now does everything the BDOS's open and read did, with the MOS alone.

Next

S18 writes a sector - and changes a file's name without the DOS.

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.