在8086汇编中,outportb和out指令之间有显着差异吗?

时间:2019-06-02 15:53:54

标签: c assembly dos

我正在使用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的实现产生了我的例行程序中没有的魔术。

1 个答案:

答案 0 :(得分:4)

C和汇编程序有一个主要区别:

我不知道您的C编译器的库,但是outp()等效于out dx,axout 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之类的模拟器)。