diff options
author | Bad Diode <bd@badd10de.dev> | 2021-04-29 16:06:34 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2021-04-29 16:06:34 +0200 |
commit | 3ec91ad217fb77ae1980b0b935e76307edf63ae1 (patch) | |
tree | a03bf45fa8b840c851232eaf71241d58b4e2e9b2 /src/common.h | |
parent | 430f41efe79e260c1bb44dfabd7a867104466db5 (diff) | |
download | gba-experiments-3ec91ad217fb77ae1980b0b935e76307edf63ae1.tar.gz gba-experiments-3ec91ad217fb77ae1980b0b935e76307edf63ae1.zip |
Initial tests with playing DMG soundssound-demo
Diffstat (limited to 'src/common.h')
-rw-r--r-- | src/common.h | 189 |
1 files changed, 153 insertions, 36 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). |
88 | typedef u16 Color; | 92 | typedef 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. | ||
91 | typedef Color Palette[16]; | 96 | typedef Color Palette[16]; |
92 | 97 | ||
98 | // Inline function to calculate the 15 bit color value. | ||
99 | static inline Color | ||
100 | rgb15(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. |
98 | typedef struct Tile { | 117 | typedef struct Tile { |
99 | u32 data[8]; | 118 | u32 row[8]; |
100 | } Tile; | 119 | } Tile; |
101 | 120 | ||
121 | // Screenblocks and charblocks (tile blocks). | ||
102 | typedef Tile TileBlock[512]; | 122 | typedef Tile TileBlock[512]; |
103 | #define TILE_MEM ((TileBlock*) MEM_VRAM) | 123 | #define TILE_MEM ((TileBlock*) MEM_VRAM) |
104 | |||
105 | // Screenblocks and charblocks for backgrounds. | ||
106 | typedef u16 ScreenBlock[1024]; | 124 | typedef u16 ScreenBlock[1024]; |
107 | typedef 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 | ||
116 | size_t se_index(size_t tile_x, size_t tile_y, size_t map_width) { | 132 | size_t |
133 | se_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 | // |
127 | typedef Color Scanline[SCREEN_WIDTH]; | 143 | typedef 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 | |||
139 | static inline Color | ||
140 | rgb15(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 | ||
194 | static inline void | ||
195 | wait_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. |
511 | void irs_main(void); | 503 | void 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) | ||
513 | void | 508 | void |
514 | irq_init() { | 509 | irq_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. |
521 | void | 516 | void |
522 | irq_stub() {} | 517 | irs_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. | ||
525 | int bios_vblank_wait(); | ||
526 | int 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 | |||
560 | typedef enum { | ||
561 | SOUND_SQUARE1 = (0x1 << 0), | ||
562 | SOUND_SQUARE2 = (0x1 << 1), | ||
563 | SOUND_WAVE = (0x4 << 2), | ||
564 | SOUND_NOISE = (0x8 << 3), | ||
565 | } SoundChannel; | ||
566 | |||
567 | u16 | ||
568 | sound_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 | |||
603 | typedef 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 | |||
618 | u32 | ||
619 | sound_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. | ||
635 | static inline void | ||
636 | wait_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 |