Add normal sprite rendering support.
Former-commit-id: 04c3d7ec3051ce4c5aeacb552688c0217b3b3d3b
This commit is contained in:
parent
8abebbe844
commit
eab08992b9
|
@ -7,6 +7,7 @@ use crate::bitfield::Bit;
|
||||||
use crate::num::FromPrimitive;
|
use crate::num::FromPrimitive;
|
||||||
|
|
||||||
mod mosaic;
|
mod mosaic;
|
||||||
|
mod obj;
|
||||||
mod sfx;
|
mod sfx;
|
||||||
|
|
||||||
pub mod regs;
|
pub mod regs;
|
||||||
|
@ -23,11 +24,11 @@ const CYCLES_SCANLINE: usize = 1232;
|
||||||
const CYCLES_VDRAW: usize = 197120;
|
const CYCLES_VDRAW: usize = 197120;
|
||||||
const CYCLES_VBLANK: usize = 83776;
|
const CYCLES_VBLANK: usize = 83776;
|
||||||
|
|
||||||
const TILE_SIZE: u32 = 0x20;
|
pub const TILE_SIZE: u32 = 0x20;
|
||||||
|
|
||||||
// TODO - remove the one in palette.rs
|
// TODO - remove the one in palette.rs
|
||||||
bitfield! {
|
bitfield! {
|
||||||
#[derive(Copy, Clone, Default)]
|
#[derive(Copy, Clone, Default, PartialEq)]
|
||||||
pub struct Rgb15(u16);
|
pub struct Rgb15(u16);
|
||||||
impl Debug;
|
impl Debug;
|
||||||
pub r, set_r: 4, 0;
|
pub r, set_r: 4, 0;
|
||||||
|
@ -67,22 +68,6 @@ pub enum PixelFormat {
|
||||||
BPP8 = 1,
|
BPP8 = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Primitive, Clone, Copy)]
|
|
||||||
pub enum BGMode {
|
|
||||||
BGMode0 = 0,
|
|
||||||
BGMode1 = 1,
|
|
||||||
BGMode2 = 2,
|
|
||||||
BGMode3 = 3,
|
|
||||||
BGMode4 = 4,
|
|
||||||
BGMode5 = 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u16> for BGMode {
|
|
||||||
fn from(v: u16) -> BGMode {
|
|
||||||
BGMode::from_u16(v).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
||||||
pub enum GpuState {
|
pub enum GpuState {
|
||||||
HDraw = 0,
|
HDraw = 0,
|
||||||
|
@ -122,32 +107,28 @@ impl std::ops::IndexMut<usize> for FrameBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Scanline([Rgb15; DISPLAY_WIDTH]);
|
pub struct Scanline<T>([T; DISPLAY_WIDTH]);
|
||||||
|
|
||||||
impl Default for Scanline {
|
impl Default for Scanline<Rgb15> {
|
||||||
fn default() -> Scanline {
|
fn default() -> Scanline<Rgb15> {
|
||||||
Scanline([Rgb15(0); DISPLAY_WIDTH])
|
Scanline([Rgb15::TRANSPARENT; DISPLAY_WIDTH])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Scanline {
|
impl<T> fmt::Debug for Scanline<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "Scanline: ")?;
|
|
||||||
for i in 0..6 {
|
|
||||||
write!(f, "#{:06x}, ", self[i].0)?;
|
|
||||||
}
|
|
||||||
write!(f, "...")
|
write!(f, "...")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Index<usize> for Scanline {
|
impl<T> std::ops::Index<usize> for Scanline<T> {
|
||||||
type Output = Rgb15;
|
type Output = T;
|
||||||
fn index(&self, index: usize) -> &Self::Output {
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
&self.0[index]
|
&self.0[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::IndexMut<usize> for Scanline {
|
impl<T> std::ops::IndexMut<usize> for Scanline<T> {
|
||||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||||
&mut self.0[index]
|
&mut self.0[index]
|
||||||
}
|
}
|
||||||
|
@ -158,10 +139,10 @@ pub struct Bg {
|
||||||
pub bgcnt: BgControl,
|
pub bgcnt: BgControl,
|
||||||
pub bgvofs: u16,
|
pub bgvofs: u16,
|
||||||
pub bghofs: u16,
|
pub bghofs: u16,
|
||||||
line: Scanline,
|
line: Scanline<Rgb15>,
|
||||||
|
|
||||||
// for mosaic
|
// for mosaic
|
||||||
mosaic_first_row: Scanline,
|
mosaic_first_row: Scanline<Rgb15>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
@ -233,6 +214,7 @@ pub struct Gpu {
|
||||||
pub bldalpha: BlendAlpha,
|
pub bldalpha: BlendAlpha,
|
||||||
pub bldy: u16,
|
pub bldy: u16,
|
||||||
|
|
||||||
|
pub obj_line: Scanline<Rgb15>,
|
||||||
pub frame_buffer: FrameBuffer,
|
pub frame_buffer: FrameBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,6 +237,7 @@ impl Gpu {
|
||||||
state: HDraw,
|
state: HDraw,
|
||||||
current_scanline: 0,
|
current_scanline: 0,
|
||||||
cycles: 0,
|
cycles: 0,
|
||||||
|
obj_line: Scanline::default(),
|
||||||
frame_buffer: FrameBuffer([0; DISPLAY_WIDTH * DISPLAY_HEIGHT]),
|
frame_buffer: FrameBuffer([0; DISPLAY_WIDTH * DISPLAY_HEIGHT]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -282,11 +265,20 @@ impl Gpu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_palette_color(&self, sb: &SysBus, index: u32, palette_index: u32) -> Rgb15 {
|
pub fn get_palette_color(
|
||||||
|
&self,
|
||||||
|
sb: &SysBus,
|
||||||
|
index: u32,
|
||||||
|
palette_index: u32,
|
||||||
|
offset: u32,
|
||||||
|
) -> Rgb15 {
|
||||||
if index == 0 || (palette_index != 0 && index % 16 == 0) {
|
if index == 0 || (palette_index != 0 && index % 16 == 0) {
|
||||||
return Rgb15::TRANSPARENT;
|
return Rgb15::TRANSPARENT;
|
||||||
}
|
}
|
||||||
Rgb15(sb.palette_ram.read_16(2 * index + 0x20 * palette_index))
|
Rgb15(
|
||||||
|
sb.palette_ram
|
||||||
|
.read_16(offset + 2 * index + 0x20 * palette_index),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_pixel(&mut self, x: i32, y: i32, p: Rgb15) {
|
fn render_pixel(&mut self, x: i32, y: i32, p: Rgb15) {
|
||||||
|
@ -315,7 +307,7 @@ impl Gpu {
|
||||||
// | [0] | [0][1] | [0] | [0][1] |
|
// | [0] | [0][1] | [0] | [0][1] |
|
||||||
// |___________|___________|_____________|___________|
|
// |___________|___________|_____________|___________|
|
||||||
//
|
//
|
||||||
let mut screen_block = match (bg_width, bg_height) {
|
let mut sbb = match (bg_width, bg_height) {
|
||||||
(256, 256) => 0,
|
(256, 256) => 0,
|
||||||
(512, 256) => bg_x / 256,
|
(512, 256) => bg_x / 256,
|
||||||
(256, 512) => bg_y / 256,
|
(256, 512) => bg_y / 256,
|
||||||
|
@ -331,14 +323,13 @@ impl Gpu {
|
||||||
let tile_py = (bg_y % 8) as u32;
|
let tile_py = (bg_y % 8) as u32;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut map_addr = tilemap_base
|
let mut map_addr =
|
||||||
+ SCREEN_BLOCK_SIZE * screen_block
|
tilemap_base + SCREEN_BLOCK_SIZE * sbb + 2 * index2d!(u32, se_row, se_column, 32);
|
||||||
+ 2 * index2d!(u32, se_row, se_column, 32);
|
|
||||||
for _ in se_row..32 {
|
for _ in se_row..32 {
|
||||||
let entry = TileMapEntry(sb.vram.read_16(map_addr - VRAM_ADDR));
|
let entry = TileMapEntry(sb.vram.read_16(map_addr - VRAM_ADDR));
|
||||||
let tile_addr = tileset_base + entry.tile_index() * tile_size;
|
let tile_addr = tileset_base + entry.tile_index() * tile_size;
|
||||||
|
|
||||||
for tile_px in start_tile_x..=7 {
|
for tile_px in start_tile_x..8 {
|
||||||
let index = self.read_pixel_index(
|
let index = self.read_pixel_index(
|
||||||
sb,
|
sb,
|
||||||
tile_addr,
|
tile_addr,
|
||||||
|
@ -350,7 +341,7 @@ impl Gpu {
|
||||||
PixelFormat::BPP4 => entry.palette_bank() as u32,
|
PixelFormat::BPP4 => entry.palette_bank() as u32,
|
||||||
PixelFormat::BPP8 => 0u32,
|
PixelFormat::BPP8 => 0u32,
|
||||||
};
|
};
|
||||||
let color = self.get_palette_color(sb, index as u32, palette_bank);
|
let color = self.get_palette_color(sb, index as u32, palette_bank, 0);
|
||||||
self.bg[bg].line[screen_x as usize] = color;
|
self.bg[bg].line[screen_x as usize] = color;
|
||||||
screen_x += 1;
|
screen_x += 1;
|
||||||
if (DISPLAY_WIDTH as u32) == screen_x {
|
if (DISPLAY_WIDTH as u32) == screen_x {
|
||||||
|
@ -362,7 +353,7 @@ impl Gpu {
|
||||||
}
|
}
|
||||||
se_row = 0;
|
se_row = 0;
|
||||||
if bg_width == 512 {
|
if bg_width == 512 {
|
||||||
screen_block = screen_block ^ 1;
|
sbb = sbb ^ 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -395,7 +386,7 @@ impl Gpu {
|
||||||
let bitmap_index = index2d!(x, y, DISPLAY_WIDTH);
|
let bitmap_index = index2d!(x, y, DISPLAY_WIDTH);
|
||||||
let bitmap_ofs = page_ofs + (bitmap_index as u32);
|
let bitmap_ofs = page_ofs + (bitmap_index as u32);
|
||||||
let index = sb.vram.read_8(bitmap_ofs as Addr) as u32;
|
let index = sb.vram.read_8(bitmap_ofs as Addr) as u32;
|
||||||
let color = self.get_palette_color(sb, index, 0);
|
let color = self.get_palette_color(sb, index, 0, 0);
|
||||||
self.bg[bg].line[x] = color;
|
self.bg[bg].line[x] = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -403,14 +394,14 @@ impl Gpu {
|
||||||
pub fn render_scanline(&mut self, sb: &mut SysBus) {
|
pub fn render_scanline(&mut self, sb: &mut SysBus) {
|
||||||
// TODO - also render objs
|
// TODO - also render objs
|
||||||
match self.dispcnt.mode() {
|
match self.dispcnt.mode() {
|
||||||
BGMode::BGMode0 => {
|
0 => {
|
||||||
for bg in 0..3 {
|
for bg in 0..3 {
|
||||||
if self.dispcnt.disp_bg(bg) {
|
if self.dispcnt.disp_bg(bg) {
|
||||||
self.scanline_reg_bg(bg, sb);
|
self.scanline_reg_bg(bg, sb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BGMode::BGMode1 => {
|
1 => {
|
||||||
if self.dispcnt.disp_bg(2) {
|
if self.dispcnt.disp_bg(2) {
|
||||||
self.scanline_aff_bg(2, sb);
|
self.scanline_aff_bg(2, sb);
|
||||||
}
|
}
|
||||||
|
@ -421,7 +412,7 @@ impl Gpu {
|
||||||
self.scanline_reg_bg(0, sb);
|
self.scanline_reg_bg(0, sb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BGMode::BGMode2 => {
|
2 => {
|
||||||
if self.dispcnt.disp_bg(3) {
|
if self.dispcnt.disp_bg(3) {
|
||||||
self.scanline_aff_bg(3, sb);
|
self.scanline_aff_bg(3, sb);
|
||||||
}
|
}
|
||||||
|
@ -429,14 +420,17 @@ impl Gpu {
|
||||||
self.scanline_aff_bg(2, sb);
|
self.scanline_aff_bg(2, sb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BGMode::BGMode3 => {
|
3 => {
|
||||||
self.scanline_mode3(2, sb);
|
self.scanline_mode3(2, sb);
|
||||||
}
|
}
|
||||||
BGMode::BGMode4 => {
|
4 => {
|
||||||
self.scanline_mode4(2, sb);
|
self.scanline_mode4(2, sb);
|
||||||
}
|
}
|
||||||
_ => panic!("{:?} not supported", self.dispcnt.mode()),
|
_ => panic!("{:?} not supported", self.dispcnt.mode()),
|
||||||
}
|
}
|
||||||
|
if self.dispcnt.disp_obj() {
|
||||||
|
self.render_objs(sb);
|
||||||
|
}
|
||||||
self.mosaic_sfx();
|
self.mosaic_sfx();
|
||||||
let post_sfx_line = self.composite_sfx(sb);
|
let post_sfx_line = self.composite_sfx(sb);
|
||||||
for x in 0..DISPLAY_WIDTH {
|
for x in 0..DISPLAY_WIDTH {
|
||||||
|
|
234
src/core/gpu/obj.rs
Normal file
234
src/core/gpu/obj.rs
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
use super::super::SysBus;
|
||||||
|
use super::regs::*;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use crate::core::sysbus::OAM_ADDR;
|
||||||
|
|
||||||
|
const OVRAM: u32 = 0x0601_0000;
|
||||||
|
const PALRAM_OFS_FG: u32 = 0x200;
|
||||||
|
const ATTRS_SIZE: u32 = 2 * 3 + 2;
|
||||||
|
|
||||||
|
struct ObjAttrs(Attribute0, Attribute1, Attribute2);
|
||||||
|
|
||||||
|
struct ObjAffineParams {
|
||||||
|
pa: i16,
|
||||||
|
pb: i16,
|
||||||
|
pc: i16,
|
||||||
|
pd: i16,
|
||||||
|
}
|
||||||
|
|
||||||
|
const AFFINE_FILL: u32 = 2 * 3;
|
||||||
|
|
||||||
|
impl ObjAffineParams {
|
||||||
|
fn from_index(sb: &SysBus, index: u32) -> ObjAffineParams {
|
||||||
|
let mut offset = AFFINE_FILL + index * 16 * 2;
|
||||||
|
let pa = sb.read_16(offset) as i16;
|
||||||
|
offset += 2 + AFFINE_FILL;
|
||||||
|
let pb = sb.read_16(offset) as i16;
|
||||||
|
offset += 2 + AFFINE_FILL;
|
||||||
|
let pc = sb.read_16(offset) as i16;
|
||||||
|
offset += 2 + AFFINE_FILL;
|
||||||
|
let pd = sb.read_16(offset) as i16;
|
||||||
|
|
||||||
|
ObjAffineParams { pa, pb, pc, pd }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjAttrs {
|
||||||
|
fn size(&self) -> (usize, usize) {
|
||||||
|
let n = 8 << self.1.size();
|
||||||
|
match self.0.shape() {
|
||||||
|
0 /* Square */ => (n, n),
|
||||||
|
1 /* Wide */ => (n, n >> 1),
|
||||||
|
2 /* Tall */ => (n >> 1, n),
|
||||||
|
_ => unreachable!("invalid obj shape")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn coords(&self) -> (usize, usize) {
|
||||||
|
(self.1.x_coord() as usize, self.0.y_coord() as usize)
|
||||||
|
}
|
||||||
|
fn tile_format(&self) -> (usize, PixelFormat) {
|
||||||
|
if self.0.is_8bpp() {
|
||||||
|
(0x40, PixelFormat::BPP8)
|
||||||
|
} else {
|
||||||
|
(0x20, PixelFormat::BPP4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn is_affine(&self) -> bool {
|
||||||
|
match self.0.objtype() {
|
||||||
|
ObjType::Affine | ObjType::AffineDoubleSize => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn affine_index(&self) -> u32 {
|
||||||
|
let attr1 = (self.1).0;
|
||||||
|
((attr1 >> 9) & 0x1f) as u32
|
||||||
|
}
|
||||||
|
fn is_hidden(&self) -> bool {
|
||||||
|
self.0.objtype() == ObjType::Hidden
|
||||||
|
}
|
||||||
|
fn flip_xy(&self) -> (bool, bool) {
|
||||||
|
if !self.is_affine() {
|
||||||
|
(self.1.h_flip(), self.1.v_flip())
|
||||||
|
} else {
|
||||||
|
(false, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_obj_attrs(sb: &SysBus, obj: usize) -> ObjAttrs {
|
||||||
|
let addr = ATTRS_SIZE * (obj as u32);
|
||||||
|
let attr0 = Attribute0(sb.oam.read_16(addr + 0));
|
||||||
|
let attr1 = Attribute1(sb.oam.read_16(addr + 2));
|
||||||
|
let attr2 = Attribute2(sb.oam.read_16(addr + 4));
|
||||||
|
ObjAttrs(attr0, attr1, attr2)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Gpu {
|
||||||
|
fn obj_tile_base(&self) -> u32 {
|
||||||
|
match self.dispcnt.mode() {
|
||||||
|
mode if mode > 2 => OVRAM + 0x4000,
|
||||||
|
_ => OVRAM,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_objs(&mut self, sb: &SysBus) {
|
||||||
|
let screen_y = self.current_scanline;
|
||||||
|
// reset the scanline
|
||||||
|
self.obj_line = Scanline::default();
|
||||||
|
for obj_num in (0..128).rev() {
|
||||||
|
let obj = read_obj_attrs(sb, obj_num);
|
||||||
|
if obj.is_hidden() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_affine = obj.is_affine();
|
||||||
|
if is_affine {
|
||||||
|
panic!("im not ready for that yet :(");
|
||||||
|
}
|
||||||
|
|
||||||
|
let (obj_x, obj_y) = obj.coords();
|
||||||
|
let (obj_w, obj_h) = obj.size();
|
||||||
|
// skip this obj if not within its bounds.
|
||||||
|
if !(screen_y >= obj_y && screen_y < obj_y + obj_h) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tile_base = self.obj_tile_base() + 0x20 * (obj.2.tile() as u32);
|
||||||
|
|
||||||
|
let (tile_size, pixel_format) = obj.tile_format();
|
||||||
|
let palette_bank = match pixel_format {
|
||||||
|
PixelFormat::BPP4 => obj.2.palette(),
|
||||||
|
_ => 0u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
let tile_array_width = match self.dispcnt.obj_mapping() {
|
||||||
|
ObjMapping::OneDimension => obj_w / 8,
|
||||||
|
ObjMapping::TwoDimension => {
|
||||||
|
if obj.0.is_8bpp() {
|
||||||
|
16
|
||||||
|
} else {
|
||||||
|
32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (xflip, yflip) = obj.flip_xy();
|
||||||
|
|
||||||
|
let end_x = obj_x + obj_w;
|
||||||
|
for screen_x in obj_x..end_x {
|
||||||
|
if screen_x > DISPLAY_WIDTH {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let mut sprite_y = screen_y - obj_y;
|
||||||
|
let mut sprite_x = screen_x - obj_x;
|
||||||
|
if (!is_affine) {
|
||||||
|
sprite_y = if yflip {
|
||||||
|
obj_h - sprite_y - 1
|
||||||
|
} else {
|
||||||
|
sprite_y
|
||||||
|
};
|
||||||
|
sprite_x = if xflip {
|
||||||
|
obj_w - sprite_x - 1
|
||||||
|
} else {
|
||||||
|
sprite_x
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let tile_x = sprite_x % 8;
|
||||||
|
let tile_y = sprite_y % 8;
|
||||||
|
let tile_addr = tile_base
|
||||||
|
+ index2d!(u32, sprite_x / 8, sprite_y / 8, tile_array_width)
|
||||||
|
* (tile_size as u32);
|
||||||
|
let pixel_index = self.read_pixel_index(
|
||||||
|
sb,
|
||||||
|
tile_addr,
|
||||||
|
tile_x as u32,
|
||||||
|
tile_y as u32,
|
||||||
|
pixel_format,
|
||||||
|
);
|
||||||
|
let pixel_color =
|
||||||
|
self.get_palette_color(sb, pixel_index as u32, palette_bank, PALRAM_OFS_FG);
|
||||||
|
if pixel_color != Rgb15::TRANSPARENT {
|
||||||
|
// TODO - handle priority
|
||||||
|
self.obj_line[screen_x] = pixel_color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Primitive, Copy, Clone, PartialEq)]
|
||||||
|
enum ObjMode {
|
||||||
|
Normal = 0b00,
|
||||||
|
Sfx = 0b01,
|
||||||
|
Window = 0b10,
|
||||||
|
Forbidden = 0b11,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> for ObjMode {
|
||||||
|
fn from(v: u16) -> ObjMode {
|
||||||
|
ObjMode::from_u16(v as u16).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Primitive, Copy, Clone, PartialEq)]
|
||||||
|
enum ObjType {
|
||||||
|
Normal = 0b00,
|
||||||
|
Affine = 0b01,
|
||||||
|
Hidden = 0b10,
|
||||||
|
AffineDoubleSize = 0b11,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> for ObjType {
|
||||||
|
fn from(v: u16) -> ObjType {
|
||||||
|
ObjType::from_u16(v as u16).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
pub struct Attribute0(u16);
|
||||||
|
u16;
|
||||||
|
y_coord, _ : 7, 0;
|
||||||
|
into ObjType, objtype, _: 9, 8;
|
||||||
|
into ObjMode, objmode, _: 11, 10;
|
||||||
|
pub mosaic, _: 12;
|
||||||
|
is_8bpp, _: 13;
|
||||||
|
shape, _: 15, 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
pub struct Attribute1(u16);
|
||||||
|
u16;
|
||||||
|
x_coord, _ : 8, 0;
|
||||||
|
h_flip, _: 12;
|
||||||
|
v_flip, _: 13;
|
||||||
|
size, _: 15, 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
pub struct Attribute2(u16);
|
||||||
|
u16;
|
||||||
|
tile, _: 9, 0;
|
||||||
|
priority, _: 11, 10;
|
||||||
|
into u32, palette, _: 15, 12;
|
||||||
|
}
|
|
@ -3,6 +3,12 @@ use super::*;
|
||||||
|
|
||||||
pub const SCREEN_BLOCK_SIZE: u32 = 0x800;
|
pub const SCREEN_BLOCK_SIZE: u32 = 0x800;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum ObjMapping {
|
||||||
|
TwoDimension,
|
||||||
|
OneDimension,
|
||||||
|
}
|
||||||
|
|
||||||
impl DisplayControl {
|
impl DisplayControl {
|
||||||
pub fn disp_bg(&self, bg: usize) -> bool {
|
pub fn disp_bg(&self, bg: usize) -> bool {
|
||||||
self.0.bit(8 + bg)
|
self.0.bit(8 + bg)
|
||||||
|
@ -10,6 +16,13 @@ impl DisplayControl {
|
||||||
pub fn is_using_windows(&self) -> bool {
|
pub fn is_using_windows(&self) -> bool {
|
||||||
self.disp_window0() || self.disp_window1() || self.disp_obj_window()
|
self.disp_window0() || self.disp_window1() || self.disp_obj_window()
|
||||||
}
|
}
|
||||||
|
pub fn obj_mapping(&self) -> ObjMapping {
|
||||||
|
if self.obj_character_vram_mapping() {
|
||||||
|
ObjMapping::OneDimension
|
||||||
|
} else {
|
||||||
|
ObjMapping::TwoDimension
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BgControl {
|
impl BgControl {
|
||||||
|
@ -55,7 +68,7 @@ bitfield! {
|
||||||
pub struct DisplayControl(u16);
|
pub struct DisplayControl(u16);
|
||||||
impl Debug;
|
impl Debug;
|
||||||
u16;
|
u16;
|
||||||
pub into BGMode, mode, set_mode: 2, 0;
|
pub mode, set_mode: 2, 0;
|
||||||
pub display_frame, set_display_frame: 4, 4;
|
pub display_frame, set_display_frame: 4, 4;
|
||||||
pub hblank_interval_free, _: 5;
|
pub hblank_interval_free, _: 5;
|
||||||
pub obj_character_vram_mapping, _: 6;
|
pub obj_character_vram_mapping, _: 6;
|
||||||
|
|
|
@ -50,6 +50,15 @@ impl Gpu {
|
||||||
// TODO - only BGs are supported, don't forget OBJs
|
// TODO - only BGs are supported, don't forget OBJs
|
||||||
// priorities are 0-4 when 0 is the highest
|
// priorities are 0-4 when 0 is the highest
|
||||||
'outer: for priority in 0..4 {
|
'outer: for priority in 0..4 {
|
||||||
|
if bflags.contains(BlendFlags::OBJ)
|
||||||
|
&& wflags.contains(WindowFlags::OBJ)
|
||||||
|
&& !self.obj_line[screen_x].is_transparent()
|
||||||
|
{
|
||||||
|
return Some(Layer {
|
||||||
|
color: self.obj_line[screen_x],
|
||||||
|
blend_flag: BlendFlags::OBJ,
|
||||||
|
});
|
||||||
|
}
|
||||||
for bg in 0..4 {
|
for bg in 0..4 {
|
||||||
let c = self.bg[bg].line[screen_x];
|
let c = self.bg[bg].line[screen_x];
|
||||||
let bflag = BlendFlags::from_bg(bg);
|
let bflag = BlendFlags::from_bg(bg);
|
||||||
|
@ -138,8 +147,8 @@ impl Gpu {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn composite_sfx(&self, sb: &SysBus) -> Scanline {
|
pub fn composite_sfx(&self, sb: &SysBus) -> Scanline<Rgb15> {
|
||||||
let mut line = Scanline::default();
|
let mut line: Scanline<Rgb15> = Scanline::default();
|
||||||
let y = self.current_scanline;
|
let y = self.current_scanline;
|
||||||
for x in 0..DISPLAY_WIDTH {
|
for x in 0..DISPLAY_WIDTH {
|
||||||
let window = self.get_active_window_type(x, y);
|
let window = self.get_active_window_type(x, y);
|
||||||
|
|
Reference in a new issue