Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libsnes/bsnes/snes/alt/cpu/dma.cpp
2 views
1
#ifdef CPU_CPP
2
3
bool CPU::dma_transfer_valid(uint8 bbus, unsigned abus) {
4
//transfers from WRAM to WRAM are invalid; chip only has one address bus
5
if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) return false;
6
return true;
7
}
8
9
bool CPU::dma_addr_valid(unsigned abus) {
10
//A-bus access to B-bus or S-CPU registers are invalid
11
if((abus & 0x40ff00) == 0x2100) return false; //$[00-3f|80-bf]:[2100-21ff]
12
if((abus & 0x40fe00) == 0x4000) return false; //$[00-3f|80-bf]:[4000-41ff]
13
if((abus & 0x40ffe0) == 0x4200) return false; //$[00-3f|80-bf]:[4200-421f]
14
if((abus & 0x40ff80) == 0x4300) return false; //$[00-3f|80-bf]:[4300-437f]
15
return true;
16
}
17
18
uint8 CPU::dma_read(unsigned abus) {
19
if(dma_addr_valid(abus) == false) return 0x00;
20
return bus.read(abus);
21
}
22
23
void CPU::dma_write(bool valid, unsigned addr, uint8 data) {
24
if(valid) bus.write(addr, data);
25
}
26
27
void CPU::dma_transfer(bool direction, uint8 bbus, unsigned abus) {
28
if(direction == 0) {
29
uint8 data = dma_read(abus);
30
add_clocks(8);
31
dma_write(dma_transfer_valid(bbus, abus), 0x2100 | bbus, data);
32
} else {
33
uint8 data = dma_transfer_valid(bbus, abus) ? bus.read(0x2100 | bbus) : 0x00;
34
add_clocks(8);
35
dma_write(dma_addr_valid(abus), abus, data);
36
}
37
}
38
39
uint8 CPU::dma_bbus(unsigned i, unsigned index) {
40
switch(channel[i].transfer_mode) { default:
41
case 0: return (channel[i].dest_addr); //0
42
case 1: return (channel[i].dest_addr + (index & 1)); //0,1
43
case 2: return (channel[i].dest_addr); //0,0
44
case 3: return (channel[i].dest_addr + ((index >> 1) & 1)); //0,0,1,1
45
case 4: return (channel[i].dest_addr + (index & 3)); //0,1,2,3
46
case 5: return (channel[i].dest_addr + (index & 1)); //0,1,0,1
47
case 6: return (channel[i].dest_addr); //0,0 [2]
48
case 7: return (channel[i].dest_addr + ((index >> 1) & 1)); //0,0,1,1 [3]
49
}
50
}
51
52
unsigned CPU::dma_addr(unsigned i) {
53
unsigned result = (channel[i].source_bank << 16) | (channel[i].source_addr);
54
55
if(channel[i].fixed_transfer == false) {
56
if(channel[i].reverse_transfer == false) {
57
channel[i].source_addr++;
58
} else {
59
channel[i].source_addr--;
60
}
61
}
62
63
return result;
64
}
65
66
unsigned CPU::hdma_addr(unsigned i) {
67
return (channel[i].source_bank << 16) | (channel[i].hdma_addr++);
68
}
69
70
unsigned CPU::hdma_iaddr(unsigned i) {
71
return (channel[i].indirect_bank << 16) | (channel[i].indirect_addr++);
72
}
73
74
void CPU::dma_run() {
75
add_clocks(16);
76
77
for(unsigned i = 0; i < 8; i++) {
78
if(channel[i].dma_enabled == false) continue;
79
add_clocks(8);
80
81
unsigned index = 0;
82
do {
83
dma_transfer(channel[i].direction, dma_bbus(i, index++), dma_addr(i));
84
} while(channel[i].dma_enabled && --channel[i].transfer_size);
85
86
channel[i].dma_enabled = false;
87
}
88
89
status.irq_lock = true;
90
}
91
92
bool CPU::hdma_active_after(unsigned i) {
93
for(unsigned n = i + 1; i < 8; i++) {
94
if(channel[i].hdma_enabled && !channel[i].hdma_completed) return true;
95
}
96
return false;
97
}
98
99
void CPU::hdma_update(unsigned i) {
100
if((channel[i].line_counter & 0x7f) == 0) {
101
channel[i].line_counter = dma_read(hdma_addr(i));
102
channel[i].hdma_completed = (channel[i].line_counter == 0);
103
channel[i].hdma_do_transfer = !channel[i].hdma_completed;
104
add_clocks(8);
105
106
if(channel[i].indirect) {
107
channel[i].indirect_addr = dma_read(hdma_addr(i)) << 8;
108
add_clocks(8);
109
110
//emulating this glitch causes a slight slowdown; only enable if needed
111
//if(!channel[i].hdma_completed || hdma_active_after(i)) {
112
channel[i].indirect_addr >>= 8;
113
channel[i].indirect_addr |= dma_read(hdma_addr(i)) << 8;
114
add_clocks(8);
115
//}
116
}
117
}
118
}
119
120
void CPU::hdma_run() {
121
unsigned channels = 0;
122
for(unsigned i = 0; i < 8; i++) {
123
if(channel[i].hdma_enabled) channels++;
124
}
125
if(channels == 0) return;
126
127
add_clocks(16);
128
for(unsigned i = 0; i < 8; i++) {
129
if(channel[i].hdma_enabled == false || channel[i].hdma_completed == true) continue;
130
channel[i].dma_enabled = false;
131
132
if(channel[i].hdma_do_transfer) {
133
static const unsigned transfer_length[] = { 1, 2, 2, 4, 4, 4, 2, 4 };
134
unsigned length = transfer_length[channel[i].transfer_mode];
135
for(unsigned index = 0; index < length; index++) {
136
unsigned addr = channel[i].indirect == false ? hdma_addr(i) : hdma_iaddr(i);
137
dma_transfer(channel[i].direction, dma_bbus(i, index), addr);
138
}
139
}
140
}
141
142
for(unsigned i = 0; i < 8; i++) {
143
if(channel[i].hdma_enabled == false || channel[i].hdma_completed == true) continue;
144
145
channel[i].line_counter--;
146
channel[i].hdma_do_transfer = channel[i].line_counter & 0x80;
147
hdma_update(i);
148
}
149
150
status.irq_lock = true;
151
}
152
153
void CPU::hdma_init() {
154
unsigned channels = 0;
155
for(unsigned i = 0; i < 8; i++) {
156
channel[i].hdma_completed = false;
157
channel[i].hdma_do_transfer = false;
158
if(channel[i].hdma_enabled) channels++;
159
}
160
if(channels == 0) return;
161
162
add_clocks(16);
163
for(unsigned i = 0; i < 8; i++) {
164
if(!channel[i].hdma_enabled) continue;
165
channel[i].dma_enabled = false;
166
167
channel[i].hdma_addr = channel[i].source_addr;
168
channel[i].line_counter = 0;
169
hdma_update(i);
170
}
171
172
status.irq_lock = true;
173
}
174
175
void CPU::dma_reset() {
176
for(unsigned i = 0; i < 8; i++) {
177
channel[i].dma_enabled = false;
178
channel[i].hdma_enabled = false;
179
180
channel[i].direction = 1;
181
channel[i].indirect = true;
182
channel[i].unused = true;
183
channel[i].reverse_transfer = true;
184
channel[i].fixed_transfer = true;
185
channel[i].transfer_mode = 0x07;
186
187
channel[i].dest_addr = 0xff;
188
channel[i].source_addr = 0xffff;
189
channel[i].source_bank = 0xff;
190
191
channel[i].transfer_size = 0xffff;
192
channel[i].indirect_addr = 0xffff;
193
194
channel[i].indirect_bank = 0xff;
195
channel[i].hdma_addr = 0xff;
196
channel[i].line_counter = 0xff;
197
channel[i].unknown = 0xff;
198
199
channel[i].hdma_completed = false;
200
channel[i].hdma_do_transfer = false;
201
}
202
}
203
204
#endif
205
206