PC Speaker 程序在 MS-DOS 下工作异常

Posted

技术标签:

【中文标题】PC Speaker 程序在 MS-DOS 下工作异常【英文标题】:PC Speaker program works odd in MS-DOS 【发布时间】:2019-11-11 12:19:38 【问题描述】:

我正在使用汇编语言在 MS-DOS 中制作一个小游戏,并使用 Borland Turbo Assembler 5.0 进行编译,目前我正处于分别测试游戏不同部分的阶段(如文本和图形屏幕以及 PC 扬声器声音) .在测试板载 PC 扬声器声音时(我正在使用 VMware Worstation Player 进行仿真),程序编译得很好,但首先,它永远不会结束(卡住,我必须重新启动虚拟机才能摆脱卡住循环),其次,声音还在(重启后清除,谢天谢地),第三,频率与我预期的完全不同。

这是我的代码:

; Testovací sekvence hry Lodě (LODE.COM)
; Testing sequence of game Lodě (Boats; LODE.COM)

P8086           ; Výběr procesoru  // CPU
MODEL TINY      ; => ZASAH.COM

DATASEG
Beep    DW 0BA7h        ; Frekvenční číslo 2983 = 400 Hz      // Frequency number
Len     DW 0001h        ; HIWORD => Délka 100 ms = 100 000 us // Length of the beep
        DW 86A0h        ; LOWORD

UDATASEG
Stck    LABEL WORD

CODESEG
        STARTUPCODE

        MOV     SP,OFFSET Stck  ; Odložme si stack  // Save our stack

        CALL    Begin

        MOV     AH,4Ch
        INT     21h
        RET

Begin PROC NEAR
        mov     bx,[OFFSET Beep]        ; Nastavme si frekvenci 400 Hz  // Set frequency
        CALL    BeepStrt
        mov     cx,[OFFSET Len]         ; Nastavme si 100 ms prodlevu   // Set delay
        mov     dx,[OFFSET Len+1]
        CALL    Delay
        CALL    BeepEnd
        ret
Begin ENDP

BeepStrt PROC NEAR
        in      al,61h          ; Stáhnout hodnotu portu 61h  // See value of port 61h
        or      al,03h          ; Zapnout bity 0 a 1          // Set bits 0 and 1
        out     61h,al          ; Poslat zpět do portu 61h    // Update port 61h
        ret
BeepStrt ENDP

BeepEnd PROC NEAR
        in      al,61h          ; Stáhnout hodnotu portu 61h  // See value of port 61h
        and     al,0FCh         ; Vypnout bity 0 a 1          // Reset bits 0 and 1
        out     61h,al          ; Poslat zpět do portu 61h    // Update port 61h
        ret
BeepEnd ENDP

Delay PROC NEAR
        mov     ah,86h          ; Bios funkce prodlevy                // BIOS delay func
                                ; Právě teď máme nastavenou prodlevu  // We now have delay set
        int     15h             ; Provedeme                           // Let's do it
        ret
Delay ENDP
END

无论我使用偏移量Len +1 还是+2,当我直接输入值时它甚至都不起作用。几年前,当我使用标准 MS-DOS 程序DEBUG.EXE 对其进行测试时,它运行良好。 TASM 没有。我想使用 TASM,因为用DEBUG 写这一切真的很痛苦。源代码文件和使用 TASM 编译(当然还有 TLINK 链接)更容易也更方便。唉,最终的结果应该总是一样的吧?

这是卡住的截图:

我的信息来自以下来源:

Make sound from the speaker using assembly INT 15h 86h: Wait

我脑子里突然冒出一件事。如果实际程序超过 64k 大小会怎样? TASM/TLINK 会阻止它编译和/或链接,并告诉生成的程序太大,还是编译到 64k 并且其余部分将被截断?我让自己面临挑战,以构建适合 64k 大小的整个程序。否则,我总是可以将.EXE 重命名为.COM,无论如何它都会工作。但是,它对于用作命令的程序而不是简单的游戏来说是有意义的。

