Organisation of an MD-100 floppy disk
=====================================

Physical disk parameters:
number of Cylinders		- 80
number of Heads			- 1
number of Sectors on a Cylinder	- 16
Sector Base			- 1
Sector Size			- 256

The disk contains 1280 logical sectors grouped into 320 clusters (each of size
of 4 sectors).
First cluster is occupied by the File Allocation Table.
Next three clusters are occupied by the Directory.
Data start from the fourth cluster.

Each cluster is assigned a 2-byte entry in the FAT (the most significant byte
first). Bit definitions for FAT entries:
bit 15		- marks an used entry
bit 14		- marks the end of chain
bits 13..12	- number of the last sector in the last cluster
bits 11..0	- number of the cluster (this or next in chain)

Structure of a directory entry:
1 byte	- file type
8 bytes	- file name, padded with spaces
3 bytes	- file extension, padded with spaces
1 byte	- unknown meaning (flag?)
2 bytes	- starting cluster for data, MSB first
1 byte	- file attribute, &H00 = read/write file, &H01 = read-only file


Communication with the host (on an example of a PB-1000)
========================================================

Data flow is controlled by four bits of the 8-bit port:
P4 - output, 1=reset, 0=normal_operation
P3 - output, FDD power control: 1=power_off, 0=power_on
P2 - output, transfer direction strobe: 1=write, 0=read
P0 - input, transfer direction acknowledge
Note: the PB-2000C uses different port bits (P5..P2)

The FDD interface uses two locations in the memory address space for data
exchange:
&H00C03	- data read from the FDD interface
&H00C04	- data sent to the FDD interface

The kind of the connected interface is determined by the host by reading the
memory location &H00C03 directly after a reset pulse, before issuing any FDD
commands. Assignment of values:
&HFF	- no I/O device present
&H00	- FA-7 (LPT, RS232, Tape Recorder)
&H55	- MD-100 (LPT, RS232, Floppy Disk)

Single transfer transaction:
1. the host sets the P2 output to 1 (write) then waits until the FDD interface
 returns 0 on the P0 input
2. the host writes a data byte to the location &H00C04
3. the host sets the P2 output to 0 (read) then waits until the FDD interface
 returns 1 on the P0 input
4. the host reads a data byte from the location &H00C03


List of FDD commands
====================

The following information is based on the PB-1000 and OM-53B ROM disassembly
----------------------------------------------------------------------------

&H00 - read the first directory entry
&H01 - read the next directory entry
&H02 - read the previous directory entry
write: command code		read: status, continue if 0, otherwise leave
write: &H00			read: number of returned bytes, LSB
write: &H00			read: number of returned bytes, MSB
; the number of bytes should be equal 17 (status byte + directory entry size),
; or 1 if an error occured
write: &H00			read: status followed by the directory entry
; repeat above transaction until specified number of bytes transferred
; meaning of status bits: bit 0 set if no directory entry returned


&H10 - write to a sequential file
write: command code		read: status, continue if 0, otherwise leave
write: number_of_bytes+2, LSB	read: status, continue if 0, otherwise leave
write: number_of_bytes+2, MSB	read: status, continue if 0, otherwise leave
write: &H00			read: status, continue if 0, otherwise leave
write: file handle		read: status, continue if 0, otherwise leave
; valid file handle range 0..15
write: data byte		read: status, continue if 0, otherwise leave
; repeat above transaction until specified number of bytes bytes written
write: &H00			read: number of returned bytes, LSB (=&H01)
write: &H00			read: number of returned bytes, MSB (=&H00)
write: &H00			read: status
; meaning of status bits
; bit 7 set	- file not opened
; bit 6 set	- access error
; bit 2 set	- device operation error


