Shop OBEX P1 Docs P2 Docs Learn Events
Hydra NES Pacman Emulator - Page 2 — Parallax Forums

Hydra NES Pacman Emulator

2»

Comments

  • DarrylDarryl Posts: 3
    edited 2010-03-31 10:30
    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 .
    640 x 480 - 26K
    640 x 480 - 21K
    640 x 480 - 41K
    640 x 480 - 23K
  • BaggersBaggers Posts: 3,019
    edited 2010-03-31 21:27
    Excellent update Darryl [noparse]:D[/noparse] well done yet again!

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    http://www.propgfx.co.uk/forum/·home of the PropGFX Lite

    ·
  • wjsteelewjsteele Posts: 697
    edited 2010-04-02 14:36
    Wow!!! Just wow! Amazing work!

    Bill
  • dr hydradr hydra Posts: 212
    edited 2010-06-14 01:07
    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?
  • dr hydradr hydra Posts: 212
    edited 2010-06-28 03:00
    Got it..Got it

    In the TV program change 'mov·· x,vf' to 'mov··· x,#2'

    Just like I thought...the old program had too many scanlines

    ··
  • JT CookJT Cook Posts: 487
    edited 2010-07-28 02:04
    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.

    But as whole really cool!
  • Oldbitcollector (Jeff)Oldbitcollector (Jeff) Posts: 8,091
    edited 2011-11-24 19:15
    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        
    
  • kuronekokuroneko Posts: 3,623
    edited 2011-11-24 19:46
    Untested (joystick write setup should only be done once really):
    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 '****1
                            andn    OUTA, clk_mask     ' JOY_CLK = 0
                            jmp     #rdmem_ret
    
    clk_mask                long    |< 23
    sld_mask                long    |< 24
    dta_mask                long    |< 25 | |< 26
    
    Joystick_write          cmp     ptr,JoystickDMA wz
            if_nz           jmp     #invalid_write
    
                            [COLOR="orange"]or      DIRA, clk_mask[/COLOR]             ' |
                            [COLOR="orange"]or      DIRA, sld_mask[/COLOR]             ' JOY_CLK and JOY_SH/LDn to outputs
                            [COLOR="orange"]andn    DIRA, dta_mask[/COLOR]             ' JOY_DATAOUT0 and JOY_DATAOUT1 to inputs
    
                            ror     _DB,#1 wc '***** 1
                            muxc    OUTA, sld_mask             ' JOY_SH/LDn = 1/0
                            jmp     #wrmem_ret
    
    Is it correct that only one data input is used during read?
  • Oldbitcollector (Jeff)Oldbitcollector (Jeff) Posts: 8,091
    edited 2011-11-24 20:00
    Yes that is correct..

    What are the mask definitions?

    OBC
  • kuronekokuroneko Posts: 3,623
    edited 2011-11-24 20:04
    What are the mask definitions?

    The ones in the middle? Or are you asking why they are used (9bit immediate constant limitation)?
    clk_mask                long    |< 23
    sld_mask                long    |< 24
    dta_mask                long    |< 25 | |< 26
    
  • Oldbitcollector (Jeff)Oldbitcollector (Jeff) Posts: 8,091
    edited 2011-11-24 20:43
    Ok, I see where you are going with this.. Makes sense, but doesn't work yet..

    OBC
  • kuronekokuroneko Posts: 3,623
    edited 2011-11-24 20:58
    You are using andn instead of and now?
  • Oldbitcollector (Jeff)Oldbitcollector (Jeff) Posts: 8,091
    edited 2011-11-24 21:13
    NES_emulator_core_010.spin

    yeah. Here's the entire chunk of code I probably should have posted just in case I've missed something relevant.

    OBC
  • kuronekokuroneko Posts: 3,623
    edited 2011-11-24 21:20
    In Joystick_read it's still and (i.e. no clock). And the shr is 25 now. Please review post #39.
  • Oldbitcollector (Jeff)Oldbitcollector (Jeff) Posts: 8,091
    edited 2011-11-24 21:33
    Yes, I missed those! (Must be all the turkey)

    NES_emulator_core_010.spin
    Corrected in this version.. Not working quite yet..

    OBC
  • kuronekokuroneko Posts: 3,623
    edited 2011-11-24 21:42
    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)?
  • kuronekokuroneko Posts: 3,623
    edited 2011-11-24 21:50
    The Hydra TV is on pins 24..26 (%011_0000). Did you adjust the TV driver?
  • Oldbitcollector (Jeff)Oldbitcollector (Jeff) Posts: 8,091
    edited 2011-11-24 21:55
    kuroneko wrote: »
    The Hydra TV is on pins 24..26 (%011_0000). Did you adjust the TV driver?

    Yes
  • kuronekokuroneko Posts: 3,623
    edited 2011-11-24 22:13
    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.
  • RoadsterRoadster Posts: 209
    edited 2011-11-25 06:13
    I have not had time to look at the code yet, but did you setup the pins correctly input / output
  • Oldbitcollector (Jeff)Oldbitcollector (Jeff) Posts: 8,091
    edited 2011-11-25 12:37
    Forum glitched.. see post below..
  • Oldbitcollector (Jeff)Oldbitcollector (Jeff) Posts: 8,091
    edited 2011-11-25 12:38
    Roadster wrote: »
    I have not had time to look at the code yet, but did you setup the pins correctly input / output

    @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....
  • kuronekokuroneko Posts: 3,623
    edited 2011-11-25 14:46
    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.
  • Oldbitcollector (Jeff)Oldbitcollector (Jeff) Posts: 8,091
    edited 2011-11-25 15:39
    It's true..

    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
  • kuronekokuroneko Posts: 3,623
    edited 2011-11-25 15:47
    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?!
Sign In or Register to comment.