

Common File Formats for Emulated TRS-80 Floppy Disks

Tim Mann
http://tim-mann.org
Last revised 4 Jul 2008
Most current TRS-80 Model I/III/4 emulators use one of three common file
formats for emulated floppy disk media. The extension .DSK is usually used for
all three formats, and some emulators transparently support two or three of
them, while others support only one. The two most common formats both
originated with emulators written by Jeff Vavasour, while the third originated
with an emulator written by David Keil, so I'll take the liberty of giving the
formats names that use their initials.

* The JV1 format originated in Jeff's Model I emulator for MS-DOS. It's a very
  simple, limited format that can represent only single-density media with 256
  byte sectors and a Model I directory on track 17.
* The JV3 format originated in Jeff's Model III/4 emulator for MS-DOS. It is
  much more flexible than JV1, but still cannot represent everything the real
  hardware can do, so some copy-protected disks cannot be represented. The
  format has been extended to support 8-inch floppies and sector sizes of other
  than 256 bytes, but not all emulators support these extensions. I indicate
  below which features are extensions and which emulators are known to support
  which extensions.
* The DMK format originated in David's Model III/4 emulator for Windows. Unlike
  JV1 and JV3, DMK is able to represent essentially everything that a real TRS-
  80 floppy disk controller could do, including copy protected TRS-80 disks.

This document describes the JV1 and JV3 formats in complete detail and
indicates (where known) which emulators support which. DMK is quite different
from JV1 and JV3 and is not described in this document, but there is a
description_on_David_Keil's_Web_page. You probably don't need any of this
information unless you are writing an emulator or working with unusual disks.

The JV1 Format

The JV1 format is simply an array of 256-byte sectors stored in a file. Byte 0
of the file is byte 0 of track 0, sector 0; byte 256 is byte 0 of track 0,
sector 1, and so forth. There are 10 sectors per track (i.e., single density),
numbered 0 through 9, and only one side. Tracks are numbered starting at 0. All
sectors on track 17 are formatted with the nonstandard data address mark 0xFA,
indicating a TRSDOS 2.3 directory. All other sectors are formatted with the
standard data address mark 0xFB.
Note: In emulations of the WD1791/3 floppy disk controller used in the Model
III and 4, it is best to present track 17 as being formatted with the standard
deleted data address mark 0xF8. The WD1791/3 cannot write 0xFA, and upon
reading, returns it as 0xFB. So Model III/4 operating systems use 0xF8 on the
directory track instead.

The JV3 Format

The JV3 format consists of a fixed-size array of sector headers, followed by an
area containing the data for each sector. As an extension to allow for more
sectors, a second block of sector headers and data can follow. Here is the
format in pseudo-C notation. The length of each data area depends on the
content of the headers, as described below.

  typedef struct {
    SectorHeader headers1[2901];
    unsigned char writeprot;
    unsigned char data1[];
    SectorHeader headers2[2901];
    unsigned char padding;
    unsigned char data2[];
  } JV3;


Write Protect and Padding

The field writeprot should normally contain 0xFF. As an extension, a value of
0x00 in his field indicates that the disk is write-protected (i.e., it is not
writable).
The field padding is currently unused. It should contain 0xFF.

The Sector Header

Each sector in a JV3 file is described by a three-byte header. The first byte
gives the sector's track number, the second gives its sector number on the
track, and the third is a set of flags.

  typedef struct {
    unsigned char track;
    unsigned char sector;
    unsigned char flags;
  } SectorHeader;

  #define JV3_DENSITY     0x80  /* 1=dden, 0=sden */
  #define JV3_DAM         0x60  /* data address mark code; see below */
  #define JV3_SIDE        0x10  /* 0=side 0, 1=side 1 */
  #define JV3_ERROR       0x08  /* 0=ok, 1=CRC error */
  #define JV3_NONIBM      0x04  /* 0=normal, 1=short */
  #define JV3_SIZE        0x03  /* in used sectors: 0=256,1=128,2=1024,3=512
                                   in free sectors: 0=512,1=1024,2=128,3=256 */

  #define JV3_FREE        0xFF  /* in track and sector fields of free sectors
  */
  #define JV3_FREEF       0xFC  /* in flags field, or'd with size code */

