So I continue programming a game engine for dos, and one of the last things I'd like to add, is a sound player (for sound blaster and compatibles).
My goal is to use a 64K block to store tiny samples (sfx, and a few drums for music at 11025 and 8000 Hz), so that the engine can play them very fast.
I got code samples that use "single cycle playback" to reproduce sounds. The problem is, the buffer storing the samples must be inside a ram "page":
If a malloc generates a 64K buffer, it will always pick a location with offset > zero, so the final part of the buffer will be at the next page, and the sound card will play noise, unless you set the DMA to the next page.
You can generate a 32K buffer inside a page with this code (from root42):
I can reduce the sample quality to fit the sound in that 32 K buffer, but it would be awesome to have 64K .
Alternatives:
-Store every sound at a different location, calculate its page : offset, and set the dma parameters every time a sample has to be played, (as the samples are tiny, it is rare they will hit a page barrier).
-Copy sounds to the 32K buffer, every time I need them (memcpy).
But these two alternatives are slower.
So I thought I could reserve the ram page using assembly, but I just can't understand very well how to use the directives (segment, at, para).
I read turbo assembler docs and I could not find any sample assigning a specific address to a segment. The only thing I found is "page" which aligns the address to 256 bytes.
I tried to use that array from c, it crashed .
I also printed the adress to see if something works:
Results:
DMA RAM Page = 6;
DMA RAM OFFSET = 0x32D0;
I want to get: page 2 (for example) and offset 0x0000 (always 0).
My goal is to use a 64K block to store tiny samples (sfx, and a few drums for music at 11025 and 8000 Hz), so that the engine can play them very fast.
I got code samples that use "single cycle playback" to reproduce sounds. The problem is, the buffer storing the samples must be inside a ram "page":
Code:
MS-DOS system memory
Page Segment:Offset address
0 0000:0000 - 0000:FFFF
1 1000:0000 - 1000:FFFF
2 2000:0000 - 2000:FFFF
3 3000:0000 - 3000:FFFF
etc ...
If a malloc generates a 64K buffer, it will always pick a location with offset > zero, so the final part of the buffer will be at the next page, and the sound card will play noise, unless you set the DMA to the next page.
You can generate a 32K buffer inside a page with this code (from root42):
Code:
void assign_dma_buffer()
{
unsigned char* temp_buf;
long linear_address;
short page1, page2;
temp_buf = (char *) malloc(32768);
linear_address = FP_SEG(temp_buf);
linear_address = (linear_address << 4)+FP_OFF(temp_buf);
page1 = linear_address >> 16;
page2 = (linear_address + 32767) >> 16;
if( page1 != page2 ) {
dma_buffer = (char *)malloc(32768);
free( temp_buf );
} else {
dma_buffer = temp_buf;
}
linear_address = FP_SEG(dma_buffer);
linear_address = (linear_address << 4)+FP_OFF(dma_buffer);
page = linear_address >> 16;
offset = linear_address & 0xFFFF;
}
I can reduce the sample quality to fit the sound in that 32 K buffer, but it would be awesome to have 64K .
Alternatives:
-Store every sound at a different location, calculate its page : offset, and set the dma parameters every time a sample has to be played, (as the samples are tiny, it is rare they will hit a page barrier).
-Copy sounds to the 32K buffer, every time I need them (memcpy).
But these two alternatives are slower.
So I thought I could reserve the ram page using assembly, but I just can't understand very well how to use the directives (segment, at, para).
I read turbo assembler docs and I could not find any sample assigning a specific address to a segment. The only thing I found is "page" which aligns the address to 256 bytes.
Code:
sound SEGMENT page ;align to 256 bytes
org 0
_sound_data label word
dw 07fffh dup (00000h)
sound ends
I tried to use that array from c, it crashed .
I also printed the adress to see if something works:
Code:
linear_address = FP_SEG(sound_data);
linear_address = (linear_address << 4)+FP_OFF(sound_data);
printf("DMA RAM Page = %i;\n",linear_address >> 16);
printf("DMA RAM OFFSET = 0x%04X;\n\n",linear_address & 0xFFFF);
Results:
DMA RAM Page = 6;
DMA RAM OFFSET = 0x32D0;
I want to get: page 2 (for example) and offset 0x0000 (always 0).
Last edited: