]>
Commit | Line | Data |
---|---|---|
6f7e9aec FB |
1 | /* |
2 | * QEMU ESP emulation | |
3 | * | |
4 | * Copyright (c) 2005 Fabrice Bellard | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | #include "vl.h" | |
25 | ||
26 | /* debug ESP card */ | |
2f275b8f | 27 | //#define DEBUG_ESP |
6f7e9aec FB |
28 | |
29 | #ifdef DEBUG_ESP | |
30 | #define DPRINTF(fmt, args...) \ | |
31 | do { printf("ESP: " fmt , ##args); } while (0) | |
32 | #else | |
33 | #define DPRINTF(fmt, args...) | |
34 | #endif | |
35 | ||
36 | #define ESPDMA_REGS 4 | |
37 | #define ESPDMA_MAXADDR (ESPDMA_REGS * 4 - 1) | |
38 | #define ESP_MAXREG 0x3f | |
4f6200f0 FB |
39 | #define TI_BUFSZ 65536 |
40 | #define DMA_VER 0xa0000000 | |
41 | #define DMA_LOADED 0x04000000 | |
6f7e9aec FB |
42 | |
43 | typedef struct ESPState { | |
44 | BlockDriverState **bd; | |
2f275b8f FB |
45 | uint8_t rregs[ESP_MAXREG]; |
46 | uint8_t wregs[ESP_MAXREG]; | |
6f7e9aec FB |
47 | int irq; |
48 | uint32_t espdmaregs[ESPDMA_REGS]; | |
2f275b8f | 49 | uint32_t ti_size; |
4f6200f0 | 50 | uint32_t ti_rptr, ti_wptr; |
2f275b8f | 51 | int ti_dir; |
4f6200f0 FB |
52 | uint8_t ti_buf[TI_BUFSZ]; |
53 | int dma; | |
6f7e9aec FB |
54 | } ESPState; |
55 | ||
2f275b8f FB |
56 | #define STAT_DO 0x00 |
57 | #define STAT_DI 0x01 | |
58 | #define STAT_CD 0x02 | |
59 | #define STAT_ST 0x03 | |
60 | #define STAT_MI 0x06 | |
61 | #define STAT_MO 0x07 | |
62 | ||
63 | #define STAT_TC 0x10 | |
64 | #define STAT_IN 0x80 | |
65 | ||
66 | #define INTR_FC 0x08 | |
67 | #define INTR_BS 0x10 | |
68 | #define INTR_DC 0x20 | |
69 | ||
70 | #define SEQ_0 0x0 | |
71 | #define SEQ_CD 0x4 | |
72 | ||
73 | static void handle_satn(ESPState *s) | |
74 | { | |
75 | uint8_t buf[32]; | |
76 | uint32_t dmaptr, dmalen; | |
77 | unsigned int i; | |
78 | int64_t nb_sectors; | |
79 | int target; | |
80 | ||
2f275b8f | 81 | dmalen = s->wregs[0] | (s->wregs[1] << 8); |
4f6200f0 FB |
82 | target = s->wregs[4] & 7; |
83 | DPRINTF("Select with ATN len %d target %d\n", dmalen, target); | |
84 | if (s->dma) { | |
85 | dmaptr = iommu_translate(s->espdmaregs[1]); | |
86 | DPRINTF("DMA Direction: %c, addr 0x%8.8x\n", s->espdmaregs[0] & 0x100? 'w': 'r', dmaptr); | |
87 | cpu_physical_memory_read(dmaptr, buf, dmalen); | |
88 | } else { | |
89 | buf[0] = 0; | |
90 | memcpy(&buf[1], s->ti_buf, dmalen); | |
91 | dmalen++; | |
92 | } | |
2f275b8f FB |
93 | for (i = 0; i < dmalen; i++) { |
94 | DPRINTF("Command %2.2x\n", buf[i]); | |
95 | } | |
96 | s->ti_dir = 0; | |
97 | s->ti_size = 0; | |
4f6200f0 FB |
98 | s->ti_rptr = 0; |
99 | s->ti_wptr = 0; | |
2f275b8f FB |
100 | |
101 | if (target > 4 || !s->bd[target]) { // No such drive | |
102 | s->rregs[4] = STAT_IN; | |
103 | s->rregs[5] = INTR_DC; | |
104 | s->rregs[6] = SEQ_0; | |
105 | s->espdmaregs[0] |= 1; | |
106 | pic_set_irq(s->irq, 1); | |
107 | return; | |
108 | } | |
109 | switch (buf[1]) { | |
110 | case 0x0: | |
111 | DPRINTF("Test Unit Ready (len %d)\n", buf[5]); | |
112 | break; | |
113 | case 0x12: | |
114 | DPRINTF("Inquiry (len %d)\n", buf[5]); | |
115 | memset(s->ti_buf, 0, 36); | |
116 | if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) { | |
117 | s->ti_buf[0] = 5; | |
118 | memcpy(&s->ti_buf[16], "QEMU CDROM ", 16); | |
119 | } else { | |
120 | s->ti_buf[0] = 0; | |
121 | memcpy(&s->ti_buf[16], "QEMU HARDDISK ", 16); | |
122 | } | |
123 | memcpy(&s->ti_buf[8], "QEMU ", 8); | |
124 | s->ti_buf[2] = 1; | |
125 | s->ti_buf[3] = 2; | |
126 | s->ti_dir = 1; | |
127 | s->ti_size = 36; | |
128 | break; | |
129 | case 0x1a: | |
130 | DPRINTF("Mode Sense(6) (page %d, len %d)\n", buf[3], buf[5]); | |
131 | break; | |
132 | case 0x25: | |
133 | DPRINTF("Read Capacity (len %d)\n", buf[5]); | |
134 | memset(s->ti_buf, 0, 8); | |
135 | bdrv_get_geometry(s->bd[target], &nb_sectors); | |
136 | s->ti_buf[0] = (nb_sectors >> 24) & 0xff; | |
137 | s->ti_buf[1] = (nb_sectors >> 16) & 0xff; | |
138 | s->ti_buf[2] = (nb_sectors >> 8) & 0xff; | |
139 | s->ti_buf[3] = nb_sectors & 0xff; | |
140 | s->ti_buf[4] = 0; | |
141 | s->ti_buf[5] = 0; | |
4f6200f0 FB |
142 | if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) |
143 | s->ti_buf[6] = 8; // sector size 2048 | |
144 | else | |
145 | s->ti_buf[6] = 2; // sector size 512 | |
2f275b8f FB |
146 | s->ti_buf[7] = 0; |
147 | s->ti_dir = 1; | |
148 | s->ti_size = 8; | |
149 | break; | |
150 | case 0x28: | |
151 | { | |
152 | int64_t offset, len; | |
153 | ||
4f6200f0 FB |
154 | if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) { |
155 | offset = ((buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]) * 4; | |
156 | len = ((buf[8] << 8) | buf[9]) * 4; | |
157 | s->ti_size = len * 2048; | |
158 | } else { | |
159 | offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]; | |
160 | len = (buf[8] << 8) | buf[9]; | |
161 | s->ti_size = len * 512; | |
162 | } | |
2f275b8f FB |
163 | DPRINTF("Read (10) (offset %lld len %lld)\n", offset, len); |
164 | bdrv_read(s->bd[target], offset, s->ti_buf, len); | |
4f6200f0 | 165 | // XXX error handling |
2f275b8f | 166 | s->ti_dir = 1; |
2f275b8f FB |
167 | break; |
168 | } | |
169 | case 0x2a: | |
170 | { | |
171 | int64_t offset, len; | |
172 | ||
4f6200f0 FB |
173 | if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) { |
174 | offset = ((buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]) * 4; | |
175 | len = ((buf[8] << 8) | buf[9]) * 4; | |
176 | s->ti_size = len * 2048; | |
177 | } else { | |
178 | offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]; | |
179 | len = (buf[8] << 8) | buf[9]; | |
180 | s->ti_size = len * 512; | |
181 | } | |
2f275b8f FB |
182 | DPRINTF("Write (10) (offset %lld len %lld)\n", offset, len); |
183 | bdrv_write(s->bd[target], offset, s->ti_buf, len); | |
4f6200f0 | 184 | // XXX error handling |
2f275b8f | 185 | s->ti_dir = 0; |
2f275b8f FB |
186 | break; |
187 | } | |
188 | default: | |
4f6200f0 | 189 | DPRINTF("Unknown SCSI command (%2.2x)\n", buf[1]); |
2f275b8f FB |
190 | break; |
191 | } | |
192 | s->rregs[4] = STAT_IN | STAT_TC | STAT_DI; | |
193 | s->rregs[5] = INTR_BS | INTR_FC; | |
194 | s->rregs[6] = SEQ_CD; | |
195 | s->espdmaregs[0] |= 1; | |
196 | pic_set_irq(s->irq, 1); | |
197 | } | |
198 | ||
199 | static void dma_write(ESPState *s, const uint8_t *buf, uint32_t len) | |
200 | { | |
201 | uint32_t dmaptr, dmalen; | |
202 | ||
2f275b8f | 203 | dmalen = s->wregs[0] | (s->wregs[1] << 8); |
4f6200f0 FB |
204 | DPRINTF("Transfer status len %d\n", dmalen); |
205 | if (s->dma) { | |
206 | dmaptr = iommu_translate(s->espdmaregs[1]); | |
207 | DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r'); | |
208 | cpu_physical_memory_write(dmaptr, buf, len); | |
209 | s->rregs[4] = STAT_IN | STAT_TC | STAT_ST; | |
210 | s->rregs[5] = INTR_BS | INTR_FC; | |
211 | s->rregs[6] = SEQ_CD; | |
212 | s->espdmaregs[0] |= 1; | |
213 | } else { | |
214 | memcpy(s->ti_buf, buf, len); | |
215 | s->ti_size = dmalen; | |
216 | s->ti_rptr = 0; | |
217 | s->ti_wptr = 0; | |
218 | } | |
2f275b8f FB |
219 | pic_set_irq(s->irq, 1); |
220 | ||
221 | } | |
4f6200f0 | 222 | |
2f275b8f FB |
223 | static const uint8_t okbuf[] = {0, 0}; |
224 | ||
225 | static void handle_ti(ESPState *s) | |
226 | { | |
227 | uint32_t dmaptr, dmalen; | |
228 | unsigned int i; | |
229 | ||
2f275b8f | 230 | dmalen = s->wregs[0] | (s->wregs[1] << 8); |
4f6200f0 FB |
231 | DPRINTF("Transfer Information len %d\n", dmalen); |
232 | if (s->dma) { | |
233 | dmaptr = iommu_translate(s->espdmaregs[1]); | |
234 | DPRINTF("DMA Direction: %c, addr 0x%8.8x\n", s->espdmaregs[0] & 0x100? 'w': 'r', dmaptr); | |
235 | for (i = 0; i < s->ti_size; i++) { | |
236 | dmaptr = iommu_translate(s->espdmaregs[1] + i); | |
237 | if (s->ti_dir) | |
238 | cpu_physical_memory_write(dmaptr, &s->ti_buf[i], 1); | |
239 | else | |
240 | cpu_physical_memory_read(dmaptr, &s->ti_buf[i], 1); | |
241 | } | |
242 | s->rregs[4] = STAT_IN | STAT_TC | STAT_ST; | |
243 | s->rregs[5] = INTR_BS; | |
244 | s->rregs[6] = 0; | |
245 | s->espdmaregs[0] |= 1; | |
246 | } else { | |
247 | s->ti_size = dmalen; | |
248 | s->ti_rptr = 0; | |
249 | s->ti_wptr = 0; | |
250 | } | |
2f275b8f FB |
251 | pic_set_irq(s->irq, 1); |
252 | } | |
253 | ||
6f7e9aec FB |
254 | static void esp_reset(void *opaque) |
255 | { | |
256 | ESPState *s = opaque; | |
2f275b8f FB |
257 | memset(s->rregs, 0, ESP_MAXREG); |
258 | s->rregs[0x0e] = 0x4; // Indicate fas100a | |
6f7e9aec FB |
259 | memset(s->espdmaregs, 0, ESPDMA_REGS * 4); |
260 | } | |
261 | ||
262 | static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr) | |
263 | { | |
264 | ESPState *s = opaque; | |
265 | uint32_t saddr; | |
266 | ||
267 | saddr = (addr & ESP_MAXREG) >> 2; | |
268 | switch (saddr) { | |
4f6200f0 FB |
269 | case 2: |
270 | // FIFO | |
271 | if (s->ti_size > 0) { | |
272 | s->ti_size--; | |
273 | s->rregs[saddr] = s->ti_buf[s->ti_rptr++]; | |
274 | pic_set_irq(s->irq, 1); | |
275 | } | |
276 | if (s->ti_size == 0) { | |
277 | s->ti_rptr = 0; | |
278 | s->ti_wptr = 0; | |
279 | } | |
280 | break; | |
6f7e9aec FB |
281 | default: |
282 | break; | |
283 | } | |
2f275b8f | 284 | DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]); |
4f6200f0 | 285 | |
2f275b8f | 286 | return s->rregs[saddr]; |
6f7e9aec FB |
287 | } |
288 | ||
289 | static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) | |
290 | { | |
291 | ESPState *s = opaque; | |
292 | uint32_t saddr; | |
293 | ||
294 | saddr = (addr & ESP_MAXREG) >> 2; | |
2f275b8f | 295 | DPRINTF("write reg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->wregs[saddr], val); |
6f7e9aec | 296 | switch (saddr) { |
4f6200f0 FB |
297 | case 0: |
298 | case 1: | |
299 | s->rregs[saddr] = val; | |
300 | break; | |
301 | case 2: | |
302 | // FIFO | |
303 | s->ti_size++; | |
304 | s->ti_buf[s->ti_wptr++] = val & 0xff; | |
305 | break; | |
6f7e9aec | 306 | case 3: |
4f6200f0 | 307 | s->rregs[saddr] = val; |
6f7e9aec | 308 | // Command |
4f6200f0 FB |
309 | if (val & 0x80) { |
310 | s->dma = 1; | |
311 | } else { | |
312 | s->dma = 0; | |
313 | } | |
6f7e9aec FB |
314 | switch(val & 0x7f) { |
315 | case 0: | |
2f275b8f FB |
316 | DPRINTF("NOP (%2.2x)\n", val); |
317 | break; | |
318 | case 1: | |
319 | DPRINTF("Flush FIFO (%2.2x)\n", val); | |
320 | s->rregs[6] = 0; | |
321 | s->rregs[5] = INTR_FC; | |
6f7e9aec FB |
322 | break; |
323 | case 2: | |
2f275b8f | 324 | DPRINTF("Chip reset (%2.2x)\n", val); |
6f7e9aec FB |
325 | esp_reset(s); |
326 | break; | |
327 | case 3: | |
2f275b8f FB |
328 | DPRINTF("Bus reset (%2.2x)\n", val); |
329 | break; | |
330 | case 0x10: | |
331 | handle_ti(s); | |
332 | break; | |
333 | case 0x11: | |
334 | DPRINTF("Initiator Command Complete Sequence (%2.2x)\n", val); | |
335 | dma_write(s, okbuf, 2); | |
336 | break; | |
337 | case 0x12: | |
338 | DPRINTF("Message Accepted (%2.2x)\n", val); | |
339 | dma_write(s, okbuf, 2); | |
340 | s->rregs[5] = INTR_DC; | |
341 | s->rregs[6] = 0; | |
6f7e9aec FB |
342 | break; |
343 | case 0x1a: | |
2f275b8f | 344 | DPRINTF("Set ATN (%2.2x)\n", val); |
6f7e9aec FB |
345 | break; |
346 | case 0x42: | |
2f275b8f FB |
347 | handle_satn(s); |
348 | break; | |
349 | case 0x43: | |
350 | DPRINTF("Set ATN & stop (%2.2x)\n", val); | |
351 | handle_satn(s); | |
352 | break; | |
353 | default: | |
4f6200f0 | 354 | DPRINTF("Unhandled ESP command (%2.2x)\n", val); |
6f7e9aec FB |
355 | break; |
356 | } | |
357 | break; | |
358 | case 4 ... 7: | |
6f7e9aec | 359 | break; |
4f6200f0 FB |
360 | case 8: |
361 | s->rregs[saddr] = val; | |
362 | break; | |
363 | case 9 ... 10: | |
364 | break; | |
365 | case 11 ... 15: | |
366 | s->rregs[saddr] = val; | |
367 | break; | |
6f7e9aec | 368 | default: |
6f7e9aec FB |
369 | break; |
370 | } | |
2f275b8f | 371 | s->wregs[saddr] = val; |
6f7e9aec FB |
372 | } |
373 | ||
374 | static CPUReadMemoryFunc *esp_mem_read[3] = { | |
375 | esp_mem_readb, | |
376 | esp_mem_readb, | |
377 | esp_mem_readb, | |
378 | }; | |
379 | ||
380 | static CPUWriteMemoryFunc *esp_mem_write[3] = { | |
381 | esp_mem_writeb, | |
382 | esp_mem_writeb, | |
383 | esp_mem_writeb, | |
384 | }; | |
385 | ||
386 | static uint32_t espdma_mem_readl(void *opaque, target_phys_addr_t addr) | |
387 | { | |
388 | ESPState *s = opaque; | |
389 | uint32_t saddr; | |
390 | ||
391 | saddr = (addr & ESPDMA_MAXADDR) >> 2; | |
4f6200f0 FB |
392 | DPRINTF("read dmareg[%d]: 0x%8.8x\n", saddr, s->espdmaregs[saddr]); |
393 | ||
6f7e9aec FB |
394 | return s->espdmaregs[saddr]; |
395 | } | |
396 | ||
397 | static void espdma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) | |
398 | { | |
399 | ESPState *s = opaque; | |
400 | uint32_t saddr; | |
401 | ||
402 | saddr = (addr & ESPDMA_MAXADDR) >> 2; | |
4f6200f0 | 403 | DPRINTF("write dmareg[%d]: 0x%8.8x -> 0x%8.8x\n", saddr, s->espdmaregs[saddr], val); |
2f275b8f FB |
404 | switch (saddr) { |
405 | case 0: | |
406 | if (!(val & 0x10)) | |
407 | pic_set_irq(s->irq, 0); | |
4f6200f0 FB |
408 | if (val & 0x80) { |
409 | esp_reset(s); | |
410 | } else if (val & 0x40) { | |
411 | val &= ~0x40; | |
412 | } else if (val == 0) | |
413 | val = 0x40; | |
414 | val &= 0x0fffffff; | |
415 | val |= DMA_VER; | |
2f275b8f | 416 | break; |
4f6200f0 FB |
417 | case 1: |
418 | s->espdmaregs[0] = DMA_LOADED; | |
419 | break; | |
2f275b8f FB |
420 | default: |
421 | break; | |
422 | } | |
6f7e9aec FB |
423 | s->espdmaregs[saddr] = val; |
424 | } | |
425 | ||
426 | static CPUReadMemoryFunc *espdma_mem_read[3] = { | |
427 | espdma_mem_readl, | |
428 | espdma_mem_readl, | |
429 | espdma_mem_readl, | |
430 | }; | |
431 | ||
432 | static CPUWriteMemoryFunc *espdma_mem_write[3] = { | |
433 | espdma_mem_writel, | |
434 | espdma_mem_writel, | |
435 | espdma_mem_writel, | |
436 | }; | |
437 | ||
438 | static void esp_save(QEMUFile *f, void *opaque) | |
439 | { | |
440 | ESPState *s = opaque; | |
2f275b8f FB |
441 | unsigned int i; |
442 | ||
443 | qemu_put_buffer(f, s->rregs, ESP_MAXREG); | |
444 | qemu_put_buffer(f, s->wregs, ESP_MAXREG); | |
445 | qemu_put_be32s(f, &s->irq); | |
446 | for (i = 0; i < ESPDMA_REGS; i++) | |
447 | qemu_put_be32s(f, &s->espdmaregs[i]); | |
4f6200f0 FB |
448 | qemu_put_be32s(f, &s->ti_size); |
449 | qemu_put_be32s(f, &s->ti_rptr); | |
450 | qemu_put_be32s(f, &s->ti_wptr); | |
451 | qemu_put_be32s(f, &s->ti_dir); | |
452 | qemu_put_buffer(f, s->ti_buf, TI_BUFSZ); | |
453 | qemu_put_be32s(f, &s->dma); | |
6f7e9aec FB |
454 | } |
455 | ||
456 | static int esp_load(QEMUFile *f, void *opaque, int version_id) | |
457 | { | |
458 | ESPState *s = opaque; | |
2f275b8f | 459 | unsigned int i; |
6f7e9aec FB |
460 | |
461 | if (version_id != 1) | |
462 | return -EINVAL; | |
463 | ||
2f275b8f FB |
464 | qemu_get_buffer(f, s->rregs, ESP_MAXREG); |
465 | qemu_get_buffer(f, s->wregs, ESP_MAXREG); | |
466 | qemu_get_be32s(f, &s->irq); | |
467 | for (i = 0; i < ESPDMA_REGS; i++) | |
468 | qemu_get_be32s(f, &s->espdmaregs[i]); | |
4f6200f0 FB |
469 | qemu_get_be32s(f, &s->ti_size); |
470 | qemu_get_be32s(f, &s->ti_rptr); | |
471 | qemu_get_be32s(f, &s->ti_wptr); | |
472 | qemu_get_be32s(f, &s->ti_dir); | |
473 | qemu_get_buffer(f, s->ti_buf, TI_BUFSZ); | |
474 | qemu_get_be32s(f, &s->dma); | |
2f275b8f | 475 | |
6f7e9aec FB |
476 | return 0; |
477 | } | |
478 | ||
479 | void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdaddr) | |
480 | { | |
481 | ESPState *s; | |
482 | int esp_io_memory, espdma_io_memory; | |
483 | ||
484 | s = qemu_mallocz(sizeof(ESPState)); | |
485 | if (!s) | |
486 | return; | |
487 | ||
488 | s->bd = bd; | |
489 | s->irq = irq; | |
490 | ||
491 | esp_io_memory = cpu_register_io_memory(0, esp_mem_read, esp_mem_write, s); | |
492 | cpu_register_physical_memory(espaddr, ESP_MAXREG*4, esp_io_memory); | |
493 | ||
494 | espdma_io_memory = cpu_register_io_memory(0, espdma_mem_read, espdma_mem_write, s); | |
495 | cpu_register_physical_memory(espdaddr, 16, espdma_io_memory); | |
496 | ||
497 | esp_reset(s); | |
498 | ||
499 | register_savevm("esp", espaddr, 1, esp_save, esp_load, s); | |
500 | qemu_register_reset(esp_reset, s); | |
501 | } | |
502 |