首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >OSDev:为什么我的VGA终端滚动不起作用?

OSDev:为什么我的VGA终端滚动不起作用?
EN

Stack Overflow用户
提问于 2018-11-03 04:10:35
回答 1查看 1.1K关注 0票数 4

我正在开发一个操作系统作为个人爱好,这样我就可以了解软件工程和计算机体系结构。

我试图让VGA终端“滚动”时,文本到达底部,或VGA_HEIGHT。我正在使用来自OSDev wiki的代码与我自己的代码混合。

我的目标是复制每一行,然后把它写到它上面的线上。

下面是我使用的代码:

代码语言:javascript
复制
void terminal_putentryat(unsigned char c, uint8_t color, size_t x, size_t y) {
    const size_t index = y * VGA_WIDTH + x;
    terminal_buffer[index] = vga_entry(c, color);
}

void terminal_putchar(char c) {
    unsigned char uc = c;

    switch(c) {
      case NEWLINE:
        terminal_row++;
        terminal_column = 0;
        terminal_putentryat(' ', terminal_color, terminal_column, terminal_row);
        update_cursor(terminal_column + 1, terminal_row);
        break;

      case '\t':
        /* TODO: Implement tab */
        terminal_column += 4;
        break;

      default:
        terminal_putentryat(uc, terminal_color, terminal_column, terminal_row);
        update_cursor(terminal_column + 1, terminal_row);
        if (++terminal_column == VGA_WIDTH) {
          terminal_column = 0;
          if (++terminal_row == VGA_HEIGHT)
            terminal_row = 0;
        }
    }
    if(terminal_row >= VGA_HEIGHT) {
      terminal_print_error();
      terminal_buffer[(15 * VGA_WIDTH) + 15] = terminal_buffer[(0 * VGA_WIDTH) + 4];
      size_t i, j;
      for(i = 0; i < VGA_WIDTH-1; i++) {
        for(j = VGA_HEIGHT-2; j > 0; j--)
          terminal_buffer[(j * VGA_WIDTH) + i] = terminal_buffer[((j+1) * VGA_WIDTH) + i];
      }
    }
}

但这种功能只起了部分作用。SPecifically,本节:

代码语言:javascript
复制
if(terminal_row >= VGA_HEIGHT) {
      terminal_print_error();
      terminal_buffer[(15 * VGA_WIDTH) + 15] = terminal_buffer[(0 * VGA_WIDTH) + 4];
      size_t i, j;
      for(i = 0; i < VGA_WIDTH-1; i++) {
        for(j = VGA_HEIGHT-2; j > 0; j--)
          terminal_buffer[(j * VGA_WIDTH) + i] = terminal_buffer[((j+1) * VGA_WIDTH) + i];
      }
    }

它只复制了部分数据。例如,当我用'printf()‘写入终端时,如果字符串比正在滚动的数据长,它将不会滚动。

代码语言:javascript
复制
/*
 * This is the screen driver. It contains functions which print
 * characters and colors to the screen
 * using the VGA controller.
 */

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

#include <kernel/tty.h>

#include "vga.h"

#define REG_SCREEN_CTRL 0x3D4
#define REG_SCREEN_DATA 0x3D5

#define NEWLINE 0x0A
#define TAB 0x09

static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 25;
static uint16_t *const VGA_MEMORY = (uint16_t *)0xC03FF000;

static size_t terminal_row;
static size_t terminal_column;
static uint8_t terminal_color;
static uint16_t *terminal_buffer;

int get_offset(int col, int row) {
  return 2 * (row * VGA_WIDTH + col);
}

int get_offset_row(int offset) {
  return offset / (2 * VGA_WIDTH);
}

int get_offset_col(int offset) {
  return (offset - (get_offset_row(offset) * 2 * VGA_WIDTH)) / 2;
}

