Code Monkey home page Code Monkey logo

gbmu's People

Contributors

42plamusse avatar cempassi avatar firelightflagboy avatar guilhemsmith avatar ragarnoy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

guilhemsmith

gbmu's Issues

Read tile maps from VRAM

Read values from the tile map for the background and the window. These values are 1 byte index of the tile.

Map positions

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:

  • bit is 0: background map is in 0x9800-0x9BFF.
  • bit is 0: background map is in 0x9C00-0x9FFF.

The Window map adress is defined by the value of the register LCDC bit 6:

  • bit is 0: window map is in 0x9800-0x9BFF.
  • bit is 0: window map is in 0x9C00-0x9FFF.

Tile sheet block

Depending on the value of the register LCDC bit 4, the tile index can point to a different block of the tile sheet:

  • bit is 0: index from the map start from 0x8000 in the tile sheet. The index is an unsigned byte so the tiles are stored in 0x8000-0x8FFF.
  • bit is 1: index from the map start from 0x9000 in the tile sheet. The index is a signed byte so the tiles are stored in 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

Implement disassembler behaviour

Our disassembler needs to have

  • An address field to inform the address of the instruction. It starts at the address of the PC
  • An instruction field that shows the representation of the opcode in a legible format
  • Option field TBD
  • Data field for hexadecimal repr of the opcode?? #129

Define global project architecture

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

Clippy warning on `gb-ppu`

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

Github action no launch all test

GitHub action doesn't launch test for other crate in workspace

edit the test part with

cargo test --workspace

to test all the workspace

Read pixel value from vram

Pixel value

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

example

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

To do

Implement a function that can read 8 pixels values from the vram.

NB

pixels values are not color but color id to be mapped to a color palette.

How to tick the `CPU` & `PPU`

How to tick the CPU & PPU

How we sync the execution between the CPU and the PPU

Terms

Term Alias Description
Cycle M-Cycle A CPU tick
Dot T-Cycle A PPU tick

How the CPU & PPU tick

In one Cycle (of the CPU) the PPU have the time to do 4 Dot (4 time faster)

A though on the implementation

One implementation could be:

  • An interface (Ticker?) that allow to tick the component that implement it (CPU, PPU, APU, SPU).
    That interface define a single method tick that take a number of tick to pass (A tick could be a Cycle or a Dot)

Debugger requirement

requirement for the debugger:

Memory Editor

  • 8bit Memory Repr
  • Memory read &write
  • Option collapsing menu
  • ASCII Display

Disassembler

  • Display the x next instruction to execute
  • Instruction write ?

Register Editor

  • Display the status of the:
    • CPU Registers
    • PPU Registers
    • Misc. Registers
  • Register read & write

Flow Controller

  • Execute an instruction
  • Execute Steps by steps:
    • by frame (maybe allow to elapse by n frames)
    • by 1 second (maybe allow to elapse by n seconds)

Debugging Bus(?)

Viewport is not resized on linux when there is no ui

I don't know if this behavior is from the egui crate or egui_sdl2_gl.

current solution

For now we should always have some ui set in our windows or have resizing disabled.

todo

make so GBWindows can have no ui (no egui context/egui painter).

Collect OAM objects

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:

  • Y position
  • X position
  • Tile index
  • Attributes

At each scanline, the ppu selects up to 10 valid object according to their Y position.

Y position

The Y position of the sprite + 16. A 8xH sprite is fully displayed on the screen with 168 - H > Y >= 16.

X position

The X position of the sprite + 8. A sprite is fully displayed on the screen with 168 > X >= 8.

Tile index

The sprite mode is defined by the bit 2 of LCDC:

  • LCDC.2==0: 8x8 sprite mode, since a sprite is made of only one tile the tile index correspond to its index.
  • LCDC.2==1: 8x16 sprite mode, a sprite is made of two adjacent tiles and the byte indicate the index of the first one. The first tile is the top of the sprite. Note that the hardware enforce the index to point at a pair value by ignoring the least significant bit of the byte.

Attributes

  • Bit 7: BG and Window over OBJ (0=No, 1=BG and Window colors 1-3 over the OBJ)
  • Bit 6: Y flip (0=Normal, 1=Vertically mirrored)
  • Bit 5: X flip (0=Normal, 1=Horizontally mirrored)
  • Bit 4: Palette number Non CGB Mode Only (0=OBP0, 1=OBP1)
  • Bit 3: Tile VRAM-Bank CGB Mode Only (0=Bank 0, 1=Bank 1)
  • Bit 2-0: Palette number CGB Mode Only (OBP0-7)

