From 6d72ee2c2ebf8bdd3618489b6e94b54511238fb9 Mon Sep 17 00:00:00 2001 From: Forest Belton Date: Mon, 27 Sep 2021 14:01:13 -0400 Subject: [PATCH] Add title screen --- assets/bg/title.png | Bin 0 -> 2836 bytes assets/tiles.png | Bin 245 -> 0 bytes src/actor.c | 24 +++++++++++++++- src/actor.h | 6 ++++ src/game.h | 47 ++++++++++++++++++++++++++++++ src/helpers.asm | 14 +++++++++ src/main.c | 68 +++++++------------------------------------- src/title.c | 44 ++++++++++++++++++++++++++++ 8 files changed, 145 insertions(+), 58 deletions(-) create mode 100644 assets/bg/title.png delete mode 100644 assets/tiles.png create mode 100644 src/game.h create mode 100644 src/helpers.asm create mode 100644 src/title.c diff --git a/assets/bg/title.png b/assets/bg/title.png new file mode 100644 index 0000000000000000000000000000000000000000..4cb5ac0e707c9212a33bcf90bc68ac269b3ebdca GIT binary patch literal 2836 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 literal 0 HcmV?d00001 diff --git a/assets/tiles.png b/assets/tiles.png deleted file mode 100644 index 97f5df93db82879a3b55b6e56f7ac5be3fc4318e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 245 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={W7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0h7I;J!GcX91fH32|H2DgkV3((hV~B_M+un_W4F){SH|IQ*&^d8pd5iMF z-jyH3yXH9U-^zNtsrKNuL*L~RPTyu=t>X!JaNL3ONIq-u2GgV4;yzsE{+Gc0cjk>* zYVON!@{6W*9lo%($tN=X*5lk4Cq&vCmpR*}&PA4x%5bvR?7RI%e2T&n7DZJKO-Bz- crYLJhhTPcdu-LEhKvyw%y85}Sb4q9e0DS~kegFUf diff --git a/src/actor.c b/src/actor.c index 8e4649f..4e51cfa 100644 --- a/src/actor.c +++ b/src/actor.c @@ -9,6 +9,9 @@ #define MAX_ACTORS 5 #define NUM_OAM_ENTRIES 40 #define TILE_WIDTH 8 +#define FRAMES_PER_ANIM_FRAME 20 + +static actor_anim_state_t ANIM_TOTAL_FRAMES[] = {2}; static uint8_t mob_ids[MAX_UNIQUE_MOBS]; static sprite16_mob mob_anim_data[MAX_UNIQUE_MOBS]; @@ -54,12 +57,31 @@ actor_t *actor_create_mob(uint8_t id) { void actor_remove(actor_t *a) { a->active = 0; } +void actor_update(void) { + actor_t *a = &all_actors[0]; + + for (uint8_t i = 0; i < ARRSIZE(all_actors); ++i) { + if (!a->active) { + continue; + } + + a->frame_counter++; + if (a->frame_counter == FRAMES_PER_ANIM_FRAME) { + a->frame_counter = 0; + a->frame_idx = (a->frame_idx + 1) % ANIM_TOTAL_FRAMES[a->anim]; + } + + a++; + } +} + void actor_flush_oam(void) { struct oam_entry *oam_ptr = &shadow_oam[0]; actor_t *a = &all_actors[0]; for (uint8_t i = 0; i < ARRSIZE(all_actors); ++i) { if (!a->active) { + a++; continue; } @@ -116,4 +138,4 @@ static uint8_t actor_load_mob_anim_data(uint8_t mob_id) { } return mob_anim_idx; -} \ No newline at end of file +} diff --git a/src/actor.h b/src/actor.h index 7773ce7..e9f24f9 100644 --- a/src/actor.h +++ b/src/actor.h @@ -16,6 +16,7 @@ typedef enum { typedef struct { uint8_t active; + uint8_t dirty; uint8_t mob_anim_idx; actor_anim_state_t anim; uint8_t frame_idx; @@ -54,6 +55,11 @@ actor_t *actor_create_mob(mob_id_t id); */ void actor_remove(actor_t *a); +/** + * @brief Update the internal state of each actor. + */ +void actor_update(void); + /** * @brief Flush actor sprite data to OAM. */ diff --git a/src/game.h b/src/game.h new file mode 100644 index 0000000..5ffe29f --- /dev/null +++ b/src/game.h @@ -0,0 +1,47 @@ +#ifndef IS_GAME_H_ +#define IS_GAME_H_ + +#define _VRAM 0x8000 +#define _VRAM8000 _VRAM +#define _VRAM8800 (_VRAM + 0x800) +#define _VRAM9000 (_VRAM + 0x1000) +#define _SCRN0 0x9800 +#define _SCRN1 0x9c00 + +#define SCRN_X 160 +#define SCRN_Y 144 +#define SCRN_X_B 20 +#define SCRN_Y_B 18 + +#define SCRN_VX 256 +#define SCRN_VY 256 +#define SCRN_VX_B 32 +#define SCRN_VX_Y 32 + +#define SCREEN_HEIGHT_TILES 18 +#define SCREEN_WIDTH_TILES 20 + +typedef enum { + GAME_STATE_TITLE, + GAME_STATE_LEVEL, + GAME_STATE_MENU, +} game_state_t; + +extern game_state_t game_state; + +/** + * @brief Render the title screen. + */ +void title(void); + +/** + * @brief Enable interrupts. + */ +void interrupts_enable(void) __preserves_regs(a, b, c, d, e, h, l); + +/** + * @brief Disable interrupts. + */ +void interrupts_disable(void) __preserves_regs(a, b, c, d, e, h, l); + +#endif diff --git a/src/helpers.asm b/src/helpers.asm new file mode 100644 index 0000000..df4ff77 --- /dev/null +++ b/src/helpers.asm @@ -0,0 +1,14 @@ +SECTION "vblank", ROM0[$40] + +handle_vblank: + reti + +SECTION "interrupt routines", ROM0 + +_interrupts_disable:: + di + ret + +_interrupts_enable:: + ei + ret diff --git a/src/main.c b/src/main.c index 05ab2c3..8e8894d 100644 --- a/src/main.c +++ b/src/main.c @@ -1,80 +1,34 @@ #include #include "actor.h" -#include "sdk/assets.h" +#include "game.h" #include "sdk/hardware.h" -#include "sdk/joypad.h" #include "sdk/oam.h" -#include "sdk/video.h" -ASSET(tiles, "tiles.2bpp"); - -uint8_t cursor_x = 0; -uint8_t cursor_y = 0; +game_state_t game_state; void main() { - // Load our initial vram (this is slow, but always works, even outside of - // vblank) - vram_memcpy(0x8000, tiles, tiles_end - tiles); - vram_memcpy(0x9000, tiles, tiles_end - tiles); + game_state = GAME_STATE_TITLE; + // Setup the OAM for sprite drawing oam_init(); - // Set some default DMG palette data + // Set up background & sprites rBGP = 0b11100100; rOBP0 = 0b11100100; rOBP1 = 0b11100100; - - // Put some sprites on the screen - shadow_oam[0].y = 0x20; - shadow_oam[0].x = 0x20; - shadow_oam[0].tile = 0x04; - shadow_oam[0].attr = 0x00; - shadow_oam[1].y = 0x24; - shadow_oam[1].x = 0x24; - shadow_oam[1].tile = 0x02; - shadow_oam[1].attr = 0x00; - - // Make sure sprites and the background are drawn - rLCDC = LCDC_ON | LCDC_OBJON | LCDC_BGON; + rLCDC = LCDC_OBJON | LCDC_BGON | LCDC_BG8000; // Setup the VBLANK interrupt, but we don't actually enable interrupt - // handling. - // We only do this, so HALT waits for VBLANK. + // handling. We only do this so HALT waits for VBLANK. rIF = 0; rIE = IE_VBLANK; - vram_memset(0x9800, 0x05, 32 * 18); - for (uint8_t x = 0; x < 20; x++) { - vram_set(0x9800 + x, 0x07); - vram_set(0x9A20 + x, 0x07); - } - for (uint8_t y = 1; y < 18; y++) { - vram_set(0x9800 + y * 0x20, 0x07); - vram_set(0x9800 + 19 + y * 0x20, 0x07); - } - vram_set(0x9800 + 3 + 3 * 0x20, 0x01); - while (1) { - joypad_update(); - if (joypad_state & PAD_LEFT) cursor_x--; - if (joypad_state & PAD_RIGHT) cursor_x++; - if (joypad_state & PAD_UP) cursor_y--; - if (joypad_state & PAD_DOWN) cursor_y++; - if (joypad_state & (PAD_A | PAD_B)) { - vram_set(0x9800 + cursor_x / 8 + cursor_y / 8 * 0x20, 0x03); + switch (game_state) { + case GAME_STATE_TITLE: + title(); + break; } - - // Screen to OAM coordinates - shadow_oam[0].y = cursor_y + 16; - shadow_oam[0].x = cursor_x + 8; - - // Wait for VBLANK - HALT(); - rIF = 0; // As global interrupts are not enabled, we need to clear the - // interrupt flag. - - // Copy the sprites into OAM memory - oam_dma_copy(); } } diff --git a/src/title.c b/src/title.c new file mode 100644 index 0000000..13e2276 --- /dev/null +++ b/src/title.c @@ -0,0 +1,44 @@ +#include + +#include "game.h" +#include "sdk/assets.h" +#include "sdk/hardware.h" +#include "sdk/joypad.h" +#include "sdk/oam.h" +#include "sdk/video.h" + +ASSET(bg_tiles, "bg/title.2bpp"); +ASSET(bg_map, "bg/title.map"); + +void title(void) { + // NOTE: Replace with lcdc_off() if necessary + rLCDC &= ~LCDC_ON; + + // Copy title screen to VRAM + memcpy((uint8_t *)_VRAM, &bg_tiles[0], bg_tiles_end - bg_tiles); + // vram_memcpy(_VRAM, &bg_tiles[0], bg_tiles_end - bg_tiles); + + uint8_t *vram_ptr = (uint8_t *)_SCRN0; + const uint8_t *map_ptr = &bg_map[0]; + + for (uint8_t y = 0; y < SCRN_Y_B; ++y) { + memcpy(vram_ptr, map_ptr, SCRN_X_B); + vram_ptr += SCRN_VX_B; + map_ptr += SCRN_X_B; + } + + rLCDC |= LCDC_ON; + interrupts_enable(); + + while (1) { + joypad_update(); + if (joypad_state & PAD_START) { + game_state = GAME_STATE_LEVEL; + break; + } + + // Wait for VBLANK. Clear IF since global interrupts are disabled. + HALT(); + rIF = 0; + } +}