程序集 8086 中的图形模式

Graphics mode in assembly 8086

我有一个名为 average 的变量,在我的 DATASEG 中,它每次都会更改,因为用户每次都输入不同的输入。我想做的是进入图形模式 (VGA),然后在那里打印 你的平均值是:然后是平均值 我知道如何像这样更改为图形模式:

mov ax, 13h  
int 10h

如果平均值高于 75 打印平均值后,我想在下方打印 你是个好学生,继续努力,否则。别担心,你会好起来的! 提前致谢。

我假设:PC VGA x86 MS DOS 平台

真实的或模拟的并不重要,除非你想要低级别的 IO 访问,这可能无法在模拟上正常工作,如 DOSBOX ...

  1. Video/Text 模式

    所以要在视频和文本模式之间切换,您需要使用 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 你 print/peek 像素通过访问段 A000h 的内存来计算,偏移量是这样计算的:

    offset = 320*y + x
    

    320x200 模式完全适合 64 KByte 段,因此您无需切换页面。这使得它非常适合简单的 asm gfx 程序....

    模式3是文本模式,其中每个字符都有2 BYTEs一个是颜色,另一个是扩展ASCII代码。同样 print/peek 是通过访问段 B800h 处的 WORD 完成的,其中偏移量为:

    offset = (80*y + x) * 2
    

    不确定很久以前这两个字节的顺序是什么,但您可以轻松测试在 0B800:0000 处写入 A 是否会在左上角呈现 A0B800: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
    
  2. 打印字符串

    对于初学者,您可以结合文本和视频模式......就像我在这里做的那样:

    这是一款简单的游戏,菜单为文本模式(打印很容易,只需将字符串复制到 VRAM 即可),精灵图形游戏已开启 320x200x256c视频模式。

    当你想在 gfx 模式下打印时,你首先需要在内存中有一些 字体。如果您查看 EGA/VGA BIOS 文档,您可以获得位于 EGA/VGA ROM 中的字体并直接使用它。我还创建了这个图像(IIRC 使用 Trident 9000 256/512KB VGA 字体),我将其用作 OpenGL 和其他东西(访问 VGA BIOS 的等宽字体) 不可能或不需要)...

    这里 你可以把它移植到 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 看看这个:

    • Reading/Writing the VGA palette colors
  3. 打印数字

    打印非负整数数值是将数字除以基数 (10) 并将 remainder + '0' 以相反的顺序打印为字符 ...

    hex 中,它更容易,因为每个数字对应于半字节 <0-15> 因此对于 16 位,您可以通过 xlat [=199= 将最高 4 位转换为 char ] 或者通过添加 '0''A' 取决于值是否低于 10 ...所以没有除法只是位 shift/mask ...打印字符并将值左移4 位来处理下一个数字 ...

    顺便说一句,在 gfx 模式下,通常更好和用户友好,而不是将值打印为数字渲染进度条,而不是像东西一样,这要容易得多......折叠成单循环渲染 H 或 V 线...喜欢 REP STOSB :) ...

  4. VESA

    对于 320x200x8bpp 以上的 Super VGA 视频模式,我们需要添加数据的页面交叉,因为 VRAM 不再适合 64KByte 段。我强烈建议为此使用 VESA (VBE) api。我们的想法是将 A000:0000 段映射到我们可以随时更改的 VRAM 的特定段。有关如何查看这些内容的更多信息:

    • gfx rendering in C/C++ (See VESA.h)
    • VESA+PCX in TASM

    如果您的 gfx 卡没有 VESA/VBE 对于大多数卡,可以使用 UniVBE 5.3 或 MS-DOS 下更新的实用程序添加它。