Skip to content

Commit 9460cfd

Browse files
committed
Support for MBC7.
1 parent 1ac2fe0 commit 9460cfd

File tree

9 files changed

+462
-1
lines changed

9 files changed

+462
-1
lines changed

core/src/main/java/eu/rekawek/coffeegb/core/Gameboy.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ public void init(EventBus eventBus, SerialEndpoint serialEndpoint, Console conso
184184
background.init(eventBus);
185185
sgbDisplay.init(eventBus);
186186
gameGenie.init(eventBus);
187+
cartridge.init(eventBus);
187188
}
188189

189190
public void run() {

core/src/main/java/eu/rekawek/coffeegb/core/memory/cart/Cartridge.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package eu.rekawek.coffeegb.core.memory.cart;
22

33
import eu.rekawek.coffeegb.core.AddressSpace;
4+
import eu.rekawek.coffeegb.core.events.EventBus;
45
import eu.rekawek.coffeegb.core.memento.Memento;
56
import eu.rekawek.coffeegb.core.memento.Originator;
67
import eu.rekawek.coffeegb.core.memory.cart.battery.Battery;
@@ -33,11 +34,17 @@ public Cartridge(Rom rom, Battery battery) {
3334
addressSpace = new Mbc3(rom, battery);
3435
} else if (type.isMbc5()) {
3536
addressSpace = new Mbc5(rom, battery);
37+
} else if (type.isMbc7()) {
38+
addressSpace = new Mbc7(rom, battery);
3639
} else {
3740
addressSpace = new BasicRom(rom);
3841
}
3942
}
4043

