-
Notifications
You must be signed in to change notification settings - Fork 0
/
Timer.java
130 lines (117 loc) · 2.84 KB
/
Timer.java
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
/**
* @author Clément Petit (282626)
* @author Yanis Berkani (271348)
*/
package ch.epfl.gameboj.component;
import java.util.Objects;
import ch.epfl.gameboj.AddressMap;
import ch.epfl.gameboj.Preconditions;
import ch.epfl.gameboj.bits.Bits;
import ch.epfl.gameboj.component.cpu.Cpu;
public final class Timer implements Component, Clocked {
private final Cpu cpu;
// Declaration of registers addresses.
int DIV = 0;
int TIMA = 0;
int TMA = 0;
int TAC = 0;
/**
* builds a timer associated to the given processor.
*
* @param cpu
* the processor
* @throws NullPointerException
* if the processor is null
*/
public Timer(Cpu cpu) {
Objects.requireNonNull(cpu);
this.cpu = cpu;
}
@Override
/**
* updates the main timer, and the secondary if needed.
*/
public void cycle(long cycle) {
boolean s0 = state();
DIV = Bits.clip(16, DIV + 4);
incIfChange(s0);
}
@Override
/**
* gives access to registers.
*/
public int read(int address) {
Preconditions.checkBits16(address);
switch (address) {
case AddressMap.REG_DIV:
return Bits.extract(DIV, 8, 8);
case AddressMap.REG_TIMA:
return TIMA;
case AddressMap.REG_TMA:
return TMA;
case AddressMap.REG_TAC:
return TAC;
default:
return NO_DATA;
}
}
@Override
/**
* gives access to registers and update the secondary timer if needed.
*/
public void write(int address, int data) {
Preconditions.checkBits8(data);
Preconditions.checkBits16(address);
boolean s0 = state();
switch (address) {
case AddressMap.REG_DIV:
DIV = 0;
break;
case AddressMap.REG_TIMA:
TIMA = data;
break;
case AddressMap.REG_TMA:
TMA = data;
break;
case AddressMap.REG_TAC:
TAC = data;
break;
}
incIfChange(s0);
}
private boolean state() {
int i = 0;
switch (Bits.extract(TAC, 0, 2)) {
case 0b00: {
i = 9;
}
break;
case 0b01: {
i = 3;
}
break;
case 0b10: {
i = 5;
}
break;
case 0b11: {
i = 7;
}
break;
default: {
}
break;
}
return (Bits.test(TAC, 2) && Bits.test(DIV, i));
}
private void incIfChange(boolean s0) {
if (s0 && !state()) {
if (TIMA == 0xFF) {
cpu.requestInterrupt(Cpu.Interrupt.TIMER);
TIMA = TMA;
} else {
TIMA += 1;
}
}
}
}