&H11 - write to a random file (only fixed number of 256 bytes)
write: command code		read: status, continue if 0, otherwise leave
write: number_of_bytes+4, LSB	read: status, continue if 0, otherwise leave
write: number_of_bytes+4, MSB	read: status, continue if 0, otherwise leave
write: &H00			read: status, continue if 0, otherwise leave
write: file handle		read: status, continue if 0, otherwise leave
; valid file handle range 0..15
write: record, LSB		read: status, continue if 0, otherwise leave
write: record, MSB		read: status, continue if 0, otherwise leave
write: data byte		read: status, continue if 0, otherwise leave
; repeat above transaction until specified number of bytes bytes written
write: &H00			read: number of returned bytes, LSB (=&H01)
write: &H00			read: number of returned bytes, MSB (=&H00)
write: &H00			read: status
; meaning of status bits
; bit 7 set	- file not opened
; bit 6 set	- access error
; bit 2 set	- device operation error


&H20 - read from a sequential file
write: command code		read: status, continue if 0, otherwise leave
write: &H02 (bytes, LSB)	read: status, continue if 0, otherwise leave
write: &H00 (bytes, MSB)	read: status, continue if 0, otherwise leave
write: &H00			read: status, continue if 0, otherwise leave
write: file handle		read: status, continue if 0, otherwise leave
; valid file handle range 0..15
write: &H00			read: number of returned bytes, LSB
write: &H00			read: number of returned bytes, MSB
; the number of returned bytes can be in range 1..257
write: &H00			read: status
; repeat following transaction until all data bytes read
write: &H00			read: data byte
; meaning of status bits
; bit 7 set	- file not opened
; bit 6 set	- access error
; bit 2 set	- no data for read


&H21 - read from a random file (only fixed number of 256 bytes)
write: command code		read: status, continue if 0, otherwise leave
write: &H04 (bytes, LSB)	read: status, continue if 0, otherwise leave
write: &H00 (bytes, MSB)	read: status, continue if 0, otherwise leave
write: &H00			read: status, continue if 0, otherwise leave
write: file handle		read: status, continue if 0, otherwise leave
; valid file handle range 0..15
write: record, LSB		read: status, continue if 0, otherwise leave
write: record, MSB		read: status, continue if 0, otherwise leave
write: &H00			read: number of returned bytes, LSB
write: &H00			read: number of returned bytes, MSB
; the number of returned bytes can be in either 1 or 257
write: &H00			read: status
; repeat following transaction until all data bytes read
write: &H00			read: data byte
; meaning of status bits
; bit 7 set	- file not opened
; bit 6 set	- access error
; bit 2 set	- no data for read


&H30 - open a sequential file for output
&H31 - open a random file for output
&H32 - open a sequential file for input
&H33 - open a random file for input
&H34 - open a sequential file for append
write: command code		read: status, continue if 0, otherwise leave
write: &H12 (bytes, LSB)	read: status, continue if 0, otherwise leave
write: &H00 (bytes, MSB)	read: status, continue if 0, otherwise leave
write: &H00			read: status, continue if 0, otherwise leave
write: file handle		read: status, continue if 0, otherwise leave
; valid file handle range 0..15
write: file type		read: status, continue if 0, otherwise leave
; repeat following transaction 14 times
; (11 characters of the file name + 3 dummy bytes)
write: file name character	read: value ignored
write: &H00			read: status, continue if 0, otherwise leave
write: &H00			read: number of returned bytes, LSB
write: &H00			read: number of returned bytes, MSB
; the number of bytes should be equal 17 (status byte + directory entry size)
; if the file was succesfully opened for read, otherwise 1 (status byte)
write: &H00			read: status followed by the directory entry
; repeat above transaction until specified number of bytes transferred
; meaning of status bits
; bit 5 set	- open error


&H40 - close file
write: command code		read: status, continue if 0, otherwise leave
write: file handle		read: status, 0 if OK
; valid file handle range 0..15, or &HFF to close all files


&H50 - delete file
write: command code		read: status, continue if 0, otherwise leave
write: &H11 (bytes, LSB)	read: status, continue if 0, otherwise leave
write: &H00 (bytes, MSB)	read: status, continue if 0, otherwise leave
write: &H00			read: status, continue if 0, otherwise leave
write: &HFF			read: status, continue if 0, otherwise leave
; repeat following transaction 14 times
; (11 characters of the file name + 3 dummy bytes)
write: file name character	read: value ignored
write: &HFF			read: status, continue if 0, otherwise leave
write: &H00			read: number of returned bytes, LSB (=&H01)
write: &H00			read: number of returned bytes, MSB (=&H00)
write: &H00			read: status
; meaning of status bits
; bit 4	cleared	- file not found
; bit 1 set	- write protected
; bit 3 set	- other error


