mirror of
https://github.com/demik/quack.git
synced 2024-12-21 14:29:22 +00:00
added resources for esp_8_bit build
This commit is contained in:
parent
66ee6a28fa
commit
bcc7802439
875
Misc/esp_8_bit/base.diff
Normal file
875
Misc/esp_8_bit/base.diff
Normal file
@ -0,0 +1,875 @@
|
||||
diff --git a/README.md b/README.md
|
||||
index c548654..7d33f55 100644
|
||||
--- a/README.md
|
||||
+++ b/README.md
|
||||
@@ -1,29 +1,59 @@
|
||||
# **ESP_8_BIT:** Atari 8 bit computers, NES and SMS game consoles on your TV with nothing more than a ESP32 and a sense of nostalgia
|
||||
-## Supports NTSC/PAL color composite video output, Bluetooth Classic or IR keyboards and joysticks; just the thing when we could all use a little distraction
|
||||
+## Supports NTSC/PAL color composite video output, Bluetooth Classic keyboards and joysticks; just the thing when we could all use a little distraction
|
||||
+## Supports classic NES (or SNES) controller hardwired to the ESP32. SELECT + LEFT to access file menu. SELECT + START -> reset
|
||||
|
||||
![ESP_8_BIT](img/esp8bit.jpg)
|
||||
|
||||
-**ESP_8_BIT** is designed to run on the ESP32 within the Arduino IDE framework. See it in action on [Youtube](https://www.youtube.com/watch?v=qFRkfeuTUrU). Schematic is pretty simple:
|
||||
+**ESP_8_BIT** is designed to run on the ESP32 within the Arduino IDE framework. See it in action on [Youtube](https://www.youtube.com/watch?v=qFRkfeuTUrU). Schematic is simple, including LVC buffers (3.3 to 5V and vice versa):
|
||||
|
||||
```
|
||||
-----------
|
||||
| |
|
||||
- | 25 |------------------> video out
|
||||
+ | 25 |----------------------------------> video out
|
||||
| |
|
||||
- | 18 |---/\/\/\/----|---> audio out
|
||||
- | | 1k |
|
||||
- | | ---
|
||||
- | ESP32 | --- 10nf
|
||||
- | | |
|
||||
- | | v gnd
|
||||
+ | | ------------
|
||||
+ | 2 |---| LVC 4245 |----/\/\/\/----|---> audio out
|
||||
+ | | ------------ 1kΩ |
|
||||
+ | | ---
|
||||
+ | | --- 10nF
|
||||
+ | ESP32 | |
|
||||
+ | | v gnd
|
||||
+ | |
|
||||
+ | | ------------
|
||||
+ | 4 |---| LVC 2T45 |---< NES (or SNES) controller DATA
|
||||
+ | | ------------
|
||||
+ | | ------------
|
||||
+ | 12 |---| LVC 4245 |---> NES (or SNES) controller CLOCK
|
||||
+ | 14 |---| LVC 4245 |---> NES (or SNES) controller LATCH
|
||||
+ | | ------------
|
||||
+ | | 5v <--> NES (or SNES) controller VCC
|
||||
+ | | gnd <--> NES (or SNES) controller GND
|
||||
| |
|
||||
- | | 3.3v <--+-+ IR Receiver
|
||||
- | | gnd <--| ) TSOP4838 etc.
|
||||
- | 0 |-------------+-+ (Optional)
|
||||
-----------
|
||||
|
||||
+
|
||||
+NES ___
|
||||
+ DATA |o o| NC
|
||||
+ LATCH |o o| NC
|
||||
+ CLOCK |o o/ 5V
|
||||
+ GND |o_/
|
||||
+
|
||||
+SNES _
|
||||
+ 5V |o|
|
||||
+ CLOCK |o|
|
||||
+ LATCH |o|
|
||||
+ DATA |o|
|
||||
+ |-|
|
||||
+ NC |o|
|
||||
+ NC |o|
|
||||
+ GND |o|
|
||||
+ -
|
||||
+
|
||||
```
|
||||
-Audio is on pin 18 by default but can be remapped.
|
||||
+Audio is on pin 2 for Quack compatibility
|
||||
+
|
||||
+Enable either NES or SNES controller with
|
||||
+#define NES_CONTROLLER or #define SNES_CONTROLLER (not both) in nintendo.h file
|
||||
|
||||
Before you compile the sketch you have 2 choices:
|
||||
```
|
||||
@@ -181,7 +211,7 @@ You will want to add a simple rc filter to the output pin of either PWM or PDM t
|
||||
|
||||
## Bringing it Together
|
||||
|
||||
-The audio/video system uses a double buffered I2S DMA to send video data line by line to the DAC. The interrupt keeps time at the line rate (15720hz for NTSC, 15600hz for PAL). A single audio sample is fed to the LED PWM and a single line of video is converted from index color to phase/amplitude at each interrupt. The IR input pin is scanned and the various IR state machines advanced on changes.
|
||||
+The audio/video system uses a double buffered I2S DMA to send video data line by line to the DAC. The interrupt keeps time at the line rate (15720hz for NTSC, 15600hz for PAL). A single audio sample is fed to the LED PWM and a single line of video is converted from index color to phase/amplitude at each interrupt. The hard wired input pin is scanned as well
|
||||
|
||||
This a/v pump is fed by the emulator running asynchronously producing frames of video and audio. The emulator may be running on a different core and may occasionally take longer than a frame time to produce a frame (SPI paging / FS etc). The interrupt driven pump won't care, it just keeps emitting the last frame.
|
||||
|
||||
@@ -195,18 +225,16 @@ How do you fit a 512k game cartridge into a device that has 384k of RAM, nearly
|
||||
|
||||
Short answer is you don't. When a large cart is selected it gets copied into `CrapFS`; an aptly named simple filesystem that takes over the `app1` partition at first boot. One copied `CrapFS` uses `esp_partition_mmap()` to map the part of the partition occupied by the cartridge into the data space of the CPU.
|
||||
|
||||
-> We are using an Audio PLL to create color composite video and a LED PWM peripheral to make the audio, a Bluetooth radio or a single GPIO pin for the joysticks and keyboard, and gpio for the IR even though there is a perfectly good peripheral for that. We are using virtual memory on a microcontroller. And it all fits on a single core. Oh how I love the ESP32.
|
||||
+> We are using an Audio PLL to create color composite video and a LED PWM peripheral to make the audio, a Bluetooth radio or a single GPIO pin for the joysticks and keyboard, and gpio for the classic controller. We are using virtual memory on a microcontroller. And it all fits on a single core. Oh how I love the ESP32.
|
||||
|
||||
|
||||
# Keyboards & Controllers
|
||||
-**ESP_8_BIT** supports Bluetooth Classic/EDR keyboards and WiiMotes along with an variety of IR keyboards and joysticks.
|
||||
+**ESP_8_BIT** supports Bluetooth Classic/EDR keyboards and WiiMotes along with either a classic NES or SNES controller
|
||||
|
||||
**On boot the software searches for new Bluetooth devices for 5 seconds**; if the device is in pairing mode the software should find it and display its name at the bottom of the screen. Some keyboards will require you to enter a "0000" to establish the connection the first time. WiiMotes should automatically pair and reconnect. WiiMote Classic controllers are also supported.
|
||||
|
||||
![Input Devices](img/inputdevices.jpg)
|
||||
|
||||
-A number of IR input devices are supported if to add a optional IR receiver (TSOP38238, TSOP4838 or equivalent) to pin 0. The IR Wireless Controllers from Atari Flashback 4 work well and have that classic Atari joystick feel. Retron IR controllers are supported as are WebTV keyboards that come in various guises: WebTV, MsnTV, Displayer, UltimateTV etc. They all should work just fine. A few places have the nice Philips variant new for $11.95 w/ free shipping (search for 'SWK-8630'). If you made a [ZorkDuino](https://hackaday.com/2014/04/30/the-zorkduino/) you will have one of these.
|
||||
-
|
||||
# Time to Play
|
||||
|
||||
If you would like to upload your own media copy them into the appropriate subfolder named for each of the emulators in the data folder. Note that the SPIFFS filesystem is fussy about filenames, keep them short, no spaces allowed. Use '[ESP32 Sketch Data Upload](https://randomnerdtutorials.com/install-esp32-filesystem-uploader-arduino-ide/)' from the 'Tools' menu to copy a prepared data folder to ESP32.
|
||||
diff --git a/data/atari800/atari_robot.xex b/data/atari800/atari_robot.xex
|
||||
deleted file mode 100644
|
||||
index b27fb91..0000000
|
||||
Binary files a/data/atari800/atari_robot.xex and /dev/null differ
|
||||
diff --git a/data/atari800/balls_forever.xex b/data/atari800/balls_forever.xex
|
||||
deleted file mode 100644
|
||||
index 7b414a0..0000000
|
||||
Binary files a/data/atari800/balls_forever.xex and /dev/null differ
|
||||
diff --git a/data/atari800/boink.xex b/data/atari800/boink.xex
|
||||
deleted file mode 100644
|
||||
index 3bad906..0000000
|
||||
Binary files a/data/atari800/boink.xex and /dev/null differ
|
||||
diff --git a/data/atari800/callisto.xex b/data/atari800/callisto.xex
|
||||
deleted file mode 100644
|
||||
index f6b42e1..0000000
|
||||
Binary files a/data/atari800/callisto.xex and /dev/null differ
|
||||
diff --git a/data/atari800/dos20.atr b/data/atari800/dos20.atr
|
||||
deleted file mode 100644
|
||||
index 2685d6d..0000000
|
||||
Binary files a/data/atari800/dos20.atr and /dev/null differ
|
||||
diff --git a/data/atari800/gravity_worms.atr b/data/atari800/gravity_worms.atr
|
||||
deleted file mode 100755
|
||||
index 645ca33..0000000
|
||||
Binary files a/data/atari800/gravity_worms.atr and /dev/null differ
|
||||
diff --git a/data/atari800/gtia_blast.xex b/data/atari800/gtia_blast.xex
|
||||
deleted file mode 100644
|
||||
index 964f0d6..0000000
|
||||
Binary files a/data/atari800/gtia_blast.xex and /dev/null differ
|
||||
diff --git a/data/atari800/janes_program.xex b/data/atari800/janes_program.xex
|
||||
deleted file mode 100644
|
||||
index 4fbbc12..0000000
|
||||
Binary files a/data/atari800/janes_program.xex and /dev/null differ
|
||||
diff --git a/data/atari800/maze.xex b/data/atari800/maze.xex
|
||||
deleted file mode 100755
|
||||
index 265191c..0000000
|
||||
Binary files a/data/atari800/maze.xex and /dev/null differ
|
||||
diff --git a/data/atari800/mini_zork.atr b/data/atari800/mini_zork.atr
|
||||
deleted file mode 100644
|
||||
index ed29c1a..0000000
|
||||
Binary files a/data/atari800/mini_zork.atr and /dev/null differ
|
||||
diff --git a/data/atari800/more.xex b/data/atari800/more.xex
|
||||
deleted file mode 100644
|
||||
index 835b1ca..0000000
|
||||
Binary files a/data/atari800/more.xex and /dev/null differ
|
||||
diff --git a/data/atari800/numen_rubik.atr b/data/atari800/numen_rubik.atr
|
||||
deleted file mode 100644
|
||||
index c198cdf..0000000
|
||||
Binary files a/data/atari800/numen_rubik.atr and /dev/null differ
|
||||
diff --git a/data/atari800/paperweight.xex b/data/atari800/paperweight.xex
|
||||
deleted file mode 100644
|
||||
index fbd30c1..0000000
|
||||
Binary files a/data/atari800/paperweight.xex and /dev/null differ
|
||||
diff --git a/data/atari800/raymaze_2000_ntsc.xex b/data/atari800/raymaze_2000_ntsc.xex
|
||||
deleted file mode 100755
|
||||
index 5d8f45f..0000000
|
||||
Binary files a/data/atari800/raymaze_2000_ntsc.xex and /dev/null differ
|
||||
diff --git a/data/atari800/runner_bear.xex b/data/atari800/runner_bear.xex
|
||||
deleted file mode 100644
|
||||
index 3fb35b3..0000000
|
||||
Binary files a/data/atari800/runner_bear.xex and /dev/null differ
|
||||
diff --git a/data/atari800/star_raiders_II.atr b/data/atari800/star_raiders_II.atr
|
||||
deleted file mode 100644
|
||||
index 05ba6a7..0000000
|
||||
Binary files a/data/atari800/star_raiders_II.atr and /dev/null differ
|
||||
diff --git a/data/atari800/wasteland.atr b/data/atari800/wasteland.atr
|
||||
deleted file mode 100755
|
||||
index 533b441..0000000
|
||||
Binary files a/data/atari800/wasteland.atr and /dev/null differ
|
||||
diff --git a/data/atari800/yoomp_nt.xex b/data/atari800/yoomp_nt.xex
|
||||
deleted file mode 100644
|
||||
index 3b20897..0000000
|
||||
Binary files a/data/atari800/yoomp_nt.xex and /dev/null differ
|
||||
diff --git a/data/nofrendo/sokoban.nes b/data/nofrendo/sokoban.nes
|
||||
deleted file mode 100755
|
||||
index 4628512..0000000
|
||||
Binary files a/data/nofrendo/sokoban.nes and /dev/null differ
|
||||
diff --git a/data/nofrendo/tokumaru_raycast.nes b/data/nofrendo/tokumaru_raycast.nes
|
||||
deleted file mode 100644
|
||||
index 7e7f9fc..0000000
|
||||
Binary files a/data/nofrendo/tokumaru_raycast.nes and /dev/null differ
|
||||
diff --git a/data/smsplus/baraburuu.sms b/data/smsplus/baraburuu.sms
|
||||
deleted file mode 100755
|
||||
index 2683e0f..0000000
|
||||
Binary files a/data/smsplus/baraburuu.sms and /dev/null differ
|
||||
diff --git a/data/smsplus/ftrack.gg b/data/smsplus/ftrack.gg
|
||||
deleted file mode 100644
|
||||
index e58a12f..0000000
|
||||
Binary files a/data/smsplus/ftrack.gg and /dev/null differ
|
||||
diff --git a/data/smsplus/nanowars8k.sms b/data/smsplus/nanowars8k.sms
|
||||
deleted file mode 100644
|
||||
index 7d7c38a..0000000
|
||||
Binary files a/data/smsplus/nanowars8k.sms and /dev/null differ
|
||||
diff --git a/esp_8_bit.ino b/esp_8_bit.ino
|
||||
index 2256665..fa45f80 100644
|
||||
--- a/esp_8_bit.ino
|
||||
+++ b/esp_8_bit.ino
|
||||
@@ -18,7 +18,8 @@
|
||||
#include "esp_int_wdt.h"
|
||||
#include "esp_spiffs.h"
|
||||
|
||||
-#define PERF // some stats about where we spend our time
|
||||
+#include "soc/efuse_reg.h"
|
||||
+#undef PERF // some stats about where we spend our time
|
||||
#include "src/emu.h"
|
||||
#include "src/video_out.h"
|
||||
|
||||
@@ -27,10 +28,10 @@
|
||||
// Supports NTSC/PAL composite video, Bluetooth Classic keyboards and joysticks
|
||||
|
||||
// Choose one of the video standards: PAL,NTSC
|
||||
-#define VIDEO_STANDARD NTSC
|
||||
+#define VIDEO_STANDARD PAL
|
||||
|
||||
// Choose one of the following emulators: EMU_NES,EMU_SMS,EMU_ATARI
|
||||
-#define EMULATOR EMU_ATARI
|
||||
+#define EMULATOR EMU_NES
|
||||
|
||||
// Many emus work fine on a single core (S2), file system access can cause a little flickering
|
||||
// #define SINGLE_CORE
|
||||
@@ -42,9 +43,29 @@
|
||||
// Folders will be auto-populated on first launch with a built in selection of sample media.
|
||||
// Use 'ESP32 Sketch Data Upload' from the 'Tools' menu to copy a prepared data folder to ESP32
|
||||
|
||||
+// Quack hardware DIP switch block
|
||||
+// on ESP32-PICO-D4, the following pins are used for connecting the
|
||||
+// embedded flash:
|
||||
+// - CLK (GPIO6)
|
||||
+// - SD0 (GPIO7)
|
||||
+// - SD1 (GPIO8)
|
||||
+// - CMD (GPIO11)
|
||||
+// - GPIO16
|
||||
+// - GPIO17
|
||||
+#include "driver/gpio.h"
|
||||
+#define GPIO_ADB 4 // Used for NES or SNES classic controller data line
|
||||
+#define GPIO_DIR 19 // Select data in or out
|
||||
+#define GPIO_OE 22 // Enable 3.3V to 5V buffer
|
||||
+#define GPIO_ADBSRC 32 // USed to select classic controller player A (0, pulled down) or B (1)
|
||||
+#define GPIO_BTOFF 33 // Used to disable bluetooth
|
||||
+#define GPIO_GREENLED 21
|
||||
+#undef GPIO_BLUELED 25 // Has to be desoldered, and resistor R1 bridged (video out)
|
||||
+#define GPIO_YELLOWLED 26
|
||||
+#define GPIO_REDLED 27
|
||||
+
|
||||
// Create a new emulator, messy ifdefs ensure that only one links at a time
|
||||
Emu* NewEmulator()
|
||||
-{
|
||||
+{
|
||||
#if (EMULATOR==EMU_NES)
|
||||
return NewNofrendo(VIDEO_STANDARD);
|
||||
#endif
|
||||
@@ -88,13 +109,16 @@ void emu_task(void* arg)
|
||||
printf("emu_task %s running on core %d at %dmhz\n",
|
||||
_emu->name.c_str(),xPortGetCoreID(),rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()));
|
||||
emu_init();
|
||||
+
|
||||
+ /* Quack: enable green led after init */
|
||||
+ gpio_set_level((gpio_num_t)GPIO_GREENLED, 1);
|
||||
for (;;)
|
||||
emu_loop();
|
||||
}
|
||||
|
||||
esp_err_t mount_filesystem()
|
||||
{
|
||||
- printf("\n\n\nesp_8_bit\n\nmounting spiffs (will take ~15 seconds if formatting for the first time)....\n");
|
||||
+ printf("\n\n\nesp_8_bit on Quack\n\nmounting spiffs (will take ~15 seconds if formatting for the first time)....\n");
|
||||
uint32_t t = millis();
|
||||
esp_vfs_spiffs_conf_t conf = {
|
||||
.base_path = "",
|
||||
@@ -111,11 +135,49 @@ esp_err_t mount_filesystem()
|
||||
}
|
||||
|
||||
void setup()
|
||||
-{
|
||||
- rtc_clk_cpu_freq_set(RTC_CPU_FREQ_240M);
|
||||
+{
|
||||
+ // from CornN64 commit 4e2e5d2
|
||||
+ int silicon_version = (REG_READ(EFUSE_BLK0_RDATA3_REG) >> 15) & 1;
|
||||
+ if (silicon_version == 0)
|
||||
+ printf("Warning this revision of the chip has an issue with the APLL and will not work properly!\n");
|
||||
+
|
||||
+ // setup Quack buffer, GPIOs and LEDs here
|
||||
+ gpio_reset_pin((gpio_num_t)GPIO_ADBSRC);
|
||||
+ gpio_set_direction((gpio_num_t)GPIO_ADBSRC, GPIO_MODE_INPUT);
|
||||
+ gpio_reset_pin((gpio_num_t)GPIO_BTOFF);
|
||||
+ gpio_set_direction((gpio_num_t)GPIO_BTOFF, GPIO_MODE_INPUT);
|
||||
+ gpio_reset_pin((gpio_num_t)GPIO_DIR);
|
||||
+ gpio_reset_pin((gpio_num_t)GPIO_OE);
|
||||
+ gpio_set_direction((gpio_num_t)GPIO_DIR, GPIO_MODE_OUTPUT);
|
||||
+ gpio_set_direction((gpio_num_t)GPIO_OE, GPIO_MODE_OUTPUT);
|
||||
+ gpio_set_direction((gpio_num_t)GPIO_ADB, GPIO_MODE_INPUT);
|
||||
+ gpio_set_level((gpio_num_t)GPIO_DIR, 0);
|
||||
+ gpio_reset_pin((gpio_num_t)GPIO_GREENLED);
|
||||
+ gpio_reset_pin((gpio_num_t)GPIO_YELLOWLED);
|
||||
+ gpio_reset_pin((gpio_num_t)GPIO_REDLED);
|
||||
+ gpio_set_direction((gpio_num_t)GPIO_GREENLED, GPIO_MODE_OUTPUT);
|
||||
+ gpio_set_direction((gpio_num_t)GPIO_YELLOWLED, GPIO_MODE_OUTPUT);
|
||||
+ gpio_set_direction((gpio_num_t)GPIO_REDLED, GPIO_MODE_OUTPUT);
|
||||
+
|
||||
+ rtc_clk_cpu_freq_set(RTC_CPU_FREQ_240M);
|
||||
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 1);
|
||||
mount_filesystem(); // mount the filesystem!
|
||||
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 0);
|
||||
_emu = NewEmulator(); // create the emulator!
|
||||
- hid_init("emu32"); // bluetooth hid on core 1!
|
||||
+
|
||||
+ // Quack hardwired player A or B
|
||||
+ if (gpio_get_level((gpio_num_t)GPIO_ADBSRC) == 0)
|
||||
+ printf("Hardwired controller is player 1\n");
|
||||
+ else
|
||||
+ printf("Hardwired controller is player 2\n");
|
||||
+
|
||||
+ if (gpio_get_level((gpio_num_t)GPIO_BTOFF) == 0) {
|
||||
+ printf("Bluetooth is OFF\n");
|
||||
+ }
|
||||
+ else {
|
||||
+ printf("Bluetooth is ON\n");
|
||||
+ hid_init("emu32"); // bluetooth hid on core 1!
|
||||
+ }
|
||||
|
||||
#ifdef SINGLE_CORE
|
||||
emu_init();
|
||||
@@ -132,10 +194,10 @@ void perf()
|
||||
if (_drawn >= _next) {
|
||||
float elapsed_us = 120*1000000/(_emu->standard ? 60 : 50);
|
||||
_next = _drawn + 120;
|
||||
-
|
||||
+
|
||||
printf("frame_time:%d drawn:%d displayed:%d blit_ticks:%d->%d, isr time:%2.2f%%\n",
|
||||
_frame_time/240,_drawn,_frame_counter,_blit_ticks_min,_blit_ticks_max,(_isr_us*100)/elapsed_us);
|
||||
-
|
||||
+
|
||||
_blit_ticks_min = 0xFFFFFFFF;
|
||||
_blit_ticks_max = 0;
|
||||
_isr_us = 0;
|
||||
@@ -147,7 +209,7 @@ void perf(){};
|
||||
|
||||
// this loop always runs on app_core (1).
|
||||
void loop()
|
||||
-{
|
||||
+{
|
||||
#ifdef SINGLE_CORE
|
||||
emu_loop();
|
||||
#else
|
||||
@@ -162,9 +224,10 @@ void loop()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
-
|
||||
+
|
||||
// update the bluetooth edr/hid stack
|
||||
- hid_update();
|
||||
+ if (gpio_get_level((gpio_num_t)GPIO_BTOFF) == 1)
|
||||
+ hid_update();
|
||||
|
||||
// Dump some stats
|
||||
perf();
|
||||
diff --git a/src/emu.cpp b/src/emu.cpp
|
||||
index 068f6f6..f3efef4 100644
|
||||
--- a/src/emu.cpp
|
||||
+++ b/src/emu.cpp
|
||||
@@ -21,12 +21,15 @@ using namespace std;
|
||||
// Map files into memory for carts bigger than physical RAM
|
||||
// Handly for NES/SMS carts
|
||||
// Uses app1 as a cache with a crappy FS on top - default arduino config gives 1280k
|
||||
+// if using optional quack optimised partition sheme, can go up to 1664k
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
#include <esp_spi_flash.h>
|
||||
#include <esp_attr.h>
|
||||
#include <esp_partition.h>
|
||||
#include "rom/miniz.h"
|
||||
+#include "driver/gpio.h"
|
||||
+#define GPIO_REDLED 27
|
||||
|
||||
// only map 1 file at a time
|
||||
spi_flash_mmap_handle_t _file_handle = 0;
|
||||
@@ -114,31 +117,38 @@ public:
|
||||
|
||||
void reformat()
|
||||
{
|
||||
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 1);
|
||||
esp_partition_erase_range(_part,0,_part->size); // erase entire partition
|
||||
memset(_buf,0xFF,sizeof(DIR_BLOCK_SIZE)); // only use the first 16k of first block
|
||||
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 0);
|
||||
}
|
||||
|
||||
uint8_t* mmap(const FlashFile* file)
|
||||
{
|
||||
if (!file)
|
||||
return 0;
|
||||
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 1);
|
||||
printf("CrapFS::mmap mapping %s offset:%08X len:%d\n",file->name,file->offset,file->len);
|
||||
void* data = 0;
|
||||
if (esp_partition_mmap(_part, file->offset, file->len, SPI_FLASH_MMAP_DATA, (const void**)&data, &_file_handle) == 0)
|
||||
{
|
||||
printf("CrapFS::mmap mapped to %08X\n",data);
|
||||
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 0);
|
||||
return (uint8_t*)data;
|
||||
}
|
||||
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// see if the file exists
|
||||
FlashFile* find(const std::string& path)
|
||||
{
|
||||
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 1);
|
||||
for (int i = 0; i < _count; i++) {
|
||||
if (_dir[i].sig == FSIG && strcmp(_dir[i].name,path.c_str()) == 0)
|
||||
return _dir + i;
|
||||
}
|
||||
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -147,6 +157,7 @@ public:
|
||||
FILE *f = fopen(path.c_str(), "rb");
|
||||
if (!f)
|
||||
return -1;
|
||||
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 1);
|
||||
#define BUF_SIZE 4096
|
||||
uint8_t* buf = new uint8_t[BUF_SIZE];
|
||||
esp_err_t err;
|
||||
@@ -164,6 +175,7 @@ public:
|
||||
}
|
||||
fclose(f);
|
||||
delete buf;
|
||||
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 0);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -173,6 +185,7 @@ public:
|
||||
uint32_t start = 0x10000;
|
||||
for (int i = 0; i < _count; i++) {
|
||||
if (_dir[i].sig != FSIG) {
|
||||
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 1);
|
||||
_dir[i].sig = FSIG;
|
||||
_dir[i].offset = start;
|
||||
_dir[i].len = len; //
|
||||
@@ -185,6 +198,7 @@ public:
|
||||
printf("CrapFS::create dir write failed %d\n",err);
|
||||
return NULL;
|
||||
}
|
||||
+ gpio_set_level((gpio_num_t)GPIO_REDLED, 0);
|
||||
if (copy(path,start,len))
|
||||
return NULL;
|
||||
return _dir+i;
|
||||
diff --git a/src/emu.h b/src/emu.h
|
||||
index f8d024b..c58ca3b 100644
|
||||
--- a/src/emu.h
|
||||
+++ b/src/emu.h
|
||||
@@ -148,7 +148,7 @@ extern "C" FILE* mkfile(const char* path);
|
||||
extern "C" int unpack(const char* dst_path, const uint8_t* d, int len);
|
||||
|
||||
void audio_write_16(const int16_t* s, int len, int channels);
|
||||
-int get_hid_ir(uint8_t* dst);
|
||||
+extern "C" int get_hid_nintendo(uint8_t* dst);
|
||||
uint32_t generic_map(uint32_t m, const uint32_t* target);
|
||||
|
||||
Emu* NewAtari800(int ntsc = 1);
|
||||
diff --git a/src/emu_nofrendo.cpp b/src/emu_nofrendo.cpp
|
||||
index df905b2..aae1268 100644
|
||||
--- a/src/emu_nofrendo.cpp
|
||||
+++ b/src/emu_nofrendo.cpp
|
||||
@@ -15,6 +15,9 @@
|
||||
** SOFTWARE.
|
||||
*/
|
||||
|
||||
+/* from CornN64 commit 063b08f7479beb147960c99396b09bdb0b8746b4 */
|
||||
+#include "freertos/FreeRTOS.h"
|
||||
+#include "freertos/task.h"
|
||||
#include "emu.h"
|
||||
#include "media.h"
|
||||
|
||||
@@ -24,6 +27,13 @@ extern "C" {
|
||||
};
|
||||
#include "math.h"
|
||||
|
||||
+//#define CALC_PALETTES
|
||||
+//recalculated color samples from CornN64 commit a5ea467c4f39ff4a01c5639e527a88eae78080fc */
|
||||
+// Choose either rossumur OG palettes or CornN64 ones below
|
||||
+// OG black is a little washed on my TFT Samsung but colors are vivid
|
||||
+// Second one palettes black is good but picture is too dark
|
||||
+// Third is way better but still a little too dark on dark tones on my old TFT
|
||||
+
|
||||
using namespace std;
|
||||
|
||||
// https://wiki.nesdev.com/w/index.php/NTSC_video
|
||||
@@ -40,7 +50,8 @@ uint32_t nes_3_phase[64] = {
|
||||
0x45454500,0x423B4200,0x403B4400,0x3D3D4500,0x3B404400,0x3B424200,0x3B444000,0x3D453D00,
|
||||
0x40443B00,0x42423B00,0x44403B00,0x453D3D00,0x443B4000,0x39393900,0x17171700,0x17171700,
|
||||
};
|
||||
-uint32_t nes_4_phase[64] = {
|
||||
+
|
||||
+/*uint32_t nes_4_phase[64] = {
|
||||
0x2C2C2C2C,0x241D1F26,0x221D2227,0x1F1D2426,0x1D1F2624,0x1D222722,0x1D24261F,0x1F26241D,
|
||||
0x2227221D,0x24261F1D,0x26241D1F,0x27221D22,0x261F1D24,0x14141414,0x14141414,0x14141414,
|
||||
0x38383838,0x2C25272E,0x2A252A2F,0x27252C2E,0x25272E2C,0x252A2F2A,0x252C2E27,0x272E2C25,
|
||||
@@ -49,11 +60,33 @@ uint32_t nes_4_phase[64] = {
|
||||
0x373C3732,0x3A3C3533,0x3C3A3335,0x3C373237,0x3C35333A,0x2B2B2B2B,0x16161616,0x16161616,
|
||||
0x45454545,0x423B3D44,0x403B4045,0x3D3B4244,0x3B3D4442,0x3B404540,0x3B42443D,0x3D44423B,
|
||||
0x4045403B,0x42443D3B,0x44423B3D,0x45403B40,0x443D3B42,0x39393939,0x17171717,0x17171717,
|
||||
-};
|
||||
+};*/
|
||||
+
|
||||
+//RGB palette from http://drag.wootest.net/misc/palgen.html -> YUV -> QAM on color carrier -> 4 phases sampled
|
||||
+/*uint32_t nes_4_phase[64] = {
|
||||
+ 0x24242424,0x1A0E1420,0x1C0C1424,0x1B0D1523,0x15142020,0x0E1C2719,0x0D1D2717,0x0F1B2216,
|
||||
+ 0x13161815,0x19181415,0x1C1A1516,0x1C1A1516,0x1B16141A,0x14141414,0x14141414,0x14141414,
|
||||
+ 0x39393939,0x30151631,0x2B0D1735,0x1F122835,0x12193730,0x0A234026,0x072C411C,0x112C381C,
|
||||
+ 0x1D2B291C,0x2A27181B,0x2E29161B,0x3028161E,0x321F1629,0x14141414,0x14141414,0x14141414,
|
||||
+ 0x50505050,0x47241E41,0x39222940,0x30253742,0x29304D45,0x26374D3C,0x243F4D32,0x23484D29,
|
||||
+ 0x2D484126,0x3A452F25,0x463E2027,0x4E341831,0x502C183C,0x1E1E1E1E,0x14141414,0x14141414,
|
||||
+ 0x50505050,0x4C3D3A4A,0x463B3E49,0x413C4549,0x3F434F4B,0x3E454F48,0x3D494F43,0x3D4D4F3F,
|
||||
+ 0x414E4A3D,0x464C433D,0x4B483C3F,0x4F443843,0x50413848,0x3B3B3B3B,0x14141414,0x14141414
|
||||
+};*/
|
||||
|
||||
+uint32_t nes_4_phase[64] = {
|
||||
+ 0x27272727,0x1B16191E,0x1D151921,0x1C151920,0x1A1A1F20,0x171E231C,0x171E231C,0x171C201A,
|
||||
+ 0x17191A18,0x1B1A1819,0x1D1C191A,0x1D1C191A,0x1C1A191C,0x17171717,0x17171717,0x17171717,
|
||||
+ 0x3A3A3A3A,0x2C1F1F2C,0x281A1E2D,0x231D282E,0x1E212F2C,0x1A263428,0x192A3423,0x1D2A3023,
|
||||
+ 0x23292822,0x28261F20,0x2A281F21,0x2B271F23,0x2D241F28,0x17171717,0x17171717,0x17171717,
|
||||
+ 0x50505050,0x3E2D2A3B,0x362B2F3A,0x332E373C,0x33374441,0x313A443C,0x303D4437,0x2F414332,
|
||||
+ 0x34413D30,0x393E342E,0x3D3A2B2F,0x41352733,0x43312839,0x21212121,0x17171717,0x17171717,
|
||||
+ 0x50505050,0x48414047,0x443F4146,0x43404447,0x44454B4A,0x43464B47,0x42484B45,0x424A4B43,
|
||||
+ 0x444A4842,0x46494442,0x48474142,0x4A453F44,0x4A433F46,0x3C3C3C3C,0x17171717,0x17171717
|
||||
+};
|
||||
|
||||
// PAL yuyv table, must be in RAM
|
||||
-uint32_t _nes_yuv_4_phase_pal[] = {
|
||||
+/*uint32_t _nes_yuv_4_phase_pal[] = {
|
||||
0x31313131,0x2D21202B,0x2720252D,0x21212B2C,0x1D23302A,0x1B263127,0x1C293023,0x202B2D22,
|
||||
0x262B2722,0x2C2B2122,0x2F2B1E23,0x31291F27,0x30251F2A,0x18181818,0x19191919,0x19191919,
|
||||
0x3D3D3D3D,0x34292833,0x2F282D34,0x29283334,0x252B3732,0x232E392E,0x2431382B,0x28333429,
|
||||
@@ -71,8 +104,51 @@ uint32_t _nes_yuv_4_phase_pal[] = {
|
||||
0x3D423B35,0x37414136,0x323F4538,0x313C473B,0x3239463F,0x2F2F2F2F,0x1A1A1A1A,0x1A1A1A1A,
|
||||
0x49494949,0x3D414845,0x43404245,0x463F3D44,0x453D3B43,0x453E3B42,0x45423B3F,0x46473E3E,
|
||||
0x454A433E,0x3E48463D,0x3943483E,0x38404A42,0x39404B44,0x3E3E3E3E,0x1B1B1B1B,0x1B1B1B1B,
|
||||
+};/*
|
||||
+
|
||||
+//RGB palette from http://drag.wootest.net/misc/palgen.html -> YUV -> QAM on color carrier -> 4 phases sampled
|
||||
+/*uint32_t _nes_yuv_4_phase_pal[] = {
|
||||
+ 0x1B1B1B1B,0x110B0E14,0x120A0F16,0x120B0F16,0x0F0F1515,0x0C131912,0x0C141911,0x0C121610,
|
||||
+ 0x0D0F0F0E,0x11100E0F,0x12110F0F,0x12110F0F,0x120F0E11,0x0D0D0D0D,0x0D0D0D0D,0x0D0D0D0D,
|
||||
+ 0x2C2C2C2C,0x20131321,0x1D0E1322,0x17111C23,0x12152420,0x0D1A291C,0x0C1E2916,0x111F2417,
|
||||
+ 0x171D1C16,0x1C1B1315,0x1F1C1315,0x201C1317,0x2118131D,0x0D0D0D0D,0x0D0D0D0D,0x0D0D0D0D,
|
||||
+ 0x3F3F3F3F,0x31201C2E,0x291E212D,0x2520292E,0x24283633,0x232B362D,0x212F3628,0x21333623,
|
||||
+ 0x25332F22,0x2B302620,0x302C1D21,0x34271926,0x36231A2C,0x15151515,0x0D0D0D0D,0x0D0D0D0D,
|
||||
+ 0x3F3F3F3F,0x39313038,0x35303137,0x33313537,0x34353B3A,0x33373B38,0x32383B35,0x323A3B33,
|
||||
+ 0x343A3832,0x36393432,0x39373133,0x3A352F34,0x3B332F37,0x2E2E2E2E,0x0D0D0D0D,0x0D0D0D0D,
|
||||
+
|
||||
+ 0x1B1B1B1B,0x0E0B1114,0x0F0A1216,0x0F0B1216,0x150F0F15,0x19130C12,0x19140C11,0x16120C10,
|
||||
+ 0x0F0F0D0E,0x0E10110F,0x0F11120F,0x0F11120F,0x0E0F1211,0x0D0D0D0D,0x0D0D0D0D,0x0D0D0D0D,
|
||||
+ 0x2C2C2C2C,0x13132021,0x130E1D22,0x1C111723,0x24151220,0x291A0D1C,0x291E0C16,0x241F1117,
|
||||
+ 0x1C1D1716,0x131B1C15,0x131C1F15,0x131C2017,0x1318211D,0x0D0D0D0D,0x0D0D0D0D,0x0D0D0D0D,
|
||||
+ 0x3F3F3F3F,0x1C20312E,0x211E292D,0x2920252E,0x36282433,0x362B232D,0x362F2128,0x36332123,
|
||||
+ 0x2F332522,0x26302B20,0x1D2C3021,0x19273426,0x1A23362C,0x15151515,0x0D0D0D0D,0x0D0D0D0D,
|
||||
+ 0x3F3F3F3F,0x30313938,0x31303537,0x35313337,0x3B35343A,0x3B373338,0x3B383235,0x3B3A3233,
|
||||
+ 0x383A3432,0x34393632,0x31373933,0x2F353A34,0x2F333B37,0x2E2E2E2E,0x0D0D0D0D,0x0D0D0D0D
|
||||
+};*/
|
||||
+
|
||||
+uint32_t _nes_yuv_4_phase_pal[] = {
|
||||
+ 0x26262626,0x1A10151F,0x1B0E1523,0x1B0F1622,0x16161F20,0x101C261A,0x101E2618,0x111B2117,
|
||||
+ 0x14161815,0x19181516,0x1C1A1517,0x1C1A1517,0x1B16151A,0x14141414,0x14141414,0x14141414,
|
||||
+ 0x3C3C3C3C,0x30181931,0x2B111933,0x21152834,0x161C3630,0x0F253E28,0x0D2C3E1F,0x152D371F,
|
||||
+ 0x202B291E,0x2A281A1C,0x2E29191D,0x2F281920,0x3121192A,0x14141414,0x14141414,0x14141414,
|
||||
+ 0x55555555,0x47292442,0x3B262C41,0x332A3942,0x2E354E47,0x2C3B4E3F,0x2A414D36,0x29494D2E,
|
||||
+ 0x3249422B,0x3C453329,0x463F252C,0x4D371E34,0x4F2F1E3E,0x1F1F1F1F,0x14141414,0x14141414,
|
||||
+ 0x55555555,0x4F42404D,0x493F424C,0x4541484C,0x4548524F,0x434A524C,0x424C5248,0x43505245,
|
||||
+ 0x46514D42,0x4A4F4742,0x4E4C4144,0x52483E47,0x52453E4B,0x3F3F3F3F,0x14141414,0x14141414,
|
||||
+
|
||||
+ 0x26262626,0x15101A1F,0x150E1B23,0x160F1B22,0x1F161620,0x261C101A,0x261E1018,0x211B1117,
|
||||
+ 0x18161415,0x15181916,0x151A1C17,0x151A1C17,0x15161B1A,0x14141414,0x14141414,0x14141414,
|
||||
+ 0x3C3C3C3C,0x19183031,0x19112B33,0x28152134,0x361C1630,0x3E250F28,0x3E2C0D1F,0x372D151F,
|
||||
+ 0x292B201E,0x1A282A1C,0x19292E1D,0x19282F20,0x1921312A,0x14141414,0x14141414,0x14141414,
|
||||
+ 0x55555555,0x24294742,0x2C263B41,0x392A3342,0x4E352E47,0x4E3B2C3F,0x4D412A36,0x4D49292E,
|
||||
+ 0x4249322B,0x33453C29,0x253F462C,0x1E374D34,0x1E2F4F3E,0x1F1F1F1F,0x14141414,0x14141414,
|
||||
+ 0x55555555,0x40424F4D,0x423F494C,0x4841454C,0x5248454F,0x524A434C,0x524C4248,0x52504345,
|
||||
+ 0x4D514642,0x474F4A42,0x414C4E44,0x3E485247,0x3E45524B,0x3F3F3F3F,0x14141414,0x14141414
|
||||
};
|
||||
|
||||
+#ifdef CALC_PALETTES
|
||||
+
|
||||
static void make_nes_pal_uv(uint8_t *u,uint8_t *v)
|
||||
{
|
||||
for (int c = 0; c < 16; c++) {
|
||||
@@ -152,6 +228,8 @@ static void make_nes_palette(int phases)
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
+#endif
|
||||
+
|
||||
uint8_t* _nofrendo_rom = 0;
|
||||
extern "C"
|
||||
char *osd_getromdata()
|
||||
@@ -219,9 +297,11 @@ public:
|
||||
|
||||
virtual void gen_palettes()
|
||||
{
|
||||
+#ifdef CALC_PALETTES
|
||||
make_nes_palette(3);
|
||||
make_nes_palette(4);
|
||||
make_alt_pal();
|
||||
+#endif
|
||||
}
|
||||
|
||||
virtual int info(const string& file, vector<string>& strs)
|
||||
@@ -437,7 +517,11 @@ public:
|
||||
return -1;
|
||||
}
|
||||
|
||||
- nes_emulate_init(path.c_str(),width,height);
|
||||
+ // from CornN64 commit 063b08f7479beb147960c99396b09bdb0b8746b4
|
||||
+ if (nes_emulate_init(path.c_str(),width,height))
|
||||
+ printf("NES init failed\n");
|
||||
+
|
||||
+ vTaskDelay(100 / portTICK_RATE_MS);
|
||||
_lines = nes_emulate_frame(true); // first frame!
|
||||
return 0;
|
||||
}
|
||||
@@ -453,7 +537,7 @@ public:
|
||||
{
|
||||
return _lines;
|
||||
}
|
||||
-
|
||||
+
|
||||
virtual int audio_buffer(int16_t* b, int len)
|
||||
{
|
||||
int n = frame_sample_count();
|
||||
diff --git a/src/gui.cpp b/src/gui.cpp
|
||||
index 2b2009a..db0a68a 100644
|
||||
--- a/src/gui.cpp
|
||||
+++ b/src/gui.cpp
|
||||
@@ -17,6 +17,11 @@
|
||||
|
||||
#include "emu.h"
|
||||
|
||||
+#include "freertos/FreeRTOS.h"
|
||||
+#include "driver/gpio.h"
|
||||
+#define GPIO_GREENLED 21
|
||||
+#define GPIO_BTOFF 33
|
||||
+
|
||||
using namespace std;
|
||||
|
||||
string get_ext(const string& s)
|
||||
@@ -633,7 +638,7 @@ public:
|
||||
}
|
||||
_hilited = max(0,min(_hilited,c-1));
|
||||
}
|
||||
-
|
||||
+
|
||||
int count()
|
||||
{
|
||||
switch (_tab) {
|
||||
@@ -1007,13 +1012,24 @@ void gui_update()
|
||||
_gui.update_video();
|
||||
|
||||
uint8_t buf[64];
|
||||
- int n = hid_get(buf,sizeof(buf)); // called from emulation loop
|
||||
- if (n > 0)
|
||||
- gui_hid(buf,n);
|
||||
-
|
||||
- n = get_hid_ir(buf);
|
||||
+ int n;
|
||||
+ if (gpio_get_level((gpio_num_t)GPIO_BTOFF) == 1)
|
||||
+ {
|
||||
+ n = hid_get(buf,sizeof(buf)); // called from emulation loop
|
||||
+ if (n > 0)
|
||||
+ gui_hid(buf,n);
|
||||
+ }
|
||||
+ n = get_hid_nintendo(buf);
|
||||
if (n > 0)
|
||||
- gui_hid(buf,n);
|
||||
+ {
|
||||
+ gui_hid(buf,n);
|
||||
+ // since code cleaning, we are a little too fast over here and the menu
|
||||
+ // bluetooth OFF is even faster. click sound doesn't always fully play.
|
||||
+ // this helps with menu flickering as well
|
||||
+ // only slow down with bluetooth off otherwise it fuck the HID server badly
|
||||
+ if (gpio_get_level((gpio_num_t)GPIO_BTOFF) == 0)
|
||||
+ ets_delay_us(5000);
|
||||
+ }
|
||||
}
|
||||
|
||||
void gui_key(int keycode, int pressed, int mods)
|
||||
@@ -1077,6 +1093,7 @@ static void ir(const uint8_t* j, int len)
|
||||
pad_key(GENERIC_DOWN,pad,81); // down
|
||||
pad_key(GENERIC_RIGHT,pad,79); // right
|
||||
pad_key(GENERIC_LEFT,pad,80); // left
|
||||
+ pad_key(GENERIC_OTHER,pad,58); // home/gui (NES controller)
|
||||
pad_key(GENERIC_RESET | GENERIC_FIRE_Z,pad,58); // home/gui
|
||||
pad_key(GENERIC_FIRE | GENERIC_FIRE_C | GENERIC_FIRE_B | GENERIC_FIRE_A,pad,40); // enter (A)
|
||||
_last_pad = pad;
|
||||
diff --git a/src/hid_server/hid_server.cpp b/src/hid_server/hid_server.cpp
|
||||
index 9672f2c..d041807 100644
|
||||
--- a/src/hid_server/hid_server.cpp
|
||||
+++ b/src/hid_server/hid_server.cpp
|
||||
@@ -27,6 +27,10 @@ using namespace std;
|
||||
#include "hci_server.h"
|
||||
#include "hid_server.h"
|
||||
|
||||
+// for bluetooth and hardwired coexistance, Quack jumpers
|
||||
+#include "driver/gpio.h"
|
||||
+#define GPIO_ADBSRC 32
|
||||
+
|
||||
enum {
|
||||
HID_SDP_PSM = 0x0001,
|
||||
HID_CONTROL_PSM = 0x0011,
|
||||
@@ -598,6 +602,10 @@ public:
|
||||
if (d->_wii_index == -1)
|
||||
{
|
||||
for (int i = 0; i < 4; i++) {
|
||||
+ if ((gpio_get_level((gpio_num_t)GPIO_ADBSRC) == 0) && i == 0)
|
||||
+ continue;
|
||||
+ if ((gpio_get_level((gpio_num_t)GPIO_ADBSRC) == 1) && i == 1)
|
||||
+ continue;
|
||||
if (wii_states[i].flags == 0) {
|
||||
wii_states[i].flags = wiimote;
|
||||
d->_wii_index = i;
|
||||
@@ -945,7 +953,7 @@ class HIDSource {
|
||||
if (d)
|
||||
d->socket_changed(cb.ss.socket,cb.ss.state);
|
||||
break;
|
||||
-
|
||||
+
|
||||
default:
|
||||
break;
|
||||
}
|
||||
diff --git a/src/video_out.h b/src/video_out.h
|
||||
index 57b82e8..866ae1f 100644
|
||||
--- a/src/video_out.h
|
||||
+++ b/src/video_out.h
|
||||
@@ -15,9 +15,22 @@
|
||||
** SOFTWARE.
|
||||
*/
|
||||
|
||||
-#define VIDEO_PIN 26
|
||||
-#define AUDIO_PIN 18 // can be any pin
|
||||
-#define IR_PIN 0 // TSOP4838 or equivalent on any pin if desired
|
||||
+#define VIDEO_PIN 25 // just for information, not selectable due to hardware limitations
|
||||
+#define AUDIO_PIN 2 // can be any pin
|
||||
+
|
||||
+//NES classic controller (wire colors might be different, double check!)
|
||||
+// ___
|
||||
+//DATA |o o| NC
|
||||
+//LATCH |o o| NC
|
||||
+//CLK |o o/ 5V
|
||||
+//GND |o_/
|
||||
+//
|
||||
+//5V (red) (5V on Quack due to hardware buffers)
|
||||
+//GND (white)
|
||||
+#define NES_CTRL_DAT 4 // # DATA controller (A or B) (black)
|
||||
+#define NES_CTRL_LATCH 14 // # LATCH (yellow)
|
||||
+#define NES_CTRL_CLK 12 // # CLOCK (green)
|
||||
+//On Quack, utilise the ADB buffer as RX for DATA and the Quadrature buffer as TX for clocks et al
|
||||
|
||||
int _pal_ = 0;
|
||||
|
||||
@@ -42,11 +55,13 @@ int _pal_ = 0;
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/i2s.h"
|
||||
|
||||
-#ifdef IR_PIN
|
||||
-#include "ir_input.h" // ir peripherals
|
||||
+#ifdef NES_CTRL_LATCH
|
||||
+#include "driver/gpio.h"
|
||||
+extern "C" {
|
||||
+#include "nintendo.h" // hard wired Nintendo controllers
|
||||
+}
|
||||
#endif
|
||||
|
||||
-
|
||||
//====================================================================================================
|
||||
//====================================================================================================
|
||||
//
|
||||
@@ -97,7 +112,7 @@ static esp_err_t start_dma(int line_width,int samples_per_cc, int ch = 1)
|
||||
_dma_desc[i].buf = (uint8_t*)heap_caps_calloc(1, n, MALLOC_CAP_DMA);
|
||||
if (!_dma_desc[i].buf)
|
||||
return -1;
|
||||
-
|
||||
+
|
||||
_dma_desc[i].owner = 1;
|
||||
_dma_desc[i].eof = 1;
|
||||
_dma_desc[i].length = n;
|
||||
@@ -171,9 +186,12 @@ void video_init_hw(int line_width, int samples_per_cc)
|
||||
ledcAttachPin(AUDIO_PIN, 0);
|
||||
ledcWrite(0,0);
|
||||
|
||||
- // IR input if used
|
||||
-#ifdef IR_PIN
|
||||
- pinMode(IR_PIN,INPUT);
|
||||
+ // Pin mappings for NES controller input
|
||||
+#ifdef NES_CTRL_LATCH
|
||||
+ pinMode(NES_CTRL_LATCH, GPIO_MODE_OUTPUT);
|
||||
+ digitalWrite(NES_CTRL_LATCH, 0);
|
||||
+ pinMode(NES_CTRL_CLK, GPIO_MODE_OUTPUT);
|
||||
+ digitalWrite(NES_CTRL_CLK, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -255,12 +273,12 @@ uint32_t xthal_get_ccount() {
|
||||
|
||||
void audio_sample(uint8_t s);
|
||||
|
||||
-void ir_sample();
|
||||
-
|
||||
-int get_hid_ir(uint8_t* buf)
|
||||
+extern "C" {
|
||||
+int get_hid_nintendo(uint8_t* buf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
+}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -351,10 +369,10 @@ void video_init(int samples_per_cc, int machine, const uint32_t* palette, int nt
|
||||
_hsync = usec(4.7);
|
||||
_pal_ = 0;
|
||||
} else {
|
||||
- pal_init();
|
||||
_pal_ = 1;
|
||||
+ pal_init();
|
||||
}
|
||||
-
|
||||
+
|
||||
_active_lines = 240;
|
||||
video_init_hw(_line_width,_samples_per_cc); // init the hardware
|
||||
}
|
||||
@@ -439,7 +457,7 @@ void IRAM_ATTR blit_pal(uint8_t* src, uint16_t* dst)
|
||||
|
||||
i += 4;
|
||||
c = *((uint32_t*)(src+i));
|
||||
-
|
||||
+
|
||||
// make 5 colors out of 4 by interpolating y: 0000 0111 1122 2223 3333
|
||||
c0 = c;
|
||||
c1 = c >> 8;
|
||||
@@ -478,7 +496,7 @@ void IRAM_ATTR blit_pal(uint8_t* src, uint16_t* dst)
|
||||
p = _palette + 64;
|
||||
dst += 88;
|
||||
break;
|
||||
-
|
||||
+
|
||||
case EMU_SMS:
|
||||
// 192 of 288 color clocks wide: roughly correct aspect ratio
|
||||
dst += 88;
|
||||
@@ -795,10 +813,6 @@ void IRAM_ATTR video_isr(volatile void* vbuf)
|
||||
audio_sample(s);
|
||||
//audio_sample(_sin64[_x++ & 0x3F]);
|
||||
|
||||
-#ifdef IR_PIN
|
||||
- ir_sample();
|
||||
-#endif
|
||||
-
|
||||
int i = _line_counter++;
|
||||
uint16_t* buf = (uint16_t*)vbuf;
|
||||
if (_pal_) {
|
||||
@@ -810,8 +824,7 @@ void IRAM_ATTR video_isr(volatile void* vbuf)
|
||||
burst(buf);
|
||||
blit(_lines[i-32],buf + _active_start);
|
||||
} else if (i < 304) { // post render/black 272-304
|
||||
- if (i < 272) // slight optimization here, once you have 2 blanking buffers
|
||||
- blanking(buf,false);
|
||||
+ blanking(buf,false);
|
||||
} else {
|
||||
pal_sync(buf,i); // 8 lines of sync 304-312
|
||||
}
|
3
Misc/esp_8_bit/boards.txt
Normal file
3
Misc/esp_8_bit/boards.txt
Normal file
@ -0,0 +1,3 @@
|
||||
pico32.menu.PartitionScheme.quack_8_bit=Quack+esp_8_bit (Optimised flash usage)
|
||||
pico32.menu.PartitionScheme.quack_8_bit.build.partitions=quack_8_bit
|
||||
pico32.menu.PartitionScheme.quack_8_bit.upload.maximum_size=1966080
|
BIN
Misc/esp_8_bit/bodge.png
Normal file
BIN
Misc/esp_8_bit/bodge.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
879
Misc/esp_8_bit/controller.diff
Normal file
879
Misc/esp_8_bit/controller.diff
Normal file
@ -0,0 +1,879 @@
|
||||
diff --git a/src/ir_input.h b/src/ir_input.h
|
||||
deleted file mode 100644
|
||||
index f02bde8..0000000
|
||||
--- a/src/ir_input.h
|
||||
+++ /dev/null
|
||||
@@ -1,672 +0,0 @@
|
||||
-
|
||||
-/* Copyright (c) 2010-2020, Peter Barrett
|
||||
- **
|
||||
- ** Permission to use, copy, modify, and/or distribute this software for
|
||||
- ** any purpose with or without fee is hereby granted, provided that the
|
||||
- ** above copyright notice and this permission notice appear in all copies.
|
||||
- **
|
||||
- ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
- ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
- ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
- ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
|
||||
- ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
- ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
- ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||
- ** SOFTWARE.
|
||||
- */
|
||||
-
|
||||
-#ifndef __ir_input__
|
||||
-#define __ir_input__
|
||||
-
|
||||
-// generate events on state changes of IR pin, feed to state machines.
|
||||
-// event timings have a resolution of HSYNC, timing is close enough between 15720 and 15600 to make this work
|
||||
-// poll to synthesize hid events at every frame
|
||||
-// i know there is a perfectly good peripheral for this in the ESP32 but this seems more fun somehow
|
||||
-
|
||||
-#define WEBTV_KEYBOARD
|
||||
-#define RETCON_CONTROLLER
|
||||
-#define FLASHBACK_CONTROLLER
|
||||
-//#define APPLE_TV_CONTROLLER
|
||||
-
|
||||
-uint8_t _ir_last = 0;
|
||||
-uint8_t _ir_count = 0;
|
||||
-uint8_t _keyDown = 0;
|
||||
-uint8_t _keyUp = 0;
|
||||
-
|
||||
-void IRAM_ATTR ir_event(uint8_t ticks, uint8_t value); // t is HSYNCH ticks, v is value
|
||||
-
|
||||
-inline void IRAM_ATTR ir_sample()
|
||||
-{
|
||||
- uint8_t ir = (GPIO.in & (1 << IR_PIN)) != 0;
|
||||
- if (ir != _ir_last)
|
||||
- {
|
||||
- ir_event(_ir_count,_ir_last);
|
||||
- _ir_count = 0;
|
||||
- _ir_last = ir;
|
||||
- }
|
||||
- if (_ir_count != 0xFF)
|
||||
- _ir_count++;
|
||||
-}
|
||||
-
|
||||
-class IRState {
|
||||
-public:
|
||||
- uint8_t _state = 0;
|
||||
- uint16_t _code = 0;
|
||||
- uint16_t _output = 0;
|
||||
- uint16_t _joy[2] = {0};
|
||||
- uint16_t _joy_last[2] = {0};
|
||||
- uint8_t _timer[2] = {0};
|
||||
-
|
||||
- void set(int i, int m, int t)
|
||||
- {
|
||||
- uint8_t b = 0;
|
||||
- if ((m & (GENERIC_LEFT | GENERIC_RIGHT)) == (GENERIC_LEFT | GENERIC_RIGHT))
|
||||
- b++; // bogus?
|
||||
- if ((m & (GENERIC_UP | GENERIC_DOWN)) == (GENERIC_UP | GENERIC_DOWN))
|
||||
- b++; // bogus?
|
||||
- if (b) {
|
||||
- printf("bogus:%04X\n",m);
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- _joy[i] = m;
|
||||
- _timer[i] = t;
|
||||
- }
|
||||
-
|
||||
- // make a fake hid event
|
||||
- int get_hid(uint8_t* dst)
|
||||
- {
|
||||
- if (_timer[0] && !--_timer[0])
|
||||
- _joy[0] = 0;
|
||||
- if (_timer[1] && !--_timer[1])
|
||||
- _joy[1] = 0;
|
||||
- if ((_joy[0] != _joy_last[0]) || (_joy[1] != _joy_last[1])) {
|
||||
- _joy_last[0] = _joy[0];
|
||||
- _joy_last[1] = _joy[1];
|
||||
- dst[0] = 0xA1;
|
||||
- dst[1] = 0x42;
|
||||
- dst[2] = _joy[0];
|
||||
- dst[3] = _joy[0]>>8;
|
||||
- dst[4] = _joy[1];
|
||||
- dst[5] = _joy[1]>>8;
|
||||
- return 6; // only sent if it changes
|
||||
- }
|
||||
- return 0;
|
||||
- }
|
||||
-};
|
||||
-
|
||||
-//==========================================================
|
||||
-//==========================================================
|
||||
-// Apple remote NEC code
|
||||
-// pretty easy to adapt to any NEC remote
|
||||
-
|
||||
-#ifdef APPLE_TV_CONTROLLER
|
||||
-
|
||||
-// Silver apple remote, 7 Bit code
|
||||
-// should work with both silvers and white
|
||||
-#define APPLE_MENU 0x40
|
||||
-#define APPLE_PLAY 0x7A
|
||||
-#define APPLE_CENTER 0x3A
|
||||
-#define APPLE_RIGHT 0x60
|
||||
-#define APPLE_LEFT 0x10
|
||||
-#define APPLE_UP 0x50
|
||||
-#define APPLE_DOWN 0x30
|
||||
-
|
||||
-#define APPLE_RELEASE 0x20 // sent after menu and play?
|
||||
-
|
||||
-// generic repeat code
|
||||
-#define NEC_REPEAT 0xAAAA
|
||||
-
|
||||
-/*
|
||||
- 9ms preamble ~142
|
||||
- 4.5ms 1 ~71 - start
|
||||
- 2.25ms ~35 - repeat
|
||||
-
|
||||
- 32 bits
|
||||
- 0 – a 562.5µs/562.5µs 9ish wide
|
||||
- 1 – a 562.5µs/1.6875ms 27ish wide
|
||||
-*/
|
||||
-
|
||||
-IRState _apple;
|
||||
-int get_hid_apple(uint8_t* dst)
|
||||
-{
|
||||
- if (_apple._output)
|
||||
- {
|
||||
- if (_apple._output != NEC_REPEAT)
|
||||
- _keyDown = (_apple._output >> 8) & 0x7F; // 7 bit code
|
||||
- _apple._output = 0;
|
||||
-
|
||||
- uint16_t k = 0;
|
||||
- switch (_keyDown) {
|
||||
- case APPLE_UP: k = GENERIC_UP; break;
|
||||
- case APPLE_DOWN: k = GENERIC_DOWN; break;
|
||||
- case APPLE_LEFT: k = GENERIC_LEFT; break;
|
||||
- case APPLE_RIGHT: k = GENERIC_RIGHT; break;
|
||||
- case APPLE_CENTER: k = GENERIC_FIRE; break;
|
||||
- case APPLE_MENU: k = GENERIC_RESET; break;
|
||||
- case APPLE_PLAY: k = GENERIC_SELECT; break;
|
||||
- }
|
||||
- _apple.set(0,k,15); // 108ms repeat period
|
||||
- }
|
||||
- return _apple.get_hid(dst);
|
||||
-}
|
||||
-
|
||||
-// NEC codes used by apple remote
|
||||
-void IRAM_ATTR ir_apple(uint8_t t, uint8_t v)
|
||||
-{
|
||||
- if (!v) {
|
||||
- if (t > 32)
|
||||
- _apple._state = 0;
|
||||
- } else {
|
||||
- if (t < 32)
|
||||
- {
|
||||
- _apple._code <<= 1;
|
||||
- if (t >= 12)
|
||||
- _apple._code |= 1;
|
||||
- if (++_apple._state == 32)
|
||||
- _apple._output = _apple._code; // Apple code in bits 14-8*
|
||||
- } else {
|
||||
- if (t > 32 && t < 40 && !_apple._state) // Repeat 2.25ms pulse 4.5ms start
|
||||
- _apple._output = NEC_REPEAT;
|
||||
- _apple._state = 0;
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-#endif
|
||||
-
|
||||
-//==========================================================
|
||||
-//==========================================================
|
||||
-// Atari Flashback 4 wireless controllers
|
||||
-
|
||||
-#ifdef FLASHBACK_CONTROLLER
|
||||
-
|
||||
-// HSYNCH period is 44/315*455 or 63.55555..us
|
||||
-// 18 bit code 1.87khz clock
|
||||
-// 2.3ms zero preamble // 36
|
||||
-// 0 is 0.27ms pulse // 4
|
||||
-// 1 is 0.80ms pulse // 13
|
||||
-
|
||||
-// Keycodes...
|
||||
-// Leading bit is 1 for player 1 control..
|
||||
-
|
||||
-#define PREAMBLE(_t) (_t >= 34 && _t <= 38)
|
||||
-#define SHORT(_t) (_t >= 2 && _t <= 6)
|
||||
-#define LONG(_t) (_t >= 11 && _t <= 15)
|
||||
-
|
||||
-// Codes come in pairs 33ms apart
|
||||
-// Sequence repeats every 133ms
|
||||
-// bitmap is released if no code for 10 vbls (167ms) or 0x01 (p1) / 0x0F (p2)
|
||||
-// up to 12 button bits, 4 bits of csum/p1/p2 indication
|
||||
-
|
||||
-// called at every loop ~60Hz
|
||||
-
|
||||
-IRState _flashback;
|
||||
-int get_hid_flashback(uint8_t* dst)
|
||||
-{
|
||||
- if (_flashback._output)
|
||||
- {
|
||||
- uint16_t m = _flashback._output >> 4; // 12 bits of buttons
|
||||
- printf("F:%04X\n",m);
|
||||
- uint8_t csum = _flashback._output & 0xF; // csum+1 for p1, csum-1 for p2
|
||||
- uint8_t s = m + (m >> 4) + (m >> 8);
|
||||
- if (((s+1) & 0xF) == csum)
|
||||
- _flashback.set(0,m,15);
|
||||
- else if (((s-1) & 0xF) == csum)
|
||||
- _flashback.set(1,m,20);
|
||||
- _flashback._output = 0;
|
||||
- }
|
||||
- return _flashback.get_hid(dst);
|
||||
-}
|
||||
-
|
||||
-void IRAM_ATTR ir_flashback(uint8_t t, uint8_t v)
|
||||
-{
|
||||
- if (_flashback._state == 0)
|
||||
- {
|
||||
- if (PREAMBLE(t) && (v == 0)) // long 0, rising edge of start bit
|
||||
- {
|
||||
- _flashback._state = 1;
|
||||
- }
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- if (v)
|
||||
- {
|
||||
- _flashback._code <<= 1;
|
||||
- if (LONG(t))
|
||||
- {
|
||||
- _flashback._code |= 1;
|
||||
- }
|
||||
- else if (!SHORT(t))
|
||||
- {
|
||||
- _flashback._state = 0; // framing error
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- if (++_flashback._state == 19)
|
||||
- {
|
||||
- _flashback._output = _flashback._code;
|
||||
- _flashback._state = 0;
|
||||
- }
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- if (!SHORT(t))
|
||||
- _flashback._state = 0; // Framing err
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-#endif
|
||||
-
|
||||
-//==========================================================
|
||||
-//==========================================================
|
||||
-// RETCON controllers
|
||||
-// 75ms keyboard repeat
|
||||
-// Preamble is 0.80ms low, 0.5 high
|
||||
-// Low: 0.57ms=0,0.27,s=1, high 0.37
|
||||
-// 16 bits
|
||||
-// Preamble = 800,500/63.55555 ~ 12.6,7.87
|
||||
-// LOW0 = 8.97
|
||||
-// LOW1 = 4.25
|
||||
-// HIGH = 5.82
|
||||
-
|
||||
-#ifdef RETCON_CONTROLLER
|
||||
-
|
||||
-// number of 63.55555 cycles per bit
|
||||
-#define PREAMBLE_L(_t) (_t >= 12 && _t <= 14) // 12/13/14 preamble
|
||||
-#define PREAMBLE_H(_t) (_t >= 6 && _t <= 10) // 8
|
||||
-#define LOW_0(_t) (_t >= 8 && _t <= 10) // 8/9/10
|
||||
-#define LOW_1(_t) (_t >= 4 && _t <= 6) // 4/5/6
|
||||
-
|
||||
-// map retcon to generic
|
||||
-const uint16_t _jmap[] = {
|
||||
- 0x0400, GENERIC_UP,
|
||||
- 0x0200, GENERIC_DOWN,
|
||||
- 0x0100, GENERIC_LEFT,
|
||||
- 0x0080, GENERIC_RIGHT,
|
||||
-
|
||||
- 0x1000, GENERIC_SELECT,
|
||||
- 0x0800, GENERIC_START,
|
||||
-
|
||||
- 0x0020, GENERIC_FIRE_X,
|
||||
- 0x0040, GENERIC_FIRE_Y,
|
||||
- 0x0002, GENERIC_FIRE_Z,
|
||||
-
|
||||
- 0x2000, GENERIC_FIRE_A,
|
||||
- 0x4000, GENERIC_FIRE_B,
|
||||
- 0x0008, GENERIC_FIRE_C,
|
||||
-};
|
||||
-
|
||||
-IRState _retcon;
|
||||
-int get_hid_retcon(uint8_t* dst)
|
||||
-{
|
||||
- if (_retcon._output)
|
||||
- {
|
||||
- uint16_t m = 0;
|
||||
- const uint16_t* jmap = _jmap;
|
||||
- int16_t i = 12;
|
||||
- uint16_t k = _retcon._output;
|
||||
- _retcon._output = 0;
|
||||
- while (i--)
|
||||
- {
|
||||
- if (jmap[0] & k)
|
||||
- m |= jmap[1];
|
||||
- jmap += 2;
|
||||
- }
|
||||
- printf("R:%04X\n",m);
|
||||
- _retcon.set(k >> 15,m,20);
|
||||
- }
|
||||
- return _retcon.get_hid(dst);
|
||||
-}
|
||||
-
|
||||
-void IRAM_ATTR ir_retcon(uint8_t t, uint8_t v)
|
||||
-{
|
||||
- if (_retcon._state == 0)
|
||||
- {
|
||||
- if (v == 0) { // start bit
|
||||
- if (PREAMBLE_L(t))
|
||||
- _retcon._state = 1;
|
||||
- }
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- if (!v)
|
||||
- {
|
||||
- _retcon._code <<= 1;
|
||||
- if (LOW_1(t))
|
||||
- _retcon._code |= 1;
|
||||
- if (_retcon._state++ == 16)
|
||||
- {
|
||||
- _retcon._output = _retcon._code;
|
||||
- _retcon._state = 0;
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-#endif
|
||||
-
|
||||
-
|
||||
-//==========================================================
|
||||
-//==========================================================
|
||||
-// Webtv keyboard
|
||||
-#ifdef WEBTV_KEYBOARD
|
||||
-
|
||||
-#define BAUDB 12 // Width of uart bit in HSYNCH
|
||||
-#define WT_PREAMBLE(_t) (_t >= 36 && _t <= 40) // 3.25 baud
|
||||
-#define SHORTBIT(_t) (_t >= 9 && _t <= 13) // 1.5ms ish
|
||||
-
|
||||
-// converts webtv keyboard to common scancodes
|
||||
-const uint8_t _ir2scancode[128] = {
|
||||
- 0x00, // 00
|
||||
- 0x00, // 02
|
||||
- 0x05, // 04 B
|
||||
- 0x00, // 06
|
||||
- 0x00, // 08
|
||||
- 0x51, // 0A Down
|
||||
- 0x00, // 0C
|
||||
- 0x00, // 0E
|
||||
- 0x00, // 10
|
||||
- 0x50, // 12 Left
|
||||
- 0xE6, // 14 Right Alt
|
||||
- 0x38, // 16 /
|
||||
- 0xE2, // 18 Left Alt
|
||||
- 0x4F, // 1A Right
|
||||
- 0x2C, // 1C Space
|
||||
- 0x11, // 1E N
|
||||
- 0x32, // 20 #
|
||||
- 0x00, // 22
|
||||
- 0x22, // 24 5
|
||||
- 0x41, // 26 F8
|
||||
- 0x3B, // 28 F2
|
||||
- 0xE4, // 2A Right Ctrl
|
||||
- 0x00, // 2C
|
||||
- 0x2E, // 2E =
|
||||
- 0x3A, // 30 F1
|
||||
- 0x4A, // 32 Home
|
||||
- 0x00, // 34
|
||||
- 0x2D, // 36 -
|
||||
- 0xE0, // 38 Left Ctrl
|
||||
- 0x35, // 3A `
|
||||
- 0x42, // 3C F9
|
||||
- 0x23, // 3E 6
|
||||
- 0x00, // 40
|
||||
- 0x00, // 42
|
||||
- 0x19, // 44 V
|
||||
- 0x37, // 46 .
|
||||
- 0x06, // 48 C
|
||||
- 0x68, // 4A F13
|
||||
- 0xE5, // 4C Right Shift
|
||||
- 0x36, // 4E ,
|
||||
- 0x1B, // 50 X
|
||||
- 0x4D, // 52 End
|
||||
- 0x00, // 54
|
||||
- 0x00, // 56
|
||||
- 0x1D, // 58 Z
|
||||
- 0x00, // 5A
|
||||
- 0x28, // 5C Return
|
||||
- 0x10, // 5E M
|
||||
- 0x00, // 60
|
||||
- 0xE7, // 62 Right GUI
|
||||
- 0x09, // 64 F
|
||||
- 0x0F, // 66 L
|
||||
- 0x07, // 68 D
|
||||
- 0x4E, // 6A PageDown
|
||||
- 0x00, // 6C
|
||||
- 0x0E, // 6E K
|
||||
- 0x16, // 70 S
|
||||
- 0x4B, // 72 PageUp
|
||||
- 0x00, // 74
|
||||
- 0x33, // 76 ;
|
||||
- 0x04, // 78 A
|
||||
- 0x00, // 7A
|
||||
- 0x31, // 7C |
|
||||
- 0x0D, // 7E J
|
||||
- 0x00, // 80
|
||||
- 0x00, // 82
|
||||
- 0x17, // 84 T
|
||||
- 0x40, // 86 F7
|
||||
- 0x3C, // 88 F3
|
||||
- 0x00, // 8A
|
||||
- 0xE1, // 8C Left Shift
|
||||
- 0x30, // 8E ]
|
||||
- 0x39, // 90 CapsLock
|
||||
- 0x00, // 92
|
||||
- 0x29, // 94 Escape
|
||||
- 0x2F, // 96 [
|
||||
- 0x2B, // 98 Tab
|
||||
- 0x00, // 9A
|
||||
- 0x2A, // 9C Backspace
|
||||
- 0x1C, // 9E Y
|
||||
- 0x00, // A0
|
||||
- 0x00, // A2
|
||||
- 0x21, // A4 4
|
||||
- 0x26, // A6 9
|
||||
- 0x20, // A8 3
|
||||
- 0x44, // AA F11
|
||||
- 0x00, // AC
|
||||
- 0x25, // AE 8
|
||||
- 0x1F, // B0 2
|
||||
- 0x00, // B2
|
||||
- 0x46, // B4 PrintScreen
|
||||
- 0x27, // B6 0
|
||||
- 0x1E, // B8 1
|
||||
- 0x45, // BA F12
|
||||
- 0x43, // BC F10
|
||||
- 0x24, // BE 7
|
||||
- 0x00, // C0
|
||||
- 0x00, // C2
|
||||
- 0x0A, // C4 G
|
||||
- 0x00, // C6
|
||||
- 0x3D, // C8 F4
|
||||
- 0x00, // CA
|
||||
- 0x00, // CC
|
||||
- 0x00, // CE
|
||||
- 0x3E, // D0 F5
|
||||
- 0x52, // D2 Up
|
||||
- 0xE3, // D4 Left GUI
|
||||
- 0x34, // D6 '
|
||||
- 0x29, // D8 Escape
|
||||
- 0x48, // DA Pause
|
||||
- 0x3F, // DC F6
|
||||
- 0x0B, // DE H
|
||||
- 0x00, // E0
|
||||
- 0x00, // E2
|
||||
- 0x15, // E4 R
|
||||
- 0x12, // E6 O
|
||||
- 0x08, // E8 E
|
||||
- 0x00, // EA
|
||||
- 0x00, // EC
|
||||
- 0x0C, // EE I
|
||||
- 0x1A, // F0 W
|
||||
- 0x00, // F2
|
||||
- 0x53, // F4 Numlock
|
||||
- 0x13, // F6 P
|
||||
- 0x14, // F8 Q
|
||||
- 0x00, // FA
|
||||
- 0x00, // FC
|
||||
- 0x18, // FE U
|
||||
-};
|
||||
-
|
||||
-
|
||||
-// IR Keyboard State
|
||||
-uint8_t _state = 0;
|
||||
-uint16_t _code = 0;
|
||||
-uint8_t _wt_keys[6] = {0};
|
||||
-uint8_t _wt_expire[6] = {0};
|
||||
-uint8_t _wt_modifiers = 0;
|
||||
-
|
||||
-static
|
||||
-uint8_t parity_check(uint8_t k)
|
||||
-{
|
||||
- uint8_t c;
|
||||
- uint8_t v = k;
|
||||
- for (c = 0; v; c++)
|
||||
- v &= v-1;
|
||||
- return (c & 1) ? k : 0;
|
||||
-}
|
||||
-
|
||||
-// make a mask from modifier keys
|
||||
-static
|
||||
-uint8_t ctrl_mask(uint8_t k)
|
||||
-{
|
||||
- switch (k) {
|
||||
- case 0x38: return KEY_MOD_LCTRL;
|
||||
- case 0x8C: return KEY_MOD_LSHIFT;
|
||||
- case 0x18: return KEY_MOD_LALT;
|
||||
- case 0xD4: return KEY_MOD_LGUI;
|
||||
- case 0x2A: return KEY_MOD_RCTRL;
|
||||
- case 0x4C: return KEY_MOD_RSHIFT;
|
||||
- case 0x14: return KEY_MOD_RALT;
|
||||
- case 0x62: return KEY_MOD_RGUI;
|
||||
- }
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-// update state of held keys
|
||||
-// produce a hid keyboard record
|
||||
-int get_hid_webtv(uint8_t* dst)
|
||||
-{
|
||||
- bool dirty = false;
|
||||
- uint8_t k = parity_check(_keyUp);
|
||||
- _keyUp = 0;
|
||||
- if (k) {
|
||||
- _wt_modifiers &= ~ctrl_mask(k);
|
||||
- for (int i = 0; i < 6; i++) {
|
||||
- if (_wt_keys[i] == k) {
|
||||
- _wt_expire[i] = 1; // key will expire this frame
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- k = parity_check(_keyDown);
|
||||
- _keyDown = 0;
|
||||
- if (k) {
|
||||
- _wt_modifiers |= ctrl_mask(k);
|
||||
-
|
||||
- // insert key into list of pressed keys
|
||||
- int j = 0;
|
||||
- for (int i = 0; i < 6; i++) {
|
||||
- if ((_wt_keys[i] == 0) || (_wt_expire[i] == 0) || (_wt_keys[i] == k)) {
|
||||
- j = i;
|
||||
- break;
|
||||
- }
|
||||
- if (_wt_expire[i] < _wt_expire[j])
|
||||
- j = i;
|
||||
- }
|
||||
- if (_wt_keys[j] != k) {
|
||||
- _wt_keys[j] = k;
|
||||
- dirty = true;
|
||||
- }
|
||||
- _wt_expire[j] = 8; // key will be down for ~130ms or 8 frames
|
||||
- }
|
||||
-
|
||||
- // generate hid keyboard events if anything was pressed or changed...
|
||||
- // A1 01 mods XX k k k k k k
|
||||
- dst[0] = 0xA1;
|
||||
- dst[1] = 0x01;
|
||||
- dst[2] = _wt_modifiers;
|
||||
- dst[3] = 0;
|
||||
- int j = 0;
|
||||
- for (int i = 0; i < 6; i++) {
|
||||
- dst[4+i] = 0;
|
||||
- if (_wt_expire[i]) {
|
||||
- if (!--_wt_expire[i])
|
||||
- dirty = true;
|
||||
- }
|
||||
- if (_wt_expire[i] == 0) {
|
||||
- _wt_keys[i] = 0;
|
||||
- } else {
|
||||
- dst[4 + j++] = _ir2scancode[_wt_keys[i] >> 1];
|
||||
- }
|
||||
- }
|
||||
- return dirty ? 10 : 0;
|
||||
-}
|
||||
-
|
||||
-// WebTV UART like keyboard protocol
|
||||
-// 3.25 bit 0 start preamble the 19 bits
|
||||
-// 10 bit code for keyup, keydown, all keys released etc
|
||||
-// 7 bit keycode + parity bit.
|
||||
-//
|
||||
-
|
||||
-#define KEYDOWN 0x4A
|
||||
-#define KEYUP 0x5E
|
||||
-void IRAM_ATTR ir_webtv(uint8_t t, uint8_t v)
|
||||
-{
|
||||
- if (_state == 0)
|
||||
- {
|
||||
- if (WT_PREAMBLE(t) && (v == 0)) // long 0, rising edge of start bit
|
||||
- _state = 1;
|
||||
- }
|
||||
- else if (_state == 1)
|
||||
- {
|
||||
- _state = (SHORTBIT(t) && (v == 1)) ? 2 : 0;
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- t += BAUDB>>1;
|
||||
- uint8_t bits = _state-2;
|
||||
- while ((t > BAUDB) && (bits < 16))
|
||||
- {
|
||||
- t -= BAUDB;
|
||||
- _code = (_code << 1) | v;
|
||||
- bits++;
|
||||
- }
|
||||
- if (bits == 16)
|
||||
- {
|
||||
- v = t <= BAUDB;
|
||||
- uint8_t md = _code >> 8;
|
||||
- _code |= v; // Low bit of code is is parity
|
||||
- if (md == KEYDOWN)
|
||||
- _keyDown = _code;
|
||||
- else if (md == KEYUP)
|
||||
- _keyUp = _code;
|
||||
- _state = 0; // got one
|
||||
- return;
|
||||
- }
|
||||
- _state = bits+2;
|
||||
- }
|
||||
-}
|
||||
-#endif
|
||||
-
|
||||
-// called from interrupt
|
||||
-void IRAM_ATTR ir_event(uint8_t t, uint8_t v)
|
||||
-{
|
||||
-#ifdef WEBTV_KEYBOARD
|
||||
- ir_webtv(t,v);
|
||||
-#endif
|
||||
-#ifdef RETCON_CONTROLLER
|
||||
- ir_retcon(t,v);
|
||||
-#endif
|
||||
-#ifdef APPLE_TV_CONTROLLER
|
||||
- ir_apple(t,v);
|
||||
-#endif
|
||||
-#ifdef FLASHBACK_CONTROLLER
|
||||
- ir_flashback(t,v);
|
||||
-#endif
|
||||
-}
|
||||
-
|
||||
-// called every frame from emu
|
||||
-int get_hid_ir(uint8_t* dst)
|
||||
-{
|
||||
- int n = 0;
|
||||
-#ifdef APPLE_TV_CONTROLLER
|
||||
- if (n = get_hid_apple(dst))
|
||||
- return n;
|
||||
-#endif
|
||||
-#ifdef RETCON_CONTROLLER
|
||||
- if (n = get_hid_retcon(dst))
|
||||
- return n;
|
||||
-#endif
|
||||
-#ifdef FLASHBACK_CONTROLLER
|
||||
- if (n = get_hid_flashback(dst))
|
||||
- return n;
|
||||
-#endif
|
||||
- #ifdef WEBTV_KEYBOARD
|
||||
- return get_hid_webtv(dst);
|
||||
- #endif
|
||||
- return 0;
|
||||
-}
|
||||
-#endif
|
||||
diff --git a/src/nintendo.h b/src/nintendo.h
|
||||
new file mode 100644
|
||||
index 0000000..ded6a51
|
||||
--- /dev/null
|
||||
+++ b/src/nintendo.h
|
||||
@@ -0,0 +1,195 @@
|
||||
+
|
||||
+/*
|
||||
+ ** Copyright (c) 2010-2020, Peter Barrett
|
||||
+ ** Copyright (c) 2024, Michel Depeige
|
||||
+ **
|
||||
+ ** Permission to use, copy, modify, and/or distribute this software for
|
||||
+ ** any purpose with or without fee is hereby granted, provided that the
|
||||
+ ** above copyright notice and this permission notice appear in all copies.
|
||||
+ **
|
||||
+ ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
+ ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
+ ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
+ ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
|
||||
+ ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
+ ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
+ ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||
+ ** SOFTWARE.
|
||||
+ */
|
||||
+
|
||||
+#ifndef __NINTENDO_H__
|
||||
+#define __NINTENDO_H__
|
||||
+
|
||||
+#undef NES_CONTROLLER
|
||||
+#define SNES_CONTROLLER
|
||||
+
|
||||
+/*
|
||||
+ * ^^ choose either NES or SNES above
|
||||
+ *
|
||||
+ * inspired from Peter's work, and mixed with code I had around + CornN64 commits:
|
||||
+ * - 8bdbe1c0ff234d1721ffe48472712b58b7f3683f
|
||||
+ * - 937a9567224ab981d41fef5ba0e9ddcf19758996
|
||||
+ *
|
||||
+ * code only uses ESP-IDF, no Arduinism
|
||||
+ *
|
||||
+ * ADBSRC switch set if the classic controller is player A (0, pulled down) or B (1)
|
||||
+ * the yellow LED on pin 26 is used as a controller activity LED by calling esp-idf stuff directly
|
||||
+ * pin 26 is DAC channel 2 but it isn't used nor enabled so it works
|
||||
+ */
|
||||
+
|
||||
+#define GPIO_YELLOWLED 26
|
||||
+#define GPIO_ADBSRC 32
|
||||
+
|
||||
+/* how many buttons to poll */
|
||||
+#ifdef NES_CONTROLLER
|
||||
+#define NC_CLOCKS 8
|
||||
+#endif
|
||||
+#ifdef SNES_CONTROLLER
|
||||
+#define NC_CLOCKS 12
|
||||
+#endif
|
||||
+
|
||||
+/*
|
||||
+ * this is added because poking into ESP-IDF is too fast. We are not letting
|
||||
+ * enough time for the controller shift register and/or the main loop in the menu
|
||||
+ */
|
||||
+#define NC_DELAY 8
|
||||
+
|
||||
+/* globals for controller memory */
|
||||
+uint16_t nc_joy[2] = {0};
|
||||
+uint16_t nc_joy_last[2] = {0};
|
||||
+
|
||||
+/* internal functions */
|
||||
+static void nc_set(int i, int m) {
|
||||
+ uint8_t b = 0;
|
||||
+
|
||||
+ /* do not store if directional pad combinaisons are impossible */
|
||||
+ if ((m & (GENERIC_LEFT | GENERIC_RIGHT)) == (GENERIC_LEFT | GENERIC_RIGHT))
|
||||
+ b++; // bogus?
|
||||
+ if ((m & (GENERIC_UP | GENERIC_DOWN)) == (GENERIC_UP | GENERIC_DOWN))
|
||||
+ b++; // bogus?
|
||||
+ if (b) {
|
||||
+ printf("bogus:%04X\n",m);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ nc_joy[i] = m;
|
||||
+}
|
||||
+
|
||||
+static int get_hid_nc(uint8_t* dst) {
|
||||
+ /* send only HID event if something changes */
|
||||
+ if ((nc_joy[0] != nc_joy_last[0]) || (nc_joy[1] != nc_joy_last[1])) {
|
||||
+ nc_joy_last[0] = nc_joy[0];
|
||||
+ nc_joy_last[1] = nc_joy[1];
|
||||
+ dst[0] = 0xA1;
|
||||
+ dst[1] = 0x42;
|
||||
+ dst[2] = nc_joy[0];
|
||||
+ dst[3] = nc_joy[0] >> 8;
|
||||
+ dst[4] = nc_joy[1];
|
||||
+ dst[5] = nc_joy[1] >> 8;
|
||||
+ return 6;
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/*************************************
|
||||
+ * Classic hard wired NES controller *
|
||||
+ *************************************/
|
||||
+enum NES_button {
|
||||
+ NES_A = 0,
|
||||
+ NES_B = 1,
|
||||
+ NES_SELECT = 2,
|
||||
+ NES_START = 3,
|
||||
+ NES_UP = 4,
|
||||
+ NES_DOWN = 5,
|
||||
+ NES_LEFT = 6,
|
||||
+ NES_RIGHT = 7,
|
||||
+};
|
||||
+
|
||||
+/***************************************
|
||||
+ * Classic hard wired SNES controllers *
|
||||
+ ***************************************/
|
||||
+enum SNES_button {
|
||||
+ SNES_B = 0,
|
||||
+ SNES_Y = 1,
|
||||
+ SNES_SELECT = 2,
|
||||
+ SNES_START = 3,
|
||||
+ SNES_UP = 4,
|
||||
+ SNES_DOWN = 5,
|
||||
+ SNES_LEFT = 6,
|
||||
+ SNES_RIGHT = 7,
|
||||
+ SNES_A = 8,
|
||||
+ SNES_X = 9,
|
||||
+ SNES_L = 10,
|
||||
+ SNES_R = 11,
|
||||
+};
|
||||
+
|
||||
+/* called every frame from emu */
|
||||
+int IRAM_ATTR get_hid_nintendo(uint8_t* dst) {
|
||||
+ uint16_t buttons = 0;
|
||||
+ uint16_t k = 0;
|
||||
+
|
||||
+ gpio_set_level((gpio_num_t)NES_CTRL_LATCH, 1);
|
||||
+ ets_delay_us(NC_DELAY);
|
||||
+ gpio_set_level((gpio_num_t)NES_CTRL_LATCH, 0);
|
||||
+ ets_delay_us(NC_DELAY);
|
||||
+
|
||||
+ for (int i = 0; i < NC_CLOCKS; i++) {
|
||||
+ buttons |= gpio_get_level((gpio_num_t)NES_CTRL_DAT) << i;
|
||||
+ gpio_set_level((gpio_num_t)NES_CTRL_CLK, 0);
|
||||
+ ets_delay_us(NC_DELAY);
|
||||
+ gpio_set_level((gpio_num_t)NES_CTRL_CLK, 1);
|
||||
+ ets_delay_us(NC_DELAY);
|
||||
+ }
|
||||
+
|
||||
+#ifdef SNES_CONTROLLER
|
||||
+ /* on NES, menu is LEFT (directional pad) + SELECT */
|
||||
+ if ((buttons & ((1<<NES_LEFT) | (1<<NES_SELECT)))==0) {
|
||||
+ k |= GENERIC_OTHER;
|
||||
+ }
|
||||
+ else {
|
||||
+ if ((buttons & (1<<NES_UP))==0) k |= GENERIC_UP;
|
||||
+ if ((buttons & (1<<NES_DOWN))==0) k |= GENERIC_DOWN;
|
||||
+ if ((buttons & (1<<NES_LEFT))==0) k |= GENERIC_LEFT;
|
||||
+ if ((buttons & (1<<NES_RIGHT))==0) k |= GENERIC_RIGHT;
|
||||
+ if ((buttons & (1<<NES_A))==0) k |= GENERIC_FIRE | GENERIC_FIRE_A;
|
||||
+ if ((buttons & (1<<NES_B))==0) k |= GENERIC_FIRE_B;
|
||||
+ if ((buttons & (1<<NES_START))==0) k |= GENERIC_START;
|
||||
+ if ((buttons & (1<<NES_SELECT))==0) k |= GENERIC_SELECT;
|
||||
+ }
|
||||
+#endif
|
||||
+#ifdef SNES_CONTROLLER
|
||||
+ /* on SNES, menu is LEFT + RIGHT buttons */
|
||||
+ if ((buttons & ((1<<SNES_L) | (1<<SNES_R)))==0) {
|
||||
+ k |= GENERIC_OTHER;
|
||||
+ }
|
||||
+ else {
|
||||
+ if ((buttons & (1 << SNES_UP)) == 0) k |= GENERIC_UP;
|
||||
+ if ((buttons & (1 << SNES_DOWN)) == 0) k |= GENERIC_DOWN;
|
||||
+ if ((buttons & (1 << SNES_LEFT)) == 0) k |= GENERIC_LEFT;
|
||||
+ if ((buttons & (1 << SNES_RIGHT)) == 0) k |= GENERIC_RIGHT;
|
||||
+ if ((buttons & (1 << SNES_A)) == 0) k |= GENERIC_FIRE | GENERIC_FIRE_A;
|
||||
+ if ((buttons & (1 << SNES_B)) == 0) k |= GENERIC_FIRE_B;
|
||||
+ if ((buttons & (1 << SNES_START)) == 0) k |= GENERIC_START;
|
||||
+ if ((buttons & (1 << SNES_SELECT)) == 0) k |= GENERIC_SELECT;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
+ /* Quack hard wired player A or B */
|
||||
+ if (gpio_get_level((gpio_num_t)GPIO_ADBSRC) == 0) {
|
||||
+ nc_set(0,k); // no repeat period
|
||||
+ nc_set(1,0); // no repeat period
|
||||
+ }
|
||||
+ else {
|
||||
+ nc_set(0,0); // no repeat period
|
||||
+ nc_set(1,k); // no repeat period
|
||||
+ }
|
||||
+
|
||||
+ //printf("NC:\t%04X, %X\n", buttons, k);
|
||||
+ if (k && buttons)
|
||||
+ gpio_set_level((gpio_num_t)GPIO_YELLOWLED, 1);
|
||||
+ else
|
||||
+ gpio_set_level((gpio_num_t)GPIO_YELLOWLED, 0);
|
||||
+
|
||||
+ return get_hid_nc(dst);
|
||||
+}
|
||||
+#endif
|
BIN
Misc/esp_8_bit/quack+esp_8_bit.jpeg
Normal file
BIN
Misc/esp_8_bit/quack+esp_8_bit.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 80 KiB |
6
Misc/esp_8_bit/quack_8_bit.csv
Normal file
6
Misc/esp_8_bit/quack_8_bit.csv
Normal file
@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x90000,
|
||||
app1, app, ota_1, 0xA0000,0x1B0000,
|
||||
spiffs, data, spiffs, 0x250000,0x1A0000,
|
|
Loading…
Reference in New Issue
Block a user