当垂直回扫位被清除时,VGA 卡是否会读取像素缓冲区?

2024-01-12

我正在开发一款使用视频模式 13h 的 DOS 游戏。

我一直遇到屏幕撕裂的问题,但直到今天我一直忽略这个问题。我认为修复这将是一个挑战,因为它将涉及延迟像素写入一段精确的时间。但这实际上是一个非常简单的修复。

您所要做的就是等待重新设置 VGA 状态字节的垂直回扫位(位 3),该位在彩色模式下可在端口 0x3da 上使用。

所以我只需要修改这个旧程序,它将我的帧缓冲区写入从 A000:0000 开始的 VGA 像素缓冲区:

WRITE_FRAME PROC

;WRITES ALL 64,000 PIXELS (32,000 WORDS) IN THE FRAME BUFFER TO VIDEO MEMORY

    push es
    push di
    push ds
    push si
    push cx

    mov cx, frame
    mov ds, cx
    xor si, si             ;ds:si -> frame buffer (source)                  

    mov cx, vidMemSeg
    mov es, cx
    xor di, di             ;es:di -> video memory (destination)

    mov cx, (scrArea)/2    ;writing 32,000 words of pixels
    rep movsw              ;write the frame


    pop cx
    pop si
    pop ds
    pop di
    pop es
    ret

WRITE_FRAME ENDP

这是等待新设置垂直回扫位的修改程序:

WRITE_FRAME PROC

;WRITES ALL 64,000 PIXELS (32,000 WORDS) IN THE FRAME BUFFER TO VIDEO MEMORY

    push es
    push di
    push ds
    push si
    push ax
    push cx
    push dx

    mov cx, frame
    mov ds, cx
    xor si, si             ;ds:si -> frame buffer (source)                  

    mov cx, vidMemSeg
    mov es, cx
    xor di, di             ;es:di -> video memory (destination)

    mov cx, (scrArea)/2    ;writing 32,000 words of pixels

                           ;If vert. retrace bit is set, wait for it to clear
    mov dx, 3dah           ;dx <- VGA status register
VRET_SET:
    in al, dx              ;al <- status byte
    and al, 8              ;is bit 3 (vertical retrace bit) set
    jnz VRET_SET           ;If so, wait for it to clear

VRET_CLR:                  ;When it's cleared, wait for it to be set
    in al, dx
    and al, 8
    jz VRET_CLR            ;loop back till vert. retrace bit is newly set

    rep movsw              ;write the frame


    pop dx
    pop cx
    pop ax
    pop si
    pop ds
    pop di
    pop es
    ret

WRITE_FRAME ENDP 

它并不完全完美。仍然有一点抖动,尤其是当精灵后面的背景向上或向下滚动时,但不再有什么问题了。

我的问题是,为什么这有效?

我的猜测是,当设置垂直回扫位时,像素已经被读入VGA卡的内存中,并且当前正在写入已经加载的像素。然而,当垂直回扫位被清除时,它正在将像素从A000:0000加载到本地存储器中。它使用 DMA 来实现这一点,对吗?

因此,只有当 VGA 卡正在写入像素(位设置)并且不加载像素(位清除)时,写入 A000:0000 才是安全的

还是我完全错了?


