summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2021-04-29 16:06:34 +0200
committerBad Diode <bd@badd10de.dev>2021-04-29 16:06:34 +0200
commit3ec91ad217fb77ae1980b0b935e76307edf63ae1 (patch)
treea03bf45fa8b840c851232eaf71241d58b4e2e9b2
parent430f41efe79e260c1bb44dfabd7a867104466db5 (diff)
downloadgba-experiments-3ec91ad217fb77ae1980b0b935e76307edf63ae1.tar.gz
gba-experiments-3ec91ad217fb77ae1980b0b935e76307edf63ae1.zip
Initial tests with playing DMG soundssound-demo
-rw-r--r--src/common.h189
-rw-r--r--src/main.c87
-rw-r--r--src/text.h2
3 files changed, 234 insertions, 44 deletions
diff --git a/src/common.h b/src/common.h
index ff5ea12..7a0c50c 100644
--- a/src/common.h
+++ b/src/common.h
@@ -83,29 +83,45 @@
83#define SCREEN_WIDTH 240 83#define SCREEN_WIDTH 240
84#define SCREEN_HEIGHT 160 84#define SCREEN_HEIGHT 160
85 85
86//
87// Colors.
88//
89
86// The GBA in mode 3 expects rbg15 colors in the VRAM, where each component 90// The GBA in mode 3 expects rbg15 colors in the VRAM, where each component
87// (RGB) have a 0--31 range. For example, pure red would be rgb15(31, 0, 0). 91// (RGB) have a 0--31 range. For example, pure red would be rgb15(31, 0, 0).
88typedef u16 Color; 92typedef u16 Color;
89 93
90// A palette is composed of 16 colors, with color at index 0 being transparent. 94// A palette is composed of 16 colors, with color at index 0 being transparent
95// for sprites.
91typedef Color Palette[16]; 96typedef Color Palette[16];
92 97
98// Inline function to calculate the 15 bit color value.
99static inline Color
100rgb15(u32 red, u32 green, u32 blue ) {
101 return (blue << 10) | (green << 5) | red;
102}
103
104// Some nice default colors.
105#define COLOR_RED rgb15(31, 0, 12)
106#define COLOR_BLUE rgb15(2, 15, 30)
107#define COLOR_CYAN rgb15(0, 30, 30)
108#define COLOR_GREY rgb15(4, 4, 4)
109#define COLOR_BLACK rgb15(0, 0, 0)
110#define COLOR_WHITE rgb15(28, 28, 28)
111
93// 112//
94// Tile memory access. 113// Tile memory access.
95// 114//
96 115
97// NOTE: Only defining 4bpp tiles for now. 116// NOTE: Only defining 4bpp tiles for now.
98typedef struct Tile { 117typedef struct Tile {
99 u32 data[8]; 118 u32 row[8];
100} Tile; 119} Tile;
101 120
121// Screenblocks and charblocks (tile blocks).
102typedef Tile TileBlock[512]; 122typedef Tile TileBlock[512];
103#define TILE_MEM ((TileBlock*) MEM_VRAM) 123#define TILE_MEM ((TileBlock*) MEM_VRAM)
104
105// Screenblocks and charblocks for backgrounds.
106typedef u16 ScreenBlock[1024]; 124typedef u16 ScreenBlock[1024];
107typedef Tile CharBlock[512];
108#define CHARBLOCK_MEM ((CharBlock*)MEM_VRAM)
109#define SCREENBLOCK_MEM ((ScreenBlock*)MEM_VRAM) 125#define SCREENBLOCK_MEM ((ScreenBlock*)MEM_VRAM)
110 126
111// Screenblock entry bits. 127// Screenblock entry bits.
@@ -113,40 +129,24 @@ typedef Tile CharBlock[512];
113#define SCREENBLOCK_ENTRY_V_FLIP (1 << 0xB) 129#define SCREENBLOCK_ENTRY_V_FLIP (1 << 0xB)
114#define SCREENBLOCK_ENTRY_PAL(N) ((N) << 0xC) 130#define SCREENBLOCK_ENTRY_PAL(N) ((N) << 0xC)
115 131
116size_t se_index(size_t tile_x, size_t tile_y, size_t map_width) { 132size_t
133se_index(size_t tile_x, size_t tile_y, size_t map_width) {
117 size_t sbb = ((tile_x >> 5) + (tile_y >> 5) * (map_width >> 5)); 134 size_t sbb = ((tile_x >> 5) + (tile_y >> 5) * (map_width >> 5));
118 return sbb * 1024 + ((tile_x & 31) + (tile_y & 31) * 32); 135 return sbb * 1024 + ((tile_x & 31) + (tile_y & 31) * 32);
119} 136}
120 137
121
122// We can treat the screen as a HxW matrix. With the following macro we can 138// We can treat the screen as a HxW matrix. With the following macro we can
123// write a pixel to the screen at the (x, y) position using: 139// write a pixel to the screen at the (x, y) position using:
124// 140//
125// FRAMEBUFFER[y][x] = color; 141// FRAMEBUFFER[y][x] = color;
126// 142//
127typedef Color Scanline[SCREEN_WIDTH]; 143typedef Color Scanline[SCREEN_WIDTH];
128#define FRAMEBUFFER ((Scanline*)MEM_VRAM) 144#define FRAMEBUFFER ((Scanline*) MEM_VRAM)
129#define SCREEN_BUFFER ((u16*) MEM_VRAM) 145#define SCREEN_BUFFER ((u16*) MEM_VRAM)
130#define PAL_BUFFER_BG ((u16*) MEM_PAL) 146#define PAL_BUFFER_BG ((u16*) MEM_PAL)
131#define PAL_BUFFER_SPRITES ((u16*) 0x05000200) 147#define PAL_BUFFER_SPRITES ((u16*)(MEM_PAL + 0x200))
132#define PAL_BANK_BG ((Palette*) MEM_PAL) 148#define PAL_BANK_BG ((Palette*) MEM_PAL)
133#define PAL_BANK_SPRITES ((Palette*) 0x05000200) 149#define PAL_BANK_SPRITES ((Palette*)(MEM_PAL + 0x200))
134
135//
136// Colors.
137//
138
139static inline Color
140rgb15(u32 red, u32 green, u32 blue ) {
141 return (blue << 10) | (green << 5) | red;
142}
143
144#define COLOR_RED rgb15(31, 0, 12)
145#define COLOR_BLUE rgb15(2, 15, 30)
146#define COLOR_CYAN rgb15(0, 30, 30)
147#define COLOR_GREY rgb15(4, 4, 4)
148#define COLOR_BLACK rgb15(0, 0, 0)
149#define COLOR_WHITE rgb15(28, 28, 28)
150 150
151// 151//
152// Sprites. 152// Sprites.
@@ -191,12 +191,6 @@ rgb15(u32 red, u32 green, u32 blue ) {
191#define OBJ_PRIORITY(N) ((N) << 0xA) 191#define OBJ_PRIORITY(N) ((N) << 0xA)
192#define OBJ_PAL_BANK(N) ((N) << 0xC) 192#define OBJ_PAL_BANK(N) ((N) << 0xC)
193 193
194static inline void
195wait_vsync(void) {
196 while(DISP_VCOUNT >= 160);
197 while(DISP_VCOUNT < 160);
198}
199
200// 194//
201// Mode 4 page flipping 195// Mode 4 page flipping
202// 196//
@@ -505,11 +499,12 @@ irs_set(IrqIndex idx, IrsFunc func) {
505 IRQ_CTRL = irq_ctrl; 499 IRQ_CTRL = irq_ctrl;
506} 500}
507 501
508#define IRS_MAIN *(IrsFunc*)(0x03007FFC)
509
510// External irs_main function, has to be written in ARM assembly. 502// External irs_main function, has to be written in ARM assembly.
511void irs_main(void); 503void irs_main(void);
512 504
505// Initialize the function pointer for the main IRS routine written in ARM
506// assembly and enable interrupts.
507#define IRS_MAIN *(IrsFunc*)(0x03007FFC)
513void 508void
514irq_init() { 509irq_init() {
515 IRS_MAIN = irs_main; 510 IRS_MAIN = irs_main;
@@ -519,6 +514,128 @@ irq_init() {
519// Stub function pointer needed for when we want to enable interrupts that don't 514// Stub function pointer needed for when we want to enable interrupts that don't
520// require a custom function, such as for the BIOS VSync. 515// require a custom function, such as for the BIOS VSync.
521void 516void
522irq_stub() {} 517irs_stub() {}
518
519//
520// BIOS function declarations.
521//
522
523// These functions declarations can be used to call the BIOS functions from the
524// asm code.
525int bios_vblank_wait();
526int bios_div(int num, int denom);
527
528//
529// Sound.
530//
531
532// Sound registers.
533#define SOUND_SQUARE1_SWEEP *((vu16*)(MEM_IO + 0x60))
534#define SOUND_SQUARE1_CTRL *((vu16*)(MEM_IO + 0x62))
535#define SOUND_SQUARE1_FREQ *((vu16*)(MEM_IO + 0x64))
536#define SOUND_SQUARE2_CTRL *((vu16*)(MEM_IO + 0x68))
537#define SOUND_SQUARE2_FREQ *((vu16*)(MEM_IO + 0x6C))
538#define SOUND_WAVE_MODE *((vu16*)(MEM_IO + 0x70))
539#define SOUND_WAVE_CTRL *((vu16*)(MEM_IO + 0x72))
540#define SOUND_WAVE_FREQ *((vu16*)(MEM_IO + 0x74))
541#define SOUND_NOISE_CTRL *((vu16*)(MEM_IO + 0x78))
542#define SOUND_NOISE_FREQ *((vu16*)(MEM_IO + 0x7C))
543#define SOUND_DMG_MASTER *((vu16*)(MEM_IO + 0x80))
544#define SOUND_DSOUND_MASTER *((vu16*)(MEM_IO + 0x82))
545#define SOUND_STATUS *((vu16*)(MEM_IO + 0x84))
546#define SOUND_BIAS *((vu16*)(MEM_IO + 0x88))
547
548// Sound DMG master bits.
549#define SOUND_VOLUME_LEFT(N) (N)
550#define SOUND_VOLUME_RIGHT(N) ((N) << 4)
551#define SOUND_ENABLE_SQUARE1_LEFT (1 << 0x8)
552#define SOUND_ENABLE_SQUARE2_LEFT (1 << 0x9)
553#define SOUND_ENABLE_WAVE_LEFT (1 << 0xA)
554#define SOUND_ENABLE_NOISE_LEFT (1 << 0xB)
555#define SOUND_ENABLE_SQUARE1_RIGHT (1 << 0xC)
556#define SOUND_ENABLE_SQUARE2_RIGHT (1 << 0xD)
557#define SOUND_ENABLE_WAVE_RIGHT (1 << 0xE)
558#define SOUND_ENABLE_NOISE_RIGHT (1 << 0xF)
559
560typedef enum {
561 SOUND_SQUARE1 = (0x1 << 0),
562 SOUND_SQUARE2 = (0x1 << 1),
563 SOUND_WAVE = (0x4 << 2),
564 SOUND_NOISE = (0x8 << 3),
565} SoundChannel;
566
567u16
568sound_volume(SoundChannel channels, u8 volume) {
569 volume = volume & 0x7;
570 channels = channels & 0xF;
571 return volume | (volume << 0x4) | (channels << 0x8) | (channels << 0xC);
572}
573
574// Sound Direct Sound master bits.
575#define SOUND_DMG25 0x0
576#define SOUND_DMG50 0x1
577#define SOUND_DMG100 0x2
578#define SOUND_DSOUND_RATIO_A (1 << 0x2)
579#define SOUND_DSOUND_RATIO_B (1 << 0x3)
580#define SOUND_DSOUND_LEFT_A (1 << 0x8)
581#define SOUND_DSOUND_RIGHT_A (1 << 0x9)
582#define SOUND_DSOUND_TIMER_A (1 << 0xA)
583#define SOUND_DSOUND_RESET_A (1 << 0xB)
584#define SOUND_DSOUND_LEFT_B (1 << 0xC)
585#define SOUND_DSOUND_RIGHT_B (1 << 0xD)
586#define SOUND_DSOUND_TIMER_B (1 << 0xE)
587#define SOUND_DSOUND_RESET_B (1 << 0xF)
588
589// Sound status bits.
590#define SOUND_ENABLE (1 << 0x7)
591
592// DMG square control bits.
593#define SOUND_SQUARE_LEN(N) (N)
594#define SOUND_SQUARE_DUTY(N) ((N) << 0x6)
595#define SOUND_SQUARE_ENV_TIME(N) ((N) << 0x8)
596#define SOUND_SQUARE_ENV_INC (1 << 0xB)
597#define SOUND_SQUARE_ENV_VOL(N) ((N) << 0xC)
598
599// DMG square frequency bits.
600#define SOUND_SQUARE_TIMED (1 << 0xE)
601#define SOUND_SQUARE_RESET (1 << 0xF)
602
603typedef enum {
604 NOTE_C,
605 NOTE_C_SHARP,
606 NOTE_D,
607 NOTE_D_SHARP,
608 NOTE_E,
609 NOTE_F,
610 NOTE_F_SHARP,
611 NOTE_G,
612 NOTE_G_SHARP,
613 NOTE_A,
614 NOTE_A_SHARP,
615 NOTE_B,
616} Note;
617
618u32
619sound_rate(Note note, u32 octave) {
620 const u32 base_rate[12] = {
621 8013, 7566, 7144, 6742, // C , C#, D , D#
622 6362, 6005, 5666, 5346, // E , F , F#, G
623 5048, 4766, 4499, 4246 // G#, A , A#, B
624 };
625 return 2048 - (base_rate[note] >> (4 + octave));
626}
627
628//
629// Misc.
630//
631
632// Custom VSync option. This will waste a lot of battery power, since the
633// machine is not clocked down. Prefer using `bios_vblank_wait()` if interrupts
634// are enabled.
635static inline void
636wait_vsync(void) {
637 while(DISP_VCOUNT >= 160);
638 while(DISP_VCOUNT < 160);
639}
523 640
524#endif // GBAEXP_COMMON_H 641#endif // GBAEXP_COMMON_H
diff --git a/src/main.c b/src/main.c
index ec2150a..30452e1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -17,9 +17,6 @@
17// BIOS calls 17// BIOS calls
18// 18//
19 19
20int bios_vblank_wait();
21int bios_div(int num, int denom);
22
23int hblank_counter = 0; 20int hblank_counter = 0;
24 21
25void 22void
@@ -48,19 +45,95 @@ int main(void) {
48 45
49 // Register interrupts. 46 // Register interrupts.
50 irq_init(); 47 irq_init();
51 irs_set(IRQ_VBLANK, irq_stub); 48 irs_set(IRQ_VBLANK, irs_stub);
52 irs_set(IRQ_HBLANK, irs_hblank_func); 49 irs_set(IRQ_HBLANK, irs_hblank_func);
53 50
51 // turn sound on
52 SOUND_STATUS = SOUND_ENABLE;
53 SOUND_DMG_MASTER = sound_volume(SOUND_SQUARE1 | SOUND_SQUARE2, 7);
54 SOUND_DSOUND_MASTER = SOUND_DMG100;
55
56 char *note_names[] = {
57 "C" , "C#" , "D" , "D#" ,
58 "E" , "F" , "F#" , "G" ,
59 "G#" , "A" , "A#" , "B"
60 };
54 int frame_counter = 0; 61 int frame_counter = 0;
62 Note active_note = NOTE_C;
63 u8 octave = 0;
64 bool playing = false;
65 bool new_note = false;
66 u8 interval = 1;
55 while(true) { 67 while(true) {
56 bios_vblank_wait(); 68 bios_vblank_wait();
57 poll_keys(); 69 poll_keys();
58 70
59 txt_position(0, 1); 71 txt_position(0, 1);
60 txt_clear_line(); 72 txt_clear_line();
61 txt_printf(" HBlank counter: %d\n", hblank_counter); 73
62 txt_clear_line(); 74 if (key_pressed(KEY_B)) {
63 txt_printf(" Frame counter: %d\n", frame_counter); 75 active_note = NOTE_C;
76 new_note = true;
77 octave = 0;
78 } else if (key_pressed(KEY_A)) {
79 active_note = NOTE_D;
80 new_note = true;
81 octave = 0;
82 } else if (key_pressed(KEY_LEFT)) {
83 active_note = NOTE_E;
84 new_note = true;
85 octave = 0;
86 } else if (key_pressed(KEY_DOWN)) {
87 active_note = NOTE_F;
88 new_note = true;
89 octave = 0;
90 } else if (key_pressed(KEY_RIGHT)) {
91 active_note = NOTE_G;
92 new_note = true;
93 octave = 0;
94 } else if (key_pressed(KEY_UP)) {
95 active_note = NOTE_A;
96 new_note = true;
97 octave = 0;
98 } else if (key_pressed(KEY_L)) {
99 active_note = NOTE_B;
100 new_note = true;
101 octave = 0;
102 } else if (key_pressed(KEY_R)) {
103 active_note = NOTE_C;
104 new_note = true;
105 octave = 1;
106 }
107 if (key_pressed(KEY_START)) {
108 interval++;
109 }
110 if (key_pressed(KEY_SELECT)) {
111 interval--;
112 }
113
114 if (key_curr > 0) {
115 txt_printf(" Playing: %s + %s\n",
116 note_names[active_note],
117 (note_names[(active_note + interval) % 12]));
118 playing = true;
119 } else {
120 playing = false;
121 }
122
123 if (playing) {
124 if (new_note) {
125 SOUND_SQUARE1_CTRL = SOUND_SQUARE_ENV_VOL(4) | SOUND_SQUARE_ENV_TIME(0);
126 SOUND_SQUARE2_CTRL = SOUND_SQUARE_ENV_VOL(4) | SOUND_SQUARE_ENV_TIME(0);
127 SOUND_SQUARE1_FREQ = SOUND_SQUARE_RESET | sound_rate(active_note, octave);
128 SOUND_SQUARE2_FREQ = SOUND_SQUARE_RESET | sound_rate((active_note + interval) % 12, octave);
129 }
130 new_note = false;
131 } else {
132 SOUND_SQUARE1_CTRL = SOUND_SQUARE_ENV_VOL(0) | SOUND_SQUARE_ENV_TIME(0);
133 SOUND_SQUARE2_CTRL = SOUND_SQUARE_ENV_VOL(0) | SOUND_SQUARE_ENV_TIME(0);
134 SOUND_SQUARE1_FREQ = 0;
135 SOUND_SQUARE2_FREQ = 0;
136 }
64 137
65 frame_counter++; 138 frame_counter++;
66 update_button_sprites(); 139 update_button_sprites();
diff --git a/src/text.h b/src/text.h
index c852fc2..f178608 100644
--- a/src/text.h
+++ b/src/text.h
@@ -61,7 +61,7 @@ txt_init(size_t bg, Color clr, size_t cb_idx) {
61 // Load font data in video memory. Each character is unpacked into a tile of 61 // Load font data in video memory. Each character is unpacked into a tile of
62 // 8 32bit values (4bpp), meaning that for the full ASCII set of 256 62 // 8 32bit values (4bpp), meaning that for the full ASCII set of 256
63 // characters, we need 8192 bytes of VRAM (8 * 4 * 256). 63 // characters, we need 8192 bytes of VRAM (8 * 4 * 256).
64 unpack_tiles(&bd_font, &CHARBLOCK_MEM[cb_idx], 256); 64 unpack_tiles(&bd_font, &TILE_MEM[cb_idx], 256);
65 65
66 // Load palette color. 66 // Load palette color.
67 PAL_BUFFER_BG[1] = clr; 67 PAL_BUFFER_BG[1] = clr;