git.y1.nz

gbdk-2020

GameBoy Development Kit
download: https://git.y1.nz/archives/gbdk.tar.gz
README | Files | Log | Refs | LICENSE

commit 7d64b490f4612cff3459b517908e781f42280308
parent fb8d8b85068265ef558b8e21779a645c685ed067
Author: Toxa <untoxa@mail.ru>
Date:   Mon, 21 Jul 2025 13:36:29 +0300

Merge branch 'develop' of https://github.com/gbdk-2020/gbdk-2020 into develop

Diffstat:
Mgbdk-lib/examples/gb/mbc5_rumble/Makefile8++++++--
Mgbdk-lib/examples/gb/mbc5_rumble/Readme.md31++++++++++++++++++++++---------
Mgbdk-lib/examples/gb/mbc5_rumble/src/main.c83++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Mgbdk-lib/examples/gb/mbc5_rumble/src/mbc5_rumble.c98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Mgbdk-lib/examples/gb/mbc5_rumble/src/mbc5_rumble.h62+++++++++++++++++++++++++++++++++++++++++++++++++++-----------
5 files changed, 229 insertions(+), 53 deletions(-)

diff --git a/gbdk-lib/examples/gb/mbc5_rumble/Makefile b/gbdk-lib/examples/gb/mbc5_rumble/Makefile @@ -23,14 +23,18 @@ 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) +# GBDK_DEBUG = ON +ifdef GBDK_DEBUG + LCCFLAGS += -debug -v +endif + # You can set the name of the ROM file here PROJECTNAME = mbc5_rumble + # EXT?=gb # Only sets extension to default (game boy .gb) if not populated SRCDIR = src OBJDIR = obj/$(EXT) diff --git a/gbdk-lib/examples/gb/mbc5_rumble/Readme.md b/gbdk-lib/examples/gb/mbc5_rumble/Readme.md @@ -1,14 +1,27 @@ -# GBDK example for using the Rumble motor on the Game Boy MBC5 Cartridge -This example demonstrates using the Rumble motor that can be on MBC5 cartridges -The Rumble motor was infamously used in Pokemon Pinball +# GBDK example for using the rumble motor on the Game Boy MBC5 Cartridge +This example demonstrates using the rumble motor that can be present +on some MBC5 cartridges. For example the rumble motor was used in Pokemon Pinball. # Makefile MBC You need to use one of the following MBCs in the Makefile: -0x1C: ROM+MBC5+RUMBLE -0x1D: ROM+MBC5+RUMBLE+SRAM -0x1E: ROM+MBC5+RUMBLE+SRAM+BATT -In this example the last one is used. You can change it in the LCCFLAGS +- `0x1C`: MBC5 + RUMBLE +- `0x1D`: MBC5 + RUMBLE + SRAM +- `0x1E`: MBC5 + RUMBLE + SRAM + BATTERY + +In this example the last one is used. You can change it in the `Makefile` # Emulators Not every emulator supports Rumble, and most of the ones that do only work -when you plug a controller. - +when you plug in a controller. The intensity of rumble using emulators may +differ from what is experienced on an actual rumble cart in a Game Boy. + +# SRAM Banks +If rumble is used in a program which does cart SRAM bank switching then the +SRAM bank should be kept track of in a variable. The `MBC5_RUMBLE_ON` and +`MBC5_RUMBLE_OFF` macros should be changed to include that SRAM bank variable +(replacing `MBC_RAM_BANK_0`) so that rumble updates do not unexpectedly alter +the cart SRAM bank. + +Likewise when switching cart SRAM banks to access data, the rumble state +should be included when calling `SWITCH_RAM()` or `SWITCH_RAM_MBC5()` so +that rumble status is not interrupted when active. + diff --git a/gbdk-lib/examples/gb/mbc5_rumble/src/main.c b/gbdk-lib/examples/gb/mbc5_rumble/src/main.c @@ -1,20 +1,85 @@ #include <gbdk/platform.h> #include <stdint.h> #include <stdio.h> +#include <gbdk/console.h> + #include "mbc5_rumble.h" +// Note about SRAM Banks +// +// If rumble is used in a program which does cart SRAM bank switching then the +// SRAM bank should be kept track of in a variable. The MBC5_RUMBLE_ON and +// MBC5_RUMBLE_OFF macros should be changed to include that SRAM bank variable +// (replacing MBC_RAM_BANK_0) so that rumble updates do not unexpectedly alter +// the cart SRAM bank. +// +// Likewise when switching cart SRAM banks to access data, the rumble state +// should be included when calling SWITCH_RAM() or SWITCH_RAM_MBC5() so +// that rumble status is not interrupted when active. + + +// New and previous values of the joypad input +uint8_t joy = 0, old_joy; +// User input macros +#define INPUT_UPDATE (old_joy=joy,joy=joypad()) +#define INPUT_BUTTON(key) (joy&(key)) +#define INPUT_BUTTONPRESS(key) ((joy & ~old_joy) & (key)) +#define INPUT_BUTTONS_PRESSED (joy & ~old_joy) + + +#define INTENS_PRNT_X 18u +#define INTENS_PRNT_Y 12u + +static void display_rumble_intensity(uint8_t value) { + gotoxy(INTENS_PRNT_X, INTENS_PRNT_Y); + printf("%hu", (uint8_t)value); +} + + void main(void) { - printf("GBDK Rumble Example.\n"); - printf("Press A for a strongRumble.\n"); - printf("Press B for a weak\nRumble.\n"); + // Install Rumble update function as aV-Blank handler + CRITICAL { + add_VBL(rumble_update); + } + + printf( + "MBC5 Rumble Example\n" + "\n" + "Press:\n" + "-ST for 1/2 second\n" + "-A for 1/4 second\n" + "-B for 1/8 second\n" + "\n" + "-SEL to stop early\n" + "\n" + "-Up more intensity\n" + "-Dn less intensity\n" + "\n" + "Rumble intensity:"); + + // Set the rumble intensity to MAX + rumble_intensity_set(RUMBLE_INTENSITY_MAX); + display_rumble_intensity(RUMBLE_INTENSITY_MAX); + + INPUT_UPDATE; while(1) { - if(joypad() & J_A) - MBC5_RUMBLE_MAX(); - else if (joypad() & J_B) - MBC5_RUMBLE_LOW(); - else - MBC5_RUMBLE_OFF; + + INPUT_UPDATE; + + switch(INPUT_BUTTONS_PRESSED) { + case J_START: rumble_start(RUMBLE_COUNT_HALF_SEC); break; + case J_A: rumble_start(RUMBLE_COUNT_QUARTER_SEC); break; + case J_B: rumble_start(RUMBLE_COUNT_EIGHTH_SEC); break; + + case J_UP: display_rumble_intensity( rumble_intensity_increase() ); + break; + case J_DOWN: display_rumble_intensity( rumble_intensity_decrease() ); + break; + + case J_SELECT: rumble_cancel(); break; + } + vsync(); } } diff --git a/gbdk-lib/examples/gb/mbc5_rumble/src/mbc5_rumble.c b/gbdk-lib/examples/gb/mbc5_rumble/src/mbc5_rumble.c @@ -1,26 +1,81 @@ #include <gbdk/platform.h> #include <stdint.h> -#define MBC_RUMBLE_BIT_ON 0b00001000u -#define MBC_RUMBLE_BIT_OFF 0b00000000u -#define MBC5_RUMBLE_ON SWITCH_RAM_MBC5(0 | MBC_RUMBLE_BIT_ON) -#define MBC5_RUMBLE_OFF SWITCH_RAM_MBC5(0 | MBC_RUMBLE_BIT_OFF) - -uint8_t rumble_counter = 0; - -void MBC5_RUMBLE_LOW(void) -{ - rumble_counter++; - if(rumble_counter%4==0)MBC5_RUMBLE_ON; - else MBC5_RUMBLE_OFF; +#include <stdbool.h> +#include "mbc5_rumble.h" + +// Power up defaults +uint8_t rumble_count = 0u; +uint8_t rumble_intensity = RUMBLE_INTENSITY_MAX; +uint8_t rumble_mask = RUMBLE_DUTY_MASK_MAX; + +// Lookup table for bitwise duty cycle masks based on intensity setting +const uint8_t rumble_masks[RUMBLE_INTENSITY_MAX + 1] = { + RUMBLE_DUTY_MASK_LOW, + RUMBLE_DUTY_MASK_MED, + RUMBLE_DUTY_MASK_HI, + RUMBLE_DUTY_MASK_MAX, +}; + + +void rumble_start(uint8_t duration) { + rumble_count = duration; +} + + +void rumble_cancel(void) { + rumble_count = RUMBLE_COUNT_DONE; + MBC5_RUMBLE_OFF; } -void MBC5_RUMBLE_MED(void) -{ - rumble_counter++; - if(rumble_counter%2==0)MBC5_RUMBLE_ON; - else MBC5_RUMBLE_OFF; + + +void rumble_set_duty_cycle(void) { + rumble_mask = rumble_masks[rumble_intensity]; } -void MBC5_RUMBLE_MAX(void) -{ - MBC5_RUMBLE_ON; -} - + +void rumble_intensity_set(uint8_t intensity) { + + if (intensity > RUMBLE_INTENSITY_MAX) + intensity = RUMBLE_INTENSITY_MAX; + rumble_intensity = intensity; + rumble_set_duty_cycle(); +} + + +uint8_t rumble_intensity_increase(void) { + + if (rumble_intensity < RUMBLE_INTENSITY_MAX) + rumble_intensity++; + rumble_set_duty_cycle(); + + return rumble_intensity; +} + + +uint8_t rumble_intensity_decrease(void) { + + if (rumble_intensity > RUMBLE_INTENSITY_MIN) + rumble_intensity--; + rumble_set_duty_cycle(); + + return rumble_intensity; +} + + +// Update cart rumble, should be called once per frame such as in a VBlank ISR +void rumble_update(void) { + + if (rumble_count) { + rumble_count--; + + // For max intensity do a 100% duty cycle (always on). + // For lower intensities use bit masked partial duty cycles. + // + // When the counter reaches the end (zero) it will get turned + // off due to no bits matching any of the masks + if (rumble_count & rumble_mask) + MBC5_RUMBLE_ON; + else + MBC5_RUMBLE_OFF; + } +} diff --git a/gbdk-lib/examples/gb/mbc5_rumble/src/mbc5_rumble.h b/gbdk-lib/examples/gb/mbc5_rumble/src/mbc5_rumble.h @@ -1,12 +1,53 @@ +#ifndef _MBC5_RUMBLE_H +#define _MBC5_RUMBLE_H + #include <gbdk/platform.h> #include <stdint.h> -#ifndef MBC5_RUMBLE_H_ -#define MBC5_RUMBLE_H_ -#define MBC_RUMBLE_BIT_ON 0b00001000u -#define MBC_RUMBLE_BIT_OFF 0b00000000u -#define MBC5_RUMBLE_ON SWITCH_RAM_MBC5(0 | MBC_RUMBLE_BIT_ON) -#define MBC5_RUMBLE_OFF SWITCH_RAM_MBC5(0 | MBC_RUMBLE_BIT_OFF) -void MBC5_RUMBLE_LOW(void); -void MBC5_RUMBLE_MED(void); -void MBC5_RUMBLE_MAX(void); -#endif - + +#define RUMBLE_FRAMES(N) (N + 1u) +#define RUMBLE_COUNT_1_SEC RUMBLE_FRAMES(60u) +#define RUMBLE_COUNT_HALF_SEC RUMBLE_FRAMES(30u) +#define RUMBLE_COUNT_QUARTER_SEC RUMBLE_FRAMES(15u) +#define RUMBLE_COUNT_EIGHTH_SEC RUMBLE_FRAMES(7u) +#define RUMBLE_COUNT_DONE 0u + + +// MAX uses an always on duty cycle. +// The others use the various waveforms below +// +// Using shorter on/off cycling makes +// the rumble feel less "lumpy" than longer +// contiguous on-off square waves with +// the same amount of total on-time frames. +// // The counter increments once per frame +// // ("Y" = on, "-" = off) +// // Bit 76543210 7 6 5 4 3 2 1 0 +#define RUMBLE_DUTY_MASK_MAX (0b11111111u) // YYYYYYYY |¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯| +#define RUMBLE_DUTY_MASK_HI (0b00000011u) // YYY-YYY- |¯¯¯¯¯|_|¯¯¯¯¯|_| +#define RUMBLE_DUTY_MASK_MED (0b00000010u) // YY--YY-- |¯¯¯|___|¯¯¯|___| +#define RUMBLE_DUTY_MASK_LOW (0b00000001u) // Y-Y-Y-Y- |¯|_|¯|_|¯|_|¯|_| + +#define RUMBLE_INTENSITY_MAX 3u +#define RUMBLE_INTENSITY_HI 2u +#define RUMBLE_INTENSITY_MED 1u +#define RUMBLE_INTENSITY_LOW 0u +#define RUMBLE_INTENSITY_MIN (RUMBLE_INTENSITY_LOW) + +#define MBC_RAM_BANK_0 0u + +#define MBC_RUMBLE_BIT_ON 0b00001000u +#define MBC_RUMBLE_BIT_OFF 0b00000000u +#define MBC5_RUMBLE_ON SWITCH_RAM_MBC5(MBC_RAM_BANK_0 | MBC_RUMBLE_BIT_ON) +#define MBC5_RUMBLE_OFF SWITCH_RAM_MBC5(MBC_RAM_BANK_0 | MBC_RUMBLE_BIT_OFF) + + +void rumble_start(uint8_t duration); +void rumble_cancel(void); + +void rumble_intensity_set(uint8_t duty_cycle); +uint8_t rumble_intensity_increase(void); +uint8_t rumble_intensity_decrease(void); + +void rumble_update(void); + +#endif // _MBC5_RUMBLE_H

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.