UrForth: some microoptimisations
[urasm.git] / src / libfdc / emu_fdc.c
blob7c6a742861afbfa60bf2501da5ba6342b5b3dcde
1 /*
2 * WD1793/uPD765 emulator
3 * Copyright (c) 2009-..., SAM style
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is furnished
10 * to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
17 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
18 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
20 * THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 * changes by Ketmar // Invisible Vector
24 * Understanding is not required. Only obedience.
25 * git commit: 84b4cb3f749ff3cb954a202b3c65d7b53ee08273
27 #include "emu_fdc.h"
30 // ////////////////////////////////////////////////////////////////////////// //
31 struct DiskIF_t {
32 int type; // DIF_xxx
33 const DiskHW *hw;
34 FDC *fdc;
35 // currently, only bit 0 has a meaning
36 // bit0: set to turn on "turbo mode" (see `FDC_FAST` constant)
37 // WARNING! don't modify this directly, use `difSetTurbo()`
38 // this is because the code has to do some housekeeping
39 unsigned flags;
43 //==========================================================================
45 // fdcUpdateCRC16
47 // add byte to CRC
49 //==========================================================================
50 static void fdcUpdateCRC16 (FDC *fdc, uint8_t val) {
51 uint32_t tkk = fdc->crc;
52 tkk ^= val<<8;
53 for (unsigned i = 8; i--; ) {
54 if ((tkk *= 2)&0x10000) tkk ^= 0x1021;
56 fdc->crc = tkk&0xffff;
60 // ////////////////////////////////////////////////////////////////////////// //
61 #define LIBFDC_FDC_EMU_API static
63 /* flag for fdcFlag: "turbo mode" */
64 #define FDC_IS_TURBO_MODE (fdc->optionTurbo)
66 #include "emu_vg93.c"
67 #include "emu_upd765.c"
70 // ////////////////////////////////////////////////////////////////////////// //
71 struct DiskHW_t {
72 const int id; /* hardware type */
73 void (*reset) (DiskIF *dif);
74 int (*in) (DiskIF *dif, int port);
75 int (*out) (DiskIF *dif, int port, int val);
76 void (*sync) (DiskIF *dif, int ns);
80 //**************************************************************************
82 // dummy (none)
84 //**************************************************************************
85 static void dumReset (DiskIF* dif) {}
86 static int dumIn (DiskIF *dif, int port) { return -1; }
87 static int dumOut (DiskIF *dif, int port, int val) { return -1; }
88 static void dumSync (DiskIF *dif, int ns) {}
92 //**************************************************************************
94 // common
96 //**************************************************************************
98 //==========================================================================
100 // fdcSync
102 //==========================================================================
103 static void fdcSync (FDC *fdc, int ns) {
104 if (fdc->plan == NULL) return; // fdc does nothing
105 //fprintf(stderr, "fdcSync:000: wait=%d; tns=%d; ns=%d; plan=%p\n", fdc->wait, fdc->tns, ns, fdc->plan);
106 fdc->wait -= ns;
107 fdc->tns += ns;
108 //fprintf(stderr, "fdcSync:001: wait=%d; tns=%d; ns=%d; plan=%p\n", fdc->wait, fdc->tns, ns, fdc->plan);
109 while (fdc->wait < 0 && fdc->plan != NULL) {
110 //fprintf(stderr, " fdcSync:002: wait=%d; tns=%d; ns=%d; plan=%p\n", fdc->wait, fdc->tns, ns, fdc->plan);
111 if (fdc->plan[fdc->pos] != NULL) {
112 fdc->plan[fdc->pos](fdc);
113 } else {
114 fdc->plan = NULL;
120 //==========================================================================
122 // dhwSync
124 //==========================================================================
125 static void dhwSync (DiskIF *dif, int ns) {
126 fdcSync(dif->fdc, ns);
131 //**************************************************************************
133 // BDI (VG93)
135 //**************************************************************************
137 //==========================================================================
139 // bdiGetPort
141 //==========================================================================
142 static int bdiGetPort (int port) {
143 int res = 0;
144 if ((port&0x9f) == 0x9f) {
145 // 1xxxxx11 : bdi system port
146 res = BDI_SYS;
147 } else {
148 // 0xxxxx11 : vg93 registers
149 switch (port&0xff) {
150 case 0x1f: res = FDC_COM; break; // 000xxx11
151 case 0x3f: res = FDC_TRK; break; // 001xxx11
152 case 0x5f: res = FDC_SEC; break; // 010xxx11
153 case 0x7f: res = FDC_DATA; break; // 011xxx11
156 return res;
160 //==========================================================================
162 // bdiIn
164 //==========================================================================
165 static int bdiIn (DiskIF *dif, int port) {
166 //const int origport = port;
167 port = bdiGetPort(port);
168 if (port == 0) return -1; /* not FDC */
169 int res;
170 if (port == BDI_SYS) {
171 res = ((dif->fdc->irq ? 0x80 : 0x00)|(dif->fdc->drq ? 0x40 : 0x00))&0xff;
172 } else {
173 res = vgRead(dif->fdc, port)&0xff;
175 //fprintf(stderr, "in BDI port #%04X (%02X); res=#%02X\n", (unsigned)origport, (unsigned)bdiGetPort(port), res&0xffU);
176 return res&0xffU;
180 //==========================================================================
182 // bdiOut
184 //==========================================================================
185 static int bdiOut (DiskIF *dif, int port, int val) {
186 //fprintf(stderr, "out BDI port #%04X (%02X) <- #%02X\n", (unsigned)port, (unsigned)bdiGetPort(port), val&0xffU);
187 port = bdiGetPort(port);
188 if (port == 0) return -1;
189 if (port == BDI_SYS) {
190 dif->fdc->sysreg = val&0xffU;
191 dif->fdc->flp = dif->fdc->flop[val&3]; // select floppy
192 vgSetMR(dif->fdc, (val&0x04 ? 1 : 0)); // master reset
193 dif->fdc->block = (val&0x08 ? 1 : 0);
194 dif->fdc->side = (val&0x10 ? 0 : 1); // side
195 dif->fdc->mfm = (val&0x40 ? 1 : 0);
196 } else {
197 vgWrite(dif->fdc, port, val);
199 return 0;
203 //==========================================================================
205 // bdiReset
207 //==========================================================================
208 static void bdiReset (DiskIF *dif) {
209 vgReset(dif->fdc);
210 dif->fdc->sysreg = 0x20|0x08; // MFM
211 bdiOut(dif, 0xff, 0x00);
215 //==========================================================================
217 // bdiSync
219 //==========================================================================
221 static void bdiSync (DiskIF *dif, int ns) {
222 fdcSync(dif->fdc, ns);
228 //**************************************************************************
230 // +3DOS (uPD765)
232 //**************************************************************************
234 //==========================================================================
236 // pdosGetPort
238 //==========================================================================
239 static int pdosGetPort (int p) {
240 int port = -1;
241 if ((p&0xf002) == 0x2000) port = 0; // A0 input of upd765
242 if ((p&0xf002) == 0x3000) port = 1; // 0:status(r), 1:data(rw)
243 return port;
247 //==========================================================================
249 // pdosIn
251 //==========================================================================
252 static int pdosIn (DiskIF *dif, int port) {
253 port = pdosGetPort(port);
254 if (port < 0) return -1;
255 //fprintf(stderr, "in %.4X\n",port);
256 return uRead(dif->fdc, port)&0xff;
260 //==========================================================================
262 // pdosOut
264 //==========================================================================
265 static int pdosOut (DiskIF *dif, int port, int val) {
266 port = pdosGetPort(port);
267 if (port < 0) return -1;
268 uWrite(dif->fdc, port, val);
269 return 0;
273 //==========================================================================
275 // pdosReset
277 //==========================================================================
278 static void pdosReset (DiskIF *dif) {
279 uReset(dif->fdc);
284 //**************************************************************************
286 // common
288 //**************************************************************************
289 static const DiskHW dhwTab[4] = {
290 {DIF_NONE, &dumReset, &dumIn, &dumOut, &dumSync},
291 {DIF_BDI, &bdiReset, &bdiIn, &bdiOut, &dhwSync},
292 {DIF_P3DOS, &pdosReset, &pdosIn, &pdosOut, &dhwSync},
293 {DIF_END, NULL, NULL, NULL, NULL}
297 //==========================================================================
299 // findDHW
301 //==========================================================================
302 static const DiskHW *findDHW (int id) {
303 for (unsigned idx = 0; dhwTab[idx].id != DIF_END; ++idx) {
304 if (dhwTab[idx].id == id) return &dhwTab[idx];
306 return NULL;
310 //==========================================================================
312 // difSetHW
314 //==========================================================================
315 void difSetHW (DiskIF *dif, int type) {
316 if (!dif) return;
317 dif->hw = findDHW(type);
318 if (!dif->hw) {
319 //fprintf(stderr, "difSetHW: unknown interface type %d!\n", type);
320 dif->hw = findDHW(DIF_NONE);
322 dif->type = dif->hw->id;
326 //==========================================================================
328 // difGetHW
330 //==========================================================================
331 int difGetHW (const DiskIF *dif) {
332 return (dif ? dif->type : DIF_NONE);
336 //==========================================================================
338 // difGetFDC
340 //==========================================================================
341 FDC *difGetFDC (DiskIF *dif) {
342 return (dif ? dif->fdc : NULL);
346 //==========================================================================
348 // difGetFDC
350 //==========================================================================
351 Floppy *difGetFloppy (DiskIF *dif, int driveidx) {
352 if (!dif || !dif->fdc || driveidx < 0 || driveidx > 3) return NULL;
353 return dif->fdc->flop[driveidx];
357 //==========================================================================
359 // difGetFDC
361 //==========================================================================
362 Floppy *difGetCurrentFloppy (DiskIF *dif) {
363 if (!dif || !dif->fdc) return NULL;
364 return dif->fdc->flp;
368 //==========================================================================
370 // difSetTurbo
372 //==========================================================================
373 void difSetTurbo (DiskIF *dif, int v) {
374 if (!dif) return;
375 v = (v ? FDC_FAST : 0);
376 if ((dif->flags&FDC_FAST) != v) {
377 if (v) dif->flags |= FDC_FAST; else dif->flags &= ~((unsigned)FDC_FAST);
378 if (dif->fdc) dif->fdc->optionTurbo = (v ? 1 : 0);
383 //==========================================================================
385 // difGetTurbo
387 //==========================================================================
388 int difGetTurbo (const DiskIF *dif) {
389 return (dif && (dif->flags&FDC_FAST) ? 1 : 0);
393 //==========================================================================
395 // difCreate
397 //==========================================================================
398 DiskIF *difCreate (int type) {
399 DiskIF *dif = malloc(sizeof(DiskIF));
400 memset(dif, 0, sizeof(DiskIF));
401 dif->fdc = malloc(sizeof(FDC));
402 memset(dif->fdc, 0x00, sizeof(FDC));
403 dif->fdc->wait = -1;
404 dif->fdc->plan = NULL;
405 dif->fdc->flop[0] = flpCreate(0);
406 dif->fdc->flop[1] = flpCreate(1);
407 dif->fdc->flop[2] = flpCreate(2);
408 dif->fdc->flop[3] = flpCreate(3);
409 dif->fdc->flp = dif->fdc->flop[0];
410 difSetHW(dif, type);
411 return dif;
415 //==========================================================================
417 // difDestroy
419 //==========================================================================
420 void difDestroy (DiskIF *dif) {
421 if (!dif) return;
422 flpDestroy(dif->fdc->flop[0]);
423 flpDestroy(dif->fdc->flop[1]);
424 flpDestroy(dif->fdc->flop[2]);
425 flpDestroy(dif->fdc->flop[3]);
426 dif->fdc->flp = NULL;
427 free(dif->fdc);
428 free(dif);
432 //==========================================================================
434 // difReset
436 //==========================================================================
437 void difReset (DiskIF *dif) {
438 if (!dif) return;
439 dif->hw->reset(dif);
443 //==========================================================================
445 // difCalcNSFromTStates
447 // tstates: tstates passed
448 // cpufreq: 3500000 for ZX Spectrum
450 //==========================================================================
451 int difCalcNSFromTStates (int tstates, int cpufreq) {
452 // 3500000 must be converted to 3.5
453 #if 0
454 const double nspt = ((double)tstates*(double)1e3)/((double)cpufreq/(double)1e6);
455 return (int)(nspt);
456 #else
457 // sadly, this can overflow, so use 64-bit integers
458 return (int)((int64_t)tstates*1000LL*1000000LL/(int64_t)cpufreq);
459 #endif
463 //==========================================================================
465 // difSync
467 // update disk interface (up to the given `ns`)
468 // call this in emulation loop
469 // `ns` is time passed from the last call (in nanoseconds)
470 // it can be calculated with `difCalcNSFromTStates()`
472 //==========================================================================
473 void difSync (DiskIF *dif, int ns) {
474 if (!dif || ns <= 0) return;
475 dif->hw->sync(dif, ns);
479 //==========================================================================
481 // difOut
483 //==========================================================================
484 int difOut (DiskIF *dif, int port, int val) {
485 if (!dif) return -1;
486 return dif->hw->out(dif, port, val);
490 //==========================================================================
492 // difIn
494 //==========================================================================
495 int difIn (DiskIF *dif, int port) {
496 if (!dif) return -1;
497 return dif->hw->in(dif, port);