https://www.nesdev.org/w/api.php?action=feedcontributions&user=%E3%81%AF%E3%82%8B%E3%81%8D&feedformat=atomNESdev Wiki - User contributions [en]2024-03-29T01:53:11ZUser contributionsMediaWiki 1.39.0https://www.nesdev.org/w/index.php?title=Nintendo_header&diff=9365Nintendo header2020-12-07T03:45:20Z<p>はるき: Game names are not zero terminated.</p>
<hr />
<div>About 33% of licensed Nintendo NES games have an header present at their last bank, at addresses $FFE0-$FFF9, right before the interrupt vectors.<br />
<br />
The info in them is frequently incomplete and/or inaccurate. When info on a particular filed is not present, a byte $00 or $FF is used for padding<br />
<br />
* '''$FFE0-$FFEF''' : Name of the game, in ASCII. (sometimes the name is abbreviated or replaced by Nintendo's code). ASCII codes 20h-5Ah allowed. Should be right-justified.<br />
* '''$FFF0-$FFF1''' : PRG checksum (either the last 16k bank, or the whole PRG-ROM). Sum of all bytes, excluding the two checksum bytes. Big-endian.<br />
* '''$FFF2-$FFF3''' : CHR checksum. 0x00 if RAM. Big-endian.<br />
* '''$FFF4''' : Data sizes. Values: 0 = 8 KiB or 64 KiB based on board type, 1 = 16 KiB, 2 = 32 KiB, 3 = 128 KiB, 4 = 256 KiB, 5 = 512 KiB<br />
**D7-D4: PRG size<br />
**D3: 0 = CHR ROM, 1 = CHR RAM<br />
**D2-D0: CHR size<br />
* '''$FFF5''' : Board Type.<br />
**D7: 0 = Horizontal [[Mirroring|nametable arrangement]], 1 = Vertical arrangement (inverse of [[iNES#Flags 6|iNES mirroring bit]])<br />
**D6-D0: 0 = NROM, 1 = CNROM, 2 = UNROM/AOROM, 3 = GNROM, 4 = MMC (any).<br />
* '''$FFF6''' : Title encoding : 0 = No title entered, 1 = ASCII, 2 = Another encoding<br />
* '''$FFF7''' : Valid Title Length - 1. 0 if no title entered.<br />
* '''$FFF8''' : Maker's code, the same used for the FDS, GB, GBC and SNES headers : 1 = Nintendo, 2-254 = everyone else. 255 must be reserved.<br />
* '''$FFF9''' : Header Validation Byte. 8-bit checksum of $FFF2-$FFF9 should = 0</div>はるきhttps://www.nesdev.org/w/index.php?title=User:%E3%81%AF%E3%82%8B%E3%81%8D&diff=17090User:はるき2020-12-07T03:40:48Z<p>はるき: </p>
<hr />
<div>I am Jason Cannon ("はるき"), an aspiring software developer and enjoy playing and programming retro games.<br />
<br />
<h5>Windows Software</h5><br />
<ul><br />
<li><b>VirutaNES_ext</b>: A branch of the VirtuaNES emulator by Norix.</li><br />
<li><b>asm6_ext</b>: A branch of the asm6 6502 assembler.</li><br />
<li><b>NES header</b>: Shows a NES game's internal Nintendo header, if it exists.</li><br />
</ul><br />
<h5>NES Games</h5><br />
<ul><br />
<li><b>Biplanes</b>: A port of the Intellivision sub-game Biplanes from Triple Action.</li><br />
</ul><br />
<br />
<br />
My website: [http://https://nyanko-studios.github.io/ Nyanko Studios]</div>はるきhttps://www.nesdev.org/w/index.php?title=User:%E3%81%AF%E3%82%8B%E3%81%8D&diff=17089User:はるき2020-12-07T03:37:51Z<p>はるき: Created page with "I am Jason Cannon ("はるき"), an aspiring software developer and enjoy playing and programming retro games. <h5>Windows Software</h5> <ul> <li><b>VirutaNES_ext</b>: A bra..."</p>
<hr />
<div>I am Jason Cannon ("はるき"), an aspiring software developer and enjoy playing and programming retro games.<br />
<br />
<h5>Windows Software</h5><br />
<ul><br />
<li><b>VirutaNES_ext</b>: A branch of the VirtuaNES emulator by Norix.</li><br />
<li><b>asm6_ext</b>: A branch of the asm6 6502 assembler.</li><br />
</ul><br />
<h5>NES Games</h5><br />
<ul><br />
<li><b>Biplanes</b>: A port of the Intellivision sub-game Biplanes from Triple Action.</li><br />
</ul><br />
<br />
<br />
My website: [http://https://nyanko-studios.github.io/ Nyanko Studios]</div>はるきhttps://www.nesdev.org/w/index.php?title=FDS_BIOS&diff=2534FDS BIOS2020-11-24T18:34:50Z<p>はるき: Changed "nobody" to "nothing".</p>
<hr />
<div>The [[Famicom Disk System]] 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]].<br />
<br />
== Pseudo-registers ==<br />
The FDS BIOS uses several bytes on the zero-page. 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.<br />
<br />
[$FF]: value last written to [[PPUCTRL|$2000]] $80 on reset.<br />
[$FE]: value last written to [[PPUMASK|$2001]] $06 on reset<br />
[$FD]: value last written to [[PPUSCROLL|$2005/1]] $00 on reset.<br />
[$FC]: value last written to [[PPUSCROLL|$2005/2]] $00 on reset.<br />
[$FB]: value last written to [[Controller reading|$4016]] $00 on reset.<br />
[$FA]: value last written to [[Family_Computer_Disk_System#FDS_Control_.28.244025.29|$4025]] $2E on reset.<br />
[$F9]: value last written to [[Family_Computer_Disk_System#External_connector_.28.244026.29|$4026]] $FF on reset.<br />
$F5..$F8 : Used by controller read routines<br />
$00..$0F is used as temporary memory for the BIOS. The main program can use it as temporary memory too.<br />
<br />
The FDS BIOS also uses 4 bytes at the lower end of the stack page to control behaviour of interrupt/reset vectors:<br />
<br />
[$0102]/[$0103]: PC action on reset<br />
[$0101]: PC action on IRQ. set to $80 on reset<br />
[$0100]: PC action on NMI. set to $C0 on reset<br />
<br />
RESET:<br />
($DFFC): disk game reset vector (if [$0102] = $35, and [$0103] = $53 or $AC)<br />
<br />
IRQ:<br />
($DFFE): disk game IRQ vector (if [$0101] = %11xxxxxx)<br />
$E1EF : BIOS acknowledge and delay (if [$0101] = %10xxxxxx)<br />
$E1CE : BIOS disk transfer (if [$0101] = %01xxxxxx)<br />
$E1D9 : BIOS disk skip bytes (if [$0101] = %00xxxxxx)<br />
<br />
NMI:<br />
($DFF6): disk game NMI vector #1 (if [$0100] = %01xxxxxx)<br />
($DFF8): disk game NMI vector #2 (if [$0100] = %10xxxxxx)<br />
($DFFA): disk game NMI vector #3 (if [$0100] = %11xxxxxx)<br />
$E19D : BIOS disable NMI (if [$0100] = %00xxxxxx)<br />
<br />
A few important notes :<br />
* After loading the boot files, $0102 is set to $35 so that the ($DFFC) vector is used and the BIOS is skipped.<br />
* $0103 indicate reset type : $AC = first boot of the game, $53 = the game was soft-reset by the user<br />
* To use a custom IRQ routine, $C0 should be manually written to $0101<br />
* There are 3 possible NMI vectors, #3 is used by default.<br />
* 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 have files loaded in.<br />
<br />
== BIOS calls ==<br />
<br />
=== Disk access routines ===<br />
<br />
* 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.<br />
* Memory at $00-$0F will be affected by those routines<br />
* Unlike the vast majority of disk drives, the FDS lacks any kind of intelligent tracking system. All BIOS load and save functions will do access to the whole disk, no matter which data they load/save. A simple way to overcome this problem is to have a custom loading routine, similar to the BIOS one but forcing the # of files to a smaller number than it actually is. That way the later files are not accessed at all and the earlier files load faster. Of course the maximal time is still taken when loading files that are late on the disk.<br />
* 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.<br />
* 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.<br />
* 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.<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Description<br />
|-<br />
| $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.<br />
|-<br />
| $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).<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $E301 || SetFileCount1 || Pointer to Disk ID, A = file count minus one = # of the last file || A = error # || Set the file count to A + 1<br />
|-<br />
| $E305 || SetFileCount || Pointer to Disk ID, A = file count || A = error # || Set the file count to A<br />
|-<br />
| $E32A || GetDiskInfo || Pointer to Disk Info || A = error # || Fills DiskInfo up with data read off the current disk.<br />
|-<br />
|}<br />
<br />
=== Low-Level Disk access routines ===<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Description<br />
|-<br />
| $E445 || CheckDiskHeader || Pointer to 10 byte string at $00 || || Compares the first 10 bytes on the disk coming after the FDS string, to 10 bytes pointed to by Ptr($00). To bypass the checking of any byte, a -1 can be placed in the equivelant place in the compare string. Otherwise, if the comparison fails, an appropriate error will be generated.<br />
|-<br />
| $E484 || GetNumFiles || || || Reads # of files stored on disk, stores the result in $06<br />
|-<br />
| $E492 || SetNumFiles || || A = # of files || Writes new number of files to disk header.<br />
|-<br />
| $E4A0 || FileMatchTest || Pointer to FileID list at $02 || || Uses a byte string pointed at by Ptr($02) to tell the disk system which files to load. The file ID's number is searched for in the string. If an exact match is found, [$09] is 0'd, and [$0E] is incremented. If no matches are found after 20 bytes, or a -1 entry is encountered, [$09] is set to -1. If the first byte in the string is -1, the BootID number is used for matching files (any FileID that is not greater than the BootID qualifies as a match).<br />
|-<br />
| $E4DA || SkipFiles || Number of files to skip in $06 || || Skips over specified number of files.<br />
|-<br />
|}<br />
<br />
=== Example code how to load files ===<br />
<br />
<pre><br />
Load:<br />
JSR LoadFiles<br />
.dw DiskID<br />
.dw LoadList<br />
BNE _Error ;Check if there is an error<br />
RTS<br />
_Error:<br />
JSR PrintError ;If so print the error number and message to screen (include side/disk changing prompts)<br />
_sideError:<br />
LDA $4032<br />
AND #$01<br />
BEQ _sideError ;Wait until disk is ejected<br />
_insert:<br />
LDA $4032<br />
AND #$01<br />
BNE _instert ;Wait until disk is inserted<br />
JMP Load<br />
<br />
DiskID:<br />
.db $01 ;Manufacturer code<br />
.db "NAME" ;4-letter code of game<br />
.db $00 ;Version<br />
.db $01 ;Disk side<br />
.db $00 ;Disk number<br />
.db $00, $00 ;Extra disk IDs<br />
<br />
LoadList ;In this example the files with IDs equal to $02, $03 or $04 will be loaded into memory<br />
.db $02, $03, $04, $FF<br />
</pre><br />
<br />
=== Error list ===<br />
<br />
Message in bold is the official BIOS message (if there is one) followed by an explanation<br />
*$00: no error<br />
*$01: '''disk set''', ($4032.0) disk not set<br />
*$02: '''battery''', ($4033.7) power supply failure<br />
*$03: ($4032.2) disk is write protected<br />
*$04: Wrong maker ID<br />
*$05: Wrong game<br />
*$06: Wrong game version<br />
*$07: '''a,b side''', wrong side number<br />
*$08: '''disk no.''', wrong disk number<br />
*$09: wrong additional disk ID 1<br />
*$0A: wrong additional disk ID 2<br />
*$20: '''disk trouble''', approval check failed<br />
*$21: '''disk trouble''', '*NINTENDO-HVC*' string in block 1 doesn't match<br />
*$22: '''disk trouble''', block type 1 expected<br />
*$23: '''disk trouble''', block type 2 expected<br />
*$24: '''disk trouble''', block type 3 expected<br />
*$25: '''disk trouble''', block type 4 expected<br />
*$27: '''disk trouble''', ($4030.4) block failed CRC<br />
*$28: '''disk trouble''', ($4030.6) file ends prematurely during read<br />
*$29: '''disk trouble''', ($4030.6) file ends prematurely during write<br />
*$30: '''disk trouble''', ($4032.1) disk is full<br />
<br />
=== Disk ID structure ===<br />
<br />
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.<br />
<br />
offset size error# description<br />
------ ---- ------ -----------<br />
0 1 $04 game manufacturer code<br />
1 4 $05 game ASCII name string<br />
5 1 $06 game version<br />
6 1 $07 disk side #<br />
7 1 $08 disk #<br />
8 1 $09 extra disk # data<br />
9 1 $10 extra disk # data<br />
A -<br />
<br />
=== File list structure ===<br />
<br />
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).<br />
<br />
Multiple files are loaded in the order as they exist on the disk, not in the order of the list.<br />
<br />
=== File header structure ===<br />
<br />
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.<br />
<br />
offset size description<br />
------ ---- -----------<br />
00 1 file ID code<br />
01 8 file name<br />
09 2 load address<br />
0B 2 file data size<br />
0D 1 file type ($00 : Program; $01 : Character; $02 : Nametable)<br />
0E 2 source address of file data (NOT written to disk)<br />
10 1 source address type ($00 : RAM, $01 : VRAM)<br />
11 -<br />
<br />
=== Disk information structure ===<br />
<br />
This is a data structure returned by a subroutine, of collected information <br />
from the disk (list of files on disk, disk size, etc.). The following table <br />
is a description of that structure.<br />
<br />
offset size<br />
------ ----<br />
0 1 game manufacturer code<br />
1 4 game ASCII name string<br />
5 1 game version<br />
6 1 disk side #<br />
7 1 disk #<br />
8 1 extra disk # data<br />
9 1 extra disk # data<br />
A 1 # of files on disk<br />
<br />
(the following block will appear for as many files as the "# of files on <br />
disk" byte indicates)<br />
<br />
B 1 file ID code<br />
C 8 file name (ASCII)<br />
<br />
(the following is present after the last file info block. Disk size is equal <br />
to the sum of each file's size entry, plus an extra 261 per file.)<br />
<br />
x 1 disk size high byte<br />
x+1 1 disk size low byte<br />
x+2 -<br />
<br />
== Other BIOS calls ==<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Affected RAM/Registers || Description<br />
|-<br />
| $E149 || Delay132 || || || || 132 clock cycle delay<br />
|-<br />
| $E153 || Delayms || || || X, Y || Delay routine, Y = delay in ms (approximate)<br />
|-<br />
| $E161 || DisPFObj || || || A, $FE || Disable sprites and background<br />
|-<br />
| $E16B || EnPFObj || || || A, $FE || Enable sprites and background<br />
|-<br />
| $E171 || DisObj || || || A, $FE || Disable sprites<br />
|-<br />
| $E178 || EnObj || || || A, $FE || Enable sprites<br />
|-<br />
| $E17E || DisPF || || || A, $FE || Disable background<br />
|-<br />
| $E185 || EnPF || || || A, $FE || Enable background<br />
|-<br />
| $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 $0100 is preserved, but further VBlanks are disabled.<br />
|-<br />
| $E7BB || VRAMStructWrite || Pointer to VRAM buffer to be written || || A, X, Y, $00, $01, $FF || Set VRAM increment to 1 (clear [[PPUCTRL]]/$FF bit 2), and write a VRAM buffer to VRAM. Read below for information on the structure.<br />
|-<br />
| $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.<br />
|-<br />
| $E86A || WriteVRAMBuffer || || || A, X, Y, $301, $302 || Write the VRAM Buffer at $302 to VRAM. Read below for information on the structure.<br />
|-<br />
| $E8B3 || ReadVRAMBuffer || X = start address of read buffer, Y = # of bytes to read || || A, X, Y || Read individual bytes from VRAM to the VRAMBuffer. (see notes below)<br />
|-<br />
| $E8D2 || PrepareVRAMString || A = High VRAM address, X = Low VRAM address, 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 copies pointed data into the VRAM buffer.<br />
|-<br />
| $E8E1 || PrepareVRAMStrings || A = High VRAM address, X = Low VRAM address, Direct pointer = data to be written to VRAM || A = $FF : no error, A = $01 : data didn't fit in buffer || A, X, Y, $00, $01, $02, $03, $04, $05, $06 || This routine copies a 2D string into the VRAM buffer. The first byte of the data determines the width and height of the following string (in tiles): Upper nybble = height, lower nybble = width.<br />
|-<br />
| $E94F || GetVRAMBufferByte || X = starting index of read buffer, Y = # of address to compare (starting at 1), $00, $01 = address to read from || carry clear : a previously read byte was returned, carry set : no byte was read, should wait next call to ReadVRAMBuffer || A, X, Y || This routine was likely planned to be used in order to avoid useless latency on a VRAM reads (see notes below). It compares the VRAM address in ($00) with the Yth (starting at 1) address of the read buffer. If both addresses match, the corresponding data byte is returned exit with c clear. If the addresses are different, the buffer address is overwritten by the address in ($00) and the routine exit with c set.<br />
|-<br />
| $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 address (assumes no scrolling, and points to first nametable at $2000-$23ff).<br />
|-<br />
| $E997 || Nam2PixelConv || $00 = High nametable address, $01 = low nametable address || $02 = Pixel X cord, $03 = Pixel Y cord || A || This routine convert a nametable address to corresponding pixel coordinates (assume no scrolling).<br />
|-<br />
| $E9B1 || Random || X = Zero Page address 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 the program is 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''.<br />
|-<br />
| $E9C8 || SpriteDMA || || || A || This routine does sprite DMA from RAM $0200-$02FF<br />
|-<br />
| $E9D3 || CounterLogic || A, Y = end Zeropage address of counters, X = start zeropage address 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.<br />
|-<br />
| $E9EB || ReadPads || || $F5 = Joypad #1 data, $F6 = Joypad #2 data || A, X, $00, $01, || This read hardwired famicom joypads.<br />
|-<br />
| $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<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $EA68 || ReadDownExpPads || $F1-$F4 = up->down transitions, $F5-$F8 = Joypad 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.<br />
|-<br />
| $EA84 || VRAMFill || A = High VRAM Address (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.<br />
|-<br />
| $EAD2 || MemFill || A = fill value, X = first page #, Y = last page # || || A, X, Y, $00, $01 || This routines fills RAM pages with specified value.<br />
|-<br />
| $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<br />
|-<br />
| $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.<br />
|-<br />
| $EB13 || ReadKeyboard || || || || Read Family Basic Keyboard expansion (detail is under analysis)<br />
|-<br />
| $EB66 || LoadTileset || A = Low VRAM Address & Flags, Y = Hi VRAM Address, 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.<br />
|-<br />
| $EC22 || unk_EC22|| || || || Some kind of logic that some games use. (detail is under analysis)<br />
|}<br />
<br />
<br />
=== VRAM write transfer structure ===<br />
<br />
The structure of VRAM buffers are as follows:<br />
<br />
SIZE CONTENTS<br />
2 VRAM Address (big endian)<br />
1 bit 0-5 length of data ($0 means a length of 64)<br />
bit 6 : 0 = copy, 1 = fill<br />
bit 7 : 0 = increment by 1, 1 = increment by 32<br />
n Data to copy to VRAM<br />
..... repeated as many times as needed<br />
1 $FF<br />
<br />
* The main structure is terminated by a $FF byte (High address is always supposed to be in $00..$3F range)<br />
* $4C is a "call" command. The 2 bytes that follow is the address of a sub-VRAM structure. The sub-structure can call another sub-structure and so on. (Nesting depth is unknown.)<br />
* $60 is a "return" command. It will terminate a sub-structure.<br />
* If Fill mode is used, the routine takes only 1 byte of data which is repeated.<br />
* The interpretation of the length differs from the otherwise similar [[Tile compression#NES Stripe Image RLE|Stripe Image buffer format]].<br />
<br />
=== VRAM Buffer notes ===<br />
<br />
The VRAM buffer is located at $0300-$03xx. $300 holds the size of the buffer (maximum), and $301 holds the end index of the buffer. The actual buffer lies at $0302-$03xx, and is of variable length.<br />
<br />
* $0300 is initialized to the value $7D, effectively making the buffer lie at $0300-$037F. It's possible to change the value here to make it bigger or smaller, but the biggest possible value is $FD, making the buffer lie at $0300-$03FF.<br />
* Format of the buffer is equivalent to the VRAM structure above, except that there are no sub-structures, no increment by 32 flag and no fill flag.<br />
* For this reason, the VRAM buffer at $0302 can be used as a sub-structure.<br />
* A call to WriteVRAMBuffer will execute faster than a call to VRAMStructWrite with $0302 as an argument, but both will have the same effect.<br />
<br />
=== Read routines and VRAM buffer ===<br />
<br />
Unlike the write routines which are very complete, the read routines are somewhat incomplete and their functionality have to be completed by the user (which limits their usefulness). Most of what follows is some sort of speculation about the usefulness of the incomplete read routines of the BIOS.<br />
<br />
The read buffer is a part of the VRAM buffer at $0300-$03xx, but the same location can't be used to transfer read and writes on the same frame (for example a RMW operation). Instead the user must manually split the buffer in two parts, the "read" buffer and the "write" buffer. The read buffer is probably supposed to always lies after the write buffer, so that when the read buffer is in use, the value in $300 (size of the write buffer) should be adjusted accordingly. The user should manually keep track of how many bytes are used for the read buffer, as well as the starting point of the read buffer (they are argument to the ReadVRAMBuffer function).<br />
<br />
The structure of the read buffer itself is trivial - only single bytes are read (there's no runs of data). Very likely the purpose is to read one or a few individual attribute table data, in order to change the color mapping of individual 2x2 squares instead of whole 4x4 square (read-modify-write operation).<br />
All reads are mapped to a structure of 3 bytes in the read buffer :<br />
SIZE CONTENTS<br />
2 VRAM Address (big endian)<br />
1 data<br />
<br />
Therfore, for each byte which is read from VRAM, 3 bytes have to be reserved in the read buffer.<br />
Once data from VRAM has been read, if it must be written back after a modification, the user need to copy it to the write buffer manually.<br />
<br />
The GetVRAMBufferByte was probably designed to prevent reading attribute tables when it has already been read in the past by a call to ReadVRAMBuffer. For example if you don't know if you're modifying another area of the same 4x4 attribute byte as previously (no need to read again), or if you're modifying part of another 4x4 attribute byte (need to read from VRAM). If the routine return with C=0, the accumulator contains the attribute byte that was already read, but if C=1, it means we should first call ReadVRAMBuffer before getting the data we want.<br />
<br />
Since reading VRAM for nothing is not a harmful operation, the user can call ReadVRAMBuffer every frame even if nothing has to be read (no need for check flags) which is probably why these routines are less complete than the write buffer related routines.<br />
<br />
=== Load Tileset notes ===<br />
<br />
The ''flags'' parameters are as follows:<br />
<br />
7 bit 0<br />
---------<br />
AAAA MMIT<br />
|||| ||||<br />
|||| |||+- Fill bit<br />
|||| ||+-- Transfer direction (0 = Write tiles, 1 = Read tiles)<br />
|||| ++--- Bitplane type (see below)<br />
++++------ Low VRAM Address (aka tile # within a row)<br />
<br />
1st bitplane 2nd bitplane Description<br />
----------- ----------- -----------<br />
0: data data+8 Normal 2-bitplane graphics<br />
1: data fill bit Single bitplane graphics. Fill bit clear : Use colors 0&1 Fill bit set : Use colors 2&3<br />
2: fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&2 Fill bit set : Use colors 1&3<br />
3: data^fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&3 Fill bit set : Use colors 1&2<br />
<br />
This makes it possible for single bitplane tiles to take all possible color schemes when they end up in VRAM. However, it is not possible to (natively) load single bitplane graphics directly from the disk into VRAM; it should be loaded into PRG-RAM before transferring the data into VRAM. In read mode, all non "data" bitplanes are replaced by dummy reads.</div>はるきhttps://www.nesdev.org/w/index.php?title=Nintendo_header&diff=9364Nintendo header2020-10-05T07:35:45Z<p>はるき: Changed hex offsets to uppercase. PRG Checksum is not clamped. PRG & CHR Checksums are stored as big-endian.</p>
<hr />
<div>About 33% of licensed Nintendo NES games have an header present at their last bank, at addresses $FFE0-$FFF9, right before the interrupt vectors.<br />
<br />
The info in them is frequently incomplete and/or inaccurate. When info on a particular filed is not present, a byte $00 or $FF is used for padding<br />
<br />
* '''$FFE0-$FFEF''' : Name of the game, in ASCII, zero terminated (sometimes the name is abbreviated or replaced by Nintendo's code). ASCII codes 20h-5Ah allowed. Should be right-justified.<br />
* '''$FFF0-$FFF1''' : PRG checksum (either the last 16k bank, or the whole PRG-ROM). Sum of all bytes, excluding the two checksum bytes. Big-endian.<br />
* '''$FFF2-$FFF3''' : CHR checksum. 0x00 if RAM. Big-endian.<br />
* '''$FFF4''' : Data sizes. Values: 0 = 8 KiB or 64 KiB based on board type, 1 = 16 KiB, 2 = 32 KiB, 3 = 128 KiB, 4 = 256 KiB, 5 = 512 KiB<br />
**D7-D4: PRG size<br />
**D3: 0 = CHR ROM, 1 = CHR RAM<br />
**D2-D0: CHR size<br />
* '''$FFF5''' : Board Type.<br />
**D7: 0 = Horizontal [[Mirroring|nametable arrangement]], 1 = Vertical arrangement (inverse of [[iNES#Flags 6|iNES mirroring bit]])<br />
**D6-D0: 0 = NROM, 1 = CNROM, 2 = UNROM/AOROM, 3 = GNROM, 4 = MMC (any).<br />
* '''$FFF6''' : Title encoding : 0 = No title entered, 1 = ASCII, 2 = Another encoding<br />
* '''$FFF7''' : Valid Title Length - 1. 0 if no title entered.<br />
* '''$FFF8''' : Maker's code, the same used for the FDS, GB, GBC and SNES headers : 1 = Nintendo, 2-254 = everyone else. 255 must be reserved.<br />
* '''$FFF9''' : Header Validation Byte. 8-bit checksum of $FFF2-$FFF9 should = 0</div>はるきhttps://www.nesdev.org/w/index.php?title=PPU_OAM&diff=9724PPU OAM2020-09-25T08:00:23Z<p>はるき: Fixed link to unofficial sprite sizes on wiki.superfamicom.org</p>
<hr />
<div>The OAM (Object Attribute Memory) is internal memory inside the PPU that contains a display list of up to 64 sprites, where each sprite's information occupies 4 bytes.<br />
<br />
=== Byte 0 ===<br />
Y position of top of sprite<br />
<br />
Sprite data is delayed by one scanline; you must subtract 1 from the sprite's Y coordinate before writing it here.<br />
Hide a sprite by writing any values in $EF-$FF here.<br />
Sprites are never displayed on the first line of the picture, and it is impossible to place a sprite partially off the top of the screen.<br />
<br />
=== Byte 1 ===<br />
Tile index number<br />
<br />
For 8x8 sprites, this is the tile number of this sprite within the pattern table selected in bit 3 of [[PPUCTRL]] ($2000).<br />
<br />
For 8x16 sprites, the PPU ignores the pattern table selection and selects a pattern table from bit 0 of this number.<br />
76543210<br />
||||||||<br />
|||||||+- Bank ($0000 or $1000) of tiles<br />
+++++++-- Tile number of top of sprite (0 to 254; bottom half gets the next tile)<br />
Thus, the pattern table memory map for 8x16 sprites looks like this:<br />
*$00: $0000-$001F<br />
*$01: $1000-$101F<br />
*$02: $0020-$003F<br />
*$03: $1020-$103F<br />
*$04: $0040-$005F<br>[...]<br />
*$FE: $0FE0-$0FFF<br />
*$FF: $1FE0-$1FFF<br />
<br />
=== Byte 2 ===<br />
Attributes<br />
76543210<br />
||||||||<br />
||||||++- Palette (4 to 7) of sprite<br />
|||+++--- Unimplemented<br />
||+------ Priority (0: in front of background; 1: behind background)<br />
|+------- Flip sprite horizontally<br />
+-------- Flip sprite vertically<br />
<br />
Flipping does not change the position of the sprite's bounding box, just the position of pixels within the sprite.<br />
If, for example, a sprite covers (120, 130) through (127, 137), it'll still cover the same area when flipped.<br />
In 8x16 mode, vertical flip flips each of the subtiles and also exchanges their position; the odd-numbered tile of a vertically flipped sprite is drawn on top.<br />
This behavior differs from the behavior of the [https://wiki.superfamicom.org/registers#toc-8 unofficial 16x32 and 32x64 pixel sprite sizes on the Super NES], which [http://wiki.superfamicom.org/snes/show/Sprites will only vertically flip each square sub-region].<br />
<br />
The three unimplemented bits of each sprite's byte 2 do not exist in the PPU and always read back as 0 on PPU revisions that allow reading PPU OAM through [[OAMDATA]] ($2004). This can be emulated by ANDing byte 2 with $E3 either when writing to or when reading from OAM. It has not been determined whether the PPU actually drives these bits low or whether this is the effect of data bus capacitance from reading the last byte of the instruction (LDA $2004, which assembles to AD 04 20).<br />
<br />
=== Byte 3 ===<br />
X position of left side of sprite.<br />
<br />
X-scroll values of $F9-FF results in parts of the sprite to be past the right edge of the screen, thus invisible. It is not possible to have a sprite partially visible on the left edge. Instead, left-clipping through [[PPUMASK| PPUMASK ($2001)]] can be used to simulate this effect.<br />
<br />
=== DMA ===<br />
Most programs write to a copy of OAM somewhere in CPU addressable RAM (often $0200-$02FF) and then copy it to OAM each frame using the [[OAMDMA]] ($4014) register. Writing N to this register causes the DMA circuitry inside the 2A03/07 to fully initialize the OAM by writing [[OAMDATA]] 256 times using successive bytes from starting at address $100*N). The CPU is suspended while the transfer is taking place.<br />
<br />
The address range to copy from could lie outside RAM, though this is only useful for static screens with no animation.<br />
<br />
Not counting the [[OAMDMA]] write tick, the above procedure takes 513 CPU cycles (+1 on odd CPU cycles): first one (or two) idle cycles, and then 256 pairs of alternating read/write cycles. (For comparison, an unrolled LDA/STA loop would usually take four times as long.)<br />
<br />
=== Sprite zero hits ===<br />
<br />
Sprites are conventionally numbered 0 to 63.<br />
Sprite 0 is the sprite controlled by OAM addresses $00-$03, sprite 1 is controlled by $04-$07, ..., and sprite 63 is controlled by $FC-$FF.<br />
<br />
While the PPU is drawing the picture, when an opaque pixel of sprite 0 overlaps an opaque pixel of the background, this is a '''sprite zero hit'''.<br />
The PPU detects this condition and sets bit 6 of [[PPUSTATUS]] ($2002) to 1 starting at this pixel, letting the CPU know how far along the PPU is in drawing the picture.<br />
<br />
Sprite 0 hit does not happen:<br />
* If background or sprite rendering is disabled in [[PPUMASK]] ($2001)<br />
* At x=0 to x=7 if the left-side clipping window is enabled (if bit 2 or bit 1 of PPUMASK is 0).<br />
* At x=255, for an obscure reason related to the pixel pipeline.<br />
* At any pixel where the background or sprite pixel is transparent (2-bit color index from the CHR pattern is %00).<br />
* If sprite 0 hit has already occurred this frame. Bit 6 of PPUSTATUS ($2002) is cleared to 0 at dot 1 of the pre-render line. This means only the first sprite 0 hit in a frame can be detected.<br />
<br />
Sprite 0 hit happens regardless of the following:<br />
* Sprite priority. Sprite 0 can still hit the background from behind.<br />
* The pixel colors. Only the CHR pattern bits are relevant, not the actual rendered colors, and ''any'' CHR color index except %00 is considered opaque.<br />
* The palette. The contents of the palette are irrelevant to sprite 0 hits. For example: a black ($0F) sprite pixel can hit a black ($0F) background as long as neither is the transparent color index %00.<br />
* The PAL PPU blanking on the left and right edges at x=0, x=1, and x=254 (see [[Overscan#PAL|Overscan]]).<br />
<br />
=== Sprite overlapping ===<br />
[[PPU sprite priority|Priority between sprites]] is determined by their address inside OAM.<br />
So to have a sprite displayed in front of another sprite in a scanline, the sprite data that occurs first will overlap any other sprites after it.<br />
For example, when sprites at OAM $0C and $28 overlap, the sprite at $0C will appear in front.<br />
<br />
=== Internal operation ===<br />
<br />
In addition to the primary OAM memory, the PPU contains 32 bytes (enough for 8 sprites) of secondary OAM memory that is not directly accessible by the program. During each visible scanline this secondary OAM is first cleared, and then a linear search of the entire primary OAM is carried out to find sprites that are within y range for the '''next''' scanline (the ''sprite evaluation'' phase). The OAM data for each sprite found to be within range is copied into the secondary OAM, which is then used to initialize eight internal sprite output units.<br />
<br />
See [[PPU rendering]] for information on precise timing.<br />
<br />
The reason sprites at lower addresses in OAM overlap sprites at higher addresses is that sprites at lower addresses also get assigned a lower address in the secondary OAM, and hence get assigned a lower-numbered sprite output unit during the loading phase. Output from lower-numbered sprite output units is wired inside the PPU to take priority over output from higher-numbered sprite output units.<br />
<br />
Sprite zero hit detection relies on the fact that sprite zero, when it is within y range for the next scanline, always gets assigned the first sprite output unit. The hit condition is basically ''sprite zero is in range'' '''AND''' ''the first sprite output unit is outputting a non-zero pixel'' '''AND''' ''the background drawing unit is outputting a non-zero pixel''. (Internally the PPU actually uses '''two''' flags: one to keep track of whether sprite zero occurs on the ''next'' scanline, and another one&mdash;initialized from the first&mdash;to keep track of whether sprite zero occurs on the ''current'' scanline. This is to avoid sprite evaluation, which takes place concurrently with potential sprite zero hits, trampling on the second flag.)<br />
<br />
=== Dynamic RAM decay ===<br />
<br />
Because OAM is implemented with dynamic RAM instead of static RAM, the data stored in OAM memory will quickly begin to decay into random bits if it is not being refreshed. The OAM memory is refreshed once per scanline while rendering is enabled (if either the sprite or background bit is enabled via the [[PPUMASK|register at $2001]]), but on an NTSC PPU this refresh is prevented whenever rendering is disabled.<br />
<br />
When rendering is turned off, or during vertical blanking between frames, the OAM memory will hold stable values for a short period before it begins to decay. It will last at least as long as an NTSC vertical blank interval (~1.3ms), but not much longer than this.<ref>[http://forums.nesdev.org/viewtopic.php?p=109548#p109548 Forum post:] Re: Just how cranky is the PPU OAM?</ref> Because of this, it is not normally useful to write to OAM outside of vertical blank, where rendering is expected to start refreshing its data soon after the write. Writes to [[OAMDMA|$4014]] or [[OAMDATA|$2004]] should usually be done in an NMI routine, or otherwise within vertical blanking.<br />
<br />
If using an advanced technique like forced blanking to manually extend the vertical blank time, it may be necessary to do the OAM DMA last, before enabling rendering mid-frame, to avoid decay.<br />
<br />
Because OAM decay is more or less random, and with timing that is sensitive to temperature or other environmental factors, it not something a game could normally rely on. Most emulators do not simulate the decay, and suffer no compatibility problems as a result. Software developers targeting the NES hardware should be careful not to rely on this.<br />
<br />
Because PAL machines have a longer vertical blanking interval, the 2C07 (PAL PPU) begins refreshing OAM somewhere around 20-24 scanlines after NMI<ref>[http://forums.nesdev.org/viewtopic.php?f=9&t=11041 Forum post:] OAM reading on PAL NES</ref><ref>[https://forums.nesdev.org/viewtopic.php?f=3&t=15763 Forum post:] PAL NES, sprite evaluation and $2004 reads/writes</ref>. This prevents the values in DRAM from decaying during the extra 50 scanlines before the picture starts. The 2C07 additionally refreshes OAM during the visible portion of the screen even if rendering is disabled. Because of this, OAM DMA must be done near the beginning of vertical blank on the 2C07, as everywhere else it will conflict with this refresh. In exchange, OAM decay does not occur at all on the PAL NES.<br />
<br />
== See also ==<br />
* [[PPU sprite evaluation]]<br />
* [[PPU sprite priority]]<br />
* [[Sprite overflow games]]<br />
* [[PPU OAM/zh|this page in Chinese]]<br />
<br />
== References ==<br />
<references /></div>はるきhttps://www.nesdev.org/w/index.php?title=FDS_BIOS&diff=2532FDS BIOS2020-02-15T22:10:50Z<p>はるき: Formatting</p>
<hr />
<div>The [[Famicom Disk System]] 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]].<br />
<br />
== Pseudo-registers ==<br />
The FDS BIOS uses several bytes on the zero-page. 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.<br />
<br />
[$FF]: value last written to [[PPUCTRL|$2000]] $80 on reset.<br />
[$FE]: value last written to [[PPUMASK|$2001]] $06 on reset<br />
[$FD]: value last written to [[PPUSCROLL|$2005/1]] $00 on reset.<br />
[$FC]: value last written to [[PPUSCROLL|$2005/2]] $00 on reset.<br />
[$FB]: value last written to [[Controller reading|$4016]] $00 on reset.<br />
[$FA]: value last written to [[Family_Computer_Disk_System#FDS_Control_.28.244025.29|$4025]] $2E on reset.<br />
[$F9]: value last written to [[Family_Computer_Disk_System#External_connector_.28.244026.29|$4026]] $FF on reset.<br />
$F5..$F8 : Used by controller read routines<br />
$00..$0F is used as temporary memory for the BIOS. The main program can use it as temporary memory too.<br />
<br />
The FDS BIOS also uses 4 bytes at the lower end of the stack page to control behaviour of interrupt/reset vectors:<br />
<br />
[$0102]/[$0103]: PC action on reset<br />
[$0101]: PC action on IRQ. set to $80 on reset<br />
[$0100]: PC action on NMI. set to $C0 on reset<br />
<br />
RESET:<br />
($DFFC): disk game reset vector (if [$0102] = $35, and [$0103] = $53 or $AC)<br />
<br />
IRQ:<br />
($DFFE): disk game IRQ vector (if [$0101] = %11xxxxxx)<br />
$E1EF : BIOS acknowledge and delay (if [$0101] = %10xxxxxx)<br />
$E1CE : BIOS disk transfer (if [$0101] = %01xxxxxx)<br />
$E1D9 : BIOS disk skip bytes (if [$0101] = %00xxxxxx)<br />
<br />
NMI:<br />
($DFF6): disk game NMI vector #1 (if [$0100] = %01xxxxxx)<br />
($DFF8): disk game NMI vector #2 (if [$0100] = %10xxxxxx)<br />
($DFFA): disk game NMI vector #3 (if [$0100] = %11xxxxxx)<br />
$E19D : BIOS disable NMI (if [$0100] = %00xxxxxx)<br />
<br />
A few important notes :<br />
* After loading the boot files, $0102 is set to $35 so that the ($DFFC) vector is used and the BIOS is skipped.<br />
* $0103 indicate reset type : $AC = first boot of the game, $53 = the game was soft-reset by the user<br />
* To use a custom IRQ routine, $C0 should be manually written to $0101<br />
* There are 3 possible NMI vectors, #3 is used by default.<br />
* 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 have files loaded in.<br />
<br />
== BIOS calls ==<br />
<br />
=== Disk access routines ===<br />
<br />
* 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.<br />
* Memory at $00-$0F will be affected by those routines<br />
* Unlike the vast majority of disk drives, the FDS lacks any kind of intelligent tracking system. All BIOS load and save functions will do access to the whole disk, no matter which data they load/save. A simple way to overcome this problem is to have a custom loading routine, similar to the BIOS one but forcing the # of files to a smaller number than it actually is. That way the later files are not accessed at all and the earlier files load faster. Of course the maximal time is still taken when loading files that are late on the disk.<br />
* 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.<br />
* 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.<br />
* 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.<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Description<br />
|-<br />
| $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.<br />
|-<br />
| $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).<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $E301 || SetFileCount1 || Pointer to Disk ID, A = file count minus one = # of the last file || A = error # || Set the file count to A + 1<br />
|-<br />
| $E305 || SetFileCount || Pointer to Disk ID, A = file count || A = error # || Set the file count to A<br />
|-<br />
| $E32A || GetDiskInfo || Pointer to Disk Info || A = error # || Fills DiskInfo up with data read off the current disk.<br />
|-<br />
|}<br />
<br />
=== Low-Level Disk access routines ===<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Description<br />
|-<br />
| $E445 || CheckDiskHeader || Pointer to 10 byte string at $00 || || Compares the first 10 bytes on the disk coming after the FDS string, to 10 bytes pointed to by Ptr($00). To bypass the checking of any byte, a -1 can be placed in the equivelant place in the compare string. Otherwise, if the comparison fails, an appropriate error will be generated.<br />
|-<br />
| $E484 || GetNumFiles || || || Reads # of files stored on disk, stores the result in $06<br />
|-<br />
| $E492 || SetNumFiles || || A = # of files || Writes new number of files to disk header.<br />
|-<br />
| $E4A0 || FileMatchTest || Pointer to FileID list at $02 || || Uses a byte string pointed at by Ptr($02) to tell the disk system which files to load. The file ID's number is searched for in the string. If an exact match is found, [$09] is 0'd, and [$0E] is incremented. If no matches are found after 20 bytes, or a -1 entry is encountered, [$09] is set to -1. If the first byte in the string is -1, the BootID number is used for matching files (any FileID that is not greater than the BootID qualifies as a match).<br />
|-<br />
| $E4DA || SkipFiles || Number of files to skip in $06 || || Skips over specified number of files.<br />
|-<br />
|}<br />
<br />
=== Example code how to load files ===<br />
<br />
<pre><br />
Load:<br />
JSR LoadFiles<br />
.dw DiskID<br />
.dw LoadList<br />
BNE _Error ;Check if there is an error<br />
RTS<br />
_Error:<br />
JSR PrintError ;If so print the error number and message to screen (include side/disk changing prompts)<br />
_sideError:<br />
LDA $4032<br />
AND #$01<br />
BEQ _sideError ;Wait until disk is ejected<br />
_insert:<br />
LDA $4032<br />
AND #$01<br />
BNE _instert ;Wait until disk is inserted<br />
JMP Load<br />
<br />
DiskID:<br />
.db $01 ;Manufacturer code<br />
.db "NAME" ;4-letter code of game<br />
.db $00 ;Version<br />
.db $01 ;Disk side<br />
.db $00 ;Disk number<br />
.db $00, $00 ;Extra disk IDs<br />
<br />
LoadList ;In this example the files with IDs equal to $02, $03 or $04 will be loaded into memory<br />
.db $02, $03, $04, $FF<br />
</pre><br />
<br />
=== Error list ===<br />
<br />
Message in bold is the official BIOS message (if there is one) followed by an explanation<br />
*$00: no error<br />
*$01: '''disk set''', ($4032.0) disk not set<br />
*$02: '''battery''', ($4033.7) power supply failure<br />
*$03: ($4032.2) disk is write protected<br />
*$04: Wrong maker ID<br />
*$05: Wrong game<br />
*$06: Wrong game version<br />
*$07: '''a,b side''', wrong side number<br />
*$08: '''disk no.''', wrong disk number<br />
*$09: wrong additional disk ID 1<br />
*$0A: wrong additional disk ID 2<br />
*$20: '''disk trouble''', approval check failed<br />
*$21: '''disk trouble''', '*NINTENDO-HVC*' string in block 1 doesn't match<br />
*$22: '''disk trouble''', block type 1 expected<br />
*$23: '''disk trouble''', block type 2 expected<br />
*$24: '''disk trouble''', block type 3 expected<br />
*$25: '''disk trouble''', block type 4 expected<br />
*$27: '''disk trouble''', ($4030.4) block failed CRC<br />
*$28: '''disk trouble''', ($4030.6) file ends prematurely during read<br />
*$29: '''disk trouble''', ($4030.6) file ends prematurely during write<br />
*$30: '''disk trouble''', ($4032.1) disk is full<br />
<br />
=== Disk ID structure ===<br />
<br />
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.<br />
<br />
offset size error# description<br />
------ ---- ------ -----------<br />
0 1 $04 game manufacturer code<br />
1 4 $05 game ASCII name string<br />
5 1 $06 game version<br />
6 1 $07 disk side #<br />
7 1 $08 disk #<br />
8 1 $09 extra disk # data<br />
9 1 $10 extra disk # data<br />
A -<br />
<br />
=== File list structure ===<br />
<br />
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).<br />
<br />
Multiple files are loaded in the order as they exist on the disk, not in the order of the list.<br />
<br />
=== File header structure ===<br />
<br />
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.<br />
<br />
offset size description<br />
------ ---- -----------<br />
00 1 file ID code<br />
01 8 file name<br />
09 2 load address<br />
0B 2 file data size<br />
0D 1 file type ($00 : Program; $01 : Character; $02 : Nametable)<br />
0E 2 source address of file data (NOT written to disk)<br />
10 1 source address type ($00 : RAM, $01 : VRAM)<br />
11 -<br />
<br />
=== Disk information structure ===<br />
<br />
This is a data structure returned by a subroutine, of collected information <br />
from the disk (list of files on disk, disk size, etc.). The following table <br />
is a description of that structure.<br />
<br />
offset size<br />
------ ----<br />
0 1 game manufacturer code<br />
1 4 game ASCII name string<br />
5 1 game version<br />
6 1 disk side #<br />
7 1 disk #<br />
8 1 extra disk # data<br />
9 1 extra disk # data<br />
A 1 # of files on disk<br />
<br />
(the following block will appear for as many files as the "# of files on <br />
disk" byte indicates)<br />
<br />
B 1 file ID code<br />
C 8 file name (ASCII)<br />
<br />
(the following is present after the last file info block. Disk size is equal <br />
to the sum of each file's size entry, plus an extra 261 per file.)<br />
<br />
x 1 disk size high byte<br />
x+1 1 disk size low byte<br />
x+2 -<br />
<br />
== Other BIOS calls ==<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Affected RAM/Registers || Description<br />
|-<br />
| $E149 || Delay132 || || || || 132 clock cycle delay<br />
|-<br />
| $E153 || Delayms || || || X, Y || Delay routine, Y = delay in ms (approximate)<br />
|-<br />
| $E161 || DisPFObj || || || A, $FE || Disable sprites and background<br />
|-<br />
| $E16B || EnPFObj || || || A, $FE || Enable sprites and background<br />
|-<br />
| $E171 || DisObj || || || A, $FE || Disable sprites<br />
|-<br />
| $E178 || EnObj || || || A, $FE || Enable sprites<br />
|-<br />
| $E17E || DisPF || || || A, $FE || Disable background<br />
|-<br />
| $E185 || EnPF || || || A, $FE || Enable background<br />
|-<br />
| $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 $0100 is preserved, but further VBlanks are disabled.<br />
|-<br />
| $E7BB || VRAMStructWrite || Pointer to VRAM buffer to be written || || A, X, Y, $00, $01, $FF || Set VRAM increment to 1 (clear [[PPUCTRL]]/$FF bit 2), and write a VRAM buffer to VRAM. Read below for information on the structure.<br />
|-<br />
| $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.<br />
|-<br />
| $E86A || WriteVRAMBuffer || || || A, X, Y, $301, $302 || Write the VRAM Buffer at $302 to VRAM. Read below for information on the structure.<br />
|-<br />
| $E8B3 || ReadVRAMBuffer || X = start address of read buffer, Y = # of bytes to read || || A, X, Y || Read individual bytes from VRAM to the VRAMBuffer. (see notes below)<br />
|-<br />
| $E8D2 || PrepareVRAMString || A = High VRAM address, X = Low VRAM address, 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 copies pointed data into the VRAM buffer.<br />
|-<br />
| $E8E1 || PrepareVRAMStrings || A = High VRAM address, X = Low VRAM address, Direct pointer = data to be written to VRAM || A = $FF : no error, A = $01 : data didn't fit in buffer || A, X, Y, $00, $01, $02, $03, $04, $05, $06 || This routine copies a 2D string into the VRAM buffer. The first byte of the data determines the width and height of the following string (in tiles): Upper nybble = height, lower nybble = width.<br />
|-<br />
| $E94F || GetVRAMBufferByte || X = starting index of read buffer, Y = # of address to compare (starting at 1), $00, $01 = address to read from || carry clear : a previously read byte was returned, carry set : no byte was read, should wait next call to ReadVRAMBuffer || A, X, Y || This routine was likely planned to be used in order to avoid useless latency on a VRAM reads (see notes below). It compares the VRAM address in ($00) with the Yth (starting at 1) address of the read buffer. If both addresses match, the corresponding data byte is returned exit with c clear. If the addresses are different, the buffer address is overwritten by the address in ($00) and the routine exit with c set.<br />
|-<br />
| $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 address (assumes no scrolling, and points to first nametable at $2000-$23ff).<br />
|-<br />
| $E997 || Nam2PixelConv || $00 = High nametable address, $01 = low nametable address || $02 = Pixel X cord, $03 = Pixel Y cord || A || This routine convert a nametable address to corresponding pixel coordinates (assume no scrolling).<br />
|-<br />
| $E9B1 || Random || X = Zero Page address 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 the program is 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''.<br />
|-<br />
| $E9C8 || SpriteDMA || || || A || This routine does sprite DMA from RAM $0200-$02FF<br />
|-<br />
| $E9D3 || CounterLogic || A, Y = end Zeropage address of counters, X = start zeropage address 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.<br />
|-<br />
| $E9EB || ReadPads || || $F5 = Joypad #1 data, $F6 = Joypad #2 data || A, X, $00, $01, || This read hardwired famicom joypads.<br />
|-<br />
| $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<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $EA68 || ReadDownExpPads || $F1-$F4 = up->down transitions, $F5-$F8 = Joypad 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.<br />
|-<br />
| $EA84 || VRAMFill || A = High VRAM Address (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.<br />
|-<br />
| $EAD2 || MemFill || A = fill value, X = first page #, Y = last page # || || A, X, Y, $00, $01 || This routines fills RAM pages with specified value.<br />
|-<br />
| $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<br />
|-<br />
| $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.<br />
|-<br />
| $EB13 || ReadKeyboard || || || || Read Family Basic Keyboard expansion (detail is under analysis)<br />
|-<br />
| $EB66 || LoadTileset || A = Low VRAM Address & Flags, Y = Hi VRAM Address, 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.<br />
|-<br />
| $EC22 || unk_EC22|| || || || Some kind of logic that some games use. (detail is under analysis)<br />
|}<br />
<br />
<br />
=== VRAM write transfer structure ===<br />
<br />
The structure of VRAM buffers are as follows:<br />
<br />
SIZE CONTENTS<br />
2 VRAM Address (big endian)<br />
1 bit 0-5 length of data ($0 means a length of 64)<br />
bit 6 : 0 = copy, 1 = fill<br />
bit 7 : 0 = increment by 1, 1 = increment by 32<br />
n Data to copy to VRAM<br />
..... repeated as many times as needed<br />
1 $FF<br />
<br />
* The main structure is terminated by a $FF byte (High address is always supposed to be in $00..$3F range)<br />
* $4C is a "call" command. The 2 bytes that follow is the address of a sub-VRAM structure. The sub-structure can call another sub-structure and so on.<br />
* $60 is a "return" command. It will terminate a sub-structure.<br />
* If Fill mode is used, the routine takes only 1 byte of data which is repeated.<br />
<br />
=== VRAM Buffer notes ===<br />
<br />
The VRAM buffer is located at $0300-$03xx. $300 holds the size of the buffer (maximum), and $301 holds the end index of the buffer. The actual buffer lies at $0302-$03xx, and is of variable length.<br />
<br />
* $0300 is initialized to the value $7D, effectively making the buffer lie at $0300-$037F. It's possible to change the value here to make it bigger or smaller, but the biggest possible value is $FD, making the buffer lie at $0300-$03FF.<br />
* Format of the buffer is equivalent to the VRAM structure above, except that there are no sub-structures, no increment by 32 flag and no fill flag.<br />
* For this reason, the VRAM buffer at $0302 can be used as a sub-structure.<br />
* A call to WriteVRAMBuffer will execute faster than a call to VRAMStructWrite with $0302 as an argument, but both will have the same effect.<br />
<br />
=== Read routines and VRAM buffer ===<br />
<br />
Unlike the write routines which are very complete, the read routines are somewhat incomplete and their functionality have to be completed by the user (which limits their usefulness). Most of what follows is some sort of speculation about the usefulness of the incomplete read routines of the BIOS.<br />
<br />
The read buffer is a part of the VRAM buffer at $0300-$03xx, but the same location can't be used to transfer read and writes on the same frame (for example a RMW operation). Instead the user must manually split the buffer in two parts, the "read" buffer and the "write" buffer. The read buffer is probably supposed to always lies after the write buffer, so that when the read buffer is in use, the value in $300 (size of the write buffer) should be adjusted accordingly. The user should manually keep track of how many bytes are used for the read buffer, as well as the starting point of the read buffer (they are argument to the ReadVRAMBuffer function).<br />
<br />
The structure of the read buffer itself is trivial - only single bytes are read (there's no runs of data). Very likely the purpose is to read one or a few individual attribute table data, in order to change the color mapping of individual 2x2 squares instead of whole 4x4 square (read-modify-write operation).<br />
All reads are mapped to a structure of 3 bytes in the read buffer :<br />
SIZE CONTENTS<br />
2 VRAM Address (big endian)<br />
1 data<br />
<br />
Therfore, for each byte which is read from VRAM, 3 bytes have to be reserved in the read buffer.<br />
Once data from VRAM has been read, if it must be written back after a modification, the user need to copy it to the write buffer manually.<br />
<br />
The GetVRAMBufferByte was probably designed to prevent reading attribute tables when it has already been read in the past by a call to ReadVRAMBuffer. For example if you don't know if you're modifying another area of the same 4x4 attribute byte as previously (no need to read again), or if you're modifying part of another 4x4 attribute byte (need to read from VRAM). If the routine return with C=0, the accumulator contains the attribute byte that was already read, but if C=1, it means we should first call ReadVRAMBuffer before getting the data we want.<br />
<br />
Since reading VRAM for nothing is not a harmful operation, the user can call ReadVRAMBuffer every frame even if nobody has to be read (no need for check flags) which is probably why these routines are less complete than the write buffer related routines.<br />
<br />
=== Load Tileset notes ===<br />
<br />
The ''flags'' parameters are as follows:<br />
<br />
7 bit 0<br />
---------<br />
AAAA MMIT<br />
|||| ||||<br />
|||| |||+- Fill bit<br />
|||| ||+-- Transfer direction (0 = Write tiles, 1 = Read tiles)<br />
|||| ++--- Bitplane type (see below)<br />
++++------ Low VRAM Address (aka tile # within a row)<br />
<br />
1st bitplane 2nd bitplane Description<br />
----------- ----------- -----------<br />
0: data data+8 Normal 2-bitplane graphics<br />
1: data fill bit Single bitplane graphics. Fill bit clear : Use colors 0&1 Fill bit set : Use colors 2&3<br />
2: fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&2 Fill bit set : Use colors 1&3<br />
3: data^fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&3 Fill bit set : Use colors 1&2<br />
<br />
This makes it possible for single bitplane tiles to take all possible color schemes when they end up in VRAM. However, it is not possible to (natively) load single bitplane graphics directly from the disk into VRAM; it should be loaded into PRG-RAM before transferring the data into VRAM. In read mode, all non "data" bitplanes are replaced by dummy reads.</div>はるきhttps://www.nesdev.org/w/index.php?title=FDS_BIOS&diff=2531FDS BIOS2020-02-15T22:04:01Z<p>はるき: Formatting</p>
<hr />
<div>The [[Famicom Disk System]] 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]].<br />
<br />
== Pseudo-registers ==<br />
The FDS BIOS uses several bytes on the zero-page. 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.<br />
<br />
[$FF]: value last written to [[PPUCTRL|$2000]] $80 on reset.<br />
[$FE]: value last written to [[PPUMASK|$2001]] $06 on reset<br />
[$FD]: value last written to [[PPUSCROLL|$2005/1]] $00 on reset.<br />
[$FC]: value last written to [[PPUSCROLL|$2005/2]] $00 on reset.<br />
[$FB]: value last written to [[Controller reading|$4016]] $00 on reset.<br />
[$FA]: value last written to [[Family_Computer_Disk_System#FDS_Control_.28.244025.29|$4025]] $2E on reset.<br />
[$F9]: value last written to [[Family_Computer_Disk_System#External_connector_.28.244026.29|$4026]] $FF on reset.<br />
$F5..$F8 : Used by controller read routines<br />
$00..$0F is used as temporary memory for the BIOS. The main program can use it as temporary memory too.<br />
<br />
The FDS BIOS also uses 4 bytes at the lower end of the stack page to control behaviour of interrupt/reset vectors:<br />
<br />
[$0102]/[$0103]: PC action on reset<br />
[$0101]: PC action on IRQ. set to $80 on reset<br />
[$0100]: PC action on NMI. set to $C0 on reset<br />
<br />
RESET:<br />
($DFFC): disk game reset vector (if [$0102] = $35, and [$0103] = $53 or $AC)<br />
<br />
IRQ:<br />
($DFFE): disk game IRQ vector (if [$0101] = %11xxxxxx)<br />
$E1EF : BIOS acknowledge and delay (if [$0101] = %10xxxxxx)<br />
$E1CE : BIOS disk transfer (if [$0101] = %01xxxxxx)<br />
$E1D9 : BIOS disk skip bytes (if [$0101] = %00xxxxxx)<br />
<br />
NMI:<br />
($DFF6): disk game NMI vector #1 (if [$0100] = %01xxxxxx)<br />
($DFF8): disk game NMI vector #2 (if [$0100] = %10xxxxxx)<br />
($DFFA): disk game NMI vector #3 (if [$0100] = %11xxxxxx)<br />
$E19D : BIOS disable NMI (if [$0100] = %00xxxxxx)<br />
<br />
A few important notes :<br />
* After loading the boot files, $0102 is set to $35 so that the ($DFFC) vector is used and the BIOS is skipped.<br />
* $0103 indicate reset type : $AC = first boot of the game, $53 = the game was soft-reset by the user<br />
* To use a custom IRQ routine, $C0 should be manually written to $0101<br />
* There are 3 possible NMI vectors, #3 is used by default.<br />
* 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 have files loaded in.<br />
<br />
== BIOS calls ==<br />
<br />
=== Disk access routines ===<br />
<br />
* 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.<br />
* Memory at $00-$0F will be affected by those routines<br />
* Unlike the vast majority of disk drives, the FDS lacks any kind of intelligent tracking system. All BIOS load and save functions will do access to the whole disk, no matter which data they load/save. A simple way to overcome this problem is to have a custom loading routine, similar to the BIOS one but forcing the # of files to a smaller number than it actually is. That way the later files are not accessed at all and the earlier files load faster. Of course the maximal time is still taken when loading files that are late on the disk.<br />
* 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.<br />
* 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.<br />
* 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.<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Description<br />
|-<br />
| $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.<br />
|-<br />
| $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).<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $E301 || SetFileCount1 || Pointer to Disk ID, A = file count minus one = # of the last file || A = error # || Set the file count to A + 1<br />
|-<br />
| $E305 || SetFileCount || Pointer to Disk ID, A = file count || A = error # || Set the file count to A<br />
|-<br />
| $E32A || GetDiskInfo || Pointer to Disk Info || A = error # || Fills DiskInfo up with data read off the current disk.<br />
|-<br />
|}<br />
<br />
=== Low-Level Disk access routines ===<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Description<br />
|-<br />
| $E445 || CheckDiskHeader || Pointer to 10 byte string at $00 || || Compares the first 10 bytes on the disk coming after the FDS string, to 10 bytes pointed to by Ptr($00). To bypass the checking of any byte, a -1 can be placed in the equivelant place in the compare string. Otherwise, if the comparison fails, an appropriate error will be generated.<br />
|-<br />
| $E484 || GetNumFiles || || || Reads # of files stored on disk, stores the result in $06<br />
|-<br />
| $E492 || SetNumFiles || || A = # of files || Writes new number of files to disk header.<br />
|-<br />
| $E4A0 || FileMatchTest || Pointer to FileID list at $02 || || Uses a byte string pointed at by Ptr($02) to tell the disk system which files to load. The file ID's number is searched for in the string. If an exact match is found, [$09] is 0'd, and [$0E] is incremented. If no matches are found after 20 bytes, or a -1 entry is encountered, [$09] is set to -1. If the first byte in the string is -1, the BootID number is used for matching files (any FileID that is not greater than the BootID qualifies as a match).<br />
|-<br />
| $E4DA || SkipFiles || Number of files to skip in $06 || || Skips over specified number of files.<br />
|-<br />
|}<br />
<br />
=== Example code how to load files ===<br />
<br />
<pre><br />
Load:<br />
JSR LoadFiles<br />
.dw DiskID<br />
.dw LoadList<br />
BNE _Error ;Check if there is an error<br />
RTS<br />
_Error:<br />
JSR PrintError ;If so print the error number and message to screen (include side/disk changing prompts)<br />
_sideError:<br />
LDA $4032<br />
AND #$01<br />
BEQ _sideError ;Wait until disk is ejected<br />
_insert:<br />
LDA $4032<br />
AND #$01<br />
BNE _instert ;Wait until disk is inserted<br />
JMP Load<br />
<br />
DiskID:<br />
.db $01 ;Manufacturer code<br />
.db "NAME" ;4-letter code of game<br />
.db $00 ;Version<br />
.db $01 ;Disk side<br />
.db $00 ;Disk number<br />
.db $00, $00 ;Extra disk IDs<br />
<br />
LoadList ;In this example the files with IDs equal to $02, $03 or $04 will be loaded into memory<br />
.db $02, $03, $04, $FF<br />
</pre><br />
<br />
=== Error list ===<br />
<br />
Message in bold is the official BIOS message (if there is one) followed by an explanation<br />
*$00: no error<br />
*$01: '''disk set''', ($4032.0) disk not set<br />
*$02: '''battery''', ($4033.7) power supply failure<br />
*$03: ($4032.2) disk is write protected<br />
*$04: Wrong maker ID<br />
*$05: Wrong game<br />
*$06: Wrong game version<br />
*$07: '''a,b side''', wrong side number<br />
*$08: '''disk no.''', wrong disk number<br />
*$09: wrong additional disk ID 1<br />
*$0A: wrong additional disk ID 2<br />
*$20: '''disk trouble''', approval check failed<br />
*$21: '''disk trouble''', '*NINTENDO-HVC*' string in block 1 doesn't match<br />
*$22: '''disk trouble''', block type 1 expected<br />
*$23: '''disk trouble''', block type 2 expected<br />
*$24: '''disk trouble''', block type 3 expected<br />
*$25: '''disk trouble''', block type 4 expected<br />
*$27: '''disk trouble''', ($4030.4) block failed CRC<br />
*$28: '''disk trouble''', ($4030.6) file ends prematurely during read<br />
*$29: '''disk trouble''', ($4030.6) file ends prematurely during write<br />
*$30: '''disk trouble''', ($4032.1) disk is full<br />
<br />
=== Disk ID structure ===<br />
<br />
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.<br />
<br />
offset size error# description<br />
------ ---- ------ -----------<br />
0 1 $04 game manufacturer code<br />
1 4 $05 game ASCII name string<br />
5 1 $06 game version<br />
6 1 $07 disk side #<br />
7 1 $08 disk #<br />
8 1 $09 extra disk # data<br />
9 1 $10 extra disk # data<br />
A -<br />
<br />
=== File list structure ===<br />
<br />
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).<br />
<br />
Multiple files are loaded in the order as they exist on the disk, not in the order of the list.<br />
<br />
=== File header structure ===<br />
<br />
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.<br />
<br />
offset size description<br />
------ ---- -----------<br />
00 1 file ID code<br />
01 8 file name<br />
09 2 load address<br />
0B 2 file data size<br />
0D 1 file type ($00 : Program; $01 : Character; $02 : Nametable)<br />
0E 2 source address of file data (NOT written to disk)<br />
10 1 source address type ($00 : RAM, $01 : VRAM)<br />
11 -<br />
<br />
=== Disk information structure ===<br />
<br />
This is a data structure returned by a subroutine, of collected information <br />
from the disk (list of files on disk, disk size, etc.). The following table <br />
is a description of that structure.<br />
<br />
offset size<br />
------ ----<br />
0 1 game manufacturer code<br />
1 4 game ASCII name string<br />
5 1 game version<br />
6 1 disk side #<br />
7 1 disk #<br />
8 1 extra disk # data<br />
9 1 extra disk # data<br />
A 1 # of files on disk<br />
<br />
(the following block will appear for as many files as the "# of files on <br />
disk" byte indicates)<br />
<br />
B 1 file ID code<br />
C 8 file name (ASCII)<br />
<br />
(the following is present after the last file info block. Disk size is equal <br />
to the sum of each file's size entry, plus an extra 261 per file.)<br />
<br />
x 1 disk size high byte<br />
x+1 1 disk size low byte<br />
x+2 -<br />
<br />
== Other BIOS calls ==<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Affected RAM/Registers || Description<br />
|-<br />
| $e149 || Delay132 || || || || 132 clock cycle delay<br />
|-<br />
| $e153 || Delayms || || || X, Y || Delay routine, Y = delay in ms (approximate)<br />
|-<br />
| $e161 || DisPFObj || || || A, $fe || Disable sprites and background<br />
|-<br />
| $e16b || EnPFObj || || || A, $fe || Enable sprites and background<br />
|-<br />
| $e171 || DisObj || || || A, $fe || Disable sprites<br />
|-<br />
| $e178 || EnObj || || || A, $fe || Enable sprites<br />
|-<br />
| $e17e || DisPF || || || A, $fe || Disable background<br />
|-<br />
| $e185 || EnPF || || || A, $fe || Enable background<br />
|-<br />
| $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.<br />
|-<br />
| $e7bb || VRAMStructWrite || Pointer to VRAM buffer to be written || || A, X, Y, $00, $01, $ff || Set VRAM increment to 1 (clear [[PPUCTRL]]/$ff bit 2), and write a VRAM buffer to VRAM. Read below for information on the structure.<br />
|-<br />
| $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.<br />
|-<br />
| $e86a || WriteVRAMBuffer || || || A, X, Y, $301, $302 || Write the VRAM Buffer at $302 to VRAM. Read below for information on the structure.<br />
|-<br />
| $e8b3 || ReadVRAMBuffer || X = start address of read buffer, Y = # of bytes to read || || A, X, Y || Read individual bytes from VRAM to the VRAMBuffer. (see notes below)<br />
|-<br />
| $e8d2 || PrepareVRAMString || A = High VRAM address, X = Low VRAM address, 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 copies pointed data into the VRAM buffer.<br />
|-<br />
| $e8e1 || PrepareVRAMStrings || A = High VRAM address, X = Low VRAM address, Direct pointer = data to be written to VRAM || A = $ff : no error, A = $01 : data didn't fit in buffer || A, X, Y, $00, $01, $02, $03, $04, $05, $06 || This routine copies a 2D string into the VRAM buffer. The first byte of the data determines the width and height of the following string (in tiles): Upper nybble = height, lower nybble = width.<br />
|-<br />
| $e94f || GetVRAMBufferByte || X = starting index of read buffer, Y = # of address to compare (starting at 1), $00, $01 = address to read from || carry clear : a previously read byte was returned, carry set : no byte was read, should wait next call to ReadVRAMBuffer || A, X, Y || This routine was likely planned to be used in order to avoid useless latency on a VRAM reads (see notes below). It compares the VRAM address in ($00) with the Yth (starting at 1) address of the read buffer. If both addresses match, the corresponding data byte is returned exit with c clear. If the addresses are different, the buffer address is overwritten by the address in ($00) and the routine exit with c set.<br />
|-<br />
| $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 address (assumes no scrolling, and points to first nametable at $2000-$23ff).<br />
|-<br />
| $e997 || Nam2PixelConv || $00 = High nametable address, $01 = low nametable address || $02 = Pixel X cord, $03 = Pixel Y cord || A || This routine convert a nametable address to corresponding pixel coordinates (assume no scrolling).<br />
|-<br />
| $e9b1 || Random || X = Zero Page address 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 the program is 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''.<br />
|-<br />
| $e9c8 || SpriteDMA || || || A || This routine does sprite DMA from RAM $200-$2ff<br />
|-<br />
| $e9d3 || CounterLogic || A, Y = end Zeropage address of counters, X = start zeropage address 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.<br />
|-<br />
| $e9eb || ReadPads || || $f5 = Joypad #1 data, $f6 = Joypad #2 data || A, X, $00, $01, || This read hardwired famicom joypads.<br />
|-<br />
| $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<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $ea68 || ReadDownExpPads || $f1-$f4 = up->down transitions, $f5-$f8 = Joypad 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.<br />
|-<br />
| $ea84 || VRAMFill || A = High VRAM Address (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.<br />
|-<br />
| $ead2 || MemFill || A = fill value, X = first page #, Y = last page # || || A, X, Y, $00, $01 || This routines fills RAM pages with specified value.<br />
|-<br />
| $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<br />
|-<br />
| $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.<br />
|-<br />
| $eb13 || ReadKeyboard || || || || Read Family Basic Keyboard expansion (detail is under analysis)<br />
|-<br />
| $eb66 || LoadTileset || A = Low VRAM Address & Flags, Y = Hi VRAM Address, 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.<br />
|-<br />
| $ec22 || unk_EC22|| || || || Some kind of logic that some games use. (detail is under analysis)<br />
|}<br />
<br />
<br />
=== VRAM write transfer structure ===<br />
<br />
The structure of VRAM buffers are as follows:<br />
<br />
SIZE CONTENTS<br />
2 VRAM Address (big endian)<br />
1 bit 0-5 length of data ($0 means a length of 64)<br />
bit 6 : 0 = copy, 1 = fill<br />
bit 7 : 0 = increment by 1, 1 = increment by 32<br />
n Data to copy to VRAM<br />
..... repeated as many times as needed<br />
1 $ff<br />
<br />
* The main structure is terminated by a $ff byte (High address is always supposed to be in $00..$3f range)<br />
* $4c is a "call" command. The 2 bytes that follow is the address of a sub-VRAM structure. The sub-structure can call another sub-structure and so on.<br />
* $60 is a "return" command. It will terminate a sub-structure.<br />
* If Fill mode is used, the routine takes only 1 byte of data which is repeated.<br />
<br />
=== VRAM Buffer notes ===<br />
<br />
The VRAM buffer is located at $300-$3xx. $300 holds the size of the buffer (maximum), and $301 holds the end index of the buffer. The actual buffer lies at $302-$3xx, and is of variable length.<br />
<br />
* $300 is initialized to the 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 the biggest possible value is $fd, making the buffer lie at $300-$3ff.<br />
* Format of the buffer is equivalent to the VRAM structure above, except that there are no sub-structures, no increment by 32 flag and no fill flag.<br />
* For this reason, the VRAM buffer at $302 can be used as a sub-structure.<br />
* A call to WriteVRAMBuffer will execute faster than a call to VRAMStructWrite with $302 as an argument, but both will have the same effect.<br />
<br />
=== Read routines and VRAM buffer ===<br />
<br />
Unlike the write routines which are very complete, the read routines are somewhat incomplete and their functionality have to be completed by the user (which limits their usefulness). Most of what follows is some sort of speculation about the usefulness of the incomplete read routines of the BIOS.<br />
<br />
The read buffer is a part of the VRAM buffer at $300-$3xx, but the same location can't be used to transfer read and writes on the same frame (for example a RMW operation). Instead the user must manually split the buffer in two parts, the "read" buffer and the "write" buffer. The read buffer is probably supposed to always lies after the write buffer, so that when the read buffer is in use, the value in $300 (size of the write buffer) should be adjusted accordingly. The user should manually keep track of how many bytes are used for the read buffer, as well as the starting point of the read buffer (they are argument to the ReadVRAMBuffer function).<br />
<br />
The structure of the read buffer itself is trivial - only single bytes are read (there's no runs of data). Very likely the purpose is to read one or a few individual attribute table data, in order to change the color mapping of individual 2x2 squares instead of whole 4x4 square (read-modify-write operation).<br />
All reads are mapped to a structure of 3 bytes in the read buffer :<br />
SIZE CONTENTS<br />
2 VRAM Address (big endian)<br />
1 data<br />
<br />
Therfore, for each byte which is read from VRAM, 3 bytes have to be reserved in the read buffer.<br />
Once data from VRAM has been read, if it must be written back after a modification, the user need to copy it to the write buffer manually.<br />
<br />
The GetVRAMBufferByte was probably designed to prevent reading attribute tables when it has already been read in the past by a call to ReadVRAMBuffer. For example if you don't know if you're modifying another area of the same 4x4 attribute byte as previously (no need to read again), or if you're modifying part of another 4x4 attribute byte (need to read from VRAM). If the routine return with C=0, the accumulator contains the attribute byte that was already read, but if C=1, it means we should first call ReadVRAMBuffer before getting the data we want.<br />
<br />
Since reading VRAM for nothing is not a harmful operation, the user can call ReadVRAMBuffer every frame even if nobody has to be read (no need for check flags) which is probably why these routines are less complete than the write buffer related routines.<br />
<br />
=== Load Tileset notes ===<br />
<br />
The ''flags'' parameters are as follows:<br />
<br />
7 bit 0<br />
---------<br />
AAAA MMIT<br />
|||| ||||<br />
|||| |||+- Fill bit<br />
|||| ||+-- Transfer direction (0 = Write tiles, 1 = Read tiles)<br />
|||| ++--- Bitplane type (see below)<br />
++++------ Low VRAM Address (aka tile # within a row)<br />
<br />
1st bitplane 2nd bitplane Description<br />
----------- ----------- -----------<br />
0: data data+8 Normal 2-bitplane graphics<br />
1: data fill bit Single bitplane graphics. Fill bit clear : Use colors 0&1 Fill bit set : Use colors 2&3<br />
2: fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&2 Fill bit set : Use colors 1&3<br />
3: data^fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&3 Fill bit set : Use colors 1&2<br />
<br />
This makes it possible for single bitplane tiles to take all possible color schemes when they end up in VRAM. However, it is not possible to (natively) load single bitplane graphics directly from the disk into VRAM; it should be loaded into PRG-RAM before transferring the data into VRAM. In read mode, all non "data" bitplanes are replaced by dummy reads.</div>はるきhttps://www.nesdev.org/w/index.php?title=FDS_BIOS&diff=2530FDS BIOS2020-02-15T22:03:27Z<p>はるき: Formatting</p>
<hr />
<div>The [[Famicom Disk System]] 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]].<br />
<br />
== Pseudo-registers ==<br />
The FDS BIOS uses several bytes on the zero-page. 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.<br />
<br />
[$FF]: value last written to [[PPUCTRL|$2000]] $80 on reset.<br />
[$FE]: value last written to [[PPUMASK|$2001]] $06 on reset<br />
[$FD]: value last written to [[PPUSCROLL|$2005/1]] $00 on reset.<br />
[$FC]: value last written to [[PPUSCROLL|$2005/2]] $00 on reset.<br />
[$FB]: value last written to [[Controller reading|$4016]] $00 on reset.<br />
[$FA]: value last written to [[Family_Computer_Disk_System#FDS_Control_.28.244025.29|$4025]] $2E on reset.<br />
[$F9]: value last written to [[Family_Computer_Disk_System#External_connector_.28.244026.29|$4026]] $FF on reset.<br />
$F5..$F8 : Used by controller read routines<br />
$00..$0F is used as temporary memory for the BIOS. The main program can use it as temporary memory too.<br />
<br />
The FDS BIOS also uses 4 bytes at the lower end of the stack page to control behaviour of interrupt/reset vectors:<br />
<br />
[$0102]/[$0103]: PC action on reset<br />
[$0101]: PC action on IRQ. set to $80 on reset<br />
[$0100]: PC action on NMI. set to $C0 on reset<br />
<br />
RESET:<br />
($DFFC): disk game reset vector (if [$0102] = $35, and [$0103] = $53 or $AC)<br />
<br />
IRQ:<br />
($DFFE): disk game IRQ vector (if [$0101] = %11xxxxxx)<br />
$E1EF : BIOS acknowledge and delay (if [$0101] = %10xxxxxx)<br />
$E1CE : BIOS disk transfer (if [$0101] = %01xxxxxx)<br />
$E1D9 : BIOS disk skip bytes (if [$0101] = %00xxxxxx)<br />
<br />
NMI:<br />
($DFF6): disk game NMI vector #1 (if [$0100] = %01xxxxxx)<br />
($DFF8): disk game NMI vector #2 (if [$0100] = %10xxxxxx)<br />
($DFFA): disk game NMI vector #3 (if [$0100] = %11xxxxxx)<br />
$E19D : BIOS disable NMI (if [$0100] = %00xxxxxx)<br />
<br />
A few important notes :<br />
* After loading the boot files, $0102 is set to $35 so that the ($DFFC) vector is used and the BIOS is skipped.<br />
* $0103 indicate reset type : $AC = first boot of the game, $53 = the game was soft-reset by the user<br />
* To use a custom IRQ routine, $C0 should be manually written to $0101<br />
* There are 3 possible NMI vectors, #3 is used by default.<br />
* 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 have files loaded in.<br />
<br />
== BIOS calls ==<br />
<br />
=== Disk access routines ===<br />
<br />
* 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.<br />
* Memory at $00-$0F will be affected by those routines<br />
* Unlike the vast majority of disk drives, the FDS lacks any kind of intelligent tracking system. All BIOS load and save functions will do access to the whole disk, no matter which data they load/save. A simple way to overcome this problem is to have a custom loading routine, similar to the BIOS one but forcing the # of files to a smaller number than it actually is. That way the later files are not accessed at all and the earlier files load faster. Of course the maximal time is still taken when loading files that are late on the disk.<br />
* 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.<br />
* 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.<br />
* 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.<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Description<br />
|-<br />
| $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.<br />
|-<br />
| $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).<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $E301 || SetFileCount1 || Pointer to Disk ID, A = file count minus one = # of the last file || A = error # || Set the file count to A + 1<br />
|-<br />
| $E305 || SetFileCount || Pointer to Disk ID, A = file count || A = error # || Set the file count to A<br />
|-<br />
| $E32A || GetDiskInfo || Pointer to Disk Info || A = error # || Fills DiskInfo up with data read off the current disk.<br />
|-<br />
|}<br />
<br />
=== Low-Level Disk access routines ===<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Description<br />
|-<br />
| $E445 || CheckDiskHeader || Pointer to 10 byte string at $00 || || Compares the first 10 bytes on the disk coming after the FDS string, to 10 bytes pointed to by Ptr($00). To bypass the checking of any byte, a -1 can be placed in the equivelant place in the compare string. Otherwise, if the comparison fails, an appropriate error will be generated.<br />
|-<br />
| $E484 || GetNumFiles || || || Reads # of files stored on disk, stores the result in $06<br />
|-<br />
| $E492 || SetNumFiles || || A = # of files || Writes new number of files to disk header.<br />
|-<br />
| $E4A0 || FileMatchTest || Pointer to FileID list at $02 || || Uses a byte string pointed at by Ptr($02) to tell the disk system which files to load. The file ID's number is searched for in the string. If an exact match is found, [$09] is 0'd, and [$0E] is incremented. If no matches are found after 20 bytes, or a -1 entry is encountered, [$09] is set to -1. If the first byte in the string is -1, the BootID number is used for matching files (any FileID that is not greater than the BootID qualifies as a match).<br />
|-<br />
| $E4DA || SkipFiles || Number of files to skip in $06 || || Skips over specified number of files.<br />
|-<br />
|}<br />
<br />
=== Example code how to load files ===<br />
<br />
<pre><br />
Load:<br />
JSR LoadFiles<br />
.dw DiskID<br />
.dw LoadList<br />
BNE _Error ;Check if there is an error<br />
RTS<br />
_Error:<br />
JSR PrintError ;If so print the error number and message to screen (include side/disk changing prompts)<br />
_sideError:<br />
LDA $4032<br />
AND #$01<br />
BEQ _sideError ;Wait until disk is ejected<br />
_insert:<br />
LDA $4032<br />
AND #$01<br />
BNE _instert ;Wait until disk is inserted<br />
JMP Load<br />
<br />
DiskID:<br />
.db $01 ;Manufacturer code<br />
.db "NAME" ;4-letter code of game<br />
.db $00 ;Version<br />
.db $01 ;Disk side<br />
.db $00 ;Disk number<br />
.db $00, $00 ;Extra disk IDs<br />
<br />
LoadList ;In this example the files with IDs equal to $02, $03 or $04 will be loaded into memory<br />
.db $02, $03, $04, $FF<br />
</pre><br />
<br />
=== Error list ===<br />
<br />
Message in bold is the official BIOS message (if there is one) followed by an explanation<br />
*$00: no error<br />
*$01: '''disk set''', ($4032.0) disk not set<br />
*$02: '''battery''', ($4033.7) power supply failure<br />
*$03: ($4032.2) disk is write protected<br />
*$04: Wrong maker ID<br />
*$05: Wrong game<br />
*$06: Wrong game version<br />
*$07: '''a,b side''', wrong side number<br />
*$08: '''disk no.''', wrong disk number<br />
*$09: wrong additional disk ID 1<br />
*$0a: wrong additional disk ID 2<br />
*$20: '''disk trouble''', approval check failed<br />
*$21: '''disk trouble''', '*NINTENDO-HVC*' string in block 1 doesn't match<br />
*$22: '''disk trouble''', block type 1 expected<br />
*$23: '''disk trouble''', block type 2 expected<br />
*$24: '''disk trouble''', block type 3 expected<br />
*$25: '''disk trouble''', block type 4 expected<br />
*$27: '''disk trouble''', ($4030.4) block failed CRC<br />
*$28: '''disk trouble''', ($4030.6) file ends prematurely during read<br />
*$29: '''disk trouble''', ($4030.6) file ends prematurely during write<br />
*$30: '''disk trouble''', ($4032.1) disk is full<br />
<br />
=== Disk ID structure ===<br />
<br />
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.<br />
<br />
offset size error# description<br />
------ ---- ------ -----------<br />
0 1 $04 game manufacturer code<br />
1 4 $05 game ASCII name string<br />
5 1 $06 game version<br />
6 1 $07 disk side #<br />
7 1 $08 disk #<br />
8 1 $09 extra disk # data<br />
9 1 $10 extra disk # data<br />
A -<br />
<br />
=== File list structure ===<br />
<br />
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).<br />
<br />
Multiple files are loaded in the order as they exist on the disk, not in the order of the list.<br />
<br />
=== File header structure ===<br />
<br />
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.<br />
<br />
offset size description<br />
------ ---- -----------<br />
00 1 file ID code<br />
01 8 file name<br />
09 2 load address<br />
0B 2 file data size<br />
0D 1 file type ($00 : Program; $01 : Character; $02 : Nametable)<br />
0E 2 source address of file data (NOT written to disk)<br />
10 1 source address type ($00 : RAM, $01 : VRAM)<br />
11 -<br />
<br />
=== Disk information structure ===<br />
<br />
This is a data structure returned by a subroutine, of collected information <br />
from the disk (list of files on disk, disk size, etc.). The following table <br />
is a description of that structure.<br />
<br />
offset size<br />
------ ----<br />
0 1 game manufacturer code<br />
1 4 game ASCII name string<br />
5 1 game version<br />
6 1 disk side #<br />
7 1 disk #<br />
8 1 extra disk # data<br />
9 1 extra disk # data<br />
A 1 # of files on disk<br />
<br />
(the following block will appear for as many files as the "# of files on <br />
disk" byte indicates)<br />
<br />
B 1 file ID code<br />
C 8 file name (ASCII)<br />
<br />
(the following is present after the last file info block. Disk size is equal <br />
to the sum of each file's size entry, plus an extra 261 per file.)<br />
<br />
x 1 disk size high byte<br />
x+1 1 disk size low byte<br />
x+2 -<br />
<br />
== Other BIOS calls ==<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Affected RAM/Registers || Description<br />
|-<br />
| $e149 || Delay132 || || || || 132 clock cycle delay<br />
|-<br />
| $e153 || Delayms || || || X, Y || Delay routine, Y = delay in ms (approximate)<br />
|-<br />
| $e161 || DisPFObj || || || A, $fe || Disable sprites and background<br />
|-<br />
| $e16b || EnPFObj || || || A, $fe || Enable sprites and background<br />
|-<br />
| $e171 || DisObj || || || A, $fe || Disable sprites<br />
|-<br />
| $e178 || EnObj || || || A, $fe || Enable sprites<br />
|-<br />
| $e17e || DisPF || || || A, $fe || Disable background<br />
|-<br />
| $e185 || EnPF || || || A, $fe || Enable background<br />
|-<br />
| $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.<br />
|-<br />
| $e7bb || VRAMStructWrite || Pointer to VRAM buffer to be written || || A, X, Y, $00, $01, $ff || Set VRAM increment to 1 (clear [[PPUCTRL]]/$ff bit 2), and write a VRAM buffer to VRAM. Read below for information on the structure.<br />
|-<br />
| $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.<br />
|-<br />
| $e86a || WriteVRAMBuffer || || || A, X, Y, $301, $302 || Write the VRAM Buffer at $302 to VRAM. Read below for information on the structure.<br />
|-<br />
| $e8b3 || ReadVRAMBuffer || X = start address of read buffer, Y = # of bytes to read || || A, X, Y || Read individual bytes from VRAM to the VRAMBuffer. (see notes below)<br />
|-<br />
| $e8d2 || PrepareVRAMString || A = High VRAM address, X = Low VRAM address, 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 copies pointed data into the VRAM buffer.<br />
|-<br />
| $e8e1 || PrepareVRAMStrings || A = High VRAM address, X = Low VRAM address, Direct pointer = data to be written to VRAM || A = $ff : no error, A = $01 : data didn't fit in buffer || A, X, Y, $00, $01, $02, $03, $04, $05, $06 || This routine copies a 2D string into the VRAM buffer. The first byte of the data determines the width and height of the following string (in tiles): Upper nybble = height, lower nybble = width.<br />
|-<br />
| $e94f || GetVRAMBufferByte || X = starting index of read buffer, Y = # of address to compare (starting at 1), $00, $01 = address to read from || carry clear : a previously read byte was returned, carry set : no byte was read, should wait next call to ReadVRAMBuffer || A, X, Y || This routine was likely planned to be used in order to avoid useless latency on a VRAM reads (see notes below). It compares the VRAM address in ($00) with the Yth (starting at 1) address of the read buffer. If both addresses match, the corresponding data byte is returned exit with c clear. If the addresses are different, the buffer address is overwritten by the address in ($00) and the routine exit with c set.<br />
|-<br />
| $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 address (assumes no scrolling, and points to first nametable at $2000-$23ff).<br />
|-<br />
| $e997 || Nam2PixelConv || $00 = High nametable address, $01 = low nametable address || $02 = Pixel X cord, $03 = Pixel Y cord || A || This routine convert a nametable address to corresponding pixel coordinates (assume no scrolling).<br />
|-<br />
| $e9b1 || Random || X = Zero Page address 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 the program is 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''.<br />
|-<br />
| $e9c8 || SpriteDMA || || || A || This routine does sprite DMA from RAM $200-$2ff<br />
|-<br />
| $e9d3 || CounterLogic || A, Y = end Zeropage address of counters, X = start zeropage address 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.<br />
|-<br />
| $e9eb || ReadPads || || $f5 = Joypad #1 data, $f6 = Joypad #2 data || A, X, $00, $01, || This read hardwired famicom joypads.<br />
|-<br />
| $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<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $ea68 || ReadDownExpPads || $f1-$f4 = up->down transitions, $f5-$f8 = Joypad 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.<br />
|-<br />
| $ea84 || VRAMFill || A = High VRAM Address (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.<br />
|-<br />
| $ead2 || MemFill || A = fill value, X = first page #, Y = last page # || || A, X, Y, $00, $01 || This routines fills RAM pages with specified value.<br />
|-<br />
| $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<br />
|-<br />
| $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.<br />
|-<br />
| $eb13 || ReadKeyboard || || || || Read Family Basic Keyboard expansion (detail is under analysis)<br />
|-<br />
| $eb66 || LoadTileset || A = Low VRAM Address & Flags, Y = Hi VRAM Address, 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.<br />
|-<br />
| $ec22 || unk_EC22|| || || || Some kind of logic that some games use. (detail is under analysis)<br />
|}<br />
<br />
<br />
=== VRAM write transfer structure ===<br />
<br />
The structure of VRAM buffers are as follows:<br />
<br />
SIZE CONTENTS<br />
2 VRAM Address (big endian)<br />
1 bit 0-5 length of data ($0 means a length of 64)<br />
bit 6 : 0 = copy, 1 = fill<br />
bit 7 : 0 = increment by 1, 1 = increment by 32<br />
n Data to copy to VRAM<br />
..... repeated as many times as needed<br />
1 $ff<br />
<br />
* The main structure is terminated by a $ff byte (High address is always supposed to be in $00..$3f range)<br />
* $4c is a "call" command. The 2 bytes that follow is the address of a sub-VRAM structure. The sub-structure can call another sub-structure and so on.<br />
* $60 is a "return" command. It will terminate a sub-structure.<br />
* If Fill mode is used, the routine takes only 1 byte of data which is repeated.<br />
<br />
=== VRAM Buffer notes ===<br />
<br />
The VRAM buffer is located at $300-$3xx. $300 holds the size of the buffer (maximum), and $301 holds the end index of the buffer. The actual buffer lies at $302-$3xx, and is of variable length.<br />
<br />
* $300 is initialized to the 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 the biggest possible value is $fd, making the buffer lie at $300-$3ff.<br />
* Format of the buffer is equivalent to the VRAM structure above, except that there are no sub-structures, no increment by 32 flag and no fill flag.<br />
* For this reason, the VRAM buffer at $302 can be used as a sub-structure.<br />
* A call to WriteVRAMBuffer will execute faster than a call to VRAMStructWrite with $302 as an argument, but both will have the same effect.<br />
<br />
=== Read routines and VRAM buffer ===<br />
<br />
Unlike the write routines which are very complete, the read routines are somewhat incomplete and their functionality have to be completed by the user (which limits their usefulness). Most of what follows is some sort of speculation about the usefulness of the incomplete read routines of the BIOS.<br />
<br />
The read buffer is a part of the VRAM buffer at $300-$3xx, but the same location can't be used to transfer read and writes on the same frame (for example a RMW operation). Instead the user must manually split the buffer in two parts, the "read" buffer and the "write" buffer. The read buffer is probably supposed to always lies after the write buffer, so that when the read buffer is in use, the value in $300 (size of the write buffer) should be adjusted accordingly. The user should manually keep track of how many bytes are used for the read buffer, as well as the starting point of the read buffer (they are argument to the ReadVRAMBuffer function).<br />
<br />
The structure of the read buffer itself is trivial - only single bytes are read (there's no runs of data). Very likely the purpose is to read one or a few individual attribute table data, in order to change the color mapping of individual 2x2 squares instead of whole 4x4 square (read-modify-write operation).<br />
All reads are mapped to a structure of 3 bytes in the read buffer :<br />
SIZE CONTENTS<br />
2 VRAM Address (big endian)<br />
1 data<br />
<br />
Therfore, for each byte which is read from VRAM, 3 bytes have to be reserved in the read buffer.<br />
Once data from VRAM has been read, if it must be written back after a modification, the user need to copy it to the write buffer manually.<br />
<br />
The GetVRAMBufferByte was probably designed to prevent reading attribute tables when it has already been read in the past by a call to ReadVRAMBuffer. For example if you don't know if you're modifying another area of the same 4x4 attribute byte as previously (no need to read again), or if you're modifying part of another 4x4 attribute byte (need to read from VRAM). If the routine return with C=0, the accumulator contains the attribute byte that was already read, but if C=1, it means we should first call ReadVRAMBuffer before getting the data we want.<br />
<br />
Since reading VRAM for nothing is not a harmful operation, the user can call ReadVRAMBuffer every frame even if nobody has to be read (no need for check flags) which is probably why these routines are less complete than the write buffer related routines.<br />
<br />
=== Load Tileset notes ===<br />
<br />
The ''flags'' parameters are as follows:<br />
<br />
7 bit 0<br />
---------<br />
AAAA MMIT<br />
|||| ||||<br />
|||| |||+- Fill bit<br />
|||| ||+-- Transfer direction (0 = Write tiles, 1 = Read tiles)<br />
|||| ++--- Bitplane type (see below)<br />
++++------ Low VRAM Address (aka tile # within a row)<br />
<br />
1st bitplane 2nd bitplane Description<br />
----------- ----------- -----------<br />
0: data data+8 Normal 2-bitplane graphics<br />
1: data fill bit Single bitplane graphics. Fill bit clear : Use colors 0&1 Fill bit set : Use colors 2&3<br />
2: fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&2 Fill bit set : Use colors 1&3<br />
3: data^fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&3 Fill bit set : Use colors 1&2<br />
<br />
This makes it possible for single bitplane tiles to take all possible color schemes when they end up in VRAM. However, it is not possible to (natively) load single bitplane graphics directly from the disk into VRAM; it should be loaded into PRG-RAM before transferring the data into VRAM. In read mode, all non "data" bitplanes are replaced by dummy reads.</div>はるきhttps://www.nesdev.org/w/index.php?title=FDS_BIOS&diff=2529FDS BIOS2020-02-15T22:01:45Z<p>はるき: Formatting</p>
<hr />
<div>The [[Famicom Disk System]] 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]].<br />
<br />
== Pseudo-registers ==<br />
The FDS BIOS uses several bytes on the zero-page. 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.<br />
<br />
[$FF]: value last written to [[PPUCTRL|$2000]] $80 on reset.<br />
[$FE]: value last written to [[PPUMASK|$2001]] $06 on reset<br />
[$FD]: value last written to [[PPUSCROLL|$2005/1]] $00 on reset.<br />
[$FC]: value last written to [[PPUSCROLL|$2005/2]] $00 on reset.<br />
[$FB]: value last written to [[Controller reading|$4016]] $00 on reset.<br />
[$FA]: value last written to [[Family_Computer_Disk_System#FDS_Control_.28.244025.29|$4025]] $2E on reset.<br />
[$F9]: value last written to [[Family_Computer_Disk_System#External_connector_.28.244026.29|$4026]] $FF on reset.<br />
$F5..$F8 : Used by controller read routines<br />
$00..$0F is used as temporary memory for the BIOS. The main program can use it as temporary memory too.<br />
<br />
The FDS BIOS also uses 4 bytes at the lower end of the stack page to control behaviour of interrupt/reset vectors:<br />
<br />
[$0102]/[$0103]: PC action on reset<br />
[$0101]: PC action on IRQ. set to $80 on reset<br />
[$0100]: PC action on NMI. set to $C0 on reset<br />
<br />
RESET:<br />
($DFFC): disk game reset vector (if [$0102] = $35, and [$0103] = $53 or $AC)<br />
<br />
IRQ:<br />
($DFFE): disk game IRQ vector (if [$0101] = %11xxxxxx)<br />
$E1EF : BIOS acknowledge and delay (if [$0101] = %10xxxxxx)<br />
$E1CE : BIOS disk transfer (if [$0101] = %01xxxxxx)<br />
$E1D9 : BIOS disk skip bytes (if [$0101] = %00xxxxxx)<br />
<br />
NMI:<br />
($DFF6): disk game NMI vector #1 (if [$0100] = %01xxxxxx)<br />
($DFF8): disk game NMI vector #2 (if [$0100] = %10xxxxxx)<br />
($DFFA): disk game NMI vector #3 (if [$0100] = %11xxxxxx)<br />
$E19D : BIOS disable NMI (if [$0100] = %00xxxxxx)<br />
<br />
A few important notes :<br />
* After loading the boot files, $0102 is set to $35 so that the ($DFFC) vector is used and the BIOS is skipped.<br />
* $0103 indicate reset type : $AC = first boot of the game, $53 = the game was soft-reset by the user<br />
* To use a custom IRQ routine, $C0 should be manually written to $0101<br />
* There are 3 possible NMI vectors, #3 is used by default.<br />
* 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 have files loaded in.<br />
<br />
== BIOS calls ==<br />
<br />
=== Disk access routines ===<br />
<br />
* 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.<br />
* Memory at $00-$0F will be affected by those routines<br />
* Unlike the vast majority of disk drives, the FDS lacks any kind of intelligent tracking system. All BIOS load and save functions will do access to the whole disk, no matter which data they load/save. A simple way to overcome this problem is to have a custom loading routine, similar to the BIOS one but forcing the # of files to a smaller number than it actually is. That way the later files are not accessed at all and the earlier files load faster. Of course the maximal time is still taken when loading files that are late on the disk.<br />
* 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.<br />
* 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.<br />
* 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.<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Description<br />
|-<br />
| $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.<br />
|-<br />
| $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).<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $E301 || SetFileCount1 || Pointer to Disk ID, A = file count minus one = # of the last file || A = error # || Set the file count to A + 1<br />
|-<br />
| $E305 || SetFileCount || Pointer to Disk ID, A = file count || A = error # || Set the file count to A<br />
|-<br />
| $E32A || GetDiskInfo || Pointer to Disk Info || A = error # || Fills DiskInfo up with data read off the current disk.<br />
|-<br />
|}<br />
<br />
=== Low-Level Disk access routines ===<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Description<br />
|-<br />
| $E445 || CheckDiskHeader || Pointer to 10 byte string at $00 || || Compares the first 10 bytes on the disk coming after the FDS string, to 10 bytes pointed to by Ptr($00). To bypass the checking of any byte, a -1 can be placed in the equivelant place in the compare string. Otherwise, if the comparison fails, an appropriate error will be generated.<br />
|-<br />
| $E484 || GetNumFiles || || || Reads # of files stored on disk, stores the result in $06<br />
|-<br />
| $E492 || SetNumFiles || || A = # of files || Writes new number of files to disk header.<br />
|-<br />
| $E4A0 || FileMatchTest || Pointer to FileID list at $02 || || Uses a byte string pointed at by Ptr($02) to tell the disk system which files to load. The file ID's number is searched for in the string. If an exact match is found, [$09] is 0'd, and [$0E] is incremented. If no matches are found after 20 bytes, or a -1 entry is encountered, [$09] is set to -1. If the first byte in the string is -1, the BootID number is used for matching files (any FileID that is not greater than the BootID qualifies as a match).<br />
|-<br />
| $E4DA || SkipFiles || Number of files to skip in $06 || || Skips over specified number of files.<br />
|-<br />
|}<br />
<br />
=== Example code how to load files ===<br />
<br />
<pre><br />
Load<br />
jsr LoadFiles<br />
.dw DiskID<br />
.dw LoadList<br />
bne _Error ;Check if there is an error<br />
rts<br />
_Error<br />
jsr PrintError ;If so print the error number and message to screen (include side/disk changing prompts)<br />
_sideError<br />
lda $4032<br />
and #$01<br />
beq _sideError ;Wait until disk is ejected<br />
_insert<br />
lda $4032<br />
and #$01<br />
bne _instert ;Wait until disk is inserted<br />
jmp Load<br />
<br />
DiskID<br />
.db $01 ;Manufacturer code<br />
.db "NAME" ;4-letter code of game<br />
.db $00 ;Version<br />
.db $01 ;Disk side<br />
.db $00 ;Disk number<br />
.db $00, $00 ;Extra disk IDs<br />
<br />
LoadList ;In this example the files with IDs equal to $02, $03 or $04 will be loaded into memory<br />
.db $02, $03, $04, $ff<br />
</pre><br />
<br />
=== Error list ===<br />
<br />
Message in bold is the official BIOS message (if there is one) followed by an explanation<br />
*$00: no error<br />
*$01: '''disk set''', ($4032.0) disk not set<br />
*$02: '''battery''', ($4033.7) power supply failure<br />
*$03: ($4032.2) disk is write protected<br />
*$04: Wrong maker ID<br />
*$05: Wrong game<br />
*$06: Wrong game version<br />
*$07: '''a,b side''', wrong side number<br />
*$08: '''disk no.''', wrong disk number<br />
*$09: wrong additional disk ID 1<br />
*$0a: wrong additional disk ID 2<br />
*$20: '''disk trouble''', approval check failed<br />
*$21: '''disk trouble''', '*NINTENDO-HVC*' string in block 1 doesn't match<br />
*$22: '''disk trouble''', block type 1 expected<br />
*$23: '''disk trouble''', block type 2 expected<br />
*$24: '''disk trouble''', block type 3 expected<br />
*$25: '''disk trouble''', block type 4 expected<br />
*$27: '''disk trouble''', ($4030.4) block failed CRC<br />
*$28: '''disk trouble''', ($4030.6) file ends prematurely during read<br />
*$29: '''disk trouble''', ($4030.6) file ends prematurely during write<br />
*$30: '''disk trouble''', ($4032.1) disk is full<br />
<br />
=== Disk ID structure ===<br />
<br />
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.<br />
<br />
offset size error# description<br />
------ ---- ------ -----------<br />
0 1 $04 game manufacturer code<br />
1 4 $05 game ASCII name string<br />
5 1 $06 game version<br />
6 1 $07 disk side #<br />
7 1 $08 disk #<br />
8 1 $09 extra disk # data<br />
9 1 $10 extra disk # data<br />
A -<br />
<br />
=== File list structure ===<br />
<br />
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).<br />
<br />
Multiple files are loaded in the order as they exist on the disk, not in the order of the list.<br />
<br />
=== File header structure ===<br />
<br />
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.<br />
<br />
offset size description<br />
------ ---- -----------<br />
00 1 file ID code<br />
01 8 file name<br />
09 2 load address<br />
0B 2 file data size<br />
0D 1 file type ($00 : Program; $01 : Character; $02 : Nametable)<br />
0E 2 source address of file data (NOT written to disk)<br />
10 1 source address type ($00 : RAM, $01 : VRAM)<br />
11 -<br />
<br />
=== Disk information structure ===<br />
<br />
This is a data structure returned by a subroutine, of collected information <br />
from the disk (list of files on disk, disk size, etc.). The following table <br />
is a description of that structure.<br />
<br />
offset size<br />
------ ----<br />
0 1 game manufacturer code<br />
1 4 game ASCII name string<br />
5 1 game version<br />
6 1 disk side #<br />
7 1 disk #<br />
8 1 extra disk # data<br />
9 1 extra disk # data<br />
A 1 # of files on disk<br />
<br />
(the following block will appear for as many files as the "# of files on <br />
disk" byte indicates)<br />
<br />
B 1 file ID code<br />
C 8 file name (ASCII)<br />
<br />
(the following is present after the last file info block. Disk size is equal <br />
to the sum of each file's size entry, plus an extra 261 per file.)<br />
<br />
x 1 disk size high byte<br />
x+1 1 disk size low byte<br />
x+2 -<br />
<br />
== Other BIOS calls ==<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Affected RAM/Registers || Description<br />
|-<br />
| $e149 || Delay132 || || || || 132 clock cycle delay<br />
|-<br />
| $e153 || Delayms || || || X, Y || Delay routine, Y = delay in ms (approximate)<br />
|-<br />
| $e161 || DisPFObj || || || A, $fe || Disable sprites and background<br />
|-<br />
| $e16b || EnPFObj || || || A, $fe || Enable sprites and background<br />
|-<br />
| $e171 || DisObj || || || A, $fe || Disable sprites<br />
|-<br />
| $e178 || EnObj || || || A, $fe || Enable sprites<br />
|-<br />
| $e17e || DisPF || || || A, $fe || Disable background<br />
|-<br />
| $e185 || EnPF || || || A, $fe || Enable background<br />
|-<br />
| $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.<br />
|-<br />
| $e7bb || VRAMStructWrite || Pointer to VRAM buffer to be written || || A, X, Y, $00, $01, $ff || Set VRAM increment to 1 (clear [[PPUCTRL]]/$ff bit 2), and write a VRAM buffer to VRAM. Read below for information on the structure.<br />
|-<br />
| $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.<br />
|-<br />
| $e86a || WriteVRAMBuffer || || || A, X, Y, $301, $302 || Write the VRAM Buffer at $302 to VRAM. Read below for information on the structure.<br />
|-<br />
| $e8b3 || ReadVRAMBuffer || X = start address of read buffer, Y = # of bytes to read || || A, X, Y || Read individual bytes from VRAM to the VRAMBuffer. (see notes below)<br />
|-<br />
| $e8d2 || PrepareVRAMString || A = High VRAM address, X = Low VRAM address, 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 copies pointed data into the VRAM buffer.<br />
|-<br />
| $e8e1 || PrepareVRAMStrings || A = High VRAM address, X = Low VRAM address, Direct pointer = data to be written to VRAM || A = $ff : no error, A = $01 : data didn't fit in buffer || A, X, Y, $00, $01, $02, $03, $04, $05, $06 || This routine copies a 2D string into the VRAM buffer. The first byte of the data determines the width and height of the following string (in tiles): Upper nybble = height, lower nybble = width.<br />
|-<br />
| $e94f || GetVRAMBufferByte || X = starting index of read buffer, Y = # of address to compare (starting at 1), $00, $01 = address to read from || carry clear : a previously read byte was returned, carry set : no byte was read, should wait next call to ReadVRAMBuffer || A, X, Y || This routine was likely planned to be used in order to avoid useless latency on a VRAM reads (see notes below). It compares the VRAM address in ($00) with the Yth (starting at 1) address of the read buffer. If both addresses match, the corresponding data byte is returned exit with c clear. If the addresses are different, the buffer address is overwritten by the address in ($00) and the routine exit with c set.<br />
|-<br />
| $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 address (assumes no scrolling, and points to first nametable at $2000-$23ff).<br />
|-<br />
| $e997 || Nam2PixelConv || $00 = High nametable address, $01 = low nametable address || $02 = Pixel X cord, $03 = Pixel Y cord || A || This routine convert a nametable address to corresponding pixel coordinates (assume no scrolling).<br />
|-<br />
| $e9b1 || Random || X = Zero Page address 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 the program is 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''.<br />
|-<br />
| $e9c8 || SpriteDMA || || || A || This routine does sprite DMA from RAM $200-$2ff<br />
|-<br />
| $e9d3 || CounterLogic || A, Y = end Zeropage address of counters, X = start zeropage address 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.<br />
|-<br />
| $e9eb || ReadPads || || $f5 = Joypad #1 data, $f6 = Joypad #2 data || A, X, $00, $01, || This read hardwired famicom joypads.<br />
|-<br />
| $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<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $ea68 || ReadDownExpPads || $f1-$f4 = up->down transitions, $f5-$f8 = Joypad 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.<br />
|-<br />
| $ea84 || VRAMFill || A = High VRAM Address (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.<br />
|-<br />
| $ead2 || MemFill || A = fill value, X = first page #, Y = last page # || || A, X, Y, $00, $01 || This routines fills RAM pages with specified value.<br />
|-<br />
| $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<br />
|-<br />
| $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.<br />
|-<br />
| $eb13 || ReadKeyboard || || || || Read Family Basic Keyboard expansion (detail is under analysis)<br />
|-<br />
| $eb66 || LoadTileset || A = Low VRAM Address & Flags, Y = Hi VRAM Address, 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.<br />
|-<br />
| $ec22 || unk_EC22|| || || || Some kind of logic that some games use. (detail is under analysis)<br />
|}<br />
<br />
<br />
=== VRAM write transfer structure ===<br />
<br />
The structure of VRAM buffers are as follows:<br />
<br />
SIZE CONTENTS<br />
2 VRAM Address (big endian)<br />
1 bit 0-5 length of data ($0 means a length of 64)<br />
bit 6 : 0 = copy, 1 = fill<br />
bit 7 : 0 = increment by 1, 1 = increment by 32<br />
n Data to copy to VRAM<br />
..... repeated as many times as needed<br />
1 $ff<br />
<br />
* The main structure is terminated by a $ff byte (High address is always supposed to be in $00..$3f range)<br />
* $4c is a "call" command. The 2 bytes that follow is the address of a sub-VRAM structure. The sub-structure can call another sub-structure and so on.<br />
* $60 is a "return" command. It will terminate a sub-structure.<br />
* If Fill mode is used, the routine takes only 1 byte of data which is repeated.<br />
<br />
=== VRAM Buffer notes ===<br />
<br />
The VRAM buffer is located at $300-$3xx. $300 holds the size of the buffer (maximum), and $301 holds the end index of the buffer. The actual buffer lies at $302-$3xx, and is of variable length.<br />
<br />
* $300 is initialized to the 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 the biggest possible value is $fd, making the buffer lie at $300-$3ff.<br />
* Format of the buffer is equivalent to the VRAM structure above, except that there are no sub-structures, no increment by 32 flag and no fill flag.<br />
* For this reason, the VRAM buffer at $302 can be used as a sub-structure.<br />
* A call to WriteVRAMBuffer will execute faster than a call to VRAMStructWrite with $302 as an argument, but both will have the same effect.<br />
<br />
=== Read routines and VRAM buffer ===<br />
<br />
Unlike the write routines which are very complete, the read routines are somewhat incomplete and their functionality have to be completed by the user (which limits their usefulness). Most of what follows is some sort of speculation about the usefulness of the incomplete read routines of the BIOS.<br />
<br />
The read buffer is a part of the VRAM buffer at $300-$3xx, but the same location can't be used to transfer read and writes on the same frame (for example a RMW operation). Instead the user must manually split the buffer in two parts, the "read" buffer and the "write" buffer. The read buffer is probably supposed to always lies after the write buffer, so that when the read buffer is in use, the value in $300 (size of the write buffer) should be adjusted accordingly. The user should manually keep track of how many bytes are used for the read buffer, as well as the starting point of the read buffer (they are argument to the ReadVRAMBuffer function).<br />
<br />
The structure of the read buffer itself is trivial - only single bytes are read (there's no runs of data). Very likely the purpose is to read one or a few individual attribute table data, in order to change the color mapping of individual 2x2 squares instead of whole 4x4 square (read-modify-write operation).<br />
All reads are mapped to a structure of 3 bytes in the read buffer :<br />
SIZE CONTENTS<br />
2 VRAM Address (big endian)<br />
1 data<br />
<br />
Therfore, for each byte which is read from VRAM, 3 bytes have to be reserved in the read buffer.<br />
Once data from VRAM has been read, if it must be written back after a modification, the user need to copy it to the write buffer manually.<br />
<br />
The GetVRAMBufferByte was probably designed to prevent reading attribute tables when it has already been read in the past by a call to ReadVRAMBuffer. For example if you don't know if you're modifying another area of the same 4x4 attribute byte as previously (no need to read again), or if you're modifying part of another 4x4 attribute byte (need to read from VRAM). If the routine return with C=0, the accumulator contains the attribute byte that was already read, but if C=1, it means we should first call ReadVRAMBuffer before getting the data we want.<br />
<br />
Since reading VRAM for nothing is not a harmful operation, the user can call ReadVRAMBuffer every frame even if nobody has to be read (no need for check flags) which is probably why these routines are less complete than the write buffer related routines.<br />
<br />
=== Load Tileset notes ===<br />
<br />
The ''flags'' parameters are as follows:<br />
<br />
7 bit 0<br />
---------<br />
AAAA MMIT<br />
|||| ||||<br />
|||| |||+- Fill bit<br />
|||| ||+-- Transfer direction (0 = Write tiles, 1 = Read tiles)<br />
|||| ++--- Bitplane type (see below)<br />
++++------ Low VRAM Address (aka tile # within a row)<br />
<br />
1st bitplane 2nd bitplane Description<br />
----------- ----------- -----------<br />
0: data data+8 Normal 2-bitplane graphics<br />
1: data fill bit Single bitplane graphics. Fill bit clear : Use colors 0&1 Fill bit set : Use colors 2&3<br />
2: fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&2 Fill bit set : Use colors 1&3<br />
3: data^fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&3 Fill bit set : Use colors 1&2<br />
<br />
This makes it possible for single bitplane tiles to take all possible color schemes when they end up in VRAM. However, it is not possible to (natively) load single bitplane graphics directly from the disk into VRAM; it should be loaded into PRG-RAM before transferring the data into VRAM. In read mode, all non "data" bitplanes are replaced by dummy reads.</div>はるきhttps://www.nesdev.org/w/index.php?title=FDS_BIOS&diff=2528FDS BIOS2020-02-15T22:00:42Z<p>はるき: Formatting</p>
<hr />
<div>The [[Famicom Disk System]] 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]].<br />
<br />
== Pseudo-registers ==<br />
The FDS BIOS uses several bytes on the zero-page. 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.<br />
<br />
[$FF]: value last written to [[PPUCTRL|$2000]] $80 on reset.<br />
[$FE]: value last written to [[PPUMASK|$2001]] $06 on reset<br />
[$FD]: value last written to [[PPUSCROLL|$2005/1]] $00 on reset.<br />
[$FC]: value last written to [[PPUSCROLL|$2005/2]] $00 on reset.<br />
[$FB]: value last written to [[Controller reading|$4016]] $00 on reset.<br />
[$FA]: value last written to [[Family_Computer_Disk_System#FDS_Control_.28.244025.29|$4025]] $2E on reset.<br />
[$F9]: value last written to [[Family_Computer_Disk_System#External_connector_.28.244026.29|$4026]] $FF on reset.<br />
$F5..$F8 : Used by controller read routines<br />
$00..$0F is used as temporary memory for the BIOS. The main program can use it as temporary memory too.<br />
<br />
The FDS BIOS also uses 4 bytes at the lower end of the stack page to control behaviour of interrupt/reset vectors:<br />
<br />
[$0102]/[$0103]: PC action on reset<br />
[$0101]: PC action on IRQ. set to $80 on reset<br />
[$0100]: PC action on NMI. set to $C0 on reset<br />
<br />
RESET:<br />
($DFFC): disk game reset vector (if [$0102] = $35, and [$0103] = $53 or $AC)<br />
<br />
IRQ:<br />
($DFFE): disk game IRQ vector (if [$0101] = %11xxxxxx)<br />
$E1EF : BIOS acknowledge and delay (if [$0101] = %10xxxxxx)<br />
$E1CE : BIOS disk transfer (if [$0101] = %01xxxxxx)<br />
$E1D9 : BIOS disk skip bytes (if [$0101] = %00xxxxxx)<br />
<br />
NMI:<br />
($DFF6): disk game NMI vector #1 (if [$0100] = %01xxxxxx)<br />
($DFF8): disk game NMI vector #2 (if [$0100] = %10xxxxxx)<br />
($DFFA): disk game NMI vector #3 (if [$0100] = %11xxxxxx)<br />
$E19D : BIOS disable NMI (if [$0100] = %00xxxxxx)<br />
<br />
A few important notes :<br />
* After loading the boot files, $0102 is set to $35 so that the ($DFFC) vector is used and the BIOS is skipped.<br />
* $0103 indicate reset type : $AC = first boot of the game, $53 = the game was soft-reset by the user<br />
* To use a custom IRQ routine, $C0 should be manually written to $0101<br />
* There are 3 possible NMI vectors, #3 is used by default.<br />
* 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 have files loaded in.<br />
<br />
== BIOS calls ==<br />
<br />
=== Disk access routines ===<br />
<br />
* 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.<br />
* Memory at $00-$0F will be affected by those routines<br />
* Unlike the vast majority of disk drives, the FDS lacks any kind of intelligent tracking system. All BIOS load and save functions will do access to the whole disk, no matter which data they load/save. A simple way to overcome this problem is to have a custom loading routine, similar to the BIOS one but forcing the # of files to a smaller number than it actually is. That way the later files are not accessed at all and the earlier files load faster. Of course the maximal time is still taken when loading files that are late on the disk.<br />
* 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.<br />
* 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.<br />
* 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.<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Description<br />
|-<br />
| $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.<br />
|-<br />
| $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).<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $E301 || SetFileCount1 || Pointer to Disk ID, A = file count minus one = # of the last file || A = error # || Set the file count to A + 1<br />
|-<br />
| $E305 || SetFileCount || Pointer to Disk ID, A = file count || A = error # || Set the file count to A<br />
|-<br />
| $E32A || GetDiskInfo || Pointer to Disk Info || A = error # || Fills DiskInfo up with data read off the current disk.<br />
|-<br />
|}<br />
<br />
=== Low-Level Disk access routines ===<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Description<br />
|-<br />
| $e445 || CheckDiskHeader || Pointer to 10 byte string at $00 || || Compares the first 10 bytes on the disk coming after the FDS string, to 10 bytes pointed to by Ptr($00). To bypass the checking of any byte, a -1 can be placed in the equivelant place in the compare string. Otherwise, if the comparison fails, an appropriate error will be generated.<br />
|-<br />
| $e484 || GetNumFiles || || || Reads number of files stored on disk, stores the result in $06<br />
|-<br />
| $e492 || SetNumFiles || || A = number of files || Writes new number of files to disk header.<br />
|-<br />
| $e4a0 || FileMatchTest || Pointer to FileID list at $02 || || Uses a byte string pointed at by Ptr($02) to tell the disk system which files to load. The file ID's number is searched for in the string. If an exact match is found, [$09] is 0'd, and [$0E] is incremented. If no matches are found after 20 bytes, or a -1 entry is encountered, [$09] is set to -1. If the first byte in the string is -1, the BootID number is used for matching files (any FileID that is not greater than the BootID qualifies as a match).<br />
|-<br />
| $e4da || SkipFiles || Number of files to skip in $06 || || Skips over specified number of files.<br />
|-<br />
|}<br />
<br />
=== Example code how to load files ===<br />
<br />
<pre><br />
Load<br />
jsr LoadFiles<br />
.dw DiskID<br />
.dw LoadList<br />
bne _Error ;Check if there is an error<br />
rts<br />
_Error<br />
jsr PrintError ;If so print the error number and message to screen (include side/disk changing prompts)<br />
_sideError<br />
lda $4032<br />
and #$01<br />
beq _sideError ;Wait until disk is ejected<br />
_insert<br />
lda $4032<br />
and #$01<br />
bne _instert ;Wait until disk is inserted<br />
jmp Load<br />
<br />
DiskID<br />
.db $01 ;Manufacturer code<br />
.db "NAME" ;4-letter code of game<br />
.db $00 ;Version<br />
.db $01 ;Disk side<br />
.db $00 ;Disk number<br />
.db $00, $00 ;Extra disk IDs<br />
<br />
LoadList ;In this example the files with IDs equal to $02, $03 or $04 will be loaded into memory<br />
.db $02, $03, $04, $ff<br />
</pre><br />
<br />
=== Error list ===<br />
<br />
Message in bold is the official BIOS message (if there is one) followed by an explanation<br />
*$00: no error<br />
*$01: '''disk set''', ($4032.0) disk not set<br />
*$02: '''battery''', ($4033.7) power supply failure<br />
*$03: ($4032.2) disk is write protected<br />
*$04: Wrong maker ID<br />
*$05: Wrong game<br />
*$06: Wrong game version<br />
*$07: '''a,b side''', wrong side number<br />
*$08: '''disk no.''', wrong disk number<br />
*$09: wrong additional disk ID 1<br />
*$0a: wrong additional disk ID 2<br />
*$20: '''disk trouble''', approval check failed<br />
*$21: '''disk trouble''', '*NINTENDO-HVC*' string in block 1 doesn't match<br />
*$22: '''disk trouble''', block type 1 expected<br />
*$23: '''disk trouble''', block type 2 expected<br />
*$24: '''disk trouble''', block type 3 expected<br />
*$25: '''disk trouble''', block type 4 expected<br />
*$27: '''disk trouble''', ($4030.4) block failed CRC<br />
*$28: '''disk trouble''', ($4030.6) file ends prematurely during read<br />
*$29: '''disk trouble''', ($4030.6) file ends prematurely during write<br />
*$30: '''disk trouble''', ($4032.1) disk is full<br />
<br />
=== Disk ID structure ===<br />
<br />
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.<br />
<br />
offset size error# description<br />
------ ---- ------ -----------<br />
0 1 $04 game manufacturer code<br />
1 4 $05 game ASCII name string<br />
5 1 $06 game version<br />
6 1 $07 disk side #<br />
7 1 $08 disk #<br />
8 1 $09 extra disk # data<br />
9 1 $10 extra disk # data<br />
A -<br />
<br />
=== File list structure ===<br />
<br />
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).<br />
<br />
Multiple files are loaded in the order as they exist on the disk, not in the order of the list.<br />
<br />
=== File header structure ===<br />
<br />
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.<br />
<br />
offset size description<br />
------ ---- -----------<br />
00 1 file ID code<br />
01 8 file name<br />
09 2 load address<br />
0B 2 file data size<br />
0D 1 file type ($00 : Program; $01 : Character; $02 : Nametable)<br />
0E 2 source address of file data (NOT written to disk)<br />
10 1 source address type ($00 : RAM, $01 : VRAM)<br />
11 -<br />
<br />
=== Disk information structure ===<br />
<br />
This is a data structure returned by a subroutine, of collected information <br />
from the disk (list of files on disk, disk size, etc.). The following table <br />
is a description of that structure.<br />
<br />
offset size<br />
------ ----<br />
0 1 game manufacturer code<br />
1 4 game ASCII name string<br />
5 1 game version<br />
6 1 disk side #<br />
7 1 disk #<br />
8 1 extra disk # data<br />
9 1 extra disk # data<br />
A 1 # of files on disk<br />
<br />
(the following block will appear for as many files as the "# of files on <br />
disk" byte indicates)<br />
<br />
B 1 file ID code<br />
C 8 file name (ASCII)<br />
<br />
(the following is present after the last file info block. Disk size is equal <br />
to the sum of each file's size entry, plus an extra 261 per file.)<br />
<br />
x 1 disk size high byte<br />
x+1 1 disk size low byte<br />
x+2 -<br />
<br />
== Other BIOS calls ==<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Affected RAM/Registers || Description<br />
|-<br />
| $e149 || Delay132 || || || || 132 clock cycle delay<br />
|-<br />
| $e153 || Delayms || || || X, Y || Delay routine, Y = delay in ms (approximate)<br />
|-<br />
| $e161 || DisPFObj || || || A, $fe || Disable sprites and background<br />
|-<br />
| $e16b || EnPFObj || || || A, $fe || Enable sprites and background<br />
|-<br />
| $e171 || DisObj || || || A, $fe || Disable sprites<br />
|-<br />
| $e178 || EnObj || || || A, $fe || Enable sprites<br />
|-<br />
| $e17e || DisPF || || || A, $fe || Disable background<br />
|-<br />
| $e185 || EnPF || || || A, $fe || Enable background<br />
|-<br />
| $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.<br />
|-<br />
| $e7bb || VRAMStructWrite || Pointer to VRAM buffer to be written || || A, X, Y, $00, $01, $ff || Set VRAM increment to 1 (clear [[PPUCTRL]]/$ff bit 2), and write a VRAM buffer to VRAM. Read below for information on the structure.<br />
|-<br />
| $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.<br />
|-<br />
| $e86a || WriteVRAMBuffer || || || A, X, Y, $301, $302 || Write the VRAM Buffer at $302 to VRAM. Read below for information on the structure.<br />
|-<br />
| $e8b3 || ReadVRAMBuffer || X = start address of read buffer, Y = # of bytes to read || || A, X, Y || Read individual bytes from VRAM to the VRAMBuffer. (see notes below)<br />
|-<br />
| $e8d2 || PrepareVRAMString || A = High VRAM address, X = Low VRAM address, 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 copies pointed data into the VRAM buffer.<br />
|-<br />
| $e8e1 || PrepareVRAMStrings || A = High VRAM address, X = Low VRAM address, Direct pointer = data to be written to VRAM || A = $ff : no error, A = $01 : data didn't fit in buffer || A, X, Y, $00, $01, $02, $03, $04, $05, $06 || This routine copies a 2D string into the VRAM buffer. The first byte of the data determines the width and height of the following string (in tiles): Upper nybble = height, lower nybble = width.<br />
|-<br />
| $e94f || GetVRAMBufferByte || X = starting index of read buffer, Y = # of address to compare (starting at 1), $00, $01 = address to read from || carry clear : a previously read byte was returned, carry set : no byte was read, should wait next call to ReadVRAMBuffer || A, X, Y || This routine was likely planned to be used in order to avoid useless latency on a VRAM reads (see notes below). It compares the VRAM address in ($00) with the Yth (starting at 1) address of the read buffer. If both addresses match, the corresponding data byte is returned exit with c clear. If the addresses are different, the buffer address is overwritten by the address in ($00) and the routine exit with c set.<br />
|-<br />
| $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 address (assumes no scrolling, and points to first nametable at $2000-$23ff).<br />
|-<br />
| $e997 || Nam2PixelConv || $00 = High nametable address, $01 = low nametable address || $02 = Pixel X cord, $03 = Pixel Y cord || A || This routine convert a nametable address to corresponding pixel coordinates (assume no scrolling).<br />
|-<br />
| $e9b1 || Random || X = Zero Page address 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 the program is 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''.<br />
|-<br />
| $e9c8 || SpriteDMA || || || A || This routine does sprite DMA from RAM $200-$2ff<br />
|-<br />
| $e9d3 || CounterLogic || A, Y = end Zeropage address of counters, X = start zeropage address 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.<br />
|-<br />
| $e9eb || ReadPads || || $f5 = Joypad #1 data, $f6 = Joypad #2 data || A, X, $00, $01, || This read hardwired famicom joypads.<br />
|-<br />
| $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<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $ea68 || ReadDownExpPads || $f1-$f4 = up->down transitions, $f5-$f8 = Joypad 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.<br />
|-<br />
| $ea84 || VRAMFill || A = High VRAM Address (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.<br />
|-<br />
| $ead2 || MemFill || A = fill value, X = first page #, Y = last page # || || A, X, Y, $00, $01 || This routines fills RAM pages with specified value.<br />
|-<br />
| $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<br />
|-<br />
| $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.<br />
|-<br />
| $eb13 || ReadKeyboard || || || || Read Family Basic Keyboard expansion (detail is under analysis)<br />
|-<br />
| $eb66 || LoadTileset || A = Low VRAM Address & Flags, Y = Hi VRAM Address, 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.<br />
|-<br />
| $ec22 || unk_EC22|| || || || Some kind of logic that some games use. (detail is under analysis)<br />
|}<br />
<br />
<br />
=== VRAM write transfer structure ===<br />
<br />
The structure of VRAM buffers are as follows:<br />
<br />
SIZE CONTENTS<br />
2 VRAM Address (big endian)<br />
1 bit 0-5 length of data ($0 means a length of 64)<br />
bit 6 : 0 = copy, 1 = fill<br />
bit 7 : 0 = increment by 1, 1 = increment by 32<br />
n Data to copy to VRAM<br />
..... repeated as many times as needed<br />
1 $ff<br />
<br />
* The main structure is terminated by a $ff byte (High address is always supposed to be in $00..$3f range)<br />
* $4c is a "call" command. The 2 bytes that follow is the address of a sub-VRAM structure. The sub-structure can call another sub-structure and so on.<br />
* $60 is a "return" command. It will terminate a sub-structure.<br />
* If Fill mode is used, the routine takes only 1 byte of data which is repeated.<br />
<br />
=== VRAM Buffer notes ===<br />
<br />
The VRAM buffer is located at $300-$3xx. $300 holds the size of the buffer (maximum), and $301 holds the end index of the buffer. The actual buffer lies at $302-$3xx, and is of variable length.<br />
<br />
* $300 is initialized to the 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 the biggest possible value is $fd, making the buffer lie at $300-$3ff.<br />
* Format of the buffer is equivalent to the VRAM structure above, except that there are no sub-structures, no increment by 32 flag and no fill flag.<br />
* For this reason, the VRAM buffer at $302 can be used as a sub-structure.<br />
* A call to WriteVRAMBuffer will execute faster than a call to VRAMStructWrite with $302 as an argument, but both will have the same effect.<br />
<br />
=== Read routines and VRAM buffer ===<br />
<br />
Unlike the write routines which are very complete, the read routines are somewhat incomplete and their functionality have to be completed by the user (which limits their usefulness). Most of what follows is some sort of speculation about the usefulness of the incomplete read routines of the BIOS.<br />
<br />
The read buffer is a part of the VRAM buffer at $300-$3xx, but the same location can't be used to transfer read and writes on the same frame (for example a RMW operation). Instead the user must manually split the buffer in two parts, the "read" buffer and the "write" buffer. The read buffer is probably supposed to always lies after the write buffer, so that when the read buffer is in use, the value in $300 (size of the write buffer) should be adjusted accordingly. The user should manually keep track of how many bytes are used for the read buffer, as well as the starting point of the read buffer (they are argument to the ReadVRAMBuffer function).<br />
<br />
The structure of the read buffer itself is trivial - only single bytes are read (there's no runs of data). Very likely the purpose is to read one or a few individual attribute table data, in order to change the color mapping of individual 2x2 squares instead of whole 4x4 square (read-modify-write operation).<br />
All reads are mapped to a structure of 3 bytes in the read buffer :<br />
SIZE CONTENTS<br />
2 VRAM Address (big endian)<br />
1 data<br />
<br />
Therfore, for each byte which is read from VRAM, 3 bytes have to be reserved in the read buffer.<br />
Once data from VRAM has been read, if it must be written back after a modification, the user need to copy it to the write buffer manually.<br />
<br />
The GetVRAMBufferByte was probably designed to prevent reading attribute tables when it has already been read in the past by a call to ReadVRAMBuffer. For example if you don't know if you're modifying another area of the same 4x4 attribute byte as previously (no need to read again), or if you're modifying part of another 4x4 attribute byte (need to read from VRAM). If the routine return with C=0, the accumulator contains the attribute byte that was already read, but if C=1, it means we should first call ReadVRAMBuffer before getting the data we want.<br />
<br />
Since reading VRAM for nothing is not a harmful operation, the user can call ReadVRAMBuffer every frame even if nobody has to be read (no need for check flags) which is probably why these routines are less complete than the write buffer related routines.<br />
<br />
=== Load Tileset notes ===<br />
<br />
The ''flags'' parameters are as follows:<br />
<br />
7 bit 0<br />
---------<br />
AAAA MMIT<br />
|||| ||||<br />
|||| |||+- Fill bit<br />
|||| ||+-- Transfer direction (0 = Write tiles, 1 = Read tiles)<br />
|||| ++--- Bitplane type (see below)<br />
++++------ Low VRAM Address (aka tile # within a row)<br />
<br />
1st bitplane 2nd bitplane Description<br />
----------- ----------- -----------<br />
0: data data+8 Normal 2-bitplane graphics<br />
1: data fill bit Single bitplane graphics. Fill bit clear : Use colors 0&1 Fill bit set : Use colors 2&3<br />
2: fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&2 Fill bit set : Use colors 1&3<br />
3: data^fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&3 Fill bit set : Use colors 1&2<br />
<br />
This makes it possible for single bitplane tiles to take all possible color schemes when they end up in VRAM. However, it is not possible to (natively) load single bitplane graphics directly from the disk into VRAM; it should be loaded into PRG-RAM before transferring the data into VRAM. In read mode, all non "data" bitplanes are replaced by dummy reads.</div>はるきhttps://www.nesdev.org/w/index.php?title=FDS_BIOS&diff=2527FDS BIOS2020-02-15T21:58:32Z<p>はるき: Added internal links to the FDS register page from the "pseudo-registers"</p>
<hr />
<div>The [[Famicom Disk System]] 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]].<br />
<br />
== Pseudo-registers ==<br />
The FDS BIOS uses several bytes on the zero-page. 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.<br />
<br />
[$FF]: value last written to [[PPUCTRL|$2000]] $80 on reset.<br />
[$FE]: value last written to [[PPUMASK|$2001]] $06 on reset<br />
[$FD]: value last written to [[PPUSCROLL|$2005/1]] $00 on reset.<br />
[$FC]: value last written to [[PPUSCROLL|$2005/2]] $00 on reset.<br />
[$FB]: value last written to [[Controller reading|$4016]] $00 on reset.<br />
[$FA]: value last written to [[Family_Computer_Disk_System#FDS_Control_.28.244025.29|$4025]] $2E on reset.<br />
[$F9]: value last written to [[Family_Computer_Disk_System#External_connector_.28.244026.29|$4026]] $FF on reset.<br />
$F5..$F8 : Used by controller read routines<br />
$00..$0F is used as temporary memory for the BIOS. The main program can use it as temporary memory too.<br />
<br />
The FDS BIOS also uses 4 bytes at the lower end of the stack page to control behaviour of interrupt/reset vectors:<br />
<br />
[$0102]/[$0103]: PC action on reset<br />
[$0101]: PC action on IRQ. set to $80 on reset<br />
[$0100]: PC action on NMI. set to $C0 on reset<br />
<br />
RESET:<br />
($DFFC): disk game reset vector (if [$0102] = $35, and [$0103] = $53 or $AC)<br />
<br />
IRQ:<br />
($DFFE): disk game IRQ vector (if [$0101] = %11xxxxxx)<br />
$E1EF : BIOS acknowledge and delay (if [$0101] = %10xxxxxx)<br />
$E1CE : BIOS disk transfer (if [$0101] = %01xxxxxx)<br />
$E1D9 : BIOS disk skip bytes (if [$0101] = %00xxxxxx)<br />
<br />
NMI:<br />
($DFF6): disk game NMI vector #1 (if [$0100] = %01xxxxxx)<br />
($DFF8): disk game NMI vector #2 (if [$0100] = %10xxxxxx)<br />
($DFFA): disk game NMI vector #3 (if [$0100] = %11xxxxxx)<br />
$E19D : BIOS disable NMI (if [$0100] = %00xxxxxx)<br />
<br />
A few important notes :<br />
* After loading the boot files, $0102 is set to $35 so that the ($DFFC) vector is used and the BIOS is skipped.<br />
* $0103 indicate reset type : $AC = first boot of the game, $53 = the game was soft-reset by the user<br />
* To use a custom IRQ routine, $C0 should be manually written to $0101<br />
* There are 3 possible NMI vectors, #3 is used by default.<br />
* 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 have files loaded in.<br />
<br />
== BIOS calls ==<br />
<br />
=== Disk access routines ===<br />
<br />
* 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.<br />
* Memory at $00-$0f will be affected by those routines<br />
* Unlike the vast majority of disk drives, the FDS lacks any kind of intelligent tracking system. All BIOS load and save functions will do access to the whole disk, no matter which data they load/save. A simple way to overcome this problem is to have a custom loading routine, similar to the BIOS one but forcing the # of files to a smaller number than it actually is. That way the later files are not accessed at all and the earlier files load faster. Of course the maximal time is still taken when loading files that are late on the disk.<br />
* 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.<br />
* 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.<br />
* 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.<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Description<br />
|-<br />
| $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.<br />
|-<br />
| $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).<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $e301 || SetFileCount1 || Pointer to Disk ID, A = file count minus one = # of the last file || A = error # || Set the file count to A + 1<br />
|-<br />
| $e305 || SetFileCount || Pointer to Disk ID, A = file count || A = error # || Set the file count to A<br />
|-<br />
| $e32a || GetDiskInfo || Pointer to Disk Info || A = error # || Fills DiskInfo up with data read off the current disk.<br />
|-<br />
|}<br />
<br />
=== Low-Level Disk access routines ===<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Description<br />
|-<br />
| $e445 || CheckDiskHeader || Pointer to 10 byte string at $00 || || Compares the first 10 bytes on the disk coming after the FDS string, to 10 bytes pointed to by Ptr($00). To bypass the checking of any byte, a -1 can be placed in the equivelant place in the compare string. Otherwise, if the comparison fails, an appropriate error will be generated.<br />
|-<br />
| $e484 || GetNumFiles || || || Reads number of files stored on disk, stores the result in $06<br />
|-<br />
| $e492 || SetNumFiles || || A = number of files || Writes new number of files to disk header.<br />
|-<br />
| $e4a0 || FileMatchTest || Pointer to FileID list at $02 || || Uses a byte string pointed at by Ptr($02) to tell the disk system which files to load. The file ID's number is searched for in the string. If an exact match is found, [$09] is 0'd, and [$0E] is incremented. If no matches are found after 20 bytes, or a -1 entry is encountered, [$09] is set to -1. If the first byte in the string is -1, the BootID number is used for matching files (any FileID that is not greater than the BootID qualifies as a match).<br />
|-<br />
| $e4da || SkipFiles || Number of files to skip in $06 || || Skips over specified number of files.<br />
|-<br />
|}<br />
<br />
=== Example code how to load files ===<br />
<br />
<pre><br />
Load<br />
jsr LoadFiles<br />
.dw DiskID<br />
.dw LoadList<br />
bne _Error ;Check if there is an error<br />
rts<br />
_Error<br />
jsr PrintError ;If so print the error number and message to screen (include side/disk changing prompts)<br />
_sideError<br />
lda $4032<br />
and #$01<br />
beq _sideError ;Wait until disk is ejected<br />
_insert<br />
lda $4032<br />
and #$01<br />
bne _instert ;Wait until disk is inserted<br />
jmp Load<br />
<br />
DiskID<br />
.db $01 ;Manufacturer code<br />
.db "NAME" ;4-letter code of game<br />
.db $00 ;Version<br />
.db $01 ;Disk side<br />
.db $00 ;Disk number<br />
.db $00, $00 ;Extra disk IDs<br />
<br />
LoadList ;In this example the files with IDs equal to $02, $03 or $04 will be loaded into memory<br />
.db $02, $03, $04, $ff<br />
</pre><br />
<br />
=== Error list ===<br />
<br />
Message in bold is the official BIOS message (if there is one) followed by an explanation<br />
*$00: no error<br />
*$01: '''disk set''', ($4032.0) disk not set<br />
*$02: '''battery''', ($4033.7) power supply failure<br />
*$03: ($4032.2) disk is write protected<br />
*$04: Wrong maker ID<br />
*$05: Wrong game<br />
*$06: Wrong game version<br />
*$07: '''a,b side''', wrong side number<br />
*$08: '''disk no.''', wrong disk number<br />
*$09: wrong additional disk ID 1<br />
*$0a: wrong additional disk ID 2<br />
*$20: '''disk trouble''', approval check failed<br />
*$21: '''disk trouble''', '*NINTENDO-HVC*' string in block 1 doesn't match<br />
*$22: '''disk trouble''', block type 1 expected<br />
*$23: '''disk trouble''', block type 2 expected<br />
*$24: '''disk trouble''', block type 3 expected<br />
*$25: '''disk trouble''', block type 4 expected<br />
*$27: '''disk trouble''', ($4030.4) block failed CRC<br />
*$28: '''disk trouble''', ($4030.6) file ends prematurely during read<br />
*$29: '''disk trouble''', ($4030.6) file ends prematurely during write<br />
*$30: '''disk trouble''', ($4032.1) disk is full<br />
<br />
=== Disk ID structure ===<br />
<br />
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.<br />
<br />
offset size error# description<br />
------ ---- ------ -----------<br />
0 1 $04 game manufacturer code<br />
1 4 $05 game ASCII name string<br />
5 1 $06 game version<br />
6 1 $07 disk side #<br />
7 1 $08 disk #<br />
8 1 $09 extra disk # data<br />
9 1 $10 extra disk # data<br />
A -<br />
<br />
=== File list structure ===<br />
<br />
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).<br />
<br />
Multiple files are loaded in the order as they exist on the disk, not in the order of the list.<br />
<br />
=== File header structure ===<br />
<br />
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.<br />
<br />
offset size description<br />
------ ---- -----------<br />
00 1 file ID code<br />
01 8 file name<br />
09 2 load address<br />
0B 2 file data size<br />
0D 1 file type ($00 : Program; $01 : Character; $02 : Nametable)<br />
0E 2 source address of file data (NOT written to disk)<br />
10 1 source address type ($00 : RAM, $01 : VRAM)<br />
11 -<br />
<br />
=== Disk information structure ===<br />
<br />
This is a data structure returned by a subroutine, of collected information <br />
from the disk (list of files on disk, disk size, etc.). The following table <br />
is a description of that structure.<br />
<br />
offset size<br />
------ ----<br />
0 1 game manufacturer code<br />
1 4 game ASCII name string<br />
5 1 game version<br />
6 1 disk side #<br />
7 1 disk #<br />
8 1 extra disk # data<br />
9 1 extra disk # data<br />
A 1 # of files on disk<br />
<br />
(the following block will appear for as many files as the "# of files on <br />
disk" byte indicates)<br />
<br />
B 1 file ID code<br />
C 8 file name (ASCII)<br />
<br />
(the following is present after the last file info block. Disk size is equal <br />
to the sum of each file's size entry, plus an extra 261 per file.)<br />
<br />
x 1 disk size high byte<br />
x+1 1 disk size low byte<br />
x+2 -<br />
<br />
== Other BIOS calls ==<br />
<br />
{| border="1" cellspacing="0" cellpadding="3"<br />
| Address || Name || Input parameters || Output parameters || Affected RAM/Registers || Description<br />
|-<br />
| $e149 || Delay132 || || || || 132 clock cycle delay<br />
|-<br />
| $e153 || Delayms || || || X, Y || Delay routine, Y = delay in ms (approximate)<br />
|-<br />
| $e161 || DisPFObj || || || A, $fe || Disable sprites and background<br />
|-<br />
| $e16b || EnPFObj || || || A, $fe || Enable sprites and background<br />
|-<br />
| $e171 || DisObj || || || A, $fe || Disable sprites<br />
|-<br />
| $e178 || EnObj || || || A, $fe || Enable sprites<br />
|-<br />
| $e17e || DisPF || || || A, $fe || Disable background<br />
|-<br />
| $e185 || EnPF || || || A, $fe || Enable background<br />
|-<br />
| $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.<br />
|-<br />
| $e7bb || VRAMStructWrite || Pointer to VRAM buffer to be written || || A, X, Y, $00, $01, $ff || Set VRAM increment to 1 (clear [[PPUCTRL]]/$ff bit 2), and write a VRAM buffer to VRAM. Read below for information on the structure.<br />
|-<br />
| $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.<br />
|-<br />
| $e86a || WriteVRAMBuffer || || || A, X, Y, $301, $302 || Write the VRAM Buffer at $302 to VRAM. Read below for information on the structure.<br />
|-<br />
| $e8b3 || ReadVRAMBuffer || X = start address of read buffer, Y = # of bytes to read || || A, X, Y || Read individual bytes from VRAM to the VRAMBuffer. (see notes below)<br />
|-<br />
| $e8d2 || PrepareVRAMString || A = High VRAM address, X = Low VRAM address, 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 copies pointed data into the VRAM buffer.<br />
|-<br />
| $e8e1 || PrepareVRAMStrings || A = High VRAM address, X = Low VRAM address, Direct pointer = data to be written to VRAM || A = $ff : no error, A = $01 : data didn't fit in buffer || A, X, Y, $00, $01, $02, $03, $04, $05, $06 || This routine copies a 2D string into the VRAM buffer. The first byte of the data determines the width and height of the following string (in tiles): Upper nybble = height, lower nybble = width.<br />
|-<br />
| $e94f || GetVRAMBufferByte || X = starting index of read buffer, Y = # of address to compare (starting at 1), $00, $01 = address to read from || carry clear : a previously read byte was returned, carry set : no byte was read, should wait next call to ReadVRAMBuffer || A, X, Y || This routine was likely planned to be used in order to avoid useless latency on a VRAM reads (see notes below). It compares the VRAM address in ($00) with the Yth (starting at 1) address of the read buffer. If both addresses match, the corresponding data byte is returned exit with c clear. If the addresses are different, the buffer address is overwritten by the address in ($00) and the routine exit with c set.<br />
|-<br />
| $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 address (assumes no scrolling, and points to first nametable at $2000-$23ff).<br />
|-<br />
| $e997 || Nam2PixelConv || $00 = High nametable address, $01 = low nametable address || $02 = Pixel X cord, $03 = Pixel Y cord || A || This routine convert a nametable address to corresponding pixel coordinates (assume no scrolling).<br />
|-<br />
| $e9b1 || Random || X = Zero Page address 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 the program is 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''.<br />
|-<br />
| $e9c8 || SpriteDMA || || || A || This routine does sprite DMA from RAM $200-$2ff<br />
|-<br />
| $e9d3 || CounterLogic || A, Y = end Zeropage address of counters, X = start zeropage address 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.<br />
|-<br />
| $e9eb || ReadPads || || $f5 = Joypad #1 data, $f6 = Joypad #2 data || A, X, $00, $01, || This read hardwired famicom joypads.<br />
|-<br />
| $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<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $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.<br />
|-<br />
| $ea68 || ReadDownExpPads || $f1-$f4 = up->down transitions, $f5-$f8 = Joypad 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.<br />
|-<br />
| $ea84 || VRAMFill || A = High VRAM Address (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.<br />
|-<br />
| $ead2 || MemFill || A = fill value, X = first page #, Y = last page # || || A, X, Y, $00, $01 || This routines fills RAM pages with specified value.<br />
|-<br />
| $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<br />
|-<br />
| $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.<br />
|-<br />
| $eb13 || ReadKeyboard || || || || Read Family Basic Keyboard expansion (detail is under analysis)<br />
|-<br />
| $eb66 || LoadTileset || A = Low VRAM Address & Flags, Y = Hi VRAM Address, 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.<br />
|-<br />
| $ec22 || unk_EC22|| || || || Some kind of logic that some games use. (detail is under analysis)<br />
|}<br />
<br />
<br />
=== VRAM write transfer structure ===<br />
<br />
The structure of VRAM buffers are as follows:<br />
<br />
SIZE CONTENTS<br />
2 VRAM Address (big endian)<br />
1 bit 0-5 length of data ($0 means a length of 64)<br />
bit 6 : 0 = copy, 1 = fill<br />
bit 7 : 0 = increment by 1, 1 = increment by 32<br />
n Data to copy to VRAM<br />
..... repeated as many times as needed<br />
1 $ff<br />
<br />
* The main structure is terminated by a $ff byte (High address is always supposed to be in $00..$3f range)<br />
* $4c is a "call" command. The 2 bytes that follow is the address of a sub-VRAM structure. The sub-structure can call another sub-structure and so on.<br />
* $60 is a "return" command. It will terminate a sub-structure.<br />
* If Fill mode is used, the routine takes only 1 byte of data which is repeated.<br />
<br />
=== VRAM Buffer notes ===<br />
<br />
The VRAM buffer is located at $300-$3xx. $300 holds the size of the buffer (maximum), and $301 holds the end index of the buffer. The actual buffer lies at $302-$3xx, and is of variable length.<br />
<br />
* $300 is initialized to the 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 the biggest possible value is $fd, making the buffer lie at $300-$3ff.<br />
* Format of the buffer is equivalent to the VRAM structure above, except that there are no sub-structures, no increment by 32 flag and no fill flag.<br />
* For this reason, the VRAM buffer at $302 can be used as a sub-structure.<br />
* A call to WriteVRAMBuffer will execute faster than a call to VRAMStructWrite with $302 as an argument, but both will have the same effect.<br />
<br />
=== Read routines and VRAM buffer ===<br />
<br />
Unlike the write routines which are very complete, the read routines are somewhat incomplete and their functionality have to be completed by the user (which limits their usefulness). Most of what follows is some sort of speculation about the usefulness of the incomplete read routines of the BIOS.<br />
<br />
The read buffer is a part of the VRAM buffer at $300-$3xx, but the same location can't be used to transfer read and writes on the same frame (for example a RMW operation). Instead the user must manually split the buffer in two parts, the "read" buffer and the "write" buffer. The read buffer is probably supposed to always lies after the write buffer, so that when the read buffer is in use, the value in $300 (size of the write buffer) should be adjusted accordingly. The user should manually keep track of how many bytes are used for the read buffer, as well as the starting point of the read buffer (they are argument to the ReadVRAMBuffer function).<br />
<br />
The structure of the read buffer itself is trivial - only single bytes are read (there's no runs of data). Very likely the purpose is to read one or a few individual attribute table data, in order to change the color mapping of individual 2x2 squares instead of whole 4x4 square (read-modify-write operation).<br />
All reads are mapped to a structure of 3 bytes in the read buffer :<br />
SIZE CONTENTS<br />
2 VRAM Address (big endian)<br />
1 data<br />
<br />
Therfore, for each byte which is read from VRAM, 3 bytes have to be reserved in the read buffer.<br />
Once data from VRAM has been read, if it must be written back after a modification, the user need to copy it to the write buffer manually.<br />
<br />
The GetVRAMBufferByte was probably designed to prevent reading attribute tables when it has already been read in the past by a call to ReadVRAMBuffer. For example if you don't know if you're modifying another area of the same 4x4 attribute byte as previously (no need to read again), or if you're modifying part of another 4x4 attribute byte (need to read from VRAM). If the routine return with C=0, the accumulator contains the attribute byte that was already read, but if C=1, it means we should first call ReadVRAMBuffer before getting the data we want.<br />
<br />
Since reading VRAM for nothing is not a harmful operation, the user can call ReadVRAMBuffer every frame even if nobody has to be read (no need for check flags) which is probably why these routines are less complete than the write buffer related routines.<br />
<br />
=== Load Tileset notes ===<br />
<br />
The ''flags'' parameters are as follows:<br />
<br />
7 bit 0<br />
---------<br />
AAAA MMIT<br />
|||| ||||<br />
|||| |||+- Fill bit<br />
|||| ||+-- Transfer direction (0 = Write tiles, 1 = Read tiles)<br />
|||| ++--- Bitplane type (see below)<br />
++++------ Low VRAM Address (aka tile # within a row)<br />
<br />
1st bitplane 2nd bitplane Description<br />
----------- ----------- -----------<br />
0: data data+8 Normal 2-bitplane graphics<br />
1: data fill bit Single bitplane graphics. Fill bit clear : Use colors 0&1 Fill bit set : Use colors 2&3<br />
2: fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&2 Fill bit set : Use colors 1&3<br />
3: data^fill bit data Single bitplane graphics. Fill bit clear : Use colors 0&3 Fill bit set : Use colors 1&2<br />
<br />
This makes it possible for single bitplane tiles to take all possible color schemes when they end up in VRAM. However, it is not possible to (natively) load single bitplane graphics directly from the disk into VRAM; it should be loaded into PRG-RAM before transferring the data into VRAM. In read mode, all non "data" bitplanes are replaced by dummy reads.</div>はるきhttps://www.nesdev.org/w/index.php?title=Emulators&diff=2356Emulators2019-08-25T21:56:26Z<p>はるき: /* Popular */</p>
<hr />
<div>This is a '''list of NES emulators'''.<br />
<br />
__TOC__<br />
<br />
== Commercial ==<br />
{| class="wikitable sortable"<br />
! Emulator name<br />
! Author<br />
! Platform(s)<br />
! Ports and/or other details<br />
|-<br />
| acNES<br />
| Nintendo<br />
| GameCube, Game Boy Advance<br />
| Used for Animal Crossing, e-Reader, and Classic NES Series. The name "acNES" is unofficial, as Nintendo has not released this emulator as a distinct product. Information from TCRF indicates that it may be called "QFC".<br />
|-<br />
| Virtual Console<br />
| Nintendo<br />
| Wii/Wii U/3DS<br />
| Most games cost 500 Nintendo Points in Wii Shop Channel<br />
|-<br />
| [http://www.dwedit.org/gba/pocketnes.php PocketNES]<br />
| loopy, FluBBa, and Dwedit<br />
| Game Boy Advance, Nintendo DS<br />
| Used commercially for some emulated re-releases by Atlus, Jaleco, Konami<br />
|}<br />
<br />
== Popular ==<br />
These are commonly used or well-established.<br />
<br />
{| class="wikitable sortable"<br />
! Emulator name<br />
! Author<br />
! Platform(s)<br />
! Ports and/or other details<br />
|-<br />
| [https://github.com/TASVideos/BizHawk BizHawk] || Multiple authors || Win32, macOS<br />
|-<br />
| [http://wiibrew.org/wiki/FCE_Ultra_GX FCE Ultra GX] || Tantric || Wii, GameCube<br />
|-<br />
| [http://fceux.com/web/home.html FCEUX] || Anthony Giorgio / Mark Doliner || Win32, Linux<br />
|-<br />
| [http://www.the-interweb.com/serendipity/index.php?/categories/9-FCEUXD-SP FCEUXD SP] || sp || Win32<br />
|-<br />
| [https://byuu.org/emulation/higan/ higan] || byuu || Win32, FreeBSD, Linux, macOS || Multi-platform<br />
|-<br />
| [http://fms.komkon.org/iNES/ iNES] || Marat Fayzullin || Win32 and Linux<br />
|-<br />
| [http://jabosoft.com/?categoryid=1 Jnes] || Jabosoft || Win32<br />
|-<br />
| [http://www.mesen.ca/ Mesen] || Sour || Win32/.NET || [http://forums.nesdev.org/viewtopic.php?p=164372#p164372 Announcement] / [https://github.com/SourMesen/Mesen Source], excellent debugger<br />
|-<br />
| [http://www.nemulator.com nemulator] || James Slepicka || Win32 (Vista/7)<br />
|-<br />
| [http://www.nesemu2.com/ nesemu2] || holodnak || Win32, OS X, Linux || [https://github.com/holodnak/nesemu2 github source]<br />
|-<br />
| [http://tnse.zophar.net/NESten.htm NESten] || TNSe || Win32<br />
|-<br />
| [http://nestopia.sourceforge.net/ NEStopia] || Martin Freij || Win32 || [http://rbelmont.mameworld.info/?page_id=200 Linux], [http://www.bannister.org/software/nestopia.htm MacOS]<br />
|-<br />
| [http://0ldsk00l.ca/nestopia/ Nestopia UE] || rdanbrook || Linux, BSD, Win7+ || a.k.a. Nestopia Undead Edition. Contains bugfixes/etc.<br />Windows binaries are available [http://sourceforge.net/projects/nestopiaue/ at Sourceforge] or [http://www.emucr.com/search/label/Nestopia at EmuCR]<br />
|-<br />
| [http://www.qmtpro.com/~nes/nintendulator/ Nintendulator] || Quietust || Win32 || [http://kkfos.aspekt.fi/projects/nes/tools/nintendulatordx/ Nintendulator DX] (by [[User:Thefox|thefox]]) for an even more-improved debugger<br />
|-<br />
| [http://problemkaputt.de/nes.htm NO$NES] || Martin Korth || Win32<br />
|-<br />
| [http://www.dwedit.org/gba/pocketnes.php PocketNES] || loopy, FluBBa, and Dwedit || Game Boy Advance || Updates on [http://www.dwedit.org/dwedit_board/viewtopic.php?id=409 Dwedit's board]<br />
|-<br />
| [http://rocknes.web.fc2.com RockNES] || Zepper (formerly Fx3) || Win32<br />
|-<br />
| [http://www.ubernes.com/ UberNES] || M \ K Productions || Win32<br />
|-<br />
| [http://virtuanes.s1.xrea.com/ VirtuaNES] || Norix || Win32 || Has a real-time memory hex-editor<br />
|}<br />
<br />
== Under development ==<br />
<br />
The following is a list of NES emulators that are supposedly under development, who their authors are, relevant home pages/sites, and the source of the announcement (direct or indirect).<br />
<br />
{{mbox<br />
| type = warning<br />
| text = '''Before considering developing your own NES emulator, ask yourself if your efforts may be better spent helping out those who already have emulators in development!'''<br />
}}<br />
<br />
{| class="wikitable sortable"<br />
! Emulator name<br />
! Author<br />
! Platform(s)<br />
! Ports and/or other details<br />
|-<br />
| [http://www.nesicide.com/ NESICIDE] || cpow || Win32/64, Linux32/64, macOS ||<br />
|-<br />
| [http://fpganes.blogspot.se/ FPGA NES] || Ludde || FPGA (hardware) ||<br />
|-<br />
| [https://rm-rfroot.net/nes_fpga/ VeriNES] || jwdonal || FPGA (hardware) || [http://forums.nesdev.org/viewtopic.php?t=6157 Announcement]<br />
|-<br />
| [http://kevtris.org/Projects/console/sections/index.html FPGA NES] || kevtris || FPGA (hardware) ||<br />
|-<br />
| [http://danstrother.com/fpga-nes/ FPGA NES] || Dan Strother || FPGA (hardware) ||<br />
|-<br />
| [http://code.google.com/p/nesface/ NESFaCE] || 6T4 || Win32 || [http://forums.nesdev.org/viewtopic.php?t=7499 Announcement]<br />
|-<br />
| [http://www.crazysmart.net.au/kindred kindred] || Overload || Win32 || [http://forums.nesdev.org/viewtopic.php?f=3&t=10429 Announcement]<br />
|-<br />
| ? || allthatremains || Win32 || [http://forums.nesdev.org/viewtopic.php?t=5108 Announcement]<br />
|-<br />
| [http://www.aminlab.cn/app/nes/ ?] || amin2312 || Flash || [http://forums.nesdev.org/viewtopic.php?t=5678 Announcement]<br />
|-<br />
| ModNES || Petruza || Portable, mainly MacOS & Win32 || [http://forums.nesdev.org/viewtopic.php?t=6159 Announcement]<br />
|-<br />
| [http://www.anes.se/ A/NES] || Morgan Johansson || AmigaOS || [http://forums.nesdev.org/viewtopic.php?t=1279 Announcement]<br />
|-<br />
| [http://forums.nesdev.org/viewtopic.php?f=3&t=6928 puNES] || FHorse || Linux32/64, Win32/64 || [http://forums.nesdev.org/viewtopic.php?t=6928 Announcement] / [https://github.com/punesemu/puNES Source]<br />
|-<br />
| [https://github.com/rohtang/famique famique] || sahib || Mac OS X, Win32, Linux || [http://forums.nesdev.org/viewtopic.php?t=5922 Announcement]<br />
|-<br />
| FooNES || aphex || Win32 || [http://forums.nesdev.org/viewtopic.php?p=75152#p75152 Announcement]<br />
|-<br />
| nesemu1 || Bisqwit || libSDL (portable), testing under Linux || [http://forums.nesdev.org/viewtopic.php?t=8385 Announcement]<br />
|-<br />
| [http://forums.nesdev.org/viewtopic.php?t=8400 MoarNES] || miker00lz || Win32 || [http://forums.nesdev.org/viewtopic.php?t=6972 Announcement]<br />
|-<br />
| jaNES || crudelios || Win32 || [http://forums.nesdev.org/viewtopic.php?p=89751 Announcement]<br />
|-<br />
| ? || neet || ? || [http://forums.nesdev.org/viewtopic.php?p=89437#p89437 Announcement]<br />
|-<br />
| ? || foobaz || Java || [http://forums.nesdev.org/viewtopic.php?t=8559 Announcement]<br />
|-<br />
| ? || MottZilla || ? || [http://forums.nesdev.org/viewtopic.php?t=8491 Announcement]<br />
|-<br />
| ? || runaway pancake || ? || [http://forums.nesdev.org/viewtopic.php?p=88478#p88478 Announcement]<br />
|-<br />
| ? || Vegenad || ? || [http://forums.nesdev.org/viewtopic.php?t=3593 Announcement]<br />
|-<br />
| ? || johnathonrh || ? || [http://forums.nesdev.org/viewtopic.php?t=5780 Announcement]<br />
|-<br />
| [http://www.yanese.com/ Yanese] || Anes || Win32 || [http://forums.nesdev.org/viewtopic.php?p=713 Announcement]<br />
|-<br />
| ? || nesemuguy || ? || [http://forums.nesdev.org/viewtopic.php?t=2798 Announcement]<br />
|-<br />
| ? || Coldberg || ? || [http://forums.nesdev.org/viewtopic.php?t=4231 Announcement]<br />
|-<br />
| ? || pops || ? || [http://forums.nesdev.org/viewtopic.php?t=6260 Announcement]<br />
|-<br />
| ? || beannaich || ? || [http://forums.nesdev.org/viewtopic.php?t=6240 Announcement]<br />
|-<br />
| ? || windwakr || ? || [http://forums.nesdev.org/viewtopic.php?t=6294 Announcement]<br />
|-<br />
| ? || JuniorZ || ? || [http://forums.nesdev.org/viewtopic.php?t=896 Announcement]<br />
|-<br />
| ? || NesHackR || ? || [http://forums.nesdev.org/viewtopic.php?t=6102 Announcement]<br />
|-<br />
| AwesomeNES || Snaer || libSDL (portable) || [http://forums.nesdev.org/viewtopic.php?t=6064 Announcement]<br />
|-<br />
| ? || BeTheDuck || ? || [http://forums.nesdev.org/viewtopic.php?t=5869 Announcement]<br />
|-<br />
| ? || Luke || ? || [http://forums.nesdev.org/viewtopic.php?t=5821 Announcement]<br />
|-<br />
| ? || The Lord || ? || [http://forums.nesdev.org/viewtopic.php?t=5795 Announcement]<br />
|-<br />
| ? || essial || ? || [http://forums.nesdev.org/viewtopic.php?t=5791 Announcement]<br />
|-<br />
| ? || JamesK89 || ? || [http://forums.nesdev.org/viewtopic.php?t=5787 Announcement]<br />
|-<br />
| ? || yaazz || OS X || [http://forums.nesdev.org/viewtopic.php?t=5723 Announcement]<br />
|-<br />
| ? || blanham || OS X || [http://forums.nesdev.org/viewtopic.php?t=5312 Announcement]<br />
|-<br />
| ? || magicphenix || ? || [http://forums.nesdev.org/viewtopic.php?t=5703 Announcement]<br />
|-<br />
| ? || happymaomao || ? || [http://forums.nesdev.org/viewtopic.php?t=5678 Announcement]<br />
|-<br />
| ? || someone_somewhere || ? || [http://forums.nesdev.org/viewtopic.php?t=5638 Announcement]<br />
|-<br />
| ? || Undubbed || ? || [http://forums.nesdev.org/viewtopic.php?t=5482 Announcement]<br />
|-<br />
| ? || Muchaserres || ? || [http://forums.nesdev.org/viewtopic.php?t=3665 Announcement]<br />
|-<br />
| ? || takeda || ? || [http://forums.nesdev.org/viewtopic.php?t=5530 Announcement]<br />
|-<br />
| ? || albailey || ? || [http://forums.nesdev.org/viewtopic.php?t=5512 Announcement]<br />
|-<br />
| ? || CaptainMuscles || ? || [http://forums.nesdev.org/viewtopic.php?t=5419 Announcement]<br />
|-<br />
| ? || hatorijr || ? || [http://forums.nesdev.org/viewtopic.php?t=5408 Announcement]<br />
|-<br />
| ? || tanoatnd || ? || [http://forums.nesdev.org/viewtopic.php?t=5189 Announcement]<br />
|-<br />
| ? || max_sweat || ? || [http://forums.nesdev.org/viewtopic.php?t=5388 Announcement]<br />
|-<br />
| ? || jjpeerless || ? || [http://forums.nesdev.org/viewtopic.php?t=5281 Announcement]<br />
|-<br />
| ? || Cloudy || ? || [http://forums.nesdev.org/viewtopic.php?t=5109 Announcement]<br />
|-<br />
| ? || meatloaf69 || ? || [http://forums.nesdev.org/viewtopic.php?p=62817#p62817 Announcement]<br />
|-<br />
| ? || parth || FPGA (hardware) || [http://forums.nesdev.org/viewtopic.php?t=4502 Announcement]<br />
|-<br />
| ? || oRBIT2002 || ? || [http://forums.nesdev.org/viewtopic.php?t=1279 Announcement]<br />
|-<br />
| ? || Elessar || ? || [http://forums.nesdev.org/viewtopic.php?t=7862 Announcement]<br />
|-<br />
| ? || cmoh89 || ? || [http://forums.nesdev.org/viewtopic.php?t=7521 Announcement]<br />
|-<br />
| [http://forums.nesdev.org/viewtopic.php?f=3&t=9935 HDNes] || mkwong98 || Win32 || [http://forums.nesdev.org/viewtopic.php?t=7848 Announcement]<br />
|-<br />
| ? || ehguacho || ? || [http://forums.nesdev.org/viewtopic.php?t=7936 Announcement]<br />
|-<br />
| ? || nop || Java || [http://forums.nesdev.org/viewtopic.php?t=8876 Announcement]<br />
|-<br />
| ? || emu_enthusiast || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=5826 Announcement]<br />
|-<br />
| [http://forums.nesdev.org/viewtopic.php?p=140741#p140741 MahNES] || HLorenzi || Win32 || [http://forums.nesdev.org/viewtopic.php?f=3&t=9054 Announcement]<br />
|-<br />
| ? || haydenmuhl || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=9101 Announcement]<br />
|-<br />
| ? || urbanspr1nter || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=9227 Announcement]<br />
|-<br />
| Fergulator || fergus_maximus || Mac OS X or X Windows || [http://forums.nesdev.org/viewtopic.php?f=3&t=9292 Announcement]<br />
|-<br />
| ? || ermular || Win32 || [http://forums.nesdev.org/viewtopic.php?f=3&t=9353 Announcement]<br />
|-<br />
| ? || alexwy || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=9608 Announcement]<br />
|-<br />
| ? || caiiio || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=9625 Announcement]<br />
|-<br />
| ? || miguelsfp || Win32 || [http://forums.nesdev.org/viewtopic.php?f=3&t=9726 Announcement]<br />
|-<br />
| ? || ember || FPGA (hardware) || [http://forums.nesdev.org/viewtopic.php?f=3&t=9653 Announcement]<br />
|-<br />
| ? || ninjis || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=9829 Announcement]<br />
|-<br />
| [http://www.geocities.jp/submarine600/html/p8/nesemu.html ?] || submarine600 || PC-8801 || [http://forums.nesdev.org/viewtopic.php?f=3&t=9921 Announcement]<br />
|-<br />
| [http://alike.se/yane/ Yane] || roku6185 || libSDL (portable), testing under Linux || [http://forums.nesdev.org/viewtopic.php?f=3&t=9969 Announcement]<br />
|-<br />
| [http://vpnes.googlecode.com/ VPNES] || x0000 || Win32 w/ SDL || <br />
|-<br />
| [https://github.com/macifom/macifom macifom] || Auston Stewart || OS X, iOS || [http://forums.nesdev.org/viewtopic.php?f=3&t=9989 Announcement]<br />
|-<br />
| [https://github.com/macifom/macifomlite macifomlite] || Auston Stewart || iOS || <br />
|-<br />
| [https://github.com/eteran/pretendo Pretendo] || proxy || Linux/BeOS/Win32 || [http://forums.nesdev.org/viewtopic.php?f=3&t=10045 Announcement]<br />
|-<br />
| ? || samfoo || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=10070 Announcement]<br />
|-<br />
| ? || LightStruk || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=10071 Announcement]<br />
|-<br />
| [http://zelex.net/nezulator Nezulator] || Zelex || JavaScript || [http://forums.nesdev.org/viewtopic.php?f=3&t=7704 Announcement]<br />
|-<br />
| [http://www.oriku.com/emuya.html EMUya] || Zelex || Ouya || [http://forums.nesdev.org/viewtopic.php?f=3&t=10002 Announcement]<br />
|-<br />
| ? || dreampeppers99 || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=10130 Announcement]<br />
|-<br />
| ? || Skypher || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=10193 Announcement]<br />
<!--<br />
|-<br />
| ? || Dartht33bagger || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=10266 Announcement]<br />
--><br />
|-<br />
| ? || ArsonIzer || Java || [http://forums.nesdev.org/viewtopic.php?f=3&t=10297 Announcement]<br />
|-<br />
| ? || SuperFXMaster || FPGA (hardware) || [http://forums.nesdev.org/viewtopic.php?f=10&t=10308 Announcement]<br />
|-<br />
| ? || Choz || Win32 w/ SDL || [http://forums.nesdev.org/viewtopic.php?f=3&t=10333 Announcement]<br />
|-<br />
| ? || sronsse || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=10348 Announcement]<br />
|-<br />
| ? || Emu6502Writer || ? || [http://forums.nesdev.org/viewtopic.php?p=116397#p116397 Announcement]<br />
|-<br />
| ? || fred || ? || [http://forums.nesdev.org/viewtopic.php?p=117245#p117245 Announcement]<br />
|-<br />
| ? || janzdott || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=10558 Announcement]<br />
|-<br />
| ? || d15ea5e || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=10569 Announcement]<br />
|-<br />
| ? || mrhyde || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=10751 Announcement]<br />
|-<br />
| ? || codeblox || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=10781 Announcement]<br />
|-<br />
| [http://return-1.com/FC87/FC87.html FC87] || Boolean || SDL (Win32, GNU/Linux) || [http://forums.nesdev.org/viewtopic.php?f=3&t=10839 Announcement]<br />
|-<br />
| phibiaNES || nIghtorius || Win32 || [http://forums.nesdev.org/viewtopic.php?f=3&t=11201 Announcement]<br />
|-<br />
| ? || Choz || ? || [http://forums.nesdev.org/viewtopic.php?f=2&t=11639 Announcement]<br />
|-<br />
| [https://github.com/nwidger/nintengo nintengo] || nwidger || Go w/ libSDL || <br />
|-<br />
| [https://github.com/rockcarry/ffnes ffnes] || rockcarry || Win32 || [http://forums.nesdev.org/viewtopic.php?f=3&t=11948 Announcement]<br />
|-<br />
| [https://github.com/peteward44/WebNES WebNES] || peteward44 || Javascript || [http://forums.nesdev.org/viewtopic.php?f=3&t=12006 Announcement] / [http://peteward44.github.io/WebNES Live demo]<br />
|-<br />
| O-Nes-Sama || Fumarumota, aLaix || SDL2 (Win32, GNU/Linux) || [http://forums.nesdev.org/viewtopic.php?f=3&t=11287 Announcement]<br />
|-<br />
| [http://nintaco.com Nintaco] || zeroone || Java (Windows, GNU/Linux, macOS) || [http://forums.nesdev.org/viewtopic.php?f=3&t=12185 Announcement]<br />
|-<br />
| ? || austere || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=12222 Announcement]<br />
|-<br />
| [https://github.com/jpikl/cfxnes cfxnes] || jonyzz || Javascript/CoffeeScript || [http://forums.nesdev.org/viewtopic.php?f=3&t=12315 Announcement] / [http://cfxnes.herokuapp.com/ Live demo]<br />
|-<br />
| ? || mreiland || ? || [http://forums.nesdev.org/viewtopic.php?p=143522#p143522 Announcement]<br />
|-<br />
| [https://github.com/amaiorano/nes-emu ?] || daroou || Win32 || [http://forums.nesdev.org/viewtopic.php?p=143752#p143752 Announcement]<br />
|-<br />
| [https://github.com/fogleman/nes fogleman/nes] || Michael Fogleman || Go with Go/GL and PortAudio || [https://medium.com/@fogleman/i-made-an-nes-emulator-here-s-what-i-learned-about-the-original-nintendo-2e078c9b28fe Medium article]<br />
|-<br />
| ? || RobertLoggia || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=12605 Announcement]<br />
|-<br />
| [https://github.com/ulfalizer/nesalizer nesalizer] || Ulfalizer || libSDL (portable), tested on Linux ||<br />
|-<br />
| ? || NewDietCoke248903 || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=12725 Announcement]<br />
|-<br />
| [https://github.com/dgrigoriou1/NES-Emu NES-Emu] || imid || C#/.NET || [http://forums.nesdev.org/viewtopic.php?p=222378#p222378 Announcement]<br />
|-<br />
| ? || mreiland || Linux (?) || [http://forums.nesdev.org/viewtopic.php?p=146814#p146814 Announcement]<br />
|-<br />
| nSide || hex_usr || ? || Fork of byuu's higan-nes. [http://forums.nesdev.org/viewtopic.php?f=3&t=12792 Announcement]<br />
|-<br />
| ? || DarylTechNES || ? || [http://forums.nesdev.org/viewtopic.php?p=147242#p147242 Announcement]<br />
|-<br />
| [https://github.com/andrew-hoffman/halfnes HalfNES] || Grapeshot || Java || [https://github.com/andrew-hoffman/halfnes/tree/master/src Github source]<br />
|-<br />
| [https://github.com/ThomsonKernel/EduNes EduNes] || thomson || SDL2 || [http://forums.nesdev.org/viewtopic.php?p=167743#p167743 Announcement]<br />
|-<br />
| ? || Feuerwerk42 || FPGA (hardware) || [http://forums.nesdev.org/viewtopic.php?f=3&t=13966&p=166338 Announcement]<br />
|-<br />
| ? || fspinolo || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=14231 Announcement]<br />
|-<br />
| ? || DaNES || ? || [http://forums.nesdev.org/viewtopic.php?f=10&t=14232 Announcement]<br />
|-<br />
| ? || charrli || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=14287 Announcement]<br />
|-<br />
| ? || amhndu || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=14359 Announcement]<br />
|-<br />
| SharpNES || colinvella || Win32/.NET || [http://forums.nesdev.org/viewtopic.php?f=3&t=11353&p=172798#p172798 Announcement]<br />
|-<br />
| ? || Alyosha_TAS || ? || [http://forums.nesdev.org/viewtopic.php?f=10&t=14411 Announcement]<br />
|-<br />
| NESfusto || DarkMoe || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=14705 Announcement]<br />
|-<br />
| ? || Bowie90333212391 || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=13143 Announcement]<br />
|-<br />
| ? || RomarioSilva || ? || [http://forums.nesdev.org/viewtopic.php?f=2&t=15104 Announcement]<br />
|-<br />
| ? || JSBryan || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=15113 Announcement]<br />
|-<br />
| ? || Sarospa || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=15138 Announcement]<br />
|-<br />
| ? || Totolasticot42 || ? || [http://forums.nesdev.org/viewtopic.php?f=2&t=15268 Announcement]<br />
|-<br />
| ? || been_jamin || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=15411 Announcement]<br />
|-<br />
| ? || wbrian || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=15537 Announcement]<br />
|-<br />
| ? || lord_Chile || ? || [http://forums.nesdev.org/viewtopic.php?f=10&t=17087 Announcement]<br />
|-<br />
| Nintendoish || drewying || Win32 || [http://forums.nesdev.org/viewtopic.php?f=3&t=17103 Announcement] / [https://github.com/drewying/Nintendoish Source]<br />
|-<br />
| ? || tdondich || JavaScript (Vue.js) || [http://forums.nesdev.org/viewtopic.php?f=3&t=17179 Announcement] / [https://github.com/tdondich/vue-2a03-emu Source]<br />
|-<br />
| ? || ace314159 || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=17267 Announcement]<br />
|-<br />
| ? || iOSBrett || Swift || [http://forums.nesdev.org/viewtopic.php?f=3&t=17272 Announcement]<br />
|-<br />
| ? || relaxok || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=17407 Announcement]<br />
|-<br />
| ? || jamieyello || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=17415 Announcement]<br />
|-<br />
| ? || JonteP || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=17663 Announcement]<br />
|-<br />
| ? || zzzz898 || ? || [http://forums.nesdev.org/viewtopic.php?p=223812#p223812 Announcement]<br />
|-<br />
| ? || ap9 || ? || [http://forums.nesdev.org/viewtopic.php?f=6&t=115&p=198800#p198800 Announcement]<br />
|-<br />
| ? || zeno84 || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=17699 Announcement]<br />
|-<br />
| ? || HastatusXXI || ? || [http://forums.nesdev.org/viewtopic.php?p=224241#p224241 Announcement]<br />
|-<br />
| ? || andsve || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=17748 Announcement]<br />
|-<br />
| ? || stickzman || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=17750 Announcement]<br />
|-<br />
| ? || babai || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=16293 Announcement]<br />
|-<br />
| ? || arcnor || ? || [http://forums.nesdev.org/viewtopic.php?f=10&t=17895 Announcement]<br />
|-<br />
| ? || cbalen || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=18014 Announcement]<br />
|-<br />
| ? || psychopathetica || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=18134 Announcement]<br />
|-<br />
| ? || Myself086 || SNES || [http://forums.nesdev.org/viewtopic.php?f=12&t=18021 Announcement]<br />
|-<br />
| ? || nflga || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=18505 Announcement]<br />
|-<br />
| ? || rodri042 || ? || [http://forums.nesdev.org/viewtopic.php?p=235687#p235687 Announcement]<br />
|-<br />
| ? || twicetimes || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=18583 Announcement]<br />
|-<br />
| ? || dawid9554 || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=18613 Announcement]<br />
|-<br />
| [https://github.com/kgabis/agnes agnes] || kgabis || C with libSDL examples || [http://forums.nesdev.org/viewtopic.php?f=3&t=18624 Announcement]<br />
|-<br />
| ? || LukasP || ? || [http://forums.nesdev.org/viewtopic.php?p=236630#p236630 Announcement]<br />
|-<br />
| [https://github.com/JorenJoestar/HydraNes HydraNES] || BadFoolPrototype || Win32 (Glew/OpenGL) || [http://forums.nesdev.org/viewtopic.php?f=3&t=18811 Announcement] / [http://forums.nesdev.org/viewtopic.php?f=10&t=3829 First mention]<br />
|-<br />
| ? || mferrantini || JavaScript || [http://forums.nesdev.org/viewtopic.php?f=10&t=18785 Announcement]<br />
|-<br />
| ? || gobblebit || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=18939 Announcement]<br />
|-<br />
| ? || AshleyJamesy || Android || First mention on Discord #programming channel<br />
|-<br />
| [https://github.com/lukexor/rustynes RustyNES] || lukexor || Rust/SDL 2 || [http://forums.nesdev.org/viewtopic.php?f=3&t=18972 Announcement]<br />
|-<br />
| ? || radis || ? || First mention on Discord #emulator-development channel<br />
|-<br />
| ? || timl132 || ? || [http://forums.nesdev.org/viewtopic.php?p=241082#p241082 Announcement]<br />
|-<br />
| ? || thejunkjon || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=19142 Announcement]<br />
|-<br />
| ? || olivecc || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=19191 Announcement]<br />
|}<br />
<br />
== Discontinued ==<br />
<br />
These are emulators which are known to be officially discontinued, i.e. abandoned or are no longer in development.<br />
<br />
{| class="wikitable sortable"<br />
! Emulator name<br />
! Author<br />
! Platform(s)<br />
! Other details<br />
|-<br />
| [http://fms.komkon.org/iNES/ iNES] || Marat Fayzullin || MS-DOS || Version discontinued; Win32 and Linux still active<br />
|-<br />
| [http://www.kryptonware.com/ Kryptonware] || rubenhbaca || Java || [http://forums.nesdev.org/viewtopic.php?f=3&t=9395 Initial development announcement]. Website has reported "under maintenance" for a very long time<br />
|-<br />
| [https://github.com/Alegend45/MSE MSE] || Alegend45 || ? || [http://forums.nesdev.org/viewtopic.php?f=3&t=8853 Initial development announcement]. GitHub account has been deleted<br />
|-<br />
| [http://eigenbloom.com/projects/nes/nestest.php?version=1 ?] || graham || Javascript || [http://forums.nesdev.org/viewtopic.php?f=3&t=10243 Initial development announcement]. Website returns internal server error<br />
|-<br />
| [https://github.com/jonathandasilvasantos/2014-alphanes-nintendo-emulator Alphanes] || zerojnt || Go || [https://forums.nesdev.org/viewtopic.php?f=3&t=14830 Initial development announcement]<br />
|-<br />
|}</div>はるき