plain n64 driver

This commit is contained in:
lif 2024-01-21 22:57:11 -08:00
parent 62ce62777d
commit a774bc4511
9 changed files with 4169 additions and 0 deletions

3
libdragon/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
build
gpsp.z64
filesystem/rom.gba

74
libdragon/Makefile Normal file
View File

@ -0,0 +1,74 @@
TARGET := gpsp.z64
SOURCES := n64main.c unf/usb.c unf/debug.c
#OPTFLAG = -O0 -ggdb
OPTFLAG = -O3 -flto=auto
CFLAGS := -Wall -Wno-unused-function -DFULLSCREEN
LDFLAGS := -flto=auto
#CFLAGS += -DTRACE_INSTRUCTIONS
#CFLAGS += -DTRACE_REGISTERS
#CFLAGS += -DTRACE_EVENTS
#CFLAGS += -DNO_PRINT_ONLY_TRACE
CORE_DIR := $(abspath ..)
HAVE_DYNAREC := 0
ifeq ($(HAVE_DYNAREC),1)
CFLAGS += -DHAVE_DYNAREC
endif
CFLAGS += -DMIPS_ARCH -DUSE_RGBA5551_FORMAT -DROM_BUFFER_SIZE=1
CFLAGS += -DHAVE_STRINGS_H -DHAVE_STDINT_H -DHAVE_INTTYPES_H -D__LIBRETRO__ -DINLINE=inline
CPU_ARCH := mips
include $(CORE_DIR)/Makefile.common
SOURCES_C := $(filter-out $(CORE_DIR)/libretro/libretro.c,$(SOURCES_C))
CFLAGS += $(INCFLAGS)
BUILD_DIR = build
CXXFLAGS := $(CFLAGS)
ASFLAGS := $(CFLAGS) -Wa,-I$(CORE_DIR) -DN64
include $(N64_INST)/include/n64.mk
N64_CFLAGS += $(OPTFLAG)
N64_CXXFLAGS += $(OPTFLAG)
OBJS := $(addprefix $(BUILD_DIR)/,$(SOURCES:.c=.o))
OBJS += $(addprefix $(BUILD_DIR)/,$(SOURCES_C:.c=.o))
OBJS += $(addprefix $(BUILD_DIR)/,$(SOURCES_CC:.cc=.o))
OBJS += $(addprefix $(BUILD_DIR)/,$(SOURCES_ASM:.S=.o))
all: $(TARGET)
clean:
$(RM) -rf $(BUILD_DIR)
.PHONY: all clean debug everdrive
$(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.cc
@mkdir -p $(dir $@)
@echo " [CXX] $<"
$(CXX) -c $(CXXFLAGS) -o $@ $<
$(TARGET): N64_ROM_TITLE = "gameplaySP64"
$(TARGET): $(BUILD_DIR)/$(TARGET:.z64=.dfs)
$(BUILD_DIR)/$(TARGET:.z64=.elf): $(OBJS)
$(BUILD_DIR)/$(TARGET:.z64=.dfs): $(wildcard filesystem/*)
debug: $(TARGET)
ares --setting DebugServer/Enabled=true \
--setting Boot/Debugger=true \
--setting General/HomebrewMode=true \
$(TARGET) | grep -vE '^CPU ffffffff[^a]' &
gdb $(BUILD_DIR)/$(TARGET:.z64=.elf) -ex 'target remote tcp::9123'
everdrive: $(TARGET)
usb64 -rom=$(TARGET)
usb64 -start
-include $(OBJS:.o=.d)

25
libdragon/README.md Normal file
View File

@ -0,0 +1,25 @@
build with libdragon unstable. https://libdragon.dev/
requires real hardware or ares-emu to run. https://ares-emu.net/
because the n64's cpu needs *ten instructions* to byte-swap a word (and eight
to byte-swap a half-word), we just leave memory in word-wise big-endian and
simply invert the lower bits of addresses whenever we access bytes or halfwords
at runtime (just one XOR instruction, and no need to touch word accesses -
the most common by a landslide - at all!)
for this scheme to work, the ROM must also already be in word-wise big-endian,
which you can achieve with binutils like so:
```
objcopy -I binary -O binary --reverse-bytes=4 .../game.gba .../gpsp/libdragon/filesystem/rom.gba
```
dynarec doesn't quite work yet - crashes for reasons i don't yet understand!
to attempt to use it and/or debug, change the variables in the Makefile.
(hint: there's also a make target `debug` that works if you have ares and
multiarch gdb, but it doesn't change the build flags - that still needs to be
done by hand in the Makefile)
ppu emulation is still done in software on the main cpu - i am hoping to make
a renderer that utilizes the RCP, but that may take some time to implement.

View File

189
libdragon/n64main.c Normal file
View File

@ -0,0 +1,189 @@
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <libdragon.h>
#include <display.h>
#include <system.h>
#include "unf/usb.h"
#include "unf/debug.h"
#include "../common.h"
#include "../memmap.h"
#include "../gba_memory.h"
#include "../gba_cc_lut.h"
// Usually 59.72750057 Hz, unless GBC_RATE is overclocked (for 60FPS)
#define GBA_FPS ((float) GBC_BASE_RATE) / (308 * 228 * 4)
//static s16 audio_sample_buffer[(audio_samples_per_frame + 1) * 2];
static s16 audio_sample_buffer[2194];
static float audio_samples_per_frame = (float)(GBA_SOUND_FREQUENCY) / (float)(GBA_FPS);
static float audio_samples_accumulator = 0.0f;
static int16_t g_joypads[JOYPAD_PORT_COUNT][RETRO_DEVICE_ID_JOYPAD_R+1];
u32 skip_next_frame = 0;
boot_mode selected_boot_mode = boot_game;
int dynarec_enable;
int sprite_limit = 1;
u32 idle_loop_target_pc = 0xFFFFFFFF;
u32 translation_gate_target_pc[MAX_TRANSLATION_GATES];
u32 translation_gate_targets = 0;
surface_t* display_surface = NULL;
size_t gba_screen_pixel_offset = 0;
static void audio_run(void)
{
u32 samples_to_read;
u32 samples_produced;
/* audio_samples_per_frame is decimal;
* get integer component */
samples_to_read = (u32)audio_samples_per_frame;
/* Account for fractional component */
audio_samples_accumulator += audio_samples_per_frame -
(float)samples_to_read;
if (audio_samples_accumulator >= 1.0f)
{
samples_to_read++;
audio_samples_accumulator -= 1.0f;
}
samples_produced = sound_read_samples(audio_sample_buffer, samples_to_read);
audio_push(audio_sample_buffer, samples_produced, false);
}
static void allocate_video_surface() {
display_surface = display_get();
gba_screen_pixels = display_surface->buffer;
gba_screen_pixels += gba_screen_pixel_offset;
}
static void video_run(void)
{
if (display_surface != NULL) {
display_show(display_surface);
}
allocate_video_surface();
}
static void setup_screen_geometry() {
resolution_t res;
filter_options_t filt;
#ifdef FULLSCREEN
res.width = GBA_SCREEN_WIDTH;
res.height = GBA_SCREEN_WIDTH * 3 / 4;
filt = FILTERS_RESAMPLE_ANTIALIAS;
#else // integer scaled, centered
res.width = 320;
res.height = 240;
filt = FILTERS_RESAMPLE;
#endif
// TODO: do any of the interlace options give us anything like interframe blending?
res.interlaced = INTERLACE_OFF;
gba_screen_pitch = res.width;
// center in screen
gba_screen_pixel_offset = ((res.width - GBA_SCREEN_WIDTH) + (res.width * (res.height - GBA_SCREEN_HEIGHT))) / 2;
display_init(res, DEPTH_16_BPP, 3, GAMMA_NONE, filt);
}
int16_t input_state(unsigned port, unsigned device, unsigned index, unsigned id) {
if (port < JOYPAD_PORT_COUNT && device == RETRO_DEVICE_JOYPAD) {
return g_joypads[port][id];
} else {
return 0;
}
}
static void input_poll() {
joypad_poll();
JOYPAD_PORT_FOREACH(i) {
joypad_buttons_t inp = joypad_get_buttons(i);
g_joypads[i][RETRO_DEVICE_ID_JOYPAD_A] = inp.a;
g_joypads[i][RETRO_DEVICE_ID_JOYPAD_B] = inp.b;
g_joypads[i][RETRO_DEVICE_ID_JOYPAD_UP] = inp.d_up;
g_joypads[i][RETRO_DEVICE_ID_JOYPAD_DOWN] = inp.d_down;
g_joypads[i][RETRO_DEVICE_ID_JOYPAD_LEFT] = inp.d_left;
g_joypads[i][RETRO_DEVICE_ID_JOYPAD_RIGHT] = inp.d_right;
g_joypads[i][RETRO_DEVICE_ID_JOYPAD_START] = inp.start;
g_joypads[i][RETRO_DEVICE_ID_JOYPAD_SELECT] = inp.z;
g_joypads[i][RETRO_DEVICE_ID_JOYPAD_L] = inp.l;
g_joypads[i][RETRO_DEVICE_ID_JOYPAD_R] = inp.r;
}
}
void set_fastforward_override(bool fastforward) {}
int stdio_stderr_redirect(char* buf, unsigned len) {
usb_write(DATATYPE_TEXT, buf, len);
return write(2, buf, len);
}
int main() {
// UNFLoader interface plumbing for flash carts
stdio_t console_calls = { 0, stdio_stderr_redirect, 0 };
//debug_initialize();
usb_initialize();
hook_stdio_calls(&console_calls);
printf("UNFLoader debug initialized!\n");
// ISViewer (i.e. ares-emu)
debug_init_isviewer();
printf("ISViewer debug initialized!\n");
dfs_init(DFS_DEFAULT_LOCATION);
setup_screen_geometry();
allocate_video_surface();
audio_init(GBA_SOUND_FREQUENCY, 4);
joypad_init();
// gpsp input.c implements this directly.
retro_set_input_state(input_state);
memcpy(bios_rom, open_gba_bios_rom, sizeof(bios_rom));
init_gamepak_buffer();
init_sound();
memset(gamepak_backup, 0xff, sizeof(gamepak_backup));
if (load_gamepak(NULL, "rom:/rom.gba", 0, 0) != 0)
{
printf("Could not load the game file.");
return -1;
}
reset_gba();
while(true) {
input_poll();
update_input();
/* This runs just a frame */
#ifdef HAVE_DYNAREC
if (dynarec_enable)
execute_arm_translate(execute_cycles);
else
#endif
{
/* Sticky bits only used in interpreter */
clear_gamepak_stickybits();
execute_arm(execute_cycles);
}
audio_run();
video_run();
}
}

2100
libdragon/unf/debug.c Executable file

File diff suppressed because it is too large Load Diff

182
libdragon/unf/debug.h Executable file
View File

@ -0,0 +1,182 @@
#ifndef UNFL_DEBUG_H
#define UNFL_DEBUG_H
/*********************************
Settings macros
*********************************/
#define LIBDRAGON
// Enable/Disable debug
#ifndef DEBUG_MODE
#define DEBUG_MODE 1 // Enable/Disable debug mode
#endif
// Settings
#define DEBUG_INIT_MSG 1 // Print a message when debug mode has initialized
#define AUTOPOLL_ENABLED 1 // Automatically poll the USB on a timer
#define AUTOPOLL_TIME 200 // Time (in milliseconds) between auto polls
#define USE_FAULTTHREAD 1 // Create a fault detection thread (libultra only)
#define USE_RDBTHREAD 1 // Create a remote debugger thread
#define OVERWRITE_OSPRINT 1 // Replaces osSyncPrintf calls with debug_printf (libultra only)
#define MAX_COMMANDS 25 // The max amount of user defined commands possible
// USB thread definitions (libultra only)
#define USB_THREAD_ID 14
#define USB_THREAD_PRI 126
#define USB_THREAD_STACK 0x2000
// Fault thread definitions (libultra only)
#define FAULT_THREAD_ID 13
#define FAULT_THREAD_PRI 125
#define FAULT_THREAD_STACK 0x2000
// Remote debugger thread definitions (libultra only)
#define RDB_THREAD_ID 15
#define RDB_THREAD_PRI 124
#define RDB_THREAD_STACK 0x2000
/*********************************
Debug Functions
*********************************/
#if DEBUG_MODE
/*==============================
debug_initialize
Initializes the debug and USB library.
Should be called last during game initialization.
==============================*/
extern void debug_initialize();
/*==============================
debug_printf
Prints a formatted message to the developer's command prompt.
Supports up to 256 characters.
@param A string to print
@param variadic arguments to print as well
==============================*/
extern void debug_printf(const char* message, ...);
/*==============================
debug_dumpbinary
Dumps a binary file through USB
@param The file to dump
@param The size of the file
==============================*/
extern void debug_dumpbinary(void* file, int size);
/*==============================
debug_screenshot
Sends the currently displayed framebuffer through USB.
DOES NOT PAUSE DRAWING THREAD! Using outside the drawing
thread may lead to a screenshot with visible tearing
==============================*/
extern void debug_screenshot();
/*==============================
debug_assert
Halts the program if the expression fails.
@param The expression to test
==============================*/
#define debug_assert(expr) (expr) ? ((void)0) : _debug_assert(#expr, __FILE__, __LINE__)
/*==============================
debug_64drivebutton
Assigns a function to be executed when the 64drive button is pressed.
@param The function pointer to execute
@param Whether or not to execute the function only on pressing (ignore holding the button down)
==============================*/
extern void debug_64drivebutton(void(*execute)(), char onpress);
/*==============================
debug_pollcommands
Check the USB for incoming commands.
==============================*/
extern void debug_pollcommands();
/*==============================
debug_addcommand
Adds a command for the USB to read.
@param The command name
@param The command description
@param The function pointer to execute
==============================*/
extern void debug_addcommand(char* command, char* description, char*(*execute)());
/*==============================
debug_parsecommand
Stores the next part of the incoming command into the provided buffer.
Make sure the buffer can fit the amount of data from debug_sizecommand!
If you pass NULL, it skips this command.
@param The buffer to store the data in
==============================*/
extern void debug_parsecommand(void* buffer);
/*==============================
debug_sizecommand
Returns the size of the data from this part of the command.
@return The size of the data in bytes, or 0
==============================*/
extern int debug_sizecommand();
/*==============================
debug_printcommands
Prints a list of commands to the developer's command prompt.
==============================*/
extern void debug_printcommands();
// Ignore this, use the macro instead
extern void _debug_assert(const char* expression, const char* file, int line);
// Include usb.h automatically
#include "usb.h"
#else
// Overwrite library functions with useless macros if debug mode is disabled
#define debug_initialize()
#define debug_printf (void)
#define debug_screenshot(a, b, c)
#define debug_assert(a)
#define debug_pollcommands()
#define debug_addcommand(a, b, c)
#define debug_parsecommand(a) NULL
#define debug_sizecommand() 0
#define debug_printcommands()
#define debug_64drivebutton(a, b)
#define usb_initialize() 0
#define usb_getcart() 0
#define usb_write(a, b, c)
#define usb_poll() 0
#define usb_read(a, b)
#define usb_skip(a)
#define usb_rewind(a)
#define usb_purge()
#endif
#endif

1456
libdragon/unf/usb.c Executable file

File diff suppressed because it is too large Load Diff

140
libdragon/unf/usb.h Executable file
View File

@ -0,0 +1,140 @@
#ifndef UNFL_USB_H
#define UNFL_USB_H
/*********************************
DataType macros
*********************************/
// UNCOMMENT THE #DEFINE IF USING LIBDRAGON
#define LIBDRAGON
// Settings
#define USE_OSRAW 0 // Use if you're doing USB operations without the PI Manager (libultra only)
#define DEBUG_ADDRESS_SIZE 8*1024*1024 // Max size of USB I/O. The bigger this value, the more ROM you lose!
#define CHECK_EMULATOR 0 // Stops the USB library from working if it detects an emulator to prevent problems
// Cart definitions
#define CART_NONE 0
#define CART_64DRIVE 1
#define CART_EVERDRIVE 2
#define CART_SC64 3
// Data types defintions
#define DATATYPE_TEXT 0x01
#define DATATYPE_RAWBINARY 0x02
#define DATATYPE_HEADER 0x03
#define DATATYPE_SCREENSHOT 0x04
#define DATATYPE_HEARTBEAT 0x05
#define DATATYPE_RDBPACKET 0x06
/*********************************
Convenience macros
*********************************/
// Use these to conveniently read the header from usb_poll()
#define USBHEADER_GETTYPE(header) (((header) & 0xFF000000) >> 24)
#define USBHEADER_GETSIZE(header) (((header) & 0x00FFFFFF))
/*********************************
USB Functions
*********************************/
/*==============================
usb_initialize
Initializes the USB buffers and pointers
@return 1 if the USB initialization was successful, 0 if not
==============================*/
extern char usb_initialize(void);
/*==============================
usb_getcart
Returns which flashcart is currently connected
@return The CART macro that corresponds to the identified flashcart
==============================*/
extern char usb_getcart(void);
/*==============================
usb_write
Writes data to the USB.
Will not write if there is data to read from USB
@param The DATATYPE that is being sent
@param A buffer with the data to send
@param The size of the data being sent
==============================*/
extern void usb_write(int datatype, const void* data, int size);
/*==============================
usb_poll
Returns the header of data being received via USB
The first byte contains the data type, the next 3 the number of bytes left to read
@return The data header, or 0
==============================*/
extern unsigned long usb_poll(void);
/*==============================
usb_read
Reads bytes from USB into the provided buffer
@param The buffer to put the read data in
@param The number of bytes to read
==============================*/
extern void usb_read(void* buffer, int size);
/*==============================
usb_skip
Skips a USB read by the specified amount of bytes
@param The number of bytes to skip
==============================*/
extern void usb_skip(int nbytes);
/*==============================
usb_rewind
Rewinds a USB read by the specified amount of bytes
@param The number of bytes to rewind
==============================*/
extern void usb_rewind(int nbytes);
/*==============================
usb_purge
Purges the incoming USB data
==============================*/
extern void usb_purge(void);
/*==============================
usb_timedout
Checks if the USB timed out recently
@return 1 if the USB timed out, 0 if not
==============================*/
extern char usb_timedout(void);
/*==============================
usb_sendheartbeat
Sends a heartbeat packet to the PC
This is done once automatically at initialization,
but can be called manually to ensure that the
host side tool is aware of the current USB protocol
version.
==============================*/
extern void usb_sendheartbeat(void);
#endif