Browse Source

add ROM autobanking and store highscores in banked RAM.

Thomas B 1 month ago
parent
commit
1f8001e4e8
13 changed files with 265 additions and 100 deletions
  1. 6
    4
      Makefile
  2. 3
    3
      src/game.c
  3. 3
    3
      src/input.c
  4. 12
    8
      src/main.c
  5. 17
    12
      src/maps.c
  6. 5
    5
      src/obj.c
  7. 149
    0
      src/score.ba0.c
  8. 0
    32
      src/score.c
  9. 8
    3
      src/score.h
  10. 2
    2
      src/sound.c
  11. 50
    25
      src/sprite_data.c
  12. 1
    0
      src/sprite_data.h
  13. 9
    3
      src/sprites.c

+ 6
- 4
Makefile View File

39
 
39
 
40
 LCCFLAGS := -Wa-l -Wl-m -Wp-MMD
40
 LCCFLAGS := -Wa-l -Wl-m -Wp-MMD
41
 LCCFLAGS += -I$(BUILD_DIR)/$(DATA_DIR)
41
 LCCFLAGS += -I$(BUILD_DIR)/$(DATA_DIR)
42
-LCCFLAGS += -Wm"-yn Duality" -Wm-yc
42
+LCCFLAGS += -Wm"-yn Duality" -Wm-yt0x1A -Wm-yoA -Wm-ya16 -Wm-yc
43
+LCCFLAGS += -autobank -Wb-ext=.rel -Wb-v -Wf-bo255
43
 
44
 
44
 EMUFLAGS := $(BIN)
45
 EMUFLAGS := $(BIN)
45
 
46
 
94
 
95
 
95
 $(BUILD_DIR)/%.o: %.c $(SPRITES)
96
 $(BUILD_DIR)/%.o: %.c $(SPRITES)
96
 	@mkdir -p $(@D)
97
 	@mkdir -p $(@D)
97
-	@echo Compiling $<
98
-	@$(LCC) $(LCCFLAGS) -c -o $@ $<
98
+	@echo Compiling Code $<
99
+	$(eval BAFLAG = $(shell echo "$<" | sed -n 's/.*\.ba\([0-9]\+\).*/\-Wf-ba\1/p'))
100
+	@$(LCC) $(LCCFLAGS) $(BAFLAG) -c -o $@ $<
99
 
101
 
100
 $(BUILD_DIR)/%.o: $(BUILD_DIR)/%.c $(SPRITES)
102
 $(BUILD_DIR)/%.o: $(BUILD_DIR)/%.c $(SPRITES)
101
 	@mkdir -p $(@D)
103
 	@mkdir -p $(@D)
102
-	@echo Compiling $<
104
+	@echo Compiling Asset $<
103
 	@$(LCC) $(LCCFLAGS) -c -o $@ $<
105
 	@$(LCC) $(LCCFLAGS) -c -o $@ $<
104
 
106
 
105
 $(BUILD_DIR)/%.o: %.s $(SPRITES)
107
 $(BUILD_DIR)/%.o: %.s $(SPRITES)

+ 3
- 3
src/game.c View File

39
 #define HEALTH_OFFSET_Y -16
39
 #define HEALTH_OFFSET_Y -16
40
 #define POWER_OFFSET_Y 16
40
 #define POWER_OFFSET_Y 16
41
 
41
 
