diff --git a/tests/tester.cpp b/tests/tester.cpp index 7b3a1fa..47d8a30 100644 --- a/tests/tester.cpp +++ b/tests/tester.cpp @@ -48,6 +48,8 @@ using z80::fast_u32; using z80::least_u8; using z80::reg; using z80::unreachable; +using z80::unused; +using z80::z80_variant; static const std::size_t max_line_size = 1024; @@ -160,7 +162,7 @@ class test_context { void handle_end_of_test_entry() { if(in_skipping_mode) - error("this line is expected, but not found"); + input.error("this line is expected, but not found"); in_skipping_mode = false; } @@ -277,6 +279,11 @@ class i8080_disasm : public disasm_base> { bool depends_on_iregp_kind() const { return false; } + + [[noreturn]] void set_variant(z80_variant v, const test_input &input) { + unused(v); + input.error("unsupported variant"); + } }; class z80_disasm : public disasm_base> { @@ -307,8 +314,18 @@ class z80_disasm : public disasm_base> { base::on_disassemble(); } + void set_variant(z80_variant v, const test_input &input) { + unused(&input); + variant = v; + } + + z80_variant on_get_z80_variant() const { + return variant; + } + private: bool does_depend_on_iregp_kind = false; + z80_variant variant = z80_variant::common; }; template @@ -814,6 +831,11 @@ class i8080_machine : public machine_base> { base::on_step(); context.match("done", static_cast(get_ticks())); } + + [[noreturn]] void set_variant(z80_variant v, const test_input &input) { + unused(v); + input.error("unsupported variant"); + } }; class z80_machine : public machine_base> { @@ -851,6 +873,18 @@ class z80_machine : public machine_base> { context.match("done", static_cast(get_ticks())); } + + void set_variant(z80_variant v, const test_input &input) { + unused(&input); + variant = v; + } + + z80_variant on_get_z80_variant() const { + return variant; + } + +private: + z80_variant variant = z80_variant::common; }; int translate_hex_digit(char c) { @@ -921,13 +955,34 @@ bool parse_set_r_directive(const char *r, fast_u8 &n, return true; } -template -void handle_directive(const test_input &input, M &mach) { +bool parse_z80_variant_directive(z80_variant &variant, + const test_input &input) { + const char *p = input.get_line(); + if(!parse(p, ".z80_variant=")) + return false; + + if(std::strcmp(p, "common") == 0) + variant = z80_variant::common; + else if(std::strcmp(p, "cmos") == 0) + variant = z80_variant::cmos; + else + input.error("unknown Z80 variant '%s'", p); + return true; +} + +template +void handle_directive(M &mach, D &dis, const test_input &input) { fast_u8 n; + z80_variant variant; if(parse_set_r_directive("b", n, input)) return mach.set_b(n); if(parse_set_r_directive("c", n, input)) return mach.set_c(n); + if(parse_z80_variant_directive(variant, input)) { + mach.set_variant(variant, input); + dis.set_variant(variant, input); + return; + } input.error("unknown directive"); } @@ -945,11 +1000,13 @@ void handle_test_entry(test_context &context) { test_input &input = context.get_input(); - // Handle directives. machine mach(context); + disasm dis(input); + + // Handle directives. const char *p = input.get_line(); while(*p == '.') { - handle_directive(input, mach); + handle_directive(mach, dis, input); p = input.read_line(); } @@ -966,7 +1023,6 @@ void handle_test_entry(test_context &context) { skip_whitespace(p); // Test instruction disassembly. - disasm dis(input); dis.set_encoding(encoding); dis.on_disassemble(); const char *instr = dis.get_output(); diff --git a/tests/tests_z80 b/tests/tests_z80 index 47b4ce4..dd5b7fc 100644 --- a/tests/tests_z80 +++ b/tests/tests_z80 @@ -1808,6 +1808,20 @@ dded61 (db 0xdd), out (c), h 16 set_index_rp ix -> hl 16 done +# NMOS and CMOS versions of OUT (C), 0 +ed71 out (c), 0x00 +... + 8 output 00 at 0000 +... +12 done + +.z80_variant=cmos +ed71 out (c), 0xff +... + 8 output ff at 0000 +... +12 done + # Block input. edb2 inir 0 m1_fetch @@ -3863,7 +3877,7 @@ dded6d (db 0xdd), xretn 0xed6d dded6e (db 0xdd), xim 0xed6e, 0 dded6f (db 0xdd), rld dded70 (db 0xdd), in (c) -dded71 (db 0xdd), out (c), 0 +dded71 (db 0xdd), out (c), 0x00 dded72 (db 0xdd), sbc hl, sp dded730000 (db 0xdd), ld (0x0000), sp dded74 (db 0xdd), xneg 0xed74 @@ -4157,7 +4171,7 @@ ed6d xretn 0xed6d ed6e xim 0xed6e, 0 ed6f rld ed70 in (c) -ed71 out (c), 0 +ed71 out (c), 0x00 ed72 sbc hl, sp ed730000 ld (0x0000), sp ed74 xneg 0xed74 @@ -4927,7 +4941,7 @@ fded6d (db 0xfd), xretn 0xed6d fded6e (db 0xfd), xim 0xed6e, 0 fded6f (db 0xfd), rld fded70 (db 0xfd), in (c) -fded71 (db 0xfd), out (c), 0 +fded71 (db 0xfd), out (c), 0x00 fded72 (db 0xfd), sbc hl, sp fded730000 (db 0xfd), ld (0x0000), sp fded74 (db 0xfd), xneg 0xed74 diff --git a/z80.h b/z80.h index 8c94574..3a14a11 100644 --- a/z80.h +++ b/z80.h @@ -142,6 +142,11 @@ enum class block_out { outi, outd, otir, otdr }; enum class condition { nz, z, nc, c, po, pe, p, m }; +enum class z80_variant { + common, + cmos, // Newer chips. +}; + // Entities for internal needs of the library. class internals { private: @@ -507,6 +512,16 @@ class root { unused(op); self().on_retn(); } + z80_variant on_get_z80_variant() { + return z80_variant::common; } + + fast_u8 on_get_out_c_r_op() { + switch(self().on_get_z80_variant()) { + case z80_variant::common: return 0; + case z80_variant::cmos: return 0xff; + } + unreachable("Unknown Z80 variant."); } + protected: const derived &self() const{ return static_cast(*this); } derived &self() { return static_cast(*this); } @@ -1738,7 +1753,7 @@ class z80_disasm self().on_format("neg"); } void on_out_c_r(reg r) { if(r == reg::at_hl) - self().on_format("out (c), 0"); + self().on_format("out (c), N", self().on_get_out_c_r_op()); else self().on_format("out (c), R", r, iregp::hl, 0); } void on_out_n_a(fast_u8 n) { @@ -3601,7 +3616,8 @@ class z80_executor : public internals::executor_base { fast_u16 bc = self().on_get_bc(); self().on_set_wz(inc16(bc)); fast_u8 n = (r == reg::at_hl) ? - 0 : self().on_get_reg(r, iregp::hl, /* d= */ 0); + self().on_get_out_c_r_op() : + self().on_get_reg(r, iregp::hl, /* d= */ 0); self().on_output_cycle(bc, n); } void on_out_n_a(fast_u8 n) { fast_u8 a = self().on_get_a();