少なくともポーリングと割り込み駆動のアプローチを紹介する上で、価値があるかもしれないサンプルをウェブ上で見つけました。setvect
割り込みメソッドには、outportb
、 、古い割り込みの保存と復元などの呼び出しが必要であることに注意してください。
次に TSR について尋ねないでください。:)
// http://ragestorm.net
// serial communications example
// interrupt driven/polled method
#include <dos.h>
#include <conio.h>
#include <stdio.h>
// serial port base addresses:
#define COM1 (0x3f8)
#define COM2 (0x2f8)
// stack size for interrupt
#define STACK_SIZE 1024
// serial ports registers:
// receive buffer
#define RBR (0x0)
// transmitter hold
#define THR (0x0)
// interrupt enable
#define IER (0x1)
// interrupt identification
#define IIR (0x2)
// fifo control
#define FCR (0x2)
// line control
#define LCR (0x3)
// modem control
#define MCR (0x4)
// line status
#define LSR (0x5)
// modem status
#define MSR (0x6)
// scratch-pad
#define SPR (0x7)
// divisor lsb byte
#define DIVLSB (0x0)
// divisor msb byte
#define DIVMSB (0x1)
// possible irqs for com ports
int com_irqs[4] = {4, 3, 4, 3};
// the com port addr being used
int used_com_addr = 0;
// the irq being used if interrupt driven
int used_com_irq = 0;
// interrupt driven or polling method?
int intr_used = 0;
// built in stack for interrupt usage
unsigned char recv_stack[STACK_SIZE];
unsigned char* next_char = recv_stack;
// old handler address
void interrupt (*old_handler)(...) = NULL;
void interrupt new_handler(...);
// get com address from bios
unsigned short get_com_addr(int com_no)
{
if ((com_no <= 0) || (com_no >= 5)) return -1;
// bios seg addr
unsigned char* biosaddr = (unsigned char *)0x400;
// set irq according to com number
used_com_irq = com_irqs[com_no - 1];
// retreive addresses bios
return *(unsigned short*)&biosaddr[(com_no - 1) << 1];
}
// detect the uart type of the used com prot addr
// this is mainly to know if fifo is available.
// 0: no uart found, 1: 8250, 2: 16450 or 8250(with spr), 3: 16550, 4: 16550A
int detect_uart_type()
{
char old_data = 0;
// check UART presentation by checking loopback mode
old_data = inportb(used_com_addr + MCR);
outportb(used_com_addr + MCR, 0x10);
if ((inportb(used_com_addr + MSR) & 0xf0)) return 0;
outportb(used_com_addr + MCR, 0x1f);
if ((inportb(used_com_addr + MSR) & 0xf0) != 0xf0) return 0;
outportb(used_com_addr + MCR, old_data);
// write values to scratch pad and readback
old_data = inportb(used_com_addr + SPR);
outportb(used_com_addr + SPR, 0x55);
if (inportb(used_com_addr + SPR) != 0x55) return 1;
outportb(used_com_addr + SPR, 0xAA);
if (inportb(used_com_addr + SPR) != 0xAA) return 1;
outportb(used_com_addr + SPR, old_data);
// enable fifo and determine version by part identification
outportb(used_com_addr + FCR, 1);
old_data = inportb(used_com_addr + FCR);
outportb(used_com_addr + FCR, 0);
if ((~old_data & 0x80)) return 2; // 16450
if ((~old_data & 0x40)) return 3; // 16550
return 4; // 16550a +
}
// inits the serial com port with a specific baud rate,
// using interrupt or polling method.
void init_com_port(int com_no, long baudrate, int intr = 0)
{
// calculate divisor relative to the baudrate
short divisor = (long)115200 / baudrate;
used_com_addr = get_com_addr(com_no);
if (used_com_addr == 0) {
printf("no valid com port!\n");
return ;
}
printf("serial com port addr 0x%x", used_com_addr);
if (intr)
printf(" [irq %d, ", used_com_irq);
else printf("[");
int uart_type = detect_uart_type();
switch(uart_type) {
//case 0: break; // port must be found already by bios.
case 1: printf("8250"); break;
case 2: printf("16450"); break;
case 3: printf("16550"); break;
case 4: printf("16550a"); break;
}
printf("] is initialized!\n");
intr_used = intr;
disable();
// turn off interrupts
outportb(used_com_addr + 1, 0);
// set dlab bit, so we can update the divisor
outportb(used_com_addr + LCR, 0x80);
// set divisor lsb
outportb(used_com_addr + DIVLSB, divisor & 0xff);
// set msb
outportb(used_com_addr + DIVMSB, (divisor >> 8) & 0xff);
// frame: 8 data bits, no parity and 1 stop bit
outportb(used_com_addr + LCR, 0x3);
// set RTS | DTR | OUT2(if intr) to inform remote system that we are ready
outportb(used_com_addr + MCR, 0x3 | ((intr == 1) << 3));
// support interrupt?
if (intr) {
// save old serial port interrupt handler address
old_handler = getvect(8 + used_com_irq);
setvect(8 + used_com_irq, new_handler);
// enable serial port irq at pic
outportb(0x21, inportb(0x21) & ~(1 << used_com_irq));
// let the interrupt be triggered upon data arrival
outportb(used_com_addr + IER, 1);
} else {
// no interrupt should be triggered
outportb(used_com_addr + IER, 0);
}
// does the uart support fifo?
if (uart_type == 4) {
// set fifo buffer of 14 bytes, clear receive and transmit fifo's
outportb(used_com_addr + FCR, 0xc7);
}
// clear delta bits
inportb(used_com_addr + LSR);
// clear incoming byte
inportb(used_com_addr + RBR);
enable();
}
// the serial port interrupt handler
// called upon received data only
// saves data in a stack
void interrupt new_handler(...)
{
unsigned char status = 0;
disable();
// read iir and msr to acknowledge the uart
inportb(used_com_addr + IIR);
inportb(used_com_addr + MSR);
// as long as data is arriving, put it in the stack
do {
// read status register
status = inportb(used_com_addr + LSR) & 0x1;
if (status & 1) {
// read data from com port to the stack
*next_char++ = inportb(used_com_addr + RBR);
// overlap offset in case the stack is full
next_char = ((next_char - recv_stack) % STACK_SIZE) + recv_stack;
}
}while (status & 1);
enable();
// let the pic know we are done
outportb(0xa0, 0x20);
outportb(0x20, 0x20);
}
// send a byte to the initialized com port
void send_byte(unsigned char ch)
{
// make sure the connection is alive
if (inportb(used_com_addr + MSR) & 0x80 == 0) return;
// make sure the transmit hold register is empty
while (inportb(used_com_addr + LSR) & 0x20 == 0) ;
// send the character
outportb(used_com_addr + THR, ch);
}
// receive a byte from the initialized com port
unsigned char recv_byte(int* is_read)
{
int i;
*is_read = 0;
if (intr_used)
if (next_char > recv_stack)
{
*is_read = 1;
return *--next_char;
} else return 0;
// is data set ready high?
for (i = 5; i > 0; i--)
if (inportb(used_com_addr + MSR) & 0x20) break;
if (!i) return -1;
// is there anything to read?
for (i = 5; i > 0; i--)
if (inportb(used_com_addr + LSR) & 0x1) break;
if (!i) return -2;
*is_read = 1;
return inportb(used_com_addr + RBR);
}
// enter loop-mode for debugging and testing.
void enter_loop_mode()
{
outportb(used_com_addr + MCR, inportb(used_com_addr + MCR) | 0x10);
}
// exit a loop mode, change to transimtter/receiver mode
void exit_loop_mode()
{
outportb(used_com_addr + MCR, inportb(used_com_addr + MCR) & ~0x10);
}
// shut down serial port connection
void shutdown_com_port()
{
disable();
if (intr_used) {
// set the old handler
setvect(8 + used_com_irq, old_handler);
// disable used serial port interrupt at pic
outportb(0x21, inportb(0x21) | (1 << used_com_irq));
}
// disable serial port interrupt
outportb(used_com_addr + IER, 0);
// disable interrupt, RTS and DTR
outportb(used_com_addr + MCR, 0);
outportb(used_com_addr + FCR, 0);
enable();
}
int main()
{
clrscr();
init_com_port(1, 9600, 1);
for(;;) {
if (kbhit()) {
int c = getch();
if (c == 27) break;
printf("%c", c);
send_byte(c);
}
int b = 0;
unsigned char c = recv_byte(&b);
if (b) printf("%c", c);
}
shutdown_com_port();
return 1;
}