From 3e565f7d35e842e246db3371776adae74d02ae62 Mon Sep 17 00:00:00 2001 From: Bananymous 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 +} + +#include + +#include +#include +#include + +long rom_size; +int scaling = 4; +int frameskip = 0; // no skip + +timing_t timing; +char *rom_filename; + +BAN::UniqPtr 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(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(SGB_WIDTH * scaling)); + ASSERT(s_window->height() == static_cast(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