User:Zzo38/Hardware NSF: Difference between revisions
No edit summary |
No edit summary |
||
Line 7: | Line 7: | ||
When the main routine is enabled, $8000-$BFFF is a single byte of RAM (used to store the current song number; mirrored) and $C000-$FFFF is ROM (also mirrored; not much ROM is needed). | When the main routine is enabled, $8000-$BFFF is a single byte of RAM (used to store the current song number; mirrored) and $C000-$FFFF is ROM (also mirrored; not much ROM is needed). | ||
Writing to $4018-$403F: If bit2 is set, the memory from $6000-$DFFF becomes read/write, otherwise it is read-only. If bit0 is set, VRC6 audio will play, otherwise it will be muted. If bit1 is set, VRC7 audio will play and otherwise is muted. If bit5 is set, Sunsoft 5B audio is played and is otherwise muted. Other bits (bit3, bit4, bit6, and bit7) are ignored. | Writing to $4018-$403F: If bit2 is set, the memory from $6000-$DFFF becomes read/write (except main routine ROM which is always read-only), otherwise it is read-only. If bit0 is set, VRC6 audio will play, otherwise it will be muted. If bit1 is set, VRC7 audio will play and otherwise is muted. If bit5 is set, Sunsoft 5B audio is played and is otherwise muted. Other bits (bit3, bit4, bit6, and bit7) are ignored. | ||
Writing to $C000-$FFFF enables main routine ROM if the low bit of the data is set, and disables if the low bit is clear. Other bits are ignored. | Writing to $C000-$FFFF enables main routine ROM if the low bit of the data is set, and disables if the low bit is clear. Other bits are ignored. | ||
Line 14: | Line 14: | ||
Another thing the hardware will do is convert the audio to digital and whenever the name tables are being accessed, make the pattern tables read as a waveform view of the music being played. | Another thing the hardware will do is convert the audio to digital and whenever the name tables are being accessed, make the pattern tables read as a waveform view of the music being played. | ||
==PRG ROM Layout== | |||
+-----+-----------------------+ | |||
| 1FF | Interrupt vectors | | |||
| 1FA | | | |||
+-----+-----------------------+ | |||
| 1F9 | Main routine | | |||
| 100 | | | |||
+-----+-----------------------+ | |||
| 0FF | .NSF header | | |||
| 080 | | | |||
+-----+-----------------------+ | |||
| 07F | Main routine helper | | |||
| 040 | | | |||
+-----+-----------------------+ | |||
| 03F | Interrupt routine | | |||
| 018 | | | |||
+-----+-----------------------+ | |||
| 017 | Stop routine | | |||
| 000 | | | |||
+-----+-----------------------+ | |||
==CHR ROM Layout== | |||
CHR ROM is ASCII, but tile $00 is blank, and tiles $10-$1F contain the hex digits "0" to "F". | |||
Printable ASCII characters use color 3 for the foreground, and hex digits use color 1 for the foreground. All tiles use color 0 for the background. This way, the palette can be changed to make it change color of the song number digits to indicate play/stop. | |||
While rendering rows below the fourth row, the CHR ROM will not be accessible and instead they will be connected to a analog-to-digital converter which puts the same data regardless of address (but which is depending on the position on screen). | |||
==Main Routine== | ==Main Routine== | ||
Line 35: | Line 65: | ||
*** Initialize the sound registers by writing $00 to $4000-$4013, $0F to $4015. | *** Initialize the sound registers by writing $00 to $4000-$4013, $0F to $4015. | ||
*** Initialize the frame counter to 4-step mode ($40 to $4017). | *** Initialize the frame counter to 4-step mode ($40 to $4017). | ||
*** Write initial bank values to $5FF6-$5FFF. | *** Write initial bank values to $5FF6-$5FFF. (If the initial bank values are all zero, instead compute them from the load address.) | ||
*** Update display to indicate song is playing. | *** Update display to indicate song is playing. | ||
*** Load value at memory $8000 into the accumulator. | *** Load value at memory $8000 into the accumulator. | ||
Line 82: | Line 112: | ||
CLI ; 1/- | CLI ; 1/- | ||
JMP wait ; 3/- | JMP wait ; 3/- | ||
==Notes== | |||
Depending on how the data is loaded onto the cartridge, some things may need to differ a bit, for example it may need to include codes to read the file. This may mean having 128 bytes of RAM in $8000-$BFFF instead of only 1 byte, and use this for the .NSF header (header $07 could be reused for the current song number), and then have another code to load the banks by first setting bit2 of $4018, and then after it is loaded, copy header $7B to $4018 which may or may not clear bit2. |
Revision as of 22:43, 3 September 2012
These are some of my ideas for hardware NSF.
Hardware
Memory from $4018-$403F would be filled by a 40-byte ROM used for an interrupt routine. (Famicom Disk System uses some of these addresses but NSF only uses the audio registers, not these ones.)
When the main routine is enabled, $8000-$BFFF is a single byte of RAM (used to store the current song number; mirrored) and $C000-$FFFF is ROM (also mirrored; not much ROM is needed).
Writing to $4018-$403F: If bit2 is set, the memory from $6000-$DFFF becomes read/write (except main routine ROM which is always read-only), otherwise it is read-only. If bit0 is set, VRC6 audio will play, otherwise it will be muted. If bit1 is set, VRC7 audio will play and otherwise is muted. If bit5 is set, Sunsoft 5B audio is played and is otherwise muted. Other bits (bit3, bit4, bit6, and bit7) are ignored.
Writing to $C000-$FFFF enables main routine ROM if the low bit of the data is set, and disables if the low bit is clear. Other bits are ignored.
The hardware generate IRQ at the rate specified in the NSF header. When IRQ is called, the IRQ vector should be read as $4018 whenever it is checked.
Another thing the hardware will do is convert the audio to digital and whenever the name tables are being accessed, make the pattern tables read as a waveform view of the music being played.
PRG ROM Layout
+-----+-----------------------+ | 1FF | Interrupt vectors | | 1FA | | +-----+-----------------------+ | 1F9 | Main routine | | 100 | | +-----+-----------------------+ | 0FF | .NSF header | | 080 | | +-----+-----------------------+ | 07F | Main routine helper | | 040 | | +-----+-----------------------+ | 03F | Interrupt routine | | 018 | | +-----+-----------------------+ | 017 | Stop routine | | 000 | | +-----+-----------------------+
CHR ROM Layout
CHR ROM is ASCII, but tile $00 is blank, and tiles $10-$1F contain the hex digits "0" to "F".
Printable ASCII characters use color 3 for the foreground, and hex digits use color 1 for the foreground. All tiles use color 0 for the background. This way, the palette can be changed to make it change color of the song number digits to indicate play/stop.
While rendering rows below the fourth row, the CHR ROM will not be accessible and instead they will be connected to a analog-to-digital converter which puts the same data regardless of address (but which is depending on the position on screen).
Main Routine
- Disable IRQ.
- Wait for PPU.
- Write the starting song number (offset $07 of header) to $8000, and then decrement it.
- Copy expansion chip byte (offset $7B of header) to $4018.
- Set up palette.
- Fill up the nametable with the title and so on from the NSF header, and with the current and maximum song number, and indicate the music is stopped.
- Enable background.
- Loop:
- Update the display of the current song number.
- If LEFT is pushed:
- Decrement current song number. If it is zero it wraps around.
- Wait for button is not pushed.
- If RIGHT is pushed:
- Increment current song number. If it is too high it wraps to zero.
- Wait for button is not pushed.
- If START is pushed:
- Initialize the sound registers by writing $00 to $4000-$4013, $0F to $4015.
- Initialize the frame counter to 4-step mode ($40 to $4017).
- Write initial bank values to $5FF6-$5FFF. (If the initial bank values are all zero, instead compute them from the load address.)
- Update display to indicate song is playing.
- Load value at memory $8000 into the accumulator.
- Load zero into the X register.
- Jump to the start routine.
- Stop routine:
- Disable interrupts.
- Turn off all audio.
- Reset the stack pointer.
- Update display to indicate song is stopped.
- Go to main loop checking which button is pushed.
Interrupt Routine
- Check if the A button is pushed.
- If the A button is pushed (it should branch if button pushed rather than skip over if unpushed because this way uses less clock cycles):
- Enable main routine ROM.
- Jump to the stop routine.
- Reset the stack pointer.
- Call the play subroutine.
- Enable interrupts.
- Run an idle loop doing nothing.
- Start routine:
- Disable main routine ROM.
- Call the init subroutine.
- Enable interrupts.
- Run an idle loop doing nothing.
Code (40 bytes, 30 clock):
.ORG $4018 intr LDX #1 ; 2/2 (X=1) STX $4016 ; 3/4 (strobe) DEX ; 1/2 (X=0) STX $4016 ; 3/4 (allow buttons to be read) LDA $4016 ; 3/4 (read "A" button) BNE stopa ; 2/2 (branch if button pushed) DEX ; 1/2 (X=255) TXS ; 1/2 (reset stack) JSR play ; 3/6 CLI ; 1/2 wait JMP wait ; 3/- (wait) stopa STA $C000 ; 3/- (write 1 to $C000) JMP stop ; 3/- start STX $C000 ; 3/- (write 0 to $C000) JSR init ; 3/- CLI ; 1/- JMP wait ; 3/-
Notes
Depending on how the data is loaded onto the cartridge, some things may need to differ a bit, for example it may need to include codes to read the file. This may mean having 128 bytes of RAM in $8000-$BFFF instead of only 1 byte, and use this for the .NSF header (header $07 could be reused for the current song number), and then have another code to load the banks by first setting bit2 of $4018, and then after it is loaded, copy header $7B to $4018 which may or may not clear bit2.