Read monochrome color palette

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.

Color

Each color is stored on 2 bits:

Value Color
0 white
1 light gray
2 dark gray
3 black

BG palette data (BGP) - 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

Object palette 0 data (OBP0) - 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)

Object palette 1 data (OBP1) - 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)

CPU opcode implementation

This issue track the opcode implementation of the CPU grouped into similar opcode

Opcode to implement

Used in DMG Bios

Load/Store/Move

Control/Branch

Arithmetic Logic Unit

Rotate/Shift

Bit opcode

Not used in DMG Bios

Load/Store/Move

Rotate/Shift

Arithmetic Logic Unit

Bit opcode

Other

With interrupt

Clippy warning on `src`

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

GBMU do not list `.gbc` rom files

When loading a rom for the main app, we are missing the .gbc files

Loading from GBMU

when load a rom

File available in the same folder

file available in the same folder

What I'm expecting

I'm expecting to be able to load Gameboy color rom files (.gbc)

Clippy warning on `gb-lcd`

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

How to implement IO register Area (DMG Only)

How to implement IO register Area

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

A though on possible implementation

I was think of the following possible implementations:

  • Each component that implement one of those register give a reference to the IO regs component:

    PPU implement the color palette register and then give a reference to the IO regs component

  • The IO regs component is the only place where we store the IO registers:
    • Each component that need one of those request a reference to the register they need

      PPU require the color palette register and request a reference to the said register to IO regs component

    • Each component interact directly with IO regs to get/set value of the register they're working on.

      PPU need to read the value of the color palette register, ask IO regs component to get the value of the said register

  • If we fallback to a sub implementation like AddressBus, we could reuse FileOperation on each purpose area and allow to control if a register can be read/write

Todo

Error handling

How 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

Input requirement for GBMU:

  • control pad:
    • up
    • down
    • left
    • right
  • buttons:
    • A / B
    • start
    • select

Bus trait implementation

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:

  • Get data from the struct it is implemented for
  • Set data in the struct it is implemented for

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.

Rework `Ticker`

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

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 {
  // ..
}

The solution I propose

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 to package `GBMU`

How can we package GBMU to allow user to install it with all of his dependencies on OSX / Linux

For OSX

For OSX, I think of the following:

  • brew script,
    allow to install like this brew install GBMU
  • .App file #48

For Linux

For Linux, we MUST be at least able to install it on XUbuntu.
I think of the following

  • brew script
  • .deb file,
    user will install GBMU with that command : dpkg -i GBMU.deb )
  • snap documentation here
    install with snap install gbmu
  • flatapk documentation here
    install with flatapk install <repo> gbmu but require to run the app like that flatapk run gbmu
  • docker container by forwarding socket for X server #45 & #61
  • appimage #61

Expose ppu memory and register

Expose only the DMG registers for now:

  • expose its memory: #102
    • the VRAM 8000 - 9FFF.
    • the OAM FE00 - FE9F.
  • expose its registers: #126
    • the lcd/ppu control register FF40.
    • the lcd/ppu status register FF41.
    • the viewport scrolling registers FF42 - FF45.
    • the DMA transfers register FF46.
    • the palettes registers FF47 - FF49.
    • the window position registers FF4A - FF4B.

BONUS: Bios

Manage GameBoy (DMG + CGB) bootstrap (scrolling logo)

add bios for:

BONUS: UX

Some feature that could be added to GBMU:

Edit this message if you want to list other feature for UX

BONUS: Save state

allow user to save the state of GBMU to be able to resume it later

Checklist

  • Save Mbcs state #530
    • Save Mbcs Ram
    • Save Mbcs register
  • WRAM #541
  • HRAM #542
  • Cpu #527
    • Save Cpu Register
    • Save IE Reg
  • PPU #533
    • Save Vram
    • Save OAM Table
    • PPU register
  • Shared

Register Editor/Viewer

WIP

  • Create columns for data and define layout/style with general
  • Define behaviour for 8 & 16 bits register display
  • Define behaviour for read functions

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.