&H60 - rename file
write: command code		read: status, continue if 0, otherwise leave
write: &H22 (bytes, LSB)	read: status, continue if 0, otherwise leave
write: &H00 (bytes, MSB)	read: status, continue if 0, otherwise leave
write: &H00			read: status, continue if 0, otherwise leave
write: &HFF			read: status, continue if 0, otherwise leave
; repeat following transaction 14 times
; (11 characters of the old file name + 3 dummy bytes)
write: file name character	read: value ignored
write: &HFF			read: status, continue if 0, otherwise leave
write: &H00			read: status, continue if 0, otherwise leave
write: &HFF			read: status, continue if 0, otherwise leave
; repeat following transaction 14 times
; (11 characters of the new file name + 3 dummy bytes)
write: file name character	read: value ignored
write: &HFF			read: status, continue if 0, otherwise leave
write: &H00			read: number of returned bytes, LSB (=&H01)
write: &H00			read: number of returned bytes, MSB (=&H00)
write: &H00			read: status
; meaning of status bits
; bit 4	cleared	- file not found
; bit 1 set	- write protected
; bit 3 set	- other error


&H70 - write physical sector
write: command code		read: status, continue if 0, otherwise leave
write: &H03 (bytes, LSB)	read: status, continue if 0, otherwise leave
write: &H01 (bytes, MSB)	read: status, continue if 0, otherwise leave
write: &H00			read: status, continue if 0, otherwise leave
write: track (0..79)		read: status, continue if 0, otherwise leave
write: sector (1..16)		read: status, continue if 0, otherwise leave
; repeat following transaction 256 times regardless of the status value
write: data byte		read: status
; check last read status value, meaning of bits:
; bit 1 set	- write protected


&H80 - read physical sector
write: command code		read: status, continue if 0, otherwise leave
write: track (0..79)		read: status, continue if 0, otherwise leave
write: sector (1..16)		read: status, continue if 0, otherwise leave
; repeat following transaction 256 times
write: &H00			read: data byte


&H90 - format disk
write: command code		read: status, 0 if OK


&HC0 - get the file size (number of records)
write: command code		read: status, continue if 0, otherwise leave
write: file handle		read: status, continue if 0, otherwise leave
write: &H00			read: status
write: &H00			read: number of records, LSB
write: &H00			read: number of records, MSB
; meaning of status bits
; bit 7 set	- open error


&HD0 - get the free disk space (number of free clusters)
write: command code		read: status, continue if 0, otherwise leave
write: &H00			read: status
write: &H00			read: number of clusters, LSB
write: &H00			read: number of clusters, MSB


Additional test results
-----------------------

An illegal command code returns status code &H20.

Any command without an inserted disk returns status code &H04.

The MD-100 file system is designed to access files in 'records' of size 256
bytes. The records are numbered from 1 up.

An attempt to open a file with illegal characters in the name is ignored.


Commands &H00..&H02 (read a directory entry) return status code &H10 when
a directory entry was returned, or &H01 when not.

Command &H10 (write sequential)
The command appends Ctrl-Z (&H1A) to the written data and fills the remaining
space in the last record with zeros.
The command writes data from the beginninig of the current record. If the
specified number of bytes to be written is not a multiply of 256, then the
subsequent call of this command will overwrite the last record of the
previously written data block.
A file opened for random access can be written sequentially.

Command &H11 (write random)
Writing past the end of the file causes the file to be extended to the
position being written.

Commands &H20 (read sequential)
The command transfers the contents of a single record. When the last record
of the file is read, neither the trailing zeros nor the byte Ctrl-Z (&H1A) are
transferred, and the status code &H01 is returned.
A file opened for random access can be read sequentially.
An attempt to read from files opened for write doesn't cause an error, but
doesn't return any meaningful data, either.

Commands &H21 (read random)
An attempt to read the terminating record of the file, or past the end of
file fails with a status code &H04.
An attempt to read from a file opened for sequential access fails with a
status code &H10.

