@ -0,0 +1,21 @@ | |||||
MIT License | |||||
Copyright (c) 2021 Daid | |||||
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. |
@ -0,0 +1,26 @@ | |||||
# Gameboy Software Development Kit | |||||
This is the Gameboy software development kit. For mixed assembly and C programming. Note that this isn't a mature project yet. | |||||
If you want to do C programming for the Gameboy, https://github.com/gbdk-2020/gbdk-2020 is much more mature. | |||||
If you want to do assembly programming for the Gameboy, https://rgbds.gbdev.io/ is the goto toolchain. | |||||
# But... but...? Why this then? | |||||
This project has a few goals: | |||||
* Thin/no abstractions. GBDK-2020 is an oddball of low level functions, badly named functions and high level functions with horrible performance and side effects. | |||||
* Mixing of C code with ASM code, without subjecting yourself to the asxxxx syntax. | |||||
* A different linker. By using the rgbds linker instead of the SDCC linker, a bunch of features that exernal tools provide on gbdk-2020 are standard. | |||||
# Usage | |||||
To use this, you need to have installed: | |||||
* sdcc (version 4.1.0) | |||||
* rgbds (version 0.5.1) | |||||
* python (version 3.6 or newer) | |||||
* A strong will, and a bit of crazy. | |||||
See https://github.com/daid/gbsdk-template for a ready to use template of a project setup. |
@ -0,0 +1,18 @@ | |||||
#ifndef GBSDK_ASSETS_H | |||||
#define GBSDK_ASSETS_H | |||||
#include <stdint.h> | |||||
#define EXTERN_ASSET(var_name) \ | |||||
extern const uint8_t var_name[]; \ | |||||
extern const uint8_t var_name ## _end[] | |||||
#define ASSET(var_name, filename) \ | |||||
void __ ## var_name ## __() __naked { \ | |||||
__asm__("_" #var_name "::"); \ | |||||
__asm__(".incbin \"_build/assets/" filename "\""); \ | |||||
__asm__("_" #var_name "_end::"); \ | |||||
} EXTERN_ASSET(var_name) | |||||
#endif//GBSDK_ASSETS_H |
@ -0,0 +1,6 @@ | |||||
; Usage: asset label_name, "filename" | |||||
MACRO asset | |||||
\1:: | |||||
INCBIN STRCAT("_build/assets/", \2) | |||||
.end:: | |||||
ENDM |
@ -0,0 +1,13 @@ | |||||
#ifndef GBSDK_BANKING_H | |||||
#define GBSDK_BANKING_H | |||||
#include <stdint.h> | |||||
extern __sfr current_bank; | |||||
inline void switch_bank(bank_nr) { | |||||
current_bank = bank_nr; | |||||
*((uint8_t*)0x2000) = bank_nr; | |||||
} | |||||
#endif//LIB_BANKING_H |
@ -0,0 +1,494 @@ | |||||
#ifndef GBSDK_HARDWARE_H | |||||
#define GBSDK_HARDWARE_H | |||||
// Register for reading joy pad info. (R/W) | |||||
static volatile __sfr __at(0x00) rP1; | |||||
#define P1_5 0b00100000 // P15 out port, set to 0 to get buttons | |||||
#define P1_4 0b00010000 // P14 out port, set to 0 to get dpad | |||||
#define P1_3 0b00001000 // P13 in port | |||||
#define P1_2 0b00000100 // P12 in port | |||||
#define P1_1 0b00000010 // P11 in port | |||||
#define P1_0 0b00000001 // P10 in port | |||||
#define P1_GET_DPAD P1_5 | |||||
#define P1_GET_BTN P1_4 | |||||
#define P1_GET_NONE (P1_4 | P1_5) | |||||
// Serial Transfer Data (R/W) | |||||
static volatile __sfr __at(0x01) rSB; | |||||
// Serial I/O Control (R/W) | |||||
static volatile __sfr __at(0x02) rSC; | |||||
// Divider register (R/W) | |||||
static volatile __sfr __at(0x04) rDIV; | |||||
// Timer counter (R/W) | |||||
static volatile __sfr __at(0x05) rTIMA; | |||||
// Timer modulo (R/W) | |||||
static volatile __sfr __at(0x06) rTMA; | |||||
// Timer control (R/W) | |||||
static volatile __sfr __at(0x07) rTAC; | |||||
#define TAC_START 0b00000100 | |||||
#define TAC_STOP 0b00000000 | |||||
#define TAC_4KHZ 0b00000000 | |||||
#define TAC_16KHZ 0b00000011 | |||||
#define TAC_65KHZ 0b00000010 | |||||
#define TAC_262KHZ 0b00000001 | |||||
// Interrupt Flag (R/W) | |||||
static volatile __sfr __at(0x0F) rIF; | |||||
// AUD1SWEEP/NR10 ($FF10) | |||||
// Sweep register (R/W) | |||||
// | |||||
// Bit 6-4 - Sweep Time | |||||
// Bit 3 - Sweep Increase/Decrease | |||||
// 0: Addition (frequency increases???) | |||||
// 1: Subtraction (frequency increases???) | |||||
// Bit 2-0 - Number of sweep shift (# 0-7) | |||||
// Sweep Time: (n*7.8ms) | |||||
static volatile __sfr __at(0x10) rNR10; | |||||
#define rAUD1SWEEP rNR10 | |||||
#define AUD1SWEEP_UP 0b00000000 | |||||
#define AUD1SWEEP_DOWN 0b00001000 | |||||
// AUD1LEN/NR11 ($FF11) | |||||
// Sound length/Wave pattern duty (R/W) | |||||
// | |||||
// Bit 7-6 - Wave Pattern Duty (00:12.5% 01:25% 10:50% 11:75%) | |||||
// Bit 5-0 - Sound length data (# 0-63) | |||||
static volatile __sfr __at(0x11) rNR11; | |||||
#define rAUD1LEN rNR11 | |||||
// AUD1ENV/NR12 ($FF12) | |||||
// Envelope (R/W) | |||||
// | |||||
// Bit 7-4 - Initial value of envelope | |||||
// Bit 3 - Envelope UP/DOWN | |||||
// 0: Decrease | |||||
// 1: Range of increase | |||||
// Bit 2-0 - Number of envelope sweep (# 0-7) | |||||
static volatile __sfr __at(0x12) rNR12; | |||||
#define rAUD1ENV rNR12 | |||||
// AUD1LOW/NR13 ($FF13) | |||||
// Frequency low byte (W) | |||||
static volatile __sfr __at(0x13) rNR13; | |||||
#define rAUD1LOW rNR13 | |||||
// AUD1HIGH/NR14 ($FF14) | |||||
// Frequency high byte (W) | |||||
// | |||||
// Bit 7 - Initial (when set, sound restarts) | |||||
// Bit 6 - Counter/consecutive selection | |||||
// Bit 2-0 - Frequency's higher 3 bits | |||||
static volatile __sfr __at(0x14) rNR14; | |||||
#define rAUD1HIGH rNR14 | |||||
// AUD2LEN/NR21 ($FF16) | |||||
// Sound Length; Wave Pattern Duty (R/W) | |||||
// | |||||
// see AUD1LEN for info | |||||
static volatile __sfr __at(0x16) rNR21; | |||||
#define rAUD2LEN rNR21 | |||||
// AUD2ENV/NR22 ($FF17) | |||||
// Envelope (R/W) | |||||
// | |||||
// see AUD1ENV for info | |||||
static volatile __sfr __at(0x17) rNR22; | |||||
#define rAUD2ENV rNR22 | |||||
// AUD2LOW/NR23 ($FF18) | |||||
// Frequency low byte (W) | |||||
static volatile __sfr __at(0x18) rNR23; | |||||
#define rAUD2LOW rNR23 | |||||
// AUD2HIGH/NR24 ($FF19) | |||||
// Frequency high byte (W) | |||||
// | |||||
// see AUD1HIGH for info | |||||
static volatile __sfr __at(0x19) rNR24; | |||||
#define rAUD2HIGH rNR24 | |||||
// AUD3ENA/NR30 ($FF1A) | |||||
// Sound on/off (R/W) | |||||
// | |||||
// Bit 7 - Sound ON/OFF (1=ON,0=OFF) | |||||
static volatile __sfr __at(0x1A) rNR30; | |||||
#define rAUD3ENA rNR30 | |||||
// AUD3LEN/NR31 ($FF1B) | |||||
// Sound length (R/W) | |||||
// | |||||
// Bit 7-0 - Sound length | |||||
static volatile __sfr __at(0x1B) rNR31; | |||||
#define rAUD3LEN rNR31 | |||||
// AUD3LEVEL/NR32 ($FF1C) | |||||
// Select output level | |||||
// | |||||
// Bit 6-5 - Select output level | |||||
// 00: 0/1 (mute) | |||||
// 01: 1/1 | |||||
// 10: 1/2 | |||||
// 11: 1/4 | |||||
static volatile __sfr __at(0x1C) rNR32; | |||||
#define rAUD3LEVEL rNR32 | |||||
// AUD3LOW/NR33 ($FF1D) | |||||
// Frequency low byte (W) | |||||
// | |||||
// see AUD1LOW for info | |||||
static volatile __sfr __at(0x1D) rNR33; | |||||
#define rAUD3LOW rNR33 | |||||
// AUD3HIGH/NR34 ($FF1E) | |||||
// Frequency high byte (W) | |||||
// | |||||
// see AUD1HIGH for info | |||||
static volatile __sfr __at(0x1E) rNR34; | |||||
#define rAUD3HIGH rNR34 | |||||
// AUD4LEN/NR41 ($FF20) | |||||
// Sound length (R/W) | |||||
// | |||||
// Bit 5-0 - Sound length data (# 0-63) | |||||
static volatile __sfr __at(0x20) rNR41; | |||||
#define rAUD4LEN rNR41 | |||||
// AUD4ENV/NR42 ($FF21) | |||||
// Envelope (R/W) | |||||
// | |||||
// see AUD1ENV for info | |||||
static volatile __sfr __at(0x21) rNR42; | |||||
#define rAUD4ENV rNR42 | |||||
// AUD4POLY/NR43 ($FF22) | |||||
// Polynomial counter (R/W) | |||||
// | |||||
// Bit 7-4 - Selection of the shift clock frequency of the (scf) | |||||
// polynomial counter (0000-1101) | |||||
// freq=drf*1/2^scf (not sure) | |||||
// Bit 3 - Selection of the polynomial counter's step | |||||
// 0: 15 steps | |||||
// 1: 7 steps | |||||
// Bit 2-0 - Selection of the dividing ratio of frequencies (drf) | |||||
// 000: f/4 001: f/8 010: f/16 011: f/24 | |||||
// 100: f/32 101: f/40 110: f/48 111: f/56 (f=4.194304 Mhz) | |||||
static volatile __sfr __at(0x22) rNR43; | |||||
#define rAUD4POLY rNR43 | |||||
// AUD4GO/NR44 ($FF23) | |||||
// | |||||
// Bit 7 - Inital | |||||
// Bit 6 - Counter/consecutive selection | |||||
static volatile __sfr __at(0x23) rNR44; | |||||
#define rAUD4GO rNR44 | |||||
// AUDVOL/NR50 ($FF24) | |||||
// Channel control / ON-OFF / Volume (R/W) | |||||
// | |||||
// Bit 7 - Vin->SO2 ON/OFF (Vin??) | |||||
// Bit 6-4 - SO2 output level (volume) (# 0-7) | |||||
// Bit 3 - Vin->SO1 ON/OFF (Vin??) | |||||
// Bit 2-0 - SO1 output level (volume) (# 0-7) | |||||
static volatile __sfr __at(0x24) rNR50; | |||||
#define rAUDVOL rNR50 | |||||
#define AUDVOL_VIN_LEFT 0b10000000 // SO2 | |||||
#define AUDVOL_VIN_RIGHT 0b00001000 // SO1 | |||||
// AUDTERM/NR51 ($FF25) | |||||
// Selection of Sound output terminal (R/W) | |||||
// | |||||
// Bit 7 - Output sound 4 to SO2 terminal | |||||
// Bit 6 - Output sound 3 to SO2 terminal | |||||
// Bit 5 - Output sound 2 to SO2 terminal | |||||
// Bit 4 - Output sound 1 to SO2 terminal | |||||
// Bit 3 - Output sound 4 to SO1 terminal | |||||
// Bit 2 - Output sound 3 to SO1 terminal | |||||
// Bit 1 - Output sound 2 to SO1 terminal | |||||
// Bit 0 - Output sound 0 to SO1 terminal | |||||
static volatile __sfr __at(0x25) rNR51; | |||||
#define rAUDTERM rNR51 | |||||
// SO2 | |||||
#define AUDTERM_4_LEFT 0b10000000 | |||||
#define AUDTERM_3_LEFT 0b01000000 | |||||
#define AUDTERM_2_LEFT 0b00100000 | |||||
#define AUDTERM_1_LEFT 0b00010000 | |||||
// SO1 | |||||
#define AUDTERM_4_RIGHT 0b00001000 | |||||
#define AUDTERM_3_RIGHT 0b00000100 | |||||
#define AUDTERM_2_RIGHT 0b00000010 | |||||
#define AUDTERM_1_RIGHT 0b00000001 | |||||
// AUDENA/NR52 ($FF26) | |||||
// Sound on/off (R/W) | |||||
// | |||||
// Bit 7 - All sound on/off (sets all audio regs to 0!) | |||||
// Bit 3 - Sound 4 ON flag (read only) | |||||
// Bit 2 - Sound 3 ON flag (read only) | |||||
// Bit 1 - Sound 2 ON flag (read only) | |||||
// Bit 0 - Sound 1 ON flag (read only) | |||||
static volatile __sfr __at(0x26) rNR52; | |||||
#define rAUDENA rNR52 | |||||
#define AUDENA_ON 0b10000000 | |||||
#define AUDENA_OFF 0b00000000 // sets all audio regs to 0! | |||||
// LCDC ($FF40) | |||||
// LCD Control (R/W) | |||||
static volatile __sfr __at(0x40) rLCDC; | |||||
#define LCDC_OFF 0b00000000 // LCD Control Operation | |||||
#define LCDC_ON 0b10000000 // LCD Control Operation | |||||
#define LCDC_WIN9800 0b00000000 // Window Tile Map Display Select | |||||
#define LCDC_WIN9C00 0b01000000 // Window Tile Map Display Select | |||||
#define LCDC_WINOFF 0b00000000 // Window Display | |||||
#define LCDC_WINON 0b00100000 // Window Display | |||||
#define LCDC_BG8800 0b00000000 // BG & Window Tile Data Select | |||||
#define LCDC_BG8000 0b00010000 // BG & Window Tile Data Select | |||||
#define LCDC_BG9800 0b00000000 // BG Tile Map Display Select | |||||
#define LCDC_BG9C00 0b00001000 // BG Tile Map Display Select | |||||
#define LCDC_OBJ8 0b00000000 // OBJ Construction | |||||
#define LCDC_OBJ16 0b00000100 // OBJ Construction | |||||
#define LCDC_OBJOFF 0b00000000 // OBJ Display | |||||
#define LCDC_OBJON 0b00000010 // OBJ Display | |||||
#define LCDC_BGOFF 0b00000000 // BG Display | |||||
#define LCDC_BGON 0b00000001 // BG Display | |||||
// STAT ($FF41) | |||||
// LCDC Status (R/W) | |||||
static volatile __sfr __at(0x41) rSTAT; | |||||
#define STAT_LYC 0b01000000 // LYC=LY Coincidence (Selectable) | |||||
#define STAT_MODE10 0b00100000 // Mode 10 | |||||
#define STAT_MODE01 0b00010000 // Mode 01 (V-Blank) | |||||
#define STAT_MODE00 0b00001000 // Mode 00 (H-Blank) | |||||
#define STAT_LYCF 0b00000100 // Coincidence Flag | |||||
#define STAT_HBL 0b00000000 // H-Blank | |||||
#define STAT_VBL 0b00000001 // V-Blank | |||||
#define STAT_OAM 0b00000010 // OAM-RAM is used by system | |||||
#define STAT_LCD 0b00000011 // Both OAM and VRAM used by system | |||||
#define STAT_BUSY 0b00000010 // When set, VRAM access is unsafe | |||||
// SCY ($FF42) | |||||
// Scroll Y (R/W) | |||||
static volatile __sfr __at(0x42) rSCY; | |||||
// SCX ($FF43) | |||||
// Scroll X (R/W) | |||||
static volatile __sfr __at(0x43) rSCX; | |||||
// LY ($FF44) | |||||
// LCDC Y-Coordinate (R) | |||||
// | |||||
// Values range from 0->153. 144->153 is the VBlank period. | |||||
static volatile __sfr __at(0x44) rLY; | |||||
// LYC ($FF45) | |||||
// LY Compare (R/W) | |||||
// | |||||
// When LY==LYC, STATF_LYCF will be set in STAT | |||||
static volatile __sfr __at(0x45) rLYC; | |||||
// DMA ($FF46) | |||||
// DMA Transfer and Start Address (W) | |||||
static volatile __sfr __at(0x46) rDMA; | |||||
// BGP ($FF47) | |||||
// BG Palette Data (W) | |||||
// | |||||
// Bit 7-6 - Intensity for %11 | |||||
// Bit 5-4 - Intensity for %10 | |||||
// Bit 3-2 - Intensity for %01 | |||||
// Bit 1-0 - Intensity for %00 | |||||
static volatile __sfr __at(0x47) rBGP; | |||||
// OBP0 ($FF48) | |||||
// Object Palette 0 Data (W) | |||||
// | |||||
// See BGP for info | |||||
static volatile __sfr __at(0x48) rOBP0; | |||||
// OBP1 ($FF49) | |||||
// Object Palette 1 Data (W) | |||||
// | |||||
// See BGP for info | |||||
static volatile __sfr __at(0x49) rOBP1; | |||||
// WY ($FF4A) | |||||
// Window Y Position (R/W) | |||||
// | |||||
// 0 <= WY <= 143 | |||||
// When WY = 0, the window is displayed from the top edge of the LCD screen. | |||||
static volatile __sfr __at(0x4A) rWY; | |||||
// WX ($FF4B) | |||||
// Window X Position (R/W) | |||||
// | |||||
// 7 <= WX <= 166 | |||||
// When WX = 7, the window is displayed from the left edge of the LCD screen. | |||||
// Values of 0-6 and 166 are unreliable due to hardware bugs. | |||||
static volatile __sfr __at(0x4B) rWX; | |||||
#if CGB | |||||
// SPEED ($FF4D) | |||||
// Select CPU Speed (R/W) | |||||
static volatile __sfr __at(0x4D) rKEY1; | |||||
#define rSPD rKEY1 | |||||
#define KEY1_DBLSPEED 0b10000000 // 0=Normal Speed, 1=Double Speed (R) | |||||
#define KEY1_PREPARE 0b00000001 // 0=No, 1=Prepare (R/W) | |||||
// VBK ($FF4F) | |||||
// Select Video RAM Bank (R/W) | |||||
// | |||||
// Bit 0 - Bank Specification (0: Specify Bank 0; 1: Specify Bank 1) | |||||
static volatile __sfr __at(0x4F) rVBK; | |||||
// HDMA1 ($FF51) | |||||
// High byte for Horizontal Blanking/General Purpose DMA source address (W) | |||||
static volatile __sfr __at(0x51) rHDMA1; | |||||
// HDMA2 ($FF52) | |||||
// Low byte for Horizontal Blanking/General Purpose DMA source address (W) | |||||
static volatile __sfr __at(0x52) rHDMA2; | |||||
// HDMA3 ($FF53) | |||||
// High byte for Horizontal Blanking/General Purpose DMA destination address (W) | |||||
static volatile __sfr __at(0x53) rHDMA3; | |||||
// HDMA4 ($FF54) | |||||
// Low byte for Horizontal Blanking/General Purpose DMA destination address (W) | |||||
static volatile __sfr __at(0x54) rHDMA4; | |||||
// HDMA5 ($FF55) | |||||
// Transfer length (in tiles minus 1)/mode/start for Horizontal Blanking, General Purpose DMA (R/W) | |||||
static volatile __sfr __at(0x55) rHDMA5; | |||||
#define HDMA5_MODE_GP 0b00000000 // General Purpose DMA (W) | |||||
#define HDMA5_MODE_HBL 0b10000000 // HBlank DMA (W) | |||||
// Once DMA has started, use HDMA5F_BUSY to check when the transfer is complete | |||||
#define HDMA5_BUSY 0b10000000 // 0=Busy (DMA still in progress), 1=Transfer complete (R) | |||||
// RP ($FF56) | |||||
// Infrared Communications Port (R/W) | |||||
static volatile __sfr __at(0x56) rRP; | |||||
#define RP_ENREAD 0b11000000 | |||||
#define RP_DATAIN 0b00000010 // 0=Receiving IR Signal, 1=Normal | |||||
#define RP_WRITE_HI 0b00000001 | |||||
#define RP_WRITE_LO 0b00000000 | |||||
// BCPS ($FF68) | |||||
// Background Color Palette Specification (R/W) | |||||
static volatile __sfr __at(0x68) rBCPS; | |||||
#define BCPS_AUTOINC 0b10000000 // Auto Increment (0=Disabled, 1=Increment after Writing) | |||||
// BCPD ($FF69) | |||||
// Background Color Palette Data (R/W) | |||||
static volatile __sfr __at(0x69) rBCPD; | |||||
// OCPS ($FF6A) | |||||
// Object Color Palette Specification (R/W) | |||||
static volatile __sfr __at(0x6A) rOCPS; | |||||
#define OCPS_AUTOINC 0b10000000 // Auto Increment (0=Disabled, 1=Increment after Writing) | |||||
// OCPD ($FF6B) | |||||
// Object Color Palette Data (R/W) | |||||
static volatile __sfr __at(0x6B) rOCPD; | |||||
// SMBK/SVBK ($FF70) | |||||
// Select Main RAM Bank (R/W) | |||||
// | |||||
// Bit 2-0 - Bank Specification (0,1: Specify Bank 1; 2-7: Specify Banks 2-7) | |||||
static volatile __sfr __at(0x70) rSVBK; | |||||
#define rSMBK rSVBK | |||||
// PCM12 ($FF76) | |||||
// Sound channel 1&2 PCM amplitude (R) | |||||
// | |||||
// Bit 7-4 - Copy of sound channel 2's PCM amplitude | |||||
// Bit 3-0 - Copy of sound channel 1's PCM amplitude | |||||
static volatile __sfr __at(0x76) rPCM12; | |||||
// PCM34 ($FF77) | |||||
// Sound channel 3&4 PCM amplitude (R) | |||||
// | |||||
// Bit 7-4 - Copy of sound channel 4's PCM amplitude | |||||
// Bit 3-0 - Copy of sound channel 3's PCM amplitude | |||||
static volatile __sfr __at(0x77) rPCM34; | |||||
#endif //CGB | |||||
// IE ($FFFF) | |||||
// Interrupt Enable (R/W) | |||||
static volatile __sfr __at(0xFF) rIE; | |||||
#define IE_HILO 0b00010000 // Transition from High to Low of Pin number P10-P13 | |||||
#define IE_SERIAL 0b00001000 // Serial I/O transfer end | |||||
#define IE_TIMER 0b00000100 // Timer Overflow | |||||
#define IE_LCDC 0b00000010 // LCDC (see STAT) | |||||
#define IE_VBLANK 0b00000001 // V-Blank | |||||
/*************************************************************************** | |||||
* | |||||
* Flags common to multiple sound channels | |||||
* | |||||
***************************************************************************/ | |||||
// Square wave duty cycle | |||||
// | |||||
// Can be used with AUD1LEN and AUD2LEN | |||||
// See AUD1LEN for more info | |||||
#define AUDLEN_DUTY_12_5 0b00000000 // 12.5% | |||||
#define AUDLEN_DUTY_25 0b01000000 // 25% | |||||
#define AUDLEN_DUTY_50 0b10000000 // 50% | |||||
#define AUDLEN_DUTY_75 0b11000000 // 75% | |||||
// Audio envelope flags | |||||
// | |||||
// Can be used with AUD1ENV, AUD2ENV, AUD4ENV | |||||
// See AUD1ENV for more info | |||||
#define AUDENV_UP 0b00001000 | |||||
#define AUDENV_DOWN 0b00000000 | |||||
// Audio trigger flags | |||||
// | |||||
// Can be used with AUD1HIGH, AUD2HIGH, AUD3HIGH | |||||
// See AUD1HIGH for more info | |||||
#define AUDHIGH_RESTART 0b10000000 | |||||
#define AUDHIGH_LENGTH_ON 0b01000000 | |||||
#define AUDHIGH_LENGTH_OFF 0b00000000 | |||||
// Shared bits between the OAM attributes and the CGB background attributes. | |||||
#define ATTR_PRI 0b10000000 | |||||
#define ATTR_YFLIP 0b01000000 | |||||
#define ATTR_XFLIP 0b00100000 | |||||
#define ATTR_PAL0 0b00000000 | |||||
#define ATTR_PAL1 0b00010000 | |||||
#if CGB | |||||
#define ATTR_BANK0 0b00000000 | |||||
#define ATTR_BANK1 0b00001000 | |||||
#define ATTR_PALMASK 0b00000111 | |||||
#endif | |||||
// Defines for specific instructions | |||||
#define HALT() __asm__("halt") | |||||
#define DISABLE_INTERRUPTS() __asm__("di") | |||||
#define ENABLE_INTERRUPTS() __asm__("ei") | |||||
#endif |
@ -0,0 +1,913 @@ | |||||
;* | |||||
;* Gameboy Hardware definitions | |||||
;* | |||||
;* Based on Jones' hardware.inc | |||||
;* And based on Carsten Sorensen's ideas. | |||||
;* | |||||
;* Rev 1.1 - 15-Jul-97 : Added define check | |||||
;* Rev 1.2 - 18-Jul-97 : Added revision check macro | |||||
;* Rev 1.3 - 19-Jul-97 : Modified for RGBASM V1.05 | |||||
;* Rev 1.4 - 27-Jul-97 : Modified for new subroutine prefixes | |||||
;* Rev 1.5 - 15-Aug-97 : Added _HRAM, PAD, CART defines | |||||
;* : and Nintendo Logo | |||||
;* Rev 1.6 - 30-Nov-97 : Added rDIV, rTIMA, rTMA, & rTAC | |||||
;* Rev 1.7 - 31-Jan-98 : Added _SCRN0, _SCRN1 | |||||
;* Rev 1.8 - 15-Feb-98 : Added rSB, rSC | |||||
;* Rev 1.9 - 16-Feb-98 : Converted I/O registers to $FFXX format | |||||
;* Rev 2.0 - : Added GBC registers | |||||
;* Rev 2.1 - : Added MBC5 & cart RAM enable/disable defines | |||||
;* Rev 2.2 - : Fixed NR42,NR43, & NR44 equates | |||||
;* Rev 2.3 - : Fixed incorrect _HRAM equate | |||||
;* Rev 2.4 - 27-Apr-13 : Added some cart defines (AntonioND) | |||||
;* Rev 2.5 - 03-May-15 : Fixed format (AntonioND) | |||||
;* Rev 2.6 - 09-Apr-16 : Added GBC OAM and cart defines (AntonioND) | |||||
;* Rev 2.7 - 19-Jan-19 : Added rPCMXX (ISSOtm) | |||||
;* Rev 2.8 - 03-Feb-19 : Added audio registers flags (�lvaro Cuesta) | |||||
;* Rev 2.9 - 28-Feb-20 : Added utility rP1 constants | |||||
;* Rev 3.0 - 27-Aug-20 : Register ordering, byte-based sizes, OAM additions, general cleanup (Blitter Object) | |||||
; If all of these are already defined, don't do it again. | |||||
IF !DEF(HARDWARE_INC) | |||||
HARDWARE_INC SET 1 | |||||
rev_Check_hardware_inc : MACRO | |||||
;NOTE: REVISION NUMBER CHANGES MUST BE ADDED | |||||
;TO SECOND PARAMETER IN FOLLOWING LINE. | |||||
IF \1 > 3.0 ;PUT REVISION NUMBER HERE | |||||
WARN "Version \1 or later of 'hardware.inc' is required." | |||||
ENDC | |||||
ENDM | |||||
_VRAM EQU $8000 ; $8000->$9FFF | |||||
_VRAM8000 EQU _VRAM | |||||
_VRAM8800 EQU _VRAM+$800 | |||||
_VRAM9000 EQU _VRAM+$1000 | |||||
_SCRN0 EQU $9800 ; $9800->$9BFF | |||||
_SCRN1 EQU $9C00 ; $9C00->$9FFF | |||||
_SRAM EQU $A000 ; $A000->$BFFF | |||||
_RAM EQU $C000 ; $C000->$CFFF / $C000->$DFFF | |||||
_RAMBANK EQU $D000 ; $D000->$DFFF | |||||
_OAMRAM EQU $FE00 ; $FE00->$FE9F | |||||
_IO EQU $FF00 ; $FF00->$FF7F,$FFFF | |||||
_AUD3WAVERAM EQU $FF30 ; $FF30->$FF3F | |||||
_HRAM EQU $FF80 ; $FF80->$FFFE | |||||
; *** MBC5 Equates *** | |||||
rRAMG EQU $0000 ; $0000->$1fff | |||||
rROMB0 EQU $2000 ; $2000->$2fff | |||||
rROMB1 EQU $3000 ; $3000->$3fff - If more than 256 ROM banks are present. | |||||
rRAMB EQU $4000 ; $4000->$5fff - Bit 3 enables rumble (if present) | |||||
;*************************************************************************** | |||||
;* | |||||
;* Custom registers | |||||
;* | |||||
;*************************************************************************** | |||||
; -- | |||||
; -- P1 ($FF00) | |||||
; -- Register for reading joy pad info. (R/W) | |||||
; -- | |||||
rP1 EQU $FF00 | |||||
P1F_5 EQU %00100000 ; P15 out port, set to 0 to get buttons | |||||
P1F_4 EQU %00010000 ; P14 out port, set to 0 to get dpad | |||||
P1F_3 EQU %00001000 ; P13 in port | |||||
P1F_2 EQU %00000100 ; P12 in port | |||||
P1F_1 EQU %00000010 ; P11 in port | |||||
P1F_0 EQU %00000001 ; P10 in port | |||||
P1F_GET_DPAD EQU P1F_5 | |||||
P1F_GET_BTN EQU P1F_4 | |||||
P1F_GET_NONE EQU P1F_4 | P1F_5 | |||||
; -- | |||||
; -- SB ($FF01) | |||||
; -- Serial Transfer Data (R/W) | |||||
; -- | |||||
rSB EQU $FF01 | |||||
; -- | |||||
; -- SC ($FF02) | |||||
; -- Serial I/O Control (R/W) | |||||
; -- | |||||
rSC EQU $FF02 | |||||
; -- | |||||
; -- DIV ($FF04) | |||||
; -- Divider register (R/W) | |||||
; -- | |||||
rDIV EQU $FF04 | |||||
; -- | |||||
; -- TIMA ($FF05) | |||||
; -- Timer counter (R/W) | |||||
; -- | |||||
rTIMA EQU $FF05 | |||||
; -- | |||||
; -- TMA ($FF06) | |||||
; -- Timer modulo (R/W) | |||||
; -- | |||||
rTMA EQU $FF06 | |||||
; -- | |||||
; -- TAC ($FF07) | |||||
; -- Timer control (R/W) | |||||
; -- | |||||
rTAC EQU $FF07 | |||||
TACF_START EQU %00000100 | |||||
TACF_STOP EQU %00000000 | |||||
TACF_4KHZ EQU %00000000 | |||||
TACF_16KHZ EQU %00000011 | |||||
TACF_65KHZ EQU %00000010 | |||||
TACF_262KHZ EQU %00000001 | |||||
; -- | |||||
; -- IF ($FF0F) | |||||
; -- Interrupt Flag (R/W) | |||||
; -- | |||||
rIF EQU $FF0F | |||||
; -- | |||||
; -- AUD1SWEEP/NR10 ($FF10) | |||||
; -- Sweep register (R/W) | |||||
; -- | |||||
; -- Bit 6-4 - Sweep Time | |||||
; -- Bit 3 - Sweep Increase/Decrease | |||||
; -- 0: Addition (frequency increases???) | |||||
; -- 1: Subtraction (frequency increases???) | |||||
; -- Bit 2-0 - Number of sweep shift (# 0-7) | |||||
; -- Sweep Time: (n*7.8ms) | |||||
; -- | |||||
rNR10 EQU $FF10 | |||||
rAUD1SWEEP EQU rNR10 | |||||
AUD1SWEEP_UP EQU %00000000 | |||||
AUD1SWEEP_DOWN EQU %00001000 | |||||
; -- | |||||
; -- AUD1LEN/NR11 ($FF11) | |||||
; -- Sound length/Wave pattern duty (R/W) | |||||
; -- | |||||
; -- Bit 7-6 - Wave Pattern Duty (00:12.5% 01:25% 10:50% 11:75%) | |||||
; -- Bit 5-0 - Sound length data (# 0-63) | |||||
; -- | |||||
rNR11 EQU $FF11 | |||||
rAUD1LEN EQU rNR11 | |||||
; -- | |||||
; -- AUD1ENV/NR12 ($FF12) | |||||
; -- Envelope (R/W) | |||||
; -- | |||||
; -- Bit 7-4 - Initial value of envelope | |||||
; -- Bit 3 - Envelope UP/DOWN | |||||
; -- 0: Decrease | |||||
; -- 1: Range of increase | |||||
; -- Bit 2-0 - Number of envelope sweep (# 0-7) | |||||
; -- | |||||
rNR12 EQU $FF12 | |||||
rAUD1ENV EQU rNR12 | |||||
; -- | |||||
; -- AUD1LOW/NR13 ($FF13) | |||||
; -- Frequency low byte (W) | |||||
; -- | |||||
rNR13 EQU $FF13 | |||||
rAUD1LOW EQU rNR13 | |||||
; -- | |||||
; -- AUD1HIGH/NR14 ($FF14) | |||||
; -- Frequency high byte (W) | |||||
; -- | |||||
; -- Bit 7 - Initial (when set, sound restarts) | |||||
; -- Bit 6 - Counter/consecutive selection | |||||
; -- Bit 2-0 - Frequency's higher 3 bits | |||||
; -- | |||||
rNR14 EQU $FF14 | |||||
rAUD1HIGH EQU rNR14 | |||||
; -- | |||||
; -- AUD2LEN/NR21 ($FF16) | |||||
; -- Sound Length; Wave Pattern Duty (R/W) | |||||
; -- | |||||
; -- see AUD1LEN for info | |||||
; -- | |||||
rNR21 EQU $FF16 | |||||
rAUD2LEN EQU rNR21 | |||||
; -- | |||||
; -- AUD2ENV/NR22 ($FF17) | |||||
; -- Envelope (R/W) | |||||
; -- | |||||
; -- see AUD1ENV for info | |||||
; -- | |||||
rNR22 EQU $FF17 | |||||
rAUD2ENV EQU rNR22 | |||||
; -- | |||||
; -- AUD2LOW/NR23 ($FF18) | |||||
; -- Frequency low byte (W) | |||||
; -- | |||||
rNR23 EQU $FF18 | |||||
rAUD2LOW EQU rNR23 | |||||
; -- | |||||
; -- AUD2HIGH/NR24 ($FF19) | |||||
; -- Frequency high byte (W) | |||||
; -- | |||||
; -- see AUD1HIGH for info | |||||
; -- | |||||
rNR24 EQU $FF19 | |||||
rAUD2HIGH EQU rNR24 | |||||
; -- | |||||
; -- AUD3ENA/NR30 ($FF1A) | |||||
; -- Sound on/off (R/W) | |||||
; -- | |||||
; -- Bit 7 - Sound ON/OFF (1=ON,0=OFF) | |||||
; -- | |||||
rNR30 EQU $FF1A | |||||
rAUD3ENA EQU rNR30 | |||||
; -- | |||||
; -- AUD3LEN/NR31 ($FF1B) | |||||
; -- Sound length (R/W) | |||||
; -- | |||||
; -- Bit 7-0 - Sound length | |||||
; -- | |||||
rNR31 EQU $FF1B | |||||
rAUD3LEN EQU rNR31 | |||||
; -- | |||||
; -- AUD3LEVEL/NR32 ($FF1C) | |||||
; -- Select output level | |||||
; -- | |||||
; -- Bit 6-5 - Select output level | |||||
; -- 00: 0/1 (mute) | |||||
; -- 01: 1/1 | |||||
; -- 10: 1/2 | |||||
; -- 11: 1/4 | |||||
; -- | |||||
rNR32 EQU $FF1C | |||||
rAUD3LEVEL EQU rNR32 | |||||
; -- | |||||
; -- AUD3LOW/NR33 ($FF1D) | |||||
; -- Frequency low byte (W) | |||||
; -- | |||||
; -- see AUD1LOW for info | |||||
; -- | |||||
rNR33 EQU $FF1D | |||||
rAUD3LOW EQU rNR33 | |||||
; -- | |||||
; -- AUD3HIGH/NR34 ($FF1E) | |||||
; -- Frequency high byte (W) | |||||
; -- | |||||
; -- see AUD1HIGH for info | |||||
; -- | |||||
rNR34 EQU $FF1E | |||||
rAUD3HIGH EQU rNR34 | |||||
; -- | |||||
; -- AUD4LEN/NR41 ($FF20) | |||||
; -- Sound length (R/W) | |||||
; -- | |||||
; -- Bit 5-0 - Sound length data (# 0-63) | |||||
; -- | |||||
rNR41 EQU $FF20 | |||||
rAUD4LEN EQU rNR41 | |||||
; -- | |||||
; -- AUD4ENV/NR42 ($FF21) | |||||
; -- Envelope (R/W) | |||||
; -- | |||||
; -- see AUD1ENV for info | |||||
; -- | |||||
rNR42 EQU $FF21 | |||||
rAUD4ENV EQU rNR42 | |||||
; -- | |||||
; -- AUD4POLY/NR43 ($FF22) | |||||
; -- Polynomial counter (R/W) | |||||
; -- | |||||
; -- Bit 7-4 - Selection of the shift clock frequency of the (scf) | |||||
; -- polynomial counter (0000-1101) | |||||
; -- freq=drf*1/2^scf (not sure) | |||||
; -- Bit 3 - Selection of the polynomial counter's step | |||||
; -- 0: 15 steps | |||||
; -- 1: 7 steps | |||||
; -- Bit 2-0 - Selection of the dividing ratio of frequencies (drf) | |||||
; -- 000: f/4 001: f/8 010: f/16 011: f/24 | |||||
; -- 100: f/32 101: f/40 110: f/48 111: f/56 (f=4.194304 Mhz) | |||||
; -- | |||||
rNR43 EQU $FF22 | |||||
rAUD4POLY EQU rNR43 | |||||
; -- | |||||
; -- AUD4GO/NR44 ($FF23) | |||||
; -- | |||||
; -- Bit 7 - Inital | |||||
; -- Bit 6 - Counter/consecutive selection | |||||
; -- | |||||
rNR44 EQU $FF23 | |||||
rAUD4GO EQU rNR44 | |||||
; -- | |||||
; -- AUDVOL/NR50 ($FF24) | |||||
; -- Channel control / ON-OFF / Volume (R/W) | |||||
; -- | |||||
; -- Bit 7 - Vin->SO2 ON/OFF (Vin??) | |||||
; -- Bit 6-4 - SO2 output level (volume) (# 0-7) | |||||
; -- Bit 3 - Vin->SO1 ON/OFF (Vin??) | |||||
; -- Bit 2-0 - SO1 output level (volume) (# 0-7) | |||||
; -- | |||||
rNR50 EQU $FF24 | |||||
rAUDVOL EQU rNR50 | |||||
AUDVOL_VIN_LEFT EQU %10000000 ; SO2 | |||||
AUDVOL_VIN_RIGHT EQU %00001000 ; SO1 | |||||
; -- | |||||
; -- AUDTERM/NR51 ($FF25) | |||||
; -- Selection of Sound output terminal (R/W) | |||||
; -- | |||||
; -- Bit 7 - Output sound 4 to SO2 terminal | |||||
; -- Bit 6 - Output sound 3 to SO2 terminal | |||||
; -- Bit 5 - Output sound 2 to SO2 terminal | |||||
; -- Bit 4 - Output sound 1 to SO2 terminal | |||||
; -- Bit 3 - Output sound 4 to SO1 terminal | |||||
; -- Bit 2 - Output sound 3 to SO1 terminal | |||||
; -- Bit 1 - Output sound 2 to SO1 terminal | |||||
; -- Bit 0 - Output sound 0 to SO1 terminal | |||||
; -- | |||||
rNR51 EQU $FF25 | |||||
rAUDTERM EQU rNR51 | |||||
; SO2 | |||||
AUDTERM_4_LEFT EQU %10000000 | |||||
AUDTERM_3_LEFT EQU %01000000 | |||||
AUDTERM_2_LEFT EQU %00100000 | |||||
AUDTERM_1_LEFT EQU %00010000 | |||||
; SO1 | |||||
AUDTERM_4_RIGHT EQU %00001000 | |||||
AUDTERM_3_RIGHT EQU %00000100 | |||||
AUDTERM_2_RIGHT EQU %00000010 | |||||
AUDTERM_1_RIGHT EQU %00000001 | |||||
; -- | |||||
; -- AUDENA/NR52 ($FF26) | |||||
; -- Sound on/off (R/W) | |||||
; -- | |||||
; -- Bit 7 - All sound on/off (sets all audio regs to 0!) | |||||
; -- Bit 3 - Sound 4 ON flag (read only) | |||||
; -- Bit 2 - Sound 3 ON flag (read only) | |||||
; -- Bit 1 - Sound 2 ON flag (read only) | |||||
; -- Bit 0 - Sound 1 ON flag (read only) | |||||
; -- | |||||
rNR52 EQU $FF26 | |||||
rAUDENA EQU rNR52 | |||||
AUDENA_ON EQU %10000000 | |||||
AUDENA_OFF EQU %00000000 ; sets all audio regs to 0! | |||||
; -- | |||||
; -- LCDC ($FF40) | |||||
; -- LCD Control (R/W) | |||||
; -- | |||||
rLCDC EQU $FF40 | |||||
LCDCF_OFF EQU %00000000 ; LCD Control Operation | |||||
LCDCF_ON EQU %10000000 ; LCD Control Operation | |||||
LCDCF_WIN9800 EQU %00000000 ; Window Tile Map Display Select | |||||
LCDCF_WIN9C00 EQU %01000000 ; Window Tile Map Display Select | |||||
LCDCF_WINOFF EQU %00000000 ; Window Display | |||||
LCDCF_WINON EQU %00100000 ; Window Display | |||||
LCDCF_BG8800 EQU %00000000 ; BG & Window Tile Data Select | |||||
LCDCF_BG8000 EQU %00010000 ; BG & Window Tile Data Select | |||||
LCDCF_BG9800 EQU %00000000 ; BG Tile Map Display Select | |||||
LCDCF_BG9C00 EQU %00001000 ; BG Tile Map Display Select | |||||
LCDCF_OBJ8 EQU %00000000 ; OBJ Construction | |||||
LCDCF_OBJ16 EQU %00000100 ; OBJ Construction | |||||
LCDCF_OBJOFF EQU %00000000 ; OBJ Display | |||||
LCDCF_OBJON EQU %00000010 ; OBJ Display | |||||
LCDCF_BGOFF EQU %00000000 ; BG Display | |||||
LCDCF_BGON EQU %00000001 ; BG Display | |||||
; "Window Character Data Select" follows BG | |||||
; -- | |||||
; -- STAT ($FF41) | |||||
; -- LCDC Status (R/W) | |||||
; -- | |||||
rSTAT EQU $FF41 | |||||
STATF_LYC EQU %01000000 ; LYC=LY Coincidence (Selectable) | |||||
STATF_MODE10 EQU %00100000 ; Mode 10 | |||||
STATF_MODE01 EQU %00010000 ; Mode 01 (V-Blank) | |||||
STATF_MODE00 EQU %00001000 ; Mode 00 (H-Blank) | |||||
STATF_LYCF EQU %00000100 ; Coincidence Flag | |||||
STATF_HBL EQU %00000000 ; H-Blank | |||||
STATF_VBL EQU %00000001 ; V-Blank | |||||
STATF_OAM EQU %00000010 ; OAM-RAM is used by system | |||||
STATF_LCD EQU %00000011 ; Both OAM and VRAM used by system | |||||
STATF_BUSY EQU %00000010 ; When set, VRAM access is unsafe | |||||
; -- | |||||
; -- SCY ($FF42) | |||||
; -- Scroll Y (R/W) | |||||
; -- | |||||
rSCY EQU $FF42 | |||||
; -- | |||||
; -- SCX ($FF43) | |||||
; -- Scroll X (R/W) | |||||
; -- | |||||
rSCX EQU $FF43 | |||||
; -- | |||||
; -- LY ($FF44) | |||||
; -- LCDC Y-Coordinate (R) | |||||
; -- | |||||
; -- Values range from 0->153. 144->153 is the VBlank period. | |||||
; -- | |||||
rLY EQU $FF44 | |||||
; -- | |||||
; -- LYC ($FF45) | |||||
; -- LY Compare (R/W) | |||||
; -- | |||||
; -- When LY==LYC, STATF_LYCF will be set in STAT | |||||
; -- | |||||
rLYC EQU $FF45 | |||||
; -- | |||||
; -- DMA ($FF46) | |||||
; -- DMA Transfer and Start Address (W) | |||||
; -- | |||||
rDMA EQU $FF46 | |||||
; -- | |||||
; -- BGP ($FF47) | |||||
; -- BG Palette Data (W) | |||||
; -- | |||||
; -- Bit 7-6 - Intensity for %11 | |||||
; -- Bit 5-4 - Intensity for %10 | |||||
; -- Bit 3-2 - Intensity for %01 | |||||
; -- Bit 1-0 - Intensity for %00 | |||||
; -- | |||||
rBGP EQU $FF47 | |||||
; -- | |||||
; -- OBP0 ($FF48) | |||||
; -- Object Palette 0 Data (W) | |||||
; -- | |||||
; -- See BGP for info | |||||
; -- | |||||
rOBP0 EQU $FF48 | |||||
; -- | |||||
; -- OBP1 ($FF49) | |||||
; -- Object Palette 1 Data (W) | |||||
; -- | |||||
; -- See BGP for info | |||||
; -- | |||||
rOBP1 EQU $FF49 | |||||
; -- | |||||
; -- WY ($FF4A) | |||||
; -- Window Y Position (R/W) | |||||
; -- | |||||
; -- 0 <= WY <= 143 | |||||
; -- When WY = 0, the window is displayed from the top edge of the LCD screen. | |||||
; -- | |||||
rWY EQU $FF4A | |||||
; -- | |||||
; -- WX ($FF4B) | |||||
; -- Window X Position (R/W) | |||||
; -- | |||||
; -- 7 <= WX <= 166 | |||||
; -- When WX = 7, the window is displayed from the left edge of the LCD screen. | |||||
; -- Values of 0-6 and 166 are unreliable due to hardware bugs. | |||||
; -- | |||||
rWX EQU $FF4B | |||||
; -- | |||||
; -- SPEED ($FF4D) | |||||
; -- Select CPU Speed (R/W) | |||||
; -- | |||||
rKEY1 EQU $FF4D | |||||
rSPD EQU rKEY1 | |||||
KEY1F_DBLSPEED EQU %10000000 ; 0=Normal Speed, 1=Double Speed (R) | |||||
KEY1F_PREPARE EQU %00000001 ; 0=No, 1=Prepare (R/W) | |||||
; -- | |||||
; -- VBK ($FF4F) | |||||
; -- Select Video RAM Bank (R/W) | |||||
; -- | |||||
; -- Bit 0 - Bank Specification (0: Specify Bank 0; 1: Specify Bank 1) | |||||
; -- | |||||
rVBK EQU $FF4F | |||||
; -- | |||||
; -- HDMA1 ($FF51) | |||||
; -- High byte for Horizontal Blanking/General Purpose DMA source address (W) | |||||
; -- CGB Mode Only | |||||
; -- | |||||
rHDMA1 EQU $FF51 | |||||
; -- | |||||
; -- HDMA2 ($FF52) | |||||
; -- Low byte for Horizontal Blanking/General Purpose DMA source address (W) | |||||
; -- CGB Mode Only | |||||
; -- | |||||
rHDMA2 EQU $FF52 | |||||
; -- | |||||
; -- HDMA3 ($FF53) | |||||
; -- High byte for Horizontal Blanking/General Purpose DMA destination address (W) | |||||
; -- CGB Mode Only | |||||
; -- | |||||
rHDMA3 EQU $FF53 | |||||
; -- | |||||
; -- HDMA4 ($FF54) | |||||
; -- Low byte for Horizontal Blanking/General Purpose DMA destination address (W) | |||||
; -- CGB Mode Only | |||||
; -- | |||||
rHDMA4 EQU $FF54 | |||||
; -- | |||||
; -- HDMA5 ($FF55) | |||||
; -- Transfer length (in tiles minus 1)/mode/start for Horizontal Blanking, General Purpose DMA (R/W) | |||||
; -- CGB Mode Only | |||||
; -- | |||||
rHDMA5 EQU $FF55 | |||||
HDMA5F_MODE_GP EQU %00000000 ; General Purpose DMA (W) | |||||
HDMA5F_MODE_HBL EQU %10000000 ; HBlank DMA (W) | |||||
; -- Once DMA has started, use HDMA5F_BUSY to check when the transfer is complete | |||||
HDMA5F_BUSY EQU %10000000 ; 0=Busy (DMA still in progress), 1=Transfer complete (R) | |||||
; -- | |||||
; -- RP ($FF56) | |||||
; -- Infrared Communications Port (R/W) | |||||
; -- CGB Mode Only | |||||
; -- | |||||
rRP EQU $FF56 | |||||
RPF_ENREAD EQU %11000000 | |||||
RPF_DATAIN EQU %00000010 ; 0=Receiving IR Signal, 1=Normal | |||||
RPF_WRITE_HI EQU %00000001 | |||||
RPF_WRITE_LO EQU %00000000 | |||||
; -- | |||||
; -- BCPS ($FF68) | |||||
; -- Background Color Palette Specification (R/W) | |||||
; -- | |||||
rBCPS EQU $FF68 | |||||
BCPSF_AUTOINC EQU %10000000 ; Auto Increment (0=Disabled, 1=Increment after Writing) | |||||
; -- | |||||
; -- BCPD ($FF69) | |||||
; -- Background Color Palette Data (R/W) | |||||
; -- | |||||
rBCPD EQU $FF69 | |||||
; -- | |||||
; -- OCPS ($FF6A) | |||||
; -- Object Color Palette Specification (R/W) | |||||
; -- | |||||
rOCPS EQU $FF6A | |||||
OCPSF_AUTOINC EQU %10000000 ; Auto Increment (0=Disabled, 1=Increment after Writing) | |||||
; -- | |||||
; -- OCPD ($FF6B) | |||||
; -- Object Color Palette Data (R/W) | |||||
; -- | |||||
rOCPD EQU $FF6B | |||||
; -- | |||||
; -- SMBK/SVBK ($FF70) | |||||
; -- Select Main RAM Bank (R/W) | |||||
; -- | |||||
; -- Bit 2-0 - Bank Specification (0,1: Specify Bank 1; 2-7: Specify Banks 2-7) | |||||
; -- | |||||
rSVBK EQU $FF70 | |||||
rSMBK EQU rSVBK | |||||
; -- | |||||
; -- PCM12 ($FF76) | |||||
; -- Sound channel 1&2 PCM amplitude (R) | |||||
; -- | |||||
; -- Bit 7-4 - Copy of sound channel 2's PCM amplitude | |||||
; -- Bit 3-0 - Copy of sound channel 1's PCM amplitude | |||||
; -- | |||||
rPCM12 EQU $FF76 | |||||
; -- | |||||
; -- PCM34 ($FF77) | |||||
; -- Sound channel 3&4 PCM amplitude (R) | |||||
; -- | |||||
; -- Bit 7-4 - Copy of sound channel 4's PCM amplitude | |||||
; -- Bit 3-0 - Copy of sound channel 3's PCM amplitude | |||||
; -- | |||||
rPCM34 EQU $FF77 | |||||
; -- | |||||
; -- IE ($FFFF) | |||||
; -- Interrupt Enable (R/W) | |||||
; -- | |||||
rIE EQU $FFFF | |||||
IEF_HILO EQU %00010000 ; Transition from High to Low of Pin number P10-P13 | |||||
IEF_SERIAL EQU %00001000 ; Serial I/O transfer end | |||||
IEF_TIMER EQU %00000100 ; Timer Overflow | |||||
IEF_LCDC EQU %00000010 ; LCDC (see STAT) | |||||
IEF_VBLANK EQU %00000001 ; V-Blank | |||||
;*************************************************************************** | |||||
;* | |||||
;* Flags common to multiple sound channels | |||||
;* | |||||
;*************************************************************************** | |||||
; -- | |||||
; -- Square wave duty cycle | |||||
; -- | |||||
; -- Can be used with AUD1LEN and AUD2LEN | |||||
; -- See AUD1LEN for more info | |||||
; -- | |||||
AUDLEN_DUTY_12_5 EQU %00000000 ; 12.5% | |||||
AUDLEN_DUTY_25 EQU %01000000 ; 25% | |||||
AUDLEN_DUTY_50 EQU %10000000 ; 50% | |||||
AUDLEN_DUTY_75 EQU %11000000 ; 75% | |||||
; -- | |||||
; -- Audio envelope flags | |||||
; -- | |||||
; -- Can be used with AUD1ENV, AUD2ENV, AUD4ENV | |||||
; -- See AUD1ENV for more info | |||||
; -- | |||||
AUDENV_UP EQU %00001000 | |||||
AUDENV_DOWN EQU %00000000 | |||||
; -- | |||||
; -- Audio trigger flags | |||||
; -- | |||||
; -- Can be used with AUD1HIGH, AUD2HIGH, AUD3HIGH | |||||
; -- See AUD1HIGH for more info | |||||
; -- | |||||
AUDHIGH_RESTART EQU %10000000 | |||||
AUDHIGH_LENGTH_ON EQU %01000000 | |||||
AUDHIGH_LENGTH_OFF EQU %00000000 | |||||
;*************************************************************************** | |||||
;* | |||||
;* CPU values on bootup (a=type, b=qualifier) | |||||
;* | |||||
;*************************************************************************** | |||||
BOOTUP_A_DMG EQU $01 ; Dot Matrix Game | |||||
BOOTUP_A_CGB EQU $11 ; Color GameBoy | |||||
BOOTUP_A_MGB EQU $FF ; Mini GameBoy (Pocket GameBoy) | |||||
; if a=BOOTUP_A_CGB, bit 0 in b can be checked to determine if real CGB or | |||||
; other system running in GBC mode | |||||
BOOTUP_B_CGB EQU %00000000 | |||||
BOOTUP_B_AGB EQU %00000001 ; GBA, GBA SP, Game Boy Player, or New GBA SP | |||||
;*************************************************************************** | |||||
;* | |||||
;* Cart related | |||||
;* | |||||
;*************************************************************************** | |||||
; $0143 Color GameBoy compatibility code | |||||
CART_COMPATIBLE_DMG EQU $00 | |||||
CART_COMPATIBLE_DMG_GBC EQU $80 | |||||
CART_COMPATIBLE_GBC EQU $C0 | |||||
; $0146 GameBoy/Super GameBoy indicator | |||||
CART_INDICATOR_GB EQU $00 | |||||
CART_INDICATOR_SGB EQU $03 | |||||
; $0147 Cartridge type | |||||
CART_ROM EQU $00 | |||||
CART_ROM_MBC1 EQU $01 | |||||
CART_ROM_MBC1_RAM EQU $02 | |||||
CART_ROM_MBC1_RAM_BAT EQU $03 | |||||
CART_ROM_MBC2 EQU $05 | |||||
CART_ROM_MBC2_BAT EQU $06 | |||||
CART_ROM_RAM EQU $08 | |||||
CART_ROM_RAM_BAT EQU $09 | |||||
CART_ROM_MMM01 EQU $0B | |||||
CART_ROM_MMM01_RAM EQU $0C | |||||
CART_ROM_MMM01_RAM_BAT EQU $0D | |||||
CART_ROM_MBC3_BAT_RTC EQU $0F | |||||
CART_ROM_MBC3_RAM_BAT_RTC EQU $10 | |||||
CART_ROM_MBC3 EQU $11 | |||||
CART_ROM_MBC3_RAM EQU $12 | |||||
CART_ROM_MBC3_RAM_BAT EQU $13 | |||||
CART_ROM_MBC5 EQU $19 | |||||
CART_ROM_MBC5_BAT EQU $1A | |||||
CART_ROM_MBC5_RAM_BAT EQU $1B | |||||
CART_ROM_MBC5_RUMBLE EQU $1C | |||||
CART_ROM_MBC5_RAM_RUMBLE EQU $1D | |||||
CART_ROM_MBC5_RAM_BAT_RUMBLE EQU $1E | |||||
CART_ROM_MBC7_RAM_BAT_GYRO EQU $22 | |||||
CART_ROM_POCKET_CAMERA EQU $FC | |||||
CART_ROM_BANDAI_TAMA5 EQU $FD | |||||
CART_ROM_HUDSON_HUC3 EQU $FE | |||||
CART_ROM_HUDSON_HUC1 EQU $FF | |||||
; $0148 ROM size | |||||
; these are kilobytes | |||||
CART_ROM_32KB EQU $00 ; 2 banks | |||||
CART_ROM_64KB EQU $01 ; 4 banks | |||||
CART_ROM_128KB EQU $02 ; 8 banks | |||||
CART_ROM_256KB EQU $03 ; 16 banks | |||||
CART_ROM_512KB EQU $04 ; 32 banks | |||||
CART_ROM_1024KB EQU $05 ; 64 banks | |||||
CART_ROM_2048KB EQU $06 ; 128 banks | |||||
CART_ROM_4096KB EQU $07 ; 256 banks | |||||
CART_ROM_8192KB EQU $08 ; 512 banks | |||||
CART_ROM_1152KB EQU $52 ; 72 banks | |||||
CART_ROM_1280KB EQU $53 ; 80 banks | |||||
CART_ROM_1536KB EQU $54 ; 96 banks | |||||
; $0149 SRAM size | |||||
; these are kilobytes | |||||
CART_SRAM_NONE EQU 0 | |||||
CART_SRAM_2KB EQU 1 ; 1 incomplete bank | |||||
CART_SRAM_8KB EQU 2 ; 1 bank | |||||
CART_SRAM_32KB EQU 3 ; 4 banks | |||||
CART_SRAM_128KB EQU 4 ; 16 banks | |||||
CART_SRAM_ENABLE EQU $0A | |||||
CART_SRAM_DISABLE EQU $00 | |||||
; $014A Destination code | |||||
CART_DEST_JAPANESE EQU $00 | |||||
CART_DEST_NON_JAPANESE EQU $01 | |||||
;*************************************************************************** | |||||
;* | |||||
;* Keypad related | |||||
;* | |||||
;*************************************************************************** | |||||
PADF_DOWN EQU $80 | |||||
PADF_UP EQU $40 | |||||
PADF_LEFT EQU $20 | |||||
PADF_RIGHT EQU $10 | |||||
PADF_START EQU $08 | |||||
PADF_SELECT EQU $04 | |||||
PADF_B EQU $02 | |||||
PADF_A EQU $01 | |||||
PADB_DOWN EQU $7 | |||||
PADB_UP EQU $6 | |||||
PADB_LEFT EQU $5 | |||||
PADB_RIGHT EQU $4 | |||||
PADB_START EQU $3 | |||||
PADB_SELECT EQU $2 | |||||
PADB_B EQU $1 | |||||
PADB_A EQU $0 | |||||
;*************************************************************************** | |||||
;* | |||||
;* Screen related | |||||
;* | |||||
;*************************************************************************** | |||||
SCRN_X EQU 160 ; Width of screen in pixels | |||||
SCRN_Y EQU 144 ; Height of screen in pixels | |||||
SCRN_X_B EQU 20 ; Width of screen in bytes | |||||
SCRN_Y_B EQU 18 ; Height of screen in bytes | |||||
SCRN_VX EQU 256 ; Virtual width of screen in pixels | |||||
SCRN_VY EQU 256 ; Virtual height of screen in pixels | |||||
SCRN_VX_B EQU 32 ; Virtual width of screen in bytes | |||||
SCRN_VY_B EQU 32 ; Virtual height of screen in bytes | |||||
;*************************************************************************** | |||||
;* | |||||
;* OAM related | |||||
;* | |||||
;*************************************************************************** | |||||
; OAM attributes | |||||
; each entry in OAM RAM is 4 bytes (sizeof_OAM_ATTRS) | |||||
RSRESET | |||||
OAMA_Y RB 1 ; y pos | |||||
OAMA_X RB 1 ; x pos | |||||
OAMA_TILEID RB 1 ; tile id | |||||
OAMA_FLAGS RB 1 ; flags (see below) | |||||
sizeof_OAM_ATTRS RB 0 | |||||
OAM_COUNT EQU 40 ; number of OAM entries in OAM RAM | |||||
; flags | |||||
OAMF_PRI EQU %10000000 ; Priority | |||||
OAMF_YFLIP EQU %01000000 ; Y flip | |||||
OAMF_XFLIP EQU %00100000 ; X flip | |||||
OAMF_PAL0 EQU %00000000 ; Palette number; 0,1 (DMG) | |||||
OAMF_PAL1 EQU %00010000 ; Palette number; 0,1 (DMG) | |||||
OAMF_BANK0 EQU %00000000 ; Bank number; 0,1 (GBC) | |||||
OAMF_BANK1 EQU %00001000 ; Bank number; 0,1 (GBC) | |||||
OAMF_PALMASK EQU %00000111 ; Palette (GBC) | |||||
OAMB_PRI EQU 7 ; Priority | |||||
OAMB_YFLIP EQU 6 ; Y flip | |||||
OAMB_XFLIP EQU 5 ; X flip | |||||
OAMB_PAL1 EQU 4 ; Palette number; 0,1 (DMG) | |||||
OAMB_BANK1 EQU 3 ; Bank number; 0,1 (GBC) | |||||
;* | |||||
;* Nintendo scrolling logo | |||||
;* (Code won't work on a real GameBoy) | |||||
;* (if next lines are altered.) | |||||
NINTENDO_LOGO : MACRO | |||||
DB $CE,$ED,$66,$66,$CC,$0D,$00,$0B,$03,$73,$00,$83,$00,$0C,$00,$0D | |||||
DB $00,$08,$11,$1F,$88,$89,$00,$0E,$DC,$CC,$6E,$E6,$DD,$DD,$D9,$99 | |||||
DB $BB,$BB,$67,$63,$6E,$0E,$EC,$CC,$DD,$DC,$99,$9F,$BB,$B9,$33,$3E | |||||
ENDM | |||||
ENDC ;HARDWARE_INC |
@ -0,0 +1,20 @@ | |||||
#ifndef GBSDK_JOYPAD_H | |||||
#define GBSDK_JOYPAD_H | |||||
#include <stdint.h> | |||||
#define PAD_DOWN 0x80 | |||||
#define PAD_UP 0x40 | |||||
#define PAD_LEFT 0x20 | |||||
#define PAD_RIGHT 0x10 | |||||
#define PAD_START 0x08 | |||||
#define PAD_SELECT 0x04 | |||||
#define PAD_B 0x02 | |||||
#define PAD_A 0x01 | |||||
extern uint8_t joypad_state; | |||||
extern uint8_t joypad_pressed; | |||||
void joypad_update(void) __preserves_regs(b, c); | |||||
#endif//GBSDK_JOYPAD_H |
@ -0,0 +1,19 @@ | |||||
#ifndef GBSDK_OAM_H | |||||
#define GBSDK_OAM_H | |||||
#include <stdint.h> | |||||
#include <sdk/hardware.h> | |||||
struct oam_entry { | |||||
uint8_t y; | |||||
uint8_t x; | |||||
uint8_t tile; | |||||
uint8_t attr; | |||||
}; | |||||
extern struct oam_entry shadow_oam[40]; | |||||
void oam_init(void) __preserves_regs(b); | |||||
void oam_dma_copy(void) __preserves_regs(b, c, d, e, h, l); | |||||
#endif//GBSDK_OAM_H |
@ -0,0 +1,39 @@ | |||||
#ifndef GBSDK_SGB_H | |||||
#define GBSDK_SGB_H | |||||
#include <stdint.h> | |||||
#if SGB | |||||
#define SGB_PAL01 0x00 //Set SGB Palette 0 & 1 | |||||
#define SGB_PAL23 0x01 //Set SGB Palette 2 & 3 | |||||
#define SGB_PAL03 0x02 //Set SGB Palette 0 & 3 | |||||
#define SGB_PAL12 0x03 //Set SGB Palette 1 & 2 | |||||
#define SGB_ATTR_BLK 0x04 //"Block" Area Designation Mode | |||||
#define SGB_ATTR_LIN 0x05 //"Line" Area Designation Mode | |||||
#define SGB_ATTR_DIV 0x06 //"Divide" Area Designation Mode | |||||
#define SGB_ATTR_CHR 0x07 //"1CHR" Area Designation Mode | |||||
#define SGB_SOUND 0x08 //Sound On/Off | |||||
#define SGB_SOU_TRN 0x09 //Transfer Sound PRG/DATA | |||||
#define SGB_PAL_SET 0x0A //Set SGB Palette Indirect | |||||
#define SGB_PAL_TRN 0x0B //Set System Color Palette Data | |||||
#define SGB_ATRC_EN 0x0C //Enable/disable Attraction Mode | |||||
#define SGB_TEST_EN 0x0D //Speed Function | |||||
#define SGB_ICON_EN 0x0E //SGB Function | |||||
#define SGB_DATA_SND 0x0F //SUPER NES WRAM Transfer 1 | |||||
#define SGB_DATA_TRN 0x10 //SUPER NES WRAM Transfer 2 | |||||
#define SGB_MLT_REQ 0x11 //Controller 2 Request | |||||
#define SGB_JUMP 0x12 //Set SNES Program Counter | |||||
#define SGB_CHR_TRN 0x13 //Transfer Character Font Data | |||||
#define SGB_PCT_TRN 0x14 //Set Screen Data Color Data | |||||
#define SGB_ATTR_TRN 0x15 //Set Attribute from ATF | |||||
#define SGB_ATTR_SET 0x16 //Set Data to ATF | |||||
#define SGB_MASK_EN 0x17 //Game Boy Window Mask | |||||
#define SGB_OBJ_TRN 0x18 //Super NES OBJ Mode | |||||
#define SGB_HEADER(command, length) ((uint8_t)(((command) << 3) | (length))) | |||||
void sgb_send(const uint8_t* packet); | |||||
#endif | |||||
#endif |
@ -0,0 +1,40 @@ | |||||
#ifndef GBSDK_VIDEO_H | |||||
#define GBSDK_VIDEO_H | |||||
#include <stdint.h> | |||||
#include <sdk/hardware.h> | |||||
//Turn off the LCD, with the proper checks that this happens during VBLANK | |||||
void lcd_off(void) __preserves_regs(b, c, d, e, h, l); | |||||
//Copy to VRAM with STAT checks, so these function are safe to write to VRAM at any time. | |||||
void vram_memcpy(uint16_t dst, void* src, uint16_t size) __preserves_regs(b, c); | |||||
void vram_memset(uint16_t dst, uint8_t value, uint16_t size) __preserves_regs(b, c); | |||||
inline void vram_set(uint16_t dst, uint8_t value) { | |||||
while(rSTAT & STAT_BUSY) {} | |||||
*((uint8_t*)dst) = value; | |||||
} | |||||
#if CGB | |||||
#define PAL24(rgb) (((rgb & 0xF80000) >> 19) | ((rgb & 0x00F800) >> 6) | ((rgb & 0x0000F8) << 7)) | |||||
void cgb_background_palette(uint16_t* palette) __preserves_regs(b, c); | |||||
void cgb_object_palette(uint16_t* palette) __preserves_regs(b, c); | |||||
inline void cgb_gdma(uint16_t dst, const void* src, uint8_t count) { | |||||
rHDMA1 = ((uint16_t)src) >> 8; | |||||
rHDMA2 = ((uint16_t)src) & 0xFF; | |||||
rHDMA3 = dst >> 8; | |||||
rHDMA4 = dst & 0xFF; | |||||
rHDMA5 = count - 1; | |||||
} | |||||
inline void cgb_hdma(uint16_t dst, const void* src, uint8_t count) { | |||||
rHDMA1 = ((uint16_t)src) >> 8; | |||||
rHDMA2 = ((uint16_t)src) & 0xFF; | |||||
rHDMA3 = dst >> 8; | |||||
rHDMA4 = dst & 0xFF; | |||||
rHDMA5 = (count - 1) | 0x80; | |||||
} | |||||
#endif | |||||
#endif//GBSDK_VIDEO_H |
@ -0,0 +1,169 @@ | |||||
.PHONY: all clean | |||||
MYDIR := $(dir $(lastword $(MAKEFILE_LIST))) | |||||
ifndef PROJECT_NAME | |||||
$(error PROJECT_NAME is not set to the name of your rom.) | |||||
endif | |||||
ifndef MBC | |||||
$(error MBC is not set. Should be set to the MBC name or value you want. Example "MBC5+RAM+BATTERY") | |||||
endif | |||||
ifndef TARGETS | |||||
$(error TARGETS is not set. Should be a combination of DMG SGB CGB) | |||||
endif | |||||
ifneq ($(filter-out DMG SGB CGB,$(TARGETS)),) | |||||
$(error TARGETS should be a combination of DMG SGB CGB, unknown: $(filter-out DMG SGB CGB,$(TARGETS))) | |||||
endif | |||||
MAPS := $(shell find level/ -type f -name '*.png' -not -name '*_coll.png') | |||||
SRC := $(shell find src/ -type f -name '*.c') $(shell find $(MYDIR)/src/ -type f -name '*.c') $(MAPS:level/%.png=_build/level/%.c) | |||||
ASM := $(shell find src/ -type f -name '*.asm') $(shell find $(MYDIR)/src/ -type f -name '*.asm') | |||||
# Which files do we use from the sdcc libc | |||||
LIBC := _memset.c gbz80/memcpy.s | |||||
LIBC += _divuchar.c _divschar.c _muluchar.c _mulschar.c _modschar.c _moduchar.c | |||||
LIBC += _divuint.c _divsint.c _mulint.c _modsint.c _moduint.c | |||||
# the 32 bit functions use 10% of ROM0, so do not include them | |||||
#LIBC += _divulong.c _divslong.c _mullong.c _modslong.c _modulong.c | |||||
LIBC_PATH := $(subst \,/,$(shell sdcc -mgbz80 --print-search-dirs | grep gbz80 | tail -n 1))/../src | |||||
ifneq (1,$(words [$(LIBC_PATH)])) | |||||
$(error "your environment or sdcc is installed in a path with spaces in it, this is not allowed, as it will break sdcc") | |||||
endif | |||||
Q ?= @ | |||||
BUILD := _build | |||||
TOOLS := $(MYDIR)/tools | |||||
OBJS := $(patsubst %, $(BUILD)/%.o, $(ASM) $(SRC)) $(patsubst %, $(BUILD)/libc/%.o, $(LIBC)) $(BUILD)/gsinit.end.o | |||||
CFLAGS := -mgbz80 -Isrc/ -I$(MYDIR)/inc -I$(BUILD)/level | |||||
ASFLAGS := -isrc/ -i$(MYDIR)/inc -i$(BUILD)/assets/ -Wall | |||||
LDFLAGS := --pad 0xFF | |||||
FIXFLAGS := --validate --pad-value 0xFF --title "$(PROJECT_NAME)" --mbc-type "$(MBC)" -l 0x33 | |||||
ROM_EXTENSION := gb | |||||
# Replace spaces with underscores in the project name. | |||||
PROJECT_NAME := $(subst $() $(),_,$(PROJECT_NAME)) | |||||
ifeq ($(filter CGB,$(TARGETS)),) # Not targeting CGB, so disable CGB features | |||||
CFLAGS += -DCGB=0 | |||||
ASFLAGS += -DCGB=0 | |||||
LDFLAGS += --dmg | |||||
else | |||||
CFLAGS += -DCGB=1 | |||||
ASFLAGS += -DCGB=1 | |||||
ifeq ($(filter DMG,$(TARGETS))$(filter SGB,$(TARGETS)),) # Check if not targeting both CGB and DMG | |||||
FIXFLAGS += --color-only | |||||
else | |||||
FIXFLAGS += --color-compatible | |||||
endif | |||||
ROM_EXTENSION := gbc | |||||
endif | |||||
ifeq ($(filter SGB,$(TARGETS)),) # Not targeting SGB, so disable SGB features | |||||
CFLAGS += -DSGB=0 | |||||
ASFLAGS += -DSGB=0 | |||||
else # Targeting SGB as well | |||||
CFLAGS += -DSGB=1 | |||||
ASFLAGS += -DSGB=1 | |||||
FIXFLAGS += --sgb-compatible | |||||
endif | |||||
ifneq ($(findstring RAM,$(MBC)),) | |||||
FIXFLAGS += --ram-size 2 | |||||
endif | |||||
all: $(PROJECT_NAME).$(ROM_EXTENSION) | |||||
# Rules to make c files | |||||
$(BUILD)/%.c.as: %.c | |||||
@echo Compiling $< | |||||
@mkdir -p $(dir $@) | |||||
$(Q)sdcc $(CFLAGS) -S $< -o $@ -Wp "-MQ $@ -MD $(@:.as=.as.d)" | |||||
-include $(patsubst %.c, $(BUILD)/%.c.as.d, $(SRC)) | |||||
$(BUILD)/libc/%.c.as: $(LIBC_PATH)/%.c | |||||
@echo Compiling $< | |||||
@mkdir -p $(dir $@) | |||||
$(Q)sdcc $(CFLAGS) -S $< -o $@ | |||||
$(BUILD)/%.c.asm: $(BUILD)/%.c.as $(TOOLS)/asmconvert.py | |||||
$(Q)python3 $(TOOLS)/asmconvert.py $(notdir $<) < $< > $@ | |||||
$(BUILD)/%.c.asm.d: $(BUILD)/%.c.asm | |||||
@mkdir -p $(dir $@) | |||||
$(Q)rgbasm $(ASFLAGS) $< -M $@ -MT $(@:.asm.d=.o) -MT $@ -MG | |||||
# rgbds dependency generation is broken, as it stops after the first missing file. so list all incbins always | |||||
$(Q)cat $< | grep -i '^ *INCBIN' | sed -E 's/ *incbin *"([^"]*)"/$(subst /,\/,$(@:.asm.d=.o)): \1/gI' >> $@ | |||||
-include $(patsubst %.c, $(BUILD)/%.c.asm.d, $(SRC)) | |||||
$(BUILD)/%.c.o: $(BUILD)/%.c.asm $(BUILD)/%.c.asm.d | |||||
@echo "Assembling (c)" $< | |||||
@mkdir -p $(dir $@) | |||||
$(Q)rgbasm $(ASFLAGS) $< -o $@ | |||||
# Rules to build libc asm files | |||||
$(BUILD)/libc/%.s.asm: $(LIBC_PATH)/%.s $(TOOLS)/asmconvert.py | |||||
@mkdir -p $(dir $@) | |||||
$(Q)python3 $(TOOLS)/asmconvert.py $(notdir $<) < $< > $@ | |||||
$(BUILD)/libc/%.s.o: $(BUILD)/libc/%.s.asm | |||||
@echo Assembling $< | |||||
@mkdir -p $(dir $@) | |||||
$(Q)rgbasm $(ASFLAGS) $< -o $@ | |||||
# Rules to build project asm files | |||||
$(BUILD)/%.asm.d: %.asm | |||||
@mkdir -p $(dir $@) | |||||
$(Q)rgbasm $(ASFLAGS) $< -M $@ -MT $(@:.d=.o) -MT $@ -MG | |||||
# rgbds dependency generation is broken, as it stops after the first missing file. so list all incbins always | |||||
$(Q)cat $< | grep -i '^ *INCBIN' | sed -E 's/incbin "([^"]*)"/$(subst /,\/,$(@:.d=.o)): \1/gI' >> $@ | |||||
$(BUILD)/%.asm.o: %.asm $(BUILD)/%.asm.d | |||||
@echo Assembling $< | |||||
@mkdir -p $(dir $@) | |||||
$(Q)rgbasm $(ASFLAGS) $< -o $@ | |||||
-include $(patsubst %.asm, $(BUILD)/%.asm.d, $(ASM)) | |||||
# Rule to build the final rom | |||||
$(PROJECT_NAME).$(ROM_EXTENSION) $(PROJECT_NAME).map $(PROJECT_NAME).sym: $(OBJS) | |||||
@echo Linking $@ | |||||
$(Q)rgblink $(LDFLAGS) $^ -o $@ -m $(basename $@).map -n $(basename $@).sym | |||||
$(Q)rgbfix $(FIXFLAGS) $@ | |||||
@python3 $(TOOLS)/romspace.py $(basename $@).map | |||||
# Asset related rules | |||||
$(BUILD)/assets/%.2bpp $(BUILD)/assets/%.map: assets/%.png | |||||
@echo Converting $< | |||||
@mkdir -p $(dir $@) | |||||
$(Q)rgbgfx \ | |||||
-t "$(BUILD)/$(shell dirname $<)/$(shell basename --suffix=.png $<).map" \ | |||||
-o "$(BUILD)/$(shell dirname $<)/$(shell basename --suffix=.png $<).2bpp" \ | |||||
-u \ | |||||
$< | |||||
$(BUILD)/level/%.h $(BUILD)/level/%.c: level/%.png level/%_coll.png | |||||
@echo Generating level $< | |||||
@mkdir -p $(BUILD)/level | |||||
@cp $< $(BUILD)/level/ | |||||
@cp $(shell dirname $<)/$(shell basename --suffix=.png $<)_coll.png $(BUILD)/level/ | |||||
@python scripts/generate_map.py --compress 1 "$(BUILD)/$<" | |||||
$(BUILD)/assets/%.oam.2bpp: assets/%.oam.png | |||||
@echo Converting $< | |||||
@mkdir -p $(dir $@) | |||||
$(Q)rgbgfx -h $< -o $@ | |||||
$(BUILD)/assets/%.1bpp: assets/%.png | |||||
@echo Converting $< | |||||
@mkdir -p $(dir $@) | |||||
$(Q)rgbgfx -d 1 $< -o $@ | |||||
# Special hack to place a ret instruction at the end of the GSINIT section. | |||||
# This has to be linked last, as that ensures that this fragment is at the end of the "GSINIT" section. | |||||
$(BUILD)/gsinit.end.o: | |||||
@/bin/echo -e 'SECTION FRAGMENT "GSINIT", ROMX, BANK[1]\n ret' | rgbasm - -o $@ | |||||
clean: | |||||
@rm -rf $(BUILD) $(PROJECT_NAME).$(ROM_EXTENSION) $(PROJECT_NAME).map $(PROJECT_NAME).sym | |||||
# Do not remove intermediate files (like assets, c->asm files, etc) | |||||
.SECONDARY: |
@ -0,0 +1,25 @@ | |||||
INCLUDE "sdk/hardware.inc" | |||||
SECTION "BankingHRAM", HRAM | |||||
wCurrentBank:: | |||||
_current_bank:: ds 1 | |||||
SECTION "BankingCode", ROM0 | |||||
bankedCall:: | |||||
___sdcc_bcall_ehl:: | |||||
ldh a, [wCurrentBank] | |||||
push af | |||||
ld a, e | |||||
ldh [wCurrentBank], a | |||||
ld [rROMB0], a | |||||
rst callHL | |||||
pop af | |||||
setCurrentBank:: | |||||
ldh [wCurrentBank], a | |||||
ld [rROMB0], a | |||||
ret | |||||
SECTION "callHL", ROM0[$0008] | |||||
callHL:: | |||||
___sdcc_call_hl:: | |||||
jp hl |
@ -0,0 +1,18 @@ | |||||
include "sdk/hardware.inc" | |||||
SECTION "Header", ROM0[$100] | |||||
di | |||||
jp startRom | |||||
ds $150 - @ | |||||
SECTION "Init", ROMX, BANK[1] | |||||
startRom: | |||||
ld sp, $E000 | |||||
call globalsInit ; init C globals | |||||
call _main | |||||
db $DD ; lock up if main returns ($DD is an invalid opcode) | |||||
SECTION FRAGMENT "GSINIT", ROMX, BANK[1] | |||||
globalsInit: | |||||
; This will be the first fragment of GSINIT. |
@ -0,0 +1,48 @@ | |||||
include "sdk/hardware.inc" | |||||
SECTION "gbsdk_joypad_ram", WRAM0 | |||||
_joypad_state:: | |||||
wJoypadState:: ds 1 | |||||
_joypad_pressed:: | |||||
wJoypadPressed:: ds 1 | |||||
SECTION "gbsdk_joypad_functions", ROM0 | |||||
_joypad_update:: | |||||
; Call this routine once per frame to update the joypad related variables. | |||||
; Routine also returns the currently pressed buttons in the a register. | |||||
ld hl, rP1 | |||||
ld [hl], P1F_GET_BTN | |||||
; After the initial enable we need to read twice to ensure | |||||
; we get the proper hardware state on real hardware | |||||
ld a, [hl] | |||||
ld a, [hl] | |||||
ld [hl], P1F_GET_DPAD | |||||
cpl ; Inputs are active low, so a bit being 0 is a button pressed. So we invert this. | |||||
and PADF_A | PADF_B | PADF_SELECT | PADF_START | |||||
ld e, a ; Store the lower 4 button bits in e | |||||
; We need to read rP1 8 times to ensure the proper button state is available. | |||||
; This is only needed on real hardware, as it takes a while for the | |||||
; inputs to change state back from the first set. | |||||
ld d, 8 | |||||
: ld a, [hl] | |||||
dec d | |||||
jr nz, :- | |||||
ld [hl], P1F_GET_NONE ; Disable the joypad inputs again, saves a tiny bit of power and allows the lines to settle before the next read | |||||
swap a ; We want the directional keys as upper 4 bits, so swap the nibbles. | |||||
cpl ; Inputs are active low, so a bit being 0 is a button pressed. So we invert this. | |||||
and PADF_RIGHT | PADF_LEFT | PADF_UP | PADF_DOWN | |||||
or e | |||||
ld e, a | |||||
; Compare the new joypad state with the previous one, and store the | |||||
; new bits in wJoypadPressed | |||||
ld hl, wJoypadState | |||||
xor [hl] | |||||
and e | |||||
ld [wJoypadPressed], a | |||||
ld a, e | |||||
ld [wJoypadState], a | |||||
ret |
@ -0,0 +1,44 @@ | |||||
include "sdk/hardware.inc" | |||||
SECTION "gbsdk_oam_memory", WRAM0, ALIGN[8] | |||||
_shadow_oam:: ; OAM Memory is for 40 sprites with 4 bytes per sprite | |||||
wShadowOAM:: | |||||
ds 40 * 4 | |||||
.end: | |||||
SECTION "gbsdk_oam_init_function", ROMX, BANK[1] | |||||
oamInit:: | |||||
_oam_init:: | |||||
ld hl, wShadowOAM | |||||
xor a | |||||
ld c, wShadowOAM.end - wShadowOAM | |||||
: ld [hl+], a | |||||
dec c | |||||
jr nz, :- | |||||
ld hl, hOAMCopyRoutine | |||||
ld de, oamCopyRoutine | |||||
ld c, hOAMCopyRoutine.end - hOAMCopyRoutine | |||||
: ld a, [de] | |||||
inc de | |||||
ld [hl+], a | |||||
dec c | |||||
jr nz, :- | |||||
; We directly copy to clear the initial OAM memory, which else contains garbage. | |||||
jp hOAMCopyRoutine | |||||
oamCopyRoutine: | |||||
LOAD "hram", HRAM | |||||
_oam_dma_copy:: | |||||
hOAMCopyRoutine:: | |||||
ld a, HIGH(wShadowOAM) | |||||
ldh [rDMA], a | |||||
ld a, $28 | |||||
.wait: | |||||
dec a | |||||
jr nz, .wait | |||||
ret | |||||
.end: | |||||
ENDL |
@ -0,0 +1,44 @@ | |||||
include "sdk/hardware.inc" | |||||
SECTION "gbsdk_sgb_functions", ROM0 | |||||
_sgb_send:: | |||||
pop de | |||||
pop hl | |||||
; hl=pointer to data to send | |||||
; SGB RESET pulse | |||||
xor a | |||||
ldh [rP1], a | |||||
push hl ; done here for delay reasons | |||||
cpl | |||||
ldh [rP1], a | |||||
push de ; done here for delay reasons | |||||
ld e, 16 ; amount of bytes to send | |||||
.byteLoop: | |||||
ld a, [hl+] | |||||
ld d, a ; byte to send in d | |||||
ld c, 8 ; amount of bits to send | |||||
.bitLoop: | |||||
bit 0, d | |||||
ld a, P1F_5 | |||||
jr z, .zero | |||||
ld a, P1F_4 | |||||
.zero: | |||||
ldh [rP1], a | |||||
rr d ; 2cycles | |||||
ld a, P1F_5 | P1F_4 ; 2cycles | |||||
dec c ; 1cycle | |||||
ldh [rP1], a | |||||
jr nz, .bitLoop | |||||
dec e | |||||
jr nz, .byteLoop | |||||
ld a, P1F_5 | |||||
ldh [rP1], a | |||||
ldh [rP1], a ; (3) | |||||
ldh [rP1], a ; (3) | |||||
xor a ; (1) | |||||
ldh [rP1], a | |||||
ret |
@ -0,0 +1,116 @@ | |||||
include "sdk/hardware.inc" | |||||
SECTION "gbsdk_video_functions", ROM0 | |||||
_lcd_off:: | |||||
: ldh a, [rLY] | |||||
cp 144 | |||||
jr c, :- | |||||
xor a | |||||
ldh [rLCDC], a | |||||
ret | |||||
_vram_memcpy:: | |||||
ld hl, sp + 7 | |||||
ld a, [hl-] | |||||
ld d, a | |||||
ld a, [hl-] | |||||
ld e, a | |||||
or a, d | |||||
ret z | |||||
; size -> de | |||||
push bc | |||||
ld a, [hl-] | |||||
ld b, a | |||||
ld a, [hl-] | |||||
ld c, a | |||||
ld a, [hl-] | |||||
ld l, [hl] | |||||
ld h, a | |||||
; src -> bc, dst -> hl, size -> de | |||||
inc d | |||||
inc e | |||||
jr .start | |||||
.copy_loop: | |||||
ldh a, [rSTAT] | |||||
and a, STATF_BUSY | |||||
jr nz, .copy_loop | |||||
ld a, [bc] | |||||
ld [hl+], a | |||||
inc bc | |||||
.start: | |||||
dec e | |||||
jr nz, .copy_loop | |||||
dec d | |||||
jr nz, .copy_loop | |||||
pop bc | |||||
ret | |||||
_vram_memset:: | |||||
ld hl, sp + 6 | |||||
ld a, [hl-] | |||||
ld d, a | |||||
ld a, [hl-] | |||||
ld e, a | |||||
or a, d | |||||
ret z | |||||
; size -> de | |||||
push bc | |||||
ld a, [hl-] | |||||
ld c, a | |||||
ld a, [hl-] | |||||
ld l, [hl] | |||||
ld h, a | |||||
; value -> c, dst -> hl, size -> de | |||||
inc d | |||||
inc e | |||||
jr .start | |||||
.copy_loop: | |||||
ldh a, [rSTAT] | |||||
and a, STATF_BUSY | |||||
jr nz, .copy_loop | |||||
ld a, c | |||||
ld [hl+], a | |||||
.start: | |||||
dec e | |||||
jr nz, .copy_loop | |||||
dec d | |||||
jr nz, .copy_loop | |||||
pop bc | |||||
ret | |||||
IF CGB | |||||
_cgb_background_palette:: | |||||
pop de | |||||
pop hl | |||||
push hl | |||||
push de | |||||
ld a, BCPSF_AUTOINC | |||||
ldh [rBCPS], a | |||||
ld e, 8*4*2 | |||||
: ld a, [hl+] | |||||
ldh [rBCPD], a | |||||
dec e | |||||
jr nz, :- | |||||
ret | |||||
_cgb_object_palette:: | |||||
pop de | |||||
pop hl | |||||
push hl | |||||
push de | |||||
ld a, OCPSF_AUTOINC | |||||
ldh [rOCPS], a | |||||
ld e, 8*4*2 | |||||
: ld a, [hl+] | |||||
ldh [rOCPD], a | |||||
dec e | |||||
jr nz, :- | |||||
ret | |||||
ENDC |
@ -0,0 +1,227 @@ | |||||
import sys | |||||
import re | |||||
## Need to convert asxxxx syntax to rgbds syntax | |||||
module = sys.argv[1] | |||||
class Token: | |||||
def __init__(self, kind, value, line_nr): | |||||
self.kind = kind | |||||
self.value = value | |||||
self.line_nr = line_nr | |||||
def isA(self, kind, value=None): | |||||
return self.kind == kind and (value is None or value == self.value) | |||||
def __repr__(self): | |||||
return "[%s:%s:%d]" % (self.kind, self.value, self.line_nr) | |||||
class Tokenizer: | |||||
TOKEN_REGEX = re.compile('|'.join('(?P<%s>%s)' % pair for pair in [ | |||||
('HEX', r'0x[0-9A-Fa-f]+'), | |||||
('ASSIGN', r'='), | |||||
('ALABEL', r'\d+\$'), | |||||
('NUMBER', r'\d+(\.\d*)?'), | |||||
('COMMENT', r';[^\n]*'), | |||||
('LABEL', r':+'), | |||||
('EXPR', r'#'), | |||||
('STRING', '"[^"]*"'), | |||||
('DIRECTIVE', r'\.[A-Za-z_][A-Za-z0-9_\.]*'), | |||||
('ID', r'[A-Za-z_][A-Za-z0-9_\.]*'), | |||||
('OP', r'[+\-*/,\(\)<>]'), | |||||
('NEWLINE', r'\n'), | |||||
('SKIP', r'[ \t]+'), | |||||
('MISMATCH', r'.'), | |||||
])) | |||||
def __init__(self, code): | |||||
self.__tokens = [] | |||||
line_num = 1 | |||||
for mo in self.TOKEN_REGEX.finditer(code): | |||||
kind = mo.lastgroup | |||||
value = mo.group() | |||||
if kind == 'MISMATCH': | |||||
print("ERR:", code.split("\n")[line_num-1]) | |||||
raise RuntimeError("Syntax error on line: %d: %s\n%s", line_num, value) | |||||
elif kind == 'SKIP': | |||||
pass | |||||
else: | |||||
if kind == 'HEX': | |||||
value = "$" + value[2:] | |||||
if kind == 'ALABEL': | |||||
value = "._ANNO_" + value[:-1] | |||||
kind = "ID" | |||||
self.__tokens.append(Token(kind, value, line_num)) | |||||
if kind == 'NEWLINE': | |||||
line_num += 1 | |||||
self.__tokens.append(Token('NEWLINE', '\n', line_num)) | |||||
def peek(self): | |||||
return self.__tokens[0] | |||||
def pop(self): | |||||
return self.__tokens.pop(0) | |||||
def expect(self, kind, value=None): | |||||
pop = self.pop() | |||||
if not pop.isA(kind, value): | |||||
if value is not None: | |||||
raise SyntaxError("%s != %s:%s" % (pop, kind, value)) | |||||
raise SyntaxError("%s != %s" % (pop, kind)) | |||||
def __bool__(self): | |||||
return bool(self.__tokens) | |||||
tok = Tokenizer(sys.stdin.read()) | |||||
def processExpression(): | |||||
while True: | |||||
t = tok.peek() | |||||
if t.isA('EXPR'): | |||||
tok.pop() | |||||
t = tok.peek() | |||||
if t.isA('OP', '<'): | |||||
sys.stdout.write('LOW') | |||||
tok.pop() | |||||
t = tok.peek() | |||||
if t.isA('OP', '>'): | |||||
sys.stdout.write('HIGH') | |||||
tok.pop() | |||||
t = tok.peek() | |||||
if t.isA('OP', '('): | |||||
tok.pop() | |||||
sys.stdout.write('(') | |||||
processExpression() | |||||
t = tok.pop() | |||||
assert t.isA('OP', ')') | |||||
sys.stdout.write(')') | |||||
if t.isA('ID') and t.value.startswith("b_"): | |||||
t.value = "BANK(%s)" % (t.value[1:]) | |||||
if t.isA('NEWLINE') or t.isA('OP', ')') or t.isA('OP', ','): | |||||
break | |||||
sys.stdout.write(t.value) | |||||
tok.pop() | |||||
def processParameter(): | |||||
t = tok.pop() | |||||
if t.isA('EXPR'): | |||||
processExpression() | |||||
elif t.isA('NEWLINE'): | |||||
return | |||||
elif t.isA('ID') or t.isA('NUMBER') or t.isA('HEX'): | |||||
sys.stdout.write(t.value) | |||||
elif t.isA('OP', '('): | |||||
sys.stdout.write("[") | |||||
processExpression() | |||||
t = tok.pop() | |||||
while not t.isA('OP', ')'): | |||||
sys.stdout.write(t.value) | |||||
t = tok.pop() | |||||
assert t.isA('OP', ')'), t | |||||
sys.stdout.write("]") | |||||
else: | |||||
raise Exception(t) | |||||
while tok: | |||||
start = tok.pop() | |||||
if start.isA('NEWLINE'): | |||||
pass | |||||
elif start.isA('COMMENT'): | |||||
print(start.value) | |||||
elif start.isA('DIRECTIVE'): | |||||
if start.value in {'.module', '.optsdcc', '.globl'}: | |||||
while not tok.pop().isA('NEWLINE'): | |||||
pass | |||||
elif start.value == '.area': | |||||
area_name = tok.pop().value | |||||
if area_name == "_DATA": | |||||
print('SECTION "%s_%s", WRAM0' % (module, area_name)) | |||||
elif area_name == "_DABS": | |||||
print('SECTION "%s_%s", SRAM' % (module, area_name)) | |||||
elif area_name == "_HOME": | |||||
print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name)) | |||||
elif area_name == "_CODE": | |||||
print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name)) | |||||
elif area_name.startswith("_CODE_"): | |||||
print('SECTION FRAGMENT "%s_%s", ROMX, BANK[%d]' % (module, area_name, int(area_name[6:]))) | |||||
elif area_name == "_CABS": | |||||
print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name)) | |||||
elif area_name == "_GSINIT": | |||||
print('SECTION FRAGMENT "GSINIT", ROMX, BANK[1]') | |||||
elif area_name == "_GSFINAL": | |||||
print('SECTION FRAGMENT "GSFINAL", ROMX, BANK[1]') | |||||
elif area_name == "_auto": | |||||
print('SECTION FRAGMENT "code_%s", ROMX' % (module)) | |||||
else: | |||||
raise Exception(area_name) | |||||
while not tok.pop().isA('NEWLINE'): | |||||
pass | |||||
elif start.value == '.ds': | |||||
sys.stdout.write("ds ") | |||||
processExpression() | |||||
sys.stdout.write("\n") | |||||
elif start.value == '.ascii': | |||||
sys.stdout.write("db ") | |||||
sys.stdout.write(tok.pop().value) | |||||
sys.stdout.write("\n") | |||||
elif start.value == '.db': | |||||
sys.stdout.write("db ") | |||||
processParameter() | |||||
while tok.peek().isA('OP', ','): | |||||
sys.stdout.write(",") | |||||
tok.pop() | |||||
processParameter() | |||||
sys.stdout.write("\n") | |||||
elif start.value == '.dw': | |||||
sys.stdout.write("dw ") | |||||
processParameter() | |||||
while tok.peek().isA('OP', ','): | |||||
sys.stdout.write(",") | |||||
tok.pop() | |||||
processParameter() | |||||
sys.stdout.write("\n") | |||||
elif start.value == '.incbin': | |||||
sys.stdout.write("incbin ") | |||||
while not tok.peek().isA('NEWLINE'): | |||||
sys.stdout.write(tok.pop().value) | |||||
sys.stdout.write("\n") | |||||
tok.pop() | |||||
else: | |||||
raise Exception(start, tok.peek()) | |||||
elif start.isA('ID'): | |||||
if tok.peek().isA('ASSIGN'): | |||||
tok.pop() | |||||
sys.stdout.write("%s = " % (start.value)) | |||||
processExpression() | |||||
sys.stdout.write("\n") | |||||
elif tok.peek().isA('LABEL'): | |||||
print("%s%s" % (start.value, tok.pop().value)) | |||||
elif start.isA('ID', 'ldhl'): | |||||
tok.expect('ID', 'sp') | |||||
tok.expect('OP', ',') | |||||
sys.stdout.write("ld hl, sp + ") | |||||
processParameter() | |||||
sys.stdout.write("\n") | |||||
elif start.isA('ID', 'lda'): | |||||
tok.expect('ID', 'hl') | |||||
tok.expect('OP', ',') | |||||
t = tok.pop() | |||||
assert t.isA('NUMBER') or t.isA('HEX') | |||||
tok.expect('OP', '(') | |||||
tok.expect('ID', 'sp') | |||||
tok.expect('OP', ')') | |||||
sys.stdout.write("ld hl, sp + %s\n" % (t.value)) | |||||
else: | |||||
sys.stdout.write("%s " % (start.value)) | |||||
if not tok.peek().isA('NEWLINE'): | |||||
processParameter() | |||||
if tok.peek().isA('OP', ','): | |||||
tok.pop() | |||||
sys.stdout.write(", ") | |||||
processParameter() | |||||
sys.stdout.write("\n") | |||||
tok.expect('NEWLINE') | |||||
else: | |||||
raise Exception(start) |
@ -0,0 +1,16 @@ | |||||
import sys | |||||
import re | |||||
SIZE = {"ROM0": 0x4000, "ROMX": 0x4000, "WRAM0": 0x2000, "HRAM": 0x007F, "VRAM": 0x4000} | |||||
area_re = re.compile(r'([A-Z0-9]+) bank #([0-9a-f]+):\n') | |||||
slack_re = re.compile(r' SLACK: \$([0-9a-f]+) byte') | |||||
for line in open(sys.argv[1], "rt"): | |||||
area = area_re.match(line) | |||||
if area: | |||||
area_name = area[1] | |||||
bank_nr = int(area[2]) | |||||
slack = slack_re.match(line) | |||||
if slack: | |||||
used = SIZE[area_name] - int(slack[1], 16) | |||||
print("%5s:%2d %04x/%04x (%.1f%%)" % (area_name, bank_nr, used, SIZE[area_name], used * 100 / SIZE[area_name])) |