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