Browse Source

Restore GBSDK subdirectory

master
Forest Belton 2 years ago
parent
commit
1d18e8e92e
20 changed files with 2316 additions and 0 deletions
  1. +21
    -0
      gbsdk/LICENSE
  2. +26
    -0
      gbsdk/README.md
  3. +18
    -0
      gbsdk/inc/sdk/assets.h
  4. +6
    -0
      gbsdk/inc/sdk/assets.inc
  5. +13
    -0
      gbsdk/inc/sdk/banking.h
  6. +494
    -0
      gbsdk/inc/sdk/hardware.h
  7. +913
    -0
      gbsdk/inc/sdk/hardware.inc
  8. +20
    -0
      gbsdk/inc/sdk/joypad.h
  9. +19
    -0
      gbsdk/inc/sdk/oam.h
  10. +39
    -0
      gbsdk/inc/sdk/sgb.h
  11. +40
    -0
      gbsdk/inc/sdk/video.h
  12. +169
    -0
      gbsdk/rules.mk
  13. +25
    -0
      gbsdk/src/banking.asm
  14. +18
    -0
      gbsdk/src/entry.asm
  15. +48
    -0
      gbsdk/src/joypad.asm
  16. +44
    -0
      gbsdk/src/oam.asm
  17. +44
    -0
      gbsdk/src/sgb.asm
  18. +116
    -0
      gbsdk/src/video.asm
  19. +227
    -0
      gbsdk/tools/asmconvert.py
  20. +16
    -0
      gbsdk/tools/romspace.py

+ 21
- 0
gbsdk/LICENSE View File

@ -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.

+ 26
- 0
gbsdk/README.md View File

@ -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.

+ 18
- 0
gbsdk/inc/sdk/assets.h View File

@ -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

+ 6
- 0
gbsdk/inc/sdk/assets.inc View File

@ -0,0 +1,6 @@
; Usage: asset label_name, "filename"
MACRO asset
\1::
INCBIN STRCAT("_build/assets/", \2)
.end::
ENDM

+ 13
- 0
gbsdk/inc/sdk/banking.h View File

@ -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

+ 494
- 0
gbsdk/inc/sdk/hardware.h View File

@ -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

+ 913
- 0
gbsdk/inc/sdk/hardware.inc View File

@ -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

+ 20
- 0
gbsdk/inc/sdk/joypad.h View File

@ -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

+ 19
- 0
gbsdk/inc/sdk/oam.h View File

@ -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

+ 39
- 0
gbsdk/inc/sdk/sgb.h View File

@ -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

+ 40
- 0
gbsdk/inc/sdk/video.h View File

@ -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

+ 169
- 0
gbsdk/rules.mk View File

@ -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:

+ 25
- 0
gbsdk/src/banking.asm View File

@ -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

+ 18
- 0
gbsdk/src/entry.asm View File

@ -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.

+ 48
- 0
gbsdk/src/joypad.asm View File

@ -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

+ 44
- 0
gbsdk/src/oam.asm View File

@ -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

+ 44
- 0
gbsdk/src/sgb.asm View File

@ -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

+ 116
- 0
gbsdk/src/video.asm View File

@ -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

+ 227
- 0
gbsdk/tools/asmconvert.py View File

@ -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)

+ 16
- 0
gbsdk/tools/romspace.py View File

@ -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]))

Loading…
Cancel
Save