source: rtems/c/src/lib/libbsp/m68k/av5282/console/console.c @ 183af89

4.115
Last change on this file since 183af89 was 183af89, checked in by Joel Sherrill <joel.sherrill@…>, on 05/03/12 at 19:17:23

Miscellaneous - Clean up file headers so patterns followed

XXX

  • Property mode set to 100644
File size: 19.3 KB
Line 
1/*
2 *  Multi UART console serial I/O.
3 *
4 *  TO DO: Add DMA input/output
5 *
6 *  $Id$
7 */
8
9#include <stdio.h>
10#include <fcntl.h>
11#include <rtems/libio.h>
12#include <rtems/termiostypes.h>
13#include <termios.h>
14#include <bsp.h>
15#include <malloc.h>
16#include <rtems/mw_uid.h>
17
18#include <rtems/bspIo.h>
19
20#define UART_INTC0_IRQ_VECTOR(x) (64+13+(x))
21
22#define MCF5282_UART_USR_ERROR ( MCF5282_UART_USR_RB | \
23                                 MCF5282_UART_USR_FE | \
24                                 MCF5282_UART_USR_PE | \
25                                 MCF5282_UART_USR_OE )
26
27static ssize_t IntUartPollWrite(int minor, const char *buf, size_t len);
28static ssize_t IntUartInterruptWrite (int minor, const char *buf, size_t len);
29
30static void _BSP_null_char( char c )
31{
32  rtems_interrupt_level level;
33
34  if (c == '\n')
35    _BSP_null_char('\r');
36  rtems_interrupt_disable(level);
37  while ( (MCF5282_UART_USR(CONSOLE_PORT) & MCF5282_UART_USR_TXRDY) == 0 )
38    continue;
39
40  MCF5282_UART_UTB(CONSOLE_PORT) = c;
41  while ( (MCF5282_UART_USR(CONSOLE_PORT) & MCF5282_UART_USR_TXRDY) == 0 )
42    continue;
43
44  rtems_interrupt_enable(level);
45}
46BSP_output_char_function_type     BSP_output_char = _BSP_null_char;
47BSP_polling_getchar_function_type BSP_poll_char = NULL;
48
49
50#define MAX_UART_INFO     3
51#define RX_BUFFER_SIZE    512
52
53struct IntUartInfoStruct
54{
55  int           iomode;
56  volatile int  uimr;
57  int           baud;
58  int           databits;
59  int           parity;
60  int           stopbits;
61  int           hwflow;
62  int           rx_in;
63  int           rx_out;
64  char          rx_buffer[RX_BUFFER_SIZE];
65  void         *ttyp;
66};
67
68struct IntUartInfoStruct IntUartInfo[MAX_UART_INFO];
69
70/*
71 * Function : IntUartSet
72 *
73 * Description : This updates the hardware UART settings.
74 */
75static void IntUartSet(
76  int minor,
77  int baud,
78  int databits,
79  int parity,
80  int stopbits,
81  int hwflow
82)
83{
84  int                       divisor;
85  uint32_t                  clock_speed;
86  uint8_t                   umr1 = 0;
87  uint8_t                   umr2 = 0;
88  struct IntUartInfoStruct *info = &IntUartInfo[minor];
89  rtems_interrupt_level     level;
90
91  rtems_interrupt_disable(level);
92
93  /* disable interrupts, clear RTS line, and disable the UARTS */
94  MCF5282_UART_UIMR(minor) = 0;
95  MCF5282_UART_UOP0(minor) = 1;
96  MCF5282_UART_UCR(minor) =
97    (MCF5282_UART_UCR_TX_DISABLED | MCF5282_UART_UCR_RX_DISABLED);
98
99  /* save the current values */
100  info->uimr     = 0;
101  info->baud     = baud;
102  info->databits = databits;
103  info->parity   = parity;
104  info->stopbits = stopbits;
105  info->hwflow   = hwflow;
106
107  clock_speed = get_CPU_clock_speed();
108  /* determine the baud divisor value */
109  divisor = (clock_speed / ( 32 * baud ));
110  if ( divisor < 2 )
111    divisor = 2;
112
113  /* check to see if doing hardware flow control */
114  if ( hwflow ) {
115    /* set hardware flow options */
116    umr1 |= MCF5282_UART_UMR1_RXRTS;
117    umr2 |= MCF5282_UART_UMR2_TXCTS;
118  }
119
120  /* determine the new umr values */
121  umr1 |= (parity | databits);
122  umr2 |= (stopbits);
123
124  /* reset the uart */
125  MCF5282_UART_UCR(minor) = MCF5282_UART_UCR_RESET_ERROR;
126  MCF5282_UART_UCR(minor) = MCF5282_UART_UCR_RESET_RX;
127  MCF5282_UART_UCR(minor) = MCF5282_UART_UCR_RESET_TX;
128
129  /* reset the uart mode register and update values */
130  MCF5282_UART_UCR(minor) = MCF5282_UART_UCR_RESET_MR;
131  MCF5282_UART_UMR(minor) = umr1;
132  MCF5282_UART_UMR(minor) = umr2;
133
134  /* set the baud rate values */
135  MCF5282_UART_UCSR(minor) =
136    (MCF5282_UART_UCSR_RCS_SYS_CLK | MCF5282_UART_UCSR_TCS_SYS_CLK);
137  MCF5282_UART_UBG1(minor) = (divisor & 0xff00) >> 8;
138  MCF5282_UART_UBG2(minor) = (divisor & 0x00ff);
139
140  /* enable the uart */
141  MCF5282_UART_UCR(minor) =
142    (MCF5282_UART_UCR_TX_ENABLED | MCF5282_UART_UCR_RX_ENABLED);
143
144  /* check to see if interrupts need to be enabled */
145  if ( info->iomode != TERMIOS_POLLED ) {
146    /* enable rx interrupts */
147    info->uimr |= MCF5282_UART_UIMR_FFULL;
148    MCF5282_UART_UIMR(minor) = info->uimr;
149  }
150
151  /* check to see if doing hardware flow control */
152  if ( hwflow ) {
153    /* assert the RTS line */
154    MCF5282_UART_UOP1(minor) = 1;
155  }
156
157  rtems_interrupt_enable(level);
158}
159
160/*
161 * Function : IntUartSetAttributes
162 *
163 * Description : This provides the hardware-dependent portion of tcsetattr().
164 * value and sets it. At the moment this just sets the baud rate.
165 *
166 * Note: The highest baudrate is 115200 as this stays within
167 * an error of +/- 5% at 25MHz processor clock
168 */
169static int IntUartSetAttributes(
170  int                   minor,
171  const struct termios *t
172)
173{
174  /* set default index values */
175  int                         baud     = (int)19200;
176  int                         databits = (int)MCF5282_UART_UMR1_BC_8;
177  int                         parity   = (int)MCF5282_UART_UMR1_PM_NONE;
178  int                         stopbits = (int)MCF5282_UART_UMR2_STOP_BITS_1;
179  int                         hwflow   = (int)0;
180  struct IntUartInfoStruct   *info     = &IntUartInfo[minor];
181
182  /* check to see if input is valid */
183  if ( t != (const struct termios *)0 ) {
184    /* determine baud rate index */
185    baud = rtems_termios_baud_to_number(t->c_cflag & CBAUD);
186
187    /* determine data bits */
188    switch ( t->c_cflag & CSIZE ) {
189      case CS5:
190        databits = (int)MCF5282_UART_UMR1_BC_5;
191        break;
192      case CS6:
193        databits = (int)MCF5282_UART_UMR1_BC_6;
194        break;
195      case CS7:
196        databits = (int)MCF5282_UART_UMR1_BC_7;
197        break;
198      case CS8:
199        databits = (int)MCF5282_UART_UMR1_BC_8;
200        break;
201    }
202
203    /* determine if parity is enabled */
204    if ( t->c_cflag & PARENB ) {
205      if ( t->c_cflag & PARODD ) {
206        /* odd parity */
207        parity = (int)MCF5282_UART_UMR1_PM_ODD;
208      } else {
209        /* even parity */
210        parity = (int)MCF5282_UART_UMR1_PM_EVEN;
211      }
212    }
213
214    /* determine stop bits */
215    if ( t->c_cflag & CSTOPB ) {
216      /* two stop bits */
217      stopbits = (int)MCF5282_UART_UMR2_STOP_BITS_2;
218    }
219
220    /* check to see if hardware flow control */
221    if ( t->c_cflag & CRTSCTS ) {
222      hwflow = 1;
223    }
224  }
225
226  /* check to see if values have changed */
227  if ( ( baud     != info->baud     ) ||
228     ( databits != info->databits ) ||
229     ( parity   != info->parity   ) ||
230     ( stopbits != info->stopbits ) ||
231     ( hwflow   != info->hwflow   ) ) {
232
233    /* call function to set values */
234    IntUartSet(minor, baud, databits, parity, stopbits, hwflow);
235  }
236
237  return RTEMS_SUCCESSFUL;
238}
239
240/*
241 * Function : IntUartInterruptHandler
242 *
243 * Description : This is the interrupt handler for the internal uart. It
244 * determines which channel caused the interrupt before queueing any received
245 * chars and dequeueing chars waiting for transmission.
246 */
247static rtems_isr IntUartInterruptHandler(rtems_vector_number v)
248{
249  unsigned int                chan = v - UART_INTC0_IRQ_VECTOR(0);
250  struct IntUartInfoStruct   *info = &IntUartInfo[chan];
251
252  /* check to see if received data */
253  if ( MCF5282_UART_UISR(chan) & MCF5282_UART_UISR_RXRDY ) {
254    /* read data and put into the receive buffer */
255    while ( MCF5282_UART_USR(chan) & MCF5282_UART_USR_RXRDY ) {
256
257      if ( MCF5282_UART_USR(chan) & MCF5282_UART_USR_ERROR ) {
258        /* clear the error */
259        MCF5282_UART_UCR(chan) = MCF5282_UART_UCR_RESET_ERROR;
260      }
261      /* put data in rx buffer and check for errors */
262      info->rx_buffer[info->rx_in] = MCF5282_UART_URB(chan);
263
264      /* update buffer values */
265      info->rx_in++;
266
267      if ( info->rx_in >= RX_BUFFER_SIZE ) {
268        info->rx_in = 0;
269      }
270    }
271
272    /* Make sure the port has been opened */
273    if ( info->ttyp ) {
274
275      /* check to see if task driven */
276      if ( info->iomode == TERMIOS_TASK_DRIVEN ) {
277        /* notify rx task that rx buffer has data */
278        rtems_termios_rxirq_occured(info->ttyp);
279      } else {
280        /* Push up the received data */
281        rtems_termios_enqueue_raw_characters(
282            info->ttyp, info->rx_buffer, info->rx_in);
283        info->rx_in    = 0;
284      }
285    }
286  }
287
288  /* check to see if data needs to be transmitted */
289  if ( ( info->uimr & MCF5282_UART_UIMR_TXRDY ) &&
290     ( MCF5282_UART_UISR(chan) & MCF5282_UART_UISR_TXRDY ) )
291  {
292
293    /* disable tx interrupts */
294    info->uimr &= ~MCF5282_UART_UIMR_TXRDY;
295    MCF5282_UART_UIMR(chan) = info->uimr;
296
297    /* tell upper level that character has been sent */
298    if ( info->ttyp )
299      rtems_termios_dequeue_characters(info->ttyp, 1);
300  }
301}
302
303/*
304 * Function : IntUartInitialize
305 *
306 * Description : This initialises the internal uart hardware for all
307 * internal uarts. If the internal uart is to be interrupt driven then the
308 * interrupt vectors are hooked.
309 */
310static void IntUartInitialize(void)
311{
312  unsigned int              chan;
313  struct IntUartInfoStruct *info;
314  rtems_isr_entry           old_handler;
315  rtems_interrupt_level     level;
316
317  for ( chan = 0; chan < MAX_UART_INFO; chan++ ) {
318    info = &IntUartInfo[chan];
319
320    info->ttyp     = NULL;
321    info->rx_in    = 0;
322    info->rx_out   = 0;
323    info->baud     = -1;
324    info->databits = -1;
325    info->parity   = -1;
326    info->stopbits = -1;
327    info->hwflow   = -1;
328    info->iomode   = TERMIOS_POLLED;
329
330    MCF5282_UART_UACR(chan) = 0;
331    MCF5282_UART_UIMR(chan) = 0;
332    if ( info->iomode != TERMIOS_POLLED ) {
333      rtems_interrupt_catch (IntUartInterruptHandler,
334                   UART_INTC0_IRQ_VECTOR(chan),
335                   &old_handler);
336    }
337
338    /* set uart default values */
339    IntUartSetAttributes(chan, NULL);
340
341    /* unmask interrupt */
342    rtems_interrupt_disable(level);
343    switch(chan) {
344      case 0:
345        MCF5282_INTC0_ICR13 = MCF5282_INTC_ICR_IL(UART0_IRQ_LEVEL) |
346                              MCF5282_INTC_ICR_IP(UART0_IRQ_PRIORITY);
347        MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT13 |
348                              MCF5282_INTC_IMRL_MASKALL);
349        break;
350
351    case 1:
352      MCF5282_INTC0_ICR14 = MCF5282_INTC_ICR_IL(UART1_IRQ_LEVEL) |
353                            MCF5282_INTC_ICR_IP(UART1_IRQ_PRIORITY);
354      MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT14 |
355                            MCF5282_INTC_IMRL_MASKALL);
356    break;
357
358    case 2:
359      MCF5282_INTC0_ICR15 = MCF5282_INTC_ICR_IL(UART2_IRQ_LEVEL) |
360                            MCF5282_INTC_ICR_IP(UART2_IRQ_PRIORITY);
361      MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT15 |
362                            MCF5282_INTC_IMRL_MASKALL);
363      break;
364    }
365    rtems_interrupt_enable(level);
366
367  } /* of chan loop */
368
369
370} /* IntUartInitialise */
371
372
373/*
374 * Function : IntUartInterruptWrite
375 *
376 * Description : This writes a single character to the appropriate uart
377 * channel. This is either called during an interrupt or in the user's task
378 * to initiate a transmit sequence. Calling this routine enables Tx
379 * interrupts.
380 */
381static ssize_t IntUartInterruptWrite(
382  int         minor,
383  const char *buf,
384  size_t      len
385)
386{
387  rtems_interrupt_level     level;
388
389  rtems_interrupt_disable(level);
390
391  /* write out character */
392  MCF5282_UART_UTB(minor) = *buf;
393
394  /* enable tx interrupt */
395  IntUartInfo[minor].uimr |= MCF5282_UART_UIMR_TXRDY;
396  MCF5282_UART_UIMR(minor) = IntUartInfo[minor].uimr;
397
398  rtems_interrupt_enable(level);
399  return 0;
400}
401
402/*
403 * Function : IntUartInterruptOpen
404 *
405 * Description : This enables interrupts when the tty is opened.
406 */
407static int IntUartInterruptOpen(
408  int   major,
409  int   minor,
410  void *arg
411)
412{
413  struct IntUartInfoStruct   *info = &IntUartInfo[minor];
414
415  /* enable the uart */
416  MCF5282_UART_UCR(minor) = (MCF5282_UART_UCR_TX_ENABLED |
417                             MCF5282_UART_UCR_RX_ENABLED);
418
419  /* check to see if interrupts need to be enabled */
420  if ( info->iomode != TERMIOS_POLLED ) {
421    /* enable rx interrupts */
422    info->uimr |= MCF5282_UART_UIMR_FFULL;
423    MCF5282_UART_UIMR(minor) = info->uimr;
424  }
425
426  /* check to see if doing hardware flow control */
427  if ( info->hwflow ) {
428    /* assert the RTS line */
429    MCF5282_UART_UOP1(minor) = 1;
430  }
431
432  return 0;
433}
434
435/*
436 * Function : IntUartInterruptClose
437 *
438 * Description : This disables interrupts when the tty is closed.
439 */
440static int IntUartInterruptClose(
441  int   major,
442  int   minor,
443  void *arg
444)
445{
446  struct IntUartInfoStruct   *info = &IntUartInfo[minor];
447
448  /* disable the interrupts and the uart */
449  MCF5282_UART_UIMR(minor) = 0;
450  MCF5282_UART_UCR(minor) =
451    (MCF5282_UART_UCR_TX_DISABLED | MCF5282_UART_UCR_RX_DISABLED);
452
453  /* reset values */
454  info->ttyp     = NULL;
455  info->uimr       = 0;
456  info->rx_in    = 0;
457  info->rx_out   = 0;
458
459  return 0;
460}
461
462/*
463 * Function : IntUartTaskRead
464 *
465 * Description : This reads all available characters from the internal uart
466 * and places them into the termios buffer.  The rx interrupts will be
467 * re-enabled after all data has been read.
468 */
469static int IntUartTaskRead(int minor)
470{
471  char                        buffer[RX_BUFFER_SIZE];
472  int                         count;
473  int                         rx_in;
474  int                         index = 0;
475  struct IntUartInfoStruct   *info  = &IntUartInfo[minor];
476
477  /* determine number of values to copy out */
478  rx_in = info->rx_in;
479  if ( info->rx_out <= rx_in ) {
480    count = rx_in - info->rx_out;
481  } else {
482    count = (RX_BUFFER_SIZE - info->rx_out) + rx_in;
483  }
484
485  /* copy data into local buffer from rx buffer */
486  while ( ( index < count ) && ( index < RX_BUFFER_SIZE ) ) {
487    /* copy data byte */
488    buffer[index] = info->rx_buffer[info->rx_out];
489    index++;
490
491    /* increment rx buffer values */
492    info->rx_out++;
493    if ( info->rx_out >= RX_BUFFER_SIZE ) {
494      info->rx_out = 0;
495    }
496  }
497
498  /* check to see if buffer is not empty */
499  if ( count > 0 ) {
500    /* set characters into termios buffer  */
501    rtems_termios_enqueue_raw_characters(info->ttyp, buffer, count);
502  }
503
504  return EOF;
505}
506
507
508
509/*
510 * Function : IntUartPollRead
511 *
512 * Description : This reads a character from the internal uart. It returns
513 * to the caller without blocking if not character is waiting.
514 */
515static int IntUartPollRead(int minor)
516{
517  if ( (MCF5282_UART_USR(minor) & MCF5282_UART_USR_RXRDY) == 0 )
518    return-1;
519
520  return MCF5282_UART_URB(minor);
521}
522
523
524/*
525 * Function : IntUartPollWrite
526 *
527 * Description : This writes out each character in the buffer to the
528 * appropriate internal uart channel waiting till each one is sucessfully
529 * transmitted.
530 */
531static ssize_t IntUartPollWrite(
532  int         minor,
533  const char *buf,
534  size_t      len
535)
536{
537  size_t retval = len;
538  /* loop over buffer */
539
540  while ( len-- ) {
541    /* block until we can transmit */
542    while ( (MCF5282_UART_USR(minor) & MCF5282_UART_USR_TXRDY) == 0 )
543      continue;
544    /* transmit data byte */
545    MCF5282_UART_UTB(minor) = *buf++;
546  }
547  return retval;
548}
549
550/*
551 * Function : console_initialize
552 *
553 * Description : This initialises termios, both sets of uart hardware before
554 * registering /dev/tty devices for each channel and the system /dev/console.
555 */
556rtems_device_driver console_initialize(
557  rtems_device_major_number  major,
558  rtems_device_minor_number  minor,
559  void                      *arg
560)
561{
562  rtems_status_code status;
563
564  /* Set up TERMIOS */
565  rtems_termios_initialize ();
566
567  /* set io modes for the different channels and initialize device */
568  IntUartInfo[minor].iomode = TERMIOS_IRQ_DRIVEN;
569  IntUartInitialize();
570
571  /* Register the console port */
572  status = rtems_io_register_name ("/dev/console", major, CONSOLE_PORT);
573  if ( status != RTEMS_SUCCESSFUL ) {
574    rtems_fatal_error_occurred (status);
575  }
576
577  /* Register the other port */
578  if ( CONSOLE_PORT != 0 ) {
579    status = rtems_io_register_name ("/dev/tty00", major, 0);
580    if ( status != RTEMS_SUCCESSFUL ) {
581      rtems_fatal_error_occurred (status);
582    }
583  }
584  if ( CONSOLE_PORT != 1 ) {
585    status = rtems_io_register_name ("/dev/tty01", major, 1);
586    if ( status != RTEMS_SUCCESSFUL ) {
587      rtems_fatal_error_occurred (status);
588    }
589  }
590
591  return RTEMS_SUCCESSFUL;
592}
593
594/*
595 * Function : console_open
596 *
597 * Description : This actually opens the device depending on the minor
598 * number set during initialisation. The device specific access routines are
599 * passed to termios when the devices is opened depending on whether it is
600 * polled or not.
601 */
602rtems_device_driver console_open(
603  rtems_device_major_number major,
604  rtems_device_minor_number minor,
605  void  * arg
606)
607{
608  rtems_status_code               status = RTEMS_INVALID_NUMBER;
609  rtems_libio_open_close_args_t  *args   = (rtems_libio_open_close_args_t *)arg;
610  struct IntUartInfoStruct       *info;
611
612  static const rtems_termios_callbacks IntUartPollCallbacks = {
613    NULL,                 /* firstOpen */
614    NULL,                 /* lastClose */
615    IntUartPollRead,      /* pollRead */
616    IntUartPollWrite,     /* write */
617    IntUartSetAttributes, /* setAttributes */
618    NULL,                 /* stopRemoteTx */
619    NULL,                 /* startRemoteTx */
620    TERMIOS_POLLED        /* mode */
621  };
622  static const rtems_termios_callbacks IntUartIntrCallbacks = {
623    IntUartInterruptOpen,  /* firstOpen */
624    IntUartInterruptClose, /* lastClose */
625    NULL,                  /* pollRead */
626    IntUartInterruptWrite, /* write */
627    IntUartSetAttributes,  /* setAttributes */
628    NULL,                  /* stopRemoteTx */
629    NULL,                  /* startRemoteTx */
630    TERMIOS_IRQ_DRIVEN     /* mode */
631  };
632
633  static const rtems_termios_callbacks IntUartTaskCallbacks = {
634    IntUartInterruptOpen,  /* firstOpen */
635    IntUartInterruptClose, /* lastClose */
636    IntUartTaskRead,       /* pollRead */
637    IntUartInterruptWrite, /* write */
638    IntUartSetAttributes,  /* setAttributes */
639    NULL,                  /* stopRemoteTx */
640    NULL,                  /* startRemoteTx */
641    TERMIOS_TASK_DRIVEN    /* mode */
642  };
643
644  /* open the port depending on the minor device number */
645  if ( ( minor >= 0 ) && ( minor < MAX_UART_INFO ) ) {
646    info = &IntUartInfo[minor];
647    switch ( info->iomode ) {
648      case TERMIOS_POLLED:
649        status = rtems_termios_open(major, minor, arg, &IntUartPollCallbacks);
650        break;
651      case TERMIOS_IRQ_DRIVEN:
652        status = rtems_termios_open(major, minor, arg, &IntUartIntrCallbacks);
653        info->ttyp = args->iop->data1;
654        break;
655      case TERMIOS_TASK_DRIVEN:
656        status = rtems_termios_open(major, minor, arg, &IntUartTaskCallbacks);
657        info->ttyp = args->iop->data1;
658        break;
659    }
660  }
661
662  return status;
663}
664
665/*
666 * Function : console_close
667 *
668 * Description : This closes the device via termios
669 */
670rtems_device_driver console_close(
671  rtems_device_major_number major,
672  rtems_device_minor_number minor,
673  void   * arg
674)
675{
676  return rtems_termios_close(arg);
677}
678
679/*
680 * Function : console_read
681 *
682 * Description : Read from the device via termios
683 */
684rtems_device_driver console_read(
685  rtems_device_major_number  major,
686  rtems_device_minor_number  minor,
687  void                      *arg
688)
689{
690  return rtems_termios_read(arg);
691}
692
693/*
694 * Function : console_write
695 *
696 * Description : Write to the device via termios
697 */
698rtems_device_driver console_write(
699  rtems_device_major_number  major,
700  rtems_device_minor_number  minor,
701  void                      *arg
702)
703{
704  return rtems_termios_write(arg);
705}
706
707/*
708 * Function : console_ioctl
709 *
710 * Description : Pass the IOCtl call to termios
711 */
712rtems_device_driver console_control(
713  rtems_device_major_number  major,
714  rtems_device_minor_number  minor,
715  void                      *arg
716)
717{
718  return rtems_termios_ioctl(arg);
719}
720
721int DEBUG_OUTCHAR(int c)
722{
723  if (c == '\n')
724    DEBUG_OUTCHAR('\r');
725  _BSP_null_char(c);
726  return c;
727}
728void DEBUG_OUTSTR(const char *msg)
729{
730  while (*msg)
731    DEBUG_OUTCHAR(*msg++);
732}
733void DEBUG_OUTNUM(int i)
734{
735  int n;
736  static const char map[] = "0123456789ABCDEF";
737
738  DEBUG_OUTCHAR(' ');
739  for (n = 28 ; n >= 0 ; n -= 4)
740      DEBUG_OUTCHAR(map[(i >> n) & 0xF]);
741}
Note: See TracBrowser for help on using the repository browser.