mid-kid
4 years ago
commit
631bb30689
17 changed files with 1191 additions and 0 deletions
@ -0,0 +1,55 @@ |
|||||
|
name := GBCartRead |
||||
|
|
||||
|
dir_source := source |
||||
|
dir_build := build |
||||
|
|
||||
|
TARGET_ARCH := -mmcu=atmega328p |
||||
|
TARGET_AVRDUDE := -patmega328p -carduino |
||||
|
SERIAL := /dev/ttyACM0 |
||||
|
|
||||
|
CC := avr-gcc |
||||
|
OBJCOPY := avr-objcopy |
||||
|
OBJDUMP := avr-objdump |
||||
|
AVRDUDE := avrdude |
||||
|
|
||||
|
OPTIM := -Os -g -fdata-sections -ffunction-sections -flto -fuse-linker-plugin -fipa-pta #-fgraphite-identity -floop-nest-optimize |
||||
|
CFLAGS := $(OPTIM) -Wall -Wextra -std=c17 -DF_CPU=16000000L |
||||
|
LDFLAGS := $(OPTIM) -Wl,--gc-sections -Wl,--print-gc-sections |
||||
|
|
||||
|
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2)) |
||||
|
objects := $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, $(call rwildcard, $(dir_source)/, *.c)) |
||||
|
|
||||
|
.SECONDEXPANSION: |
||||
|
|
||||
|
.PHONY: all |
||||
|
all: $(name).hex $(name).lst |
||||
|
|
||||
|
.PHONY: clean |
||||
|
clean: |
||||
|
rm -rf $(dir_build) $(name).hex $(name).lst |
||||
|
|
||||
|
.PHONY: upload |
||||
|
upload: $(name).hex $(name).lst |
||||
|
$(AVRDUDE) -v $(TARGET_AVRDUDE) -P$(SERIAL) -Uflash:w:$<:i |
||||
|
|
||||
|
.PHONY: screen |
||||
|
screen: upload |
||||
|
minicom -D $(SERIAL) -b 2000000 |
||||
|
|
||||
|
%.hex: $(dir_build)/%.elf |
||||
|
$(OBJCOPY) -O ihex -R .eeprom $< $@ |
||||
|
|
||||
|
%.lst: $(dir_build)/%.elf |
||||
|
$(OBJDUMP) -h -S $< > $@ |
||||
|
|
||||
|
$(dir_build)/$(name).elf: $(objects) | $$(dir $$@) |
||||
|
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ |
||||
|
|
||||
|
$(dir_build)/%.o: $(dir_source)/%.c | $$(dir $$@) |
||||
|
$(COMPILE.c) -MMD -MF $(@:.o=.d) $(OUTPUT_OPTION) $< |
||||
|
|
||||
|
.PRECIOUS: %/ |
||||
|
%/: |
||||
|
mkdir -p $@ |
||||
|
|
||||
|
-include $(patsubst %.o, %.d, $(objects)) |
@ -0,0 +1,226 @@ |
|||||
|
#include "cart.h" |
||||
|
|
||||
|
#include <stdint.h> |
||||
|
#include <avr/io.h> |
||||
|
#include <util/delay.h> |
||||
|
|
||||
|
#include "utils.h" |
||||
|
#include "pins.h" |
||||
|
#include "spi.h" |
||||
|
#include "rom.h" |
||||
|
|
||||
|
|
||||
|
// Most of the pulses described here are arbitrarily chosen,
|
||||
|
// to satisfy my idiotic UX requirements with the leds.
|
||||
|
// Here's a pretty cool bus analysis:
|
||||
|
// https://dhole.github.io/post/gameboy_cartridge_emu_1/
|
||||
|
|
||||
|
static void cart_address(const uint16_t address) |
||||
|
{ |
||||
|
spi_transfer(address >> 8); |
||||
|
spi_transfer(address & 0xFF); |
||||
|
writepin(PIN_STORE, HIGH); |
||||
|
__asm__ volatile ("nop"); |
||||
|
writepin(PIN_STORE, LOW); |
||||
|
} |
||||
|
|
||||
|
uint8_t cart_read(const uint16_t address) |
||||
|
{ |
||||
|
cart_address(address); |
||||
|
__asm__ volatile ("nop"); |
||||
|
return PINS_DAT; |
||||
|
} |
||||
|
|
||||
|
void cart_write(const uint16_t address, const uint8_t byte) |
||||
|
{ |
||||
|
cart_address(address); |
||||
|
PORTB = (PORTB & ~PINS_DATB) | (byte >> 6); |
||||
|
PORTD = (PORTD & ~PINS_DATD) | (byte << 2); |
||||
|
writepin(PIN_WRITE, LOW); |
||||
|
__asm__ volatile ("nop"); |
||||
|
writepin(PIN_WRITE, HIGH); |
||||
|
} |
||||
|
|
||||
|
void cart_set_read(void) |
||||
|
{ |
||||
|
// Set the data pins as pull-up inputs
|
||||
|
DDRB &= ~PINS_DATB; |
||||
|
DDRD &= ~PINS_DATD; |
||||
|
PORTB |= PINS_DATB; |
||||
|
PORTD |= PINS_DATD; |
||||
|
|
||||
|
writepin(PIN_READ, LOW); |
||||
|
writepin(PIN_WRITE, HIGH); |
||||
|
} |
||||
|
|
||||
|
void cart_set_write(void) |
||||
|
{ |
||||
|
// Set the data pins as low outputs
|
||||
|
DDRB |= PINS_DATB; |
||||
|
DDRD |= PINS_DATD; |
||||
|
PORTB &= ~PINS_DATB; |
||||
|
PORTD &= ~PINS_DATD; |
||||
|
|
||||
|
writepin(PIN_READ, HIGH); |
||||
|
writepin(PIN_WRITE, HIGH); |
||||
|
} |
||||
|
|
||||
|
void cart_set_standby(void) |
||||
|
{ |
||||
|
// Set the data pins as pull-up inputs
|
||||
|
DDRB &= ~PINS_DATB; |
||||
|
DDRD &= ~PINS_DATD; |
||||
|
PORTB |= PINS_DATB; |
||||
|
PORTD |= PINS_DATD; |
||||
|
|
||||
|
writepin(PIN_READ, HIGH); |
||||
|
writepin(PIN_WRITE, HIGH); |
||||
|
} |
||||
|
|
||||
|
void cart_select_rom(void) |
||||
|
{ |
||||
|
writepin(PIN_CS, HIGH); |
||||
|
} |
||||
|
|
||||
|
void cart_select_ram(void) |
||||
|
{ |
||||
|
writepin(PIN_CS, LOW); |
||||
|
} |
||||
|
|
||||
|
void cart_init(void) |
||||
|
{ |
||||
|
// Set the appropriate pins as output
|
||||
|
DDRB |= pin(PIN_STORE); |
||||
|
DDRC |= pin(PIN_READ) | pin(PIN_WRITE) | pin(PIN_CS); |
||||
|
|
||||
|
// Initialize the cart as standby
|
||||
|
cart_set_standby(); |
||||
|
cart_select_rom(); |
||||
|
|
||||
|
// Initialize the shift register
|
||||
|
writepin(PIN_STORE, LOW); |
||||
|
spi_init(SPI_DIV8); |
||||
|
cart_address(0); |
||||
|
} |
||||
|
|
||||
|
void cart_off(void) |
||||
|
{ |
||||
|
cart_address(0); |
||||
|
spi_off(); |
||||
|
PORTC &= ~pin(PIN_READ) & ~pin(PIN_CS) & ~pin(PIN_WRITE); |
||||
|
writepin(PIN_STORE, LOW); |
||||
|
} |
||||
|
|
||||
|
int cart_boot(struct cart_info *info) |
||||
|
{ |
||||
|
cart_set_read(); |
||||
|
|
||||
|
// "simulate" the GB power-up process, by reading the logo once, before comparing.
|
||||
|
// This might be compatible with carts that hijack the logo on bootup.
|
||||
|
// There is no point in simulating this more accurately (i.e. timing)
|
||||
|
// if we don't even simulate the RD/WR pulses properly.
|
||||
|
// http://gbdev.gg8.se/wiki/articles/Gameboy_Bootstrap_ROM
|
||||
|
|
||||
|
// Read the logo once (to display on the screen)
|
||||
|
for (unsigned i = rom_offset(logo); |
||||
|
i < rom_offset_end(logo); i++) cart_read(i); |
||||
|
|
||||
|
// Read the logo again (to verify it)
|
||||
|
for (unsigned i = rom_offset(logo); i < rom_offset_end(logo); i++) { |
||||
|
if (rom_logo[i - rom_offset(logo)] != cart_read(i)) { |
||||
|
cart_set_standby(); |
||||
|
return 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Read and verify the checksum (and save some info from the header)
|
||||
|
uint8_t checksum = rom_offset(header_checksum) - rom_offset(title); |
||||
|
for (unsigned i = rom_offset(title); |
||||
|
i < rom_offset_end(header_checksum); i++) { |
||||
|
uint8_t c = cart_read(i); |
||||
|
checksum += c; |
||||
|
|
||||
|
if (i == rom_offset(type)) info->type = c; |
||||
|
if (i == rom_offset(rom_size)) info->rom_size = c; |
||||
|
if (i == rom_offset(ram_size)) info->ram_size = c; |
||||
|
} |
||||
|
|
||||
|
cart_set_standby(); |
||||
|
|
||||
|
return checksum; |
||||
|
} |
||||
|
|
||||
|
// https://ia801906.us.archive.org/19/items/GameBoyProgManVer1.1/GameBoyProgManVer1.1.pdf (page 215)
|
||||
|
|
||||
|
// NOTE: These functions only support MBC1, MBC2, MBC3 and MBC5.
|
||||
|
// Thankfully, that covers most of the licensed carts.
|
||||
|
|
||||
|
void cart_rom_bank(__attribute__((unused)) struct cart_info *info, const unsigned bank) |
||||
|
{ |
||||
|
// MBC1: 0x2000-0x3FFF (bit 0-4) 0x4000-0x5FFF (bit 5-6, if 0x6000-0x7FFF is set to 0)
|
||||
|
// MBC2: 0x2100-0x21FF (bit 0-3)
|
||||
|
// MBC3: 0x2000-0x3FFF (bit 0-6)
|
||||
|
// MBC5: 0x2000-0x2FFF (bit 0-7) 0x3000-0x3FFF (bit 8)
|
||||
|
cart_set_write(); |
||||
|
|
||||
|
if (info->type >= ROM_TYPE_MBC1 && |
||||
|
info->type <= ROM_TYPE_MBC1_RAM_BATTERY) { |
||||
|
cart_write(0x2000, bank & 0x1F); |
||||
|
cart_write(0x6000, 0); |
||||
|
cart_write(0x4000, bank >> 5); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
cart_write(0x2100, bank); |
||||
|
if (info->type >= ROM_TYPE_MBC5 && |
||||
|
info->type <= ROM_TYPE_MBC5_RUMBLE_RAM_BATTERY) { |
||||
|
cart_write(0x3000, bank >> 8); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void cart_ram_enable(__attribute__((unused)) struct cart_info *info) |
||||
|
{ |
||||
|
// MBC1: 0x0000-0x1FFF = 0x0A
|
||||
|
// MBC2: 0x0000-0x0FFF = 0x0A
|
||||
|
// MBC3: 0x0000-0x1FFF = 0x0A
|
||||
|
// MBC5: 0x0000-0x1FFF = 0x0A
|
||||
|
cart_set_write(); |
||||
|
cart_write(0x0000, 0x0A); |
||||
|
cart_select_ram(); |
||||
|
} |
||||
|
|
||||
|
void cart_ram_disable(__attribute__((unused)) struct cart_info *info) |
||||
|
{ |
||||
|
// MBC1: 0x0000-0x1FFF = 0x00
|
||||
|
// MBC2: 0x0000-0x0FFF = 0x00
|
||||
|
// MBC3: 0x0000-0x1FFF = 0x00
|
||||
|
// MBC5: 0x0000-0x1FFF = 0x00
|
||||
|
cart_set_write(); |
||||
|
cart_select_rom(); |
||||
|
cart_write(0x0000, 0x00); |
||||
|
} |
||||
|
|
||||
|
void cart_ram_bank(__attribute__((unused)) struct cart_info *info, const unsigned bank) |
||||
|
{ |
||||
|
// MBC1: 0x4000-0x5FFF (bit 0-1, if 0x6000-0x7FFF is set to 1)
|
||||
|
// MBC2: None, it has only one bank. (Writing to 0x4000-0x5FFF should have no effect)
|
||||
|
// MBC3: 0x4000-0x5FFF (bit 0-1) NOTE: Writing values 0x08-0x0C opens up possible problems with the RTC!
|
||||
|
// MBC5: 0x4000-0x5FFF (bit 0-3)
|
||||
|
cart_set_write(); |
||||
|
|
||||
|
if (info->type >= ROM_TYPE_MBC1 && |
||||
|
info->type <= ROM_TYPE_MBC1_RAM_BATTERY) { |
||||
|
cart_write(0x6000, 1); |
||||
|
} |
||||
|
|
||||
|
// Since writing 0x08-0x0C here opens up the timer,
|
||||
|
// it opens up the possibility for corruption.
|
||||
|
// As such, it's a good idea to check for it...
|
||||
|
if (info->type >= ROM_TYPE_MBC3_TIMER_BATTERY && |
||||
|
info->type <= ROM_TYPE_MBC3_RAM_BATTERY) { |
||||
|
cart_write(0x4000, bank & 0x07); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
cart_write(0x4000, bank); |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <stdint.h> |
||||
|
|
||||
|
struct cart_info { |
||||
|
int type; |
||||
|
int rom_size; |
||||
|
int ram_size; |
||||
|
}; |
||||
|
|
||||
|
uint8_t cart_read(const uint16_t address); |
||||
|
void cart_write(const uint16_t address, const uint8_t byte); |
||||
|
void cart_set_read(void); |
||||
|
void cart_set_write(void); |
||||
|
void cart_set_standby(void); |
||||
|
void cart_select_rom(void); |
||||
|
void cart_select_ram(void); |
||||
|
void cart_init(void); |
||||
|
void cart_off(void); |
||||
|
int cart_boot(struct cart_info *info); |
||||
|
void cart_rom_bank(struct cart_info *info, unsigned bank); |
||||
|
void cart_ram_enable(struct cart_info *info); |
||||
|
void cart_ram_disable(struct cart_info *info); |
||||
|
void cart_ram_bank(struct cart_info *info, unsigned bank); |
@ -0,0 +1,263 @@ |
|||||
|
// AVR-libc documentation:
|
||||
|
// http://www.nongnu.org/avr-libc/user-manual/modules.html
|
||||
|
|
||||
|
// AVR assembly instructions:
|
||||
|
// https://www.microchip.com/webdoc/avrassembler/avrassembler.wb_instruction_list.html
|
||||
|
|
||||
|
// ATmega datasheet:
|
||||
|
// http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega328_P%20AVR%20MCU%20with%20picoPower%20Technology%20Data%20Sheet%2040001984A.pdf
|
||||
|
|
||||
|
/*
|
||||
|
* FIXME: The current hardware design is questionable. |
||||
|
* |
||||
|
* The leds. Why does the power (MOSFET) led even have a mosfet to begin |
||||
|
* with? It doesn't seem to be doing much. |
||||
|
* Furthermore, why aren't the RD and WR leds inverted? Both of these pins |
||||
|
* on the cart are active low, not high. |
||||
|
*/ |
||||
|
|
||||
|
#include <stdint.h> |
||||
|
#include <avr/io.h> |
||||
|
#include <avr/interrupt.h> |
||||
|
#include <util/delay.h> |
||||
|
|
||||
|
#include "utils.h" |
||||
|
#include "pins.h" |
||||
|
#include "serial.h" |
||||
|
#include "cart.h" |
||||
|
#include "proto.h" |
||||
|
|
||||
|
void proto_send_u32(const uint32_t val) |
||||
|
{ |
||||
|
char *cval = (char *)&val; |
||||
|
serial_putchar(cval[0]); |
||||
|
serial_putchar(cval[1]); |
||||
|
serial_putchar(cval[2]); |
||||
|
serial_putchar(cval[3]); |
||||
|
} |
||||
|
|
||||
|
uint32_t proto_read_u32(void) |
||||
|
{ |
||||
|
uint32_t val; |
||||
|
char *cval = (char *)&val; |
||||
|
cval[0] = serial_getchar(); |
||||
|
cval[1] = serial_getchar(); |
||||
|
cval[2] = serial_getchar(); |
||||
|
cval[3] = serial_getchar(); |
||||
|
return val; |
||||
|
} |
||||
|
|
||||
|
struct proto_client_packet proto_read_packet(void) |
||||
|
{ |
||||
|
struct proto_client_packet packet; |
||||
|
packet.command = serial_getchar(); |
||||
|
packet.address = proto_read_u32(); |
||||
|
packet.size = proto_read_u32(); |
||||
|
return packet; |
||||
|
} |
||||
|
|
||||
|
void client_read_raw(uint32_t address, uint32_t size) |
||||
|
{ |
||||
|
if (address > (uint32_t)0xFFFF || address + size > (uint32_t)0xFFFF) { |
||||
|
serial_putchar(PROTO_READER_FAIL); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
serial_putchar(PROTO_READER_OK); |
||||
|
cart_set_read(); |
||||
|
if (address & 0x8000) cart_select_ram(); |
||||
|
for (unsigned i = address; i < address + size; i++) { |
||||
|
serial_putchar_inline(cart_read(i)); |
||||
|
} |
||||
|
cart_select_rom(); |
||||
|
} |
||||
|
|
||||
|
void client_write_raw(uint32_t address, uint32_t size) |
||||
|
{ |
||||
|
if (address > (uint32_t)0xFFFF || address + size > (uint32_t)0xFFFF) { |
||||
|
serial_putchar(PROTO_READER_FAIL); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
serial_putchar(PROTO_READER_OK); |
||||
|
cart_set_write(); |
||||
|
if (address & 0x8000) cart_select_ram(); |
||||
|
for (unsigned i = address; i < address + size; i++) { |
||||
|
cart_write(i, serial_getchar_inline()); |
||||
|
} |
||||
|
cart_select_rom(); |
||||
|
} |
||||
|
|
||||
|
void client_read_rom(struct cart_info *cart, uint32_t address, uint32_t size) |
||||
|
{ |
||||
|
// 0x1FF = max bank (MBC5, 9 bits)
|
||||
|
if (address > (uint32_t)0x4000 * 0x200 || |
||||
|
address + size > (uint32_t)0x4000 * 0x200) { |
||||
|
serial_putchar(PROTO_READER_FAIL); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
unsigned bank = address / (uint32_t)0x4000; |
||||
|
unsigned bank_end = (address + size - 1) / (uint32_t)0x4000; |
||||
|
unsigned offset = 0; // Offset for the first bank we read, which can be bank 0.
|
||||
|
if (bank) { |
||||
|
cart_rom_bank(cart, bank); |
||||
|
|
||||
|
// Any bank other than 0 starts at 0x4000.
|
||||
|
offset = 0x4000; |
||||
|
} |
||||
|
|
||||
|
// If we're not reading cross-bank, just do one read.
|
||||
|
if (bank == bank_end) return client_read_raw(offset + address % 0x4000, size); |
||||
|
|
||||
|
serial_putchar(PROTO_READER_OK); |
||||
|
cart_set_read(); |
||||
|
|
||||
|
// Read the current bank until the end
|
||||
|
for (unsigned i = offset + address % 0x4000; i < offset + 0x4000; i++) { |
||||
|
serial_putchar_inline(cart_read(i)); |
||||
|
} |
||||
|
|
||||
|
// Read all the entire banks
|
||||
|
for (unsigned i = bank + 1; i < bank_end; i++) { |
||||
|
cart_rom_bank(cart, i); |
||||
|
cart_set_read(); |
||||
|
for (unsigned i = 0x4000; i < (unsigned)0x4000 + 0x4000; i++) { |
||||
|
serial_putchar_inline(cart_read(i)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Read the final bank until the end
|
||||
|
cart_rom_bank(cart, bank_end); |
||||
|
cart_set_read(); |
||||
|
for (unsigned i = 0x4000; i <= 0x4000 + (size - 1) % 0x4000; i++) { |
||||
|
serial_putchar_inline(cart_read(i)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void client_read_ram(struct cart_info *cart, uint32_t address, uint32_t size) |
||||
|
{ |
||||
|
// 0xF = max bank (MBC5, 4 bits)
|
||||
|
if (address > (uint32_t)0x2000 * 0x10 || |
||||
|
address + size > (uint32_t)0x2000 * 0x10) { |
||||
|
serial_putchar(PROTO_READER_FAIL); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
unsigned bank = address / (uint32_t)0x2000; |
||||
|
unsigned bank_end = (address + size - 1) / (uint32_t)0x2000; |
||||
|
cart_ram_enable(cart); |
||||
|
cart_ram_bank(cart, bank); |
||||
|
|
||||
|
// If we're not reading cross-bank, just do one read.
|
||||
|
if (bank == bank_end) return client_read_raw(0xA000 + address % 0x2000, size); |
||||
|
|
||||
|
serial_putchar(PROTO_READER_OK); |
||||
|
cart_set_read(); |
||||
|
|
||||
|
// Read the current bank until the end
|
||||
|
for (unsigned i = 0xA000 + address % 0x2000; i < 0xA000 + 0x2000; i++) { |
||||
|
serial_putchar_inline(cart_read(i)); |
||||
|
} |
||||
|
|
||||
|
// Read all the entire banks
|
||||
|
for (unsigned i = bank + 1; i < bank_end; i++) { |
||||
|
cart_ram_bank(cart, i); |
||||
|
cart_set_read(); |
||||
|
for (unsigned i = 0xA000; i < 0xA000 + 0x2000; i++) { |
||||
|
serial_putchar_inline(cart_read(i)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Read the final bank until the end
|
||||
|
cart_ram_bank(cart, bank_end); |
||||
|
cart_set_read(); |
||||
|
for (unsigned i = 0xA000; i <= 0xA000 + (size - 1) % 0x2000; i++) { |
||||
|
serial_putchar_inline(cart_read(i)); |
||||
|
} |
||||
|
|
||||
|
cart_ram_disable(cart); |
||||
|
} |
||||
|
|
||||
|
int main(void) |
||||
|
{ |
||||
|
struct cart_info cart; |
||||
|
|
||||
|
// Configure reset button interrupt
|
||||
|
//PCICR = 0;
|
||||
|
//PCMSK1 = _BV(PCINT9);
|
||||
|
|
||||
|
// Initialize the program
|
||||
|
serial_init(2000000); |
||||
|
pinmode(PIN_MOSFET, OUTPUT); |
||||
|
writepin(PIN_MOSFET, LOW); |
||||
|
cart_init(); |
||||
|
sei(); |
||||
|
|
||||
|
int checksum = cart_boot(&cart); |
||||
|
if (checksum) { |
||||
|
// Notify the user that the cart couldn't be read properly.
|
||||
|
writepin(PIN_READ, LOW); |
||||
|
writepin(PIN_WRITE, LOW); |
||||
|
} |
||||
|
|
||||
|
sbi(PCICR, PCIE1); |
||||
|
while (serial_getchar() != PROTO_CLIENT_INIT); |
||||
|
cbi(PCICR, PCIE1); |
||||
|
serial_putchar(checksum ? PROTO_READER_FAIL : PROTO_READER_OK); |
||||
|
|
||||
|
// Interpret commands
|
||||
|
for (;;) { |
||||
|
serial_drain(); |
||||
|
cart_set_standby(); |
||||
|
sbi(PCICR, PCIE1); |
||||
|
struct proto_client_packet packet = proto_read_packet(); |
||||
|
cbi(PCICR, PCIE1); |
||||
|
|
||||
|
switch (packet.command) { |
||||
|
case PROTO_CLIENT_READ_RAW: |
||||
|
client_read_raw(packet.address, packet.size); |
||||
|
break; |
||||
|
|
||||
|
case PROTO_CLIENT_READ_ROM: |
||||
|
client_read_rom(&cart, packet.address, packet.size); |
||||
|
break; |
||||
|
|
||||
|
case PROTO_CLIENT_READ_RAM: |
||||
|
client_read_ram(&cart, packet.address, packet.size); |
||||
|
break; |
||||
|
|
||||
|
case PROTO_CLIENT_WRITE_RAW: |
||||
|
client_write_raw(packet.address, packet.size); |
||||
|
break; |
||||
|
|
||||
|
case PROTO_CLIENT_WRITE_ROM: |
||||
|
case PROTO_CLIENT_WRITE_RAM: |
||||
|
default: |
||||
|
serial_putchar(PROTO_READER_FAIL); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void exit(__attribute__((unused)) int status) |
||||
|
{ |
||||
|
serial_drain(); |
||||
|
//sbi(PCICR, PCIE1);
|
||||
|
cart_off(); |
||||
|
for (;;); |
||||
|
} |
||||
|
|
||||
|
//ISR(PCINT1_vect) {
|
||||
|
//// Debounce it
|
||||
|
//int pressed = readpin(PIN_SWITCH);
|
||||
|
//int debounce = 0x2DAB;
|
||||
|
//while (debounce--) {
|
||||
|
//if (pressed != readpin(PIN_SWITCH)) return;
|
||||
|
//}
|
||||
|
|
||||
|
//// Check if it was released
|
||||
|
//if (pressed) return;
|
||||
|
|
||||
|
//_delay_ms(10);
|
||||
|
//__asm__ volatile ("jmp 0");
|
||||
|
//}
|
@ -0,0 +1,58 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
// Pin mappings as on the arduino board
|
||||
|
#define PIN_D0 D, 0 |
||||
|
#define PIN_D1 D, 1 |
||||
|
#define PIN_D2 D, 2 |
||||
|
#define PIN_D3 D, 3 |
||||
|
#define PIN_D4 D, 4 |
||||
|
#define PIN_D5 D, 5 |
||||
|
#define PIN_D6 D, 6 |
||||
|
#define PIN_D7 D, 7 |
||||
|
#define PIN_D8 B, 0 |
||||
|
#define PIN_D9 B, 1 |
||||
|
#define PIN_D10 B, 2 |
||||
|
#define PIN_D11 B, 3 |
||||
|
#define PIN_D12 B, 4 |
||||
|
#define PIN_D13 B, 5 |
||||
|
|
||||
|
#define PIN_A0 C, 0 |
||||
|
#define PIN_A1 C, 1 |
||||
|
#define PIN_A2 C, 2 |
||||
|
#define PIN_A3 C, 3 |
||||
|
#define PIN_A4 C, 4 |
||||
|
#define PIN_A5 C, 5 |
||||
|
|
||||
|
// Alternative names for certain pins
|
||||
|
#define PIN_SPI_SS PIN_D10 |
||||
|
#define PIN_SPI_MOSI PIN_D11 |
||||
|
#define PIN_SPI_MISO PIN_D12 |
||||
|
#define PIN_SPI_SCK PIN_D13 |
||||
|
#define PIN_WIRE_SDA PIN_A4 |
||||
|
#define PIN_WIRE_SCL PIN_A5 |
||||
|
#define LED_BUILTIN PIN_D13 |
||||
|
|
||||
|
// Button
|
||||
|
#define PIN_MOSFET PIN_A0 |
||||
|
#define PIN_SWITCH PIN_A1 |
||||
|
|
||||
|
// Cart
|
||||
|
#define PIN_WRITE PIN_A3 |
||||
|
#define PIN_CS PIN_A4 |
||||
|
#define PIN_READ PIN_A5 |
||||
|
|
||||
|
// Shift registers
|
||||
|
#define PIN_STORE PIN_D10 |
||||
|
|
||||
|
// Data pins
|
||||
|
#define PIN_DAT0 PIN_D2 |
||||
|
#define PIN_DAT1 PIN_D3 |
||||
|
#define PIN_DAT2 PIN_D4 |
||||
|
#define PIN_DAT3 PIN_D5 |
||||
|
#define PIN_DAT4 PIN_D6 |
||||
|
#define PIN_DAT5 PIN_D7 |
||||
|
#define PIN_DAT6 PIN_D8 |
||||
|
#define PIN_DAT7 PIN_D9 |
||||
|
#define PINS_DATB (pin(PIN_DAT7) | pin(PIN_DAT6)) |
||||
|
#define PINS_DATD (pin(PIN_DAT5) | pin(PIN_DAT4) | pin(PIN_DAT3) | pin(PIN_DAT2) | pin(PIN_DAT1) | pin(PIN_DAT0)) |
||||
|
#define PINS_DAT (PINB << 6 | PIND >> 2) |
@ -0,0 +1,55 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
enum proto_reader { |
||||
|
// Sent when the operation was successful
|
||||
|
PROTO_READER_OK, |
||||
|
|
||||
|
// Sent after a failed operation
|
||||
|
PROTO_READER_FAIL |
||||
|
}; |
||||
|
|
||||
|
enum proto_client { |
||||
|
// Packet:
|
||||
|
// u8 - command
|
||||
|
// Response:
|
||||
|
// u8 - status
|
||||
|
PROTO_CLIENT_INIT, |
||||
|
|
||||
|
// Packet:
|
||||
|
// u8 - command
|
||||
|
// u32 - start address
|
||||
|
// u32 - size
|
||||
|
// Response:
|
||||
|
// u8 - status
|
||||
|
// u8[] - data (if status == PROTO_READER_OK)
|
||||
|
|
||||
|
// Read a raw address with whatever current state
|
||||
|
// (useful for RTC and other cart addons)
|
||||
|
PROTO_CLIENT_READ_RAW, |
||||
|
// Read an absolute ROM address, switching banks as needed
|
||||
|
PROTO_CLIENT_READ_ROM, |
||||
|
// Read an absolute SRAM address, switching banks as needed
|
||||
|
PROTO_CLIENT_READ_RAM, |
||||
|
|
||||
|
// Packet:
|
||||
|
// u8 - command
|
||||
|
// u32 - start address
|
||||
|
// u32 - size
|
||||
|
// u8[] - data
|
||||
|
// Response:
|
||||
|
// u8 - status
|
||||
|
|
||||
|
// Write a raw address with whatever current state
|
||||
|
// (useful for RTC and other cart addons)
|
||||
|
PROTO_CLIENT_WRITE_RAW, |
||||
|
// Write an absolute ROM address, switching banks as needed
|
||||
|
PROTO_CLIENT_WRITE_ROM, |
||||
|
// Write an absolute SRAM address, switching banks as needed
|
||||
|
PROTO_CLIENT_WRITE_RAM |
||||
|
}; |
||||
|
|
||||
|
struct proto_client_packet { |
||||
|
uint8_t command; |
||||
|
uint32_t address; |
||||
|
uint32_t size; |
||||
|
} __attribute__((packed)); |
@ -0,0 +1,9 @@ |
|||||
|
#include "rom.h" |
||||
|
|
||||
|
#include <stdint.h> |
||||
|
|
||||
|
uint8_t rom_logo[] = { |
||||
|
0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B, 0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D, |
||||
|
0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E, 0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99, |
||||
|
0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC, 0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E |
||||
|
}; |
@ -0,0 +1,107 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <stddef.h> |
||||
|
#include <stdint.h> |
||||
|
|
||||
|
#define rom_offset(member) (0x100 + offsetof(struct rom_header, member)) |
||||
|
#define rom_offset_end(member) (0x100 + offsetof(struct rom_header, member) + sizeof(((struct rom_header *)0)->member)) |
||||
|
|
||||
|
enum rom_cgb { |
||||
|
ROM_CGB_SUPPORTED = 0x80, |
||||
|
ROM_CGB_ONLY = 0xC0 |
||||
|
}; |
||||
|
|
||||
|
enum rom_sgb { |
||||
|
ROM_SGB_NO = 0x00, |
||||
|
ROM_SGB_SUPPORTED = 0x03 |
||||
|
}; |
||||
|
|
||||
|
enum rom_type { |
||||
|
ROM_TYPE_ROM_ONLY = 0x00, |
||||
|
|
||||
|
ROM_TYPE_MBC1 = 0x01, |
||||
|
ROM_TYPE_MBC1_RAM = 0x2, |
||||
|
ROM_TYPE_MBC1_RAM_BATTERY = 0x03, |
||||
|
|
||||
|
// UNUSED = 0x04
|
||||
|
|
||||
|
ROM_TYPE_MBC2 = 0x05, |
||||
|
ROM_TYPE_MBC2_BATTERY = 0x06, |
||||
|
|
||||
|
// UNUSED = 0x07
|
||||
|
|
||||
|
ROM_TYPE_ROM_RAM = 0x08, |
||||
|
ROM_TYPE_ROM_RAM_BATTERY = 0x09, |
||||
|
|
||||
|
// UNUSED = 0x0A
|
||||
|
|
||||
|
ROM_TYPE_MMM01 = 0x0B, |
||||
|
ROM_TYPE_MMM01_RAM = 0x0C, |
||||
|
ROM_TYPE_MMM01_RAM_BATTERY = 0x0D, |
||||
|
|
||||
|
// UNUSED = 0x0E
|
||||
|
|
||||
|
ROM_TYPE_MBC3_TIMER_BATTERY = 0x0F, |
||||
|
ROM_TYPE_MBC3_TIMER_RAM_BATTERY = 0x10, |
||||
|
ROM_TYPE_MBC3 = 0x11, |
||||
|
ROM_TYPE_MBC3_RAM = 0x12, |
||||
|
ROM_TYPE_MBC3_RAM_BATTERY = 0x13, |
||||
|
|
||||
|
// UNUSED = 0x14
|
||||
|
|
||||
|
// Doesn't exist, but included for "completeness".
|
||||
|
ROM_TYPE_MBC4 = 0x15, |
||||
|
ROM_TYPE_MBC4_RAM = 0x16, |
||||
|
ROM_TYPE_MBC4_RAM_BATTERY = 0x17, |
||||
|
|
||||
|
// UNUSED = 0x18
|
||||
|
|
||||
|
ROM_TYPE_MBC5 = 0x19, |
||||
|
ROM_TYPE_MBC5_RAM = 0x1A, |
||||
|
ROM_TYPE_MBC5_RAM_BATTERY = 0x1B, |
||||
|
ROM_TYPE_MBC5_RUMBLE = 0x1C, |
||||
|
ROM_TYPE_MBC5_RUMBLE_RAM = 0x1D, |
||||
|
ROM_TYPE_MBC5_RUMBLE_RAM_BATTERY = 0x1E, |
||||
|
|
||||
|
// UNUSED = 0x1F
|
||||
|
|
||||
|
ROM_TYPE_MBC6_RAM_BATTERY = 0x20, |
||||
|
|
||||
|
// UNUSED = 0x21
|
||||
|
|
||||
|
ROM_TYPE_MBC7_ACCEL_RAM_BATTERY = 0x22, |
||||
|
|
||||
|
// UNUSED ...
|
||||
|
|
||||
|
ROM_TYPE_POCKET_CAMERA = 0xFC, |
||||
|
ROM_TYPE_BANDAI_TAMA5 = 0xFD, |
||||
|
ROM_TYPE_HUC3 = 0xFE, |
||||
|
ROM_TYPE_HUC1_RAM_BATTERY = 0xFF |
||||
|
}; |
||||
|
|
||||
|
enum rom_region { |
||||
|
ROM_REGION_JAPANESE = 0x00, |
||||
|
ROM_REGION_INTERNATIONAL = 0x01 |
||||
|
}; |
||||
|
|
||||
|
// http://gbdev.gg8.se/files/docs/mirrors/pandocs.html#thecartridgeheader
|
||||
|
// https://raw.githubusercontent.com/AntonioND/giibiiadvance/master/docs/TCAGBD.pdf (page 51)
|
||||
|
struct rom_header { |
||||
|
uint8_t entry[4]; |
||||
|
uint8_t logo[0x30]; |
||||
|
uint8_t title[0xB]; |
||||
|
uint8_t manufacturer[4]; |
||||
|
uint8_t cgb_flag; |
||||
|
uint8_t licensee[2]; |
||||
|
uint8_t sgb_flag; |
||||
|
uint8_t type; |
||||
|
uint8_t rom_size; // 0x8000 << x
|
||||
|
uint8_t ram_size; // MBC2 uses 0, but has a size of 512 * 4 bytes
|
||||
|
uint8_t region; |
||||
|
uint8_t old_licensee; // If 0x33, use the new licensee
|
||||
|
uint8_t version; |
||||
|
uint8_t header_checksum; |
||||
|
uint8_t global_checksum[2]; // Big endian
|
||||
|
} __attribute__((packed)); |
||||
|
|
||||
|
extern uint8_t rom_logo[]; |
@ -0,0 +1 @@ |
|||||
|
/home/mid-kid/Stuff/Workspace/bitsandpieces/atmega/serial.c |
@ -0,0 +1 @@ |
|||||
|
/home/mid-kid/Stuff/Workspace/bitsandpieces/atmega/serial.h |
@ -0,0 +1 @@ |
|||||
|
/home/mid-kid/Stuff/Workspace/bitsandpieces/atmega/spi.c |
@ -0,0 +1 @@ |
|||||
|
/home/mid-kid/Stuff/Workspace/bitsandpieces/atmega/spi.h |
@ -0,0 +1 @@ |
|||||
|
/home/mid-kid/Stuff/Workspace/bitsandpieces/atmega/utils.h |
@ -0,0 +1,47 @@ |
|||||
|
name := GBCartRead |
||||
|
|
||||
|
dir_source := source |
||||
|
dir_build := build |
||||
|
|
||||
|
CFLAGS := -O0 -g -Wall -Wextra -std=gnu17 $(CFLAGS) |
||||
|
|
||||
|
CFLAGS += $(shell pkg-config --cflags libserialport) |
||||
|
LDLIBS += $(shell pkg-config --libs libserialport) |
||||
|
|
||||
|
SANIT := -fsanitize=address -fsanitize=pointer-compare -fsanitize=pointer-subtract -fsanitize=leak -fsanitize=undefined |
||||
|
OPTIM := -Os -fdata-sections -ffunction-sections -flto -fuse-linker-plugin -fipa-pta -Wl,--gc-sections -Wl,--print-gc-sections #-fgraphite-identity -floop-nest-optimize |
||||
|
|
||||
|
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2)) |
||||
|
objects := $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, $(call rwildcard, $(dir_source)/, *.c)) |
||||
|
|
||||
|
.SECONDEXPANSION: |
||||
|
|
||||
|
.PHONY: all |
||||
|
all: $(name) |
||||
|
|
||||
|
.PHONY: clean |
||||
|
clean: |
||||
|
rm -rf $(dir_build) $(name) |
||||
|
|
||||
|
.PHONY: sanit |
||||
|
sanit: CFLAGS += $(SANIT) |
||||
|
sanit: LDFLAGS += $(SANIT) |
||||
|
sanit: $(name) |
||||
|
|
||||
|
.PHONY: optim |
||||
|
optim: CFLAGS += $(OPTIM) |
||||
|
optim: LDFLAGS += $(OPTIM) |
||||
|
optim: $(name) |
||||
|
strip --strip-all --strip-unneeded $(name) |
||||
|
|
||||
|
$(name): $(objects) |
||||
|
$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ |
||||
|
|
||||
|
$(dir_build)/%.o: $(dir_source)/%.c | $$(dir $$@) |
||||
|
$(COMPILE.c) -MMD -MF $(@:.o=.d) $(OUTPUT_OPTION) $< |
||||
|
|
||||
|
.PRECIOUS: %/ |
||||
|
%/: |
||||
|
mkdir -p $@ |
||||
|
|
||||
|
-include $(patsubst %.o, %.d, $(objects)) |
@ -0,0 +1,144 @@ |
|||||
|
#include <stdio.h> |
||||
|
#include <stdint.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <unistd.h> |
||||
|
#include <locale.h> |
||||
|
#include <libserialport.h> |
||||
|
|
||||
|
#include <sys/time.h> |
||||
|
|
||||
|
#ifdef __GNUC__ |
||||
|
#define A_UNUSED __attribute__((unused)) |
||||
|
#else |
||||
|
#define A_UNUSED |
||||
|
#endif |
||||
|
|
||||
|
struct sp_port *serial_guess_port(void) |
||||
|
{ |
||||
|
struct sp_port **ports; |
||||
|
|
||||
|
if (sp_list_ports(&ports) != SP_OK || ports[0] == NULL) { |
||||
|
puts("No serial devices detected"); |
||||
|
return NULL; |
||||
|
} |
||||
|
|
||||
|
if (ports[1] != NULL) { |
||||
|
puts("Please select a port"); |
||||
|
return NULL; |
||||
|
} |
||||
|
|
||||
|
return ports[0]; |
||||
|
} |
||||
|
|
||||
|
char *program_name; |
||||
|
|
||||
|
void serial_error(char *message) |
||||
|
{ |
||||
|
char *error = sp_last_error_message(); |
||||
|
fprintf(stderr, "%s: %s: %s\n", program_name, message, error); |
||||
|
sp_free_error_message(error); |
||||
|
} |
||||
|
|
||||
|
int main(int argc, char *argv[]) |
||||
|
{ |
||||
|
program_name = argv[0]; |
||||
|
setlocale(LC_ALL, ""); |
||||
|
|
||||
|
long save = 0; |
||||
|
if (argc > 1) { |
||||
|
save = strtol(argv[1], NULL, 0); |
||||
|
} |
||||
|
|
||||
|
struct sp_port *port; |
||||
|
|
||||
|
if (!(port = serial_guess_port())) return 1; |
||||
|
puts(sp_get_port_name(port)); |
||||
|
|
||||
|
if (sp_open(port, SP_MODE_READ_WRITE) != SP_OK || |
||||
|
sp_set_baudrate(port, 2000000) != SP_OK || |
||||
|
sp_set_flowcontrol(port, SP_FLOWCONTROL_NONE) != SP_OK) { |
||||
|
serial_error("Failed to open port"); |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
usleep(100000LL); |
||||
|
sp_flush(port, SP_BUF_BOTH); |
||||
|
|
||||
|
fputs("Waiting for device... ", stdout); |
||||
|
fflush(stdout); |
||||
|
for (;;) { |
||||
|
char c = 0; |
||||
|
sp_blocking_write(port, &c, 1, 0); |
||||
|
if (sp_blocking_read(port, &c, 1, 1000) == 1) { |
||||
|
if (c == 0) { |
||||
|
puts("ROM OK!"); |
||||
|
} else { |
||||
|
puts("ROM ERROR"); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
char command = 1; |
||||
|
uint32_t address = 0x147; |
||||
|
uint32_t size = 3; |
||||
|
sp_blocking_write(port, &command, 1, 0); |
||||
|
sp_blocking_write(port, &address, 4, 0); |
||||
|
sp_blocking_write(port, &size, 4, 0); |
||||
|
|
||||
|
sp_blocking_read(port, &command, 1, 0); |
||||
|
if (command != 0) { |
||||
|
puts("An error has occurred"); |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
char cart_type = 0; |
||||
|
sp_blocking_read(port, &cart_type, 1, 0); |
||||
|
printf("Cart type: %02X\n", cart_type); |
||||
|
|
||||
|
char rom_size = 0; |
||||
|
sp_blocking_read(port, &rom_size, 1, 0); |
||||
|
printf("Rom size: %02X\n", rom_size); |
||||
|
|
||||
|
char ram_size = 0; |
||||
|
sp_blocking_read(port, &ram_size, 1, 0); |
||||
|
printf("Ram size: %02X\n", ram_size); |
||||
|
|
||||
|
command = save ? 3 : 2; |
||||
|
address = 0; |
||||
|
if (!save) { |
||||
|
size = 0x8000 << rom_size; |
||||
|
} else { |
||||
|
size = save; |
||||
|
} |
||||
|
sp_blocking_write(port, &command, 1, 0); |
||||
|
sp_blocking_write(port, &address, 4, 0); |
||||
|
sp_blocking_write(port, &size, 4, 0); |
||||
|
sp_blocking_read(port, &command, 1, 0); |
||||
|
if (command != 0) { |
||||
|
puts("An error has occurred"); |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
struct timeval begin, end; |
||||
|
gettimeofday(&begin, NULL); |
||||
|
|
||||
|
FILE *file = fopen("out.bin", "w"); |
||||
|
for (unsigned x = 0; x < size; x += 0x200) { |
||||
|
char c[0x200]; |
||||
|
sp_blocking_read(port, &c, 0x200, 0); |
||||
|
if (!save) { |
||||
|
printf("\r%02X:%04X", x / 0x4000, x % 0x4000); |
||||
|
} else { |
||||
|
printf("\r%02X:%04X", x / 0x2000, x % 0x2000); |
||||
|
} |
||||
|
fflush(stdout); |
||||
|
fwrite(&c, 0x200, 1, file); |
||||
|
} |
||||
|
putchar('\n'); |
||||
|
fclose(file); |
||||
|
|
||||
|
gettimeofday(&end, NULL); |
||||
|
double dt = (double)end.tv_sec - begin.tv_sec + ((double)end.tv_usec - begin.tv_usec) / 1000000; |
||||
|
printf("Average speed: %f KB/s\n", size / 1000 / dt); |
||||
|
} |
@ -0,0 +1,87 @@ |
|||||
|
#!/bin/sh |
||||
|
set -e |
||||
|
|
||||
|
PREFIX="$PWD/toolchain" |
||||
|
TARGET=avr |
||||
|
export CFLAGS='-Os -pipe' |
||||
|
export CXXFLAGS="$CFLAGS" |
||||
|
export PATH="$PREFIX/bin:$PATH" |
||||
|
export MAKEFLAGS="-j$(nproc)" |
||||
|
|
||||
|
mkdir -p "$PREFIX/src" |
||||
|
( cd "$PREFIX/src" |
||||
|
wget -c https://ftp.gnu.org/gnu/binutils/binutils-2.30.tar.xz |
||||
|
wget -c https://ftp.gnu.org/gnu/gcc/gcc-8.1.0/gcc-8.1.0.tar.xz |
||||
|
wget -c https://ftp.gnu.org/gnu/gmp/gmp-6.1.2.tar.xz |
||||
|
wget -c https://ftp.gnu.org/gnu/mpfr/mpfr-4.0.1.tar.xz |
||||
|
wget -c https://ftp.gnu.org/gnu/mpc/mpc-1.1.0.tar.gz |
||||
|
wget -c http://isl.gforge.inria.fr/isl-0.19.tar.xz |
||||
|
wget -c https://download.savannah.gnu.org/releases/avr-libc/avr-libc-2.0.0.tar.bz2 |
||||
|
wget -c https://download.savannah.gnu.org/releases/avrdude/avrdude-6.3.tar.gz |
||||
|
) |
||||
|
|
||||
|
( cd "$PREFIX/src" |
||||
|
rm -rf binutils-*/ |
||||
|
tar xf binutils-*.tar.xz |
||||
|
cd binutils-*/ |
||||
|
mkdir build |
||||
|
cd build |
||||
|
../configure --prefix="$PREFIX" --target="$TARGET" --disable-nls |
||||
|
make |
||||
|
make install |
||||
|
) |
||||
|
|
||||
|
( cd "$PREFIX/src" |
||||
|
rm -rf gcc-*/ |
||||
|
tar xf gcc-*.tar.xz |
||||
|
cd gcc-*/ |
||||
|
tar xf ../gmp-*.tar.xz |
||||
|
tar xf ../mpfr-*.tar.xz |
||||
|
tar xf ../mpc-*.tar.gz |
||||
|
tar xf ../isl-*.tar.xz |
||||
|
mv gmp-*/ gmp |
||||
|
mv mpfr-*/ mpfr |
||||
|
mv mpc-*/ mpc |
||||
|
mv isl-*/ isl |
||||
|
sed -i -e 's/\.\/fixinc\.sh/-c true/' gcc/Makefile.in |
||||
|
mkdir -p "$PREFIX/$TARGET/include" |
||||
|
mkdir build |
||||
|
cd build |
||||
|
../configure --prefix="$PREFIX" --target="$TARGET" --enable-languages=c,c++ --disable-nls |
||||
|
make |
||||
|
make install |
||||
|
) |
||||
|
|
||||
|
( cd "$PREFIX/src" |
||||
|
rm -rf avr-libc-*/ |
||||
|
tar xf avr-libc-*.tar.bz2 |
||||
|
cd avr-libc-*/ |
||||
|
./configure --prefix="$PREFIX" --host="$TARGET" |
||||
|
make |
||||
|
make install |
||||
|
) |
||||
|
|
||||
|
( cd "$PREFIX/src" |
||||
|
rm -rf avrdude-*/ |
||||
|
tar xf avrdude-*.tar.gz |
||||
|
cd avrdude-*/ |
||||
|
mkdir build |
||||
|
cd build |
||||
|
../configure --prefix="$PREFIX" |
||||
|
make |
||||
|
make install |
||||
|
) |
||||
|
|
||||
|
( cd "$PREFIX/src" |
||||
|
rm -rf binutils-*/ |
||||
|
rm -rf gcc-*/ |
||||
|
rm -rf avr-libc-*/ |
||||
|
rm -rf avrdude-*/ |
||||
|
#find "$PREFIX" -type f -name '*.la' -delete |
||||
|
) |
||||
|
|
||||
|
cat > "$PREFIX/env" << EOF |
||||
|
export PATH="$PREFIX/bin:\$PATH" |
||||
|
EOF |
||||
|
|
||||
|
echo 'Run "source toolchain/env" before running make!' |
@ -0,0 +1,111 @@ |
|||||
|
#!/bin/sh |
||||
|
set -e |
||||
|
|
||||
|
PREFIX="$PWD/toolchain" |
||||
|
TARGET=x86_64-linux-musl |
||||
|
ARCH=x86_64 |
||||
|
export CFLAGS='-Os -pipe' |
||||
|
export CXXFLAGS="$CFLAGS" |
||||
|
export PATH="$PREFIX/bin:$PATH" |
||||
|
export MAKEFLAGS="-j$(nproc)" |
||||
|
|
||||
|
mkdir -p "$PREFIX/src" |
||||
|
( cd "$PREFIX/src" |
||||
|
wget -c https://ftp.gnu.org/gnu/binutils/binutils-2.30.tar.xz |
||||
|
wget -c https://ftp.gnu.org/gnu/gcc/gcc-8.1.0/gcc-8.1.0.tar.xz |
||||
|
wget -c https://ftp.gnu.org/gnu/gmp/gmp-6.1.2.tar.xz |
||||
|
wget -c https://ftp.gnu.org/gnu/mpfr/mpfr-4.0.1.tar.xz |
||||
|
wget -c https://ftp.gnu.org/gnu/mpc/mpc-1.1.0.tar.gz |
||||
|
wget -c http://isl.gforge.inria.fr/isl-0.19.tar.xz |
||||
|
wget -c https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.16.7.tar.xz |
||||
|
wget -c https://www.musl-libc.org/releases/musl-1.1.19.tar.gz |
||||
|
wget -c http://sigrok.org/download/source/libserialport/libserialport-0.1.1.tar.gz |
||||
|
) |
||||
|
|
||||
|
( cd "$PREFIX/src" |
||||
|
rm -rf binutils-*/ |
||||
|
tar xf binutils-*.tar.xz |
||||
|
cd binutils-*/ |
||||
|
mkdir build |
||||
|
cd build |
||||
|
../configure --prefix="$PREFIX" --target="$TARGET" --disable-nls --disable-multilib |
||||
|
make |
||||
|
make install |
||||
|
) |
||||
|
|
||||
|
( cd "$PREFIX/src" |
||||
|
rm -rf gcc-*/ |
||||
|
tar xf gcc-*.tar.xz |
||||
|
cd gcc-*/ |
||||
|
tar xf ../gmp-*.tar.xz |
||||
|
tar xf ../mpfr-*.tar.xz |
||||
|
tar xf ../mpc-*.tar.gz |
||||
|
tar xf ../isl-*.tar.xz |
||||
|
mv gmp-*/ gmp |
||||
|
mv mpfr-*/ mpfr |
||||
|
mv mpc-*/ mpc |
||||
|
mv isl-*/ isl |
||||
|
mkdir build |
||||
|
cd build |
||||
|
../configure --prefix="$PREFIX" --target="$TARGET" --disable-nls --disable-multilib --disable-libsanitizer --disable-libmpx --enable-languages=c,c++ |
||||
|
make all-gcc |
||||
|
make install-gcc |
||||
|
) |
||||
|
|
||||
|
( cd "$PREFIX/src" |
||||
|
rm -rf linux-*/ |
||||
|
tar xf linux-*.tar.xz |
||||
|
cd linux-*/ |
||||
|
make ARCH="$ARCH" INSTALL_HDR_PATH="$PWD/install" headers_install |
||||
|
find install -type f -a ! -name '*.h' -delete |
||||
|
cp -aT install/include "$PREFIX/$TARGET/include" |
||||
|
) |
||||
|
|
||||
|
( cd "$PREFIX/src" |
||||
|
rm -rf musl-*/ |
||||
|
tar xf musl-*.tar.gz |
||||
|
cd musl-*/ |
||||
|
./configure --prefix="$PREFIX/$TARGET" --host="$TARGET" --syslibdir='$(prefix)/lib' |
||||
|
make install-headers |
||||
|
) |
||||
|
|
||||
|
( cd "$PREFIX/src" |
||||
|
cd gcc-*/build |
||||
|
make MAKE='make enable_shared=no' all-target-libgcc |
||||
|
make MAKE='make enable_shared=no' install-target-libgcc |
||||
|
) |
||||
|
|
||||
|
( cd "$PREFIX/src" |
||||
|
cd musl-*/ |
||||
|
make LIBCC="$PREFIX/lib/gcc/$TARGET/*/libgcc.a" install |
||||
|
) |
||||
|
|
||||
|
( cd "$PREFIX/src" |
||||
|
cd gcc-*/build |
||||
|
make |
||||
|
make install |
||||
|
) |
||||
|
|
||||
|
( cd "$PREFIX/src" |
||||
|
rm -rf libserialport-*/ |
||||
|
tar xf libserialport-*.tar.gz |
||||
|
cd libserialport-*/ |
||||
|
./configure --prefix="$PREFIX/$TARGET" --host="$TARGET" |
||||
|
make |
||||
|
make install |
||||
|
) |
||||
|
|
||||
|
( cd "$PREFIX/src" |
||||
|
rm -rf binutils-*/ |
||||
|
rm -rf gcc-*/ |
||||
|
rm -rf linux-*/ |
||||
|
rm -rf musl-*/ |
||||
|
rm -rf libserialport-*/ |
||||
|
#find "$PREFIX" -type f -name '*.la' -delete |
||||
|
) |
||||
|
|
||||
|
cat > "$PREFIX/env" << EOF |
||||
|
export PATH="$PREFIX/bin:\$PATH" |
||||
|
EOF |
||||
|
|
||||
|
echo 'Run "source toolchain/env" before running make!' |
Loading…
Reference in new issue