Command &H30 (open a file for sequential output)
If a file with the specified name doesn't exist, an empty file containing
only Ctrl-Z (&H1A) is created.
If an existing file is opened for sequential output, its contents is erased
with a single byte Ctrl-Z (&H1A).
The record number is set to the first record of the file.
On success the status code &H00 is returned.
This command doesn't return a directory entry, regardless of whether the file
already existed or not.
If there's no free directory entry, the command fails with the status code
&H40 in the transaction following the file name along with three dummy bytes.

Command &H31 (open a file for random output)
If a file with the specified name doesn't exist, an empty file containing
only Ctrl-Z (&H1A) is created and status code &H00 is returned.
If an existing file is opened, status code &H10 and a directory entry is
returned. The record number is set to the first record of the file.
If there's no free directory entry, the command fails with the status code
&H40 in the transaction following the file name along with three dummy bytes.

Commands &H34 (open a file for append)
This command can only open an existing file. If a file with the specified name
doesn't exist, the status code &H00 is returned, but the file isn't created.
On success the command returns:
- status code &H10
- directory entry
- contents of the terminating record (as the command &H20)
The record number points to the terminating record of the file. This record
will be overwritten by the subsequent command &H10 (write sequential).

Commands &H32,&H33 (open a file for input)
If a matching file name is found, then the status code &H10 and a directory
entry is returned, otherwise the status code &H00 and no directory entry is
returned.
The record number is set to the first record of the file.
Trying to open an already opened file fails with a status code &H20, and no
directory entry is returned.
The file type is ignored, so the host has to check the corresponding byte in
the returned directory entry.

Command &H40 (close file) always returns status code &H00, even if the file
isn't opened.
If the file handle has bit 7 set, all files are closed.

Command &H50 (delete file) returns status code &H10 if the file was found,
or &H00 if it wasn't.
An opened file can be deleted.

Command &H60 (rename file) returns following status codes:
- &H10 if the file is successfully renamed
- &H00 if the file isn't found
- &H18 if the file is found, but can't be renamed, because a file of the new
  name already exists

Command &HC0 (get the file size) doesn't count the terminating record when
a file is opened for random access.


Summary of status bits
----------------------

bit 7 set	- file not opened
bit 6 set	- no room on the disk
bit 5 set	- invalid command
bit 4 set	- file of specified name found
bit 3 set	- rename failed, because file of specified name already exists
bit 2 set	- an attempt to read past the end of file
		  disk not inserted
bit 1 set	- disk write protected
bit 0 set	- access to the last record of file
		  end of the directory


Examples for the PB-1000
========================

Example 1: check whether the FDD interface is present
-----------------------------------------------------

	PRE	IX,&H0C03
	SLW
	PST	PD,&HD4
	PST	PE,&HDC
	XRCM	$0,$0,8		;C7 40 E0, delay
	PST	PD,&HC4
	XRCM	$0,$0,8		;C7 40 E0, delay
	LD	$0,(IX+$31)
	FST
	SBC	$0,&H55		;flag Zero if the interface is present
	RTN

; It is more convenient to check the copy of the identification byte read
; after the power on by the operating system, as in the procedure ISFDD below.


Common routines
---------------

ISFDD:	PRE	IX,&H6BFA	;OPTCD, option code
	LD	$0,(IX+$31)
	PRE	IX,&H0C03
	SBC	$0,&H55
	RTN

; read port P0
GETP0:	PHS	$1
	GPO	$1
	GPO	$0
	SBC	$1,$0
	PPS	$1
	JR	NZ,GETP0
	ANC	$0,$30
	RTN

; single FDD transaction
; write byte $1 to the FDD, then read byte $0
; on entry: values of IX and PD expected as set by the ISFDD routine
; no time limit checking
TRANS:	CAL	GETP0
	JR	NZ,TRANS	;wait until transfer direction acknowledged
	ST	$1,(IX+$30)
	PST	PD,&HC0		;transfer direction: reading from the FDD
TRAN1:	CAL	GETP0
	JR	Z,TRAN1		;wait until transfer direction acknowledged
	LD	$0,(IX+$31)
	PST	PD,&HC4		;transfer direction: writing to the FDD
	SBC	$0,$31		;test for zero
	RTN

