From 58a503d2c491bde97ca136a76e8e086cb4df0054 Mon Sep 17 00:00:00 2001 From: Amneesh Singh Date: Sat, 22 Jun 2024 04:16:12 +0530 Subject: [PATCH] io: add display unit added rendering for modes 3,4,5 also changed how memory structuring works Signed-off-by: Amneesh Singh --- include/bus.hh | 47 +---- include/io/display/display.hh | 159 +++++++++++++++ include/io/dma.hh | 39 ++++ include/io/io.hh | 13 +- include/io/lcd.hh | 84 -------- include/io/sound.hh | 2 +- include/memory.hh | 60 ++++++ src/bus.cc | 85 ++++---- src/io/display/display.cc | 23 +++ src/io/display/meson.build | 3 + src/io/display/render.cc | 51 +++++ src/io/io.cc | 359 +++++++++++++++++++++++----------- src/io/meson.build | 2 +- 13 files changed, 652 insertions(+), 275 deletions(-) create mode 100644 include/io/display/display.hh create mode 100644 include/io/dma.hh delete mode 100644 include/io/lcd.hh create mode 100644 include/memory.hh create mode 100644 src/io/display/display.cc create mode 100644 src/io/display/meson.build create mode 100644 src/io/display/render.cc diff --git a/include/bus.hh b/include/bus.hh index 3737bd7..9a1fdfe 100644 --- a/include/bus.hh +++ b/include/bus.hh @@ -2,9 +2,8 @@ #include "header.hh" #include "io/io.hh" +#include "memory.hh" #include -#include -#include #include namespace matar { @@ -85,7 +84,7 @@ class Bus { } template - std::optional read(uint32_t address) const; + T read(uint32_t address) const; template void write(uint32_t address, T value); @@ -101,45 +100,11 @@ class Bus { static constexpr decltype(cycle_map) init_cycle_count(); std::unique_ptr io; + Memory bios = {}; + Memory<0x40000> board_wram = {}; + Memory<0x80000> chip_wram = {}; + Memory<> rom; -#define MEMORY_REGION(name, start) \ - static constexpr uint32_t name##_START = start; \ - static constexpr uint8_t name##_REGION = start >> 24 & 0xF; - -#define DECL_MEMORY(name, ident, start, end) \ - MEMORY_REGION(name, start) \ - std::array ident = {}; - - MEMORY_REGION(BIOS, 0x00000000) - std::array bios = {}; - - // board working RAM - DECL_MEMORY(BOARD_WRAM, board_wram, 0x02000000, 0x0203FFFF) - - // chip working RAM - DECL_MEMORY(CHIP_WRAM, chip_wram, 0x03000000, 0x03007FFF) - - // palette RAM - DECL_MEMORY(PALETTE_RAM, palette_ram, 0x05000000, 0x050003FF) - - // video RAM - DECL_MEMORY(VRAM, vram, 0x06000000, 0x06017FFF) - - // OAM OBJ attributes - DECL_MEMORY(OAM_OBJ_ATTR, oam_obj_attr, 0x07000000, 0x070003FF) - -#undef DECL_MEMORY - - MEMORY_REGION(ROM_0, 0x08000000) - MEMORY_REGION(ROM_1, 0x0A000000) - MEMORY_REGION(ROM_2, 0x0C000000) - - MEMORY_REGION(IO, 0x04000000) - static constexpr uint32_t IO_END = 0x040003FE; - -#undef MEMORY_REGION - - std::vector rom; Header header; void parse_header(); }; diff --git a/include/io/display/display.hh b/include/io/display/display.hh new file mode 100644 index 0000000..ed2e756 --- /dev/null +++ b/include/io/display/display.hh @@ -0,0 +1,159 @@ +#include "memory.hh" +#include +#include +#include +#include + +// NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays) +namespace matar { +namespace display { +static constexpr int LCD_WIDTH = 240; + +// there are 5 modes +static constexpr uint N_MODES = 6; +// there are 4 backgrounds that can be layered depending on mode +// there is also 1 object layer +static constexpr uint N_BACKGROUNDS = 4; + +static constexpr uint32_t PRAM_START = 0x5000000; +static constexpr uint32_t VRAM_START = 0x6000000; +static constexpr uint32_t OAM_START = 0x7000000; + +template>> +struct Point { + T x; + T y; +}; + +struct Color { + public: + Color(uint16_t raw) + : red(raw & 0b11111) + , green(raw >> 5 & 0b11111) + , blue(raw >> 10 & 0b11111) {} + + uint16_t read() const { + return (red & 0b11111) | ((green << 5) & 0b11111) | + ((blue << 10) & 0b11111); + } + + private: + uint8_t red; + uint8_t green; + uint8_t blue; +}; + +struct DisplayControl { + struct { + uint8_t mode : 3; + int : 1; // unused + bool frame_select_1 : 1; + bool hblank_free_interval : 1; + bool obj_character_vram_mapping : 1; + bool forced_blank : 1; + bool screen_display_0 : 1; + bool screen_display_1 : 1; + bool screen_display_2 : 1; + bool screen_display_3 : 1; + bool screen_display_obj : 1; + bool window_display_0 : 1; + bool window_display_1 : 1; + bool obj_window_display : 1; + } value; + + uint16_t read() const { return std::bit_cast(value); }; + void write(uint16_t raw) { value = std::bit_cast(raw); }; +}; + +struct DisplayStatus { + struct { + bool vblank_flag : 1; + bool hblank_flag : 1; + bool vcounter_flag : 1; + bool vblank_irq_enable : 1; + bool hblank_irq_enable : 1; + bool vcounter_irq_enable : 1; + int : 2; // unused + uint8_t vcount_setting : 8; + } value; + + uint16_t read() const { return std::bit_cast(value); }; + void write(uint16_t raw) { value = std::bit_cast(raw); }; +}; + +struct BackgroundControl { + struct { + uint8_t priority : 2; + uint8_t character_base_block : 2; + int : 2; // unused + bool mosaic : 1; + bool colors256 : 1; + uint8_t screen_base_block : 5; + bool bg_2_3_wraparound : 1; + uint8_t screen_size : 2; + } value; + + uint16_t read() const { return std::bit_cast(value); }; + void write(uint16_t raw) { value = std::bit_cast(raw); }; +}; + +struct RotationScaling { + // these are all 16 bit signed "fixed point" floats + // shifted by 8 + int16_t a; + int16_t b; + int16_t c; + int16_t d; + + // following points have 28 bit signed "fixed point" floats as coords + // shifted by 8 + Point ref; + + private: + Point internal [[maybe_unused]] + ; +}; + +struct Display { + public: + using u16 = uint16_t; + + Memory<0x400> pram; + Memory<0x18000> vram; + Memory<0x400> oam; + + DisplayControl lcd_control; + DisplayStatus general_lcd_status; + u16 vertical_counter; + BackgroundControl bg_control[4]; + Point bg0_offset; + Point bg1_offset; + Point bg2_offset; + Point bg3_offset; + RotationScaling bg2_rot_scale; + RotationScaling bg3_rot_scale; + u16 win0_horizontal_dimensions; + u16 win1_horizontal_dimensions; + u16 win0_vertical_dimensions; + u16 win1_vertical_dimensions; + u16 inside_win_0_1; + u16 outside_win; + u16 mosaic_size; + u16 color_special_effects_selection; + u16 alpha_blending_coefficients; + u16 brightness_coefficient; + + private: + // 1 color is 16 bits in ARGB555 format + std::array, N_BACKGROUNDS> scanline_buffers; + + template> + void render_bitmap_mode(); + + template= 0 && LAYER <= 3>> + void render_text_layer(); +}; +} +} +// NOLINTEND(cppcoreguidelines-avoid-c-arrays) diff --git a/include/io/dma.hh b/include/io/dma.hh new file mode 100644 index 0000000..aa88aff --- /dev/null +++ b/include/io/dma.hh @@ -0,0 +1,39 @@ +#include +#include + +namespace matar { +// NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays) +struct DmaControl { + struct { + int : 4; // this is supposed to be 5 bits, however, to align the struct + // to 16 bits, we will adjust for the first LSB in the + // read/write + uint8_t dst_adjustment : 2; + uint8_t src_adjustment : 2; + bool repeat : 1; + bool transfer_32 : 1; + int : 1; + uint8_t start_timing : 2; + bool irq_enable : 1; + bool enable : 1; + } value; + + uint16_t read() const { return std::bit_cast(value) << 1; }; + void write(uint16_t raw) { + value = std::bit_cast(static_cast(raw >> 1)); + }; +}; + +struct Dma { + using u16 = uint16_t; + + struct { + u16 source[2]; + u16 destination[2]; + u16 word_count; + DmaControl control; + } channels[4]; +}; +// NOLINTEND(cppcoreguidelines-avoid-c-arrays) + +} diff --git a/include/io/io.hh b/include/io/io.hh index bddb440..5008e62 100644 --- a/include/io/io.hh +++ b/include/io/io.hh @@ -1,11 +1,14 @@ #pragma once -#include "lcd.hh" + +#include "display/display.hh" +#include "dma.hh" #include "sound.hh" #include #include namespace matar { -class Bus; +class Bus; // forward declaration + class IoDevices { public: IoDevices(std::weak_ptr); @@ -30,9 +33,11 @@ class IoDevices { bool low_power_mode; } system = {}; - struct lcd lcd = {}; - struct sound sound = {}; + display::Display display = {}; + Sound sound = {}; + Dma dma = {}; std::weak_ptr bus; + friend class Bus; }; } diff --git a/include/io/lcd.hh b/include/io/lcd.hh deleted file mode 100644 index fa9bd21..0000000 --- a/include/io/lcd.hh +++ /dev/null @@ -1,84 +0,0 @@ -#include - -// NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays) - -/* - 4000000h 2 R/W DISPCNT LCD Control - 4000002h 2 R/W - Undocumented - Green Swap - 4000004h 2 R/W DISPSTAT General LCD Status (STAT,LYC) - 4000006h 2 R VCOUNT Vertical Counter (LY) - 4000008h 2 R/W BG0CNT BG0 Control - 400000Ah 2 R/W BG1CNT BG1 Control - 400000Ch 2 R/W BG2CNT BG2 Control - 400000Eh 2 R/W BG3CNT BG3 Control - 4000010h 2 W BG0HOFS BG0 X-Offset - 4000012h 2 W BG0VOFS BG0 Y-Offset - 4000014h 2 W BG1HOFS BG1 X-Offset - 4000016h 2 W BG1VOFS BG1 Y-Offset - 4000018h 2 W BG2HOFS BG2 X-Offset - 400001Ah 2 W BG2VOFS BG2 Y-Offset - 400001Ch 2 W BG3HOFS BG3 X-Offset - 400001Eh 2 W BG3VOFS BG3 Y-Offset - 4000020h 2 W BG2PA BG2 Rotation/Scaling Parameter A (dx) - 4000022h 2 W BG2PB BG2 Rotation/Scaling Parameter B (dmx) - 4000024h 2 W BG2PC BG2 Rotation/Scaling Parameter C (dy) - 4000026h 2 W BG2PD BG2 Rotation/Scaling Parameter D (dmy) - 4000028h 4 W BG2X BG2 Reference Point X-Coordinate - 400002Ch 4 W BG2Y BG2 Reference Point Y-Coordinate - 4000030h 2 W BG3PA BG3 Rotation/Scaling Parameter A (dx) - 4000032h 2 W BG3PB BG3 Rotation/Scaling Parameter B (dmx) - 4000034h 2 W BG3PC BG3 Rotation/Scaling Parameter C (dy) - 4000036h 2 W BG3PD BG3 Rotation/Scaling Parameter D (dmy) - 4000038h 4 W BG3X BG3 Reference Point X-Coordinate - 400003Ch 4 W BG3Y BG3 Reference Point Y-Coordinate - 4000040h 2 W WIN0H Window 0 Horizontal Dimensions - 4000042h 2 W WIN1H Window 1 Horizontal Dimensions - 4000044h 2 W WIN0V Window 0 Vertical Dimensions - 4000046h 2 W WIN1V Window 1 Vertical Dimensions - 4000048h 2 R/W WININ Inside of Window 0 and 1 - 400004Ah 2 R/W WINOUT Inside of OBJ Window & Outside of Windows - 400004Ch 2 W MOSAIC Mosaic Size - 400004Eh - - Not used - 4000050h 2 R/W BLDCNT Color Special Effects Selection - 4000052h 2 R/W BLDALPHA Alpha Blending Coefficients - 4000054h 2 W BLDY Brightness (Fade-In/Out) Coefficient - 4000056h - - Not used -*/ - -struct lcd { - using u16 = uint16_t; - - u16 lcd_control; - u16 general_lcd_status; - u16 vertical_counter; - u16 bg0_control; - u16 bg1_control; - u16 bg2_control; - u16 bg3_control; - u16 bg0_x_offset; - u16 bg0_y_offset; - u16 bg1_x_offset; - u16 bg1_y_offset; - u16 bg2_x_offset; - u16 bg2_y_offset; - u16 bg3_x_offset; - u16 bg3_y_offset; - u16 bg2_rot_scaling_parameters[4]; - u16 bg2_reference_x[2]; - u16 bg2_reference_y[2]; - u16 bg3_rot_scaling_parameters[4]; - u16 bg3_reference_x[2]; - u16 bg3_reference_y[2]; - u16 win0_horizontal_dimensions; - u16 win1_horizontal_dimensions; - u16 win0_vertical_dimensions; - u16 win1_vertical_dimensions; - u16 inside_win_0_1; - u16 outside_win; - u16 mosaic_size; - u16 color_special_effects_selection; - u16 alpha_blending_coefficients; - u16 brightness_coefficient; -}; - -// NOLINTEND(cppcoreguidelines-avoid-c-arrays) diff --git a/include/io/sound.hh b/include/io/sound.hh index 20f9fbf..8c6cde0 100644 --- a/include/io/sound.hh +++ b/include/io/sound.hh @@ -30,7 +30,7 @@ 40000A4h 4 W FIFO_B Channel B FIFO, Data 0-3 */ -struct sound { +struct Sound { using u16 = uint16_t; // channel 1 diff --git a/include/memory.hh b/include/memory.hh new file mode 100644 index 0000000..2841d3f --- /dev/null +++ b/include/memory.hh @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include +#include + +// ill use [] instead of at because i dont want if (...) throw conditions for +// all accesses to improve performance (?) + +// we are also not gonna perform bound checks, as i expect the user to handle +// those + +namespace matar { +template +class Memory { + // we can use either a vector or an array with this + using Container = std:: + conditional_t<(N != 0), std::array, std::vector>; + + public: + Memory() = default; + Memory(auto x) + : memory(x) {} + + uint8_t read_byte(std::size_t idx) const { return memory[idx]; } + + void write_byte(std::size_t idx, uint8_t byte) { memory[idx] = byte; } + + uint16_t read_halfword(std::size_t idx) const { + return memory[idx] | memory[idx + 1] << 8; + } + + void write_halfword(std::size_t idx, uint16_t halfword) { + memory[idx] = halfword & 0xFF; + memory[idx + 1] = halfword >> 8 & 0xFF; + } + + uint32_t read_word(std::size_t idx) const { + return memory[idx] | memory[idx + 1] << 8 | memory[idx + 2] << 16 | + memory[idx + 3] << 24; + } + + void write_word(std::size_t idx, uint32_t word) { + memory[idx] = word & 0xFF; + memory[idx + 1] = word >> 8 & 0xFF; + memory[idx + 2] = word >> 16 & 0xFF; + memory[idx + 3] = word >> 24 & 0xFF; + } + + uint8_t& operator[](std::size_t idx) { return memory.at(idx); } + + Container& data() { return memory; } + + std::size_t size() const { return memory.size(); } + + private: + Container memory; +}; +} diff --git a/src/bus.cc b/src/bus.cc index fe3759d..008b025 100644 --- a/src/bus.cc +++ b/src/bus.cc @@ -5,13 +5,32 @@ namespace matar { +// Constants +#define MEMORY(AREA, start) \ + static constexpr uint32_t AREA##_START = start; \ + static constexpr uint8_t AREA##_REGION = (AREA##_START >> 24) & 0xFF; + +MEMORY(BIOS, 0x0000000); +MEMORY(BOARD_WRAM, 0x2000000); +MEMORY(CHIP_WRAM, 0x3000000); +MEMORY(PRAM, display::PRAM_START); +MEMORY(VRAM, display::VRAM_START); +MEMORY(OAM, display::OAM_START); +MEMORY(ROM_0, 0x8000000); +MEMORY(ROM_1, 0xA000000); +MEMORY(ROM_2, 0xC000000); +static constexpr uint32_t IO_START = 0x4000000; +static constexpr uint32_t IO_END = 0x40003FE; + +#undef MEMORY + Bus::Bus(Private, std::array&& bios, std::vector&& rom) : cycle_map(init_cycle_count()) , bios(std::move(bios)) , rom(std::move(rom)) { - std::string bios_hash = crypto::sha256(this->bios); + std::string bios_hash = crypto::sha256(this->bios.data()); static constexpr std::string_view expected_hash = "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570"; @@ -69,17 +88,16 @@ Bus::init_cycle_count() { map[IO_REGION] = { 1, 1, 1, 1 }; map[OAM_REGION] = { 1, 1, 1, 1 }; */ - map[3] = { 1, 1, 1, 1 }; - map[BOARD_WRAM_REGION] = { .n16 = 3, .n32 = 6, .s16 = 3, .s32 = 6 }; - map[PALETTE_RAM_REGION] = { .n16 = 1, .n32 = 2, .s16 = 1, .s32 = 2 }; - map[VRAM_REGION] = { .n16 = 1, .n32 = 2, .s16 = 1, .s32 = 2 }; + map[BOARD_WRAM_REGION] = { .n16 = 3, .n32 = 6, .s16 = 3, .s32 = 6 }; + map[PRAM_REGION] = { .n16 = 1, .n32 = 2, .s16 = 1, .s32 = 2 }; + map[VRAM_REGION] = { .n16 = 1, .n32 = 2, .s16 = 1, .s32 = 2 }; // TODO: GamePak access cycles return map; } template -std::optional +T Bus::read(uint32_t address) const { // this is cleaned than std::enable_if @@ -99,12 +117,11 @@ Bus::read(uint32_t address) const { if (i > area.size() - N) \ break; \ if constexpr (std::is_same_v) \ - return area[i]; \ + return area.read_byte(i); \ else if constexpr (std::is_same_v) \ - return area[i] | area[i + 1] << 8; \ + return area.read_halfword(i); \ else if constexpr (std::is_same_v) \ - return area[i] | area[i + 1] << 8 | area[i + 2] << 16 | \ - area[i + 3] << 24; \ + return area.read_word(i); \ } #define MATCHES_PAK(AREA, area) \ @@ -114,9 +131,9 @@ Bus::read(uint32_t address) const { MATCHES(BIOS, bios) MATCHES(BOARD_WRAM, board_wram) MATCHES(CHIP_WRAM, chip_wram) - MATCHES(PALETTE_RAM, palette_ram) - MATCHES(VRAM, vram) - MATCHES(OAM_OBJ_ATTR, oam_obj_attr) + MATCHES(PRAM, io->display.pram) + MATCHES(VRAM, io->display.vram) + MATCHES(OAM, io->display.oam) MATCHES_PAK(ROM_0, rom) MATCHES_PAK(ROM_1, rom) @@ -125,8 +142,14 @@ Bus::read(uint32_t address) const { #undef MATCHES } - glogger.error("Invalid memory region read"); - return {}; + glogger.error("invalid memory region read at {:08x}", address); + + if constexpr (std::is_same_v) + return 0xFF; + else if constexpr (std::is_same_v) + return 0xFFFF; + else if constexpr (std::is_same_v) + return 0xFFFFFFFF; } template @@ -140,36 +163,32 @@ Bus::write(uint32_t address, T value) { : std::is_same_v ? 2 : std::is_same_v ? 4 : 0; + switch (address >> 24 & 0xF) { #define MATCHES(AREA, area) \ case AREA##_REGION: { \ uint32_t i = address - AREA##_START; \ if (i > area.size() - N) \ break; \ - if constexpr (std::is_same_v) { \ - area[i] = value; \ - } else if constexpr (std::is_same_v) { \ - area[i] = value & 0xFF; \ - area[i + 1] = value >> 8 & 0xFF; \ - } else if constexpr (std::is_same_v) { \ - area[i] = value & 0xFF; \ - area[i + 1] = value >> 8 & 0xFF; \ - area[i + 2] = value >> 16 & 0xFF; \ - area[i + 3] = value >> 24 & 0xFF; \ - } \ + if constexpr (std::is_same_v) \ + area.write_byte(i, value); \ + else if constexpr (std::is_same_v) \ + area.write_halfword(i, value); \ + else if constexpr (std::is_same_v) \ + area.write_word(i, value); \ return; \ } MATCHES(BOARD_WRAM, board_wram) MATCHES(CHIP_WRAM, chip_wram) - MATCHES(PALETTE_RAM, palette_ram) - MATCHES(VRAM, vram) - MATCHES(OAM_OBJ_ATTR, oam_obj_attr) + MATCHES(PRAM, io->display.pram) + MATCHES(VRAM, io->display.vram) + MATCHES(OAM, io->display.oam) #undef MATCHES } - glogger.error("Invalid memory region written"); + glogger.error("invalid memory region written at {:08x}", address); } uint8_t @@ -177,7 +196,7 @@ Bus::read_byte(uint32_t address) { if (address >= IO_START && address <= IO_END) return io->read_byte(address); - return read(address).value_or(0xFF); + return read(address); } void @@ -198,7 +217,7 @@ Bus::read_halfword(uint32_t address) { if (address >= IO_START && address <= IO_END) return io->read_halfword(address); - return read(address).value_or(0xFFFF); + return read(address); } void @@ -222,7 +241,7 @@ Bus::read_word(uint32_t address) { if (address >= IO_START && address <= IO_END) return io->read_word(address); - return read(address).value_or(0xFFFFFFFF); + return read(address); } void diff --git a/src/io/display/display.cc b/src/io/display/display.cc new file mode 100644 index 0000000..dfe1879 --- /dev/null +++ b/src/io/display/display.cc @@ -0,0 +1,23 @@ +#include "io/display/display.hh" + +namespace matar { +namespace display { + +/* +static constexpr uint LCD_HEIGHT = 160; +static constexpr uint LCD_WIDTH = 240; +static constexpr uint BLANK = 68; + +static constexpr uint PIXEL_CYCLES = 4; // 4 +static constexpr uint HDRAW_CYCLES = LCD_WIDTH * PIXEL_CYCLES + 46; // 1006 +static constexpr uint HBLANK_CYCLES = BLANK * PIXEL_CYCLES - 46; // 226 +static constexpr uint HREFRESH_CYCLES = HDRAW_CYCLES + HBLANK_CYCLES; // 1232 +static constexpr uint VDRAW_CYCLES = LCD_HEIGHT * HREFRESH_CYCLES; // 197120 +static constexpr uint VBLANK_CYCLES = BLANK * HREFRESH_CYCLES; // 83776 +static constexpr uint VREFRESH_CYCLES = VDRAW_CYCLES + VBLANK_CYCLES; // 280896 +*/ + +void +Display::mode_3() {} +} +} diff --git a/src/io/display/meson.build b/src/io/display/meson.build new file mode 100644 index 0000000..d522634 --- /dev/null +++ b/src/io/display/meson.build @@ -0,0 +1,3 @@ +lib_sources += files( + 'display.cc' +) \ No newline at end of file diff --git a/src/io/display/render.cc b/src/io/display/render.cc new file mode 100644 index 0000000..717e719 --- /dev/null +++ b/src/io/display/render.cc @@ -0,0 +1,51 @@ +#include "io/display/display.hh" + +namespace matar { +namespace display { + +struct TextScreen { + uint16_t tile_number : 10; + bool mirror_horizontal : 1; + bool mirror_vertical : 1; + uint8_t palette_number : 4; +}; + +// if 16th bit is set, this will denote the transparent color in rgb555 format +static constexpr uint16_t TRANSPARENT_RGB555 = 0x8000; + +template +void +Display::render_bitmap_mode() { + static constexpr std::size_t VIEWPORT_WIDTH = MODE == 5 ? 160 : 240; + + for (int x = 0; x < LCD_WIDTH; x++) { + // pixel to texel for x + // shift by 8 cuz both ref.x and a are fixed point floats shifted by 8 + int32_t x_ = (bg2_rot_scale.ref.x + x * bg2_rot_scale.a) >> 8; + int32_t y_ = (bg2_rot_scale.ref.y + x * bg2_rot_scale.c) >> 8; + + // ignore handling area overflow for bitmap modes + // i am not sure how well this will turn out + + std::size_t idx = y_ * VIEWPORT_WIDTH + x_; + + // mode 3 and 5 takes 2 bytes per pixel + if constexpr (MODE != 4) + idx *= 2; + + // offset + if constexpr (MODE != 3) { + std::size_t offset = + lcd_control.value.frame_select_1 ? 0xA000 : 0x0000; + idx += offset; + } + + // read two bytes + if constexpr (MODE == 4) + scanline_buffers[2][x] = pram.read_halfword(vram.read_byte(idx)); + else + scanline_buffers[2][x] = vram.read_halfword(idx); + } +} +} +} diff --git a/src/io/io.cc b/src/io/io.cc index 22153a0..2e3f944 100644 --- a/src/io/io.cc +++ b/src/io/io.cc @@ -76,6 +76,24 @@ ADDR FIFO_A_H = 0x40000A2; ADDR FIFO_B_L = 0x40000A4; ADDR FIFO_B_H = 0x40000A6; +// dma +ADDR DMA0SAD = 0x40000B0; +ADDR DMA0DAD = 0x40000B4; +ADDR DMA0CNT_L = 0x40000B8; +ADDR DMA0CNT_H = 0x40000BA; +ADDR DMA1SAD = 0x40000BC; +ADDR DMA1DAD = 0x40000C0; +ADDR DMA1CNT_L = 0x40000C4; +ADDR DMA1CNT_H = 0x40000C6; +ADDR DMA2SAD = 0x40000C8; +ADDR DMA2DAD = 0x40000CC; +ADDR DMA2CNT_L = 0x40000D0; +ADDR DMA2CNT_H = 0x40000D2; +ADDR DMA3SAD = 0x40000D4; +ADDR DMA3DAD = 0x40000D8; +ADDR DMA3CNT_L = 0x40000DC; +ADDR DMA3CNT_H = 0x40000DE; + // system ADDR POSTFLG = 0x4000300; ADDR IME = 0x4000208; @@ -130,45 +148,87 @@ IoDevices::read_halfword(uint32_t address) const { case name: \ return var; - // lcd - READ(DISPCNT, lcd.lcd_control) - READ(DISPSTAT, lcd.general_lcd_status) - READ(VCOUNT, lcd.vertical_counter) - READ(WININ, lcd.inside_win_0_1) - READ(WINOUT, lcd.outside_win) - READ(BLDCNT, lcd.color_special_effects_selection) - READ(BLDALPHA, lcd.alpha_blending_coefficients) + // lcd + case DISPCNT: + return display.lcd_control.read(); + case DISPSTAT: + return display.general_lcd_status.read(); + case BG0CNT: + return display.bg_control[0].read(); + case BG1CNT: + return display.bg_control[1].read(); + case BG2CNT: + return display.bg_control[2].read(); + case BG3CNT: + return display.bg_control[3].read(); - // sound - READ(SOUND1CNT_L, sound.ch1_sweep) - READ(SOUND1CNT_H, sound.ch1_duty_length_env) - READ(SOUND1CNT_X, sound.ch1_freq_control) - READ(SOUND2CNT_L, sound.ch2_duty_length_env) - READ(SOUND2CNT_H, sound.ch2_freq_control) - READ(SOUND3CNT_L, sound.ch3_stop_wave_ram_select) - READ(SOUND3CNT_H, sound.ch3_length_volume) - READ(SOUND3CNT_X, sound.ch3_freq_control) - READ(WAVE_RAM0_L, sound.ch3_wave_pattern[0]); - READ(WAVE_RAM0_H, sound.ch3_wave_pattern[1]); - READ(WAVE_RAM1_L, sound.ch3_wave_pattern[2]); - READ(WAVE_RAM1_H, sound.ch3_wave_pattern[3]); - READ(WAVE_RAM2_L, sound.ch3_wave_pattern[4]); - READ(WAVE_RAM2_H, sound.ch3_wave_pattern[5]); - READ(WAVE_RAM3_L, sound.ch3_wave_pattern[6]); - READ(WAVE_RAM3_H, sound.ch3_wave_pattern[7]); - READ(SOUND4CNT_L, sound.ch4_length_env); - READ(SOUND4CNT_H, sound.ch4_freq_control); - READ(SOUNDCNT_L, sound.ctrl_stereo_volume); - READ(SOUNDCNT_H, sound.ctrl_mixing); - READ(SOUNDCNT_X, sound.ctrl_sound_on_off); - READ(SOUNDBIAS, sound.pwm_control); + READ(VCOUNT, display.vertical_counter) + READ(WININ, display.inside_win_0_1) + READ(WINOUT, display.outside_win) + READ(BLDCNT, display.color_special_effects_selection) + READ(BLDALPHA, display.alpha_blending_coefficients) - // system - READ(POSTFLG, system.post_boot_flag) - READ(IME, system.interrupt_master_enabler) - READ(IE, system.interrupt_enable); - READ(IF, system.interrupt_request_flags); - READ(WAITCNT, system.waitstate_control); + // sound + READ(SOUND1CNT_L, sound.ch1_sweep) + READ(SOUND1CNT_H, sound.ch1_duty_length_env) + READ(SOUND1CNT_X, sound.ch1_freq_control) + READ(SOUND2CNT_L, sound.ch2_duty_length_env) + READ(SOUND2CNT_H, sound.ch2_freq_control) + READ(SOUND3CNT_L, sound.ch3_stop_wave_ram_select) + READ(SOUND3CNT_H, sound.ch3_length_volume) + READ(SOUND3CNT_X, sound.ch3_freq_control) + READ(WAVE_RAM0_L, sound.ch3_wave_pattern[0]); + READ(WAVE_RAM0_H, sound.ch3_wave_pattern[1]); + READ(WAVE_RAM1_L, sound.ch3_wave_pattern[2]); + READ(WAVE_RAM1_H, sound.ch3_wave_pattern[3]); + READ(WAVE_RAM2_L, sound.ch3_wave_pattern[4]); + READ(WAVE_RAM2_H, sound.ch3_wave_pattern[5]); + READ(WAVE_RAM3_L, sound.ch3_wave_pattern[6]); + READ(WAVE_RAM3_H, sound.ch3_wave_pattern[7]); + READ(SOUND4CNT_L, sound.ch4_length_env); + READ(SOUND4CNT_H, sound.ch4_freq_control); + READ(SOUNDCNT_L, sound.ctrl_stereo_volume); + READ(SOUNDCNT_H, sound.ctrl_mixing); + READ(SOUNDCNT_X, sound.ctrl_sound_on_off); + READ(SOUNDBIAS, sound.pwm_control); + + // dma + case DMA0CNT_H: + return dma.channels[0].control.read(); + case DMA1CNT_H: + return dma.channels[1].control.read(); + case DMA2CNT_H: + return dma.channels[2].control.read(); + case DMA3CNT_H: + return dma.channels[3].control.read(); + + READ(DMA0SAD, dma.channels[0].source[0]); + READ(DMA0SAD + 2, dma.channels[0].source[1]); + READ(DMA0DAD, dma.channels[0].destination[0]); + READ(DMA0DAD + 2, dma.channels[0].destination[1]); + READ(DMA0CNT_L, dma.channels[0].word_count); + READ(DMA1SAD, dma.channels[1].source[0]); + READ(DMA1SAD + 2, dma.channels[1].source[1]); + READ(DMA1DAD, dma.channels[1].destination[0]); + READ(DMA1DAD + 2, dma.channels[1].destination[1]); + READ(DMA1CNT_L, dma.channels[1].word_count); + READ(DMA2SAD, dma.channels[2].source[0]); + READ(DMA2SAD + 2, dma.channels[2].source[1]); + READ(DMA2DAD, dma.channels[2].destination[0]); + READ(DMA2DAD + 2, dma.channels[2].destination[1]); + READ(DMA2CNT_L, dma.channels[2].word_count); + READ(DMA3SAD, dma.channels[3].source[0]); + READ(DMA3SAD + 2, dma.channels[3].source[1]); + READ(DMA3DAD, dma.channels[3].destination[0]); + READ(DMA3DAD + 2, dma.channels[3].destination[1]); + READ(DMA3CNT_L, dma.channels[3].word_count); + + // system + READ(POSTFLG, system.post_boot_flag) + READ(IME, system.interrupt_master_enabler) + READ(IE, system.interrupt_enable); + READ(IF, system.interrupt_request_flags); + READ(WAITCNT, system.waitstate_control); #undef READ @@ -181,6 +241,18 @@ IoDevices::read_halfword(uint32_t address) const { void IoDevices::write_halfword(uint32_t address, uint16_t halfword) { + // set lower 16 bits for reference points (BG 2/3) + auto ref_low = [](uint32_t original, uint16_t low) { + return static_cast((original & 0xFFFF0000) | low); + }; + + // set upper 12 bits for reference points (BG 2/3) + // and sign extend + auto ref_high = [](uint32_t original, uint16_t high) { + return static_cast( + ((((high & 0xFFF) << 16) | (original & 0xFFFF)) << 4) >> 4); + }; + switch (address) { #define WRITE(name, var) \ @@ -194,82 +266,146 @@ IoDevices::write_halfword(uint32_t address, uint16_t halfword) { break; // lcd - WRITE(DISPCNT, lcd.lcd_control) - WRITE(DISPSTAT, lcd.general_lcd_status) - WRITE(BG0CNT, lcd.bg0_control) - WRITE(BG1CNT, lcd.bg1_control) - WRITE(BG2CNT, lcd.bg2_control) - WRITE(BG3CNT, lcd.bg3_control) - WRITE(BG0HOFS, lcd.bg0_x_offset) - WRITE(BG0VOFS, lcd.bg0_y_offset) - WRITE(BG1HOFS, lcd.bg1_x_offset) - WRITE(BG1VOFS, lcd.bg1_y_offset) - WRITE(BG2HOFS, lcd.bg2_x_offset) - WRITE(BG2VOFS, lcd.bg2_y_offset) - WRITE(BG3HOFS, lcd.bg3_x_offset) - WRITE(BG3VOFS, lcd.bg3_y_offset) - WRITE(BG2PA, lcd.bg2_rot_scaling_parameters[0]) - WRITE(BG2PB, lcd.bg2_rot_scaling_parameters[1]) - WRITE(BG2PC, lcd.bg2_rot_scaling_parameters[2]) - WRITE(BG2PD, lcd.bg2_rot_scaling_parameters[3]) - WRITE(BG2X_L, lcd.bg2_reference_x[0]) - WRITE(BG2X_H, lcd.bg2_reference_x[1]) - WRITE(BG2Y_L, lcd.bg2_reference_y[0]) - WRITE(BG2Y_H, lcd.bg2_reference_y[1]) - WRITE(BG3PA, lcd.bg3_rot_scaling_parameters[0]) - WRITE(BG3PB, lcd.bg3_rot_scaling_parameters[1]) - WRITE(BG3PC, lcd.bg3_rot_scaling_parameters[2]) - WRITE(BG3PD, lcd.bg3_rot_scaling_parameters[3]) - WRITE(BG3X_L, lcd.bg3_reference_x[0]) - WRITE(BG3X_H, lcd.bg3_reference_x[1]) - WRITE(BG3Y_L, lcd.bg3_reference_y[0]) - WRITE(BG3Y_H, lcd.bg3_reference_y[1]) - WRITE(WIN0H, lcd.win0_horizontal_dimensions) - WRITE(WIN1H, lcd.win1_horizontal_dimensions) - WRITE(WIN0V, lcd.win0_vertical_dimensions) - WRITE(WIN1V, lcd.win1_vertical_dimensions) - WRITE(WININ, lcd.inside_win_0_1) - WRITE(WINOUT, lcd.outside_win) - WRITE(MOSAIC, lcd.mosaic_size) - WRITE(BLDCNT, lcd.color_special_effects_selection) - WRITE(BLDALPHA, lcd.alpha_blending_coefficients) - WRITE(BLDY, lcd.brightness_coefficient) + case DISPCNT: + display.lcd_control.write(halfword); + break; + case DISPSTAT: + display.general_lcd_status.write(halfword); + break; + case BG0CNT: + display.bg_control[0].write(halfword); + break; + case BG1CNT: + display.bg_control[1].write(halfword); + break; + case BG2CNT: + display.bg_control[2].write(halfword); + break; + case BG3CNT: + display.bg_control[3].write(halfword); + break; - // sound - WRITE(SOUND1CNT_L, sound.ch1_sweep) - WRITE(SOUND1CNT_H, sound.ch1_duty_length_env) - WRITE(SOUND1CNT_X, sound.ch1_freq_control) - WRITE(SOUND2CNT_L, sound.ch2_duty_length_env) - WRITE(SOUND2CNT_H, sound.ch2_freq_control) - WRITE(SOUND3CNT_L, sound.ch3_stop_wave_ram_select) - WRITE(SOUND3CNT_H, sound.ch3_length_volume) - WRITE(SOUND3CNT_X, sound.ch3_freq_control) - WRITE(WAVE_RAM0_L, sound.ch3_wave_pattern[0]); - WRITE(WAVE_RAM0_H, sound.ch3_wave_pattern[1]); - WRITE(WAVE_RAM1_L, sound.ch3_wave_pattern[2]); - WRITE(WAVE_RAM1_H, sound.ch3_wave_pattern[3]); - WRITE(WAVE_RAM2_L, sound.ch3_wave_pattern[4]); - WRITE(WAVE_RAM2_H, sound.ch3_wave_pattern[5]); - WRITE(WAVE_RAM3_L, sound.ch3_wave_pattern[6]); - WRITE(WAVE_RAM3_H, sound.ch3_wave_pattern[7]); - WRITE(SOUND4CNT_L, sound.ch4_length_env); - WRITE(SOUND4CNT_H, sound.ch4_freq_control); - WRITE(SOUNDCNT_L, sound.ctrl_stereo_volume); - WRITE(SOUNDCNT_H, sound.ctrl_mixing); - WRITE(SOUNDCNT_X, sound.ctrl_sound_on_off); - WRITE(SOUNDBIAS, sound.pwm_control); - WRITE(FIFO_A_L, sound.fifo_a[0]); - WRITE(FIFO_A_H, sound.fifo_a[1]); - WRITE(FIFO_B_L, sound.fifo_b[0]); - WRITE(FIFO_B_H, sound.fifo_b[1]); + WRITE(BG0HOFS, display.bg0_offset.x) + WRITE(BG0VOFS, display.bg0_offset.y) + WRITE(BG1HOFS, display.bg1_offset.x) + WRITE(BG1VOFS, display.bg1_offset.y) + WRITE(BG2HOFS, display.bg2_offset.x) + WRITE(BG2VOFS, display.bg2_offset.y) + WRITE(BG3HOFS, display.bg3_offset.x) + WRITE(BG3VOFS, display.bg3_offset.y) + WRITE(BG2PA, display.bg2_rot_scale.a) + WRITE(BG2PB, display.bg2_rot_scale.b) + WRITE(BG2PC, display.bg2_rot_scale.c) + WRITE(BG2PD, display.bg2_rot_scale.d) + WRITE_2(BG2X_L, + display.bg2_rot_scale.ref.x, + ref_low(display.bg2_rot_scale.ref.x, halfword)); + WRITE_2(BG2X_H, + display.bg2_rot_scale.ref.x, + ref_high(display.bg2_rot_scale.ref.x, halfword)); + WRITE_2(BG2Y_L, + display.bg2_rot_scale.ref.y, + ref_low(display.bg2_rot_scale.ref.y, halfword)); + WRITE_2(BG2Y_H, + display.bg2_rot_scale.ref.y, + ref_high(display.bg2_rot_scale.ref.y, halfword)); + WRITE(BG3PA, display.bg3_rot_scale.a) + WRITE(BG3PB, display.bg3_rot_scale.b) + WRITE(BG3PC, display.bg3_rot_scale.c) + WRITE(BG3PD, display.bg3_rot_scale.d) + WRITE_2(BG3X_L, + display.bg3_rot_scale.ref.x, + ref_low(display.bg3_rot_scale.ref.x, halfword)); + WRITE_2(BG3X_H, + display.bg3_rot_scale.ref.x, + ref_high(display.bg3_rot_scale.ref.x, halfword)); + WRITE_2(BG3Y_L, + display.bg3_rot_scale.ref.y, + ref_low(display.bg3_rot_scale.ref.y, halfword)); + WRITE_2(BG3Y_H, + display.bg3_rot_scale.ref.y, + ref_high(display.bg3_rot_scale.ref.y, halfword)); + WRITE(WIN0H, display.win0_horizontal_dimensions) + WRITE(WIN1H, display.win1_horizontal_dimensions) + WRITE(WIN0V, display.win0_vertical_dimensions) + WRITE(WIN1V, display.win1_vertical_dimensions) + WRITE(WININ, display.inside_win_0_1) + WRITE(WINOUT, display.outside_win) + WRITE(MOSAIC, display.mosaic_size) + WRITE(BLDCNT, display.color_special_effects_selection) + WRITE(BLDALPHA, display.alpha_blending_coefficients) + WRITE(BLDY, display.brightness_coefficient) - // system - WRITE_2(POSTFLG, system.post_boot_flag, halfword & 1) - WRITE_2(IME, system.interrupt_master_enabler, halfword & 1) - WRITE(IE, system.interrupt_enable); - WRITE(IF, system.interrupt_request_flags); - WRITE(WAITCNT, system.waitstate_control); - WRITE_2(HALTCNT, system.low_power_mode, get_bit(halfword, 7)); + // sound + WRITE(SOUND1CNT_L, sound.ch1_sweep) + WRITE(SOUND1CNT_H, sound.ch1_duty_length_env) + WRITE(SOUND1CNT_X, sound.ch1_freq_control) + WRITE(SOUND2CNT_L, sound.ch2_duty_length_env) + WRITE(SOUND2CNT_H, sound.ch2_freq_control) + WRITE(SOUND3CNT_L, sound.ch3_stop_wave_ram_select) + WRITE(SOUND3CNT_H, sound.ch3_length_volume) + WRITE(SOUND3CNT_X, sound.ch3_freq_control) + WRITE(WAVE_RAM0_L, sound.ch3_wave_pattern[0]); + WRITE(WAVE_RAM0_H, sound.ch3_wave_pattern[1]); + WRITE(WAVE_RAM1_L, sound.ch3_wave_pattern[2]); + WRITE(WAVE_RAM1_H, sound.ch3_wave_pattern[3]); + WRITE(WAVE_RAM2_L, sound.ch3_wave_pattern[4]); + WRITE(WAVE_RAM2_H, sound.ch3_wave_pattern[5]); + WRITE(WAVE_RAM3_L, sound.ch3_wave_pattern[6]); + WRITE(WAVE_RAM3_H, sound.ch3_wave_pattern[7]); + WRITE(SOUND4CNT_L, sound.ch4_length_env); + WRITE(SOUND4CNT_H, sound.ch4_freq_control); + WRITE(SOUNDCNT_L, sound.ctrl_stereo_volume); + WRITE(SOUNDCNT_H, sound.ctrl_mixing); + WRITE(SOUNDCNT_X, sound.ctrl_sound_on_off); + WRITE(SOUNDBIAS, sound.pwm_control); + WRITE(FIFO_A_L, sound.fifo_a[0]); + WRITE(FIFO_A_H, sound.fifo_a[1]); + WRITE(FIFO_B_L, sound.fifo_b[0]); + WRITE(FIFO_B_H, sound.fifo_b[1]); + + // dma + case DMA0CNT_H: + dma.channels[0].control.write(halfword); + break; + case DMA1CNT_H: + dma.channels[1].control.write(halfword); + break; + case DMA2CNT_H: + dma.channels[2].control.write(halfword); + break; + case DMA3CNT_H: + dma.channels[3].control.write(halfword); + break; + + WRITE(DMA0SAD, dma.channels[0].source[0]); + WRITE(DMA0SAD + 2, dma.channels[0].source[1]); + WRITE(DMA0DAD, dma.channels[0].destination[0]); + WRITE(DMA0DAD + 2, dma.channels[0].destination[1]); + WRITE(DMA0CNT_L, dma.channels[0].word_count); + WRITE(DMA1SAD, dma.channels[1].source[0]); + WRITE(DMA1SAD + 2, dma.channels[1].source[1]); + WRITE(DMA1DAD, dma.channels[1].destination[0]); + WRITE(DMA1DAD + 2, dma.channels[1].destination[1]); + WRITE(DMA1CNT_L, dma.channels[1].word_count); + WRITE(DMA2SAD, dma.channels[2].source[0]); + WRITE(DMA2SAD + 2, dma.channels[2].source[1]); + WRITE(DMA2DAD, dma.channels[2].destination[0]); + WRITE(DMA2DAD + 2, dma.channels[2].destination[1]); + WRITE(DMA2CNT_L, dma.channels[2].word_count); + WRITE(DMA3SAD, dma.channels[3].source[0]); + WRITE(DMA3SAD + 2, dma.channels[3].source[1]); + WRITE(DMA3DAD, dma.channels[3].destination[0]); + WRITE(DMA3DAD + 2, dma.channels[3].destination[1]); + WRITE(DMA3CNT_L, dma.channels[3].word_count); + + // system + WRITE_2(POSTFLG, system.post_boot_flag, halfword & 1) + WRITE_2(IME, system.interrupt_master_enabler, halfword & 1) + WRITE(IE, system.interrupt_enable); + WRITE(IF, system.interrupt_request_flags); + WRITE(WAITCNT, system.waitstate_control); + WRITE_2(HALTCNT, system.low_power_mode, get_bit(halfword, 7)); #undef WRITE #undef WRITE_2 @@ -279,4 +415,5 @@ IoDevices::write_halfword(uint32_t address, uint16_t halfword) { } return; } + } diff --git a/src/io/meson.build b/src/io/meson.build index 7b86144..7b94227 100644 --- a/src/io/meson.build +++ b/src/io/meson.build @@ -1,3 +1,3 @@ lib_sources += files( - 'io.cc', + 'io.cc' ) \ No newline at end of file