static void scroll() {
  if(terminal_row >= VGA_HEIGHT) {

  }
}
void terminal_print_error(void) {
  if(terminal_row >= VGA_HEIGHT) {
    terminal_row = 0;
    /* print white/red E to bottom right corner of screen */
    terminal_putentryat('E', vga_entry_color(VGA_COLOR_RED, VGA_COLOR_WHITE),
                        VGA_WIDTH - 1, VGA_HEIGHT - 1);
  }
}

void terminal_initialize(void) {
    terminal_row = 0;
    terminal_column = 0;
    terminal_color = vga_entry_color(VGA_COLOR_BLACK, VGA_COLOR_CYAN);
    terminal_buffer = VGA_MEMORY;
    for (size_t y = 0; y < VGA_HEIGHT; y++) {
        for (size_t x = 0; x < VGA_WIDTH; x++) {
            const size_t index = y * VGA_WIDTH + x;
            terminal_buffer[index] = vga_entry(' ', terminal_color);
        }
    }
}

void terminal_setcolor(uint8_t color) {
    terminal_color = color;
}

void terminal_putentryat(unsigned char c, uint8_t color, size_t x, size_t y) {
    const size_t index = y * VGA_WIDTH + x;
    terminal_buffer[index] = vga_entry(c, color);
}

void terminal_putchar(char c) {
    unsigned char uc = c;

    switch(c) {
      case NEWLINE:
        terminal_row++;
        terminal_column = 0;
        terminal_putentryat(' ', terminal_color, terminal_column, terminal_row);
        update_cursor(terminal_column + 1, terminal_row);
        break;

      case '\t':
        /* TODO: Implement tab */
        terminal_column += 4;
        break;

      default:
        terminal_putentryat(uc, terminal_color, terminal_column, terminal_row);
        update_cursor(terminal_column + 1, terminal_row);
        if (++terminal_column == VGA_WIDTH) {
          terminal_column = 0;
          if (++terminal_row == VGA_HEIGHT)
            terminal_row = 0;
        }
    }
    if(terminal_row >= VGA_HEIGHT) {
      terminal_print_error();
      terminal_buffer[(15 * VGA_WIDTH) + 15] = terminal_buffer[(0 * VGA_WIDTH) + 4];
      size_t i, j;
      for(i = 0; i < VGA_WIDTH-1; i++) {
        for(j = VGA_HEIGHT-2; j > 0; j--)
          terminal_buffer[(j * VGA_WIDTH) + i] = terminal_buffer[((j+1) * VGA_WIDTH) + i];
      }
    }
}

void terminal_write(const char *data, size_t size) {
    for (size_t i = 0; i < size; i++)
        terminal_putchar(data[i]);
}

void terminal_writestring(const char *data) {
    terminal_write(data, strlen(data));
}

/* inb */
unsigned char port_byte_in(unsigned short port) {
  unsigned char result;
  asm ("in %%dx, %%al" : "=a" (result) : "d" (port));
  return result;
}

void port_byte_out(unsigned short port, unsigned char data) {
  asm ("out %%al, %%dx" : : "a" (data), "d" (port));
}

int get_cursor_offset() {
  /* Use the VGA ports to get the current cursor position
   * 1. Ask for high byte of the cursor offset (data 14)
   * 2. Ask for low byte (data 15)
   */
  port_byte_out(REG_SCREEN_CTRL, 14);
  int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */
  port_byte_out(REG_SCREEN_CTRL, 15);
  offset += port_byte_in(REG_SCREEN_DATA);
  return offset * 2; /* Position * size of character cell */
}

void set_cursor_offset(int offset) {
  /* Similar to get_cursor_offset, but instead of reading we write data */
  offset /= 2;
  port_byte_out(REG_SCREEN_CTRL, 14);
  port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset >> 8));
  port_byte_out(REG_SCREEN_CTRL, 15);
  port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset & 0xff));
}

void update_cursor(int x, int y) {
  uint16_t pos = y * VGA_WIDTH + x;

  port_byte_out(REG_SCREEN_CTRL, 15);
  port_byte_out(REG_SCREEN_DATA, (uint8_t)(pos & 0xFF));
  port_byte_out(REG_SCREEN_CTRL, 14);
  port_byte_out(REG_SCREEN_DATA, (uint8_t)(pos >> 8) & 0xFF);
}

