From: "Ki " To: nesemdev@egroups.com Subject: [nesemdev] Translated "Fds-nori.txt" toEnglish Due to couple of requests, I translated the FDS info provided by Nori. The original file you can get from nesdev.parodius.com(fds-nori.zip) is partially broken and it is not fully readable. There are some parts in this translation that I guessed what is written. Most of them should be right, but I suspect some are wrong. Also there were many parts I couldn't translate in better words due to lack of my English skill...(T-T) Please note that I can't be responsible for things written in this doc, since I'm not the one who wrote the original, nor do I have the disk system. So please use the info AT YOUR OWN RISK. Thanks, and enjoy :-). -------------------------------------------------------- *DiskSystem 1. Format of the QuickDisk Data on disk is, as shown on the table below, recorded in a unit of block. The first byte in a block is recognized as identification of the block, and data length is decided. At the start and the end of the block there are "Block Start Mark" and "Block End Mark" recorded respectively. Although it is not so important for emulators, the "Block Start Mark" is the value $80, and the "Block End Mark" is 2-byte CRC value(?) of recorded data, which is used for verifying the data in a block. Disk Layout GAP non-record period Block Start Mark ($80) shows start of the block (not recorded for FDS) Block ID (VOL Label=$01) volume label block Data (55 Bytes) Block End Mark (2Bytes) for verifying (not recorded for FDS) GAP Block Start Mark ($80) Block ID (File Amount=$02) file number block Data (1 Bytes) Block End Mark (2Bytes) GAP Block Start Mark ($80) Block ID (File Header=$03) file header block Data (15 Bytes) Block End Mark (2Bytes) GAP Block Start Mark ($80) Block ID (File Data=$04) file data block Data length is specified in the header Block End Mark (2Bytes) GAP Block Start Mark ($80) Block ID (File Header=$03) file header block : : : 1.1 Details in each block << Volume Label Block >> This block contains size, name, etc. of the game SIZE CONTENTS 1 $01 14 FC Disk String "**NINTENDO-HVC**" 1 Manufacture Code Same code as used in GameBoy(?) 4 Game Name Code 1 Game Version Number 1 Side Number 0: Side-A 1: Side-B 1 Disk Number 1 Err.9 (Ext Disk Indicate No) 1 Err.10 (Ext Disk Indicate No) 1 Boot Read File Code Specify ??? code read on boot 5 Unknown 3 Manufacture Permit Date(???) Recorded in BCD, in the year of "showa"(+1925) 10 Unknown 3 Created Date Recorded in BCD, in the year of "showa"(+1925) 9 Unknown << File Amount Block >> This block contains the total number of files recorded on disk. The info in this block is checked only at system boot. Since there may exist more files than the number recorded in this block, emulators should ignore the value recorded in this block. SIZE CONTENTS 1 $02 1 File Amount << File Header Block >> This block is recorded as the header information of the File Data Block following next. The disk system handles read operations from a disk through the file identification code. Since this process is done by BIOS, emulators only have to care about the file length. SIZE CONTENTS 1 $03 1 File Number Here the file SEQ number is recorded, but it is not checked. 1 File Indicate Code (file identification code) ID specified at disk-read function call 8 File Name 2 File Address the destination address when loading 2 File Size 1 Kind of File 0:Program(PRAM) 1:Character(CRAM) 2:Name table(VRAM) The destination is shown. << File Data Block >>  The block containing actual data. SIZE CONTENTS 1 $04 -- disk data 1.2. FDS Format There are actually two types of FDS file formats. One of which is the format used by Famtasia Ver 4 and before, and the other format is used by FanWen's fwNES. The first (Famtasia's) format uses files containing data of each side of a disk, while the second (FanWen's) format is made so that the whole game data would be in a single file. FanWen's FDS file contains 16-byte header information shown below, followed by the block ID data explained in disk layout section (1). (the "block start mark" and the "block end mark" is not recorded) SIZE CONTENTS 4 ID: "FDS^Z" 1 number of disks 11 Reserved=0 The data is fixed at 65500 bytes in each disk. 2. Disk I/O Register Adr Mask $4020 (Write) XXXXXXXX Timer Interrupt lower byte Set lower byte of timer interrupt cycles. To enable the setting, set D1 of $4022 to 1. $4021 (Write) XXXXXXXX Timer Interrupt higher byte Set upper byte of timer interrupt cycles. $4022 (Write) ??????T? Timer Interrupt Control T: Set 1 to start the timer count. Once IRQ occured, T must be set to 1 again to make another IRQ occur. $4023 (Write) ??????SD 2C33 I/O Control S:Sound I/O 0:Disable 1:Enable D:Disk I/O 0:Disable 1:Enable $4024 (Write) XXXXXXXX QD Write Data Write will be valid only when $4023.D0=1, $4025.D2=0, $4030.D7=1 $4025 (Write) IS1BMRTD Disk I/O Control I:Interrupt Transfer 0: Transfer without using IRQ 1: Enable IRQ when the drive becomes ready for read/write S:Read/Write Start Turn on motor. Set to 1 when the drive becomes ready for read/write. While in GAP period, it is required to clear the bit and set to 1 again in order to read the next block. 1:Unknown Always 1 B:Block end mark Read/Write Set 1 when reading/writing the Block End Mark. M:Miroring 0:LHLH 1:LLHH R:Read/Write Mode 0: Write process 1: Read process T:Transfer Reset Set 1 to reset transfer timing to the initial state. D:Drive Motor Control 0: Stop motor 1: Turn on motor $4026 (Write) BXXXXXXX External Connector Output Data B: Wired-ORed with BATTERY SENCE terminal Always set 1 or battery error will occur XXXXXXX: Data output to the expansion terminal on the back of the RAM adapter. Set all the bits to 1 for data input $4030 (Read) IE?B??TD Disk I/O Status I:Disk Data Read/Write Enable 1 when disk is readable/writable E:End of Head 1 when disk head is on the most inner track. B:Block end mark Exist 1 when the Block End Mark was not found. T:Disk I/O Interrupt(???) 1 when IRQ occured within the 2C33??? D:Timer Interrupt 1 when Timer IRQ has occured. $4031 (Read) XXXXXXXX QD Read Data Read is valid only when $4023.D0=1, $4025.D2=1, $4030.D7=1 $4032 (Read) ?1???PRS QD Status P:Write Protect 0: Write Enabled 1: Write Disabled (either or DiskEject) R:Drive Ready 0: Drive Ready(from the time motor is turned on, and read init is done, and until disk head arrives the inner most track, or disk is ejected) 1: Drive Not Ready S: Card Set 0: Card is set 1: Card is ejected or write-protected $4033 (Read) BXXXXXXX External Connector Input Data/Battery Sence B:Battery Sence 1: normal 0: voltage is low XXXXXXX: input data of the terminal on the back side 3. Procedure for reading/writing disk on emulators For developing emulators, follow the steps below. (controls are not as same as real machine) Although it seems that there are so many registers and is complex to emulate, read/write operations are basically done only by BIOS, and there is no need to emulate them perfectly (do try-and-error and it will be OK once you are able to read =)). Once you emulate $4025 controls correctly, you will make it. << Boot QD from BIOS >> (Emulate $4025/$4030/$4031/$4032/$4033) - It is OK to ignore D4, D0 of $4025 - $4030 is always $80 - $4031 returns data when $4025.D2=1. Other than that, it can be 0 or previous data, or anything. - For $4032, emulate D0 first, and 0 for other bits. - $4033 is always 0 - Trigger IRQ on every 100 clock cycles. - When IRQ is disabled($4025D7=0), disable IRQ even during transfer. - There is no need to care Block Start Mark / End Mark (when reading FDS format) - Start reading each block when D6 of $4025 changes from 0 --> 1. Note: Ignore any read which exceed block length Loading Disk Image on Emulators (If you want to know the timings in detail, please refer to goroh's document.) 1) Initialize FDS file pointer Initialize file position pointer on $4025 D1=1 2) Start Loading VOL Lable Start loading VOL lable on $4025 D6=0->1 $4025 Trigger IRQ on every 100 clock cycles when D7=1, and increment file position offset.  $4025 Continue loading VOL lable until D6=0->1. If exceed in data length, send dummy data (any data will be OK) When transfer complete, D7 of $4025 is set to 0, so disable IRQ. 3) Start Loading Number of Files Start loading number of file on $4025 D6=0->1. Same as 2) 4) Start Loading File Header Same as 2) 5) Start Loading File Data Same as 2) Go back to 4) when reading the next file. Note: D1 of $4025 is set to 1 when an error occur, or start reading other data, thus all must be initialized. << Writing to QD >> Writing process is done by dummy-reading until it reaches the block to write to, and then setting D2 of $4025 to 0. When D2 of $4025 is set to 0, your emulator must do following: - $4031 returns dummy data - Ignore $4024 except when $4025.D2=0 - The first write to block is the Block start mark($08) and this must be ignored (don't record to FDS file). The rest of the write process is as same as read process exept for $4024 is used, instead of $4031 used for read process. << Support for Specific Games >> - You need to implement the rest of all the registers (especially $4032) in order to support games which read/write disk in their own way, or those which check disk set/eject status. 4. Expanded Sound Sound frequency is obtained by the formula below. [Frequency][Hz] = [Frequency Value] * 27/64 5.Disk I/O Register Adr Mask $4040- $407F (Read) (Write) --XXXXXX Wave Data - Written data is valid only when D7 of $4089 is 1 - Set 1-period of wave data - 0-level of the wave data is 0x20 $4080 (Write) VFXXXXXX Volume Control V:Volume Setting 0:Fade-in/out 1:Volume Setting F:Fade-in.out Setting 0:Fade-in 1:Fade-out Note: Valid only when V=0 XXXXXX:Fade-in/out speed or Volume Value Fade-in/out speed 0(Fast)<--->3f(Slow) Volume 0(Min/off)<--->3f(Max) - Fade-in/out starts at D8(???) of $408A set to 1 $4081 ???????? Unknown $4082 (Write) XXXXXXXX Lower Frequence - Set lower byte of the base frequency - The value written will take effect immediately, despite a state of the sweep timer. - Write to this register will reset the frequency value set via $4085. $4083 (Write) ????XXXX Upper Frequence - Set upper 4 bits of the base frequency. - The value written will take effect immediately, despite a state of the sweep timer. - Write to this register will reset the frequency value set via $4085. $4084 (Write) MMTTTTTT Sweep Control 1 (Sweep Mode/Speed setting) MM:Sweep Mode Setting 00:Increase Frequence TTTTTT:Increase Speed Setting 0: Immediately set to the value of $4085 1(Fast)<--->3f(Slow) - Increase frequency towards the value of $4085 by specified speed. Note: When the value of $4085 becomes smaller than the base frequency $4082/3, the frequency once becomes 0xfff, and then the frequency is increased again from 0. 01:Decrease Frequence TTTTTT:Decrease Speed Setting 0: Immediately set to the value of $4085/2 1(Fast)<--->3f(Slow) - Decrease frequency towards the value of $4085/2 by specified speed. Note: When the value of $4085 becomes greater than the base frequency $4082/3, the frequency once becomes 0, and then the frequency is decreased again from 0xfff. 10/11:Proportional Frequence TTTTTT:Proportional Frequence Setting 0: base frequency $4082/3 <--> 3f: frequency value of $4085 0: takes effect immediately   1(Slow)<--->3f(fast)   - after specified time elapsed, set the frequency   - as different from 00/01, oppsite values of speed are set for written values to take effect. - not only frequency value set via $4085, after the amount of time set here elapsed, the LFO values set via $4086/$4087/$4088 is set. Therefore there will be no sound until the time set by this register is elapsed, even if you set values $4086-$4088 for rapid frequency changes. (it is internally counted, however) $4085 (Write) ?XXXXXXX Sweep Control2 (Sweep Frequence setting) - details are under analysis at present data&=0x7f; if( data < 0x60){ iSweepFreq = (int)( ((DWORD)iBaseFreq*data)>> 5); iSweepFreq += iBaseFreq; }else{ data=((~data)&0x1f)+1; iSweepFreq = -1*(int) ((DWORD)iBaseFreq*data)>> 5); iSweepFreq += iBaseFreq; } $4086 (Write) XXXXXXXX LFO Speed Setting(Low) - set frequency change speed. the larger, the faster. the frequency is set via $4088. - the value set here is the internal speed, and the actual timing of sound output depends on the value written to $4084. - the LFO is not effective when 0 is written to this register or D7 of $4087 is set to 1. $4087 (Write) SXXXXXXX LFO Speed Setting(High) S:$4088 Write 0:Disable 1:Enable XXXXXXX:LFO Speed (High) $4088 (Write) -----fff LFO Modulation Frequence setting fff:Moduration data:0 1 2 3 4 5 6 7 freq:0+1+2+3 0-3-2-1 - the value can be set only when D7 of $4087 is 1. - there is a 32-byte buffer for which frequency modulation can be defined. - the pointer is initialized when $4084/$4087 is accessed. $4089 (Write) W?????VV Sound Control Register 1 W:Wave data setting 0:Disable 1:Enable and Sound off VV:Reverb Level(?) - volume changes as values are written (analyzing) $408A (Write) E??????? Sound Control Register 2 E:Envelope setting 0:Disable 1:Fade-in/out Enable ----------------------------------------------------------------