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

查看:116
本文介绍了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步:如何移动它?

很明显,这意味着交替擦拭屏幕,循环更新和绘制像素.当然,它会在屏幕上飞行,因此我猜您将访问内部时钟的毫秒,进行比较,然后在大于某个常数时进行更新.

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.

刚刚开始组装.我确实知道如何使用标签来创建伪函数,因此假设我很可能在示例中可以做到这一点.

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&list= PLJv7Sh0ZDUnpNnhNm3msK1C4K_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 OS

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. 无限重复

不确定是否可以影响qemu的速度,例如dosbox中的周期数(及其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,当光束开始在监视器上绘制第一条线时,它仍将清除屏幕,因此它将绘制黑色的空线.一旦代码开始绘制正方形,它将最终赶上光束,大约在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) = 60 Hz显示器上为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计时器在55毫秒内滴答,而大多数屏幕还是60Hz,因此在60Hz上需要16.6毫秒滴答是平滑的,刷新率更高时更是如此.现在,正方形每第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.

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

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方式,以防止撕裂")

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中.隐藏部分,并在完成绘图后切换VRAM的显示部分以显示新准备的内容.这有助于避免进行64k复制,但是实际上写入VRAM的速度很慢,因此只有在像素透支很少的情况下才值得这样做.当您有很多透支时,它已经太慢了(没有60FPS的机会),并且即使将最终的64k复制到VRAM,将其在普通RAM之外的屏幕上绘制也实际上更快了.

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天全站免登陆