summaryrefslogtreecommitdiff
path: root/mknes_apu.c
blob: 8b9f45bddfe22a4e300fd121c58de0fa2b8aaa12 (plain)
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168


static void ppu_tick(struct nes_state *state);
static uint8_t memory_read(struct nes_state *state, uint32_t offset);

// DMC frequency table (NTSC)
static const uint16_t dmc_rate_table[16] = {
	428, 380, 340, 320, 286, 254, 226, 214,
	190, 160, 142, 128, 106,  85,  72,  54
};

// $4015 write
static void apu_write4015(struct nes_state *state, uint8_t val) {
	struct apu_state *apu = &state->apu;

	if(val & 0x10) {
		if(!apu->dmc_dma_enabled || apu->dmc_bytes_remaining == 0) {
			apu->dmc_current_addr = 0xC000 + ((uint16_t)apu->dmc_sample_addr << 6);
			apu->dmc_bytes_remaining = ((uint16_t)apu->dmc_sample_len << 4);
		}
		apu->dmc_dma_enabled = 1;
	} else {
		apu->dmc_dma_enabled = 0;
	}
}

// $4015 read
static uint8_t apu_read4015(struct nes_state *state) {
	struct apu_state *apu = &state->apu;
	uint8_t result = 0;

	if(apu->dmc_bytes_remaining > 0) {
		result |= 0x10;
	}
	if(apu->irq_pending) {
		result |= 0x40;
	}

	// Reading $4015 clears the frame IRQ flag
	apu->irq_pending = 0;
	// Only clear CPU IRQ if DMC isn't requesting it
	if(!(apu->dmc_bytes_remaining > 0 && apu->dmc_irq_enable)) {
		state->cpu.irq_pending = 0;
	}

	return result;
}

// $4010–$4013, $4015, $4017 write
static void apu_write(struct nes_state *state, uint16_t addr, uint8_t val) {
	struct apu_state *apu = &state->apu;

	switch(addr) {
		case 0x4010: {
			apu->dmc_irq_enable = (val >> 7) & 1;
			apu->dmc_loop_flag = (val >> 6) & 1;
			apu->dmc_freq_index = val & 0x0F;
		} break;
		case 0x4011: {
			// DAC write ignored
		} break;
		case 0x4012: {
			apu->dmc_sample_addr = val;
		} break;
		case 0x4013: {
			apu->dmc_sample_len = val;
		} break;
		case 0x4015: {
			apu_write4015(state, val);
		} break;
		case 0x4017: {
			// Frame counter control
			apu->mode = (val >> 7) & 1;
			apu->irq_inhibit = (val >> 6) & 1;

			// If IRQ inhibit flag is set, clear the frame IRQ
			if(apu->irq_inhibit) {
				apu->irq_pending = 0;
				// Only clear CPU IRQ if DMC isn't requesting it
				if(!(apu->dmc_bytes_remaining > 0 && apu->dmc_irq_enable)) {
					state->cpu.irq_pending = 0;
				}
			}

			// Reset frame counter (with delay, but we'll approximate immediately for now)
			apu->frame_cycle = 0;

			// If 5-step mode, immediately clock half-frame and quarter-frame
			// (For timing purposes without audio, we can leave this empty)
		} break;
	}
}

// APU tick
static inline void apu_tick(struct nes_state *state) {
	struct apu_state *apu = &state->apu;

	apu->frame_cycle++;

	if(apu->mode == 0) {
		if(apu->frame_cycle == 7457) {
			// Quarter frame
		} else if(apu->frame_cycle == 14913) {
			// Quarter frame
			// Half frame
		} else if(apu->frame_cycle == 22371) {
			// Quarter frame
		} else if(apu->frame_cycle == 29829) {
			// Half frame
			if(!apu->irq_inhibit) {
				apu->irq_pending = 1;
				state->cpu.irq_pending = 1;
			}
		} else if(apu->frame_cycle >= 29830) {
			apu->frame_cycle = 0;
		}
	} else {
		if(apu->frame_cycle == 7457) {
			// Quarter frame
		} else if(apu->frame_cycle == 14913) {
			// Quarter frame
			// Half frame
		} else if(apu->frame_cycle == 22371) {
			// Quarter frame
		} else if(apu->frame_cycle == 29829) {
			// Half frame
		} else if(apu->frame_cycle == 37281) {
			// Quarter frame
		} else if(apu->frame_cycle >= 37282) {
			apu->frame_cycle = 0;
		}
	}

	// if(apu->dmc_dma_enabled && apu->dmc_bytes_remaining > 0) {
	// 	apu->dmc_sample_timer++;
	// 	if(apu->dmc_sample_timer >= dmc_rate_table[apu->dmc_freq_index]) {
	// 		apu->dmc_sample_timer = 0;

	// 		uint8_t val = memory_read(state, apu->dmc_current_addr);
	// 		(void)val;

	// 		apu->dmc_current_addr++;
	// 		if(apu->dmc_current_addr == 0x0000) {
	// 			apu->dmc_current_addr = 0x8000;
	// 		}

	// 		apu->dmc_bytes_remaining--;

	// 		if(apu->dmc_bytes_remaining == 0) {
	// 			if(apu->dmc_loop_flag) {
	// 				apu->dmc_current_addr = 0xc000 + ((uint16_t)apu->dmc_sample_addr << 6);
	// 				apu->dmc_bytes_remaining = ((uint16_t)apu->dmc_sample_len << 4);
	// 			} else {
	// 				if(apu->dmc_irq_enable) {
	// 					apu->irq_pending = 1;
	// 					state->cpu.irq_pending = 1;
	// 				}
	// 				apu->dmc_dma_enabled = 0;
	// 			}
	// 		}

	// 		for(uint32_t i = 0; i < 4; i++) {
	// 			state->cpu.cycles++;
	// 			ppu_tick(state);
	// 		}
	// 	}
	// }
}