《操作系统》的实验代码。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

466 lines
13 KiB

12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
  1. #include <defs.h>
  2. #include <x86.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <kbdreg.h>
  6. #include <picirq.h>
  7. #include <trap.h>
  8. /* stupid I/O delay routine necessitated by historical PC design flaws */
  9. static void
  10. delay(void) {
  11. inb(0x84);
  12. inb(0x84);
  13. inb(0x84);
  14. inb(0x84);
  15. }
  16. /***** Serial I/O code *****/
  17. #define COM1 0x3F8
  18. #define COM_RX 0 // In: Receive buffer (DLAB=0)
  19. #define COM_TX 0 // Out: Transmit buffer (DLAB=0)
  20. #define COM_DLL 0 // Out: Divisor Latch Low (DLAB=1)
  21. #define COM_DLM 1 // Out: Divisor Latch High (DLAB=1)
  22. #define COM_IER 1 // Out: Interrupt Enable Register
  23. #define COM_IER_RDI 0x01 // Enable receiver data interrupt
  24. #define COM_IIR 2 // In: Interrupt ID Register
  25. #define COM_FCR 2 // Out: FIFO Control Register
  26. #define COM_LCR 3 // Out: Line Control Register
  27. #define COM_LCR_DLAB 0x80 // Divisor latch access bit
  28. #define COM_LCR_WLEN8 0x03 // Wordlength: 8 bits
  29. #define COM_MCR 4 // Out: Modem Control Register
  30. #define COM_MCR_RTS 0x02 // RTS complement
  31. #define COM_MCR_DTR 0x01 // DTR complement
  32. #define COM_MCR_OUT2 0x08 // Out2 complement
  33. #define COM_LSR 5 // In: Line Status Register
  34. #define COM_LSR_DATA 0x01 // Data available
  35. #define COM_LSR_TXRDY 0x20 // Transmit buffer avail
  36. #define COM_LSR_TSRE 0x40 // Transmitter off
  37. #define MONO_BASE 0x3B4
  38. #define MONO_BUF 0xB0000
  39. #define CGA_BASE 0x3D4
  40. #define CGA_BUF 0xB8000
  41. #define CRT_ROWS 25
  42. #define CRT_COLS 80
  43. #define CRT_SIZE (CRT_ROWS * CRT_COLS)
  44. #define LPTPORT 0x378
  45. static uint16_t *crt_buf;
  46. static uint16_t crt_pos;
  47. static uint16_t addr_6845;
  48. // 显示器初始化,CGA 是 Color Graphics Adapter 的缩写
  49. // CGA显存按照下面的方式映射:
  50. // -- 0xB0000 - 0xB7777 单色字符模式
  51. // -- 0xB8000 - 0xBFFFF 彩色字符模式及 CGA 兼容图形模式
  52. // 6845芯片是IBM PC中的视频控制器
  53. // CPU通过IO地址0x3B4-0x3B5来驱动6845控制单色显示,通过IO地址0x3D4-0x3D5来控制彩色显示。
  54. // -- 数据寄存器 映射 到 端口 0x3D5或0x3B5
  55. // -- 索引寄存器 0x3D4或0x3B4,决定在数据寄存器中的数据表示什么。
  56. /* TEXT-mode CGA/VGA display output */
  57. static void
  58. cga_init(void) {
  59. volatile uint16_t *cp = (uint16_t *)CGA_BUF; //CGA_BUF: 0xB8000 (彩色显示的显存物理基址)
  60. uint16_t was = *cp; //保存当前显存0xB8000处的值
  61. *cp = (uint16_t) 0xA55A; // 给这个地址随便写个值,看看能否再读出同样的值
  62. if (*cp != 0xA55A) { // 如果读不出来,说明没有这块显存,即是单显配置
  63. cp = (uint16_t*)MONO_BUF; //设置为单显的显存基址 MONO_BUF: 0xB0000
  64. addr_6845 = MONO_BASE; //设置为单显控制的IO地址,MONO_BASE: 0x3B4
  65. } else { // 如果读出来了,有这块显存,即是彩显配置
  66. *cp = was; //还原原来显存位置的值
  67. addr_6845 = CGA_BASE; // 设置为彩显控制的IO地址,CGA_BASE: 0x3D4
  68. }
  69. // Extract cursor location
  70. // 6845索引寄存器的index 0x0E(及十进制的14)== 光标位置(高位)
  71. // 6845索引寄存器的index 0x0F(及十进制的15)== 光标位置(低位)
  72. // 6845 reg 15 : Cursor Address (Low Byte)
  73. uint32_t pos;
  74. outb(addr_6845, 14);
  75. pos = inb(addr_6845 + 1) << 8; //读出了光标位置(高位)
  76. outb(addr_6845, 15);
  77. pos |= inb(addr_6845 + 1); //读出了光标位置(低位)
  78. crt_buf = (uint16_t*) cp; //crt_buf是CGA显存起始地址
  79. crt_pos = pos; //crt_pos是CGA当前光标位置
  80. }
  81. static bool serial_exists = 0;
  82. static void
  83. serial_init(void) {
  84. // Turn off the FIFO
  85. outb(COM1 + COM_FCR, 0);
  86. // Set speed; requires DLAB latch
  87. outb(COM1 + COM_LCR, COM_LCR_DLAB);
  88. outb(COM1 + COM_DLL, (uint8_t) (115200 / 9600));
  89. outb(COM1 + COM_DLM, 0);
  90. // 8 data bits, 1 stop bit, parity off; turn off DLAB latch
  91. outb(COM1 + COM_LCR, COM_LCR_WLEN8 & ~COM_LCR_DLAB);
  92. // No modem controls
  93. outb(COM1 + COM_MCR, 0);
  94. // Enable rcv interrupts
  95. outb(COM1 + COM_IER, COM_IER_RDI);
  96. // Clear any preexisting overrun indications and interrupts
  97. // Serial port doesn't exist if COM_LSR returns 0xFF
  98. serial_exists = (inb(COM1 + COM_LSR) != 0xFF);
  99. (void) inb(COM1+COM_IIR);
  100. (void) inb(COM1+COM_RX);
  101. if (serial_exists) {
  102. pic_enable(IRQ_COM1);
  103. }
  104. }
  105. static void
  106. lpt_putc_sub(int c) {
  107. int i;
  108. for (i = 0; !(inb(LPTPORT + 1) & 0x80) && i < 12800; i ++) {
  109. delay();
  110. }
  111. outb(LPTPORT + 0, c);
  112. outb(LPTPORT + 2, 0x08 | 0x04 | 0x01);
  113. outb(LPTPORT + 2, 0x08);
  114. }
  115. /* lpt_putc - copy console output to parallel port */
  116. static void
  117. lpt_putc(int c) {
  118. if (c != '\b') {
  119. lpt_putc_sub(c);
  120. }
  121. else {
  122. lpt_putc_sub('\b');
  123. lpt_putc_sub(' ');
  124. lpt_putc_sub('\b');
  125. }
  126. }
  127. /* cga_putc - print character to console */
  128. static void
  129. cga_putc(int c) {
  130. // set black on white
  131. if (!(c & ~0xFF)) {
  132. c |= 0x0700;
  133. }
  134. switch (c & 0xff) {
  135. case '\b':
  136. if (crt_pos > 0) {
  137. crt_pos --;
  138. crt_buf[crt_pos] = (c & ~0xff) | ' ';
  139. }
  140. break;
  141. case '\n':
  142. crt_pos += CRT_COLS;
  143. case '\r':
  144. crt_pos -= (crt_pos % CRT_COLS);
  145. break;
  146. default:
  147. crt_buf[crt_pos ++] = c; // write the character
  148. break;
  149. }
  150. // What is the purpose of this?
  151. if (crt_pos >= CRT_SIZE) {
  152. int i;
  153. memmove(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
  154. for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i ++) {
  155. crt_buf[i] = 0x0700 | ' ';
  156. }
  157. crt_pos -= CRT_COLS;
  158. }
  159. // move that little blinky thing
  160. outb(addr_6845, 14);
  161. outb(addr_6845 + 1, crt_pos >> 8);
  162. outb(addr_6845, 15);
  163. outb(addr_6845 + 1, crt_pos);
  164. }
  165. static void
  166. serial_putc_sub(int c) {
  167. int i;
  168. for (i = 0; !(inb(COM1 + COM_LSR) & COM_LSR_TXRDY) && i < 12800; i ++) {
  169. delay();
  170. }
  171. outb(COM1 + COM_TX, c);
  172. }
  173. /* serial_putc - print character to serial port */
  174. static void
  175. serial_putc(int c) {
  176. if (c != '\b') {
  177. serial_putc_sub(c);
  178. }
  179. else {
  180. serial_putc_sub('\b');
  181. serial_putc_sub(' ');
  182. serial_putc_sub('\b');
  183. }
  184. }
  185. /* *
  186. * Here we manage the console input buffer, where we stash characters
  187. * received from the keyboard or serial port whenever the corresponding
  188. * interrupt occurs.
  189. * */
  190. #define CONSBUFSIZE 512
  191. static struct {
  192. uint8_t buf[CONSBUFSIZE];
  193. uint32_t rpos;
  194. uint32_t wpos;
  195. } cons;
  196. /* *
  197. * cons_intr - called by device interrupt routines to feed input
  198. * characters into the circular console input buffer.
  199. * */
  200. static void
  201. cons_intr(int (*proc)(void)) {
  202. int c;
  203. while ((c = (*proc)()) != -1) {
  204. if (c != 0) {
  205. cons.buf[cons.wpos ++] = c;
  206. if (cons.wpos == CONSBUFSIZE) {
  207. cons.wpos = 0;
  208. }
  209. }
  210. }
  211. }
  212. /* serial_proc_data - get data from serial port */
  213. static int
  214. serial_proc_data(void) {
  215. if (!(inb(COM1 + COM_LSR) & COM_LSR_DATA)) {
  216. return -1;
  217. }
  218. int c = inb(COM1 + COM_RX);
  219. if (c == 127) {
  220. c = '\b';
  221. }
  222. return c;
  223. }
  224. /* serial_intr - try to feed input characters from serial port */
  225. void
  226. serial_intr(void) {
  227. if (serial_exists) {
  228. cons_intr(serial_proc_data);
  229. }
  230. }
  231. /***** Keyboard input code *****/
  232. #define NO 0
  233. #define SHIFT (1<<0)
  234. #define CTL (1<<1)
  235. #define ALT (1<<2)
  236. #define CAPSLOCK (1<<3)
  237. #define NUMLOCK (1<<4)
  238. #define SCROLLLOCK (1<<5)
  239. #define E0ESC (1<<6)
  240. static uint8_t shiftcode[256] = {
  241. [0x1D] CTL,
  242. [0x2A] SHIFT,
  243. [0x36] SHIFT,
  244. [0x38] ALT,
  245. [0x9D] CTL,
  246. [0xB8] ALT
  247. };
  248. static uint8_t togglecode[256] = {
  249. [0x3A] CAPSLOCK,
  250. [0x45] NUMLOCK,
  251. [0x46] SCROLLLOCK
  252. };
  253. static uint8_t normalmap[256] = {
  254. NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00
  255. '7', '8', '9', '0', '-', '=', '\b', '\t',
  256. 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10
  257. 'o', 'p', '[', ']', '\n', NO, 'a', 's',
  258. 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20
  259. '\'', '`', NO, '\\', 'z', 'x', 'c', 'v',
  260. 'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30
  261. NO, ' ', NO, NO, NO, NO, NO, NO,
  262. NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
  263. '8', '9', '-', '4', '5', '6', '+', '1',
  264. '2', '3', '0', '.', NO, NO, NO, NO, // 0x50
  265. [0xC7] KEY_HOME, [0x9C] '\n' /*KP_Enter*/,
  266. [0xB5] '/' /*KP_Div*/, [0xC8] KEY_UP,
  267. [0xC9] KEY_PGUP, [0xCB] KEY_LF,
  268. [0xCD] KEY_RT, [0xCF] KEY_END,
  269. [0xD0] KEY_DN, [0xD1] KEY_PGDN,
  270. [0xD2] KEY_INS, [0xD3] KEY_DEL
  271. };
  272. static uint8_t shiftmap[256] = {
  273. NO, 033, '!', '@', '#', '$', '%', '^', // 0x00
  274. '&', '*', '(', ')', '_', '+', '\b', '\t',
  275. 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10
  276. 'O', 'P', '{', '}', '\n', NO, 'A', 'S',
  277. 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20
  278. '"', '~', NO, '|', 'Z', 'X', 'C', 'V',
  279. 'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30
  280. NO, ' ', NO, NO, NO, NO, NO, NO,
  281. NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
  282. '8', '9', '-', '4', '5', '6', '+', '1',
  283. '2', '3', '0', '.', NO, NO, NO, NO, // 0x50
  284. [0xC7] KEY_HOME, [0x9C] '\n' /*KP_Enter*/,
  285. [0xB5] '/' /*KP_Div*/, [0xC8] KEY_UP,
  286. [0xC9] KEY_PGUP, [0xCB] KEY_LF,
  287. [0xCD] KEY_RT, [0xCF] KEY_END,
  288. [0xD0] KEY_DN, [0xD1] KEY_PGDN,
  289. [0xD2] KEY_INS, [0xD3] KEY_DEL
  290. };
  291. #define C(x) (x - '@')
  292. static uint8_t ctlmap[256] = {
  293. NO, NO, NO, NO, NO, NO, NO, NO,
  294. NO, NO, NO, NO, NO, NO, NO, NO,
  295. C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'),
  296. C('O'), C('P'), NO, NO, '\r', NO, C('A'), C('S'),
  297. C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), NO,
  298. NO, NO, NO, C('\\'), C('Z'), C('X'), C('C'), C('V'),
  299. C('B'), C('N'), C('M'), NO, NO, C('/'), NO, NO,
  300. [0x97] KEY_HOME,
  301. [0xB5] C('/'), [0xC8] KEY_UP,
  302. [0xC9] KEY_PGUP, [0xCB] KEY_LF,
  303. [0xCD] KEY_RT, [0xCF] KEY_END,
  304. [0xD0] KEY_DN, [0xD1] KEY_PGDN,
  305. [0xD2] KEY_INS, [0xD3] KEY_DEL
  306. };
  307. static uint8_t *charcode[4] = {
  308. normalmap,
  309. shiftmap,
  310. ctlmap,
  311. ctlmap
  312. };
  313. /* *
  314. * kbd_proc_data - get data from keyboard
  315. *
  316. * The kbd_proc_data() function gets data from the keyboard.
  317. * If we finish a character, return it, else 0. And return -1 if no data.
  318. * */
  319. static int
  320. kbd_proc_data(void) {
  321. int c;
  322. uint8_t data;
  323. static uint32_t shift;
  324. if ((inb(KBSTATP) & KBS_DIB) == 0) {
  325. return -1;
  326. }
  327. data = inb(KBDATAP);
  328. if (data == 0xE0) {
  329. // E0 escape character
  330. shift |= E0ESC;
  331. return 0;
  332. } else if (data & 0x80) {
  333. // Key released
  334. data = (shift & E0ESC ? data : data & 0x7F);
  335. shift &= ~(shiftcode[data] | E0ESC);
  336. return 0;
  337. } else if (shift & E0ESC) {
  338. // Last character was an E0 escape; or with 0x80
  339. data |= 0x80;
  340. shift &= ~E0ESC;
  341. }
  342. shift |= shiftcode[data];
  343. shift ^= togglecode[data];
  344. c = charcode[shift & (CTL | SHIFT)][data];
  345. if (shift & CAPSLOCK) {
  346. if ('a' <= c && c <= 'z')
  347. c += 'A' - 'a';
  348. else if ('A' <= c && c <= 'Z')
  349. c += 'a' - 'A';
  350. }
  351. // Process special keys
  352. // Ctrl-Alt-Del: reboot
  353. if (!(~shift & (CTL | ALT)) && c == KEY_DEL) {
  354. cprintf("Rebooting!\n");
  355. outb(0x92, 0x3); // courtesy of Chris Frost
  356. }
  357. return c;
  358. }
  359. /* kbd_intr - try to feed input characters from keyboard */
  360. static void
  361. kbd_intr(void) {
  362. cons_intr(kbd_proc_data);
  363. }
  364. static void
  365. kbd_init(void) {
  366. // drain the kbd buffer
  367. kbd_intr();
  368. pic_enable(IRQ_KBD);
  369. }
  370. /* cons_init - initializes the console devices */
  371. void
  372. cons_init(void) {
  373. cga_init();
  374. serial_init();
  375. kbd_init();
  376. if (!serial_exists) {
  377. cprintf("serial port does not exist!!\n");
  378. }
  379. }
  380. /* cons_putc - print a single character @c to console devices */
  381. void
  382. cons_putc(int c) {
  383. lpt_putc(c);
  384. cga_putc(c);
  385. serial_putc(c);
  386. }
  387. /* *
  388. * cons_getc - return the next input character from console,
  389. * or 0 if none waiting.
  390. * */
  391. int
  392. cons_getc(void) {
  393. int c;
  394. // poll for any pending input characters,
  395. // so that this function works even when interrupts are disabled
  396. // (e.g., when called from the kernel monitor).
  397. serial_intr();
  398. kbd_intr();
  399. // grab the next character from the input buffer.
  400. if (cons.rpos != cons.wpos) {
  401. c = cons.buf[cons.rpos ++];
  402. if (cons.rpos == CONSBUFSIZE) {
  403. cons.rpos = 0;
  404. }
  405. return c;
  406. }
  407. return 0;
  408. }