commit 564214cb40c25f39df11387b82e0d0cfed10a84c Author: Lorenzo Carletti Date: Wed Dec 14 21:43:29 2022 +0100 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..857cd6f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +*.elf +*.sav diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..eb9ea1f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Lorenzooone + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..41d2794 --- /dev/null +++ b/Makefile @@ -0,0 +1,171 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM) +endif + +include $(DEVKITARM)/gba_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output, if this ends with _mb a multiboot image is generated +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# GRAPHICS is a list of directories containing files to be processed by grit +# INCLUDES is a list of directories containing header files +#--------------------------------------------------------------------------------- +TARGET := $(shell basename $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +GRAPHICS := graphics +INCLUDES := include + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -mthumb -mthumb-interwork + +CFLAGS := -g -Wall -save-temps -O3\ + -mcpu=arm7tdmi -mtune=arm7tdmi\ + -fomit-frame-pointer\ + -ffast-math \ + -fno-tree-loop-distribute-patterns \ + $(ARCH) + +CFLAGS += $(INCLUDE) + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions + +ASFLAGS := $(ARCH) +LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $@).map + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lgba + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(LIBGBA) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ + $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(PNGFILES:.png=.o) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +all : $(BUILD) +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).elf $(TARGET).gba + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).gba : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + + +#--------------------------------------------------------------------------------- +# The bin2o rule should be copied and modified +# for each extension used in the data directories +#--------------------------------------------------------------------------------- + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .bin extension +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with the .raw extension +#--------------------------------------------------------------------------------- +%.raw.o : %.raw +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +#--------------------------------------------------------------------------------- +# This rule creates assembly source files using grit +# grit takes an image file and a .grit describing how the file is to be processed +# add additional rules like this for each image extension +# you use in the graphics folders +#--------------------------------------------------------------------------------- +%.s %.h : %.png %.grit +#--------------------------------------------------------------------------------- + grit $< -fts -o$* + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/dump_reader.py b/dump_reader.py new file mode 100644 index 0000000..65b16b6 --- /dev/null +++ b/dump_reader.py @@ -0,0 +1,211 @@ +import usb.core +import usb.util +import signal +import sys +import traceback +import time +import os + +dev = None +sleep_timer = 0.01 +sram_transfer_val = 2 +rom_transfer_val = 1 +rom_bank_size = 0x40 +rom_banks = [2,4,8,16,32,64,128,256,512,0,0,0,0,0,0,0,0,0,72,80,96] +sram_banks_sizes = [0,8,0x20,0x20,0x20,0x20,2] +sram_banks = [0,1,1,4,16,8,1] +section_size = 0x100 +normal_nybble = 0x10 +check_nybble = 0x40 + +def rom_transfer(data, transfer_size): + print("Starting ROM dump...") + size = rom_bank_size + banks = rom_banks[transfer_size] + + return transfer_bank(data, size, banks) + +def sram_transfer(data, transfer_size): + print("Starting SRAM dump...") + size = sram_banks_sizes[transfer_size] + banks = sram_banks[transfer_size] + + return transfer_bank(data, size, banks) + +def transfer_bank(data, size, banks): + if(banks == 0): + print("Nothing to dump!") + return None + + res = [] + for i in range(banks): + print("Bank " + str(i+ 1) + " out of " + str(banks) + "!") + for j in range(size): + print("Section " + str(j+ 1) + " out of " + str(size) + "!") + res += read_section(data) + return res + +def read_section(data): + buf = [] + checked = False + half = False + + while(not checked): + for i in range(2): + accepted = False + while not accepted: + sleep_func() + sendByte(data[i]) + recv = receiveByte() + high_recv = recv & 0xF0 + if(high_recv == check_nybble or high_recv == normal_nybble): + #print("send: 0x%02x" % data[i]) + #print("recv: 0x%02x" % recv) + if(high_recv == check_nybble): + if(half): + checked = True + half = True + accepted = True + data[i] = recv & 0xF; + #else: + #print("BAD RECV: 0x%02x" % recv) + val = (data[1] | (data[0] << 4)) + if(checked): + if(val == 0): + checked = False + buf = [] + else: + buf += [val] + #print("Data: 0x%02x" % val) + if(half): + # Handle a "nybble" desync + sleep_func() + sendByte(data[0]) + recv = receiveByte() + half = False + + return buf[0:len(buf)-1] # There is an extra transfer in order to ensure the last byte is correct. Discard it + +def transfer_func(): + print("Waiting for the transfer to start...") + + data = [0,0] + buf = read_section(data) # Read the starting information + + if(len(buf) > 2): + print("The transfer was previously interrupted. Please reset the GameBoy!") + return + + transfer_type = buf[0] + transfer_size = buf[1] + if(transfer_type == rom_transfer_val): + res = rom_transfer(data, transfer_size) + elif(transfer_type == sram_transfer_val): + res = sram_transfer(data, transfer_size) + + if res is not None: + if(len(sys.argv) > 1): + target = sys.argv[1] + else: + target = input("Please enter where to save the dump: ") + + newFile = open(target, "wb") + newFile.write(bytearray(res)) + newFile.close() + return + +# Function needed in order to make sure there is enough time for the slave to prepare the next byte. +def sleep_func(): + time.sleep(sleep_timer) + +# Code dependant on this connection method +def sendByte(byte_to_send): + epOut.write(byte_to_send.to_bytes(1, byteorder='big')) + return + +def receiveByte(): + return int.from_bytes(epIn.read(epIn.wMaxPacketSize, 100), byteorder='big') + +# Things for the USB connection part +def exit_gracefully(): + if dev is not None: + usb.util.dispose_resources(dev) + if(os.name != "nt"): + if reattach: + dev.attach_kernel_driver(0) + print('Done.') + +def signal_handler(sig, frame): + print('You pressed Ctrl+C!') + exit_gracefully() + +signal.signal(signal.SIGINT, signal_handler) + +# The execution path +try: + devices = list(usb.core.find(find_all=True,idVendor=0xcafe, idProduct=0x4011)) + for d in devices: + #print('Device: %s' % d.product) + dev = d + + if dev is None: + raise ValueError('Device not found') + + reattach = False + if(os.name != "nt"): + if dev.is_kernel_driver_active(0): + try: + reattach = True + dev.detach_kernel_driver(0) + print("kernel driver detached") + except usb.core.USBError as e: + sys.exit("Could not detach kernel driver: %s" % str(e)) + else: + print("no kernel driver attached") + + dev.reset() + + dev.set_configuration() + + cfg = dev.get_active_configuration() + + #print('Configuration: %s' % cfg) + + intf = cfg[(2,0)] # Or find interface with class 0xff + + #print('Interface: %s' % intf) + + epIn = usb.util.find_descriptor( + intf, + custom_match = \ + lambda e: \ + usb.util.endpoint_direction(e.bEndpointAddress) == \ + usb.util.ENDPOINT_IN) + + assert epIn is not None + + #print('EP In: %s' % epIn) + + epOut = usb.util.find_descriptor( + intf, + # match the first OUT endpoint + custom_match = \ + lambda e: \ + usb.util.endpoint_direction(e.bEndpointAddress) == \ + usb.util.ENDPOINT_OUT) + + assert epOut is not None + + #print('EP Out: %s' % epOut) + + # Control transfer to enable webserial on device + #print("control transfer out...") + dev.ctrl_transfer(bmRequestType = 1, bRequest = 0x22, wIndex = 2, wValue = 0x01) + + transfer_func() + + exit_gracefully() +except: + #traceback.print_exc() + print("Unexpected exception: ", sys.exc_info()[0]) + exit_gracefully() \ No newline at end of file diff --git a/source/gb_dump_receiver.c b/source/gb_dump_receiver.c new file mode 100644 index 0000000..0e5ed81 --- /dev/null +++ b/source/gb_dump_receiver.c @@ -0,0 +1,118 @@ +#include +#include "gb_dump_receiver.h" +#include "sio.h" + +#define NORMAL_BYTE 0x10 +#define CHECK_BYTE 0x40 +#define VCOUNT_TIMEOUT 28 +#define FLAG_CHECK 0x100 +#define OK_SIGNAL 1 +#define BUFFER_SIZE 0x100 +#define DUMP_POSITION EWRAM +#define SRAM_TRANSFER 2 +#define ROM_TRANSFER 1 + +const u8 rom_bank_sizes[] = {64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64}; +const u16 rom_banks[] = {2,4,8,16,32,64,128,256,512,0,0,0,0,0,0,0,0,0,72,80,96}; +const u8 sram_bank_sizes[] = {0,8,0x20,0x20,0x20,0x20,2}; +const u16 sram_banks[] = {0,1,1,4,16,8,1}; + +int read_gb_dump_val(int); +int read_sector(u8*, int, int*); + +int read_dump(int max_size) { + u8 buffer[BUFFER_SIZE]; + int result = 0, size = 0, keep_going = 0, bank_size, banks, total_sections; + + init_sio_normal(SIO_MASTER, SIO_8); + + while (!keep_going) { + size = read_sector(buffer, BUFFER_SIZE, &result); + + if(size > 2) + return GENERIC_DUMP_ERROR; + if(size == 2) + keep_going = 1; + } + + if(buffer[0] == ROM_TRANSFER) { + bank_size = rom_bank_sizes[buffer[1]]; + banks = rom_banks[buffer[1]]; + } + else if(buffer[0] == SRAM_TRANSFER) { + bank_size = sram_bank_sizes[buffer[1]]; + banks = sram_banks[buffer[1]]; + } + else + return GENERIC_DUMP_ERROR; + + total_sections = banks * bank_size; + + if(BUFFER_SIZE * total_sections > max_size) + return SIZE_DUMP_ERROR; + + for (int i = 0; i < total_sections; i++) { + iprintf("\x1b[2J"); + iprintf("Dumping: %d/%d\n", i + 1, total_sections); + size = read_sector(buffer, BUFFER_SIZE, &result); + if(size != BUFFER_SIZE) + return GENERIC_DUMP_ERROR; + for(int j = 0; j < BUFFER_SIZE; j++) + *((u8*)(DUMP_POSITION + (i * BUFFER_SIZE) + j)) = buffer[j]; + } + + return total_sections * BUFFER_SIZE; +} + +int read_gb_dump_val(int data) { + int flag_value = 0; + u8 received[2], envelope[2]; + + for(int i = 0; i < 2; i++) { + received[i] = timed_sio_normal_master((data & (0xF0 >> (i * 4))) >> (4 - (i * 4)), SIO_8, VCOUNT_TIMEOUT); + envelope[i] = received[i] & 0xF0; + received[i] = (received[i] & 0xF) << (4 - (i * 4)); + + if ((envelope[i] != NORMAL_BYTE) && (envelope[i] != CHECK_BYTE)) + i--; + } + + if (envelope[1] != envelope[0]) { + timed_sio_normal_master(received[0], SIO_8, VCOUNT_TIMEOUT); + return -1; + } + + if(envelope[0] == CHECK_BYTE) + flag_value = FLAG_CHECK; + + return flag_value | (received[0] | received[1]); +} + +int read_sector(u8* buffer, int max_size, int* result) { + int index = 0, got_section = 0; + int curr_result; + + while(!got_section) { + curr_result = read_gb_dump_val(*result); + if(curr_result != -1) { + *result = curr_result & 0xFF; + + if (!(curr_result & FLAG_CHECK)) { + if (index >= max_size) + index++; + else + buffer[index++] = *result; + if(index > (max_size + 1)) + index = 0; + } + else if(*result == OK_SIGNAL) + got_section = 1; + else + index = 0; + } + else + index = 0; + } + + return index - 1; +} diff --git a/source/gb_dump_receiver.h b/source/gb_dump_receiver.h new file mode 100644 index 0000000..23745ba --- /dev/null +++ b/source/gb_dump_receiver.h @@ -0,0 +1,10 @@ +#ifndef GB_DUMP_RECEIVER__ +#define GB_DUMP_RECEIVER__ + +#define DUMP_OK 0 +#define GENERIC_DUMP_ERROR -1 +#define SIZE_DUMP_ERROR -2 + +int read_dump(int); + +#endif \ No newline at end of file diff --git a/source/main.c b/source/main.c new file mode 100644 index 0000000..0bc515a --- /dev/null +++ b/source/main.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: MIT +// +// Copyright (c) 2020 Antonio Niño Díaz (AntonioND) + +#include +#include + +#include + +#include "multiboot_handler.h" +#include "gb_dump_receiver.h" +#include "save.h" + +// -------------------------------------------------------------------- + +#define PACKED __attribute__((packed)) +#define ALWAYS_INLINE __attribute__((always_inline)) static inline +#define MAX_DUMP_SIZE 0x20000 +#define REG_WAITCNT *(vu16*)(REG_BASE + 0x204) // Wait state Control +#define SAVE_TYPES 4 +#define SRAM_SAVE_TYPE 0 +#define FLASH_64_SAVE_TYPE 1 +#define FLASH_128_SAVE_TYPE 2 +#define ROM_SAVE_TYPE 3 + +// -------------------------------------------------------------------- + +const char* save_strings[] = {"SRAM 64KiB", "Flash 64 KiB", "Flash 128 KiB", "Inside ROM"}; + +void save_to_memory(int size) { + int chosen_save_type = 0; + u8 copied_properly = 0; + int decided = 0, completed = 0; + u16 keys; + REG_WAITCNT |= 3; + + while(!completed) { + while(!decided) { + iprintf("\x1b[2J"); + iprintf("Save type: %s\n\n", save_strings[chosen_save_type]); + iprintf("LEFT/RIGHT: Change save type\n\n"); + iprintf("START: Save\n\n"); + + scanKeys(); + keys = keysDown(); + + while ((!(keys & KEY_START)) && (!(keys & KEY_LEFT)) && (!(keys & KEY_RIGHT))) { + VBlankIntrWait(); + scanKeys(); + keys = keysDown(); + } + + if(keys & KEY_START) + decided = 1; + else if(keys & KEY_LEFT) + chosen_save_type -= 1; + else if(keys & KEY_RIGHT) + chosen_save_type += 1; + + if(chosen_save_type < 0) + chosen_save_type = SAVE_TYPES - 1; + if(chosen_save_type >= SAVE_TYPES) + chosen_save_type = 0; + } + + iprintf("\x1b[2J"); + + switch (chosen_save_type) { + case SRAM_SAVE_TYPE: + sram_write((u8*)EWRAM, size); + copied_properly = is_sram_correct((u8*)EWRAM, size); + break; + case FLASH_64_SAVE_TYPE: + flash_write((u8*)EWRAM, size, 0); + copied_properly = is_flash_correct((u8*)EWRAM, size, 0); + break; + case FLASH_128_SAVE_TYPE: + flash_write((u8*)EWRAM, size, 1); + copied_properly = is_flash_correct((u8*)EWRAM, size, 1); + break; + case ROM_SAVE_TYPE: + rom_write((u8*)EWRAM, size); + copied_properly = is_rom_correct((u8*)EWRAM, size); + default: + break; + } + + if(copied_properly) { + iprintf("All went well!\n\n"); + completed = 1; + } + else { + iprintf("Not everything went right!\n\n"); + iprintf("START: Try saving again\n\n"); + iprintf("SELECT: Abort\n\n"); + + scanKeys(); + keys = keysDown(); + + while ((!(keys & KEY_START)) && (!(keys & KEY_SELECT))) { + VBlankIntrWait(); + scanKeys(); + keys = keysDown(); + } + + if(keys & KEY_SELECT) { + completed = 1; + iprintf("\x1b[2J"); + } + else + decided = 0; + } + } + + if(chosen_save_type == ROM_SAVE_TYPE) + iprintf("Save location: 0x%X\n\n", get_rom_address()-0x8000000); +} + +int main(void) +{ + int val = 0; + u16 keys; + enum MULTIBOOT_RESULTS result; + + irqInit(); + irqEnable(IRQ_VBLANK); + + consoleDemoInit(); + + iprintf("\x1b[2J"); + + while (1) { + iprintf("START: Send the dumper\n\n"); + iprintf("SELECT: Dump\n\n"); + + scanKeys(); + keys = keysDown(); + + while ((!(keys & KEY_START)) && (!(keys & KEY_SELECT))) { + VBlankIntrWait(); + scanKeys(); + keys = keysDown(); + } + + if(keys & KEY_START) { + result = multiboot_normal((u16*)payload, (u16*)(payload + PAYLOAD_SIZE)); + + iprintf("\x1b[2J"); + + if(result == MB_SUCCESS) + iprintf("Multiboot successful!\n\n"); + else if(result == MB_NO_INIT_SYNC) + iprintf("Couldn't sync.\nTry again!\n\n"); + else + iprintf("There was an error.\nTry again!\n\n"); + } + else { + val = read_dump(MAX_DUMP_SIZE); + + iprintf("\x1b[2J"); + + if(val == GENERIC_DUMP_ERROR) + iprintf("There was an error!\nTry again!\n\n"); + else if(val == SIZE_DUMP_ERROR) + iprintf("Dump size is too great!\n\n"); + else { + save_to_memory(val); + } + } + } + + return 0; +} diff --git a/source/multiboot_handler.c b/source/multiboot_handler.c new file mode 100644 index 0000000..06bd1ba --- /dev/null +++ b/source/multiboot_handler.c @@ -0,0 +1,110 @@ +#include +#include "multiboot_handler.h" +#include "sio.h" + +#define MULTIBOOT_VCOUNTWAIT 2 + +int multiboot_normal_send(int); + +int multiboot_normal_send(int data) { + // Only this part of REG_SIODATA32 is used during setup. + // The rest is handled by SWI $25 + return (timed_sio_normal_master(data, SIO_32, MULTIBOOT_VCOUNTWAIT) >> 0x10); +} + +void wait_fun(int read_value, int wait) { + int frames; + + //REG_IME = 1; + //for (frames = 0; frames < 64; ++frames) { + if(wait) + VBlankIntrWait(); + //} + + iprintf("Read_value: %d!\n", read_value); + + //REG_IME = 0; +} + +enum MULTIBOOT_RESULTS multiboot_normal (u16* data, u16* end) { + int response; + u8 clientMask = 0; + int attempts, sends, halves; + u8 answer, handshake; + const u8 palette = 0x81; + const int paletteCmd = 0x6300 | palette; + MultiBootParam mp; + + init_sio_normal(SIO_MASTER, SIO_32); + + for(attempts = 0; attempts < 32; attempts++) { + for (sends = 0; sends < 16; sends++) { + response = multiboot_normal_send(0x6200); + + if ((response & 0xfff0) == 0x7200) + clientMask |= (response & 0xf); + } + + if (clientMask) + break; + else + wait_fun(response, 1); + } + + if (!clientMask) { + return MB_NO_INIT_SYNC; + } + + wait_fun(response, 0); + + clientMask &= 0xF; + response = multiboot_normal_send(0x6100 | clientMask); + if (response != (0x7200 | clientMask)) + return MB_WRONG_ANSWER; + + for (halves = 0; halves < 0x60; ++halves) { + response = multiboot_normal_send(*data++); + + if (response != ((0x60 - halves) << 8 | clientMask)) + return MB_HEADER_ISSUE; + + wait_fun(response, 0); + } + + response = multiboot_normal_send(0x6200); + if (response != (clientMask)) + return MB_WRONG_ANSWER; + + response = multiboot_normal_send(0x6200 | clientMask); + if (response != (0x7200 | clientMask)) + return MB_WRONG_ANSWER; + + while ((response & 0xFF00) != 0x7300) { + response = multiboot_normal_send(paletteCmd); + + wait_fun(response, 0); + } + + answer = response&0xFF; + handshake = 0x11 + 0xFF + 0xFF + answer; + + response = multiboot_normal_send(0x6400 | handshake); + if ((response & 0xFF00) != 0x7300) + return MB_WRONG_ANSWER; + + wait_fun(response, 0); + + mp.handshake_data = handshake; + mp.client_data[0] = answer; + mp.client_data[1] = 0xFF; + mp.client_data[2] = 0xFF; + mp.palette_data = palette; + mp.client_bit = clientMask; + mp.boot_srcp = data; + mp.boot_endp = end; + + if(MultiBoot(&mp, MODE32_NORMAL)) + return MB_SWI_FAILURE; + + return MB_SUCCESS; +} \ No newline at end of file diff --git a/source/multiboot_handler.h b/source/multiboot_handler.h new file mode 100644 index 0000000..4a044ff --- /dev/null +++ b/source/multiboot_handler.h @@ -0,0 +1,8 @@ +#ifndef MULTIBOOT_HANDLER__ +#define MULTIBOOT_HANDLER__ + +enum MULTIBOOT_RESULTS {MB_SUCCESS, MB_NO_INIT_SYNC, MB_WRONG_ANSWER, MB_HEADER_ISSUE, MB_SWI_FAILURE}; + +enum MULTIBOOT_RESULTS multiboot_normal(u16*, u16*); + +#endif \ No newline at end of file diff --git a/source/save.c b/source/save.c new file mode 100644 index 0000000..c63af2e --- /dev/null +++ b/source/save.c @@ -0,0 +1,103 @@ +#include +#include "save.h" + +#define FLASH_WRITE_CMD *(flash_access+0x5555) = 0xAA;*(flash_access+0x2AAA) = 0x55;*(flash_access+0x5555) = 0xA0; +#define FLASH_BANK_CMD *(flash_access+0x5555) = 0xAA;*(flash_access+0x2AAA) = 0x55;*(flash_access+0x5555) = 0xB0; +#define timeout 0x1000 +#define BANK_SIZE 0x10000 + +IWRAM_CODE void sram_write(u8* data, int size) { + vu8* sram_access = (vu8*)SRAM; + for(int i = 0; i < size; i++) + *(sram_access+i) = data[i]; +} + +IWRAM_CODE int is_sram_correct(u8* data, int size) { + vu8* sram_access = (vu8*)SRAM; + for(int i = 0; i < size; i++) + if (*(sram_access+i) != data[i]) + return 0; + if(size > BANK_SIZE) + return 0; + return 1; +} + +void flash_write(u8* data, int size, int has_banks) { + vu8* flash_access = (vu8*)SRAM; + int base_difference; + if(has_banks) { + FLASH_BANK_CMD + *(flash_access) = 0; + base_difference = 0; + } + for(int i = 0; i < size; i++) { + if((i == BANK_SIZE) && has_banks) { + FLASH_BANK_CMD + *(flash_access) = 1; + base_difference = BANK_SIZE; + } + FLASH_WRITE_CMD + *(flash_access+i-base_difference) = data[i]; + for(int j = 0; (j < timeout) && (*(flash_access+i-base_difference) != data[i]); j++); + } + if(has_banks) { + FLASH_BANK_CMD + *(flash_access) = 0; + base_difference = 0; + } +} + +int is_flash_correct(u8* data, int size, int has_banks) { + vu8* flash_access = (vu8*)SRAM; + int base_difference; + if(has_banks) { + FLASH_BANK_CMD + *(flash_access) = 0; + base_difference = 0; + } + for(int i = 0; i < size; i++) { + if((i == BANK_SIZE) && has_banks) { + FLASH_BANK_CMD + *(flash_access) = 1; + base_difference = BANK_SIZE; + } + if (*(flash_access+i-base_difference) != data[i]) { + if(has_banks) { + FLASH_BANK_CMD + *(flash_access) = 0; + base_difference = 0; + } + return 0; + } + } + if(has_banks) { + FLASH_BANK_CMD + *(flash_access) = 0; + base_difference = 0; + if(size > (2*BANK_SIZE)) + return 0; + } + else if(size > BANK_SIZE) + return 0; + return 1; +} + +void rom_write(u8* data, int size) { + vu8* free_section_ptr = (vu8*)free_section; + for(int i = 0; i < size; i++) + *(free_section_ptr+i) = data[i]; +} + +unsigned int get_rom_address() { + return (unsigned int)free_section; +} + +int is_rom_correct(u8* data, int size) { + vu8* free_section_ptr = (vu8*)free_section; + for(int i = 0; i < size; i++) + if (*(free_section_ptr+i) != data[i]) + return 0; + if(size > (2*BANK_SIZE)) + return 0; + return 1; +} \ No newline at end of file diff --git a/source/save.h b/source/save.h new file mode 100644 index 0000000..ecb591e --- /dev/null +++ b/source/save.h @@ -0,0 +1,17 @@ +#ifndef SAVE__ +#define SAVE__ + +// For SRAM-based carts +IWRAM_CODE int is_sram_correct(u8* data, int size); +IWRAM_CODE void sram_write(u8* data, int size); + +// For flash-ROM based carts +void flash_write(u8* data, int size, int has_banks); +int is_flash_correct(u8* data, int size, int has_banks); + +// For the repro carts with a writable ROM +int is_rom_correct(u8* data, int size); +void rom_write(u8* data, int size); +unsigned int get_rom_address(void); + +#endif \ No newline at end of file diff --git a/source/sio.c b/source/sio.c new file mode 100644 index 0000000..3990332 --- /dev/null +++ b/source/sio.c @@ -0,0 +1,95 @@ +#include +#include "sio.h" + +void sio_normal_inner_slave(void); +void sio_normal_inner_master(void); + +int timed_sio_normal_master(int data, int is_32, int vCountWait) { + u8 curr_vcount, target_vcount; + + if(is_32) + REG_SIODATA32 = data; + else + REG_SIODATA8 = (data & 0xFF); + + + // - Wait at least 36 us between sends (this is a bit more, but it works) + curr_vcount = REG_VCOUNT; + target_vcount = curr_vcount + vCountWait; + if(target_vcount >= 0xE4) + target_vcount -= 0xE4; + while (target_vcount != REG_VCOUNT); + + // - Set Start flag. + REG_SIOCNT |= SIO_START; + // - Wait for IRQ (or for Start bit to become zero). + while (REG_SIOCNT & SIO_START); + + // - Process received data. + if(is_32) + return REG_SIODATA32; + else + return (REG_SIODATA8 & 0xFF); +} + +void sio_normal_inner_slave() { + // - Set Start=0 and SO=0 (SO=LOW indicates that slave is (almost) ready). + REG_SIOCNT &= ~(SIO_START | SIO_SO_HIGH); + // - Set Start=1 and SO=1 (SO=HIGH indicates not ready, applied after transfer). + // (Expl. Old SO=LOW kept output until 1st clock bit received). + // (Expl. New SO=HIGH is automatically output at transfer completion). + REG_SIOCNT |= SIO_START | SIO_SO_HIGH; + // - Set SO to LOW to indicate that master may start now. + REG_SIOCNT &= ~SIO_SO_HIGH; + // - Wait for IRQ (or for Start bit to become zero). (Check timeout here!) + while (REG_SIOCNT & SIO_START); + + //Stop next transfer + REG_SIOCNT |= SIO_SO_HIGH; +} + +void sio_normal_inner_master() { + // - Wait for SI to become LOW (slave ready). (Check timeout here!) + while (REG_SIOCNT & SIO_RDY); + // - Set Start flag. + REG_SIOCNT |= SIO_START; + // - Wait for IRQ (or for Start bit to become zero). + while (REG_SIOCNT & SIO_START); +} + +void init_sio_normal(int is_master, int is_32) { + u16 sio_cnt_val = 0; + + if(is_32) + sio_cnt_val |= SIO_32BIT; + else + sio_cnt_val |= SIO_8BIT; + + if(is_master) + sio_cnt_val |= SIO_CLK_INT; + else + sio_cnt_val |= SIO_SO_HIGH; + + + REG_RCNT = R_NORMAL; + REG_SIOCNT = sio_cnt_val; +} + +int sio_normal(int data, int is_master, int is_32) { + // - Initialize data which is to be sent to master. + if(is_32) + REG_SIODATA32 = data; + else + REG_SIODATA8 = (data & 0xFF); + + if(is_master) + sio_normal_inner_master(); + else + sio_normal_inner_slave(); + + // - Process received data. + if(is_32) + return REG_SIODATA32; + else + return (REG_SIODATA8 & 0xFF); +} \ No newline at end of file diff --git a/source/sio.h b/source/sio.h new file mode 100644 index 0000000..832be37 --- /dev/null +++ b/source/sio.h @@ -0,0 +1,14 @@ +#ifndef SIO__ +#define SIO__ + +#define SIO_32 1 +#define SIO_8 0 + +#define SIO_MASTER 1 +#define SIO_SLAVE 0 + +void init_sio_normal(int is_master, int is_32); +int sio_normal(int data, int is_master, int is_32); +int timed_sio_normal_master(int data, int is_32, int vCountWait); + +#endif \ No newline at end of file