我在旧的,1994年时代的DOS编码游戏编程乐趣,并输出数据从一个RAD文件到OPL3 FM合成芯片。我快用完药箱了。
为了提高处理硬件寄存器时的性能(特别是因为我正在通过定时中断更新FM Synth芯片(以保持速度),并希望尽快返回主代码),我想在汇编中重写寄存器输出。
然而,虽然这是可行的,但它似乎在synth芯片的输出中引入了失真,而当我使用pure 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的实现会产生一些我的例程中没有的魔力。

最佳答案

C和汇编程序有一个主要区别:
我不知道你的C编译器库,但是outp()要么等于out dx,ax要么等于out dx,al
(我猜它相当于out dx,al
然而,在汇编程序中,有时用outp()替换out dx,ax,有时用out dx,al替换。两者中一定有一个是错的。
如果一个真正的声卡有一个8位的is a接口,并且outp()相当于out dx,al,那么下面的汇编程序代码:

mov ax,regnum
out dx,ax
inc dx
mov al,regval
out dx,al

... 从汇编程序获取的数据等于以下C程序:
outportb(address, regnum);
outportb(address + 1, regnum >> 8); // This may cause the noise
outportb(address + 1, regval);

尝试用out dx,ax替换out dx,al
(不幸的是,我不知道DOSbox是否模拟了一个8位或16位接口的声卡。)
请注意
在阅读了我之前所写的内容之后,您可以尝试只使用一条out指令同时写入寄存器和值:
mov al,regnum
mov ah,regval
out dx,ax

很有可能这将与一个8位接口的真正的声卡一起工作。
然而,这可能不适用于带有16位接口的真实声卡(也可能不适用于DOSbox这样的模拟器)。

关于c - 8086汇编中的outportb和out指令之间有显着区别吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56416630/

10-12 00:52