The track field gives both the physical track number on which the sector lies
and the logical track number that is formatted in the sector's ID. Numbering
starts from 0. Thus it is not possible to represent a copy-protected disk where
sectors were deliberately formatted with incorrect track numbers. The format
allows for 255 tracks (numbered 0 through 0xFE), but emulators may impose lower
limits. You can expect at least 80 tracks to be supported; xtrs currently
allows up to 96.
The sector field gives the logical sector number that is formatted into the
sector's ID field. The physical order of sectors on the track is not explicitly
represented, but xtrs (and perhaps other emulators) present them in the order
they are recorded in the JV3 file; thus when a TRS-80 program formats a track
with interleave and reads back the sector ids, they come back in the same
interleaved order.
The flags field packs in a lot of information:

* If JV3_DENSITY is set, the sector was formatted in double density (MFM); if
  not, it was formatted in single density (FM).
* The 2-bit JV3_DAM field encodes the sector's data address mark. Here are the
  basic meanings of the codes:
   ____________________________________________________________________
  |JV3_DAM_value_____|Single_density____________|Double_density________|
  |0x00______________|0xFB_(Normal)_____________|0xFB_(Normal)_________|
  |0x20______________|0xFA_(User-defined)_______|0xF8_(Deleted)________|
  |0x40______________|0xF9_(User-defined)_______|Invalid;_unused_______|
  |0x60______________|0xF8_(Deleted)____________|Invalid;_unused_______|

  The treatment of single density data address marks by the Western Digital
  1771 and 179x controllers used in original TRS-80 hardware is rather
  inconvenient. The WD1771 (used in the TRS-80 Model I) is capable of writing
  any of the four DAMs shown above, and can distinguish amongst all four on
  reading. The WD179x (used in the Model III and 4), however, can write only
  0xFB or 0xF8, and on reading, it cannot distinguish between 0xFB and 0xFA, or
  between 0xF8 and 0xF9.
  TRS-80 operating systems differentiate directory sectors from ordinary data
  sectors by giving them different data address marks. Unfortunately, Model I
  TRSDOS uses 0xFA on directory sectors, which a WD179x cannot write and cannot
  distinguish from the 0xFB used on data sectors. Other Model I operating
  systems (such as LDOS) typically write 0xF8 on directory sectors but accept
  either 0xFA or 0xF8 when reading them, allowing them to read TRSDOS disks.
  Compatibility problems remain when attempting to use Model I TRSDOS disks on
  a Model III or 4, and when attempting to use LDOS disks with Model I TRSDOS.
  An emulator can easily paper over these compatibility problems by behaving
  differently from the real hardware. All currently known emulators do so,
  making (at least) the following changes to strictly correct behavior: (1) If
  TRS-80 software attempts to write or format a 0xF8 DAM in single density, it
  is instead recorded in the JV3 header as 0xFA. (2) If TRS-80 software reads a
  single density sector that has the 0xFA DAM using an emulated WD179x, it is
  returned as 0xF8 instead. These changes hide all the DOS compatibility
  problems described in the previous paragraph.
  It might be worthwhile to make precisely correct behavior available as a run-
  time emulator option, as this might allow more types of "protected" self-
  booting TRS-80 disks to work correctly. This option is available in xtrs
  version 3.2 and later, but not in any other known emulators.
  The following tables summarize the DAM behavior of both the WD1771 and the
  WD179x. On the WD1771, read status bits 6,5 are used to report the DAM of the
  sector just read, while write command bits 1,0 select the DAM to write; on
  the WD179x, only read status bit 5 and write command bit 0 are significant:
  read status bit 6 is always 0, while write command bit 1 is used for a
  function unrelated to DAM selection. The behavior of these bits on the actual
  hardware is shown first in each cell; the modifications typically made by
  emulators follow in parentheses. It should be emphasized that WD1771 behavior
  shown here correctly describes the actual hardware, even though it
  contradicts the Western Digital data sheet; the data sheet erroneously
  transposes the column headings for status bits 6 and 5. As an aside, it seems
  likely that the engineers who designed the WD179x were misled by this error,
  as the value that the WD179x returns in bit 5 is the value that the WD1771
  was documented as returning in bit 5, but that it actually returns in bit 6.
  Read status bits 6,5:
   ____________________________________________________________________
  |DAM____|WD1771_____|WD179x_single__________|WD179x_double___________|
  |0xFB___|0,0________|0,0____________________|0,0_____________________|
  |0xFA___|0,1________|0,0_(0,1)______________|impossible______________|
  |0xF9___|1,0________|0,1____________________|impossible______________|
  |0xF8___|1,1________|0,1____________________|0,1_____________________|

  Write command bits 1,0:
   ____________________________________________________________________
  |Bits__|WD1771___________|WD179x_single_______|WD179x_double_________|
  |0,0___|0xFB_____________|0xFB________________|0xFB__________________|
  |0,1___|0xFA_____________|0xF8_(0xFA)_________|0xF8__________________|
  |1,0___|0xF9_____________|0xFB________________|0xFB__________________|
  |1,1___|0xF8_(0xFA)______|0xF8_(0xFA)_________|0xF8__________________|

