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