我已经在汇编(游戏)中编写了一个bootloader。引导加载程序使用bios WAIT函数(int 0x15,ah 0x86)来处理帧之间的延迟。我用BOCHS进行调试,一切都很精彩(时机非常完美)。我还使用isogenimage
工具制作了一个可引导的iso,并在virtualbox中测试了我的引导程序,一切都按预期工作。所以我认为延迟在虚拟环境中工作是安全的。
现在这里有一个特殊的部分:当我将引导加载程序写入USB驱动器并从中启动时,WAIT功能等待的时间比虚拟机上的时间长。我估计它的时间要长2-3倍。至少。帧之间的这种长时间延迟对于游戏来说是绝对不可接受的。
世界上有什么可能导致这种行为?我的有害生物钟可能以较慢的速度(> 18微秒)运行吗?但肯定会违反一些IBM标准?这对我来说是莫名其妙的。
顺便说一句,如果我删除WAIT中断,引导加载程序会按预期运行(对于我的目的而言太快),所以它不是我的实际代码运行缓慢。另外,我在最近的~2013笔记本电脑上运行,所以它不能因为性能低下而无法运行。
任何见解,谢谢!
编辑:我有想法在另一台物理计算机上进行测试,而且它也非常轻松!所以也许它不是一个"糟糕的bios" :)它在两台计算机上运行缓慢的几率非常低(我认为),所以这可能意味着......
CODE:
;MACRO CONSTANTS
%define p_width 10 ; player width
%define p_left_offset 20 ; space between left margin and player's left side
%define p_right_offset 290 ; space between right margin and player's right side; for linear player drawing purposes (xres - p_width - p_left_offset)
%define beak_width 5
%define xres 320 ; VGA resolution width
%define yres 200 ; VGA resolution height
%define buffer_addr 0x1000 ; start of offscrean buffer
[bits 16]
[org 0x7c00]
section .text
boot:
jmp start
times 3-($-$$) DB 0x90 ; Support 2 or 3 byte encoded JMPs before BPB.
; Dos 4.0 EBPB 1.44MB floppy
OEMname: db "mkfs.fat" ; mkfs.fat is what OEMname mkdosfs uses
bytesPerSector: dw 512
sectPerCluster: db 1
reservedSectors: dw 1
numFAT: db 2
numRootDirEntries: dw 224
numSectors: dw 2880
mediaType: db 0xf0
numFATsectors: dw 9
sectorsPerTrack: dw 18
numHeads: dw 2
numHiddenSectors: dd 0
numSectorsHuge: dd 0
driveNum: db 0
reserved: db 0
signature: db 0x29
volumeID: dd 0x2d7e5a1a
volumeLabel: db "NO NAME "
fileSysType: db "FAT12 "
start:
mov ax, 0x13 ; VGA 16bit colors 320x200 mode
int 0x10 ; call video update bios
mov ax, 0xA000 ; Video memory startaddr
mov es, ax ; Real buffer video segment into segment register (for segment:offset format)
mov ax, buffer_addr ; temp storage of virtual buffer addr
mov gs, ax ; Virtual buffer segment stored in GS
xor ax, ax
mov ds, ax ; zero offset to access vars defined in ;Data secition
gameloop:
call clear_buffer ; prepare to draw by clearing memory buffer
call draw_player ; draw player to memory buffer
call switch_buffers ; copy memory buffer to vram
add word [y_offset], 0x03 ; downward velocity, moves player around
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; HERE IS THE WAIT INTERRUPT THAT CAUSES THE PROBLEM ON REAL HARDWARE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WAIT (sleep) for a little bit
mov ah, 0x86 ; specify for int 0x15 WAIT interupt
mov cx, 0x0006 ; high word of wait time
mov dx, 0xffff ; low word of wait time
int 0x15 ; waits for cx:dx 1,000,000ths of a second
jmp gameloop
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
hlt
; Draws the player square on screen, with y offset of y_offset
draw_player:
xor si, si ; make sure si is 0; this will be pixel offset accumulatore
xor bx, bx ; this will be row counter; paintnig will stop if bl == p_width
mov ax, xres ; used to get space above player
mul word [y_offset] ; result will be in dx:ax (high, low bytes)
add si, ax ; add top offset to pixel accumulator
accum_p_offset:
inc bl ; keep track of rows drawn.
add si, p_left_offset ; now offset is at starting point to paint
mov cx, si ; cx looks into the future
add cx, p_width ; cx will be the goal pixel
draw_p_pixel: ; draws the player pixels
inc si ; go to next pixel (compensate for 0-start)
mov [gs:si], byte 0x0E ; move white pixel to virtual memory at offset ax
cmp si, cx ; check if ax caught up with cx
jne draw_p_pixel ; if not, draw next player pixel
ret
clear_buffer:
mov cx, 32000 ; number of times loop is performaned. 320 * 200 / 2 (because moving words)
xor si, si ; clear si to make sure acumulator starts at first pixel
zero_buf:
mov [gs:si], word 0x0101 ; set background to blue
add si, 2 ; increment accumulator
loop zero_buf ; jumps to zero_buf if cx is not equal to 0, then decrements cx
ret
; copies contents from memory buffer to vram
switch_buffers:
push ds ; save old data-segment (needed for getting the address of databytes later on)
mov si, gs ; use si as temp for gs (virtual memory segment)
mov ds, si ; move into datasegment the addr of virtual memory segment for movsw instruction
xor si, si ; clear accumulators for string operation
xor di, di
mov cx, 32000 ; how many words to copy 320 * 200 / 2
cld ; copy direction
rep movsw ; repeat copy from ds:si to es:di until cx is 0 (copies buffer from memory to vram)
pop ds ; restores data segment to reference databytes properly after switch_buffers
ret
;DATA
y_offset:
dw 0x16
; FILLER
times 510 - ($-$$) db 0
dw 0xaa55