Previous 199869 Revisions Next

r26091 Sunday 10th November, 2013 at 13:47:10 UTC by Michael Zapf
ti990: Moved into ti99 subdirectory. (nw)
[src/mess]mess.mak
[src/mess/drivers]ti990_10.c ti990_4.c
[src/mess/machine]990_dk.c 990_dk.h 990_hd.c 990_hd.h 990_tap.c 990_tap.h ti990.c ti990.h
[src/mess/machine/ti99]990_dk.c* 990_dk.h* 990_hd.c* 990_hd.h* 990_tap.c* 990_tap.h* ti990.c* ti990.h*

trunk/src/mess/mess.mak
r26090r26091
808808   $(MESS_MACHINE)/sed1200.o   \
809809   $(MESS_MACHINE)/serial.o    \
810810   $(MESS_MACHINE)/smartmed.o  \
811   $(MESS_MACHINE)/strata.o    \
811812   $(MESS_MACHINE)/smc92x4.o   \
812813   $(MESS_MACHINE)/sonydriv.o  \
813814   $(MESS_MACHINE)/teleprinter.o   \
r26090r26091
19321933   $(MESS_VIDEO)/t6a04.o       \
19331934   $(MESS_MACHINE)/ti85.o      \
19341935   $(MESS_DRIVERS)/ti89.o      \
1935   $(MESS_MACHINE)/990_hd.o    \
1936   $(MESS_MACHINE)/990_tap.o   \
1937   $(MESS_MACHINE)/ti990.o     \
1936   $(MESS_MACHINE)/ti99/990_dk.o    \
1937   $(MESS_MACHINE)/ti99/990_hd.o    \
1938   $(MESS_MACHINE)/ti99/990_tap.o   \
1939   $(MESS_MACHINE)/ti99/ti990.o     \
19381940   $(MESS_MACHINE)/ti99/datamux.o  \
19391941   $(MESS_MACHINE)/ti99/videowrp.o \
19401942   $(MESS_MACHINE)/ti99/grom.o \
r26090r26091
19621964   $(MESS_MACHINE)/ti99/genboard.o \
19631965   $(MESS_MACHINE)/ti99/memex.o    \
19641966   $(MESS_MACHINE)/ti99/horizon.o  \
1965   $(MESS_MACHINE)/strata.o    \
1966   $(MESS_MACHINE)/990_dk.o    \
19671967   $(MESS_DRIVERS)/ti990_4.o   \
19681968   $(MESS_DRIVERS)/ti99_4x.o   \
19691969   $(MESS_DRIVERS)/ti99_4p.o   \
19701970   $(MESS_DRIVERS)/ti99_8.o    \
19711971   $(MESS_DRIVERS)/geneve.o    \
19721972   $(MESS_DRIVERS)/tm990189.o  \
1973   $(MESS_DRIVERS)/ti990_10.o  \
19731974   $(MESS_VIDEO)/911_vdt.o     \
19741975   $(MESS_VIDEO)/733_asr.o     \
1975   $(MESS_DRIVERS)/ti990_10.o  \
19761976   $(MESS_DRIVERS)/ti99_2.o    \
19771977   $(MESS_VIDEO)/avigo.o       \
19781978   $(MESS_DRIVERS)/avigo.o     \
trunk/src/mess/drivers/ti990_4.c
r26090r26091
3737
3838#include "emu.h"
3939#include "cpu/tms9900/tms9900l.h"
40#include "machine/ti990.h"
40#include "machine/ti99/ti990.h"
4141#if VIDEO_911
4242#include "video/911_vdt.h"
4343#include "sound/beep.h"
r26090r26091
4545#include "video/733_asr.h"
4646#endif
4747#include "imagedev/flopdrv.h"
48#include "machine/990_dk.h"
48#include "machine/ti99/990_dk.h"
4949
5050
5151class ti990_4_state : public driver_device
trunk/src/mess/drivers/ti990_10.c
r26090r26091
7171
7272#include "cpu/tms9900/tms9900l.h"
7373#include "sound/beep.h"
74#include "machine/ti990.h"
75#include "machine/990_hd.h"
76#include "machine/990_tap.h"
74#include "machine/ti99/ti990.h"
75#include "machine/ti99/990_hd.h"
76#include "machine/ti99/990_tap.h"
7777#include "video/911_vdt.h"
7878
7979
trunk/src/mess/machine/990_tap.c
r26090r26091
1/*
2    990_tap.c: emulation of a generic ti990 tape controller, for use with
3    TILINE-based TI990 systems (TI990/10, /12, /12LR, /10A, Business system 300
4    and 300A).
5
6    This core will emulate the common feature set found in every tape controller.
7    Most controllers support additional features, but are still compatible with
8    the basic feature set.  I have a little documentation on two specific
9    tape controllers (MT3200 and WD800/WD800A), but I have not tried to emulate
10    controller-specific features.
11
12
13    Long description: see 2234398-9701 and 2306140-9701.
14
15
16    Raphael Nabet 2002
17*/
18/*
19    Image encoding:
20
21
22    2 bytes: record len - little-endian
23    2 bytes: always 0s (length MSBs?)
24    len bytes: data
25    2 bytes: record len - little-endian
26    2 bytes: always 0s (length MSBs?)
27
28    4 0s: EOF mark
29*/
30
31#include "emu.h"
32#include "990_tap.h"
33#include "devlegcy.h"
34
35
36static void update_interrupt(device_t *device);
37
38#define MAX_TAPE_UNIT 4
39
40struct tape_unit_t
41{
42   device_image_interface *img;        /* image descriptor */
43   unsigned int bot : 1;   /* TRUE if we are at the beginning of tape */
44   unsigned int eot : 1;   /* TRUE if we are at the end of tape */
45   unsigned int wp : 1;    /* TRUE if tape is write-protected */
46};
47
48struct tap_990_t
49{
50   UINT16 w[8];
51
52   const ti990_tpc_interface *intf;
53
54   tape_unit_t t[MAX_TAPE_UNIT];
55};
56
57struct ti990_tape_t
58{
59   int dummy;
60};
61
62enum
63{
64   w0_offline          = 0x8000,
65   w0_BOT              = 0x4000,
66   w0_EOR              = 0x2000,
67   w0_EOF              = 0x1000,
68   w0_EOT              = 0x0800,
69   w0_write_ring       = 0x0400,
70   w0_tape_rewinding   = 0x0200,
71   w0_command_timeout  = 0x0100,
72
73   w0_rewind_status    = 0x00f0,
74   w0_rewind_mask      = 0x000f,
75
76   w6_unit0_sel        = 0x8000,
77   w6_unit1_sel        = 0x4000,
78   w6_unit2_sel        = 0x2000,
79   w6_unit3_sel        = 0x1000,
80   w6_command          = 0x0f00,
81
82   w7_idle             = 0x8000,
83   w7_complete         = 0x4000,
84   w7_error            = 0x2000,
85   w7_int_enable       = 0x1000,
86   w7_PE_format        = 0x0200,
87   w7_abnormal_completion  = 0x0100,
88   w7_interface_parity_err = 0x0080,
89   w7_err_correction_enabled   = 0x0040,
90   w7_hard_error           = 0x0020,
91   w7_tiline_parity_err    = 0x0010,
92   w7_tiline_timing_err    = 0x0008,
93   w7_tiline_timeout_err   = 0x0004,
94   /*w7_format_error       = 0x0002,*/
95   w7_tape_error       = 0x0001
96};
97
98static const UINT16 w_mask[8] =
99{
100   0x000f,     /* Controllers should prevent overwriting of w0 status bits, and I know
101                that some controllers do so. */
102   0xffff,
103   0xffff,
104   0xffff,
105   0xffff,
106   0xffff,
107   0xffff,
108   0xf3ff      /* Don't overwrite reserved bits */
109};
110
111static int tape_get_id(device_t *image)
112{
113   int drive =0;
114   if (strcmp(image->tag(), ":tape0") == 0) drive = 0;
115   if (strcmp(image->tag(), ":tape1") == 0) drive = 1;
116   if (strcmp(image->tag(), ":tape2") == 0) drive = 2;
117   if (strcmp(image->tag(), ":tape3") == 0) drive = 3;
118   return drive;
119}
120
121/*****************************************************************************
122    INLINE FUNCTIONS
123*****************************************************************************/
124INLINE tap_990_t *get_safe_token(device_t *device)
125{
126   assert(device != NULL);
127   assert(device->type() == TI990_TAPE_CTRL);
128
129   return (tap_990_t *)downcast<tap_990_device *>(device)->token();
130}
131
132
133/*
134    Parse the tape select lines, and return the corresponding tape unit.
135    (-1 if none)
136*/
137static int cur_tape_unit(device_t *device)
138{
139   int reply;
140   tap_990_t *tpc = get_safe_token(device);
141
142   if (tpc->w[6] & w6_unit0_sel)
143      reply = 0;
144   else if (tpc->w[6] & w6_unit1_sel)
145      reply = 1;
146   else if (tpc->w[6] & w6_unit2_sel)
147      reply = 2;
148   else if (tpc->w[6] & w6_unit3_sel)
149      reply = 3;
150   else
151      reply = -1;
152
153   if (reply >= MAX_TAPE_UNIT)
154      reply = -1;
155
156   return reply;
157}
158
159/*
160    Update interrupt state
161*/
162static void update_interrupt(device_t *device)
163{
164   tap_990_t *tpc = get_safe_token(device);
165   if (tpc->intf->interrupt_callback)
166      (*tpc->intf->interrupt_callback)(device->machine(), (tpc->w[7] & w7_idle)
167                           && (((tpc->w[7] & w7_int_enable) && (tpc->w[7] & (w7_complete | w7_error)))
168                              || ((tpc->w[0] & ~(tpc->w[0] >> 4)) & w0_rewind_mask)));
169}
170
171/*
172    Handle the read binary forward command: read the next record on tape.
173*/
174static void cmd_read_binary_forward(device_t *device)
175{
176   UINT8 buffer[256];
177   int reclen;
178
179   int dma_address;
180   int char_count;
181   int read_offset;
182
183   int rec_count = 0;
184   int chunk_len;
185   int bytes_to_read;
186   int bytes_read;
187   int i;
188   tap_990_t *tpc = get_safe_token(device);
189   int tap_sel = cur_tape_unit(device);
190
191   if (tap_sel == -1)
192   {
193      /* No idea what to report... */
194      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
195      update_interrupt(device);
196      return;
197   }
198   else if (! tpc->t[tap_sel].img->exists())
199   {   /* offline */
200      tpc->w[0] |= w0_offline;
201      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
202      update_interrupt(device);
203      return;
204   }
205#if 0
206   else if (0)
207   {   /* rewind in progress */
208      tpc->w[0] |= 0x80 >> tap_sel;
209      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
210      update_interrupt(device);
211      return;
212   }
213#endif
214
215   tpc->t[tap_sel].bot = 0;
216
217   dma_address = ((((int) tpc->w[6]) << 16) | tpc->w[5]) & 0x1ffffe;
218   char_count = tpc->w[4];
219   read_offset = tpc->w[3];
220
221   bytes_read = tpc->t[tap_sel].img->fread(buffer, 4);
222   if (bytes_read != 4)
223   {
224      if (bytes_read == 0)
225      {   /* legitimate EOF */
226         tpc->t[tap_sel].eot = 1;
227         tpc->w[0] |= w0_EOT;    /* or should it be w0_command_timeout? */
228         tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
229         update_interrupt(device);
230         goto update_registers;
231      }
232      else
233      {   /* illegitimate EOF */
234         /* No idea what to report... */
235         /* eject tape to avoid catastrophes */
236         logerror("Tape error\n");
237         tpc->t[tap_sel].img->unload();
238         tpc->w[0] |= w0_offline;
239         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
240         update_interrupt(device);
241         goto update_registers;
242      }
243   }
244   reclen = (((int) buffer[1]) << 8) | buffer[0];
245   if (buffer[2] || buffer[3])
246   {   /* no idea what these bytes mean */
247      logerror("Tape error\n");
248      logerror("Tape format looks gooofy\n");
249      /* eject tape to avoid catastrophes */
250      tpc->t[tap_sel].img->unload();
251      tpc->w[0] |= w0_offline;
252      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
253      update_interrupt(device);
254      goto update_registers;
255   }
256
257   /* test for EOF mark */
258   if (reclen == 0)
259   {
260      logerror("read binary forward: found EOF, requested %d\n", char_count);
261      tpc->w[0] |= w0_EOF;
262      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
263      update_interrupt(device);
264      goto update_registers;
265   }
266
267   logerror("read binary forward: rec length %d, requested %d\n", reclen, char_count);
268
269   rec_count = reclen;
270
271   /* skip up to read_offset bytes */
272   chunk_len = (read_offset > rec_count) ? rec_count : read_offset;
273
274   if (tpc->t[tap_sel].img->fseek(chunk_len, SEEK_CUR))
275   {   /* eject tape */
276      logerror("Tape error\n");
277      tpc->t[tap_sel].img->unload();
278      tpc->w[0] |= w0_offline;
279      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
280      update_interrupt(device);
281      goto update_registers;
282   }
283
284   rec_count -= chunk_len;
285   read_offset -= chunk_len;
286   if (read_offset)
287   {
288      tpc->w[0] |= w0_EOR;
289      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
290      update_interrupt(device);
291      goto skip_trailer;
292   }
293
294   /* read up to char_count bytes */
295   chunk_len = (char_count > rec_count) ? rec_count : char_count;
296
297   for (; chunk_len>0; )
298   {
299      bytes_to_read = (chunk_len < sizeof(buffer)) ? chunk_len : sizeof(buffer);
300      bytes_read = tpc->t[tap_sel].img->fread(buffer, bytes_to_read);
301
302      if (bytes_read & 1)
303      {
304         buffer[bytes_read] = 0xff;
305      }
306
307      /* DMA */
308      for (i=0; i<bytes_read; i+=2)
309      {
310         device->machine().device("maincpu")->memory().space(AS_PROGRAM).write_word(dma_address, (((int) buffer[i]) << 8) | buffer[i+1]);
311         dma_address = (dma_address + 2) & 0x1ffffe;
312      }
313
314      rec_count -= bytes_read;
315      char_count -= bytes_read;
316      chunk_len -= bytes_read;
317
318      if (bytes_read != bytes_to_read)
319      {   /* eject tape */
320         logerror("Tape error\n");
321         tpc->t[tap_sel].img->unload();
322         tpc->w[0] |= w0_offline;
323         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
324         update_interrupt(device);
325         goto update_registers;
326      }
327   }
328
329   if (char_count)
330   {
331      tpc->w[0] |= w0_EOR;
332      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
333      update_interrupt(device);
334      goto skip_trailer;
335   }
336
337   if (rec_count)
338   {   /* skip end of record */
339      if (tpc->t[tap_sel].img->fseek(rec_count, SEEK_CUR))
340      {   /* eject tape */
341         logerror("Tape error\n");
342         tpc->t[tap_sel].img->unload();
343         tpc->w[0] |= w0_offline;
344         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
345         update_interrupt(device);
346         goto update_registers;
347      }
348   }
349
350skip_trailer:
351   if (tpc->t[tap_sel].img->fread(buffer, 4) != 4)
352   {   /* eject tape */
353      logerror("Tape error\n");
354      tpc->t[tap_sel].img->unload();
355      tpc->w[0] |= w0_offline;
356      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
357      update_interrupt(device);
358      goto update_registers;
359   }
360
361   if (reclen != ((((int) buffer[1]) << 8) | buffer[0]))
362   {   /* eject tape */
363      logerror("Tape error\n");
364      tpc->t[tap_sel].img->unload();
365      tpc->w[0] |= w0_offline;
366      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
367      update_interrupt(device);
368      goto update_registers;
369   }
370   if (buffer[2] || buffer[3])
371   {   /* no idea what these bytes mean */
372      logerror("Tape error\n");
373      logerror("Tape format looks gooofy\n");
374      /* eject tape to avoid catastrophes */
375      tpc->t[tap_sel].img->unload();
376      tpc->w[0] |= w0_offline;
377      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
378      update_interrupt(device);
379      goto update_registers;
380   }
381
382   if (! (tpc->w[7] & w7_error))
383   {
384      tpc->w[7] |= w7_idle | w7_complete;
385      update_interrupt(device);
386   }
387
388update_registers:
389   tpc->w[1] = rec_count & 0xffff;
390   tpc->w[2] = (rec_count >> 8) & 0xff;
391   tpc->w[3] = read_offset;
392   tpc->w[4] = char_count;
393   tpc->w[5] = (dma_address >> 1) & 0xffff;
394   tpc->w[6] = (tpc->w[6] & 0xffe0) | ((dma_address >> 17) & 0xf);
395}
396
397/*
398    Handle the record skip forward command: skip a specified number of records.
399*/
400static void cmd_record_skip_forward(device_t *device)
401{
402   UINT8 buffer[4];
403   int reclen;
404
405   int record_count;
406   int bytes_read;
407   tap_990_t *tpc = get_safe_token(device);
408   int tap_sel = cur_tape_unit(device);
409
410   if (tap_sel == -1)
411   {
412      /* No idea what to report... */
413      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
414      update_interrupt(device);
415      return;
416   }
417   else if (! tpc->t[tap_sel].img->exists())
418   {   /* offline */
419      tpc->w[0] |= w0_offline;
420      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
421      update_interrupt(device);
422      return;
423   }
424#if 0
425   else if (0)
426   {   /* rewind in progress */
427      tpc->w[0] |= 0x80 >> tap_sel;
428      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
429      update_interrupt(device);
430      return;
431   }
432#endif
433
434   record_count = tpc->w[4];
435
436   if (record_count)
437      tpc->t[tap_sel].bot = 0;
438
439   while (record_count > 0)
440   {
441      bytes_read = tpc->t[tap_sel].img->fread(buffer, 4);
442      if (bytes_read != 4)
443      {
444         if (bytes_read == 0)
445         {   /* legitimate EOF */
446            tpc->t[tap_sel].eot = 1;
447            tpc->w[0] |= w0_EOT;    /* or should it be w0_command_timeout? */
448            tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
449            update_interrupt(device);
450            goto update_registers;
451         }
452         else
453         {   /* illegitimate EOF */
454            /* No idea what to report... */
455            /* eject tape to avoid catastrophes */
456            tpc->t[tap_sel].img->unload();
457            tpc->w[0] |= w0_offline;
458            tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
459            update_interrupt(device);
460            goto update_registers;
461         }
462      }
463      reclen = (((int) buffer[1]) << 8) | buffer[0];
464      if (buffer[2] || buffer[3])
465      {   /* no idea what these bytes mean */
466         logerror("Tape format looks gooofy\n");
467         /* eject tape to avoid catastrophes */
468         tpc->t[tap_sel].img->unload();
469         tpc->w[0] |= w0_offline;
470         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
471         update_interrupt(device);
472         goto update_registers;
473      }
474
475      /* test for EOF mark */
476      if (reclen == 0)
477      {
478         logerror("record skip forward: found EOF\n");
479         tpc->w[0] |= w0_EOF;
480         tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
481         update_interrupt(device);
482         goto update_registers;
483      }
484
485      /* skip record data */
486      if (tpc->t[tap_sel].img->fseek(reclen, SEEK_CUR))
487      {   /* eject tape */
488         tpc->t[tap_sel].img->unload();
489         tpc->w[0] |= w0_offline;
490         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
491         update_interrupt(device);
492         goto update_registers;
493      }
494
495      if (tpc->t[tap_sel].img->fread(buffer, 4) != 4)
496      {   /* eject tape */
497         tpc->t[tap_sel].img->unload();
498         tpc->w[0] |= w0_offline;
499         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
500         update_interrupt(device);
501         goto update_registers;
502      }
503
504      if (reclen != ((((int) buffer[1]) << 8) | buffer[0]))
505      {   /* eject tape */
506         tpc->t[tap_sel].img->unload();
507         tpc->w[0] |= w0_offline;
508         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
509         update_interrupt(device);
510         goto update_registers;
511      }
512      if (buffer[2] || buffer[3])
513      {   /* no idea what these bytes mean */
514         logerror("Tape format looks gooofy\n");
515         /* eject tape to avoid catastrophes */
516         tpc->t[tap_sel].img->unload();
517         tpc->w[0] |= w0_offline;
518         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
519         update_interrupt(device);
520         goto update_registers;
521      }
522
523      record_count--;
524   }
525
526   tpc->w[7] |= w7_idle | w7_complete;
527   update_interrupt(device);
528
529update_registers:
530   tpc->w[4] = record_count;
531}
532
533/*
534    Handle the record skip reverse command: skip a specified number of records backwards.
535*/
536static void cmd_record_skip_reverse(device_t *device)
537{
538   UINT8 buffer[4];
539   int reclen;
540
541   int record_count;
542
543   int bytes_read;
544   tap_990_t *tpc = get_safe_token(device);
545   int tap_sel = cur_tape_unit(device);
546
547   if (tap_sel == -1)
548   {
549      /* No idea what to report... */
550      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
551      update_interrupt(device);
552      return;
553   }
554   else if (! tpc->t[tap_sel].img->exists())
555   {   /* offline */
556      tpc->w[0] |= w0_offline;
557      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
558      update_interrupt(device);
559      return;
560   }
561#if 0
562   else if (0)
563   {   /* rewind in progress */
564      tpc->w[0] |= 0x80 >> tap_sel;
565      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
566      update_interrupt(device);
567      return;
568   }
569#endif
570
571   record_count = tpc->w[4];
572
573   if (record_count)
574      tpc->t[tap_sel].eot = 0;
575
576   while (record_count > 0)
577   {
578      if (tpc->t[tap_sel].img->ftell() == 0)
579      {   /* bot */
580         tpc->t[tap_sel].bot = 1;
581         tpc->w[0] |= w0_BOT;
582         tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
583         update_interrupt(device);
584         goto update_registers;
585      }
586      if (tpc->t[tap_sel].img->fseek(-4, SEEK_CUR))
587      {   /* eject tape */
588         tpc->t[tap_sel].img->unload();
589         tpc->w[0] |= w0_offline;
590         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
591         update_interrupt(device);
592         goto update_registers;
593      }
594      bytes_read = tpc->t[tap_sel].img->fread(buffer, 4);
595      if (bytes_read != 4)
596      {
597         /* illegitimate EOF */
598         /* No idea what to report... */
599         /* eject tape to avoid catastrophes */
600         tpc->t[tap_sel].img->unload();
601         tpc->w[0] |= w0_offline;
602         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
603         update_interrupt(device);
604         goto update_registers;
605      }
606      reclen = (((int) buffer[1]) << 8) | buffer[0];
607      if (buffer[2] || buffer[3])
608      {   /* no idea what these bytes mean */
609         logerror("Tape format looks gooofy\n");
610         /* eject tape to avoid catastrophes */
611         tpc->t[tap_sel].img->unload();
612         tpc->w[0] |= w0_offline;
613         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
614         update_interrupt(device);
615         goto update_registers;
616      }
617
618      /* look for EOF mark */
619      if (reclen == 0)
620      {
621         logerror("record skip reverse: found EOF\n");
622         if (tpc->t[tap_sel].img->fseek(-4, SEEK_CUR))
623         {   /* eject tape */
624            tpc->t[tap_sel].img->unload();
625            tpc->w[0] |= w0_offline;
626            tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
627            update_interrupt(device);
628            goto update_registers;
629         }
630         tpc->w[0] |= w0_EOF;
631         tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
632         update_interrupt(device);
633         goto update_registers;
634      }
635
636      if (tpc->t[tap_sel].img->fseek(-reclen-8, SEEK_CUR))
637      {   /* eject tape */
638         tpc->t[tap_sel].img->unload();
639         tpc->w[0] |= w0_offline;
640         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
641         update_interrupt(device);
642         goto update_registers;
643      }
644
645      if (tpc->t[tap_sel].img->fread(buffer, 4) != 4)
646      {   /* eject tape */
647         tpc->t[tap_sel].img->unload();
648         tpc->w[0] |= w0_offline;
649         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
650         update_interrupt(device);
651         goto update_registers;
652      }
653      if (reclen != ((((int) buffer[1]) << 8) | buffer[0]))
654      {   /* eject tape */
655         tpc->t[tap_sel].img->unload();
656         tpc->w[0] |= w0_offline;
657         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
658         update_interrupt(device);
659         goto update_registers;
660      }
661      if (buffer[2] || buffer[3])
662      {   /* no idea what these bytes mean */
663         logerror("Tape format looks gooofy\n");
664         /* eject tape to avoid catastrophes */
665         tpc->t[tap_sel].img->unload();
666         tpc->w[0] |= w0_offline;
667         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
668         update_interrupt(device);
669         goto update_registers;
670      }
671
672      if (tpc->t[tap_sel].img->fseek(-4, SEEK_CUR))
673      {   /* eject tape */
674         tpc->t[tap_sel].img->unload();
675         tpc->w[0] |= w0_offline;
676         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
677         update_interrupt(device);
678         goto update_registers;
679      }
680
681      record_count--;
682   }
683
684   tpc->w[7] |= w7_idle | w7_complete;
685   update_interrupt(device);
686
687update_registers:
688   tpc->w[4] = record_count;
689}
690
691/*
692    Handle the rewind command: rewind to BOT.
693*/
694static void cmd_rewind(device_t *device)
695{
696   tap_990_t *tpc = get_safe_token(device);
697   int tap_sel = cur_tape_unit(device);
698
699   if (tap_sel == -1)
700   {
701      /* No idea what to report... */
702      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
703      update_interrupt(device);
704      return;
705   }
706   else if (! tpc->t[tap_sel].img->exists())
707   {   /* offline */
708      tpc->w[0] |= w0_offline;
709      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
710      update_interrupt(device);
711      return;
712   }
713#if 0
714   else if (0)
715   {   /* rewind in progress */
716      tpc->w[0] |= 0x80 >> tap_sel;
717      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
718      update_interrupt(device);
719      return;
720   }
721#endif
722
723   tpc->t[tap_sel].eot = 0;
724
725   if (tpc->t[tap_sel].img->fseek(0, SEEK_SET))
726   {   /* eject tape */
727      tpc->t[tap_sel].img->unload();
728      tpc->w[0] |= w0_offline;
729      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
730      update_interrupt(device);
731      return;
732   }
733   tpc->t[tap_sel].bot = 1;
734
735   tpc->w[7] |= w7_idle | w7_complete;
736   update_interrupt(device);
737}
738
739/*
740    Handle the rewind and offline command: disable the tape unit.
741*/
742static void cmd_rewind_and_offline(device_t *device)
743{
744   tap_990_t *tpc = get_safe_token(device);
745   int tap_sel = cur_tape_unit(device);
746
747   if (tap_sel == -1)
748   {
749      /* No idea what to report... */
750      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
751      update_interrupt(device);
752      return;
753   }
754   else if (! tpc->t[tap_sel].img->exists())
755   {   /* offline */
756      tpc->w[0] |= w0_offline;
757      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
758      update_interrupt(device);
759      return;
760   }
761#if 0
762   else if (0)
763   {   /* rewind in progress */
764      tpc->w[0] |= 0x80 >> tap_sel;
765      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
766      update_interrupt(device);
767      return;
768   }
769#endif
770
771   /* eject tape */
772   tpc->t[tap_sel].img->unload();
773
774   tpc->w[7] |= w7_idle | w7_complete;
775   update_interrupt(device);
776}
777
778/*
779    Handle the read transport status command: return the current tape status.
780*/
781static void read_transport_status(device_t *device)
782{
783   tap_990_t *tpc = get_safe_token(device);
784   int tap_sel = cur_tape_unit(device);
785
786   if (tap_sel == -1)
787   {
788      /* No idea what to report... */
789      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
790      update_interrupt(device);
791   }
792   else if (! tpc->t[tap_sel].img->exists())
793   {   /* offline */
794      tpc->w[0] |= w0_offline;
795      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
796      update_interrupt(device);
797   }
798#if 0
799   else if (0)
800   {   /* rewind in progress */
801      tpc->w[0] |= /*...*/;
802      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
803      update_interrupt(device);
804   }
805#endif
806   else
807   {   /* no particular error condition */
808      if (tpc->t[tap_sel].bot)
809         tpc->w[0] |= w0_BOT;
810      if (tpc->t[tap_sel].eot)
811         tpc->w[0] |= w0_EOT;
812      if (tpc->t[tap_sel].wp)
813         tpc->w[0] |= w0_write_ring;
814      tpc->w[7] |= w7_idle | w7_complete;
815      update_interrupt(device);
816   }
817}
818
819/*
820    Parse command code and execute the command.
821*/
822static void execute_command(device_t *device)
823{
824   /* hack */
825   tap_990_t *tpc = get_safe_token(device);
826   tpc->w[0] &= 0xff;
827
828   switch ((tpc->w[6] & w6_command) >> 8)
829   {
830   case 0x00:
831   case 0x0C:
832   case 0x0E:
833      /* NOP */
834      logerror("NOP\n");
835      tpc->w[7] |= w7_idle | w7_complete;
836      update_interrupt(device);
837      break;
838   case 0x01:
839      /* buffer sync: means nothing under emulation */
840      logerror("buffer sync\n");
841      tpc->w[7] |= w7_idle | w7_complete;
842      update_interrupt(device);
843      break;
844   case 0x02:
845      /* write EOF - not emulated */
846      logerror("write EOF\n");
847      /* ... */
848      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
849      update_interrupt(device);
850      break;
851   case 0x03:
852      /* record skip reverse - not fully tested */
853      logerror("record skip reverse\n");
854      cmd_record_skip_reverse(device);
855      break;
856   case 0x04:
857      /* read binary forward */
858      logerror("read binary forward\n");
859      cmd_read_binary_forward(device);
860      break;
861   case 0x05:
862      /* record skip forward - not tested */
863      logerror("record skip forward\n");
864      cmd_record_skip_forward(device);
865      break;
866   case 0x06:
867      /* write binary forward - not emulated */
868      logerror("write binary forward\n");
869      /* ... */
870      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
871      update_interrupt(device);
872      break;
873   case 0x07:
874      /* erase - not emulated */
875      logerror("erase\n");
876      /* ... */
877      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
878      update_interrupt(device);
879      break;
880   case 0x08:
881   case 0x09:
882      /* read transport status */
883      logerror("read transport status\n");
884      read_transport_status(device);
885      break;
886   case 0x0A:
887      /* rewind - not tested */
888      logerror("rewind\n");
889      cmd_rewind(device);
890      break;
891   case 0x0B:
892      /* rewind and offline - not tested */
893      logerror("rewind and offline\n");
894      cmd_rewind_and_offline(device);
895      break;
896   case 0x0F:
897      /* extended control and status - not emulated */
898      logerror("extended control and status\n");
899      /* ... */
900      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
901      update_interrupt(device);
902      break;
903   }
904}
905
906
907/*
908    Read one register in TPCS space
909*/
910READ16_DEVICE_HANDLER(ti990_tpc_r)
911{
912   tap_990_t *tpc = get_safe_token(device);
913   if (offset < 8)
914      return tpc->w[offset];
915   else
916      return 0;
917}
918
919/*
920    Write one register in TPCS space.  Execute command if w7_idle is cleared.
921*/
922WRITE16_DEVICE_HANDLER(ti990_tpc_w)
923{
924   tap_990_t *tpc = get_safe_token(device);
925   if (offset < 8)
926   {
927      /* write protect if a command is in progress */
928      if (tpc->w[7] & w7_idle)
929      {
930         UINT16 old_data = tpc->w[offset];
931
932         /* Only write writable bits AND honor byte accesses (ha!) */
933         tpc->w[offset] = (tpc->w[offset] & ((~w_mask[offset]) | mem_mask)) | (data & w_mask[offset] & ~mem_mask);
934
935         if ((offset == 0) || (offset == 7))
936            update_interrupt(device);
937
938         if ((offset == 7) && (old_data & w7_idle) && ! (data & w7_idle))
939         {   /* idle has been cleared: start command execution */
940            execute_command(device);
941         }
942      }
943   }
944}
945
946class ti990_tape_image_device : public device_t,
947                           public device_image_interface
948{
949public:
950   // construction/destruction
951   ti990_tape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
952
953   // image-level overrides
954   virtual iodevice_t image_type() const { return IO_MAGTAPE; }
955
956   virtual bool is_readable()  const { return 1; }
957   virtual bool is_writeable() const { return 1; }
958   virtual bool is_creatable() const { return 1; }
959   virtual bool must_be_loaded() const { return 0; }
960   virtual bool is_reset_on_load() const { return 0; }
961   virtual const char *image_interface() const { return NULL; }
962   virtual const char *file_extensions() const { return "tap"; }
963   virtual const option_guide *create_option_guide() const { return NULL; }
964
965   virtual bool call_load();
966   virtual void call_unload();
967protected:
968   // device-level overrides
969   virtual void device_config_complete();
970   virtual void device_start();
971};
972
973const device_type TI990_TAPE = &device_creator<ti990_tape_image_device>;
974
975ti990_tape_image_device::ti990_tape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
976   : device_t(mconfig, TI990_TAPE, "TI990 Magnetic Tape", tag, owner, clock, "ti990_tape_image", __FILE__),
977      device_image_interface(mconfig, *this)
978{
979}
980
981void ti990_tape_image_device::device_config_complete()
982{
983   update_names();
984}
985
986void ti990_tape_image_device::device_start()
987{
988   tape_unit_t *t;
989   tap_990_t *tpc = get_safe_token(owner());
990   int id = tape_get_id(this);
991
992   t = &tpc->t[id];
993   memset(t, 0, sizeof(*t));
994
995   t->img = this;
996   t->wp = 1;
997   t->bot = 0;
998   t->eot = 0;
999}
1000
1001/*
1002    Open a tape image
1003*/
1004bool ti990_tape_image_device::call_load()
1005{
1006   tape_unit_t *t;
1007   tap_990_t *tpc = get_safe_token(owner());
1008   int id = tape_get_id(this);
1009
1010   t = &tpc->t[id];
1011   memset(t, 0, sizeof(*t));
1012
1013   /* tell whether the image is writable */
1014   t->wp = is_readonly();
1015
1016   t->bot = 1;
1017
1018   return IMAGE_INIT_PASS;
1019}
1020
1021/*
1022    Close a tape image
1023*/
1024void ti990_tape_image_device::call_unload()
1025{
1026   tape_unit_t *t;
1027   tap_990_t *tpc = get_safe_token(owner());
1028   int id = tape_get_id(this);
1029
1030   t = &tpc->t[id];
1031   t->wp = 1;
1032   t->bot = 0;
1033   t->eot = 0;
1034}
1035
1036#define MCFG_TI990_TAPE_ADD(_tag)   \
1037   MCFG_DEVICE_ADD((_tag),  TI990_TAPE, 0)
1038
1039
1040static MACHINE_CONFIG_FRAGMENT( tap_990 )
1041   MCFG_TI990_TAPE_ADD("tape0")
1042   MCFG_TI990_TAPE_ADD("tape1")
1043   MCFG_TI990_TAPE_ADD("tape2")
1044   MCFG_TI990_TAPE_ADD("tape3")
1045MACHINE_CONFIG_END
1046
1047/*
1048    Init the tape controller core
1049*/
1050static DEVICE_START(tap_990)
1051{
1052   tap_990_t *tpc = get_safe_token(device);
1053   /* verify that we have an interface assigned */
1054   assert(device->static_config() != NULL);
1055
1056   /* copy interface pointer */
1057   tpc->intf = (const ti990_tpc_interface*)device->static_config();
1058
1059   memset(tpc->w, 0, sizeof(tpc->w));
1060   /* The PE bit is always set for the MT3200 (but not MT1600) */
1061   /* According to MT3200 manual, w7 bit #4 (reserved) is always set */
1062   tpc->w[7] = w7_idle /*| w7_PE_format*/ | 0x0800;
1063
1064   update_interrupt(device);
1065}
1066
1067
1068const device_type TI990_TAPE_CTRL = &device_creator<tap_990_device>;
1069
1070tap_990_device::tap_990_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
1071   : device_t(mconfig, TI990_TAPE_CTRL, "Generic TI990 Tape Controller", tag, owner, clock, "tap_990", __FILE__)
1072{
1073   m_token = global_alloc_clear(tap_990_t);
1074}
1075
1076//-------------------------------------------------
1077//  device_config_complete - perform any
1078//  operations now that the configuration is
1079//  complete
1080//-------------------------------------------------
1081
1082void tap_990_device::device_config_complete()
1083{
1084}
1085
1086//-------------------------------------------------
1087//  device_start - device-specific startup
1088//-------------------------------------------------
1089
1090void tap_990_device::device_start()
1091{
1092   DEVICE_START_NAME( tap_990 )(this);
1093}
1094
1095//-------------------------------------------------
1096//  device_mconfig_additions - return a pointer to
1097//  the device's machine fragment
1098//-------------------------------------------------
1099
1100machine_config_constructor tap_990_device::device_mconfig_additions() const
1101{
1102   return MACHINE_CONFIG_NAME( tap_990  );
1103}
trunk/src/mess/machine/990_tap.h
r26090r26091
1/*
2    990_tap.h: include file for 990_tap.c
3*/
4extern DECLARE_READ16_DEVICE_HANDLER(ti990_tpc_r);
5extern DECLARE_WRITE16_DEVICE_HANDLER(ti990_tpc_w);
6
7/***************************************************************************
8    TYPE DEFINITIONS
9***************************************************************************/
10
11struct ti990_tpc_interface
12{
13   void (*interrupt_callback)(running_machine &machine, int state);
14};
15/***************************************************************************
16    MACROS
17***************************************************************************/
18
19class tap_990_device : public device_t
20{
21public:
22   tap_990_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
23   ~tap_990_device() { global_free(m_token); }
24
25   // access to legacy token
26   void *token() const { assert(m_token != NULL); return m_token; }
27protected:
28   // device-level overrides
29   virtual void device_config_complete();
30   virtual void device_start();
31   virtual machine_config_constructor device_mconfig_additions() const;
32private:
33   // internal state
34   void *m_token;
35};
36
37extern const device_type TI990_TAPE_CTRL;
38
39
40#define MCFG_TI990_TAPE_CTRL_ADD(_tag, _intrf)  \
41   MCFG_DEVICE_ADD((_tag),  TI990_TAPE_CTRL, 0)\
42   MCFG_DEVICE_CONFIG(_intrf)
trunk/src/mess/machine/ti990.c
r26090r26091
1/*
2    machine/ti990.c
3
4    Emulation for a few generic aspects of TI990
5*/
6
7#include "emu.h"
8#include "ti990.h"
9
10/*
11    Interrupt priority encoder.  Actually part of the CPU board.
12*/
13static UINT16 intlines;
14
15void ti990_reset_int(void)
16{
17   intlines = 0;
18}
19
20void ti990_set_int_line(running_machine &machine, int line, int state)
21{
22   int level;
23
24
25   if (state)
26      intlines |= (1 << line);
27   else
28      intlines &= ~ (1 << line);
29
30   if (intlines)
31   {
32      for (level = 0; ! (intlines & (1 << level)); level++)
33         ;
34      machine.device("maincpu")->execute().set_input_line_and_vector(0, ASSERT_LINE, level);  /* interrupt it, baby */
35   }
36   else
37      machine.device("maincpu")->execute().set_input_line(0, CLEAR_LINE);
38}
39
40void ti990_set_int2(device_t *device, int state)
41{
42   ti990_set_int_line(device->machine(), 2, state);
43}
44
45void ti990_set_int3(running_machine &machine, int state)
46{
47   ti990_set_int_line(machine, 3, state);
48}
49
50void ti990_set_int6(running_machine &machine, int state)
51{
52   ti990_set_int_line(machine, 6, state);
53}
54
55void ti990_set_int7(running_machine &machine, int state)
56{
57   ti990_set_int_line(machine, 7, state);
58}
59
60void ti990_set_int9(running_machine &machine, int state)
61{
62   ti990_set_int_line(machine, 9, state);
63}
64
65void ti990_set_int10(running_machine &machine, int state)
66{
67   ti990_set_int_line(machine, 10, state);
68}
69
70void ti990_set_int13(running_machine &machine, int state)
71{
72   ti990_set_int_line(machine, 13, state);
73}
74
75/*
76    hold and debounce load line (emulation is inaccurate)
77*/
78
79static TIMER_CALLBACK(clear_load)
80{
81   machine.device("maincpu")->execute().set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
82}
83
84void ti990_hold_load(running_machine &machine)
85{
86   machine.device("maincpu")->execute().set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
87   machine.scheduler().timer_set(attotime::from_msec(100), FUNC(clear_load));
88}
89
90/*
91    line interrupt
92*/
93
94/* ckon_state: 1 if line clock active (RTCLR flip-flop on TI990/10 schematics -
95SMI sheet 4) */
96static char ckon_state;
97
98void ti990_line_interrupt(running_machine &machine)
99{
100   if (ckon_state)
101      ti990_set_int_line(machine, 5, 1);
102}
103
104void ti990_ckon_ckof_callback(device_t *device, int state)
105{
106   ckon_state = state;
107   if (! ckon_state)
108      ti990_set_int_line(device->machine(), 5, 0);
109}
110
111
112
113/*
114    Control panel emulation
115
116    three panel types
117    * operator panel
118    * programmer panel
119    * MDU (external unit connected instead of the control panel, as seen in
120      945401-9701 p. 2-5 though 2-15)
121
122    Operator panel:
123    * Power led
124    * Fault led
125    * Off/On/Load switch
126
127    Programmer panel:
128    * 16 status light, 32 switches, IDLE, RUN leds
129    * interface to a low-level debugger in ROMs
130
131    * MDU:
132    * includes a programmer panel, a tape unit, and a few parts
133      (diagnostic tape, diagnostic ROMs, etc.)
134
135    CRU output:
136    0-7: lights 0-7
137    8: increment scan
138    9: clear scan (according to 990 handbook)
139    A: run light (additionally sets all data LEDs to 1s, the scan count to 0b10 and enables the HALT/SIE switch)
140    B: fault light
141    C: Memory Error Interrupt clear
142    D: Start panel timer
143    E: Set SIE function (interrupt after 2 instructions are executed)
144    F: flag (according to 990 handbook)
145
146    input :
147    0-7: switches 0-7 (or data from MDU tape)
148    8: scan count bit 1
149    9: scan count bit 0
150    A: timer active
151    B: programmer panel not present or locked
152    C: char in MDU tape unit buffer?
153    D: unused?
154    E: if 0, MDU unit present
155    F: flag (according to 990 handbook)
156*/
157
158   READ8_HANDLER ( ti990_panel_read )
159{
160   if (offset == 1)
161      return 0x48;
162
163   return 0;
164}
165
166WRITE8_HANDLER ( ti990_panel_write )
167{
168}
169
170
171/*
172    CPU board soft reset (RSET instruction)
173*/
174void ti990_cpuboard_reset(void)
175{
176   ckon_state = 0;
177}
trunk/src/mess/machine/ti990.h
r26090r26091
1/*
2    ti990.h: header file for ti990.c
3*/
4
5void ti990_reset_int(void);
6void ti990_set_int_line(running_machine &machine, int line, int state);
7void ti990_set_int2(device_t *device, int state);
8void ti990_set_int3(running_machine &machine, int state);
9void ti990_set_int6(running_machine &machine, int state);
10void ti990_set_int7(running_machine &machine, int state);
11void ti990_set_int9(running_machine &machine, int state);
12void ti990_set_int10(running_machine &machine, int state);
13void ti990_set_int13(running_machine &machine, int state);
14
15void ti990_hold_load(running_machine &machine);
16
17   DECLARE_READ8_HANDLER ( ti990_panel_read );
18DECLARE_WRITE8_HANDLER ( ti990_panel_write );
19
20void ti990_line_interrupt(running_machine &machine);
21void ti990_ckon_ckof_callback(device_t *device, int state);
22
23void ti990_cpuboard_reset(void);
trunk/src/mess/machine/990_hd.c
r26090r26091
1/*
2    990_hd.c: emulation of a generic ti990 hard disk controller, for use with
3    TILINE-based TI990 systems (TI990/10, /12, /12LR, /10A, Business system 300
4    and 300A).
5
6    This core will emulate the common feature set found in every disk controller.
7    Most controllers support additional features, but are still compatible with
8    the basic feature set.  I have a little documentation on two specific
9    disk controllers (WD900 and WD800/WD800A), but I have not tried to emulate
10    controller-specific features.
11
12
13    Long description: see 2234398-9701 and 2306140-9701.
14
15
16    Raphael Nabet 2002-2003
17*/
18
19#include "emu.h"
20
21#include "990_hd.h"
22
23#include "harddisk.h"
24#include "imagedev/harddriv.h"
25
26static void update_interrupt(running_machine &machine);
27
28/* max disk units per controller: 4 is the protocol limit, but it may be
29overriden if more than one controller is used */
30#define MAX_DISK_UNIT 4
31
32/* Max sector length is bytes.  Generally 256, except for a few older disk
33units which use 288-byte-long sectors, and SCSI units which generally use
34standard 512-byte-long sectors. */
35/* I chose a limit of 512.  No need to use more until someone writes CD-ROMs
36for TI990. */
37#define MAX_SECTOR_SIZE 512
38
39/* Description of custom format */
40/* We can use MAME's harddisk.c image format instead. */
41
42/* machine-independent big-endian 32-bit integer */
43struct UINT32BE
44{
45   UINT8 bytes[4];
46};
47
48INLINE UINT32 get_UINT32BE(UINT32BE word)
49{
50   return (word.bytes[0] << 24) | (word.bytes[1] << 16) | (word.bytes[2] << 8) | word.bytes[3];
51}
52
53#ifdef UNUSED_FUNCTION
54INLINE void set_UINT32BE(UINT32BE *word, UINT32 data)
55{
56   word->bytes[0] = (data >> 24) & 0xff;
57   word->bytes[1] = (data >> 16) & 0xff;
58   word->bytes[2] = (data >> 8) & 0xff;
59   word->bytes[3] = data & 0xff;
60}
61#endif
62
63/* disk image header */
64struct disk_image_header
65{
66   UINT32BE cylinders;         /* number of cylinders on hard disk (big-endian) */
67   UINT32BE heads;             /* number of heads on hard disk (big-endian) */
68   UINT32BE sectors_per_track; /* number of sectors per track on hard disk (big-endian) */
69   UINT32BE bytes_per_sector;  /* number of bytes of data per sector on hard disk (big-endian) */
70};
71
72enum
73{
74   header_len = sizeof(disk_image_header)
75};
76
77enum format_t
78{
79   format_mame,
80   format_old
81};
82
83/* disk drive unit descriptor */
84struct hd_unit_t
85{
86   device_image_interface *img;                        /* image descriptor */
87   format_t format;
88   hard_disk_file *hd_handle;      /* mame hard disk descriptor - only if format == format_mame */
89   unsigned int wp : 1;                    /* TRUE if disk is write-protected */
90   unsigned int unsafe : 1;                /* TRUE when a disk has just been connected */
91
92   /* disk geometry */
93   unsigned int cylinders, heads, sectors_per_track, bytes_per_sector;
94};
95
96/* disk controller */
97struct hdc_t
98{
99   UINT16 w[8];
100
101   void (*interrupt_callback)(running_machine &machine, int state);
102
103   hd_unit_t d[MAX_DISK_UNIT];
104};
105
106/* masks for individual bits controller registers */
107enum
108{
109   w0_offline          = 0x8000,
110   w0_not_ready        = 0x4000,
111   w0_write_protect    = 0x2000,
112   w0_unsafe           = 0x1000,
113   w0_end_of_cylinder  = 0x0800,
114   w0_seek_incomplete  = 0x0400,
115   /*w0_offset_active  = 0x0200,*/
116   w0_pack_change      = 0x0100,
117
118   w0_attn_lines       = 0x00f0,
119   w0_attn_mask        = 0x000f,
120
121   w1_extended_command = 0xc000,
122   /*w1_strobe_early       = 0x2000,
123   w1_strobe_late      = 0x1000,*/
124   w1_transfer_inhibit = 0x0800,
125   w1_command          = 0x0700,
126   w1_offset           = 0x0080,
127   w1_offset_forward   = 0x0040,
128   w1_head_address     = 0x003f,
129
130   w6_unit0_sel        = 0x0800,
131   w6_unit1_sel        = 0x0400,
132   w6_unit2_sel        = 0x0200,
133   w6_unit3_sel        = 0x0100,
134
135   w7_idle             = 0x8000,
136   w7_complete         = 0x4000,
137   w7_error            = 0x2000,
138   w7_int_enable       = 0x1000,
139   /*w7_lock_out           = 0x0800,*/
140   w7_retry            = 0x0400,
141   w7_ecc              = 0x0200,
142   w7_abnormal_completion  = 0x0100,
143   w7_memory_error         = 0x0080,
144   w7_data_error           = 0x0040,
145   w7_tiline_timeout_err   = 0x0020,
146   w7_header_err           = 0x0010,
147   w7_rate_err             = 0x0008,
148   w7_command_time_out_err = 0x0004,
149   w7_search_err           = 0x0002,
150   w7_unit_err             = 0x0001
151};
152
153/* masks for computer-controlled bit in each controller register  */
154static const UINT16 w_mask[8] =
155{
156   0x000f,     /* Controllers should prevent overwriting of w0 status bits, and I know
157                that some controllers do so. */
158   0xffff,
159   0xffff,
160   0xffff,
161   0xffff,
162   0xffff,
163   0xffff,
164   0xf7ff      /* Don't overwrite reserved bits */
165};
166
167static hdc_t hdc;
168
169
170static int get_id_from_device( device_t *device )
171{
172   int id = -1;
173
174   if ( ! strcmp( ":harddisk1", device->tag() ) )
175   {
176      id = 0;
177   }
178   else if ( ! strcmp( ":harddisk2", device->tag() ) )
179   {
180      id = 1;
181   }
182   else if ( ! strcmp( ":harddisk3", device->tag() ) )
183   {
184      id = 2;
185   }
186   else if ( ! strcmp( ":harddisk4", device->tag() ) )
187   {
188      id = 3;
189   }
190   assert( id >= 0 );
191
192   return id;
193}
194
195
196/*
197    Initialize hard disk unit and open a hard disk image
198*/
199static DEVICE_IMAGE_LOAD_LEGACY( ti990_hd )
200{
201   int id = get_id_from_device( &image.device() );
202   hd_unit_t *d;
203   hard_disk_file  *hd_file;
204
205   d = &hdc.d[id];
206   d->img = &image;
207
208   hd_file = dynamic_cast<harddisk_image_device *>(&image)->get_hard_disk_file();
209
210   if ( hd_file )
211   {
212      const hard_disk_info *standard_header;
213
214      d->format = format_mame;
215      d->hd_handle = hd_file;
216
217      /* use standard hard disk image header. */
218      standard_header = hard_disk_get_info(d->hd_handle);
219
220      d->cylinders = standard_header->cylinders;
221      d->heads = standard_header->heads;
222      d->sectors_per_track = standard_header->sectors;
223      d->bytes_per_sector = standard_header->sectorbytes;
224   }
225   else
226   {
227      /* older, custom format */
228      disk_image_header custom_header;
229      int bytes_read;
230
231      /* set file descriptor */
232      d->format = format_old;
233      d->hd_handle = NULL;
234
235      /* use custom image header. */
236      /* to convert old header-less images to this format, insert a 16-byte
237      header as follow: 00 00 03 8f  00 00 00 05  00 00 00 21  00 00 01 00 */
238      d->img->fseek(0, SEEK_SET);
239      bytes_read = d->img->fread(&custom_header, sizeof(custom_header));
240      if (bytes_read != sizeof(custom_header))
241      {
242         d->format = format_mame;    /* don't care */
243         d->wp = 1;
244         d->unsafe = 1;
245         return IMAGE_INIT_FAIL;
246      }
247
248      d->cylinders = get_UINT32BE(custom_header.cylinders);
249      d->heads = get_UINT32BE(custom_header.heads);
250      d->sectors_per_track = get_UINT32BE(custom_header.sectors_per_track);
251      d->bytes_per_sector = get_UINT32BE(custom_header.bytes_per_sector);
252   }
253
254   if (d->bytes_per_sector > MAX_SECTOR_SIZE)
255   {
256      d->format = format_mame;
257      d->hd_handle = NULL;
258      d->wp = 1;
259      d->unsafe = 1;
260      return IMAGE_INIT_FAIL;
261   }
262
263   /* tell whether the image is writable */
264   d->wp = image.is_readonly();
265
266   d->unsafe = 1;
267   /* set attention line */
268   hdc.w[0] |= (0x80 >> id);
269
270   return IMAGE_INIT_PASS;
271}
272
273/*
274    close a hard disk image
275*/
276static DEVICE_IMAGE_UNLOAD_LEGACY( ti990_hd )
277{
278   int id = get_id_from_device( image );
279   hd_unit_t *d;
280
281   d = &hdc.d[id];
282
283   d->format = format_mame;    /* don't care */
284   d->hd_handle = NULL;
285   d->wp = 1;
286   d->unsafe = 1;
287
288   /* clear attention line */
289   hdc.w[0] &= ~ (0x80 >> id);
290}
291
292/*
293    Return true if a HD image has been loaded
294*/
295INLINE int is_unit_loaded(int unit)
296{
297   int reply = 0;
298
299   switch (hdc.d[unit].format)
300   {
301   case format_mame:
302      reply = (hdc.d[unit].hd_handle != NULL);
303      break;
304
305   case format_old:
306      reply = (hdc.d[unit].img->exists() ? 1 : 0);
307      break;
308   }
309
310   return reply;
311}
312
313/*
314    Init the hdc core
315*/
316MACHINE_START(ti990_hdc)
317{
318   int i;
319
320   /* initialize harddisk information */
321   /* attention lines will be set by DEVICE_IMAGE_LOD */
322   for (i=0; i<MAX_DISK_UNIT; i++)
323   {
324      hdc.d[i].format = format_mame;
325      hdc.d[i].hd_handle = NULL;
326      hdc.d[i].wp = 1;
327      hdc.d[i].unsafe = 1;
328   }
329}
330
331
332void ti990_hdc_init(running_machine &machine, void (*interrupt_callback)(running_machine &machine, int state))
333{
334   memset(hdc.w, 0, sizeof(hdc.w));
335   hdc.w[7] = w7_idle;
336
337   /* get references to harddisk devices */
338   hdc.d[0].img = dynamic_cast<device_image_interface *>(machine.device("harddisk1"));
339   hdc.d[1].img = dynamic_cast<device_image_interface *>(machine.device("harddisk2"));
340   hdc.d[2].img = dynamic_cast<device_image_interface *>(machine.device("harddisk3"));
341   hdc.d[3].img = dynamic_cast<device_image_interface *>(machine.device("harddisk4"));
342
343   hdc.interrupt_callback = interrupt_callback;
344
345   update_interrupt(machine);
346}
347
348
349/*
350    Parse the disk select lines, and return the corresponding tape unit.
351    (-1 if none)
352*/
353static int cur_disk_unit(void)
354{
355   int reply;
356
357
358   if (hdc.w[6] & w6_unit0_sel)
359      reply = 0;
360   else if (hdc.w[6] & w6_unit1_sel)
361      reply = 1;
362   else if (hdc.w[6] & w6_unit2_sel)
363      reply = 2;
364   else if (hdc.w[6] & w6_unit3_sel)
365      reply = 3;
366   else
367      reply = -1;
368
369   if (reply >= MAX_DISK_UNIT)
370      reply = -1;
371
372   return reply;
373}
374
375/*
376    Update interrupt state
377*/
378static void update_interrupt(running_machine &machine)
379{
380   if (hdc.interrupt_callback)
381      (*hdc.interrupt_callback)(machine, (hdc.w[7] & w7_idle)
382                           && (((hdc.w[7] & w7_int_enable) && (hdc.w[7] & (w7_complete | w7_error)))
383                              || ((hdc.w[0] & (hdc.w[0] >> 4)) & w0_attn_mask)));
384}
385
386/*
387    Check that a sector address is valid.
388
389    Terminate current command and return non-zero if the address is invalid.
390*/
391static int check_sector_address(running_machine &machine, int unit, unsigned int cylinder, unsigned int head, unsigned int sector)
392{
393   if ((cylinder > hdc.d[unit].cylinders) || (head > hdc.d[unit].heads) || (sector > hdc.d[unit].sectors_per_track))
394   {   /* invalid address */
395      if (cylinder > hdc.d[unit].cylinders)
396      {
397         hdc.w[0] |= w0_seek_incomplete;
398         hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
399      }
400      else if (head > hdc.d[unit].heads)
401      {
402         hdc.w[0] |= w0_end_of_cylinder;
403         hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
404      }
405      else if (sector > hdc.d[unit].sectors_per_track)
406         hdc.w[7] |= w7_idle | w7_error | w7_command_time_out_err;
407      update_interrupt(machine);
408      return 1;
409   }
410
411   return 0;
412}
413
414/*
415    Seek to sector whose address is given
416*/
417static int sector_to_lba(running_machine &machine, int unit, unsigned int cylinder, unsigned int head, unsigned int sector, unsigned int *lba)
418{
419   if (check_sector_address(machine, unit, cylinder, head, sector))
420      return 1;
421
422   * lba = (cylinder*hdc.d[unit].heads + head)*hdc.d[unit].sectors_per_track + sector;
423
424   return 0;
425}
426
427/*
428    Read one given sector
429*/
430static int read_sector(int unit, unsigned int lba, void *buffer, unsigned int bytes_to_read)
431{
432   unsigned long byte_position;
433   unsigned int bytes_read;
434
435   switch (hdc.d[unit].format)
436   {
437   case format_mame:
438      bytes_read = hdc.d[unit].bytes_per_sector * hard_disk_read(hdc.d[unit].hd_handle, lba, buffer);
439      if (bytes_read > bytes_to_read)
440         bytes_read = bytes_to_read;
441      break;
442
443   case format_old:
444      byte_position = lba*hdc.d[unit].bytes_per_sector + header_len;
445      hdc.d[unit].img->fseek(byte_position, SEEK_SET);
446      bytes_read = hdc.d[unit].img->fread(buffer, bytes_to_read);
447      break;
448
449   default:
450      bytes_read = 0;
451      break;
452   }
453
454   return bytes_read;
455}
456
457/*
458    Write one given sector
459*/
460static int write_sector(int unit, unsigned int lba, const void *buffer, unsigned int bytes_to_write)
461{
462   unsigned long byte_position;
463   unsigned int bytes_written;
464
465   switch (hdc.d[unit].format)
466   {
467   case format_mame:
468      bytes_written = hdc.d[unit].bytes_per_sector * hard_disk_write(hdc.d[unit].hd_handle, lba, buffer);
469      if (bytes_written > bytes_to_write)
470         bytes_written = bytes_to_write;
471      break;
472
473   case format_old:
474      byte_position = lba*hdc.d[unit].bytes_per_sector + header_len;
475      hdc.d[unit].img->fseek(byte_position, SEEK_SET);
476      bytes_written = hdc.d[unit].img->fwrite(buffer, bytes_to_write);
477      break;
478
479   default:
480      bytes_written = 0;
481      break;
482   }
483
484   return bytes_written;
485}
486
487/*
488    Handle the store registers command: read the drive geometry.
489*/
490static void store_registers(running_machine &machine)
491{
492   int dma_address;
493   int byte_count;
494
495   UINT16 buffer[3];
496   int i, real_word_count;
497
498   int dsk_sel = cur_disk_unit();
499
500
501   if (dsk_sel == -1)
502   {
503      /* No idea what to report... */
504      hdc.w[7] |= w7_idle | w7_error | w7_abnormal_completion;
505      update_interrupt(machine);
506      return;
507   }
508   else if (! is_unit_loaded(dsk_sel))
509   {   /* offline */
510      hdc.w[0] |= w0_offline | w0_not_ready;
511      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
512      update_interrupt(machine);
513      return;
514   }
515
516   hdc.d[dsk_sel].unsafe = 0;      /* I think */
517
518   dma_address = ((((int) hdc.w[6]) << 16) | hdc.w[5]) & 0x1ffffe;
519   byte_count = hdc.w[4] & 0xfffe;
520
521   /* formatted words per track */
522   buffer[0] = (hdc.d[dsk_sel].sectors_per_track*hdc.d[dsk_sel].bytes_per_sector) >> 1;
523   /* MSByte: sectors per track; LSByte: bytes of overhead per sector */
524   buffer[1] = (hdc.d[dsk_sel].sectors_per_track << 8) | 0;
525   /* bits 0-4: heads; bits 5-15: cylinders */
526   buffer[2] = (hdc.d[dsk_sel].heads << 11) | hdc.d[dsk_sel].cylinders;
527
528   real_word_count = byte_count >> 1;
529   if (real_word_count > 3)
530      real_word_count = 3;
531
532   /* DMA */
533   if (! (hdc.w[1] & w1_transfer_inhibit))
534      for (i=0; i<real_word_count; i++)
535      {
536         machine.device("maincpu")->memory().space(AS_PROGRAM).write_word(dma_address, buffer[i]);
537         dma_address = (dma_address + 2) & 0x1ffffe;
538      }
539
540   hdc.w[7] |= w7_idle | w7_complete;
541   update_interrupt(machine);
542}
543
544/*
545    Handle the write format command: format a complete track on disk.
546
547    The emulation just clears the track data in the disk image.
548*/
549static void write_format(running_machine &machine)
550{
551   unsigned int cylinder, head, sector;
552   unsigned int lba;
553
554   UINT8 buffer[MAX_SECTOR_SIZE];
555   int bytes_written;
556
557   int dsk_sel = cur_disk_unit();
558
559
560   if (dsk_sel == -1)
561   {
562      /* No idea what to report... */
563      hdc.w[7] |= w7_idle | w7_error | w7_abnormal_completion;
564      update_interrupt(machine);
565      return;
566   }
567   else if (! is_unit_loaded(dsk_sel))
568   {   /* offline */
569      hdc.w[0] |= w0_offline | w0_not_ready;
570      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
571      update_interrupt(machine);
572      return;
573   }
574   else if (hdc.d[dsk_sel].unsafe)
575   {   /* disk in unsafe condition */
576      hdc.w[0] |= w0_unsafe | w0_pack_change;
577      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
578      update_interrupt(machine);
579      return;
580   }
581   else if (hdc.d[dsk_sel].wp)
582   {   /* disk write-protected */
583      hdc.w[0] |= w0_write_protect;
584      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
585      update_interrupt(machine);
586      return;
587   }
588
589   cylinder = hdc.w[3];
590   head = hdc.w[1] & w1_head_address;
591
592   if (sector_to_lba(machine, dsk_sel, cylinder, head, 0, &lba))
593      return;
594
595   memset(buffer, 0, hdc.d[dsk_sel].bytes_per_sector);
596
597   for (sector=0; sector<hdc.d[dsk_sel].sectors_per_track; sector++)
598   {
599      bytes_written = write_sector(dsk_sel, lba, buffer, hdc.d[dsk_sel].bytes_per_sector);
600
601      if (bytes_written != hdc.d[dsk_sel].bytes_per_sector)
602      {
603         hdc.w[0] |= w0_offline | w0_not_ready;
604         hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
605         update_interrupt(machine);
606         return;
607      }
608
609      lba++;
610   }
611
612   hdc.w[7] |= w7_idle | w7_complete;
613   update_interrupt(machine);
614}
615
616/*
617    Handle the read data command: read a variable number of sectors from disk.
618*/
619static void read_data(running_machine &machine)
620{
621   int dma_address;
622   int byte_count;
623
624   unsigned int cylinder, head, sector;
625   unsigned int lba;
626
627   UINT8 buffer[MAX_SECTOR_SIZE];
628   int bytes_to_read;
629   int bytes_read;
630   int i;
631
632   int dsk_sel = cur_disk_unit();
633
634
635   if (dsk_sel == -1)
636   {
637      /* No idea what to report... */
638      hdc.w[7] |= w7_idle | w7_error | w7_abnormal_completion;
639      update_interrupt(machine);
640      return;
641   }
642   else if (! is_unit_loaded(dsk_sel))
643   {   /* offline */
644      hdc.w[0] |= w0_offline | w0_not_ready;
645      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
646      update_interrupt(machine);
647      return;
648   }
649   else if (hdc.d[dsk_sel].unsafe)
650   {   /* disk in unsafe condition */
651      hdc.w[0] |= w0_unsafe | w0_pack_change;
652      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
653      update_interrupt(machine);
654      return;
655   }
656
657   dma_address = ((((int) hdc.w[6]) << 16) | hdc.w[5]) & 0x1ffffe;
658   byte_count = hdc.w[4] & 0xfffe;
659
660   cylinder = hdc.w[3];
661   head = hdc.w[1] & w1_head_address;
662   sector = hdc.w[2] & 0xff;
663
664   if (sector_to_lba(machine, dsk_sel, cylinder, head, sector, &lba))
665      return;
666
667   while (byte_count)
668   {   /* read data sector per sector */
669      if (cylinder > hdc.d[dsk_sel].cylinders)
670      {
671         hdc.w[0] |= w0_seek_incomplete;
672         hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
673         update_interrupt(machine);
674         return;
675      }
676
677      bytes_to_read = (byte_count < hdc.d[dsk_sel].bytes_per_sector) ? byte_count : hdc.d[dsk_sel].bytes_per_sector;
678      bytes_read = read_sector(dsk_sel, lba, buffer, bytes_to_read);
679
680      if (bytes_read != bytes_to_read)
681      {   /* behave as if the controller could not found the sector ID mark */
682         hdc.w[7] |= w7_idle | w7_error | w7_command_time_out_err;
683         update_interrupt(machine);
684         return;
685      }
686
687      /* DMA */
688      if (! (hdc.w[1] & w1_transfer_inhibit))
689         for (i=0; i<bytes_read; i+=2)
690         {
691            machine.device("maincpu")->memory().space(AS_PROGRAM).write_word(dma_address, (((int) buffer[i]) << 8) | buffer[i+1]);
692            dma_address = (dma_address + 2) & 0x1ffffe;
693         }
694
695      byte_count -= bytes_read;
696
697      /* update sector address to point to next sector */
698      lba++;
699      sector++;
700      if (sector == hdc.d[dsk_sel].sectors_per_track)
701      {
702         sector = 0;
703         head++;
704         if (head == hdc.d[dsk_sel].heads)
705         {
706            head = 0;
707            cylinder++;
708         }
709      }
710   }
711
712   hdc.w[7] |= w7_idle | w7_complete;
713   update_interrupt(machine);
714}
715
716/*
717    Handle the write data command: write a variable number of sectors from disk.
718*/
719static void write_data(running_machine &machine)
720{
721   int dma_address;
722   int byte_count;
723
724   unsigned int cylinder, head, sector;
725   unsigned int lba;
726
727   UINT8 buffer[MAX_SECTOR_SIZE];
728   UINT16 word;
729   int bytes_written;
730   int i;
731
732   int dsk_sel = cur_disk_unit();
733
734
735   if (dsk_sel == -1)
736   {
737      /* No idea what to report... */
738      hdc.w[7] |= w7_idle | w7_error | w7_abnormal_completion;
739      update_interrupt(machine);
740      return;
741   }
742   else if (! is_unit_loaded(dsk_sel))
743   {   /* offline */
744      hdc.w[0] |= w0_offline | w0_not_ready;
745      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
746      update_interrupt(machine);
747      return;
748   }
749   else if (hdc.d[dsk_sel].unsafe)
750   {   /* disk in unsafe condition */
751      hdc.w[0] |= w0_unsafe | w0_pack_change;
752      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
753      update_interrupt(machine);
754      return;
755   }
756   else if (hdc.d[dsk_sel].wp)
757   {   /* disk write-protected */
758      hdc.w[0] |= w0_write_protect;
759      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
760      update_interrupt(machine);
761      return;
762   }
763
764   dma_address = ((((int) hdc.w[6]) << 16) | hdc.w[5]) & 0x1ffffe;
765   byte_count = hdc.w[4] & 0xfffe;
766
767   cylinder = hdc.w[3];
768   head = hdc.w[1] & w1_head_address;
769   sector = hdc.w[2] & 0xff;
770
771   if (sector_to_lba(machine, dsk_sel, cylinder, head, sector, &lba))
772      return;
773
774   while (byte_count > 0)
775   {   /* write data sector per sector */
776      if (cylinder > hdc.d[dsk_sel].cylinders)
777      {
778         hdc.w[0] |= w0_seek_incomplete;
779         hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
780         update_interrupt(machine);
781         return;
782      }
783
784      /* DMA */
785      for (i=0; (i<byte_count) && (i<hdc.d[dsk_sel].bytes_per_sector); i+=2)
786      {
787         word = machine.device("maincpu")->memory().space(AS_PROGRAM).read_word(dma_address);
788         buffer[i] = word >> 8;
789         buffer[i+1] = word & 0xff;
790
791         dma_address = (dma_address + 2) & 0x1ffffe;
792      }
793      /* fill with 0s if we did not reach sector end */
794      for (; i<hdc.d[dsk_sel].bytes_per_sector; i+=2)
795         buffer[i] = buffer[i+1] = 0;
796
797      bytes_written = write_sector(dsk_sel, lba, buffer, hdc.d[dsk_sel].bytes_per_sector);
798
799      if (bytes_written != hdc.d[dsk_sel].bytes_per_sector)
800      {
801         hdc.w[0] |= w0_offline | w0_not_ready;
802         hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
803         update_interrupt(machine);
804         return;
805      }
806
807      byte_count -= bytes_written;
808
809      /* update sector address to point to next sector */
810      lba++;
811      sector++;
812      if (sector == hdc.d[dsk_sel].sectors_per_track)
813      {
814         sector = 0;
815         head++;
816         if (head == hdc.d[dsk_sel].heads)
817         {
818            head = 0;
819            cylinder++;
820         }
821      }
822   }
823
824   hdc.w[7] |= w7_idle | w7_complete;
825   update_interrupt(machine);
826}
827
828/*
829    Handle the unformatted read command: read drive geometry information.
830*/
831static void unformatted_read(running_machine &machine)
832{
833   int dma_address;
834   int byte_count;
835
836   unsigned int cylinder, head, sector;
837
838   UINT16 buffer[3];
839   int i, real_word_count;
840
841   int dsk_sel = cur_disk_unit();
842
843
844   if (dsk_sel == -1)
845   {
846      /* No idea what to report... */
847      hdc.w[7] |= w7_idle | w7_error | w7_abnormal_completion;
848      update_interrupt(machine);
849      return;
850   }
851   else if (! is_unit_loaded(dsk_sel))
852   {   /* offline */
853      hdc.w[0] |= w0_offline | w0_not_ready;
854      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
855      update_interrupt(machine);
856      return;
857   }
858   else if (hdc.d[dsk_sel].unsafe)
859   {   /* disk in unsafe condition */
860      hdc.w[0] |= w0_unsafe | w0_pack_change;
861      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
862      update_interrupt(machine);
863      return;
864   }
865
866   dma_address = ((((int) hdc.w[6]) << 16) | hdc.w[5]) & 0x1ffffe;
867   byte_count = hdc.w[4] & 0xfffe;
868
869   cylinder = hdc.w[3];
870   head = hdc.w[1] & w1_head_address;
871   sector = hdc.w[2] & 0xff;
872
873   if (check_sector_address(machine, dsk_sel, cylinder, head, sector))
874      return;
875
876   dma_address = ((((int) hdc.w[6]) << 16) | hdc.w[5]) & 0x1ffffe;
877   byte_count = hdc.w[4] & 0xfffe;
878
879   /* bits 0-4: head address; bits 5-15: cylinder address */
880   buffer[0] = (head << 11) | cylinder;
881   /* MSByte: sectors per record (1); LSByte: sector address */
882   buffer[1] = (1 << 8) | sector;
883   /* formatted words per record */
884   buffer[2] = hdc.d[dsk_sel].bytes_per_sector >> 1;
885
886   real_word_count = byte_count >> 1;
887   if (real_word_count > 3)
888      real_word_count = 3;
889
890   /* DMA */
891   if (! (hdc.w[1] & w1_transfer_inhibit))
892      for (i=0; i<real_word_count; i++)
893      {
894         machine.device("maincpu")->memory().space(AS_PROGRAM).write_word(dma_address, buffer[i]);
895         dma_address = (dma_address + 2) & 0x1ffffe;
896      }
897
898   hdc.w[7] |= w7_idle | w7_complete;
899   update_interrupt(machine);
900}
901
902/*
903    Handle the restore command: return to track 0.
904*/
905static void restore(running_machine &machine)
906{
907   int dsk_sel = cur_disk_unit();
908
909
910   if (dsk_sel == -1)
911   {
912      /* No idea what to report... */
913      hdc.w[7] |= w7_idle | w7_error | w7_abnormal_completion;
914      update_interrupt(machine);
915      return;
916   }
917   else if (! is_unit_loaded(dsk_sel))
918   {   /* offline */
919      hdc.w[0] |= w0_offline | w0_not_ready;
920      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
921      update_interrupt(machine);
922      return;
923   }
924
925   hdc.d[dsk_sel].unsafe = 0;      /* I think */
926
927   /*if (seek_to_sector(dsk_sel, 0, 0, 0))
928       return;*/
929
930   hdc.w[7] |= w7_idle | w7_complete;
931   update_interrupt(machine);
932}
933
934/*
935    Parse command code and execute the command.
936*/
937static void execute_command(running_machine &machine)
938{
939   /* hack */
940   hdc.w[0] &= 0xff;
941
942   if (hdc.w[1] & w1_extended_command)
943      logerror("extended commands not supported\n");
944
945   switch (/*((hdc.w[1] & w1_extended_command) >> 11) |*/ ((hdc.w[1] & w1_command) >> 8))
946   {
947   case 0x00: //0b000:
948      /* store registers */
949      logerror("store registers\n");
950      store_registers(machine);
951      break;
952   case 0x01: //0b001:
953      /* write format */
954      logerror("write format\n");
955      write_format(machine);
956      break;
957   case 0x02: //0b010:
958      /* read data */
959      logerror("read data\n");
960      read_data(machine);
961      break;
962   case 0x03: //0b011:
963      /* write data */
964      logerror("write data\n");
965      write_data(machine);
966      break;
967   case 0x04: //0b100:
968      /* unformatted read */
969      logerror("unformatted read\n");
970      unformatted_read(machine);
971      break;
972   case 0x05: //0b101:
973      /* unformatted write */
974      logerror("unformatted write\n");
975      /* ... */
976      hdc.w[7] |= w7_idle | w7_error | w7_abnormal_completion;
977      update_interrupt(machine);
978      break;
979   case 0x06: //0b110:
980      /* seek */
981      logerror("seek\n");
982      /* This command can (almost) safely be ignored */
983      hdc.w[7] |= w7_idle | w7_complete;
984      update_interrupt(machine);
985      break;
986   case 0x07: //0b111:
987      /* restore */
988      logerror("restore\n");
989      restore(machine);
990      break;
991   }
992}
993
994/*
995    Read one register in TPCS space
996*/
997READ16_HANDLER(ti990_hdc_r)
998{
999   if (offset < 8)
1000      return hdc.w[offset];
1001   else
1002      return 0;
1003}
1004
1005/*
1006    Write one register in TPCS space.  Execute command if w7_idle is cleared.
1007*/
1008WRITE16_HANDLER(ti990_hdc_w)
1009{
1010   if (offset < 8)
1011   {
1012      /* write protect if a command is in progress */
1013      if (hdc.w[7] & w7_idle)
1014      {
1015         UINT16 old_data = hdc.w[offset];
1016
1017         /* Only write writable bits AND honor byte accesses (ha!) */
1018         hdc.w[offset] = (hdc.w[offset] & ((~w_mask[offset]) | mem_mask)) | (data & w_mask[offset] & ~mem_mask);
1019
1020         if ((offset == 0) || (offset == 7))
1021            update_interrupt(space.machine());
1022
1023         if ((offset == 7) && (old_data & w7_idle) && ! (data & w7_idle))
1024         {   /* idle has been cleared: start command execution */
1025            execute_command(space.machine());
1026         }
1027      }
1028   }
1029}
1030
1031
1032static const struct harddisk_interface ti990_harddisk_config =
1033{
1034   DEVICE_IMAGE_LOAD_NAME_LEGACY( ti990_hd ),
1035   DEVICE_IMAGE_UNLOAD_NAME_LEGACY( ti990_hd ),
1036   NULL,
1037   NULL
1038};
1039
1040MACHINE_CONFIG_FRAGMENT( ti990_hdc )
1041   MCFG_HARDDISK_CONFIG_ADD( "harddisk1", ti990_harddisk_config )
1042   MCFG_HARDDISK_CONFIG_ADD( "harddisk2", ti990_harddisk_config )
1043   MCFG_HARDDISK_CONFIG_ADD( "harddisk3", ti990_harddisk_config )
1044   MCFG_HARDDISK_CONFIG_ADD( "harddisk4", ti990_harddisk_config )
1045MACHINE_CONFIG_END
trunk/src/mess/machine/990_hd.h
r26090r26091
1/*
2    990_hd.h: include file for 990_hd.c
3*/
4#ifndef __990_HD_H_
5#define __990_HD_H_
6
7#include "imagedev/harddriv.h"
8
9MACHINE_START( ti990_hdc );
10
11void ti990_hdc_init(running_machine &machine, void (*interrupt_callback)(running_machine &machine, int state));
12
13DECLARE_READ16_HANDLER(ti990_hdc_r);
14DECLARE_WRITE16_HANDLER(ti990_hdc_w);
15
16MACHINE_CONFIG_EXTERN( ti990_hdc );
17
18#endif
trunk/src/mess/machine/990_dk.c
r26090r26091
1/*
2    990_dk.c: emulation of a TI FD800 'Diablo' floppy disk controller
3    controller, for use with any TI990 system (and possibly any system which
4    implements the CRU bus).
5
6    This floppy disk controller supports IBM-format 8" SSSD and DSSD floppies.
7
8    Raphael Nabet 2003
9*/
10
11#include "emu.h"
12
13#include "formats/basicdsk.h"
14#include "imagedev/flopdrv.h"
15#include "990_dk.h"
16
17#define MAX_FLOPPIES 4
18
19enum buf_mode_t {
20   bm_off, bm_read, bm_write
21};
22static struct
23{
24   running_machine *machine;
25   UINT16 recv_buf;
26   UINT16 stat_reg;
27   UINT16 xmit_buf;
28   UINT16 cmd_reg;
29
30   int interrupt_f_f;
31   void (*interrupt_callback)(running_machine &, int state);
32
33   UINT8 buf[128];
34   int buf_pos;
35   buf_mode_t buf_mode;
36   int unit;
37   int head;
38   int sector;
39   /*int non_seq_mode;*/
40   int ddam;
41
42   struct
43   {
44      device_image_interface *img;
45      int phys_cylinder;
46      int log_cylinder[2];
47      int seclen;
48   } drv[MAX_FLOPPIES];
49} fd800;
50
51/* status bits */
52enum
53{
54   status_OP_complete  = 1 << 0,
55   status_XFER_ready   = 1 << 1,
56   status_drv_not_ready= 1 << 2,
57   status_dat_chk_err  = 1 << 3,
58   status_seek_err     = 1 << 4,
59   status_invalid_cmd  = 1 << 5,
60   status_no_addr_mark = 1 << 6,
61   status_equ_chk_err  = 1 << 7,
62   status_ID_chk_err   = 1 << 8,
63   status_ID_not_found = 1 << 9,
64   status_ctlr_busy    = 1 << 10,
65   status_write_prot   = 1 << 11,
66   status_del_sector   = 1 << 12,
67   status_interrupt    = 1 << 15,
68
69   status_unit_shift   = 13
70};
71
72LEGACY_FLOPPY_OPTIONS_START(fd800)
73#if 1
74   /* SSSD 8" */
75   LEGACY_FLOPPY_OPTION(fd800, "dsk", "TI990 8\" SSSD disk image", basicdsk_identify_default, basicdsk_construct_default, NULL,
76      HEADS([1])
77      TRACKS([77])
78      SECTORS([26])
79      SECTOR_LENGTH([128])
80      FIRST_SECTOR_ID([1]))
81#elif 0
82   /* DSSD 8" */
83   LEGACY_FLOPPY_OPTION(fd800, "dsk", "TI990 8\" DSSD disk image", basicdsk_identify_default, basicdsk_construct_default, NULL,
84      HEADS([2])
85      TRACKS([77])
86      SECTORS([26])
87      SECTOR_LENGTH([128])
88      FIRST_SECTOR_ID([1]))
89#endif
90LEGACY_FLOPPY_OPTIONS_END
91
92static void fd800_field_interrupt(void)
93{
94   if (fd800.interrupt_callback)
95      (*fd800.interrupt_callback)(*fd800.machine, (fd800.stat_reg & status_interrupt) && ! fd800.interrupt_f_f);
96}
97
98static void fd800_unload_proc(device_image_interface &image)
99{
100   int unit = floppy_get_drive(&image.device());
101
102   fd800.drv[unit].log_cylinder[0] = fd800.drv[unit].log_cylinder[1] = -1;
103}
104
105void fd800_machine_init(running_machine &machine, void (*interrupt_callback)(running_machine &machine, int state))
106{
107   int i;
108
109   fd800.machine = &machine;
110   fd800.interrupt_callback = interrupt_callback;
111
112   fd800.stat_reg = 0;
113   fd800.interrupt_f_f = 1;
114
115   fd800.buf_pos = 0;
116   fd800.buf_mode = bm_off;
117
118   for (i=0; i<MAX_FLOPPIES; i++)
119   {
120      fd800.drv[i].img = dynamic_cast<device_image_interface *>(floppy_get_device(machine, i));
121      fd800.drv[i].phys_cylinder = -1;
122      fd800.drv[i].log_cylinder[0] = fd800.drv[i].log_cylinder[1] = -1;
123      fd800.drv[i].seclen = 64;
124      floppy_install_unload_proc(&fd800.drv[i].img->device(), fd800_unload_proc);
125   }
126
127   fd800_field_interrupt();
128}
129
130/*
131    Read the first id field that can be found on the floppy disk.
132
133    unit: floppy drive index
134    head: selected head
135    cylinder_id: cylinder ID read
136    sector_id: sector ID read
137
138    Return TRUE if an ID was found
139*/
140static int fd800_read_id(int unit, int head, int *cylinder_id, int *sector_id)
141{
142   /*UINT8 revolution_count;*/
143   chrn_id id;
144
145   /*revolution_count = 0;*/
146
147   /*while (revolution_count < 2)*/
148   /*{*/
149      if (floppy_drive_get_next_id(&fd800.drv[unit].img->device(), head, &id))
150      {
151         if (cylinder_id)
152            *cylinder_id = id.C;
153         if (sector_id)
154            *sector_id = id.R;
155         return TRUE;
156      }
157   /*}*/
158
159   return FALSE;
160}
161
162/*
163    Find a sector by id.
164
165    unit: floppy drive index
166    head: selected head
167    sector: sector ID to search
168    data_id: data ID to be used when calling sector read/write functions
169
170    Return TRUE if the given sector ID was found
171*/
172static int fd800_find_sector(int unit, int head, int sector, int *data_id)
173{
174   UINT8 revolution_count;
175   chrn_id id;
176
177   revolution_count = 0;
178
179   while (revolution_count < 2)
180   {
181      if (floppy_drive_get_next_id(&fd800.drv[unit].img->device(), head, &id))
182      {
183         /* compare id */
184         if ((id.R == sector) && (id.N == 0))
185         {
186            *data_id = id.data_id;
187            /* get ddam status */
188            /*w->ddam = id.flags & ID_FLAG_DELETED_DATA;*/
189            return TRUE;
190         }
191      }
192   }
193
194   return FALSE;
195}
196
197/*
198    Perform seek command
199
200    unit: floppy drive index
201    cylinder: track to seek for
202    head: head for which the seek is performed
203
204    Return FALSE if the seek was successful
205*/
206static int fd800_do_seek(int unit, int cylinder, int head)
207{
208   int retries;
209
210   if (cylinder > 76)
211   {
212      fd800.stat_reg |= status_invalid_cmd;
213      return TRUE;
214   }
215
216   if (!fd800.drv[unit].img->exists())
217   {
218      fd800.stat_reg |= status_drv_not_ready; /* right??? */
219      return TRUE;
220   }
221
222   if (fd800.drv[unit].log_cylinder[head] == -1)
223   {   /* current track ID is unknown: read it */
224      if (!fd800_read_id(unit, head, &fd800.drv[unit].log_cylinder[head], NULL))
225      {
226         fd800.stat_reg |= status_ID_not_found;
227         return TRUE;
228      }
229   }
230   /* exit if we are already at the requested track */
231   if (fd800.drv[unit].log_cylinder[head] == cylinder)
232   {
233      /*fd800.stat_reg |= status_OP_complete;*/
234      return FALSE;
235   }
236   for (retries=0; retries<10; retries++)
237   {   /* seek to requested track */
238      floppy_drive_seek(&fd800.drv[unit].img->device(), cylinder-fd800.drv[unit].log_cylinder[head]);
239      /* update physical track position */
240      if (fd800.drv[unit].phys_cylinder != -1)
241         fd800.drv[unit].phys_cylinder += cylinder-fd800.drv[unit].log_cylinder[head];
242      /* read new track ID */
243      if (!fd800_read_id(unit, head, &fd800.drv[unit].log_cylinder[head], NULL))
244      {
245         fd800.drv[unit].log_cylinder[head] = -1;
246         fd800.stat_reg |= status_ID_not_found;
247         return TRUE;
248      }
249      /* exit if we have reached the requested track */
250      if (fd800.drv[unit].log_cylinder[head] == cylinder)
251      {
252         /*fd800.stat_reg |= status_OP_complete;*/
253         return FALSE;
254      }
255   }
256   /* track not found */
257   fd800.stat_reg |= status_seek_err;
258   return TRUE;
259}
260
261/*
262    Perform restore command
263
264    unit: floppy drive index
265
266    Return FALSE if the restore was successful
267*/
268static int fd800_do_restore(int unit)
269{
270   int seek_count = 0;
271   int seek_complete;
272
273   if (!fd800.drv[unit].img->exists())
274   {
275      fd800.stat_reg |= status_drv_not_ready; /* right??? */
276      return TRUE;
277   }
278
279   /* limit iterations to 76 to prevent an endless loop if the disc is locked */
280   while (!(seek_complete = !floppy_tk00_r(&fd800.drv[unit].img->device())) && (seek_count < 76))
281   {
282      floppy_drive_seek(&fd800.drv[unit].img->device(), -1);
283      seek_count++;
284   }
285   if (! seek_complete)
286   {
287      fd800.drv[unit].phys_cylinder = -1;
288      fd800.stat_reg |= status_seek_err;
289   }
290   else
291   {
292      fd800.drv[unit].phys_cylinder = 0;
293      /*fd800.stat_reg |= status_OP_complete;*/
294   }
295
296   return ! seek_complete;
297}
298
299/*
300    Perform a read operation for one sector
301*/
302static void fd800_do_read(void)
303{
304   int data_id;
305
306   if ((fd800.sector == 0) || (fd800.sector > 26))
307   {
308      fd800.stat_reg |= status_invalid_cmd;
309      return;
310   }
311
312   if (!fd800_find_sector(fd800.unit, fd800.head, fd800.sector, &data_id))
313   {
314      fd800.stat_reg |= status_ID_not_found;
315      return;
316   }
317
318   floppy_drive_read_sector_data(&fd800.drv[fd800.unit].img->device(), fd800.head, data_id, fd800.buf, 128);
319   fd800.buf_pos = 0;
320   fd800.buf_mode = bm_read;
321   fd800.recv_buf = (fd800.buf[fd800.buf_pos<<1] << 8) | fd800.buf[(fd800.buf_pos<<1)+1];
322
323   fd800.stat_reg |= status_XFER_ready;
324   fd800.stat_reg |= status_OP_complete;   /* right??? */
325}
326
327/*
328    Perform a write operation for one sector
329*/
330static void fd800_do_write(void)
331{
332   int data_id;
333
334   if (fd800.drv[fd800.unit].seclen < 64)
335      /* fill with 0s */
336      memset(fd800.buf+(fd800.drv[fd800.unit].seclen<<1), 0, (64-fd800.drv[fd800.unit].seclen)<<1);
337
338   if (!fd800_find_sector(fd800.unit, fd800.head, fd800.sector, &data_id))
339   {
340      fd800.stat_reg |= status_ID_not_found;
341      return;
342   }
343
344   floppy_drive_write_sector_data(&fd800.drv[fd800.unit].img->device(), fd800.head, data_id, fd800.buf, 128, fd800.ddam);
345   fd800.buf_pos = 0;
346   fd800.buf_mode = bm_write;
347
348   fd800.stat_reg |= status_XFER_ready;
349   fd800.stat_reg |= status_OP_complete;   /* right??? */
350}
351
352/*
353    Execute a fdc command
354*/
355static void fd800_do_cmd(void)
356{
357   int unit;
358   int cylinder;
359   int head;
360   int seclen;
361   int sector;
362
363
364   if (fd800.buf_mode != bm_off)
365   {   /* All commands in the midst of read or write are interpreted as Stop */
366      unit = (fd800.cmd_reg >> 10) & 3;
367
368      /* reset status */
369      fd800.stat_reg = unit << status_unit_shift;
370
371      fd800.buf_pos = 0;
372      fd800.buf_mode = bm_off;
373
374      fd800.stat_reg |= status_OP_complete;
375
376      fd800.stat_reg |= status_interrupt;
377      fd800_field_interrupt();
378
379      return;
380   }
381
382   switch (fd800.cmd_reg >> 12)
383   {
384   case 0:     /* select
385                    bits 16-25: 0s
386                    bits 26-27: unit number (0-3) */
387      unit = (fd800.cmd_reg >> 10) & 3;
388
389      /* reset status */
390      fd800.stat_reg = unit << status_unit_shift;
391
392      if (!fd800.drv[unit].img->exists())
393         fd800.stat_reg |= status_drv_not_ready; /* right??? */
394      else if (fd800.drv[unit].img->is_readonly())
395         fd800.stat_reg |= status_write_prot;
396      else
397         fd800.stat_reg |= status_OP_complete;
398
399      fd800.stat_reg |= status_interrupt;
400      fd800_field_interrupt();
401      break;
402
403   case 1:     /* seek
404                    bits 16-22: cylinder number (0-76)
405                    bits 23-24: 0s
406                    bits 25: head number (1=upper)
407                    bits 26-27: unit number (0-3) */
408      unit = (fd800.cmd_reg >> 10) & 3;
409      head = (fd800.cmd_reg >> 9) & 1;
410      cylinder = fd800.cmd_reg & 0x7f;
411
412      /* reset status */
413      fd800.stat_reg = unit << status_unit_shift;
414
415      if (!fd800_do_seek(unit, cylinder, head))
416         fd800.stat_reg |= status_OP_complete;
417
418      fd800.stat_reg |= status_interrupt;
419      fd800_field_interrupt();
420      break;
421
422   case 2:     /* restore
423                    bits 16-25: 0s
424                    bits 26-27: unit number (0-3) */
425      unit = (fd800.cmd_reg >> 10) & 3;
426
427      /* reset status */
428      fd800.stat_reg = unit << status_unit_shift;
429
430      if (!fd800_do_restore(unit))
431         fd800.stat_reg |= status_OP_complete;
432
433      fd800.stat_reg |= status_interrupt;
434      fd800_field_interrupt();
435      break;
436
437   case 3:     /* sector length
438                    bits 16-22: sector word count (0-64)
439                    bits 23-25: 0s
440                    bits 26-27: unit number (0-3) */
441      unit = (fd800.cmd_reg >> 10) & 3;
442      seclen = fd800.cmd_reg & 0x7f;
443
444      /* reset status */
445      fd800.stat_reg = unit << status_unit_shift;
446
447      if ((seclen > 64) || (seclen == 0))
448      {
449         fd800.stat_reg |= status_invalid_cmd;
450      }
451      else
452      {
453         fd800.drv[unit].seclen = seclen;
454         fd800.stat_reg |= status_OP_complete;
455      }
456
457      fd800.stat_reg |= status_interrupt;
458      fd800_field_interrupt();
459      break;
460
461   case 4:     /* read
462                    bits 16-20: sector number (1-26)
463                    bits 21-23: 0s
464                    bit 24: no sequential sectoring (1=active)
465                    bit 25: head number (1=upper)
466                    bits 26-27: unit number (0-3) */
467      unit = (fd800.cmd_reg >> 10) & 3;
468      head = (fd800.cmd_reg >> 9) & 1;
469      /*non_seq_mode = (fd800.cmd_reg >> 8) & 1;*/
470      sector = fd800.cmd_reg & 0x1f;
471
472      fd800.unit = unit;
473      fd800.head = head;
474      fd800.sector = sector;
475      /*fd800.non_seq_mode = non_seq_mode;*/
476
477      /* reset status */
478      fd800.stat_reg = unit << status_unit_shift;
479
480      fd800_do_read();
481
482      fd800.stat_reg |= status_interrupt;
483      fd800_field_interrupt();
484      break;
485
486   case 5:     /* read ID
487                    bits 16-24: 0s
488                    bit 25: head number (1=upper)
489                    bits 26-27: unit number (0-3) */
490      unit = (fd800.cmd_reg >> 10) & 3;
491      head = (fd800.cmd_reg >> 9) & 1;
492
493      /* reset status */
494      fd800.stat_reg = unit << status_unit_shift;
495
496      if (!fd800_read_id(unit, head, &cylinder, &sector))
497      {
498         fd800.stat_reg |= status_ID_not_found;
499      }
500      else
501      {
502         fd800.recv_buf = (cylinder << 8) | sector;
503         fd800.stat_reg |= status_OP_complete;
504      }
505
506      fd800.stat_reg |= status_interrupt;
507      fd800_field_interrupt();
508      break;
509
510   case 6:     /* read unformatted
511                    bits 16-20: sector number (1-26)
512                    bits 21-24: 0s
513                    bit 25: head number (1=upper)
514                    bits 26-27: unit number (0-3) */
515      /* ... */
516      break;
517
518   case 7:     /* write
519                    bits 16-20: sector number (1-26)
520                    bits 21-24: 0s
521                    bit 25: head number (1=upper)
522                    bits 26-27: unit number (0-3) */
523      unit = (fd800.cmd_reg >> 10) & 3;
524      head = (fd800.cmd_reg >> 9) & 1;
525      sector = fd800.cmd_reg & 0x1f;
526
527      /* reset status */
528      fd800.stat_reg = unit << status_unit_shift;
529
530      if ((fd800.sector == 0) || (fd800.sector > 26))
531      {
532         fd800.stat_reg |= status_invalid_cmd;
533      }
534      else
535      {
536         fd800.unit = unit;
537         fd800.head = head;
538         fd800.sector = sector;
539         fd800.ddam = 0;
540
541         fd800.buf_pos = 0;
542         fd800.buf_mode = bm_write;
543         fd800.stat_reg |= status_XFER_ready;
544         fd800.stat_reg |= status_OP_complete;   /* right??? */
545      }
546
547      fd800.stat_reg |= status_interrupt;
548      fd800_field_interrupt();
549      break;
550
551   case 8:     /* write delete
552                    bits 16-20: sector number (1-26)
553                    bits 21-24: 0s
554                    bit 25: head number (1=upper)
555                    bits 26-27: unit number (0-3) */
556      unit = (fd800.cmd_reg >> 10) & 3;
557      head = (fd800.cmd_reg >> 9) & 1;
558      sector = fd800.cmd_reg & 0x1f;
559
560      /* reset status */
561      fd800.stat_reg = unit << status_unit_shift;
562
563      if ((fd800.sector == 0) || (fd800.sector > 26))
564      {
565         fd800.stat_reg |= status_invalid_cmd;
566      }
567      else
568      {
569         fd800.unit = unit;
570         fd800.head = head;
571         fd800.sector = sector;
572         fd800.ddam = 1;
573
574         fd800.buf_pos = 0;
575         fd800.buf_mode = bm_write;
576         fd800.stat_reg |= status_XFER_ready;
577         fd800.stat_reg |= status_OP_complete;   /* right??? */
578      }
579
580      fd800.stat_reg |= status_interrupt;
581      fd800_field_interrupt();
582      break;
583
584   case 9:     /* format track
585                    bits 16-23: track ID (0-255, normally current cylinder index, or 255 for bad track)
586                    bit 24: verify only (1 - verify, 0 - format & verify)
587                    bit 25: head number (1=upper)
588                    bits 26-27: unit number (0-3) */
589      /* ... */
590      break;
591
592   case 10:    /* load int mask
593                    bit 16: bad mask for interrupt (0 = unmask or enable interrupt)
594                    bits 17-27: 0s */
595      fd800.interrupt_f_f = fd800.cmd_reg & 1;
596      fd800_field_interrupt();
597      break;
598
599   case 11:    /* stop
600                    bits 16-25: 0s
601                    bits 26-27: unit number (0-3) */
602      unit = (fd800.cmd_reg >> 10) & 3;
603
604      /* reset status */
605      fd800.stat_reg = unit << status_unit_shift;
606
607      fd800.stat_reg |= status_OP_complete;
608
609      fd800.stat_reg |= status_interrupt;
610      fd800_field_interrupt();
611      break;
612
613   case 12:    /* step head
614                    bits 16-22: track number (0-76)
615                    bits 23-25: 0s
616                    bits 26-27: unit number (0-3) */
617      unit = (fd800.cmd_reg >> 10) & 3;
618      cylinder = fd800.cmd_reg & 0x7f;
619
620      if (cylinder > 76)
621      {
622         fd800.stat_reg |= status_invalid_cmd;
623      }
624      else if ((fd800.drv[unit].phys_cylinder != -1) || (!fd800_do_restore(unit)))
625      {
626         floppy_drive_seek(&fd800.drv[unit].img->device(), cylinder-fd800.drv[unit].phys_cylinder);
627         fd800.stat_reg |= status_OP_complete;
628      }
629
630      fd800.stat_reg |= status_interrupt;
631      fd800_field_interrupt();
632      break;
633
634   case 13:    /* maintenance commands
635                    bits 16-23: according to extended command code
636                    bits 24-27: extended command code (0-7) */
637      switch ((fd800.cmd_reg >> 8) & 15)
638      {
639      case 0: /* reset
640                    bits 16-23: 0s */
641         /* ... */
642         break;
643      case 1: /* retry inhibit
644                    bits 16-23: 0s */
645         /* ... */
646         break;
647      case 2: /* LED test
648                    bit 16: 1
649                    bits 17-19: 0s
650                    bit 20: LED #2 enable
651                    bit 21: LED #3 enable
652                    bit 22: LED #4 enable
653                    bit 23: enable LEDs */
654         /* ... */
655         break;
656      case 3: /* program error (a.k.a. invalid command)
657                    bits 16-23: 0s */
658         /* ... */
659         break;
660      case 4: /* memory read
661                    bits 16-20: controller memory address (shifted left by 8 to generate 9900 address)
662                    bits 21-23: 0s */
663         /* ... */
664         break;
665      case 5: /* RAM load
666                    bit 16: 0
667                    bits 17-23: RAM offset (shifted left by 1 and offset by >1800 to generate 9900 address) */
668         /* ... */
669         break;
670      case 6: /* RAM run
671                    bit 16: 0
672                    bits 17-23: RAM offset (shifted left by 1 and offset by >1800 to generate 9900 address) */
673         /* ... */
674         break;
675      case 7: /* power up simulation
676                    bits 16-23: 0s */
677         /* ... */
678         break;
679      }
680      /* ... */
681      break;
682
683   case 14:    /* IPL
684                    bits 16-22: track number (0-76)
685                    bit 23: 0
686                    bit 24: no sequential sectoring (1=active)
687                    bit 25: head number (1=upper)
688                    bits 26-27: unit number (0-3) */
689      unit = (fd800.cmd_reg >> 10) & 3;
690      head = (fd800.cmd_reg >> 9) & 1;
691      /*non_seq_mode = (fd800.cmd_reg >> 8) & 1;*/
692      cylinder = fd800.cmd_reg & 0x7f;
693
694      if (!fd800_do_seek(unit, cylinder, head))
695      {
696         fd800.unit = unit;
697         fd800.head = head;
698         fd800.sector = 1;
699         /*fd800.non_seq_mode = non_seq_mode;*/
700
701         fd800_do_read();
702      }
703
704      fd800.stat_reg |= status_interrupt;
705      fd800_field_interrupt();
706      break;
707
708   case 15:    /* Clear Status port
709                    bits 16-27: 0s */
710      fd800.stat_reg = 0;
711      fd800_field_interrupt();
712      break;
713   }
714}
715
716/*
717    read one CRU bit
718
719    0-15: receive buffer
720    16-31: status:
721        16: OP complete (1 -> complete???)
722        17: Xfer ready (XFER) (1 -> ready???)
723        18: drive not ready
724        19: data check error
725        20: seek error/??????
726        21 invalid command/??????
727        22: no address mark found/??????
728        23: equipment check error/??????
729        24: ID check error
730        25: ID not found
731        26: Controller busy (CTLBSY) (0 -> controller is ready)
732        27: write protect
733        28: deleted sector detected
734        29: unit LSB
735        30: unit MSB
736        31: Interrupt (CBUSY???) (1 -> controller is ready)
737*/
738   READ8_HANDLER(fd800_cru_r)
739{
740   int reply = 0;
741
742   switch (offset)
743   {
744   case 0:
745   case 1:
746      /* receive buffer */
747      reply = fd800.recv_buf >> (offset*8);
748      break;
749
750   case 2:
751   case 3:
752      /* status register */
753      reply = fd800.stat_reg >> ((offset-2)*8);
754      break;
755   }
756
757   return reply;
758}
759
760/*
761    write one CRU bit
762
763    0-15: controller data word (transmit buffer)
764    16-31: controller command word (command register)
765    16-23: parameter value
766    24: flag bit/extended command code
767    25: head select/extended command code
768    26: FD unit number LSB/extended command code
769    27: FD unit number MSB/extended command code
770    28-31: command code
771*/
772WRITE8_HANDLER(fd800_cru_w)
773{
774   switch (offset)
775   {
776   case 0:
777   case 1:
778   case 2:
779   case 3:
780   case 4:
781   case 5:
782   case 6:
783   case 7:
784   case 8:
785   case 9:
786   case 10:
787   case 11:
788   case 12:
789   case 13:
790   case 14:
791   case 15:
792      /* transmit buffer */
793      if (data)
794         fd800.xmit_buf |= 1 << offset;
795      else
796         fd800.xmit_buf &= ~(1 << offset);
797      if (offset == 15)
798      {
799         switch (fd800.buf_mode)
800         {
801         case bm_off:
802            break;
803         case bm_read:
804            fd800.buf_pos++;
805            if (fd800.buf_pos == fd800.drv[fd800.unit].seclen)
806            {   /* end of sector */
807               if (fd800.sector == 26)
808               {   /* end of track -> end command (right???) */
809                  fd800.stat_reg &= ~status_XFER_ready;
810                  fd800.stat_reg |= status_OP_complete;
811                  fd800.stat_reg |= status_interrupt;
812                  fd800.buf_mode = bm_off;
813                  fd800_field_interrupt();
814               }
815               else
816               {   /* read next sector */
817                  fd800.sector++;
818                  fd800.stat_reg &= ~status_XFER_ready | status_OP_complete | status_interrupt;
819                  fd800_do_read();
820                  fd800.stat_reg |= status_interrupt;
821                  fd800_field_interrupt();
822               }
823            }
824            else
825               fd800.recv_buf = (fd800.buf[fd800.buf_pos<<1] << 8) | fd800.buf[(fd800.buf_pos<<1)+1];
826            break;
827
828         case bm_write:
829            fd800.buf[fd800.buf_pos<<1] = fd800.xmit_buf >> 8;
830            fd800.buf[(fd800.buf_pos<<1)+1] = fd800.xmit_buf & 0xff;
831            fd800.buf_pos++;
832            if (fd800.buf_pos == fd800.drv[fd800.unit].seclen)
833            {   /* end of sector */
834               fd800_do_write();
835               if (fd800.sector == 26)
836               {
837                  /* end of track -> end command (right???) */
838                  fd800.stat_reg &= ~status_XFER_ready;
839                  fd800.stat_reg |= status_OP_complete;
840                  fd800.stat_reg |= status_interrupt;
841                  fd800.buf_mode = bm_off;
842                  fd800_field_interrupt();
843               }
844               else
845               {   /* increment to next sector */
846                  fd800.sector++;
847                  fd800.stat_reg |= status_interrupt;
848                  fd800_field_interrupt();
849               }
850            }
851            break;
852         }
853      }
854      break;
855
856   case 16:
857   case 17:
858   case 18:
859   case 19:
860   case 20:
861   case 21:
862   case 22:
863   case 23:
864   case 24:
865   case 25:
866   case 26:
867   case 27:
868   case 28:
869   case 29:
870   case 30:
871   case 31:
872      /* command register */
873      if (data)
874         fd800.cmd_reg |= 1 << (offset-16);
875      else
876         fd800.cmd_reg &= ~(1 << (offset-16));
877      if (offset == 31)
878         fd800_do_cmd();
879      break;
880   }
881}
trunk/src/mess/machine/990_dk.h
r26090r26091
1/*
2    990_dk.h: include file for 990_dk.c
3*/
4LEGACY_FLOPPY_OPTIONS_EXTERN(fd800);
5
6void fd800_machine_init(running_machine &machine, void (*interrupt_callback)(running_machine &machine, int state));
7
8extern  DECLARE_READ8_HANDLER(fd800_cru_r);
9extern DECLARE_WRITE8_HANDLER(fd800_cru_w);
trunk/src/mess/machine/ti99/990_hd.c
r0r26091
1/*
2    990_hd.c: emulation of a generic ti990 hard disk controller, for use with
3    TILINE-based TI990 systems (TI990/10, /12, /12LR, /10A, Business system 300
4    and 300A).
5
6    This core will emulate the common feature set found in every disk controller.
7    Most controllers support additional features, but are still compatible with
8    the basic feature set.  I have a little documentation on two specific
9    disk controllers (WD900 and WD800/WD800A), but I have not tried to emulate
10    controller-specific features.
11
12
13    Long description: see 2234398-9701 and 2306140-9701.
14
15
16    Raphael Nabet 2002-2003
17*/
18
19#include "emu.h"
20
21#include "990_hd.h"
22
23#include "harddisk.h"
24#include "imagedev/harddriv.h"
25
26static void update_interrupt(running_machine &machine);
27
28/* max disk units per controller: 4 is the protocol limit, but it may be
29overriden if more than one controller is used */
30#define MAX_DISK_UNIT 4
31
32/* Max sector length is bytes.  Generally 256, except for a few older disk
33units which use 288-byte-long sectors, and SCSI units which generally use
34standard 512-byte-long sectors. */
35/* I chose a limit of 512.  No need to use more until someone writes CD-ROMs
36for TI990. */
37#define MAX_SECTOR_SIZE 512
38
39/* Description of custom format */
40/* We can use MAME's harddisk.c image format instead. */
41
42/* machine-independent big-endian 32-bit integer */
43struct UINT32BE
44{
45   UINT8 bytes[4];
46};
47
48INLINE UINT32 get_UINT32BE(UINT32BE word)
49{
50   return (word.bytes[0] << 24) | (word.bytes[1] << 16) | (word.bytes[2] << 8) | word.bytes[3];
51}
52
53#ifdef UNUSED_FUNCTION
54INLINE void set_UINT32BE(UINT32BE *word, UINT32 data)
55{
56   word->bytes[0] = (data >> 24) & 0xff;
57   word->bytes[1] = (data >> 16) & 0xff;
58   word->bytes[2] = (data >> 8) & 0xff;
59   word->bytes[3] = data & 0xff;
60}
61#endif
62
63/* disk image header */
64struct disk_image_header
65{
66   UINT32BE cylinders;         /* number of cylinders on hard disk (big-endian) */
67   UINT32BE heads;             /* number of heads on hard disk (big-endian) */
68   UINT32BE sectors_per_track; /* number of sectors per track on hard disk (big-endian) */
69   UINT32BE bytes_per_sector;  /* number of bytes of data per sector on hard disk (big-endian) */
70};
71
72enum
73{
74   header_len = sizeof(disk_image_header)
75};
76
77enum format_t
78{
79   format_mame,
80   format_old
81};
82
83/* disk drive unit descriptor */
84struct hd_unit_t
85{
86   device_image_interface *img;                        /* image descriptor */
87   format_t format;
88   hard_disk_file *hd_handle;      /* mame hard disk descriptor - only if format == format_mame */
89   unsigned int wp : 1;                    /* TRUE if disk is write-protected */
90   unsigned int unsafe : 1;                /* TRUE when a disk has just been connected */
91
92   /* disk geometry */
93   unsigned int cylinders, heads, sectors_per_track, bytes_per_sector;
94};
95
96/* disk controller */
97struct hdc_t
98{
99   UINT16 w[8];
100
101   void (*interrupt_callback)(running_machine &machine, int state);
102
103   hd_unit_t d[MAX_DISK_UNIT];
104};
105
106/* masks for individual bits controller registers */
107enum
108{
109   w0_offline          = 0x8000,
110   w0_not_ready        = 0x4000,
111   w0_write_protect    = 0x2000,
112   w0_unsafe           = 0x1000,
113   w0_end_of_cylinder  = 0x0800,
114   w0_seek_incomplete  = 0x0400,
115   /*w0_offset_active  = 0x0200,*/
116   w0_pack_change      = 0x0100,
117
118   w0_attn_lines       = 0x00f0,
119   w0_attn_mask        = 0x000f,
120
121   w1_extended_command = 0xc000,
122   /*w1_strobe_early       = 0x2000,
123   w1_strobe_late      = 0x1000,*/
124   w1_transfer_inhibit = 0x0800,
125   w1_command          = 0x0700,
126   w1_offset           = 0x0080,
127   w1_offset_forward   = 0x0040,
128   w1_head_address     = 0x003f,
129
130   w6_unit0_sel        = 0x0800,
131   w6_unit1_sel        = 0x0400,
132   w6_unit2_sel        = 0x0200,
133   w6_unit3_sel        = 0x0100,
134
135   w7_idle             = 0x8000,
136   w7_complete         = 0x4000,
137   w7_error            = 0x2000,
138   w7_int_enable       = 0x1000,
139   /*w7_lock_out           = 0x0800,*/
140   w7_retry            = 0x0400,
141   w7_ecc              = 0x0200,
142   w7_abnormal_completion  = 0x0100,
143   w7_memory_error         = 0x0080,
144   w7_data_error           = 0x0040,
145   w7_tiline_timeout_err   = 0x0020,
146   w7_header_err           = 0x0010,
147   w7_rate_err             = 0x0008,
148   w7_command_time_out_err = 0x0004,
149   w7_search_err           = 0x0002,
150   w7_unit_err             = 0x0001
151};
152
153/* masks for computer-controlled bit in each controller register  */
154static const UINT16 w_mask[8] =
155{
156   0x000f,     /* Controllers should prevent overwriting of w0 status bits, and I know
157                that some controllers do so. */
158   0xffff,
159   0xffff,
160   0xffff,
161   0xffff,
162   0xffff,
163   0xffff,
164   0xf7ff      /* Don't overwrite reserved bits */
165};
166
167static hdc_t hdc;
168
169
170static int get_id_from_device( device_t *device )
171{
172   int id = -1;
173
174   if ( ! strcmp( ":harddisk1", device->tag() ) )
175   {
176      id = 0;
177   }
178   else if ( ! strcmp( ":harddisk2", device->tag() ) )
179   {
180      id = 1;
181   }
182   else if ( ! strcmp( ":harddisk3", device->tag() ) )
183   {
184      id = 2;
185   }
186   else if ( ! strcmp( ":harddisk4", device->tag() ) )
187   {
188      id = 3;
189   }
190   assert( id >= 0 );
191
192   return id;
193}
194
195
196/*
197    Initialize hard disk unit and open a hard disk image
198*/
199static DEVICE_IMAGE_LOAD_LEGACY( ti990_hd )
200{
201   int id = get_id_from_device( &image.device() );
202   hd_unit_t *d;
203   hard_disk_file  *hd_file;
204
205   d = &hdc.d[id];
206   d->img = &image;
207
208   hd_file = dynamic_cast<harddisk_image_device *>(&image)->get_hard_disk_file();
209
210   if ( hd_file )
211   {
212      const hard_disk_info *standard_header;
213
214      d->format = format_mame;
215      d->hd_handle = hd_file;
216
217      /* use standard hard disk image header. */
218      standard_header = hard_disk_get_info(d->hd_handle);
219
220      d->cylinders = standard_header->cylinders;
221      d->heads = standard_header->heads;
222      d->sectors_per_track = standard_header->sectors;
223      d->bytes_per_sector = standard_header->sectorbytes;
224   }
225   else
226   {
227      /* older, custom format */
228      disk_image_header custom_header;
229      int bytes_read;
230
231      /* set file descriptor */
232      d->format = format_old;
233      d->hd_handle = NULL;
234
235      /* use custom image header. */
236      /* to convert old header-less images to this format, insert a 16-byte
237      header as follow: 00 00 03 8f  00 00 00 05  00 00 00 21  00 00 01 00 */
238      d->img->fseek(0, SEEK_SET);
239      bytes_read = d->img->fread(&custom_header, sizeof(custom_header));
240      if (bytes_read != sizeof(custom_header))
241      {
242         d->format = format_mame;    /* don't care */
243         d->wp = 1;
244         d->unsafe = 1;
245         return IMAGE_INIT_FAIL;
246      }
247
248      d->cylinders = get_UINT32BE(custom_header.cylinders);
249      d->heads = get_UINT32BE(custom_header.heads);
250      d->sectors_per_track = get_UINT32BE(custom_header.sectors_per_track);
251      d->bytes_per_sector = get_UINT32BE(custom_header.bytes_per_sector);
252   }
253
254   if (d->bytes_per_sector > MAX_SECTOR_SIZE)
255   {
256      d->format = format_mame;
257      d->hd_handle = NULL;
258      d->wp = 1;
259      d->unsafe = 1;
260      return IMAGE_INIT_FAIL;
261   }
262
263   /* tell whether the image is writable */
264   d->wp = image.is_readonly();
265
266   d->unsafe = 1;
267   /* set attention line */
268   hdc.w[0] |= (0x80 >> id);
269
270   return IMAGE_INIT_PASS;
271}
272
273/*
274    close a hard disk image
275*/
276static DEVICE_IMAGE_UNLOAD_LEGACY( ti990_hd )
277{
278   int id = get_id_from_device( image );
279   hd_unit_t *d;
280
281   d = &hdc.d[id];
282
283   d->format = format_mame;    /* don't care */
284   d->hd_handle = NULL;
285   d->wp = 1;
286   d->unsafe = 1;
287
288   /* clear attention line */
289   hdc.w[0] &= ~ (0x80 >> id);
290}
291
292/*
293    Return true if a HD image has been loaded
294*/
295INLINE int is_unit_loaded(int unit)
296{
297   int reply = 0;
298
299   switch (hdc.d[unit].format)
300   {
301   case format_mame:
302      reply = (hdc.d[unit].hd_handle != NULL);
303      break;
304
305   case format_old:
306      reply = (hdc.d[unit].img->exists() ? 1 : 0);
307      break;
308   }
309
310   return reply;
311}
312
313/*
314    Init the hdc core
315*/
316MACHINE_START(ti990_hdc)
317{
318   int i;
319
320   /* initialize harddisk information */
321   /* attention lines will be set by DEVICE_IMAGE_LOD */
322   for (i=0; i<MAX_DISK_UNIT; i++)
323   {
324      hdc.d[i].format = format_mame;
325      hdc.d[i].hd_handle = NULL;
326      hdc.d[i].wp = 1;
327      hdc.d[i].unsafe = 1;
328   }
329}
330
331
332void ti990_hdc_init(running_machine &machine, void (*interrupt_callback)(running_machine &machine, int state))
333{
334   memset(hdc.w, 0, sizeof(hdc.w));
335   hdc.w[7] = w7_idle;
336
337   /* get references to harddisk devices */
338   hdc.d[0].img = dynamic_cast<device_image_interface *>(machine.device("harddisk1"));
339   hdc.d[1].img = dynamic_cast<device_image_interface *>(machine.device("harddisk2"));
340   hdc.d[2].img = dynamic_cast<device_image_interface *>(machine.device("harddisk3"));
341   hdc.d[3].img = dynamic_cast<device_image_interface *>(machine.device("harddisk4"));
342
343   hdc.interrupt_callback = interrupt_callback;
344
345   update_interrupt(machine);
346}
347
348
349/*
350    Parse the disk select lines, and return the corresponding tape unit.
351    (-1 if none)
352*/
353static int cur_disk_unit(void)
354{
355   int reply;
356
357
358   if (hdc.w[6] & w6_unit0_sel)
359      reply = 0;
360   else if (hdc.w[6] & w6_unit1_sel)
361      reply = 1;
362   else if (hdc.w[6] & w6_unit2_sel)
363      reply = 2;
364   else if (hdc.w[6] & w6_unit3_sel)
365      reply = 3;
366   else
367      reply = -1;
368
369   if (reply >= MAX_DISK_UNIT)
370      reply = -1;
371
372   return reply;
373}
374
375/*
376    Update interrupt state
377*/
378static void update_interrupt(running_machine &machine)
379{
380   if (hdc.interrupt_callback)
381      (*hdc.interrupt_callback)(machine, (hdc.w[7] & w7_idle)
382                           && (((hdc.w[7] & w7_int_enable) && (hdc.w[7] & (w7_complete | w7_error)))
383                              || ((hdc.w[0] & (hdc.w[0] >> 4)) & w0_attn_mask)));
384}
385
386/*
387    Check that a sector address is valid.
388
389    Terminate current command and return non-zero if the address is invalid.
390*/
391static int check_sector_address(running_machine &machine, int unit, unsigned int cylinder, unsigned int head, unsigned int sector)
392{
393   if ((cylinder > hdc.d[unit].cylinders) || (head > hdc.d[unit].heads) || (sector > hdc.d[unit].sectors_per_track))
394   {   /* invalid address */
395      if (cylinder > hdc.d[unit].cylinders)
396      {
397         hdc.w[0] |= w0_seek_incomplete;
398         hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
399      }
400      else if (head > hdc.d[unit].heads)
401      {
402         hdc.w[0] |= w0_end_of_cylinder;
403         hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
404      }
405      else if (sector > hdc.d[unit].sectors_per_track)
406         hdc.w[7] |= w7_idle | w7_error | w7_command_time_out_err;
407      update_interrupt(machine);
408      return 1;
409   }
410
411   return 0;
412}
413
414/*
415    Seek to sector whose address is given
416*/
417static int sector_to_lba(running_machine &machine, int unit, unsigned int cylinder, unsigned int head, unsigned int sector, unsigned int *lba)
418{
419   if (check_sector_address(machine, unit, cylinder, head, sector))
420      return 1;
421
422   * lba = (cylinder*hdc.d[unit].heads + head)*hdc.d[unit].sectors_per_track + sector;
423
424   return 0;
425}
426
427/*
428    Read one given sector
429*/
430static int read_sector(int unit, unsigned int lba, void *buffer, unsigned int bytes_to_read)
431{
432   unsigned long byte_position;
433   unsigned int bytes_read;
434
435   switch (hdc.d[unit].format)
436   {
437   case format_mame:
438      bytes_read = hdc.d[unit].bytes_per_sector * hard_disk_read(hdc.d[unit].hd_handle, lba, buffer);
439      if (bytes_read > bytes_to_read)
440         bytes_read = bytes_to_read;
441      break;
442
443   case format_old:
444      byte_position = lba*hdc.d[unit].bytes_per_sector + header_len;
445      hdc.d[unit].img->fseek(byte_position, SEEK_SET);
446      bytes_read = hdc.d[unit].img->fread(buffer, bytes_to_read);
447      break;
448
449   default:
450      bytes_read = 0;
451      break;
452   }
453
454   return bytes_read;
455}
456
457/*
458    Write one given sector
459*/
460static int write_sector(int unit, unsigned int lba, const void *buffer, unsigned int bytes_to_write)
461{
462   unsigned long byte_position;
463   unsigned int bytes_written;
464
465   switch (hdc.d[unit].format)
466   {
467   case format_mame:
468      bytes_written = hdc.d[unit].bytes_per_sector * hard_disk_write(hdc.d[unit].hd_handle, lba, buffer);
469      if (bytes_written > bytes_to_write)
470         bytes_written = bytes_to_write;
471      break;
472
473   case format_old:
474      byte_position = lba*hdc.d[unit].bytes_per_sector + header_len;
475      hdc.d[unit].img->fseek(byte_position, SEEK_SET);
476      bytes_written = hdc.d[unit].img->fwrite(buffer, bytes_to_write);
477      break;
478
479   default:
480      bytes_written = 0;
481      break;
482   }
483
484   return bytes_written;
485}
486
487/*
488    Handle the store registers command: read the drive geometry.
489*/
490static void store_registers(running_machine &machine)
491{
492   int dma_address;
493   int byte_count;
494
495   UINT16 buffer[3];
496   int i, real_word_count;
497
498   int dsk_sel = cur_disk_unit();
499
500
501   if (dsk_sel == -1)
502   {
503      /* No idea what to report... */
504      hdc.w[7] |= w7_idle | w7_error | w7_abnormal_completion;
505      update_interrupt(machine);
506      return;
507   }
508   else if (! is_unit_loaded(dsk_sel))
509   {   /* offline */
510      hdc.w[0] |= w0_offline | w0_not_ready;
511      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
512      update_interrupt(machine);
513      return;
514   }
515
516   hdc.d[dsk_sel].unsafe = 0;      /* I think */
517
518   dma_address = ((((int) hdc.w[6]) << 16) | hdc.w[5]) & 0x1ffffe;
519   byte_count = hdc.w[4] & 0xfffe;
520
521   /* formatted words per track */
522   buffer[0] = (hdc.d[dsk_sel].sectors_per_track*hdc.d[dsk_sel].bytes_per_sector) >> 1;
523   /* MSByte: sectors per track; LSByte: bytes of overhead per sector */
524   buffer[1] = (hdc.d[dsk_sel].sectors_per_track << 8) | 0;
525   /* bits 0-4: heads; bits 5-15: cylinders */
526   buffer[2] = (hdc.d[dsk_sel].heads << 11) | hdc.d[dsk_sel].cylinders;
527
528   real_word_count = byte_count >> 1;
529   if (real_word_count > 3)
530      real_word_count = 3;
531
532   /* DMA */
533   if (! (hdc.w[1] & w1_transfer_inhibit))
534      for (i=0; i<real_word_count; i++)
535      {
536         machine.device("maincpu")->memory().space(AS_PROGRAM).write_word(dma_address, buffer[i]);
537         dma_address = (dma_address + 2) & 0x1ffffe;
538      }
539
540   hdc.w[7] |= w7_idle | w7_complete;
541   update_interrupt(machine);
542}
543
544/*
545    Handle the write format command: format a complete track on disk.
546
547    The emulation just clears the track data in the disk image.
548*/
549static void write_format(running_machine &machine)
550{
551   unsigned int cylinder, head, sector;
552   unsigned int lba;
553
554   UINT8 buffer[MAX_SECTOR_SIZE];
555   int bytes_written;
556
557   int dsk_sel = cur_disk_unit();
558
559
560   if (dsk_sel == -1)
561   {
562      /* No idea what to report... */
563      hdc.w[7] |= w7_idle | w7_error | w7_abnormal_completion;
564      update_interrupt(machine);
565      return;
566   }
567   else if (! is_unit_loaded(dsk_sel))
568   {   /* offline */
569      hdc.w[0] |= w0_offline | w0_not_ready;
570      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
571      update_interrupt(machine);
572      return;
573   }
574   else if (hdc.d[dsk_sel].unsafe)
575   {   /* disk in unsafe condition */
576      hdc.w[0] |= w0_unsafe | w0_pack_change;
577      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
578      update_interrupt(machine);
579      return;
580   }
581   else if (hdc.d[dsk_sel].wp)
582   {   /* disk write-protected */
583      hdc.w[0] |= w0_write_protect;
584      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
585      update_interrupt(machine);
586      return;
587   }
588
589   cylinder = hdc.w[3];
590   head = hdc.w[1] & w1_head_address;
591
592   if (sector_to_lba(machine, dsk_sel, cylinder, head, 0, &lba))
593      return;
594
595   memset(buffer, 0, hdc.d[dsk_sel].bytes_per_sector);
596
597   for (sector=0; sector<hdc.d[dsk_sel].sectors_per_track; sector++)
598   {
599      bytes_written = write_sector(dsk_sel, lba, buffer, hdc.d[dsk_sel].bytes_per_sector);
600
601      if (bytes_written != hdc.d[dsk_sel].bytes_per_sector)
602      {
603         hdc.w[0] |= w0_offline | w0_not_ready;
604         hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
605         update_interrupt(machine);
606         return;
607      }
608
609      lba++;
610   }
611
612   hdc.w[7] |= w7_idle | w7_complete;
613   update_interrupt(machine);
614}
615
616/*
617    Handle the read data command: read a variable number of sectors from disk.
618*/
619static void read_data(running_machine &machine)
620{
621   int dma_address;
622   int byte_count;
623
624   unsigned int cylinder, head, sector;
625   unsigned int lba;
626
627   UINT8 buffer[MAX_SECTOR_SIZE];
628   int bytes_to_read;
629   int bytes_read;
630   int i;
631
632   int dsk_sel = cur_disk_unit();
633
634
635   if (dsk_sel == -1)
636   {
637      /* No idea what to report... */
638      hdc.w[7] |= w7_idle | w7_error | w7_abnormal_completion;
639      update_interrupt(machine);
640      return;
641   }
642   else if (! is_unit_loaded(dsk_sel))
643   {   /* offline */
644      hdc.w[0] |= w0_offline | w0_not_ready;
645      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
646      update_interrupt(machine);
647      return;
648   }
649   else if (hdc.d[dsk_sel].unsafe)
650   {   /* disk in unsafe condition */
651      hdc.w[0] |= w0_unsafe | w0_pack_change;
652      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
653      update_interrupt(machine);
654      return;
655   }
656
657   dma_address = ((((int) hdc.w[6]) << 16) | hdc.w[5]) & 0x1ffffe;
658   byte_count = hdc.w[4] & 0xfffe;
659
660   cylinder = hdc.w[3];
661   head = hdc.w[1] & w1_head_address;
662   sector = hdc.w[2] & 0xff;
663
664   if (sector_to_lba(machine, dsk_sel, cylinder, head, sector, &lba))
665      return;
666
667   while (byte_count)
668   {   /* read data sector per sector */
669      if (cylinder > hdc.d[dsk_sel].cylinders)
670      {
671         hdc.w[0] |= w0_seek_incomplete;
672         hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
673         update_interrupt(machine);
674         return;
675      }
676
677      bytes_to_read = (byte_count < hdc.d[dsk_sel].bytes_per_sector) ? byte_count : hdc.d[dsk_sel].bytes_per_sector;
678      bytes_read = read_sector(dsk_sel, lba, buffer, bytes_to_read);
679
680      if (bytes_read != bytes_to_read)
681      {   /* behave as if the controller could not found the sector ID mark */
682         hdc.w[7] |= w7_idle | w7_error | w7_command_time_out_err;
683         update_interrupt(machine);
684         return;
685      }
686
687      /* DMA */
688      if (! (hdc.w[1] & w1_transfer_inhibit))
689         for (i=0; i<bytes_read; i+=2)
690         {
691            machine.device("maincpu")->memory().space(AS_PROGRAM).write_word(dma_address, (((int) buffer[i]) << 8) | buffer[i+1]);
692            dma_address = (dma_address + 2) & 0x1ffffe;
693         }
694
695      byte_count -= bytes_read;
696
697      /* update sector address to point to next sector */
698      lba++;
699      sector++;
700      if (sector == hdc.d[dsk_sel].sectors_per_track)
701      {
702         sector = 0;
703         head++;
704         if (head == hdc.d[dsk_sel].heads)
705         {
706            head = 0;
707            cylinder++;
708         }
709      }
710   }
711
712   hdc.w[7] |= w7_idle | w7_complete;
713   update_interrupt(machine);
714}
715
716/*
717    Handle the write data command: write a variable number of sectors from disk.
718*/
719static void write_data(running_machine &machine)
720{
721   int dma_address;
722   int byte_count;
723
724   unsigned int cylinder, head, sector;
725   unsigned int lba;
726
727   UINT8 buffer[MAX_SECTOR_SIZE];
728   UINT16 word;
729   int bytes_written;
730   int i;
731
732   int dsk_sel = cur_disk_unit();
733
734
735   if (dsk_sel == -1)
736   {
737      /* No idea what to report... */
738      hdc.w[7] |= w7_idle | w7_error | w7_abnormal_completion;
739      update_interrupt(machine);
740      return;
741   }
742   else if (! is_unit_loaded(dsk_sel))
743   {   /* offline */
744      hdc.w[0] |= w0_offline | w0_not_ready;
745      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
746      update_interrupt(machine);
747      return;
748   }
749   else if (hdc.d[dsk_sel].unsafe)
750   {   /* disk in unsafe condition */
751      hdc.w[0] |= w0_unsafe | w0_pack_change;
752      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
753      update_interrupt(machine);
754      return;
755   }
756   else if (hdc.d[dsk_sel].wp)
757   {   /* disk write-protected */
758      hdc.w[0] |= w0_write_protect;
759      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
760      update_interrupt(machine);
761      return;
762   }
763
764   dma_address = ((((int) hdc.w[6]) << 16) | hdc.w[5]) & 0x1ffffe;
765   byte_count = hdc.w[4] & 0xfffe;
766
767   cylinder = hdc.w[3];
768   head = hdc.w[1] & w1_head_address;
769   sector = hdc.w[2] & 0xff;
770
771   if (sector_to_lba(machine, dsk_sel, cylinder, head, sector, &lba))
772      return;
773
774   while (byte_count > 0)
775   {   /* write data sector per sector */
776      if (cylinder > hdc.d[dsk_sel].cylinders)
777      {
778         hdc.w[0] |= w0_seek_incomplete;
779         hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
780         update_interrupt(machine);
781         return;
782      }
783
784      /* DMA */
785      for (i=0; (i<byte_count) && (i<hdc.d[dsk_sel].bytes_per_sector); i+=2)
786      {
787         word = machine.device("maincpu")->memory().space(AS_PROGRAM).read_word(dma_address);
788         buffer[i] = word >> 8;
789         buffer[i+1] = word & 0xff;
790
791         dma_address = (dma_address + 2) & 0x1ffffe;
792      }
793      /* fill with 0s if we did not reach sector end */
794      for (; i<hdc.d[dsk_sel].bytes_per_sector; i+=2)
795         buffer[i] = buffer[i+1] = 0;
796
797      bytes_written = write_sector(dsk_sel, lba, buffer, hdc.d[dsk_sel].bytes_per_sector);
798
799      if (bytes_written != hdc.d[dsk_sel].bytes_per_sector)
800      {
801         hdc.w[0] |= w0_offline | w0_not_ready;
802         hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
803         update_interrupt(machine);
804         return;
805      }
806
807      byte_count -= bytes_written;
808
809      /* update sector address to point to next sector */
810      lba++;
811      sector++;
812      if (sector == hdc.d[dsk_sel].sectors_per_track)
813      {
814         sector = 0;
815         head++;
816         if (head == hdc.d[dsk_sel].heads)
817         {
818            head = 0;
819            cylinder++;
820         }
821      }
822   }
823
824   hdc.w[7] |= w7_idle | w7_complete;
825   update_interrupt(machine);
826}
827
828/*
829    Handle the unformatted read command: read drive geometry information.
830*/
831static void unformatted_read(running_machine &machine)
832{
833   int dma_address;
834   int byte_count;
835
836   unsigned int cylinder, head, sector;
837
838   UINT16 buffer[3];
839   int i, real_word_count;
840
841   int dsk_sel = cur_disk_unit();
842
843
844   if (dsk_sel == -1)
845   {
846      /* No idea what to report... */
847      hdc.w[7] |= w7_idle | w7_error | w7_abnormal_completion;
848      update_interrupt(machine);
849      return;
850   }
851   else if (! is_unit_loaded(dsk_sel))
852   {   /* offline */
853      hdc.w[0] |= w0_offline | w0_not_ready;
854      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
855      update_interrupt(machine);
856      return;
857   }
858   else if (hdc.d[dsk_sel].unsafe)
859   {   /* disk in unsafe condition */
860      hdc.w[0] |= w0_unsafe | w0_pack_change;
861      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
862      update_interrupt(machine);
863      return;
864   }
865
866   dma_address = ((((int) hdc.w[6]) << 16) | hdc.w[5]) & 0x1ffffe;
867   byte_count = hdc.w[4] & 0xfffe;
868
869   cylinder = hdc.w[3];
870   head = hdc.w[1] & w1_head_address;
871   sector = hdc.w[2] & 0xff;
872
873   if (check_sector_address(machine, dsk_sel, cylinder, head, sector))
874      return;
875
876   dma_address = ((((int) hdc.w[6]) << 16) | hdc.w[5]) & 0x1ffffe;
877   byte_count = hdc.w[4] & 0xfffe;
878
879   /* bits 0-4: head address; bits 5-15: cylinder address */
880   buffer[0] = (head << 11) | cylinder;
881   /* MSByte: sectors per record (1); LSByte: sector address */
882   buffer[1] = (1 << 8) | sector;
883   /* formatted words per record */
884   buffer[2] = hdc.d[dsk_sel].bytes_per_sector >> 1;
885
886   real_word_count = byte_count >> 1;
887   if (real_word_count > 3)
888      real_word_count = 3;
889
890   /* DMA */
891   if (! (hdc.w[1] & w1_transfer_inhibit))
892      for (i=0; i<real_word_count; i++)
893      {
894         machine.device("maincpu")->memory().space(AS_PROGRAM).write_word(dma_address, buffer[i]);
895         dma_address = (dma_address + 2) & 0x1ffffe;
896      }
897
898   hdc.w[7] |= w7_idle | w7_complete;
899   update_interrupt(machine);
900}
901
902/*
903    Handle the restore command: return to track 0.
904*/
905static void restore(running_machine &machine)
906{
907   int dsk_sel = cur_disk_unit();
908
909
910   if (dsk_sel == -1)
911   {
912      /* No idea what to report... */
913      hdc.w[7] |= w7_idle | w7_error | w7_abnormal_completion;
914      update_interrupt(machine);
915      return;
916   }
917   else if (! is_unit_loaded(dsk_sel))
918   {   /* offline */
919      hdc.w[0] |= w0_offline | w0_not_ready;
920      hdc.w[7] |= w7_idle | w7_error | w7_unit_err;
921      update_interrupt(machine);
922      return;
923   }
924
925   hdc.d[dsk_sel].unsafe = 0;      /* I think */
926
927   /*if (seek_to_sector(dsk_sel, 0, 0, 0))
928       return;*/
929
930   hdc.w[7] |= w7_idle | w7_complete;
931   update_interrupt(machine);
932}
933
934/*
935    Parse command code and execute the command.
936*/
937static void execute_command(running_machine &machine)
938{
939   /* hack */
940   hdc.w[0] &= 0xff;
941
942   if (hdc.w[1] & w1_extended_command)
943      logerror("extended commands not supported\n");
944
945   switch (/*((hdc.w[1] & w1_extended_command) >> 11) |*/ ((hdc.w[1] & w1_command) >> 8))
946   {
947   case 0x00: //0b000:
948      /* store registers */
949      logerror("store registers\n");
950      store_registers(machine);
951      break;
952   case 0x01: //0b001:
953      /* write format */
954      logerror("write format\n");
955      write_format(machine);
956      break;
957   case 0x02: //0b010:
958      /* read data */
959      logerror("read data\n");
960      read_data(machine);
961      break;
962   case 0x03: //0b011:
963      /* write data */
964      logerror("write data\n");
965      write_data(machine);
966      break;
967   case 0x04: //0b100:
968      /* unformatted read */
969      logerror("unformatted read\n");
970      unformatted_read(machine);
971      break;
972   case 0x05: //0b101:
973      /* unformatted write */
974      logerror("unformatted write\n");
975      /* ... */
976      hdc.w[7] |= w7_idle | w7_error | w7_abnormal_completion;
977      update_interrupt(machine);
978      break;
979   case 0x06: //0b110:
980      /* seek */
981      logerror("seek\n");
982      /* This command can (almost) safely be ignored */
983      hdc.w[7] |= w7_idle | w7_complete;
984      update_interrupt(machine);
985      break;
986   case 0x07: //0b111:
987      /* restore */
988      logerror("restore\n");
989      restore(machine);
990      break;
991   }
992}
993
994/*
995    Read one register in TPCS space
996*/
997READ16_HANDLER(ti990_hdc_r)
998{
999   if (offset < 8)
1000      return hdc.w[offset];
1001   else
1002      return 0;
1003}
1004
1005/*
1006    Write one register in TPCS space.  Execute command if w7_idle is cleared.
1007*/
1008WRITE16_HANDLER(ti990_hdc_w)
1009{
1010   if (offset < 8)
1011   {
1012      /* write protect if a command is in progress */
1013      if (hdc.w[7] & w7_idle)
1014      {
1015         UINT16 old_data = hdc.w[offset];
1016
1017         /* Only write writable bits AND honor byte accesses (ha!) */
1018         hdc.w[offset] = (hdc.w[offset] & ((~w_mask[offset]) | mem_mask)) | (data & w_mask[offset] & ~mem_mask);
1019
1020         if ((offset == 0) || (offset == 7))
1021            update_interrupt(space.machine());
1022
1023         if ((offset == 7) && (old_data & w7_idle) && ! (data & w7_idle))
1024         {   /* idle has been cleared: start command execution */
1025            execute_command(space.machine());
1026         }
1027      }
1028   }
1029}
1030
1031
1032static const struct harddisk_interface ti990_harddisk_config =
1033{
1034   DEVICE_IMAGE_LOAD_NAME_LEGACY( ti990_hd ),
1035   DEVICE_IMAGE_UNLOAD_NAME_LEGACY( ti990_hd ),
1036   NULL,
1037   NULL
1038};
1039
1040MACHINE_CONFIG_FRAGMENT( ti990_hdc )
1041   MCFG_HARDDISK_CONFIG_ADD( "harddisk1", ti990_harddisk_config )
1042   MCFG_HARDDISK_CONFIG_ADD( "harddisk2", ti990_harddisk_config )
1043   MCFG_HARDDISK_CONFIG_ADD( "harddisk3", ti990_harddisk_config )
1044   MCFG_HARDDISK_CONFIG_ADD( "harddisk4", ti990_harddisk_config )
1045MACHINE_CONFIG_END
Property changes on: trunk/src/mess/machine/ti99/990_hd.c
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/mess/machine/ti99/990_dk.c
r0r26091
1/*
2    990_dk.c: emulation of a TI FD800 'Diablo' floppy disk controller
3    controller, for use with any TI990 system (and possibly any system which
4    implements the CRU bus).
5
6    This floppy disk controller supports IBM-format 8" SSSD and DSSD floppies.
7
8    Raphael Nabet 2003
9*/
10
11#include "emu.h"
12
13#include "formats/basicdsk.h"
14#include "imagedev/flopdrv.h"
15#include "990_dk.h"
16
17#define MAX_FLOPPIES 4
18
19enum buf_mode_t {
20   bm_off, bm_read, bm_write
21};
22static struct
23{
24   running_machine *machine;
25   UINT16 recv_buf;
26   UINT16 stat_reg;
27   UINT16 xmit_buf;
28   UINT16 cmd_reg;
29
30   int interrupt_f_f;
31   void (*interrupt_callback)(running_machine &, int state);
32
33   UINT8 buf[128];
34   int buf_pos;
35   buf_mode_t buf_mode;
36   int unit;
37   int head;
38   int sector;
39   /*int non_seq_mode;*/
40   int ddam;
41
42   struct
43   {
44      device_image_interface *img;
45      int phys_cylinder;
46      int log_cylinder[2];
47      int seclen;
48   } drv[MAX_FLOPPIES];
49} fd800;
50
51/* status bits */
52enum
53{
54   status_OP_complete  = 1 << 0,
55   status_XFER_ready   = 1 << 1,
56   status_drv_not_ready= 1 << 2,
57   status_dat_chk_err  = 1 << 3,
58   status_seek_err     = 1 << 4,
59   status_invalid_cmd  = 1 << 5,
60   status_no_addr_mark = 1 << 6,
61   status_equ_chk_err  = 1 << 7,
62   status_ID_chk_err   = 1 << 8,
63   status_ID_not_found = 1 << 9,
64   status_ctlr_busy    = 1 << 10,
65   status_write_prot   = 1 << 11,
66   status_del_sector   = 1 << 12,
67   status_interrupt    = 1 << 15,
68
69   status_unit_shift   = 13
70};
71
72LEGACY_FLOPPY_OPTIONS_START(fd800)
73#if 1
74   /* SSSD 8" */
75   LEGACY_FLOPPY_OPTION(fd800, "dsk", "TI990 8\" SSSD disk image", basicdsk_identify_default, basicdsk_construct_default, NULL,
76      HEADS([1])
77      TRACKS([77])
78      SECTORS([26])
79      SECTOR_LENGTH([128])
80      FIRST_SECTOR_ID([1]))
81#elif 0
82   /* DSSD 8" */
83   LEGACY_FLOPPY_OPTION(fd800, "dsk", "TI990 8\" DSSD disk image", basicdsk_identify_default, basicdsk_construct_default, NULL,
84      HEADS([2])
85      TRACKS([77])
86      SECTORS([26])
87      SECTOR_LENGTH([128])
88      FIRST_SECTOR_ID([1]))
89#endif
90LEGACY_FLOPPY_OPTIONS_END
91
92static void fd800_field_interrupt(void)
93{
94   if (fd800.interrupt_callback)
95      (*fd800.interrupt_callback)(*fd800.machine, (fd800.stat_reg & status_interrupt) && ! fd800.interrupt_f_f);
96}
97
98static void fd800_unload_proc(device_image_interface &image)
99{
100   int unit = floppy_get_drive(&image.device());
101
102   fd800.drv[unit].log_cylinder[0] = fd800.drv[unit].log_cylinder[1] = -1;
103}
104
105void fd800_machine_init(running_machine &machine, void (*interrupt_callback)(running_machine &machine, int state))
106{
107   int i;
108
109   fd800.machine = &machine;
110   fd800.interrupt_callback = interrupt_callback;
111
112   fd800.stat_reg = 0;
113   fd800.interrupt_f_f = 1;
114
115   fd800.buf_pos = 0;
116   fd800.buf_mode = bm_off;
117
118   for (i=0; i<MAX_FLOPPIES; i++)
119   {
120      fd800.drv[i].img = dynamic_cast<device_image_interface *>(floppy_get_device(machine, i));
121      fd800.drv[i].phys_cylinder = -1;
122      fd800.drv[i].log_cylinder[0] = fd800.drv[i].log_cylinder[1] = -1;
123      fd800.drv[i].seclen = 64;
124      floppy_install_unload_proc(&fd800.drv[i].img->device(), fd800_unload_proc);
125   }
126
127   fd800_field_interrupt();
128}
129
130/*
131    Read the first id field that can be found on the floppy disk.
132
133    unit: floppy drive index
134    head: selected head
135    cylinder_id: cylinder ID read
136    sector_id: sector ID read
137
138    Return TRUE if an ID was found
139*/
140static int fd800_read_id(int unit, int head, int *cylinder_id, int *sector_id)
141{
142   /*UINT8 revolution_count;*/
143   chrn_id id;
144
145   /*revolution_count = 0;*/
146
147   /*while (revolution_count < 2)*/
148   /*{*/
149      if (floppy_drive_get_next_id(&fd800.drv[unit].img->device(), head, &id))
150      {
151         if (cylinder_id)
152            *cylinder_id = id.C;
153         if (sector_id)
154            *sector_id = id.R;
155         return TRUE;
156      }
157   /*}*/
158
159   return FALSE;
160}
161
162/*
163    Find a sector by id.
164
165    unit: floppy drive index
166    head: selected head
167    sector: sector ID to search
168    data_id: data ID to be used when calling sector read/write functions
169
170    Return TRUE if the given sector ID was found
171*/
172static int fd800_find_sector(int unit, int head, int sector, int *data_id)
173{
174   UINT8 revolution_count;
175   chrn_id id;
176
177   revolution_count = 0;
178
179   while (revolution_count < 2)
180   {
181      if (floppy_drive_get_next_id(&fd800.drv[unit].img->device(), head, &id))
182      {
183         /* compare id */
184         if ((id.R == sector) && (id.N == 0))
185         {
186            *data_id = id.data_id;
187            /* get ddam status */
188            /*w->ddam = id.flags & ID_FLAG_DELETED_DATA;*/
189            return TRUE;
190         }
191      }
192   }
193
194   return FALSE;
195}
196
197/*
198    Perform seek command
199
200    unit: floppy drive index
201    cylinder: track to seek for
202    head: head for which the seek is performed
203
204    Return FALSE if the seek was successful
205*/
206static int fd800_do_seek(int unit, int cylinder, int head)
207{
208   int retries;
209
210   if (cylinder > 76)
211   {
212      fd800.stat_reg |= status_invalid_cmd;
213      return TRUE;
214   }
215
216   if (!fd800.drv[unit].img->exists())
217   {
218      fd800.stat_reg |= status_drv_not_ready; /* right??? */
219      return TRUE;
220   }
221
222   if (fd800.drv[unit].log_cylinder[head] == -1)
223   {   /* current track ID is unknown: read it */
224      if (!fd800_read_id(unit, head, &fd800.drv[unit].log_cylinder[head], NULL))
225      {
226         fd800.stat_reg |= status_ID_not_found;
227         return TRUE;
228      }
229   }
230   /* exit if we are already at the requested track */
231   if (fd800.drv[unit].log_cylinder[head] == cylinder)
232   {
233      /*fd800.stat_reg |= status_OP_complete;*/
234      return FALSE;
235   }
236   for (retries=0; retries<10; retries++)
237   {   /* seek to requested track */
238      floppy_drive_seek(&fd800.drv[unit].img->device(), cylinder-fd800.drv[unit].log_cylinder[head]);
239      /* update physical track position */
240      if (fd800.drv[unit].phys_cylinder != -1)
241         fd800.drv[unit].phys_cylinder += cylinder-fd800.drv[unit].log_cylinder[head];
242      /* read new track ID */
243      if (!fd800_read_id(unit, head, &fd800.drv[unit].log_cylinder[head], NULL))
244      {
245         fd800.drv[unit].log_cylinder[head] = -1;
246         fd800.stat_reg |= status_ID_not_found;
247         return TRUE;
248      }
249      /* exit if we have reached the requested track */
250      if (fd800.drv[unit].log_cylinder[head] == cylinder)
251      {
252         /*fd800.stat_reg |= status_OP_complete;*/
253         return FALSE;
254      }
255   }
256   /* track not found */
257   fd800.stat_reg |= status_seek_err;
258   return TRUE;
259}
260
261/*
262    Perform restore command
263
264    unit: floppy drive index
265
266    Return FALSE if the restore was successful
267*/
268static int fd800_do_restore(int unit)
269{
270   int seek_count = 0;
271   int seek_complete;
272
273   if (!fd800.drv[unit].img->exists())
274   {
275      fd800.stat_reg |= status_drv_not_ready; /* right??? */
276      return TRUE;
277   }
278
279   /* limit iterations to 76 to prevent an endless loop if the disc is locked */
280   while (!(seek_complete = !floppy_tk00_r(&fd800.drv[unit].img->device())) && (seek_count < 76))
281   {
282      floppy_drive_seek(&fd800.drv[unit].img->device(), -1);
283      seek_count++;
284   }
285   if (! seek_complete)
286   {
287      fd800.drv[unit].phys_cylinder = -1;
288      fd800.stat_reg |= status_seek_err;
289   }
290   else
291   {
292      fd800.drv[unit].phys_cylinder = 0;
293      /*fd800.stat_reg |= status_OP_complete;*/
294   }
295
296   return ! seek_complete;
297}
298
299/*
300    Perform a read operation for one sector
301*/
302static void fd800_do_read(void)
303{
304   int data_id;
305
306   if ((fd800.sector == 0) || (fd800.sector > 26))
307   {
308      fd800.stat_reg |= status_invalid_cmd;
309      return;
310   }
311
312   if (!fd800_find_sector(fd800.unit, fd800.head, fd800.sector, &data_id))
313   {
314      fd800.stat_reg |= status_ID_not_found;
315      return;
316   }
317
318   floppy_drive_read_sector_data(&fd800.drv[fd800.unit].img->device(), fd800.head, data_id, fd800.buf, 128);
319   fd800.buf_pos = 0;
320   fd800.buf_mode = bm_read;
321   fd800.recv_buf = (fd800.buf[fd800.buf_pos<<1] << 8) | fd800.buf[(fd800.buf_pos<<1)+1];
322
323   fd800.stat_reg |= status_XFER_ready;
324   fd800.stat_reg |= status_OP_complete;   /* right??? */
325}
326
327/*
328    Perform a write operation for one sector
329*/
330static void fd800_do_write(void)
331{
332   int data_id;
333
334   if (fd800.drv[fd800.unit].seclen < 64)
335      /* fill with 0s */
336      memset(fd800.buf+(fd800.drv[fd800.unit].seclen<<1), 0, (64-fd800.drv[fd800.unit].seclen)<<1);
337
338   if (!fd800_find_sector(fd800.unit, fd800.head, fd800.sector, &data_id))
339   {
340      fd800.stat_reg |= status_ID_not_found;
341      return;
342   }
343
344   floppy_drive_write_sector_data(&fd800.drv[fd800.unit].img->device(), fd800.head, data_id, fd800.buf, 128, fd800.ddam);
345   fd800.buf_pos = 0;
346   fd800.buf_mode = bm_write;
347
348   fd800.stat_reg |= status_XFER_ready;
349   fd800.stat_reg |= status_OP_complete;   /* right??? */
350}
351
352/*
353    Execute a fdc command
354*/
355static void fd800_do_cmd(void)
356{
357   int unit;
358   int cylinder;
359   int head;
360   int seclen;
361   int sector;
362
363
364   if (fd800.buf_mode != bm_off)
365   {   /* All commands in the midst of read or write are interpreted as Stop */
366      unit = (fd800.cmd_reg >> 10) & 3;
367
368      /* reset status */
369      fd800.stat_reg = unit << status_unit_shift;
370
371      fd800.buf_pos = 0;
372      fd800.buf_mode = bm_off;
373
374      fd800.stat_reg |= status_OP_complete;
375
376      fd800.stat_reg |= status_interrupt;
377      fd800_field_interrupt();
378
379      return;
380   }
381
382   switch (fd800.cmd_reg >> 12)
383   {
384   case 0:     /* select
385                    bits 16-25: 0s
386                    bits 26-27: unit number (0-3) */
387      unit = (fd800.cmd_reg >> 10) & 3;
388
389      /* reset status */
390      fd800.stat_reg = unit << status_unit_shift;
391
392      if (!fd800.drv[unit].img->exists())
393         fd800.stat_reg |= status_drv_not_ready; /* right??? */
394      else if (fd800.drv[unit].img->is_readonly())
395         fd800.stat_reg |= status_write_prot;
396      else
397         fd800.stat_reg |= status_OP_complete;
398
399      fd800.stat_reg |= status_interrupt;
400      fd800_field_interrupt();
401      break;
402
403   case 1:     /* seek
404                    bits 16-22: cylinder number (0-76)
405                    bits 23-24: 0s
406                    bits 25: head number (1=upper)
407                    bits 26-27: unit number (0-3) */
408      unit = (fd800.cmd_reg >> 10) & 3;
409      head = (fd800.cmd_reg >> 9) & 1;
410      cylinder = fd800.cmd_reg & 0x7f;
411
412      /* reset status */
413      fd800.stat_reg = unit << status_unit_shift;
414
415      if (!fd800_do_seek(unit, cylinder, head))
416         fd800.stat_reg |= status_OP_complete;
417
418      fd800.stat_reg |= status_interrupt;
419      fd800_field_interrupt();
420      break;
421
422   case 2:     /* restore
423                    bits 16-25: 0s
424                    bits 26-27: unit number (0-3) */
425      unit = (fd800.cmd_reg >> 10) & 3;
426
427      /* reset status */
428      fd800.stat_reg = unit << status_unit_shift;
429
430      if (!fd800_do_restore(unit))
431         fd800.stat_reg |= status_OP_complete;
432
433      fd800.stat_reg |= status_interrupt;
434      fd800_field_interrupt();
435      break;
436
437   case 3:     /* sector length
438                    bits 16-22: sector word count (0-64)
439                    bits 23-25: 0s
440                    bits 26-27: unit number (0-3) */
441      unit = (fd800.cmd_reg >> 10) & 3;
442      seclen = fd800.cmd_reg & 0x7f;
443
444      /* reset status */
445      fd800.stat_reg = unit << status_unit_shift;
446
447      if ((seclen > 64) || (seclen == 0))
448      {
449         fd800.stat_reg |= status_invalid_cmd;
450      }
451      else
452      {
453         fd800.drv[unit].seclen = seclen;
454         fd800.stat_reg |= status_OP_complete;
455      }
456
457      fd800.stat_reg |= status_interrupt;
458      fd800_field_interrupt();
459      break;
460
461   case 4:     /* read
462                    bits 16-20: sector number (1-26)
463                    bits 21-23: 0s
464                    bit 24: no sequential sectoring (1=active)
465                    bit 25: head number (1=upper)
466                    bits 26-27: unit number (0-3) */
467      unit = (fd800.cmd_reg >> 10) & 3;
468      head = (fd800.cmd_reg >> 9) & 1;
469      /*non_seq_mode = (fd800.cmd_reg >> 8) & 1;*/
470      sector = fd800.cmd_reg & 0x1f;
471
472      fd800.unit = unit;
473      fd800.head = head;
474      fd800.sector = sector;
475      /*fd800.non_seq_mode = non_seq_mode;*/
476
477      /* reset status */
478      fd800.stat_reg = unit << status_unit_shift;
479
480      fd800_do_read();
481
482      fd800.stat_reg |= status_interrupt;
483      fd800_field_interrupt();
484      break;
485
486   case 5:     /* read ID
487                    bits 16-24: 0s
488                    bit 25: head number (1=upper)
489                    bits 26-27: unit number (0-3) */
490      unit = (fd800.cmd_reg >> 10) & 3;
491      head = (fd800.cmd_reg >> 9) & 1;
492
493      /* reset status */
494      fd800.stat_reg = unit << status_unit_shift;
495
496      if (!fd800_read_id(unit, head, &cylinder, &sector))
497      {
498         fd800.stat_reg |= status_ID_not_found;
499      }
500      else
501      {
502         fd800.recv_buf = (cylinder << 8) | sector;
503         fd800.stat_reg |= status_OP_complete;
504      }
505
506      fd800.stat_reg |= status_interrupt;
507      fd800_field_interrupt();
508      break;
509
510   case 6:     /* read unformatted
511                    bits 16-20: sector number (1-26)
512                    bits 21-24: 0s
513                    bit 25: head number (1=upper)
514                    bits 26-27: unit number (0-3) */
515      /* ... */
516      break;
517
518   case 7:     /* write
519                    bits 16-20: sector number (1-26)
520                    bits 21-24: 0s
521                    bit 25: head number (1=upper)
522                    bits 26-27: unit number (0-3) */
523      unit = (fd800.cmd_reg >> 10) & 3;
524      head = (fd800.cmd_reg >> 9) & 1;
525      sector = fd800.cmd_reg & 0x1f;
526
527      /* reset status */
528      fd800.stat_reg = unit << status_unit_shift;
529
530      if ((fd800.sector == 0) || (fd800.sector > 26))
531      {
532         fd800.stat_reg |= status_invalid_cmd;
533      }
534      else
535      {
536         fd800.unit = unit;
537         fd800.head = head;
538         fd800.sector = sector;
539         fd800.ddam = 0;
540
541         fd800.buf_pos = 0;
542         fd800.buf_mode = bm_write;
543         fd800.stat_reg |= status_XFER_ready;
544         fd800.stat_reg |= status_OP_complete;   /* right??? */
545      }
546
547      fd800.stat_reg |= status_interrupt;
548      fd800_field_interrupt();
549      break;
550
551   case 8:     /* write delete
552                    bits 16-20: sector number (1-26)
553                    bits 21-24: 0s
554                    bit 25: head number (1=upper)
555                    bits 26-27: unit number (0-3) */
556      unit = (fd800.cmd_reg >> 10) & 3;
557      head = (fd800.cmd_reg >> 9) & 1;
558      sector = fd800.cmd_reg & 0x1f;
559
560      /* reset status */
561      fd800.stat_reg = unit << status_unit_shift;
562
563      if ((fd800.sector == 0) || (fd800.sector > 26))
564      {
565         fd800.stat_reg |= status_invalid_cmd;
566      }
567      else
568      {
569         fd800.unit = unit;
570         fd800.head = head;
571         fd800.sector = sector;
572         fd800.ddam = 1;
573
574         fd800.buf_pos = 0;
575         fd800.buf_mode = bm_write;
576         fd800.stat_reg |= status_XFER_ready;
577         fd800.stat_reg |= status_OP_complete;   /* right??? */
578      }
579
580      fd800.stat_reg |= status_interrupt;
581      fd800_field_interrupt();
582      break;
583
584   case 9:     /* format track
585                    bits 16-23: track ID (0-255, normally current cylinder index, or 255 for bad track)
586                    bit 24: verify only (1 - verify, 0 - format & verify)
587                    bit 25: head number (1=upper)
588                    bits 26-27: unit number (0-3) */
589      /* ... */
590      break;
591
592   case 10:    /* load int mask
593                    bit 16: bad mask for interrupt (0 = unmask or enable interrupt)
594                    bits 17-27: 0s */
595      fd800.interrupt_f_f = fd800.cmd_reg & 1;
596      fd800_field_interrupt();
597      break;
598
599   case 11:    /* stop
600                    bits 16-25: 0s
601                    bits 26-27: unit number (0-3) */
602      unit = (fd800.cmd_reg >> 10) & 3;
603
604      /* reset status */
605      fd800.stat_reg = unit << status_unit_shift;
606
607      fd800.stat_reg |= status_OP_complete;
608
609      fd800.stat_reg |= status_interrupt;
610      fd800_field_interrupt();
611      break;
612
613   case 12:    /* step head
614                    bits 16-22: track number (0-76)
615                    bits 23-25: 0s
616                    bits 26-27: unit number (0-3) */
617      unit = (fd800.cmd_reg >> 10) & 3;
618      cylinder = fd800.cmd_reg & 0x7f;
619
620      if (cylinder > 76)
621      {
622         fd800.stat_reg |= status_invalid_cmd;
623      }
624      else if ((fd800.drv[unit].phys_cylinder != -1) || (!fd800_do_restore(unit)))
625      {
626         floppy_drive_seek(&fd800.drv[unit].img->device(), cylinder-fd800.drv[unit].phys_cylinder);
627         fd800.stat_reg |= status_OP_complete;
628      }
629
630      fd800.stat_reg |= status_interrupt;
631      fd800_field_interrupt();
632      break;
633
634   case 13:    /* maintenance commands
635                    bits 16-23: according to extended command code
636                    bits 24-27: extended command code (0-7) */
637      switch ((fd800.cmd_reg >> 8) & 15)
638      {
639      case 0: /* reset
640                    bits 16-23: 0s */
641         /* ... */
642         break;
643      case 1: /* retry inhibit
644                    bits 16-23: 0s */
645         /* ... */
646         break;
647      case 2: /* LED test
648                    bit 16: 1
649                    bits 17-19: 0s
650                    bit 20: LED #2 enable
651                    bit 21: LED #3 enable
652                    bit 22: LED #4 enable
653                    bit 23: enable LEDs */
654         /* ... */
655         break;
656      case 3: /* program error (a.k.a. invalid command)
657                    bits 16-23: 0s */
658         /* ... */
659         break;
660      case 4: /* memory read
661                    bits 16-20: controller memory address (shifted left by 8 to generate 9900 address)
662                    bits 21-23: 0s */
663         /* ... */
664         break;
665      case 5: /* RAM load
666                    bit 16: 0
667                    bits 17-23: RAM offset (shifted left by 1 and offset by >1800 to generate 9900 address) */
668         /* ... */
669         break;
670      case 6: /* RAM run
671                    bit 16: 0
672                    bits 17-23: RAM offset (shifted left by 1 and offset by >1800 to generate 9900 address) */
673         /* ... */
674         break;
675      case 7: /* power up simulation
676                    bits 16-23: 0s */
677         /* ... */
678         break;
679      }
680      /* ... */
681      break;
682
683   case 14:    /* IPL
684                    bits 16-22: track number (0-76)
685                    bit 23: 0
686                    bit 24: no sequential sectoring (1=active)
687                    bit 25: head number (1=upper)
688                    bits 26-27: unit number (0-3) */
689      unit = (fd800.cmd_reg >> 10) & 3;
690      head = (fd800.cmd_reg >> 9) & 1;
691      /*non_seq_mode = (fd800.cmd_reg >> 8) & 1;*/
692      cylinder = fd800.cmd_reg & 0x7f;
693
694      if (!fd800_do_seek(unit, cylinder, head))
695      {
696         fd800.unit = unit;
697         fd800.head = head;
698         fd800.sector = 1;
699         /*fd800.non_seq_mode = non_seq_mode;*/
700
701         fd800_do_read();
702      }
703
704      fd800.stat_reg |= status_interrupt;
705      fd800_field_interrupt();
706      break;
707
708   case 15:    /* Clear Status port
709                    bits 16-27: 0s */
710      fd800.stat_reg = 0;
711      fd800_field_interrupt();
712      break;
713   }
714}
715
716/*
717    read one CRU bit
718
719    0-15: receive buffer
720    16-31: status:
721        16: OP complete (1 -> complete???)
722        17: Xfer ready (XFER) (1 -> ready???)
723        18: drive not ready
724        19: data check error
725        20: seek error/??????
726        21 invalid command/??????
727        22: no address mark found/??????
728        23: equipment check error/??????
729        24: ID check error
730        25: ID not found
731        26: Controller busy (CTLBSY) (0 -> controller is ready)
732        27: write protect
733        28: deleted sector detected
734        29: unit LSB
735        30: unit MSB
736        31: Interrupt (CBUSY???) (1 -> controller is ready)
737*/
738   READ8_HANDLER(fd800_cru_r)
739{
740   int reply = 0;
741
742   switch (offset)
743   {
744   case 0:
745   case 1:
746      /* receive buffer */
747      reply = fd800.recv_buf >> (offset*8);
748      break;
749
750   case 2:
751   case 3:
752      /* status register */
753      reply = fd800.stat_reg >> ((offset-2)*8);
754      break;
755   }
756
757   return reply;
758}
759
760/*
761    write one CRU bit
762
763    0-15: controller data word (transmit buffer)
764    16-31: controller command word (command register)
765    16-23: parameter value
766    24: flag bit/extended command code
767    25: head select/extended command code
768    26: FD unit number LSB/extended command code
769    27: FD unit number MSB/extended command code
770    28-31: command code
771*/
772WRITE8_HANDLER(fd800_cru_w)
773{
774   switch (offset)
775   {
776   case 0:
777   case 1:
778   case 2:
779   case 3:
780   case 4:
781   case 5:
782   case 6:
783   case 7:
784   case 8:
785   case 9:
786   case 10:
787   case 11:
788   case 12:
789   case 13:
790   case 14:
791   case 15:
792      /* transmit buffer */
793      if (data)
794         fd800.xmit_buf |= 1 << offset;
795      else
796         fd800.xmit_buf &= ~(1 << offset);
797      if (offset == 15)
798      {
799         switch (fd800.buf_mode)
800         {
801         case bm_off:
802            break;
803         case bm_read:
804            fd800.buf_pos++;
805            if (fd800.buf_pos == fd800.drv[fd800.unit].seclen)
806            {   /* end of sector */
807               if (fd800.sector == 26)
808               {   /* end of track -> end command (right???) */
809                  fd800.stat_reg &= ~status_XFER_ready;
810                  fd800.stat_reg |= status_OP_complete;
811                  fd800.stat_reg |= status_interrupt;
812                  fd800.buf_mode = bm_off;
813                  fd800_field_interrupt();
814               }
815               else
816               {   /* read next sector */
817                  fd800.sector++;
818                  fd800.stat_reg &= ~status_XFER_ready | status_OP_complete | status_interrupt;
819                  fd800_do_read();
820                  fd800.stat_reg |= status_interrupt;
821                  fd800_field_interrupt();
822               }
823            }
824            else
825               fd800.recv_buf = (fd800.buf[fd800.buf_pos<<1] << 8) | fd800.buf[(fd800.buf_pos<<1)+1];
826            break;
827
828         case bm_write:
829            fd800.buf[fd800.buf_pos<<1] = fd800.xmit_buf >> 8;
830            fd800.buf[(fd800.buf_pos<<1)+1] = fd800.xmit_buf & 0xff;
831            fd800.buf_pos++;
832            if (fd800.buf_pos == fd800.drv[fd800.unit].seclen)
833            {   /* end of sector */
834               fd800_do_write();
835               if (fd800.sector == 26)
836               {
837                  /* end of track -> end command (right???) */
838                  fd800.stat_reg &= ~status_XFER_ready;
839                  fd800.stat_reg |= status_OP_complete;
840                  fd800.stat_reg |= status_interrupt;
841                  fd800.buf_mode = bm_off;
842                  fd800_field_interrupt();
843               }
844               else
845               {   /* increment to next sector */
846                  fd800.sector++;
847                  fd800.stat_reg |= status_interrupt;
848                  fd800_field_interrupt();
849               }
850            }
851            break;
852         }
853      }
854      break;
855
856   case 16:
857   case 17:
858   case 18:
859   case 19:
860   case 20:
861   case 21:
862   case 22:
863   case 23:
864   case 24:
865   case 25:
866   case 26:
867   case 27:
868   case 28:
869   case 29:
870   case 30:
871   case 31:
872      /* command register */
873      if (data)
874         fd800.cmd_reg |= 1 << (offset-16);
875      else
876         fd800.cmd_reg &= ~(1 << (offset-16));
877      if (offset == 31)
878         fd800_do_cmd();
879      break;
880   }
881}
Property changes on: trunk/src/mess/machine/ti99/990_dk.c
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/mess/machine/ti99/990_hd.h
r0r26091
1/*
2    990_hd.h: include file for 990_hd.c
3*/
4#ifndef __990_HD_H_
5#define __990_HD_H_
6
7#include "imagedev/harddriv.h"
8
9MACHINE_START( ti990_hdc );
10
11void ti990_hdc_init(running_machine &machine, void (*interrupt_callback)(running_machine &machine, int state));
12
13DECLARE_READ16_HANDLER(ti990_hdc_r);
14DECLARE_WRITE16_HANDLER(ti990_hdc_w);
15
16MACHINE_CONFIG_EXTERN( ti990_hdc );
17
18#endif
Property changes on: trunk/src/mess/machine/ti99/990_hd.h
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/mess/machine/ti99/990_dk.h
r0r26091
1/*
2    990_dk.h: include file for 990_dk.c
3*/
4LEGACY_FLOPPY_OPTIONS_EXTERN(fd800);
5
6void fd800_machine_init(running_machine &machine, void (*interrupt_callback)(running_machine &machine, int state));
7
8extern  DECLARE_READ8_HANDLER(fd800_cru_r);
9extern DECLARE_WRITE8_HANDLER(fd800_cru_w);
Property changes on: trunk/src/mess/machine/ti99/990_dk.h
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/mess/machine/ti99/990_tap.c
r0r26091
1/*
2    990_tap.c: emulation of a generic ti990 tape controller, for use with
3    TILINE-based TI990 systems (TI990/10, /12, /12LR, /10A, Business system 300
4    and 300A).
5
6    This core will emulate the common feature set found in every tape controller.
7    Most controllers support additional features, but are still compatible with
8    the basic feature set.  I have a little documentation on two specific
9    tape controllers (MT3200 and WD800/WD800A), but I have not tried to emulate
10    controller-specific features.
11
12
13    Long description: see 2234398-9701 and 2306140-9701.
14
15
16    Raphael Nabet 2002
17*/
18/*
19    Image encoding:
20
21
22    2 bytes: record len - little-endian
23    2 bytes: always 0s (length MSBs?)
24    len bytes: data
25    2 bytes: record len - little-endian
26    2 bytes: always 0s (length MSBs?)
27
28    4 0s: EOF mark
29*/
30
31#include "emu.h"
32#include "990_tap.h"
33#include "devlegcy.h"
34
35
36static void update_interrupt(device_t *device);
37
38#define MAX_TAPE_UNIT 4
39
40struct tape_unit_t
41{
42   device_image_interface *img;        /* image descriptor */
43   unsigned int bot : 1;   /* TRUE if we are at the beginning of tape */
44   unsigned int eot : 1;   /* TRUE if we are at the end of tape */
45   unsigned int wp : 1;    /* TRUE if tape is write-protected */
46};
47
48struct tap_990_t
49{
50   UINT16 w[8];
51
52   const ti990_tpc_interface *intf;
53
54   tape_unit_t t[MAX_TAPE_UNIT];
55};
56
57struct ti990_tape_t
58{
59   int dummy;
60};
61
62enum
63{
64   w0_offline          = 0x8000,
65   w0_BOT              = 0x4000,
66   w0_EOR              = 0x2000,
67   w0_EOF              = 0x1000,
68   w0_EOT              = 0x0800,
69   w0_write_ring       = 0x0400,
70   w0_tape_rewinding   = 0x0200,
71   w0_command_timeout  = 0x0100,
72
73   w0_rewind_status    = 0x00f0,
74   w0_rewind_mask      = 0x000f,
75
76   w6_unit0_sel        = 0x8000,
77   w6_unit1_sel        = 0x4000,
78   w6_unit2_sel        = 0x2000,
79   w6_unit3_sel        = 0x1000,
80   w6_command          = 0x0f00,
81
82   w7_idle             = 0x8000,
83   w7_complete         = 0x4000,
84   w7_error            = 0x2000,
85   w7_int_enable       = 0x1000,
86   w7_PE_format        = 0x0200,
87   w7_abnormal_completion  = 0x0100,
88   w7_interface_parity_err = 0x0080,
89   w7_err_correction_enabled   = 0x0040,
90   w7_hard_error           = 0x0020,
91   w7_tiline_parity_err    = 0x0010,
92   w7_tiline_timing_err    = 0x0008,
93   w7_tiline_timeout_err   = 0x0004,
94   /*w7_format_error       = 0x0002,*/
95   w7_tape_error       = 0x0001
96};
97
98static const UINT16 w_mask[8] =
99{
100   0x000f,     /* Controllers should prevent overwriting of w0 status bits, and I know
101                that some controllers do so. */
102   0xffff,
103   0xffff,
104   0xffff,
105   0xffff,
106   0xffff,
107   0xffff,
108   0xf3ff      /* Don't overwrite reserved bits */
109};
110
111static int tape_get_id(device_t *image)
112{
113   int drive =0;
114   if (strcmp(image->tag(), ":tape0") == 0) drive = 0;
115   if (strcmp(image->tag(), ":tape1") == 0) drive = 1;
116   if (strcmp(image->tag(), ":tape2") == 0) drive = 2;
117   if (strcmp(image->tag(), ":tape3") == 0) drive = 3;
118   return drive;
119}
120
121/*****************************************************************************
122    INLINE FUNCTIONS
123*****************************************************************************/
124INLINE tap_990_t *get_safe_token(device_t *device)
125{
126   assert(device != NULL);
127   assert(device->type() == TI990_TAPE_CTRL);
128
129   return (tap_990_t *)downcast<tap_990_device *>(device)->token();
130}
131
132
133/*
134    Parse the tape select lines, and return the corresponding tape unit.
135    (-1 if none)
136*/
137static int cur_tape_unit(device_t *device)
138{
139   int reply;
140   tap_990_t *tpc = get_safe_token(device);
141
142   if (tpc->w[6] & w6_unit0_sel)
143      reply = 0;
144   else if (tpc->w[6] & w6_unit1_sel)
145      reply = 1;
146   else if (tpc->w[6] & w6_unit2_sel)
147      reply = 2;
148   else if (tpc->w[6] & w6_unit3_sel)
149      reply = 3;
150   else
151      reply = -1;
152
153   if (reply >= MAX_TAPE_UNIT)
154      reply = -1;
155
156   return reply;
157}
158
159/*
160    Update interrupt state
161*/
162static void update_interrupt(device_t *device)
163{
164   tap_990_t *tpc = get_safe_token(device);
165   if (tpc->intf->interrupt_callback)
166      (*tpc->intf->interrupt_callback)(device->machine(), (tpc->w[7] & w7_idle)
167                           && (((tpc->w[7] & w7_int_enable) && (tpc->w[7] & (w7_complete | w7_error)))
168                              || ((tpc->w[0] & ~(tpc->w[0] >> 4)) & w0_rewind_mask)));
169}
170
171/*
172    Handle the read binary forward command: read the next record on tape.
173*/
174static void cmd_read_binary_forward(device_t *device)
175{
176   UINT8 buffer[256];
177   int reclen;
178
179   int dma_address;
180   int char_count;
181   int read_offset;
182
183   int rec_count = 0;
184   int chunk_len;
185   int bytes_to_read;
186   int bytes_read;
187   int i;
188   tap_990_t *tpc = get_safe_token(device);
189   int tap_sel = cur_tape_unit(device);
190
191   if (tap_sel == -1)
192   {
193      /* No idea what to report... */
194      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
195      update_interrupt(device);
196      return;
197   }
198   else if (! tpc->t[tap_sel].img->exists())
199   {   /* offline */
200      tpc->w[0] |= w0_offline;
201      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
202      update_interrupt(device);
203      return;
204   }
205#if 0
206   else if (0)
207   {   /* rewind in progress */
208      tpc->w[0] |= 0x80 >> tap_sel;
209      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
210      update_interrupt(device);
211      return;
212   }
213#endif
214
215   tpc->t[tap_sel].bot = 0;
216
217   dma_address = ((((int) tpc->w[6]) << 16) | tpc->w[5]) & 0x1ffffe;
218   char_count = tpc->w[4];
219   read_offset = tpc->w[3];
220
221   bytes_read = tpc->t[tap_sel].img->fread(buffer, 4);
222   if (bytes_read != 4)
223   {
224      if (bytes_read == 0)
225      {   /* legitimate EOF */
226         tpc->t[tap_sel].eot = 1;
227         tpc->w[0] |= w0_EOT;    /* or should it be w0_command_timeout? */
228         tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
229         update_interrupt(device);
230         goto update_registers;
231      }
232      else
233      {   /* illegitimate EOF */
234         /* No idea what to report... */
235         /* eject tape to avoid catastrophes */
236         logerror("Tape error\n");
237         tpc->t[tap_sel].img->unload();
238         tpc->w[0] |= w0_offline;
239         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
240         update_interrupt(device);
241         goto update_registers;
242      }
243   }
244   reclen = (((int) buffer[1]) << 8) | buffer[0];
245   if (buffer[2] || buffer[3])
246   {   /* no idea what these bytes mean */
247      logerror("Tape error\n");
248      logerror("Tape format looks gooofy\n");
249      /* eject tape to avoid catastrophes */
250      tpc->t[tap_sel].img->unload();
251      tpc->w[0] |= w0_offline;
252      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
253      update_interrupt(device);
254      goto update_registers;
255   }
256
257   /* test for EOF mark */
258   if (reclen == 0)
259   {
260      logerror("read binary forward: found EOF, requested %d\n", char_count);
261      tpc->w[0] |= w0_EOF;
262      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
263      update_interrupt(device);
264      goto update_registers;
265   }
266
267   logerror("read binary forward: rec length %d, requested %d\n", reclen, char_count);
268
269   rec_count = reclen;
270
271   /* skip up to read_offset bytes */
272   chunk_len = (read_offset > rec_count) ? rec_count : read_offset;
273
274   if (tpc->t[tap_sel].img->fseek(chunk_len, SEEK_CUR))
275   {   /* eject tape */
276      logerror("Tape error\n");
277      tpc->t[tap_sel].img->unload();
278      tpc->w[0] |= w0_offline;
279      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
280      update_interrupt(device);
281      goto update_registers;
282   }
283
284   rec_count -= chunk_len;
285   read_offset -= chunk_len;
286   if (read_offset)
287   {
288      tpc->w[0] |= w0_EOR;
289      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
290      update_interrupt(device);
291      goto skip_trailer;
292   }
293
294   /* read up to char_count bytes */
295   chunk_len = (char_count > rec_count) ? rec_count : char_count;
296
297   for (; chunk_len>0; )
298   {
299      bytes_to_read = (chunk_len < sizeof(buffer)) ? chunk_len : sizeof(buffer);
300      bytes_read = tpc->t[tap_sel].img->fread(buffer, bytes_to_read);
301
302      if (bytes_read & 1)
303      {
304         buffer[bytes_read] = 0xff;
305      }
306
307      /* DMA */
308      for (i=0; i<bytes_read; i+=2)
309      {
310         device->machine().device("maincpu")->memory().space(AS_PROGRAM).write_word(dma_address, (((int) buffer[i]) << 8) | buffer[i+1]);
311         dma_address = (dma_address + 2) & 0x1ffffe;
312      }
313
314      rec_count -= bytes_read;
315      char_count -= bytes_read;
316      chunk_len -= bytes_read;
317
318      if (bytes_read != bytes_to_read)
319      {   /* eject tape */
320         logerror("Tape error\n");
321         tpc->t[tap_sel].img->unload();
322         tpc->w[0] |= w0_offline;
323         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
324         update_interrupt(device);
325         goto update_registers;
326      }
327   }
328
329   if (char_count)
330   {
331      tpc->w[0] |= w0_EOR;
332      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
333      update_interrupt(device);
334      goto skip_trailer;
335   }
336
337   if (rec_count)
338   {   /* skip end of record */
339      if (tpc->t[tap_sel].img->fseek(rec_count, SEEK_CUR))
340      {   /* eject tape */
341         logerror("Tape error\n");
342         tpc->t[tap_sel].img->unload();
343         tpc->w[0] |= w0_offline;
344         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
345         update_interrupt(device);
346         goto update_registers;
347      }
348   }
349
350skip_trailer:
351   if (tpc->t[tap_sel].img->fread(buffer, 4) != 4)
352   {   /* eject tape */
353      logerror("Tape error\n");
354      tpc->t[tap_sel].img->unload();
355      tpc->w[0] |= w0_offline;
356      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
357      update_interrupt(device);
358      goto update_registers;
359   }
360
361   if (reclen != ((((int) buffer[1]) << 8) | buffer[0]))
362   {   /* eject tape */
363      logerror("Tape error\n");
364      tpc->t[tap_sel].img->unload();
365      tpc->w[0] |= w0_offline;
366      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
367      update_interrupt(device);
368      goto update_registers;
369   }
370   if (buffer[2] || buffer[3])
371   {   /* no idea what these bytes mean */
372      logerror("Tape error\n");
373      logerror("Tape format looks gooofy\n");
374      /* eject tape to avoid catastrophes */
375      tpc->t[tap_sel].img->unload();
376      tpc->w[0] |= w0_offline;
377      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
378      update_interrupt(device);
379      goto update_registers;
380   }
381
382   if (! (tpc->w[7] & w7_error))
383   {
384      tpc->w[7] |= w7_idle | w7_complete;
385      update_interrupt(device);
386   }
387
388update_registers:
389   tpc->w[1] = rec_count & 0xffff;
390   tpc->w[2] = (rec_count >> 8) & 0xff;
391   tpc->w[3] = read_offset;
392   tpc->w[4] = char_count;
393   tpc->w[5] = (dma_address >> 1) & 0xffff;
394   tpc->w[6] = (tpc->w[6] & 0xffe0) | ((dma_address >> 17) & 0xf);
395}
396
397/*
398    Handle the record skip forward command: skip a specified number of records.
399*/
400static void cmd_record_skip_forward(device_t *device)
401{
402   UINT8 buffer[4];
403   int reclen;
404
405   int record_count;
406   int bytes_read;
407   tap_990_t *tpc = get_safe_token(device);
408   int tap_sel = cur_tape_unit(device);
409
410   if (tap_sel == -1)
411   {
412      /* No idea what to report... */
413      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
414      update_interrupt(device);
415      return;
416   }
417   else if (! tpc->t[tap_sel].img->exists())
418   {   /* offline */
419      tpc->w[0] |= w0_offline;
420      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
421      update_interrupt(device);
422      return;
423   }
424#if 0
425   else if (0)
426   {   /* rewind in progress */
427      tpc->w[0] |= 0x80 >> tap_sel;
428      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
429      update_interrupt(device);
430      return;
431   }
432#endif
433
434   record_count = tpc->w[4];
435
436   if (record_count)
437      tpc->t[tap_sel].bot = 0;
438
439   while (record_count > 0)
440   {
441      bytes_read = tpc->t[tap_sel].img->fread(buffer, 4);
442      if (bytes_read != 4)
443      {
444         if (bytes_read == 0)
445         {   /* legitimate EOF */
446            tpc->t[tap_sel].eot = 1;
447            tpc->w[0] |= w0_EOT;    /* or should it be w0_command_timeout? */
448            tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
449            update_interrupt(device);
450            goto update_registers;
451         }
452         else
453         {   /* illegitimate EOF */
454            /* No idea what to report... */
455            /* eject tape to avoid catastrophes */
456            tpc->t[tap_sel].img->unload();
457            tpc->w[0] |= w0_offline;
458            tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
459            update_interrupt(device);
460            goto update_registers;
461         }
462      }
463      reclen = (((int) buffer[1]) << 8) | buffer[0];
464      if (buffer[2] || buffer[3])
465      {   /* no idea what these bytes mean */
466         logerror("Tape format looks gooofy\n");
467         /* eject tape to avoid catastrophes */
468         tpc->t[tap_sel].img->unload();
469         tpc->w[0] |= w0_offline;
470         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
471         update_interrupt(device);
472         goto update_registers;
473      }
474
475      /* test for EOF mark */
476      if (reclen == 0)
477      {
478         logerror("record skip forward: found EOF\n");
479         tpc->w[0] |= w0_EOF;
480         tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
481         update_interrupt(device);
482         goto update_registers;
483      }
484
485      /* skip record data */
486      if (tpc->t[tap_sel].img->fseek(reclen, SEEK_CUR))
487      {   /* eject tape */
488         tpc->t[tap_sel].img->unload();
489         tpc->w[0] |= w0_offline;
490         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
491         update_interrupt(device);
492         goto update_registers;
493      }
494
495      if (tpc->t[tap_sel].img->fread(buffer, 4) != 4)
496      {   /* eject tape */
497         tpc->t[tap_sel].img->unload();
498         tpc->w[0] |= w0_offline;
499         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
500         update_interrupt(device);
501         goto update_registers;
502      }
503
504      if (reclen != ((((int) buffer[1]) << 8) | buffer[0]))
505      {   /* eject tape */
506         tpc->t[tap_sel].img->unload();
507         tpc->w[0] |= w0_offline;
508         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
509         update_interrupt(device);
510         goto update_registers;
511      }
512      if (buffer[2] || buffer[3])
513      {   /* no idea what these bytes mean */
514         logerror("Tape format looks gooofy\n");
515         /* eject tape to avoid catastrophes */
516         tpc->t[tap_sel].img->unload();
517         tpc->w[0] |= w0_offline;
518         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
519         update_interrupt(device);
520         goto update_registers;
521      }
522
523      record_count--;
524   }
525
526   tpc->w[7] |= w7_idle | w7_complete;
527   update_interrupt(device);
528
529update_registers:
530   tpc->w[4] = record_count;
531}
532
533/*
534    Handle the record skip reverse command: skip a specified number of records backwards.
535*/
536static void cmd_record_skip_reverse(device_t *device)
537{
538   UINT8 buffer[4];
539   int reclen;
540
541   int record_count;
542
543   int bytes_read;
544   tap_990_t *tpc = get_safe_token(device);
545   int tap_sel = cur_tape_unit(device);
546
547   if (tap_sel == -1)
548   {
549      /* No idea what to report... */
550      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
551      update_interrupt(device);
552      return;
553   }
554   else if (! tpc->t[tap_sel].img->exists())
555   {   /* offline */
556      tpc->w[0] |= w0_offline;
557      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
558      update_interrupt(device);
559      return;
560   }
561#if 0
562   else if (0)
563   {   /* rewind in progress */
564      tpc->w[0] |= 0x80 >> tap_sel;
565      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
566      update_interrupt(device);
567      return;
568   }
569#endif
570
571   record_count = tpc->w[4];
572
573   if (record_count)
574      tpc->t[tap_sel].eot = 0;
575
576   while (record_count > 0)
577   {
578      if (tpc->t[tap_sel].img->ftell() == 0)
579      {   /* bot */
580         tpc->t[tap_sel].bot = 1;
581         tpc->w[0] |= w0_BOT;
582         tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
583         update_interrupt(device);
584         goto update_registers;
585      }
586      if (tpc->t[tap_sel].img->fseek(-4, SEEK_CUR))
587      {   /* eject tape */
588         tpc->t[tap_sel].img->unload();
589         tpc->w[0] |= w0_offline;
590         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
591         update_interrupt(device);
592         goto update_registers;
593      }
594      bytes_read = tpc->t[tap_sel].img->fread(buffer, 4);
595      if (bytes_read != 4)
596      {
597         /* illegitimate EOF */
598         /* No idea what to report... */
599         /* eject tape to avoid catastrophes */
600         tpc->t[tap_sel].img->unload();
601         tpc->w[0] |= w0_offline;
602         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
603         update_interrupt(device);
604         goto update_registers;
605      }
606      reclen = (((int) buffer[1]) << 8) | buffer[0];
607      if (buffer[2] || buffer[3])
608      {   /* no idea what these bytes mean */
609         logerror("Tape format looks gooofy\n");
610         /* eject tape to avoid catastrophes */
611         tpc->t[tap_sel].img->unload();
612         tpc->w[0] |= w0_offline;
613         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
614         update_interrupt(device);
615         goto update_registers;
616      }
617
618      /* look for EOF mark */
619      if (reclen == 0)
620      {
621         logerror("record skip reverse: found EOF\n");
622         if (tpc->t[tap_sel].img->fseek(-4, SEEK_CUR))
623         {   /* eject tape */
624            tpc->t[tap_sel].img->unload();
625            tpc->w[0] |= w0_offline;
626            tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
627            update_interrupt(device);
628            goto update_registers;
629         }
630         tpc->w[0] |= w0_EOF;
631         tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
632         update_interrupt(device);
633         goto update_registers;
634      }
635
636      if (tpc->t[tap_sel].img->fseek(-reclen-8, SEEK_CUR))
637      {   /* eject tape */
638         tpc->t[tap_sel].img->unload();
639         tpc->w[0] |= w0_offline;
640         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
641         update_interrupt(device);
642         goto update_registers;
643      }
644
645      if (tpc->t[tap_sel].img->fread(buffer, 4) != 4)
646      {   /* eject tape */
647         tpc->t[tap_sel].img->unload();
648         tpc->w[0] |= w0_offline;
649         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
650         update_interrupt(device);
651         goto update_registers;
652      }
653      if (reclen != ((((int) buffer[1]) << 8) | buffer[0]))
654      {   /* eject tape */
655         tpc->t[tap_sel].img->unload();
656         tpc->w[0] |= w0_offline;
657         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
658         update_interrupt(device);
659         goto update_registers;
660      }
661      if (buffer[2] || buffer[3])
662      {   /* no idea what these bytes mean */
663         logerror("Tape format looks gooofy\n");
664         /* eject tape to avoid catastrophes */
665         tpc->t[tap_sel].img->unload();
666         tpc->w[0] |= w0_offline;
667         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
668         update_interrupt(device);
669         goto update_registers;
670      }
671
672      if (tpc->t[tap_sel].img->fseek(-4, SEEK_CUR))
673      {   /* eject tape */
674         tpc->t[tap_sel].img->unload();
675         tpc->w[0] |= w0_offline;
676         tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
677         update_interrupt(device);
678         goto update_registers;
679      }
680
681      record_count--;
682   }
683
684   tpc->w[7] |= w7_idle | w7_complete;
685   update_interrupt(device);
686
687update_registers:
688   tpc->w[4] = record_count;
689}
690
691/*
692    Handle the rewind command: rewind to BOT.
693*/
694static void cmd_rewind(device_t *device)
695{
696   tap_990_t *tpc = get_safe_token(device);
697   int tap_sel = cur_tape_unit(device);
698
699   if (tap_sel == -1)
700   {
701      /* No idea what to report... */
702      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
703      update_interrupt(device);
704      return;
705   }
706   else if (! tpc->t[tap_sel].img->exists())
707   {   /* offline */
708      tpc->w[0] |= w0_offline;
709      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
710      update_interrupt(device);
711      return;
712   }
713#if 0
714   else if (0)
715   {   /* rewind in progress */
716      tpc->w[0] |= 0x80 >> tap_sel;
717      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
718      update_interrupt(device);
719      return;
720   }
721#endif
722
723   tpc->t[tap_sel].eot = 0;
724
725   if (tpc->t[tap_sel].img->fseek(0, SEEK_SET))
726   {   /* eject tape */
727      tpc->t[tap_sel].img->unload();
728      tpc->w[0] |= w0_offline;
729      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
730      update_interrupt(device);
731      return;
732   }
733   tpc->t[tap_sel].bot = 1;
734
735   tpc->w[7] |= w7_idle | w7_complete;
736   update_interrupt(device);
737}
738
739/*
740    Handle the rewind and offline command: disable the tape unit.
741*/
742static void cmd_rewind_and_offline(device_t *device)
743{
744   tap_990_t *tpc = get_safe_token(device);
745   int tap_sel = cur_tape_unit(device);
746
747   if (tap_sel == -1)
748   {
749      /* No idea what to report... */
750      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
751      update_interrupt(device);
752      return;
753   }
754   else if (! tpc->t[tap_sel].img->exists())
755   {   /* offline */
756      tpc->w[0] |= w0_offline;
757      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
758      update_interrupt(device);
759      return;
760   }
761#if 0
762   else if (0)
763   {   /* rewind in progress */
764      tpc->w[0] |= 0x80 >> tap_sel;
765      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
766      update_interrupt(device);
767      return;
768   }
769#endif
770
771   /* eject tape */
772   tpc->t[tap_sel].img->unload();
773
774   tpc->w[7] |= w7_idle | w7_complete;
775   update_interrupt(device);
776}
777
778/*
779    Handle the read transport status command: return the current tape status.
780*/
781static void read_transport_status(device_t *device)
782{
783   tap_990_t *tpc = get_safe_token(device);
784   int tap_sel = cur_tape_unit(device);
785
786   if (tap_sel == -1)
787   {
788      /* No idea what to report... */
789      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
790      update_interrupt(device);
791   }
792   else if (! tpc->t[tap_sel].img->exists())
793   {   /* offline */
794      tpc->w[0] |= w0_offline;
795      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
796      update_interrupt(device);
797   }
798#if 0
799   else if (0)
800   {   /* rewind in progress */
801      tpc->w[0] |= /*...*/;
802      tpc->w[7] |= w7_idle | w7_error | w7_tape_error;
803      update_interrupt(device);
804   }
805#endif
806   else
807   {   /* no particular error condition */
808      if (tpc->t[tap_sel].bot)
809         tpc->w[0] |= w0_BOT;
810      if (tpc->t[tap_sel].eot)
811         tpc->w[0] |= w0_EOT;
812      if (tpc->t[tap_sel].wp)
813         tpc->w[0] |= w0_write_ring;
814      tpc->w[7] |= w7_idle | w7_complete;
815      update_interrupt(device);
816   }
817}
818
819/*
820    Parse command code and execute the command.
821*/
822static void execute_command(device_t *device)
823{
824   /* hack */
825   tap_990_t *tpc = get_safe_token(device);
826   tpc->w[0] &= 0xff;
827
828   switch ((tpc->w[6] & w6_command) >> 8)
829   {
830   case 0x00:
831   case 0x0C:
832   case 0x0E:
833      /* NOP */
834      logerror("NOP\n");
835      tpc->w[7] |= w7_idle | w7_complete;
836      update_interrupt(device);
837      break;
838   case 0x01:
839      /* buffer sync: means nothing under emulation */
840      logerror("buffer sync\n");
841      tpc->w[7] |= w7_idle | w7_complete;
842      update_interrupt(device);
843      break;
844   case 0x02:
845      /* write EOF - not emulated */
846      logerror("write EOF\n");
847      /* ... */
848      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
849      update_interrupt(device);
850      break;
851   case 0x03:
852      /* record skip reverse - not fully tested */
853      logerror("record skip reverse\n");
854      cmd_record_skip_reverse(device);
855      break;
856   case 0x04:
857      /* read binary forward */
858      logerror("read binary forward\n");
859      cmd_read_binary_forward(device);
860      break;
861   case 0x05:
862      /* record skip forward - not tested */
863      logerror("record skip forward\n");
864      cmd_record_skip_forward(device);
865      break;
866   case 0x06:
867      /* write binary forward - not emulated */
868      logerror("write binary forward\n");
869      /* ... */
870      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
871      update_interrupt(device);
872      break;
873   case 0x07:
874      /* erase - not emulated */
875      logerror("erase\n");
876      /* ... */
877      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
878      update_interrupt(device);
879      break;
880   case 0x08:
881   case 0x09:
882      /* read transport status */
883      logerror("read transport status\n");
884      read_transport_status(device);
885      break;
886   case 0x0A:
887      /* rewind - not tested */
888      logerror("rewind\n");
889      cmd_rewind(device);
890      break;
891   case 0x0B:
892      /* rewind and offline - not tested */
893      logerror("rewind and offline\n");
894      cmd_rewind_and_offline(device);
895      break;
896   case 0x0F:
897      /* extended control and status - not emulated */
898      logerror("extended control and status\n");
899      /* ... */
900      tpc->w[7] |= w7_idle | w7_error | w7_hard_error;
901      update_interrupt(device);
902      break;
903   }
904}
905
906
907/*
908    Read one register in TPCS space
909*/
910READ16_DEVICE_HANDLER(ti990_tpc_r)
911{
912   tap_990_t *tpc = get_safe_token(device);
913   if (offset < 8)
914      return tpc->w[offset];
915   else
916      return 0;
917}
918
919/*
920    Write one register in TPCS space.  Execute command if w7_idle is cleared.
921*/
922WRITE16_DEVICE_HANDLER(ti990_tpc_w)
923{
924   tap_990_t *tpc = get_safe_token(device);
925   if (offset < 8)
926   {
927      /* write protect if a command is in progress */
928      if (tpc->w[7] & w7_idle)
929      {
930         UINT16 old_data = tpc->w[offset];
931
932         /* Only write writable bits AND honor byte accesses (ha!) */
933         tpc->w[offset] = (tpc->w[offset] & ((~w_mask[offset]) | mem_mask)) | (data & w_mask[offset] & ~mem_mask);
934
935         if ((offset == 0) || (offset == 7))
936            update_interrupt(device);
937
938         if ((offset == 7) && (old_data & w7_idle) && ! (data & w7_idle))
939         {   /* idle has been cleared: start command execution */
940            execute_command(device);
941         }
942      }
943   }
944}
945
946class ti990_tape_image_device : public device_t,
947                           public device_image_interface
948{
949public:
950   // construction/destruction
951   ti990_tape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
952
953   // image-level overrides
954   virtual iodevice_t image_type() const { return IO_MAGTAPE; }
955
956   virtual bool is_readable()  const { return 1; }
957   virtual bool is_writeable() const { return 1; }
958   virtual bool is_creatable() const { return 1; }
959   virtual bool must_be_loaded() const { return 0; }
960   virtual bool is_reset_on_load() const { return 0; }
961   virtual const char *image_interface() const { return NULL; }
962   virtual const char *file_extensions() const { return "tap"; }
963   virtual const option_guide *create_option_guide() const { return NULL; }
964
965   virtual bool call_load();
966   virtual void call_unload();
967protected:
968   // device-level overrides
969   virtual void device_config_complete();
970   virtual void device_start();
971};
972
973const device_type TI990_TAPE = &device_creator<ti990_tape_image_device>;
974
975ti990_tape_image_device::ti990_tape_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
976   : device_t(mconfig, TI990_TAPE, "TI990 Magnetic Tape", tag, owner, clock, "ti990_tape_image", __FILE__),
977      device_image_interface(mconfig, *this)
978{
979}
980
981void ti990_tape_image_device::device_config_complete()
982{
983   update_names();
984}
985
986void ti990_tape_image_device::device_start()
987{
988   tape_unit_t *t;
989   tap_990_t *tpc = get_safe_token(owner());
990   int id = tape_get_id(this);
991
992   t = &tpc->t[id];
993   memset(t, 0, sizeof(*t));
994
995   t->img = this;
996   t->wp = 1;
997   t->bot = 0;
998   t->eot = 0;
999}
1000
1001/*
1002    Open a tape image
1003*/
1004bool ti990_tape_image_device::call_load()
1005{
1006   tape_unit_t *t;
1007   tap_990_t *tpc = get_safe_token(owner());
1008   int id = tape_get_id(this);
1009
1010   t = &tpc->t[id];
1011   memset(t, 0, sizeof(*t));
1012
1013   /* tell whether the image is writable */
1014   t->wp = is_readonly();
1015
1016   t->bot = 1;
1017
1018   return IMAGE_INIT_PASS;
1019}
1020
1021/*
1022    Close a tape image
1023*/
1024void ti990_tape_image_device::call_unload()
1025{
1026   tape_unit_t *t;
1027   tap_990_t *tpc = get_safe_token(owner());
1028   int id = tape_get_id(this);
1029
1030   t = &tpc->t[id];
1031   t->wp = 1;
1032   t->bot = 0;
1033   t->eot = 0;
1034}
1035
1036#define MCFG_TI990_TAPE_ADD(_tag)   \
1037   MCFG_DEVICE_ADD((_tag),  TI990_TAPE, 0)
1038
1039
1040static MACHINE_CONFIG_FRAGMENT( tap_990 )
1041   MCFG_TI990_TAPE_ADD("tape0")
1042   MCFG_TI990_TAPE_ADD("tape1")
1043   MCFG_TI990_TAPE_ADD("tape2")
1044   MCFG_TI990_TAPE_ADD("tape3")
1045MACHINE_CONFIG_END
1046
1047/*
1048    Init the tape controller core
1049*/
1050static DEVICE_START(tap_990)
1051{
1052   tap_990_t *tpc = get_safe_token(device);
1053   /* verify that we have an interface assigned */
1054   assert(device->static_config() != NULL);
1055
1056   /* copy interface pointer */
1057   tpc->intf = (const ti990_tpc_interface*)device->static_config();
1058
1059   memset(tpc->w, 0, sizeof(tpc->w));
1060   /* The PE bit is always set for the MT3200 (but not MT1600) */
1061   /* According to MT3200 manual, w7 bit #4 (reserved) is always set */
1062   tpc->w[7] = w7_idle /*| w7_PE_format*/ | 0x0800;
1063
1064   update_interrupt(device);
1065}
1066
1067
1068const device_type TI990_TAPE_CTRL = &device_creator<tap_990_device>;
1069
1070tap_990_device::tap_990_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
1071   : device_t(mconfig, TI990_TAPE_CTRL, "Generic TI990 Tape Controller", tag, owner, clock, "tap_990", __FILE__)
1072{
1073   m_token = global_alloc_clear(tap_990_t);
1074}
1075
1076//-------------------------------------------------
1077//  device_config_complete - perform any
1078//  operations now that the configuration is
1079//  complete
1080//-------------------------------------------------
1081
1082void tap_990_device::device_config_complete()
1083{
1084}
1085
1086//-------------------------------------------------
1087//  device_start - device-specific startup
1088//-------------------------------------------------
1089
1090void tap_990_device::device_start()
1091{
1092   DEVICE_START_NAME( tap_990 )(this);
1093}
1094
1095//-------------------------------------------------
1096//  device_mconfig_additions - return a pointer to
1097//  the device's machine fragment
1098//-------------------------------------------------
1099
1100machine_config_constructor tap_990_device::device_mconfig_additions() const
1101{
1102   return MACHINE_CONFIG_NAME( tap_990  );
1103}
Property changes on: trunk/src/mess/machine/ti99/990_tap.c
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/mess/machine/ti99/990_tap.h
r0r26091
1/*
2    990_tap.h: include file for 990_tap.c
3*/
4extern DECLARE_READ16_DEVICE_HANDLER(ti990_tpc_r);
5extern DECLARE_WRITE16_DEVICE_HANDLER(ti990_tpc_w);
6
7/***************************************************************************
8    TYPE DEFINITIONS
9***************************************************************************/
10
11struct ti990_tpc_interface
12{
13   void (*interrupt_callback)(running_machine &machine, int state);
14};
15/***************************************************************************
16    MACROS
17***************************************************************************/
18
19class tap_990_device : public device_t
20{
21public:
22   tap_990_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
23   ~tap_990_device() { global_free(m_token); }
24
25   // access to legacy token
26   void *token() const { assert(m_token != NULL); return m_token; }
27protected:
28   // device-level overrides
29   virtual void device_config_complete();
30   virtual void device_start();
31   virtual machine_config_constructor device_mconfig_additions() const;
32private:
33   // internal state
34   void *m_token;
35};
36
37extern const device_type TI990_TAPE_CTRL;
38
39
40#define MCFG_TI990_TAPE_CTRL_ADD(_tag, _intrf)  \
41   MCFG_DEVICE_ADD((_tag),  TI990_TAPE_CTRL, 0)\
42   MCFG_DEVICE_CONFIG(_intrf)
Property changes on: trunk/src/mess/machine/ti99/990_tap.h
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/mess/machine/ti99/ti990.c
r0r26091
1/*
2    machine/ti990.c
3
4    Emulation for a few generic aspects of TI990
5*/
6
7#include "emu.h"
8#include "ti990.h"
9
10/*
11    Interrupt priority encoder.  Actually part of the CPU board.
12*/
13static UINT16 intlines;
14
15void ti990_reset_int(void)
16{
17   intlines = 0;
18}
19
20void ti990_set_int_line(running_machine &machine, int line, int state)
21{
22   int level;
23
24
25   if (state)
26      intlines |= (1 << line);
27   else
28      intlines &= ~ (1 << line);
29
30   if (intlines)
31   {
32      for (level = 0; ! (intlines & (1 << level)); level++)
33         ;
34      machine.device("maincpu")->execute().set_input_line_and_vector(0, ASSERT_LINE, level);  /* interrupt it, baby */
35   }
36   else
37      machine.device("maincpu")->execute().set_input_line(0, CLEAR_LINE);
38}
39
40void ti990_set_int2(device_t *device, int state)
41{
42   ti990_set_int_line(device->machine(), 2, state);
43}
44
45void ti990_set_int3(running_machine &machine, int state)
46{
47   ti990_set_int_line(machine, 3, state);
48}
49
50void ti990_set_int6(running_machine &machine, int state)
51{
52   ti990_set_int_line(machine, 6, state);
53}
54
55void ti990_set_int7(running_machine &machine, int state)
56{
57   ti990_set_int_line(machine, 7, state);
58}
59
60void ti990_set_int9(running_machine &machine, int state)
61{
62   ti990_set_int_line(machine, 9, state);
63}
64
65void ti990_set_int10(running_machine &machine, int state)
66{
67   ti990_set_int_line(machine, 10, state);
68}
69
70void ti990_set_int13(running_machine &machine, int state)
71{
72   ti990_set_int_line(machine, 13, state);
73}
74
75/*
76    hold and debounce load line (emulation is inaccurate)
77*/
78
79static TIMER_CALLBACK(clear_load)
80{
81   machine.device("maincpu")->execute().set_input_line(INPUT_LINE_NMI, CLEAR_LINE);
82}
83
84void ti990_hold_load(running_machine &machine)
85{
86   machine.device("maincpu")->execute().set_input_line(INPUT_LINE_NMI, ASSERT_LINE);
87   machine.scheduler().timer_set(attotime::from_msec(100), FUNC(clear_load));
88}
89
90/*
91    line interrupt
92*/
93
94/* ckon_state: 1 if line clock active (RTCLR flip-flop on TI990/10 schematics -
95SMI sheet 4) */
96static char ckon_state;
97
98void ti990_line_interrupt(running_machine &machine)
99{
100   if (ckon_state)
101      ti990_set_int_line(machine, 5, 1);
102}
103
104void ti990_ckon_ckof_callback(device_t *device, int state)
105{
106   ckon_state = state;
107   if (! ckon_state)
108      ti990_set_int_line(device->machine(), 5, 0);
109}
110
111
112
113/*
114    Control panel emulation
115
116    three panel types
117    * operator panel
118    * programmer panel
119    * MDU (external unit connected instead of the control panel, as seen in
120      945401-9701 p. 2-5 though 2-15)
121
122    Operator panel:
123    * Power led
124    * Fault led
125    * Off/On/Load switch
126
127    Programmer panel:
128    * 16 status light, 32 switches, IDLE, RUN leds
129    * interface to a low-level debugger in ROMs
130
131    * MDU:
132    * includes a programmer panel, a tape unit, and a few parts
133      (diagnostic tape, diagnostic ROMs, etc.)
134
135    CRU output:
136    0-7: lights 0-7
137    8: increment scan
138    9: clear scan (according to 990 handbook)
139    A: run light (additionally sets all data LEDs to 1s, the scan count to 0b10 and enables the HALT/SIE switch)
140    B: fault light
141    C: Memory Error Interrupt clear
142    D: Start panel timer
143    E: Set SIE function (interrupt after 2 instructions are executed)
144    F: flag (according to 990 handbook)
145
146    input :
147    0-7: switches 0-7 (or data from MDU tape)
148    8: scan count bit 1
149    9: scan count bit 0
150    A: timer active
151    B: programmer panel not present or locked
152    C: char in MDU tape unit buffer?
153    D: unused?
154    E: if 0, MDU unit present
155    F: flag (according to 990 handbook)
156*/
157
158   READ8_HANDLER ( ti990_panel_read )
159{
160   if (offset == 1)
161      return 0x48;
162
163   return 0;
164}
165
166WRITE8_HANDLER ( ti990_panel_write )
167{
168}
169
170
171/*
172    CPU board soft reset (RSET instruction)
173*/
174void ti990_cpuboard_reset(void)
175{
176   ckon_state = 0;
177}
Property changes on: trunk/src/mess/machine/ti99/ti990.c
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/mess/machine/ti99/ti990.h
r0r26091
1/*
2    ti990.h: header file for ti990.c
3*/
4
5void ti990_reset_int(void);
6void ti990_set_int_line(running_machine &machine, int line, int state);
7void ti990_set_int2(device_t *device, int state);
8void ti990_set_int3(running_machine &machine, int state);
9void ti990_set_int6(running_machine &machine, int state);
10void ti990_set_int7(running_machine &machine, int state);
11void ti990_set_int9(running_machine &machine, int state);
12void ti990_set_int10(running_machine &machine, int state);
13void ti990_set_int13(running_machine &machine, int state);
14
15void ti990_hold_load(running_machine &machine);
16
17   DECLARE_READ8_HANDLER ( ti990_panel_read );
18DECLARE_WRITE8_HANDLER ( ti990_panel_write );
19
20void ti990_line_interrupt(running_machine &machine);
21void ti990_ckon_ckof_callback(device_t *device, int state);
22
23void ti990_cpuboard_reset(void);
Property changes on: trunk/src/mess/machine/ti99/ti990.h
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native

Previous 199869 Revisions Next


© 1997-2024 The MAME Team