Initial creation of sourceware repository
[external/binutils.git] / sim / ppc / hw_com.c
1 /*  This file is part of the program psim.
2     
3     Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
4     
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9     
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14     
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18     
19     */
20
21
22 #ifndef _HW_COM_C_
23 #define _HW_COM_C_
24
25 #ifndef STATIC_INLINE_HW_COM
26 #define STATIC_INLINE_HW_COM STATIC_INLINE
27 #endif
28
29 #include "device_table.h"
30
31 #ifdef HAVE_STRING_H
32 #include <string.h>
33 #else
34 #ifdef HAVE_STRINGS_H
35 #include <strings.h>
36 #endif
37 #endif
38
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42 #ifdef HAVE_STDLIB_H
43 #include <stdlib.h>
44 #endif
45
46 /* DEVICE
47    
48
49    com - '550 compatible serial device
50    
51
52    DESCRIPTION
53    
54
55    Models the basics of the 8 register '550 serial device.  The model 
56    includes an interrupt line, input and output fifos, and status 
57    information.
58
59    Independent configuration of the devices input and output streams is 
60    allowed: use either the console or a file (buffered or unbuffered) as 
61    the data source/sink; specify the real-time delay between each character 
62    transfer.
63
64    When the devices input stream is being taken from a file, the end of 
65    file is signaled by a loss of carrier (the loss of carrier may be 
66    incorrectly proceeded by a single null character).
67    
68
69    PROPERTIES
70    
71
72    reg = <address> <size> ... (optional - note 1)
73
74    List of <address> <size> pairs.  Each pair specifies an address for
75    the devices 8 registers.  The address should be 8 byte aligned.
76
77
78    alternate-reg = <address> <size> ... (optional - note 1)
79
80    Alternative addreses for the registers.
81
82
83    assigned-addresses = <address> <size> ... (optional - note 1)
84    
85    On a PCI bus, this property specifies the addresses assigned to the 
86    device.  The values reflect the devices configuration base registers.
87    
88    Note 1: At least one of "assigned-addresses", "reg" or "alternative-reg" 
89    must be specified.  If "assigned-addresses" is specified the other 
90    address specifications are ignored.
91    
92    
93    input-file = <file-name> (optional)
94
95    File to take all serial port input from (instead of the simulation
96    console).
97
98
99    output-file = <file-name> (optional)
100
101    File to send all output to (instead of the simulation console).
102
103
104    input-buffering = "unbuffered" (optional)
105
106    Specifying "unbuffered" buffering disables buffering on the serial
107    devices input stream (all data is immediatly read).  In the future,
108    this option may be used to provide input buffering alternatives.
109
110
111    output-buffering = "unbuffered" (optional)
112
113    Specifying "unbuffered" buffering disables buffering on the serial 
114    devices output stream (all data is immediatly written).  In the future, 
115    this option may be extended to include other buffering alternatives.
116
117
118    input-delay = <integer-delay> (optional)
119
120    Specify the number of ticks after the current character has been
121    read from the serial port that the next character becomes
122    available.
123
124
125    output-delay = <integer-delay> (optional)
126
127    Specify the number of ticks after a character has been written to
128    the empty output fifo that the fifo finishes draining.  Any
129    characters written to the output fifo before it has drained will
130    not be lost and will still be displayed.
131
132
133    EXAMPLES
134
135
136    |  /iobus@0xf0000000/com@0x3000/reg 0x3000 8
137
138    Create a simple console device at address <<0x3000>> within
139    <<iobus>>.  Since iobus starts at address <<0xf0000000>> the
140    absolute address of the serial port will be <<0xf0003000>>.
141
142    The device will always be ready for I/O (no delay properties specified) 
143    and both the input and output streams will use the simulation console 
144    (no file properties).
145
146
147    |  $ psim \
148    |    -o '/cpus/cpu@0' \
149    |    -o '/iobus@0xf0000000/com@0x4000/reg 0x4000 8' \
150    |    -o '/iobus@0xf0000000/com@0x4000/input-file /etc/passwd' \
151    |    -o '/iobus@0xf0000000/com@0x4000/input-delay 1000' \
152    |    -o '/iobus@0xf0000000/com@0x4000 > 0 int /cpus/cpu@0x0' \
153    |    psim-test/hw-com/cat.be 0xf0004000
154
155    The serial port (at address <<0xf0004000>> is configured so that it
156    takes its input from the file <</etc/passwd>> while its output is
157    allowed to appear on the simulation console.
158    
159    The node <</cpus/cpu@0>> was explicitly specified to ensure that it had 
160    been created before any interrupts were attached to it.
161
162    The program <<psim-test/hw-com/cat>> copies any characters on the serial 
163    port's input (<</etc/passwd>>) to its output (the console).  
164    Consequently, the aove program will display the contents of the file 
165    <</etc/passwd>> on the screen.
166
167
168    BUGS
169
170
171    IEEE 1275 requires that a device on a PCI bus have, as its first reg 
172    entry, the address of its configuration space registers.  Currently, 
173    this device does not even implement configuration registers.
174    
175    This model does not attempt to model the '550's input and output fifos.  
176    Instead, the input fifo is limited to a single character at a time, 
177    while the output fifo is effectivly infinite.  Consequently, unlike the 
178    '550, this device will not discard output characters once a stream of 16 
179    have been written to the data output register.
180
181    The input and output can only be taken from a file (or the current 
182    terminal device).  In the future, the <<com>> device should allow the 
183    specification of other data streams (such as an xterm or TK window).
184
185    The input blocks if no data is available.
186
187    Interrupts have not been tested.
188
189    */
190
191 enum {
192   max_hw_com_registers = 8,
193 };
194
195 typedef struct _com_port {
196   int ready;
197   int delay;
198   int interrupting;
199   FILE *file;
200 } com_port;
201
202 typedef struct _com_modem {
203   int carrier;
204   int carrier_changed;
205   int interrupting;
206 } com_modem;
207
208 typedef struct _hw_com_device {
209   com_port input;
210   com_port output;
211   com_modem modem;
212   char dlab[2];
213   char reg[max_hw_com_registers];
214   int interrupting;
215 } hw_com_device;
216
217
218 static void
219 hw_com_device_init_data(device *me)
220 {
221   hw_com_device *com = (hw_com_device*)device_data(me);
222   /* clean up */
223   if (com->output.file != NULL)
224     fclose(com->output.file);
225   if (com->input.file != NULL)
226     fclose(com->input.file);
227   memset(com, 0, sizeof(hw_com_device));
228
229   /* the fifo speed */
230   com->output.delay = (device_find_property(me, "output-delay") != NULL
231                        ? device_find_integer_property(me, "output-delay")
232                        : 0);
233   com->input.delay = (device_find_property(me, "input-delay") != NULL
234                       ? device_find_integer_property(me, "input-delay")
235                       : 0);
236
237   /* the data source/sink */
238   if (device_find_property(me, "input-file") != NULL) {
239     const char *input_file = device_find_string_property(me, "input-file");
240     com->input.file = fopen(input_file, "r");
241     if (com->input.file == NULL)
242       device_error(me, "Problem opening input file %s\n", input_file);
243     if (device_find_property(me, "input-buffering") != NULL) {
244       const char *buffering = device_find_string_property(me, "input-buffering");
245       if (strcmp(buffering, "unbuffered") == 0)
246         setbuf(com->input.file, NULL);
247     }
248   }
249   if (device_find_property(me, "output-file") != NULL) {
250     const char *output_file = device_find_string_property(me, "output-file");
251     com->output.file = fopen(output_file, "w");
252     if (com->input.file == NULL)
253       device_error(me, "Problem opening output file %s\n", output_file);
254     if (device_find_property(me, "output-buffering") != NULL) {
255       const char *buffering = device_find_string_property(me, "output-buffering");
256       if (strcmp(buffering, "unbuffered") == 0)
257         setbuf(com->output.file, NULL);
258     }
259   }
260
261   /* ready from the start */
262   com->input.ready = 1;
263   com->modem.carrier = 1;
264   com->output.ready = 1;
265 }
266
267
268 static void
269 update_com_interrupts(device *me,
270                       hw_com_device *com)
271 {
272   int interrupting;
273   com->modem.interrupting = (com->modem.carrier_changed && (com->reg[1] & 0x80));
274   com->input.interrupting = (com->input.ready && (com->reg[1] & 0x1));
275   com->output.interrupting = (com->output.ready && (com->reg[1] & 0x2));
276   interrupting = (com->input.interrupting
277                   || com->output.interrupting
278                   || com->modem.interrupting);
279
280   if (interrupting) {
281     if (!com->interrupting) {
282       device_interrupt_event(me, 0 /*port*/, 1 /*value*/, NULL, 0);
283     }
284   }
285   else /*!interrupting*/ {
286     if (com->interrupting)
287       device_interrupt_event(me, 0 /*port*/, 0 /*value*/, NULL, 0);
288   }
289   com->interrupting = interrupting;
290 }
291
292
293 static void
294 make_read_ready(void *data)
295 {
296   device *me = (device*)data;
297   hw_com_device *com = (hw_com_device*)device_data(me);
298   com->input.ready = 1;
299   update_com_interrupts(me, com);
300 }
301
302 static void
303 read_com(device *me,
304          hw_com_device *com,
305          unsigned_word a,
306          char val[1])
307 {
308   unsigned_word addr = a % 8;
309
310   /* the divisor latch is special */
311   if (com->reg[3] & 0x8 && addr < 2) {
312     *val = com->dlab[addr];
313     return;
314   }
315
316   switch (addr) {
317   
318   case 0:
319     /* fifo */
320     if (!com->modem.carrier)
321       *val = '\0';
322     if (com->input.ready) {
323       /* read the char in */
324       if (com->input.file == NULL) {
325         if (sim_io_read_stdin(val, 1) < 0)
326           com->modem.carrier_changed = 1;
327       }
328       else {
329         if (fread(val, 1, 1, com->input.file) == 0)
330           com->modem.carrier_changed = 1;
331       }
332       /* setup for next read */
333       if (com->modem.carrier_changed) {
334         /* once lost carrier, never ready */
335         com->modem.carrier = 0;
336         com->input.ready = 0;
337         *val = '\0';
338       }
339       else if (com->input.delay > 0) {
340         com->input.ready = 0;
341         device_event_queue_schedule(me, com->input.delay, make_read_ready, me);
342       }
343     }
344     else {
345       /* discard it? */
346       /* overflow input fifo? */
347       *val = '\0';
348     }
349     break;
350
351   case 2:
352     /* interrupt ident */
353     if (com->interrupting) {
354       if (com->input.interrupting)
355         *val = 0x4;
356       else if (com->output.interrupting)
357         *val = 0x2;
358       else if (com->modem.interrupting == 0)
359         *val = 0;
360       else
361         device_error(me, "bad elif for interrupts\n");
362     }
363     else
364       *val = 0x1;
365     break;
366
367   case 5:
368     /* line status */
369     *val = ((com->input.ready ? 0x1 : 0)
370             | (com->output.ready ? 0x60 : 0)
371             );
372     break;
373
374   case 6:
375     /* modem status */
376     *val = ((com->modem.carrier_changed ? 0x08 : 0)
377             | (com->modem.carrier ? 0x80 : 0)
378             );
379     com->modem.carrier_changed = 0;
380     break;
381
382   default:
383     *val = com->reg[addr];
384     break;
385
386   }
387   update_com_interrupts(me, com);
388 }
389
390 static unsigned
391 hw_com_io_read_buffer_callback(device *me,
392                                void *dest,
393                                int space,
394                                unsigned_word addr,
395                                unsigned nr_bytes,
396                                cpu *processor,
397                                unsigned_word cia)
398 {
399   hw_com_device *com = device_data(me);
400   int i;
401   for (i = 0; i < nr_bytes; i++) {
402     read_com(me, com, addr + i, &((char*)dest)[i]);
403   }
404   return nr_bytes;
405 }
406
407
408 static void
409 make_write_ready(void *data)
410 {
411   device *me = (device*)data;
412   hw_com_device *com = (hw_com_device*)device_data(me);
413   com->output.ready = 1;
414   update_com_interrupts(me, com);
415 }
416
417 static void
418 write_com(device *me,
419           hw_com_device *com,
420           unsigned_word a,
421           char val)
422 {
423   unsigned_word addr = a % 8;
424
425   /* the divisor latch is special */
426   if (com->reg[3] & 0x8 && addr < 2) {
427     com->dlab[addr] = val;
428     return;
429   }
430
431   switch (addr) {
432   
433   case 0:
434     /* fifo */
435     if (com->output.file == NULL) {
436       sim_io_write_stdout(&val, 1);
437     }
438     else {
439       fwrite(&val, 1, 1, com->output.file);
440     }
441     /* setup for next write */
442     if (com->output.ready && com->output.delay > 0) {
443       com->output.ready = 0;
444       device_event_queue_schedule(me, com->output.delay, make_write_ready, me);
445     }
446     break;
447
448   default:
449     com->reg[addr] = val;
450     break;
451
452   }
453   update_com_interrupts(me, com);
454 }
455
456 static unsigned
457 hw_com_io_write_buffer_callback(device *me,
458                                 const void *source,
459                                 int space,
460                                 unsigned_word addr,
461                                 unsigned nr_bytes,
462                                 cpu *processor,
463                                 unsigned_word cia)
464 {
465   hw_com_device *com = device_data(me);
466   int i;
467   for (i = 0; i < nr_bytes; i++) {
468     write_com(me, com, addr + i, ((char*)source)[i]);
469   }
470   return nr_bytes;
471 }
472
473
474 /* instances of the hw_com device */
475
476 static void
477 hw_com_instance_delete(device_instance *instance)
478 {
479   /* nothing to delete, the hw_com is attached to the device */
480   return;
481 }
482
483 static int
484 hw_com_instance_read(device_instance *instance,
485                      void *buf,
486                      unsigned_word len)
487 {
488   device *me = device_instance_device(instance);
489   hw_com_device *com = device_data(me);
490   if (com->input.file == NULL)
491     return sim_io_read_stdin(buf, len);
492   else {
493     return fread(buf, 1, len, com->input.file);
494   }
495 }
496
497 static int
498 hw_com_instance_write(device_instance *instance,
499                       const void *buf,
500                       unsigned_word len)
501 {
502   device *me = device_instance_device(instance);
503   hw_com_device *com = device_data(me);
504   if (com->output.file == NULL)
505     return sim_io_write_stdout(buf, len);
506   else {
507     return fwrite(buf, 1, len, com->output.file);
508   }
509 }
510
511 static const device_instance_callbacks hw_com_instance_callbacks = {
512   hw_com_instance_delete,
513   hw_com_instance_read,
514   hw_com_instance_write,
515 };
516
517 static device_instance *
518 hw_com_create_instance(device *me,
519                        const char *path,
520                        const char *args)
521 {
522   /* point an instance directly at the device */
523   return device_create_instance_from(me, NULL,
524                                      device_data(me),
525                                      path, args,
526                                      &hw_com_instance_callbacks);
527 }
528
529
530 static device_callbacks const hw_com_callbacks = {
531   { generic_device_init_address,
532     hw_com_device_init_data },
533   { NULL, }, /* address */
534   { hw_com_io_read_buffer_callback,
535       hw_com_io_write_buffer_callback, },
536   { NULL, }, /* DMA */
537   { NULL, }, /* interrupt */
538   { NULL, }, /* unit */
539   hw_com_create_instance,
540 };
541
542
543 static void *
544 hw_com_create(const char *name,
545               const device_unit *unit_address,
546               const char *args)
547 {
548   /* create the descriptor */
549   hw_com_device *hw_com = ZALLOC(hw_com_device);
550   return hw_com;
551 }
552
553
554 const device_descriptor hw_com_device_descriptor[] = {
555   { "com", hw_com_create, &hw_com_callbacks },
556   { NULL },
557 };
558
559 #endif /* _HW_COM_C_ */