Assembly 如何更改字符串(32位汇编内核)的前景色?

Assembly 如何更改字符串(32位汇编内核)的前景色?,assembly,x86,kernel,osdev,vga,Assembly,X86,Kernel,Osdev,Vga,我目前正在为自己的操作系统编程(只是为了好玩,我16岁了),我的输出打印功能有问题。我想更改文本颜色(而不是背景色),但它不起作用 我已经编写了自己的printf函数SystemOutPrint/ln(以Java命名,因为Java是我的“主要”语言,适用于那些想要更改的人),并发现我可以通过在AH寄存器中写入来更改背景色。在此函数之前,我在内核中什么都没做,引导加载程序只将GDT、LDT和模式从16位设置为32位。因此,在mov ebx 0xb8000之前,视频内存不会被触动 相关代码: kma

我目前正在为自己的操作系统编程(只是为了好玩,我16岁了),我的输出打印功能有问题。我想更改文本颜色(而不是背景色),但它不起作用

我已经编写了自己的
printf
函数
SystemOutPrint/ln
(以Java命名,因为Java是我的“主要”语言,适用于那些想要更改的人),并发现我可以通过在AH寄存器中写入来更改背景色。在此函数之前,我在内核中什么都没做,引导加载程序只将GDT、LDT和模式从16位设置为32位。因此,在mov ebx 0xb8000之前,视频内存不会被触动

相关代码

kmain:
mov ebx, 0xb8000    ;Video Memory
mov esi, kernelVersion
mov ah, 0x0
;setting ah to 0x0 is not neccessary, because its the default but if you
;would put in A for example it would be light green, etc.
call SystemOutPrintln

SystemOutPrintln:
mov ecx, ebx

.printChar:
lodsb
test al,al
jz .newLine
or eax,0x0F00
mov word [ebx], ax
add ebx, 2
jmp .printChar

.newLine:
mov edx, ebx
sub edx, ecx
mov ecx, 0x000A0
sub ecx, edx
add ebx, ecx
ret

kernelVersion: db "Kernel Version: 0.0.1", 0
我尝试的内容:更改eax中的每个字节以查找前导颜色的属性字节。通过这样的尝试和错误,比如,我发现,改变ah对背景色有效,但al用于测试al,al用于查找字符串的结尾,eax中不是ax的部分被函数简单地忽略。我没有尝试更改其他寄存器中的某些内容,因为它们被用于其他内容,或者根本不起作用,所以这对我来说没有意义。一个网站(我没有找到链接)说属性字节的定义是这样的:BG颜色的十六进制值(比如F表示白色)*16=F0+FG颜色的十六进制值(让我们用A表示浅绿色)FA应该是什么。如果我执行“mov ah,0xFA”,我会将背景更改为白色,但前景仍然是白色(默认)


为此提供一个最小可还原的示例将不再是最小的,因为我还必须给您引导加载程序和GDT。但是如果有人能告诉我哪个字节是前景颜色属性字节,那就足够了,这样我就可以集中精力让那个字节工作,而不必在尝试和错误中重写整个视频记忆。

首先;让我们稍微优化一下代码。具体而言,对于此循环:

.printChar:
    lodsb
    or al,al
    jz .newLine
    or eax,0x0F00
    mov word [ebx], ax
    add ebx, 2
    jmp .printChar
..在第一次迭代后,
ah
中的值不会改变;这样就可以将指令从循环中提升出来以提高性能。另外,
或eax,0x0F000
与较短的
或ah,0x0F
具有相同的效果。这两个变化最终都是这样的:

    or ah,0x0F
.printChar:
    lodsb
    or al,al
    jz .newLine
    mov word [ebx], ax
    add ebx, 2
    jmp .printChar
    or ah,0x0F          ;Force the foreground colour for all characters to be white