VGA 卡没有读取数据的单独缓冲区。 (请记住,当 VGA 刚刚出现时,即使是 32kiB 的 DRAM 也很昂贵。而且,内存带宽很低。一些显卡曾经使用双端口内存 https://en.wikipedia.org/wiki/Video_RAM_(dual-ported_DRAM)因此来自CPU的访问不会干扰扫描输出;当 CRTC / RAMDAC 读取像素数据时,它可以在一个端口上读/写。)

期间垂直消隐间隔 https://en.wikipedia.org/wiki/Vertical_blanking_interval,显卡根本不读取或写入视频 RAM;它的存在是为了让 CRT 可以将电子束偏转板的电压改变回屏幕顶部,而无需在屏幕上画一条线。然后 VGA 硬件开始读取视频 RAM,以便再次扫描输出下一帧。

(现代硬件当然不驱动 CRT,但按“消隐间隔”顺序读取 VRAM 仍然是一件事)。


等待该位被设置然后清除有助于使您的代码可能在消隐间隔开始时开始运行,而不是在消隐间隔结束时开始运行。

如果修改视频 RAM 的代码运行得足够快,它会在硬件再次开始读取之前完成,因此不会出现撕裂现象。 (实际上,因为您正在按扫描输出顺序写入屏幕,它只需要足够快以保持领先于光栅扫描,因此屏幕输出不会通过 memcpy 并在帧稍后显示一些“旧”像素。)

在旧硬件上,rep movsw在 VBI 期间复制整个数据帧的速度不够快,尤其是在通过 ISA 总线写入内存映射 I/O 时。相反,你通常会双缓冲 https://en.wikipedia.org/wiki/Multiple_buffering#Double_buffering_in_computer_graphics通过在 VBI 期间更改 VGA 基点以指向已绘制的帧。因此,您在一个缓冲区中进行绘制,同时扫描另一个缓冲区,从而为您提供了整个帧间隔来更新它,而不仅仅是 VBI。


rep movsw runs very在实际的现代 CPU 上速度很快(例如,如果您以实模式启动现代 PC)。如果 VRAM 映射为 WC(又名 USWC:不可缓存的推测写入组合),则rep movsw一次将复制 16 或 32 个字节(快速字符串模式甚至 ERMSB(增强型 Rep Mov/Stos B)),受益于写入组合缓冲区。 (WC 内存上的常规存储类似于普通 WB(回写)内存上的 NT 存储)。英特尔勘误表(像 IvyBridge BU2 https://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/3rd-gen-core-family-mobile-specification-update.pdf)表明 WC 内存上的 REP MOVS 确实以这种方式工作:如果将页面从 WC 跨入 UC 内存,则某些到 UC 内存的存储可能会使用宽快速字符串存储而不是单独的 16 位存储进行rep movsw。这意味着 CPU 必须对 WC 内存进行宽存储。

如果源数据在 L1d 或 L2 缓存中很热,因为您刚刚写入了它,并且目标是 USWC 视频 RAM,则使用 blitting 它rep movsw在 VBI 期间应该可以轻松完成。如果它被映射为 UC(这曾经是 BIOS 选项,当时 WC 是一个相对较新的功能,至少在 Pentium III/早期 K8 主板上),那么现代的多 GHz PC 可能仍然足够快。

(BTW, repne cmpsb仍然很慢,但rep movs/stos 很快)。

顺便说一句,即使对于集成显卡,“视频 RAM”仍然只是常规 DRAM 的一部分,它也将是 UC(不可缓存)或 WC(不可缓存写组合)。当然,现在大多数 VGA 接口都是模拟的。不过,VGA 内存可能是图形硬件使用的真正帧缓冲区(如果在裸机上运行,​​而不是在 DOSBOX 或其他模拟器上运行)。

无论如何,在低分辨率的现代硬件上,您可能只检查被清除的位就可以了,因为与刷新率相比,副本运行得如此之快,以至于出现任何撕裂的机会几乎为零。或者,第一个或两个像素可能来自旧帧。


在 DOSBOX 上模拟具有真实时钟速度的真实旧 PC:

@Ped7G 说 https://stackoverflow.com/questions/50461049/do-vga-cards-read-in-the-pixel-buffer-when-the-vertical-retrace-bit-is-cleared/50461509?noredirect=1#comment87937404_50461049 rep movsw在 VBI 期间复制帧的速度不够快,除非您将 DOSBOX 设置为以 ~70MHz 或“动态/最大”速度模拟 486。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当垂直回扫位被清除时,VGA 卡是否会读取像素缓冲区? 的相关文章

  • I 类型指令的 MIPS 周期数 (addi)

    So on https en wikipedia org wiki Cycles per instruction https en wikipedia org wiki Cycles per instruction它说 对于多周期MIPS
  • 如何在汇编程序中使用 C 库?

    我想知道如何用汇编语言编写文本编辑器 但现代操作系统需要 C 库 特别是对于它们的窗口系统 我找到了这个page http pengu1n is programmer com posts 8304 html 这对我有很大帮助 但我想知道是否
  • 推送 64 位 intel osx

    我想将 64 位地址压入堆栈 如下所示 asm pushq 0x1122334455667788 但我得到编译错误 我只能按以下方式推送 asm pushq 0x11223344 有人可以帮助我理解我的错误吗 我是装配新手 所以如果我的问题
  • 汇编语言中的全局_start是什么?

    这是我的汇编级代码 section text global start start mov eax 4 mov ebx 1 mov ecx mesg mov edx size int 0x80 exit mov eax 1 int 0x80
  • 如何调试 iOS 应用程序在启动时崩溃,仅在程序集文件中设置断点

    我遇到了当前正在开发的应用程序的问题 问题是应用程序在启动时在后台运行一段时间后崩溃 并且仅在这种情况下 在应用程序被杀死时启动应用程序不会导致调试器或手机崩溃 无论是否进行调试 在后台启动应用程序大约 5 10 分钟都不会导致崩溃 在后台
  • 汇编编程语言:程序仅当输入为 ESC 时退出,并在退出前要求确认(y/n),否则循环

    我只是汇编语言编程的初学者 我们的第一个任务是让程序仅在输入为 ESC 时退出 退出之前请求确认 y n 否则循环 我知道 ESC 在 ASCII 代码中具有等效值 但我对插入位置或是否需要添加更多内容感到困惑 请帮我 这是程序 model
  • 装配中出现奇怪的字符?

    我写了以下代码 386 model small stack 100h data text db Paper 0 code start lea dx text mov ah 9h int 21h mov ah 4ch int 21h end
  • 汇编中如何计算负数

    我是汇编新手 我有一个关于如何表示负数的问题 我有三个 DWORDS 变量 比方说 result DWORD 0 i DWORD 3 j DWORD 5 我想计算这个公式 结果 i j 8 但是 当我执行 i j 时 由于符号 结果将是一个
  • 为什么这个 C++ 包装类没有被内联掉?

    EDIT 我的构建系统出了问题 我还在弄清楚到底是什么 但是gcc产生了奇怪的结果 尽管它是 cpp文件 但是一旦我使用了g 然后它按预期工作 对于我一直遇到麻烦的事情来说 这是一个非常精简的测试用例 其中使用数字包装类 我认为会内联 使我
  • 为什么我可以使用 ret 退出 main?

    我即将弄清楚程序堆栈到底是如何设置的 我了解到用以下方式调用该函数 call pointer 实际上等同于 mov register pc programcounter add register 1 where 1 is one instr
  • Clang 使用 -nostdlib 生成崩溃代码

    我正在尝试为可执行文件设置自己的运行时环境 但无法使用 clang v3 4 1ubuntu1 目标 x86 64 pc linux gnu 来生成没有段错误的可执行文件 我已将问题简化为以下内容 如果我有一个文件 crt1 c 除了满足
  • 汇编-符号标志和奇偶校验标志

    我不明白什么时候设置标志标志 什么时候设置奇偶校验 据我所知 符号标志表示运算结果的符号 0表示正数 1表示负数 那么为什么在下一个代码中 mov al 5 sub al 124 SF为零 结果是负数 关于PF 为什么a和b中设置了PF a
  • CALL指令是否总是将EIP指向的地址压入堆栈?

    x86架构中函数调用时是否存在返回地址不入栈的情况 No CALL根据定义 将在跳转到目标地址之前将返回地址压入堆栈 该返回地址是EIP or RIP sizeof call instruction 通常为 5 个字节 英特尔 64 和 I
  • 为什么 RISC-V S-B 和 U-J 指令类型以这种方式编码?

    我正在读一本书 计算机组织与设计RISC V版 我遇到了 S B 和 U J 指令类型的编码 我上面提到的那些类型有奇怪的编码立即字段 S B 类型将直接字段分为两部分 这是有道理的 因为所有指令编码都必须相似 但我无法理解为什么立即字段以
  • 从汇编程序获取命令行参数

    通读 专业汇编语言书籍 似乎它提供了用于读取命令行参数的错误代码 我纠正了一点 现在它从段错误变成了读取参数计数 然后是段错误 这是完整的代码 data output1 asciz There are d params n output2
  • 在 x86-64 CPU 上通过交叉修改代码重现意外行为

    Question 对于可能在 x86 或 x86 x64 系统上触发意外行为的交叉修改代码有哪些想法 在这些系统中 交叉修改代码中的所有操作均已正确完成 但在执行处理器之前执行序列化指令除外修改代码 如下所述 我有一个 Core 2 Duo
  • 在 x86 ASM 中测试零通常哪个更快:“TEST EAX, EAX”与“TEST AL, AL”?

    测试 AL 中的字节是否为零 非零通常哪个更快 TEST EAX EAX TEST AL AL 假设之前有一个 MOVZX EAX BYTE PTR ESP 4 指令加载了一个带有零扩展的字节参数到 EAX 的其余部分 防止了我已经知道的组
  • 寄存器寻址模式与直接寻址模式

    我在试卷中遇到过这个问题 它指出 哪种给定的寻址模式更快 为什么 寄存器寻址方式 直接寻址方式 现在根据我的说法 寄存器寻址模式应该更快 因为寄存器是计算机中最快的存储位置 这是正确答案吗 请帮忙 谢谢 两种寻址模式之间的区别是 地址的来源
  • 弹出 x86 堆栈以访问函数 arg 时出现分段错误

    我正在尝试链接 x86 程序集和 C 我的C程序 extern int plus 10 int include
  • 使用 MIPS 从 Big Endian 到 Little Endian 无需逻辑运算?

    我正在使用 MIPS QtSpim 将 32 位字从 Big Endian 转换为 Little Endian 我下面显示的内容已检查且正确 不过我想知道还有什么其他方法可以让我进行转换 我虽然只使用了旋转和移位 但如果没有逻辑运算 我就无

随机推荐

  • r中使用sqldf时连接数据库失败

    我将 csv 文件加载到我的 R 中 当我尝试使用 sqldf 选择某些列时 它总是转到 Error in local drv Failed to connect to database Error Access denied for us
  • 为什么我的 NextJS 代码运行多次,如何避免这种情况? [复制]

    这个问题在这里已经有答案了 我一直在尝试连接 Web 应用程序的前端和后端 但遇到一个问题 当用户创建帐户时 会在数据库中创建该用户的多个对象 我用了console log并发现我的 NextJS 代码是重复的 我发现这个稍微相关的帖子 组
  • 如何获取BLE广告通道索引号

    我想在读取广告数据包时获取广告频道索引号 我只能看到广告信息由标头信息 UUID 主编号 次编号和 RSSI 值组成 这些信息中是否嵌入了频道号 我能知道我是否正在读取通道 37 38 39 上的数据包吗 在使用 HCI 与蓝牙控制器通信的
  • 尝试 Keras SimpleRNN 时出现 NotImplementedError

    我正在尝试使用下面的代码在我的 Jupyter Labs Notebook 中使用 Keras SimpleRNN 实现一个非常基本的 RNN 模型 为什么我会收到错误 应该做什么 我的Python版本是3 8 11 Keras是2 4 3
  • VS2015:将“watch”:true添加到tsconfig.json导致JsErrorScriptException(0x30001)

    我创建了一个虚拟基本项目VS2015 社区更新 3 版本 14 0 25424 00 使用这个很棒的教程 aspnet core web api angularjs 2 http www talkingdotnet com cascadin
  • 用百分比预加载 - javascript/jquery

    我进行了谷歌搜索 但找不到用百分比加载的方法 有人知道我如何找到这样的例子吗 我需要在显示内容之前对网站进行 0 100 的预加载 不带栏 但我找不到任何示例 我推荐这个插件 太奇妙了 从下载http demo inwebson com d
  • TypeScript 中的构造函数,缺少什么?

    我试图弄清楚如何将 TypeScript 提供的类型安全性与 JS 中旧的普通构造函数一起使用 我有一个非常简单的示例 看起来很简单 但我错过了一些东西并且无法使用 TypeScript 进行编译 interface IMyService
  • 是否可以使用 jQuery.Support 检查 PNG 支持?

    我的问题是关于jQuery 支持 http docs jquery com Utilities jQuery support system 我想知道是否可以使用这种方法判断浏览器是否支持半透明背景PNG图像 编辑 我对特定问题的 CSS 解
  • 巨大的交易日志 - 这正常吗?

    我有一个 5GB 数据库和一个 20GB 事务日志 SQL Server 2005 不知道为什么它这么大 或者是什么让它这么大 它曾经大约是数据库大小的 1 2 DB 每月增长约 1GB 对于相对于数据库文件大小来说事务日志应该有多大 是否
  • 更新 cabal 时出错

    我无法在 Windows 7 上更新 Cabal 跑步时cabal install cabal install 我收到以下错误 Linking C Users sberg AppData Local Temp Cabal 1 18 1 2
  • 执行(点击)与订阅

    编辑 在 RxJs 6 之前 tap被称为do 更新了标题以反映点击 我想了解使用的最佳实践是什么 subscribe and do可观察量的方法 例如 如果我需要在从服务器加载初始数据后做一些工作 const init Observabl
  • 如何在 aspnet core 应用程序中使用多个目录来存放静态文件?

    默认情况下 wwwroot 目录可以存放静态文件 但是 在某些情况下 最好有两个静态文件目录 例如 让 webpack 将构建转储到一个 gitignored 目录中 并将一些图像文件 favicon 等保存在非 gitignored 目录
  • 如何禁用 UITableView 垂直向上滚动?

    我在用着EGORefreshTableHeader与 UITableView 中的静态单元格 在 nib 中定义 结合使用 我希望能够向下滚动表格视图以触发EGORefreshTableHeader从而刷新静态单元中的数据 但是 由于静态单
  • 无法使用 CONFIG_CC_STACKPROTECTOR_STRONG:编译器不支持 -fstack-protector-strong

    我在我的笔记本电脑上运行 Ubuntu 16 10 我最近从以下网站下载了 Linux 内核git 但是当我尝试从终端使用 make 命令编译它时 它正在给我 error Cannot use CONFIG CC STACKPROTECTO
  • 无法在 Chrome (mac) 中打开某些 html 文件

    有一些 HTML 文件 我没有找到模式 无法通过终端 GUI 或右键单击 通过 Sublime Text 3 在浏览器中打开 打开 打开文件的默认浏览器肯定设置为Chrome 当我说在浏览器中打开文件时 它会将我带到 Chrome 窗口 但
  • 更改 SQL Server 中的主键列

    UPDATE 以下是查询结果的约束 SELECT FROM INFORMATION SCHEMA KEY COLUMN USAGE WHERE TABLE NAME history CONSTRAINT NAME COLUMN NAME O
  • C# 检查文件是否为媒体文件

    我需要一种方法来告诉我文件是图像 音频还是视频文件 我可以用 C 做到这一点吗 这取决于您希望它有多坚固 最简单的方法是检查扩展名 如下所示 static string mediaExtensions PNG JPG JPEG BMP GI
  • 如何从sql server中给定邮政编码和给定半径(以英里为单位)附近的表中获取所有其他邮政编码或(纬度和经度)?

    我有带有纬度 经度和邮政编码的表格 我想选择靠近给定邮政编码和半径 以英里为单位 的记录 例如 用户将输入邮政编码 NW44JL 和半径 1 英里 我需要从表中获取邮政编码 NW44JL 1 英里半径内的所有记录 谁能帮我得到这个 谢谢 D
  • 如何使用 javascript 检测 kindle fire?

    我正在尝试使用 javascript 检测我的网站是否在 kindle fire 移动设备上运行 我尝试过 navigator userAgent 和 navigator appVersion 但我在 kindle 上得到了这个结果 5 0
  • 当垂直回扫位被清除时,VGA 卡是否会读取像素缓冲区?

    我正在开发一款使用视频模式 13h 的 DOS 游戏 我一直遇到屏幕撕裂的问题 但直到今天我一直忽略这个问题 我认为修复这将是一个挑战 因为它将涉及延迟像素写入一段精确的时间 但这实际上是一个非常简单的修复 您所要做的就是等待重新设置 VG