OS Diary Day 1 console.c

console.c


static void
lpt_putc(int c)
{
        int i;

/* 0x378~0x37A parallel printer port

0378    w    data port
0379    r/w    status port
         bit 7 = 0 busy * 0x80
         bit 6 = 0 acknowledge
         bit 5 = 1 out of paper
         bit 4 = 1 printer is selected
         bit 3 = 0 error
         bit 2 = 0 IRQ has occurred
         bit 1-0 reserved

037A    r/w    control port
         bit 7-5 reserved
         bit 4 = 1 enable IRQ
         bit 3 = 1 select printer    * 0x08
         bit 2 = 0 initialize printer *0x04
         bit 1 = 1 automatic line feed
         bit 0 = 1 strobe        * 0x01
*/

        for (i = 0; !(inb(0x378+1) & 0x80) && i < 12800; i++) // when not busy, or time out

                delay();
        outb(0x378+0, c);    // write char c

        outb(0x378+2, 0x08|0x04|0x01);    // initialize printer

        outb(0x378+2, 0x08);        // select printer

}


void cga_init(void)
{
  volatile uint16_t *cp;
  uint16_t was;
  unsigned pos;

 cp = (uint16_t*) (KERNBASE + CGA_BUF); 
/*
CGA_BUF: 0xB8000 (物理地址)
为了使地址转换后(-KERNBASE)还是0xB8000,那么就需要+KERNBASE
*/
was = *cp; //保存当前光标所在值
*cp = (uint16_t)0xA55A; // ?? *********
if (*cp != 0xA55A) {
 cp = (uint16_t*)(KERNBASE + MONO_BUF);
 addr_6845 = MONO_BASE; /* CGA_BASE: 0x3B4 */
}else {
  *cp = was; // 还原原来的值
  addr_6845 = CGA_BASE; /* CGA_BASE: 0x3D4 */
  }
  /* Extract cursor location */
  outb(addr_6845, 14); // 写控制寄存器,0x0E:获得cursor location high,其值在下一个寄存器
  pos = inb(addr_6845 + 1) << 8; 
  outb(addr_6845, 15); // 写控制寄存器,0x0F:获得cursor location low,其值在下一个寄存器
  pos |= inb(addr_6845 + 1);

  crt_buf = (uint16_t*) cp; //临时变量赋值给全局变量
  crt_pos = pos;    //保存当前光标位置
}



void
cga_putc(int c)
{
        // if no attribute given, then use black on white

        if (!(c & ~0xFF))    // c 低16位为字符值,高16位为显示属性

                c |= 0x0700;

        switch (c & 0xff) {    // 去除属性

        case '\b':        // backspace

                if (crt_pos > 0) {
                        crt_pos--;
                        crt_buf[crt_pos] = (c & ~0xff) | ' ';    // 删除处使用空格填充

                }
                break;
        case '\n':        //new line:换行, 自动添加回车

                crt_pos += CRT_COLS;
                /* fallthru */
        case '\r':        //carriage return:回车, 跳动行首

                crt_pos -= (crt_pos % CRT_COLS);
                break;
        case '\t':        //table:制表符,转换成5个空格

                cons_putc(' ');
                cons_putc(' ');
                cons_putc(' ');
                cons_putc(' ');
                cons_putc(' ');
                break;
        default:
                crt_buf[crt_pos++] = c; /* write the character */
                break;
        }

        // What is the purpose of this? 如果一屏写满,则向上滚动一行

        if (crt_pos >= CRT_SIZE) {
                int i;
         // 所有数据向前挪动一行,最上面一行数据丢失

                memmove(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
         // 清空最后一行,用空格填充

                for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++)
                        crt_buf[i] = 0x0700 | ' ';
                crt_pos -= CRT_COLS;
        }
    
    // 写光标位置

        /* move that little blinky thing */
        outb(addr_6845, 14);
        outb(addr_6845 + 1, crt_pos >> 8);
        outb(addr_6845, 15);
        outb(addr_6845 + 1, crt_pos);
}


/*早期的键盘实际上是一种5pin的键盘,称作AT键盘,是1984年IBM PC的标准键盘。在1987年IBM进行了改进,从而变成了现在的PS/2键盘。
当按下一个键或释放一个键,键盘都会发送键盘扫描码到主机。比如按下A,键盘就会发送0x1C到主机。如果持续按A,当经过一个给定时间后,就会发送0x1C到主机。当键盘被释放,键盘会发送0xF0加键码到主机,告诉主机键盘哪个键被释放。当再次按下A,键盘就会再次发送0x1C到主机。键盘的每一个键都有一个特定的键码,无论SHIFT、Num Lock、Caps Lock、Scroll Lock键是否被按下,键盘总是发送同样的键码,主机的键盘BIOS负责区分SHIFT、Num Lock、Caps Lock、Scroll Lock键的状态。键盘有101个键,而PS/2接口只有8比特。因此,并不是所有的键都只有一个字节的键码。扩展键盘中有一些键的键码是双字节的,以 E0开头,比如向左键为E04B。有些键的扫描码非常夸张,比如Pause Brk键的键码为E11D45
*/

/*
PS/2接口只有8比特,单字节最多能表示256个值,每个键被按下和被释放均需有不同的值表示,所以256个最多表示128个按键,为了区分和便于记忆,键被释放时的码值=0x80+键被按下时的码值,0和0x80没有被用作键值,最早的键盘按键较少,一个字节够用,随着扩展键的引入,单字节键值显得不够用了,就得使用多字节(2字节居多),以双字节为被按下键值=E0后面加一个字节xx,那么被释放时键值=E0后面加(0x80+xx)
比如:
a 的被按下键值为1E,那么被释放时键值为9E
HOME 的被按下键值为0E47,被释放时键值为0EC7
Pause Brk键的被按下键码为E11D45,被释放时的键码为E19DC5

*/


/***** Keyboard input code *****/

#define NO 0

#define SHIFT (1<<0)
#define CTL (1<<1)
#define ALT (1<<2)

#define CAPSLOCK (1<<3)
#define NUMLOCK (1<<4)
#define SCROLLLOCK (1<<5)

#define E0ESC (1<<6)


static uint8_t shiftcode[256] =
{
        [0x1D] CTL,        //左Ctrl,    1D

        [0x2A] SHIFT,        //左shift    2A

        [0x36] SHIFT,        //右shift    36

        [0x38] ALT,        //左alt        38

        [0x9D] CTL,        //右Ctrl    E01D

        [0xB8] ALT        //右alt        E038

};

static uint8_t togglecode[256] =
{
        [0x3A] CAPSLOCK,    // 3A

        [0x45] NUMLOCK,    // 45

        [0x46] SCROLLLOCK    // 46

};

// 按键码对应字符的asiic码(没有按下shift键),如 0x2 => '1'

static uint8_t normalmap[256] =
{
        NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00

        '7', '8', '9', '0', '-', '=', '\b', '\t',
        'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10

        'o', 'p', '[', ']', '\n', NO, 'a', 's',
        'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20

        '\'', '`', NO, '\\', 'z', 'x', 'c', 'v',
        'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30

        NO, ' ', NO, NO, NO, NO, NO, NO,
        NO, NO, NO, NO, NO, NO, NO, '7', // 0x40

        '8', '9', '-', '4', '5', '6', '+', '1',
        '2', '3', '0', '.', NO, NO, NO, NO, // 0x50

        [0xC7] KEY_HOME, [0x9C] '\n' /*KP_Enter*/,
        [0xB5] '/' /*KP_Div*/, [0xC8] KEY_UP,
        [0xC9] KEY_PGUP, [0xCB] KEY_LF,
        [0xCD] KEY_RT, [0xCF] KEY_END,
        [0xD0] KEY_DN, [0xD1] KEY_PGDN,
        [0xD2] KEY_INS, [0xD3] KEY_DEL
};

// 当按下shift键时,按键码对应字符的asiic码,如 0x2 => '!'

static uint8_t shiftmap[256] =
{
        NO, 033, '!', '@', '#', '$', '%', '^', // 0x00

        '&', '*', '(', ')', '_', '+', '\b', '\t',
        'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', // 0x10

        'O', 'P', '{', '}', '\n', NO, 'A', 'S',
        'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', // 0x20

        '"', '~', NO, '|', 'Z', 'X', 'C', 'V',
        'B', 'N', 'M', '<', '>', '?', NO, '*', // 0x30

        NO, ' ', NO, NO, NO, NO, NO, NO,
        NO, NO, NO, NO, NO, NO, NO, '7', // 0x40

        '8', '9', '-', '4', '5', '6', '+', '1',
        '2', '3', '0', '.', NO, NO, NO, NO, // 0x50

        [0xC7] KEY_HOME, [0x9C] '\n' /*KP_Enter*/,
        [0xB5] '/' /*KP_Div*/, [0xC8] KEY_UP,
        [0xC9] KEY_PGUP, [0xCB] KEY_LF,
        [0xCD] KEY_RT, [0xCF] KEY_END,
        [0xD0] KEY_DN, [0xD1] KEY_PGDN,
        [0xD2] KEY_INS, [0xD3] KEY_DEL
};

#define C(x) (x - '@')

static uint8_t ctlmap[256] =
{
        NO, NO, NO, NO, NO, NO, NO, NO,
        NO, NO, NO, NO, NO, NO, NO, NO,
        C('Q'), C('W'), C('E'), C('R'), C('T'), C('Y'), C('U'), C('I'),
        C('O'), C('P'), NO, NO, '\r', NO, C('A'), C('S'),
        C('D'), C('F'), C('G'), C('H'), C('J'), C('K'), C('L'), NO,
        NO, NO, NO, C('\\'), C('Z'), C('X'), C('C'), C('V'),
        C('B'), C('N'), C('M'), NO, NO, C('/'), NO, NO,
        [0x97] KEY_HOME,
        [0xB5] C('/'), [0xC8] KEY_UP,
        [0xC9] KEY_PGUP, [0xCB] KEY_LF,
        [0xCD] KEY_RT, [0xCF] KEY_END,
        [0xD0] KEY_DN, [0xD1] KEY_PGDN,
        [0xD2] KEY_INS, [0xD3] KEY_DEL
};

