Skip to content

Commit 9ffe6dd

Browse files
committed
cleanup PR, add some tests
1 parent 938d066 commit 9ffe6dd

File tree

5 files changed

+156
-109
lines changed

5 files changed

+156
-109
lines changed

src/nmi/render_mode_play_and_demo.asm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ render_mode_play_and_demo:
9393
and #RENDER_SCORE
9494
beq @renderHz
9595

96-
; 9 safe tile writes freed from stats / hz
96+
; 7 safe tile writes freed from stats / hz
9797
; (lazy render hz for 10 more)
9898
; 1 added in level (3 total)
9999
; 2 added in lines (5 total)

tests/src/cycle_count.rs

Lines changed: 53 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,56 @@
11
use crate::{util, score, labels, playfield};
2-
use rusticnes_core::nes::NesState;
32

43
pub fn count_cycles() {
4+
test_hz_cycles();
5+
test_max_score_cycles();
6+
test_mode_score_cycles();
7+
}
8+
9+
fn test_hz_cycles() {
10+
// check max hz calculation amount
11+
let mut emu = util::emulator(None);
12+
13+
let hz_flag = labels::get("hzFlag") as usize;
14+
let game_mode = labels::get("gameMode") as usize;
15+
let x = labels::get("tetriminoX") as usize;
16+
let y = labels::get("tetriminoY") as usize;
17+
let main_loop = labels::get("mainLoop");
18+
let level_number = labels::get("levelNumber") as usize;
19+
let debounce = labels::get("hzDebounceThreshold") as usize;
20+
21+
util::run_n_vblanks(&mut emu, 3);
22+
23+
emu.memory.iram_raw[hz_flag] = 1;
24+
emu.memory.iram_raw[level_number] = 18;
25+
emu.memory.iram_raw[game_mode] = 4;
26+
emu.registers.pc = main_loop;
27+
28+
util::run_n_vblanks(&mut emu, 5);
29+
30+
let mut highest = 0;
31+
32+
for buttons in &[
33+
"L.L.L.L.L",
34+
"RR...R...R...R.R.",
35+
".....L.L..L.L.",
36+
"LLL..L.L...L.R.R..L...R....L....L...L....L",
37+
"R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R.R",
38+
] {
39+
buttons.chars().for_each(|button| {
40+
emu.memory.iram_raw[x] = 5;
41+
emu.memory.iram_raw[y] = 0;
42+
util::set_controller(&mut emu, button);
43+
highest = highest.max(util::cycles_to_vblank(&mut emu));
44+
});
45+
46+
util::run_n_vblanks(&mut emu, debounce + 1);
47+
}
48+
49+
println!("hz display most cycles {}", highest);
50+
}
51+
52+
fn test_max_score_cycles() {
53+
// check max scoring cycle amount
554
let mut emu = util::emulator(None);
655

756
let completed_lines = labels::get("completedLines") as usize;
@@ -18,7 +67,6 @@ pub fn count_cycles() {
1867

1968
let mut highest = 0;
2069

21-
// check every linecount on every level
2270
for level in 0..=255 {
2371
for lines in 0..=4 {
2472
let count = score(999999, lines, level);
@@ -30,9 +78,10 @@ pub fn count_cycles() {
3078
}
3179

3280
println!("scoring routine most cycles: {}", highest);
81+
}
3382

83+
fn test_mode_score_cycles() {
3484
// check clock cycles frames in each mode
35-
3685
let mut emu = util::emulator(None);
3786

3887
for mode in 0..labels::get("MODE_GAME_QUANTITY") {
@@ -76,7 +125,7 @@ pub fn count_cycles() {
76125
emu.memory.iram_raw[labels::get("autorepeatY") as usize] = 0;
77126

78127
for _ in 0..45 {
79-
let cycles = cycles_to_vblank(&mut emu);
128+
let cycles = util::cycles_to_vblank(&mut emu);
80129

81130
if cycles > highest {
82131
highest = cycles;
@@ -88,64 +137,4 @@ pub fn count_cycles() {
88137

89138
println!("cycles {} lines {} mode {}", highest, lines, mode);
90139
}
91-
92-
}
93-
94-
fn cycles_to_vblank(emu: &mut NesState) -> u32 {
95-
let vblank = labels::get("verticalBlankingInterval") as usize;
96-
let mut cycles = 0;
97-
let mut done = false;
98-
99-
while emu.ppu.current_scanline == 242 {
100-
emu.cycle();
101-
if !done {
102-
cycles += 1;
103-
if emu.memory.iram_raw[vblank] == 1 {
104-
done = true;
105-
}
106-
}
107-
let mut i = 0;
108-
while emu.cpu.tick >= 1 && i < 10 {
109-
emu.cycle();
110-
if !done {
111-
cycles += 1;
112-
if emu.memory.iram_raw[vblank] == 1 {
113-
done = true;
114-
}
115-
}
116-
i += 1;
117-
}
118-
if emu.ppu.current_frame != emu.last_frame {
119-
emu.event_tracker.swap_buffers();
120-
emu.last_frame = emu.ppu.current_frame;
121-
}
122-
}
123-
emu.memory.iram_raw[vblank] = 1;
124-
done = false;
125-
while emu.ppu.current_scanline != 242 {
126-
emu.cycle();
127-
if !done {
128-
cycles += 1;
129-
if emu.memory.iram_raw[vblank] == 0 {
130-
done = true;
131-
}
132-
}
133-
let mut i = 0;
134-
while emu.cpu.tick >= 1 && i < 10 {
135-
emu.cycle();
136-
if !done {
137-
cycles += 1;
138-
if emu.memory.iram_raw[vblank] == 0 {
139-
done = true;
140-
}
141-
}
142-
i += 1;
143-
}
144-
if emu.ppu.current_frame != emu.last_frame {
145-
emu.event_tracker.swap_buffers();
146-
emu.last_frame = emu.ppu.current_frame;
147-
}
148-
}
149-
150-
cycles
151140
}

tests/src/hz_display.rs

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{input, labels, util};
1+
use crate::{labels, util};
22
use rusticnes_core::nes::NesState;
33

44
pub fn test() {
@@ -9,9 +9,7 @@ pub fn test() {
99
fn test_standard() {
1010
let mut emu = util::emulator(None);
1111

12-
for _ in 0..4 {
13-
emu.run_until_vblank();
14-
}
12+
util::run_n_vblanks(&mut emu, 4);
1513

1614
let hz_flag = labels::get("hzFlag") as usize;
1715
let game_mode = labels::get("gameMode") as usize;
@@ -90,25 +88,12 @@ fn test_tspin() {
9088
assert_hz_display(&mut emu, HzSpeed(0, 0), 1, -3, Dir::Right);
9189
}
9290

93-
// note:
9491
fn run_input_string(emu: &mut NesState, inputs: &str) {
9592
for button in inputs.chars() {
96-
let controller_data = match button {
97-
'L' => input::LEFT,
98-
'R' => input::RIGHT,
99-
'D' => input::DOWN,
100-
'U' => input::UP,
101-
'A' => input::A,
102-
'B' => input::B,
103-
'S' => input::START,
104-
'T' => input::SELECT,
105-
'.' => 0,
106-
_ => panic!("character '{}' cannot be used as controller input", button),
107-
};
108-
util::set_controller(emu, controller_data);
93+
util::set_controller(emu, button);
10994
emu.run_until_vblank();
11095
}
111-
util::set_controller(emu, 0);
96+
util::set_controller(emu, '.');
11297
}
11398

11499
#[derive(PartialEq)]

tests/src/util.rs

Lines changed: 77 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use rusticnes_core::nes::NesState;
22
use rusticnes_core::{ cartridge, opcodes, opcode_info };
3-
use minifb::{Key, KeyRepeat};
4-
use crate::{labels, video};
3+
use crate::{input, labels};
54

65
pub fn emulator(rom: Option<&[u8]>) -> NesState {
76
let rom = rom.unwrap_or(include_bytes!("../../tetris.nes"));
@@ -12,43 +11,38 @@ pub fn emulator(rom: Option<&[u8]>) -> NesState {
1211
emu
1312
}
1413

15-
pub fn run_n_vblanks(emu: &mut NesState, n: i32) {
14+
pub fn run_n_vblanks(emu: &mut NesState, n: usize) {
1615
for _ in 0..n {
1716
emu.run_until_vblank();
1817
}
1918
}
2019

21-
/// debug helper for showing the current visual state.
22-
/// hotkeys: 'q' closes window, 's' steps by a frame
23-
#[allow(dead_code)]
24-
pub fn preview(emu: &mut NesState) {
25-
let mut view = video::Video::new();
26-
view.window.set_key_repeat_rate(0.1);
27-
loop {
28-
if !view.window.is_open() {
29-
break;
30-
}
31-
if view.window.is_key_pressed(Key::Q, KeyRepeat::No) {
32-
break;
33-
}
34-
if view.window.is_key_pressed(Key::S, KeyRepeat::Yes) {
35-
emu.run_until_vblank();
36-
}
37-
view.render(emu);
38-
}
39-
}
40-
4120
// emu.p1_input inverts the traditional bit assignments for the controller
4221
// (e.g. 0x01 corresponds to A) to more accurately emulate how bits are read
4322
// in.
44-
pub fn set_controller(emu: &mut NesState, buttons: u8) {
23+
pub fn set_controller_raw(emu: &mut NesState, buttons: u8) {
4524
let mut flipped_buttons = 0u8;
4625
for i in 0..8 {
4726
flipped_buttons |= ((buttons >> i) & 1) << (7-i);
4827
}
4928
emu.p1_input = flipped_buttons;
5029
}
5130

31+
pub fn set_controller(emu: &mut NesState, button: char) {
32+
set_controller_raw(emu, match button {
33+
'L' => input::LEFT,
34+
'R' => input::RIGHT,
35+
'D' => input::DOWN,
36+
'U' => input::UP,
37+
'A' => input::A,
38+
'B' => input::B,
39+
'S' => input::START,
40+
'T' => input::SELECT,
41+
'.' => 0,
42+
_ => panic!("character '{}' cannot be used as controller input", button),
43+
});
44+
}
45+
5246
pub fn run_to_return(emu: &mut NesState, print: bool) {
5347
opcodes::push(emu, 0);
5448
opcodes::push(emu, 0);
@@ -90,6 +84,65 @@ pub fn cycles_to_return(emu: &mut NesState) -> u32 {
9084
cycles
9185
}
9286

87+
pub fn cycles_to_vblank(emu: &mut NesState) -> u32 {
88+
let vblank = labels::get("verticalBlankingInterval") as usize;
89+
let mut cycles = 0;
90+
let mut done = false;
91+
92+
while emu.ppu.current_scanline == 242 {
93+
emu.cycle();
94+
if !done {
95+
cycles += 1;
96+
if emu.memory.iram_raw[vblank] == 1 {
97+
done = true;
98+
}
99+
}
100+
let mut i = 0;
101+
while emu.cpu.tick >= 1 && i < 10 {
102+
emu.cycle();
103+
if !done {
104+
cycles += 1;
105+
if emu.memory.iram_raw[vblank] == 1 {
106+
done = true;
107+
}
108+
}
109+
i += 1;
110+
}
111+
if emu.ppu.current_frame != emu.last_frame {
112+
emu.event_tracker.swap_buffers();
113+
emu.last_frame = emu.ppu.current_frame;
114+
}
115+
}
116+
emu.memory.iram_raw[vblank] = 1;
117+
done = false;
118+
while emu.ppu.current_scanline != 242 {
119+
emu.cycle();
120+
if !done {
121+
cycles += 1;
122+
if emu.memory.iram_raw[vblank] == 0 {
123+
done = true;
124+
}
125+
}
126+
let mut i = 0;
127+
while emu.cpu.tick >= 1 && i < 10 {
128+
emu.cycle();
129+
if !done {
130+
cycles += 1;
131+
if emu.memory.iram_raw[vblank] == 0 {
132+
done = true;
133+
}
134+
}
135+
i += 1;
136+
}
137+
if emu.ppu.current_frame != emu.last_frame {
138+
emu.event_tracker.swap_buffers();
139+
emu.last_frame = emu.ppu.current_frame;
140+
}
141+
}
142+
143+
cycles
144+
}
145+
93146
pub fn print_step(emu: &mut NesState) {
94147
if let Some(label) = labels::from_addr(emu.registers.pc) {
95148
println!("{}:", label);

tests/src/video.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use rusticnes_core::nes::NesState;
2-
use minifb::{Window, WindowOptions};
2+
use minifb::{Window, WindowOptions, Key, KeyRepeat};
33

44
pub const WIDTH: usize = 256;
55
pub const HEIGHT: usize = 240;
@@ -30,3 +30,23 @@ impl Video {
3030
self.window.update_with_buffer(&emu.ppu.filtered_screen, WIDTH, HEIGHT).unwrap();
3131
}
3232
}
33+
34+
/// debug helper for showing the current visual state.
35+
/// hotkeys: 'q' closes window, 's' steps by a frame
36+
#[allow(dead_code)]
37+
pub fn preview(emu: &mut NesState) {
38+
let mut view = Video::new();
39+
view.window.set_key_repeat_rate(0.1);
40+
loop {
41+
if !view.window.is_open() {
42+
break;
43+
}
44+
if view.window.is_key_pressed(Key::Q, KeyRepeat::No) {
45+
break;
46+
}
47+
if view.window.is_key_pressed(Key::S, KeyRepeat::Yes) {
48+
emu.run_until_vblank();
49+
}
50+
view.render(emu);
51+
}
52+
}

0 commit comments

Comments
 (0)