在程序集13h模式下“读取”图形屏幕

时间:2018-05-22 02:44:06

标签: assembly emu8086

有没有办法在程序集(emu8086)中“读取”13h模式屏幕?

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

有没有办法检查屏幕上是否有彩色像素,或者您是否建议另一种方法来实现“比较”痕迹的目标。

提前致谢。

1 个答案:

答案 0 :(得分:1)

16b实模式(BIOS / DOS)中的13h VGA模式具有驻留在地址A000:0000的视频ram,可随意读/写。

此外,您需要更复杂的评分算法,因为只需跟踪" 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服务:

  

输入:

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

返回时:

AL = color of pixel read

但一般来说,由于int呼叫本身的性能损失,BIOS服务非常慢,而且BIOS必须从头再次计算每个像素的偏移量,而例如你的"评估跟踪&#34 ;可以使用一些预期的屏幕"缓冲区用于比较连续重复使用前一个偏移量的像素到达下一个像素,从而避免过多的计算。

因此,您应该尝试制作类似于我在示例中填充屏幕的方式,处理"批处理"中的像素。

即使是优化为单词(两个像素)与字节(一个像素)的读取确实对386时代的真实硬件有所帮助,但这可能使您的任务过于复杂,JFYI甚至这些微妙的细节确实带来了差异然后

edit2:关于评分算法:

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

  • 有原创" A"内存中的缓冲区
  • 计算" A"在屏幕上= toPaint
  • 让用户使用" B"
  • 进行绘制
  • 计算" A"在屏幕上= notPaint
  • 计算" B"从屏幕上" 0"在buffer = overPaintL1
  • 在内部缓冲区[0,0] .. [maxX-2,,maxY-2]内重新运行并将每个像素更改为" A"如果有" A"在当前像素的右侧或底侧=这将使" A"形状较厚(可能运行两次 - 三次以覆盖+ -2或+ -3像素关闭)
  • 计算" B"缓冲区中的0 = overPaintL2
  • 制作" A"更厚一次
  • 计算" B"缓冲区中的0 = overPaintL3

现在最终得分可能是:

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

其中w0..w3是"权重"奖励/ malus,w0应该是最强的,因为用户确实绘制了像素完美的数量(比如50 ...也错过A像素也是-50然后),w1应该是非常小的malus,像-1(只有1个像素关闭),w2可能是-5(2像素关闭),w3可能像-10(像素完全脱离形状)。

因此,如果用户具有~200像素的形状(50x50像素的正方形),并且他透支了184个像素:notPaint = 16,并且基本上几乎到处都是+ -1 pix关闭(创建2px宽方形):overPaintL1 = 200 ,有时他会多走一点:overPaintL2 = 15,overPaintL3 = 35

然后得分= 50 *(200-16)+ -1 * 200 + -5 * 15 + -10 * 35 = 8575 (在这样的例子中,满分为50 * 200 = 10000)。

也许你需要重新调整重量和加厚,但我认为这种方法最终可以起作用。

编辑:关于加厚的另外一个注意事项...你必须在所有方向上均匀地加厚,以使" B"在每个方向上均匀地过度绘制,因此我原来的右/底检查无效,但在单个缓冲区中进行4方向检查也是不可能的。所以要么你需要引入第二个内部缓冲区,从一个到另一个加厚,或者先进行两遍加厚,然后向右延伸,向后延伸,向左延伸+向上延伸,或者你可以用&绘制形状#34; A"进入[-N .. + N,-N .. + N]位置的内部缓冲区,覆盖原始设计周围的+ -N像素。

双缓冲器检测任何" A"在源缓冲区中用于当前目标位置的3x3网格内部可能是最容易编码的。

我的原始描述确实只在左侧+顶部方向加厚了A形状,因此底部/右侧的透支比左侧/顶部的透支更加不利。

嗯...最后它变得更加棘手然后直观地预期,确保你用小部件编码,并分别和正确地调试每个部分,还包括角落情况和极端输入。