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