* JV3_SIDE reflects both the physical side on which the sector is recorded and
  the side number formatted in the sector's ID. Thus it is not possible to
  represent a copy-protected disk where sectors were formatted with incorrect
  side numbers or with values other than 0 or 1 in the sector ID's 8-bit side
  field.
* JV3_ERROR, if set, indicates that the sector should show a data CRC error
  when read. The actual value of the two CRC bytes is not represented.
* JV3_NONIBM is an xtrs-specific extension that partially supports the WD1771's
  feature of "non-IBM" sector sizes. Only a small subset of the functionality
  is supported, with some special kludges specifically to make the VTOS 3.0
  copy protection scheme work. It is probably best to treat this bit as a "must
  be zero" field in other emulators; see the xtrs source code if you are really
  sure you want to know how it works, and keep your barf bag handy.
* JV3_SIZE is an extension to support sector sizes of other than 256 bytes.
  This field is encoded differently depending on whether the sector is in use
  or free (not in use):
   ____________________________________________________________________
  |Size|IBM size code in     |JV3_SIZE field value|JV3_SIZE field value|
  |____|sector_ID____________|if_in_use___________|if_free_____________|
  |128_|00___________________|1___________________|2___________________|
  |256_|01___________________|0___________________|3___________________|
  |512_|02___________________|3___________________|0___________________|
  |1024|03___________________|2___________________|1___________________|

  Thus, if a sector is in use, xor'ing its JV3_SIZE field with 1 gives the IBM
  size code that appears in its sector ID size field. If a sector is free,
  xor'ing its JV3_SIZE field with 2 gives its IBM size code. This
  representation was chosen because the original JV3 format supports only 256-
  byte sectors, always places zeros in the JV3_SIZE field for sectors that are
  in use, and always places ones in the field for sectors that are free.

If a sector header is not in use (free), its track and sector fields must
contain 0xFF. Its flags field must contain 0xFC plus the appropriate free JV3_
SIZE field value given above.

The Data Blocks

For each sector header, there is one data block. The blocks are placed in the
same order as the headers that describe them, and are tightly packed. Thus to
find the data block for the nth header, you need to know the total size of data
blocks 0 to n - 1. A sector header marked as free does have a corresponding
data block, of the size indicated in its size field. However, the file can (and
in fact should; see below) end immediately after the last in-use data block.

The Second Header Block

The second header block is present if and only if the file is long enough to
contain it. It starts immediately after the last data block described by the
first header block. Thus to find the second header block, you need to know the
total size of all data blocks described by the first header block.
It's obvious, by the way, how to extend the format to more than two header
blocks, but no known emulators support more than two. Having more than two is
not useful to describe any floppy format that was supported by any real TRS-80.
One block is sufficient for 5.25-inch DD drives, and two are sufficient for 8-
inch drives, 5.25-inch HD drives, or 3.5-inch HD drives.

