16 位动画 - 入门 [英] 16 bit animation - getting started

查看:26
本文介绍了16 位动画 - 入门的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

花了一段时间,但终于在 16 位图形中获得了平方 1.在这里,我清除屏幕并绘制一个像素:

Took a while but finally got to square 1 in 16 bit graphics. Here I clear the screen and draw a single pixel:

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 步:如何让它移动?

Step 2: How do I make it move?

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

Clearly that means alternately wiping the screen, updating and drawing the pixel in a loop. Of course it would fly across the screen, so I would guess you would access the internal clock's millis, compare, then update when it is greater than some constant.

Assembly 刚刚开始.我确实知道如何使用标签来创建伪函数,所以假设我可能已经在示例中完成了该操作.

Just getting started in Assembly. I do know how to use a label to make a pseudo function so suppose I probabaly could have gone ahead and done that in the example.

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

I am compiling from nasm as bin then open direct in qemu. Note I don't use a linker and therefore don't need to use .text or any of that other .bss Just trying to work from raw binaries.

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

I am also trying to document everything I learn on YouTube if anybody is interested in some Getting Started in Lower Level Machine Code tutorials: https://www.youtube.com/watch?v=XJdcoHjzvCo&list=PLJv7Sh0ZDUnpNnhNm3msK1C4K_8SzfMvO

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

If anybody else is also on a parallel path of trying to write a kernel, their own Operating System, Compiler, or learn more about 16 bit game graphics in assembly, feel free to join in the KAOS Project and help create a 100% video documented OS: https://github.com/musicalglass/KAOS

KAOS 无 BS 操作系统

KAOS The no BS OS

推荐答案

这里是80x80平方最粗的动画.

Here is the most crude animation of 80x80 square.

它的工作原理是:

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

不确定您是否可以像 dosbox 中的周期数一样影响 qemu 的速度(以及它的 VGA 仿真有多准确).

Not sure if you can affect speed of qemu like the cycles count in dosbox (and how accurate its VGA emulation is).

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

Maybe try this in DOSBOX first (just rename the binary to "test.com", the source below will work as COM file too), to understand the pitfalls of graphics programming.

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

Then in dosbox you can use Ctrl+F11/F12 to subtract/add machine cycles (speed of PC) to see what happens when this crude algorithm is used on very slow PCs.

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

On fast PC the screen is cleared before the beam returns to first line, so the square is drawn ahead of beam, and everything looks solid.

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

But my default setting of dosbox is slow ~286/386 PC-like, which will be still clearing the screen while the beam starts drawing first line on monitor, so it will draw the black empty lines. Once the code will start to draw the square, it will eventually catch up to the beam, somewhere around line ~50, so bottom ~30 lines of the square are visible.

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

If you will play with machine speed, you can see more artefacts, like the square is completely drawn behind beam (not visible to user), or even blinking (when the whole drawing takes longer than single frame refresh (1000/60 = 16.6ms on 60Hz monitor).

    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 分钟,看看我能不能让它工作......


Now I see then INT 8 timer interrupt is actually BIOS provided, so I can rewrite this example to use that timing to show you difference (VSYNC vs timer animation) ... hmm... I'm extremely reluctant to, because timer animations sucks (I mean, even VSYNC animations have to work with timer to make up for skipped frames, but that's too complex for short example, but timer-based animations sucks inherently by design). I will give it ~10min at most and see if I can make it work...

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

Ok, the INT 08h timer based version (don't watch if you are also prone to epileptic seizures from blinking images):

    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 计时器默认在 55ms 内滴答,而大多数屏幕都是/are_again 60Hz,因此需要 16.6ms 滴答才能在 60Hz 上平滑,更高刷新率时更短率.现在方块每 3-4 显示帧移动 +1 像素.

  1. int 8 timer by default is ticking in 55ms, while most of the screens were/are_again 60Hz, so 16.6ms tick is needed to be smooth on 60Hz, even less with higher refresh rates. Now the square moves +1 pixel every 3rd-4th display frame.

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

even if the timer would be 10ms, it would still blink like crazy, because the erasing of screen + drawing square in new position is not in sync with the display beam.

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

The 1. can be resolved by reconfiguring the 8253/8254 PIT.

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

The 2. can be resolved by drawing into offscreen buffer first, and then copying the final image to real VRAM (ideally in VSYNC-ed way to prevent "tearing")

这两个例子都非常粗糙,基本上只是证明清除屏幕+在新位置绘图"确实可以制作动画.并且这还不足以达到最基本的质量.

Both example are very crude, basically just demonstrating that "clearing screen + drawing in new position" really does animate stuff. And that it is not sufficient enough to achieve even basic quality.

为了使任何事情变得合理,您必须使用更复杂的逻辑,但这在很大程度上取决于您绘制和动画的内容以及方式.

To get anything reasonable you have use more sophisticated logic, but that strongly depends on what you are drawing and animating and how.

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

A general purpose approach with VGA is to either use offscreen buffer and copy it to VRAM when drawing is finished (wasting machine cycles on 64k bytes copy.. may sounds laughable today, but it was big deal in 1990).

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

Or use the VGA control registers to set up one of the unofficial "x modes" and set up the VGA memory layout in a way to support double/triple buffering scheme, so you draw new frame directly into VRAM, but into the hidden part, and when the drawing is finished, you switched the displayed part of VRAM to show the newly prepared content. This helped to avoid the 64k copy, but writing to VRAM was actually quite slow, so it was worth the effort only in situations when you had little overdrawing of pixels. When you had lot of overdraw, it was already too slow (no chance for 60FPS), and drawing it offscreen in ordinary RAM made it actually faster, even with the final 64k copy to VRAM.

这篇关于16 位动画 - 入门的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