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