diff options
author | Ian C <ianc@noddybox.co.uk> | 2016-04-15 15:58:09 +0100 |
---|---|---|
committer | Ian C <ianc@noddybox.co.uk> | 2016-04-15 15:58:09 +0100 |
commit | 24ebb224a11d31bfe039352fd29d2b89368583ac (patch) | |
tree | ee34d23fc3f10a47591ef5d0c027a7efa881be1e /src/gbout.c | |
parent | 044adfa27731c0314bc4c9340fd1eebfaf3cf270 (diff) |
Started on Gameboy support.
Diffstat (limited to 'src/gbout.c')
-rw-r--r-- | src/gbout.c | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/src/gbout.c b/src/gbout.c new file mode 100644 index 0000000..8fa3e7a --- /dev/null +++ b/src/gbout.c @@ -0,0 +1,432 @@ +/* + + casm - Simple, portable assembler + + Copyright (C) 2003-2015 Ian Cowburn (ianc@noddybox.demon.co.uk) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + ------------------------------------------------------------------------- + + Gameboy ROM output handler. + +*/ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "global.h" +#include "expr.h" +#include "codepage.h" +#include "gbout.h" + + +/* ---------------------------------------- MACROS & TYPES +*/ + +enum option_t +{ + OPT_COLOUR, + OPT_SUPER, + OPT_CART_RAM, + OPT_CART_TYPE, + OPT_RST, + OPT_IRQ +}; + +static const ValueTable option_set[] = +{ + {"gameboy-colour", OPT_COLOUR}, + {"gameboy-color", OPT_COLOUR}, + {"gameboy-super", OPT_SUPER}, + {"gameboy-cart-ram", OPT_CART_RAM}, + {"gameboy-cart-type", OPT_CART_RAM}, + {"gameboy-irq", OPT_IRQ}, + {NULL} +}; + +typedef enum +{ + IRQ_VBLANK, + IRQ_LCD, + IRQ_TIMER, + IRQ_SERIAL, + IRQ_JOYPAD +} IRQ_Type; + +static ValueTable irq_table[] = +{ + {"vbl", IRQ_VBLANK}, + {"lcd", IRQ_LCD}, + {"timer", IRQ_TIMER}, + {"serial", IRQ_SERIAL}, + {"joypad", IRQ_JOYPAD}, + {NULL} +}; + +static struct +{ + int is_colour; + int is_super; + int cart_ram; + int cart_type; + int irq_vector[5]; +} option = +{ + FALSE, FALSE, -1, -1, + {-1, -1, -1, -1, -1} +}; + + +/* ---------------------------------------- PRIVATE FUNCTIONS +*/ + +static int PokeB(Byte *mem, int addr, Byte b) +{ + mem[addr++] = b; + return (addr % 0x10000); +} + + +static int PokeW(Byte *mem, int addr, int w) +{ + addr = PokeB(mem, addr, w & 0xff); + return PokeB(mem, addr, (w & 0xff00) >> 8); +} + + +static int PokeS(Byte *mem, int addr, const char *str, int maxlen) +{ + while(*str && maxlen--) + { + addr = PokeB(mem, addr, CodeFromNative(CP_ASCII, *str++)); + } + + return addr; +} + + +static int PokeCode(Byte *mem, int addr, const int *code) +{ + while(*code != -1) + { + addr = PokeB(mem, addr, *code++); + } + + return addr; +} + + + +/* ---------------------------------------- INTERFACES +*/ +const ValueTable *GBOutputOptions(void) +{ + return option_set; +} + +CommandStatus GBOutputSetOption(int opt, int argc, char *argv[], + int quoted[], char *err, size_t errsize) +{ + const ValueTable *val; + int f; + + CMD_ARGC_CHECK(1); + + switch(opt) + { + case OPT_COLOUR: + option.is_colour = ParseTrueFalse(argv[0], TRUE); + break; + + case OPT_SUPER: + option.is_super = ParseTrueFalse(argv[0], TRUE); + break; + + case OPT_CART_RAM: + CMD_EXPR(argv[0], option.cart_ram); + break; + + case OPT_CART_TYPE: + CMD_EXPR(argv[0], option.cart_type); + break; + + case OPT_IRQ: + CMD_ARGC_CHECK(2); + CMD_TABLE(argv[0], irq_table, val); + CMD_EXPR(argv[1], f); + option.irq_vector[val->value] = f; + break; + + default: + break; + } + + return CMD_OK; + +} + +int GBOutput(const char *filename, const char *filename_bank, + MemoryBank **bank, int count, char *error, size_t error_size) +{ + static const int 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, -1 + }; + + FILE *fp = fopen(filename, "wb"); + int f; + int offset; + int rom_size; + unsigned global_csum = 0; + Byte hdr_csum = 0; + Byte *mem; + + if (!fp) + { + snprintf(error, error_size, "Failed to create %s", filename); + return FALSE; + } + + /* Check bank sizes and layouts + */ + if (count == 1 && (bank[0]->min_address_used < 0x150 || + bank[0]->max_address_used > 0x7fff)) + { + snprintf(error, error_size, "A simple ROM must be in the address " + "space 0x150 to 0x7fff"); + return FALSE; + } + + if (count > 1 && (bank[0]->min_address_used < 0x150 || + bank[0]->max_address_used > 0x3fff)) + { + snprintf(error, error_size, "Bank zero of a banked ROM must be in the " + "address space 0x150 to 0x3fff"); + return FALSE; + } + + for(f = 1 ; f < count; f++) + { + if (bank[f]->min_address_used < 0x4000 || + bank[f]->max_address_used > 0x7fff) + { + snprintf(error, error_size, + "Bank %u must be in the address space " + "0x4000 to 0x7fff", bank[f]->number); + return FALSE; + } + } + + if (option.cart_type == -1) + { + if (count == 1) + { + option.cart_type = 0; + } + else + { + option.cart_type = 1; + } + + if (option.cart_ram != -1) + { + option.cart_type = 3; + } + } + + if (count == 1) + { + rom_size = 0; + } + else + { + rom_size = (count / 4) + 1; + } + + mem = bank[0]->memory; + + /* Create the RST handlers + */ + for(f = 0; f < 8; f++) + { + int addr = f * 8; + + addr = PokeB(mem, addr, 0xc3); + addr = PokeW(mem, addr, 0x100); + } + + /* Create the IRQ handlers + */ + for(f = 0; f < 5; f++) + { + int addr = (f * 8) + 0x40; + + if (option.irq_vector[f] == -1) + { + addr = PokeB(mem, addr, 0xd9); + } + else + { + addr = PokeB(mem, addr, 0xc3); + addr = PokeW(mem, addr, option.irq_vector[f]); + } + } + + /* Create the entry point + */ + PokeB(mem, 0x100, 0); + PokeB(mem, 0x101, 0xc3); + PokeW(mem, 0x102, bank[0]->min_address_used); + + /* Title (new smaller size) + */ + PokeS(mem, 0x134, filename, 11); + + /* GBC flag + */ + if (option.is_colour) + { + PokeB(mem, 0x143, 0xc0); + } + + /* Super Gameboy flag + */ + if (option.is_super) + { + PokeB(mem, 0x146, 3); + } + + /* Type/ROM size + */ + PokeB(mem, 0x147, option.cart_type); + PokeB(mem, 0x148, rom_size); + + /* RAM size + */ + switch(option.cart_ram) + { + case 2: + PokeB(mem, 0x149, 1); + break; + + case 8: + PokeB(mem, 0x149, 2); + break; + + case 32: + PokeB(mem, 0x149, 3); + break; + + case 128: + PokeB(mem, 0x149, 4); + break; + + case 64: + PokeB(mem, 0x149, 5); + break; + + default: + PokeB(mem, 0x149, 0); + break; + } + + /* To be sold everywhere + */ + PokeB(mem, 0x14a, 1); + + /* Old licensee + */ + if (option.is_super) + { + PokeB(mem, 0x14a, 0x33); + } + else + { + PokeB(mem, 0x14a, 0); + } + + /* Header checksum + */ + for(f = 0x134 ; f < 0x14d; f++) + { + hdr_csum -= mem[f]; + } + + PokeB(mem, 0x14d, hdr_csum); + + /* Global checksum + */ + if (count == 1) + { + for(f = 0x150; f < 0x8000; f++) + { + global_csum += mem[f]; + } + } + else + { + int r; + + for(f = 0x150; f < 0x4000; f++) + { + global_csum += mem[f]; + } + + for(r = 1; r < count; r++) + { + for(f = 0x4000; f < 0x8000; f++) + { + global_csum += bank[r]->memory[f]; + } + } + } + + PokeW(mem, 0x14e, global_csum); + + /* Output the ROM contents + */ + if (count == 1) + { + fwrite(mem, 0x8000, 1, fp); + } + else + { + int r; + + fwrite(mem, 0x4000, 1, fp); + + for(r = 1; r < count; r++) + { + for(f = 0x4000; f < 0x8000; f++) + { + fwrite(bank[r]->memory + 0x4000, 0x4000, 1, fp); + } + } + } + + + fclose(fp); + + return TRUE; +} + + +/* +vim: ai sw=4 ts=8 expandtab +*/ |