dev_osiop.cc Source File

Back to the index.

dev_osiop.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008-2009 Anders Gavare. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * 3. The name of the author may not be used to endorse or promote products
13  * derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  *
28  * COMMENT: NCR 53C710 SCSI I/O Processor (SIOP)
29  *
30  * Based on reverse-engineering OpenBSD's osiop.c, and a PDF manual:
31  * "Symbios SYM53C710 SCSI I/O Processor Technical Manual, version 3.1".
32  *
33  * See e.g. openbsd/sys/dev/microcode/siop/osiop.ss for an example of what
34  * the SCRIPTS assembly language looks like (or osiop.out for the raw result).
35  *
36  *
37  * TODOs:
38  *
39  * o) Target mode. Right now, only Initiator mode is implemented.
40  * o) Errors. Right now, the emulator aborts if there is a SCSI error.
41  * o) Allow all phases to do partial transfers. Right now, only data in
42  * and data out support this (hackish).
43  */
44 
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 
49 #include "console.h"
50 #include "cpu.h"
51 #include "device.h"
52 #include "diskimage.h"
53 #include "interrupt.h"
54 #include "machine.h"
55 #include "memory.h"
56 #include "misc.h"
57 
58 #include "thirdparty/osiopreg.h"
59 
60 
61 /* #define debug fatal */
62 const int osiop_debug = 0;
63 
64 static const char *phases[8] =
65  { "DATA_OUT", "DATA_IN", "COMMAND", "STATUS",
66  "RESERVED_OUT", "RESERVED_IN", "MSG_OUT", "MSG_IN" };
67 
68 #define DEV_OSIOP_LENGTH OSIOP_NREGS
69 #define OSIOP_CHIP_REVISION 2
70 
71 #define MAX_SCRIPTS_PER_CHUNK 256 /* 256 may be a reasonable value? */
72 
73 #define OSIOP_TICK_SHIFT 17
74 
75 struct osiop_data {
76  struct interrupt irq;
77  int asserted;
78 
80 
81  /* Current transfer: */
84  size_t data_offset;
85 
86  /* Cached emulated physical RAM page lookup: */
87  uint32_t last_phys_page;
88  uint8_t *last_host_page;
89 
90  /* ALU: */
91  int carry;
92 
93  /*
94  * Most of these are byte-addressed, but some are not. (Note: For
95  * convenience, 32-bit words are stored in host byte order!)
96  */
97  uint8_t reg[OSIOP_NREGS];
98 };
99 
100 
101 static void osiop_free_xfer(struct osiop_data *d)
102 {
103  if (d->xferp != NULL)
105 
106  d->xferp = NULL;
107 }
108 
109 
110 /* Allocate memory for a new transfer. */
111 static void osiop_new_xfer(struct osiop_data *d, int target_scsi_id)
112 {
113  if (d->xferp != NULL) {
114  fatal("WARNING! osiop_new_xfer(): freeing previous"
115  " transfer\n");
116  osiop_free_xfer(d);
117  }
118 
119  d->selected_id = target_scsi_id;
120  d->xferp = scsi_transfer_alloc();
121 }
122 
123 
124 static int osiop_get_scsi_phase(struct osiop_data *d)
125 {
126  return OSIOP_PHASE(d->reg[OSIOP_SOCL]);
127 }
128 
129 
130 static void osiop_set_scsi_phase(struct osiop_data *d, int phase)
131 {
132  int mask = OSIOP_MSG | OSIOP_CD | OSIOP_IO;
133  d->reg[OSIOP_SOCL] &= ~mask;
134  d->reg[OSIOP_SOCL] |= (phase & mask);
135 }
136 
137 
138 /*
139  * osiop_update_sip_and_dip():
140  *
141  * The SIP bit in ISTAT is basically a "summary" of whether there is
142  * a non-masked SCSI interrupt. Similarly for DIP, for DMA.
143  *
144  * However, the SIP and DIP bits are set even if interrupts are
145  * masked away using DIEN and SIEN. (At least that is how I understood
146  * things from reading OpenBSD's osiop.c osiop_poll().)
147  */
148 static int osiop_update_sip_and_dip(struct osiop_data *d)
149 {
150  int assert = 0;
151 
152  /* First, let's assume no interrupt assertions. */
154 
155  /* Any enabled SCSI interrupts? */
156  if (d->reg[OSIOP_SSTAT0])
158  if (d->reg[OSIOP_SSTAT0] & d->reg[OSIOP_SIEN])
159  assert = 1;
160 
161  /* Or enabled DMA interrupts? */
162  if (d->reg[OSIOP_DSTAT] & ~OSIOP_DIEN_RES)
164  if (d->reg[OSIOP_DSTAT] & d->reg[OSIOP_DIEN] & ~OSIOP_DIEN_RES)
165  assert = 1;
166 
167  return assert;
168 }
169 
170 
171 /*
172  * osiop_reassert_interrupts():
173  *
174  * Recalculate interrupt assertions; if either of the SIP or DIP bits
175  * in the ISTAT register is set, then we should cause an interrupt.
176  *
177  * (The d->asserted stuff is to make sure we don't call INTERRUPT_DEASSERT
178  * etc. too often when not necessary. Just an optimization.)
179  */
180 static void osiop_reassert_interrupts(struct osiop_data *d)
181 {
182  int assert = 0;
183 
184  if (osiop_update_sip_and_dip(d))
185  assert = 1;
186 
187  if (assert && !d->asserted)
188  INTERRUPT_ASSERT(d->irq);
189  if (!assert && d->asserted)
191 
192  d->asserted = assert;
193 }
194 
195 
196 /* Helper: returns a word in host order, from emulated physical RAM. */
197 static uint32_t read_word(struct osiop_data *d, struct cpu *cpu, uint32_t addr)
198 {
199  uint32_t word;
200 
201  if (d->last_host_page == NULL ||
202  d->last_phys_page != (addr & 0xfffff000)) {
203  d->last_phys_page = addr & 0xfffff000;
204  d->last_host_page =
206  }
207 
208  word = *((uint32_t *) (d->last_host_page + (addr & 0xffc)));
209 
210  if (cpu->byte_order == EMUL_LITTLE_ENDIAN)
211  word = LE32_TO_HOST(word);
212  else
213  word = BE32_TO_HOST(word);
214 
215  return word;
216 }
217 
218 
219 /* Helper: reads/writes single bytes from emulated physical RAM. */
220 static uint8_t read_byte(struct osiop_data *d, struct cpu *cpu, uint32_t addr)
221 {
222  if (d->last_host_page == NULL ||
223  d->last_phys_page != (addr & 0xfffff000)) {
224  d->last_phys_page = addr & 0xfffff000;
225  d->last_host_page =
227  }
228 
229  return d->last_host_page[addr & 0xfff];
230 }
231 static void write_byte(struct osiop_data *d, struct cpu *cpu, uint32_t addr, uint8_t byte)
232 {
233  if (d->last_host_page == NULL ||
234  d->last_phys_page != (addr & 0xfffff000)) {
235  d->last_phys_page = addr & 0xfffff000;
236  d->last_host_page =
238  }
239 
240  d->last_host_page[addr & 0xfff] = byte;
241 }
242 
243 
244 /*
245  * osiop_get_next_scripts_word():
246  *
247  * Reads a 32-bit word at physical memory location DSP, and returns it
248  * in host order. DSP is then advanced to point to the next instruction word.
249  *
250  * (NOTE/TODO: This could be optimized to read from a host page directly,
251  * but then care must be taken when the instruction pointer (DSP) goes
252  * over a page boundary to the next page. At least the page lookup
253  * could be "cached"...)
254  */
255 uint32_t osiop_get_next_scripts_word(struct cpu *cpu, struct osiop_data *d)
256 {
257  uint32_t *dspp = (uint32_t*) &d->reg[OSIOP_DSP];
258  uint32_t dsp = *dspp;
259  uint32_t instr;
260 
261  if (dsp & 3) {
262  fatal("osiop_get_next_scripts_word: unaligned DSP 0x%08x\n",
263  dsp);
264  exit(1);
265  }
266 
267  instr = read_word(d, cpu, dsp);
268 
269  dsp += sizeof(instr);
270  *dspp = dsp;
271 
272  return instr;
273 }
274 
275 
276 /*
277  * osiop_execute_scripts_instr():
278  *
279  * Interprets a single SCRIPTS machine code instruction. Returns 1 if
280  * execution should continue, or 0 if there was an interrupt.
281  *
282  * See "Symbios SYM53C710 SCSI I/O Processor Technical Manual, version 3.1"
283  * chapter 5 for details about the instruction set.
284  */
285 int osiop_execute_scripts_instr(struct cpu *cpu, struct osiop_data *d)
286 {
287  uint32_t *dsap = (uint32_t*) &d->reg[OSIOP_DSA];
288  uint32_t *dnadp = (uint32_t*) &d->reg[OSIOP_DNAD];
289  uint32_t *dbcp = (uint32_t*) &d->reg[OSIOP_DBC];
290  uint32_t *dspsp = (uint32_t*) &d->reg[OSIOP_DSPS];
291  uint32_t *dspp = (uint32_t*) &d->reg[OSIOP_DSP];
292  uint32_t *tempp = (uint32_t*) &d->reg[OSIOP_TEMP];
293 
294  uint32_t dspOrig = *dspp;
295  uint32_t instr1 = osiop_get_next_scripts_word(cpu, d);
296  uint32_t instr2 = osiop_get_next_scripts_word(cpu, d);
297  uint32_t dbc, target_addr = 0;
298  uint8_t dcmd;
299  int32_t reladdr;
300  int opcode, phase, relative_addressing, table_indirect_addressing;
301  int select_with_atn, scsi_ids_to_select = -1, scsi_id_to_select;
302  int test_carry, compare_data, compare_phase;
303  int jump_if_true, wait_for_valid_phase;
304  int comparison = 0, interrupt_instead_of_branch = 0;
305 
306  /*
307  * According to the 53C710 manual, chapter 5 (introduction): the first
308  * 32-bit word is always loaded into DCMD and DBC, the second into
309  * DSPS, the third (only used by memory move instructions) is loaded
310  * into the TEMP register.
311  */
312 
313  dcmd = d->reg[OSIOP_DCMD] = instr1 >> 24;
314  dbc = *dbcp = instr1 & 0x00ffffff;
315  *dspsp = instr2;
316 
317  reladdr = (instr2 << 8);
318  reladdr >>= 8;
319 
320  opcode = (dcmd >> 3) & 7;
321  phase = dcmd & 7;
322 
323  if (osiop_debug)
324  debug("{ SCRIPTS @ 0x%08x: 0x%08x 0x%08x",
325  (int) dspOrig, (int) instr1, (int) instr2);
326 
327  switch (dcmd & 0xc0) {
328 
329  case 0x00:
330  {
331  int ofs1 = instr1 & 0x00ffffff;
332  int ofs2 = instr2 & 0x00ffffff;
333  int indirect_addressing = dcmd & 0x20;
334  uint32_t dsa = *dsap;
335  uint32_t addr, xfer_byte_count, xfer_addr;
336  int32_t tmp = ofs2 << 8;
337  int res;
338  size_t i;
339 
340  tmp >>= 8;
341  table_indirect_addressing = dcmd & 0x10;
342 
343  opcode = (dcmd >> 3) & 1;
344 
345  switch (opcode) {
346  case 0: if (osiop_debug)
347  debug(": CHMOV");
348  break;
349  case 1: if (osiop_debug)
350  debug(": MOVE");
351  break;
352  }
353 
354  if (indirect_addressing) {
355  fatal("osiop: TODO: indirect_addressing move\n");
356  exit(1);
357  }
358 
359  if (!table_indirect_addressing) {
360  fatal("osiop: TODO: !table_indirect_addressing move\n");
361  exit(1);
362  }
363 
364  if (osiop_debug)
365  debug(" FROM %i", tmp);
366 
367  if (ofs1 != ofs2) {
368  fatal("osiop: TODO: move ofs1!=ofs2\n");
369  exit(1);
370  }
371 
372  if (phase != osiop_get_scsi_phase(d)) {
373  fatal("osiop: TODO: move: wait for phase. "
374  "phase = %i, osiop_get_scsi_phase = %i\n",
375  phase, osiop_get_scsi_phase(d));
376  exit(1);
377  }
378 
379  if (osiop_debug)
380  debug(" WHEN %s", phases[phase]);
381 
382  addr = dsa + tmp;
383  xfer_byte_count = read_word(d, cpu, addr) & 0x00ffffff;
384  xfer_addr = read_word(d, cpu, addr+4);
385 
386  switch (phase) {
387 
388  case MSG_OUT_PHASE:
390  &d->xferp->msg_out, xfer_byte_count, 0);
391 
392  i = 0;
393  while (xfer_byte_count > 0) {
394  uint8_t byte = read_byte(d, cpu, xfer_addr);
395  /* debug(" reading msg_out byte @ 0x%08x = 0x%02x\n",
396  xfer_addr, byte); */
397  d->xferp->msg_out[i++] = byte;
398  xfer_addr ++;
399  xfer_byte_count --;
400  }
401 
402  osiop_set_scsi_phase(d, COMMAND_PHASE);
403  break;
404 
405  case COMMAND_PHASE:
407  &d->xferp->cmd, xfer_byte_count, 0);
408 
409  i = 0;
410  while (xfer_byte_count > 0) {
411  uint8_t byte = read_byte(d, cpu, xfer_addr);
412  /* debug(" reading cmd byte @ 0x%08x = 0x%02x\n",
413  xfer_addr, byte); */
414  d->xferp->cmd[i++] = byte;
415  xfer_addr ++;
416  xfer_byte_count --;
417  }
418 
419  res = diskimage_scsicommand(cpu,
421  if (res == 0) {
422  fatal("osiop TODO: error\n");
423  exit(1);
424  }
425 
426  d->data_offset = 0;
427 
428  if (res == 2)
429  osiop_set_scsi_phase(d, DATA_OUT_PHASE);
430  else if (d->xferp->data_in_len > 0)
431  osiop_set_scsi_phase(d, DATA_IN_PHASE);
432  else
433  osiop_set_scsi_phase(d, STATUS_PHASE);
434  break;
435 
436  case DATA_OUT_PHASE:
437  if (d->xferp->data_out == NULL)
439  &d->xferp->data_out, d->xferp->data_out_len, 0);
440 
441  while (xfer_byte_count > 0) {
442  uint8_t byte = read_byte(d, cpu, xfer_addr);
443  /* debug(" reading data_out byte @ 0x%08x = 0x%02x\n",
444  xfer_addr, byte); */
445  d->xferp->data_out[d->xferp->data_out_offset++] = byte;
446  xfer_addr ++;
447  xfer_byte_count --;
448  }
449 
450  /* Rerun the command to actually write out the data: */
451  res = diskimage_scsicommand(cpu,
453  if (res == 0) {
454  fatal("osiop TODO: error on rerun\n");
455  exit(1);
456  } else if (res == 2) {
457  /* Stay at data out phase. */
458  } else {
459  osiop_set_scsi_phase(d, STATUS_PHASE);
460  }
461  break;
462 
463  case DATA_IN_PHASE:
464  i = 0;
465  while (xfer_byte_count > 0 && i + d->data_offset < d->xferp->data_in_len) {
466  uint8_t byte = d->xferp->data_in[i + d->data_offset];
467  i ++;
468  /* debug(" writing data_in byte @ 0x%08x = 0x%02x\n",
469  xfer_addr, byte); */
470  write_byte(d, cpu, xfer_addr, byte);
471  xfer_addr ++;
472  xfer_byte_count --;
473  }
474 
475  d->data_offset += i;
476  if (d->data_offset >= d->xferp->data_in_len)
477  osiop_set_scsi_phase(d, STATUS_PHASE);
478  break;
479 
480  case STATUS_PHASE:
481  i = 0;
482  while (xfer_byte_count > 0 && i < d->xferp->status_len) {
483  uint8_t byte = d->xferp->status[i++];
484  /* debug(" writing status byte @ 0x%08x = 0x%02x\n",
485  xfer_addr, byte); */
486  write_byte(d, cpu, xfer_addr, byte);
487  xfer_addr ++;
488  xfer_byte_count --;
489  }
490 
491  osiop_set_scsi_phase(d, MSG_IN_PHASE);
492  break;
493 
494  case MSG_IN_PHASE:
495  i = 0;
496  while (xfer_byte_count > 0 && i < d->xferp->msg_in_len) {
497  uint8_t byte = d->xferp->msg_in[i++];
498  /* debug(" writing msg_in byte @ 0x%08x = 0x%02x\n",
499  xfer_addr, byte); */
500  write_byte(d, cpu, xfer_addr, byte);
501  xfer_addr ++;
502  xfer_byte_count --;
503  }
504 
505  /* Done. */
506  osiop_free_xfer(d);
507  break;
508 
509  default:fatal("osiop: TODO: unimplemented move, "
510  "phase=%i\n", phase);
511  exit(1);
512  }
513 
514  /* Transfer complete. */
515  *dnadp = xfer_addr;
516  *dbcp = xfer_byte_count;
517 
518  d->reg[OSIOP_DFIFO] = 0; /* TODO */
519  }
520  break;
521 
522  case 0x40:
523  /* I/O or Read/Write */
524  relative_addressing = dcmd & 4;
525  table_indirect_addressing = dcmd & 2;
526  select_with_atn = dcmd & 1;
527  scsi_ids_to_select = (dbc >> 16) & 0xff;
528 
529  switch (opcode) {
530  case 0: if (osiop_debug)
531  debug(": SELECT");
532  if (select_with_atn) {
533  if (osiop_debug)
534  debug(" ATN");
535  d->reg[OSIOP_SOCL] |= OSIOP_ATN;
536  }
537 
538  if (table_indirect_addressing) {
539  uint32_t dsa = *dsap;
540  uint32_t addr, word;
541  int32_t tmp = dbc << 8;
542  tmp >>= 8;
543  addr = dsa + tmp;
544 
545  if (osiop_debug)
546  debug(" FROM %i", dbc);
547 
548  word = read_word(d, cpu, addr);
549  scsi_ids_to_select = (word >> 16) & 0xff;
550  }
551 
552  if (scsi_ids_to_select == 0) {
553  fatal("osiop: TODO: scsi_ids_to_select = 0!\n");
554  exit(1);
555  }
556 
557  scsi_id_to_select = 0;
558  while (!(scsi_ids_to_select & 1)) {
559  scsi_ids_to_select >>= 1;
560  scsi_id_to_select ++;
561  }
562 
563  scsi_ids_to_select &= ~1;
564  if (scsi_ids_to_select != 0) {
565  fatal("osiop: TODO: multiselect?\n");
566  exit(1);
567  }
568 
569  if (osiop_debug)
570  debug(" [SCSI ID %i]", scsi_id_to_select);
571 
572  if (relative_addressing) {
573  /* Note: Relative to _current_ DSP value, not
574  what the DSP was when the current
575  instruction was read! */
576  target_addr = reladdr + *dspp;
577  if (osiop_debug)
578  debug(" REL(%i)", reladdr);
579  } else {
580  target_addr = instr2;
581  if (osiop_debug)
582  debug(" 0x%08x", instr2);
583  }
584 
585  if (diskimage_exist(cpu->machine,
586  scsi_id_to_select, DISKIMAGE_SCSI)) {
587  osiop_new_xfer(d, scsi_id_to_select);
588 
589  /* TODO: Just a guess, so far: */
590  osiop_set_scsi_phase(d, MSG_OUT_PHASE);
591  } else {
592  d->selected_id = -1;
593 
594 #if 1
595  /* TODO: The scsi ID does not exist.
596  Should we simply timeout: */
597  d->scripts_running = 0;
598 #else
599  /* or branch to the reselect address? */
600  *(uint32_t*) &d->reg[OSIOP_DSP] = target_addr;
601 #endif
602  }
603 
604  break;
605 
606  case 1: if (osiop_debug)
607  debug(": WAIT DISCONNECT");
608  /* TODO */
609  break;
610  case 2: if (osiop_debug)
611  debug(": WAIT RESELECT");
612  fatal("osiop: TODO: wait reselect\n");
613  exit(1);
614  break;
615 
616  case 3: if (osiop_debug)
617  debug(": SET");
618  break;
619  case 4: if (osiop_debug)
620  debug(": CLEAR");
621  break;
622 
623  default:fatal(": UNIMPLEMENTED dcmd=0x%02x opcode=%i\n",
624  dcmd, opcode);
625  exit(1);
626  }
627 
628  /* SET or CLEAR: */
629  if (opcode == 3 || opcode == 4) {
630  int bit_atn = (dbc >> 3) & 1;
631  int bit_ack = (dbc >> 6) & 1;
632  int bit_target = (dbc >> 9) & 1;
633  int bit_carry = (dbc >> 10) & 1;
634 
635  if (osiop_debug) {
636  if (bit_atn)
637  debug(" ATN");
638  if (bit_ack)
639  debug(" ACK");
640  if (bit_target)
641  debug(" TARGET");
642  if (bit_carry)
643  debug(" CARRY");
644  }
645 
646  if (opcode == 3) {
647  d->reg[OSIOP_SOCL] |= (bit_ack * OSIOP_ACK);
648  d->reg[OSIOP_SOCL] |= (bit_atn * OSIOP_ATN);
649  if (bit_carry)
650  d->carry = 1;
651  d->reg[OSIOP_SCNTL0] |=
652  (bit_target * OSIOP_SCNTL0_TRG);
653  } else {
654  d->reg[OSIOP_SOCL] &= ~(bit_ack * OSIOP_ACK);
655  d->reg[OSIOP_SOCL] &= ~(bit_atn * OSIOP_ATN);
656  if (bit_carry)
657  d->carry = 0;
658  d->reg[OSIOP_SCNTL0] &=
659  ~(bit_target * OSIOP_SCNTL0_TRG);
660  }
661  }
662 
663  break;
664 
665  case 0x80:
666  /* Transfer Control */
667  relative_addressing = (dbc >> 23) & 1;
668  test_carry = (dbc >> 21) & 1;
669  jump_if_true = (dbc >> 19) & 1;
670  compare_data = (dbc >> 18) & 1;
671  compare_phase = (dbc >> 17) & 1;
672  wait_for_valid_phase = (dbc >> 16) & 1;
673 
674  switch (opcode) {
675  case 0: if (osiop_debug)
676  debug(": JUMP");
677  break;
678  case 1: if (osiop_debug)
679  debug(": CALL");
680  break;
681  case 2: if (osiop_debug)
682  debug(": RETURN");
683  break;
684  case 3: if (osiop_debug)
685  debug(": INTERRUPT");
686  break;
687  default:fatal(": UNIMPLEMENTED dcmd=0x%02x opcode=%i\n",
688  dcmd, opcode);
689  exit(1);
690  }
691 
692  if (opcode == 0 || opcode == 1) {
693  if (relative_addressing) {
694  /* Note: Relative to _current_ DSP value,
695  not what it was when the current instruction
696  was read! */
697  target_addr = reladdr + *dspp;
698  if (osiop_debug)
699  debug(" REL(%i)", reladdr);
700  } else {
701  target_addr = instr2;
702  if (osiop_debug)
703  debug(" 0x%08x", instr2);
704  }
705  } else {
706  if (opcode == 2) {
707  /* Return: */
708  target_addr = *tempp;
709  } else {
710  /* Interrupt: */
711  interrupt_instead_of_branch = 1;
712  }
713  }
714 
715  if (test_carry) {
716  if (compare_data || compare_phase) {
717  fatal("osiop: TODO: test_carry cannot be"
718  " combined with other tests!\n");
719  exit(1);
720  }
721 
722  fatal("osiop: TODO: test_carry\n");
723  exit(1);
724  }
725 
726  if (compare_data) {
727  fatal("osiop: TODO: compare_data\n");
728  exit(1);
729  }
730 
731  if (compare_phase) {
732  int cur_phase;
733 
734  if (osiop_debug)
735  debug(" %s %s%s",
736  wait_for_valid_phase ? "WHEN" : "IF",
737  jump_if_true? "" : "NOT ",
738  phases[phase]);
739 
740  /* TODO: wait for valid phase! */
741 
742  cur_phase = osiop_get_scsi_phase(d);
743 
744  comparison = (phase == cur_phase);
745  }
746 
747  if (!test_carry && !compare_data && !compare_phase)
748  jump_if_true = comparison = 1;
749 
750  if ((jump_if_true && comparison) ||
751  (!jump_if_true && !comparison)) {
752  /* Perform the branch or interrupt. */
753  if (interrupt_instead_of_branch) {
754  d->scripts_running = 0;
756  } else {
757  *dspp = target_addr;
758  }
759  }
760 
761  break;
762 
763  case 0xc0:
764  if (osiop_debug)
765  debug(": MEMORY MOVE");
766  /* TODO: third instruction word! */
767  fatal(": TODO\n");
768  exit(1);
769  break;
770 
771  default:fatal(": UNIMPLEMENTED dcmd 0x%02x\n", dcmd);
772  exit(1);
773  }
774 
775  if (osiop_debug)
776  debug(" }\n");
777 
778  return 1;
779 }
780 
781 
782 /*
783  * osiop_execute_scripts():
784  *
785  * Interprets SCRIPTS machine code by reading one instruction word at a time,
786  * and executing it.
787  */
788 void osiop_execute_scripts(struct cpu *cpu, struct osiop_data *d)
789 {
790  int n = 0;
791 
792  if (osiop_debug)
793  debug("{ SCRIPTS start }\n");
794 
795  while (d->scripts_running && n < MAX_SCRIPTS_PER_CHUNK &&
797  n++;
798 
799  if (osiop_debug)
800  debug("{ SCRIPTS end }\n");
801 }
802 
803 
805 {
806  struct osiop_data *d = (struct osiop_data *) extra;
807 
808  if (d->scripts_running)
809  osiop_execute_scripts(cpu, d);
810 
811  osiop_reassert_interrupts(d);
812 }
813 
814 
816 {
817  uint64_t idata = 0, odata = 0;
818  uint8_t oldreg = 0;
819  struct osiop_data *d = (struct osiop_data *) extra;
820  int origofs = relative_addr;
821  int non1lenOk = 0;
822 
823  idata = memory_readmax64(cpu, data, len);
824 
825  /* Make relative_addr suit addresses in osiopreg.h: */
826  if (cpu->byte_order == EMUL_BIG_ENDIAN) {
827  relative_addr =
828  (relative_addr & ~3) |
829  (3 - (relative_addr & 3));
830  }
831 
832  if (len == sizeof(uint32_t)) {
833  /*
834  * NOTE: These are stored in HOST byte order!
835  */
836  switch (origofs) {
837  case OSIOP_DSA:
838  case OSIOP_TEMP:
839  case OSIOP_DBC:
840  case OSIOP_DNAD:
841  case OSIOP_DSP:
842  case OSIOP_DSPS:
843  case OSIOP_SCRATCH:
844  case OSIOP_ADDER:
845  relative_addr = origofs;
846  non1lenOk = 1;
847 
848  uint32_t *p = (uint32_t*) &d->reg[origofs];
849 
850  if (writeflag == MEM_WRITE)
851  *p = idata;
852  else
853  odata = *p;
854 
855  break;
856  }
857  } else {
858  /* Byte access: */
859  oldreg = d->reg[relative_addr];
860 
861  if (writeflag == MEM_WRITE)
862  d->reg[relative_addr] = idata;
863  else
864  odata = oldreg;
865  }
866 
867  switch (relative_addr) {
868 
869  case OSIOP_SCNTL0:
870  case OSIOP_SCNTL1:
871  break;
872 
873  case OSIOP_SIEN:
874  /* Used by OpenBSD/mvme88k during probing: */
875  if (len == 4)
876  non1lenOk = 1;
877  if (writeflag == MEM_WRITE)
878  osiop_reassert_interrupts(d);
879  break;
880 
881  case OSIOP_SCID:
882  if (idata != oldreg) {
883  fatal("osiop TODO: attempt to change SCID?\n");
884  exit(1);
885  }
886  break;
887 
888  case OSIOP_SBDL:
889  if (writeflag == MEM_WRITE)
890  fatal("[ osiop: SBDL set to 0x%x, but should be "
891  "read-only! ]\n", (int) idata);
892  break;
893 
894  case OSIOP_SBCL:
895  if (writeflag == MEM_WRITE) {
896  if (osiop_debug)
897  debug("[ osiop: SBCL set to 0x%x ]\n", (int) idata);
898  }
899  break;
900 
901  case OSIOP_DSTAT:
902  /* Cleared when read. Most likely not writable. */
903  odata = d->reg[OSIOP_DSTAT];
905  osiop_reassert_interrupts(d);
906  break;
907 
908  case OSIOP_SSTAT0:
909  /* Cleared when read. Most likely not writable. */
910  odata = d->reg[OSIOP_SSTAT0];
911  d->reg[OSIOP_SSTAT0] = 0;
912  osiop_reassert_interrupts(d);
913  break;
914 
915  case OSIOP_SSTAT1:
916  case OSIOP_SSTAT2:
917  break;
918 
919  case OSIOP_DSA:
920  if (writeflag == MEM_WRITE) {
921  if (osiop_debug)
922  debug("[ osiop: DSA set to 0x%x ]\n", (int) idata);
923  }
924  break;
925 
926  case OSIOP_CTEST0:
927  case OSIOP_CTEST1:
928  case OSIOP_CTEST2:
929  case OSIOP_CTEST3:
930  case OSIOP_CTEST4:
931  case OSIOP_CTEST5:
932  case OSIOP_CTEST6:
933  case OSIOP_CTEST7:
934  break;
935 
936  case OSIOP_TEMP:
937  if (writeflag == MEM_WRITE) {
938  if (osiop_debug)
939  debug("[ osiop: TEMP set to 0x%x ]\n", (int) idata);
940  }
941  break;
942 
943  case OSIOP_DFIFO:
944  break;
945 
946  case OSIOP_ISTAT:
947  if (writeflag == MEM_WRITE) {
948  if ((idata & 0x3f) != 0x00) {
949  fatal("osiop TODO: istat 0x%x\n", (int) idata);
950  exit(1);
951  }
952 
953  d->reg[relative_addr] = idata &
955  osiop_reassert_interrupts(d);
956  }
957  break;
958 
959  case OSIOP_CTEST8:
960  odata = (odata & 0xf) | (OSIOP_CHIP_REVISION << 4);
961  break;
962 
963  case OSIOP_DBC:
964  break;
965 
966  case OSIOP_DSP:
967  if (writeflag == MEM_WRITE) {
968  if (osiop_debug)
969  debug("[ osiop: DSP set to 0x%x ]\n", (int) idata);
970 
971  d->scripts_running = 1;
972  osiop_execute_scripts(cpu, d);
973  osiop_reassert_interrupts(d);
974  }
975  break;
976 
977  case OSIOP_DSPS:
978  case OSIOP_SCRATCH:
979  break;
980 
981  case OSIOP_DMODE:
982  break;
983 
984  case OSIOP_DIEN:
985  if (writeflag == MEM_WRITE)
986  osiop_reassert_interrupts(d);
987  break;
988 
989  case OSIOP_DWT:
990  break;
991 
992  case OSIOP_DCNTL:
993  if (writeflag == MEM_WRITE) {
994  if (idata & OSIOP_DCNTL_SSM) {
995  fatal("osiop TODO: SSM\n");
996  exit(1);
997  }
998  if (idata & OSIOP_DCNTL_LLM) {
999  fatal("osiop TODO: LLM\n");
1000  exit(1);
1001  }
1002  if (idata & OSIOP_DCNTL_STD) {
1003  fatal("osiop TODO: STD\n");
1004  exit(1);
1005  }
1006  }
1007  break;
1008 
1009  default:
1010  if (writeflag == MEM_READ) {
1011  fatal("[ osiop: read from 0x%02lx ]\n",
1012  (long)relative_addr);
1013  } else {
1014  fatal("[ osiop: write to 0x%02lx: 0x%02x ]\n",
1015  (long)relative_addr, (int)idata);
1016  }
1017 
1018  exit(1);
1019  }
1020 
1021  if (len != 1 && !non1lenOk) {
1022  fatal("[ osiop: TODO: len != 1, addr 0x%0x ]\n",
1023  (int)relative_addr);
1024  exit(1);
1025  }
1026 
1027  if (writeflag == MEM_READ)
1028  memory_writemax64(cpu, data, len, odata);
1029 
1030  return 1;
1031 }
1032 
1033 
1034 DEVINIT(osiop)
1035 {
1036  struct osiop_data *d;
1037 
1038  CHECK_ALLOCATION(d = (struct osiop_data *) malloc(sizeof(struct osiop_data)));
1039  memset(d, 0, sizeof(struct osiop_data));
1040 
1041  /* Set up initial device register values: */
1042  d->reg[OSIOP_SCID] = OSIOP_SCID_VALUE(7);
1043 
1044  /* OpenBSD's osiop_checkintr needs this: */
1046 
1048 
1051  dev_osiop_access, d, DM_DEFAULT, NULL);
1052 
1054  dev_osiop_tick, d, OSIOP_TICK_SHIFT);
1055 
1056  return 1;
1057 }
1058 
#define OSIOP_DMODE
Definition: osiopreg.h:111
uint64_t memory_readmax64(struct cpu *cpu, unsigned char *buf, int len)
Definition: memory.cc:55
void fatal(const char *fmt,...)
Definition: main.cc:152
#define OSIOP_MSG
Definition: osiopreg.h:185
#define DM_DEFAULT
Definition: memory.h:130
size_t data_out_len
Definition: diskimage.h:105
#define OSIOP_SBDL
Definition: osiopreg.h:70
#define OSIOP_IO
Definition: osiopreg.h:187
int diskimage_exist(struct machine *machine, int id, int type)
Definition: diskimage.cc:106
size_t cmd_len
Definition: diskimage.h:100
size_t msg_out_len
Definition: diskimage.h:98
#define OSIOP_DCNTL_STD
Definition: osiopreg.h:374
int scripts_running
Definition: dev_osiop.cc:79
#define OSIOP_SCID
Definition: osiopreg.h:63
#define OSIOP_SCID_VALUE(i)
Definition: osiopreg.h:162
#define OSIOP_CTEST2
Definition: osiopreg.h:82
#define OSIOP_DSA
Definition: osiopreg.h:78
#define BE32_TO_HOST(x)
Definition: misc.h:181
struct memory * mem
Definition: cpu.h:362
#define DISKIMAGE_SCSI
Definition: diskimage.h:40
#define OSIOP_DSTAT_SIR
Definition: osiopreg.h:215
#define OSIOP_ATN
Definition: osiopreg.h:184
#define DEV_OSIOP_LENGTH
Definition: dev_osiop.cc:68
#define OSIOP_ISTAT_ABRT
Definition: osiopreg.h:324
#define OSIOP_SSTAT0
Definition: osiopreg.h:74
struct machine * machine
Definition: cpu.h:328
#define OSIOP_TEMP
Definition: osiopreg.h:90
DEVICE_TICK(osiop)
Definition: dev_osiop.cc:804
#define MEM_READ
Definition: memory.h:116
uint32_t osiop_get_next_scripts_word(struct cpu *cpu, struct osiop_data *d)
Definition: dev_osiop.cc:255
#define instr(n)
struct memory * memory
Definition: machine.h:126
#define OSIOP_SSTAT1
Definition: osiopreg.h:75
size_t data_in_len
Definition: diskimage.h:110
#define OSIOP_SIEN
Definition: osiopreg.h:61
#define OSIOP_CTEST0
Definition: osiopreg.h:80
#define OSIOP_ISTAT_SIP
Definition: osiopreg.h:330
unsigned char * data_out
Definition: diskimage.h:104
#define OSIOP_DCNTL_SSM
Definition: osiopreg.h:372
#define OSIOP_DSTAT_DFE
Definition: osiopreg.h:210
size_t status_len
Definition: diskimage.h:114
size_t data_out_offset
Definition: diskimage.h:106
#define OSIOP_DNAD
Definition: osiopreg.h:103
uint8_t * last_host_page
Definition: dev_osiop.cc:88
uint32_t last_phys_page
Definition: dev_osiop.cc:87
#define OSIOP_SOCL
Definition: osiopreg.h:66
unsigned char * status
Definition: diskimage.h:113
#define OSIOP_SCRATCH
Definition: osiopreg.h:109
#define EMUL_LITTLE_ENDIAN
Definition: misc.h:164
#define CHECK_ALLOCATION(ptr)
Definition: misc.h:239
unsigned char * data_in
Definition: diskimage.h:109
#define OSIOP_DCNTL
Definition: osiopreg.h:114
#define LE32_TO_HOST(x)
Definition: misc.h:180
int osiop_execute_scripts_instr(struct cpu *cpu, struct osiop_data *d)
Definition: dev_osiop.cc:285
#define OSIOP_ISTAT
Definition: osiopreg.h:93
#define OSIOP_CTEST3
Definition: osiopreg.h:83
struct scsi_transfer * scsi_transfer_alloc(void)
#define OSIOP_NREGS
Definition: osiopreg.h:118
size_t data_offset
Definition: dev_osiop.cc:84
#define OSIOP_PHASE(x)
Definition: osiopreg.h:189
#define OSIOP_CHIP_REVISION
Definition: dev_osiop.cc:69
#define OSIOP_ACK
Definition: osiopreg.h:181
#define OSIOP_DSTAT
Definition: osiopreg.h:73
struct interrupt irq
Definition: dev_osiop.cc:76
void scsi_transfer_free(struct scsi_transfer *p)
#define OSIOP_CTEST1
Definition: osiopreg.h:81
u_short data
Definition: siireg.h:79
unsigned char * msg_out
Definition: diskimage.h:97
#define OSIOP_SBCL
Definition: osiopreg.h:71
#define INTERRUPT_ASSERT(istruct)
Definition: interrupt.h:74
#define OSIOP_SCNTL0
Definition: osiopreg.h:58
#define OSIOP_CTEST6
Definition: osiopreg.h:87
#define MEM_WRITE
Definition: memory.h:117
#define COMMAND_PHASE
Definition: aic7xxx_reg.h:1611
#define STATUS_PHASE
Definition: aic7xxx_reg.h:1610
#define MSG_IN_PHASE
Definition: aic7xxx_reg.h:1612
size_t msg_in_len
Definition: diskimage.h:112
DEVICE_ACCESS(osiop)
Definition: dev_osiop.cc:815
Definition: device.h:40
int selected_id
Definition: dev_osiop.cc:82
#define OSIOP_SSTAT2
Definition: osiopreg.h:76
#define OSIOP_SCNTL0_TRG
Definition: osiopreg.h:135
void osiop_execute_scripts(struct cpu *cpu, struct osiop_data *d)
Definition: dev_osiop.cc:788
DEVINIT(osiop)
Definition: dev_osiop.cc:1034
#define OSIOP_ISTAT_RST
Definition: osiopreg.h:325
uint32_t addr
#define DATA_IN_PHASE
Definition: aic7xxx_reg.h:1614
void scsi_transfer_allocbuf(size_t *lenp, unsigned char **pp, size_t want_len, int clearflag)
#define debug
Definition: dev_adb.cc:57
#define OSIOP_ADDER
Definition: osiopreg.h:116
#define OSIOP_CTEST8
Definition: osiopreg.h:94
#define OSIOP_DIEN
Definition: osiopreg.h:112
#define INTERRUPT_CONNECT(name, istruct)
Definition: interrupt.h:77
Definition: cpu.h:326
#define OSIOP_CTEST1_FMT
Definition: osiopreg.h:264
#define OSIOP_ISTAT_DIP
Definition: osiopreg.h:331
struct machine * machine
Definition: device.h:41
struct scsi_transfer * xferp
Definition: dev_osiop.cc:83
#define OSIOP_DWT
Definition: osiopreg.h:113
#define OSIOP_DSP
Definition: osiopreg.h:105
#define OSIOP_CTEST7
Definition: osiopreg.h:88
void memory_writemax64(struct cpu *cpu, unsigned char *buf, int len, uint64_t data)
Definition: memory.cc:89
#define OSIOP_CTEST5
Definition: osiopreg.h:86
void memory_device_register(struct memory *mem, const char *, uint64_t baseaddr, uint64_t len, int(*f)(struct cpu *, struct memory *, uint64_t, unsigned char *, size_t, int, void *), void *extra, int flags, unsigned char *dyntrans_data)
Definition: memory.cc:339
uint8_t reg[OSIOP_NREGS]
Definition: dev_osiop.cc:97
#define OSIOP_DCMD
Definition: osiopreg.h:101
#define DATA_OUT_PHASE
Definition: aic7xxx_reg.h:1615
unsigned char * msg_in
Definition: diskimage.h:111
uint8_t byte_order
Definition: cpu.h:347
#define OSIOP_CTEST4
Definition: osiopreg.h:85
#define OSIOP_SCNTL1
Definition: osiopreg.h:59
#define OSIOP_DIEN_RES
Definition: osiopreg.h:356
#define OSIOP_DBC
Definition: osiopreg.h:97
addr & if(addr >=0x24 &&page !=NULL)
int asserted
Definition: dev_osiop.cc:77
uint64_t addr
Definition: device.h:46
#define OSIOP_DCNTL_LLM
Definition: osiopreg.h:373
void machine_add_tickfunction(struct machine *machine, void(*func)(struct cpu *, void *), void *extra, int clockshift)
Definition: machine.cc:280
#define OSIOP_DFIFO
Definition: osiopreg.h:92
#define OSIOP_CD
Definition: osiopreg.h:186
#define OSIOP_DSPS
Definition: osiopreg.h:107
unsigned char * cmd
Definition: diskimage.h:99
int diskimage_scsicommand(struct cpu *cpu, int id, int type, struct scsi_transfer *xferp)
const int osiop_debug
Definition: dev_osiop.cc:62
#define MAX_SCRIPTS_PER_CHUNK
Definition: dev_osiop.cc:71
#define OSIOP_TICK_SHIFT
Definition: dev_osiop.cc:73
char * interrupt_path
Definition: device.h:50
#define INTERRUPT_DEASSERT(istruct)
Definition: interrupt.h:75
#define MSG_OUT_PHASE
Definition: aic7xxx_reg.h:1613
#define EMUL_BIG_ENDIAN
Definition: misc.h:165
unsigned char * memory_paddr_to_hostaddr(struct memory *mem, uint64_t paddr, int writeflag)
Definition: memory.cc:495

Generated on Fri Dec 7 2018 19:52:23 for GXemul by doxygen 1.8.13