42
-static void status(uint8_t health, uint8_t power, uint8_t *hiwater) {
42
+static void status(uint8_t health, uint8_t power, uint8_t *hiwater) NONBANKED {
43
     if (health > 0) {
43
     if (health > 0) {
44
         switch (health >> 6) {
44
         switch (health >> 6) {
45
             case 3:
45
             case 3:
75
     }
75
     }
76
 }
76
 }
77
 
77
 
78
-int32_t game(void) {
78
+int32_t game(void) NONBANKED {
79
     disable_interrupts();
79
     disable_interrupts();
80
     DISPLAY_OFF;
80
     DISPLAY_OFF;
81
     map_game();
81
     map_game();
104
     obj_add(SPR_SHOT_DARK, -32, -32, 0, 0);
104
     obj_add(SPR_SHOT_DARK, -32, -32, 0, 0);
105
 
105
 
106
     win_game_draw(score);
106
     win_game_draw(score);
107
-    move_win(MINWNDPOSX + 0, MINWNDPOSY + DEVICE_SCREEN_PX_HEIGHT - 16);
107
+    move_win(MINWNDPOSX, MINWNDPOSY + DEVICE_SCREEN_PX_HEIGHT - 16);
108
 
108
 
109
     SHOW_WIN;
109
     SHOW_WIN;
110
     DISPLAY_ON;
110
     DISPLAY_ON;

+ 3
- 3
src/input.c View File

24
 static uint8_t joyp = 0;
24
 static uint8_t joyp = 0;
25
 static uint8_t old_joyp = 0;
25
 static uint8_t old_joyp = 0;
26
 
26
 
27
-void key_read(void) {
27
+void key_read(void) NONBANKED {
28
     old_joyp = joyp;
28
     old_joyp = joyp;
29
     joyp = joypad();
29
     joyp = joypad();
30
 }
30
 }
31
 
31
 
32
-uint8_t key_down(uint8_t key) {
32
+uint8_t key_down(uint8_t key) NONBANKED {
33
     return joyp & key;
33
     return joyp & key;
34
 }
34
 }
35
 
35
 
36
-uint8_t key_pressed(uint8_t key) {
36
+uint8_t key_pressed(uint8_t key) NONBANKED {
37
     return (joyp ^ old_joyp) & joyp & key;
37
     return (joyp ^ old_joyp) & joyp & key;
38
 }
38
 }

+ 12
- 8
src/main.c View File

32
 #include "game.h"
32
 #include "game.h"
33
 #include "score.h"
33
 #include "score.h"
34
 
34
 
35
-static void highscore(uint8_t is_black) {
35
+static void highscore(uint8_t is_black) NONBANKED {
36
     HIDE_WIN;
36
     HIDE_WIN;
37
 
37
 
38
     hide_sprites_range(SPR_NUM_START, MAX_HARDWARE_SPRITES);
38
     hide_sprites_range(SPR_NUM_START, MAX_HARDWARE_SPRITES);
39
     win_score_clear();
39
     win_score_clear();
40
 
40
 
41
     for (uint8_t i = 0; i < SCORE_NUM; i++) {
41
     for (uint8_t i = 0; i < SCORE_NUM; i++) {
42
-        int32_t score = is_black ? -score_lowest(i) : score_highest(i);
42
+        int32_t score = is_black ? -score_lowest(i).score : score_highest(i).score;
43
         win_score_draw(score, i, is_black);
43
         win_score_draw(score, i, is_black);
44
     }
44
     }
45
 
45
 
57
     }
57
     }
58
 }
58
 }
59
 
59
 
60
-static void splash_win(void) {
60
+static void splash_win(void) NONBANKED {
61
     HIDE_WIN;
61
     HIDE_WIN;
62
 
62
 
63
     // initially show the top 1 scores
63
     // initially show the top 1 scores
64
-    int32_t low = score_lowest(0);
65
-    int32_t high = score_highest(0);
64
+    int32_t low = score_lowest(0).score;
65
+    int32_t high = score_highest(0).score;
66
 
66
 
67
     // only show on splash if they fit
67
     // only show on splash if they fit
68
     if ((low >= -99999) && (high <= 99999)) {
68
     if ((low >= -99999) && (high <= 99999)) {
73
     SHOW_WIN;
73
     SHOW_WIN;
74
 }
74
 }
75
 
75
 
76
-static void splash(void) {
76
+static void splash(void) NONBANKED {
77
     disable_interrupts();
77
     disable_interrupts();
78
     DISPLAY_OFF;
78
     DISPLAY_OFF;
79
     map_title();
79
     map_title();
111
     }
111
     }
112
 }
112
 }
113
 
113
 
114
-void main(void) {
114
+void main(void) NONBANKED {
115
     spr_init();
115
     spr_init();
116
     snd_init();
116
     snd_init();
117
     win_init();
117
     win_init();
125
 
125
 
126
     while (1) {
126
     while (1) {
127
         int32_t score = game();
127
         int32_t score = game();
128
-        score_add(score);
128
+
129
+        // TODO ask for name of player
130
+        struct scores s = { .name = 0x00, .score = score };
131
+        score_add(s);
132
+
129
         splash();
133
         splash();
130
     }
134
     }
131
 }
135
 }

+ 17
- 12
src/maps.c View File

45
     RGB8(  0,  0,  0), RGB8(  0,  0,  0), RGB8(248,252,248), RGB8(  0,  0,  0)
45
     RGB8(  0,  0,  0), RGB8(  0,  0,  0), RGB8(248,252,248), RGB8(  0,  0,  0)
46
 };
46
 };
47
 
47
 
48
-void map_title(void) {
48
+void map_title(void) NONBANKED {
49
+    SWITCH_ROM(BANK(title_map));
49
     set_bkg_palette(OAMF_CGB_PAL0, title_map_PALETTE_COUNT, title_map_palettes);
50
     set_bkg_palette(OAMF_CGB_PAL0, title_map_PALETTE_COUNT, title_map_palettes);
50
     set_bkg_data(0, title_map_TILE_COUNT, title_map_tiles);
51
     set_bkg_data(0, title_map_TILE_COUNT, title_map_tiles);
51
     set_bkg_attributes(0, 0, title_map_MAP_ATTRIBUTES_WIDTH, title_map_MAP_ATTRIBUTES_HEIGHT, title_map_MAP_ATTRIBUTES);
52
     set_bkg_attributes(0, 0, title_map_MAP_ATTRIBUTES_WIDTH, title_map_MAP_ATTRIBUTES_HEIGHT, title_map_MAP_ATTRIBUTES);
52
     set_bkg_tiles(0, 0, title_map_WIDTH / title_map_TILE_W, title_map_HEIGHT / title_map_TILE_H, title_map_map);
53
     set_bkg_tiles(0, 0, title_map_WIDTH / title_map_TILE_W, title_map_HEIGHT / title_map_TILE_H, title_map_map);
53
 }
54
 }
54
 
55
 
55
-void map_game(void) {
56
+void map_game(void) NONBANKED {
57
+    SWITCH_ROM(BANK(bg_map));
56
     set_bkg_palette(OAMF_CGB_PAL0, bg_map_PALETTE_COUNT, bg_map_palettes);
58
     set_bkg_palette(OAMF_CGB_PAL0, bg_map_PALETTE_COUNT, bg_map_palettes);
57
     set_bkg_data(0, bg_map_TILE_COUNT, bg_map_tiles);
59
     set_bkg_data(0, bg_map_TILE_COUNT, bg_map_tiles);
58
     set_bkg_attributes(0, 0, bg_map_MAP_ATTRIBUTES_WIDTH, bg_map_MAP_ATTRIBUTES_HEIGHT, bg_map_MAP_ATTRIBUTES);
60
     set_bkg_attributes(0, 0, bg_map_MAP_ATTRIBUTES_WIDTH, bg_map_MAP_ATTRIBUTES_HEIGHT, bg_map_MAP_ATTRIBUTES);
59
     set_bkg_tiles(0, 0, bg_map_WIDTH / bg_map_TILE_W, bg_map_HEIGHT / bg_map_TILE_H, bg_map_map);
61
     set_bkg_tiles(0, 0, bg_map_WIDTH / bg_map_TILE_W, bg_map_HEIGHT / bg_map_TILE_H, bg_map_map);
60
 }
62
 }
61
 
63
 
