From 1d18e8e92e6d25fcbb4cd39d61b6416be1b9200e Mon Sep 17 00:00:00 2001 From: Forest Belton Date: Wed, 29 Sep 2021 06:14:41 -0400 Subject: [PATCH] Restore GBSDK subdirectory --- gbsdk/LICENSE | 21 + gbsdk/README.md | 26 ++ gbsdk/inc/sdk/assets.h | 18 + gbsdk/inc/sdk/assets.inc | 6 + gbsdk/inc/sdk/banking.h | 13 + gbsdk/inc/sdk/hardware.h | 494 ++++++++++++++++++++ gbsdk/inc/sdk/hardware.inc | 913 +++++++++++++++++++++++++++++++++++++ gbsdk/inc/sdk/joypad.h | 20 + gbsdk/inc/sdk/oam.h | 19 + gbsdk/inc/sdk/sgb.h | 39 ++ gbsdk/inc/sdk/video.h | 40 ++ gbsdk/rules.mk | 169 +++++++ gbsdk/src/banking.asm | 25 + gbsdk/src/entry.asm | 18 + gbsdk/src/joypad.asm | 48 ++ gbsdk/src/oam.asm | 44 ++ gbsdk/src/sgb.asm | 44 ++ gbsdk/src/video.asm | 116 +++++ gbsdk/tools/asmconvert.py | 227 +++++++++ gbsdk/tools/romspace.py | 16 + 20 files changed, 2316 insertions(+) create mode 100644 gbsdk/LICENSE create mode 100644 gbsdk/README.md create mode 100644 gbsdk/inc/sdk/assets.h create mode 100644 gbsdk/inc/sdk/assets.inc create mode 100644 gbsdk/inc/sdk/banking.h create mode 100644 gbsdk/inc/sdk/hardware.h create mode 100644 gbsdk/inc/sdk/hardware.inc create mode 100644 gbsdk/inc/sdk/joypad.h create mode 100644 gbsdk/inc/sdk/oam.h create mode 100644 gbsdk/inc/sdk/sgb.h create mode 100644 gbsdk/inc/sdk/video.h create mode 100644 gbsdk/rules.mk create mode 100644 gbsdk/src/banking.asm create mode 100644 gbsdk/src/entry.asm create mode 100644 gbsdk/src/joypad.asm create mode 100644 gbsdk/src/oam.asm create mode 100644 gbsdk/src/sgb.asm create mode 100644 gbsdk/src/video.asm create mode 100644 gbsdk/tools/asmconvert.py create mode 100644 gbsdk/tools/romspace.py diff --git a/gbsdk/LICENSE b/gbsdk/LICENSE new file mode 100644 index 0000000..731e7d7 --- /dev/null +++ b/gbsdk/LICENSE @@ -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. diff --git a/gbsdk/README.md b/gbsdk/README.md new file mode 100644 index 0000000..c60a74e --- /dev/null +++ b/gbsdk/README.md @@ -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. diff --git a/gbsdk/inc/sdk/assets.h b/gbsdk/inc/sdk/assets.h new file mode 100644 index 0000000..70f5d1e --- /dev/null +++ b/gbsdk/inc/sdk/assets.h @@ -0,0 +1,18 @@ +#ifndef GBSDK_ASSETS_H +#define GBSDK_ASSETS_H + +#include + +#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 diff --git a/gbsdk/inc/sdk/assets.inc b/gbsdk/inc/sdk/assets.inc new file mode 100644 index 0000000..14ccf6e --- /dev/null +++ b/gbsdk/inc/sdk/assets.inc @@ -0,0 +1,6 @@ +; Usage: asset label_name, "filename" +MACRO asset +\1:: + INCBIN STRCAT("_build/assets/", \2) +.end:: +ENDM diff --git a/gbsdk/inc/sdk/banking.h b/gbsdk/inc/sdk/banking.h new file mode 100644 index 0000000..0593e99 --- /dev/null +++ b/gbsdk/inc/sdk/banking.h @@ -0,0 +1,13 @@ +#ifndef GBSDK_BANKING_H +#define GBSDK_BANKING_H + +#include + +extern __sfr current_bank; + +inline void switch_bank(bank_nr) { + current_bank = bank_nr; + *((uint8_t*)0x2000) = bank_nr; +} + +#endif//LIB_BANKING_H diff --git a/gbsdk/inc/sdk/hardware.h b/gbsdk/inc/sdk/hardware.h new file mode 100644 index 0000000..e611d3b --- /dev/null +++ b/gbsdk/inc/sdk/hardware.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 diff --git a/gbsdk/inc/sdk/hardware.inc b/gbsdk/inc/sdk/hardware.inc new file mode 100644 index 0000000..d676af7 --- /dev/null +++ b/gbsdk/inc/sdk/hardware.inc @@ -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 diff --git a/gbsdk/inc/sdk/joypad.h b/gbsdk/inc/sdk/joypad.h new file mode 100644 index 0000000..5102a6c --- /dev/null +++ b/gbsdk/inc/sdk/joypad.h @@ -0,0 +1,20 @@ +#ifndef GBSDK_JOYPAD_H +#define GBSDK_JOYPAD_H + +#include + +#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 diff --git a/gbsdk/inc/sdk/oam.h b/gbsdk/inc/sdk/oam.h new file mode 100644 index 0000000..dd6fe35 --- /dev/null +++ b/gbsdk/inc/sdk/oam.h @@ -0,0 +1,19 @@ +#ifndef GBSDK_OAM_H +#define GBSDK_OAM_H + +#include +#include + + +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 diff --git a/gbsdk/inc/sdk/sgb.h b/gbsdk/inc/sdk/sgb.h new file mode 100644 index 0000000..c5d2127 --- /dev/null +++ b/gbsdk/inc/sdk/sgb.h @@ -0,0 +1,39 @@ +#ifndef GBSDK_SGB_H +#define GBSDK_SGB_H + +#include + +#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 diff --git a/gbsdk/inc/sdk/video.h b/gbsdk/inc/sdk/video.h new file mode 100644 index 0000000..7cba700 --- /dev/null +++ b/gbsdk/inc/sdk/video.h @@ -0,0 +1,40 @@ +#ifndef GBSDK_VIDEO_H +#define GBSDK_VIDEO_H + +#include +#include + + +//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 diff --git a/gbsdk/rules.mk b/gbsdk/rules.mk new file mode 100644 index 0000000..8e8d2a2 --- /dev/null +++ b/gbsdk/rules.mk @@ -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: diff --git a/gbsdk/src/banking.asm b/gbsdk/src/banking.asm new file mode 100644 index 0000000..5e9d1d6 --- /dev/null +++ b/gbsdk/src/banking.asm @@ -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 diff --git a/gbsdk/src/entry.asm b/gbsdk/src/entry.asm new file mode 100644 index 0000000..7399881 --- /dev/null +++ b/gbsdk/src/entry.asm @@ -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. diff --git a/gbsdk/src/joypad.asm b/gbsdk/src/joypad.asm new file mode 100644 index 0000000..d06ae99 --- /dev/null +++ b/gbsdk/src/joypad.asm @@ -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 diff --git a/gbsdk/src/oam.asm b/gbsdk/src/oam.asm new file mode 100644 index 0000000..cbe3daf --- /dev/null +++ b/gbsdk/src/oam.asm @@ -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 diff --git a/gbsdk/src/sgb.asm b/gbsdk/src/sgb.asm new file mode 100644 index 0000000..908c34a --- /dev/null +++ b/gbsdk/src/sgb.asm @@ -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 diff --git a/gbsdk/src/video.asm b/gbsdk/src/video.asm new file mode 100644 index 0000000..ffb12e1 --- /dev/null +++ b/gbsdk/src/video.asm @@ -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 diff --git a/gbsdk/tools/asmconvert.py b/gbsdk/tools/asmconvert.py new file mode 100644 index 0000000..90c8b41 --- /dev/null +++ b/gbsdk/tools/asmconvert.py @@ -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) diff --git a/gbsdk/tools/romspace.py b/gbsdk/tools/romspace.py new file mode 100644 index 0000000..bae86db --- /dev/null +++ b/gbsdk/tools/romspace.py @@ -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]))