static uint8_t *charcode[4] = {
        normalmap,
        shiftmap,
        ctlmap,
        ctlmap
};


/*
 * Get data from the keyboard. If we finish a character, return it. Else 0.
 * Return -1 if no data.
 */

static int
kbd_proc_data(void)
{
        int c;
        uint8_t data;
        static uint32_t shift;    // 数据存在bss段中,这部分数据初始化为0,下次调用该函数时shift里的值还保存着

        
    // 读键盘控制状态寄存器0x64,如果第一位为1则表示有数据;KBSTATP,KBS_DIB在inc/kbdreg.h中定义

        if ((inb(KBSTATP) & KBS_DIB) == 0)
                return -1;
    
    //读键盘数据寄存器0x60

        data = inb(KBDATAP);

    /* 如果想知道键被按下和被释放时的键盘扫描码,可以加上一条输出语句输出码值,很直观!
        cprintf(" %02x ",data);
    */


        if (data == 0xE0) {
                // E0 escape character,扩展按键,扫描码为2字节;E0后面还跟有一个字节值

                shift |= E0ESC;
                return 0;
        } else if (data & 0x80) {    
                // Key released,除去E0,最高位为1(+0x80)的,应该是键释放的扫描码

         // 如果该扫描码是E0 escape的,那么保持值不变;否则清除最高位的1,其值

         // 变为建按下是的码值。这么做的目的就是为了处理左右ctrl,alt和shift键,

         // 即在建释放时清除其shiftcode值,因为它们的shiftcode值会影响其他字符

         // 的解析

                data = (shift & E0ESC ? data : data & 0x7F);
                shift &= ~(shiftcode[data] | E0ESC);    // 清除之前的shiftcode,表示

                                // 释放了ctrl,alt或者shift

                                // 如果该扫描码是E0 escape的

                                // 清除E0 escape标记

                return 0;
        } else if (shift & E0ESC) {
                 // Last character was an E0 escape; or with 0x80

         // 上一个字节值为E0,那么该字节应该是键被按下时扫描码的第二字节,

         // 让其与上0x80,用以后面处理时区分单字节扫描码

                data |= 0x80;
                shift &= ~E0ESC;    // 清除E0 escape

        }

        shift |= shiftcode[data];    // 键被按下时,置ctrl,alt和shift键被按下的状态

        shift ^= togglecode[data];    // CAPSLOCK,NUMLOCK,SCROLLLOCK这三个键没按一次,

                    // 它们的状态就改变(0,1变化),这里用异或巧妙的实现

                    // 它们的状态也会影响后面按键字符的解析


     // static uint8_t *charcode[4] = {normalmap,shiftmap,ctlmap,ctlmap};

     // CTL = 1 (01); SHIFT = 2 (10). CTL | SHIFT = 3 (11)

     // CTL和SHIFT二者组合值为0~3, 对应charcode里面的四个不同字符值map

        c = charcode[shift & (CTL | SHIFT)][data];
        if (shift & CAPSLOCK) {    // CAPSLOCK 影响字母字符解析

                if ('a' <= c && c <= 'z')    // 小写->大写

                        c += 'A' - 'a';
                else if ('A' <= c && c <= 'Z')    // 大写(shift键按下时)->小写

                        c += 'a' - 'A';
        }

        // Process special keys

        // Ctrl-Alt-Del: reboot

        if (!(~shift & (CTL | ALT)) && c == KEY_DEL) {
                cprintf("Rebooting!\n");
                outb(0x92, 0x3); // courtesy of Chris Frost

        // PS/2 POS, 0092: bit 1 = 1 indicates A20 active

        // bit 0 = 0 system reset or write

        //      1 pulse alternate reset pin (alternate CPU reset)

        }
        return c;
}


// return the next input character from the console, or 0 if none waitingint
cons_getc(void)
{
  int c;

  // poll for any pending input characters,
  // so that this function works even when interrupts are disabled
  // (e.g., when called from the kernel monitor).
  serial_intr(); // 输入又可能来自串口或者键盘, xxx_intr设置终端响应函数
  kbd_intr(); // 调用cons_intr(int (*proc)(void)),proc为各自的接口处理函数
  // 将输入保存到cons.buf内

  // grab the next character from the input buffer.
  if (cons.rpos != cons.wpos) { // buffer内有输入;使用数组作为循环buffer
  c = cons.buf[cons.rpos++];
  if (cons.rpos == CONSBUFSIZE) // 循环buffer
  cons.rpos = 0;
  return c;
  }
  return 0;

}

// output a character to the console
void
cons_putc(int c)
{
  lpt_putc(c); // 输出到parallel printer port,打印机
  cga_putc(c); // 输出到Color Graphics Adapter,显示器
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值