gbdk-2020 | GameBoy Development Kit |
| download: https://git.y1.nz/archives/gbdk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
commit caf8e51f211b6c50a8d977e3a9413c4d694d4e0f parent ef66f382063c4f9da40f18f0fafb27091c48055f Author: bbbbbr <bbbbbr@users.noreply.github.com> Date: Fri, 18 Apr 2025 00:27:30 -0700 Merge pull request #759 from bbbbbr/examples/mbc3_rtc Examples: Game Boy: Minimal MBC3 RTC example Diffstat:
| A | gbdk-lib/examples/gb/mbc3_rtc/Makefile | 94 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | gbdk-lib/examples/gb/mbc3_rtc/Makefile.targets | 54 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | gbdk-lib/examples/gb/mbc3_rtc/Readme.md | 10 | ++++++++++ |
| A | gbdk-lib/examples/gb/mbc3_rtc/src/main.c | 145 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | gbdk-lib/examples/gb/mbc3_rtc/src/mbc3_rtc.c | 30 | ++++++++++++++++++++++++++++++ |
| A | gbdk-lib/examples/gb/mbc3_rtc/src/mbc3_rtc.h | 36 | ++++++++++++++++++++++++++++++++++++ |
6 files changed, 369 insertions(+), 0 deletions(-)
diff --git a/gbdk-lib/examples/gb/mbc3_rtc/Makefile b/gbdk-lib/examples/gb/mbc3_rtc/Makefile @@ -0,0 +1,94 @@ +# If you move this project you can change the directory +# to match your GBDK root directory (ex: GBDK_HOME = "C:/GBDK/" +ifndef GBDK_HOME + GBDK_HOME = ../../../ +endif + +LCC = $(GBDK_HOME)bin/lcc + +# Set platforms to build here, spaced separated. (These are in the separate Makefile.targets) +# They can also be built/cleaned individually: "make gg" and "make gg-clean" +# Possible are: gb gbc pocket megaduck sms gg +TARGETS= gb # gb pocket megaduck sms gg nes + +# Configure platform specific LCC flags here: +LCCFLAGS_gb = -Wl-yt0x10 -Wm-ya4 # Set an MBC for banking (0x10: ROM+MBC3+RTC+RAM+BAT) with 4 cart SRAM banks +LCCFLAGS_pocket = -Wl-yt0x10 -Wm-ya4 # Usually the same as required for .gb +LCCFLAGS_duck = # -Wl-yt0x10 -Wm-ya4 # Usually the same as required for .gb +LCCFLAGS_gbc = -Wl-yt0x10 -Wm-ya4 -Wm-yc # Same as .gb with: -Wm-yc (gb & gbc) or Wm-yC (gbc exclusive) +LCCFLAGS_sms = +LCCFLAGS_gg = +LCCFLAGS_nes = + +LCCFLAGS += $(LCCFLAGS_$(EXT)) # This adds the current platform specific LCC Flags + +LCCFLAGS += -Wl-j # -Wm-yS -Wm-yoA -Wm-ya4 -autobank -Wb-ext=.rel -Wb-v # MBC + Autobanking related flags +# LCCFLAGS += -debug # Uncomment to enable debug output +# LCCFLAGS += -v # Uncomment for lcc verbose output +LCCFLAGS += -Wf-MMD -Wf-Wp-MP # Header file dependency output (-MMD) for Makefile use + per-header Phony rules (-MP) +CFLAGS += -Wf-MMD -Wf-Wp-MP # Header file dependency output (-MMD) for Makefile use + per-header Phony rules (-MP) + +# You can set the name of the ROM file here +PROJECTNAME = mbc3_rtc + +# EXT?=gb # Only sets extension to default (game boy .gb) if not populated +SRCDIR = src +OBJDIR = obj/$(EXT) +RESDIR = res +BINDIR = build/$(EXT) +MKDIRS = $(OBJDIR) $(BINDIR) # See bottom of Makefile for directory auto-creation + +BINS = $(OBJDIR)/$(PROJECTNAME).$(EXT) +CSOURCES = $(foreach dir,$(SRCDIR),$(notdir $(wildcard $(dir)/*.c))) $(foreach dir,$(RESDIR),$(notdir $(wildcard $(dir)/*.c))) + +ASMSOURCES = $(foreach dir,$(SRCDIR),$(notdir $(wildcard $(dir)/*.s))) +OBJS = $(CSOURCES:%.c=$(OBJDIR)/%.o) $(ASMSOURCES:%.s=$(OBJDIR)/%.o) + +# Dependencies (using output from -Wf-MMD -Wf-Wp-MP) +DEPS = $(OBJS:%.o=%.d) + +-include $(DEPS) + +# Builds all targets sequentially +all: $(TARGETS) + +test: + echo $(CSOURCES) + + +# Compile .c files in "src/" to .o object files +$(OBJDIR)/%.o: $(SRCDIR)/%.c + $(LCC) $(CFLAGS) -c -o $@ $< + +# Compile .c files in "res/" to .o object files +$(OBJDIR)/%.o: $(RESDIR)/%.c + $(LCC) $(CFLAGS) -c -o $@ $< + +# Compile .s assembly files in "src/" to .o object files +$(OBJDIR)/%.o: $(SRCDIR)/%.s + $(LCC) $(CFLAGS) -c -o $@ $< + +# If needed, compile .c files in "src/" to .s assembly files +# (not required if .c is compiled directly to .o) +$(OBJDIR)/%.s: $(SRCDIR)/%.c + $(LCC) $(CFLAGS) -S -o $@ $< + +# Link the compiled object files into a .gb ROM file +$(BINS): $(OBJS) + $(LCC) $(LCCFLAGS) $(CFLAGS) -o $(BINDIR)/$(PROJECTNAME).$(EXT) $(OBJS) + +clean: + @echo Cleaning + @for target in $(TARGETS); do \ + $(MAKE) $$target-clean; \ + done + +# Include available build targets +include Makefile.targets + + +# create necessary directories after Makefile is parsed but before build +# info prevents the command from being pasted into the makefile +ifneq ($(strip $(EXT)),) # Only make the directories if EXT has been set by a target +$(info $(shell mkdir -p $(MKDIRS))) +endif diff --git a/gbdk-lib/examples/gb/mbc3_rtc/Makefile.targets b/gbdk-lib/examples/gb/mbc3_rtc/Makefile.targets @@ -0,0 +1,54 @@ + +# Platform specific flags for compiling (only populate if they're both present) +ifneq ($(strip $(PORT)),) +ifneq ($(strip $(PLAT)),) +CFLAGS += -m$(PORT):$(PLAT) +endif +endif + +# Called by the individual targets below to build a ROM +build-target: $(BINS) + +clean-target: + rm -rf $(OBJDIR) + rm -rf $(BINDIR) + +gb-clean: + ${MAKE} clean-target EXT=gb +gb: + ${MAKE} build-target PORT=sm83 PLAT=gb EXT=gb + + +gbc-clean: + ${MAKE} clean-target EXT=gbc +gbc: + ${MAKE} build-target PORT=sm83 PLAT=gb EXT=gbc + + +pocket-clean: + ${MAKE} clean-target EXT=pocket +pocket: + ${MAKE} build-target PORT=sm83 PLAT=ap EXT=pocket + + +megaduck-clean: + ${MAKE} clean-target EXT=duck +megaduck: + ${MAKE} build-target PORT=sm83 PLAT=duck EXT=duck + + +sms-clean: + ${MAKE} clean-target EXT=sms +sms: + ${MAKE} build-target PORT=z80 PLAT=sms EXT=sms + + +gg-clean: + ${MAKE} clean-target EXT=gg +gg: + ${MAKE} build-target PORT=z80 PLAT=gg EXT=gg + +nes-clean: + ${MAKE} clean-target EXT=nes +nes: + ${MAKE} build-target PORT=mos6502 PLAT=nes EXT=nes diff --git a/gbdk-lib/examples/gb/mbc3_rtc/Readme.md b/gbdk-lib/examples/gb/mbc3_rtc/Readme.md @@ -0,0 +1,9 @@ +# GBDK example for the Game Boy MBC3 Real Time Clock (RTC) +How to interface with the MBC3 Real Time Clock + +## RTC example +- Polling the RTC and displaying elapsed time +- Resetting the RTC to zero + +See the Pandocs MBC3 documentation for additional hardware details: +https://gbdev.io/pandocs/MBC3.html + diff --git a/gbdk-lib/examples/gb/mbc3_rtc/src/main.c b/gbdk-lib/examples/gb/mbc3_rtc/src/main.c @@ -0,0 +1,145 @@ +#include <gbdk/platform.h> + +#include <gbdk/console.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> + +#include "mbc3_rtc.h" + + +// MBC3 RTC example +// +// Reads and displays the elapsed time since last +// reset from the Real Time Clock (RTC) on the MBC3 cart. +// +// It also allows resetting all the date/time components to zero. +// +// If your project does not use the included Makefile then +// make sure to set the MBC type in the cart header +// to 0x10 (RTC + SRAM) using this for lcc: -Wl-yt0x10 +// Also set the number of cart SRAM banks: -Wm-ya4 + + +// RTC data +typedef struct rtc_data_t { + uint16_t day; + uint8_t hour; + uint8_t min; + uint8_t sec; +} rtc_data_t; + +rtc_data_t rtc; + +static void read_and_show_rtc_data(rtc_data_t * p_rtc); +static void write_rtc_data(rtc_data_t * p_rtc); + + + +// Example of reading and then displaying RTC date/time +static void read_and_show_rtc_data(rtc_data_t * p_rtc) { + + // Read RTC data + RTC_LATCH(); + p_rtc->day = RTC_GET(RTC_VALUE_DAY); + p_rtc->hour = RTC_GET(RTC_VALUE_HOUR); + p_rtc->min = RTC_GET(RTC_VALUE_MIN); + p_rtc->sec = RTC_GET(RTC_VALUE_SEC); + + gotoxy(0,6); + printf("Elapsed RTC Time:\n"); + printf("Days: %u \n" + "Time: %u:%u:%u \n", + (uint16_t)p_rtc->day, + (uint16_t)p_rtc->hour, + (uint16_t)p_rtc->min, + (uint16_t)p_rtc->sec); +} + + +static void write_rtc_data(rtc_data_t * p_rtc) { + + // In this example just zero out the RTC data + // so that when it's read back it will indicate + // how much time has elapsed since setting it. + p_rtc->day = 0u; // Supported range: 0-511 + p_rtc->hour = 0u; // Supported range: 0-23 + p_rtc->min = 0u; // Supported range: 0-59 + p_rtc->sec = 0u; // Supported range: 0-59 + + RTC_SET(RTC_VALUE_DAY, p_rtc->day); + RTC_SET(RTC_VALUE_HOUR, p_rtc->hour); + RTC_SET(RTC_VALUE_MIN, p_rtc->min); + RTC_SET(RTC_VALUE_SEC, p_rtc->sec); + + gotoxy(0,11); + printf("Set RTC to:\n"); + printf("Days: %u \n" + "Time: %u:%u:%u \n", + (uint16_t)p_rtc->day, + (uint16_t)p_rtc->hour, + (uint16_t)p_rtc->min, + (uint16_t)p_rtc->sec); +} + + +static void clear_rtc_send_message(void) { + + gotoxy(0,11); + printf(" \n"); + printf(" \n" + " \n"); +} + + +void main(void) { + + // Enable RAM for RTC register access and start the RTC ticking + ENABLE_RAM; + RTC_START(true); + + uint8_t gamepad; + + SPRITES_8x8; + SHOW_SPRITES; + SHOW_BKG; + + printf("RTC Date/Time Demo\n" + "\n" + "* Press SELECT to \n" + " set new RTC info.\n"); + + uint8_t clear_notice_count = 0; + while(1) { + + // Wait for the start of a new frame, then read the gamepad + vsync(); + gamepad = joypad(); + + // Send RTC data to device if SELECT is pressed + // otherwise check if it's time to read and show RTC data + if (gamepad & J_SELECT) { + write_rtc_data(&rtc); + // Queue clearing the "sent rtc date/time" message after a few seconds + clear_notice_count = 6; + + // Wait until SELECT is released before continuing + waitpadup(); + } + else { + // Update Displayed RTC data every 32 frames (about twice per second) + if ((sys_time & 0x1Fu) == 0u) { + read_and_show_rtc_data(&rtc); + + if (clear_notice_count) { + clear_notice_count--; + if (clear_notice_count == 0) { + clear_rtc_send_message(); + } + } + } + } + + } + +} diff --git a/gbdk-lib/examples/gb/mbc3_rtc/src/mbc3_rtc.c b/gbdk-lib/examples/gb/mbc3_rtc/src/mbc3_rtc.c @@ -0,0 +1,30 @@ +#include <gbdk/platform.h> + +#include "mbc3_rtc.h" + +volatile uint8_t _current_ram_bank; + +uint16_t RTC_GET(const rtc_dateparts_e part) { + uint16_t v; + RTC_SELECT(part); + v = RTC_VALUE_REG; + if (part == RTC_VALUE_DAY) { + RTC_SELECT(RTC_VALUE_FLAGS); + if (RTC_VALUE_REG & 0x01) v |= 0x0100u; + } + return v; +} + +void RTC_SET(const rtc_dateparts_e part, const uint16_t v) { + RTC_SELECT(part); + RTC_VALUE_REG = v; + if (part == RTC_VALUE_DAY) { + RTC_SELECT(RTC_VALUE_FLAGS); + RTC_VALUE_REG = (RTC_VALUE_REG & 0x0e) | (uint8_t)((v >> 8) & 0x01); + } +} + +void RTC_START(const uint8_t start) { + RTC_SELECT(RTC_VALUE_FLAGS); + if (start) RTC_VALUE_REG &= ~RTC_TIMER_STOP; else RTC_VALUE_REG |= RTC_TIMER_STOP; +} diff --git a/gbdk-lib/examples/gb/mbc3_rtc/src/mbc3_rtc.h b/gbdk-lib/examples/gb/mbc3_rtc/src/mbc3_rtc.h @@ -0,0 +1,35 @@ +#ifndef _RTC_H_INCLUDE +#define _RTC_H_INCLUDE + +#include <gbdk/platform.h> +#include <stdint.h> + +volatile uint8_t AT(0x4000) RTC_SELECT_REG; +volatile uint8_t AT(0x6000) RTC_LATCH_REG; +volatile uint8_t AT(0xA000) RTC_VALUE_REG; + +#define RTC_TIMER_STOP 0b01000000 + +typedef enum { + RTC_VALUE_SEC = 0x08, + RTC_VALUE_MIN, + RTC_VALUE_HOUR, + RTC_VALUE_DAY +} rtc_dateparts_e; + +#define RTC_VALUE_FLAGS 0x0c + +#define RAM_BANKS_ONLY 0x0fu +#define RAM_BANKS_AND_FLAGS 0xffu + +extern volatile uint8_t _current_ram_bank; + +inline void SWITCH_RAM_BANK(uint8_t bank, uint8_t mask) { SWITCH_RAM(_current_ram_bank = ((_current_ram_bank & ~mask) | (bank & mask))); } +inline void RTC_SELECT(uint8_t what) { SWITCH_RAM_BANK(what, RAM_BANKS_ONLY); } +inline void RTC_LATCH(void) { RTC_LATCH_REG = 0; RTC_LATCH_REG = 1; } + +uint16_t RTC_GET(const rtc_dateparts_e part); +void RTC_SET(const rtc_dateparts_e part, const uint16_t v); +void RTC_START(const uint8_t start); + +#endif +
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.