PC Speaker 程序在 MS-DOS 下运行异常
PC Speaker program works odd in MS-DOS
我正在 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 链接)的源代码文件和编译要容易得多,也方便得多。唉,最终的结果应该总是一样的吧?
这是卡住的截图:
我的信息来自这些来源:
有一件事突然出现在我的脑海中。如果实际程序超过 64k 大小会怎样? TASM/TLINK 是否会阻止它编译 and/or 链接并告知生成的程序太大,或者它会编译到 64k 而其余部分将被截断?我让自己面临构建整个程序以适应 64k 大小的挑战。否则,我总是可以将 .EXE
重命名为 .COM
,无论如何它都会起作用。但是,对于用作命令的程序而不是简单的游戏来说,它是有意义的。
第一个问题
这个数据:
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
..启用 "timer 2 gate" 和 "speaker data";允许定时器控制扬声器。
它没有设置定时器。因为没有设置定时器,所以你最终得到 "random whatever who-knows-what"(与 400 Hz 不同的频率,或静音)。
如果您查看链接到的页面 (http://www.intel-assembler.it/portale/5/make-sound-from-the-speaker-in-assembly/8255-8255-8284-asm-program-example.asp);你基本上跳过了它的整个下半部分。
What will happen if the actual program exceeds 64k size?
那要看你怎么compiled/linked了。有几种不同的内存模型(请参阅 http://www.c-jump.com/CIS77/ASM/Directives/D77_0030_models.htm )。取决于您使用的内存模型;要么当你超过 64 KiB 时它会崩溃,要么它会在你接近超过 64 KiB 之前因为 "segmentation bugs" (例如忘记在某处使用正确的段覆盖前缀)而崩溃。 25 年前每个人都停止使用 DOS(和实模式)有很多非常充分的理由;这是其中之一。 ;-)
我正在 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 链接)的源代码文件和编译要容易得多,也方便得多。唉,最终的结果应该总是一样的吧?
这是卡住的截图:
我的信息来自这些来源:
有一件事突然出现在我的脑海中。如果实际程序超过 64k 大小会怎样? TASM/TLINK 是否会阻止它编译 and/or 链接并告知生成的程序太大,或者它会编译到 64k 而其余部分将被截断?我让自己面临构建整个程序以适应 64k 大小的挑战。否则,我总是可以将 .EXE
重命名为 .COM
,无论如何它都会起作用。但是,对于用作命令的程序而不是简单的游戏来说,它是有意义的。
第一个问题
这个数据:
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
..启用 "timer 2 gate" 和 "speaker data";允许定时器控制扬声器。
它没有设置定时器。因为没有设置定时器,所以你最终得到 "random whatever who-knows-what"(与 400 Hz 不同的频率,或静音)。
如果您查看链接到的页面 (http://www.intel-assembler.it/portale/5/make-sound-from-the-speaker-in-assembly/8255-8255-8284-asm-program-example.asp);你基本上跳过了它的整个下半部分。
What will happen if the actual program exceeds 64k size?
那要看你怎么compiled/linked了。有几种不同的内存模型(请参阅 http://www.c-jump.com/CIS77/ASM/Directives/D77_0030_models.htm )。取决于您使用的内存模型;要么当你超过 64 KiB 时它会崩溃,要么它会在你接近超过 64 KiB 之前因为 "segmentation bugs" (例如忘记在某处使用正确的段覆盖前缀)而崩溃。 25 年前每个人都停止使用 DOS(和实模式)有很多非常充分的理由;这是其中之一。 ;-)