1 /*
2  *  REU.cpp - 17xx REU emulation
3  *
4  *  Frodo (C) 1994-1997,2002 Christian Bauer
5  *
6 
7  *
8  * Incompatibilities:
9  * ------------------
10  *
11  *  - REU interrupts are not emulated
12  *  - Transfer time is not accounted for, all transfers
13  *    are done in 0 cycles
14  */
15 
16 #include "sysdeps.h"
17 
18 #include "REU.h"
19 #include "CPUC64.h"
20 #include "Prefs.h"
21 
22 
23 /*
24  *  Constructor
25  */
26 
REU(MOS6510 * CPU)27 REU::REU(MOS6510 *CPU) : the_cpu(CPU)
28 {
29 	int i;
30 
31 	// Init registers
32 	regs[0] = 0x40;
33 	for (i=1; i<11; i++)
34 		regs[i] = 0;
35 	for (i=11; i<16; i++)
36 		regs[i] = 0xff;
37 
38 	ex_ram = NULL;
39 	ram_size = ram_mask = 0;
40 
41 	// Allocate RAM
42 	open_close_reu(REU_NONE, ThePrefs.REUSize);
43 }
44 
45 
46 /*
47  *  Destructor
48  */
49 
~REU()50 REU::~REU()
51 {
52 	// Free RAM
53 	open_close_reu(ThePrefs.REUSize, REU_NONE);
54 }
55 
56 
57 /*
58  *  Prefs may have changed, reallocate expansion RAM
59  */
60 
NewPrefs(Prefs * prefs)61 void REU::NewPrefs(Prefs *prefs)
62 {
63 	open_close_reu(ThePrefs.REUSize, prefs->REUSize);
64 }
65 
66 
67 /*
68  *  Allocate/free expansion RAM
69  */
70 
open_close_reu(int old_size,int new_size)71 void REU::open_close_reu(int old_size, int new_size)
72 {
73 	if (old_size == new_size)
74 		return;
75 
76 	// Free old RAM
77 	if (old_size != REU_NONE) {
78 		delete[] ex_ram;
79 		ex_ram = NULL;
80 	}
81 
82 	// Allocate new RAM
83 	if (new_size != REU_NONE) {
84 		switch (new_size) {
85 			case REU_128K:
86 				ram_size = 0x20000;
87 				break;
88 			case REU_256K:
89 				ram_size = 0x40000;
90 				break;
91 			case REU_512K:
92 				ram_size = 0x80000;
93 				break;
94 		}
95 		ram_mask = ram_size - 1;
96 		ex_ram = new uint8[ram_size];
97 
98 		// Set size bit in status register
99 		if (ram_size > 0x20000)
100 			regs[0] |= 0x10;
101 		else
102 			regs[0] &= 0xef;
103 	}
104 }
105 
106 
107 /*
108  *  Reset the REU
109  */
110 
Reset(void)111 void REU::Reset(void)
112 {
113 	int i;
114 
115 	for (i=1; i<11; i++)
116 		regs[i] = 0;
117 	for (i=11; i<16; i++)
118 		regs[i] = 0xff;
119 
120 	if (ram_size > 0x20000)
121 		regs[0] = 0x50;
122 	else
123 		regs[0] = 0x40;
124 }
125 
126 
127 /*
128  *  Read from REU register
129  */
130 
ReadRegister(uint16 adr)131 uint8 REU::ReadRegister(uint16 adr)
132 {
133 	if (ex_ram == NULL)
134 		return rand();
135 
136 	switch (adr) {
137 		case 0:{
138 			uint8 ret = regs[0];
139 			regs[0] &= 0x1f;
140 			return ret;
141 		}
142 		case 6:
143 			return regs[6] | 0xf8;
144 		case 9:
145 			return regs[9] | 0x1f;
146 		case 10:
147 			return regs[10] | 0x3f;
148 		default:
149 			return regs[adr];
150 	}
151 }
152 
153 
154 /*
155  *  Write to REU register
156  */
157 
WriteRegister(uint16 adr,uint8 byte)158 void REU::WriteRegister(uint16 adr, uint8 byte)
159 {
160 	if (ex_ram == NULL)
161 		return;
162 
163 	switch (adr) {
164 		case 0:		// Status register is read-only
165 		case 11:	// Unconnected registers
166 		case 12:
167 		case 13:
168 		case 14:
169 		case 15:
170 			break;
171 		case 1:		// Command register
172 			regs[1] = byte;
173 			if ((byte & 0x90) == 0x90)
174 				execute_dma();
175 			break;
176 		default:
177 			regs[adr] = byte;
178 			break;
179 	}
180 }
181 
182 
183 /*
184  *  CPU triggered REU by writing to $ff00
185  */
186 
FF00Trigger(void)187 void REU::FF00Trigger(void)
188 {
189 	if (ex_ram == NULL)
190 		return;
191 
192 	if ((regs[1] & 0x90) == 0x80)
193 		execute_dma();
194 }
195 
196 
197 /*
198  *  Execute REU DMA transfer
199  */
200 
execute_dma(void)201 void REU::execute_dma(void)
202 {
203 	// Get C64 and REU transfer base addresses
204 	uint16 c64_adr = regs[2] | (regs[3] << 8);
205 	uint32 reu_adr = regs[4] | (regs[5] << 8) | (regs[6] << 16);
206 
207 	// Calculate transfer length
208 	int length = regs[7] | (regs[8] << 8);
209 	if (!length)
210 		length = 0x10000;
211 
212 	// Calculate address increments
213 	uint32 c64_inc = (regs[10] & 0x80) ? 0 : 1;
214 	uint32 reu_inc = (regs[10] & 0x40) ? 0 : 1;
215 
216 	// Do transfer
217 	switch (regs[1] & 3) {
218 
219 		case 0:		// C64 -> REU
220 			for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc)
221 				ex_ram[reu_adr & ram_mask] = the_cpu->REUReadByte(c64_adr);
222 			break;
223 
224 		case 1:		// C64 <- REU
225 			for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc)
226 				the_cpu->REUWriteByte(c64_adr, ex_ram[reu_adr & ram_mask]);
227 			break;
228 
229 		case 2:		// C64 <-> REU
230 			for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc) {
231 				uint8 tmp = the_cpu->REUReadByte(c64_adr);
232 				the_cpu->REUWriteByte(c64_adr, ex_ram[reu_adr & ram_mask]);
233 				ex_ram[reu_adr & ram_mask] = tmp;
234 			}
235 			break;
236 
237 		case 3:		// Compare
238 			for (; length--; c64_adr+=c64_inc, reu_adr+=reu_inc)
239 				if (ex_ram[reu_adr & ram_mask] != the_cpu->REUReadByte(c64_adr)) {
240 					regs[0] |= 0x20;
241 					break;
242 				}
243 			break;
244 	}
245 
246 	// Update address and length registers if autoload is off
247 	if (!(regs[1] & 0x20)) {
248 		regs[2] = c64_adr;
249 		regs[3] = c64_adr >> 8;
250 		regs[4] = reu_adr;
251 		regs[5] = reu_adr >> 8;
252 		regs[6] = reu_adr >> 16;
253 		regs[7] = length + 1;
254 		regs[8] = (length + 1) >> 8;
255 	}
256 
257 	// Set complete bit in status register
258 	regs[0] |= 0x40;
259 
260 	// Clear execute bit in command register
261 	regs[1] &= 0x7f;
262 }
263