├── LICENSE ├── README.md ├── inc └── sdk │ ├── assets.h │ ├── assets.inc │ ├── banking.h │ ├── hardware.h │ ├── hardware.inc │ ├── interrupt.h │ ├── joypad.h │ ├── oam.h │ ├── sgb.h │ ├── system.h │ └── video.h ├── rules.mk ├── src ├── banking.asm ├── entry.asm ├── joypad.asm ├── oam.asm ├── sgb.asm └── video.asm └── tools ├── asmconvert.py └── romspace.py /LICENSE: -------------------------------------------------------------------------------- 1 | This software is provided 'as-is', without any express or implied 2 | warranty. In no event will the authors be held liable for any damages 3 | arising from the use of this software. 4 | 5 | Permission is granted to anyone to use this software for any purpose, 6 | including commercial applications, and to alter it and redistribute it 7 | freely, subject to the following restrictions: 8 | 9 | 1. The origin of this software must not be misrepresented; you must not 10 | claim that you wrote the original software. If you use this software 11 | in a product, an acknowledgment in the product documentation would be 12 | appreciated but is not required. 13 | 2. Altered source versions must be plainly marked as such, and must not be 14 | misrepresented as being the original software. 15 | 3. This notice may not be removed or altered from any source distribution. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gameboy Software Development Kit 2 | 3 | This is the Gameboy software development kit. For mixed assembly and C programming. Note that this isn't a mature project yet. 4 | 5 | If you want to do C programming for the Gameboy, https://github.com/gbdk-2020/gbdk-2020 is much more mature. 6 | 7 | If you want to do assembly programming for the Gameboy, https://rgbds.gbdev.io/ is the goto toolchain. 8 | 9 | # But... but...? Why this then? 10 | 11 | This project has a few goals: 12 | 13 | * 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. 14 | * Mixing of C code with ASM code, without subjecting yourself to the asxxxx syntax. 15 | * 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. 16 | 17 | # Usage 18 | 19 | To use this, you need to have installed: 20 | 21 | * sdcc (version 4.2.0) 22 | * rgbds (version 0.5.1) 23 | * python (version 3.6 or newer) 24 | * A strong will, and a bit of crazy. 25 | 26 | See https://github.com/daid/gbsdk-template for a ready to use template of a project setup. 27 | -------------------------------------------------------------------------------- /inc/sdk/assets.h: -------------------------------------------------------------------------------- 1 | #ifndef GBSDK_ASSETS_H 2 | #define GBSDK_ASSETS_H 3 | 4 | #include 5 | 6 | #define EXTERN_ASSET(var_name) \ 7 | extern const uint8_t var_name[]; \ 8 | extern const uint8_t var_name ## _end[]; \ 9 | extern const void __bank__ ## var_name 10 | 11 | #define ASSET(var_name, filename) \ 12 | void __ ## var_name ## __() __naked { \ 13 | __asm__("_" #var_name "::"); \ 14 | __asm__(".incbin \"_build/assets/" filename "\""); \ 15 | __asm__("_" #var_name "_end::"); \ 16 | } EXTERN_ASSET(var_name) 17 | 18 | 19 | #endif//GBSDK_ASSETS_H 20 | -------------------------------------------------------------------------------- /inc/sdk/assets.inc: -------------------------------------------------------------------------------- 1 | ; Usage: asset label_name, "filename" 2 | MACRO asset 3 | \1:: 4 | INCBIN STRCAT("_build/assets/", \2) 5 | .end:: 6 | ENDM 7 | -------------------------------------------------------------------------------- /inc/sdk/banking.h: -------------------------------------------------------------------------------- 1 | #ifndef GBSDK_BANKING_H 2 | #define GBSDK_BANKING_H 3 | 4 | #include 5 | 6 | extern __sfr current_bank; 7 | 8 | inline void switch_bank(uint8_t bank_nr) { 9 | current_bank = bank_nr; 10 | *((uint8_t*)0x2000) = bank_nr; 11 | } 12 | #define BANK_OF(symbol) ((uint8_t)&__bank__ ## symbol) 13 | 14 | #endif//GBSDK_BANKING_H 15 | -------------------------------------------------------------------------------- /inc/sdk/hardware.h: -------------------------------------------------------------------------------- 1 | #ifndef GBSDK_HARDWARE_H 2 | #define GBSDK_HARDWARE_H 3 | 4 | // We need to disable warning 182, as SDCC 4.1.0 incorrectly thinks registers are in 0x0000 to 0x00FF range. 5 | // putting the registers in the 0x0000-0x00FF range works most of the time, but sometimes causes incorrect 6 | // code generation. 7 | #pragma disable_warning 182 8 | 9 | // Register for reading joy pad info. (R/W) 10 | static volatile __sfr __at(0xFF00) rP1; 11 | 12 | #define P1_5 0b00100000 // P15 out port, set to 0 to get buttons 13 | #define P1_4 0b00010000 // P14 out port, set to 0 to get dpad 14 | #define P1_3 0b00001000 // P13 in port 15 | #define P1_2 0b00000100 // P12 in port 16 | #define P1_1 0b00000010 // P11 in port 17 | #define P1_0 0b00000001 // P10 in port 18 | 19 | #define P1_GET_DPAD P1_5 20 | #define P1_GET_BTN P1_4 21 | #define P1_GET_NONE (P1_4 | P1_5) 22 | 23 | // Serial Transfer Data (R/W) 24 | static volatile __sfr __at(0xFF01) rSB; 25 | 26 | // Serial I/O Control (R/W) 27 | static volatile __sfr __at(0xFF02) rSC; 28 | 29 | #define SCF_START 0b10000000 // Transfer Start Flag (1=Transfer in progress, or requested) 30 | #define SCF_SPEED 0b00000010 // Clock Speed (0=Normal, 1=Fast) ** CGB Mode Only ** 31 | #define SCF_SOURCE 0b00000001 // Shift Clock (0=External Clock, 1=Internal Clock) 32 | 33 | #define SCB_START 7 34 | #define SCB_SPEED 1 35 | #define SCB_SOURCE 0 36 | 37 | // Divider register (R/W) 38 | static volatile __sfr __at(0xFF04) rDIV; 39 | 40 | // Timer counter (R/W) 41 | static volatile __sfr __at(0xFF05) rTIMA; 42 | 43 | // Timer modulo (R/W) 44 | static volatile __sfr __at(0xFF06) rTMA; 45 | 46 | // Timer control (R/W) 47 | static volatile __sfr __at(0xFF07) rTAC; 48 | 49 | #define TAC_START 0b00000100 50 | #define TAC_STOP 0b00000000 51 | #define TAC_4KHZ 0b00000000 52 | #define TAC_16KHZ 0b00000011 53 | #define TAC_65KHZ 0b00000010 54 | #define TAC_262KHZ 0b00000001 55 | 56 | // Interrupt Flag (R/W) 57 | static volatile __sfr __at(0xFF0F) rIF; 58 | 59 | // AUD1SWEEP/NR10 ($FF10) 60 | // Sweep register (R/W) 61 | // 62 | // Bit 6-4 - Sweep Time 63 | // Bit 3 - Sweep Increase/Decrease 64 | // 0: Addition (frequency increases???) 65 | // 1: Subtraction (frequency increases???) 66 | // Bit 2-0 - Number of sweep shift (# 0-7) 67 | // Sweep Time: (n*7.8ms) 68 | static volatile __sfr __at(0xFF10) rNR10; 69 | #define rAUD1SWEEP rNR10 70 | 71 | #define AUD1SWEEP_UP 0b00000000 72 | #define AUD1SWEEP_DOWN 0b00001000 73 | 74 | // AUD1LEN/NR11 ($FF11) 75 | // Sound length/Wave pattern duty (R/W) 76 | // 77 | // Bit 7-6 - Wave Pattern Duty (00:12.5% 01:25% 10:50% 11:75%) 78 | // Bit 5-0 - Sound length data (# 0-63) 79 | static volatile __sfr __at(0xFF11) rNR11; 80 | #define rAUD1LEN rNR11 81 | 82 | // AUD1ENV/NR12 ($FF12) 83 | // Envelope (R/W) 84 | // 85 | // Bit 7-4 - Initial value of envelope 86 | // Bit 3 - Envelope UP/DOWN 87 | // 0: Decrease 88 | // 1: Range of increase 89 | // Bit 2-0 - Number of envelope sweep (# 0-7) 90 | static volatile __sfr __at(0xFF12) rNR12; 91 | #define rAUD1ENV rNR12 92 | 93 | // AUD1LOW/NR13 ($FF13) 94 | // Frequency low byte (W) 95 | static volatile __sfr __at(0xFF13) rNR13; 96 | #define rAUD1LOW rNR13 97 | 98 | // AUD1HIGH/NR14 ($FF14) 99 | // Frequency high byte (W) 100 | // 101 | // Bit 7 - Initial (when set, sound restarts) 102 | // Bit 6 - Counter/consecutive selection 103 | // Bit 2-0 - Frequency's higher 3 bits 104 | static volatile __sfr __at(0xFF14) rNR14; 105 | #define rAUD1HIGH rNR14 106 | 107 | // AUD2LEN/NR21 ($FF16) 108 | // Sound Length; Wave Pattern Duty (R/W) 109 | // 110 | // see AUD1LEN for info 111 | static volatile __sfr __at(0xFF16) rNR21; 112 | #define rAUD2LEN rNR21 113 | 114 | // AUD2ENV/NR22 ($FF17) 115 | // Envelope (R/W) 116 | // 117 | // see AUD1ENV for info 118 | static volatile __sfr __at(0xFF17) rNR22; 119 | #define rAUD2ENV rNR22 120 | 121 | // AUD2LOW/NR23 ($FF18) 122 | // Frequency low byte (W) 123 | static volatile __sfr __at(0xFF18) rNR23; 124 | #define rAUD2LOW rNR23 125 | 126 | // AUD2HIGH/NR24 ($FF19) 127 | // Frequency high byte (W) 128 | // 129 | // see AUD1HIGH for info 130 | static volatile __sfr __at(0xFF19) rNR24; 131 | #define rAUD2HIGH rNR24 132 | 133 | // AUD3ENA/NR30 ($FF1A) 134 | // Sound on/off (R/W) 135 | // 136 | // Bit 7 - Sound ON/OFF (1=ON,0=OFF) 137 | static volatile __sfr __at(0xFF1A) rNR30; 138 | #define rAUD3ENA rNR30 139 | 140 | #define AUD3ENA_OFF 0b00000000 141 | #define AUD3ENA_ON 0b10000000 142 | 143 | // AUD3LEN/NR31 ($FF1B) 144 | // Sound length (R/W) 145 | // 146 | // Bit 7-0 - Sound length 147 | static volatile __sfr __at(0xFF1B) rNR31; 148 | #define rAUD3LEN rNR31 149 | 150 | // AUD3LEVEL/NR32 ($FF1C) 151 | // Select output level 152 | // 153 | // Bit 6-5 - Select output level 154 | // 00: 0/1 (mute) 155 | // 01: 1/1 156 | // 10: 1/2 157 | // 11: 1/4 158 | static volatile __sfr __at(0xFF1C) rNR32; 159 | #define rAUD3LEVEL rNR32 160 | 161 | #define AUD3LEVEL_MUTE 0b00000000 162 | #define AUD3LEVEL_100 0b00100000 163 | #define AUD3LEVEL_50 0b01000000 164 | #define AUD3LEVEL_25 0b01100000 165 | 166 | // AUD3LOW/NR33 ($FF1D) 167 | // Frequency low byte (W) 168 | // 169 | // see AUD1LOW for info 170 | static volatile __sfr __at(0xFF1D) rNR33; 171 | #define rAUD3LOW rNR33 172 | 173 | // AUD3HIGH/NR34 ($FF1E) 174 | // Frequency high byte (W) 175 | // 176 | // see AUD1HIGH for info 177 | static volatile __sfr __at(0xFF1E) rNR34; 178 | #define rAUD3HIGH rNR34 179 | 180 | // AUD4LEN/NR41 ($FF20) 181 | // Sound length (R/W) 182 | // 183 | // Bit 5-0 - Sound length data (# 0-63) 184 | static volatile __sfr __at(0xFF20) rNR41; 185 | #define rAUD4LEN rNR41 186 | 187 | // AUD4ENV/NR42 ($FF21) 188 | // Envelope (R/W) 189 | // 190 | // see AUD1ENV for info 191 | static volatile __sfr __at(0xFF21) rNR42; 192 | #define rAUD4ENV rNR42 193 | 194 | // AUD4POLY/NR43 ($FF22) 195 | // Polynomial counter (R/W) 196 | // 197 | // Bit 7-4 - Selection of the shift clock frequency of the (scf) 198 | // polynomial counter (0000-1101) 199 | // freq=drf*1/2^scf (not sure) 200 | // Bit 3 - Selection of the polynomial counter's step 201 | // 0: 15 steps 202 | // 1: 7 steps 203 | // Bit 2-0 - Selection of the dividing ratio of frequencies (drf) 204 | // 000: f/4 001: f/8 010: f/16 011: f/24 205 | // 100: f/32 101: f/40 110: f/48 111: f/56 (f=4.194304 Mhz) 206 | static volatile __sfr __at(0xFF22) rNR43; 207 | #define rAUD4POLY rNR43 208 | 209 | #define AUD4POLY_15STEP 0b00000000 210 | #define AUD4POLY_7STEP 0b00001000 211 | 212 | // AUD4GO/NR44 ($FF23) 213 | // 214 | // Bit 7 - Initial (when set, sound restarts) 215 | // Bit 6 - Counter/consecutive selection 216 | static volatile __sfr __at(0xFF23) rNR44; 217 | #define rAUD4GO rNR44 218 | 219 | // AUDVOL/NR50 ($FF24) 220 | // Channel control / ON-OFF / Volume (R/W) 221 | // 222 | // Bit 7 - Vin->SO2 ON/OFF (left) 223 | // Bit 6-4 - SO2 output level (left speaker) (# 0-7) 224 | // Bit 3 - Vin->SO1 ON/OFF (right) 225 | // Bit 2-0 - SO1 output level (right speaker) (# 0-7) 226 | static volatile __sfr __at(0xFF24) rNR50; 227 | #define rAUDVOL rNR50 228 | 229 | #define AUDVOL_VIN_LEFT 0b10000000 // SO2 230 | #define AUDVOL_VIN_RIGHT 0b00001000 // SO1 231 | 232 | // AUDTERM/NR51 ($FF25) 233 | // Selection of Sound output terminal (R/W) 234 | // 235 | // Bit 7 - Output channel 4 to SO2 terminal (left) 236 | // Bit 6 - Output channel 3 to SO2 terminal (left) 237 | // Bit 5 - Output channel 2 to SO2 terminal (left) 238 | // Bit 4 - Output channel 1 to SO2 terminal (left) 239 | // Bit 3 - Output channel 4 to SO1 terminal (right) 240 | // Bit 2 - Output channel 3 to SO1 terminal (right) 241 | // Bit 1 - Output channel 2 to SO1 terminal (right) 242 | // Bit 0 - Output channel 1 to SO1 terminal (right) 243 | static volatile __sfr __at(0xFF25) rNR51; 244 | #define rAUDTERM rNR51 245 | 246 | // SO2 247 | #define AUDTERM_4_LEFT 0b10000000 248 | #define AUDTERM_3_LEFT 0b01000000 249 | #define AUDTERM_2_LEFT 0b00100000 250 | #define AUDTERM_1_LEFT 0b00010000 251 | // SO1 252 | #define AUDTERM_4_RIGHT 0b00001000 253 | #define AUDTERM_3_RIGHT 0b00000100 254 | #define AUDTERM_2_RIGHT 0b00000010 255 | #define AUDTERM_1_RIGHT 0b00000001 256 | 257 | // AUDENA/NR52 ($FF26) 258 | // Sound on/off (R/W) 259 | // 260 | // Bit 7 - All sound on/off (sets all audio regs to 0!) 261 | // Bit 3 - Sound 4 ON flag (read only) 262 | // Bit 2 - Sound 3 ON flag (read only) 263 | // Bit 1 - Sound 2 ON flag (read only) 264 | // Bit 0 - Sound 1 ON flag (read only) 265 | static volatile __sfr __at(0xFF26) rNR52; 266 | #define rAUDENA rNR52 267 | 268 | #define AUDENA_ON 0b10000000 269 | #define AUDENA_OFF 0b00000000 // sets all audio regs to 0! 270 | 271 | // LCDC ($FF40) 272 | // LCD Control (R/W) 273 | static volatile __sfr __at(0xFF40) rLCDC; 274 | 275 | #define LCDC_OFF 0b00000000 // LCD Control Operation 276 | #define LCDC_ON 0b10000000 // LCD Control Operation 277 | #define LCDC_WIN9800 0b00000000 // Window Tile Map Display Select 278 | #define LCDC_WIN9C00 0b01000000 // Window Tile Map Display Select 279 | #define LCDC_WINOFF 0b00000000 // Window Display 280 | #define LCDC_WINON 0b00100000 // Window Display 281 | #define LCDC_BG8800 0b00000000 // BG & Window Tile Data Select 282 | #define LCDC_BG8000 0b00010000 // BG & Window Tile Data Select 283 | #define LCDC_BG9800 0b00000000 // BG Tile Map Display Select 284 | #define LCDC_BG9C00 0b00001000 // BG Tile Map Display Select 285 | #define LCDC_OBJ8 0b00000000 // OBJ Construction 286 | #define LCDC_OBJ16 0b00000100 // OBJ Construction 287 | #define LCDC_OBJOFF 0b00000000 // OBJ Display 288 | #define LCDC_OBJON 0b00000010 // OBJ Display 289 | #define LCDC_BGOFF 0b00000000 // BG Display 290 | #define LCDC_BGON 0b00000001 // BG Display 291 | 292 | // STAT ($FF41) 293 | // LCDC Status (R/W) 294 | static volatile __sfr __at(0xFF41) rSTAT; 295 | 296 | #define STAT_LYC 0b01000000 // LYC=LY Coincidence (Selectable) 297 | #define STAT_MODE10 0b00100000 // Mode 10 298 | #define STAT_MODE01 0b00010000 // Mode 01 (V-Blank) 299 | #define STAT_MODE00 0b00001000 // Mode 00 (H-Blank) 300 | #define STAT_LYCF 0b00000100 // Coincidence Flag 301 | #define STAT_HBL 0b00000000 // H-Blank 302 | #define STAT_VBL 0b00000001 // V-Blank 303 | #define STAT_OAM 0b00000010 // OAM-RAM is used by system 304 | #define STAT_LCD 0b00000011 // Both OAM and VRAM used by system 305 | #define STAT_BUSY 0b00000010 // When set, VRAM access is unsafe 306 | 307 | #define STATB_LYC 6 308 | #define STATB_MODE10 5 309 | #define STATB_MODE01 4 310 | #define STATB_MODE00 3 311 | #define STATB_LYCF 2 312 | #define STATB_BUSY 1 313 | 314 | // SCY ($FF42) 315 | // Scroll Y (R/W) 316 | static volatile __sfr __at(0xFF42) rSCY; 317 | 318 | // SCX ($FF43) 319 | // Scroll X (R/W) 320 | static volatile __sfr __at(0xFF43) rSCX; 321 | 322 | // LY ($FF44) 323 | // LCDC Y-Coordinate (R) 324 | // 325 | // Values range from 0->153. 144->153 is the VBlank period. 326 | static volatile __sfr __at(0xFF44) rLY; 327 | 328 | // LYC ($FF45) 329 | // LY Compare (R/W) 330 | // 331 | // When LY==LYC, STATF_LYCF will be set in STAT 332 | static volatile __sfr __at(0xFF45) rLYC; 333 | 334 | // DMA ($FF46) 335 | // DMA Transfer and Start Address (W) 336 | static volatile __sfr __at(0xFF46) rDMA; 337 | 338 | // BGP ($FF47) 339 | // BG Palette Data (W) 340 | // 341 | // Bit 7-6 - Intensity for %11 342 | // Bit 5-4 - Intensity for %10 343 | // Bit 3-2 - Intensity for %01 344 | // Bit 1-0 - Intensity for %00 345 | static volatile __sfr __at(0xFF47) rBGP; 346 | 347 | // OBP0 ($FF48) 348 | // Object Palette 0 Data (W) 349 | // 350 | // See BGP for info 351 | static volatile __sfr __at(0xFF48) rOBP0; 352 | 353 | // OBP1 ($FF49) 354 | // Object Palette 1 Data (W) 355 | // 356 | // See BGP for info 357 | static volatile __sfr __at(0xFF49) rOBP1; 358 | 359 | // WY ($FF4A) 360 | // Window Y Position (R/W) 361 | // 362 | // 0 <= WY <= 143 363 | // When WY = 0, the window is displayed from the top edge of the LCD screen. 364 | static volatile __sfr __at(0xFF4A) rWY; 365 | 366 | // WX ($FF4B) 367 | // Window X Position (R/W) 368 | // 369 | // 7 <= WX <= 166 370 | // When WX = 7, the window is displayed from the left edge of the LCD screen. 371 | // Values of 0-6 and 166 are unreliable due to hardware bugs. 372 | static volatile __sfr __at(0xFF4B) rWX; 373 | 374 | #define WX_OFS 7 // add this to a screen position to get a WX position 375 | 376 | 377 | #if CGB 378 | // SPEED ($FF4D) 379 | // Select CPU Speed (R/W) 380 | static volatile __sfr __at(0xFF4D) rKEY1; 381 | #define rSPD rKEY1 382 | 383 | #define KEY1_DBLSPEED 0b10000000 // 0=Normal Speed, 1=Double Speed (R) 384 | #define KEY1_PREPARE 0b00000001 // 0=No, 1=Prepare (R/W) 385 | 386 | // VBK ($FF4F) 387 | // Select Video RAM Bank (R/W) 388 | // 389 | // Bit 0 - Bank Specification (0: Specify Bank 0; 1: Specify Bank 1) 390 | static volatile __sfr __at(0xFF4F) rVBK; 391 | 392 | // HDMA1 ($FF51) 393 | // High byte for Horizontal Blanking/General Purpose DMA source address (W) 394 | static volatile __sfr __at(0xFF51) rHDMA1; 395 | 396 | // HDMA2 ($FF52) 397 | // Low byte for Horizontal Blanking/General Purpose DMA source address (W) 398 | static volatile __sfr __at(0xFF52) rHDMA2; 399 | 400 | // HDMA3 ($FF53) 401 | // High byte for Horizontal Blanking/General Purpose DMA destination address (W) 402 | static volatile __sfr __at(0xFF53) rHDMA3; 403 | 404 | // HDMA4 ($FF54) 405 | // Low byte for Horizontal Blanking/General Purpose DMA destination address (W) 406 | static volatile __sfr __at(0xFF54) rHDMA4; 407 | 408 | // HDMA5 ($FF55) 409 | // Transfer length (in tiles minus 1)/mode/start for Horizontal Blanking, General Purpose DMA (R/W) 410 | static volatile __sfr __at(0xFF55) rHDMA5; 411 | 412 | #define HDMA5_MODE_GP 0b00000000 // General Purpose DMA (W) 413 | #define HDMA5_MODE_HBL 0b10000000 // HBlank DMA (W) 414 | 415 | // Once DMA has started, use HDMA5F_BUSY to check when the transfer is complete 416 | #define HDMA5_BUSY 0b10000000 // 0=Busy (DMA still in progress), 1=Transfer complete (R) 417 | 418 | // RP ($FF56) 419 | // Infrared Communications Port (R/W) 420 | static volatile __sfr __at(0xFF56) rRP; 421 | 422 | #define RP_ENREAD 0b11000000 423 | #define RP_DATAIN 0b00000010 // 0=Receiving IR Signal, 1=Normal 424 | #define RP_WRITE_HI 0b00000001 425 | #define RP_WRITE_LO 0b00000000 426 | 427 | // BCPS ($FF68) 428 | // Background Color Palette Specification (R/W) 429 | static volatile __sfr __at(0xFF68) rBCPS; 430 | #define BCPS_AUTOINC 0b10000000 // Auto Increment (0=Disabled, 1=Increment after Writing) 431 | 432 | // BCPD ($FF69) 433 | // Background Color Palette Data (R/W) 434 | static volatile __sfr __at(0xFF69) rBCPD; 435 | 436 | // OCPS ($FF6A) 437 | // Object Color Palette Specification (R/W) 438 | static volatile __sfr __at(0xFF6A) rOCPS; 439 | #define OCPS_AUTOINC 0b10000000 // Auto Increment (0=Disabled, 1=Increment after Writing) 440 | 441 | // OCPD ($FF6B) 442 | // Object Color Palette Data (R/W) 443 | static volatile __sfr __at(0xFF6B) rOCPD; 444 | 445 | // SMBK/SVBK ($FF70) 446 | // Select Main RAM Bank (R/W) 447 | // 448 | // Bit 2-0 - Bank Specification (0,1: Specify Bank 1; 2-7: Specify Banks 2-7) 449 | static volatile __sfr __at(0xFF70) rSVBK; 450 | #define rSMBK rSVBK 451 | 452 | // PCM12 ($FF76) 453 | // Sound channel 1&2 PCM amplitude (R) 454 | // 455 | // Bit 7-4 - Copy of sound channel 2's PCM amplitude 456 | // Bit 3-0 - Copy of sound channel 1's PCM amplitude 457 | static volatile __sfr __at(0xFF76) rPCM12; 458 | 459 | // PCM34 ($FF77) 460 | // Sound channel 3&4 PCM amplitude (R) 461 | // 462 | // Bit 7-4 - Copy of sound channel 4's PCM amplitude 463 | // Bit 3-0 - Copy of sound channel 3's PCM amplitude 464 | static volatile __sfr __at(0xFF77) rPCM34; 465 | 466 | #endif //CGB 467 | 468 | // IE ($FFFF) 469 | // Interrupt Enable (R/W) 470 | static volatile __sfr __at(0xFFFF) rIE; 471 | 472 | #define IE_HILO 0b00010000 // Transition from High to Low of Pin number P10-P13 473 | #define IE_SERIAL 0b00001000 // Serial I/O transfer end 474 | #define IE_TIMER 0b00000100 // Timer Overflow 475 | #define IE_STAT 0b00000010 // STAT 476 | #define IE_VBLANK 0b00000001 // V-Blank 477 | 478 | #define IEB_HILO 4 479 | #define IEB_SERIAL 3 480 | #define IEB_TIMER 2 481 | #define IEB_STAT 1 482 | #define IEB_VBLANK 0 483 | 484 | 485 | /*************************************************************************** 486 | * 487 | * Flags common to multiple sound channels 488 | * 489 | ***************************************************************************/ 490 | 491 | // Square wave duty cycle 492 | // 493 | // Can be used with AUD1LEN and AUD2LEN 494 | // See AUD1LEN for more info 495 | #define AUDLEN_DUTY_12_5 0b00000000 // 12.5% 496 | #define AUDLEN_DUTY_25 0b01000000 // 25% 497 | #define AUDLEN_DUTY_50 0b10000000 // 50% 498 | #define AUDLEN_DUTY_75 0b11000000 // 75% 499 | 500 | // Audio envelope flags 501 | // 502 | // Can be used with AUD1ENV, AUD2ENV, AUD4ENV 503 | // See AUD1ENV for more info 504 | #define AUDENV_UP 0b00001000 505 | #define AUDENV_DOWN 0b00000000 506 | 507 | // Audio trigger flags 508 | // 509 | // Can be used with AUD1HIGH, AUD2HIGH, AUD3HIGH 510 | // See AUD1HIGH for more info 511 | #define AUDHIGH_RESTART 0b10000000 512 | #define AUDHIGH_LENGTH_ON 0b01000000 513 | #define AUDHIGH_LENGTH_OFF 0b00000000 514 | 515 | // Shared bits between the OAM attributes and the CGB background attributes. 516 | #define ATTR_PRI 0b10000000 517 | #define ATTR_YFLIP 0b01000000 518 | #define ATTR_XFLIP 0b00100000 519 | #define ATTR_PAL0 0b00000000 520 | #define ATTR_PAL1 0b00010000 521 | #if CGB 522 | #define ATTR_BANK0 0b00000000 523 | #define ATTR_BANK1 0b00001000 524 | #define ATTR_PALMASK 0b00000111 525 | #endif 526 | 527 | // Deprecated constants. Please avoid using. 528 | #define IE_LCDC 0b00000010 529 | 530 | #endif 531 | -------------------------------------------------------------------------------- /inc/sdk/hardware.inc: -------------------------------------------------------------------------------- 1 | ;* 2 | ;* Gameboy Hardware definitions 3 | ;* 4 | ;* Based on Jones' hardware.inc 5 | ;* And based on Carsten Sorensen's ideas. 6 | ;* 7 | ;* Rev 1.1 - 15-Jul-97 : Added define check 8 | ;* Rev 1.2 - 18-Jul-97 : Added revision check macro 9 | ;* Rev 1.3 - 19-Jul-97 : Modified for RGBASM V1.05 10 | ;* Rev 1.4 - 27-Jul-97 : Modified for new subroutine prefixes 11 | ;* Rev 1.5 - 15-Aug-97 : Added _HRAM, PAD, CART defines 12 | ;* : and Nintendo Logo 13 | ;* Rev 1.6 - 30-Nov-97 : Added rDIV, rTIMA, rTMA, & rTAC 14 | ;* Rev 1.7 - 31-Jan-98 : Added _SCRN0, _SCRN1 15 | ;* Rev 1.8 - 15-Feb-98 : Added rSB, rSC 16 | ;* Rev 1.9 - 16-Feb-98 : Converted I/O registers to $FFXX format 17 | ;* Rev 2.0 - : Added GBC registers 18 | ;* Rev 2.1 - : Added MBC5 & cart RAM enable/disable defines 19 | ;* Rev 2.2 - : Fixed NR42,NR43, & NR44 equates 20 | ;* Rev 2.3 - : Fixed incorrect _HRAM equate 21 | ;* Rev 2.4 - 27-Apr-13 : Added some cart defines (AntonioND) 22 | ;* Rev 2.5 - 03-May-15 : Fixed format (AntonioND) 23 | ;* Rev 2.6 - 09-Apr-16 : Added GBC OAM and cart defines (AntonioND) 24 | ;* Rev 2.7 - 19-Jan-19 : Added rPCMXX (ISSOtm) 25 | ;* Rev 2.8 - 03-Feb-19 : Added audio registers flags (Álvaro Cuesta) 26 | ;* Rev 2.9 - 28-Feb-20 : Added utility rP1 constants 27 | ;* Rev 3.0 - 27-Aug-20 : Register ordering, byte-based sizes, OAM additions, general cleanup (Blitter Object) 28 | ;* Rev 4.0 - 03-May-21 : Updated to use RGBDS 0.5.0 syntax, changed IEF_LCDC to IEF_STAT (Eievui) 29 | ;* Rev 4.1 - 16-Aug-21 : Added more flags, bit number defines, and offset constants for OAM and window positions (rondnelson99) 30 | ;* Rev 4.2 - 04-Sep-21 : Added CH3- and CH4-specific audio registers flags (ISSOtm) 31 | ;* Rev 4.3 - 07-Nov-21 : Deprecate VRAM address constants (Eievui) 32 | ;* Rev 4.4 - 11-Jan-22 : Deprecate VRAM CART_SRAM_2KB constant (avivace) 33 | ;* Rev 4.5 - 03-Mar-22 : Added bit number definitions for OCPS, BCPS and LCDC (sukus) 34 | 35 | IF __RGBDS_MAJOR__ == 0 && __RGBDS_MINOR__ < 5 36 | FAIL "This version of 'hardware.inc' requires RGBDS version 0.5.0 or later." 37 | ENDC 38 | 39 | ; If all of these are already defined, don't do it again. 40 | 41 | IF !DEF(HARDWARE_INC) 42 | DEF HARDWARE_INC EQU 1 43 | 44 | MACRO rev_Check_hardware_inc 45 | ;NOTE: REVISION NUMBER CHANGES MUST BE ADDED 46 | ;TO SECOND PARAMETER IN FOLLOWING LINE. 47 | IF \1 > 4.5 ;PUT REVISION NUMBER HERE 48 | WARN "Version \1 or later of 'hardware.inc' is required." 49 | ENDC 50 | ENDM 51 | 52 | DEF _VRAM EQU $8000 ; $8000->$9FFF 53 | DEF _SCRN0 EQU $9800 ; $9800->$9BFF 54 | DEF _SCRN1 EQU $9C00 ; $9C00->$9FFF 55 | DEF _SRAM EQU $A000 ; $A000->$BFFF 56 | DEF _RAM EQU $C000 ; $C000->$CFFF / $C000->$DFFF 57 | DEF _RAMBANK EQU $D000 ; $D000->$DFFF 58 | DEF _OAMRAM EQU $FE00 ; $FE00->$FE9F 59 | DEF _IO EQU $FF00 ; $FF00->$FF7F,$FFFF 60 | DEF _AUD3WAVERAM EQU $FF30 ; $FF30->$FF3F 61 | DEF _HRAM EQU $FF80 ; $FF80->$FFFE 62 | 63 | ; *** MBC5 Equates *** 64 | 65 | DEF rRAMG EQU $0000 ; $0000->$1fff 66 | DEF rROMB0 EQU $2000 ; $2000->$2fff 67 | DEF rROMB1 EQU $3000 ; $3000->$3fff - If more than 256 ROM banks are present. 68 | DEF rRAMB EQU $4000 ; $4000->$5fff - Bit 3 enables rumble (if present) 69 | 70 | 71 | ;*************************************************************************** 72 | ;* 73 | ;* Custom registers 74 | ;* 75 | ;*************************************************************************** 76 | 77 | ; -- 78 | ; -- P1 ($FF00) 79 | ; -- Register for reading joy pad info. (R/W) 80 | ; -- 81 | DEF rP1 EQU $FF00 82 | 83 | DEF P1F_5 EQU %00100000 ; P15 out port, set to 0 to get buttons 84 | DEF P1F_4 EQU %00010000 ; P14 out port, set to 0 to get dpad 85 | DEF P1F_3 EQU %00001000 ; P13 in port 86 | DEF P1F_2 EQU %00000100 ; P12 in port 87 | DEF P1F_1 EQU %00000010 ; P11 in port 88 | DEF P1F_0 EQU %00000001 ; P10 in port 89 | 90 | DEF P1F_GET_DPAD EQU P1F_5 91 | DEF P1F_GET_BTN EQU P1F_4 92 | DEF P1F_GET_NONE EQU P1F_4 | P1F_5 93 | 94 | 95 | ; -- 96 | ; -- SB ($FF01) 97 | ; -- Serial Transfer Data (R/W) 98 | ; -- 99 | DEF rSB EQU $FF01 100 | 101 | 102 | ; -- 103 | ; -- SC ($FF02) 104 | ; -- Serial I/O Control (R/W) 105 | ; -- 106 | DEF rSC EQU $FF02 107 | 108 | DEF SCF_START EQU %10000000 ;Transfer Start Flag (1=Transfer in progress, or requested) 109 | DEF SCF_SPEED EQU %00000010 ;Clock Speed (0=Normal, 1=Fast) ** CGB Mode Only ** 110 | DEF SCF_SOURCE EQU %00000001 ;Shift Clock (0=External Clock, 1=Internal Clock) 111 | 112 | DEF SCB_START EQU 7 113 | DEF SCB_SPEED EQU 1 114 | DEF SCB_SOURCE EQU 0 115 | 116 | ; -- 117 | ; -- DIV ($FF04) 118 | ; -- Divider register (R/W) 119 | ; -- 120 | DEF rDIV EQU $FF04 121 | 122 | 123 | ; -- 124 | ; -- TIMA ($FF05) 125 | ; -- Timer counter (R/W) 126 | ; -- 127 | DEF rTIMA EQU $FF05 128 | 129 | 130 | ; -- 131 | ; -- TMA ($FF06) 132 | ; -- Timer modulo (R/W) 133 | ; -- 134 | DEF rTMA EQU $FF06 135 | 136 | 137 | ; -- 138 | ; -- TAC ($FF07) 139 | ; -- Timer control (R/W) 140 | ; -- 141 | DEF rTAC EQU $FF07 142 | 143 | DEF TACF_START EQU %00000100 144 | DEF TACF_STOP EQU %00000000 145 | DEF TACF_4KHZ EQU %00000000 146 | DEF TACF_16KHZ EQU %00000011 147 | DEF TACF_65KHZ EQU %00000010 148 | DEF TACF_262KHZ EQU %00000001 149 | 150 | DEF TACB_START EQU 2 151 | 152 | 153 | ; -- 154 | ; -- IF ($FF0F) 155 | ; -- Interrupt Flag (R/W) 156 | ; -- 157 | DEF rIF EQU $FF0F 158 | 159 | 160 | ; -- 161 | ; -- AUD1SWEEP/NR10 ($FF10) 162 | ; -- Sweep register (R/W) 163 | ; -- 164 | ; -- Bit 6-4 - Sweep Time 165 | ; -- Bit 3 - Sweep Increase/Decrease 166 | ; -- 0: Addition (frequency increases???) 167 | ; -- 1: Subtraction (frequency increases???) 168 | ; -- Bit 2-0 - Number of sweep shift (# 0-7) 169 | ; -- Sweep Time: (n*7.8ms) 170 | ; -- 171 | DEF rNR10 EQU $FF10 172 | DEF rAUD1SWEEP EQU rNR10 173 | 174 | DEF AUD1SWEEP_UP EQU %00000000 175 | DEF AUD1SWEEP_DOWN EQU %00001000 176 | 177 | 178 | ; -- 179 | ; -- AUD1LEN/NR11 ($FF11) 180 | ; -- Sound length/Wave pattern duty (R/W) 181 | ; -- 182 | ; -- Bit 7-6 - Wave Pattern Duty (00:12.5% 01:25% 10:50% 11:75%) 183 | ; -- Bit 5-0 - Sound length data (# 0-63) 184 | ; -- 185 | DEF rNR11 EQU $FF11 186 | DEF rAUD1LEN EQU rNR11 187 | 188 | 189 | ; -- 190 | ; -- AUD1ENV/NR12 ($FF12) 191 | ; -- Envelope (R/W) 192 | ; -- 193 | ; -- Bit 7-4 - Initial value of envelope 194 | ; -- Bit 3 - Envelope UP/DOWN 195 | ; -- 0: Decrease 196 | ; -- 1: Range of increase 197 | ; -- Bit 2-0 - Number of envelope sweep (# 0-7) 198 | ; -- 199 | DEF rNR12 EQU $FF12 200 | DEF rAUD1ENV EQU rNR12 201 | 202 | 203 | ; -- 204 | ; -- AUD1LOW/NR13 ($FF13) 205 | ; -- Frequency low byte (W) 206 | ; -- 207 | DEF rNR13 EQU $FF13 208 | DEF rAUD1LOW EQU rNR13 209 | 210 | 211 | ; -- 212 | ; -- AUD1HIGH/NR14 ($FF14) 213 | ; -- Frequency high byte (W) 214 | ; -- 215 | ; -- Bit 7 - Initial (when set, sound restarts) 216 | ; -- Bit 6 - Counter/consecutive selection 217 | ; -- Bit 2-0 - Frequency's higher 3 bits 218 | ; -- 219 | DEF rNR14 EQU $FF14 220 | DEF rAUD1HIGH EQU rNR14 221 | 222 | 223 | ; -- 224 | ; -- AUD2LEN/NR21 ($FF16) 225 | ; -- Sound Length; Wave Pattern Duty (R/W) 226 | ; -- 227 | ; -- see AUD1LEN for info 228 | ; -- 229 | DEF rNR21 EQU $FF16 230 | DEF rAUD2LEN EQU rNR21 231 | 232 | 233 | ; -- 234 | ; -- AUD2ENV/NR22 ($FF17) 235 | ; -- Envelope (R/W) 236 | ; -- 237 | ; -- see AUD1ENV for info 238 | ; -- 239 | DEF rNR22 EQU $FF17 240 | DEF rAUD2ENV EQU rNR22 241 | 242 | 243 | ; -- 244 | ; -- AUD2LOW/NR23 ($FF18) 245 | ; -- Frequency low byte (W) 246 | ; -- 247 | DEF rNR23 EQU $FF18 248 | DEF rAUD2LOW EQU rNR23 249 | 250 | 251 | ; -- 252 | ; -- AUD2HIGH/NR24 ($FF19) 253 | ; -- Frequency high byte (W) 254 | ; -- 255 | ; -- see AUD1HIGH for info 256 | ; -- 257 | DEF rNR24 EQU $FF19 258 | DEF rAUD2HIGH EQU rNR24 259 | 260 | 261 | ; -- 262 | ; -- AUD3ENA/NR30 ($FF1A) 263 | ; -- Sound on/off (R/W) 264 | ; -- 265 | ; -- Bit 7 - Sound ON/OFF (1=ON,0=OFF) 266 | ; -- 267 | DEF rNR30 EQU $FF1A 268 | DEF rAUD3ENA EQU rNR30 269 | 270 | DEF AUD3ENA_OFF EQU %00000000 271 | DEF AUD3ENA_ON EQU %10000000 272 | 273 | 274 | ; -- 275 | ; -- AUD3LEN/NR31 ($FF1B) 276 | ; -- Sound length (R/W) 277 | ; -- 278 | ; -- Bit 7-0 - Sound length 279 | ; -- 280 | DEF rNR31 EQU $FF1B 281 | DEF rAUD3LEN EQU rNR31 282 | 283 | 284 | ; -- 285 | ; -- AUD3LEVEL/NR32 ($FF1C) 286 | ; -- Select output level 287 | ; -- 288 | ; -- Bit 6-5 - Select output level 289 | ; -- 00: 0/1 (mute) 290 | ; -- 01: 1/1 291 | ; -- 10: 1/2 292 | ; -- 11: 1/4 293 | ; -- 294 | DEF rNR32 EQU $FF1C 295 | DEF rAUD3LEVEL EQU rNR32 296 | 297 | DEF AUD3LEVEL_MUTE EQU %00000000 298 | DEF AUD3LEVEL_100 EQU %00100000 299 | DEF AUD3LEVEL_50 EQU %01000000 300 | DEF AUD3LEVEL_25 EQU %01100000 301 | 302 | 303 | ; -- 304 | ; -- AUD3LOW/NR33 ($FF1D) 305 | ; -- Frequency low byte (W) 306 | ; -- 307 | ; -- see AUD1LOW for info 308 | ; -- 309 | DEF rNR33 EQU $FF1D 310 | DEF rAUD3LOW EQU rNR33 311 | 312 | 313 | ; -- 314 | ; -- AUD3HIGH/NR34 ($FF1E) 315 | ; -- Frequency high byte (W) 316 | ; -- 317 | ; -- see AUD1HIGH for info 318 | ; -- 319 | DEF rNR34 EQU $FF1E 320 | DEF rAUD3HIGH EQU rNR34 321 | 322 | 323 | ; -- 324 | ; -- AUD4LEN/NR41 ($FF20) 325 | ; -- Sound length (R/W) 326 | ; -- 327 | ; -- Bit 5-0 - Sound length data (# 0-63) 328 | ; -- 329 | DEF rNR41 EQU $FF20 330 | DEF rAUD4LEN EQU rNR41 331 | 332 | 333 | ; -- 334 | ; -- AUD4ENV/NR42 ($FF21) 335 | ; -- Envelope (R/W) 336 | ; -- 337 | ; -- see AUD1ENV for info 338 | ; -- 339 | DEF rNR42 EQU $FF21 340 | DEF rAUD4ENV EQU rNR42 341 | 342 | 343 | ; -- 344 | ; -- AUD4POLY/NR43 ($FF22) 345 | ; -- Polynomial counter (R/W) 346 | ; -- 347 | ; -- Bit 7-4 - Selection of the shift clock frequency of the (scf) 348 | ; -- polynomial counter (0000-1101) 349 | ; -- freq=drf*1/2^scf (not sure) 350 | ; -- Bit 3 - Selection of the polynomial counter's step 351 | ; -- 0: 15 steps 352 | ; -- 1: 7 steps 353 | ; -- Bit 2-0 - Selection of the dividing ratio of frequencies (drf) 354 | ; -- 000: f/4 001: f/8 010: f/16 011: f/24 355 | ; -- 100: f/32 101: f/40 110: f/48 111: f/56 (f=4.194304 Mhz) 356 | ; -- 357 | DEF rNR43 EQU $FF22 358 | DEF rAUD4POLY EQU rNR43 359 | 360 | DEF AUD4POLY_15STEP EQU %00000000 361 | DEF AUD4POLY_7STEP EQU %00001000 362 | 363 | 364 | ; -- 365 | ; -- AUD4GO/NR44 ($FF23) 366 | ; -- 367 | ; -- Bit 7 - Initial (when set, sound restarts) 368 | ; -- Bit 6 - Counter/consecutive selection 369 | ; -- 370 | DEF rNR44 EQU $FF23 371 | DEF rAUD4GO EQU rNR44 372 | 373 | 374 | ; -- 375 | ; -- AUDVOL/NR50 ($FF24) 376 | ; -- Channel control / ON-OFF / Volume (R/W) 377 | ; -- 378 | ; -- Bit 7 - Vin->SO2 ON/OFF (left) 379 | ; -- Bit 6-4 - SO2 output level (left speaker) (# 0-7) 380 | ; -- Bit 3 - Vin->SO1 ON/OFF (right) 381 | ; -- Bit 2-0 - SO1 output level (right speaker) (# 0-7) 382 | ; -- 383 | DEF rNR50 EQU $FF24 384 | DEF rAUDVOL EQU rNR50 385 | 386 | DEF AUDVOL_VIN_LEFT EQU %10000000 ; SO2 387 | DEF AUDVOL_VIN_RIGHT EQU %00001000 ; SO1 388 | 389 | 390 | ; -- 391 | ; -- AUDTERM/NR51 ($FF25) 392 | ; -- Selection of Sound output terminal (R/W) 393 | ; -- 394 | ; -- Bit 7 - Output channel 4 to SO2 terminal (left) 395 | ; -- Bit 6 - Output channel 3 to SO2 terminal (left) 396 | ; -- Bit 5 - Output channel 2 to SO2 terminal (left) 397 | ; -- Bit 4 - Output channel 1 to SO2 terminal (left) 398 | ; -- Bit 3 - Output channel 4 to SO1 terminal (right) 399 | ; -- Bit 2 - Output channel 3 to SO1 terminal (right) 400 | ; -- Bit 1 - Output channel 2 to SO1 terminal (right) 401 | ; -- Bit 0 - Output channel 1 to SO1 terminal (right) 402 | ; -- 403 | DEF rNR51 EQU $FF25 404 | DEF rAUDTERM EQU rNR51 405 | 406 | ; SO2 407 | DEF AUDTERM_4_LEFT EQU %10000000 408 | DEF AUDTERM_3_LEFT EQU %01000000 409 | DEF AUDTERM_2_LEFT EQU %00100000 410 | DEF AUDTERM_1_LEFT EQU %00010000 411 | ; SO1 412 | DEF AUDTERM_4_RIGHT EQU %00001000 413 | DEF AUDTERM_3_RIGHT EQU %00000100 414 | DEF AUDTERM_2_RIGHT EQU %00000010 415 | DEF AUDTERM_1_RIGHT EQU %00000001 416 | 417 | 418 | ; -- 419 | ; -- AUDENA/NR52 ($FF26) 420 | ; -- Sound on/off (R/W) 421 | ; -- 422 | ; -- Bit 7 - All sound on/off (sets all audio regs to 0!) 423 | ; -- Bit 3 - Sound 4 ON flag (read only) 424 | ; -- Bit 2 - Sound 3 ON flag (read only) 425 | ; -- Bit 1 - Sound 2 ON flag (read only) 426 | ; -- Bit 0 - Sound 1 ON flag (read only) 427 | ; -- 428 | DEF rNR52 EQU $FF26 429 | DEF rAUDENA EQU rNR52 430 | 431 | DEF AUDENA_ON EQU %10000000 432 | DEF AUDENA_OFF EQU %00000000 ; sets all audio regs to 0! 433 | 434 | 435 | ; -- 436 | ; -- LCDC ($FF40) 437 | ; -- LCD Control (R/W) 438 | ; -- 439 | DEF rLCDC EQU $FF40 440 | 441 | DEF LCDCF_OFF EQU %00000000 ; LCD Control Operation 442 | DEF LCDCF_ON EQU %10000000 ; LCD Control Operation 443 | DEF LCDCF_WIN9800 EQU %00000000 ; Window Tile Map Display Select 444 | DEF LCDCF_WIN9C00 EQU %01000000 ; Window Tile Map Display Select 445 | DEF LCDCF_WINOFF EQU %00000000 ; Window Display 446 | DEF LCDCF_WINON EQU %00100000 ; Window Display 447 | DEF LCDCF_BG8800 EQU %00000000 ; BG & Window Tile Data Select 448 | DEF LCDCF_BG8000 EQU %00010000 ; BG & Window Tile Data Select 449 | DEF LCDCF_BG9800 EQU %00000000 ; BG Tile Map Display Select 450 | DEF LCDCF_BG9C00 EQU %00001000 ; BG Tile Map Display Select 451 | DEF LCDCF_OBJ8 EQU %00000000 ; OBJ Construction 452 | DEF LCDCF_OBJ16 EQU %00000100 ; OBJ Construction 453 | DEF LCDCF_OBJOFF EQU %00000000 ; OBJ Display 454 | DEF LCDCF_OBJON EQU %00000010 ; OBJ Display 455 | DEF LCDCF_BGOFF EQU %00000000 ; BG Display 456 | DEF LCDCF_BGON EQU %00000001 ; BG Display 457 | 458 | DEF LCDCB_ON EQU 7 ; LCD Control Operation 459 | DEF LCDCB_WIN9C00 EQU 6 ; Window Tile Map Display Select 460 | DEF LCDCB_WINON EQU 5 ; Window Display 461 | DEF LCDCB_BG8000 EQU 4 ; BG & Window Tile Data Select 462 | DEF LCDCB_BG9C00 EQU 3 ; BG Tile Map Display Select 463 | DEF LCDCB_OBJ16 EQU 2 ; OBJ Construction 464 | DEF LCDCB_OBJON EQU 1 ; OBJ Display 465 | DEF LCDCB_BGON EQU 0 ; BG Display 466 | ; "Window Character Data Select" follows BG 467 | 468 | 469 | ; -- 470 | ; -- STAT ($FF41) 471 | ; -- LCDC Status (R/W) 472 | ; -- 473 | DEF rSTAT EQU $FF41 474 | 475 | DEF STATF_LYC EQU %01000000 ; LYC=LY Coincidence (Selectable) 476 | DEF STATF_MODE10 EQU %00100000 ; Mode 10 477 | DEF STATF_MODE01 EQU %00010000 ; Mode 01 (V-Blank) 478 | DEF STATF_MODE00 EQU %00001000 ; Mode 00 (H-Blank) 479 | DEF STATF_LYCF EQU %00000100 ; Coincidence Flag 480 | DEF STATF_HBL EQU %00000000 ; H-Blank 481 | DEF STATF_VBL EQU %00000001 ; V-Blank 482 | DEF STATF_OAM EQU %00000010 ; OAM-RAM is used by system 483 | DEF STATF_LCD EQU %00000011 ; Both OAM and VRAM used by system 484 | DEF STATF_BUSY EQU %00000010 ; When set, VRAM access is unsafe 485 | 486 | DEF STATB_LYC EQU 6 487 | DEF STATB_MODE10 EQU 5 488 | DEF STATB_MODE01 EQU 4 489 | DEF STATB_MODE00 EQU 3 490 | DEF STATB_LYCF EQU 2 491 | DEF STATB_BUSY EQU 1 492 | 493 | ; -- 494 | ; -- SCY ($FF42) 495 | ; -- Scroll Y (R/W) 496 | ; -- 497 | DEF rSCY EQU $FF42 498 | 499 | 500 | ; -- 501 | ; -- SCX ($FF43) 502 | ; -- Scroll X (R/W) 503 | ; -- 504 | DEF rSCX EQU $FF43 505 | 506 | 507 | ; -- 508 | ; -- LY ($FF44) 509 | ; -- LCDC Y-Coordinate (R) 510 | ; -- 511 | ; -- Values range from 0->153. 144->153 is the VBlank period. 512 | ; -- 513 | DEF rLY EQU $FF44 514 | 515 | 516 | ; -- 517 | ; -- LYC ($FF45) 518 | ; -- LY Compare (R/W) 519 | ; -- 520 | ; -- When LY==LYC, STATF_LYCF will be set in STAT 521 | ; -- 522 | DEF rLYC EQU $FF45 523 | 524 | 525 | ; -- 526 | ; -- DMA ($FF46) 527 | ; -- DMA Transfer and Start Address (W) 528 | ; -- 529 | DEF rDMA EQU $FF46 530 | 531 | 532 | ; -- 533 | ; -- BGP ($FF47) 534 | ; -- BG Palette Data (W) 535 | ; -- 536 | ; -- Bit 7-6 - Intensity for %11 537 | ; -- Bit 5-4 - Intensity for %10 538 | ; -- Bit 3-2 - Intensity for %01 539 | ; -- Bit 1-0 - Intensity for %00 540 | ; -- 541 | DEF rBGP EQU $FF47 542 | 543 | 544 | ; -- 545 | ; -- OBP0 ($FF48) 546 | ; -- Object Palette 0 Data (W) 547 | ; -- 548 | ; -- See BGP for info 549 | ; -- 550 | DEF rOBP0 EQU $FF48 551 | 552 | 553 | ; -- 554 | ; -- OBP1 ($FF49) 555 | ; -- Object Palette 1 Data (W) 556 | ; -- 557 | ; -- See BGP for info 558 | ; -- 559 | DEF rOBP1 EQU $FF49 560 | 561 | 562 | ; -- 563 | ; -- WY ($FF4A) 564 | ; -- Window Y Position (R/W) 565 | ; -- 566 | ; -- 0 <= WY <= 143 567 | ; -- When WY = 0, the window is displayed from the top edge of the LCD screen. 568 | ; -- 569 | DEF rWY EQU $FF4A 570 | 571 | 572 | ; -- 573 | ; -- WX ($FF4B) 574 | ; -- Window X Position (R/W) 575 | ; -- 576 | ; -- 7 <= WX <= 166 577 | ; -- When WX = 7, the window is displayed from the left edge of the LCD screen. 578 | ; -- Values of 0-6 and 166 are unreliable due to hardware bugs. 579 | ; -- 580 | DEF rWX EQU $FF4B 581 | 582 | DEF WX_OFS EQU 7 ; add this to a screen position to get a WX position 583 | 584 | 585 | ; -- 586 | ; -- SPEED ($FF4D) 587 | ; -- Select CPU Speed (R/W) 588 | ; -- 589 | DEF rKEY1 EQU $FF4D 590 | DEF rSPD EQU rKEY1 591 | 592 | DEF KEY1F_DBLSPEED EQU %10000000 ; 0=Normal Speed, 1=Double Speed (R) 593 | DEF KEY1F_PREPARE EQU %00000001 ; 0=No, 1=Prepare (R/W) 594 | 595 | 596 | ; -- 597 | ; -- VBK ($FF4F) 598 | ; -- Select Video RAM Bank (R/W) 599 | ; -- 600 | ; -- Bit 0 - Bank Specification (0: Specify Bank 0; 1: Specify Bank 1) 601 | ; -- 602 | DEF rVBK EQU $FF4F 603 | 604 | 605 | ; -- 606 | ; -- HDMA1 ($FF51) 607 | ; -- High byte for Horizontal Blanking/General Purpose DMA source address (W) 608 | ; -- CGB Mode Only 609 | ; -- 610 | DEF rHDMA1 EQU $FF51 611 | 612 | 613 | ; -- 614 | ; -- HDMA2 ($FF52) 615 | ; -- Low byte for Horizontal Blanking/General Purpose DMA source address (W) 616 | ; -- CGB Mode Only 617 | ; -- 618 | DEF rHDMA2 EQU $FF52 619 | 620 | 621 | ; -- 622 | ; -- HDMA3 ($FF53) 623 | ; -- High byte for Horizontal Blanking/General Purpose DMA destination address (W) 624 | ; -- CGB Mode Only 625 | ; -- 626 | DEF rHDMA3 EQU $FF53 627 | 628 | 629 | ; -- 630 | ; -- HDMA4 ($FF54) 631 | ; -- Low byte for Horizontal Blanking/General Purpose DMA destination address (W) 632 | ; -- CGB Mode Only 633 | ; -- 634 | DEF rHDMA4 EQU $FF54 635 | 636 | 637 | ; -- 638 | ; -- HDMA5 ($FF55) 639 | ; -- Transfer length (in tiles minus 1)/mode/start for Horizontal Blanking, General Purpose DMA (R/W) 640 | ; -- CGB Mode Only 641 | ; -- 642 | DEF rHDMA5 EQU $FF55 643 | 644 | DEF HDMA5F_MODE_GP EQU %00000000 ; General Purpose DMA (W) 645 | DEF HDMA5F_MODE_HBL EQU %10000000 ; HBlank DMA (W) 646 | DEF HDMA5B_MODE EQU 7 ; DMA mode select (W) 647 | 648 | ; -- Once DMA has started, use HDMA5F_BUSY to check when the transfer is complete 649 | DEF HDMA5F_BUSY EQU %10000000 ; 0=Busy (DMA still in progress), 1=Transfer complete (R) 650 | 651 | 652 | ; -- 653 | ; -- RP ($FF56) 654 | ; -- Infrared Communications Port (R/W) 655 | ; -- CGB Mode Only 656 | ; -- 657 | DEF rRP EQU $FF56 658 | 659 | DEF RPF_ENREAD EQU %11000000 660 | DEF RPF_DATAIN EQU %00000010 ; 0=Receiving IR Signal, 1=Normal 661 | DEF RPF_WRITE_HI EQU %00000001 662 | DEF RPF_WRITE_LO EQU %00000000 663 | 664 | 665 | ; -- 666 | ; -- BCPS ($FF68) 667 | ; -- Background Color Palette Specification (R/W) 668 | ; -- 669 | DEF rBCPS EQU $FF68 670 | 671 | DEF BCPSF_AUTOINC EQU %10000000 ; Auto Increment (0=Disabled, 1=Increment after Writing) 672 | DEF BCPSB_AUTOINC EQU 7 673 | 674 | 675 | ; -- 676 | ; -- BCPD ($FF69) 677 | ; -- Background Color Palette Data (R/W) 678 | ; -- 679 | DEF rBCPD EQU $FF69 680 | 681 | 682 | ; -- 683 | ; -- OCPS ($FF6A) 684 | ; -- Object Color Palette Specification (R/W) 685 | ; -- 686 | DEF rOCPS EQU $FF6A 687 | 688 | DEF OCPSF_AUTOINC EQU %10000000 ; Auto Increment (0=Disabled, 1=Increment after Writing) 689 | DEF OCPSB_AUTOINC EQU 7 690 | 691 | 692 | ; -- 693 | ; -- OCPD ($FF6B) 694 | ; -- Object Color Palette Data (R/W) 695 | ; -- 696 | DEF rOCPD EQU $FF6B 697 | 698 | 699 | ; -- 700 | ; -- SMBK/SVBK ($FF70) 701 | ; -- Select Main RAM Bank (R/W) 702 | ; -- 703 | ; -- Bit 2-0 - Bank Specification (0,1: Specify Bank 1; 2-7: Specify Banks 2-7) 704 | ; -- 705 | DEF rSVBK EQU $FF70 706 | DEF rSMBK EQU rSVBK 707 | 708 | 709 | ; -- 710 | ; -- PCM12 ($FF76) 711 | ; -- Sound channel 1&2 PCM amplitude (R) 712 | ; -- 713 | ; -- Bit 7-4 - Copy of sound channel 2's PCM amplitude 714 | ; -- Bit 3-0 - Copy of sound channel 1's PCM amplitude 715 | ; -- 716 | DEF rPCM12 EQU $FF76 717 | 718 | 719 | ; -- 720 | ; -- PCM34 ($FF77) 721 | ; -- Sound channel 3&4 PCM amplitude (R) 722 | ; -- 723 | ; -- Bit 7-4 - Copy of sound channel 4's PCM amplitude 724 | ; -- Bit 3-0 - Copy of sound channel 3's PCM amplitude 725 | ; -- 726 | DEF rPCM34 EQU $FF77 727 | 728 | 729 | ; -- 730 | ; -- IE ($FFFF) 731 | ; -- Interrupt Enable (R/W) 732 | ; -- 733 | DEF rIE EQU $FFFF 734 | 735 | DEF IEF_HILO EQU %00010000 ; Transition from High to Low of Pin number P10-P13 736 | DEF IEF_SERIAL EQU %00001000 ; Serial I/O transfer end 737 | DEF IEF_TIMER EQU %00000100 ; Timer Overflow 738 | DEF IEF_STAT EQU %00000010 ; STAT 739 | DEF IEF_VBLANK EQU %00000001 ; V-Blank 740 | 741 | DEF IEB_HILO EQU 4 742 | DEF IEB_SERIAL EQU 3 743 | DEF IEB_TIMER EQU 2 744 | DEF IEB_STAT EQU 1 745 | DEF IEB_VBLANK EQU 0 746 | 747 | 748 | ;*************************************************************************** 749 | ;* 750 | ;* Flags common to multiple sound channels 751 | ;* 752 | ;*************************************************************************** 753 | 754 | ; -- 755 | ; -- Square wave duty cycle 756 | ; -- 757 | ; -- Can be used with AUD1LEN and AUD2LEN 758 | ; -- See AUD1LEN for more info 759 | ; -- 760 | DEF AUDLEN_DUTY_12_5 EQU %00000000 ; 12.5% 761 | DEF AUDLEN_DUTY_25 EQU %01000000 ; 25% 762 | DEF AUDLEN_DUTY_50 EQU %10000000 ; 50% 763 | DEF AUDLEN_DUTY_75 EQU %11000000 ; 75% 764 | 765 | 766 | ; -- 767 | ; -- Audio envelope flags 768 | ; -- 769 | ; -- Can be used with AUD1ENV, AUD2ENV, AUD4ENV 770 | ; -- See AUD1ENV for more info 771 | ; -- 772 | DEF AUDENV_UP EQU %00001000 773 | DEF AUDENV_DOWN EQU %00000000 774 | 775 | 776 | ; -- 777 | ; -- Audio trigger flags 778 | ; -- 779 | ; -- Can be used with AUD1HIGH, AUD2HIGH, AUD3HIGH 780 | ; -- See AUD1HIGH for more info 781 | ; -- 782 | 783 | DEF AUDHIGH_RESTART EQU %10000000 784 | DEF AUDHIGH_LENGTH_ON EQU %01000000 785 | DEF AUDHIGH_LENGTH_OFF EQU %00000000 786 | 787 | 788 | ;*************************************************************************** 789 | ;* 790 | ;* CPU values on bootup (a=type, b=qualifier) 791 | ;* 792 | ;*************************************************************************** 793 | 794 | DEF BOOTUP_A_DMG EQU $01 ; Dot Matrix Game 795 | DEF BOOTUP_A_CGB EQU $11 ; Color GameBoy 796 | DEF BOOTUP_A_MGB EQU $FF ; Mini GameBoy (Pocket GameBoy) 797 | 798 | ; if a=BOOTUP_A_CGB, bit 0 in b can be checked to determine if real CGB or 799 | ; other system running in GBC mode 800 | DEF BOOTUP_B_CGB EQU %00000000 801 | DEF BOOTUP_B_AGB EQU %00000001 ; GBA, GBA SP, Game Boy Player, or New GBA SP 802 | 803 | 804 | ;*************************************************************************** 805 | ;* 806 | ;* Cart related 807 | ;* 808 | ;*************************************************************************** 809 | 810 | ; $0143 Color GameBoy compatibility code 811 | DEF CART_COMPATIBLE_DMG EQU $00 812 | DEF CART_COMPATIBLE_DMG_GBC EQU $80 813 | DEF CART_COMPATIBLE_GBC EQU $C0 814 | 815 | ; $0146 GameBoy/Super GameBoy indicator 816 | DEF CART_INDICATOR_GB EQU $00 817 | DEF CART_INDICATOR_SGB EQU $03 818 | 819 | ; $0147 Cartridge type 820 | DEF CART_ROM EQU $00 821 | DEF CART_ROM_MBC1 EQU $01 822 | DEF CART_ROM_MBC1_RAM EQU $02 823 | DEF CART_ROM_MBC1_RAM_BAT EQU $03 824 | DEF CART_ROM_MBC2 EQU $05 825 | DEF CART_ROM_MBC2_BAT EQU $06 826 | DEF CART_ROM_RAM EQU $08 827 | DEF CART_ROM_RAM_BAT EQU $09 828 | DEF CART_ROM_MMM01 EQU $0B 829 | DEF CART_ROM_MMM01_RAM EQU $0C 830 | DEF CART_ROM_MMM01_RAM_BAT EQU $0D 831 | DEF CART_ROM_MBC3_BAT_RTC EQU $0F 832 | DEF CART_ROM_MBC3_RAM_BAT_RTC EQU $10 833 | DEF CART_ROM_MBC3 EQU $11 834 | DEF CART_ROM_MBC3_RAM EQU $12 835 | DEF CART_ROM_MBC3_RAM_BAT EQU $13 836 | DEF CART_ROM_MBC5 EQU $19 837 | DEF CART_ROM_MBC5_BAT EQU $1A 838 | DEF CART_ROM_MBC5_RAM_BAT EQU $1B 839 | DEF CART_ROM_MBC5_RUMBLE EQU $1C 840 | DEF CART_ROM_MBC5_RAM_RUMBLE EQU $1D 841 | DEF CART_ROM_MBC5_RAM_BAT_RUMBLE EQU $1E 842 | DEF CART_ROM_MBC7_RAM_BAT_GYRO EQU $22 843 | DEF CART_ROM_POCKET_CAMERA EQU $FC 844 | DEF CART_ROM_BANDAI_TAMA5 EQU $FD 845 | DEF CART_ROM_HUDSON_HUC3 EQU $FE 846 | DEF CART_ROM_HUDSON_HUC1 EQU $FF 847 | 848 | ; $0148 ROM size 849 | ; these are kilobytes 850 | DEF CART_ROM_32KB EQU $00 ; 2 banks 851 | DEF CART_ROM_64KB EQU $01 ; 4 banks 852 | DEF CART_ROM_128KB EQU $02 ; 8 banks 853 | DEF CART_ROM_256KB EQU $03 ; 16 banks 854 | DEF CART_ROM_512KB EQU $04 ; 32 banks 855 | DEF CART_ROM_1024KB EQU $05 ; 64 banks 856 | DEF CART_ROM_2048KB EQU $06 ; 128 banks 857 | DEF CART_ROM_4096KB EQU $07 ; 256 banks 858 | DEF CART_ROM_8192KB EQU $08 ; 512 banks 859 | DEF CART_ROM_1152KB EQU $52 ; 72 banks 860 | DEF CART_ROM_1280KB EQU $53 ; 80 banks 861 | DEF CART_ROM_1536KB EQU $54 ; 96 banks 862 | 863 | ; $0149 SRAM size 864 | ; these are kilobytes 865 | DEF CART_SRAM_NONE EQU 0 866 | DEF CART_SRAM_8KB EQU 2 ; 1 bank 867 | DEF CART_SRAM_32KB EQU 3 ; 4 banks 868 | DEF CART_SRAM_128KB EQU 4 ; 16 banks 869 | 870 | DEF CART_SRAM_ENABLE EQU $0A 871 | DEF CART_SRAM_DISABLE EQU $00 872 | 873 | ; $014A Destination code 874 | DEF CART_DEST_JAPANESE EQU $00 875 | DEF CART_DEST_NON_JAPANESE EQU $01 876 | 877 | 878 | ;*************************************************************************** 879 | ;* 880 | ;* Keypad related 881 | ;* 882 | ;*************************************************************************** 883 | 884 | DEF PADF_DOWN EQU $80 885 | DEF PADF_UP EQU $40 886 | DEF PADF_LEFT EQU $20 887 | DEF PADF_RIGHT EQU $10 888 | DEF PADF_START EQU $08 889 | DEF PADF_SELECT EQU $04 890 | DEF PADF_B EQU $02 891 | DEF PADF_A EQU $01 892 | 893 | DEF PADB_DOWN EQU $7 894 | DEF PADB_UP EQU $6 895 | DEF PADB_LEFT EQU $5 896 | DEF PADB_RIGHT EQU $4 897 | DEF PADB_START EQU $3 898 | DEF PADB_SELECT EQU $2 899 | DEF PADB_B EQU $1 900 | DEF PADB_A EQU $0 901 | 902 | 903 | ;*************************************************************************** 904 | ;* 905 | ;* Screen related 906 | ;* 907 | ;*************************************************************************** 908 | 909 | DEF SCRN_X EQU 160 ; Width of screen in pixels 910 | DEF SCRN_Y EQU 144 ; Height of screen in pixels 911 | DEF SCRN_X_B EQU 20 ; Width of screen in bytes 912 | DEF SCRN_Y_B EQU 18 ; Height of screen in bytes 913 | 914 | DEF SCRN_VX EQU 256 ; Virtual width of screen in pixels 915 | DEF SCRN_VY EQU 256 ; Virtual height of screen in pixels 916 | DEF SCRN_VX_B EQU 32 ; Virtual width of screen in bytes 917 | DEF SCRN_VY_B EQU 32 ; Virtual height of screen in bytes 918 | 919 | 920 | ;*************************************************************************** 921 | ;* 922 | ;* OAM related 923 | ;* 924 | ;*************************************************************************** 925 | 926 | ; OAM attributes 927 | ; each entry in OAM RAM is 4 bytes (sizeof_OAM_ATTRS) 928 | RSRESET 929 | DEF OAMA_Y RB 1 ; y pos plus 16 930 | DEF OAMA_X RB 1 ; x pos plus 8 931 | DEF OAMA_TILEID RB 1 ; tile id 932 | DEF OAMA_FLAGS RB 1 ; flags (see below) 933 | DEF sizeof_OAM_ATTRS RB 0 934 | 935 | DEF OAM_Y_OFS EQU 16 ; add this to a screen-relative Y position to get an OAM Y position 936 | DEF OAM_X_OFS EQU 8 ; add this to a screen-relative X position to get an OAM X position 937 | 938 | DEF OAM_COUNT EQU 40 ; number of OAM entries in OAM RAM 939 | 940 | ; flags 941 | DEF OAMF_PRI EQU %10000000 ; Priority 942 | DEF OAMF_YFLIP EQU %01000000 ; Y flip 943 | DEF OAMF_XFLIP EQU %00100000 ; X flip 944 | DEF OAMF_PAL0 EQU %00000000 ; Palette number; 0,1 (DMG) 945 | DEF OAMF_PAL1 EQU %00010000 ; Palette number; 0,1 (DMG) 946 | DEF OAMF_BANK0 EQU %00000000 ; Bank number; 0,1 (GBC) 947 | DEF OAMF_BANK1 EQU %00001000 ; Bank number; 0,1 (GBC) 948 | 949 | DEF OAMF_PALMASK EQU %00000111 ; Palette (GBC) 950 | 951 | DEF OAMB_PRI EQU 7 ; Priority 952 | DEF OAMB_YFLIP EQU 6 ; Y flip 953 | DEF OAMB_XFLIP EQU 5 ; X flip 954 | DEF OAMB_PAL1 EQU 4 ; Palette number; 0,1 (DMG) 955 | DEF OAMB_BANK1 EQU 3 ; Bank number; 0,1 (GBC) 956 | 957 | 958 | ;* 959 | ;* Nintendo scrolling logo 960 | ;* (Code won't work on a real GameBoy) 961 | ;* (if next lines are altered.) 962 | MACRO NINTENDO_LOGO 963 | DB $CE,$ED,$66,$66,$CC,$0D,$00,$0B,$03,$73,$00,$83,$00,$0C,$00,$0D 964 | DB $00,$08,$11,$1F,$88,$89,$00,$0E,$DC,$CC,$6E,$E6,$DD,$DD,$D9,$99 965 | DB $BB,$BB,$67,$63,$6E,$0E,$EC,$CC,$DD,$DC,$99,$9F,$BB,$B9,$33,$3E 966 | ENDM 967 | 968 | ; Deprecated constants. Please avoid using. 969 | 970 | DEF IEF_LCDC EQU %00000010 ; LCDC (see STAT) 971 | DEF _VRAM8000 EQU _VRAM 972 | DEF _VRAM8800 EQU _VRAM+$800 973 | DEF _VRAM9000 EQU _VRAM+$1000 974 | DEF CART_SRAM_2KB EQU 1 ; 1 incomplete bank 975 | 976 | 977 | ENDC ;HARDWARE_INC 978 | -------------------------------------------------------------------------------- /inc/sdk/interrupt.h: -------------------------------------------------------------------------------- 1 | #ifndef GBSDK_INTERRUPT_H 2 | #define GBSDK_INTERRUPT_H 3 | 4 | #include 5 | 6 | 7 | // Macro to define an interrupt routine. 8 | #define ISR(vector) \ 9 | void __isr_entry__ ## vector () __naked { \ 10 | __asm__(" .pusharea"); \ 11 | __asm__(" .area VECTOR_" #vector); \ 12 | __asm__(" jp ___isr_vector__" #vector); \ 13 | __asm__(" .poparea"); \ 14 | } \ 15 | void __isr_vector__ ## vector () __interrupt(vector) __critical 16 | #define ISR_VBLANK() ISR(0x40) 17 | #define ISR_STAT() ISR(0x48) 18 | #define ISR_TIMER() ISR(0x50) 19 | #define ISR_SERIAL() ISR(0x58) 20 | #define ISR_JOYPAD() ISR(0x60) 21 | 22 | // Defines for specific instructions related to interrupts 23 | #define HALT() __asm__("halt") 24 | #define DISABLE_INTERRUPTS() __asm__("di") 25 | #define ENABLE_INTERRUPTS() __asm__("ei") 26 | 27 | #endif//GBSDK_INTERRUPT_H 28 | -------------------------------------------------------------------------------- /inc/sdk/joypad.h: -------------------------------------------------------------------------------- 1 | #ifndef GBSDK_JOYPAD_H 2 | #define GBSDK_JOYPAD_H 3 | 4 | #include 5 | 6 | #define PAD_DOWN 0x80 7 | #define PAD_UP 0x40 8 | #define PAD_LEFT 0x20 9 | #define PAD_RIGHT 0x10 10 | #define PAD_START 0x08 11 | #define PAD_SELECT 0x04 12 | #define PAD_B 0x02 13 | #define PAD_A 0x01 14 | 15 | extern uint8_t joypad_state; 16 | extern uint8_t joypad_pressed; 17 | 18 | void joypad_update(void) __preserves_regs(b, c); 19 | 20 | #endif//GBSDK_JOYPAD_H 21 | -------------------------------------------------------------------------------- /inc/sdk/oam.h: -------------------------------------------------------------------------------- 1 | #ifndef GBSDK_OAM_H 2 | #define GBSDK_OAM_H 3 | 4 | #include 5 | #include 6 | 7 | #define OAM_Y_OFS 16 // add this to a screen-relative Y position to get an OAM Y position 8 | #define OAM_X_OFS 8 // add this to a screen-relative X position to get an OAM X position 9 | 10 | struct oam_entry { 11 | uint8_t y; 12 | uint8_t x; 13 | uint8_t tile; 14 | uint8_t attr; 15 | }; 16 | extern struct oam_entry shadow_oam[40]; 17 | 18 | void oam_init(void) __preserves_regs(b); 19 | void oam_dma_copy(void) __preserves_regs(b, c, d, e, h, l); 20 | 21 | #endif//GBSDK_OAM_H 22 | -------------------------------------------------------------------------------- /inc/sdk/sgb.h: -------------------------------------------------------------------------------- 1 | #ifndef GBSDK_SGB_H 2 | #define GBSDK_SGB_H 3 | 4 | #include 5 | 6 | #if SGB 7 | 8 | #define SGB_PAL01 0x00 //Set SGB Palette 0 & 1 9 | #define SGB_PAL23 0x01 //Set SGB Palette 2 & 3 10 | #define SGB_PAL03 0x02 //Set SGB Palette 0 & 3 11 | #define SGB_PAL12 0x03 //Set SGB Palette 1 & 2 12 | #define SGB_ATTR_BLK 0x04 //"Block" Area Designation Mode 13 | #define SGB_ATTR_LIN 0x05 //"Line" Area Designation Mode 14 | #define SGB_ATTR_DIV 0x06 //"Divide" Area Designation Mode 15 | #define SGB_ATTR_CHR 0x07 //"1CHR" Area Designation Mode 16 | #define SGB_SOUND 0x08 //Sound On/Off 17 | #define SGB_SOU_TRN 0x09 //Transfer Sound PRG/DATA 18 | #define SGB_PAL_SET 0x0A //Set SGB Palette Indirect 19 | #define SGB_PAL_TRN 0x0B //Set System Color Palette Data 20 | #define SGB_ATRC_EN 0x0C //Enable/disable Attraction Mode 21 | #define SGB_TEST_EN 0x0D //Speed Function 22 | #define SGB_ICON_EN 0x0E //SGB Function 23 | #define SGB_DATA_SND 0x0F //SUPER NES WRAM Transfer 1 24 | #define SGB_DATA_TRN 0x10 //SUPER NES WRAM Transfer 2 25 | #define SGB_MLT_REQ 0x11 //Controller 2 Request 26 | #define SGB_JUMP 0x12 //Set SNES Program Counter 27 | #define SGB_CHR_TRN 0x13 //Transfer Character Font Data 28 | #define SGB_PCT_TRN 0x14 //Set Screen Data Color Data 29 | #define SGB_ATTR_TRN 0x15 //Set Attribute from ATF 30 | #define SGB_ATTR_SET 0x16 //Set Data to ATF 31 | #define SGB_MASK_EN 0x17 //Game Boy Window Mask 32 | #define SGB_OBJ_TRN 0x18 //Super NES OBJ Mode 33 | 34 | #define SGB_HEADER(command, length) ((uint8_t)(((command) << 3) | (length))) 35 | 36 | void sgb_send(const uint8_t* packet) __preserves_regs(b); 37 | #endif 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /inc/sdk/system.h: -------------------------------------------------------------------------------- 1 | #ifndef GBSDK_SYSTEM_H 2 | #define GBSDK_SYSTEM_H 3 | 4 | #include 5 | #include 6 | 7 | #define CPU_DMG 0x01 // Dot Matrix Game / Super GameBoy 1 8 | #define CPU_CGB 0x11 // Color GameBoy / Game Boy Advance 9 | #define CPU_MGB 0xFF // Mini GameBoy (Pocket GameBoy) / Super GameBoy 2 10 | 11 | extern uint8_t cpu_type; // DMG, CGB or MGB 12 | #if CGB 13 | extern bool system_is_gba; // Is the system a GBA or a CGB? (Only valid if cpu_type == CPU_CGB) 14 | #endif 15 | 16 | #endif//GBSDK_SYSTEM_H 17 | -------------------------------------------------------------------------------- /inc/sdk/video.h: -------------------------------------------------------------------------------- 1 | #ifndef GBSDK_VIDEO_H 2 | #define GBSDK_VIDEO_H 3 | 4 | #include 5 | #include 6 | 7 | 8 | //Turn off the LCD, with the proper checks that this happens during VBLANK 9 | void lcd_off(void) __preserves_regs(b, c, d, e, h, l); 10 | 11 | //Copy to VRAM with STAT checks, so these function are safe to write to VRAM at any time. 12 | void vram_memcpy(uint16_t dst, void* src, uint16_t size); 13 | void vram_memset(uint16_t dst, uint8_t value, uint16_t size) __preserves_regs(b); 14 | inline void vram_set(uint16_t dst, uint8_t value) { 15 | while(rSTAT & STAT_BUSY) {} 16 | *((uint8_t*)dst) = value; 17 | } 18 | 19 | #if CGB 20 | #define PAL24(rgb) (((rgb & 0xF80000) >> 19) | ((rgb & 0x00F800) >> 6) | ((rgb & 0x0000F8) << 7)) 21 | 22 | void cgb_background_palette(uint16_t* palette) __preserves_regs(b, c); 23 | void cgb_object_palette(uint16_t* palette) __preserves_regs(b, c); 24 | inline void cgb_gdma(uint16_t dst, const void* src, uint8_t count) { 25 | rHDMA1 = ((uint16_t)src) >> 8; 26 | rHDMA2 = ((uint16_t)src) & 0xFF; 27 | rHDMA3 = dst >> 8; 28 | rHDMA4 = dst & 0xFF; 29 | rHDMA5 = count - 1; 30 | } 31 | inline void cgb_hdma(uint16_t dst, const void* src, uint8_t count) { 32 | rHDMA1 = ((uint16_t)src) >> 8; 33 | rHDMA2 = ((uint16_t)src) & 0xFF; 34 | rHDMA3 = dst >> 8; 35 | rHDMA4 = dst & 0xFF; 36 | rHDMA5 = (count - 1) | 0x80; 37 | } 38 | #endif 39 | 40 | #endif//GBSDK_VIDEO_H 41 | -------------------------------------------------------------------------------- /rules.mk: -------------------------------------------------------------------------------- 1 | .PHONY: all clean 2 | 3 | # SDCC renamed the `gbz80` arch to `sm83` in 4.2.0 4 | ARCH := sm83 5 | SDCC_VERSTRING := $(shell sdcc --version) 6 | ifeq ($(findstring sm83,$(SDCC_VERSTRING)),) 7 | $(info $(SDCC_VERSTRING)) 8 | $(error Installed SDCC version is unsupported. Minimum supported version is 4.2.0) 9 | endif 10 | 11 | MYDIR := $(dir $(lastword $(MAKEFILE_LIST))) 12 | 13 | ifndef PROJECT_NAME 14 | $(error PROJECT_NAME is not set to the name of your rom.) 15 | endif 16 | ifndef MBC 17 | $(error MBC is not set. Should be set to the MBC name or value you want. Example "MBC5+RAM+BATTERY") 18 | endif 19 | ifndef TARGETS 20 | $(error TARGETS is not set. Should be a combination of DMG SGB CGB) 21 | endif 22 | ifneq ($(filter-out DMG SGB CGB,$(TARGETS)),) 23 | $(error TARGETS should be a combination of DMG SGB CGB, unknown: $(filter-out DMG SGB CGB,$(TARGETS))) 24 | endif 25 | 26 | SRC := $(shell find src/ -type f -name '*.c') $(shell find $(MYDIR)/src/ -type f -name '*.c') 27 | ASM := $(shell find src/ -type f -name '*.asm') $(shell find $(MYDIR)/src/ -type f -name '*.asm') 28 | 29 | # Which files do we use from the sdcc libc 30 | LIBC := _memset.c $(ARCH)/memcpy.s 31 | LIBC += _divuchar.c _divschar.c _muluchar.c _mulschar.c _modschar.c _moduchar.c 32 | LIBC += _divuint.c _divsint.c _mulint.c _modsint.c _moduint.c 33 | # the 32 bit functions use 10% of ROM0, so do not include them 34 | #LIBC += _divulong.c _divslong.c _mullong.c _modslong.c _modulong.c 35 | LIBC_PATH := $(subst \,/,$(shell sdcc -m$(ARCH) --print-search-dirs | grep $(ARCH) | tail -n 1))/../src 36 | 37 | ifneq (1,$(words [$(LIBC_PATH)])) 38 | $(error "your environment or sdcc is installed in a path with spaces in it, this is not allowed, as it will break sdcc") 39 | endif 40 | 41 | Q ?= @ 42 | 43 | BUILD := _build 44 | TOOLS := $(MYDIR)/tools 45 | OBJS := $(patsubst %, $(BUILD)/%.o, $(ASM) $(SRC)) $(patsubst %, $(BUILD)/libc/%.o, $(LIBC)) $(BUILD)/gsinit.end.o 46 | 47 | CFLAGS := -m$(ARCH) -Isrc/ -I$(MYDIR)/inc --max-allocs-per-node 25000 48 | ASFLAGS := -isrc/ -i$(MYDIR)/inc -i$(BUILD)/assets/ -Wall 49 | LDFLAGS := --pad 0xFF --wramx 50 | FIXFLAGS := --validate --pad-value 0xFF --title "$(PROJECT_NAME)" --mbc-type "$(MBC)" -l 0x33 51 | ROM_EXTENSION := gb 52 | # Replace spaces with underscores in the project name. 53 | PROJECT_NAME := $(subst $() $(),_,$(PROJECT_NAME)) 54 | 55 | ifeq ($(filter CGB,$(TARGETS)),) # Not targeting CGB, so disable CGB features 56 | CFLAGS += -DCGB=0 57 | ASFLAGS += -DCGB=0 58 | LDFLAGS += --dmg 59 | else 60 | CFLAGS += -DCGB=1 61 | ASFLAGS += -DCGB=1 62 | ifeq ($(filter DMG,$(TARGETS))$(filter SGB,$(TARGETS)),) # Check if not targeting both CGB and DMG 63 | FIXFLAGS += --color-only 64 | else 65 | FIXFLAGS += --color-compatible 66 | endif 67 | ROM_EXTENSION := gbc 68 | endif 69 | ifeq ($(filter SGB,$(TARGETS)),) # Not targeting SGB, so disable SGB features 70 | CFLAGS += -DSGB=0 71 | ASFLAGS += -DSGB=0 72 | else # Targeting SGB as well 73 | CFLAGS += -DSGB=1 74 | ASFLAGS += -DSGB=1 75 | FIXFLAGS += --sgb-compatible 76 | endif 77 | ifneq ($(findstring RAM,$(MBC)),) 78 | FIXFLAGS += --ram-size 2 79 | endif 80 | 81 | all: $(PROJECT_NAME).$(ROM_EXTENSION) 82 | 83 | # Rules to make c files 84 | $(BUILD)/%.c.as: %.c 85 | @echo Compiling $< 86 | @mkdir -p $(dir $@) 87 | $(Q)sdcc $(CFLAGS) -S $< -o $@ -Wp "-MQ $@ -MD $(@:.as=.as.d)" 88 | -include $(patsubst %.c, $(BUILD)/%.c.as.d, $(SRC)) 89 | 90 | $(BUILD)/libc/%.c.as: $(LIBC_PATH)/%.c 91 | @echo Compiling $< 92 | @mkdir -p $(dir $@) 93 | $(Q)sdcc $(CFLAGS) -S $< -o $@ 94 | 95 | $(BUILD)/%.c.asm: $(BUILD)/%.c.as $(TOOLS)/asmconvert.py 96 | $(Q)python3 $(TOOLS)/asmconvert.py $(notdir $<) < $< > $@ 97 | 98 | $(BUILD)/%.c.asm.d: $(BUILD)/%.c.asm 99 | @mkdir -p $(dir $@) 100 | $(Q)rgbasm $(ASFLAGS) $< -M $@ -MT $(@:.asm.d=.o) -MT $@ -MG 101 | # rgbds dependency generation is broken, as it stops after the first missing file. so list all incbins always 102 | $(Q)cat $< | grep -i '^ *INCBIN' | sed -E 's/ *incbin *"([^"]*)"/$(subst /,\/,$(@:.asm.d=.o)): \1/gI' >> $@ 103 | -include $(patsubst %.c, $(BUILD)/%.c.asm.d, $(SRC)) 104 | 105 | $(BUILD)/%.c.o: $(BUILD)/%.c.asm $(BUILD)/%.c.asm.d 106 | @echo "Assembling (c)" $< 107 | @mkdir -p $(dir $@) 108 | $(Q)rgbasm $(ASFLAGS) $< -o $@ 109 | 110 | # Rules to build libc asm files 111 | $(BUILD)/libc/%.s.asm: $(LIBC_PATH)/%.s $(TOOLS)/asmconvert.py 112 | @mkdir -p $(dir $@) 113 | $(Q)python3 $(TOOLS)/asmconvert.py $(notdir $<) < $< > $@ 114 | 115 | $(BUILD)/libc/%.s.o: $(BUILD)/libc/%.s.asm 116 | @echo Assembling $< 117 | @mkdir -p $(dir $@) 118 | $(Q)rgbasm $(ASFLAGS) $< -o $@ 119 | 120 | # Rules to build project asm files 121 | $(BUILD)/%.asm.d: %.asm 122 | @mkdir -p $(dir $@) 123 | $(Q)rgbasm $(ASFLAGS) $< -M $@ -MT $(@:.d=.o) -MT $@ -MG 124 | # rgbds dependency generation is broken, as it stops after the first missing file. so list all incbins always 125 | $(Q)cat $< | grep -i '^ *INCBIN' | sed -E 's/incbin "([^"]*)"/$(subst /,\/,$(@:.d=.o)): \1/gI' >> $@ 126 | 127 | $(BUILD)/%.asm.o: %.asm $(BUILD)/%.asm.d 128 | @echo Assembling $< 129 | @mkdir -p $(dir $@) 130 | $(Q)rgbasm $(ASFLAGS) $< -o $@ 131 | -include $(patsubst %.asm, $(BUILD)/%.asm.d, $(ASM)) 132 | 133 | # Rule to build the final rom 134 | $(PROJECT_NAME).$(ROM_EXTENSION) $(PROJECT_NAME).map $(PROJECT_NAME).sym: $(OBJS) 135 | @echo Linking $@ 136 | $(Q)rgblink $(LDFLAGS) $^ -o $@ -m $(basename $@).map -n $(basename $@).sym 137 | $(Q)rgbfix $(FIXFLAGS) $@ 138 | @python3 $(TOOLS)/romspace.py $(basename $@).map 139 | 140 | # Asset related rules 141 | $(BUILD)/assets/%.2bpp: assets/%.png 142 | @echo Converting $< 143 | @mkdir -p $(dir $@) 144 | $(Q)rgbgfx $< -o $@ 145 | 146 | $(BUILD)/assets/%.oam.2bpp: assets/%.oam.png 147 | @echo Converting $< 148 | @mkdir -p $(dir $@) 149 | $(Q)rgbgfx -h $< -o $@ 150 | 151 | $(BUILD)/assets/%.1bpp: assets/%.png 152 | @echo Converting $< 153 | @mkdir -p $(dir $@) 154 | $(Q)rgbgfx -d 1 $< -o $@ 155 | 156 | # Special hack to place a ret instruction at the end of the GSINIT section. 157 | # This has to be linked last, as that ensures that this fragment is at the end of the "GSINIT" section. 158 | $(BUILD)/gsinit.end.o: 159 | @printf 'SECTION FRAGMENT "GSINIT", ROMX, BANK[1]\n ret' | rgbasm - -o $@ 160 | 161 | clean: 162 | @rm -rf $(BUILD) $(PROJECT_NAME).$(ROM_EXTENSION) $(PROJECT_NAME).map $(PROJECT_NAME).sym 163 | 164 | # Do not remove intermediate files (like assets, c->asm files, etc) 165 | .SECONDARY: 166 | -------------------------------------------------------------------------------- /src/banking.asm: -------------------------------------------------------------------------------- 1 | INCLUDE "sdk/hardware.inc" 2 | 3 | SECTION "BankingHRAM", HRAM 4 | wCurrentBank:: 5 | _current_bank:: ds 1 6 | 7 | SECTION "BankingCode", ROM0 8 | bankedCall:: 9 | ___sdcc_bcall_ehl:: 10 | ldh a, [wCurrentBank] 11 | push af 12 | ld a, e 13 | ldh [wCurrentBank], a 14 | ld [rROMB0], a 15 | rst callHL 16 | pop af 17 | setCurrentBank:: 18 | ldh [wCurrentBank], a 19 | ld [rROMB0], a 20 | ret 21 | 22 | SECTION "callHL", ROM0[$0008] 23 | callHL:: 24 | ___sdcc_call_hl:: 25 | jp hl 26 | -------------------------------------------------------------------------------- /src/entry.asm: -------------------------------------------------------------------------------- 1 | include "sdk/hardware.inc" 2 | 3 | SECTION "gbsdk_systeminfo_ram", WRAM0 4 | _cpu_type:: 5 | wCpuType:: ds 1 6 | 7 | IF CGB 8 | _system_is_gba:: 9 | wSystemIsGba:: ds 1 10 | ENDC 11 | 12 | 13 | SECTION "Header", ROM0[$100] 14 | di 15 | jp startRom 16 | ds $150 - @ 17 | 18 | SECTION "Init", ROMX, BANK[1] 19 | startRom: 20 | ld sp, $E000 ; Set stack pointer to the top of WRAM 21 | ld [wCpuType], a ; Detect system type based on initial register state 22 | IF CGB 23 | ld a, b 24 | ld [wSystemIsGba], a 25 | ENDC 26 | call ___globalsInitCode ; init C globals 27 | call _main 28 | db $DD ; lock up if main returns ($DD is an invalid opcode) 29 | 30 | 31 | SECTION FRAGMENT "GSINIT", ROMX, BANK[1] 32 | ___globalsInitCode: 33 | ; This will be the first fragment of GSINIT. 34 | ld bc, sizeof("INITIALIZER") 35 | assert sizeof("INITIALIZER") == sizeof("INITIALIZED") 36 | ld hl, startof("INITIALIZER") 37 | ld de, startof("INITIALIZED") 38 | : ld a, b 39 | or c 40 | jr z, :+ 41 | ld a, [hl+] 42 | ld [de], a 43 | inc de 44 | dec bc 45 | jr :- 46 | : 47 | 48 | SECTION FRAGMENT "INITIALIZER", ROMX, BANK[1] 49 | ; This will be the first fragment of INITIALIZER. 50 | ; Other fragments can fill this with data which will be copied to WRAM in the "INITIALIZED" fragment 51 | 52 | SECTION FRAGMENT "INITIALIZED", WRAM0 53 | ; This will be the first fragment of INITIALIZED. 54 | -------------------------------------------------------------------------------- /src/joypad.asm: -------------------------------------------------------------------------------- 1 | include "sdk/hardware.inc" 2 | 3 | SECTION "gbsdk_joypad_ram", WRAM0 4 | _joypad_state:: 5 | wJoypadState:: ds 1 6 | _joypad_pressed:: 7 | wJoypadPressed:: ds 1 8 | 9 | 10 | SECTION "gbsdk_joypad_functions", ROM0 11 | _joypad_update:: 12 | ; Call this routine once per frame to update the joypad related variables. 13 | ; Routine also returns the currently pressed buttons in the a register. 14 | ld hl, rP1 15 | ld [hl], P1F_GET_BTN 16 | ; After the initial enable we need to read twice to ensure 17 | ; we get the proper hardware state on real hardware 18 | ld a, [hl] 19 | ld a, [hl] 20 | ld [hl], P1F_GET_DPAD 21 | cpl ; Inputs are active low, so a bit being 0 is a button pressed. So we invert this. 22 | and PADF_A | PADF_B | PADF_SELECT | PADF_START 23 | ld e, a ; Store the lower 4 button bits in e 24 | 25 | ; We need to read rP1 8 times to ensure the proper button state is available. 26 | ; This is only needed on real hardware, as it takes a while for the 27 | ; inputs to change state back from the first set. 28 | ld d, 8 29 | : ld a, [hl] 30 | dec d 31 | jr nz, :- 32 | 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 33 | 34 | swap a ; We want the directional keys as upper 4 bits, so swap the nibbles. 35 | cpl ; Inputs are active low, so a bit being 0 is a button pressed. So we invert this. 36 | and PADF_RIGHT | PADF_LEFT | PADF_UP | PADF_DOWN 37 | or e 38 | ld e, a 39 | 40 | ; Compare the new joypad state with the previous one, and store the 41 | ; new bits in wJoypadPressed 42 | ld hl, wJoypadState 43 | xor [hl] 44 | and e 45 | ld [wJoypadPressed], a 46 | ld a, e 47 | ld [wJoypadState], a 48 | ret 49 | -------------------------------------------------------------------------------- /src/oam.asm: -------------------------------------------------------------------------------- 1 | include "sdk/hardware.inc" 2 | 3 | SECTION "gbsdk_oam_memory", WRAM0, ALIGN[8] 4 | _shadow_oam:: ; OAM Memory is for 40 sprites with 4 bytes per sprite 5 | wShadowOAM:: 6 | ds 40 * 4 7 | .end: 8 | 9 | SECTION "gbsdk_oam_init_function", ROMX, BANK[1] 10 | oamInit:: 11 | _oam_init:: 12 | ld hl, wShadowOAM 13 | xor a 14 | ld c, wShadowOAM.end - wShadowOAM 15 | : ld [hl+], a 16 | dec c 17 | jr nz, :- 18 | 19 | ld hl, hOAMCopyRoutine 20 | ld de, oamCopyRoutine 21 | ld c, hOAMCopyRoutine.end - hOAMCopyRoutine 22 | : ld a, [de] 23 | inc de 24 | ld [hl+], a 25 | dec c 26 | jr nz, :- 27 | 28 | ; We directly copy to clear the initial OAM memory, which else contains garbage. 29 | jp hOAMCopyRoutine 30 | 31 | 32 | oamCopyRoutine: 33 | LOAD "hram", HRAM 34 | _oam_dma_copy:: 35 | hOAMCopyRoutine:: 36 | ld a, HIGH(wShadowOAM) 37 | ldh [rDMA], a 38 | ld a, $28 39 | .wait: 40 | dec a 41 | jr nz, .wait 42 | ret 43 | .end: 44 | ENDL 45 | -------------------------------------------------------------------------------- /src/sgb.asm: -------------------------------------------------------------------------------- 1 | include "sdk/hardware.inc" 2 | 3 | IF SGB 4 | 5 | SECTION "gbsdk_sgb_functions", ROM0 6 | _sgb_send:: 7 | ; SGB RESET pulse 8 | xor a 9 | ldh [rP1], a 10 | push hl ; for delay only 11 | cpl 12 | ldh [rP1], a 13 | pop hl ; for delay only 14 | 15 | ld h, d ; hl=pointer to data to send 16 | ld l, e 17 | ld e, 16 ; amount of bytes to send 18 | 19 | .byteLoop: 20 | ld a, [hl+] 21 | ld d, a ; byte to send in d 22 | ld c, 8 ; amount of bits to send 23 | 24 | .bitLoop: 25 | bit 0, d 26 | ld a, P1F_5 27 | jr z, .zero 28 | ld a, P1F_4 29 | .zero: 30 | ldh [rP1], a 31 | rr d ; 2cycles 32 | ld a, P1F_5 | P1F_4 ; 2cycles 33 | dec c ; 1cycle 34 | ldh [rP1], a 35 | jr nz, .bitLoop 36 | dec e 37 | jr nz, .byteLoop 38 | ld a, P1F_5 39 | ldh [rP1], a 40 | ldh [rP1], a ; (3) 41 | ldh [rP1], a ; (3) 42 | xor a ; (1) 43 | ldh [rP1], a 44 | 45 | ret 46 | 47 | ENDC 48 | -------------------------------------------------------------------------------- /src/video.asm: -------------------------------------------------------------------------------- 1 | include "sdk/hardware.inc" 2 | 3 | SECTION "gbsdk_video_functions", ROM0 4 | _lcd_off:: 5 | : ldh a, [rLY] 6 | cp 144 7 | jr c, :- 8 | xor a 9 | ldh [rLCDC], a 10 | ret 11 | 12 | _vram_memcpy:: 13 | ; src is in bc, dst is in de, size is on the stack 14 | 15 | ld hl, sp + 2 16 | ld a, [hl+] 17 | ld h, [hl] 18 | ld l, a 19 | or a, h 20 | ret z 21 | ; size -> hl 22 | 23 | ld a, h 24 | ld h, d 25 | ld d, a 26 | 27 | ld a, l 28 | ld l, e 29 | ld e, a 30 | ; swap hl and de, so now: 31 | ; src -> bc, dst -> hl, size -> de 32 | inc d 33 | inc e 34 | jr .start 35 | 36 | .copy_loop: 37 | ldh a, [rSTAT] 38 | and a, STATF_BUSY 39 | jr nz, .copy_loop 40 | ld a, [bc] 41 | ld [hl+], a 42 | inc bc 43 | .start: 44 | dec e 45 | jr nz, .copy_loop 46 | dec d 47 | jr nz, .copy_loop 48 | 49 | ret 50 | 51 | _vram_memset:: 52 | ; value is in a, dst is in de, size is on the stack 53 | ld c, a 54 | ; value -> c 55 | 56 | ld hl, sp + 2 57 | ld a, [hl+] 58 | ld h, [hl] 59 | ld l, a 60 | or a, h 61 | ret z 62 | ; size -> hl 63 | 64 | ld a, h 65 | ld h, d 66 | ld d, a 67 | 68 | ld a, l 69 | ld l, e 70 | ld e, a 71 | ; swap hl and de, so now: 72 | ; value -> c, dst -> hl, size -> de 73 | inc d 74 | inc e 75 | jr .start 76 | 77 | .copy_loop: 78 | ldh a, [rSTAT] 79 | and a, STATF_BUSY 80 | jr nz, .copy_loop 81 | ld a, c 82 | ld [hl+], a 83 | .start: 84 | dec e 85 | jr nz, .copy_loop 86 | dec d 87 | jr nz, .copy_loop 88 | 89 | ret 90 | 91 | IF CGB 92 | 93 | _cgb_background_palette:: 94 | ld h, d 95 | ld l, e 96 | ld a, BCPSF_AUTOINC 97 | ldh [rBCPS], a 98 | ld e, 8*4*2 99 | : ld a, [hl+] 100 | ldh [rBCPD], a 101 | dec e 102 | jr nz, :- 103 | ret 104 | 105 | _cgb_object_palette:: 106 | ld h, d 107 | ld l, e 108 | ld a, OCPSF_AUTOINC 109 | ldh [rOCPS], a 110 | ld e, 8*4*2 111 | : ld a, [hl+] 112 | ldh [rOCPD], a 113 | dec e 114 | jr nz, :- 115 | ret 116 | 117 | ENDC 118 | -------------------------------------------------------------------------------- /tools/asmconvert.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import re 3 | 4 | ## Need to convert asxxxx syntax to rgbds syntax 5 | 6 | module = sys.argv[1] 7 | 8 | class Token: 9 | def __init__(self, kind, value, line_nr): 10 | self.kind = kind 11 | self.value = value 12 | self.line_nr = line_nr 13 | 14 | def isA(self, kind, value=None): 15 | return self.kind == kind and (value is None or value == self.value) 16 | 17 | def __repr__(self): 18 | return "[%s:%s:%d]" % (self.kind, self.value, self.line_nr) 19 | 20 | 21 | class Tokenizer: 22 | TOKEN_REGEX = re.compile('|'.join('(?P<%s>%s)' % pair for pair in [ 23 | ('HEX', r'0x[0-9A-Fa-f]+'), 24 | ('ASSIGN', r'='), 25 | ('ALABEL', r'\d+\$'), 26 | ('NUMBER', r'\d+(\.\d*)?'), 27 | ('COMMENT', r';[^\n]*'), 28 | ('LABEL', r':+'), 29 | ('EXPR', r'#'), 30 | ('STRING', '"[^"]*"'), 31 | ('DIRECTIVE', r'\.[A-Za-z_][A-Za-z0-9_\.]*'), 32 | ('ID', r'[A-Za-z_][A-Za-z0-9_\.]*'), 33 | ('OP', r'[+\-*/,\(\)<>]'), 34 | ('NEWLINE', r'\n'), 35 | ('SKIP', r'[ \t]+'), 36 | ('MISMATCH', r'.'), 37 | ])) 38 | 39 | def __init__(self, code): 40 | self.__tokens = [] 41 | line_num = 1 42 | for mo in self.TOKEN_REGEX.finditer(code): 43 | kind = mo.lastgroup 44 | value = mo.group() 45 | if kind == 'MISMATCH': 46 | print("ERR:", code.split("\n")[line_num-1]) 47 | raise RuntimeError("Syntax error on line: %d: %s\n%s", line_num, value) 48 | elif kind == 'SKIP': 49 | pass 50 | else: 51 | if kind == 'HEX': 52 | value = "$" + value[2:] 53 | if kind == 'ALABEL': 54 | value = "._ANNO_" + value[:-1] 55 | kind = "ID" 56 | self.__tokens.append(Token(kind, value, line_num)) 57 | if kind == 'NEWLINE': 58 | line_num += 1 59 | self.__tokens.append(Token('NEWLINE', '\n', line_num)) 60 | 61 | def peek(self): 62 | return self.__tokens[0] 63 | 64 | def pop(self): 65 | return self.__tokens.pop(0) 66 | 67 | def expect(self, kind, value=None): 68 | pop = self.pop() 69 | if not pop.isA(kind, value): 70 | if value is not None: 71 | raise SyntaxError("%s != %s:%s" % (pop, kind, value)) 72 | raise SyntaxError("%s != %s" % (pop, kind)) 73 | 74 | def __bool__(self): 75 | return bool(self.__tokens) 76 | 77 | tok = Tokenizer(sys.stdin.read()) 78 | 79 | def processExpression(): 80 | while True: 81 | t = tok.peek() 82 | if t.isA('EXPR'): 83 | tok.pop() 84 | t = tok.peek() 85 | if t.isA('OP', '<'): 86 | sys.stdout.write('LOW') 87 | tok.pop() 88 | t = tok.peek() 89 | if t.isA('OP', '>'): 90 | sys.stdout.write('HIGH') 91 | tok.pop() 92 | t = tok.peek() 93 | if t.isA('OP', '('): 94 | tok.pop() 95 | sys.stdout.write('(') 96 | processExpression() 97 | t = tok.pop() 98 | assert t.isA('OP', ')') 99 | sys.stdout.write(')') 100 | if t.isA('ID') and t.value.startswith("b_"): 101 | t.value = "BANK(%s)" % (t.value[1:]) 102 | if t.isA('ID') and t.value.startswith("___bank__"): 103 | t.value = "BANK(%s)" % (t.value[8:]) 104 | if t.isA('NEWLINE') or t.isA('OP', ')') or t.isA('OP', ','): 105 | break 106 | sys.stdout.write(t.value) 107 | tok.pop() 108 | 109 | def processParameter(): 110 | t = tok.pop() 111 | if t.isA('EXPR'): 112 | processExpression() 113 | elif t.isA('NEWLINE'): 114 | return 115 | elif t.isA('ID') or t.isA('NUMBER') or t.isA('HEX'): 116 | sys.stdout.write(t.value) 117 | elif t.isA('OP', '('): 118 | sys.stdout.write("[") 119 | processExpression() 120 | t = tok.pop() 121 | while not t.isA('OP', ')'): 122 | sys.stdout.write(t.value) 123 | t = tok.pop() 124 | assert t.isA('OP', ')'), t 125 | sys.stdout.write("]") 126 | else: 127 | raise Exception(t) 128 | 129 | while tok: 130 | start = tok.pop() 131 | if start.isA('NEWLINE'): 132 | pass 133 | elif start.isA('COMMENT'): 134 | print(start.value) 135 | elif start.isA('DIRECTIVE'): 136 | if start.value in {'.module', '.optsdcc', '.globl'}: 137 | while not tok.pop().isA('NEWLINE'): 138 | pass 139 | elif start.value == '.pusharea': 140 | print('PUSHS') 141 | elif start.value == '.poparea': 142 | print('POPS') 143 | elif start.value == '.area': 144 | area_name = tok.pop().value 145 | if area_name == "_DATA": 146 | print('SECTION "%s_%s", WRAM0' % (module, area_name)) 147 | elif area_name == "_INITIALIZED": 148 | print('SECTION FRAGMENT "INITIALIZED", WRAM0') 149 | elif area_name == "_DABS": 150 | print('SECTION "%s_%s", SRAM' % (module, area_name)) 151 | elif area_name == "_HOME": 152 | print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name)) 153 | elif area_name == "_CODE": 154 | print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name)) 155 | elif area_name.startswith("_CODE_"): 156 | print('SECTION FRAGMENT "%s_%s", ROMX, BANK[%d]' % (module, area_name, int(area_name[6:]))) 157 | elif area_name == "_CABS": 158 | print('SECTION FRAGMENT "%s_%s", ROM0' % (module, area_name)) 159 | elif area_name == "_GSINIT": 160 | print('SECTION FRAGMENT "GSINIT", ROMX, BANK[1]') 161 | elif area_name == "_GSFINAL": 162 | print('SECTION FRAGMENT "GSFINAL", ROMX, BANK[1]') 163 | elif area_name == "_INITIALIZER": 164 | print('SECTION FRAGMENT "INITIALIZER", ROMX, BANK[1]') 165 | elif area_name == "_auto": 166 | print('SECTION FRAGMENT "code_%s", ROMX' % (module)) 167 | elif area_name.startswith("VECTOR_"): 168 | print('SECTION "%s", ROM0[$%04x]' % (area_name, int(area_name[7:], 16))) 169 | else: 170 | raise Exception(area_name) 171 | while not tok.pop().isA('NEWLINE'): 172 | pass 173 | elif start.value == '.ds': 174 | sys.stdout.write("ds ") 175 | processExpression() 176 | sys.stdout.write("\n") 177 | elif start.value == '.ascii': 178 | sys.stdout.write("db ") 179 | sys.stdout.write(tok.pop().value) 180 | sys.stdout.write("\n") 181 | elif start.value == '.db' or start.value == '.byte': 182 | sys.stdout.write("db ") 183 | processParameter() 184 | while tok.peek().isA('OP', ','): 185 | sys.stdout.write(",") 186 | tok.pop() 187 | processParameter() 188 | sys.stdout.write("\n") 189 | elif start.value == '.dw': 190 | sys.stdout.write("dw ") 191 | processParameter() 192 | while tok.peek().isA('OP', ','): 193 | sys.stdout.write(",") 194 | tok.pop() 195 | processParameter() 196 | sys.stdout.write("\n") 197 | elif start.value == '.incbin': 198 | sys.stdout.write("incbin ") 199 | while not tok.peek().isA('NEWLINE'): 200 | sys.stdout.write(tok.pop().value) 201 | sys.stdout.write("\n") 202 | tok.pop() 203 | else: 204 | raise Exception(start, tok.peek()) 205 | elif start.isA('ID'): 206 | if tok.peek().isA('ASSIGN'): 207 | tok.pop() 208 | sys.stdout.write("%s = " % (start.value)) 209 | processExpression() 210 | sys.stdout.write("\n") 211 | elif tok.peek().isA('LABEL'): 212 | print("%s%s" % (start.value, tok.pop().value)) 213 | elif start.isA('ID', 'ldhl'): 214 | tok.expect('ID', 'sp') 215 | tok.expect('OP', ',') 216 | sys.stdout.write("ld hl, sp + ") 217 | processParameter() 218 | sys.stdout.write("\n") 219 | elif start.isA('ID', 'lda'): 220 | tok.expect('ID', 'hl') 221 | tok.expect('OP', ',') 222 | t = tok.pop() 223 | assert t.isA('NUMBER') or t.isA('HEX') 224 | tok.expect('OP', '(') 225 | tok.expect('ID', 'sp') 226 | tok.expect('OP', ')') 227 | sys.stdout.write("ld hl, sp + %s\n" % (t.value)) 228 | elif start.isA('ID', 'jp') and tok.peek().isA('OP', '('): 229 | tok.pop() 230 | tok.expect('ID', 'hl') 231 | tok.expect('OP', ')') 232 | sys.stdout.write("jp hl\n") 233 | else: 234 | sys.stdout.write("%s " % (start.value)) 235 | if not tok.peek().isA('NEWLINE'): 236 | processParameter() 237 | if tok.peek().isA('OP', ','): 238 | tok.pop() 239 | sys.stdout.write(", ") 240 | processParameter() 241 | sys.stdout.write("\n") 242 | tok.expect('NEWLINE') 243 | else: 244 | raise Exception(start) 245 | -------------------------------------------------------------------------------- /tools/romspace.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import re 3 | 4 | SIZE = {"ROM0": 0x4000, "ROMX": 0x4000, "WRAM0": 0x2000, "SRAM": 0x2000, "HRAM": 0x007F, "VRAM": 0x4000} 5 | 6 | area_re = re.compile(r'([A-Z0-9]+) bank #([0-9a-f]+):\n') 7 | slack_re = re.compile(r' SLACK: \$([0-9a-f]+) byte') 8 | areas = [] 9 | for line in open(sys.argv[1], "rt"): 10 | area = area_re.match(line) 11 | if area: 12 | area_name = area[1] 13 | bank_nr = int(area[2]) 14 | slack = slack_re.match(line) 15 | if slack: 16 | areas.append((area_name, bank_nr, int(slack[1], 16))) 17 | 18 | # Change size of ROM0 to 8000 if there are no ROMX sections. 19 | # Needed when using the '--tiny' option with RGBLINK. 20 | if not any([x for x in areas if x[0] == "ROMX"]): 21 | SIZE["ROM0"] = 0x8000 22 | 23 | for area in areas: 24 | used = SIZE[area[0]] - area[2] 25 | print("%5s:%2d %04x/%04x (%.1f%%)" % (area[0], area[1], used, SIZE[area[0]], used * 100 / SIZE[area[0]])) 26 | --------------------------------------------------------------------------------