I have ported my old project “pNesX” game console emulator to the nucleo.
Dependencies: SDFileSystem mbed
Intro
I have ported my old project “pNesX” to the STM32 Nucleo. The pNesX is a NES emulator for the PlayStation that I have created 16 years ago!
Emulation part was almost without change, the sound part was newly added.
Parts
STM32 Nucleo F446RE |
QVGA 2.2 TFT SPI (with the SD card slot) |
Audio jack(TS or TRS) |
USB Connector |
Register 100k, 10k, 4.7k, 100 |
Capacitor 0.01uF, 2.2uF |
Breadboard |
Wires |
Computer Speakers |
USB GamePad |
Wiring diagram
TFT J2 | Nucleo |
---|---|
VCC | 3V3 |
GND | GND |
CS | PB_5(D4) |
Reset | PA_10(D2) Pull Up(100k) |
D/C | PA_8(D7) |
MOSI | PA_7(D11) |
SCK | PA_5(D13) |
LED | LED-100ohm-3V3 |
MISO | PA_6(D12) |
TFT J4 | Nucleo |
---|---|
SD_CS | PA_9 |
SD_MOSI | PB_15 |
SD_MISO | PB_14 |
SD_SCK | PB_13 |
Audio | Nucleo |
---|---|
TIP | PA_4(A2) |
USB con. | Nucleo |
---|---|
GND | GND |
+ | PA_12 |
- | PA_11 |
5V | 5V |
Limitations
- Since the rest of the RAM is about 50kbyte, maximum capacity of the game ROM is about 50kbyte.
- The length of the file name up to 32 characters.
- The number of files in the folder is up to 100.
Used Library
- SDFileSystem by Neil Thiessen
- F401RE-USBHost by Norimasa Okamoto
- USBHostGamepad by Yuuichi Akagawa
pNesX_Mapper.cpp
- Committer:
- beaglescout007
- Date:
- 2016-04-03
- Revision:
- 0:3dac1f1bc9e0
File content as of revision 0:3dac1f1bc9e0:
/*===================================================================*/ /* */ /* pNesX_Mapper.cpp : pNesX Mapper Function */ /* */ /* 1999/11/03 Racoon New preparation */ /* */ /*===================================================================*/ /*-------------------------------------------------------------------*/ /* Include files */ /*-------------------------------------------------------------------*/ #include "pNesX.h" #include "pNesX_System.h" #include "pNesX_Mapper.h" #include "K6502.h" /*-------------------------------------------------------------------*/ /* Table of Mapper initialize function */ /*-------------------------------------------------------------------*/ struct MapperTable_tag MapperTable[] = { { 0, Map0_Init }, { 1, Map1_Init }, { 2, Map2_Init }, { 3, Map3_Init }, { 4, Map4_Init }, { -1, NULL } }; /*===================================================================*/ /* */ /* Mapper 0 */ /* */ /*===================================================================*/ /*-------------------------------------------------------------------*/ /* Initialize Mapper 0 */ /*-------------------------------------------------------------------*/ void Map0_Init() { /* Initialize Mapper */ MapperInit = Map0_Init; /* Write to Mapper */ MapperWrite = Map0_Write; /* Callback at VSync */ MapperVSync = Map0_VSync; /* Callback at HSync */ MapperHSync = Map0_HSync; /* Set ROM Banks */ ROMBANK0 = ROMPAGE( 0 ); ROMBANK1 = ROMPAGE( 1 ); ROMBANK2 = ROMLASTPAGE( 1 ); ROMBANK3 = ROMLASTPAGE( 0 ); /* Set PPU Banks */ if ( NesHeader.byVRomSize > 0 ) { for ( int nPage = 0; nPage < 8; ++nPage ) PPUBANK[ nPage ] = &VROM[ nPage * 0x400 ]; pNesX_SetupChr(); } /* Set up wiring of the interrupt pin */ K6502_Set_Int_Wiring( 1, 1 ); } /*-------------------------------------------------------------------*/ /* Mapper 0 Write Function */ /*-------------------------------------------------------------------*/ void Map0_Write( WORD wAddr, BYTE byData ) { /* * Dummy Write to Mapper * */ } /*-------------------------------------------------------------------*/ /* Mapper 0 V-Sync Function */ /*-------------------------------------------------------------------*/ void Map0_VSync() { /* * Dummy Callback at VSync * */ } /*-------------------------------------------------------------------*/ /* Mapper 0 H-Sync Function */ /*-------------------------------------------------------------------*/ void Map0_HSync() { /* * Dummy Callback at HSync * */ } /*===================================================================*/ /* */ /* Mapper 1 */ /* */ /*===================================================================*/ BYTE Map1_Reg[ 4 ]; int Map1_Cnt; BYTE Map1_Latch; /*-------------------------------------------------------------------*/ /* Initialize Mapper 1 */ /*-------------------------------------------------------------------*/ void Map1_Init() { int nPage; /* Initialize Mapper */ MapperInit = Map1_Init; /* Write to Mapper */ MapperWrite = Map1_Write; /* Callback at VSync */ MapperVSync = Map0_VSync; /* Callback at HSync */ MapperHSync = Map0_HSync; pNesX_MemorySet( Map1_Reg, 0, sizeof Map1_Reg ); Map1_Cnt = Map1_Latch = 0; /* Set ROM Banks */ ROMBANK0 = ROMPAGE( 0 ); ROMBANK1 = ROMPAGE( 1 ); ROMBANK2 = ROMLASTPAGE( 1 ); ROMBANK3 = ROMLASTPAGE( 0 ); /* Set PPU VROM Banks */ if ( NesHeader.byVRomSize > 0 ) { for ( nPage = 0; nPage < 8; ++nPage ) PPUBANK[ nPage ] = &VROM[ nPage * 0x400 ]; pNesX_SetupChr(); } /* Set up wiring of the interrupt pin */ K6502_Set_Int_Wiring( 1, 1 ); } /*-------------------------------------------------------------------*/ /* Mapper 1 Write Function */ /*-------------------------------------------------------------------*/ void Map1_Write( WORD wAddr, BYTE byData ) { /* * MMC1 */ int nReg; int nROMPos; int nBank; int nVBank; int nPage; nReg = ( wAddr >> 13 ) - 4; if ( byData & 0x80 ) { // Reset Map1_Reg[ 0 ] |= 0xc; Map1_Cnt = 0; Map1_Latch = 0; return; } else { // Bit Data Set Map1_Latch |= ( ( byData & 1 ) << Map1_Cnt ); ++Map1_Cnt; } if ( Map1_Cnt < 5 ) return; // 5Bits Latched Map1_Reg[ nReg ] = ( nReg == 3 ) ? ( Map1_Latch & 0xf ) : Map1_Latch; Map1_Cnt = 0; Map1_Latch = 0; // Name Table Mirroring pNesX_Mirroring( ( Map1_Reg[ 0 ] & 3 ) ^ 3 ); // Select Program ROM Bank // 256K ROM Position Selection nROMPos = 0; if ( NesHeader.byRomSize == 32 ) { /* 512K Program ROM */ // 0 (First 256K) or 32 (Second 256K) nROMPos = ( Map1_Reg[ 1 ] & 0x10 ) << 1; } else if ( NesHeader.byRomSize == 64 ) { /* 1024K Program ROM */ if ( Map1_Reg[ 0 ] & 0x10 ) { // Acknowledge 256K selection register 1 nROMPos = ( ( Map1_Reg[ 1 ] & 0x10 ) << 1 ) | ( ( Map1_Reg[ 2 ] & 0x10 ) << 2 ); } else { // Ignore 256K selection register 1 // 0 (First 256K) or 64 (Third 256K) nROMPos = ( Map1_Reg[ 1 ] & 0x10 ) << 2; } } // Set Program ROM Bank if ( !( Map1_Reg[ 0 ] & 8 ) ) { // 32K ROM Bank /* Set ROM Banks */ nBank = ( ( Map1_Reg[ 3 ] << 2 ) + nROMPos ) % ( NesHeader.byRomSize << 1 ); ROMBANK0 = ROMPAGE( nBank ); ROMBANK1 = ROMPAGE( nBank + 1 ); ROMBANK2 = ROMPAGE( nBank + 2 ); ROMBANK3 = ROMPAGE( nBank + 3 ); } else if ( Map1_Reg[ 0 ] & 4 ) { // 16K ROM Bank at 0x8000 nBank = ( ( Map1_Reg[ 3 ] << 1 ) + nROMPos ) % ( NesHeader.byRomSize << 1 ); ROMBANK0 = ROMPAGE( nBank ); ROMBANK1 = ROMPAGE( nBank + 1 ); ROMBANK2 = ROMLASTPAGE( 1 ); ROMBANK3 = ROMLASTPAGE( 0 ); } else { // 16K ROM Bank at 0xc000 nBank = ( ( Map1_Reg[ 3 ] << 1 ) + nROMPos ) % ( NesHeader.byRomSize << 1 ); ROMBANK0 = ROMPAGE( 0 ); ROMBANK1 = ROMPAGE( 1 ); ROMBANK2 = ROMPAGE( nBank ); ROMBANK3 = ROMPAGE( nBank + 1 ); } // Select PPU VROM Bank if ( NesHeader.byVRomSize > 0 ) { if ( Map1_Reg[ 0 ] & 0x10 ) { // 4K VROM Switching nVBank = Map1_Reg[ 1 ] % ( NesHeader.byVRomSize << 1 ); for ( nPage = 0; nPage < 4; ++nPage ) PPUBANK[ nPage ] = &VROM[ nVBank * 0x1000 + nPage * 0x400 ]; nVBank = Map1_Reg[ 2 ] % ( NesHeader.byVRomSize << 1 ); for ( nPage = 0; nPage < 4; ++nPage ) PPUBANK[ nPage + 4 ] = &VROM[ nVBank * 0x1000 + nPage * 0x400 ]; } else { // 8K VROM Switching nVBank = ( Map1_Reg[ 1 ] & 0xe ) % ( NesHeader.byVRomSize << 1 ); for ( nPage = 0; nPage < 8; ++nPage ) PPUBANK[ nPage ] = &VROM[ nVBank * 0x1000 + nPage * 0x400 ]; } pNesX_SetupChr(); } } /*===================================================================*/ /* */ /* Mapper 2 (UNROM) */ /* */ /*===================================================================*/ /*-------------------------------------------------------------------*/ /* Initialize Mapper 2 */ /*-------------------------------------------------------------------*/ void Map2_Init() { /* Initialize Mapper */ MapperInit = Map2_Init; /* Write to Mapper */ MapperWrite = Map2_Write; /* Callback at VSync */ MapperVSync = Map0_VSync; /* Callback at HSync */ MapperHSync = Map0_HSync; /* Set ROM Banks */ ROMBANK0 = ROMPAGE( 0 ); ROMBANK1 = ROMPAGE( 1 ); ROMBANK2 = ROMLASTPAGE( 1 ); ROMBANK3 = ROMLASTPAGE( 0 ); /* Set up wiring of the interrupt pin */ K6502_Set_Int_Wiring( 1, 1 ); } /*-------------------------------------------------------------------*/ /* Mapper 2 Write Function */ /*-------------------------------------------------------------------*/ void Map2_Write( WORD wAddr, BYTE byData ) { /* Set ROM Banks */ byData %= NesHeader.byRomSize; byData <<= 1; ROMBANK0 = ROMPAGE( byData ); ROMBANK1 = ROMPAGE( byData + 1 ); } /*===================================================================*/ /* */ /* Mapper 3 (VROM Switch) */ /* */ /*===================================================================*/ /*-------------------------------------------------------------------*/ /* Initialize Mapper 3 */ /*-------------------------------------------------------------------*/ void Map3_Init() { int nPage; /* Initialize Mapper */ MapperInit = Map3_Init; /* Write to Mapper */ MapperWrite = Map3_Write; /* Callback at VSync */ MapperVSync = Map0_VSync; /* Callback at HSync */ MapperHSync = Map0_HSync; /* Set ROM Banks */ ROMBANK0 = ROMPAGE( 0 ); ROMBANK1 = ROMPAGE( 1 ); ROMBANK2 = ROMLASTPAGE( 1 ); ROMBANK3 = ROMLASTPAGE( 0 ); /* Set PPU Banks */ if ( NesHeader.byVRomSize > 0 ) { for ( nPage = 0; nPage < 8; ++nPage ) PPUBANK[ nPage ] = &VROM[ nPage * 0x400 ]; pNesX_SetupChr(); } /* Set up wiring of the interrupt pin */ /* "DragonQuest" doesn't run if IRQ isn't made to occur in CLI */ K6502_Set_Int_Wiring( 1, 0 ); } /*-------------------------------------------------------------------*/ /* Mapper 3 Write Function */ /*-------------------------------------------------------------------*/ void Map3_Write( WORD wAddr, BYTE byData ) { int nPage; /* Set PPU Banks */ byData %= NesHeader.byVRomSize; for ( nPage = 0; nPage < 8; ++nPage ) PPUBANK[ nPage ] = &VROM[ byData * 0x2000 + nPage * 0x400 ]; pNesX_SetupChr(); } /*===================================================================*/ /* */ /* Mapper 4 (MMC3) */ /* */ /*===================================================================*/ BYTE Map4_VROM_Base; BYTE Map4_ROM_Base; BYTE Map4_Cmd; BYTE Map4_Banks_Reg[ 8 ]; BYTE Map4_IRQ_Cnt; BYTE Map4_IRQ_Set; BYTE Map4_IRQ_Enable; /*-------------------------------------------------------------------*/ /* Initialize Mapper 4 */ /*-------------------------------------------------------------------*/ void Map4_Init() { /* Initialize Mapper */ MapperInit = Map4_Init; /* Write to Mapper */ MapperWrite = Map4_Write; /* Callback at VSync */ MapperVSync = Map4_VSync; /* Callback at HSync */ MapperHSync = Map4_HSync; Map4_VROM_Base = Map4_ROM_Base = Map4_Cmd = Map4_IRQ_Cnt = Map4_IRQ_Set = Map4_IRQ_Enable = 0; Map4_Banks_Reg[ 0 ] = 0; Map4_Banks_Reg[ 1 ] = 2; Map4_Banks_Reg[ 2 ] = 4; Map4_Banks_Reg[ 3 ] = 5; Map4_Banks_Reg[ 4 ] = 6; Map4_Banks_Reg[ 5 ] = 7; Map4_Banks_Reg[ 6 ] = 0; Map4_Banks_Reg[ 7 ] = 1; Map4_Write( 0x8000, 0 ); Map4_Write( 0x8001, 0 ); /* Set up wiring of the interrupt pin */ K6502_Set_Int_Wiring( 1, 1 ); } /*-------------------------------------------------------------------*/ /* Mapper 4 Write Function */ /*-------------------------------------------------------------------*/ void Map4_Write( WORD wAddr, BYTE byData ) { WORD wMapAddr; wMapAddr = wAddr & 0xe001; switch ( wMapAddr ) { case 0xa000: // Name Table Mirroring pNesX_Mirroring( ~byData & 1 ); break; case 0xa001: break; case 0xc000: Map4_IRQ_Cnt = byData; break; case 0xc001: Map4_IRQ_Set = byData; break; case 0xe000: Map4_IRQ_Cnt = Map4_IRQ_Set; Map4_IRQ_Enable = 0; break; case 0xe001: if ( Map4_IRQ_Cnt > 0 ) Map4_IRQ_Enable = 1; break; default: if ( wMapAddr == 0x8000 ) { Map4_VROM_Base = byData >> 7; Map4_ROM_Base = ( byData >> 6 ) & 1; Map4_Cmd = byData & 7; return; } else { if ( Map4_Cmd >= 6 ) byData %= ( NesHeader.byRomSize << 1 ); else if ( NesHeader.byVRomSize > 0 ) { if ( Map4_Cmd < 2 ) byData = ( byData & 0xfe ) % ( NesHeader.byVRomSize << 3 ); else if ( Map4_Cmd < 6 ) byData %= ( NesHeader.byVRomSize << 3 ); } Map4_Banks_Reg[ Map4_Cmd ] = byData; } /* Set VROM Banks */ if ( NesHeader.byVRomSize > 0 ) { if ( Map4_VROM_Base ) { PPUBANK[ 0 ] = VROMPAGE( Map4_Banks_Reg[ 2 ] ); PPUBANK[ 1 ] = VROMPAGE( Map4_Banks_Reg[ 3 ] ); PPUBANK[ 2 ] = VROMPAGE( Map4_Banks_Reg[ 4 ] ); PPUBANK[ 3 ] = VROMPAGE( Map4_Banks_Reg[ 5 ] ); PPUBANK[ 4 ] = VROMPAGE( Map4_Banks_Reg[ 0 ] ); PPUBANK[ 5 ] = VROMPAGE( Map4_Banks_Reg[ 0 ] + 1 ); PPUBANK[ 6 ] = VROMPAGE( Map4_Banks_Reg[ 1 ] ); PPUBANK[ 7 ] = VROMPAGE( Map4_Banks_Reg[ 1 ] + 1 ); } else { PPUBANK[ 0 ] = VROMPAGE( Map4_Banks_Reg[ 0 ] ); PPUBANK[ 1 ] = VROMPAGE( Map4_Banks_Reg[ 0 ] + 1 ); PPUBANK[ 2 ] = VROMPAGE( Map4_Banks_Reg[ 1 ] ); PPUBANK[ 3 ] = VROMPAGE( Map4_Banks_Reg[ 1 ] + 1 ); PPUBANK[ 4 ] = VROMPAGE( Map4_Banks_Reg[ 2 ] ); PPUBANK[ 5 ] = VROMPAGE( Map4_Banks_Reg[ 3 ] ); PPUBANK[ 6 ] = VROMPAGE( Map4_Banks_Reg[ 4 ] ); PPUBANK[ 7 ] = VROMPAGE( Map4_Banks_Reg[ 5 ] ); } pNesX_SetupChr(); } if ( Map4_ROM_Base ) { ROMBANK0 = ROMLASTPAGE( 1 ); ROMBANK1 = ROMPAGE( Map4_Banks_Reg[ 7 ] ); ROMBANK2 = ROMPAGE( Map4_Banks_Reg[ 6 ] ); ROMBANK3 = ROMLASTPAGE( 0 ); } else { ROMBANK0 = ROMPAGE( Map4_Banks_Reg[ 6 ] ); ROMBANK1 = ROMPAGE( Map4_Banks_Reg[ 7 ] ); ROMBANK2 = ROMLASTPAGE( 1 ); ROMBANK3 = ROMLASTPAGE( 0 ); } } } /*-------------------------------------------------------------------*/ /* Mapper 4 V-Sync Function */ /*-------------------------------------------------------------------*/ void Map4_VSync() { /* * Callback at VSync * */ Map4_IRQ_Cnt = Map4_IRQ_Set; } /*-------------------------------------------------------------------*/ /* Mapper 4 H-Sync Function */ /*-------------------------------------------------------------------*/ void Map4_HSync() { /* * Callback at HSync * */ if ( Map4_IRQ_Enable && PPU_Scanline >= SCAN_ON_SCREEN_START && PPU_Scanline < SCAN_BOTTOM_OFF_SCREEN_START && //PPU_ScanTable[ PPU_Scanline ] == SCAN_ON_SCREEN && ( PPU_R1 & ( R1_SHOW_SCR | R1_SHOW_SP ) ) == ( R1_SHOW_SCR | R1_SHOW_SP ) && Map4_IRQ_Cnt > 0 ) { --Map4_IRQ_Cnt; if ( Map4_IRQ_Cnt == 0 ) { Map4_IRQ_Cnt = Map4_IRQ_Set; IRQ_REQ; } } }