.printChar:
    lodsb               ;al = next character
    or al,al            ;Is the next character zero?
    jz .newLine         ; yes, don't print it and move to the next line instead
    mov word [ebx], ax  ;Store next character (from string) and attribute (from outside the loop)
    add ebx, 2          ;bx = address to store next character and attribute
    jmp .printChar
现在添加一些注释,如下所示:

    or ah,0x0F
.printChar:
    lodsb
    or al,al
    jz .newLine
    mov word [ebx], ax
    add ebx, 2
    jmp .printChar
    or ah,0x0F          ;Force the foreground colour for all characters to be white
.printChar:
    lodsb               ;al = next character
    or al,al            ;Is the next character zero?
    jz .newLine         ; yes, don't print it and move to the next line instead
    mov word [ebx], ax  ;Store next character (from string) and attribute (from outside the loop)
    add ebx, 2          ;bx = address to store next character and attribute
    jmp .printChar
请注意,注释(例如“强制所有字符的前景色为白色”)非常有用,因为:

  • 它们减少了理解代码应该做什么所需的时间

  • 它们可以很容易地看到指令没有按照注释中的说明执行的bug


我无法更改FG颜色的原因是or
eax,0x0f00


f
是应该定义前景色的半字节,但是由于
,我之前放在那里的所有内容都被覆盖了。感谢Peter Cordes问我为什么“在循环内使用or
eax,0x0f00
,而不是在循环外设置一次AH。”因此,我尝试将
保留在外,并且按照预期的方式工作,只是不要再忽略我以前设置的FG了。

顺便问一下:为什么网站会把我的代码格式化搞得如此糟糕?在编辑中,它看起来像它应该看起来的样子,在这里它是一个完整的混乱。堆栈溢出使用了一种标记的变体。有关详细信息,请参阅。重要的是在VGA文本模式下存储到VGA内存的字节数。因为将AX存储到内存中,所以属性字节(该对的较高地址)来自AH。但是IDK为什么在循环内部使用低效的
或al,al
而不是
测试al,al
,或者为什么在循环外部使用
或eax,0x0f00
而不是只设置AH一次
lodsb
已将新的AL合并到现有的EAX中。您是如何尝试更改FG颜色的?这不是因为你没有展示任何你尝试过的无效的东西。(或者你在哪里存储这些字节,或者之前你对VGA模式所做的任何事情)。谢谢你的回答,当我读到Peter Cordes的评论并想“等等,我为什么把它放在这里?”,我自己也明白了。是的,我将使用更多的注释,几周后我又开始在操作系统上工作,但没有看到我将fg全部变成白色。
mov word[ebx],ax
可以写成
mov[ebx],ax
。可以通过在循环的底部使用检查NUL(0)的条件分支来优化循环。这需要在循环之前有一个无条件的分支,该循环在检查角色的循环内跳转。同样,如果OP使用EDI作为视频地址,那么
mov[ebx],ax
添加ebx,2
也可以写成
stosw
。正如peter指出的,
或al,al
最好使用
测试al,al
来完成。如果您要讨论优化这一点,您可以使它在CPU上运行得更快,而不必将al与EAX分开重命名。(Haswell和更高版本,以及所有非英特尔产品)。将
或al,al
替换为
测试al,al
,这样循环携带的dep链只是重复合并EAX的新低位字节。因此
test/jz
可以进行宏融合。有更多细节。@MichaelPetch:
stosw
不是现代CPU上的速度优化,只是代码大小。在AMD/Intel CPU上,STOS是3个UOP,而在mov上是2个融合域UOP。Haswell和更高版本的
lodsb/w
也是3个UOP,而
lodsd/q
是2个UOP。但是,是的,倾斜循环以将条件分支置于底部会有所帮助;在大多数CPU上,这会限制前端带宽。将第一次迭代的一部分剥离到循环的中间是
jmp
的替代方案。e、 g.使用
movzx eax,字节[esi]
/
或eax,0x0f00
/落入一个