【问题讨论】:

【参考方案1】:

第一个问题

这个数据:

Len     DW 0001h        ; HIWORD => Délka 100 ms = 100 000 us // Length of the beep
        DW 86A0h        ; LOWORD

..还有这段代码:

    mov     cx,[OFFSET Len]         ; Nastavme si 100 ms prodlevu   // Set delay
    mov     dx,[OFFSET Len+1]
    CALL    Delay

..需要更像:

Len     DW 0001h        ; LOWORD => Délka 100 ms = 100 000 us // Length of the beep
        DW 86A0h        ; HIWORD

..和:

    mov     cx,[OFFSET Len]         ; Nastavme si 100 ms prodlevu   // Set delay
    mov     dx,[OFFSET Len+2]
    CALL    Delay

由于这些错误,您会得到 0xA0001000 微秒(大约 44 分钟)的延迟。

第二个问题

这段代码:

BeepStrt PROC NEAR
    in      al,61h          ; Stáhnout hodnotu portu 61h  // See value of port 61h
    or      al,03h          ; Zapnout bity 0 a 1          // Set bits 0 and 1
    out     61h,al          ; Poslat zpět do portu 61h    // Update port 61h
    ret
BeepStrt ENDP

..启用“定时器 2 门”和“扬声器数据”;这允许计时器控制扬声器。

它不设置计时器。因为没有设置计时器,所以你最终会得到“随机的,谁知道什么”(与 400 Hz 不同的频率,或静音)。

如果您查看链接到的页面(http://www.intel-assembler.it/portale/5/make-sound-from-the-speaker-in-assembly/8255-8255-8284-asm-program-example.asp);你基本上已经跳过了整个下半部分。

如果实际程序超过 64k 大小会怎样?

这取决于您如何编译/链接它。有几种不同的内存模型(参见http://www.c-jump.com/CIS77/ASM/Directives/D77_0030_models.htm)。取决于您使用的内存模型;当您超过 64 KiB 时它会崩溃,或者在您接近超过 64 KiB 之前它会因为“分段错误”(例如忘记在某处使用正确的分段覆盖前缀)而崩溃。 25 年前大家停止使用 DOS(和实模式)的原因有很多;这就是其中之一。 ;-)

【讨论】:

我的主,我是个混蛋!谢谢,我现在明白了? 是的,你是对的,我完全忘记了初始化计时器。 顺便说一句,前面的顺序是正确的。我更喜欢延迟 0x0001A000,它仍然会产生比我预期更大的延迟。然而,鉴于计时器尚未初始化,程序更像是未定义的行为并挂断了。 所以在我尝试过之后(由于某种原因,指向一个地址不能作为频率的字面值,当从内存地址提供数据时,我的音调高了 10 倍比直接喂频数时)。现在它改变了频率,但由于某种原因,计时器根本不起作用。我调查并发现了一点,但非常重要的注意事项:This service is not available for the PC, PCjr, and the XT. (vitaly_filatov.tripod.com/ng/asm/asm_026.13.html)。那么这是否意味着 MS-DOS 不支持该功能?那么有什么解决方法吗? 顺便说一句,我从那里得到了那段代码:***.com/questions/15201955/… 也试过这个小技巧(迫使 AL 归零):***.com/a/44037597/2614606 - 仍然不起作用。中断页面说它应该被XT-286和AT支持。我不确定 VMware 模拟了哪些硬件,但它不支持该功能(模拟不同的硬件),或者它有问题。

以上是关于PC Speaker 程序在 MS-DOS 下工作异常的主要内容,如果未能解决你的问题,请参考以下文章

工程类型介绍

bat文件命令大全

移动硬盘在苹果机上格式化后在PC上面无法识别

说话人分类(Speaker Diarisation)

ms-dos下怎么同时用两条命令?

Linux基础命令---unzip