Added line-based hscroll and fixed an hscroll bug

The hscroll table was multiplying by 2 (because scroll a and b values
are next to each other) but it should have multiplied by 4 because
each value is also 2 bytes and the array is of u8.

I added hscroll by-line support by using a different function for the
line scroll vs the cell or whole screen scrolling.  There are still
a bunch of glitches in scroll b's scroll values that I need to fix
This commit is contained in:
transistor 2021-12-20 20:11:43 -08:00
parent 4ca0c9b625
commit 18e54f4a44
2 changed files with 73 additions and 19 deletions

View File

@ -313,15 +313,15 @@ impl Ym7101State {
(((rgb & 0xF00) as u32) >> 4) | (((rgb & 0x0F0) as u32) << 8) | (((rgb & 0x00F) as u32) << 20) (((rgb & 0xF00) as u32) >> 4) | (((rgb & 0x0F0) as u32) << 8) | (((rgb & 0x00F) as u32) << 20)
} }
pub fn get_pattern_iter<'a>(&'a self, pattern_name: u16) -> PatternIterator<'a> { pub fn get_pattern_iter<'a>(&'a self, pattern_name: u16, line: i8) -> PatternIterator<'a> {
let pattern_addr = (pattern_name & 0x07FF) << 5; let pattern_addr = (pattern_name & 0x07FF) << 5;
let pattern_palette = ((pattern_name & 0x6000) >> 13) as u8; let pattern_palette = ((pattern_name & 0x6000) >> 13) as u8;
let h_rev = (pattern_name & 0x0800) != 0; let h_rev = (pattern_name & 0x0800) != 0;
let v_rev = (pattern_name & 0x1000) != 0; let v_rev = (pattern_name & 0x1000) != 0;
PatternIterator::new(&self, pattern_addr as u32, pattern_palette, h_rev, v_rev) PatternIterator::new(&self, pattern_addr as u32, pattern_palette, h_rev, v_rev, line)
} }
pub fn get_hscroll(&self, hcell: usize) -> (u32, u32) { pub fn get_hscroll(&self, hcell: usize, column: usize) -> (u32, u32) {
let base_addr = match self.mode_3 & MODE3_BF_H_SCROLL_MODE { let base_addr = match self.mode_3 & MODE3_BF_H_SCROLL_MODE {
0 => self.hscroll_addr, 0 => self.hscroll_addr,
2 => self.hscroll_addr + (hcell << 4), 2 => self.hscroll_addr + (hcell << 4),
@ -329,8 +329,9 @@ impl Ym7101State {
_ => panic!("Unsupported horizontal scroll mode"), _ => panic!("Unsupported horizontal scroll mode"),
}; };
let scroll_a = read_beu16(&self.vram[base_addr..]) as u32 & 0x3FF; let scroll_addr = base_addr + (column * 2 * 2);
let scroll_b = read_beu16(&self.vram[base_addr + 2..]) as u32 & 0x3FF; let scroll_a = read_beu16(&self.vram[scroll_addr..]) as u32 & 0x3FF;
let scroll_b = read_beu16(&self.vram[scroll_addr + 2..]) as u32 & 0x3FF;
(scroll_a, scroll_b) (scroll_a, scroll_b)
} }
@ -367,10 +368,15 @@ impl Ym7101State {
pub fn draw_pattern(&mut self, frame: &mut Frame, pattern: u16, pixel_x: u32, pixel_y: u32) { pub fn draw_pattern(&mut self, frame: &mut Frame, pattern: u16, pixel_x: u32, pixel_y: u32) {
let iter = self.get_pattern_iter(pattern); let iter = self.get_pattern_iter(pattern, 0);
frame.blit(pixel_x, pixel_y, iter, 8, 8); frame.blit(pixel_x, pixel_y, iter, 8, 8);
} }
pub fn draw_pattern_line(&mut self, frame: &mut Frame, pattern: u16, pixel_x: u32, pixel_y: u32, line: i8) {
let iter = self.get_pattern_iter(pattern, line);
frame.blit(pixel_x, pixel_y, iter, 8, 1);
}
pub fn draw_frame(&mut self, frame: &mut Frame) { pub fn draw_frame(&mut self, frame: &mut Frame) {
self.draw_background(frame); self.draw_background(frame);
self.draw_scrolls(frame); self.draw_scrolls(frame);
@ -383,25 +389,61 @@ impl Ym7101State {
frame.clear(bg_colour); frame.clear(bg_colour);
} }
#[inline(always)]
pub fn draw_scrolls(&mut self, frame: &mut Frame) { pub fn draw_scrolls(&mut self, frame: &mut Frame) {
let (scroll_h, scroll_v) = self.scroll_size; if (self.mode_3 & MODE3_BF_H_SCROLL_MODE) != 3 {
let (cells_h, cells_v) = self.screen_size; self.draw_scrolls_cell(frame);
} else {
if scroll_h == 0 || scroll_v == 0 { self.draw_scrolls_line(frame);
return;
} }
}
pub fn draw_scrolls_cell(&mut self, frame: &mut Frame) {
let (cells_h, cells_v) = self.screen_size;
for cell_y in 0..cells_v { for cell_y in 0..cells_v {
let (vscrolling_a, vscrolling_b) = self.get_vscroll(cell_y); let (vscrolling_a, vscrolling_b) = self.get_vscroll(cell_y);
for cell_x in 0..cells_h { for cell_x in 0..cells_h {
let (hscrolling_a, hscrolling_b) = self.get_hscroll(cell_x); let (hscrolling_a, hscrolling_b) = self.get_hscroll(cell_x, 0);
let pattern_b = self.get_scroll_b_pattern(cell_x, cell_y, hscrolling_b as usize, vscrolling_b as usize); let pattern_b = self.get_scroll_b_pattern(cell_x, cell_y, hscrolling_b as usize, vscrolling_b as usize);
let pattern_a = self.get_scroll_a_pattern(cell_x, cell_y, hscrolling_a as usize, vscrolling_a as usize); let pattern_a = self.get_scroll_a_pattern(cell_x, cell_y, hscrolling_a as usize, vscrolling_a as usize);
//if (pattern_b & 0x8000) != 0 && (pattern_a & 0x8000) == 0 { //if (pattern_b & 0x8000) != 0 && (pattern_a & 0x8000) == 0 {
self.draw_pattern(frame, pattern_b, (cell_x << 3) as u32, (cell_y << 3) as u32); self.draw_pattern(frame, pattern_b, (cell_x << 3) as u32, (cell_y << 3) as u32 - (vscrolling_b % 8));
self.draw_pattern(frame, pattern_a, (cell_x << 3) as u32, (cell_y << 3) as u32); self.draw_pattern(frame, pattern_a, (cell_x << 3) as u32, (cell_y << 3) as u32 - (vscrolling_b % 8));
}
}
}
pub fn draw_scrolls_line(&mut self, frame: &mut Frame) {
let (cells_h, cells_v) = self.screen_size;
for cell_y in 0..cells_v {
let (vscrolling_a, vscrolling_b) = self.get_vscroll(cell_y);
for cell_x in 0..cells_h {
let (hscrolling_a, hscrolling_b) = self.get_hscroll(cell_x, 0);
let pattern_b = self.get_scroll_b_pattern(cell_x, cell_y, hscrolling_b as usize, vscrolling_b as usize);
for line in 0..8 {
let (_, offset_b) = self.get_hscroll(cell_x, line as usize);
self.draw_pattern_line(frame, pattern_b, (cell_x << 3) as u32 + (offset_b % 8), (cell_y << 3) as u32 + line as u32 - (vscrolling_b % 8), line);
}
}
}
for cell_y in 0..cells_v {
let (vscrolling_a, vscrolling_b) = self.get_vscroll(cell_y);
for cell_x in 0..cells_h {
let (hscrolling_a, hscrolling_b) = self.get_hscroll(cell_x, 0);
let pattern_a = self.get_scroll_a_pattern(cell_x, cell_y, hscrolling_a as usize, vscrolling_a as usize);
for line in 0..8 {
let (offset_a, _) = self.get_hscroll(cell_x, line as usize);
self.draw_pattern_line(frame, pattern_a, (cell_x << 3) as u32 + (offset_a % 8), (cell_y << 3) as u32 + line as u32 - (vscrolling_a % 8), line);
}
} }
} }
} }
@ -462,7 +504,7 @@ impl Ym7101State {
let (h, v) = (if !h_rev { ih } else { size_h - 1 - ih }, if !v_rev { iv } else { size_v - 1 - iv }); let (h, v) = (if !h_rev { ih } else { size_h - 1 - ih }, if !v_rev { iv } else { size_v - 1 - iv });
let (x, y) = (h_pos + ih * 8, v_pos + iv * 8); let (x, y) = (h_pos + ih * 8, v_pos + iv * 8);
if x > 128 && x < pos_limit_h && y > 128 && y < pos_limit_v { if x > 128 && x < pos_limit_h && y > 128 && y < pos_limit_v {
let iter = self.get_pattern_iter(((pattern_name & 0x07FF) + (h * size_v) + v) | (pattern_name & 0xF800)); let iter = self.get_pattern_iter(((pattern_name & 0x07FF) + (h * size_v) + v) | (pattern_name & 0xF800), 0);
frame.blit(x as u32 - 128, y as u32 - 128, iter, 8, 8); frame.blit(x as u32 - 128, y as u32 - 128, iter, 8, 8);
} }
@ -493,14 +535,14 @@ pub struct PatternIterator<'a> {
} }
impl<'a> PatternIterator<'a> { impl<'a> PatternIterator<'a> {
pub fn new(state: &'a Ym7101State, start: u32, palette: u8, h_rev: bool, v_rev: bool) -> Self { pub fn new(state: &'a Ym7101State, start: u32, palette: u8, h_rev: bool, v_rev: bool, line: i8) -> Self {
Self { Self {
state, state,
palette, palette,
base: start as usize, base: start as usize,
h_rev, h_rev,
v_rev, v_rev,
line: 0, line,
col: 0, col: 0,
second: false, second: false,
} }

View File

@ -1,5 +1,17 @@
* when a MOVE's targets are both ARegDec, the 2 extra clocks can be combined (-2 clocks in this one case) * there is possibly a cpu bug in causing the acceleration in sonic 2 to be very slow. Speeding up the CPU makes it playably fast, but I think it's still
incorrect, in that the top speed is faster than it should be, but the acceleration is correct...
* for the speed problem, I've actually noticed the level vint is only called every ~33_200_000 ns which is twice as long as it should be, so maybe there's
a vint problem, maybe even a toggle trigger that should be a one-shot
* there is definitely something related to interrupts; 2 interrupts occur in between each call to Vint_Level (ie. Vint_Level only runs every 2 frames (33.2ms) instead
of every frame (16.6 ms), which perfectly explains the slowdown without it being related to an instruction implementation error... but why is this happening?
Maybe it's because the clock advances too much and first interrupt doesn't complete, or maybe there's a problem with interrupts and masking...
It does appear to do I/O with the VDP between te interrupts so it doesn't seem to be just waiting around
* for the line based hscroll, you need to add that extra cell cycle...
* there is still some kind of hscroll glitch, and it seems to entirely be caused by the horizontal scroll offset value. I could be calculating or adding/moding
it incorrectly somewhere somehow, or the data in the hscroll table could be incorrect due to the cpu impl
* go through the testcases and make sure they are decoded correctly
@ -40,6 +52,7 @@ Audio:
Debugger: Debugger:
* i need a way to debug only the cpu and not the coprocessor, but that's tricky without a way to id or compare Transmutables
* how can you improve the debugger? * how can you improve the debugger?
* the command line definitely needs to be fixed so it prints the prompt correctly * the command line definitely needs to be fixed so it prints the prompt correctly
* debugger could maybe even allows arrows left/right for editing, and up/down for history * debugger could maybe even allows arrows left/right for editing, and up/down for history
@ -66,7 +79,6 @@ Macintosh:
68000: 68000:
* add instruction timing to M68k
* check all instructions in the docs * check all instructions in the docs
* unimplemented: BFFFO, BFINS, CHK, ILLEGAL, NBCD, NEGX, RTR, RTD * unimplemented: BFFFO, BFINS, CHK, ILLEGAL, NBCD, NEGX, RTR, RTD