; transmit a word $2,$3 to the FDD
SENDW:	LD	$1,$2		;LSB
	CAL	TRANS
	RTN	NZ
	LD	$1,$3		;MSB
	CAL	TRANS
	RTN

; receive a word $2,$3 from the FDD
RECW:	LD	$1,$31
	CAL	TRANS
	LD	$2,$0		;LSB
	LD	$1,$31
	CAL	TRANS
	LD	$3,$0		;MSB
	RTN

; transmit a data block of size $2,$3 pointed to by IZ
SENDM:	LDI	$1,(IZ+$31)
	CAL	TRANS
	RTN	NZ
	SBW	$2,$30
	JR	NZ,SENDM
	RTN

; receive a data block of size $2,$3 to the memory location pointed to by IZ
RECM:	SBW	$2,$30
	RTN	C
	LD	$1,$31
	CAL	TRANS
	STI	$0,(IZ+$31)
	JR	RECM


Example 2: read the sector $3 from the track $2 to the address IZ
-----------------------------------------------------------------

; the program exits with NZ when an error occured
	CAL	ISFDD
	RTN	NZ		;leave when FDD interface absent
	LD	$1,&H80		;command code
	CAL	TRANS
	RTN	NZ		;leave when status <> 0
	CAL	SENDW		;track in $2, sector in $3
	RTN	NZ
	LDW	$2,256		;number of read bytes
	CAL	RECM
	SB	$0,$0
	RTN


Common routines to handle files
-------------------------------

; all functions exit with NZ when an error occured
; last status code returned in $0

; open a file specified by the data block pointed to by IZ
OPEN:	LDW	$2,21		;data block size
	CAL	SENDM
	RTN	NZ
	CAL	RECW		;$2,$3 <- number of returned bytes
	LD	$1,$31
	CAL	TRANS
	PHS	$0		;status
OPEN2:	SBW	$2,$30
	JR	Z,OPEN3
	LD	$1,$31
	CAL	TRANS		;remaining returned data ignored
	JR	OPEN2
OPEN3:	PPS	$0
	ANC	$0,&H20
	RTN

; close file $6
CLOSE:	LD	$1,&H40		;command code: close file
	CAL	TRANS
	RTN	NZ
	LD	$1,$6		;file handle
	CAL	TRANS
	RTN

; write to the sequential file $6 number of bytes $4,$5 pointed to by IZ
WRITE:	LD	$1,&H10		;command code: write to a sequential file
	CAL	TRANS
	RTN	NZ
	LDW	$2,2
	ADW	$2,$4
	CAL	SENDW		;number_of_bytes+2 in $2,$3
	RTN	NZ
	LD	$1,$31
	CAL	TRANS
	RTN	NZ
	LD	$1,$6		;file handle
	CAL	TRANS
	RTN	NZ
	LDW	$2,$4		;number of bytes
	CAL	SENDM
	RTN	NZ
	CAL	RECW		;$2,$3 <- number of returned bytes, ignored
	LD	$1,$31
	CAL	TRANS		;$0 <- status
	ANC	$0,&HC4
	RTN

; read from a sequential file $6 to the address IZ
; number of read bytes in $4,$5
READ:	LD	$1,&H20		;command code: read from a sequential file
	CAL	TRANS
	RTN	NZ
	LDW	$2,2		;number of bytes
	CAL	SENDW
	RTN	NZ
	LD	$1,$31
	CAL	TRANS
	RTN	NZ
	LD	$1,$6		;file handle
	CAL	TRANS
	RTN	NZ
	CAL	RECW		;$2,$3 <- number of returned bytes
	LD	$1,$31
	CAL	TRANS
	PHS	$0		;status
	SBW	$2,$30
	LDW	$4,$2
	CAL	RECM
	PPS	$0
	ANC	$0,&HC4
	RTN


Example 3: write to a sequential file
-------------------------------------

MAIN:	CAL	ISFDD
	RTN	NZ		;leave when FDD interface absent
; open (or create if not exists) a sequential file for write
	PRE	IZ,FILE1
	LD	$6,(IZ+4)	;file handle
	CAL	OPEN
	RTN	NZ
