Skip to content

Commit 63cca26

Browse files
committed
start working on timing and add some missing cycle advancements in SM83
blargg's instr_timing test doesn't fail to start the timer anymore, but it does to fail to test the timer. cpu_instrs test #2 (interrupts) passes now, however I'm not sure if I'm ready to believe that.
1 parent 2459405 commit 63cca26

10 files changed

+196
-15
lines changed

CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ set(SOURCES
1919
src/main.cpp
2020
src/ppu.cpp
2121
src/sm83.cpp
22+
src/timer.cpp
2223
)
2324

2425
set(HEADERS
@@ -29,6 +30,7 @@ set(HEADERS
2930
src/logging.h
3031
src/ppu.h
3132
src/sm83.h
33+
src/timer.h
3234
src/types.h
3335
)
3436

src/bus.cpp

+51-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
#include "bus.h"
33
#include "logging.h"
44

5-
Bus::Bus(BootROM& bootrom, Cartridge& cartridge, PPU& ppu)
6-
: bootrom(bootrom), cartridge(cartridge), ppu(ppu) {
5+
Bus::Bus(BootROM& bootrom, Cartridge& cartridge, PPU& ppu, Timer& timer)
6+
: bootrom(bootrom), cartridge(cartridge), ppu(ppu), timer(timer) {
77
boot_rom_enabled = true;
88
memory = std::array<u8, 0x10000>();
99
std::fill(memory.begin(), memory.end(), 0xFF);
@@ -46,7 +46,7 @@ u8 Bus::Read8(u16 addr) {
4646
LDEBUG("reading 0x%02X from 0x%04X (Cartridge RAM)", memory[addr], addr);
4747
return memory[addr];
4848
} else {
49-
LWARN("attempted to read from cartridge RAM while it is disabled (from 0x%04X)", addr);
49+
// LWARN("attempted to read from cartridge RAM while it is disabled (from 0x%04X)", addr);
5050
return 0xFF;
5151
}
5252
}
@@ -260,6 +260,30 @@ u8 Bus::ReadIO(u16 addr) {
260260
LDEBUG("reading 0x%02X from 0xFF00 (Joypad)", 0xCF);
261261
// LWARN("(hack) all reads from 0xFF00 are currently stubbed to 0xCF");
262262
return 0xCF;
263+
case 0xFF04:
264+
{
265+
u8 div = timer.GetDivider();
266+
LDEBUG("reading 0x%02X from DIV (0xFF04)", div);
267+
return div;
268+
}
269+
case 0xFF05:
270+
{
271+
u8 tima = timer.GetTIMA();
272+
LDEBUG("reading 0x%02X from TIMA (0xFF05)", tima);
273+
return tima;
274+
}
275+
case 0xFF06:
276+
{
277+
u8 tma = timer.GetTMA();
278+
LDEBUG("reading 0x%02X from TMA (0xFF06)", tma);
279+
return tma;
280+
}
281+
case 0xFF07:
282+
{
283+
u8 tac = timer.GetTAC();
284+
LDEBUG("reading 0x%02X from TAC (0xFF07)", tac);
285+
return tac;
286+
}
263287
case 0xFF0F:
264288
// Interrupt fetch
265289
return memory[0xFF0F];
@@ -316,6 +340,30 @@ void Bus::WriteIO(u16 addr, u8 value) {
316340
// putchar(value);
317341
LDEBUG("writing 0x%02X to Serial data (0xFF01)", value);
318342
return;
343+
case 0xFF04:
344+
// Timer divider
345+
// All writes to this set it to 0.
346+
timer.ResetDivider();
347+
memory[0xFF04] = 0x00;
348+
return;
349+
case 0xFF05:
350+
// Timer counter
351+
LDEBUG("writing 0x%02X to TIMA (0xFF05)", value);
352+
timer.SetTIMA(value);
353+
memory[0xFF05] = value;
354+
return;
355+
case 0xFF06:
356+
// Timer modulo
357+
LDEBUG("writing 0x%02X to TMA (0xFF06)", value);
358+
timer.SetTMA(value);
359+
memory[0xFF06] = value;
360+
return;
361+
case 0xFF07:
362+
// Timer control
363+
LDEBUG("writing 0x%02X to TAC (0xFF07)", value);
364+
timer.SetTAC(value);
365+
memory[0xFF07] = value;
366+
return;
319367
case 0xFF0F:
320368
// Interrupt fetch
321369
memory[0xFF0F] = value;

src/bus.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
#include "bootrom.h"
55
#include "cartridge.h"
66
#include "ppu.h"
7+
#include "timer.h"
78
#include "types.h"
89

910
class Bus {
1011
public:
11-
Bus(BootROM& bootrom, Cartridge& cartridge, PPU& ppu);
12+
Bus(BootROM& bootrom, Cartridge& cartridge, PPU& ppu, Timer& timer);
1213

1314
u8 Read8(u16 addr);
1415
void Write8(u16 addr, u8 value);
@@ -37,4 +38,5 @@ class Bus {
3738
BootROM bootrom;
3839
Cartridge cartridge;
3940
PPU& ppu;
41+
Timer& timer;
4042
};

src/gb.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include "ppu.h"
55

66
GB::GB(BootROM bootrom, Cartridge cartridge)
7-
: bus(bootrom, cartridge, ppu), ppu(bus), sm83(bus) {
7+
: bus(bootrom, cartridge, ppu, timer), ppu(bus), sm83(bus), timer(bus) {
88
LINFO("powering on...");
99
cycles = 0;
1010
}
@@ -13,6 +13,7 @@ void GB::Run() {
1313
u8 c = sm83.Tick();
1414
cycles += c;
1515
ppu.Tick(c);
16+
timer.Tick(c);
1617
}
1718

1819
Bus GB::GetBus() {

src/gb.h

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "bus.h"
66
#include "ppu.h"
77
#include "sm83.h"
8+
#include "timer.h"
89

910
class GB {
1011
public:
@@ -17,6 +18,7 @@ class GB {
1718
Bus bus;
1819
PPU ppu;
1920
SM83 sm83;
21+
Timer timer;
2022

2123
u64 cycles;
2224
};

src/ppu.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ PPU::PPU(Bus& bus)
1515
}
1616

1717
void PPU::Tick(u8 cycles) {
18-
if (cycles == 0) {
19-
// LWARN("The executed instruction advanced zero cycles");
20-
}
2118
vcycles += cycles;
2219

2320
switch (mode) {

src/sm83.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,19 @@ u8 SM83::Tick() {
3030
}
3131

3232
pc_at_opcode = pc;
33+
// if (pc_at_opcode == 0xC31B) {
34+
// LFATAL("div=%02X tima=%02X", bus.Read8(0xFF04), bus.Read8(0xFF05));
35+
// }
3336
const u8 opcode = GetByteFromPC();
3437

3538
// LTRACE("executing opcode 0x%02X at 0x%04X", opcode, pc_at_opcode);
3639
if (!ExecuteOpcode(opcode)) {
3740
std::exit(0);
3841
}
3942

43+
if (cycles_to_advance == 0) {
44+
LWARN("The executed opcode %02X advanced zero cycles", opcode);
45+
}
4046
return cycles_to_advance;
4147
}
4248

@@ -105,6 +111,7 @@ void SM83::StackPop(u16* word_reg) {
105111
bool SM83::ExecuteOpcode(const u8 opcode) {
106112
switch (opcode) {
107113
case 0xCB:
114+
AdvanceCycles(4);
108115
if (!ExecuteCBOpcode(GetByteFromPC())) {
109116
return false;
110117
}
@@ -1549,6 +1556,8 @@ void SM83::ld_dbc_a() {
15491556
LTRACE("LD (BC), A");
15501557

15511558
bus.Write8(bc, a);
1559+
1560+
AdvanceCycles(8);
15521561
}
15531562

15541563
void SM83::ld_dc_a() {
@@ -2411,6 +2420,8 @@ void SM83::rlc_dhl() {
24112420
SetCarryFlag(should_carry);
24122421

24132422
bus.Write8(hl, result | should_carry);
2423+
2424+
AdvanceCycles(16);
24142425
}
24152426

24162427
void SM83::rlc_r(u8* reg) {

src/timer.cpp

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include "bus.h"
2+
#include "logging.h"
3+
#include "timer.h"
4+
5+
Timer::Timer(Bus& bus)
6+
: bus(bus) {
7+
div = 0x00;
8+
tima = 0x00;
9+
tma = 0x00;
10+
tac = 0x00;
11+
timer_enable = false;
12+
div_cycles = 0;
13+
tima_cycles = 0;
14+
}
15+
16+
void Timer::Tick(u64 cycles_to_add) {
17+
if (timer_enable) {
18+
tima_cycles += cycles_to_add;
19+
20+
if (tima_cycles >= GetTACFrequency()) {
21+
tima_cycles -= GetTACFrequency();
22+
tima++;
23+
24+
// overflow
25+
if (tima == 0) {
26+
tima = tma;
27+
// request a timer interrupt
28+
bus.Write8(0xFF0F, bus.Read8(0xFF0F) | 0x4);
29+
// LFATAL("Setting tima to 0x%02X", tma);
30+
}
31+
}
32+
}
33+
34+
div_cycles += cycles_to_add;
35+
if (div_cycles >= 256) {
36+
div_cycles -= 256;
37+
div++;
38+
}
39+
}
40+
41+
u8 Timer::GetDivider() {
42+
return div;
43+
}
44+
45+
void Timer::ResetDivider() {
46+
div = 0x00;
47+
}
48+
49+
u8 Timer::GetTIMA() {
50+
return tima;
51+
}
52+
53+
void Timer::SetTIMA(u8 value) {
54+
tima = value;
55+
}
56+
57+
u8 Timer::GetTMA() {
58+
return tma;
59+
}
60+
61+
void Timer::SetTMA(u8 value) {
62+
tma = value;
63+
}
64+
65+
u8 Timer::GetTAC() {
66+
return tac;
67+
}
68+
69+
void Timer::SetTAC(u8 value) {
70+
tac = value;
71+
timer_enable = (tac & (1 << 2));
72+
}
73+
74+
u32 Timer::GetTACFrequency() {
75+
switch (tac & 0b11) {
76+
case 0b00: return 1024;
77+
case 0b01: return 16;
78+
case 0b10: return 64;
79+
case 0b11: return 256;
80+
default:
81+
LERROR("unreachable TAC frequency");
82+
return 1;
83+
}
84+
}

src/timer.h

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#pragma once
2+
3+
#include "types.h"
4+
5+
class Timer {
6+
public:
7+
Timer(Bus& bus);
8+
9+
void Tick(u64 cycles_to_add);
10+
11+
u8 GetDivider();
12+
void ResetDivider();
13+
u8 GetTIMA();
14+
void SetTIMA(u8 value);
15+
u8 GetTMA();
16+
void SetTMA(u8 value);
17+
u8 GetTAC();
18+
void SetTAC(u8 value);
19+
20+
private:
21+
u32 GetTACFrequency();
22+
23+
u8 div; // divider
24+
u8 tima; // timer counter
25+
u8 tma; // timer modulo
26+
u8 tac; // timer control
27+
28+
bool timer_enable;
29+
30+
u64 div_cycles;
31+
u64 tima_cycles;
32+
33+
Bus& bus;
34+
};

tests.md

+7-7
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Some of these tests will always fail due to currently unimplemented features, li
2323
| Test | Result |
2424
| ---- | ------ |
2525
| 01-special | Passed |
26-
| 02-interrupts | Failed (Timer doesn't work)
26+
| 02-interrupts | Passed |
2727
| 03-op sp,hl | Passed |
2828
| 04-op r,imm | Passed |
2929
| 05-op rp | Passed |
@@ -54,7 +54,7 @@ Some of these tests will always fail due to currently unimplemented features, li
5454
Failed (3DB103C3)
5555

5656
## instr_timing
57-
Failed (#255)
57+
Failed (Timer doesn't work properly)
5858

5959
## interrupt_time
6060
Failed
@@ -151,15 +151,15 @@ Failed
151151
| ret_timing | Failed (Round 1) |
152152
| rst_timing | Failed |
153153
| serial/boot_sclk_align-dmgABCmgb | Failed (No serial intr) |
154-
| timer/div_write | Passed |
155-
| timer/rapid_toggle | Failed (No intr) |
156-
| timer/tim00_div_trigger | Failed |
154+
| timer/div_write | Failed (intr) |
155+
| timer/rapid_toggle | Failed |
156+
| timer/tim00_div_trigger | Passed |
157157
| timer/tim00 | Failed |
158158
| timer/tim01_div_trigger | Failed |
159-
| timer/tim01 | Failed |
159+
| timer/tim01 | Passed |
160160
| timer/tim10_div_trigger | Failed |
161161
| timer/tim10 | Failed |
162-
| timer/tim11_div_trigger | Failed |
162+
| timer/tim11_div_trigger | Passed |
163163
| timer/tim11 | Failed |
164164
| timer/tima_reload | Failed |
165165
| timer/tima_write_reloading | Failed |

0 commit comments

Comments
 (0)