Merge branch ucsim-034-pre3 to main trunk; new version 0.4
[fw/sdcc] / 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 #include <termios.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <sys/time.h>
38 #include <strings.h>
39
40 // local
41 #include "serialcl.h"
42 #include "regs51.h"
43 #include "uc51cl.h"
44
45
46 cl_serial::cl_serial(class cl_uc *auc):
47   cl_hw(auc, HW_UART, 0, "uart")
48 {}
49
50 cl_serial::~cl_serial(void)
51 {
52   if (serial_out)
53     {
54       if (isatty(fileno(serial_out)))
55         tcsetattr(fileno(serial_out), TCSANOW, &saved_attributes_out);
56       fclose(serial_out);
57     }
58   if (serial_in)
59     {
60       if (isatty(fileno(serial_in)))
61         tcsetattr(fileno(serial_in), TCSANOW, &saved_attributes_in);
62       fclose(serial_in);
63     }
64 }
65
66 int
67 cl_serial::init(void)
68 {
69   class cl_mem *sfr;
70   int i;
71   struct termios tattr;
72
73   sfr= uc->mem(MEM_SFR);
74   if (sfr)
75     {
76       //sbuf= sfr->register_hw(SBUF, this, 0);
77       //pcon= sfr->register_hw(PCON, this, 0);
78       //scon= sfr->register_hw(SCON, this, 0);
79       register_cell(sfr, SBUF, &sbuf, wtd_restore_write);
80       register_cell(sfr, PCON, &pcon, wtd_restore_write);
81       register_cell(sfr, SCON, &scon, wtd_restore_write);
82     }
83
84   serial_in = (FILE*)uc->sim->app->args->get_parg(0, "Ser_in");
85   serial_out= (FILE*)uc->sim->app->args->get_parg(0, "Ser_out");
86   if (serial_in)
87     {
88       // making `serial' unbuffered
89       if (setvbuf(serial_in, NULL, _IONBF, 0))
90         perror("Unbuffer serial input channel");
91       // setting O_NONBLOCK
92       if ((i= fcntl(fileno(serial_in), F_GETFL, 0)) < 0)
93         perror("Get flags of serial input");
94       i|= O_NONBLOCK;
95       if (fcntl(fileno(serial_in), F_SETFL, i) < 0)
96         perror("Set flags of serial input");
97       // switching terminal to noncanonical mode
98       if (isatty(fileno(serial_in)))
99         {
100           tcgetattr(fileno(serial_in), &saved_attributes_in);
101           tcgetattr(fileno(serial_in), &tattr);
102           tattr.c_lflag&= ~(ICANON|ECHO);
103           tattr.c_cc[VMIN] = 1;
104           tattr.c_cc[VTIME]= 0;
105           tcsetattr(fileno(serial_in), TCSAFLUSH, &tattr);
106         }
107       else
108         fprintf(stderr, "Warning: serial input interface connected to a "
109                 "non-terminal file.\n");
110     }
111   if (serial_out)
112     {
113       // making `serial' unbuffered
114       if (setvbuf(serial_out, NULL, _IONBF, 0))
115         perror("Unbuffer serial output channel");
116       // setting O_NONBLOCK
117       if ((i= fcntl(fileno(serial_out), F_GETFL, 0)) < 0)
118         perror("Get flags of serial output");
119       i|= O_NONBLOCK;
120       if (fcntl(fileno(serial_out), F_SETFL, i) < 0)
121         perror("Set flags of serial output");
122       // switching terminal to noncanonical mode
123       if (isatty(fileno(serial_out)))
124         {
125           tcgetattr(fileno(serial_out), &saved_attributes_out);
126           tcgetattr(fileno(serial_out), &tattr);
127           tattr.c_lflag&= ~(ICANON|ECHO);
128           tattr.c_cc[VMIN] = 1;
129           tattr.c_cc[VTIME]= 0;
130           tcsetattr(fileno(serial_out), TCSAFLUSH, &tattr);
131         }
132       else
133         fprintf(stderr, "Warning: serial output interface connected to a "
134                 "non-terminal file.\n");
135     }
136
137   class cl_hw *t2= uc->get_hw(HW_TIMER, 2, 0);
138   if ((there_is_t2= t2 != 0))
139     {
140       t_mem d= sfr->get(T2CON);
141       t2_baud= d & (bmRCLK | bmTCLK);
142     }
143   else
144     t2_baud= DD_FALSE;
145
146   return(0);
147 }
148
149 void
150 cl_serial::new_hw_added(class cl_hw *new_hw)
151 {
152   if (new_hw->cathegory == HW_TIMER &&
153       new_hw->id == 2)
154     {
155       there_is_t2= DD_TRUE;
156       t_mem d= uc->mem(MEM_SFR)->get(T2CON);
157       t2_baud= d & (bmRCLK | bmTCLK);
158     }
159 }
160
161 void
162 cl_serial::added_to_uc(void)
163 {
164   uc->it_sources->add(new cl_it_src(bmES , SCON, bmTI , 0x0023, false,
165                                     "serial transmit", 6));
166   uc->it_sources->add(new cl_it_src(bmES , SCON, bmRI , 0x0023, false,
167                                     "serial receive", 6));
168 }
169
170 t_mem
171 cl_serial::read(class cl_cell *cell)
172 {
173   if (cell == sbuf)
174     return(s_in);
175   else
176     return(cell->get());
177 }
178
179 void
180 cl_serial::write(class cl_cell *cell, t_mem *val)
181 {
182   if (cell == sbuf)
183     {
184       s_out= *val;
185       s_sending= DD_TRUE;
186       s_tr_bit = 0;
187       s_tr_tick= 0;
188       s_tr_t1= 0;
189     }
190   if (cell == scon)
191     {
192       _mode= *val >> 6;
193       _bmREN= *val & bmREN;
194       _bits= 8;
195       switch (_mode)
196         {
197         case 0:
198           _bits= 8;
199           _divby= 12;
200           break;
201         case 1:
202           _bits= 10;
203           _divby= _bmSMOD?16:32;
204           break;
205         case 2:
206           _bits= 11;
207           _divby= _bmSMOD?16:32;
208           break;
209         case 3:
210           _bits= 11;
211           _divby= _bmSMOD?16:32;
212           break;
213         }
214     }
215   else if (cell == pcon)
216     {
217       _bmSMOD= *val & bmSMOD;
218       /*switch (_mode)
219         {
220         case 1:
221           _divby= _bmSMOD?16:32;
222           break;
223         case 2:
224           _divby= _bmSMOD?16:32;
225           break;
226         case 3:
227           _divby= _bmSMOD?16:32;
228           break;
229           }*/
230       if (_mode)
231         _divby= _bmSMOD?16:32;
232     }
233 }
234
235 /*void
236 cl_serial::mem_cell_changed(class cl_mem *mem, t_addr addr)
237 {
238   class cl_mem *sfr= uc->mem(MEM_SFR);
239   t_mem d;
240
241   d= sbuf->get();
242   write(sbuf, &d);
243   d= pcon->get();
244   write(pcon, &d);
245   d= scon->get();
246   write(scon, &d);
247 }*/
248
249 int
250 cl_serial::serial_bit_cnt(void)
251 {
252   //int divby= 12;
253   int *tr_src= 0, *rec_src= 0;
254
255   switch (_mode)
256     {
257     case 0:
258       //divby  = 12;
259       tr_src = &s_tr_tick;
260       rec_src= &s_rec_tick;
261       break;
262     case 1:
263     case 3:
264       //divby  = (/*pcon->get()&bmSMOD*/_bmSMOD)?16:32;
265       tr_src = &s_tr_t1;
266       rec_src= &s_rec_t1;
267       break;
268     case 2:
269       //divby  = (/*pcon->get()&bmSMOD*/_bmSMOD)?16:32;
270       tr_src = &s_tr_tick;
271       rec_src= &s_rec_tick;
272       break;
273     }
274   if (t2_baud)
275     _divby= 16;
276   if (s_sending)
277     {
278       while (*tr_src >= _divby)
279         {
280           (*tr_src)-= _divby;
281           s_tr_bit++;
282           //printf("serial bit sent %d\n",uc->ticks->ticks);
283         }
284     }
285   if (s_receiving)
286     {
287       while (*rec_src >= _divby)
288         {
289           (*rec_src)-= _divby;
290           s_rec_bit++;
291         }
292     }
293   return(0);
294 }
295
296 int
297 cl_serial::tick(int cycles)
298 {
299   char c;
300
301   serial_bit_cnt(/*_mode*/);
302   if (s_sending &&
303       (s_tr_bit >= _bits))
304     {
305       s_sending= DD_FALSE;
306       scon->set_bit1(bmTI);
307       if (serial_out)
308         {
309           putc(s_out, serial_out);
310           fflush(serial_out);
311         }
312       s_tr_bit-= _bits;
313       //printf("serial out %d bit rems %d\n",s_tr_bit,uc->ticks->ticks);
314     }
315   if ((/*scn & bmREN*/_bmREN) &&
316       serial_in &&
317       !s_receiving)
318     {
319       fd_set set; static struct timeval timeout= {0,0};
320       FD_ZERO(&set);
321       FD_SET(fileno(serial_in), &set);
322       int i= select(fileno(serial_in)+1, &set, NULL, NULL, &timeout);
323       if (i > 0 &&
324           FD_ISSET(fileno(serial_in), &set))
325         {
326           s_receiving= DD_TRUE;
327           s_rec_bit= 0;
328           s_rec_tick= /*uc51->*/s_rec_t1= 0;
329         }
330     }
331   if (s_receiving &&
332       (s_rec_bit >= _bits))
333     {
334       if (::read(fileno(serial_in), &c, 1) == 1)
335         {
336           s_in= c;
337           sbuf->set(s_in);
338           received(c);
339         }
340       s_receiving= DD_FALSE;
341       s_rec_bit-= _bits;
342     }
343   
344   int l;
345   s_tr_tick+= (l= cycles * uc->clock_per_cycle());
346   s_rec_tick+= l;
347   return(0);
348 }
349
350 void
351 cl_serial::received(int c)
352 {
353   scon->set_bit1(bmRI);
354 }
355
356 void
357 cl_serial::reset(void)
358 {
359   s_tr_t1    = 0;
360   s_rec_t1   = 0;
361   s_tr_tick  = 0;
362   s_rec_tick = 0;
363   s_in       = 0;
364   s_out      = 0;
365   s_sending  = DD_FALSE;
366   s_receiving= DD_FALSE;
367   s_rec_bit  = 0;
368   s_tr_bit   = 0;
369 }
370
371 void
372 cl_serial::happen(class cl_hw *where, enum hw_event he, void *params)
373 {
374   if (where->cathegory == HW_TIMER)
375     {
376       if (where->id == 1)
377         {
378           //printf("serial: timer overflowed %ld\n", uc->ticks->ticks);
379           s_rec_t1++;
380           s_tr_t1++;
381         }
382       if (where->id == 2 /*&& there_is_t2*/)
383         {
384           switch (he)
385             {
386             case EV_T2_MODE_CHANGED:
387               {
388                 if (!t2_baud)
389                   s_rec_t1= s_tr_t1= 0;
390                 t_mem *d= (t_mem *)params;
391                 t2_baud= *d & (bmRCLK | bmTCLK);
392                 break;
393               }
394             case EV_OVERFLOW:
395               //printf("T2 \abaud ov r%d t%d\n",s_rec_t1,s_tr_t1);
396               s_rec_t1++;
397               s_tr_t1++;
398               break;
399             default: break;
400             }
401         }
402     }
403 }
404
405 void
406 cl_serial::print_info(class cl_console *con)
407 {
408   char *modes[]= { "Shift, fixed clock",
409                    "8 bit UART timer clocked",
410                    "9 bit UART fixed clock",
411                    "9 bit UART timer clocked" };
412   int sc= scon->get();
413
414   con->dd_printf("%s[%d]", id_string, id);
415   int mode= (sc&(bmSM0|bmSM1))>>6;
416   con->dd_printf(" %s", modes[mode]);
417   if (mode == 1 || mode == 2)
418     con->dd_printf(" (timer%d)", (t2_baud)?2:1);
419   con->dd_printf(" MultiProc=%s",
420                  (mode&2)?((sc&bmSM2)?"ON":"OFF"):"none");
421   con->dd_printf(" irq=%s", (uc->get_mem(MEM_SFR, IE)&bmES)?"en":"dis");
422   con->dd_printf(" prio=%d", uc->it_priority(bmPS));
423   con->dd_printf("\n");
424
425   con->dd_printf("Receiver");
426   con->dd_printf(" %s", (sc&bmREN)?"ON":"OFF");
427   con->dd_printf(" RB8=%c", (sc&bmRB8)?'1':'0');
428   con->dd_printf(" irq=%c", (sc&bmRI)?'1':'0');
429   con->dd_printf("\n");
430
431   con->dd_printf("Transmitter");
432   con->dd_printf(" TB8=%c", (sc&bmTB8)?'1':'0');
433   con->dd_printf(" irq=%c", (sc&bmTI)?'1':'0');
434   con->dd_printf("\n");
435   /*con->dd_printf("s_rec_t1=%d s_rec_bit=%d s_rec_tick=%d\n",
436                  s_rec_t1, s_rec_bit, s_rec_tick);
437   con->dd_printf("s_tr_t1=%d s_tr_bit=%d s_tr_tick=%d\n",
438                  s_tr_t1, s_tr_bit, s_tr_tick);
439                  con->dd_printf("divby=%d bits=%d\n", _divby, _bits);*/
440 }
441
442
443 /* End of s51.src/serial.cc */