source: rtems/c/src/lib/libbsp/m68k/mcf52235/console/console.c @ 9374e9b0

4.104.114.9
Last change on this file since 9374e9b0 was 9374e9b0, checked in by Chris Johns <chrisj@…>, on Jun 19, 2008 at 5:46:19 AM

2008-06-19 Matthew Riek <matthew.riek@…>

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