ragarnoy / gbmu Goto Github PK
View Code? Open in Web Editor NEW(Rust) GameBoy (color) eMUlator
(Rust) GameBoy (color) eMUlator
Set the value at FF50
to non-zero to disable boot ROM
Read values from the tile map for the background and the window. These values are 1 byte index of the tile.
The tile maps are stored in 0x9800-0x9BFF
and/or in 0x9C00-0x9FFF
.
The Background map adress is defined by the value of the register LCDC bit 3:
0x9800-0x9BFF
.0x9C00-0x9FFF
.The Window map adress is defined by the value of the register LCDC bit 6:
0x9800-0x9BFF
.0x9C00-0x9FFF
.Depending on the value of the register LCDC bit 4, the tile index can point to a different block of the tile sheet:
0x8000-0x8FFF
.0x8800-0x97FF
.block | obj index | bg/win index if LCDC.4==0 | bg/win index if LCDC.4==1 |
---|---|---|---|
0x8000-0x87FF |
0 - 127 | 0 - 127 | |
0x8800-0x8FFF |
128 - 255 | 128 - 255 | -128 - -1 |
0x9000-0x97FF |
0 - 127 |
TBD
Our disassembler needs to have
This is the architecture I'm currently envisionning for the project. Feel free to propose anything else.
gb-cpu gb-dbg gb-lcd gb-rom .......
\ \ / /
GBMU (workspace crate(?))
main.rs defaults to CLI app
bin/app.rs builds "app" like binaries
Possible issue : gb-dbg might depend on gb-lcd for graphic crate
warning: this `else { if .. }` block can be collapsed
--> gb-ppu/src/lib.rs:24:28
|
24 | } else {
| ____________________________^
25 | | if (i + j) % 2 == 0 {
26 | | [100; 3]
27 | | } else {
28 | | [200; 3]
29 | | }
30 | | };
| |_____________________^
|
= note: `#[warn(clippy::collapsible_else_if)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
help: collapse nested if block
|
24 | } else if (i + j) % 2 == 0 {
25 | [100; 3]
26 | } else {
27 | [200; 3]
28 | };
|
warning: you should consider adding a `Default` implementation for `PPU`
--> gb-ppu/src/lib.rs:8:5
|
8 | / pub fn new() -> Self {
9 | | Self {
10 | | pixels: [[255; 3]; TEXTURE_SIZE],
11 | | }
12 | | }
| |_____^
|
= note: `#[warn(clippy::new_without_default)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
help: try this
|
7 | impl Default for PPU {
8 | fn default() -> Self {
9 | Self::new()
10 | }
11 | }
|
warning: 2 warnings emitted
JR NZ
JR Z
JR NC
JR C
JR
GitHub action doesn't launch test for other crate in workspace
edit the test part with
cargo test --workspace
to test all the workspace
Invalid range detection for root rom bank. It should include 0x3FFF.
fn read_rom(&self, addr: Box<dyn Address<Area>>) -> Result<u8, Error> {
// ...
let is_root_bank = address < 0x3fff;
// ...
if is_root_bank {
// ...
} else {
let address = address - 0x4000; // Here
// ...
}
}
Pixels values are stored in the vram, between 0x8000 and 0x97FF. A pixel value is defined on 2 bits (so between 0 and 3). A couple of bytes defines 8 pixels values. The bits of the first byte are the least significant bits of the pixels values, and the bits of the second byte are the most significant bits of the pixels values. In both bytes, bit 7 represents the leftmost pixel, and bit 0 the rightmost.
From the pan doc
0x3366 -> 00110011
01100110
hexa | bin |
---|---|
0x33 |
00110011 |
0x66 |
01100110 |
Pixels values:
Index | 1st bit | 2nd bit | Value |
---|---|---|---|
0 | 0 | 0 | 0 |
1 | 0 | 1 | 2 |
2 | 1 | 1 | 3 |
3 | 1 | 0 | 1 |
4 | 0 | 0 | 0 |
5 | 0 | 1 | 2 |
6 | 1 | 1 | 3 |
7 | 1 | 0 | 1 |
which gives the following line of pixels: 13201320
Implement a function that can read 8 pixels values from the vram.
pixels values are not color but color id to be mapped to a color palette.
CPU
& PPU
How we sync the execution between the CPU
and the PPU
Term | Alias | Description |
---|---|---|
Cycle | M-Cycle | A CPU tick |
Dot | T-Cycle | A PPU tick |
CPU
& PPU
tickIn one Cycle
(of the CPU
) the PPU
have the time to do 4 Dot
(4 time faster)
One implementation could be:
Ticker
?) that allow to tick the component that implement it (CPU
, PPU
, APU
, SPU
).tick
that take a number of tick to pass (A tick could be a Cycle
or a Dot
)requirement for the debugger:
n
frames)n
seconds)I don't know if this behavior is from the egui crate or egui_sdl2_gl.
For now we should always have some ui set in our windows or have resizing disabled.
make so GBWindows can have no ui (no egui context/egui painter).
JP HL
JP u16
JP Z, u16
JP NZ, u16
JP C, u16
JP NC, u16
The Object Attribute Table hold the data of 40 objects (sprites) in 0xFE00-0xFE9F
. An object can be a 8x8 or a 8x16 sprite. Each object is made of 4 bytes:
At each scanline, the ppu selects up to 10 valid object according to their Y position.
The Y position of the sprite + 16. A 8xH sprite is fully displayed on the screen with 168 - H > Y >= 16.
The X position of the sprite + 8. A sprite is fully displayed on the screen with 168 > X >= 8.
The sprite mode is defined by the bit 2 of LCDC:
The monochrome palette are stored in register of one byte each (FF47
, FF48
and FF49
).
A palette map a color index from the tile data to a color for the screen.
Each color is stored on 2 bits:
Value | Color |
---|---|
0 | white |
1 | light gray |
2 | dark gray |
3 | black |
FF47
Bit 7-6 - Color for index 3
Bit 5-4 - Color for index 2
Bit 3-2 - Color for index 1
Bit 1-0 - Color for index 0
FF48
Bit 7-6 - Color for index 3
Bit 5-4 - Color for index 2
Bit 3-2 - Color for index 1
Bit 1-0 - ignored (index 0 is transparent for objects)
FF49
Bit 7-6 - Color for index 3
Bit 5-4 - Color for index 2
Bit 3-2 - Color for index 1
Bit 1-0 - ignored (index 0 is transparent for objects)
cf #36
This issue track the opcode implementation of the CPU
grouped into similar opcode
DMG Bios
DMG Bios
warning: this `if` statement can be collapsed
--> src/main.rs:57:17
|
57 | / if ui.button("Debug").clicked() {
58 | | if debug_window.is_none() {
59 | | debug_window = Some(
60 | | GBWindow::new("GBMU Debug", (800, 600), false, &video_subsystem)
... |
63 | | }
64 | | }
| |_________________^
|
= note: `#[warn(clippy::collapsible_if)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
help: collapse nested if block
|
57 | if ui.button("Debug").clicked() && debug_window.is_none() {
58 | debug_window = Some(
59 | GBWindow::new("GBMU Debug", (800, 600), false, &video_subsystem)
60 | .expect("Error while building debug window"),
61 | );
62 | }
|
warning: use of `unwrap_or` followed by a function call
--> src/main.rs:52:53
|
52 | ... std::env::current_dir().unwrap_or(std::path::PathBuf::from("/")),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| std::path::PathBuf::from("/"))`
|
= note: `#[warn(clippy::or_fun_call)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
warning: 2 warnings emitted
rework cpu microcode to use ControlFlow
Github Action doesn't cache the cargo artifact. resulting in redownload & rebuilding the deps losing a lot of time ~3min each
warning: constants have by default a `'static` lifetime
--> gb-lcd/src/render.rs:17:16
|
17 | const VS_SRC: &'static str = include_str!("render.vert");
| -^^^^^^^---- help: consider removing `'static`: `&str`
|
= note: `#[warn(clippy::redundant_static_lifetimes)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
warning: constants have by default a `'static` lifetime
--> gb-lcd/src/render.rs:18:16
|
18 | const FS_SRC: &'static str = include_str!("render.frag");
| -^^^^^^^---- help: consider removing `'static`: `&str`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
warning: you should consider adding a `Default` implementation for `render::Render`
--> gb-lcd/src/render.rs:101:5
|
101 | / pub fn new() -> Self {
102 | | // Create Vertex Array Object
103 | | let mut vao = 0;
104 | | let mut vbo = 0;
... |
139 | | }
140 | | }
| |_____^
|
= note: `#[warn(clippy::new_without_default)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
help: try this
|
100 | impl Default for render::Render {
101 | fn default() -> Self {
102 | Self::new()
103 | }
104 | }
|
warning: redundant closure
--> gb-lcd/src/window.rs:39:22
|
39 | .map_err(|err| Error::GBWindowInit(err))?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `Error::GBWindowInit`
|
= note: `#[warn(clippy::redundant_closure)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
warning: redundant closure
--> gb-lcd/src/window.rs:47:26
|
47 | .map_err(|err| Error::GBWindowInit(err))?
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `Error::GBWindowInit`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
warning: redundant closure
--> gb-lcd/src/window.rs:87:22
|
87 | .map_err(|err| Error::GBWindowFrame(err))?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `Error::GBWindowFrame`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
warning: redundant closure
--> gb-lcd/src/window.rs:103:22
|
103 | .map_err(|err| Error::GBWindowFrame(err))?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `Error::GBWindowFrame`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
warning: unneeded `return` statement
--> gb-lcd/src/window.rs:132:9
|
132 | return Ok(());
| ^^^^^^^^^^^^^^ help: remove `return`: `Ok(())`
|
= note: `#[warn(clippy::needless_return)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
warning: redundant closure
--> gb-lcd/src/window.rs:122:22
|
122 | .map_err(|err| Error::GBWindowFrame(err))?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `Error::GBWindowFrame`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
warning: unneeded `return` statement
--> gb-lcd/src/window.rs:146:9
|
146 | return false;
| ^^^^^^^^^^^^^ help: remove `return`: `false`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
warning: this lifetime isn't used in the function definition
--> gb-lcd/src/lib.rs:8:13
|
8 | pub fn init<'a>() -> Result<(Sdl, VideoSubsystem, EventPump), Error> {
| ^^
|
= note: `#[warn(clippy::extra_unused_lifetimes)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
warning: unneeded `return` statement
--> gb-lcd/src/lib.rs:21:5
|
21 | return Ok((sdl_context, video_subsystem, event_pump));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `Ok((sdl_context, video_subsystem, event_pump))`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
warning: redundant closure
--> gb-lcd/src/lib.rs:9:44
|
9 | let sdl_context = sdl2::init().map_err(|err| Error::MainSys(err))?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `Error::MainSys`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
warning: redundant closure
--> gb-lcd/src/lib.rs:10:55
|
10 | let video_subsystem = sdl_context.video().map_err(|err| Error::MainSys(err))?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `Error::MainSys`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
warning: redundant closure
--> gb-lcd/src/lib.rs:14:18
|
14 | .map_err(|err| Error::MainSys(err))?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `Error::MainSys`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
warning: 15 warnings emitted
The IO register Area is mapped to FF00
to FF7F
in the address bus
Start | End | First appeared | Purpose |
---|---|---|---|
0xFF00 |
DMG | Controller | |
0xFF01 |
0xFF02 |
DMG | Communication |
0xFF04 |
0xFF07 |
DMG | Divider and Timer |
0xFF10 |
0xFF26 |
DMG | Sound |
0xFF30 |
0xFF3F |
DMG | Waveform RAM |
0xFF40 |
0xFF4B |
DMG | LCD |
0xFF4F |
CGB | VRAM Bank Select | |
0xFF50 |
DMG | Set to non-zero to disable boot ROM | |
0xFF51 |
0xFF55 |
CGB | VRAM DMA |
0xFF68 |
0xFF69 |
CGB | BG / OBJ Palettes |
0xFF70 |
CGB | WRAM Bank Select |
I was think of the following possible implementations:
IO regs
component:
PPU
implement the color palette register and then give a reference to theIO regs
component
IO regs
component is the only place where we store the IO registers:
PPU
require the color palette register and request a reference to the said register toIO regs
component
IO regs
to get/set value of the register they're working on.
PPU
need to read the value of the color palette register, askIO regs
component to get the value of the said register
AddressBus
, we could reuse FileOperation
on each purpose area and allow to control if a register can be read/writeHow should we handle errors, knowing that the gameboy didn't really "handle" errors, we should have something that makes debugging easy.
Should all workspace crate depend on an error handling crate ? Or should they all use a crate like anyhow
?
Input requirement for GBMU
:
A
/ B
start
select
The Bus trait should be the basic interface to all memory owning components, whatever the type (ROM, RAM, Registers, etc).
This trait will be able to:
A bus is a communication system that transfers data between components inside a computer.
The structs holding memory should implement this trait to manage the storage and retrieval of data.
Functions:
- get: return {Item} at location {T} in &self.
- set: Set {Data} at location {T} in &self, return {Result}
Types:
- T: The type of data the bus is indexed by.
- Item: The type of data returned by a get on the Bus.
- Result: The type of data return by a set on the Bus.
- Data: The type of Data set by the bus.
I've been think about the trait gb_clock::ticker::Ticker
while implementing it for Cpu
and thinking how do I test the implementation.
The problem with the current definition (Ticker<B: Bus<u8> + Bus<u16>>
is:
You have to implement
Ticker
for each type of bus you want to use and those may have a different implementation from one another
let say I want to implement Ticker
for 2 type of Bus: AddressBus
and MockAddressBus
.
with the current implementation It require to do:
impl Ticker<AddressBus> for Cpu {
// ...
}
impl Ticker<MockAddressBus> for Cpu {
// ..
}
I suggest to rewrite the trait Ticker
to following
trait Ticker {
// previous method of `Ticker` ...
fn tick<B: Bus<u8> + Bus<u16>>(&mut self, adr_bus: &mut B);
}
notice here that only Ticker::tick
is changed to hold the generic Bus.
with the suggestion, If I want to implement the 2 buses (AddressBus
and MockAddressBus
)
impl Ticker for Cpu {
// other method of `Ticker` ...
fn tick<B: Bus<u8> + Bus<u16>>(&mut self, adr_bus: &mut B) {
// ...
}
}
This suggestion is inspired by serde::Serialize
How can we package GBMU
to allow user to install it with all of his dependencies on OSX
/ Linux
For OSX, I think of the following:
brew
script,brew install GBMU
.App
file #48For Linux, we MUST be at least able to install it on XUbuntu.
I think of the following
brew
script.deb
file,GBMU
with that command : dpkg -i GBMU.deb
)snap install gbmu
flatapk install <repo> gbmu
but require to run the app like that flatapk run gbmu
docker
container by forwarding socket for X server #45 & #61appimage
#61Expose only the DMG registers for now:
8000 - 9FFF
.FE00 - FE9F
.FF40
.FF41
.FF42 - FF45
.FF46
.FF47 - FF49
.FF4A - FF4B
.Some feature that could be added to GBMU
:
Edit this message if you want to list other feature for UX
WIP
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.