【发布时间】:2019-06-02 15:53:54
【问题描述】:
我正在使用 1994 年的旧 DOS 进行编码,以获得游戏编程的乐趣,并且正在将 RAD 文件中的数据输出到 OPL3 FM 合成芯片中。我正在使用 DOSBox。
为了在处理硬件寄存器时提高性能(特别是因为我正在通过定时中断(以保持速度)更新 FM Synth 芯片并希望尽快回到主代码中)我想重写我的寄存器在汇编中输出。
但是,虽然这可行,但它似乎会在合成器芯片的输出中引入失真,而当我使用纯 C 的 outportb 时不存在这种失真,我想我会在这里询问是否有人能告诉我发生了什么。 'out dx,al' 和 'outp(int, char)' 之间是否存在我遗漏的特殊细微差别?
OPLStatus status = NONE;
unsigned int primAd;
unsigned int secAd;
unsigned int backAd;
void OPLWriteImpl(void *argument, unsigned int registerNumber, unsigned char registerValue) {
(void)argument; /* Unused variable */
if(status == OPL3 || status == DUALOPL2) {
if(registerNumber <= REGISTER_THRESHOLD) {
outp(primAd, registerNumber);
outp(primAd+1, registerValue);
}
else {
outp(secAd, registerNumber);
outp(secAd+1, registerValue);
}
}
else {
outp(backAd, registerNumber);
outp(backAd+1, registerValue);
}
}
EXTRN status:WORD, primAd:WORD, backAd:WORD
.CODE
PUBLIC OPLWriteImpl
OPLWriteImpl PROC FAR C argument:DWORD, regnum:WORD, regval:BYTE
cmp status,0 ; if status == 0, go to OPL3
je three
cmp status,2 ; if status == DUAL, also go to OPL3
jne two ; otherwise, go to OPL2
three:
mov dx,primAd ; put OPL3 primary bank in dx
cmp regnum,100h ; check if regnum <=than threshold (256)
jle prim ; if so, go straight to primary bank
inc dx ; otherwise, writing to secondary bank (primary+2)
inc dx
prim:
mov ax,regnum ; put the register number in ax
out dx,ax ; output to register
inc dx ; go to data address
mov al,regval ; put the value in al
out dx,al ; output to data
jmp done ; finished
two:
mov dx,backAd
mov ax,regnum
out dx,ax
mov cx,6
loopOne:
in al,dx
loop loopOne
inc dx
mov al,regval
out dx,al
dec dx
mov cx,36
loopTwo:
in al,dx
loop loopTwo
done:
ret ; return to C
OPLWriteImpl ENDP
当使用我的 C 例程时,音乐可以像在 RAD Tracker 中一样正确输出。但是当我使用我的装配程序时,一些乐器似乎有一种奇怪的邋遢的声音。我真的不能责怪 Dosbox 仿真,因为就像我说的,它与纯 C 一起工作得很好。差异不是特别大,但它非常明显。我唯一的猜测是,outportb 的实现做了一些我的例程中没有的魔法。
【问题讨论】:
-
汇编代码似乎做了一些额外的事情?那些循环会减慢例行程序吗?还是您破坏了调用者的
cx寄存器?将您的代码与 C 例程的反汇编进行比较可能会有所帮助。