diff --git a/src/65c02isa.csv b/src/65c02isa.csv index 034ae6a..ab9d42d 100644 --- a/src/65c02isa.csv +++ b/src/65c02isa.csv @@ -63,7 +63,7 @@ OP_BIT_ABSX,"bit",ABSOLUTEX,3,emul_bit,false OP_AND_ABSX,"and",ABSOLUTEX,3,emul_and,false OP_ROL_ABSX,"rol",ABSOLUTEX,3,emul_rol,false OP_BBR3_REL,"bbr3",ZPR,2,emul_bbr3,true -OP_RTI,"rti",IMPLIED,1,NULL,false +OP_RTI,"rti",IMPLIED,1,emul_rti,true OP_EOR_IZPX,"eor",IZPX,2,emul_eor,false OP_NOPI_43,"invalid",IMMEDIATE,2,NULL,false OP_NOPI_44,"invalid",IMPLIED,1,NULL,false diff --git a/src/emulation.c b/src/emulation.c index 2276c33..710e41b 100644 --- a/src/emulation.c +++ b/src/emulation.c @@ -639,6 +639,20 @@ emul_ply(rk65c02emu_t *e, void *id, instruction_t *i) e->regs.Y = stack_pop(e); } +/* RTI - return from interrupt */ +void +emul_rti(rk65c02emu_t *e, void *id, instruction_t *i) +{ + uint16_t retaddr; + + /* restore processor status from stack */ + e->regs.P = stack_pop(e) | P_UNDEFINED; + /* restore PC */ + retaddr = stack_pop(e); + retaddr|= stack_pop(e) << 8; + e->regs.PC = retaddr; +} + /* RTS - return from subroutine */ void emul_rts(rk65c02emu_t *e, void *id, instruction_t *i) diff --git a/test/test_interrupt.c b/test/test_interrupt.c index 2634bd0..857cee6 100644 --- a/test/test_interrupt.c +++ b/test/test_interrupt.c @@ -10,18 +10,19 @@ #include "instruction.h" #include "utils.h" +#define ISR_ADDR 0xC100 + /* * Test case for software generated interrupt (by BRK instruction). */ ATF_TC_WITHOUT_HEAD(intr_brk); ATF_TC_BODY(intr_brk, tc) { - const uint16_t isr_addr = 0xC100; + const uint16_t isr_addr = ISR_ADDR; rk65c02emu_t e; bus_t b; - b = bus_init(); e = rk65c02_init(&b); @@ -61,9 +62,62 @@ ATF_TC_BODY(intr_brk, tc) */ } +/* + * Test case for return from interrupt by RTI instruction. + */ +ATF_TC_WITHOUT_HEAD(intr_rti); +ATF_TC_BODY(intr_rti, tc) +{ + bus_t b; + rk65c02emu_t e; + uint8_t *asmbuf; + uint16_t israsmpc; + uint8_t bsize; + + b = bus_init(); + e = rk65c02_init(&b); + + israsmpc = ISR_ADDR; + + ATF_REQUIRE(assemble_single_implied(&asmbuf, &bsize, "nop")); + ATF_REQUIRE(bus_load_buf(&b, israsmpc, asmbuf, bsize)); + free(asmbuf); + israsmpc += bsize; + + ATF_REQUIRE(assemble_single_implied(&asmbuf, &bsize, "rti")); + ATF_REQUIRE(bus_load_buf(&b, israsmpc, asmbuf, bsize)); + free(asmbuf); + israsmpc += bsize; + + ATF_REQUIRE(assemble_single_implied(&asmbuf, &bsize, "nop")); + ATF_REQUIRE(bus_load_buf(&b, ROM_LOAD_ADDR, asmbuf, bsize)); + free(asmbuf); + + /* There's a return address and saved processor flags on stack. */ + e.regs.SP = 0xFF; + stack_push(&e, ROM_LOAD_ADDR >> 8); + stack_push(&e, ROM_LOAD_ADDR & 0xFF); + stack_push(&e, e.regs.P); + + /* We're in the middle of interrupt service routine, just before RTI. */ + e.regs.PC = ISR_ADDR; + rk65c02_step(&e, 1); + ATF_CHECK(e.regs.PC == ISR_ADDR + 1); + rk65c02_dump_regs(&e); + rk65c02_dump_stack(&e, 0x4); + + /* Step onto RTI. */ + rk65c02_step(&e, 1); + rk65c02_dump_regs(&e); + /* Check if we're back in the main program. */ + ATF_CHECK(e.regs.PC == ROM_LOAD_ADDR); + +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, intr_brk); + ATF_TP_ADD_TC(tp, intr_rti); return (atf_no_error()); }