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());
 }