use framebuffer
This commit is contained in:
@@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "gb.h"
|
||||||
|
|
||||||
|
// display is rotated, so TFT_WIDTH/HEIGHT cannot be used
|
||||||
|
#define DISPLAY_WIDTH TFT_HEIGHT
|
||||||
|
#define DISPLAY_HEIGHT TFT_WIDTH
|
||||||
|
|
||||||
enum class ScalingMode {
|
enum class ScalingMode {
|
||||||
NORMAL = 0,
|
NORMAL = 0,
|
||||||
STRETCH,
|
STRETCH,
|
||||||
@@ -11,6 +17,10 @@ enum class ScalingMode {
|
|||||||
|
|
||||||
extern volatile ScalingMode scalingMode;
|
extern volatile ScalingMode scalingMode;
|
||||||
|
|
||||||
|
extern struct gb_s gb;
|
||||||
|
extern palette_t palette; // Colour palette
|
||||||
|
extern uint_fast32_t frames;
|
||||||
|
|
||||||
/* Multicore command structure. */
|
/* Multicore command structure. */
|
||||||
union core_cmd {
|
union core_cmd {
|
||||||
struct
|
struct
|
||||||
@@ -31,6 +41,12 @@ union core_cmd {
|
|||||||
uint32_t full;
|
uint32_t full;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void nextPalette();
|
||||||
|
|
||||||
|
void prevPalette();
|
||||||
|
|
||||||
void lcd_draw_line(struct gb_s* gb, const uint8_t* pixels, const uint_fast8_t line);
|
void lcd_draw_line(struct gb_s* gb, const uint8_t* pixels, const uint_fast8_t line);
|
||||||
|
|
||||||
void core1_init();
|
void core1_init();
|
||||||
|
|
||||||
|
void reset();
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "peanut_gb_options.h"
|
||||||
|
#include "peanut_gb.h"
|
||||||
|
|
||||||
|
#include "gbcolors.h"
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define PEANUT_GB_HEADER_ONLY
|
||||||
|
#include "peanut_gb_options.h"
|
||||||
|
#include "peanut_gb.h"
|
||||||
|
|
||||||
|
#define GBCOLOR_HEADER_ONLY
|
||||||
|
#include "gbcolors.h"
|
||||||
+239
@@ -0,0 +1,239 @@
|
|||||||
|
#include "common.h"
|
||||||
|
#include "input.h"
|
||||||
|
|
||||||
|
#include "gb.h"
|
||||||
|
|
||||||
|
static struct
|
||||||
|
{
|
||||||
|
unsigned a : 1;
|
||||||
|
unsigned b : 1;
|
||||||
|
unsigned select : 1;
|
||||||
|
unsigned start : 1;
|
||||||
|
unsigned right : 1;
|
||||||
|
unsigned left : 1;
|
||||||
|
unsigned up : 1;
|
||||||
|
unsigned down : 1;
|
||||||
|
} prev_joypad_bits;
|
||||||
|
|
||||||
|
#ifndef USE_PAD_GPIO
|
||||||
|
static PCF8574 pcf8574(0x20, 16, 17);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef USE_PAD_GPIO
|
||||||
|
void initJoypad() {
|
||||||
|
gpio_set_function(GPIO_UP, GPIO_FUNC_SIO);
|
||||||
|
gpio_set_function(GPIO_DOWN, GPIO_FUNC_SIO);
|
||||||
|
gpio_set_function(GPIO_LEFT, GPIO_FUNC_SIO);
|
||||||
|
gpio_set_function(GPIO_RIGHT, GPIO_FUNC_SIO);
|
||||||
|
gpio_set_function(GPIO_A, GPIO_FUNC_SIO);
|
||||||
|
gpio_set_function(GPIO_B, GPIO_FUNC_SIO);
|
||||||
|
gpio_set_function(GPIO_SELECT, GPIO_FUNC_SIO);
|
||||||
|
gpio_set_function(GPIO_START, GPIO_FUNC_SIO);
|
||||||
|
|
||||||
|
gpio_set_dir(GPIO_UP, false);
|
||||||
|
gpio_set_dir(GPIO_DOWN, false);
|
||||||
|
gpio_set_dir(GPIO_LEFT, false);
|
||||||
|
gpio_set_dir(GPIO_RIGHT, false);
|
||||||
|
gpio_set_dir(GPIO_A, false);
|
||||||
|
gpio_set_dir(GPIO_B, false);
|
||||||
|
gpio_set_dir(GPIO_SELECT, false);
|
||||||
|
gpio_set_dir(GPIO_START, false);
|
||||||
|
|
||||||
|
gpio_pull_up(GPIO_UP);
|
||||||
|
gpio_pull_up(GPIO_DOWN);
|
||||||
|
gpio_pull_up(GPIO_LEFT);
|
||||||
|
gpio_pull_up(GPIO_RIGHT);
|
||||||
|
gpio_pull_up(GPIO_A);
|
||||||
|
gpio_pull_up(GPIO_B);
|
||||||
|
gpio_pull_up(GPIO_SELECT);
|
||||||
|
gpio_pull_up(GPIO_START);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void initJoypad() {
|
||||||
|
for (int pin = 0; pin < 8; ++pin) {
|
||||||
|
pcf8574.pinMode(pin, INPUT_PULLUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
pcf8574.setLatency(5);
|
||||||
|
|
||||||
|
if (pcf8574.begin()){
|
||||||
|
Serial.println("PCF8574 initialized");
|
||||||
|
}else{
|
||||||
|
Serial.println("PCF8574 initialization failed");
|
||||||
|
while (true) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void handleSerial() {
|
||||||
|
static uint64_t start_time = time_us_64();
|
||||||
|
|
||||||
|
/* Serial monitor commands */
|
||||||
|
int input = Serial.read();
|
||||||
|
if (input <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (input) {
|
||||||
|
case 'i':
|
||||||
|
gb.direct.interlace = !gb.direct.interlace;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
gb.direct.frame_skip = !gb.direct.frame_skip;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'b': {
|
||||||
|
uint64_t end_time;
|
||||||
|
uint32_t diff;
|
||||||
|
uint32_t fps;
|
||||||
|
|
||||||
|
end_time = time_us_64();
|
||||||
|
diff = end_time - start_time;
|
||||||
|
fps = ((uint64_t)frames * 1000 * 1000) / diff;
|
||||||
|
Serial.printf("Frames: %u\n"
|
||||||
|
"Time: %lu us\n"
|
||||||
|
"FPS: %lu\n",
|
||||||
|
frames, diff, fps);
|
||||||
|
Serial.flush();
|
||||||
|
frames = 0;
|
||||||
|
start_time = time_us_64();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '\n':
|
||||||
|
case '\r': {
|
||||||
|
gb.direct.joypad_bits.start = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '\b': {
|
||||||
|
gb.direct.joypad_bits.select = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '8': {
|
||||||
|
gb.direct.joypad_bits.up = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '2': {
|
||||||
|
gb.direct.joypad_bits.down = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '4': {
|
||||||
|
gb.direct.joypad_bits.left = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '6': {
|
||||||
|
gb.direct.joypad_bits.right = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'z':
|
||||||
|
case 'w': {
|
||||||
|
gb.direct.joypad_bits.a = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'x': {
|
||||||
|
gb.direct.joypad_bits.b = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'q':
|
||||||
|
reset();
|
||||||
|
|
||||||
|
case 'p':
|
||||||
|
nextPalette();
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handlePad() {
|
||||||
|
/* Update buttons state */
|
||||||
|
prev_joypad_bits.up = gb.direct.joypad_bits.up;
|
||||||
|
prev_joypad_bits.down = gb.direct.joypad_bits.down;
|
||||||
|
prev_joypad_bits.left = gb.direct.joypad_bits.left;
|
||||||
|
prev_joypad_bits.right = gb.direct.joypad_bits.right;
|
||||||
|
prev_joypad_bits.a = gb.direct.joypad_bits.a;
|
||||||
|
prev_joypad_bits.b = gb.direct.joypad_bits.b;
|
||||||
|
prev_joypad_bits.select = gb.direct.joypad_bits.select;
|
||||||
|
prev_joypad_bits.start = gb.direct.joypad_bits.start;
|
||||||
|
|
||||||
|
#ifdef USE_PAD_GPIO
|
||||||
|
gb.direct.joypad_bits.up = gpio_get(PIN_UP);
|
||||||
|
gb.direct.joypad_bits.down = gpio_get(PIN_DOWN);
|
||||||
|
gb.direct.joypad_bits.left = gpio_get(PIN_LEFT);
|
||||||
|
gb.direct.joypad_bits.right = gpio_get(PIN_RIGHT);
|
||||||
|
gb.direct.joypad_bits.a = gpio_get(PIN_A);
|
||||||
|
gb.direct.joypad_bits.b = gpio_get(PIN_B);
|
||||||
|
gb.direct.joypad_bits.select = gpio_get(PIN_SELECT);
|
||||||
|
gb.direct.joypad_bits.start = gpio_get(PIN_START);
|
||||||
|
#else
|
||||||
|
#if 0
|
||||||
|
Serial.printf("pins:\n");
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
int in = pcf8574.digitalRead(i);
|
||||||
|
Serial.printf(" %d", in);
|
||||||
|
}
|
||||||
|
Serial.printf("\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
gb.direct.joypad_bits.up = pcf8574.digitalRead(PIN_UP);
|
||||||
|
gb.direct.joypad_bits.down = pcf8574.digitalRead(PIN_DOWN);
|
||||||
|
gb.direct.joypad_bits.left = pcf8574.digitalRead(PIN_LEFT);
|
||||||
|
gb.direct.joypad_bits.right = pcf8574.digitalRead(PIN_RIGHT);
|
||||||
|
gb.direct.joypad_bits.a = pcf8574.digitalRead(PIN_A);
|
||||||
|
gb.direct.joypad_bits.b = pcf8574.digitalRead(PIN_B);
|
||||||
|
gb.direct.joypad_bits.select = pcf8574.digitalRead(PIN_SELECT);
|
||||||
|
gb.direct.joypad_bits.start = pcf8574.digitalRead(PIN_START);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* hotkeys (select + * combo)*/
|
||||||
|
if (!gb.direct.joypad_bits.select) {
|
||||||
|
#if ENABLE_SOUND
|
||||||
|
if (!gb.direct.joypad_bits.up && prev_joypad_bits.up) {
|
||||||
|
/* select + up: increase sound volume */
|
||||||
|
i2s_increase_volume(&i2s_config);
|
||||||
|
}
|
||||||
|
if (!gb.direct.joypad_bits.down && prev_joypad_bits.down) {
|
||||||
|
/* select + down: decrease sound volume */
|
||||||
|
i2s_decrease_volume(&i2s_config);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!gb.direct.joypad_bits.right && prev_joypad_bits.right) {
|
||||||
|
/* select + right: select the next manual color palette */
|
||||||
|
nextPalette();
|
||||||
|
}
|
||||||
|
if (!gb.direct.joypad_bits.left && prev_joypad_bits.left) {
|
||||||
|
/* select + left: select the previous manual color palette */
|
||||||
|
prevPalette();
|
||||||
|
}
|
||||||
|
if (!gb.direct.joypad_bits.start && prev_joypad_bits.start) {
|
||||||
|
/* select + start: save ram and resets to the game selection menu */
|
||||||
|
#if ENABLE_SDCARD
|
||||||
|
write_cart_ram_file(&gb);
|
||||||
|
#endif
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
if (!gb.direct.joypad_bits.a && prev_joypad_bits.a) {
|
||||||
|
/* select + A: enable/disable frame-skip => fast-forward */
|
||||||
|
gb.direct.frame_skip = !gb.direct.frame_skip;
|
||||||
|
Serial.printf("I gb.direct.frame_skip = %d\n", gb.direct.frame_skip);
|
||||||
|
}
|
||||||
|
if (!gb.direct.joypad_bits.b && prev_joypad_bits.b) {
|
||||||
|
/* select + B: change scaling mode */
|
||||||
|
scalingMode = (ScalingMode)(((int) scalingMode + 1) % ((int) ScalingMode::COUNT));
|
||||||
|
union core_cmd cmd;
|
||||||
|
cmd.cmd = CORE_CMD_IDLE_SET;
|
||||||
|
multicore_fifo_push_blocking(cmd.full);
|
||||||
|
Serial.printf("I Scaling mode: = %d\n", scalingMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <PCF8574.h>
|
||||||
|
|
||||||
|
/* GPIO Connections. */
|
||||||
|
#ifdef USE_PAD_GPIO
|
||||||
|
#define PIN_UP 2
|
||||||
|
#define PIN_DOWN 3
|
||||||
|
#define PIN_LEFT 4
|
||||||
|
#define PIN_RIGHT 5
|
||||||
|
#define PIN_A 6
|
||||||
|
#define PIN_B 7
|
||||||
|
#define PIN_SELECT 8
|
||||||
|
#define PIN_START 9
|
||||||
|
#else
|
||||||
|
#define PIN_UP 0
|
||||||
|
#define PIN_DOWN 1
|
||||||
|
#define PIN_LEFT 2
|
||||||
|
#define PIN_RIGHT 3
|
||||||
|
#define PIN_A 5
|
||||||
|
#define PIN_B 4
|
||||||
|
#define PIN_SELECT 6
|
||||||
|
#define PIN_START 7
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void initJoypad();
|
||||||
|
|
||||||
|
void handlePad();
|
||||||
|
void handleSerial();
|
||||||
+68
-42
@@ -1,23 +1,21 @@
|
|||||||
#include <TFT_eSPI.h>
|
#include <TFT_eSPI.h>
|
||||||
|
|
||||||
#define PEANUT_GB_HEADER_ONLY
|
#include "common.h"
|
||||||
#include "peanut_gb_options.h"
|
|
||||||
#include "peanut_gb.h"
|
|
||||||
|
|
||||||
// Note: must be included before core
|
#define USE_FRAMEBUFFER
|
||||||
#include "gbcolors.h"
|
|
||||||
#include "core.h"
|
|
||||||
|
|
||||||
static TFT_eSPI tft = TFT_eSPI();
|
static TFT_eSPI tft = TFT_eSPI();
|
||||||
|
#ifdef USE_FRAMEBUFFER
|
||||||
|
static TFT_eSprite framebuffer = TFT_eSprite(&tft);
|
||||||
|
#else
|
||||||
|
static TFT_eSPI& framebuffer = tft;
|
||||||
|
#endif
|
||||||
|
|
||||||
static uint8_t scaledLineOffsetTable[LCD_HEIGHT]; // scaled to 240 lines
|
static uint8_t scaledLineOffsetTable[LCD_HEIGHT]; // scaled to 240 lines
|
||||||
|
|
||||||
/* Pixel data is stored in here. */
|
/* Pixel data is stored in here. */
|
||||||
static uint8_t pixels_buffer[LCD_WIDTH];
|
static uint8_t pixels_buffer[LCD_WIDTH];
|
||||||
|
|
||||||
extern struct gb_s gb;
|
|
||||||
extern palette_t palette; // Colour palette
|
|
||||||
|
|
||||||
volatile ScalingMode scalingMode = ScalingMode::NORMAL;
|
volatile ScalingMode scalingMode = ScalingMode::NORMAL;
|
||||||
|
|
||||||
static int lcd_line_busy = 0;
|
static int lcd_line_busy = 0;
|
||||||
@@ -57,35 +55,54 @@ void lcd_draw_line(struct gb_s* gb, const uint8_t pixels[LCD_WIDTH],
|
|||||||
multicore_fifo_push_blocking(cmd.full);
|
multicore_fifo_push_blocking(cmd.full);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_write_pixels_normal(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb) {
|
#ifdef USE_FRAMEBUFFER
|
||||||
const uint16_t colOffset = (tft.width() - nmemb) / 2;
|
void lcd_pushColors(size_t offset, const uint16_t* pixels, uint_fast16_t count) {
|
||||||
const uint16_t lineOffset = (tft.height() - LCD_HEIGHT) / 2;
|
uint16_t* image = (uint16_t*) framebuffer.getPointer();
|
||||||
tft.setAddrWindow(colOffset, lineOffset + line, nmemb, 1);
|
memcpy(&image[offset], pixels, count * sizeof(uint16_t));
|
||||||
tft.pushColors((uint16_t*) pixels, nmemb, true);
|
}
|
||||||
|
#else
|
||||||
|
void lcd_pushColors(uint16_t colOffset, uint16_t lineOffset, const uint16_t* pixels, uint_fast16_t count) {
|
||||||
|
framebuffer.setAddrWindow(colOffset, lineOffset, count, 1);
|
||||||
|
tft.pushColors((uint16_t*) pixels, count, true);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void lcd_write_pixels_normal(const uint16_t* pixels, uint8_t line, uint_fast16_t count) {
|
||||||
|
const uint16_t colOffset = (DISPLAY_WIDTH - count) / 2;
|
||||||
|
const uint16_t screenLineOffset = (DISPLAY_HEIGHT - LCD_HEIGHT) / 2;
|
||||||
|
const uint16_t lineOffset = screenLineOffset + line;
|
||||||
|
lcd_pushColors(lineOffset * DISPLAY_WIDTH + colOffset, pixels, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_write_pixels_stretched(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb) {
|
void lcd_write_pixels_stretched(const uint16_t* pixels, uint8_t line, uint_fast16_t count) {
|
||||||
static uint16_t doubledPixels[320];
|
static uint16_t doubledPixels[DISPLAY_WIDTH];
|
||||||
uint16_t pos = 0;
|
uint16_t pos = 0;
|
||||||
for (int col = 0; col < nmemb; ++col) {
|
for (int col = 0; col < count; ++col) {
|
||||||
doubledPixels[pos++] = pixels[col];
|
doubledPixels[pos++] = pixels[col];
|
||||||
doubledPixels[pos++] = pixels[col];
|
doubledPixels[pos++] = pixels[col];
|
||||||
}
|
}
|
||||||
const uint16_t stretchedWidth = pos;
|
const uint16_t stretchedWidth = pos;
|
||||||
|
|
||||||
uint8_t repeatedLines = IS_REPEATED(line);
|
const uint8_t lineRepeated = IS_REPEATED(line);
|
||||||
tft.setAddrWindow(0, scaledLineOffsetTable[line], stretchedWidth, 1);
|
const uint16_t lineOffset = scaledLineOffsetTable[line];
|
||||||
tft.pushColors((uint16_t*)doubledPixels, stretchedWidth, true);
|
#ifdef USE_FRAMEBUFFER
|
||||||
if (repeatedLines) {
|
size_t offset = lineOffset * DISPLAY_WIDTH;
|
||||||
tft.setAddrWindow(0, scaledLineOffsetTable[line] + 1, stretchedWidth, 1);
|
lcd_pushColors(offset, doubledPixels, stretchedWidth);
|
||||||
tft.pushColors((uint16_t*)doubledPixels, stretchedWidth, true);
|
if (lineRepeated) {
|
||||||
|
lcd_pushColors(offset + DISPLAY_WIDTH, doubledPixels, stretchedWidth);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
lcd_pushColors(0, lineOffset, doubledPixels, stretchedWidth);
|
||||||
|
if (lineRepeated) {
|
||||||
|
lcd_pushColors(0, lineOffset, doubledPixels, stretchedWidth);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_write_pixels_stretched_keep_aspect(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb) {
|
void lcd_write_pixels_stretched_keep_aspect(const uint16_t* pixels, uint8_t line, uint_fast16_t count) {
|
||||||
static uint16_t doubledPixels[320];
|
static uint16_t doubledPixels[DISPLAY_WIDTH];
|
||||||
uint16_t pos = 0;
|
uint16_t pos = 0;
|
||||||
for (int col = 0; col < nmemb; ++col) {
|
for (int col = 0; col < count; ++col) {
|
||||||
doubledPixels[pos++] = pixels[col];
|
doubledPixels[pos++] = pixels[col];
|
||||||
if (IS_REPEATED(col)) {
|
if (IS_REPEATED(col)) {
|
||||||
doubledPixels[pos++] = pixels[col];
|
doubledPixels[pos++] = pixels[col];
|
||||||
@@ -93,15 +110,22 @@ void lcd_write_pixels_stretched_keep_aspect(const uint16_t* pixels, uint8_t line
|
|||||||
}
|
}
|
||||||
const uint16_t stretchedWidth = pos;
|
const uint16_t stretchedWidth = pos;
|
||||||
|
|
||||||
const uint16_t colOffset = (tft.width() - stretchedWidth) / 2;
|
const uint16_t colOffset = (DISPLAY_WIDTH - stretchedWidth) / 2;
|
||||||
|
|
||||||
uint8_t repeatedLines = IS_REPEATED(line);
|
uint8_t lineRepeated = IS_REPEATED(line);
|
||||||
tft.setAddrWindow(colOffset, scaledLineOffsetTable[line], stretchedWidth, 1);
|
const uint16_t lineOffset = scaledLineOffsetTable[line];
|
||||||
tft.pushColors((uint16_t*)doubledPixels, stretchedWidth, true);
|
#ifdef USE_FRAMEBUFFER
|
||||||
if (repeatedLines) {
|
size_t offset = lineOffset * DISPLAY_WIDTH + colOffset;
|
||||||
tft.setAddrWindow(colOffset, scaledLineOffsetTable[line] + 1, stretchedWidth, 1);
|
lcd_pushColors(offset, doubledPixels, stretchedWidth);
|
||||||
tft.pushColors((uint16_t*)doubledPixels, stretchedWidth, true);
|
if (lineRepeated) {
|
||||||
|
lcd_pushColors(offset + DISPLAY_WIDTH, doubledPixels, stretchedWidth);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
lcd_pushColors(colOffset, lineOffset, doubledPixels, stretchedWidth);
|
||||||
|
if (lineRepeated) {
|
||||||
|
lcd_pushColors(colOffset, lineOffset, doubledPixels, stretchedWidth);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_write_pixels(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb) {
|
void lcd_write_pixels(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb) {
|
||||||
@@ -121,16 +145,16 @@ void lcd_write_pixels(const uint16_t* pixels, uint8_t line, uint_fast16_t nmemb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void lcd_fill(uint16_t color) {
|
void lcd_fill(uint16_t color) {
|
||||||
|
#ifdef USE_FRAMEBUFFER
|
||||||
|
framebuffer.fillSprite(color);
|
||||||
|
#else
|
||||||
tft.fillScreen(color);
|
tft.fillScreen(color);
|
||||||
}
|
#endif
|
||||||
|
|
||||||
void lcd_fill_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t color) {
|
|
||||||
tft.fillRect(0, 0, tft.width(), tft.height(), TFT_BLACK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void lcd_text(char* s, uint8_t x, uint8_t y, uint16_t color, uint16_t bgcolor) {
|
void lcd_text(char* s, uint8_t x, uint8_t y, uint16_t color, uint16_t bgcolor) {
|
||||||
tft.setTextColor(TFT_WHITE, TFT_BLACK); // TODO
|
framebuffer.setTextColor(TFT_WHITE, TFT_BLACK); // TODO
|
||||||
tft.drawString(s, x, y);
|
framebuffer.drawString(s, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
void core1_lcd_draw_line(const uint_fast8_t line) {
|
void core1_lcd_draw_line(const uint_fast8_t line) {
|
||||||
@@ -176,10 +200,12 @@ void core1_init() {
|
|||||||
lcd_init();
|
lcd_init();
|
||||||
|
|
||||||
/* Clear LCD screen. */
|
/* Clear LCD screen. */
|
||||||
lcd_fill(0x0000);
|
lcd_fill(TFT_BLACK);
|
||||||
|
|
||||||
/* Set LCD window to DMG size. */
|
#ifdef USE_FRAMEBUFFER
|
||||||
lcd_fill_rect(31, 16, LCD_WIDTH, LCD_HEIGHT, 0x0000);
|
framebuffer.setColorDepth(16);
|
||||||
|
framebuffer.createSprite(tft.width(), tft.height());
|
||||||
|
#endif
|
||||||
|
|
||||||
calcExtraLineTable();
|
calcExtraLineTable();
|
||||||
|
|
||||||
|
|||||||
+11
-267
@@ -13,9 +13,7 @@
|
|||||||
* PERFORMANCE OF THIS SOFTWARE.
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Peanut-GB emulator settings
|
#include "gb.h"
|
||||||
#include "peanut_gb_options.h"
|
|
||||||
#include "peanut_gb.h"
|
|
||||||
|
|
||||||
/* C Headers */
|
/* C Headers */
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -25,40 +23,19 @@
|
|||||||
/* RP2040 Headers */
|
/* RP2040 Headers */
|
||||||
#include <hardware/vreg.h>
|
#include <hardware/vreg.h>
|
||||||
|
|
||||||
#include <PCF8574.h>
|
|
||||||
|
|
||||||
/* Project headers */
|
/* Project headers */
|
||||||
#include "hedley.h"
|
#include "hedley.h"
|
||||||
#include "minigb_apu.h"
|
#include "minigb_apu.h"
|
||||||
|
|
||||||
// #include "sdcard.h"
|
// #include "sdcard.h"
|
||||||
#include "core.h"
|
#include "common.h"
|
||||||
#include "game_bin.h"
|
#include "game_bin.h"
|
||||||
#include "i2s.h"
|
#include "i2s.h"
|
||||||
|
|
||||||
#define GBCOLOR_HEADER_ONLY
|
#define GBCOLOR_HEADER_ONLY
|
||||||
#include "gbcolors.h"
|
#include "gbcolors.h"
|
||||||
|
|
||||||
/* GPIO Connections. */
|
#include "input.h"
|
||||||
#ifdef USE_PAD_GPIO
|
|
||||||
#define PIN_UP 2
|
|
||||||
#define PIN_DOWN 3
|
|
||||||
#define PIN_LEFT 4
|
|
||||||
#define PIN_RIGHT 5
|
|
||||||
#define PIN_A 6
|
|
||||||
#define PIN_B 7
|
|
||||||
#define PIN_SELECT 8
|
|
||||||
#define PIN_START 9
|
|
||||||
#else
|
|
||||||
#define PIN_UP 0
|
|
||||||
#define PIN_DOWN 1
|
|
||||||
#define PIN_LEFT 2
|
|
||||||
#define PIN_RIGHT 3
|
|
||||||
#define PIN_A 5
|
|
||||||
#define PIN_B 4
|
|
||||||
#define PIN_SELECT 6
|
|
||||||
#define PIN_START 7
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if ENABLE_SOUND
|
#if ENABLE_SOUND
|
||||||
/**
|
/**
|
||||||
@@ -84,22 +61,10 @@ static unsigned char rom_bank0[65536];
|
|||||||
static uint8_t ram[32768];
|
static uint8_t ram[32768];
|
||||||
static uint8_t manual_palette_selected = 0;
|
static uint8_t manual_palette_selected = 0;
|
||||||
|
|
||||||
static PCF8574 pcf8574(0x20, 16, 17);
|
|
||||||
|
|
||||||
struct gb_s gb;
|
struct gb_s gb;
|
||||||
palette_t palette; // Colour palette
|
palette_t palette; // Colour palette
|
||||||
|
|
||||||
static struct
|
uint_fast32_t frames = 0;
|
||||||
{
|
|
||||||
unsigned a : 1;
|
|
||||||
unsigned b : 1;
|
|
||||||
unsigned select : 1;
|
|
||||||
unsigned start : 1;
|
|
||||||
unsigned right : 1;
|
|
||||||
unsigned left : 1;
|
|
||||||
unsigned up : 1;
|
|
||||||
unsigned down : 1;
|
|
||||||
} prev_joypad_bits;
|
|
||||||
|
|
||||||
#define putstdio(x) write(1, x, strlen(x))
|
#define putstdio(x) write(1, x, strlen(x))
|
||||||
|
|
||||||
@@ -145,14 +110,12 @@ void gb_error(struct gb_s* gb, const enum gb_error_e gb_err, const uint16_t addr
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
void startEmulator() {
|
void startEmulator() {
|
||||||
#if ENABLE_LCD
|
#if ENABLE_LCD
|
||||||
#if ENABLE_SDCARD
|
#if ENABLE_SDCARD
|
||||||
/* ROM File selector */
|
/* ROM File selector */
|
||||||
lcd_init();
|
lcd_init();
|
||||||
lcd_fill(TFT_BLACK); // 0x0000);
|
tft.fillScreen(TFT_BLACK);
|
||||||
rom_file_selector();
|
rom_file_selector();
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@@ -204,55 +167,7 @@ void reset() {
|
|||||||
watchdog_reboot(0,0,0);
|
watchdog_reboot(0,0,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_PAD_GPIO
|
void overclock() {
|
||||||
void initJoypad() {
|
|
||||||
gpio_set_function(GPIO_UP, GPIO_FUNC_SIO);
|
|
||||||
gpio_set_function(GPIO_DOWN, GPIO_FUNC_SIO);
|
|
||||||
gpio_set_function(GPIO_LEFT, GPIO_FUNC_SIO);
|
|
||||||
gpio_set_function(GPIO_RIGHT, GPIO_FUNC_SIO);
|
|
||||||
gpio_set_function(GPIO_A, GPIO_FUNC_SIO);
|
|
||||||
gpio_set_function(GPIO_B, GPIO_FUNC_SIO);
|
|
||||||
gpio_set_function(GPIO_SELECT, GPIO_FUNC_SIO);
|
|
||||||
gpio_set_function(GPIO_START, GPIO_FUNC_SIO);
|
|
||||||
|
|
||||||
gpio_set_dir(GPIO_UP, false);
|
|
||||||
gpio_set_dir(GPIO_DOWN, false);
|
|
||||||
gpio_set_dir(GPIO_LEFT, false);
|
|
||||||
gpio_set_dir(GPIO_RIGHT, false);
|
|
||||||
gpio_set_dir(GPIO_A, false);
|
|
||||||
gpio_set_dir(GPIO_B, false);
|
|
||||||
gpio_set_dir(GPIO_SELECT, false);
|
|
||||||
gpio_set_dir(GPIO_START, false);
|
|
||||||
|
|
||||||
gpio_pull_up(GPIO_UP);
|
|
||||||
gpio_pull_up(GPIO_DOWN);
|
|
||||||
gpio_pull_up(GPIO_LEFT);
|
|
||||||
gpio_pull_up(GPIO_RIGHT);
|
|
||||||
gpio_pull_up(GPIO_A);
|
|
||||||
gpio_pull_up(GPIO_B);
|
|
||||||
gpio_pull_up(GPIO_SELECT);
|
|
||||||
gpio_pull_up(GPIO_START);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void initJoypad() {
|
|
||||||
for (int pin = 0; pin < 8; ++pin) {
|
|
||||||
pcf8574.pinMode(pin, INPUT_PULLUP);
|
|
||||||
}
|
|
||||||
|
|
||||||
pcf8574.setLatency(5);
|
|
||||||
|
|
||||||
if (pcf8574.begin()){
|
|
||||||
Serial.println("PCF8574 initialized");
|
|
||||||
}else{
|
|
||||||
Serial.println("PCF8574 initialization failed");
|
|
||||||
while (true) ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void setup(void) {
|
|
||||||
/* Overclock. */
|
|
||||||
{
|
|
||||||
const unsigned vco = 1596 * 1000 * 1000; /* 266MHz */
|
const unsigned vco = 1596 * 1000 * 1000; /* 266MHz */
|
||||||
const unsigned div1 = 6, div2 = 1;
|
const unsigned div1 = 6, div2 = 1;
|
||||||
|
|
||||||
@@ -260,7 +175,10 @@ void setup(void) {
|
|||||||
sleep_ms(2);
|
sleep_ms(2);
|
||||||
set_sys_clock_pll(vco, div1, div2);
|
set_sys_clock_pll(vco, div1, div2);
|
||||||
sleep_ms(2);
|
sleep_ms(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
overclock();
|
||||||
|
|
||||||
/* Initialise USB serial connection for debugging. */
|
/* Initialise USB serial connection for debugging. */
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
@@ -312,181 +230,7 @@ void prevPalette() {
|
|||||||
manual_assign_palette(palette, manual_palette_selected);
|
manual_assign_palette(palette, manual_palette_selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleSerial(uint_fast32_t& frames) {
|
|
||||||
static uint64_t start_time = time_us_64();
|
|
||||||
|
|
||||||
/* Serial monitor commands */
|
|
||||||
int input = Serial.read();
|
|
||||||
if (input <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (input) {
|
|
||||||
case 'i':
|
|
||||||
gb.direct.interlace = !gb.direct.interlace;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'f':
|
|
||||||
gb.direct.frame_skip = !gb.direct.frame_skip;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'b': {
|
|
||||||
uint64_t end_time;
|
|
||||||
uint32_t diff;
|
|
||||||
uint32_t fps;
|
|
||||||
|
|
||||||
end_time = time_us_64();
|
|
||||||
diff = end_time - start_time;
|
|
||||||
fps = ((uint64_t)frames * 1000 * 1000) / diff;
|
|
||||||
Serial.printf("Frames: %u\n"
|
|
||||||
"Time: %lu us\n"
|
|
||||||
"FPS: %lu\n",
|
|
||||||
frames, diff, fps);
|
|
||||||
Serial.flush();
|
|
||||||
frames = 0;
|
|
||||||
start_time = time_us_64();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case '\n':
|
|
||||||
case '\r': {
|
|
||||||
gb.direct.joypad_bits.start = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case '\b': {
|
|
||||||
gb.direct.joypad_bits.select = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case '8': {
|
|
||||||
gb.direct.joypad_bits.up = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case '2': {
|
|
||||||
gb.direct.joypad_bits.down = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case '4': {
|
|
||||||
gb.direct.joypad_bits.left = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case '6': {
|
|
||||||
gb.direct.joypad_bits.right = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'z':
|
|
||||||
case 'w': {
|
|
||||||
gb.direct.joypad_bits.a = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'x': {
|
|
||||||
gb.direct.joypad_bits.b = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'q':
|
|
||||||
reset();
|
|
||||||
|
|
||||||
case 'p':
|
|
||||||
nextPalette();
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void handlePad() {
|
|
||||||
/* Update buttons state */
|
|
||||||
prev_joypad_bits.up = gb.direct.joypad_bits.up;
|
|
||||||
prev_joypad_bits.down = gb.direct.joypad_bits.down;
|
|
||||||
prev_joypad_bits.left = gb.direct.joypad_bits.left;
|
|
||||||
prev_joypad_bits.right = gb.direct.joypad_bits.right;
|
|
||||||
prev_joypad_bits.a = gb.direct.joypad_bits.a;
|
|
||||||
prev_joypad_bits.b = gb.direct.joypad_bits.b;
|
|
||||||
prev_joypad_bits.select = gb.direct.joypad_bits.select;
|
|
||||||
prev_joypad_bits.start = gb.direct.joypad_bits.start;
|
|
||||||
|
|
||||||
#ifdef USE_PAD_GPIO
|
|
||||||
gb.direct.joypad_bits.up = gpio_get(PIN_UP);
|
|
||||||
gb.direct.joypad_bits.down = gpio_get(PIN_DOWN);
|
|
||||||
gb.direct.joypad_bits.left = gpio_get(PIN_LEFT);
|
|
||||||
gb.direct.joypad_bits.right = gpio_get(PIN_RIGHT);
|
|
||||||
gb.direct.joypad_bits.a = gpio_get(PIN_A);
|
|
||||||
gb.direct.joypad_bits.b = gpio_get(PIN_B);
|
|
||||||
gb.direct.joypad_bits.select = gpio_get(PIN_SELECT);
|
|
||||||
gb.direct.joypad_bits.start = gpio_get(PIN_START);
|
|
||||||
#else
|
|
||||||
#if 0
|
|
||||||
Serial.printf("pins:\n");
|
|
||||||
for (int i = 0; i < 8; ++i) {
|
|
||||||
int in = pcf8574.digitalRead(i);
|
|
||||||
Serial.printf(" %d", in);
|
|
||||||
}
|
|
||||||
Serial.printf("\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
gb.direct.joypad_bits.up = pcf8574.digitalRead(PIN_UP);
|
|
||||||
gb.direct.joypad_bits.down = pcf8574.digitalRead(PIN_DOWN);
|
|
||||||
gb.direct.joypad_bits.left = pcf8574.digitalRead(PIN_LEFT);
|
|
||||||
gb.direct.joypad_bits.right = pcf8574.digitalRead(PIN_RIGHT);
|
|
||||||
gb.direct.joypad_bits.a = pcf8574.digitalRead(PIN_A);
|
|
||||||
gb.direct.joypad_bits.b = pcf8574.digitalRead(PIN_B);
|
|
||||||
gb.direct.joypad_bits.select = pcf8574.digitalRead(PIN_SELECT);
|
|
||||||
gb.direct.joypad_bits.start = pcf8574.digitalRead(PIN_START);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* hotkeys (select + * combo)*/
|
|
||||||
if (!gb.direct.joypad_bits.select) {
|
|
||||||
#if ENABLE_SOUND
|
|
||||||
if (!gb.direct.joypad_bits.up && prev_joypad_bits.up) {
|
|
||||||
/* select + up: increase sound volume */
|
|
||||||
i2s_increase_volume(&i2s_config);
|
|
||||||
}
|
|
||||||
if (!gb.direct.joypad_bits.down && prev_joypad_bits.down) {
|
|
||||||
/* select + down: decrease sound volume */
|
|
||||||
i2s_decrease_volume(&i2s_config);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (!gb.direct.joypad_bits.right && prev_joypad_bits.right) {
|
|
||||||
/* select + right: select the next manual color palette */
|
|
||||||
nextPalette();
|
|
||||||
}
|
|
||||||
if (!gb.direct.joypad_bits.left && prev_joypad_bits.left) {
|
|
||||||
/* select + left: select the previous manual color palette */
|
|
||||||
prevPalette();
|
|
||||||
}
|
|
||||||
if (!gb.direct.joypad_bits.start && prev_joypad_bits.start) {
|
|
||||||
/* select + start: save ram and resets to the game selection menu */
|
|
||||||
#if ENABLE_SDCARD
|
|
||||||
write_cart_ram_file(&gb);
|
|
||||||
#endif
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
if (!gb.direct.joypad_bits.a && prev_joypad_bits.a) {
|
|
||||||
/* select + A: enable/disable frame-skip => fast-forward */
|
|
||||||
gb.direct.frame_skip = !gb.direct.frame_skip;
|
|
||||||
Serial.printf("I gb.direct.frame_skip = %d\n", gb.direct.frame_skip);
|
|
||||||
}
|
|
||||||
if (!gb.direct.joypad_bits.b && prev_joypad_bits.b) {
|
|
||||||
/* select + B: change scaling mode */
|
|
||||||
scalingMode = (ScalingMode)(((int) scalingMode + 1) % ((int) ScalingMode::COUNT));
|
|
||||||
union core_cmd cmd;
|
|
||||||
cmd.cmd = CORE_CMD_IDLE_SET;
|
|
||||||
multicore_fifo_push_blocking(cmd.full);
|
|
||||||
Serial.printf("I Scaling mode: = %d\n", scalingMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
static uint_fast32_t frames = 0;
|
|
||||||
|
|
||||||
gb.gb_frame = 0;
|
gb.gb_frame = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
@@ -503,5 +247,5 @@ void loop() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
handlePad();
|
handlePad();
|
||||||
handleSerial(frames);
|
handleSerial();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,14 +23,12 @@
|
|||||||
// Tell the library to use parallel mode (otherwise SPI is assumed)
|
// Tell the library to use parallel mode (otherwise SPI is assumed)
|
||||||
//#define TFT_PARALLEL_8_BIT
|
//#define TFT_PARALLEL_8_BIT
|
||||||
#define TFT_PARALLEL_16_BIT // **** 16-bit parallel ONLY for RP2040 processor ****
|
#define TFT_PARALLEL_16_BIT // **** 16-bit parallel ONLY for RP2040 processor ****
|
||||||
|
#define RP2040_PIO_CLK_DIV 4 // 60ns
|
||||||
// Display type - only define if RPi display
|
|
||||||
//#define RPI_DISPLAY_TYPE // 20MHz maximum SPI
|
|
||||||
|
|
||||||
// Only define one driver, the other ones must be commented out
|
|
||||||
#define ILI9341_DRIVER // Generic driver for common displays
|
#define ILI9341_DRIVER // Generic driver for common displays
|
||||||
//#define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172
|
//#define ILI9341_2_DRIVER // Alternative ILI9341 driver, see https://github.com/Bodmer/TFT_eSPI/issues/1172
|
||||||
|
|
||||||
|
//#define ST7735_DRIVER
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display
|
#define ST7789_2_DRIVER // Minimal configuration option, define additional parameters below for this display
|
||||||
#define TFT_INVERSION_OFF
|
#define TFT_INVERSION_OFF
|
||||||
@@ -66,7 +64,7 @@
|
|||||||
#define TFT_D7 7
|
#define TFT_D7 7
|
||||||
#define TFT_WR 19 // Write strobe for modified Raspberry Pi TFT only
|
#define TFT_WR 19 // Write strobe for modified Raspberry Pi TFT only
|
||||||
#define TFT_DC 20 // Data Command control pin
|
#define TFT_DC 20 // Data Command control pin
|
||||||
#define TFT_CS 21 // Chip select control pin D8
|
//#define TFT_CS 21 // Chip select control pin D8
|
||||||
#define TFT_RST 22 // Reset pin (could connect to NodeMCU RST, see next line)
|
#define TFT_RST 22 // Reset pin (could connect to NodeMCU RST, see next line)
|
||||||
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
|
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user