2023-11-20 02:42:40 +00:00
use crate ::testutil ::{ self , assert_flags_eq } ;
2023-11-19 00:05:05 +00:00
use ferris_boi ::{
2023-11-20 19:25:07 +00:00
cpu ::{ instructions ::Instruction , Processor } ,
2023-11-19 00:05:05 +00:00
register ,
} ;
use test_case ::test_case ;
2023-11-20 02:42:40 +00:00
struct DAAInputFlags {
subtract : bool ,
half_carry : bool ,
full_carry : bool ,
}
struct DAAOutputFlags {
zero : bool ,
full_carry : bool ,
}
2023-11-19 00:05:05 +00:00
#[ test_case(1) ]
#[ test_case(0) ]
fn test_set_carry_flag_always_sets_to_1 ( starting_value : u8 ) {
let mut processor = Processor ::default ( ) ;
processor
. registers
. set_flag_bit ( register ::Flag ::Carry , starting_value ) ;
let data = [ 0x37 , 0x03 ] ;
2023-11-20 19:25:07 +00:00
let ( ins , extra_data ) = Instruction ::from_data ( & data ) . expect ( " could not parse instruction " ) ;
2023-11-19 00:05:05 +00:00
assert_eq! ( extra_data , & [ 0x03 ] ) ;
2023-11-20 19:25:07 +00:00
processor . run_instruction ( ins ) ;
2023-11-19 00:05:05 +00:00
let new_carry_bit = processor . registers . get_flag_bit ( register ::Flag ::Carry ) ;
assert_eq! ( 1 , new_carry_bit ) ;
}
#[ test_case(1, 0) ]
#[ test_case(0, 1) ]
fn test_complement_carry_bit ( starting_value : u8 , expected_value : u8 ) {
let mut processor = Processor ::default ( ) ;
processor
. registers
. set_flag_bit ( register ::Flag ::Carry , starting_value ) ;
let data = [ 0x3F , 0x03 ] ;
2023-11-20 19:25:07 +00:00
let ( ins , extra_data ) = Instruction ::from_data ( & data ) . expect ( " could not parse instruction " ) ;
2023-11-19 00:05:05 +00:00
assert_eq! ( extra_data , & [ 0x03 ] ) ;
2023-11-20 19:25:07 +00:00
processor . run_instruction ( ins ) ;
2023-11-19 00:05:05 +00:00
let new_carry_bit = processor . registers . get_flag_bit ( register ::Flag ::Carry ) ;
assert_eq! ( expected_value , new_carry_bit ) ;
}
#[ test_case(0x37) ]
#[ test_case(0x3F) ]
fn test_all_carry_bit_instructions_adjust_flags ( opcode : u8 ) {
let mut processor = Processor ::default ( ) ;
testutil ::set_opposite_of_expected_flags ( & mut processor , ( 0 , 0 , 0 , 1 ) ) ;
let data = [ opcode , 0x03 ] ;
2023-11-20 19:25:07 +00:00
let ( ins , extra_data ) = Instruction ::from_data ( & data ) . expect ( " could not parse instruction " ) ;
2023-11-19 00:05:05 +00:00
assert_eq! ( extra_data , & [ 0x03 ] ) ;
2023-11-20 19:25:07 +00:00
processor . run_instruction ( ins ) ;
2023-11-19 00:05:05 +00:00
testutil ::assert_flags_eq! (
processor ,
// Always zero
( register ::Flag ::HalfCarry , 0 ) ,
( register ::Flag ::Carry , 1 ) ,
( register ::Flag ::Subtract , 0 ) ,
// Value is preserved
( register ::Flag ::Zero , 1 ) ,
) ;
}
2023-11-19 00:22:31 +00:00
#[ test ]
fn test_complement_a_register_value ( ) {
let mut processor = Processor ::default ( ) ;
processor . registers . a = 0xF0 ;
let data = [ 0x2F , 0x03 ] ;
2023-11-20 19:25:07 +00:00
let ( ins , extra_data ) = Instruction ::from_data ( & data ) . expect ( " could not parse instruction " ) ;
2023-11-19 00:22:31 +00:00
assert_eq! ( extra_data , & [ 0x03 ] ) ;
2023-11-20 19:25:07 +00:00
processor . run_instruction ( ins ) ;
2023-11-19 00:22:31 +00:00
assert_eq! ( 0x0F , processor . registers . a ) ;
}
#[ test ]
fn test_complement_a_register_flags ( ) {
let mut processor = Processor ::default ( ) ;
processor . registers . a = 0xF0 ;
testutil ::set_opposite_of_expected_flags ( & mut processor , ( 0 , 1 , 1 , 0 ) ) ;
let data = [ 0x2F , 0x03 ] ;
2023-11-20 19:25:07 +00:00
let ( ins , extra_data ) = Instruction ::from_data ( & data ) . expect ( " could not parse instruction " ) ;
2023-11-19 00:22:31 +00:00
assert_eq! ( extra_data , & [ 0x03 ] ) ;
2023-11-20 19:25:07 +00:00
processor . run_instruction ( ins ) ;
2023-11-19 00:22:31 +00:00
testutil ::assert_flags_eq! (
processor ,
// Always 1
( register ::Flag ::HalfCarry , 1 ) ,
( register ::Flag ::Subtract , 1 ) ,
// Value is preserved
( register ::Flag ::Carry , 1 ) ,
( register ::Flag ::Zero , 1 ) ,
) ;
}
2023-11-20 02:42:40 +00:00
#[ test_case(0x22, DAAInputFlags{subtract: false, half_carry: false, full_carry: false}, 0x22, DAAOutputFlags{zero: false, full_carry: false}; " both digits less than 9 should not adjust A register " ) ]
#[ test_case(0x0A, DAAInputFlags{subtract: false, half_carry: false, full_carry: false}, 0x10, DAAOutputFlags{zero: false, full_carry: false}; " adjust result by 0x06 if lower nibble is greater than 9 " ) ]
#[ test_case(0xA5, DAAInputFlags{subtract: false, half_carry: false, full_carry: false}, 0x05, DAAOutputFlags{zero: false, full_carry: true}; " adjust result by 0x60 if greater than 99 " ) ]
#[ test_case(0x20, DAAInputFlags{subtract: false, half_carry: true, full_carry: false}, 0x26, DAAOutputFlags{zero: false, full_carry: false}; " adjust result by 0x06 if half carry was performed " ) ]
#[ test_case(0x33, DAAInputFlags{subtract: false, half_carry: false, full_carry: true}, 0x93, DAAOutputFlags{zero: false, full_carry: true}; " adjust result by 0x60 if full carry was performed " ) ]
#[ test_case(0x26, DAAInputFlags{subtract: true, half_carry: true, full_carry: false}, 0x20, DAAOutputFlags{zero: false, full_carry: false}; " adjust result by -0x06 if half carry and a subtract were performed " ) ]
#[ test_case(0x93, DAAInputFlags{subtract: true, half_carry: false, full_carry: true}, 0x33, DAAOutputFlags{zero: false, full_carry: true}; " adjust result by -0x60 if full carry and a subtract were performed " ) ]
#[ test_case(0x00, DAAInputFlags{subtract: false, half_carry: false, full_carry: false}, 0x00, DAAOutputFlags{zero: true, full_carry: false}; " zero flag is true if result is zero " ) ]
#[ test_case(0x9C, DAAInputFlags{subtract: false, half_carry: true, full_carry: false}, 0x02, DAAOutputFlags{zero: false, full_carry: true}; " just because the upper nibble is 9 does not mean that 0x06 is added " ) ]
fn test_daa (
a_value : u8 ,
flags : DAAInputFlags ,
expected_a_value : u8 ,
expected_flags : DAAOutputFlags ,
) {
let mut processor = Processor ::default ( ) ;
processor . registers . a = a_value ;
processor
. registers
. set_flag_bit ( register ::Flag ::Carry , flags . full_carry . into ( ) ) ;
processor
. registers
. set_flag_bit ( register ::Flag ::HalfCarry , flags . half_carry . into ( ) ) ;
processor
. registers
. set_flag_bit ( register ::Flag ::Subtract , flags . subtract . into ( ) ) ;
processor
. registers
// Set the opposite of expected so we know we're setting it right
. set_flag_bit ( register ::Flag ::Zero , ( ! expected_flags . zero ) . into ( ) ) ;
let data = [ 0x27 , 0x06 ] ;
2023-11-20 19:25:07 +00:00
let ( ins , extra_data ) = Instruction ::from_data ( & data ) . expect ( " could not parse instruction " ) ;
2023-11-20 02:42:40 +00:00
assert_eq! ( extra_data , & [ 0x06 ] ) ;
2023-11-20 19:25:07 +00:00
processor . run_instruction ( ins ) ;
2023-11-20 02:42:40 +00:00
assert_eq! ( expected_a_value , processor . registers . a ) ;
assert_flags_eq! (
processor ,
( register ::Flag ::Carry , u8 ::from ( expected_flags . full_carry ) ) ,
( register ::Flag ::Zero , u8 ::from ( expected_flags . zero ) )
) ;
}
2023-11-20 23:02:34 +00:00
#[ test ]
fn test_nop_executes_successfully ( ) {
let mut processor = Processor ::default ( ) ;
let data = [ 0x00 , 0x06 ] ;
let ( ins , extra_data ) = Instruction ::from_data ( & data ) . expect ( " could not parse instruction " ) ;
assert_eq! ( extra_data , & [ 0x06 ] ) ;
processor . run_instruction ( ins ) ;
// uhhh it does nothing
assert_eq! ( processor . num_cycles , 4 ) ;
}
2023-11-20 23:23:25 +00:00
#[ test ]
fn test_enable_interrupts_enables_interrupts_on_the_following_instruction ( ) {
let mut processor = Processor ::default ( ) ;
[
// Enable interrupts
0xFB , // Nop
0x00 ,
]
. iter ( )
. copied ( )
. enumerate ( )
. for_each ( | ( idx , opcode ) | {
processor
. memory
. set (
usize ::from ( processor . registers . program_counter ) + idx ,
opcode ,
)
. expect ( " could not program data " ) ;
} ) ;
assert! ( ! processor . interrupts_enabled ( ) ) ;
processor . run_next_instruction ( ) ;
assert! ( ! processor . interrupts_enabled ( ) ) ;
processor . run_next_instruction ( ) ;
assert! ( processor . interrupts_enabled ( ) ) ;
}
#[ test ]
fn test_disable_interrupts_disables_interrupts_once_enabled ( ) {
let mut processor = Processor ::default ( ) ;
[
// Enable interrupts
0xFB , // Nop
2023-11-21 18:47:14 +00:00
0x00 , 0xF3 ,
2023-11-20 23:23:25 +00:00
]
. iter ( )
. copied ( )
. enumerate ( )
. for_each ( | ( idx , opcode ) | {
processor
. memory
. set (
usize ::from ( processor . registers . program_counter ) + idx ,
opcode ,
)
. expect ( " could not program data " ) ;
} ) ;
assert! ( ! processor . interrupts_enabled ( ) ) ;
processor . run_next_instruction ( ) ;
assert! ( ! processor . interrupts_enabled ( ) ) ;
processor . run_next_instruction ( ) ;
assert! ( processor . interrupts_enabled ( ) ) ;
processor . run_next_instruction ( ) ;
assert! ( ! processor . interrupts_enabled ( ) ) ;
}