【问题标题】:"Read" the graphics screen in assembly 13h mode在汇编 13h 模式下“读取”图形屏幕
【发布时间】:2018-10-31 17:04:11
【问题描述】:

有没有办法“读取”Assembly (emu8086) 中的 13h 模式屏幕?

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

有没有一种方法可以检查屏幕上的彩色像素,或者您是否提出了另一种方法来实现“比较”轨迹的目标。

提前致谢。

【问题讨论】:

  • 你通过写内存来画画。你不能从同一个地址读回来吗?

标签: assembly emu8086


【解决方案1】:

16b 实模式 (BIOS/DOS) 中的 13h VGA 模式的视频内存位于地址 A000:0000,您可以随意读/写。

您还需要更复杂的评分算法,因为仅跟踪“A”的数量不会告诉您,用户将“B”洒在其他区域多少(即用户只用 B 填满整个屏幕将获胜,因为A=0)。

13h VGA 模式下的视频内存操作示例(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 必须从头开始重新计算每个像素的偏移量,而例如您的“评估跟踪”可以使用一些“预期屏幕” " 缓冲区连续比较像素,重复使用前一个的偏移量到达下一个,避免过度计算。

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

在 386 时代,即使是优化为字(两个像素)与字节(一个像素)的阅读也确实有助于真正的硬件,但这可能会使您的任务过于复杂,JFYI,即使如此细微的细节在当时也确实有所作为。


edit2:关于评分算法:

取决于你想要的分数,但你可以这样做:

  • 内存中有原始的“A”缓冲区
  • 计算屏幕上“A”的数量 = toPaint
  • 让用户使用“B”进行绘制
  • 计算屏幕上“A”的数量 = notPaint
  • 从屏幕在缓冲区中的“0”上方计算“B”= overPaintL1
  • 在内部缓冲区 [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 是奖励/恶意的“权重”,w0 应该是最强的,因为这是用户绘制的完美像素数量(比如 50 ......每个错过的 A 像素也是 -50) , w1 应该是非常小的malus,例如-1(仅偏离1 个像素),w2 可能类似于-5(偏离2 个像素),w3 可能类似于-10(完全偏离形状的像素)。

因此,如果用户的形状约为 200 像素(50x50 像素的正方形),并且他过度绘制了 184 像素:notPaint = 16,并且基本上命中几乎所有地方 +-1 pix off(创建 2px 宽的正方形):overPaintL1 = 200 ,有时他会走得更远:overPaintL2 = 15,overPaintL3 = 35

那么得分 = 50 * (200-16) + -1 * 200 + -5 * 15 + -10 * 35 = 8575 (此类示例中的满分是 50*200 = 10000)。

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


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

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

我最初的描述确实只在左+上方向加厚了 A 形状,所以在底部/右侧的过度绘制会比在左侧/顶部的过度绘制更受惩罚。

嗯...最终结果证明它比直觉预期的要复杂得多,请确保您按小部分编写代码,并分别正确地调试每个部分,包括极端情况和极端输入。

【讨论】:

  • 非常感谢。
  • @ArmandoHerrera 我添加了一些关于得分和你的任务的想法,也许在你自己尝试之前不要阅读它们,所以我不会从你那里窃取任务的主要乐趣(计算出一些算法)。 :)
  • 再次感谢,真的
猜你喜欢
  • 2010-12-31
  • 2012-06-07
  • 2013-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-23
相关资源
最近更新 更多