As the third (and probably final) instalment in this saga I am attaching the third version of this Hydra-NES simulator. The primary upgrade for this version is the inclusion of sound. This means emulating the NES Audio Processing Unit (APU). Adding code for another cog meant that the emulation code (PPU, APU, tv driver, emulator) couldn't fit in the Hydra main memory at the same time as the ROM image (even for these 16KB games). The work-around in this version is that the game ROM is loaded into the Hydra's EEPROM and downloaded when the main emulator starts. This also allows several game ROMs to be held in the Hydra's EEPROM ready to go. I also added a "Chooser" routine which allows you to choose which ROM to play from those in the EEPROM. There are no more games included in this version (just the three in the previous version) plus one more short file which is basically an NES sound demo which I found on the Internet and which demonstrates that the APU emulator works (more or less).
For this to work you need to do the following:
1. Unzip the attached file.
2. Load the Propellor tool and click on "NES_EEPROM_loader_001.spin". This spin file includes (as an object) whatever ROM you have left uncommented in (around) line 83.
3. Load the EEPROM loader program into the Hydra with F10 and then (when it is running on the Hydra) press "A" on the game controller. This loads the ROM image that you have selected into the Hydra's EEPROM.
4. Go back to the Propellor tool and uncomment the next ROM (around line 83 again) (of course, dont forget to comment out the first one).
5. Again press F10 and then, when the EEPROM-loader program loads into the Hydra, press "A". Repeat this process until you have loaded all the ROMs that you want (or that will fit) into the available EEPROM. If something goes wrong and you want to start again, press the "B" button on the Game Controller and the number of ROMs loaded resets to zero and you have to start again. You will notice that the font changes each time you load a different ROM - this is because the font (the tilemap) is specific to each game and is loaded with the ROM image and there isn't enough space to have a separate font (tilemap) along with the EEPROM loader program and the ROM image itself.
Anyway, once you have done all this you should see the Hydra screen something like the screenshot below.
6. You are now ready to start playing the games. Click on "NES_Game_emulator_012.spin" and load it into the Hydra with F10. The screenshot below shows what should come up. You can select the game you want with the up and down arrows on the game controller. Then press "A" on the game controller and the game will download from the EEPROM into the Hydra memory and the emulation will start. You will see some garbage on the screen for a second or two (since the ROM download to main Hydra memory overwrites the memory that is temporarily used for the screen display). All going well this should disappear very quickly. Once you start playing you should be able to hear the sound.
There are also screenshots from Pacman and Galaga.
There are still some quirks and things that are not quite right. The sound emulation is good in some respects but you may hear things that don't quite sound like the original. (Particularly, the music in Donkey Kong sounds a bit odd!). Oh well... It takes so much time to get it right.
If anyone wants to persevere getting another ROM working there is the ability to run the emulator in step-by-step mode - so you can see what is happening in all of the registers and in the memory of the NES. You can also set a breakpoint and have the emulation stop when it reaches the breakpoint. However, there aren't enough cogs to run the display full speed/have sound/and run the debugger, so if you want to debug the game you have to give up either some sprites or sound(!).
Thanks·for the positive feedback and comments. I don't read this forum very often so if you want me to respond, email: darryl.biggar@stanfordalumni.org .
Awesome program...however, the rolling screen is a problem...has anyone fixed this...it looks like it the timing is off in the tv.spin program.· Are there too many vertical lines...or is the counter off?
This is the first chance I have had to try this and I was really impressed! I couldn't believe it ran at or close to full speed! I had rename a file to search for the EEPROM tile set instead of pacman tileset, but after that it worked ok. The only oddities I have noticed is that on Pacman on the intermission stage the colors are off, and in Donkey Kong the rivet level was missing the ladder graphics (maybe they were just black?) and the hammer colors were off.
I'm trying to port this over to the El' Jugador, but the assembly in the emulation core to set to read the NES controller on 3,4,5,6 instead of 23,24,25,26 as the El' Jugador uses.
Could someone fluent in Propeller assembly lend a hand?
The relevant section from: NES_emulator_core_10.spin
Joystick_read cmp ptr, #$16 wz ' $4016 read
if_nz jmp #rdmem_ret
mov _DB, INA ' read all 32-bits of input including gamepads
or OUTA, #%000001000 ' JOY_CLK = 1
shr _DB, #5 ' shift joystick data to bit 1
xor _DB, #1
and _DB, #1 '****1
and OUTA,#%111110111 ' JOY_CLK = 0
jmp #rdmem_ret
'
wrmem mov ptr, _AB '
' cmp ptr,breakpoint wz
' if_z mov emu_mode,#0
testn ptr, RAMmask wz
if_z add ptr,RAMbase
if_z jmp #write_ret
cmp ptr,H4000 wc,wz
if_ae jmp #H4000_write
and ptr,#7
cmp ptr,#3 wz,wc
if_b add ptr,PPUctrl
if_b jmp #write_ret
if_z mov OAMaddr,_DB ' $2003 _DB -> OAMoffset
if_z jmp #wrmem_ret
cmp ptr,#4 wz
if_z rdword ptr,sprite_data_ptr ' $2004 _DB -> SpriteMem[OAMoffset]
if_z add ptr,OAMaddr
if_z jmp #write_ret
cmp ptr,#5 wz
if_z rdword ptr,xscroll
if_z shl ptr,#8
if_z add ptr,_DB
if_z wrword ptr,xscroll
if_z jmp #wrmem_ret ' 2005 - not used so save the space
cmp ptr,#6 wz
if_z shl VRAMaddress,#8 ' $2006 _DB -> upper or lower byte of VRAMaddress
if_z add VRAMaddress,_DB
if_z and VRAMaddress,OOOOFFFF
if_z jmp #wrmem_ret
mov temp,VRAMaddress ' $2007 _DB -> PatternTableMem[VRAMaddress]
cmp temp,H3F00 wc,wz
if_ae jmp #H3F00_write
' test temp,H2000 wz
' if_z rdword ptr,pattern_table_ptr
' if_z jmp #write1
test temp,H0800 wz
and temp,H03FF
if_nz add temp,H0400
rdword ptr,name_table_ptr
write1 add ptr,temp
rdbyte temp,PPUctrl
test temp,#4 wz
if_z add VRAMaddress,#1
if_nz add VRAMaddress,#32
write_ret wrbyte _DB, ptr
wrmem_ret ret
H3F00_write and temp,#31
rdword ptr,pallette_ptr
' mov emu_mode,#0
jmp #write1
H4000_write cmp ptr,DMAaccess wz
if_nz jmp #Joystick_write
mov ptr,_DB ' DMAaccess write
shl ptr,#8
add ptr,RAMbase
wrword ptr,sprite_data_ptr
jmp #wrmem_ret
Joystick_write cmp ptr,JoystickDMA wz
if_nz jmp #invalid_write
or DIRA, #%000011000 ' JOY_CLK and JOY_SH/LDn to outputs
and DIRA, #%110011111 ' JOY_DATAOUT0 and JOY_DATAOUT1 to inputs
ror _DB,#1 wc '***** 1
if_c or OUTA, #%000010000 ' JOY_SH/LDn = 1
if_nc and OUTA, #%111101111 ' JOY_SH/LDn = 0
jmp #wrmem_ret
That's as far as I can go without having h/w. If you know that the previous setup worked [3..6] then simply subtract 20 from the mask shifts and adjust the shr in read again. That should restore previous functionality. Do you have test programs for this? Is there any interference from other objects (the top level object uses SPIN to read from 3..6)?
Beats me. Can you verify that the latch/clk pins do anything at all? Or are they completely static?
Note that the core file has a few variable definitions *after* res. ctr and temp look OK, not sure about the 2 address entries though. Anyway, this shouldn't affect the joystick interface.
Maybe it's too early yet ... but from the looks of it all you added was this:
Joystick_read cmp ptr, #$16 wz ' $4016 read
if_nz jmp #rdmem_ret
mov _DB, INA ' read all 32-bits of input including gamepads
or OUTA, clk_mask ' JOY_CLK = 1
shr _DB, #25 ' shift joystick data to bit 1
xor _DB, #1
and _DB, #1
[COLOR="red"]and dira,clk_mask[/COLOR]
andn OUTA, clk_mask ' JOY_CLK = 0
jmp #rdmem_ret
All this does is reset the load/shift output (if previously set in write) which isn't even used in this subroutine. What am I missing here? I mean the way the code was originally you had to issue at least one write before you could read as clock and load/shift are only configured during write. That's why my initial comment to do dira configuration during cog startup.
I just had a look at my first gamepad driver (Hydra), latching is done by sending a high pulse (LHL) then clock-in the data. Can you somehow verify that this sequence is used here? Looks like not driving load/shift somehow affects its level?!
Comments
For this to work you need to do the following:
1. Unzip the attached file.
2. Load the Propellor tool and click on "NES_EEPROM_loader_001.spin". This spin file includes (as an object) whatever ROM you have left uncommented in (around) line 83.
3. Load the EEPROM loader program into the Hydra with F10 and then (when it is running on the Hydra) press "A" on the game controller. This loads the ROM image that you have selected into the Hydra's EEPROM.
4. Go back to the Propellor tool and uncomment the next ROM (around line 83 again) (of course, dont forget to comment out the first one).
5. Again press F10 and then, when the EEPROM-loader program loads into the Hydra, press "A". Repeat this process until you have loaded all the ROMs that you want (or that will fit) into the available EEPROM. If something goes wrong and you want to start again, press the "B" button on the Game Controller and the number of ROMs loaded resets to zero and you have to start again. You will notice that the font changes each time you load a different ROM - this is because the font (the tilemap) is specific to each game and is loaded with the ROM image and there isn't enough space to have a separate font (tilemap) along with the EEPROM loader program and the ROM image itself.
Anyway, once you have done all this you should see the Hydra screen something like the screenshot below.
6. You are now ready to start playing the games. Click on "NES_Game_emulator_012.spin" and load it into the Hydra with F10. The screenshot below shows what should come up. You can select the game you want with the up and down arrows on the game controller. Then press "A" on the game controller and the game will download from the EEPROM into the Hydra memory and the emulation will start. You will see some garbage on the screen for a second or two (since the ROM download to main Hydra memory overwrites the memory that is temporarily used for the screen display). All going well this should disappear very quickly. Once you start playing you should be able to hear the sound.
There are also screenshots from Pacman and Galaga.
There are still some quirks and things that are not quite right. The sound emulation is good in some respects but you may hear things that don't quite sound like the original. (Particularly, the music in Donkey Kong sounds a bit odd!). Oh well... It takes so much time to get it right.
If anyone wants to persevere getting another ROM working there is the ability to run the emulator in step-by-step mode - so you can see what is happening in all of the registers and in the memory of the NES. You can also set a breakpoint and have the emulation stop when it reaches the breakpoint. However, there aren't enough cogs to run the display full speed/have sound/and run the debugger, so if you want to debug the game you have to give up either some sprites or sound(!).
Thanks·for the positive feedback and comments. I don't read this forum very often so if you want me to respond, email: darryl.biggar@stanfordalumni.org .
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
http://www.propgfx.co.uk/forum/·home of the PropGFX Lite
·
Bill
In the TV program change 'mov·· x,vf' to 'mov··· x,#2'
Just like I thought...the old program had too many scanlines
··
But as whole really cool!
Could someone fluent in Propeller assembly lend a hand?
The relevant section from: NES_emulator_core_10.spin
What are the mask definitions?
OBC
The ones in the middle? Or are you asking why they are used (9bit immediate constant limitation)?
OBC
yeah. Here's the entire chunk of code I probably should have posted just in case I've missed something relevant.
OBC
NES_emulator_core_010.spin
Corrected in this version.. Not working quite yet..
OBC
Yes
Note that the core file has a few variable definitions *after* res. ctr and temp look OK, not sure about the 2 address entries though. Anyway, this shouldn't affect the joystick interface.
@Roadster, you hit the nail right on the head!
A quick review of Graham's Assembler howto showed me where it should go.
Thanks to Kuroneko & Roadster we have an easily adjustable version of the soundless version of this emulator.
Nes Emulator compatible with Propeller Platform & El' Jugador:
http://dl.dropbox.com/u/7557533/Propellerpowered/NES_Emulator_4_ElJugador_Soundless.zip
Now to see if I can get the newer version working on this setup....
I'm no Propeller Assembly expert (Heck I don't even play one on TV), but I do know that things didn't start clicking until I put that line in.
If you assembly masterminds can figure out why, by all means post up.. But it works now.
OBC