"Read" 汇编 13h 模式下的图形屏幕

"Read" the graphics screen in assembly 13h mode

有没有办法 "read" Assembly (emu8086) 中的 13h 模式屏幕?

我想要做的是在 13h 模式下绘制一个形状(颜色 A),然后让用户尝试描绘它(颜色 B),有效地在其他像素上重绘。之后我想做的是 "read" 屏幕检查颜色 A 中存在多少像素。假设用户进行了不错的跟踪工作,大多数 A 色像素比我原来形状的像素少,通过这个指标我会给用户打分。

有没有办法检查屏幕上是否有彩色像素,或者您是否提出了另一种实现 "comparing" 痕迹目标的方法。

提前致谢。

16b实模式下的13h VGA模式(BIOS/DOS)的显存地址为A000:0000,随意read/write即可。

此外,您还需要更复杂的评分算法,因为仅跟踪“A”的数量并不能告诉您用户将“B”溢出到其他区域的程度(即用户只需用 B 填满整个屏幕即可获胜) , 因为 A=0).

13h VGA 模式下的视频 ram 操作示例(TASM 语法):

;filename: so_13h.asm
.model small
.code
start:
    mov  ax,13h     ; ah = 0 set mode, al = 13h 320x200 256col mode
    int  10h        ; set gfx mode
    push 0A000h
    pop  es         ; es = A000 (video ram segment)
    ; fill video ram with some pattern
    xor  di,di
    mov  bp,200
lines_loop:
    mov  dx,64
    mov  ax,1010h
shade_loop:
    stosw           ; write 5 pixels
    stosw
    stosb
    ; modify colours to create sort of "dither" pattern
    xchg al,ah
    inc  ah
    dec  dx
    jnz  shade_loop ; write 320 pixels with different shades
    ; write 200 lines
    dec  bp
    jnz  lines_loop
    ; read pixel back example
    mov  di,(13*320 + 56) ; read pixel from [x, y] = [56, 13]
    mov  al,es:[di]
    ; here AL = 16h (colour of pixel at [56, 13] position)
    ; wait for any key
    xor  ax,ax
    int  16h
    ; restore text mode
    mov  ax,3       ; ah = 0, al = 3 (text mode 80x25)
    int  10h
    ; terminate code
    mov  ah,4Ch
    int  21h
end start

在dosbox下构建+运行我使用了:

tasm so_13h.asm
tlink /x so_13h.obj
so_13h.exe

编辑:还有 int 10h "Read Graphics Pixel at Coordinates" 的 BIOS 服务:

input:

AH = 0D
BH = page number, see VIDEO PAGES
CX = column number (zero based)
DX = row number (zero based)

on return:

AL = color of pixel read

但由于 int 调用本身的性能下降,BIOS 服务通常非常慢,而且 BIOS 必须从头开始重新计算每个像素的偏移量,而例如您的“评估跟踪”可以使用一些“预期屏幕”缓冲区来比较连续重复使用前一个像素的偏移量以到达下一个像素,避免过多的计算。

所以您应该尝试生成类似于我在示例中填充屏幕的方式,“批量”处理像素的方式。

即使阅读优化为字(两个像素)与字节(一个像素)在 386 时代确实对真正的 HW 有所帮助,但这可能使您的任务过于复杂,仅供参考,即使如此细微的细节在当时也确实有所不同。


edit2:关于评分算法:

取决于你想要得分的准确程度,但你可以这样做:

  • 内存中有原始的“A”缓冲区
  • 计算屏幕上“A”的数量 = toPaint
  • 让用户用“B”绘图
  • 计算屏幕上“A”的数量 = notPaint
  • 从缓冲区中“0”上方的屏幕计算“B”= overPaintL1
  • re-运行 inside internal buffer [0,0]..[maxX-2,maxY-2] 如果右侧有“A”,则将每个像素更改为“A”或当前像素的底部 = 这将使“A”形更厚(可能 运行 两倍三次覆盖 +-2 或 +-3 像素)
  • 在缓冲区 = overPaintL2 中计算“B”超过 0
  • 再把“A”加粗一次
  • 在缓冲区 = overPaintL3 中计算“B”超过 0

现在最终得分可能是这样的:

score = w0 * (toPaint - notPaint) + w1 * overPaintL1 + w2 * overPaintL2 + w3 * overPaintL3

其中 w0..w3 是 bonus/malus 的“权重”,w0 应该是最强的,因为这是用户绘制的完美像素数量(比如 50 ......每个错过的 A 像素是-50 那么),w1 应该是非常小的 malus,例如 -1(仅偏离 1 个像素),w2 可能类似于 -5(偏离 2 个像素),w3 可能类似于 -10(完全偏离形状的像素) .

因此,如果用户的形状约为 200 像素(50x50 像素的正方形),并且他透支了 184 像素:notPaint = 16,并且基本上几乎所有地方都命中 +-1 像素偏移(创建 2px 宽的正方形):overPaintL1 = 200,有时他会稍微偏离一点:overPaintL2 = 15, overPaintL3 = 35

那么得分=50*(200-16)+-1*200+-5*15+-10*35=8575 (这个例子中的满分是50*200 = 10000)

也许你需要重新调整权重和加厚很多,但我认为这种方法最终可以奏效。


编辑:关于增厚的更多注意事项...您必须在所有方向上均等地增厚,以使“B”在每个方向上的透支成本均等,因此我原来的 right/bottom 检查无效,但正在做单缓冲区中的 4 方向检查也是不可能的。所以要么你需要引入第二个内部缓冲区,从一个到另一个增厚,或者做两次增厚,首先向前并向右+底部延伸,然后向后向后并向左+顶部延伸,或者你可以用“绘制形状” A" 到 [-N..+N,-N..+N] 位置的内部缓冲区,以覆盖原始设计周围的 +-N 像素。

检测当前目标位置的源缓冲区中 3x3 网格内的任何“A”的双缓冲区可能最容易编写代码。

我原来的描述确实只在左+上方向加厚了A形,所以在bottom/right上的透支比在left/top上的透支更受惩罚。

嗯...最终证明它比直觉预期的更棘手,请确保按小部分编写代码,并分别正确地调试每个部分,还包括极端情况和极端输入。