/*
void enable_cursor(uint8_t cursor_start, uint8_t cursor_end) {
  outb(0x3D4, 0x0A);
  outb(0x3D, (inb(0x3D5) & 0xC0) | cursor_start);

  outb(0x3D4, 0x0B);
  outb(0x3D5, (inb(0x3D5) & 0xE0) | cursor_end);
}
*/
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-11-03 04:58:54

我相信循环滚动的工作原理基本上是正确的,不过,在导致您所遇到的一些bug之前,很多代码都是正确的。

开关语句中的默认情况是,当光标宽度超过屏幕的右侧时,将增加光标行。如果行增量超过屏幕的底部边缘,则if (++terminal_row == VGA_HEIGHT)将光标行重置为0。这将防止滚动代码运行。您应该删除if (++terminal_row == VGA_HEIGHT) terminal_row = 0;,并将其替换为terminal_row++;,因为开关后面的逻辑处理终端行变量。

我建议将修改terminal_rowterminal_column的逻辑与验证、重置和滚动这些变量的逻辑分开。例如,如果将'\t‘字符放置在一行的最后3个字符中,则会将字符溢出到下一行,而不会将terminal_rowterminal_column vars更新到它们应该位于的位置。

  • 您的换行符总是在行的开头留下一个空白字符,因为您在将光标修改为换行符之后执行terminal_putentryat操作,而不是在之前。实际上,您不应该对任何换行符执行terminal_putentryat操作,因为显然没有任何字符改变,只有光标位置。
  • 您可能希望修改\t的处理,以调用terminal_write(' ');,而不是直接修改列变量。这简化了实际更新终端的逻辑。上文第二段详细介绍了此更改解决的一些问题。
  • update_cursor()只应该被调用一次,在terminal_putchar()的末尾,因为您放置的每个字符都应该更新游标。如果您希望terminal_putchar()处理0宽度的字符,这可能会改变,但这在我看来是违反直觉的,因为这个函数是专门为处理显示的字符而设计的。
  • 将终端缓冲区向上修改为滚动的循环从不清除底部的字符行。
  • 函数底部处理terminal_row >= VGA_HEIGHT的逻辑从不将terminal_row重置为有效值。它确实调用了terminal_print_error(),但是当您希望将行保持在底部时,此函数会将您的行重置为0。 terminal_putchar(char ){无符号字符uc = c;//句柄字符输出和终端_行/列修改开关( c) {大小写纽线: terminal_row++;terminal_column = 0;中断;大小写‘t’:terminal_write(‘');中断;默认值: terminal_putentryat(uc、terminal_color、terminal_column、terminal_row);terminal_column++;} //句柄在terminal_column上验证terminal_row之前,因为terminal_column中的逻辑可以更新terminal_row if(terminal_column >= VGA_WIDTH) { terminal_column = 0;} //句柄验证terminal_row,并在必要时向上滚动屏幕。如果( terminal_row >= VGA_HEIGHT) { //您不应该需要terminal_print_error(),因为您正在处理terminal_row >= VGA_HEIGHT // terminal_print_error();//这一行做什么?显示将第16行的第16字符设置为与第1行的第5字符相同的值。// terminal_buffer(15 * VGA_WIDTH) + 15 = terminal_buffer(0 * VGA_WIDTH) + 4;size_t i,j;for(i = 0;i< VGA_WIDTH-1;i++) { for(j =VGA_i++2;j> 0;j-){ terminal_buffer(j * VGA_WIDTH) +i= terminal_buffer((j+1) * VGA_WIDTH) + i;} //还清除(i= 0;i< VGA_WIDTH-1;i++) { terminal_putentryat(‘',terminal_color,i,VGA_WIDTH 1)的底部行;} terminal_row =VGA_ the 1;} update_cursor(terminal_column,terminal_row)};
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53128322

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档