first commit

This commit is contained in:
Lorenzo Carletti 2022-12-14 21:43:29 +01:00
commit 564214cb40
13 changed files with 1054 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
build/
*.elf
*.sav

21
LICENSE Normal file
View File

@ -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.

171
Makefile Normal file
View File

@ -0,0 +1,171 @@
#---------------------------------------------------------------------------------
# Clear the implicit built in rules
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>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
#---------------------------------------------------------------------------------

211
dump_reader.py Normal file
View File

@ -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()

118
source/gb_dump_receiver.c Normal file
View File

@ -0,0 +1,118 @@
#include <gba.h>
#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;
}

10
source/gb_dump_receiver.h Normal file
View File

@ -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

173
source/main.c Normal file
View File

@ -0,0 +1,173 @@
// SPDX-License-Identifier: MIT
//
// Copyright (c) 2020 Antonio Niño Díaz (AntonioND)
#include <stdio.h>
#include <string.h>
#include <gba.h>
#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;
}

110
source/multiboot_handler.c Normal file
View File

@ -0,0 +1,110 @@
#include <gba.h>
#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;
}

View File

@ -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

103
source/save.c Normal file
View File

@ -0,0 +1,103 @@
#include <gba.h>
#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;
}

17
source/save.h Normal file
View File

@ -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

95
source/sio.c Normal file
View File

@ -0,0 +1,95 @@
#include <gba.h>
#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);
}

14
source/sio.h Normal file
View File

@ -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