1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2026-03-10 21:25:31 +00:00
Files
8bitworkshop/presets/gb/hello.c

263 lines
9.9 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//#resource "gb/global.sgb"
//#link "gb/crt0.sgb"
//#link "gb/sfr.sgb"
#include <stdint.h>
#include "gb/types.h"
#include "gb/hardware.h"
#include "gb/gb.h"
/* VRAM regions */
#define TILE_DATA ((volatile uint8_t *)0x8000) /* 384 tiles × 16 bytes */
#define BG_MAP ((volatile uint8_t *)0x9800) /* 32×32 tile map */
/* ---------------------------------------------------------------------------
* wait_vblank
* Spin until LY reaches 144 (start of V-Blank period).
* ------------------------------------------------------------------------- */
static void wait_vblank(void)
{
while (rLY != 144);
}
/* ---------------------------------------------------------------------------
* Minimal 8×8 ASCII font (space 0x20 … 'Z' 0x5A)
* Each character is 8 bytes, one byte per row, MSB = leftmost pixel.
* Only printable upper-case letters, digits, space, and basic punctuation
* are included.
* ------------------------------------------------------------------------- */
static const uint8_t font[][8] = {
/* 0x20 ' ' */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
/* 0x21 '!' */ {0x18,0x18,0x18,0x18,0x00,0x00,0x18,0x00},
/* 0x22 '"' */ {0x6C,0x6C,0x00,0x00,0x00,0x00,0x00,0x00},
/* 0x23 '#' */ {0x6C,0x6C,0xFE,0x6C,0xFE,0x6C,0x6C,0x00},
/* 0x24 '$' */ {0x18,0x7E,0xC0,0x7C,0x06,0xFC,0x18,0x00},
/* 0x25 '%' */ {0xC6,0xCC,0x18,0x30,0x66,0xC6,0x00,0x00},
/* 0x26 '&' */ {0x38,0x6C,0x68,0x76,0xDC,0xCC,0x76,0x00},
/* 0x27 ''' */ {0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00},
/* 0x28 '(' */ {0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00},
/* 0x29 ')' */ {0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00},
/* 0x2A '*' */ {0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00},
/* 0x2B '+' */ {0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00},
/* 0x2C ',' */ {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30},
/* 0x2D '-' */ {0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00},
/* 0x2E '.' */ {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00},
/* 0x2F '/' */ {0x06,0x0C,0x18,0x30,0x60,0xC0,0x00,0x00},
/* 0x30 '0' */ {0x7C,0xC6,0xCE,0xD6,0xE6,0xC6,0x7C,0x00},
/* 0x31 '1' */ {0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00},
/* 0x32 '2' */ {0x7C,0xC6,0x06,0x1C,0x70,0xC6,0xFE,0x00},
/* 0x33 '3' */ {0x7C,0xC6,0x06,0x3C,0x06,0xC6,0x7C,0x00},
/* 0x34 '4' */ {0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x1E,0x00},
/* 0x35 '5' */ {0xFE,0xC0,0xF8,0x0C,0x06,0xCC,0x78,0x00},
/* 0x36 '6' */ {0x38,0x60,0xC0,0xF8,0xCC,0xCC,0x78,0x00},
/* 0x37 '7' */ {0xFE,0xC6,0x0C,0x18,0x30,0x30,0x30,0x00},
/* 0x38 '8' */ {0x78,0xCC,0xCC,0x78,0xCC,0xCC,0x78,0x00},
/* 0x39 '9' */ {0x78,0xCC,0xCC,0x7C,0x0C,0x18,0x70,0x00},
/* 0x3A ':' */ {0x00,0x18,0x18,0x00,0x18,0x18,0x00,0x00},
/* 0x3B ';' */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
/* 0x3C '<' */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
/* 0x3D '=' */ {0x00,0x00,0x7E,0x00,0x7E,0x00,0x00,0x00},
/* 0x3E '>' */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
/* 0x3F '?' */ {0x7C,0xC6,0x06,0x1C,0x18,0x00,0x18,0x00},
/* 0x40 '@' */ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
/* 0x41 'A' */ {0x10,0x38,0x6C,0xC6,0xFE,0xC6,0xC6,0x00},
/* 0x42 'B' */ {0xFC,0xC6,0xC6,0xFC,0xC6,0xC6,0xFC,0x00},
/* 0x43 'C' */ {0x78,0xCC,0xC0,0xC0,0xC0,0xCC,0x78,0x00},
/* 0x44 'D' */ {0xF8,0xCC,0xC6,0xC6,0xC6,0xCC,0xF8,0x00},
/* 0x45 'E' */ {0xFE,0xC0,0xC0,0xF8,0xC0,0xC0,0xFE,0x00},
/* 0x46 'F' */ {0xFE,0xC0,0xC0,0xF8,0xC0,0xC0,0xC0,0x00},
/* 0x47 'G' */ {0x78,0xCC,0xC0,0xDE,0xCC,0xCC,0x7E,0x00},
/* 0x48 'H' */ {0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0x00},
/* 0x49 'I' */ {0x7E,0x18,0x18,0x18,0x18,0x18,0x7E,0x00},
/* 0x4A 'J' */ {0x1E,0x06,0x06,0x06,0xC6,0xC6,0x7C,0x00},
/* 0x4B 'K' */ {0xC6,0xCC,0xD8,0xF0,0xD8,0xCC,0xC6,0x00},
/* 0x4C 'L' */ {0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xFE,0x00},
/* 0x4D 'M' */ {0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0x00},
/* 0x4E 'N' */ {0xC6,0xE6,0xF6,0xDE,0xCE,0xC6,0xC6,0x00},
/* 0x4F 'O' */ {0x78,0xCC,0xCC,0xCC,0xCC,0xCC,0x78,0x00},
/* 0x50 'P' */ {0xFC,0xC6,0xC6,0xFC,0xC0,0xC0,0xC0,0x00},
/* 0x51 'Q' */ {0x78,0xCC,0xCC,0xCC,0xEC,0x78,0x1C,0x00},
/* 0x52 'R' */ {0xFC,0xC6,0xC6,0xFC,0xD8,0xCC,0xC6,0x00},
/* 0x53 'S' */ {0x7C,0xC6,0xC0,0x7C,0x06,0xC6,0x7C,0x00},
/* 0x54 'T' */ {0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x00},
/* 0x55 'U' */ {0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00},
/* 0x56 'V' */ {0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x10,0x00},
/* 0x57 'W' */ {0xC6,0xC6,0xD6,0xFE,0xFE,0xEE,0xC6,0x00},
/* 0x58 'X' */ {0xC6,0x6C,0x38,0x38,0x38,0x6C,0xC6,0x00},
/* 0x59 'Y' */ {0x66,0x66,0x66,0x3C,0x18,0x18,0x18,0x00},
/* 0x5A 'Z' */ {0xFE,0x06,0x0C,0x18,0x30,0x60,0xFE,0x00},
};
#define FONT_FIRST 0x20
#define FONT_LAST 0x5A
#define FONT_COUNT (FONT_LAST - FONT_FIRST + 1)
/* ---------------------------------------------------------------------------
* load_font
* Write font glyphs into VRAM tile data.
* The Game Boy tile format stores two bits per pixel across two bitplanes;
* for a 2-colour font we set both bitplanes to the same byte so colour
* index 3 (black) is used for set pixels and 0 (white) for clear pixels.
* Tile 0 is reserved as blank; glyphs start at tile 1.
* ------------------------------------------------------------------------- */
static void load_font(void)
{
uint8_t t, r;
/* Tile 0: blank (already zeroed by boot ROM, but be explicit) */
for (r = 0; r < 16; r++) {
TILE_DATA[r] = 0x00;
}
/* Tiles 1 … FONT_COUNT: one tile per glyph */
for (t = 0; t < FONT_COUNT; t++) {
volatile uint8_t *dst = TILE_DATA + (t + 1) * 16;
for (r = 0; r < 8; r++) {
uint8_t row = font[t][r];
dst[r * 2] = row; /* bitplane 0 */
dst[r * 2 + 1] = row; /* bitplane 1 — same → colour index 3 */
}
}
}
/* ---------------------------------------------------------------------------
* put_char / put_str
* Write a tile index into the BG map at tile position (col, row).
* Tile index = (ascii - FONT_FIRST) + 1, clamped to blank for unknowns.
* ------------------------------------------------------------------------- */
static void put_char(uint8_t col, uint8_t row, char c)
{
uint8_t tile;
if (c >= FONT_FIRST && c <= FONT_LAST)
tile = (uint8_t)(c - FONT_FIRST) + 1;
else
tile = 0; /* blank */
BG_MAP[row * 32u + col] = tile;
}
static void put_str(uint8_t col, uint8_t row, const char *s)
{
while (*s) {
put_char(col++, row, *s++);
}
}
/* ---------------------------------------------------------------------------
* clear_map
* Fill the visible 20×18 region of the BG map with tile 0 (blank).
* ------------------------------------------------------------------------- */
static void clear_map(void)
{
uint8_t r, c;
for (r = 0; r < 18; r++)
for (c = 0; c < 20; c++)
BG_MAP[r * 32u + c] = 0;
}
/* ---------------------------------------------------------------------------
* read_joypad
* Returns the buttons currently held.
* Bits: 7=start 6=select 5=B 4=A (action keys, P14 low)
* 3=down 2=up 1=left 0=right (d-pad, P15 low)
* We return a simple mask where bit 7 = START, bit 0 = RIGHT.
* ------------------------------------------------------------------------- */
#define BTN_START (1 << 7)
#define BTN_SELECT (1 << 6)
#define BTN_B (1 << 5)
#define BTN_A (1 << 4)
#define BTN_DOWN (1 << 3)
#define BTN_UP (1 << 2)
#define BTN_LEFT (1 << 1)
#define BTN_RIGHT (1 << 0)
static uint8_t read_joypad(void)
{
uint8_t result = 0;
/* Read action buttons (P14 = 0) */
P1_REG = 0x20;
P1_REG; P1_REG; /* dummy reads for settling */
result |= ((~P1_REG) & 0x0F) << 4;
/* Read d-pad (P15 = 0) */
P1_REG = 0x10;
P1_REG; P1_REG;
result |= ((~P1_REG) & 0x0F);
P1_REG = 0x30; /* deselect both */
return result;
}
/* ---------------------------------------------------------------------------
* main
* ------------------------------------------------------------------------- */
void main(void)
{
uint8_t prev_joy = 0;
uint8_t curr_joy = 0;
uint8_t screen = 0;
/* --- Disable LCD so we can safely write to VRAM --- */
wait_vblank();
LCDC_REG = 0x00;
/* --- Load font, clear map, set palette --- */
load_font();
clear_map();
/*
* Palette: 0=white, 1=light grey, 2=dark grey, 3=black
* BGP format: bits[1:0]=colour0, bits[3:2]=colour1, etc.
* 0xE4 = 11 10 01 00 → shade 0→0,1→1,2→2,3→3 (standard)
*/
BGP_REG = 0xE4;
SCX_REG = 0;
SCY_REG = 0;
/* --- Draw initial screen --- */
put_str(3, 1, "GAME BOY DEMO");
put_str(2, 3, "HELLO, WORLD!");
put_str(1, 5, "BUILT WITH SDCC");
put_str(0, 16, "PRESS START");
/*
* Re-enable LCD:
* Bit 7: LCD on
* Bit 4: tile data at 0x8000 (unsigned indexing)
* Bit 0: BG enable
*/
LCDC_REG = LCDCF_ON | LCDCF_WIN9800 | LCDCF_BG8000 | LCDCF_B_BGON;
/* --- Main loop --- */
for (;;) {
wait_vblank();
curr_joy = read_joypad();
/* Edge detect START */
if ((curr_joy & BTN_START) && !(prev_joy & BTN_START)) {
if (screen == 0) {
/* Switch to second screen */
wait_vblank();
LCDC_REG = 0x00;
clear_map();
put_str(1, 4, "YOU PRESSED START!");
put_str(3, 6, "HAVE FUN :)");
put_str(0, 16, "PRESS START AGAIN");
LCDC_REG = LCDCF_ON | LCDCF_B_BGON;
screen = 1;
} else {
/* Back to title */
wait_vblank();
LCDC_REG = 0x00;
clear_map();
put_str(3, 1, "GAME BOY DEMO");
put_str(2, 3, "HELLO, WORLD!");
put_str(1, 5, "BUILT WITH SDCC");
put_str(0, 16, "PRESS START");
LCDC_REG = LCDCF_ON | LCDCF_B_BGON;
screen = 0;
}
}
prev_joy = curr_joy;
}
}