62
-void win_init(void) {
64
+void win_init(void) NONBANKED {
65
+    SWITCH_ROM(BANK(numbers));
63
     set_bkg_palette(OAMF_CGB_PAL0 + bg_map_PALETTE_COUNT, numbers_PALETTE_COUNT, numbers_palettes);
66
     set_bkg_palette(OAMF_CGB_PAL0 + bg_map_PALETTE_COUNT, numbers_PALETTE_COUNT, numbers_palettes);
64
     set_bkg_palette(OAMF_CGB_PAL0 + bg_map_PALETTE_COUNT + 1, numbers_PALETTE_COUNT, num_pal_inv);
67
     set_bkg_palette(OAMF_CGB_PAL0 + bg_map_PALETTE_COUNT + 1, numbers_PALETTE_COUNT, num_pal_inv);
65
     set_win_data(bg_map_TILE_COUNT, numbers_TILE_COUNT, numbers_tiles);
68
     set_win_data(bg_map_TILE_COUNT, numbers_TILE_COUNT, numbers_tiles);
66
 }
69
 }
67
 
70
 
68
 static void set_win_based(uint8_t x, uint8_t y, uint8_t w, uint8_t h,
71
 static void set_win_based(uint8_t x, uint8_t y, uint8_t w, uint8_t h,
69
-                          const uint8_t *tiles, uint8_t base_tile, const uint8_t *attributes) {
72
+                          const uint8_t *tiles, uint8_t base_tile, const uint8_t *attributes) NONBANKED {
70
     VBK_REG = VBK_ATTRIBUTES;
73
     VBK_REG = VBK_ATTRIBUTES;
71
     set_win_tiles(x, y, w, h, attributes);
74
     set_win_tiles(x, y, w, h, attributes);
72
     VBK_REG = VBK_TILES;
75
     VBK_REG = VBK_TILES;
73
     set_win_based_tiles(x, y, w, h, tiles, base_tile);
76
     set_win_based_tiles(x, y, w, h, tiles, base_tile);
74
 }
77
 }
75
 
78
 
76
-static void digit(uint8_t val, uint8_t digit, uint8_t x_off, uint8_t y_off, uint8_t is_black) {
79
+static void digit(uint8_t val, uint8_t digit, uint8_t x_off, uint8_t y_off, uint8_t is_black) NONBANKED {
80
+    SWITCH_ROM(BANK(numbers));
77
     uint8_t off = val * numbers_WIDTH / numbers_TILE_W;
81
     uint8_t off = val * numbers_WIDTH / numbers_TILE_W;
78
 
82
 
79
     set_win_based(x_off + (digit * numbers_WIDTH / numbers_TILE_W), y_off,
83
     set_win_based(x_off + (digit * numbers_WIDTH / numbers_TILE_W), y_off,
87
                   (is_black ? num_attr_2 : num_attr_1) + off);
91
                   (is_black ? num_attr_2 : num_attr_1) + off);
88
 }
92
 }
89
 
93
 
90
-static void number(int32_t score, uint8_t x_off, uint8_t y_off, uint8_t is_black) {
94
+static void number(int32_t score, uint8_t x_off, uint8_t y_off, uint8_t is_black) NONBANKED {
91
     // TODO can not set numbers larger than int16 max?!
95
     // TODO can not set numbers larger than int16 max?!
92
     //score = 32767 + 1; // wtf?!
96
     //score = 32767 + 1; // wtf?!
93
 
97
 
112
     }
116
     }
113
 }
117
 }
114
 
118
 
115
-static void fill_win(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t tile, uint8_t attr) {
119
+static void fill_win(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t tile, uint8_t attr) NONBANKED {
116
     VBK_REG = VBK_ATTRIBUTES;
120
     VBK_REG = VBK_ATTRIBUTES;
117
     fill_win_rect(x, y, w, h, attr);
121
     fill_win_rect(x, y, w, h, attr);
118
     VBK_REG = VBK_TILES;
122
     VBK_REG = VBK_TILES;
119
     fill_win_rect(x, y, w, h, tile);
123
     fill_win_rect(x, y, w, h, tile);
120
 }
124
 }
121
 
125
 
122
-void win_splash_draw(int32_t lowest, int32_t highest) {
126
+void win_splash_draw(int32_t lowest, int32_t highest) NONBANKED {
123
     // reuse full black and white tiles at 0 and 1 from splash bg
127
     // reuse full black and white tiles at 0 and 1 from splash bg
124
     fill_win(0, 0, 10, 2, 0, 0x00);
128
     fill_win(0, 0, 10, 2, 0, 0x00);
125
     fill_win(10, 0, 10, 2, 1, 0x00);
129
     fill_win(10, 0, 10, 2, 1, 0x00);
128
     number(highest, 0xFE, 0, 0);
132
     number(highest, 0xFE, 0, 0);
129
 }
133
 }
130
 
134
 
131
-void win_score_clear(void) {
135
+void win_score_clear(void) NONBANKED {
136
+    SWITCH_ROM(BANK(title_map));
132
     set_win_based(0, 0,
137
     set_win_based(0, 0,
133
                   title_map_WIDTH / title_map_TILE_W, title_map_HEIGHT / title_map_TILE_H,
138
                   title_map_WIDTH / title_map_TILE_W, title_map_HEIGHT / title_map_TILE_H,
134
                   title_map_map, 0, title_map_MAP_ATTRIBUTES);
139
                   title_map_map, 0, title_map_MAP_ATTRIBUTES);
135
 }
140
 }
136
 
141
 
137
-void win_score_draw(int32_t score, uint8_t off, uint8_t is_black) {
142
+void win_score_draw(int32_t score, uint8_t off, uint8_t is_black) NONBANKED {
138
     number(off, 1, 4 + off * 3, is_black);
143
     number(off, 1, 4 + off * 3, is_black);
139
     number(score, 5, 4 + off * 3, is_black);
144
     number(score, 5, 4 + off * 3, is_black);
140
 }
145
 }
141
 
146
 
