Skip to content

Instantly share code, notes, and snippets.

@Madsy
Last active April 2, 2019 19:39
Show Gist options
  • Save Madsy/80f6410d6200194abb54b51967046c74 to your computer and use it in GitHub Desktop.
Save Madsy/80f6410d6200194abb54b51967046c74 to your computer and use it in GitHub Desktop.
SNES 'lorom' toolchain.
#!/bin/bash
PREFIX=$(HOME)/snes-toolchain
#clone binutils development branch
git clone git://sourceware.org/git/binutils-gdb.git binutils-gdb
cd binutils-gdb
# Checkout a *very* old binutils commit from 2005, as gas support for the coff-w65 target was discontinued a long time ago.
# This version is the best one I found so far.
git checkout 0fdc72dad81fc7f32b898cb408da0300667f728b
# Remove gdb directory as gdb does not support the coff-w65 BFD / target. It probably did before
# this commit, but become broken somewhere down the line
# autoconf will just ignore the missing project directory and not recurse
rm -rf ./gdb
# --program-prefix added due to bug where host and target are not added as prefix
../binutils-gdb/configure --build=x86_64-pc-linux-gnu --host=x86_64-pc-linux-gnu --target=w65-none-system \
--enable-plugins --enable-shared --disable-werror --with-system-zlib --prefix=$(PREFIX) \
--disable-gdb --program-prefix=w65-none-system-
#Workaround: Systems nowadays have a too new version of makeinfo to generate the documentation, which breaks the install step
#Overriding $(MAKEINFO) to true at least lets us complete the build and install.
make -j4 install MAKEINFO=true
exit 0
/*
Copyright 2017 Mads Elvheim / 'Madsy'
Redistribution and use in source and binary forms, with or without modification, are permitted provided
that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions
and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
============================== SNES 'lorom' linker script for GNU Binutils ===========================
General information:
======================================================================================================
bank0: (rom address 0x000000, snes address 0x8000) <--- last 64 bytes here contains the SNES header.
bank1: (rom address 0x008000, snes address 0x18000)
bank2: (rom address 0x010000, snes address 0x28000)
bank3: (rom address 0x018000, snes address 0x38000)
bank4: (rom address 0x020000, snes address 0x48000)
...
And so on, you get the drift.
The number of mirrored ROM address regions depends on which SNES game's physical pinouts,
the ROM size, the cartridge memory controller and whether the cart has SRAM or not. To get the behavior you want
when testing in an emulator, it might be good idea to fill the SNES header with data from another
'lorom' game to pretend that your ROM image is an existing game the emulator knows about.
To fill the SNES header with information, look for the _snesheader label in "init.s".
Either hardcode the values, or use an external tool to update the header after the linking step.
======================================================================================================
Usage of the linker script:
======================================================================================================
The ROM image is split into 32k memory regions in order to correct for the SNES' 'lowrom' memory mapping.
The regions are tightly packed in the image, while the SNES only sees the ROM image mapped to the upper
32KiB in each of its 65k banks from 0x00 to 0x3F. Access the higher banks to get a continuous region of ROM.
The memory region setup lets you put read-only data and code exactly in the bank you want.
For example, to put code into bank 0x00, put your code into the .text0 section and data into .rodata0 section.
To put code into bank 0x04, put your code and data into the .text4 and .rodata4 sections, and so on.
For brevity, only output sections for banks 0x00 to 0x04 is defined, which is usually enough. To add more,
simple follow the same pattern.
*/
OUTPUT_FORMAT("coff-w65")
OUTPUT_ARCH(w65)
SEARCH_DIR(.)
BANK_SIZE = 32k;
SNES_HEADER_SIZE = 64;
MEMORY
{
wram (rw) : ORIGIN = 0x000000, LENGTH = 8k
rom_bank_00 (rx) : ORIGIN = 0x008000, LENGTH = (32k - 64)
header_bank (rx) : ORIGIN = 0x00FFC0, LENGTH = 64
rom_bank_01 (rx) : ORIGIN = 0x018000, LENGTH = 32k
rom_bank_02 (rx) : ORIGIN = 0x028000, LENGTH = 32k
rom_bank_03 (rx) : ORIGIN = 0x038000, LENGTH = 32k /* this is the end of a 1 MBit cart */
rom_bank_04 (rx) : ORIGIN = 0x048000, LENGTH = 32k
rom_bank_05 (rx) : ORIGIN = 0x058000, LENGTH = 32k
rom_bank_06 (rx) : ORIGIN = 0x068000, LENGTH = 32k
rom_bank_07 (rx) : ORIGIN = 0x078000, LENGTH = 32k /* this is the end of a 2 MBit cart */
rom_bank_08 (rx) : ORIGIN = 0x088000, LENGTH = 32k
rom_bank_09 (rx) : ORIGIN = 0x098000, LENGTH = 32k
rom_bank_0A (rx) : ORIGIN = 0x0A8000, LENGTH = 32k
rom_bank_0B (rx) : ORIGIN = 0x0B8000, LENGTH = 32k
rom_bank_0C (rx) : ORIGIN = 0x0C8000, LENGTH = 32k
rom_bank_0D (rx) : ORIGIN = 0x0D8000, LENGTH = 32k
rom_bank_0E (rx) : ORIGIN = 0x0E8000, LENGTH = 32k
rom_bank_0F (rx) : ORIGIN = 0x0F8000, LENGTH = 32k /* this is the end of a 4 MBit cart */
rom_bank_10 (rx) : ORIGIN = 0x108000, LENGTH = 32k
rom_bank_11 (rx) : ORIGIN = 0x118000, LENGTH = 32k
rom_bank_12 (rx) : ORIGIN = 0x128000, LENGTH = 32k
rom_bank_13 (rx) : ORIGIN = 0x138000, LENGTH = 32k
rom_bank_14 (rx) : ORIGIN = 0x148000, LENGTH = 32k
rom_bank_15 (rx) : ORIGIN = 0x158000, LENGTH = 32k
rom_bank_16 (rx) : ORIGIN = 0x168000, LENGTH = 32k
rom_bank_17 (rx) : ORIGIN = 0x178000, LENGTH = 32k
rom_bank_18 (rx) : ORIGIN = 0x188000, LENGTH = 32k
rom_bank_19 (rx) : ORIGIN = 0x198000, LENGTH = 32k
rom_bank_1A (rx) : ORIGIN = 0x1A8000, LENGTH = 32k
rom_bank_1B (rx) : ORIGIN = 0x1B8000, LENGTH = 32k
rom_bank_1C (rx) : ORIGIN = 0x1C8000, LENGTH = 32k
rom_bank_1D (rx) : ORIGIN = 0x1D8000, LENGTH = 32k
rom_bank_1E (rx) : ORIGIN = 0x1E8000, LENGTH = 32k
rom_bank_1F (rx) : ORIGIN = 0x1F8000, LENGTH = 32k /* this is the end of a 8 MBit cart */
rom_bank_20 (rx) : ORIGIN = 0x208000, LENGTH = 32k
rom_bank_21 (rx) : ORIGIN = 0x218000, LENGTH = 32k
rom_bank_22 (rx) : ORIGIN = 0x228000, LENGTH = 32k
rom_bank_23 (rx) : ORIGIN = 0x238000, LENGTH = 32k
rom_bank_24 (rx) : ORIGIN = 0x248000, LENGTH = 32k
rom_bank_25 (rx) : ORIGIN = 0x258000, LENGTH = 32k
rom_bank_26 (rx) : ORIGIN = 0x268000, LENGTH = 32k
rom_bank_27 (rx) : ORIGIN = 0x278000, LENGTH = 32k
rom_bank_28 (rx) : ORIGIN = 0x288000, LENGTH = 32k
rom_bank_29 (rx) : ORIGIN = 0x298000, LENGTH = 32k
rom_bank_2A (rx) : ORIGIN = 0x2A8000, LENGTH = 32k
rom_bank_2B (rx) : ORIGIN = 0x2B8000, LENGTH = 32k
rom_bank_2C (rx) : ORIGIN = 0x2C8000, LENGTH = 32k
rom_bank_2D (rx) : ORIGIN = 0x2D8000, LENGTH = 32k
rom_bank_2E (rx) : ORIGIN = 0x2E8000, LENGTH = 32k
rom_bank_2F (rx) : ORIGIN = 0x2F8000, LENGTH = 32k
rom_bank_30 (rx) : ORIGIN = 0x308000, LENGTH = 32k
rom_bank_31 (rx) : ORIGIN = 0x318000, LENGTH = 32k
rom_bank_32 (rx) : ORIGIN = 0x328000, LENGTH = 32k
rom_bank_33 (rx) : ORIGIN = 0x338000, LENGTH = 32k
rom_bank_34 (rx) : ORIGIN = 0x348000, LENGTH = 32k
rom_bank_35 (rx) : ORIGIN = 0x358000, LENGTH = 32k
rom_bank_36 (rx) : ORIGIN = 0x368000, LENGTH = 32k
rom_bank_37 (rx) : ORIGIN = 0x378000, LENGTH = 32k
rom_bank_38 (rx) : ORIGIN = 0x388000, LENGTH = 32k
rom_bank_39 (rx) : ORIGIN = 0x398000, LENGTH = 32k
rom_bank_3A (rx) : ORIGIN = 0x3A8000, LENGTH = 32k
rom_bank_3B (rx) : ORIGIN = 0x3B8000, LENGTH = 32k
rom_bank_3C (rx) : ORIGIN = 0x3C8000, LENGTH = 32k
rom_bank_3D (rx) : ORIGIN = 0x3D8000, LENGTH = 32k
rom_bank_3E (rx) : ORIGIN = 0x3E8000, LENGTH = 32k
rom_bank_3F (rx) : ORIGIN = 0x3F8000, LENGTH = 32k /* this is the end of a 16 MBit cart */
}
SECTIONS
{
.bank0 :
{
KEEP(*(.vectors .vectors.*))
*(.text0 .text0.*)
*(.rodata0 .rodata0.*)
/* BUG WORKAROUND. THE FOLLOWING POSITION JUMP CAN NOT BE A NORMAL ALIGNMENT!!
AGAIN, DO NOT USE . = ALIGN(x) here!
LD will troll you and put the SNES header 'x' bytes back. This will happen no matter how
large you make the header_bank region.*/
. = (32k - 64);
} > rom_bank_00 = 0xFF
_bank0_end = .;
.sheader : AT (_bank0_end)
{
*(.snesh)
. = ALIGN(32k);
} > header_bank
_header_bank_end = .;
.bank1 : AT (_header_bank_end)
{
*(.text1 .text1.*)
*(.rodata1 .rodata1.*)
. = ALIGN(32k);
} > rom_bank_01
_bank1_end = .;
.bank2 : AT (_bank1_end)
{
*(.text2 .text2.*)
*(.rodata2 .rodata2.*)
. = ALIGN(32k);
} > rom_bank_02
_bank2_end = .;
.bank3 : AT (_bank2_end)
{
*(.text3 .text3.*)
*(.rodata3 .rodata3.*)
. = ALIGN(32k);
} > rom_bank_03
_bank3_end = .;
.bank4 : AT (_bank3_end)
{
*(.text4 .text4.*)
*(.rodata4 .rodata4.*)
. = ALIGN(32k);
} > rom_bank_04
_bank4_end = .;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment