parent
53e9eab0cd
commit
95a80bfe81
|
@ -0,0 +1,17 @@
|
|||
#!/bin/bash ../install.sh
|
||||
|
||||
NAME='tinygb'
|
||||
VERSION='git'
|
||||
DOWNLOAD_URL="https://github.com/jewelcodes/tinygb.git#57fdaff675a6b5b963b2b6624868d9698eabe375"
|
||||
|
||||
configure() {
|
||||
:
|
||||
}
|
||||
|
||||
build() {
|
||||
make -f Makefile.banan_os -j$(nproc) || exit 1
|
||||
}
|
||||
|
||||
install() {
|
||||
cp -v tinygb "$BANAN_SYSROOT"/usr/bin || exit 1
|
||||
}
|
|
@ -0,0 +1,422 @@
|
|||
From 3e565f7d35e842e246db3371776adae74d02ae62 Mon Sep 17 00:00:00 2001
|
||||
From: Bananymous <bananymousosq@gmail.com>
|
||||
Date: Wed, 23 Apr 2025 13:10:38 +0300
|
||||
Subject: [PATCH] Add support for banan-os
|
||||
|
||||
---
|
||||
Makefile.banan_os | 28 +++
|
||||
src/platform/banan-os/main.cpp | 365 +++++++++++++++++++++++++++++++
|
||||
src/platform/banan-os/main.cpp.o | Bin 0 -> 23536 bytes
|
||||
3 files changed, 393 insertions(+)
|
||||
create mode 100644 Makefile.banan_os
|
||||
create mode 100644 src/platform/banan-os/main.cpp
|
||||
create mode 100644 src/platform/banan-os/main.cpp.o
|
||||
|
||||
diff --git a/Makefile.banan_os b/Makefile.banan_os
|
||||
new file mode 100644
|
||||
index 0000000..22e191e
|
||||
--- /dev/null
|
||||
+++ b/Makefile.banan_os
|
||||
@@ -0,0 +1,28 @@
|
||||
+CC = $(BANAN_ARCH)-banan_os-gcc
|
||||
+CXX = $(BANAN_ARCH)-banan_os-g++
|
||||
+LD = $(BANAN_ARCH)-banan_os-gcc
|
||||
+
|
||||
+CFLAGS = -c -O2 -Isrc/include -Wall
|
||||
+CXXFLAGS = --std=c++20
|
||||
+LDFLAGS = -O2 -lgui -linput
|
||||
+
|
||||
+SRC := $(shell find src -name "*.c" -not -path 'src/platform/*') $(shell find src/platform/banan-os -name "*.c" -or -name "*.cpp")
|
||||
+OBJ := $(addsuffix .o,$(SRC))
|
||||
+
|
||||
+all: tinygb
|
||||
+
|
||||
+clean:
|
||||
+ @rm -f $(OBJ)
|
||||
+ @rm -f tinygb
|
||||
+
|
||||
+%.c.o: %.c
|
||||
+ @echo -e "\x1B[0;1;35m [ CC ]\x1B[0m $@"
|
||||
+ $(CC) -o $@ $(CFLAGS) $<
|
||||
+
|
||||
+%.cpp.o: %.cpp
|
||||
+ @echo -e "\x1B[0;1;35m [ CC ]\x1B[0m $@"
|
||||
+ $(CXX) -o $@ $(CFLAGS) $(CXXFLAGS) $<
|
||||
+
|
||||
+tinygb: $(OBJ)
|
||||
+ @echo -e "\x1B[0;1;36m [ LD ]\x1B[0m tinygb"
|
||||
+ $(LD) $(OBJ) -o tinygb $(LDFLAGS)
|
||||
diff --git a/src/platform/banan-os/main.cpp b/src/platform/banan-os/main.cpp
|
||||
new file mode 100644
|
||||
index 0000000..e9c6a02
|
||||
--- /dev/null
|
||||
+++ b/src/platform/banan-os/main.cpp
|
||||
@@ -0,0 +1,365 @@
|
||||
+
|
||||
+/* tinygb - a tiny gameboy emulator
|
||||
+ (c) 2022 by jewel */
|
||||
+
|
||||
+extern "C" {
|
||||
+#include <tinygb.h>
|
||||
+}
|
||||
+
|
||||
+#include <LibGUI/Window.h>
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <time.h>
|
||||
+
|
||||
+long rom_size;
|
||||
+int scaling = 4;
|
||||
+int frameskip = 0; // no skip
|
||||
+
|
||||
+timing_t timing;
|
||||
+char *rom_filename;
|
||||
+
|
||||
+BAN::UniqPtr<LibGUI::Window> s_window;
|
||||
+
|
||||
+// Key Config
|
||||
+LibInput::Key key_a;
|
||||
+LibInput::Key key_b;
|
||||
+LibInput::Key key_start;
|
||||
+LibInput::Key key_select;
|
||||
+LibInput::Key key_up;
|
||||
+LibInput::Key key_down;
|
||||
+LibInput::Key key_left;
|
||||
+LibInput::Key key_right;
|
||||
+LibInput::Key key_throttle;
|
||||
+
|
||||
+LibInput::Key get_key(const char* keyname)
|
||||
+{
|
||||
+ if (keyname == nullptr);
|
||||
+ else if (!strcmp("a", keyname)) return LibInput::Key::A;
|
||||
+ else if (!strcmp("b", keyname)) return LibInput::Key::B;
|
||||
+ else if (!strcmp("c", keyname)) return LibInput::Key::C;
|
||||
+ else if (!strcmp("d", keyname)) return LibInput::Key::D;
|
||||
+ else if (!strcmp("e", keyname)) return LibInput::Key::E;
|
||||
+ else if (!strcmp("f", keyname)) return LibInput::Key::F;
|
||||
+ else if (!strcmp("g", keyname)) return LibInput::Key::G;
|
||||
+ else if (!strcmp("h", keyname)) return LibInput::Key::H;
|
||||
+ else if (!strcmp("i", keyname)) return LibInput::Key::I;
|
||||
+ else if (!strcmp("j", keyname)) return LibInput::Key::J;
|
||||
+ else if (!strcmp("k", keyname)) return LibInput::Key::K;
|
||||
+ else if (!strcmp("l", keyname)) return LibInput::Key::L;
|
||||
+ else if (!strcmp("m", keyname)) return LibInput::Key::M;
|
||||
+ else if (!strcmp("n", keyname)) return LibInput::Key::N;
|
||||
+ else if (!strcmp("o", keyname)) return LibInput::Key::O;
|
||||
+ else if (!strcmp("p", keyname)) return LibInput::Key::P;
|
||||
+ else if (!strcmp("q", keyname)) return LibInput::Key::Q;
|
||||
+ else if (!strcmp("r", keyname)) return LibInput::Key::R;
|
||||
+ else if (!strcmp("s", keyname)) return LibInput::Key::S;
|
||||
+ else if (!strcmp("t", keyname)) return LibInput::Key::T;
|
||||
+ else if (!strcmp("u", keyname)) return LibInput::Key::U;
|
||||
+ else if (!strcmp("v", keyname)) return LibInput::Key::V;
|
||||
+ else if (!strcmp("w", keyname)) return LibInput::Key::W;
|
||||
+ else if (!strcmp("x", keyname)) return LibInput::Key::X;
|
||||
+ else if (!strcmp("y", keyname)) return LibInput::Key::Y;
|
||||
+ else if (!strcmp("z", keyname)) return LibInput::Key::Z;
|
||||
+ else if (!strcmp("0", keyname)) return LibInput::Key::_0;
|
||||
+ else if (!strcmp("1", keyname)) return LibInput::Key::_1;
|
||||
+ else if (!strcmp("2", keyname)) return LibInput::Key::_2;
|
||||
+ else if (!strcmp("3", keyname)) return LibInput::Key::_3;
|
||||
+ else if (!strcmp("4", keyname)) return LibInput::Key::_4;
|
||||
+ else if (!strcmp("5", keyname)) return LibInput::Key::_5;
|
||||
+ else if (!strcmp("6", keyname)) return LibInput::Key::_6;
|
||||
+ else if (!strcmp("7", keyname)) return LibInput::Key::_7;
|
||||
+ else if (!strcmp("8", keyname)) return LibInput::Key::_8;
|
||||
+ else if (!strcmp("9", keyname)) return LibInput::Key::_9;
|
||||
+ else if (!strcmp("up", keyname)) return LibInput::Key::ArrowUp;
|
||||
+ else if (!strcmp("down", keyname)) return LibInput::Key::ArrowDown;
|
||||
+ else if (!strcmp("left", keyname)) return LibInput::Key::ArrowLeft;
|
||||
+ else if (!strcmp("right", keyname)) return LibInput::Key::ArrowRight;
|
||||
+ else if (!strcmp("space", keyname)) return LibInput::Key::Space;
|
||||
+ else if (!strcmp("rshift", keyname)) return LibInput::Key::RightShift;
|
||||
+ else if (!strcmp("lshift", keyname)) return LibInput::Key::LeftShift;
|
||||
+ else if (!strcmp("backspace", keyname)) return LibInput::Key::Backspace;
|
||||
+ else if (!strcmp("delete", keyname)) return LibInput::Key::Delete;
|
||||
+ else if (!strcmp("tab", keyname)) return LibInput::Key::Tab;
|
||||
+ else if (!strcmp("escape", keyname)) return LibInput::Key::Escape;
|
||||
+ else if (!strcmp("exclamation", keyname)) return LibInput::Key::ExclamationMark;
|
||||
+ else if (!strcmp("at", keyname)) return LibInput::Key::AtSign;
|
||||
+ else if (!strcmp("hash", keyname)) return LibInput::Key::Hashtag;
|
||||
+ else if (!strcmp("dollar", keyname)) return LibInput::Key::Dollar;
|
||||
+ else if (!strcmp("percent", keyname)) return LibInput::Key::Percent;
|
||||
+ else if (!strcmp("caret", keyname)) return LibInput::Key::Caret;
|
||||
+ else if (!strcmp("ampersand", keyname)) return LibInput::Key::Ampersand;
|
||||
+ else if (!strcmp("asterisk", keyname)) return LibInput::Key::Asterix;
|
||||
+ else if (!strcmp("leftparenthesis", keyname)) return LibInput::Key::OpenParenthesis;
|
||||
+ else if (!strcmp("rightparenthesis", keyname)) return LibInput::Key::CloseParenthesis;
|
||||
+
|
||||
+ return LibInput::Key::None;
|
||||
+}
|
||||
+
|
||||
+static void load_keys()
|
||||
+{
|
||||
+ key_a = get_key(config_file.a);
|
||||
+ if (key_a == LibInput::Key::None)
|
||||
+ key_a = LibInput::Key::Z;
|
||||
+
|
||||
+ key_b = get_key(config_file.b);
|
||||
+ if (key_b == LibInput::Key::None)
|
||||
+ key_b = LibInput::Key::X;
|
||||
+
|
||||
+ key_start = get_key(config_file.start);
|
||||
+ if (key_start == LibInput::Key::None)
|
||||
+ key_start = LibInput::Key::Enter;
|
||||
+
|
||||
+ key_select = get_key(config_file.select);
|
||||
+ if (key_select == LibInput::Key::None)
|
||||
+ key_select = LibInput::Key::RightShift;
|
||||
+
|
||||
+ key_up = get_key(config_file.up);
|
||||
+ if (key_up == LibInput::Key::None)
|
||||
+ key_up = LibInput::Key::ArrowUp;
|
||||
+
|
||||
+ key_down = get_key(config_file.down);
|
||||
+ if (key_down == LibInput::Key::None)
|
||||
+ key_down = LibInput::Key::ArrowDown;
|
||||
+
|
||||
+ key_left = get_key(config_file.left);
|
||||
+ if (key_left == LibInput::Key::None)
|
||||
+ key_left = LibInput::Key::ArrowLeft;
|
||||
+
|
||||
+ key_right = get_key(config_file.right);
|
||||
+ if (key_right == LibInput::Key::None)
|
||||
+ key_right = LibInput::Key::ArrowRight;
|
||||
+
|
||||
+ key_throttle = get_key(config_file.throttle);
|
||||
+ if (key_throttle == LibInput::Key::None)
|
||||
+ key_throttle = LibInput::Key::Space;
|
||||
+}
|
||||
+
|
||||
+void delay(int ms)
|
||||
+{
|
||||
+ const timespec ts {
|
||||
+ .tv_sec = static_cast<time_t>(ms / 1000),
|
||||
+ .tv_nsec = (ms % 1000) * 1000000
|
||||
+ };
|
||||
+ nanosleep(&ts, nullptr);
|
||||
+}
|
||||
+
|
||||
+void destroy_window()
|
||||
+{
|
||||
+ s_window.clear();
|
||||
+}
|
||||
+
|
||||
+void update_window(uint32_t *framebuffer)
|
||||
+{
|
||||
+ for (int i = 0; i < scaled_h; i++)
|
||||
+ {
|
||||
+ uint32_t* src = &framebuffer[i * scaled_w];
|
||||
+ uint32_t* dst = using_sgb_border
|
||||
+ ? &s_window->pixels()[(i + gb_y) * s_window->width() + gb_x]
|
||||
+ : &s_window->pixels()[i * s_window->width()];
|
||||
+ memcpy(dst, src, scaled_w * 4);
|
||||
+ }
|
||||
+
|
||||
+ if (framecount > frameskip)
|
||||
+ {
|
||||
+ s_window->invalidate();
|
||||
+ framecount = 0;
|
||||
+ drawn_frames++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void update_border(uint32_t *framebuffer)
|
||||
+{
|
||||
+ for (int i = 0; i < sgb_scaled_h; i++)
|
||||
+ {
|
||||
+ uint32_t* src = &framebuffer[i * sgb_scaled_w];
|
||||
+ uint32_t* dst = &s_window->pixels()[i * s_window->width()];
|
||||
+ memcpy(dst, src, sgb_scaled_w*4);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void resize_sgb_window()
|
||||
+{
|
||||
+ s_window->request_resize(SGB_WIDTH * scaling, SGB_HEIGHT * scaling);
|
||||
+
|
||||
+ bool resized { false };
|
||||
+ s_window->set_resize_window_event_callback([&]() { resized = true; });
|
||||
+ while (!resized)
|
||||
+ s_window->poll_events();
|
||||
+ s_window->set_resize_window_event_callback({});
|
||||
+
|
||||
+ ASSERT(s_window->width() == static_cast<uint32_t>(SGB_WIDTH * scaling));
|
||||
+ ASSERT(s_window->height() == static_cast<uint32_t>(SGB_HEIGHT * scaling));
|
||||
+}
|
||||
+
|
||||
+int main(int argc, char **argv)
|
||||
+{
|
||||
+ if(argc != 2)
|
||||
+ {
|
||||
+ fprintf(stdout, "usage: %s rom_name\n", argv[0]);
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ rom_filename = argv[1];
|
||||
+
|
||||
+ open_log();
|
||||
+ open_config();
|
||||
+ load_keys();
|
||||
+
|
||||
+ // open the rom
|
||||
+ FILE* rom_file = fopen(rom_filename, "r");
|
||||
+ if (rom_file == nullptr)
|
||||
+ {
|
||||
+ write_log("unable to open %s for reading: %s\n", rom_filename, strerror(errno));
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ fseek(rom_file, 0L, SEEK_END);
|
||||
+ rom_size = ftell(rom_file);
|
||||
+ fseek(rom_file, 0L, SEEK_SET);
|
||||
+
|
||||
+ write_log("loading rom from file %s, %d KiB\n", rom_filename, rom_size / 1024);
|
||||
+
|
||||
+ rom = malloc(rom_size);
|
||||
+ if (rom == nullptr)
|
||||
+ {
|
||||
+ write_log("unable to allocate memory\n");
|
||||
+ fclose(rom_file);
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ if (fread(rom, 1, rom_size, rom_file) <= 0)
|
||||
+ {
|
||||
+ write_log("an error occured while reading from rom file: %s\n", strerror(errno));
|
||||
+ fclose(rom_file);
|
||||
+ free(rom);
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ fclose(rom_file);
|
||||
+
|
||||
+ if (auto ret = LibGUI::Window::create(GB_WIDTH * scaling, GB_HEIGHT * scaling, "tinygb"_sv); !ret.is_error())
|
||||
+ s_window = ret.release_value();
|
||||
+ else
|
||||
+ {
|
||||
+ write_log("couldn't create SDL window: %s\n", ret.error().get_message());
|
||||
+ free(rom);
|
||||
+ return 1;
|
||||
+ }
|
||||
+
|
||||
+ s_window->set_key_event_callback([](LibGUI::EventPacket::KeyEvent::event_t event) {
|
||||
+ int key = 0;
|
||||
+ if (event.key == key_left)
|
||||
+ key = JOYPAD_LEFT;
|
||||
+ else if (event.key == key_right)
|
||||
+ key = JOYPAD_RIGHT;
|
||||
+ else if (event.key == key_up)
|
||||
+ key = JOYPAD_UP;
|
||||
+ else if (event.key == key_down)
|
||||
+ key = JOYPAD_DOWN;
|
||||
+ else if (event.key == key_a)
|
||||
+ key = JOYPAD_A;
|
||||
+ else if (event.key == key_b)
|
||||
+ key = JOYPAD_B;
|
||||
+ else if (event.key == key_start)
|
||||
+ key = JOYPAD_START;
|
||||
+ else if (event.key == key_select)
|
||||
+ key = JOYPAD_SELECT;
|
||||
+ else if (event.key == key_throttle)
|
||||
+ throttle_enabled = event.released();
|
||||
+ else if (event.pressed() && (event.key == LibInput::Key::Plus || event.key == LibInput::Key::Equals))
|
||||
+ next_palette();
|
||||
+ else if (event.pressed() && (event.key == LibInput::Key::Hyphen))
|
||||
+ prev_palette();
|
||||
+
|
||||
+ if (key)
|
||||
+ joypad_handle(event.pressed(), key);
|
||||
+ });
|
||||
+
|
||||
+ // start emulation
|
||||
+ memory_start();
|
||||
+ cpu_start();
|
||||
+ display_start();
|
||||
+ timer_start();
|
||||
+ sound_start();
|
||||
+
|
||||
+ time_t rawtime;
|
||||
+ struct tm *timeinfo;
|
||||
+ int sec = 500; // any invalid number
|
||||
+ int percentage;
|
||||
+ int throttle_underflow = 0;
|
||||
+ int throttle_target = throttle_lo + SPEED_ALLOWANCE;
|
||||
+
|
||||
+ for (;;)
|
||||
+ {
|
||||
+ s_window->poll_events();
|
||||
+
|
||||
+ for (timing.current_cycles = 0; timing.current_cycles < timing.main_cycles;)
|
||||
+ {
|
||||
+ cpu_cycle();
|
||||
+ display_cycle();
|
||||
+ timer_cycle();
|
||||
+ }
|
||||
+
|
||||
+ time(&rawtime);
|
||||
+ timeinfo = localtime(&rawtime);
|
||||
+
|
||||
+ if (sec != timeinfo->tm_sec)
|
||||
+ {
|
||||
+ sec = timeinfo->tm_sec;
|
||||
+ percentage = (drawn_frames * 1000) / 597;
|
||||
+
|
||||
+ // adjust cpu throttle according to acceptable fps (98%-102%)
|
||||
+ if (throttle_enabled){
|
||||
+ if(percentage < throttle_lo) {
|
||||
+ // emulation is too slow
|
||||
+ if(!throttle_time) {
|
||||
+ // throttle_time--;
|
||||
+
|
||||
+ if(!throttle_underflow) {
|
||||
+ throttle_underflow = 1;
|
||||
+ write_log("WARNING: CPU throttle interval has underflown, emulation may be too slow\n");
|
||||
+ }
|
||||
+ } else {
|
||||
+ //write_log("too slow; decreasing throttle time: %d\n", throttle_time);
|
||||
+
|
||||
+ // this will speed up the speed adjustments for a more natural feel
|
||||
+ if(percentage < (throttle_target/3)) throttle_time /= 3;
|
||||
+ else if(percentage < (throttle_target/2)) throttle_time /= 2;
|
||||
+ else throttle_time--;
|
||||
+ }
|
||||
+
|
||||
+ // prevent this from going too low
|
||||
+ if(throttle_time <= (THROTTLE_THRESHOLD/3)) {
|
||||
+ cycles_per_throttle += (cycles_per_throttle/5); // delay 20% less often
|
||||
+ throttle_time = (THROTTLE_THRESHOLD/3);
|
||||
+ }
|
||||
+ } else if(percentage > throttle_hi) {
|
||||
+ // emulation is too fast
|
||||
+ //write_log("too fast; increasing throttle time: %d\n", throttle_time);
|
||||
+
|
||||
+ if(throttle_time) {
|
||||
+ // to make sure we're not multiplying zero
|
||||
+ if(percentage > (throttle_target*3)) throttle_time *= 3;
|
||||
+ else if(percentage > (throttle_target*2)) throttle_time *= 2;
|
||||
+ else throttle_time++;
|
||||
+ }
|
||||
+ else {
|
||||
+ throttle_time++;
|
||||
+ }
|
||||
+
|
||||
+ // prevent unnecessary lag
|
||||
+ if(throttle_time > THROTTLE_THRESHOLD) {
|
||||
+ cycles_per_throttle -= (cycles_per_throttle/5); // delay 20% more often
|
||||
+ throttle_time = THROTTLE_THRESHOLD;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ drawn_frames = 0;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ die(0, "");
|
||||
+ return 0;
|
||||
+}
|
||||
--
|
||||
2.49.0
|
||||
|
Loading…
Reference in New Issue