; write a text to the file
	PRE	IZ,DATA1	;data to write
	LDW	$4,DATA2-DATA1	;data size
	CAL	WRITE
; close the file
	CAL	CLOSE
	RTN

; data block for the function OPEN
FILE1:	DB	&H30		;command code
	DW	18		;number of bytes
	DB	&H00
	DB	1		;file handle
	DB	&H24		;file type: sequential ASCII
	DB	"HELLO   TXT"	;file name
	DB	0,0,0		;dummy
	DB	&H00

; data to write
DATA1:	DB	"Hello world!",&H0D,&H0A
DATA2:


Example 4: display the contents of a sequential file
----------------------------------------------------

MAIN:	CAL	ISFDD
	RTN	NZ		;leave when FDD interface absent
; open a sequential file for read
	PRE	IZ,FILE2
	LD	$6,(IZ+4)	;file handle
	CAL	OPEN
	RTN	NZ
; read a data chunk to the buffer BUF2
RD1:	LDW	$8,BUF2
	PRE	IZ,$8
	CAL	READ
	JR	NZ,RD4		;error
; display the contents of the buffer BUF2
	PHS	$0
RD3:	SBW	$4,$30
	JR	C,RD3A
	LD	$16,($8)
	PHSM	$9,6
	CAL	&HFF9E		;OUTAC, display char. $16 on the OUTDV device
	PPSM	$4,6
	ADW	$8,$30
	JR	RD3
RD3A:	PPS	$0
	ANC	$0,&H01		;end of file?
	JR	Z,RD1
; close the file
RD4:	CAL	CLOSE
	RTN

; data block for the function OPEN
FILE2:	DB	&H32		;command code
	DW	18		;number of bytes
	DB	&H00
	DB	1		;file handle
	DB	&H24		;file type: sequential ASCII
	DB	"HELLO   TXT"	;file name
	DB	0,0,0		;dummy
	DB	&H00

BUF2:	DS	256


Example 5: browse the floppy disk directory
-------------------------------------------

MAIN:	CAL	ISFDD
	RTN	NZ		;leave when FDD interface absent
	LD	$1,&H00		;command code: first directory entry
MAIN1:	LDW	$18,BUF3
	PRE	IZ,$18
	CAL	RDDIR
	JR	Z,MAIN2
; beep if an error occured
	CAL	&HAAE3		;low tone beep
	JR	MAIN3
; display the file name
MAIN2:	LD	$16,&H0B	;print control code 'home'
	CAL	&HFF9E		;OUTAC, display char. $16 on the OUTDV device
	CAL	DIFN
; wait for a key
MAIN3:	GRE	IX,$16
	CAL	&HE8B9		;KYIN
	PRE	IX,$16
	LD	$1,&H01		;command code: next directory entry
	SBC	$0,&H1F		;arrow down pressed?
	JR	Z,MAIN1
	LD	$1,&H02		;command code: previous directory entry
	SBC	$0,&H1E		;arrow up pressed?
	JR	Z,MAIN1
	JR	MAIN3

; read a directory entry to the buffer pointed to by the register IZ
; the procedure expects the command code in $1 and returns NZ when an error
; occured
RDDIR:	CAL	TRANS
	RTN	NZ
	CAL	RECW		;$2,$3 <- number of returned bytes, LSB
	LD	$1,$31
	CAL	TRANS
	PHS	$0		;status
	SBW	$2,$30
	LDW	$4,$2
	CAL	RECM
	PPS	$0
	ANC	$0,&H01
	RTN

; display the file name from a directory entry pointed to by $18,$19
DIFN:	LD	$17,11		;number of characters
DIFN1:	ADW	$18,$30
	LD	$16,($18)
	SBC	$16,&H20
	JR	C,DIFN2
	SBC	$16,&H7F
	JR	C,DIFN3
DIFN2:	LD	$16,&H2A	;invalid characters replaced with asterisks
DIFN3:	CAL	&HFF9E		;OUTAC, display char. $16 on the OUTDV device
	SB	$17,$30
	JR	NZ,DIFN1
	RTN

BUF3:	DS	17
