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_System_Nucleo.cpp
- Committer:
- beaglescout007
- Date:
- 2016-04-03
- Revision:
- 0:3dac1f1bc9e0
File content as of revision 0:3dac1f1bc9e0:
/*===================================================================*/ /* */ /* pNesX_System_Nucleo.cpp : The function which depends on a system */ /* (for Nucleo F446RE) */ /* */ /* 2016/1/20 Racoon */ /* */ /*===================================================================*/ /*-------------------------------------------------------------------*/ /* Include files */ /*-------------------------------------------------------------------*/ #include "mbed.h" #include "SDFileSystem.h" #include "tft.h" //#define PS_GAMEPAD #if !defined(PS_GAMEPAD) #include "USBHostGamepad.h" USBHostGamepad *pad; #else #include "pspad.h" #endif #include "pNesX.h" #include "pNesX_System.h" extern int pNesX_Filer(); extern void ApuMute(bool mute); SDFileSystem *sd; //(PB_15, PB_14, PB_13, PA_9, "sd", NC, SDFileSystem::SWITCH_NONE, 20000000); DigitalIn userbutton(USER_BUTTON); extern WORD LineData[][256]; extern WORD *pDrawData; //#define SHOW_FPS #if defined(SHOW_FPS) Timer timer; int fps = 0; int dmawait; #endif /*-------------------------------------------------------------------*/ /* ROM image file information */ /*-------------------------------------------------------------------*/ extern char szRomFolder[]; extern char szRomFilename[]; extern char szRomPath[]; char szRomName[ 256 ]; char szSaveName[ 256 ]; int nSRAM_SaveFlag; // Palette data const WORD NesPalette[ 64 ] = { 0xef7b,0x1f00,0x1700,0x5741,0x1090,0x04a8,0x80a8,0xa088,0x8051,0xc003,0x4003,0xc002,0x0b02,0x0000,0x0000,0x0000, 0xf7bd,0xdf03,0xdf02,0x3f6a,0x19d8,0x0be0,0xc0f9,0xe2e2,0xe0ab,0xc005,0x4005,0x4805,0x5104,0x0000,0x0000,0x0000, 0xdfff,0xff3d,0x5f6c,0xdf9b,0xdffb,0xd3fa,0xcbfb,0x08fd,0xc0fd,0xc3bf,0xca5e,0xd35f,0x5b07,0xcf7b,0x0000,0x0000, 0xffff,0x3fa7,0xdfbd,0xdfdd,0xdffd,0x38fd,0x96f6,0x15ff,0xcffe,0xcfdf,0xd7bf,0xdbbf,0xff07,0xdffe,0x0000,0x0000 }; /*-------------------------------------------------------------------*/ /* Function prototypes */ /*-------------------------------------------------------------------*/ int LoadSRAM(); int SaveSRAM(); /*===================================================================*/ /* */ /* LoadSRAM() : Load a SRAM */ /* */ /*===================================================================*/ int LoadSRAM() { /* * Load a SRAM * * Return values * 0 : Normally * -1 : SRAM data couldn't be read */ return 0; } /*===================================================================*/ /* */ /* SaveSRAM() : Save a SRAM */ /* */ /*===================================================================*/ int SaveSRAM() { /* * Save a SRAM * * Return values * 0 : Normally * -1 : SRAM data couldn't be written */ // Successful return 0; } /*===================================================================*/ /* */ /* pNesX_Menu() : Menu screen */ /* */ /*===================================================================*/ int pNesX_Menu() { /* * Menu screen * * Return values * 0 : Normally * -1 : Exit pNesX */ #if defined(SHOW_FPS) timer.stop(); #endif ApuMute(true); switch (pNesX_Filer()) { case 0: // Selected a file if ( pNesX_Load( szRomPath ) == 0 ) { // Set a ROM image name strcpy( szRomName, szRomFilename ); // Load SRAM LoadSRAM(); } break; case 1: // Return to Emu break; case 2: // Reset if ( szRomName[ 0 ] != 0 ) { // Reset pNesX pNesX_Reset(); } break; case -1: // Error printf("Filer Error\r\n"); while(1); } tft_clear(TFT_BLACK); tft_set_window(32, 8, NES_DISP_WIDTH + 32 - 1, NES_DISP_HEIGHT + 8 - 1); ApuMute(false); #if defined(SHOW_FPS) timer.start(); #endif return 0; } /*===================================================================*/ /* */ /* pNesX_ReadRom() : Read ROM image file */ /* */ /*===================================================================*/ int pNesX_ReadRom( const char *pszFileName ) { /* * Read ROM image file * * Parameters * const char *pszFileName (Read) * * Return values * 0 : Normally * -1 : Error */ FileHandle* file; /* Open ROM file */ file = sd->open(pszFileName, O_RDONLY); if ( file == NULL ) { printf("open error\r\n"); return -1; } /* Read ROM Header */ file->read( &NesHeader, sizeof NesHeader); if ( memcmp( NesHeader.byID, "NES\x1a", 4 ) != 0 ) { /* not .nes file */ file->close(); return -1; } /* Clear SRAM */ memset( SRAM, 0, SRAM_SIZE ); /* If trainer presents Read Triner at 0x7000-0x71ff */ if ( NesHeader.byInfo1 & 4 ) { printf("Read Trainer\r\n"); file->read( &SRAM[ 0x1000 ], 512 ); } printf("RomSize: %d\r\n", NesHeader.byRomSize); /* Allocate Memory for ROM Image */ ROM = (BYTE *)malloc( NesHeader.byRomSize * 0x4000 ); printf("ROM addr:%x\r\n", ROM); /* Read ROM Image */ file->read( ROM, 0x4000 * NesHeader.byRomSize ); if ( NesHeader.byVRomSize > 0 ) { /* Allocate Memory for VROM Image */ VROM = (BYTE *)malloc( NesHeader.byVRomSize * 0x2000 ); printf("VROM addr:%x\r\n", VROM); /* Read VROM Image */ file->read( VROM, 0x2000 * NesHeader.byVRomSize ); } /* File close */ file->close(); /* Successful */ return 0; } /*===================================================================*/ /* */ /* pNesX_ReleaseRom() : Release a memory for ROM */ /* */ /*===================================================================*/ void pNesX_ReleaseRom() { /* * Release a memory for ROM * */ if ( ROM ) { free( ROM ); ROM = NULL; } if ( VROM ) { free( VROM ); VROM = NULL; } } /*===================================================================*/ /* */ /* pNesX_LoadFrame() : */ /* Transfer the contents of work frame on the screen */ /* */ /*===================================================================*/ void pNesX_LoadFrame() { /* * Transfer the contents of work frame on the screen * */ #if defined(SHOW_FPS) fps++; if (timer.read_ms() >= 1000) { timer.stop(); printf("%d %d %d\r\n", fps, timer.read_ms(), dmawait); fps = 0; timer.reset(); timer.start(); } dmawait = 0; #endif } void pNesX_TransmitLinedata() { while (SpiHandle.State != HAL_SPI_STATE_READY) { #if defined(SHOW_FPS) dmawait++; #endif } HAL_SPI_Transmit_DMA(&SpiHandle, (uint8_t *)pDrawData, 256 * 2); } #if !defined(PS_GAMEPAD) const BYTE UsbPadTable[] = {0x10, 0x90, 0x80, 0xa0, 0x20, 0x60, 0x40, 0x50}; #endif /*===================================================================*/ /* */ /* pNesX_PadState() : Get a joypad state */ /* */ /*===================================================================*/ void pNesX_PadState( DWORD *pdwPad1, DWORD *pdwPad2, DWORD *pdwSystem ) { /* * Get a joypad state * * Parameters * DWORD *pdwPad1 (Write) * Joypad 1 State R L D U St Se B A * * DWORD *pdwPad2 (Write) * Joypad 2 State * * DWORD *pdwSystem (Write) * Input for pNesX * */ #if !defined(PS_GAMEPAD) *pdwPad1 = ((pad->report[4] & 0x20) >> 5) | ((pad->report[4] & 0x10) >> 3) | ((pad->report[5] & 0x30) >> 2); if (!(pad->report[4] & 8)) { *pdwPad1 |= UsbPadTable[pad->report[4] & 7]; } *pdwPad1 = *pdwPad1 | ( *pdwPad1 << 8 ); *pdwPad2 = 0; *pdwSystem = ((pad->report[5] & 0x4) >> 2) | (((pad->report[5] & 0xa) == 0xa) << 1); USBHost::poll(); #else unsigned short pad1, pad2; pspad_read(&pad1, &pad2); // R L D U St Se B A // SE -- -- ST U R D L L2 R2 L1 R1 TR O X SQ *pdwPad1 = ((pad1 & 0x0400) >> 3) | ((pad1 & 0x0100) >> 2) | ((pad1 & 0x0200) >> 4) | ((pad1 & 0x0800) >> 7) | ((pad1 & 0x1000) >> 9) | ((pad1 & 0x8000) >> 13) | ((pad1 & 1) << 1) | ((pad1 & 2) >> 1); *pdwPad1 = *pdwPad1 | ( *pdwPad1 << 8 ); *pdwPad2 = 0; *pdwSystem = ((pad1 & 0x80) >> 7) | (((pad1 & 0x50) == 0x50) << 1); #endif } /*===================================================================*/ /* */ /* pNesX_MemoryCopy() : memcpy */ /* */ /*===================================================================*/ void *pNesX_MemoryCopy( void *dest, const void *src, int count ) { /* * memcpy * * Parameters * void *dest (Write) * Points to the starting address of the copied block to destination * * const void *src (Read) * Points to the starting address of the block of memory to copy * * int count (Read) * Specifies the size, in bytes, of the block of memory to copy * * Return values * Pointer of destination */ memcpy(dest, src, count ); return dest; } /*===================================================================*/ /* */ /* pNesX_MemorySet() : */ /* */ /*===================================================================*/ void *pNesX_MemorySet( void *dest, int c, int count ) { /* * memset * * Parameters * void *dest (Write) * Points to the starting address of the block of memory to fill * * int c (Read) * Specifies the byte value with which to fill the memory block * * int count (Read) * Specifies the size, in bytes, of the block of memory to fill * * Return values * Pointer of destination */ memset(dest, c, count); return dest; } /*===================================================================*/ /* */ /* DebugPrint() : Print debug message */ /* */ /*===================================================================*/ void pNesX_DebugPrint( char *pszMsg ) { } /*===================================================================*/ /* */ /* pNesX Initialise */ /* */ /*===================================================================*/ int main() { // TFT initialize tft_init(); tft_clear(TFT_BLACK); tft_set_window(32, 8, NES_DISP_WIDTH + 32 - 1, NES_DISP_HEIGHT + 8 - 1); // SD card Initialize sd = new SDFileSystem(PB_15, PB_14, PB_13, PA_9, "sd", NC, SDFileSystem::SWITCH_NONE, 20000000); sd->crc(true); sd->large_frames(true); sd->write_validation(true); strcpy(szRomFolder, "/sd"); #if !defined(PS_GAMEPAD) // USB Gmaepad Initialize pad = new USBHostGamepad(); pad->connect(); #else pspad_init(); #endif // Apu Initialize ApuInit(); /*-------------------------------------------------------------------*/ /* Start pNesX */ /*-------------------------------------------------------------------*/ pNesX_Main(); }