mirror of
https://github.com/Lorenzooone/Pokemon-Gen3-to-Gen-X.git
synced 2026-03-21 17:24:39 -05:00
first commit
This commit is contained in:
commit
564214cb40
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
build/
|
||||
*.elf
|
||||
*.sav
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal 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
171
Makefile
Normal 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
211
dump_reader.py
Normal 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
118
source/gb_dump_receiver.c
Normal 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
10
source/gb_dump_receiver.h
Normal 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
173
source/main.c
Normal 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
110
source/multiboot_handler.c
Normal 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;
|
||||
}
|
||||
8
source/multiboot_handler.h
Normal file
8
source/multiboot_handler.h
Normal 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
103
source/save.c
Normal 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
17
source/save.h
Normal 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
95
source/sio.c
Normal 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
14
source/sio.h
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user