视频/文本模式
所以要在视频和文本模式之间切换,您需要使用 VGA BIOS:
mov ax,mode ; here select which mode you want
int 16 ; this calls EGA/VGA/VESA BIOS
video modes 很多,这里有两个非常重要的:
mode | type | segment | resolution | align
----------------------------------------------------
03 | text | B800h | 80x25 chars | 2 Byte
19 | video | A000h | 320x200x256 colors | 1 Byte
在视频模式 19 中,您可以通过访问段 A000h 的内存来打印/查看像素,其中偏移量的计算方式如下:
offset = 320*y + x
320x200 模式完全适合64 KByte 段,因此您无需切换页面。这使它成为简单 asm gfx 程序的理想选择 ....
模式3是文本模式,其中每个字符都有2 BYTEs一个是颜色,另一个是扩展ASCII代码。再次打印/查看是通过在段 B800h 访问 WORD 来完成的,其中偏移量是:
offset = (80*y + x) * 2
不知道这两个字节在很久以前的顺序是什么,但您可以轻松测试在0B800:0000 处写入A 是否会在左上角呈现A 或0B800:0001。文本模式中的 IIRC 颜色只是调色板中的前 16 种颜色,颜色字节编码墨水纸的亮度和闪光。此文本模式也是您的 MS-DOS shell 工作的默认模式,因此您应该在程序退出之前将其设置回来。
所以你的程序应该是这样的:
start:
mov ax,19 ; set video mode
int 16
mainloop:
; here your stuff
exit:
mov ax,3
int 16
ret
打印字符串
对于初学者,您可以结合文本和视频模式......就像我在这里做的那样:
这是一个简单的游戏,其中菜单处于文本模式(打印很容易,只需将字符串复制到 VRAM 中),而 sprite 图形游戏处于 320x200x256c 视频模式。
当您想在 gfx 模式下打印时,您首先需要在内存中有一些 字体。如果您查看 EGA/VGA BIOS 文档,您可以获得位于 EGA/VGA ROM 中的字体并直接使用它。我还创建了这个图像(IIRC 使用 Trident 9000 256/512KB VGA 字体),我将它用作 OpenGL 和其他东西的等宽字体(访问 VGA BIOS 是不可能或不想要)...
这里GLSL example of using it for printing你可以把它移植到CPU/VGA/asm,但是在CPU上打印更简单,不需要像 GLSL 片段这样可怕的东西。
所以您只需要从 ASCII 代码计算图像位置并将其像素复制到 VRAM 中。在 asm 中粗略的位图并不容易,更容易的是直接以二进制形式(作为db 的集合),因此您可以编写一些简单的 C++ (或其他)脚本来加载图像并将其转换为 asm 源...
这是我多年前在 NASM 中编写的一些古老的 320x200x256 colors 打印库(直接使用 EGA/VGA 字体):
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;GFX mode 13h print librrary ver:1.0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;txti init font adress
;char cx=color,al=ASCII,scr:di<=al ;cl=ch => no background
;print scr:di <= ds:si ,cx=color cl=ch => no background
;printl scr:di text after call ,cx=color ...
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
txti: pusha ;init font adress
push es
mov ax,1130h ; VGA BIOS - font info
mov bh,3 ; font 8 x 8 pixels
int 10h ; ES:BP returns font address
mov [cs:fonts],es ;get font adr
mov [cs:fonto],bp
pop es
popa
ret
fonts dw 0 ; font address for printing ...
fonto dw 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
char: pusha ;cx=color,al=ASCII,scr:di<=al ;cl=ch => no background
push ds
push es
push word 0A000h
pop es
sub ah,ah
shl ax,3
mov ds,[cs:fonts]
mov si,[cs:fonto]
add si,ax
mov dh,8
.char0: mov dl,8
lodsb
mov ah,al
.char1: mov al,cl
rcl ah,1
jc .char2
mov al,ch
.char2: cmp cl,ch
jz .char3
mov [es:di],al
.char3: inc di
dec dl
jnz .char1
add di,320-8
dec dh
jnz .char0
pop es
pop ds
popa
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
print: pusha ;scr:di <= ds:si ,cx=color cl=ch => no background
.l0: lodsb
or al,al
jz .esc
call char
add di,8
jmp short .l0
.esc: popa
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
printl: mov [cs:.dat],si ;scr:di text after call ,cx=color ...
pop si
push ax
push di
push ds
push cs
pop ds
.l0: lodsb
or al,al
jz .esc
call char
add di,8
jmp short .l0
.esc: pop ds
pop di
pop ax
push si
add di,9*320
mov si,[cs:.dat]
ret
.dat: dw 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; end. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
所以要使用它,程序应该是这样的:
start:
call txti ; just once at program startup
mov di,50+320*10
mov cx,127
call printl
db 'SPEKTRA software & hardware',0
mov di,50+320*30
mov cx,127
call printl
db 'print test',0
print 通过设置 ds,si 使用,因此它指向您的以空字符结尾的字符串。正如您所看到的,printl 不需要它,因为它使用直接位于 printl 调用之后的字符串,并且程序在它之后继续......这样你不需要指针设置指令或任何额外的标签......颜色是在cl,ch 中,一个是墨水,另一个是纸。如果cl==ch 则不会仅渲染墨水像素,如果您在文本后面有图像或 gfx 背景,则不会渲染任何纸张......颜色的值可能不可见我从我的一个游戏中获取了颜色,它设置了它自己的调色板,所以如果什么都看不到尝试设置不同的cl,ch,比如mov cx,0305h看看这个: