Imported Upstream version 2.9.0
[debian/cc1111] / sim / ucsim / s51.src / serial.cc
1 /*
2  * Simulator of microcontrollers (serial.cc)
3  *
4  * Copyright (C) 1999,99 Drotos Daniel, Talker Bt.
5  *
6  * To contact author send email to drdani@mazsola.iit.uni-miskolc.hu
7  *
8  */
9
10 /* This file is part of microcontroller simulator: ucsim.
11
12 UCSIM is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
16
17 UCSIM is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with UCSIM; see the file COPYING.  If not, write to the Free
24 Software Foundation, 59 Temple Place - Suite 330, Boston, MA
25 02111-1307, USA. */
26 /*@1@*/
27
28 #include "ddconfig.h"
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33 #ifdef HAVE_TERMIOS_H
34 #include <termios.h>
35 #endif
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <sys/time.h>
42 #include <strings.h>
43 #include <assert.h>
44
45 // prj
46 #include "globals.h"
47
48 // local
49 #include "serialcl.h"
50 #include "regs51.h"
51 #include "uc51cl.h"
52 #include "cmdutil.h"
53
54
55 cl_serial::cl_serial(class cl_uc *auc):
56   cl_hw(auc, HW_UART, 0, "uart")
57 {
58   serial_in= serial_out= NIL;
59 }
60
61 cl_serial::~cl_serial(void)
62 {
63   if (serial_in != serial_out && serial_in)
64     {
65 #ifdef HAVE_TERMIOS_H
66       if (isatty(fileno(serial_in)))
67         tcsetattr(fileno(serial_in), TCSANOW, &saved_attributes_in);
68 #endif
69       fclose(serial_in);
70     }
71   if (serial_out)
72     {
73 #ifdef HAVE_TERMIOS_H
74       if (isatty(fileno(serial_out)))
75         tcsetattr(fileno(serial_out), TCSANOW, &saved_attributes_out);
76 #endif
77       fclose(serial_out);
78     }
79   delete serial_in_file_option;
80   delete serial_out_file_option;
81 }
82
83 int
84 cl_serial::init(void)
85 {
86 #ifdef HAVE_TERMIOS_H
87   int i;
88   struct termios tattr;
89 #endif
90
91   set_name("mcs51_uart");
92   sfr= uc->address_space(MEM_SFR_ID);
93   if (sfr)
94     {
95       //sbuf= sfr->register_hw(SBUF, this, 0);
96       //pcon= sfr->register_hw(PCON, this, 0);
97       //scon= sfr->register_hw(SCON, this, 0);
98       register_cell(sfr, SBUF, &sbuf, wtd_restore_write);
99       register_cell(sfr, PCON, &pcon, wtd_restore_write);
100       register_cell(sfr, SCON, &scon, wtd_restore_write);
101     }
102
103   serial_in_file_option= new cl_optref(this);
104   serial_in_file_option->init();
105   serial_in_file_option->use("serial_in_file");
106   serial_out_file_option= new cl_optref(this);
107   serial_out_file_option->init();
108   serial_out_file_option->use("serial_out_file");
109
110   //char *fni, *fno;
111   /*if ((fni= serial_in_file_option->get_value((char*)0)))
112     serial_in= fopen(fni, "r");
113   if ((fno= serial_out_file_option->get_value((char*)0)))
114   serial_out= fopen(fno, "w");*/
115
116   //serial_in = (FILE*)application->args->get_parg(0, "Ser_in");
117   //serial_out= (FILE*)application->args->get_parg(0, "Ser_out");
118   serial_in = (FILE*)serial_in_file_option->get_value((void*)0);
119   serial_out= (FILE*)serial_out_file_option->get_value((void*)0);
120
121   if (serial_in != serial_out && serial_in)
122     {
123       // making `serial' unbuffered
124       if (setvbuf(serial_in, NULL, _IONBF, 0))
125         perror("Unbuffer serial input channel");
126 #ifdef _WIN32
127       if (CH_SERIAL != get_handle_type((HANDLE)_get_osfhandle(fileno(serial_in))))
128 #elif defined HAVE_TERMIOS_H
129       // setting O_NONBLOCK
130       if ((i= fcntl(fileno(serial_in), F_GETFL, 0)) < 0)
131         perror("Get flags of serial input");
132       i|= O_NONBLOCK;
133       if (fcntl(fileno(serial_in), F_SETFL, i) < 0)
134         perror("Set flags of serial input");
135       // switching terminal to noncanonical mode
136       if (isatty(fileno(serial_in)))
137         {
138           tcgetattr(fileno(serial_in), &saved_attributes_in);
139           tcgetattr(fileno(serial_in), &tattr);
140           tattr.c_lflag&= ~(ICANON|ECHO);
141           tattr.c_cc[VMIN] = 1;
142           tattr.c_cc[VTIME]= 0;
143           tcsetattr(fileno(serial_in), TCSAFLUSH, &tattr);
144         }
145       else
146 #endif
147         fprintf(stderr, "Warning: serial input interface connected to a "
148                 "non-terminal file.\n");
149     }
150   if (serial_out)
151     {
152       // making `serial' unbuffered
153       if (setvbuf(serial_out, NULL, _IONBF, 0))
154         perror("Unbuffer serial output channel");
155 #ifdef _WIN32
156       if (CH_SERIAL != get_handle_type((HANDLE)_get_osfhandle(fileno(serial_out))))
157 #elif defined HAVE_TERMIOS_H
158       // setting O_NONBLOCK
159       if ((i= fcntl(fileno(serial_out), F_GETFL, 0)) < 0)
160         perror("Get flags of serial output");
161       i|= O_NONBLOCK;
162       if (fcntl(fileno(serial_out), F_SETFL, i) < 0)
163         perror("Set flags of serial output");
164       // switching terminal to noncanonical mode
165       if (isatty(fileno(serial_out)))
166         {
167           tcgetattr(fileno(serial_out), &saved_attributes_out);
168           tcgetattr(fileno(serial_out), &tattr);
169           tattr.c_lflag&= ~(ICANON|ECHO);
170           tattr.c_cc[VMIN] = 1;
171           tattr.c_cc[VTIME]= 0;
172           tcsetattr(fileno(serial_out), TCSAFLUSH, &tattr);
173         }
174       else
175 #endif
176         fprintf(stderr, "Warning: serial output interface connected to a "
177                 "non-terminal file.\n");
178     }
179
180   class cl_hw *t2= uc->get_hw(HW_TIMER, 2, 0);
181   if ((there_is_t2= t2 != 0))
182     {
183       t_mem d= sfr->get(T2CON);
184       t2_baud= d & (bmRCLK | bmTCLK);
185     }
186   else
187     t2_baud= DD_FALSE;
188
189   return(0);
190 }
191
192 void
193 cl_serial::new_hw_added(class cl_hw *new_hw)
194 {
195   if (new_hw->cathegory == HW_TIMER &&
196       new_hw->id == 2)
197     {
198       there_is_t2= DD_TRUE;
199       t_mem d= sfr->get(T2CON);
200       t2_baud= d & (bmRCLK | bmTCLK);
201     }
202 }
203
204 void
205 cl_serial::added_to_uc(void)
206 {
207   uc->it_sources->add(new cl_it_src(bmES , SCON, bmTI , 0x0023, false,
208                                     "serial transmit", 6));
209   uc->it_sources->add(new cl_it_src(bmES , SCON, bmRI , 0x0023, false,
210                                     "serial receive", 6));
211 }
212
213 t_mem
214 cl_serial::read(class cl_memory_cell *cell)
215 {
216   if (cell == sbuf)
217     return(s_in);
218   else
219     return(cell->get());
220 }
221
222 void
223 cl_serial::write(class cl_memory_cell *cell, t_mem *val)
224 {
225   if (cell == sbuf)
226     {
227       s_out= *val;
228       s_sending= DD_TRUE;
229       s_tr_bit = 0;
230       s_tr_tick= 0;
231       s_tr_t1= 0;
232     }
233   if (cell == scon)
234     {
235       _mode= *val >> 6;
236       _bmREN= *val & bmREN;
237       _bits= 8;
238       switch (_mode)
239         {
240         case 0:
241           _bits= 8;
242           _divby= 12;
243           break;
244         case 1:
245           _bits= 10;
246           _divby= _bmSMOD?16:32;
247           break;
248         case 2:
249           _bits= 11;
250           _divby= _bmSMOD?16:32;
251           break;
252         case 3:
253           _bits= 11;
254           _divby= _bmSMOD?16:32;
255           break;
256         }
257     }
258   else if (cell == pcon)
259     {
260       _bmSMOD= *val & bmSMOD;
261       /*switch (_mode)
262         {
263         case 1:
264           _divby= _bmSMOD?16:32;
265           break;
266         case 2:
267           _divby= _bmSMOD?16:32;
268           break;
269         case 3:
270           _divby= _bmSMOD?16:32;
271           break;
272           }*/
273       if (_mode)
274         _divby= _bmSMOD?16:32;
275     }
276 }
277
278 /*void
279 cl_serial::mem_cell_changed(class cl_m *mem, t_addr addr)
280 {
281   t_mem d;
282
283   d= sbuf->get();
284   write(sbuf, &d);
285   d= pcon->get();
286   write(pcon, &d);
287   d= scon->get();
288   write(scon, &d);
289 }*/
290
291 int
292 cl_serial::serial_bit_cnt(void)
293 {
294   //int divby= 12;
295   int *tr_src= 0, *rec_src= 0;
296
297   switch (_mode)
298     {
299     case 0:
300       //divby  = 12;
301       tr_src = &s_tr_tick;
302       rec_src= &s_rec_tick;
303       break;
304     case 1:
305     case 3:
306       //divby  = (/*pcon->get()&bmSMOD*/_bmSMOD)?16:32;
307       tr_src = &s_tr_t1;
308       rec_src= &s_rec_t1;
309       break;
310     case 2:
311       //divby  = (/*pcon->get()&bmSMOD*/_bmSMOD)?16:32;
312       tr_src = &s_tr_tick;
313       rec_src= &s_rec_tick;
314       break;
315     }
316   if (t2_baud)
317     _divby= 16;
318   if (s_sending)
319     {
320       while (*tr_src >= _divby)
321         {
322           (*tr_src)-= _divby;
323           s_tr_bit++;
324           //printf("serial bit sent %d\n",uc->ticks->ticks);
325         }
326     }
327   if (s_receiving)
328     {
329       while (*rec_src >= _divby)
330         {
331           (*rec_src)-= _divby;
332           s_rec_bit++;
333         }
334     }
335   return(0);
336 }
337
338 int
339 cl_serial::tick(int cycles)
340 {
341   char c;
342
343   serial_bit_cnt(/*_mode*/);
344   if (s_sending &&
345       (s_tr_bit >= _bits))
346     {
347       s_sending= DD_FALSE;
348       scon->set_bit1(bmTI);
349       if (serial_out)
350         {
351           putc(s_out, serial_out);
352           fflush(serial_out);
353         }
354       s_tr_bit-= _bits;
355       //printf("serial out %d bit rems %d\n",s_tr_bit,uc->ticks->ticks);
356     }
357   if ((/*scn & bmREN*/_bmREN) &&
358       serial_in &&
359       !s_receiving)
360     {
361 #ifdef _WIN32
362       HANDLE handle = (HANDLE)_get_osfhandle(fileno(serial_in));
363       assert(INVALID_HANDLE_VALUE != handle);
364
365       if (input_avail(handle))
366 #else
367       if (input_avail(fileno(serial_in)))
368 #endif
369         {
370           s_receiving= DD_TRUE;
371           s_rec_bit= 0;
372           s_rec_tick= /*uc51->*/s_rec_t1= 0;
373         }
374     }
375   if (s_receiving &&
376       (s_rec_bit >= _bits))
377     {
378       if (::read(fileno(serial_in), &c, 1) == 1)
379         {
380           s_in= c;
381           sbuf->set(s_in);
382           received(c);
383         }
384       s_receiving= DD_FALSE;
385       s_rec_bit-= _bits;
386     }
387
388   int l;
389   s_tr_tick+= (l= cycles * uc->clock_per_cycle());
390   s_rec_tick+= l;
391   return(0);
392 }
393
394 void
395 cl_serial::received(int c)
396 {
397   scon->set_bit1(bmRI);
398 }
399
400 void
401 cl_serial::reset(void)
402 {
403   s_tr_t1    = 0;
404   s_rec_t1   = 0;
405   s_tr_tick  = 0;
406   s_rec_tick = 0;
407   s_in       = 0;
408   s_out      = 0;
409   s_sending  = DD_FALSE;
410   s_receiving= DD_FALSE;
411   s_rec_bit  = 0;
412   s_tr_bit   = 0;
413 }
414
415 void
416 cl_serial::happen(class cl_hw *where, enum hw_event he, void *params)
417 {
418   if (where->cathegory == HW_TIMER)
419     {
420       if (where->id == 1)
421         {
422           //printf("serial: timer overflowed %ld\n", uc->ticks->ticks);
423           s_rec_t1++;
424           s_tr_t1++;
425         }
426       if (where->id == 2 /*&& there_is_t2*/)
427         {
428           switch (he)
429             {
430             case EV_T2_MODE_CHANGED:
431               {
432                 if (!t2_baud)
433                   s_rec_t1= s_tr_t1= 0;
434                 t_mem *d= (t_mem *)params;
435                 t2_baud= *d & (bmRCLK | bmTCLK);
436                 break;
437               }
438             case EV_OVERFLOW:
439               //printf("T2 \abaud ov r%d t%d\n",s_rec_t1,s_tr_t1);
440               s_rec_t1++;
441               s_tr_t1++;
442               break;
443             default: break;
444             }
445         }
446     }
447 }
448
449 void
450 cl_serial::print_info(class cl_console_base *con)
451 {
452   const char *modes[]= { "Shift, fixed clock",
453                    "8 bit UART timer clocked",
454                    "9 bit UART fixed clock",
455                    "9 bit UART timer clocked" };
456   int sc= scon->get();
457
458   con->dd_printf("%s[%d]", id_string, id);
459   int mode= (sc&(bmSM0|bmSM1))>>6;
460   con->dd_printf(" %s", modes[mode]);
461   if (mode == 1 || mode == 2)
462     con->dd_printf(" (timer%d)", (t2_baud)?2:1);
463   con->dd_printf(" MultiProc=%s",
464                  (mode&2)?((sc&bmSM2)?"ON":"OFF"):"none");
465   con->dd_printf(" irq=%s", (sfr->get(IE)&bmES)?"en":"dis");
466   con->dd_printf(" prio=%d", uc->it_priority(bmPS));
467   con->dd_printf("\n");
468
469   con->dd_printf("Receiver");
470   con->dd_printf(" %s", (sc&bmREN)?"ON":"OFF");
471   con->dd_printf(" RB8=%c", (sc&bmRB8)?'1':'0');
472   con->dd_printf(" irq=%c", (sc&bmRI)?'1':'0');
473   con->dd_printf("\n");
474
475   con->dd_printf("Transmitter");
476   con->dd_printf(" TB8=%c", (sc&bmTB8)?'1':'0');
477   con->dd_printf(" irq=%c", (sc&bmTI)?'1':'0');
478   con->dd_printf("\n");
479   /*con->dd_printf("s_rec_t1=%d s_rec_bit=%d s_rec_tick=%d\n",
480                  s_rec_t1, s_rec_bit, s_rec_tick);
481   con->dd_printf("s_tr_t1=%d s_tr_bit=%d s_tr_tick=%d\n",
482                  s_tr_t1, s_tr_bit, s_tr_tick);
483                  con->dd_printf("divby=%d bits=%d\n", _divby, _bits);*/
484 }
485
486
487 /* End of s51.src/serial.cc */