【问题标题】:16 bit animation - getting started16 位动画 - 入门
【发布时间】:2017-08-08 04:37:28
【问题描述】:

花了一些时间,但终于在 16 位图形中得到了平方 1。 这里我清屏并绘制一个像素:

mov ax, 0a000h
mov es, ax      ; es - Extra Segment now points to the VGA location

mov ax, 0013h
int 10h

xor al, al
mov dx, 3c8h
out dx, al

inc dx
mov al, 63
out dx, al
out dx, al
out dx, al

mov ax, 0100h
int 21h
mov ax, 4c00h
int 21h

; draw single pixel :)

mov ah, 0ch;
mov al, 03h     ; color
mov cx, 70      ; x co-ordinate
mov dx, 70      ; y co-ordinate
; mov bh,1      ; page #
int 10h 

times 510-($-$$) db 0   ; PadZeros:
dw 0xaa55       ; MagicNumber

第 2 步:如何让它移动?

显然,这意味着在循环中交替擦除屏幕、更新和绘制像素。当然它会飞过屏幕,所以我猜你会访问内部时钟的毫秒,比较,然后在它大于某个常数时更新。

刚开始组装。我确实知道如何使用标签来制作伪函数,所以假设我可能已经在示例中继续这样做了。

我从 nasm 编译为 bin,然后在 qemu 中直接打开。注意我不使用链接器,因此不需要使用 .text 或任何其他 .bss 只是尝试使用原始二进制文件。

如果有人对低级机器代码入门教程感兴趣,我也会尝试记录我在 YouTube 上学到的所有内容: https://www.youtube.com/watch?v=XJdcoHjzvCo&list=PLJv7Sh0ZDUnpNnhNm3msK1C4K_8SzfMvO

如果其他人也在尝试编写内核、他们自己的操作系统、编译器或了解更多关于汇编中的 16 位游戏图形的并行路径,请随时加入 KAOS 项目并帮助创建 100 % 视频记录操作系统: https://github.com/musicalglass/KAOS

KAOS 没有 BS 的操作系统

【问题讨论】:

  • 如果我正确读取了该源代码,它会将颜色索引 0 设置为全白,然后等待一个键并退出(如果加载了一些 DOS,它会退出)。 ; draw single pixel后面的代码不可达? (现在我明白了,它是引导扇区,所以int 21h 是无效的,不知何故不会崩溃,只是返回,然后你通过 BIOS 绘制像素......我明白了)你为什么要通过 BIOS 绘制像素? :-o .. 只需像mov al,3 mov es:[70*320+70],al 一样将其写入内存。关于你的代码“教程”......是的,学生辅导和存档,听起来不错,但我更喜欢不那么悲惨的情景喜剧。
  • 您应该访问wiki.osdev.org/Main_Page
  • 如何让它动起来很明显:1)清晰(以后有背景的时候会变成“恢复背景”,下一个阶段是“我每帧都从头开始画整个帧” ) 旧的。 2)画新的。 ...“太宽泛”的部分是时间,在真正的硬件时代,VSYNC 同步已被大量使用,因为这是使其平滑的唯一方法(只要您的代码执行得足够快以与光束相提并论)。然后 CRT 以更高的频率显示。卖掉了,所有的 vsync 时间都关闭了,游戏运行得更快了,等等。现在 LCD 大多是 60Hz = 足以进行实验
  • 但是要对动画进行适当的计时,这实际上是一个相当复杂的话题(我的意思是远远超出简单的 SO 答案,但这也不是火箭科学)。一旦你没有固定的屏幕刷新率,你将不得不在这里和那里做一些妥协,可能没有为每个显示器提供完美的解决方案(尤其是在旧的“2D 视差”平面滚动的情况下)。如果您不关心没有视觉伪影的平滑图形,那么只需读取一些硬件时钟(只要确保您不使用机器周期之一,它通过 cpu 频率动态加速/减速)并插入位置。跨度>
  • 好的,到目前为止,我得到了通常没有帮助的答案,任何人都可以通过谷歌搜索找到。您会发现无数的论坛链接,您可以在其中获得相同的陈词滥调答案; 1:和你争论:“为什么世界上会有人想做 16 位图形?” 2:去其他地方读一本关于操作系统开发的书。 3:陈述显而易见:“动画是一系列帧,这里还有一些我刚刚从 WIKI 搜索中抓取的更多无用的东西”太棒了!非常感谢!你们都想表现得如此博学,但没有人回答一个非常简陋的问题

标签: animation assembly nasm machine-code


【解决方案1】:

这里是80x80正方形最粗的动画。

它的工作原理是:

  1. 等待 VGA 的垂直回扫开始空白周期(光束正在返回屏幕开始处)
  2. 将整个 VRAM 设置为零(“清除整个屏幕”)
  3. 在“bx”位置绘制 80x80 正方形
  4. 将 bx 调整 +-1 并保持在 0..239 范围内
  5. 无限重复

不确定您是否可以像 dosbox 中的循环计数那样影响 qemu 的速度(以及它的 VGA 仿真的准确度)。

不妨先在 DOSBOX 中尝试一下(只需将二进制文件重命名为“test.com”,下面的源代码也可以作为 COM 文件使用),以了解图形编程的陷阱。

然后在 dosbox 中,您可以使用 Ctrl+F11/F12 来减/加机器周期(PC 的速度),看看在非常慢的 PC 上使用这种粗略的算法时会发生什么。

在快速 PC 上,屏幕在光束返回第一行之前被清除,因此正方形在光束之前绘制,一切看起来都是实心的。

