Family Computer Disk System: Difference between revisions

From NESdev Wiki
Jump to navigationJump to search
(Added non-disk BIOS calls)
(Move ccovell's tools to "See Also", link asm6f example, and add extra info to the FDS & BIOS summaries.)
 
(106 intermediate revisions by 21 users not shown)
Line 1: Line 1:
The '''Famicom Disk System''' was a Japan-exclusive storage device for the Famicom, designed to reduce Nintendo's cost of making copies of games by switching from mask [[ROM]] chips to a storage medium based on Mitsumi's Quick Disk.
[[File:Nintendo-Famicom-Disk-System.jpg|300px|thumb|right|Famicom, FDS RAM adapter, and FDS disk drive]]
Unfortunately for Nintendo, it also reduced the pirates' cost of making copies of games.
<div class="infobox">
 
<div style="text-align: center">'''FDS'''</div>
== .FDS format ==
{| class="oddf0 floatright"
'''fwNES''' was an [[NES emulator]] developed by Fan Wan Yang.  Its most lasting contribution to the NES scene was its disk image file format, an image of the Quick Disk media.
| '''Company'''
 
| Nintendo
The FDS format (file name suffix <code>.fds</code>) is a way to store Famicom Disk System disk data. It consists of the following sections, in order:
|-
# Header (16 bytes)
| '''Complexity'''
# Disk data (65500 * ''x'' bytes)
| ASIC
 
|-
The format of the header is as follows:
| '''Pinout'''
* 0-3: Constant $46 $44 $53 $1A ("FDS" followed by MS-DOS end-of-file)
| [[RP2C33 pinout]]
* 4: Number of disk sides in 65500 byte units
|-
* 5-15: Zero filled
| '''BIOS PRG ROM size'''
 
| 8K
Note: Most games are an even number of sides. Ports from NROM were one side. No commercial FDS game had an odd number of sides greater than 1.
|-
 
| '''PRG RAM capacity'''
== Overview ==
| 32K
* PRG ROM : 8 KB BIOS at $E000-$FFFF
|-
* PRG RAM: 32 KB at $6000-$DFFF
| '''CHR capacity'''
* CHR capacity: 8 KB RAM
| 8K
* Nametable [[mirroring]]: Controlled by mapper
|-
* Subject to [[bus conflict]]s: No
| '''Disk capacity'''
Games are stored on one or multiple disk sides. The FDS BIOS is used to load data from disks to PRG RAM or VRAM, and games can execute form there.
| ~64K per side
|-
| '''Mirroring'''
| H or V, switchable
|-
| '''Bus conflicts'''
| No
|-
| '''IRQ'''
| Yes
|-
| '''Audio'''
| Yes
|}</div>
The '''Famicom Disk System''' is a Japan-exclusive storage device for the Famicom, designed to reduce Nintendo's cost of making copies of games by switching from mask [[ROM]] chips to a storage medium based on Mitsumi's Quick Disk.
Unfortunately for Nintendo, it also reduced the pirates' cost of making copies of games. Games are stored on one or multiple disk sides. The [[FDS BIOS]] is used to load data from disks to PRG RAM or VRAM, and games can execute from there. Additional hardware features include a timer [[IRQ]] and a [[FDS audio | wavetable channel]].


== FDS Disk Side format ==
== Hardware ==


Each disk side must be structured into block as follows :
The Famicom disk system comes in two parts: The disk drive and the RAM adapter.


1, 2, 3, 4, 3, 4, ...., 3, 4
The RAM adapter is a special shaped cartridge that contains the RAM chips and an ASIC with DRAM controller, IRQ hardware, sound generation hardware, serial interface for the disk drive, and parallel port. The Disk Drive has to be powered separately and is only connected to the Famicom/NES via a [[FDS RAM adaptor cable pinout|serial cable]] to the RAM adapter.


The 3, 4 pattern should be repeated once per file present on the disk.
Most disk drives contain two motors: a spindle motor that spins the disk at a specific speed, and a stepper motor which moves the read/write head between each circular data track. By comparison, the FDS only contains a single motor that does both at once, so it instead stores the data in a single spiral-shaped track. There is a mechanism that detects when the head reaches the end of the disc and makes it return to the start (making an audible click). Because of this limitation, random access to the disc is impossible, making FDS disk drive data access behave similarly to a reel of tape (but ''much'' faster). Data can only be accessed by spinning the disc, waiting for the head to reach the inner edge of the disc, then waiting again until the desired data file is reached. A complete cycle through the entire disc takes about 7 seconds.


From the last file, fill the side with all 0 so that the side has exactly 65550 is reached.
The disc drive only contains basic electronics, there is no "intelligence" in it; therefore, the serial interface almost directly represents what is stored on the disc.


== Block format ==
=== Disks ===


=== Disk info block (block 1) ===
The FDS disk is a modified version of the Mitsumi Quick Disk.


SIZE  CONTENTS
See:
1      $01
* [[FDS disk format]] - the disk data format and file structure
14    FC Disk String  "*NINTENDO-HVC*" (not including the ")
* [[FDS file format]] (.FDS) - an archival file format for storing and emulating FDS disks
1      Manufacture Code 
        Same code as used in GameBoy(?)
4      Game Name Code
1      Game Version Number (start with $00, increase each revision)
1      Side Number 
          $00: Side-A 
          $01: Side-B
1      Disk Number (fist disk is actually $00)
1      Err.9  (indicate disk # ?, usually $00)
1      Err.10 (indicate disk # ?, usually $00)
1      Boot Read File Code
5      Unknown
3      Manufacture Permit Date
        Recorded in BCD, in the year of "showa"(+1925)
        For example, 21 march 2010 becomes $85, $03, $21 (because 2010-1925 = 85)
10    Unknown
3      Created Date 
        Recorded in BCD, in the year of "showa"(+1925)
9      Unknown


The '''*NINTENDO-HVC*''', stored in ASCI standard, strings proves this is a FDS disk, and if the string doesn't exactly match the BIOS will refuse to read the disk further.
== Banks ==
If the FDS is started with a disk whose side number and disk number aren't both 0, it will be prompted to insert the first disk side. Some games do however make this number 0 even for the second disk to make it bootable as well.
All Banks are fixed
 
* PPU $0000-$1FFF: 8k CHR RAM
All files which IDs is smaller or equal than the boot read file code will be loaded when the game is booting.
* CPU $6000-$DFFF: 32k PRG RAM
 
* CPU $E000-$FFFF: 8k BIOS PRG ROM
The FDS also has a trademark security system similar to what Sega used on some of its consoles.
The 224-byte text at PPU $2800-$28DF must match data in the BIOS starting at $ED37.
This data says roughly "produced by or under license from Nintendo" in the SMB1/Zelda character encoding.
Traditionally, the first file on a disk is a nametable type file loaded into $2800 which is named <code>KYODAKU-</code> (きょだく or [[wiktionary:許|許]][[wiktionary:諾|諾]] means approval).
 
=== File amount block (block 2) ===
 
This block contains the total number of files recorded on disk.  The info in this block is checked only at system boot.  Since there may exist more files than the number recorded in this block, emulators should ignore the value recorded in this block.
 
SIZE  CONTENTS
1        $02
1      File Amount
 
=== File header block  (block 3) ===
 
SIZE    CONTENTS
1        $03
1        File Number 
1        File Indicate Code (file identification code) 
          ID specified at disk-read function call
8        File Name
2        File Address (16-bit little endian)
          the destination address when loading
2        File Size (16-bit little endian)
1        Kind of File 
          0:Program (PRAM) 
          1:Character (CRAM) 
          2:Name table (VRAM)
 
The file Number must go in increasing order, first file is 0.
File IDs can be freely assigned, and this is the number which will decide which file is loaded from the disk (instead of the file number). An ID smaller than the boot number means the file is a boot file, and will be loaded on first startup.
 
File names are uppercase ASCII.
 
=== File data block (block 4) ===
 
SIZE      CONTENTS
1        $04
--        disk data


== Registers ==
== Registers ==


$402x registers are write-only
$402x registers are write-only,
$403x registers are read-only
$403x registers are read-only


 
=== Timer IRQ reload value low ($4020) ===
=== IRQ timer low ($4020) ===


  7  bit  0
  7  bit  0
Line 118: Line 74:
  LLLL LLLL
  LLLL LLLL
  |||| ||||
  |||| ||||
  ++++-++++- 8 LSB of IRQ timer
  ++++-++++- 8 LSB of timer IRQ reload value


=== IRQ timer high ($4021) ===
=== Timer IRQ reload value high ($4021) ===


  7  bit  0
  7  bit  0
Line 126: Line 82:
  LLLL LLLL
  LLLL LLLL
  |||| ||||
  |||| ||||
  ++++-++++- 8 MSB of IRQ timer
  ++++-++++- 8 MSB of timer IRQ reload value


=== IRQ timer enable ($4022) ===
Unlike $4022, $4020 and $4021 are not affected by the $4023.0 (disk registers enabled) flag - the reload value can be altered even when disk registers are disabled.
 
=== Timer IRQ control ($4022) ===


  7  bit  0
  7  bit  0
  ---------
  ---------
  xxxx xxEx
  xxxx xxER
         |
         ||
         +-- Enable IRQ timer
        |-- Timer IRQ Repeat Flag
         +-- Timer IRQ Enabled
 
When $4022 is written to with bit 1 (IRQ enabled) set, the reload value is copied into the IRQ's counter.
Each CPU clock cycle the counter is decremented by one if the enable flag is set.
 
When the counter's value is 0 and the IRQ enable flag is on, the following happens on every CPU cycle:
* An IRQ is generated.
* The IRQ counter is reset to its reload value (contained in $4020+$4021)
* If the IRQ repeat flag is NOT set, the IRQ enabled flag is cleared and the counter stops.


Each CPU clock cycle the timer is decremented by one if the enable flag is set.
Notes:
When the counter reach 0, an IRQ is generated. Read $4030 to acknowledge the IRQ.
* This register is affected by the $4023.0 (Enable disk I/O registers) flag - if disk registers are disabled, it is impossible to start the IRQ counter (writing to $4022 has no effect).
* Clearing $4023.0 will immediately stop the IRQ counter and acknowledge any pending timer IRQs.
* Writing to $4022 with bit 1 (IRQ enabled) cleared will stop the IRQ counter and acknowledge any pending timer IRQs.
* Enabling timer IRQs when the reload value is set to 0 will cause an IRQ immediately. Doing this with the repeat flag enabled will cause an infinite loop of IRQs on every CPU cycle.
* Since the disk transfer routine also uses IRQs, it's very important to disable timer IRQs before doing any access to the disk.


Note : Since the disk transfer routine also uses IRQs, it's very important to disable timer IRQs before doing any access to the disk.
There are only 3 known ways to acknowledge the timer IRQ:
* Read $4030
* Disable timer IRQs by writing to $4022
* Disable disk registers by writing to $4023


=== Master I/O enable ($4023) ===
=== Master I/O enable ($4023) ===
Line 150: Line 124:
         +-- Enable sound I/O registers
         +-- Enable sound I/O registers


This register sounds obscure. FDS bios just writes $00 then $83 to it.
The FDS BIOS writes $00, then $83 to it during reset. The purpose of bit 7 is unknown.
 
Disabling disk registers disables both disk and timer IRQs.


=== Write data register ($4024) ===
=== Write data register ($4024) ===


The data that this register is programmed with will be the next 8-bit quantity to load into the shift register (next time the byte transfer flag raises), and to be shifted out and appear on pin 5 of the RAM adaptor cable (2C33 pin 52).
The data that this register is programmed with will be the next 8-bit quantity to load into the shift register (next time the byte transfer flag raises), and to be shifted out and appear on pin 5 of the RAM adapter cable (2C33 pin 52).
 
Writing to this register acknowledges disk IRQs.{{Citation needed}}


=== FDS Control ($4025) ===
=== FDS Control ($4025) ===
  7  bit  0
  7  bit  0
  ---------
  ---------
  IS1B MRTD
  IE1C MRDT
  |||| ||||
  |||| ||||
  |||| |||+- Drive Motor Control 
  |||| |||+- Transfer Reset
  |||| |||    0: Stop motor
  |||| |||    0: Reset transfer timing to the initial state.
|||| |||    1: Turn on motor
  |||| ||+-- Drive Motor Control (0: start, 1: stop)
|||| ||+-- Transfer Reset
  |||| |+--- Transfer Mode (0: write; 1: read)
|||| ||        Set 1 to reset transfer timing to the initial state.
  |||| +---- Mirroring (0: vertical; 1: horizontal)
  |||| |+--- Read / Write mode
  |||+------ CRC Transfer Control (1: transfer CRC value)
  |||| |     (0: write; 1: read)
  ||+------- Unknown, always set to '1'
  |||| +---- Mirroring (0: horizontal; 1: vertical)
  |+-------- CRC Enabled (0: disable/reset, 1: enable)
  |||+------ CRC control (set during CRC calculation of transfer)
  +--------- Interrupt Enabled
  ||+------- Always set to '1'
               1: Generate an IRQ every time the byte transfer flag is raised.
  |+-------- Read/Write Start 
 
|            Turn on motor.  Set to 1 when the drive becomes ready for read/write
Notes:
  +--------- Interrupt Transfer 
* Disabling the CRC resets its state. The FDS BIOS disables the CRC between file blocks, then enables it before accessing each file block to calculate/verify their CRC values.
               0: Transfer without using IRQ
* To change the mirroring on the fly, a read-modify-write of its [[FDS BIOS#Zero-page variables|mirror]] should be done to prevent altering unrelated bits.
              1: Enable IRQ when the drive becomes ready for


A FDS game that wants to change mirroring probably don't want to touch motor related bits, so it should do a read-modify-write from the pseudo registers (see below).
Writing to this register acknowledges disk IRQ.


=== External connector ($4026) ===
=== External connector ($4026) ===
Line 183: Line 160:
Output of expansion terminal where there's a shutter on the back of the ram card. The outputs of $4026 (open-collector with 4.7K ohm pull-ups (except on bit 7)), are shared with the inputs on $4033.
Output of expansion terminal where there's a shutter on the back of the ram card. The outputs of $4026 (open-collector with 4.7K ohm pull-ups (except on bit 7)), are shared with the inputs on $4033.


=== Disk Status Register 0 ($4030) ===
=== Disk Status register ($4030) ===
  7  bit  0
  7  bit  0
  ---------
  ---------
  IExB xxTD
  IExB xxTD
  ||||  ||
  || |  ||
  ||||  |+- Timer Interrupt (1: an IRQ occurred)
  || |  |+- Timer Interrupt (1: an IRQ occurred)
  ||||  +-- Byte transfer flag. Set every time 8 bits have been transfered between the RAM adaptor & disk drive (service $4024/$4031).  
  || |  +-- Byte transfer flag. Set every time 8 bits have been transferred between the RAM adaptor & disk drive (service $4024/$4031).  
  ||||      Reset when $4024, $4031, or $4030 has been serviced.
  || |      Reset when $4024, $4031, or $4030 has been serviced.
  |||+------ CRC control (0: CRC passed; 1: CRC error)
  || +------ CRC control (0: CRC passed; 1: CRC error)
  |+-------- End of Head (1 when disk head is on the most inner track)
  |+-------- End of Head (1 when disk head is on the most inner track)
  +--------- Disk Data Read/Write Enable (1 when disk is readable/writable)
  +--------- Disk Data Read/Write Enable (1 when disk is readable/writeable)
 
Reading this register acknowledges timer and disk IRQs.
 
=== Read data register ($4031) ===
=== Read data register ($4031) ===


This register is loaded with the contents of an internal shift register every time the byte transfer flag raises. The shift register recieves it's serial data via pin 9 of the RAM adaptor cable (2C33 pin 51).
This register is loaded with the contents of an internal shift register every time the byte transfer flag raises. The shift register receives its serial data via pin 9 of the RAM adapter cable (2C33 pin 51).
 
Reading this register acknowledges disk IRQs.


=== Disk drive status register ($4032) ===
=== Disk drive status register ($4032) ===
Line 205: Line 186:
       |||
       |||
       ||+- Disk flag  (0: Disk inserted; 1: Disk not inserted)
       ||+- Disk flag  (0: Disk inserted; 1: Disk not inserted)
       |+-- Ready flag (0: Disk read; 1: Disk not ready)
       |+-- Ready flag (0: Disk readу; 1: Disk not ready)
       +--- Protect flag (0: Not write protected; 1: Write protected or disk ejected)
       +--- Protect flag (0: Not write protected; 1: Write protected or disk ejected)
Notes:
*The Ready flag corresponds to the drive head's position. It is set to 1 when the head reaches the end of the disk, and cleared once it returns to the beginning of the disk.
*The Protect flag corresponds to the write protect tab present on the upper-left corner of the inserted disk side. It is set to 1 if the tab is broken.
Reading this register acknowledges disk IRQs.{{Citation needed}}


=== External connector read ($4033) ===
=== External connector read ($4033) ===
Line 214: Line 201:
  |||| ||||
  |||| ||||
  |+++-++++- Input from expansion terminal where there's a shutter on the back of the ram card.
  |+++-++++- Input from expansion terminal where there's a shutter on the back of the ram card.
  +--------- Battery status (0: Good; 1: Voltage is low).
  +--------- Battery status (0: Voltage is low; 1: Good).


When a bit is clear in $4026 port it will read back as '0' here (including battery bit) because of how open collector input works.
When a bit is clear in $4026 port it will read back as '0' here (including battery bit) because of how open collector input works. Battery bit should be checked when the motor is on, otherwise it always will be read as 0.


=== Sound ($4040-$4089) ===
=== Sound ($4040-$4092) ===


For details on sound information, see [[FDS_audio|FDS audio]].
For details on sound information, see [[FDS_audio|FDS audio]].


== Pseudo-registers ==
== BIOS ==
Those registers are used by the FDS bios and FDS programs. They are used to overcome the problem that NES/FDS registers are write only, so it is effectively possible to modify only one bit of them without affecting other bits.


[$FF]:  value last written to [[$2000]]  $80 on reset.
The FDS contains a fixed 8KB BIOS at $E000-FFFF. This controls the Famicom at power-on and reset, dispatches the NMI and IRQ, and offers an API for accessing the [[FDS disk format|data on disk]]. Routines for common tasks including controller reading and PPU handling are also provided for programmer convenience.
[$FE]:  value last written to [[$2001]]  $06 on reset
[$FD]:  value last written to [[$2005]]#1 0'd on reset.
[$FC]:  value last written to [[$2005]]#2 0'd on reset.
[$FB]:  value last written to [[$4016]]  0'd on reset.
[$FA]:  value last written to $4025  $2E on reset.
[$F9]:  value last written to $4026  $FF on reset.
$F5..$F8 : Used by controller read routines
$00..$0F is used as temporary memory for the BIOS. The main program can use it as temporary memory too.


Those values are used by the BIOS to determine action on interrupt.
See: [[FDS BIOS]]
($0102): PC action on reset
[$0101]: PC action on IRQ. set to $80 on reset
[$0100]: PC action on NMI. set to $C0 on reset
($DFFE): disk game IRQ vector    (if [$0101] = 11xxxxxxB)
($DFFC): disk game reset vector  (if ($0102) = $5335, or $AC35)
($DFFA): disk game NMI vector #3 (if [$0100] = 11xxxxxxB)
($DFF8): disk game NMI vector #2 (if [$0100] = 10xxxxxxB)
($DFF6): disk game NMI vector #1 (if [$0100] = 01xxxxxxB)


A few important notes :
== See Also ==
* $102 indicate reset type : $AC = first boot of the game, $53 = the game was soft-reseted by the user
* [[FDS BIOS]]
* To use your own IRQ routine, you must manually write $c0 to $101
* [[FDS disk format]]
* There is 3 possible NMI vectors, #3 is used by default.
* [[FDS file format]] ('''.FDS''')
* On first start, the mirroring is set to horizontal, the stack pointer is $ff, and the I flag is ''clear''. System RAM is filled with values used by the BIOS, and PRG RAM is uninitialized, except for parts of it which has files loaded in.
* [[FDS audio]]
 
* [[FDS RAM adaptor cable pinout]]
== BIOS calls ==
* [[RP2C33 pinout]]
 
* [[iNES mapper 020|iNES mapper 20]] - Reserved for FDS dumps, but not widely used for it.
=== Disk acess routines ===
* [[TNES]] - Nintendo 3DS Virtual Console ROM format with support for FDS disk images.
 
* [https://github.com/bbbradsmith/NES-ca65-example/tree/fds GitHub repository:] Simple FDS example for ca65
* Routines takes one or two pointers as arguments. Those are placed directly after the jsr instruction : The subroutines uses the return address in stack to fetch the pointers and fix the return address.
* [https://github.com/TakuikaNinja/asm6f-fds-example GitHub repository:] Simple FDS example for asm6f
* Memory at $00-$0f will be affected by those routines
* [//forums.nesdev.org/viewtopic.php?p=194826#p194826 Forum post:] Skipping the FDS license screen
* Don't expect disk calls to return quick; it may take several seconds to complete.
* [https://www.chrismcovell.com/software.html FDS List] by ccovell - command line utility to inspect FDS disk image contents.
* All non-disk IRQ sources (timer, DMC and APU frame) should be properly disabled before calling any of these routines. The value at [$0101] however, is preserved on entry, and restored on exit.
* [https://www.chrismcovell.com/fds-lister.html FDS Lister] by ccovell - utility to inspect FDS disk contents that runs on an FDS.
* On return of those routines, A = $00 means no error occurred, other number is error #. Main program should test if an error occurred with the BEQ or BNE instruction, BEQ will branch if no error, and BNE will branch if there is an error.
* The structures defined below are used to identify files & disks in the access routines. The argument pointers should point to these structures in the program.


== References ==
* [//nesdev.org/FDS%20technical%20reference.txt FDS technical reference.txt] by Brad Taylor (old/outdated)
* [http://cmpslv2.starfree.jp/Famic/Famdis.htm Enri's Famicom Disk System page] (Japanese)
* [https://web.archive.org/web/20091023182159/http://www2.odn.ne.jp/~haf09260/Famic/Famdis.htm Enri's Famicom Disk System page] (Japanese) (old/outdated)
* [//nesdev.org/fds-nori.txt fds-nori.txt] - FDS reference in Japanese by Nori (old/outdated)
* [//forums.nesdev.org/viewtopic.php?p=194867#p194867 Forum post]: .fds format: Can checksums be heuristically detected? - Includes a CRC implementation in C.
* [//forums.nesdev.org/viewtopic.php?f=3&t=16507 Forum post]: FDS IRQ reload flag/value
<references/>


{|
[[Category:Mappers using $4020-$5FFF]][[Category:Mappers with cycle IRQs]]
| Adress || Name || Input parameters || Output parameters || Description
|-
| $e1f8 || LoadFiles || Pointer to Disk ID, Pointer to File List || A = error #, Y = # of files loaded || Loads files specified by DiskID into memory from disk. Load
addresses are decided by the file's header.
|-
| $e237 || AppendFile || Pointer to Disk ID, Pointer to File Header || A = error # || Appends the file data given by DiskID to the disk. This means
that the file is tacked onto the end of the disk, and the disk file count is incremented. The file is then read back to verify the write. If an error occurs during verification, the disk's file count is decremented (logically hiding the written file).
|-
| $e239 || WriteFile || Pointer to Disk ID, Pointer to File Header, A = file # || A = error # || Same as "Append File", but instead of writing the file to the  end of the disk, A specifies the sequential position on the disk to write the file (0 is the first). This also has the effect of setting the disk's file count to the A value, therefore logically hiding any other files that may reside after the written one.
|-
| $e23a || GetDiskInfo || Pointer to Disk Info || A = error # || Fills DiskInfo up with data read off the current disk.
|-
| $e2bb || AdjustFileCount || Pointer to Disk ID, A = number to reduce current file count by || A = error # || Reads in disk's file count, decrements it by A, then writes the new value back.
|-
| $e2b7 || CheckFileCount || Pointer to Disk ID, A = # to set file count to || A = error # || Reads in disk's file count, compares it to A, then sets the disk's file count to A.
|-
| $e305 || SetFileCount || Pointer to Disk ID, A = file count || A = error # || Set the file count to A
|-
| $e301 || SetFileCount1 || Pointer to Disk ID, A = file count minus one = # of the last file || A = error # || Set the file count to A + 1
|-
|}
 
 
 
=== Disk ID structure ===
 
This is a commonly used string. It consists of 10 bytes which are all compared directly against bytes 15..24 (right after the '*NINTENDO-HVC*' string) of the disk's header block (block type 1; always the first one on the disk). If any of the bytes fail the comparison, an appropriate error # is generated. Comparisons of immaterial data can be skipped by placing an $FF byte in the appropriate place in the DiskID string (for example, when the ROM BIOS boots a disk, it sets all the fields in the DiskID string to -1, except disk side #, and disk #, which are set to 0 (so these fields have to match 0)). The following chart describes the DiskID structure, and the error #'s returned when a comparison fails.
 
offset size error# description
------ ---- ------ -----------
0 1 $04 game manufacturer code
1 4 $05 game ASCII name string
5 1 $06 game version
6 1 $07 disk side #
7 1 $08 disk #
8 1 $09 extra disk # data
9 1 $10 extra disk # data
A -
 
=== File list structure ===
 
This is a list of 1-byte IDs of files to load. All files that matches any ID in the list are loaded. A list of up to 20 IDs is possible at a time, smaller lists should be terminated by a $ff byte (this implies a file ID can never be $ff).
 
=== File header structure ===
 
This structure is specified when a file is to be written to the disk. The first 14 bytes of this structure directly specify the data to use for generating a file header block (type 3, bytes [2..15]) to write to disk. The last 2 entries concern the file data to be written to disk (block type 4). The following is a table describing the FileHeader structure.
 
offset size description
------ ---- -----------
00 1 file ID code
01 8 file name
09 2 load address
0B 2 file data size
0D 1 file type ($00 : Program; $01 : Character; $02 : Nametable)
0E 2 source address of file data (NOT written to disk)
10 1 source address type ($00 : RAM, $01 : VRAM)
11 -
 
=== Disk information structure ===
 
This is a data structure returned by a subroutine, of collected information
from the disk (list of files on disk, disk size, etc.). The following table
is a description of that structure.
 
offset size
------  ----
0 1 game manufacturer code
1 4 game ASCII name string
5 1 game version
6 1 disk side #
7 1 disk #
8 1 extra disk # data
9 1 extra disk # data
A 1 # of files on disk
 
(the following block will appear for as many files as the "# of files on
disk" byte indicates)
 
B 1 file ID code
C 8 file name (ASCII)
 
(the following is present after the last file info block. Disk size is equal
to the sum of each file's size entry, plus an extra 261 per file.)
 
x 1 disk size high byte
x+1 1 disk size low  byte
x+2 -
 
== Other BIOS calls ==
 
{|
| Adress || Name || Input parameters || Output parameters || Affected RAM/Registers || Description
|-
| $e149 || Delay132 || || || || 132 clock cycle delay
|-
| $e161 || DisPFObj || || || A, $fe || Disable sprites and background
|-
| $e16b || EnPFObj || || || A, $fe || Enable sprites and background
|-
| $e170 || DisObj || || || A, $fe || Disable sprites
|-
| $e178 || EnObj || || || A, $fe || Enable sprites
|-
| $e17e || DisPF || || || A, $fe || Disable background
|-
| $e185 || EnPF || || || A, $fe || Enable background
|-
| $e1b2 || VINTWait || || || $ff || Wait until next Vblank NMI fires, and return (for programs that does it the "everything in main" way). NMI vector selection at $100 is preserved, but further VBlanks are disabled.
|-
| $e7bb || VRAMStructWrite || Pointer to VRAM Structure to be written || || A, X, Y, $00, $01, $ff || Set VRAM increment to 1 (clear $2000/$ff bit 2), and write a VRAM structure data to VRAM. Read below for information on the structure.
|-
| $e844 || FetchDirectPtr || || $00, $01 = pointer fetched || A, X, Y, $05, $06 || Fetch a direct pointer from the stack (the pointer should be placed after the return address of the routine that calls this one (see "important notes" above)), save the pointer at ($00) and fix the return address.
|-
| $e86a || WriteVRAMBuffer || || || A, X, Y, $301, $302 || Write a VRAMBuffer structure to VRAM. Read below for information on the structure.
|-
| $e8b3 || ReadVRAMBuffer || X = start adress in buffer, Y = # of bytes to read || || A, X, Y || Read individual bytes from VRAM to the VRAMBuffer. Apparently this routine has been made so that, when combined to WriteVRAMBuffer, it can easily do read-modify-write operations to individual VRAM bytes.
|-
| $e8d2 || PrepareVRAMString || A = High VRAM adress, X = Low VRAM adress, Y = string length, Direct Pointer = data to be written to VRAM || A = $ff : no error, A = $01 : string didn't fit in buffer || A, X, Y, $00, $01, $02, $03, $04, $05, $06 || This routine puts pointed data in the VRAM buffer.
|-
| $e8e1 || PrepareVRAMStrings || A = High VRAM adress, X = Low VRAM adress, Direct pointer = data to be written to VRAM || A = $ff : no error, A = $01 : strings didn't fit in buffer || A, X, Y, $00, $01, $02, $03, $04, $05, $06 || This routine put pointed data in VRAM buffer across multiple strings which are starts 32 bytes apart (one nametable row). The first byte tells the dimention of the strings : High nybble = # of rows, low nybble = # of columns.
|-
| $e94f || ??? || || || || Under analysis, VRAM String related
|-
| $e97d || Pixel2NamConv || $02 = Pixel X cord, $03 = Pixel Y cord || $00 = High Nametable address, $01 = Low Nametable address || A || This routine convert pixel screen coordinates to corresponding nametable adress (assmue no scrolling, and points to first nametable at $2000-$23ff).
|-
| $e997 || Nam2PixelConv || $00 = High nametable address, $01 = low nametable address || $02 = Pixel X cord, $03 = Pixel Y cord || A || This routine convert a nametable adress to corresponding pixel coordinates (assume no scrolling).
|-
| $e9b1 || Random || X = Zero Page adress where the random bytes are placed, Y = # of shift register bytes (normally $02) || || A, X, Y, $00 || This is a shift-register based random number generator, normally takes 2 bytes (using more won't affect random sequence). On reset you are supposed to write some non-zero values here (BIOS uses writes $d0, $d0), and call this routine several times before the data is actually random. Each call of this routine will shift the bytes ''right''.
|-
| $e9c8 || SpriteDMA || || || A || This routine does sprite DMA from RAM $200-$2ff
|-
| $e9d3 || CounterLogic || A, Y = end zeropage address of counters, X = start zeropage adress of counters || || A, X, $00 || This decrements several counters in Zeropage. The first counter is a decimal counter 9 -> 8 -> 7 -> ... -> 1 -> 0 -> 9 -> ... Counters 1...A are simply decremented and stays at 0. Counters A+1...Y are decremented when the first counter does a 0 -> 9 transition, and stays at 0.
|-
| $e9eb || ReadPads || || $f5 = Joypad #1 data, $f6 = Joypad #2 data || A, X, $00, $01, || This read hardwired famicom joypads.
|-
| $ea1a || ReadDownPads || || $f5 = Joypad #1 up->down transitions, $f6 = Joypad #2 up->down transitions $f7 = Joypad #1 data, $f8 = Joypad #2 data || A, X, $00, $01 || This reads hardwired famicom joypads, and detect up->down button transitions
|-
| $ea1f || ReadOrDownPads || || $f5 = Joypad #1 up->down transitions, $f6 = Joypad #2 up->down transitions $f7 = Joypad #1 data, $f8 = Joypad #2 data || A, X, $00, $01 || This read both hardwired famicom and expansion port joypads and detect up->down button transitions.
|-
| $ea36 || ReadDownVerifyPads || || $f5 = Joypad #1 up->down transitions, $f6 = Joypad #2 up->down transitions $f7 = Joypad #1 data, $f8 = Joypad #2 data || A, X, $00, $01 || This reads hardwired famicom joypads, and detect up->down button transitions. Data is read until two consecutive read matches to work around the DMC reading glitches.
|-
| $ea4c || ReadOrDownVerifyPads || || $f5 = Joypad #1 up->down transitions, $f6 = Joypad #2 up->down transitions $f7 = Joypad #1 data, $f8 = Joypad #2 data || A, X, $00, $01 || This read both hardwired famicom and expansion port joypads and detect up->down button transitions. Data is read until two consecutive read matches to work around the DMC reading glitches.
|-
| $ea68 || ReadDownExpPads || $f1-$f4 = up->down transitions, $f5-$f8 = Joypda data in the order : Pad1, Pad2, Expansion1, Expansion2 || || A, X, $00, $01 || This read both hardwired famicom and expansion port joypad, but stores their data separately instead of ORing them together like the other routines does. This routine is NOT DMC fortified.
|-
| $ea84 || VRAMFill || A = High VRAM Adress (aka tile row #), X = Fill value, Y = # of tile rows OR attribute fill data || || A, X, Y, $00, $01, $02 || This routine does 2 things : If A < $20, it fills pattern table data with the value in X for 16 * Y tiles. If A >= $20, it fills the corresponding nametable with the value in X and attribute table with the value in Y.
|-
| $ead2 || MemFill || A = fill value, X = first page #, Y = last page # || || A, X, Y, $00, $01 || This routines fills RAM pages with specified value.
|-
| $eaea || SetScroll || || || A || This routine set scroll registers according to values in $fc, $fd and $ff. Should typically be called in VBlank after VRAM updates
|-
| $eafd || JumpEngine || A = Jump table entry || || A, X, Y, $00, $01 || The instruction calling this is supposed to be followed by a jump table (16-bit pointers little endian, up to 128 pointers). A is the entry # to jump to, return address on stack is used to get jump table entries.
|-
| $eb13 || ??? || || || || Analyzing at present (related to something that plugs into expansion port, light gun maybe ?)
|-
| $eb66 || LoadTileset || A = Low VRAM Adress & Flags, Y = Hi VRAM Adress, X = # of tiles to transfer to/from VRAM || || A, X, Y, $00, $01, $02, $03, $04 || This routine can read and write 2BP and 1BP tilesets to/from VRAM. See appendix below about the flags.
|}
 
 
=== VRAM Structures ===
 
The VRAM structures is as following
 
 
SIZE  CONTENTS
2      VRAM Adress (big endian)
1      bit 0-5 length of data ($0 means 64 byte data)
        bit 6 : 0 = copy, 1 = fill
        bit 7 : 0 = increment by 1, 1 = increment by 32
1..64  Data to copy to VRAM
 
* The main structure is terminated by a negative byte. (High adress is always supposed to be in $00..$3f range)
* $4c is a "call" command. 2 bytes that follow is adress of a sub-VRAM structure. The sub-structure can call another sub-structure and so on.
* $60 is a "return" command. It will terminate a sub-structure.
* If Fill mode is used, the routine takes only 1 byte of data which is repeated.
 
=== VRAM Buffer considerations ===
 
VRAM buffer is located at $300-$3xx. $300 holds the size of the buffer (maximum), and $301 the end index of the buffer. The actual buffer lies at $302-$3xx.
 
* $300 is initialized to value $7d, effectively making the buffer lie at $300-$37f. It's possible to change the value here to make it bigger or smaller, but you the biggest possible value is $fd, making the buffer lie at $300-$3ff.
* Format of the buffer is equivalent to VRAM structure above, exept that there is no sub-structures, no increment by 32 flag and no fill flag.
* Read routines are still obsure and analyzed at the moment
 
=== Load Tileset considerations ===
 
The ''flags'' parameters comes as follow :
 
7  bit  0
---------
AAAA MMIT
|||| ||||
|||| |||+- Fill bit
|||| ||+-- Transfer direction (0 = Write tiles, 1 = Read tiles)
|||| ++--- Bitplane type
++++------ Low VRAM Adress (aka tile # within a row)
 
  Bitplane types :
 
        1st bitplane 2nd bitplane
        ----------- -----------
    0:  data         data+8
    1:  data         fill bit
    2:  fill bit        data
    3:  data^fill bit data
 
This makes it possible for 1BP tiles to take all possible color schemes when they are written as 2BP.
In read mode all non "data" bitplanes are replaced by dummy reads.
 
== References ==
* [http://nesdev.parodius.com/FDSListWIN.zip FDS Lister by ccovell]

Latest revision as of 02:12, 18 August 2024

Famicom, FDS RAM adapter, and FDS disk drive
FDS
Company Nintendo
Complexity ASIC
Pinout RP2C33 pinout
BIOS PRG ROM size 8K
PRG RAM capacity 32K
CHR capacity 8K
Disk capacity ~64K per side
Mirroring H or V, switchable
Bus conflicts No
IRQ Yes
Audio Yes

The Famicom Disk System is a Japan-exclusive storage device for the Famicom, designed to reduce Nintendo's cost of making copies of games by switching from mask ROM chips to a storage medium based on Mitsumi's Quick Disk. Unfortunately for Nintendo, it also reduced the pirates' cost of making copies of games. Games are stored on one or multiple disk sides. The FDS BIOS is used to load data from disks to PRG RAM or VRAM, and games can execute from there. Additional hardware features include a timer IRQ and a wavetable channel.

Hardware

The Famicom disk system comes in two parts: The disk drive and the RAM adapter.

The RAM adapter is a special shaped cartridge that contains the RAM chips and an ASIC with DRAM controller, IRQ hardware, sound generation hardware, serial interface for the disk drive, and parallel port. The Disk Drive has to be powered separately and is only connected to the Famicom/NES via a serial cable to the RAM adapter.

Most disk drives contain two motors: a spindle motor that spins the disk at a specific speed, and a stepper motor which moves the read/write head between each circular data track. By comparison, the FDS only contains a single motor that does both at once, so it instead stores the data in a single spiral-shaped track. There is a mechanism that detects when the head reaches the end of the disc and makes it return to the start (making an audible click). Because of this limitation, random access to the disc is impossible, making FDS disk drive data access behave similarly to a reel of tape (but much faster). Data can only be accessed by spinning the disc, waiting for the head to reach the inner edge of the disc, then waiting again until the desired data file is reached. A complete cycle through the entire disc takes about 7 seconds.

The disc drive only contains basic electronics, there is no "intelligence" in it; therefore, the serial interface almost directly represents what is stored on the disc.

Disks

The FDS disk is a modified version of the Mitsumi Quick Disk.

See:

Banks

All Banks are fixed

  • PPU $0000-$1FFF: 8k CHR RAM
  • CPU $6000-$DFFF: 32k PRG RAM
  • CPU $E000-$FFFF: 8k BIOS PRG ROM

Registers

$402x registers are write-only, $403x registers are read-only

Timer IRQ reload value low ($4020)

7  bit  0
---------
LLLL LLLL
|||| ||||
++++-++++- 8 LSB of timer IRQ reload value

Timer IRQ reload value high ($4021)

7  bit  0
---------
LLLL LLLL
|||| ||||
++++-++++- 8 MSB of timer IRQ reload value

Unlike $4022, $4020 and $4021 are not affected by the $4023.0 (disk registers enabled) flag - the reload value can be altered even when disk registers are disabled.

Timer IRQ control ($4022)

7  bit  0
---------
xxxx xxER
       ||
       |-- Timer IRQ Repeat Flag
       +-- Timer IRQ Enabled

When $4022 is written to with bit 1 (IRQ enabled) set, the reload value is copied into the IRQ's counter. Each CPU clock cycle the counter is decremented by one if the enable flag is set.

When the counter's value is 0 and the IRQ enable flag is on, the following happens on every CPU cycle:

  • An IRQ is generated.
  • The IRQ counter is reset to its reload value (contained in $4020+$4021)
  • If the IRQ repeat flag is NOT set, the IRQ enabled flag is cleared and the counter stops.

Notes:

  • This register is affected by the $4023.0 (Enable disk I/O registers) flag - if disk registers are disabled, it is impossible to start the IRQ counter (writing to $4022 has no effect).
  • Clearing $4023.0 will immediately stop the IRQ counter and acknowledge any pending timer IRQs.
  • Writing to $4022 with bit 1 (IRQ enabled) cleared will stop the IRQ counter and acknowledge any pending timer IRQs.
  • Enabling timer IRQs when the reload value is set to 0 will cause an IRQ immediately. Doing this with the repeat flag enabled will cause an infinite loop of IRQs on every CPU cycle.
  • Since the disk transfer routine also uses IRQs, it's very important to disable timer IRQs before doing any access to the disk.

There are only 3 known ways to acknowledge the timer IRQ:

  • Read $4030
  • Disable timer IRQs by writing to $4022
  • Disable disk registers by writing to $4023

Master I/O enable ($4023)

7  bit  0
---------
xxxx xxSD
       ||
       |+- Enable disk I/O registers
       +-- Enable sound I/O registers

The FDS BIOS writes $00, then $83 to it during reset. The purpose of bit 7 is unknown.

Disabling disk registers disables both disk and timer IRQs.

Write data register ($4024)

The data that this register is programmed with will be the next 8-bit quantity to load into the shift register (next time the byte transfer flag raises), and to be shifted out and appear on pin 5 of the RAM adapter cable (2C33 pin 52).

Writing to this register acknowledges disk IRQs.[citation needed]

FDS Control ($4025)

7  bit  0
---------
IE1C MRDT
|||| ||||
|||| |||+- Transfer Reset
|||| |||     0: Reset transfer timing to the initial state.
|||| ||+-- Drive Motor Control (0: start, 1: stop)
|||| |+--- Transfer Mode (0: write; 1: read)
|||| +---- Mirroring (0: vertical; 1: horizontal)
|||+------ CRC Transfer Control (1: transfer CRC value)
||+------- Unknown, always set to '1'
|+-------- CRC Enabled (0: disable/reset, 1: enable)
+--------- Interrupt Enabled
             1: Generate an IRQ every time the byte transfer flag is raised.

Notes:

  • Disabling the CRC resets its state. The FDS BIOS disables the CRC between file blocks, then enables it before accessing each file block to calculate/verify their CRC values.
  • To change the mirroring on the fly, a read-modify-write of its mirror should be done to prevent altering unrelated bits.

Writing to this register acknowledges disk IRQ.

External connector ($4026)

Output of expansion terminal where there's a shutter on the back of the ram card. The outputs of $4026 (open-collector with 4.7K ohm pull-ups (except on bit 7)), are shared with the inputs on $4033.

Disk Status register ($4030)

7  bit  0
---------
IExB xxTD
|| |   ||
|| |   |+- Timer Interrupt (1: an IRQ occurred)
|| |   +-- Byte transfer flag. Set every time 8 bits have been transferred between the RAM adaptor & disk drive (service $4024/$4031). 
|| |       Reset when $4024, $4031, or $4030 has been serviced.
|| +------ CRC control (0: CRC passed; 1: CRC error)
|+-------- End of Head (1 when disk head is on the most inner track)
+--------- Disk Data Read/Write Enable (1 when disk is readable/writeable)

Reading this register acknowledges timer and disk IRQs.

Read data register ($4031)

This register is loaded with the contents of an internal shift register every time the byte transfer flag raises. The shift register receives its serial data via pin 9 of the RAM adapter cable (2C33 pin 51).

Reading this register acknowledges disk IRQs.

Disk drive status register ($4032)

7  bit  0
---------
xxxx xPRS
      |||
      ||+- Disk flag  (0: Disk inserted; 1: Disk not inserted)
      |+-- Ready flag (0: Disk readу; 1: Disk not ready)
      +--- Protect flag (0: Not write protected; 1: Write protected or disk ejected)

Notes:

  • The Ready flag corresponds to the drive head's position. It is set to 1 when the head reaches the end of the disk, and cleared once it returns to the beginning of the disk.
  • The Protect flag corresponds to the write protect tab present on the upper-left corner of the inserted disk side. It is set to 1 if the tab is broken.

Reading this register acknowledges disk IRQs.[citation needed]

External connector read ($4033)

7  bit  0
---------
BIII IIII
|||| ||||
|+++-++++- Input from expansion terminal where there's a shutter on the back of the ram card.
+--------- Battery status (0: Voltage is low; 1: Good).

When a bit is clear in $4026 port it will read back as '0' here (including battery bit) because of how open collector input works. Battery bit should be checked when the motor is on, otherwise it always will be read as 0.

Sound ($4040-$4092)

For details on sound information, see FDS audio.

BIOS

The FDS contains a fixed 8KB BIOS at $E000-FFFF. This controls the Famicom at power-on and reset, dispatches the NMI and IRQ, and offers an API for accessing the data on disk. Routines for common tasks including controller reading and PPU handling are also provided for programmer convenience.

See: FDS BIOS

See Also

References