Allocating and Freeing Sectors

Allocating and freeing sectors is a little tricky (if you support the extension
to sizes other than 256 bytes). When a new emulated disk is first created, it
should have one header block, filled with free 256-byte sectors. When a new
sector is formatted, if a free sector of the correct size exists, it can be
reused; otherwise, the next sector after the highest in-use sector can be
chosen and changed to the correct size. In the latter case, since there is no
data after the sector that is being resized, resizing it does not require
anything to move. When a sector is erased (because the track it is on is being
reformatted), it should normally be freed without changing its size, so that
the data of later sectors need not be moved.
If the highest in-use sector is freed, the emulator should search backward for
the new highest in-use sector, and should truncate the file to eliminate any
sector data for trailing free sectors, as well as the second header block if it
is no longer needed. Why do this? If the user reformats some sectors to be
shorter or longer than they used to be, the nominal start of the second header
block (as defined above) will shift. If you have garbage at the end of the
file, and the block shifts to start somewhere in the garbage, then when you
read the file back in again later, you'll have a second header block full of
garbage that will confuse you.

Support

Here is a summary of which emulators support which formats and which features.
The data is believed current as of this document's revision date. Future
versions of some of these emulators may support more (or fewer!) features. If
an emulator does not support a feature, it will still work with disks created
by emulators that do support the feature, as long as those disks didn't require
that feature when they were created. For example, an emulator that supports the
second header block will not create a second header block unless you format
more that 2901 sectors on one disk; note that an 80 track, double sided, double
density 5.25-inch disk contains less than 2901 sectors.
 
 _____________________________________________________________________________
|            |   |              |           |           |JV3   |          |   |
|            |   |JV3 Basic     |JV3 Write  |JV3 Second |Sizes |JV3 Non-  |   |
|            |JV1|features      |protect    |header     |other |IBM kludge|DMK|
|            |   |              |           |block      |than  |          |   |
|____________|___|______________|___________|___________|256___|__________|___|
|Jeff        |   |              |           |           |      |          |   |
|Vavasour    |Yes|No            |No         |No         |No    |No        |No |
|Model I     |   |              |           |           |      |          |   |
|v3.02u______|___|______________|___________|___________|______|__________|___|
|Jeff        |   |              |           |           |      |          |   |
|Vavasour    |No |Yes           |No (future)|No         |No    |No        |No |
|Model III/  |   |              |           |           |      |          |   |
|4_v2.3______|___|______________|___________|___________|______|__________|___|
|Matthew Reed|   |              |           |           |      |          |   |
|Model I/III |Yes|Yes           |No (future)|No (future)|No    |No        |No |
|v1.10_______|___|______________|___________|___________|______|__________|___|
|Matthew Reed|   |              |           |           |      |          |   |
|Model 4     |Yes|Yes           |Yes        |Yes        |No    |No        |No |
|v1.01_______|___|______________|___________|___________|______|__________|___|
|Matthew Reed|Yes|Yes           |Yes        |Yes        |Yes   |Yes       |No |
|TRS32_1.0___|___|______________|___________|___________|______|__________|___|
|xtrs Model  |Yes|Yes           |Yes        |Yes        |Yes   |Yes       |Yes|
|I/III/4_v3.7|___|______________|___________|___________|______|__________|___|
|WinTRS80    |   |              |Respected  |           |      |          |   |
|Model I/III/|Yes|Yes           |but not    |Yes        |No    |No        |No |
|4_v1.02_____|___|______________|generated__|___________|______|__________|___|
|Yves        |   |              |           |           |      |          |   |
|Lempereur   |Yes|No            |No         |No         |No    |No        |No |
|Model_I_____|___|______________|___________|___________|______|__________|___|
|David Keil  |   |Yes, with some|Respected  |           |      |          |   |
|Model III/4 |Yes|limitations   |but not    |No         |Yes   |No        |Yes|
|____________|___|______________|generated__|___________|______|__________|___|

Additional information or corrections for this table are welcome.
-------------------------------------------------------------------------------
Back_to_the_TRS-80_Page | My_home_page
