Skip to content

Commit ff74980

Browse files
committed
2026-05-15 09:47:27
1 parent ba0ea21 commit ff74980

2 files changed

Lines changed: 106 additions & 107 deletions

File tree

src/gpu.rs

Lines changed: 105 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,34 @@ impl From<u8> for Attr {
174174

175175
pub struct Gpu {
176176
glo: Rc<RefCell<Global>>,
177+
178+
bcps: Bgpi,
179+
// This register allows to read/write data to the CGBs Background Palette Memory, addressed through Register FF68.
180+
// Each color is defined by two bytes (Bit 0-7 in first byte).
181+
// Bit 0-4 Red Intensity (00-1F)
182+
// Bit 5-9 Green Intensity (00-1F)
183+
// Bit 10-14 Blue Intensity (00-1F)
184+
// Much like VRAM, data in Palette Memory cannot be read/written during the time when the LCD Controller is
185+
// reading from it. (That is when the STAT register indicates Mode 3). Note: All background colors are initialized
186+
// as white by the boot ROM, but it's a good idea to initialize at least one color yourself (for example if you
187+
// include a soft-reset mechanic).
188+
//
189+
// Note: Type [[[u8; 3]; 4]; 8] equals with [u8; 64].
190+
bcpd: [[[u8; 3]; 4]; 8],
191+
// Digital image with mode RGB. Size = 144 * 160 * 3.
192+
// 3---------
193+
// ----------
194+
// ----------
195+
// ---------- 160
196+
// 144
197+
data: [[[u8; 3]; SCREEN_W]; SCREEN_H],
177198
// The LCD controller operates on a 222 Hz = 4.194 MHz dot clock. An entire frame is 154 scanlines, 70224 dots, or
178199
// 16.74 ms. On scanlines 0 through 143, the LCD controller cycles through modes 2, 3, and 0 once every 456 dots.
179200
// Scanlines 144 through 153 are mode 1.
180201
dots: u32,
181202
lcdc: Lcdc,
203+
ocps: Bgpi,
204+
ocpd: [[[u8; 3]; 4]; 8],
182205
// Extra dots to add to the mode3 threshold due to sprites on the current scanline. Computed when mode2 fires;
183206
// represents floor(T_penalty/4)*4 extra dots before mode0.
184207
pena: u32,
@@ -191,59 +214,8 @@ pub struct Gpu {
191214
// When a new scene is rendered, synchronization is triggered.
192215
sigv: Signal,
193216
stat: Stat,
194-
// Scroll Y (R/W), Scroll X (R/W).
195-
// Specifies the position in the 256x256 pixels BG map (32x32 tiles) which is to be displayed at the upper/left LCD
196-
// display position. Values in range from 0-255 may be used for X/Y each, the video controller automatically wraps
197-
// back to the upper (left) position in BG map when drawing exceeds the lower (right) border of the BG map area.
198-
sx: u8,
199-
sy: u8,
200-
// Window Y Position (R/W), Window X Position minus 7 (R/W).
201-
wx: u8,
202-
wy: u8,
203-
// The Gameboy permanently compares the value of the LYC and LY registers. When both values are identical, the
204-
// coincident bit in the STAT register becomes set, and (if enabled) a STAT interrupt is requested.
205-
lc: u8,
206-
// The LY indicates the vertical line to which the present data is transferred to the LCD Driver. The LY can take
207-
// on any value between 0 through 153. The values between 144 and 153 indicate the V-Blank period. Writing will
208-
// reset the counter.
209-
ly: u8,
210-
211-
// Digital image with mode RGB. Size = 144 * 160 * 3.
212-
// 3---------
213-
// ----------
214-
// ----------
215-
// ---------- 160
216-
// 144
217-
pub data: [[[u8; 3]; SCREEN_W]; SCREEN_H],
218-
219-
// This register assigns gray shades to the color numbers of the BG and Window tiles.
220-
bgp: u8,
221-
// This register assigns gray shades for sprite palette 0. It works exactly as BGP (FF47), except that the lower
222-
// two bits aren't used because sprite data 00 is transparent.
223-
op0: u8,
224-
// This register assigns gray shades for sprite palette 1. It works exactly as BGP (FF47), except that the lower
225-
// two bits aren't used because sprite data 00 is transparent.
226-
op1: u8,
227-
228-
cbgpi: Bgpi,
229-
// This register allows to read/write data to the CGBs Background Palette Memory, addressed through Register FF68.
230-
// Each color is defined by two bytes (Bit 0-7 in first byte).
231-
// Bit 0-4 Red Intensity (00-1F)
232-
// Bit 5-9 Green Intensity (00-1F)
233-
// Bit 10-14 Blue Intensity (00-1F)
234-
// Much like VRAM, data in Palette Memory cannot be read/written during the time when the LCD Controller is
235-
// reading from it. (That is when the STAT register indicates Mode 3). Note: All background colors are initialized
236-
// as white by the boot ROM, but it's a good idea to initialize at least one color yourself (for example if you
237-
// include a soft-reset mechanic).
238-
//
239-
// Note: Type [[[u8; 3]; 4]; 8] equals with [u8; 64].
240-
cbgpd: [[[u8; 3]; 4]; 8],
241-
242-
cobpi: Bgpi,
243-
cobpd: [[[u8; 3]; 4]; 8],
244-
245217
ram: [u8; 0x4000],
246-
ram_bank: usize,
218+
rbk: usize,
247219
// VRAM Sprite Attribute Table (OAM)
248220
// Gameboy video controller can display up to 40 sprites either in 8x8 or in 8x16 pixels. Because of a limitation of
249221
// hardware, only ten sprites can be displayed per scan line. Sprite patterns have the same format as BG tiles, but
@@ -274,42 +246,69 @@ pub struct Gpu {
274246
// Bit3 Tile VRAM-Bank **CGB Mode Only** (0=Bank 0, 1=Bank 1)
275247
// Bit2-0 Palette number **CGB Mode Only** (OBP0-7)
276248
oam: [u8; 0xa0],
277-
249+
// This register assigns gray shades to the color numbers of the BG and Window tiles.
250+
bgp: u8,
251+
// This register assigns gray shades for sprite palette 0. It works exactly as BGP (FF47), except that the lower
252+
// two bits aren't used because sprite data 00 is transparent.
253+
op0: u8,
254+
// This register assigns gray shades for sprite palette 1. It works exactly as BGP (FF47), except that the lower
255+
// two bits aren't used because sprite data 00 is transparent.
256+
op1: u8,
257+
// Scroll Y (R/W), Scroll X (R/W).
258+
// Specifies the position in the 256x256 pixels BG map (32x32 tiles) which is to be displayed at the upper/left LCD
259+
// display position. Values in range from 0-255 may be used for X/Y each, the video controller automatically wraps
260+
// back to the upper (left) position in BG map when drawing exceeds the lower (right) border of the BG map area.
261+
sx: u8,
262+
sy: u8,
263+
// Window Y Position (R/W), Window X Position minus 7 (R/W).
264+
wx: u8,
265+
wy: u8,
266+
// The Gameboy permanently compares the value of the LYC and LY registers. When both values are identical, the
267+
// coincident bit in the STAT register becomes set, and (if enabled) a STAT interrupt is requested.
268+
lc: u8,
269+
// The LY indicates the vertical line to which the present data is transferred to the LCD Driver. The LY can take
270+
// on any value between 0 through 153. The values between 144 and 153 indicate the V-Blank period. Writing will
271+
// reset the counter.
272+
ly: u8,
278273
prio: [(bool, usize); SCREEN_W],
279274
}
280275

281276
impl Gpu {
282277
pub fn power_up(glo: Rc<RefCell<Global>>) -> Self {
283278
Self {
284279
glo,
280+
bcps: Bgpi::power_up(),
281+
bcpd: [[[0u8; 3]; 4]; 8],
282+
data: [[[0xffu8; 3]; SCREEN_W]; SCREEN_H],
285283
dots: 0,
286284
lcdc: Lcdc::power_up(),
285+
ocps: Bgpi::power_up(),
286+
ocpd: [[[0u8; 3]; 4]; 8],
287287
pena: 0,
288288
sigh: Signal::power_up(),
289289
sigq: 0x00,
290290
sigv: Signal::power_up(),
291291
stat: Stat::power_up(),
292+
ram: [0x00; 0x4000],
293+
rbk: 0x00,
294+
oam: [0x00; 0xa0],
295+
bgp: 0x00,
296+
op0: 0x00,
297+
op1: 0x01,
292298
sx: 0x00,
293299
sy: 0x00,
294300
wx: 0x00,
295301
wy: 0x00,
296302
lc: 0x00,
297303
ly: 0x00,
298-
data: [[[0xffu8; 3]; SCREEN_W]; SCREEN_H],
299-
bgp: 0x00,
300-
op0: 0x00,
301-
op1: 0x01,
302-
cbgpi: Bgpi::power_up(),
303-
cbgpd: [[[0u8; 3]; 4]; 8],
304-
cobpi: Bgpi::power_up(),
305-
cobpd: [[[0u8; 3]; 4]; 8],
306-
ram: [0x00; 0x4000],
307-
ram_bank: 0x00,
308-
oam: [0x00; 0xa0],
309304
prio: [(true, 0); SCREEN_W],
310305
}
311306
}
312307

308+
pub fn image(&self) -> &[[[u8; 3]; SCREEN_W]; SCREEN_H] {
309+
&self.data
310+
}
311+
313312
// DMG OAM corruption (mode 2 only). Row 0 and rows ≥20 are immune.
314313
pub fn oam_corrupt(&mut self, kind: OamBug) {
315314
if !self.lcdc.bit7() || self.stat.mode() != 2 || self.ly >= 144 {
@@ -410,7 +409,7 @@ impl Gpu {
410409
match self.glo.clone().borrow().term {
411410
Term::DMG => self.pixel_gre(x, self.gray_shades(self.bgp, col)),
412411
Term::CGB => {
413-
let [r, g, b] = self.cbgpd[attr.paln()][col];
412+
let [r, g, b] = self.bcpd[attr.paln()][col];
414413
self.pixel_rgb(x, r, g, b)
415414
}
416415
}
@@ -501,7 +500,7 @@ impl Gpu {
501500
self.pixel_gre(sx as usize, self.gray_shades(pal, col));
502501
}
503502
Term::CGB => {
504-
let [r, g, b] = self.cobpd[at.paln()][col];
503+
let [r, g, b] = self.ocpd[at.paln()][col];
505504
self.pixel_rgb(sx as usize, r, g, b);
506505
}
507506
}
@@ -610,7 +609,7 @@ impl Memory for Gpu {
610609
// VRAM is locked during mode 3, and also during the 4T pre-mode-3 period (mode 2, dots >= 76) where
611610
// the bus is already claimed by the GPU, matching real DMG hardware bus timing.
612611
let locked = self.stat.mode() == 3 || (self.stat.mode() == 2 && self.dots >= 76);
613-
if !locked { self.ram[self.ram_bank * 0x2000 + a as usize - 0x8000] } else { 0xff }
612+
if !locked { self.ram[self.rbk * 0x2000 + a as usize - 0x8000] } else { 0xff }
614613
}
615614
0xfe00..=0xfe9f => {
616615
// OAM is locked (returns 0xFF) during mode 2 (OAM scan) and mode 3 (pixel transfer). It is also locked
@@ -631,32 +630,32 @@ impl Memory for Gpu {
631630
0xff49 => self.op1,
632631
0xff4a => self.wy,
633632
0xff4b => self.wx,
634-
0xff4f => 0xfe | self.ram_bank as u8,
635-
0xff68 => self.cbgpi.lb(0xff68),
633+
0xff4f => 0xfe | self.rbk as u8,
634+
0xff68 => self.bcps.lb(0xff68),
636635
0xff69 => {
637-
let r = self.cbgpi.addr() as usize >> 3;
638-
let c = self.cbgpi.addr() as usize >> 1 & 0x3;
639-
if self.cbgpi.addr() & 0x01 == 0x00 {
640-
let a = self.cbgpd[r][c][0];
641-
let b = self.cbgpd[r][c][1] << 5;
636+
let r = self.bcps.addr() as usize >> 3;
637+
let c = self.bcps.addr() as usize >> 1 & 0x3;
638+
if self.bcps.addr() & 0x01 == 0x00 {
639+
let a = self.bcpd[r][c][0];
640+
let b = self.bcpd[r][c][1] << 5;
642641
a | b
643642
} else {
644-
let a = self.cbgpd[r][c][1] >> 3;
645-
let b = self.cbgpd[r][c][2] << 2;
643+
let a = self.bcpd[r][c][1] >> 3;
644+
let b = self.bcpd[r][c][2] << 2;
646645
a | b
647646
}
648647
}
649-
0xff6a => self.cobpi.lb(0xff6a),
648+
0xff6a => self.ocps.lb(0xff6a),
650649
0xff6b => {
651-
let r = self.cobpi.addr() as usize >> 3;
652-
let c = self.cobpi.addr() as usize >> 1 & 0x3;
653-
if self.cobpi.addr() & 0x01 == 0x00 {
654-
let a = self.cobpd[r][c][0];
655-
let b = self.cobpd[r][c][1] << 5;
650+
let r = self.ocps.addr() as usize >> 3;
651+
let c = self.ocps.addr() as usize >> 1 & 0x3;
652+
if self.ocps.addr() & 0x01 == 0x00 {
653+
let a = self.ocpd[r][c][0];
654+
let b = self.ocpd[r][c][1] << 5;
656655
a | b
657656
} else {
658-
let a = self.cobpd[r][c][1] >> 3;
659-
let b = self.cobpd[r][c][2] << 2;
657+
let a = self.ocpd[r][c][1] >> 3;
658+
let b = self.ocpd[r][c][2] << 2;
660659
a | b
661660
}
662661
}
@@ -670,7 +669,7 @@ impl Memory for Gpu {
670669
if self.stat.mode() == 3 {
671670
return;
672671
}
673-
self.ram[self.ram_bank * 0x2000 + a as usize - 0x8000] = v;
672+
self.ram[self.rbk * 0x2000 + a as usize - 0x8000] = v;
674673
}
675674
0xfe00..=0xfe9f => {
676675
// OAM writes are ignored during mode 3 (pixel transfer) and during mode 2 (OAM scan) while dots < 76.
@@ -726,35 +725,35 @@ impl Memory for Gpu {
726725
0xff49 => self.op1 = v,
727726
0xff4a => self.wy = v,
728727
0xff4b => self.wx = v,
729-
0xff4f => self.ram_bank = (v & 0x01) as usize,
730-
0xff68 => self.cbgpi.sb(0xff68, v),
728+
0xff4f => self.rbk = (v & 0x01) as usize,
729+
0xff68 => self.bcps.sb(0xff68, v),
731730
0xff69 => {
732-
let r = self.cbgpi.addr() as usize >> 3;
733-
let c = self.cbgpi.addr() as usize >> 1 & 0x03;
734-
if self.cbgpi.addr() & 0x01 == 0x00 {
735-
self.cbgpd[r][c][0] = v & 0x1f;
736-
self.cbgpd[r][c][1] = (self.cbgpd[r][c][1] & 0x18) | (v >> 5);
731+
let r = self.bcps.addr() as usize >> 3;
732+
let c = self.bcps.addr() as usize >> 1 & 0x03;
733+
if self.bcps.addr() & 0x01 == 0x00 {
734+
self.bcpd[r][c][0] = v & 0x1f;
735+
self.bcpd[r][c][1] = (self.bcpd[r][c][1] & 0x18) | (v >> 5);
737736
} else {
738-
self.cbgpd[r][c][1] = (self.cbgpd[r][c][1] & 0x07) | ((v & 0x03) << 3);
739-
self.cbgpd[r][c][2] = (v >> 2) & 0x1f;
737+
self.bcpd[r][c][1] = (self.bcpd[r][c][1] & 0x07) | ((v & 0x03) << 3);
738+
self.bcpd[r][c][2] = (v >> 2) & 0x1f;
740739
}
741-
if self.cbgpi.auto() {
742-
self.cbgpi.incr();
740+
if self.bcps.auto() {
741+
self.bcps.incr();
743742
}
744743
}
745-
0xff6a => self.cobpi.sb(0xff6a, v),
744+
0xff6a => self.ocps.sb(0xff6a, v),
746745
0xff6b => {
747-
let r = self.cobpi.addr() as usize >> 3;
748-
let c = self.cobpi.addr() as usize >> 1 & 0x03;
749-
if self.cobpi.addr() & 0x01 == 0x00 {
750-
self.cobpd[r][c][0] = v & 0x1f;
751-
self.cobpd[r][c][1] = (self.cobpd[r][c][1] & 0x18) | (v >> 5);
746+
let r = self.ocps.addr() as usize >> 3;
747+
let c = self.ocps.addr() as usize >> 1 & 0x03;
748+
if self.ocps.addr() & 0x01 == 0x00 {
749+
self.ocpd[r][c][0] = v & 0x1f;
750+
self.ocpd[r][c][1] = (self.ocpd[r][c][1] & 0x18) | (v >> 5);
752751
} else {
753-
self.cobpd[r][c][1] = (self.cobpd[r][c][1] & 0x07) | ((v & 0x03) << 3);
754-
self.cobpd[r][c][2] = (v >> 2) & 0x1f;
752+
self.ocpd[r][c][1] = (self.ocpd[r][c][1] & 0x07) | ((v & 0x03) << 3);
753+
self.ocpd[r][c][2] = (v >> 2) & 0x1f;
755754
}
756-
if self.cobpi.auto() {
757-
self.cobpi.incr();
755+
if self.ocps.auto() {
756+
self.ocps.incr();
758757
}
759758
}
760759
_ => unreachable!(),

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ fn mode_minifb(argu: &Argument) {
192192
// Update the window
193193
if mbrd.mmu.borrow_mut().gpu.sigv_censor() {
194194
let mut i: usize = 0;
195-
for l in mbrd.mmu.borrow().gpu.data.iter() {
195+
for l in mbrd.mmu.borrow().gpu.image().iter() {
196196
for w in l.iter() {
197197
let b = u32::from(w[0]) << 16;
198198
let g = u32::from(w[1]) << 8;

0 commit comments

Comments
 (0)