/* * ----------------------------------------------------------------------- * * Copyright 1994-2008 H. Peter Anvin - All Rights Reserved * Copyright 2009-2014 Intel Corporation; author: H. Peter Anvin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, Inc., 53 Temple Place Ste 330, * Boston MA 02111-1307, USA; either version 2 of the License, or * (at your option) any later version; incorporated herein by reference. * * ----------------------------------------------------------------------- * * * conio.c * * Console I/O code, except: * writechr, writestr_early - module-dependent * writestr, crlf - writestr.inc * writehex* - writehex.inc */ #include #include #include #include #include #include #include #include #include "bios.h" #include "graphics.h" union screen _cursor; union screen _screensize; /* * Serial console stuff. */ __export uint16_t SerialPort = 0; /* Serial port base (or 0 for no serial port) */ __export uint8_t FlowInput = 0; /* Input bits for serial flow */ __export uint16_t BaudDivisor = 115200/9600; /* Baud rate divisor */ __export uint8_t FlowIgnore = 0; /* Ignore input unless these bits set */ __export uint16_t DisplayCon = 0x01; /* Display console enabled */ __export uint8_t FlowOutput = 0; /* Output to assert for serial flow */ __export uint8_t DisplayMask = 0x07; /* Display modes mask */ uint8_t ScrollAttribute = 0x07; /* Grey on white (normal text color) */ /* * loadkeys: Load a LILO-style keymap * * Returns 0 on success, or -1 on error. */ __export int loadkeys(const char *filename) { FILE *f; f = fopen(filename, "r"); if (!f) return -1; fread(KbdMap, 1, sizeof(KbdMap), f); fclose(f); return 0; } /* * write_serial: If serial output is enabled, write character on * serial port. */ __export void write_serial(char data) { if (!SerialPort) return; if (!(DisplayMask & 0x04)) return; while (1) { char ch; ch = inb(SerialPort + 5); /* LSR */ /* Wait for space in transmit register */ if (!(ch & 0x20)) continue; /* Wait for input flow control */ ch = inb(SerialPort + 6); ch &= FlowInput; if (ch != FlowInput) continue; break; } outb(data, SerialPort); /* Send data */ io_delay(); } void pm_write_serial(com32sys_t *regs) { write_serial(regs->eax.b[0]); } void serialcfg(uint16_t *iobase, uint16_t *divisor, uint16_t *flowctl) { uint8_t al, ah; *iobase = SerialPort; *divisor = BaudDivisor; al = FlowOutput; ah = FlowInput; al |= ah; ah = FlowIgnore; ah >>= 4; if (!DisplayCon) ah |= 0x80; *flowctl = al | (ah << 8); } void pm_serialcfg(com32sys_t *regs) { serialcfg(®s->eax.w[0], ®s->ecx.w[0], ®s->ebx.w[0]); } /* * write_serial_str: write_serial for strings */ __export void write_serial_str(char *data) { char ch; while ((ch = *data++)) write_serial(ch); } /* * pollchar: check if we have an input character pending * * Returns 1 if character pending. */ int bios_pollchar(void) { com32sys_t ireg, oreg; uint8_t data = 0; memset(&ireg, 0, sizeof(ireg)); ireg.eax.b[1] = 0x11; /* Poll keyboard */ __intcall(0x16, &ireg, &oreg); if (!(oreg.eflags.l & EFLAGS_ZF)) return 1; if (SerialPort) { cli(); /* Already-queued input? */ if (SerialTail == SerialHead) { /* LSR */ data = inb(SerialPort + 5) & 1; if (data) { /* MSR */ data = inb(SerialPort + 6); /* Required status bits */ data &= FlowIgnore; if (data == FlowIgnore) data = 1; else data = 0; } } else data = 1; sti(); } return data; } __export int pollchar(void) { return firmware->i_ops->pollchar(); } void pm_pollchar(com32sys_t *regs) { if (pollchar()) regs->eflags.l &= ~EFLAGS_ZF; else regs->eflags.l |= EFLAGS_ZF; } char bios_getchar(char *hi) { com32sys_t ireg, oreg; unsigned char data; memset(&ireg, 0, sizeof(ireg)); memset(&oreg, 0, sizeof(oreg)); while (1) { __idle(); ireg.eax.b[1] = 0x11; /* Poll keyboard */ __intcall(0x16, &ireg, &oreg); if (oreg.eflags.l & EFLAGS_ZF) { if (!SerialPort) continue; cli(); if (SerialTail != SerialHead) { /* serial queued */ sti(); /* We already know we'll consume data */ data = *SerialTail++; if (SerialTail > SerialHead + serial_buf_size) SerialTail = SerialHead; } else { /* LSR */ data = inb(SerialPort + 5) & 1; if (!data) { sti(); continue; } data = inb(SerialPort + 6); data &= FlowIgnore; if (data != FlowIgnore) { sti(); continue; } data = inb(SerialPort); sti(); break; } } else { /* Keyboard input? */ ireg.eax.b[1] = 0x10; /* Get keyboard input */ __intcall(0x16, &ireg, &oreg); data = oreg.eax.b[0]; *hi = oreg.eax.b[1]; if (data == 0xE0) data = 0; if (data) { /* Convert character sets */ data = KbdMap[data]; } } break; } reset_idle(); /* Character received */ return data; } uint8_t bios_shiftflags(void) { com32sys_t reg; uint8_t ah, al; memset(®, 0, sizeof reg); reg.eax.b[1] = 0x12; __intcall(0x16, ®, ®); ah = reg.eax.b[1]; al = reg.eax.b[0]; /* * According to the Interrupt List, "many machines" don't correctly * fold the Alt state, presumably because it might be AltGr. * Explicitly fold the Alt and Ctrl states; it fits our needs * better. */ if (ah & 0x0a) al |= 0x08; if (ah & 0x05) al |= 0x04; return al; } __export uint8_t kbd_shiftflags(void) { if (firmware->i_ops->shiftflags) return firmware->i_ops->shiftflags(); else return 0; /* Unavailable on this firmware */ } /* * getchar: Read a character from keyboard or serial port */ __export char getchar(char *hi) { return firmware->i_ops->getchar(hi); } void pm_getchar(com32sys_t *regs) { regs->eax.b[0] = getchar((char *)®s->eax.b[1]); }