-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCPU.cpp
148 lines (129 loc) · 2.78 KB
/
CPU.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#include "CPU.h"
#include "InstructionSet.h"
#include "Bus.h"
CPU::CPU(Bus* b)
: bus(b)
{
Reset();
}
void CPU::Reset() {
cycles = 0;
interupt_master_enable = false;
halt = false;
stop = false;
//Gameboy boot state after bios
//These values are true only for DMG
//TODO: Display scrolling logo and boot sound
AF = 0x01B0;
BC = 0x0013;
DE = 0x00D8;
SP = 0xFFFE;
PC = 0x0100;
}
//Service interrupt if IME is enabled and corresponding IE/IF fields are set
//When servicing IME is disabled, corresponding IF is disabled,
//PC is pushed to stack and jumps to interrupt handler address
//This process takes 5 cycles
void CPU::HandleInterupts()
{
uint8_t service = bus->regs.int_enable & bus->regs.int_request;
// There are interupts which can be serviced
if (service)
{
// TODO handle instructions when in halt mode and ime=0
// When IME is disabled and system is halted the next instruction is skipped
// Apparently this doesn't occur on CGB mode
// I've read contradicting information on this however
halt = false;
stop = false;
if (interupt_master_enable)
{
if (BIT_CHECK(service, INT_VBLANK))
{
interupt_master_enable = false;
BIT_CLEAR(bus->regs.int_request, INT_VBLANK);
PUSH(PC);
PC = 0x0040;
}
else if (BIT_CHECK(service, INT_LCDC))
{
interupt_master_enable = false;
BIT_CLEAR(bus->regs.int_request, INT_LCDC);
PUSH(PC);
PC = 0x0048;
}
else if (BIT_CHECK(service, INT_TIMER))
{
interupt_master_enable = false;
BIT_CLEAR(bus->regs.int_request, INT_TIMER);
PUSH(PC);
PC = 0x0050;
}
else if (BIT_CHECK(service, INT_SERIAL))
{
interupt_master_enable = false;
BIT_CLEAR(bus->regs.int_request, INT_SERIAL);
PUSH(PC);
PC = 0x0058;
}
else if (BIT_CHECK(service, INT_INPUT))
{
interupt_master_enable = false;
BIT_CLEAR(bus->regs.int_request, INT_INPUT);
PUSH(PC);
PC = 0x0060;
}
// PUSH increments cycles by 4 + 1 = 5
cycles += 1;
}
}
}
void CPU::write(uint16_t address, uint8_t data)
{
bus->Write(address, data);
}
uint8_t CPU::read(uint16_t address)
{
return bus->Read(address);
}
inline uint8_t CPU::GetByteAtPC() {
return bus->Read(PC++);
}
inline uint16_t CPU::GetWordAtPC() {
uint16_t word;
word = bus->Read(PC++);
word |= bus->Read(PC++) << 8;
return word;
}
uint8_t CPU::Step() {
cycles = 0;
HandleInterupts();
if (stop)
{
return cycles;
}
if (halt)
{
cycles = 1;
return cycles;
}
uint8_t opcode = GetByteAtPC();
switch (insTable[opcode].getLength()) {
case 1:
break;
case 2:
operand = GetByteAtPC();
break;
case 3:
operand = GetWordAtPC();
break;
default:
break;
};
(this->*jumpTable[opcode].execute)();
return cycles;
}
void CPU::CB() {
uint8_t opcode = GetByteAtPC();
(this->*cb_jumpTable[opcode].execute)();
}