但是我的 dosbox 的默认设置是慢的 ~286/386 PC-like,当光束开始在监视器上绘制第一行时,它仍然会清除屏幕,所以它会绘制黑色的空线。一旦代码开始绘制正方形,它最终会赶上光束,大约 50 行左右,因此正方形的底部约 30 行可见。

如果你会玩机器速度,你会看到更多的人工制品,比如正方形完全在光束后面绘制(用户不可见),甚至闪烁(当整个绘图花费的时间超过单帧刷新时(1000/60 = 在 60Hz 显示器上为 16.6 毫秒)。

    BITS    16

    MOV     ax,13h
    INT     10h             ; 320x200 256colour VGA mode
    MOV     ax,0a000h
    MOV     es,ax           ; video RAM segment

    XOR     bx,bx           ; square position = 0
    MOV     si,1            ; direction of movement

AnimateLoop:
    CALL    waitforRetrace  ; destroys al, dx
    ; clear whole screen
    XOR     di,di
    XOR     eax,eax
    MOV     cx,320*200/4
    REP STOSD
    ; draw 80x80 pixels square with color 3
    MOV     eax,0x03030303
    MOV     di,bx
    MOV     dx,80           ; height
drawSquareLoop:
    MOV     cx,80/4
    REP STOSD               ; draw 80 pixels (single line)
    ADD     di,320-80       ; next line address
    DEC     dx
    JNZ     drawSquareLoop
    ; move it left/right
    ADD     bx,si           ; move it first
    CMP     bx,240
    JB      AnimateLoop     ; 0..239 are OK
    ; too far on either side, reverse the movement
    NEG     si
    ADD     bx,si           ; fix position to valid range
    JMP     AnimateLoop

waitforRetrace:
    MOV     dx,03dah
waitforRetraceEnd:
    IN      al,dx
    AND     al,08h
    JNZ     waitforRetraceEnd
waitforRetraceStart:
    IN      al,dx
    AND     al,08h
    JZ      waitforRetraceStart
    RET

    times 510-($-$$) db 0   ; PadZeros:
    dw 0xaa55       ; MagicNumber

现在我看到INT 8 定时器中断实际上是 BIOS 提供的,所以我可以重写这个例子来使用那个时间来向你展示差异(VSYNC 与定时器动画)......嗯......我非常不愿意,因为计时器动画很糟糕(我的意思是,即使是 VSYNC 动画也必须使用计时器来弥补跳帧,但这对于简短的例子来说太复杂了,但是基于计时器的动画在设计上本身就很糟糕)。我最多给它 10 分钟,看看我能不能让它工作......

好的,INT 08h 基于计时器的版本(如果您也容易因闪烁图像而癫痫发作,请不要观看):

    BITS    16
    MOV     ax,13h
    INT     10h
    XOR     ax,ax
    ; ds = 0 segment (dangerous, don't do this at home)
    MOV     ds,ax
    MOV     ax,0a000h
    MOV     es,ax           ; video RAM segment
AnimateLoop:
    ; clear whole screen
    XOR     di,di
    XOR     eax,eax
    MOV     cx,320*200/4
    REP STOSD
    ; draw square with color 3
    MOV     eax,0x03030303
    ; fetch position from BIOS timer-tick value
    ; (ticking every 55ms by default)
    MOVZX   di,byte [0x046C]    ; di = 0..255 from [0:046C]
    MOV     dx,80           ; height
drawSquareLoop:
    MOV     cx,80/4
    REP STOSD
    ADD     di,320-80       ; next line address
    DEC     dx
    JNZ     drawSquareLoop
    JMP     AnimateLoop

    times 510-($-$$) db 0   ; PadZeros:
    dw 0xaa55       ; MagicNumber

它有两个主要问题:

  1. int 8 计时器默认以 55 毫秒为单位计时,而大多数屏幕仍为 60 赫兹,因此需要 16.6 毫秒的计时才能在 60 赫兹上平滑,更高刷新率时甚至更少。现在方块每第 3-4 个显示帧移动 +1 个像素。

  2. 即使定时器是10ms,它仍然会疯狂闪烁,因为擦除屏幕+在新位置绘制正方形与显示光束不同步。

1.可以通过重新配置8253/8254 PIT来解决。

2. 可以通过先绘制到屏幕外缓冲区,然后将最终图像复制到真实 VRAM 来解决(理想情况下以 VSYNC-ed 方式防止“撕裂”)


这两个例子都非常粗糙,基本上只是证明“清屏+在新位置绘图”确实可以制作动画。而且即使达到基本的质量也是不够的。

为了得到任何合理的东西,你必须使用更复杂的逻辑,但这在很大程度上取决于你正在绘制和动画的内容以及如何绘制。

VGA 的一种通用方法是使用屏幕外缓冲区并在绘制完成时将其复制到 VRAM(在 64k 字节副本上浪费机器周期。今天听起来很可笑,但在 1990 年这很重要)。

或者使用 VGA 控制寄存器设置一种非官方的“x 模式”,并以支持双/三缓冲方案的方式设置 VGA 内存布局,因此您可以将新帧直接绘制到 VRAM 中,但隐藏部分,绘制完成后,切换显存的显示部分,显示新准备的内容。这有助于避免 64k 副本,但写入 VRAM 实际上很慢,因此只有在像素透支很少的情况下才值得努力。当你有很多过度绘制时,它已经太慢了(没有机会达到 60FPS),而在普通 RAM 中将其绘制到屏幕外实际上会更快,即使最终将 64k 复制到 VRAM 也是如此。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    • 2022-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多