I am making a small game in MS-DOS using Assembly language and compiling with Borland Turbo Assembler 5.0, currently I am in phase of testing different parts of the game separately (like text and graphics screens as well as PC Speaker sound). While testing out onboard PC Speaker sound (I'm using VMware Worstation Player for emulation), the program compiles just fine, but first of all, it never ends (gets stuck, I have to reboot the virtual machine to get out of stuck loop), second of all, the sound is still on (gets cleared when rebooted, thankfully), and third of all, the frequency is totally different than I expected.
This is my code:
; 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
No matter if I use the offset Len
+1 or +2, it didn't even work when I fed it the values directly. When I was testing it few years ago with standard MS-DOS program DEBUG.EXE
, it worked perfectly. It doesn't with TASM. I want to use TASM, because it's really pain in the *** to write it all with DEBUG
. Source code file and compiling using TASM (and of course linking with TLINK) is much easier and much more convenient. Allas, the end result should always be the same, right?
This is the screenshot of it get stuck:
And I've got my informations from these sources:
One thing actually popped in my head. What will happen if the actual program exceeds 64k size? Will TASM/TLINK prevent it from compiling and/or linking and tells that the resulting program is too big, or it will compile up to 64k and rest will be truncated? I've put myself to a challenge to build entire program to fit in 64k size. Otherwise, I can always rename .EXE
to .COM
, it will work anyway. However, it makes sense for a program used as a command, rather than a simple game.
First Problem
This data:
Len DW 0001h ; HIWORD => Délka 100 ms = 100 000 us // Length of the beep
DW 86A0h ; LOWORD
..and this code:
mov cx,[OFFSET Len] ; Nastavme si 100 ms prodlevu // Set delay
mov dx,[OFFSET Len+1]
CALL Delay
..need to be more like:
Len DW 0001h ; LOWORD => Délka 100 ms = 100 000 us // Length of the beep
DW 86A0h ; HIWORD
..and:
mov cx,[OFFSET Len] ; Nastavme si 100 ms prodlevu // Set delay
mov dx,[OFFSET Len+2]
CALL Delay
Because of these bugs you get a delay of 0xA0001000 microseconds (about 44 minutes).
Second Problem
This code:
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
..enables the "timer 2 gate" and "speaker data"; which allows the timer to control the speaker.
It does not setup the timer. Because nothing sets up the timer you end up with "random whatever who-knows-what" (a different frequency to 400 Hz, or silence).
If you look at the page you linked to ( http://www.intel-assembler.it/portale/5/make-sound-from-the-speaker-in-assembly/8255-8255-8284-asm-program-example.asp ); you've basically skipped the entire bottom half of it.
What will happen if the actual program exceeds 64k size?
That depends on how you compiled/linked it. There's several different memory models (see http://www.c-jump.com/CIS77/ASM/Directives/D77_0030_models.htm ). Depending on which memory model you use; either it'll crash when you exceed 64 KiB, or it'll crash because of "segmentation bugs" (e.g. forgetting to use the right segment override prefix somewhere) before you ever get close to exceeding 64 KiB. There are many extremely good reasons why everyone stopped using DOS (and real mode) 25 years ago; and this is one of them. ;-)