我正在使用1994年的DOS进行旧版本的DOS编程,以便进行游戏编程,并且正在将RAD文件中的数据输出到OPL3 FM Synthesis芯片中。我正在用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的实现产生了我的例行程序中没有的魔术。
答案 0 :(得分:4)
C和汇编程序有一个主要区别:
我不知道您的C编译器的库,但是outp()
等效于out dx,ax
或out dx,al
。
(我想它等同于out dx,al
。)
但是,在汇编程序中,有时会将outp()
替换为out dx,ax
,有时会将out dx,al
替换为outp()
。两者之一一定是错误的。
如果真实的声卡具有8位ISA连接器,并且 out dx,al
等效于mov ax,regnum
out dx,ax
inc dx
mov al,regval
out dx,al
,则以下汇编代码:
从您的汇编程序中提取的outportb(address, regnum); outportb(address + 1, regnum >> 8); // This may cause the noise outportb(address + 1, regval);
...等于以下C程序:
out dx,ax
尝试将out dx,al
替换为out
。
(不幸的是,我不知道DOSbox是否模拟具有8位或16位连接器的声卡。)
请注意
在阅读我之前写的内容之后,您可能会尝试仅使用一条mov al,regnum
mov ah,regval
out dx,ax
指令来同时写寄存器和值:
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
很有可能与带有8位连接器的真实声卡一起使用。
但是,这可能不适用于带有16位连接器的真实声卡(也可能不适用于DOSbox之类的模拟器)。