为什么我在 vga 图形模式下写入一个字符后读到 0H?
Why I read 0H after a character write in vga graphic mode?
在实模式(或虚拟8086)下,当我切换到vga图形模式(00DH,00EH,012H)并使用BIOS功能显示一个字符(int 00AH,int 00EH)时,什么都不能从 0xA0000 读取除了一堆零。该字符确实显示在屏幕上,但从内存中看不出来。尽管在文本模式下一切正常(1H、3H、...)。
format binary
use16
org 07C00H
_TELETYPE_GRAPHIC_40x25 = 00DH
_TELETYPE_GRAPHIC_80x25 = 00EH
_TELETYPE_GRAPHIC_80x30 = 012H
cli
mov ax, _TELETYPE_GRAPHIC_80x30
int 010H
mov ax, 00E41H
mov bx, 2H
int 010H
mov bx, 0A000H
mov es, bx
xor di, di
xor ax, ax
mov cx, 04000H
cld
rep scasb
jz _always
hlt
jmp $-1H
_always:
; ...
我尝试在 64 位 Windows 的 DOSBox 中使用 TurboDebugger 在 图形模式 13h (320*200px) 中读取视频内存 Windows 并且它按预期工作:REPE SCASB
以 DI=0143h 和
加载的值停止
MOV AL,[ES:DI-1]
是 2.
ES:0(140h 字节)的第一行像素全为 0(黑色),第二行从 ES:0140h 开始有两个黑色像素(0 字节),后面是值为 [=12 的字节=](蓝色),也就是大写字母的尖端A字形。
然而,由于调试器和 DOSBox 仿真的不完善,无法使用 Alt-F5 在 TurboDebugger 用户屏幕和 CPU window) 之间切换来检查视频内存。
对于 平面图形模式 0Dh、0Eh、12h,情况甚至更加复杂。 VGA 将所有四个内存平面(页面)映射到同一线性地址 A0000h,您必须首先通过直接输出到 CRT 端口来 select 页面(请参阅@fuz 第一条评论中的 link)。
您可能希望从 videoram 加载非零字节并使用 INT 10h
而不是 hlt
打印它,只是为了确保它在那里。
我记得大约 30 年前我是如何使用 Borland TD.EXE 和 TDREMOTE.EXE 使用通过串口连接的两台 DOS 计算机调试我的图形程序的。我能够逐步执行写入 EGA/VGA CRT 寄存器的指令并观察效果,而无需在调试器 window 和用户屏幕之间切换。
我不确定你想要实现什么,但在图形模式 13h 的非模拟 DOS 中你可能会成功。
谢谢。这是因为,我不明白平面图形的真正含义:)。
这段代码工作正常,当我 select 飞机 2:
mov ax, 00DH
int 010H
mov dx, 3CEH
mov al, 4H
out dx, al
mov dx, 3CFH
mov al, 1H
out dx, al
mov ax, 00A41H
mov bx, 2H
mov cx, 2H
int 010H
; I can read now pixel value from 00A0000H
事实上,我想实现几个 tty (tty1,...,tty8),为此我使用虚拟 8086 和分页。并且多个 tty 可以同时处于不同的模式。我最初的想法是当一个 ring=3 程序想要写入一个不是当前 tty 的 tty 时,我更改页面 table 以将视频 ram 重定向到内部 tty regen 缓冲区。例如在 bochs 中,当当前 tty 的分辨率为 12H 并且用户想要写入分辨率为 3H 的 tty2 时,它使用此映射:
0x0000000000000000-0x000000000009ffff -> 0x000000000000-0x00000009ffff
0x00000000000a0000-0x00000000000b7fff -> 0x000000010000-0x000000027fff ; trick to avoid to clear the vram when chaning mode
0x00000000000b8000-0x00000000000bbfff -> 0x00000021c000-0x00000021ffff ; regen internal buffer
0x00000000000bc000-0x00000000000bffff -> 0x00000002c000-0x00000002ffff
0x00000000000c0000-0x000000000011ffff -> 0x0000000c0000-0x00000011ffff
在实模式(或虚拟8086)下,当我切换到vga图形模式(00DH,00EH,012H)并使用BIOS功能显示一个字符(int 00AH,int 00EH)时,什么都不能从 0xA0000 读取除了一堆零。该字符确实显示在屏幕上,但从内存中看不出来。尽管在文本模式下一切正常(1H、3H、...)。
format binary
use16
org 07C00H
_TELETYPE_GRAPHIC_40x25 = 00DH
_TELETYPE_GRAPHIC_80x25 = 00EH
_TELETYPE_GRAPHIC_80x30 = 012H
cli
mov ax, _TELETYPE_GRAPHIC_80x30
int 010H
mov ax, 00E41H
mov bx, 2H
int 010H
mov bx, 0A000H
mov es, bx
xor di, di
xor ax, ax
mov cx, 04000H
cld
rep scasb
jz _always
hlt
jmp $-1H
_always:
; ...
我尝试在 64 位 Windows 的 DOSBox 中使用 TurboDebugger 在 图形模式 13h (320*200px) 中读取视频内存 Windows 并且它按预期工作:REPE SCASB
以 DI=0143h 和
加载的值停止
MOV AL,[ES:DI-1]
是 2.
ES:0(140h 字节)的第一行像素全为 0(黑色),第二行从 ES:0140h 开始有两个黑色像素(0 字节),后面是值为 [=12 的字节=](蓝色),也就是大写字母的尖端A字形。
然而,由于调试器和 DOSBox 仿真的不完善,无法使用 Alt-F5 在 TurboDebugger 用户屏幕和 CPU window) 之间切换来检查视频内存。
对于 平面图形模式 0Dh、0Eh、12h,情况甚至更加复杂。 VGA 将所有四个内存平面(页面)映射到同一线性地址 A0000h,您必须首先通过直接输出到 CRT 端口来 select 页面(请参阅@fuz 第一条评论中的 link)。
您可能希望从 videoram 加载非零字节并使用 INT 10h
而不是 hlt
打印它,只是为了确保它在那里。
我记得大约 30 年前我是如何使用 Borland TD.EXE 和 TDREMOTE.EXE 使用通过串口连接的两台 DOS 计算机调试我的图形程序的。我能够逐步执行写入 EGA/VGA CRT 寄存器的指令并观察效果,而无需在调试器 window 和用户屏幕之间切换。
我不确定你想要实现什么,但在图形模式 13h 的非模拟 DOS 中你可能会成功。
谢谢。这是因为,我不明白平面图形的真正含义:)。 这段代码工作正常,当我 select 飞机 2:
mov ax, 00DH
int 010H
mov dx, 3CEH
mov al, 4H
out dx, al
mov dx, 3CFH
mov al, 1H
out dx, al
mov ax, 00A41H
mov bx, 2H
mov cx, 2H
int 010H
; I can read now pixel value from 00A0000H
事实上,我想实现几个 tty (tty1,...,tty8),为此我使用虚拟 8086 和分页。并且多个 tty 可以同时处于不同的模式。我最初的想法是当一个 ring=3 程序想要写入一个不是当前 tty 的 tty 时,我更改页面 table 以将视频 ram 重定向到内部 tty regen 缓冲区。例如在 bochs 中,当当前 tty 的分辨率为 12H 并且用户想要写入分辨率为 3H 的 tty2 时,它使用此映射:
0x0000000000000000-0x000000000009ffff -> 0x000000000000-0x00000009ffff
0x00000000000a0000-0x00000000000b7fff -> 0x000000010000-0x000000027fff ; trick to avoid to clear the vram when chaning mode
0x00000000000b8000-0x00000000000bbfff -> 0x00000021c000-0x00000021ffff ; regen internal buffer
0x00000000000bc000-0x00000000000bffff -> 0x00000002c000-0x00000002ffff
0x00000000000c0000-0x000000000011ffff -> 0x0000000c0000-0x00000011ffff