44+
public void init(EventBus eventBus) {
45+
addressSpace.init(eventBus);
46+
}
47+
4148
@Override
4249
public boolean accepts(int address) {
4350
return addressSpace.accepts(address);

core/src/main/java/eu/rekawek/coffeegb/core/memory/cart/CartridgeType.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ public enum CartridgeType {
2424
ROM_MBC5_RAM_BATTERY(0x01b),
2525
ROM_MBC5_RUMBLE(0x1c),
2626
ROM_MBC5_RUMBLE_SRAM(0x1d),
27-
ROM_MBC5_RUMBLE_SRAM_BATTERY(0x1e);
27+
ROM_MBC5_RUMBLE_SRAM_BATTERY(0x1e),
28+
ROM_MBC7_SENSOR_RUMBLE_RAM_BATTERY(0x22);
2829

2930
private final int id;
3031

@@ -48,6 +49,10 @@ public boolean isMbc5() {
4849
return nameContainsSegment("MBC5");
4950
}
5051

52+
public boolean isMbc7() {
53+
return nameContainsSegment("MBC7");
54+
}
55+
5156
public boolean isMmm01() {
5257
return nameContainsSegment("MMM01");
5358
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package eu.rekawek.coffeegb.core.memory.cart;
22

33
import eu.rekawek.coffeegb.core.AddressSpace;
4+
import eu.rekawek.coffeegb.core.events.EventBus;
45
import eu.rekawek.coffeegb.core.memento.Originator;
56

67
import java.io.Serializable;
78

89
public interface MemoryController extends AddressSpace, Serializable, Originator<MemoryController> {
910
default void flushRam() {
1011
}
12+
13+
default void init(EventBus eventBus) {
14+
}
1115
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package eu.rekawek.coffeegb.core.memory.cart.type;
2+
3+
import eu.rekawek.coffeegb.core.events.Event;
4+
5+
public record AccelerometerEvent(double x, double y) implements Event {
6+
}
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package eu.rekawek.coffeegb.core.memory.cart.type;
2+
3+
import eu.rekawek.coffeegb.core.events.EventBus;
4+
import eu.rekawek.coffeegb.core.memento.Memento;
5+
import eu.rekawek.coffeegb.core.memory.cart.MemoryController;
6+
import eu.rekawek.coffeegb.core.memory.cart.Rom;
7+
import eu.rekawek.coffeegb.core.memory.cart.battery.Battery;
8+
9+
/**
10+
* MBC7 Mapper implementation.
11+
* Used by Kirby's Tilt 'n' Tumble.
12+
* Features ROM banking, accelerometer (sensor), and EEPROM.
13+
*/
14+
public class Mbc7 implements MemoryController {
15+
16+
private final int romBanks;
17+
18+
private final int[] cartridge;
19+
20+
private int selectedRomBank = 1;
21+
22+
private boolean ramWriteEnabled1;
23+
24+
private boolean ramWriteEnabled2;
25+
26+
private double x, y = 0;
27+
28+
private int latchX = 0x8000;
29+
30+
private int latchY = 0x8000;
31+
32+
private int latchState = 0;
33+
34+
private final Mbc7Eeprom eeprom;
35+
36+
public Mbc7(Rom rom, Battery battery) {
37+
this.cartridge = rom.getRom();
38+
this.romBanks = rom.getRomBanks();
39+
eeprom = new Mbc7Eeprom(battery);
40+
}
41+
42+
public void init(EventBus eventBus) {
43+
eventBus.register(event -> {
44+
synchronized (this) {
45+
x = event.x();
46+
y = event.y();
47+
}
48+
}, AccelerometerEvent.class);
49+
}
50+
51+
@Override
52+
public boolean accepts(int address) {
53+
return (address >= 0x0000 && address < 0x8000) || (address >= 0xa000 && address < 0xc000);
54+
}
55+
56+
@Override
57+
public void setByte(int address, int value) {
58+
if (address >= 0x0000 && address < 0x2000) {
59+
if (value == 0x0a) {
60+
ramWriteEnabled1 = true;
61+
} else if (value == 0x00) {
62+
ramWriteEnabled1 = false;
63+
ramWriteEnabled2 = false;
64+
}
65+
} else if (address >= 0x2000 && address < 0x3000) {
66+
selectedRomBank = (selectedRomBank & 0x100) | value;
67+
} else if (address >= 0x3000 && address < 0x4000) {
68+
selectedRomBank = (selectedRomBank & 0x0ff) | ((value & 1) << 8);
69+
} else if (address >= 0x4000 && address < 0x6000) {
70+
if (value == 0x40 && ramWriteEnabled1) {
71+
ramWriteEnabled2 = true;
72+
}
73+
} else if (address >= 0xa000 && address < 0xb000 && ramWriteEnabled2) {
74+
int a = (address >> 4) & 0xf;
75+
if (a == 0) {
76+
if (value == 0x55) {
77+
latchState = 1;
78+
latchX = 0x8000;
79+
latchY = 0x8000;
80+
}
81+
} else if (a == 1) {
82+
if (value == 0xaa) {
83+
synchronized (this) {
84+
latchX = 0x81d0 + (int) (x * 0x70);
85+
latchY = 0x81d0 + (int) (y * 0x70);
86+
}
87+
latchState = 0;
88+
}
89+
} else if (a == 8) {
90+
writeEeprom(value);
91+
}
92+
}
93+
}
94+
95+
@Override
96+
public int getByte(int address) {
97+
if (address >= 0x0000 && address < 0x4000) {
98+
return getRomByte(0, address);
99+
} else if (address >= 0x4000 && address < 0x8000) {
100+
return getRomByte(selectedRomBank % romBanks, address - 0x4000);
101+
} else if (address >= 0xa000 && address < 0xc000) {
102+
if (!ramWriteEnabled2) {
103+
return 0xff;
104+
}
105+
return switch ((address >> 4) & 0xf) {
106+
case 2 -> latchX & 0xff;
107+
case 3 -> (latchX >> 8) & 0xff;
108+
case 4 -> latchY & 0xff;
109+
case 5 -> (latchY >> 8) & 0xff;
110+
case 6 -> 0;
111+
case 8 -> readEeprom();
112+
default -> 0xff;
113+
};
114+
} else {
115+
throw new IllegalArgumentException(Integer.toHexString(address));
116+
}
117+
}
118+
119+
private void writeEeprom(int value) {
120+
eeprom.write(value);
121+
}
122+
123+
private int readEeprom() {
124+
return eeprom.read();
125+
}
126+
127+
private int getRomByte(int bank, int address) {
128+
int cartOffset = bank * 0x4000 + address;
129+
if (cartOffset < cartridge.length) {
130+
return cartridge[cartOffset];
131+
} else {
132+
return 0xff;
133+
}
134+
}
135+
136+
@Override
137+
public void flushRam() {
138+
eeprom.flush();
139+
}
140+
141+
@Override
142+
public Memento<MemoryController> saveToMemento() {
143+
return new Mbc7Memento(
144+
selectedRomBank,
145+
ramWriteEnabled1,
146+
ramWriteEnabled2,
147+
x,
148+
y,
149+
latchX,
150+
latchY,
151+
latchState,
152+
eeprom.saveToMemento()
153+
);
154+
}
155+
156+
@Override
157+
public void restoreFromMemento(Memento<MemoryController> memento) {
158+
if (!(memento instanceof Mbc7Memento mem)) {
159+
throw new IllegalArgumentException("Invalid memento type");
160+
}
161+
this.selectedRomBank = mem.selectedRomBank;
162+
this.ramWriteEnabled1 = mem.ramWriteEnabled1;
163+
this.ramWriteEnabled2 = mem.ramWriteEnabled2;
164+
this.x = mem.x;
165+
this.y = mem.y;
166+
this.latchX = mem.latchX;
167+
this.latchY = mem.latchY;
168+
this.latchState = mem.latchState;
169+
this.eeprom.restoreFromMemento(mem.eepromMemento);
170+
}
171+
172+
private record Mbc7Memento(
173+
int selectedRomBank,
174+
boolean ramWriteEnabled1,
175+
boolean ramWriteEnabled2,
176+
double x,
177+
double y,
178+
int latchX,
179+
int latchY,
180+
int latchState,
181+
Memento<Mbc7Eeprom> eepromMemento
182+
) implements Memento<MemoryController> {
183+
}
184+
}

0 commit comments

Comments
 (0)