142
-void win_game_draw(int32_t score) {
143
-    fill_win(0, 0, 20, 2, bg_map_TILE_COUNT + numbers_TILE_COUNT, 0x81);
147
+void win_game_draw(int32_t score) NONBANKED {
148
+    fill_win(0, 0, 20, 2, (uint8_t)bg_map_TILE_COUNT + (uint8_t)numbers_TILE_COUNT, 0x81);
144
 
149
 
145
     uint8_t is_black = 0;
150
     uint8_t is_black = 0;
146
     if (score < 0) {
151
     if (score < 0) {

+ 5
- 5
src/obj.c View File

73
 
73
 
74
 static struct obj objs[MAX_OBJ];
74
 static struct obj objs[MAX_OBJ];
75
 
75
 
76
-void obj_init(void) {
76
+void obj_init(void) NONBANKED {
77
     memset(objs, 0, sizeof(objs));
77
     memset(objs, 0, sizeof(objs));
78
 }
78
 }
79
 
79
 
80
-static uint8_t cnt_sprite(enum SPRITES sprite) {
80
+static uint8_t cnt_sprite(enum SPRITES sprite) NONBANKED {
81
     uint8_t cnt = 0;
81
     uint8_t cnt = 0;
82
     for (uint8_t i = 0; i < MAX_OBJ; i++) {
82
     for (uint8_t i = 0; i < MAX_OBJ; i++) {
83
         if (!objs[i].active) {
83
         if (!objs[i].active) {
90
     return cnt;
90
     return cnt;
91
 }
91
 }
92
 
92
 
93
-enum OBJ_STATE obj_add(enum SPRITES sprite, int16_t off_x, int16_t off_y, int16_t spd_x, int16_t spd_y) {
93
+enum OBJ_STATE obj_add(enum SPRITES sprite, int16_t off_x, int16_t off_y, int16_t spd_x, int16_t spd_y) NONBANKED {
94
     uint8_t obj_cnt = 0xFF;
94
     uint8_t obj_cnt = 0xFF;
95
     for (uint8_t i = 0; i < MAX_OBJ; i++) {
95
     for (uint8_t i = 0; i < MAX_OBJ; i++) {
96
         if (!objs[i].active) {
96
         if (!objs[i].active) {
125
 #define PICKUP_SMALL_RANGE (10 << POS_SCALE_OBJS)
125
 #define PICKUP_SMALL_RANGE (10 << POS_SCALE_OBJS)
126
 #define PICKUP_LARGE_RANGE (16 << POS_SCALE_OBJS)
126
 #define PICKUP_LARGE_RANGE (16 << POS_SCALE_OBJS)
127
 
127
 
128
-int16_t obj_act(int16_t *spd_off_x, int16_t *spd_off_y, int32_t *score) {
128
+int16_t obj_act(int16_t *spd_off_x, int16_t *spd_off_y, int32_t *score) NONBANKED {
129
     int16_t damage = 0;
129
     int16_t damage = 0;
130
 
130
 
131
     for (uint8_t i = 0; i < MAX_OBJ; i++) {
131
     for (uint8_t i = 0; i < MAX_OBJ; i++) {
194
     return damage;
194
     return damage;
195
 }
195
 }
196
 
196
 
197
-void obj_draw(int16_t spd_x, int16_t spd_y, uint8_t *hiwater) {
197
+void obj_draw(int16_t spd_x, int16_t spd_y, uint8_t *hiwater) NONBANKED {
198
     for (uint8_t i = 0; i < MAX_OBJ; i++) {
198
     for (uint8_t i = 0; i < MAX_OBJ; i++) {
199
         if (!objs[i].active) {
199
         if (!objs[i].active) {
200
             continue;
200
             continue;

+ 149
- 0
src/score.ba0.c View File

1
+/*
2
+ * score.ba0.c
3
+ * Duality
4
+ *
5
+ * Copyright (C) 2025 Thomas Buck <thomas@xythobuz.de>
6
+ *
7
+ * This program is free software: you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation, either version 3 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * See <http://www.gnu.org/licenses/>.
18
+ */
19
+
20
+#include <gbdk/platform.h>
21
+
22
+#include "score.h"
23
+
24
+static struct scores scores[SCORE_NUM * 2];
25
+static uint32_t scores_crc;
26
+
27
+static uint16_t convert_name(char a, char b, char c) {
28
+    // convert to lowercase
29
+    if ((a >= 'A') && (a <= 'Z')) a = a - 'A' + 'a';
30
+    if ((b >= 'A') && (b <= 'Z')) b = b - 'A' + 'a';
31
+    if ((c >= 'A') && (c <= 'Z')) c = c - 'A' + 'a';
32
+
33
+    // skip invalid characters
34
+    if ((a < 'a') || (a > 'z')) a = 'x';
35
+    if ((b < 'a') || (b > 'z')) b = 'x';
36
+    if ((c < 'a') || (c > 'z')) c = 'x';
37
+
38
+    // zero offset in alphabet
39
+    a -= 'a';
40
+    b -= 'a';
41
+    c -= 'a';
42
+
43
+    return (a << 10) | (b << 5) | c;
44
+}
45
+
46
+static uint32_t calc_crc(void) NONBANKED {
47
+    const uint8_t *d = (const uint8_t *)scores;
48
+
49
+    uint32_t c = 0xFFFFFFFF;
50
+    for (size_t i = 0; i < sizeof(scores); i++) {
51
+        // adapted from "Hacker's Delight"
52
+        c ^= d[i];
53
+        for (size_t j = 0; j < 8; j++) {
54
+            uint32_t mask = -(c & 1);
55
+            c = (c >> 1) ^ (0xEDB88320 & mask);
56
+        }
57
+    }
58
+
59
+    return ~c;
60
+}
61
+
62
+static uint8_t check_crc(void) NONBANKED {
63
+    return (calc_crc() == scores_crc) ? 1 : 0;
64
+}
65
+
66
+static void score_init(void) NONBANKED {
67
+    // TODO
68
+    scores[0].name = convert_name('a', 'b', 'c');
69
+    scores[0].score = 10000;
70
+
71
+    scores[1].name = convert_name('a', 'b', 'c');
72
+    scores[1].score = 8000;
73
+
74
+    scores[2].name = convert_name('a', 'b', 'c');
75
+    scores[2].score = 6000;
76
+
77
+    scores[3].name = convert_name('a', 'b', 'c');
78
+    scores[3].score = 4000;
79
+
80
+    scores[4].name = convert_name('a', 'b', 'c');
81
+    scores[4].score = 2000;
82
+
83
+    scores[5].name = convert_name('a', 'b', 'c');
84
+    scores[5].score = -2000;
85
+
86
+    scores[6].name = convert_name('a', 'b', 'c');
87
+    scores[6].score = -4000;
88
+
89
+    scores[7].name = convert_name('a', 'b', 'c');
90
+    scores[7].score = -6000;
91
+
92
+    scores[8].name = convert_name('a', 'b', 'c');
93
+    scores[8].score = -8000;
94
+
95
+    scores[9].name = convert_name('a', 'b', 'c');
96
+    scores[9].score = -10000;
97
+
98
+    scores_crc = calc_crc();
99
+}
100
+
101
+void score_add(struct scores score) NONBANKED {
102
+    ENABLE_RAM;
103
+    SWITCH_RAM(0);
104
+
105
+    // initialize score table when data is invalid
106
+    if (!check_crc()) {
107
+        score_init();
108
+    }
109
+
110
+    // TODO
111
+
112
+    DISABLE_RAM;
113
+}
114
+
115
+struct scores score_highest(uint8_t off) NONBANKED {
116
+    ENABLE_RAM;
117
+    SWITCH_RAM(0);
118
+
119
+    // initialize score table when data is invalid
120
+    if (!check_crc()) {
121
+        score_init();
122
+    }
123
+
124
+    if (off >= SCORE_NUM) {
125
+        off = SCORE_NUM - 1;
126
+    }
127
+    struct scores r = scores[off];
128
+
129
+    DISABLE_RAM;
130
+    return r;
131
+}
132
+
133
+struct scores score_lowest(uint8_t off) NONBANKED {
134
+    ENABLE_RAM;
135
+    SWITCH_RAM(0);
136
+
137
+    // initialize score table when data is invalid
138
+    if (!check_crc()) {
139
+        score_init();
140
+    }
141
+
142
+    if (off >= SCORE_NUM) {
143
+        off = SCORE_NUM - 1;
144
+    }
145
+    struct scores r = scores[(SCORE_NUM * 2) - 1 - off];
146
+
147
+    DISABLE_RAM;
148
+    return r;
149
+}

+ 0
- 32
src/score.c View File

1
-/*
2
- * score.c
3
- * Duality
4
- *
5
- * Copyright (C) 2025 Thomas Buck <thomas@xythobuz.de>
6
- *
7
- * This program is free software: you can redistribute it and/or modify
8
- * it under the terms of the GNU General Public License as published by
9
- * the Free Software Foundation, either version 3 of the License, or
10
- * (at your option) any later version.
11
- *
12
- * This program is distributed in the hope that it will be useful,
13
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
- * GNU General Public License for more details.
16
- *
17
- * See <http://www.gnu.org/licenses/>.
18
- */
19
-
20
-#include "score.h"
21
-
22
-void score_add(int32_t score) {
23
-
24
-}
25
-
26
-int32_t score_highest(uint8_t off) {
27
-    return 8888 - off;
28
-}
29
-
30
-int32_t score_lowest(uint8_t off) {
31
-    return -(9999 - off);
32
-}

+ 8
- 3
src/score.h View File

24
 
24
 
25
 #define SCORE_NUM 5
25
 #define SCORE_NUM 5
26
 
26
 
27
-void score_add(int32_t score);
28
-int32_t score_highest(uint8_t off);
29
-int32_t score_lowest(uint8_t off);
27
+struct scores {
28
+    uint16_t name;
29
+    int32_t score;
30
+};
31
+
32
+void score_add(struct scores score);
33
+struct scores score_highest(uint8_t off);
34
+struct scores score_lowest(uint8_t off);
30
 
35
 
31
 #endif // __SCORE_H__
36
 #endif // __SCORE_H__

+ 2
- 2
src/sound.c View File

27
 
27
 
28
 #include "sound.h"
28
 #include "sound.h"
29
 
29
 
30
-void snd_init(void) {
30
+void snd_init(void) NONBANKED {
31
     NR52_REG = 0x80; // sound on
31
     NR52_REG = 0x80; // sound on
32
     NR51_REG = 0xFF; // all channels on left and right
32
     NR51_REG = 0xFF; // all channels on left and right
33
 
33
 
38
 #endif
38
 #endif
39
 }
39
 }
40
 
40
 
41
-void snd_noise(void) {
41
+void snd_noise(void) NONBANKED {
42
     NR41_REG = 0x2F; // length timer, higher value is shorter time (up to 0x3F)
42
     NR41_REG = 0x2F; // length timer, higher value is shorter time (up to 0x3F)
43
     NR42_REG = 0xF0; // initially full volume, no volume changes over time
43
     NR42_REG = 0xF0; // initially full volume, no volume changes over time
44
     NR43_REG = 0x11; // frequency distribution
44
     NR43_REG = 0x11; // frequency distribution

+ 50
- 25
src/sprite_data.c View File

51
         .pa = rockshp_0_palettes,
51
         .pa = rockshp_0_palettes,
52
         .pa_i = OAMF_CGB_PAL0,
52
         .pa_i = OAMF_CGB_PAL0,
53
         .cnt = rockshp_0_TILE_COUNT,
53
         .cnt = rockshp_0_TILE_COUNT,
54
-        .off = TILE_NUM_START
54
+        .off = TILE_NUM_START,
55
+        .bank = BANK(rockshp_0),
55
     },
56
     },
56
     { // SPR_SHIP_90
57
     { // SPR_SHIP_90
57
         .ms = rockshp_90_metasprites,
58
         .ms = rockshp_90_metasprites,
59
         .pa = NULL,
60
         .pa = NULL,
60
         .pa_i = OAMF_CGB_PAL0,
61
         .pa_i = OAMF_CGB_PAL0,
61
         .cnt = rockshp_90_TILE_COUNT,
62
         .cnt = rockshp_90_TILE_COUNT,
62
-        .off = TILE_NUM_START
63
+        .off = TILE_NUM_START,
64
+        .bank = BANK(rockshp_90),
63
     },
65
     },
64
     { // SPR_THRUST_0
66
     { // SPR_THRUST_0
65
         .ms = thrust_0_metasprites,
67
         .ms = thrust_0_metasprites,
67
         .pa = thrust_0_palettes,
69
         .pa = thrust_0_palettes,
68
         .pa_i = OAMF_CGB_PAL1,
70
         .pa_i = OAMF_CGB_PAL1,
69
         .cnt = thrust_0_TILE_COUNT,
71
         .cnt = thrust_0_TILE_COUNT,
70
-        .off = TILE_NUM_START
72
+        .off = TILE_NUM_START,
73
+        .bank = BANK(thrust_0),
71
     },
74
     },
72
     { // SPR_THRUST_90
75
     { // SPR_THRUST_90
73
         .ms = thrust_90_metasprites,
76
         .ms = thrust_90_metasprites,
75
         .pa = NULL,
78
         .pa = NULL,
76
         .pa_i = OAMF_CGB_PAL1,
79
         .pa_i = OAMF_CGB_PAL1,
77
         .cnt = thrust_90_TILE_COUNT,
80
         .cnt = thrust_90_TILE_COUNT,
78
-        .off = TILE_NUM_START
81
+        .off = TILE_NUM_START,
82
+        .bank = BANK(thrust_90),
79
     },
83
     },
80
     { // SPR_LIGHT
84
     { // SPR_LIGHT
81
         .ms = light_metasprites,
85
         .ms = light_metasprites,
83
         .pa = light_palettes,
87
         .pa = light_palettes,
84
         .pa_i = OAMF_CGB_PAL2,
88
         .pa_i = OAMF_CGB_PAL2,
85
         .cnt = light_TILE_COUNT,
89
         .cnt = light_TILE_COUNT,
86
-        .off = TILE_NUM_START
90
+        .off = TILE_NUM_START,
91
+        .bank = BANK(light),
87
     },
92
     },
88
     { // SPR_DARK
93
     { // SPR_DARK
89
         .ms = dark_metasprites,
94
         .ms = dark_metasprites,
91
         .pa = dark_palettes,
96
         .pa = dark_palettes,
92
         .pa_i = OAMF_CGB_PAL3,
97
         .pa_i = OAMF_CGB_PAL3,
93
         .cnt = dark_TILE_COUNT,
98
         .cnt = dark_TILE_COUNT,
94
-        .off = TILE_NUM_START
99
+        .off = TILE_NUM_START,
100
+        .bank = BANK(dark),
95
     },
101
     },
96
     { // SPR_SHOT
102
     { // SPR_SHOT
97
         .ms = shoot_metasprites,
103
         .ms = shoot_metasprites,
99
         .pa = shoot_palettes,
105
         .pa = shoot_palettes,
100
         .pa_i = OAMF_CGB_PAL4,
106
         .pa_i = OAMF_CGB_PAL4,
101
         .cnt = shoot_TILE_COUNT,
107
         .cnt = shoot_TILE_COUNT,
102
-        .off = TILE_NUM_START
108
+        .off = TILE_NUM_START,
109
+        .bank = BANK(shoot),
103
     },
110
     },
104
     { // SPR_SHOT_LIGHT
111
     { // SPR_SHOT_LIGHT
105
         .ms = shoot_metasprites,
112
         .ms = shoot_metasprites,
107
         .pa = NULL,
114
         .pa = NULL,
108
         .pa_i = OAMF_CGB_PAL2,
115
         .pa_i = OAMF_CGB_PAL2,
109
         .cnt = shoot_TILE_COUNT,
116
         .cnt = shoot_TILE_COUNT,
110
-        .off = SPR_SHOT
117
+        .off = SPR_SHOT,
118
+        .bank = BANK(shoot),
111
     },
119
     },
112
     { // SPR_SHOT_DARK
120
     { // SPR_SHOT_DARK
113
         .ms = shoot_metasprites,
121
         .ms = shoot_metasprites,
115
         .pa = NULL,
123
         .pa = NULL,
116
         .pa_i = OAMF_CGB_PAL3,
124
         .pa_i = OAMF_CGB_PAL3,
117
         .cnt = shoot_TILE_COUNT,
125
         .cnt = shoot_TILE_COUNT,
118
-        .off = SPR_SHOT
126
+        .off = SPR_SHOT,
127
+        .bank = BANK(shoot),
119
     },
128
     },
120
     { // SPR_HEALTH_1
129
     { // SPR_HEALTH_1
121
         .ms = bar_1_metasprites,
130
         .ms = bar_1_metasprites,
123
         .pa = bar_1_palettes,
132
         .pa = bar_1_palettes,
124
         .pa_i = OAMF_CGB_PAL5,
133
         .pa_i = OAMF_CGB_PAL5,
125
         .cnt = bar_1_TILE_COUNT,
134
         .cnt = bar_1_TILE_COUNT,
126
-        .off = TILE_NUM_START
135
+        .off = TILE_NUM_START,
136
+        .bank = BANK(bar_1),
127
     },
137
     },
128
     { // SPR_HEALTH_2
138
     { // SPR_HEALTH_2
129
         .ms = bar_2_metasprites,
139
         .ms = bar_2_metasprites,
131
         .pa = NULL,
141
         .pa = NULL,
132
         .pa_i = OAMF_CGB_PAL5,
142
         .pa_i = OAMF_CGB_PAL5,
133
         .cnt = bar_2_TILE_COUNT,
143
         .cnt = bar_2_TILE_COUNT,
134
-        .off = TILE_NUM_START
144
+        .off = TILE_NUM_START,
145
+        .bank = BANK(bar_2),
135
     },
146
     },
136
     { // SPR_HEALTH_3
147
     { // SPR_HEALTH_3
137
         .ms = bar_3_metasprites,
148
         .ms = bar_3_metasprites,
139
         .pa = NULL,
150
         .pa = NULL,
140
         .pa_i = OAMF_CGB_PAL5,
151
         .pa_i = OAMF_CGB_PAL5,
141
         .cnt = bar_3_TILE_COUNT,
152
         .cnt = bar_3_TILE_COUNT,
142
-        .off = TILE_NUM_START
153
+        .off = TILE_NUM_START,
154
+        .bank = BANK(bar_3),
143
     },
155
     },
144
     { // SPR_HEALTH_4
156
     { // SPR_HEALTH_4
145
         .ms = bar_4_metasprites,
157
         .ms = bar_4_metasprites,
147
         .pa = NULL,
159
         .pa = NULL,
148
         .pa_i = OAMF_CGB_PAL5,
160
         .pa_i = OAMF_CGB_PAL5,
149
         .cnt = bar_4_TILE_COUNT,
161
         .cnt = bar_4_TILE_COUNT,
150
-        .off = TILE_NUM_START
162
+        .off = TILE_NUM_START,
163
+        .bank = BANK(bar_4),
151
     },
164
     },
152
     { // SPR_HEALTH_5
165
     { // SPR_HEALTH_5
153
         .ms = bar_5_metasprites,
166
         .ms = bar_5_metasprites,
155
         .pa = NULL,
168
         .pa = NULL,
156
         .pa_i = OAMF_CGB_PAL5,
169
         .pa_i = OAMF_CGB_PAL5,
157
         .cnt = bar_5_TILE_COUNT,
170
         .cnt = bar_5_TILE_COUNT,
158
-        .off = TILE_NUM_START
171
+        .off = TILE_NUM_START,
172
+        .bank = BANK(bar_5),
159
     },
173
     },
160
     { // SPR_HEALTH_6
174
     { // SPR_HEALTH_6
161
         .ms = bar_6_metasprites,
175
         .ms = bar_6_metasprites,
163
         .pa = NULL,
177
         .pa = NULL,
164
         .pa_i = OAMF_CGB_PAL5,
178
         .pa_i = OAMF_CGB_PAL5,
165
         .cnt = bar_6_TILE_COUNT,
179
         .cnt = bar_6_TILE_COUNT,
166
-        .off = TILE_NUM_START
180
+        .off = TILE_NUM_START,
181
+        .bank = BANK(bar_6),
167
     },
182
     },
168
     { // SPR_HEALTH_7
183
     { // SPR_HEALTH_7
169
         .ms = bar_7_metasprites,
184
         .ms = bar_7_metasprites,
171
         .pa = NULL,
186
         .pa = NULL,
172
         .pa_i = OAMF_CGB_PAL5,
187
         .pa_i = OAMF_CGB_PAL5,
173
         .cnt = bar_7_TILE_COUNT,
188
         .cnt = bar_7_TILE_COUNT,
174
-        .off = TILE_NUM_START
189
+        .off = TILE_NUM_START,
190
+        .bank = BANK(bar_7),
175
     },
191
     },
176
     { // SPR_HEALTH_8
192
     { // SPR_HEALTH_8
177
         .ms = bar_8_metasprites,
193
         .ms = bar_8_metasprites,
179
         .pa = NULL,
195
         .pa = NULL,
180
         .pa_i = OAMF_CGB_PAL5,
196
         .pa_i = OAMF_CGB_PAL5,
181
         .cnt = bar_8_TILE_COUNT,
197
         .cnt = bar_8_TILE_COUNT,
182
-        .off = TILE_NUM_START
198
+        .off = TILE_NUM_START,
199
+        .bank = BANK(bar_8),
183
     },
200
     },
184
     { // SPR_POWER_1
201
     { // SPR_POWER_1
185
         .ms = bar_1_metasprites,
202
         .ms = bar_1_metasprites,
187
         .pa = power_palettes,
204
         .pa = power_palettes,
188
         .pa_i = OAMF_CGB_PAL6,
205
         .pa_i = OAMF_CGB_PAL6,
189
         .cnt = bar_1_TILE_COUNT,
206
         .cnt = bar_1_TILE_COUNT,
190
-        .off = SPR_HEALTH_1
207
+        .off = SPR_HEALTH_1,
208
+        .bank = BANK(bar_1),
191
     },
209
     },
192
     { // SPR_POWER_2
210
     { // SPR_POWER_2
193
         .ms = bar_2_metasprites,
211
         .ms = bar_2_metasprites,
195
         .pa = NULL,
213
         .pa = NULL,
196
         .pa_i = OAMF_CGB_PAL6,
214
         .pa_i = OAMF_CGB_PAL6,
197
         .cnt = bar_2_TILE_COUNT,
215
         .cnt = bar_2_TILE_COUNT,
198
-        .off = SPR_HEALTH_2
216
+        .off = SPR_HEALTH_2,
217
+        .bank = BANK(bar_2),
199
     },
218
     },
200
     { // SPR_POWER_3
219
     { // SPR_POWER_3
201
         .ms = bar_3_metasprites,
220
         .ms = bar_3_metasprites,
203
         .pa = NULL,
222
         .pa = NULL,
204
         .pa_i = OAMF_CGB_PAL6,
223
         .pa_i = OAMF_CGB_PAL6,
205
         .cnt = bar_3_TILE_COUNT,
224
         .cnt = bar_3_TILE_COUNT,
206
-        .off = SPR_HEALTH_3
225
+        .off = SPR_HEALTH_3,
226
+        .bank = BANK(bar_3),
207
     },
227
     },
208
     { // SPR_POWER_4
228
     { // SPR_POWER_4
209
         .ms = bar_4_metasprites,
229
         .ms = bar_4_metasprites,
211
         .pa = NULL,
231
         .pa = NULL,
212
         .pa_i = OAMF_CGB_PAL6,
232
         .pa_i = OAMF_CGB_PAL6,
213
         .cnt = bar_4_TILE_COUNT,
233
         .cnt = bar_4_TILE_COUNT,
214
-        .off = SPR_HEALTH_4
234
+        .off = SPR_HEALTH_4,
235
+        .bank = BANK(bar_4),
215
     },
236
     },
216
     { // SPR_POWER_5
237
     { // SPR_POWER_5
217
         .ms = bar_5_metasprites,
238
         .ms = bar_5_metasprites,
219
         .pa = NULL,
240
         .pa = NULL,
220
         .pa_i = OAMF_CGB_PAL6,
241
         .pa_i = OAMF_CGB_PAL6,
221
         .cnt = bar_5_TILE_COUNT,
242
         .cnt = bar_5_TILE_COUNT,
222
-        .off = SPR_HEALTH_5
243
+        .off = SPR_HEALTH_5,
244
+        .bank = BANK(bar_5),
223
     },
245
     },
224
     { // SPR_POWER_6
246
     { // SPR_POWER_6
225
         .ms = bar_6_metasprites,
247
         .ms = bar_6_metasprites,
227
         .pa = NULL,
249
         .pa = NULL,
228
         .pa_i = OAMF_CGB_PAL6,
250
         .pa_i = OAMF_CGB_PAL6,
229
         .cnt = bar_6_TILE_COUNT,
251
         .cnt = bar_6_TILE_COUNT,
230
-        .off = SPR_HEALTH_6
252
+        .off = SPR_HEALTH_6,
253
+        .bank = BANK(bar_6),
231
     },
254
     },
232
     { // SPR_POWER_7
255
     { // SPR_POWER_7
233
         .ms = bar_7_metasprites,
256
         .ms = bar_7_metasprites,
235
         .pa = NULL,
258
         .pa = NULL,
236
         .pa_i = OAMF_CGB_PAL6,
259
         .pa_i = OAMF_CGB_PAL6,
237
         .cnt = bar_7_TILE_COUNT,
260
         .cnt = bar_7_TILE_COUNT,
238
-        .off = SPR_HEALTH_7
261
+        .off = SPR_HEALTH_7,
262
+        .bank = BANK(bar_7),
239
     },
263
     },
240
     { // SPR_POWER_8
264
     { // SPR_POWER_8
241
         .ms = bar_8_metasprites,
265
         .ms = bar_8_metasprites,
243
         .pa = NULL,
267
         .pa = NULL,
244
         .pa_i = OAMF_CGB_PAL6,
268
         .pa_i = OAMF_CGB_PAL6,
245
         .cnt = bar_8_TILE_COUNT,
269
         .cnt = bar_8_TILE_COUNT,
246
-        .off = SPR_HEALTH_8
270
+        .off = SPR_HEALTH_8,
271
+        .bank = BANK(bar_8),
247
     }
272
     }
248
 };
273
 };

+ 1
- 0
src/sprite_data.h View File

39
     uint8_t pa_i;
39
     uint8_t pa_i;
40
     uint8_t cnt;
40
     uint8_t cnt;
41
     uint8_t off;
41
     uint8_t off;
42
+    uint8_t bank;
42
 };
43
 };
43
 
44
 
44
 extern struct sprites metasprites[SPRITE_COUNT];
45
 extern struct sprites metasprites[SPRITE_COUNT];

+ 9
- 3
src/sprites.c View File

22
 
22
 
23
 #include "sprite_data.h"
23
 #include "sprite_data.h"
24
 
24
 
25
-void spr_init(void) {
25
+void spr_init(void) NONBANKED {
26
     uint8_t off = TILE_NUM_START;
26
     uint8_t off = TILE_NUM_START;
27
     for (uint8_t i = 0; i < SPRITE_COUNT; i++) {
27
     for (uint8_t i = 0; i < SPRITE_COUNT; i++) {
28
+        SWITCH_ROM(metasprites[i].bank);
29
+
28
         if (metasprites[i].pa != NULL) {
30
         if (metasprites[i].pa != NULL) {
29
             set_sprite_palette(metasprites[i].pa_i, 1, metasprites[i].pa);
31
             set_sprite_palette(metasprites[i].pa_i, 1, metasprites[i].pa);
30
         }
32
         }
39
     }
41
     }
40
 }
42
 }
41
 
43
 
42
-void spr_draw(enum SPRITES sprite, enum SPRITE_FLIP flip, int8_t x_off, int8_t y_off, uint8_t frame, uint8_t *hiwater) {
44
+void spr_draw(enum SPRITES sprite, enum SPRITE_FLIP flip,
45
+              int8_t x_off, int8_t y_off, uint8_t frame,
46
+              uint8_t *hiwater) NONBANKED {
47
+    SWITCH_ROM(metasprites[sprite].bank);
48
+
43
     switch (flip) {
49
     switch (flip) {
44
         case FLIP_Y:
50
         case FLIP_Y:
45
             *hiwater += move_metasprite_flipy(
51
             *hiwater += move_metasprite_flipy(
76
     }
82
     }
77
 }
83
 }
78
 
84
 
79
-void spr_ship(enum SPRITE_ROT rot, uint8_t moving, uint8_t *hiwater) {
85
+void spr_ship(enum SPRITE_ROT rot, uint8_t moving, uint8_t *hiwater) NONBANKED {
80
     switch (rot) {
86
     switch (rot) {
81
         case ROT_0:
87
         case ROT_0:
82
             spr_draw(SPR_SHIP_0, FLIP_NONE, 0, 0, 0, hiwater);
88
             spr_draw(SPR_SHIP_0, FLIP_NONE, 0, 0, 0, hiwater);

Loading…
Cancel
Save