Skip to content

Commit e8b44e1

Browse files
committed
Add interrupt test
1 parent 2743d2e commit e8b44e1

File tree

11 files changed

+187
-4
lines changed

11 files changed

+187
-4
lines changed

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ All tests passed.
3333
| Check write value history | | :heavy_check_mark: | |
3434
- Evaluate processor information
3535
- Cycle count
36+
- Simulate interrupt at any memory addresses
37+
- NMI
38+
- IRQ
3639
- Assertion operator
3740
- *"eq"* (Equal to)
3841
- *"ne"* (Not equal to)
@@ -134,8 +137,10 @@ ca65 V2.18 - Ubuntu 2.19-1
134137
- [Check value of stack](https://github.com/AsaiYusuke/6502_test_executor/blob/master/example/test/ok/stack.value.check.test.json)
135138
- [rts to caller](https://github.com/AsaiYusuke/6502_test_executor/blob/master/example/test/ok/stack.rts.check.test.json)
136139

137-
### Processor information
138-
- [Cycle count](https://github.com/AsaiYusuke/6502_test_executor/blob/master/example/test/ok/error.timeout.test.json)
140+
### Processor conditions
141+
- [Cycle count information](https://github.com/AsaiYusuke/6502_test_executor/blob/master/example/test/ok/error.timeout.test.json)
142+
- [Interrupt hooks](https://github.com/AsaiYusuke/6502_test_executor/blob/master/example/test/ok/interrupt.test.json)
143+
139144
### Error handling
140145
- [Write to readonly memory](https://github.com/AsaiYusuke/6502_test_executor/blob/master/example/test/fail/error.readonly.test.json)
141146
- [Access to out of segment](https://github.com/AsaiYusuke/6502_test_executor/blob/master/example/test/fail/error.out_of_segment.test.json)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"$schema": "../../../schema/testcase.schema.json",
3+
"target": {
4+
"start": {
5+
"label": "control"
6+
}
7+
},
8+
"cases": {
9+
"NMI interrupt": {
10+
"setup": {
11+
"memory": [
12+
{
13+
"label": "vector_addr",
14+
"value": 0
15+
},
16+
{
17+
"label": "APU_PAD1",
18+
"sequence": [
19+
0,
20+
1,
21+
1,
22+
0
23+
]
24+
}
25+
],
26+
"interrupt": [
27+
{
28+
"type": "NMI",
29+
"hook": {
30+
"label": "save_id"
31+
}
32+
}
33+
]
34+
},
35+
"expected": {
36+
"memory": [
37+
{
38+
"label": "vector_addr",
39+
"value": {
40+
"eq": 4
41+
}
42+
},
43+
{
44+
"label": "nmi_lock",
45+
"readCount": {
46+
"gt": 0
47+
}
48+
}
49+
]
50+
}
51+
}
52+
}
53+
}

include/condition/condition.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "condition/condition_register_pc.h"
77
#include "condition/condition_memory.h"
88
#include "condition/condition_stack.h"
9+
#include "condition/condition_interrupt.h"
910
#include "condition/condition_timeout.h"
1011
#include "nlohmann/json.hpp"
1112

@@ -22,6 +23,7 @@ class condition
2223
vector<condition_memory> memory_defs;
2324
condition_register_pc *register_pc_def;
2425
condition_stack *stack_def;
26+
vector<condition_interrupt> interrupt_defs;
2527
condition_timeout *timeout_def;
2628

2729
protected:
@@ -31,6 +33,7 @@ class condition
3133
vector<condition_register_status_flag> get_status_flag_defs();
3234
vector<condition_memory> get_memory_defs();
3335
condition_stack *get_stack_def();
36+
vector<condition_interrupt> get_interrupt_defs();
3437
condition_timeout *get_timeout_def();
3538

3639
public:
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#pragma once
2+
3+
#include "enum/interrupt_type.h"
4+
#include "emulation/emulation_devices.h"
5+
#include "nlohmann/json.hpp"
6+
7+
using namespace std;
8+
9+
using json = nlohmann::json;
10+
11+
class condition_interrupt
12+
{
13+
private:
14+
interrupt_type type;
15+
uint8_t hook_address;
16+
17+
public:
18+
condition_interrupt(emulation_devices *_device, json condition);
19+
interrupt_type get_type();
20+
uint8_t get_hook_address();
21+
};

include/emulation/cpu_device.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#include "nlohmann/json.hpp"
77
#include "args_parser.h"
88
#include "enum/register_type.h"
9-
#include "enum/runtime_error_type.h"
9+
#include "enum/interrupt_type.h"
1010
#include "runtime_error_result.h"
1111

1212
#define TEST_RETURN_ADDRESS 0xFFFF
@@ -32,6 +32,7 @@ class cpu_device
3232
vector<pair<inst_type, uint16_t>> call_stack;
3333
uint16_t currentPC;
3434
uint16_t endPC;
35+
map<uint8_t, interrupt_type> interrupt_defs;
3536

3637
public:
3738
cpu_device(emulation_devices *_device, args_parser *args, json config);
@@ -43,6 +44,7 @@ class cpu_device
4344
uint8_t get_register(register_type type);
4445
void set_register(register_type type, uint8_t value);
4546
vector<uint8_t> get_stack();
47+
void add_interrupt_hook(interrupt_type type, uint8_t address);
4648

4749
void print();
4850
vector<uint16_t> get_call_stack();

include/enum/interrupt_type.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#pragma once
2+
3+
#include <map>
4+
#include <string>
5+
6+
using namespace std;
7+
8+
enum class interrupt_type
9+
{
10+
// NMI
11+
NMI,
12+
// IRQ
13+
IRQ
14+
};
15+
16+
static map<string, interrupt_type> interrupt_name_type_map = {
17+
{"NMI", interrupt_type::NMI},
18+
{"IRQ", interrupt_type::IRQ}};

schema/testcase.schema.json

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,32 @@
6565
"stack": {
6666
"title": "Stack",
6767
"$ref": "#/definitions/setup/stack"
68+
},
69+
"interrupt": {
70+
"title": "Interrupt",
71+
"type": "array",
72+
"minItems": 1,
73+
"items": {
74+
"type": "object",
75+
"required": [
76+
"type",
77+
"hook"
78+
],
79+
"additionalProperties": false,
80+
"properties": {
81+
"type": {
82+
"type": "string",
83+
"enum": [
84+
"NMI",
85+
"IRQ"
86+
]
87+
},
88+
"hook": {
89+
"title": "Hook address",
90+
"$ref": "#/definitions/address"
91+
}
92+
}
93+
}
6894
}
6995
}
7096
},
@@ -340,7 +366,7 @@
340366
"type": "object",
341367
"allOf": [
342368
{
343-
"$ref": "#/definitions/address"
369+
"$ref": "#/definitions/address"
344370
}
345371
],
346372
"additionalProperties": false,

src/condition/condition.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ condition::condition(emulation_devices *_device, json condition_json)
4040

4141
stack_def = new condition_stack(device, condition_json["stack"]);
4242

43+
if (condition_json["interrupt"].is_array())
44+
for (auto &interrupt_def : condition_json["interrupt"])
45+
interrupt_defs.push_back(
46+
condition_interrupt(
47+
device,
48+
interrupt_def));
49+
4350
timeout_def = new condition_timeout(device, condition_json["timeout"]);
4451
}
4552

@@ -73,6 +80,11 @@ condition_stack *condition::get_stack_def()
7380
return stack_def;
7481
}
7582

83+
vector<condition_interrupt> condition::get_interrupt_defs()
84+
{
85+
return interrupt_defs;
86+
}
87+
7688
condition_timeout *condition::get_timeout_def()
7789
{
7890
return timeout_def;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include "condition/condition_interrupt.h"
2+
#include "util/value_convert.h"
3+
4+
condition_interrupt::condition_interrupt(emulation_devices *device, json condition)
5+
{
6+
type = interrupt_name_type_map[condition["type"]];
7+
hook_address = value_convert::get_address(device, condition["hook"]);
8+
}
9+
10+
interrupt_type condition_interrupt::get_type()
11+
{
12+
return type;
13+
}
14+
15+
uint8_t condition_interrupt::get_hook_address()
16+
{
17+
return hook_address;
18+
}

src/emulation/cpu_device.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ void cpu_device::execute()
4646
{
4747
bool isCallInstr = false;
4848
bool isReternInstr = false;
49+
bool isReterned = false;
4950

5051
currentPC = cpu->GetPC();
5152

@@ -60,6 +61,7 @@ void cpu_device::execute()
6061
{
6162
call_stack.pop_back();
6263
call_stack.pop_back();
64+
isReterned = true;
6365
}
6466
else
6567
{
@@ -76,6 +78,21 @@ void cpu_device::execute()
7678
if (isReternInstr)
7779
call_stack.push_back(make_pair(inst_type::retern, cpu->GetPC()));
7880

81+
if (interrupt_defs.count(cpu->GetPC()) > 0 && !isReterned)
82+
{
83+
call_stack.push_back(make_pair(inst_type::call, cpu->GetPC()));
84+
switch(interrupt_defs[cpu->GetPC()])
85+
{
86+
case interrupt_type::NMI:
87+
cpu->NMI();
88+
break;
89+
case interrupt_type::IRQ:
90+
cpu->IRQ();
91+
break;
92+
}
93+
call_stack.push_back(make_pair(inst_type::call, cpu->GetPC()));
94+
}
95+
7996
} while (cpu->GetPC() != TEST_RETURN_ADDRESS && cpu->GetPC() != endPC && cycle_count <= get_max_cycle_count());
8097

8198
if (cycle_count > get_max_cycle_count())
@@ -128,6 +145,11 @@ vector<uint8_t> cpu_device::get_stack()
128145
return result_stack;
129146
}
130147

148+
void cpu_device::add_interrupt_hook(interrupt_type type, uint8_t address)
149+
{
150+
interrupt_defs[address] = type;
151+
}
152+
131153
void cpu_device::print()
132154
{
133155
printf("Register result:\n");

0 commit comments

Comments
 (0)