aboutsummaryrefslogtreecommitdiff
path: root/pokeytest
diff options
context:
space:
mode:
Diffstat (limited to 'pokeytest')
-rw-r--r--pokeytest/Makefile15
-rw-r--r--pokeytest/playsdl2.c153
-rw-r--r--pokeytest/playsnd.c114
-rw-r--r--pokeytest/pokey.c764
-rw-r--r--pokeytest/pokey.h77
-rw-r--r--pokeytest/pokey11.txt277
-rw-r--r--pokeytest/pokey11_readme.txt119
-rw-r--r--pokeytest/readme.txt25
8 files changed, 1544 insertions, 0 deletions
diff --git a/pokeytest/Makefile b/pokeytest/Makefile
new file mode 100644
index 0000000..e629344
--- /dev/null
+++ b/pokeytest/Makefile
@@ -0,0 +1,15 @@
+
+all:
+ gcc -O2 -Wall -o playsdl2 playsdl2.c pokey.c `pkg-config sdl2 --cflags --libs`
+
+#gcc -O2 -Wall -o playsnd playsnd.c pokey.c
+
+#test: all
+#./playsnd > 1.raw
+#aplay -r 44100 -c 1 -t raw -f u8 1.raw
+
+test: all
+ ./playsdl2 $(TEST_SFX)
+
+clean:
+ rm -f *.o playsnd playsdl2 *.raw core
diff --git a/pokeytest/playsdl2.c b/pokeytest/playsdl2.c
new file mode 100644
index 0000000..3970a2e
--- /dev/null
+++ b/pokeytest/playsdl2.c
@@ -0,0 +1,153 @@
+#include <SDL.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "pokey.h"
+
+#define ROM_FILE "../jumpmanjr.rom"
+#define ROM_SIZE 0x4000
+
+#define FREQ 44100
+
+#define PAL_BUF_SIZE 882
+#define NTSC_BUF_SIZE 735
+
+#define PAL_DELAY 20
+#define NTSC_DELAY 17
+
+unsigned char sndbuf[PAL_BUF_SIZE + 1];
+
+int buf_size = NTSC_BUF_SIZE;
+int delay = NTSC_DELAY;
+
+unsigned char rom[ROM_SIZE];
+
+void usage(void) {
+ fprintf(stderr, "usage: playsnd [-p] 0xaddr\n");
+ exit(1);
+}
+
+void load_rom(void) {
+ int size;
+ FILE *f;
+
+ f = fopen(ROM_FILE, "rb");
+ if(!f) {
+ perror(ROM_FILE);
+ usage();
+ }
+
+ size = fread(rom, 1, ROM_SIZE, f);
+ if(size != ROM_SIZE) {
+ fprintf(stderr, "file %s should be %d bytes long, not %d\n",
+ ROM_FILE, ROM_SIZE, size);
+ usage();
+ }
+
+ fprintf(stderr, "loaded ROM %s, %d bytes\n", ROM_FILE, size);
+}
+
+void play_frames(unsigned char count) {
+ fprintf(stderr, "playing for %d frames\n", count);
+ while(count--) {
+ SDL_Delay(delay);
+ }
+}
+
+void play_sfx(int addr) {
+ unsigned char *p = rom + (addr - 0x8000);
+
+ while(1) {
+ fprintf(stderr, "$%04lx: $%02x\n", (p - rom + 0x8000), *p);
+ switch(*p) {
+ case 0:
+ fprintf(stderr, "opcode 0 (snd_end)\n");
+ return;
+ case 1:
+ fprintf(stderr, "opcode 1 (snd_audc)\n");
+ Update_pokey_sound(audc1, *++p);
+ play_frames(*++p);
+ ++p;
+ break;
+ case 2:
+ fprintf(stderr, "opcode 2 (snd_jump)\n");
+ addr = *++p;
+ addr |= ( (*++p) << 8);
+ addr -= 0x8000;
+ p = rom + addr;
+ break;
+ case 3:
+ fprintf(stderr, "opcode 3 (snd_rest)\n");
+ Update_pokey_sound(audc1, 0);
+ play_frames(*++p);
+ ++p;
+ break;
+ default:
+ fprintf(stderr, "opcode $%02x (snd_note)\n", *p);
+ Update_pokey_sound(audf1, *p);
+ play_frames(*++p);
+ ++p;
+ break;
+ }
+ }
+}
+
+void sdl_audio_callback(void *userdata, Uint8* stream, int len) {
+ Pokey_process(stream, len);
+}
+
+void init_sdl_audio(void) {
+ SDL_AudioSpec want, have;
+ SDL_AudioDeviceID dev;
+
+ if(SDL_Init(SDL_INIT_AUDIO) < 0) {
+ fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
+ exit(-1);
+ }
+ atexit(SDL_Quit);
+
+ SDL_memset(&want, 0, sizeof(want));
+ want.freq = FREQ;
+ want.format = AUDIO_U8;
+ want.channels = 1;
+ want.samples = buf_size;
+ want.callback = sdl_audio_callback;
+
+ dev = SDL_OpenAudioDevice(0, 0, &want, &have, 0);
+ if(!dev) {
+ fprintf(stderr, "Failed to open audio: %s\n", SDL_GetError());
+ exit(-1);
+ }
+
+ SDL_PauseAudioDevice(dev, 0);
+}
+
+int main(int argc, char **argv) {
+ int addr;
+
+ if(argc < 2 || argc > 3) usage();
+
+ if(argc == 3) {
+ buf_size = PAL_BUF_SIZE;
+ delay = PAL_DELAY;
+ addr = (int)strtol(argv[2], 0, 16);
+ } else {
+ addr = (int)strtol(argv[1], 0, 16);
+ }
+
+ if(addr < 0x8000 || addr > 0xbfff) {
+ fprintf(stderr, "invalid address, must be 0x8fff thru 0xbfff\n");
+ usage();
+ }
+
+ load_rom();
+
+ fprintf(stderr, "playing sfx at $%04x, buf_size %d\n", addr, buf_size);
+
+ Pokey_sound_init(FREQ_17_APPROX, FREQ);
+ init_sdl_audio();
+ play_sfx(addr);
+
+ return(0);
+}
diff --git a/pokeytest/playsnd.c b/pokeytest/playsnd.c
new file mode 100644
index 0000000..0f76879
--- /dev/null
+++ b/pokeytest/playsnd.c
@@ -0,0 +1,114 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "pokey.h"
+
+#define ROM_FILE "../jumpmanjr.rom"
+#define ROM_SIZE 0x4000
+
+#define PAL_BUF_SIZE 882
+#define NTSC_BUF_SIZE 735
+
+unsigned char sndbuf[PAL_BUF_SIZE + 1];
+
+unsigned char rom[ROM_SIZE];
+
+void usage(void) {
+ fprintf(stderr, "usage: playsnd [-p] 0xaddr\n");
+ exit(1);
+}
+
+void load_rom(void) {
+ int size;
+ FILE *f;
+
+ f = fopen(ROM_FILE, "rb");
+ if(!f) {
+ perror(ROM_FILE);
+ usage();
+ }
+
+ size = fread(rom, 1, ROM_SIZE, f);
+ if(size != ROM_SIZE) {
+ fprintf(stderr, "file %s should be %d bytes long, not %d\n",
+ ROM_FILE, ROM_SIZE, size);
+ usage();
+ }
+
+ fprintf(stderr, "loaded ROM %s, %d bytes\n", ROM_FILE, size);
+}
+
+void play_frames(unsigned char count, int buf_size) {
+ fprintf(stderr, "playing for %d frames\n", count);
+ while(count--) {
+ Pokey_process(sndbuf, buf_size);
+ fwrite(sndbuf, buf_size, 1, stdout);
+ }
+}
+
+void play_sfx(int addr, int buf_size) {
+ unsigned char *p = rom + (addr - 0x8000);
+
+ while(1) {
+ fprintf(stderr, "$%04lx: $%02x\n", (p - rom + 0x8000), *p);
+ switch(*p) {
+ case 0:
+ fprintf(stderr, "opcode 0 (snd_end)\n");
+ return;
+ case 1:
+ fprintf(stderr, "opcode 1 (snd_audc)\n");
+ Update_pokey_sound(audc1, *++p);
+ play_frames(*++p, buf_size);
+ ++p;
+ break;
+ case 2:
+ fprintf(stderr, "opcode 2 (snd_jump)\n");
+ addr = *++p;
+ addr |= ( (*++p) << 8);
+ addr -= 0x8000;
+ p = rom + addr;
+ break;
+ case 3:
+ fprintf(stderr, "opcode 3 (snd_rest)\n");
+ Update_pokey_sound(audc1, 0);
+ play_frames(*++p, buf_size);
+ ++p;
+ break;
+ default:
+ fprintf(stderr, "opcode $%02x (snd_note)\n", *p);
+ Update_pokey_sound(audf1, *p);
+ play_frames(*++p, buf_size);
+ ++p;
+ break;
+ }
+ }
+}
+
+int main(int argc, char **argv) {
+ int buf_size = NTSC_BUF_SIZE;
+ int addr;
+
+ if(argc < 2 || argc > 3) usage();
+
+ if(argc == 3) {
+ buf_size = PAL_BUF_SIZE;
+ addr = (int)strtol(argv[2], 0, 0);
+ } else {
+ addr = (int)strtol(argv[1], 0, 0);
+ }
+
+ if(addr < 0x8000 || addr > 0xbfff) {
+ fprintf(stderr, "invalid address, must be 0x8fff thru 0xbfff\n");
+ usage();
+ }
+
+ load_rom();
+
+ fprintf(stderr, "playing sfx at $%04x, buf_size %d\n", addr, buf_size);
+
+ Pokey_sound_init(FREQ_17_APPROX, 44100);
+ play_sfx(addr, buf_size);
+
+ return(0);
+}
+
diff --git a/pokeytest/pokey.c b/pokeytest/pokey.c
new file mode 100644
index 0000000..11a51af
--- /dev/null
+++ b/pokeytest/pokey.c
@@ -0,0 +1,764 @@
+/*****************************************************************************/
+/* */
+/* Module: POKEY Chip Simulator, V1.1 */
+/* Purpose: To emulate the sound generation hardware of the Atari POKEY chip.*/
+/* Author: Ron Fries */
+/* Date: September 22, 1996 */
+/* */
+/*****************************************************************************/
+/* */
+/* License Information and Copyright Notice */
+/* ======================================== */
+/* */
+/* PokeySound is Copyright(c) 1996 by Ron Fries */
+/* */
+/* This library is free software; you can redistribute it and/or modify it */
+/* under the terms of version 2 of the GNU Library General Public License */
+/* as published by the Free Software Foundation. */
+/* */
+/* This library is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library */
+/* General Public License for more details. */
+/* To obtain a copy of the GNU Library General Public License, write to the */
+/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+/* */
+/* Any permitted reproduction of these routines, in whole or in part, must */
+/* bear this legend. */
+/* */
+/*****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "pokey.h"
+
+/* CONSTANT DEFINITIONS */
+
+/* definitions for AUDCx (D201, D203, D205, D207) */
+#define NOTPOLY5 0x80 /* selects POLY5 or direct CLOCK */
+#define POLY4 0x40 /* selects POLY4 or POLY17 */
+#define PURE 0x20 /* selects POLY4/17 or PURE tone */
+#define VOL_ONLY 0x10 /* selects VOLUME OUTPUT ONLY */
+#define VOLUME_MASK 0x0f /* volume mask */
+
+/* definitions for AUDCTL (D208) */
+#define POLY9 0x80 /* selects POLY9 or POLY17 */
+#define CH1_179 0x40 /* selects 1.78979 MHz for Ch 1 */
+#define CH3_179 0x20 /* selects 1.78979 MHz for Ch 3 */
+#define CH1_CH2 0x10 /* clocks channel 1 w/channel 2 */
+#define CH3_CH4 0x08 /* clocks channel 3 w/channel 4 */
+#define CH1_FILTER 0x04 /* selects channel 1 high pass filter */
+#define CH2_FILTER 0x02 /* selects channel 2 high pass filter */
+#define CLOCK_15 0x01 /* selects 15.6999kHz or 63.9210kHz */
+
+#define AUDF1_C 0xd200
+#define AUDC1_C 0xd201
+#define AUDF2_C 0xd202
+#define AUDC2_C 0xd203
+#define AUDF3_C 0xd204
+#define AUDC3_C 0xd205
+#define AUDF4_C 0xd206
+#define AUDC4_C 0xd207
+#define AUDCTL_C 0xd208
+
+/* for accuracy, the 64kHz and 15kHz clocks are exact divisions of
+ the 1.79MHz clock */
+#define DIV_64 28 /* divisor for 1.79MHz clock to 64 kHz */
+#define DIV_15 114 /* divisor for 1.79MHz clock to 15 kHz */
+
+/* the size (in entries) of the 4 polynomial tables */
+#define POLY4_SIZE 0x000f
+#define POLY5_SIZE 0x001f
+#define POLY9_SIZE 0x01ff
+
+#ifdef COMP16 /* if 16-bit compiler */
+#define POLY17_SIZE 0x00007fffL /* reduced to 15 bits for simplicity */
+#else
+#define POLY17_SIZE 0x0001ffffL /* else use the full 17 bits */
+#endif
+
+/* channel definitions */
+#define CHAN1 0
+#define CHAN2 1
+#define CHAN3 2
+#define CHAN4 3
+#define SAMPLE 4
+
+
+#define FALSE 0
+#define TRUE 1
+
+
+/* GLOBAL VARIABLE DEFINITIONS */
+
+/* structures to hold the 9 pokey control bytes */
+static uint8 AUDF[4]; /* AUDFx (D200, D202, D204, D206) */
+static uint8 AUDC[4]; /* AUDCx (D201, D203, D205, D207) */
+static uint8 AUDCTL; /* AUDCTL (D208) */
+
+static uint8 Outbit[4]; /* current state of the output (high or low) */
+
+static uint8 Outvol[4]; /* last output volume for each channel */
+
+
+/* Initialze the bit patterns for the polynomials. */
+
+/* The 4bit and 5bit patterns are the identical ones used in the pokey chip. */
+/* Though the patterns could be packed with 8 bits per byte, using only a */
+/* single bit per byte keeps the math simple, which is important for */
+/* efficient processing. */
+
+static uint8 bit4[POLY4_SIZE] =
+ { 1,1,0,1,1,1,0,0,0,0,1,0,1,0,0 };
+
+static uint8 bit5[POLY5_SIZE] =
+ { 0,0,1,1,0,0,0,1,1,1,1,0,0,1,0,1,0,1,1,0,1,1,1,0,1,0,0,0,0,0,1 };
+
+static uint8 bit17[POLY17_SIZE]; /* Rather than have a table with 131071 */
+ /* entries, I use a random number generator. */
+ /* It shouldn't make much difference since */
+ /* the pattern rarely repeats anyway. */
+
+static uint32 Poly17_size; /* global for the poly 17 size, since it can */
+ /* be changed from 17 bit to 9 bit */
+
+static uint32 Poly_adjust; /* the amount that the polynomial will need */
+ /* to be adjusted to process the next bit */
+
+static uint32 P4=0, /* Global position pointer for the 4-bit POLY array */
+ P5=0, /* Global position pointer for the 5-bit POLY array */
+ P17=0; /* Global position pointer for the 17-bit POLY array */
+
+static uint32 Div_n_cnt[4], /* Divide by n counter. one for each channel */
+ Div_n_max[4]; /* Divide by n maximum, one for each channel */
+
+static uint32 Samp_n_max, /* Sample max. For accuracy, it is *256 */
+ Samp_n_cnt[2]; /* Sample cnt. */
+
+static uint32 Base_mult; /* selects either 64Khz or 15Khz clock mult */
+
+/*****************************************************************************/
+/* In my routines, I treat the sample output as another divide by N counter */
+/* For better accuracy, the Samp_n_cnt has a fixed binary decimal point */
+/* which has 8 binary digits to the right of the decimal point. I use a two */
+/* byte array to give me a minimum of 40 bits, and then use pointer math to */
+/* reference either the 24.8 whole/fraction combination or the 32-bit whole */
+/* only number. This is mainly used to keep the math simple for */
+/* optimization. See below: */
+/* */
+/* xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | xxxxxxxx xxxxxxxx xxxxxxxx.xxxxxxxx */
+/* unused unused unused whole whole whole whole fraction */
+/* */
+/* Samp_n_cnt[0] gives me a 32-bit int 24 whole bits with 8 fractional bits, */
+/* while (uint32 *)((uint8 *)(&Samp_n_cnt[0])+1) gives me the 32-bit whole */
+/* number only. */
+/*****************************************************************************/
+
+
+/*****************************************************************************/
+/* Module: Pokey_sound_init() */
+/* Purpose: to handle the power-up initialization functions */
+/* these functions should only be executed on a cold-restart */
+/* */
+/* Author: Ron Fries */
+/* Date: September 22, 1996 */
+/* */
+/* Inputs: freq17 - the value for the '1.79MHz' Pokey audio clock */
+/* playback_freq - the playback frequency in samples per second */
+/* */
+/* Outputs: Adjusts local globals - no return value */
+/* */
+/*****************************************************************************/
+
+void Pokey_sound_init (uint32 freq17, uint16 playback_freq)
+{
+ uint8 chan;
+ int32 n;
+
+ /* fill the 17bit polynomial with random bits */
+ for (n=0; n<POLY17_SIZE; n++)
+ {
+ bit17[n] = rand() & 0x01; /* fill poly 17 with random bits */
+ }
+
+ /* start all of the polynomial counters at zero */
+ Poly_adjust = 0;
+ P4 = 0;
+ P5 = 0;
+ P17 = 0;
+
+ /* calculate the sample 'divide by N' value based on the playback freq. */
+ Samp_n_max = ((uint32)freq17 << 8) / playback_freq;
+
+ Samp_n_cnt[0] = 0; /* initialize all bits of the sample */
+ Samp_n_cnt[1] = 0; /* 'divide by N' counter */
+
+ Poly17_size = POLY17_SIZE;
+
+ for (chan = CHAN1; chan <= CHAN4; chan++)
+ {
+ Outvol[chan] = 0;
+ Outbit[chan] = 0;
+ Div_n_cnt[chan] = 0;
+ Div_n_max[chan] = 0x7fffffffL;
+ AUDC[chan] = 0;
+ AUDF[chan] = 0;
+ }
+
+ AUDCTL = 0;
+
+ Base_mult = DIV_64;
+}
+
+
+/*****************************************************************************/
+/* Module: Update_pokey_sound() */
+/* Purpose: To process the latest control values stored in the AUDF, AUDC, */
+/* and AUDCTL registers. It pre-calculates as much information as */
+/* possible for better performance. This routine has not been */
+/* optimized. */
+/* */
+/* Author: Ron Fries */
+/* Date: September 22, 1996 */
+/* */
+/* Inputs: addr - the address of the parameter to be changed */
+/* val - the new value to be placed in the specified address */
+/* */
+/* Outputs: Adjusts local globals - no return value */
+/* */
+/*****************************************************************************/
+
+void Update_pokey_sound (uint16 addr, uint8 val)
+{
+ uint32 new_val = 0;
+ uint8 chan;
+ uint8 chan_mask;
+
+ /* determine which address was changed */
+ switch (addr)
+ {
+ case AUDF1_C:
+ AUDF[CHAN1] = val;
+ chan_mask = 1 << CHAN1;
+
+ if (AUDCTL & CH1_CH2) /* if ch 1&2 tied together */
+ chan_mask |= 1 << CHAN2; /* then also change on ch2 */
+ break;
+
+ case AUDC1_C:
+ AUDC[CHAN1] = val;
+ chan_mask = 1 << CHAN1;
+ break;
+
+ case AUDF2_C:
+ AUDF[CHAN2] = val;
+ chan_mask = 1 << CHAN2;
+ break;
+
+ case AUDC2_C:
+ AUDC[CHAN2] = val;
+ chan_mask = 1 << CHAN2;
+ break;
+
+ case AUDF3_C:
+ AUDF[CHAN3] = val;
+ chan_mask = 1 << CHAN3;
+
+ if (AUDCTL & CH3_CH4) /* if ch 3&4 tied together */
+ chan_mask |= 1 << CHAN4; /* then also change on ch4 */
+ break;
+
+ case AUDC3_C:
+ AUDC[CHAN3] = val;
+ chan_mask = 1 << CHAN3;
+ break;
+
+ case AUDF4_C:
+ AUDF[CHAN4] = val;
+ chan_mask = 1 << CHAN4;
+ break;
+
+ case AUDC4_C:
+ AUDC[CHAN4] = val;
+ chan_mask = 1 << CHAN4;
+ break;
+
+ case AUDCTL_C:
+ AUDCTL = val;
+ chan_mask = 15; /* all channels */
+
+ /* set poly17 counter to 9- or 17-bit */
+ if (AUDCTL & POLY9)
+ Poly17_size = POLY9_SIZE;
+ else
+ Poly17_size = POLY17_SIZE;
+
+ /* determine the base multiplier for the 'div by n' calculations */
+ if (AUDCTL & CLOCK_15)
+ Base_mult = DIV_15;
+ else
+ Base_mult = DIV_64;
+
+ break;
+
+ default:
+ chan_mask = 0;
+ break;
+ }
+
+ /************************************************************/
+ /* As defined in the manual, the exact Div_n_cnt values are */
+ /* different depending on the frequency and resolution: */
+ /* 64 kHz or 15 kHz - AUDF + 1 */
+ /* 1 MHz, 8-bit - AUDF + 4 */
+ /* 1 MHz, 16-bit - AUDF[CHAN1]+256*AUDF[CHAN2] + 7 */
+ /************************************************************/
+
+ /* only reset the channels that have changed */
+
+ if (chan_mask & (1 << CHAN1))
+ {
+ /* process channel 1 frequency */
+ if (AUDCTL & CH1_179)
+ new_val = AUDF[CHAN1] + 4;
+ else
+ new_val = (AUDF[CHAN1] + 1) * Base_mult;
+
+ if (new_val != Div_n_max[CHAN1])
+ {
+ Div_n_max[CHAN1] = new_val;
+ Div_n_cnt[CHAN1] = 0;
+ }
+ }
+
+ if (chan_mask & (1 << CHAN2))
+ {
+ /* process channel 2 frequency */
+ if (AUDCTL & CH1_CH2)
+ if (AUDCTL & CH1_179)
+ new_val = AUDF[CHAN2] * 256 + AUDF[CHAN1] + 7;
+ else
+ new_val = (AUDF[CHAN2] * 256 + AUDF[CHAN1] + 1) * Base_mult;
+ else
+ new_val = (AUDF[CHAN2] + 1) * Base_mult;
+
+ if (new_val != Div_n_max[CHAN2])
+ {
+ Div_n_max[CHAN2] = new_val;
+ Div_n_cnt[CHAN2] = 0;
+ }
+ }
+
+ if (chan_mask & (1 << CHAN3))
+ {
+ /* process channel 3 frequency */
+ if (AUDCTL & CH3_179)
+ new_val = AUDF[CHAN3] + 4;
+ else
+ new_val= (AUDF[CHAN3] + 1) * Base_mult;
+
+ if (new_val!= Div_n_max[CHAN3])
+ {
+ Div_n_max[CHAN3] = new_val;
+ Div_n_cnt[CHAN3] = 0;
+ }
+ }
+
+ if (chan_mask & (1 << CHAN4))
+ {
+ /* process channel 4 frequency */
+ if (AUDCTL & CH3_CH4)
+ if (AUDCTL & CH3_179)
+ new_val = AUDF[CHAN4] * 256 + AUDF[CHAN3] + 7;
+ else
+ new_val = (AUDF[CHAN4] * 256 + AUDF[CHAN3] + 1) * Base_mult;
+ else
+ new_val = (AUDF[CHAN4] + 1) * Base_mult;
+
+ if (new_val != Div_n_max[CHAN4])
+ {
+ Div_n_max[CHAN4] = new_val;
+ Div_n_cnt[CHAN4] = 0;
+ }
+ }
+
+ /* if channel is volume only, set current output */
+ for (chan = CHAN1; chan <= CHAN4; chan++)
+ {
+ if (chan_mask & (1 << chan))
+ {
+ /* I've disabled any frequencies that exceed the sampling
+ frequency. There isn't much point in processing frequencies
+ that the hardware can't reproduce. I've also disabled
+ processing if the volume is zero. */
+
+ /* if the channel is volume only */
+ /* or the channel is off (volume == 0) */
+ /* or the channel freq is greater than the playback freq */
+ if ((AUDC[chan] & VOL_ONLY) ||
+ ((AUDC[chan] & VOLUME_MASK) == 0) ||
+ (Div_n_max[chan] < (Samp_n_max >> 8)))
+ {
+ /* then set the channel to the selected volume */
+ Outvol[chan] = AUDC[chan] & VOLUME_MASK;
+ /* and set channel freq to max to reduce processing */
+ Div_n_max[chan] = 0x7fffffffL;
+ }
+ }
+ }
+}
+
+
+/*****************************************************************************/
+/* Module: Pokey_process_2() */
+/* Purpose: To fill the output buffer with the sound output based on the */
+/* pokey chip parameters. This routine has not been optimized. */
+/* Though it is not used by the program, I've left it for reference.*/
+/* */
+/* Author: Ron Fries */
+/* Date: September 22, 1996 */
+/* */
+/* Inputs: *buffer - pointer to the buffer where the audio output will */
+/* be placed */
+/* n - size of the playback buffer */
+/* */
+/* Outputs: the buffer will be filled with n bytes of audio - no return val */
+/* */
+/*****************************************************************************/
+
+void Pokey_process_2 (register unsigned char *buffer, register uint16 n)
+{
+ register uint32 *samp_cnt_w_ptr;
+ register uint32 event_min;
+ register uint8 next_event;
+ register uint8 cur_val;
+ register uint8 chan;
+
+ /* set a pointer to the whole portion of the samp_n_cnt */
+ samp_cnt_w_ptr = (uint32 *)((uint8 *)(&Samp_n_cnt[0])+1);
+
+ /* loop until the buffer is filled */
+ while (n)
+ {
+ /* Normally the routine would simply decrement the 'div by N' */
+ /* counters and react when they reach zero. Since we normally */
+ /* won't be processing except once every 80 or so counts, */
+ /* I've optimized by finding the smallest count and then */
+ /* 'accelerated' time by adjusting all pointers by that amount. */
+
+ /* find next smallest event (either sample or chan 1-4) */
+ next_event = SAMPLE;
+ event_min = *samp_cnt_w_ptr;
+
+ for (chan = CHAN1; chan <= CHAN4; chan++)
+ {
+ if (Div_n_cnt[chan] <= event_min)
+ {
+ event_min = Div_n_cnt[chan];
+ next_event = chan;
+ }
+ }
+
+
+ /* decrement all counters by the smallest count found */
+ for (chan = CHAN1; chan <= CHAN4; chan++)
+ {
+ Div_n_cnt[chan] -= event_min;
+ }
+
+ *samp_cnt_w_ptr -= event_min;
+
+ /* since the polynomials require a mod (%) function which is
+ division, I don't adjust the polynomials on the SAMPLE events,
+ only the CHAN events. I have to keep track of the change,
+ though. */
+ Poly_adjust += event_min;
+
+ /* if the next event is a channel change */
+ if (next_event != SAMPLE)
+ {
+ /* shift the polynomial counters */
+ P4 = (P4 + Poly_adjust) % POLY4_SIZE;
+ P5 = (P5 + Poly_adjust) % POLY5_SIZE;
+ P17 = (P17 + Poly_adjust) % Poly17_size;
+
+ /* reset the polynomial adjust counter to zero */
+ Poly_adjust = 0;
+
+ /* adjust channel counter */
+ Div_n_cnt[next_event] += Div_n_max[next_event];
+
+ /* From here, a good understanding of the hardware is required */
+ /* to understand what is happening. I won't be able to provide */
+ /* much description to explain it here. */
+
+ /* if the output is pure or the output is poly5 and the poly5 bit */
+ /* is set */
+ if ((AUDC[next_event] & NOTPOLY5) || bit5[P5])
+ {
+ /* if the PURE bit is set */
+ if (AUDC[next_event] & PURE)
+ {
+ /* then simply toggle the output */
+ Outbit[next_event] = !Outbit[next_event];
+ }
+ /* otherwise if POLY4 is selected */
+ else if (AUDC[next_event] & POLY4)
+ {
+ /* then use the poly4 bit */
+ Outbit[next_event] = bit4[P4];
+ }
+ else
+ {
+ /* otherwise use the poly17 bit */
+ Outbit[next_event] = bit17[P17];
+ }
+ }
+
+ /* At this point I haven't emulated the filters. Though I don't
+ expect it to be complicated, I don't believe this feature is
+ used much anyway. I'll work on it later. */
+ if ((next_event == CHAN1) || (next_event == CHAN3))
+ {
+ /* INSERT FILTER HERE */
+ }
+
+ /* if the current output bit is set */
+ if (Outbit[next_event])
+ {
+ /* then set to the current volume */
+ Outvol[next_event] = AUDC[next_event] & VOLUME_MASK;
+ }
+ else
+ {
+ /* set the volume to zero */
+ Outvol[next_event] = 0;
+ }
+ }
+ else /* otherwise we're processing a sample */
+ {
+ /* adjust the sample counter - note we're using the 24.8 integer
+ which includes an 8 bit fraction for accuracy */
+ *Samp_n_cnt += Samp_n_max;
+
+ cur_val = 0;
+
+ /* add the output values of all 4 channels */
+ for (chan = CHAN1; chan <= CHAN4; chan++)
+ {
+ cur_val += Outvol[chan];
+ }
+
+ /* multiply the volume by 4 and add 8 to center around 128 */
+ /* NOTE: this statement could be eliminated for efficiency, */
+ /* though the volume would be lower. */
+ cur_val = (cur_val << 2) + 8;
+
+ /* add the current value to the output buffer */
+ *buffer++ = cur_val;
+
+ /* and indicate one less byte in the buffer */
+ n--;
+ }
+ }
+}
+
+
+/*****************************************************************************/
+/* Module: Pokey_process() */
+/* Purpose: To fill the output buffer with the sound output based on the */
+/* pokey chip parameters. This routine has not been optimized. */
+/* Though it is not used by the program, I've left it for reference.*/
+/* */
+/* Author: Ron Fries */
+/* Date: September 22, 1996 */
+/* */
+/* Inputs: *buffer - pointer to the buffer where the audio output will */
+/* be placed */
+/* n - size of the playback buffer */
+/* */
+/* Outputs: the buffer will be filled with n bytes of audio - no return val */
+/* */
+/*****************************************************************************/
+
+void Pokey_process (register unsigned char *buffer, register uint16 n)
+{
+ register uint32 *div_n_ptr;
+ register uint32 *samp_cnt_w_ptr;
+ register uint32 event_min;
+ register uint8 next_event;
+ register uint8 cur_val;
+ register uint8 *out_ptr;
+ register uint8 audc;
+ register uint8 toggle;
+
+
+ /* set a pointer to the whole portion of the samp_n_cnt */
+ samp_cnt_w_ptr = (uint32 *)((uint8 *)(&Samp_n_cnt[0])+1);
+
+ /* set a pointer for optimization */
+ out_ptr = Outvol;
+
+ /* The current output is pre-determined and then adjusted based on each */
+ /* output change for increased performance (less over-all math). */
+ /* add the output values of all 4 channels */
+ cur_val = 2; /* start with a small offset */
+ cur_val += *out_ptr++;
+ cur_val += *out_ptr++;
+ cur_val += *out_ptr++;
+ cur_val += *out_ptr++;
+
+ /* loop until the buffer is filled */
+ while (n)
+ {
+ /* Normally the routine would simply decrement the 'div by N' */
+ /* counters and react when they reach zero. Since we normally */
+ /* won't be processing except once every 80 or so counts, */
+ /* I've optimized by finding the smallest count and then */
+ /* 'accelerated' time by adjusting all pointers by that amount. */
+
+ /* find next smallest event (either sample or chan 1-4) */
+ next_event = SAMPLE;
+ event_min = *samp_cnt_w_ptr;
+
+ /* Though I could have used a loop here, this is faster */
+ div_n_ptr = Div_n_cnt;
+ if (*div_n_ptr <= event_min)
+ {
+ event_min = *div_n_ptr;
+ next_event = CHAN1;
+ }
+ div_n_ptr++;
+ if (*div_n_ptr <= event_min)
+ {
+ event_min = *div_n_ptr;
+ next_event = CHAN2;
+ }
+ div_n_ptr++;
+ if (*div_n_ptr <= event_min)
+ {
+ event_min = *div_n_ptr;
+ next_event = CHAN3;
+ }
+ div_n_ptr++;
+ if (*div_n_ptr <= event_min)
+ {
+ event_min = *div_n_ptr;
+ next_event = CHAN4;
+ }
+
+ /* decrement all counters by the smallest count found */
+ /* again, no loop for efficiency */
+ *div_n_ptr -= event_min;
+ div_n_ptr--;
+ *div_n_ptr -= event_min;
+ div_n_ptr--;
+ *div_n_ptr -= event_min;
+ div_n_ptr--;
+ *div_n_ptr -= event_min;
+
+ *samp_cnt_w_ptr -= event_min;
+
+ /* since the polynomials require a mod (%) function which is
+ division, I don't adjust the polynomials on the SAMPLE events,
+ only the CHAN events. I have to keep track of the change,
+ though. */
+ Poly_adjust += event_min;
+
+ /* if the next event is a channel change */
+ if (next_event != SAMPLE)
+ {
+ /* shift the polynomial counters */
+ P4 = (P4 + Poly_adjust) % POLY4_SIZE;
+ P5 = (P5 + Poly_adjust) % POLY5_SIZE;
+ P17 = (P17 + Poly_adjust) % Poly17_size;
+
+ /* reset the polynomial adjust counter to zero */
+ Poly_adjust = 0;
+
+ /* adjust channel counter */
+ Div_n_cnt[next_event] += Div_n_max[next_event];
+
+ /* get the current AUDC into a register (for optimization) */
+ audc = AUDC[next_event];
+
+ /* set a pointer to the current output (for opt...) */
+ out_ptr = &Outvol[next_event];
+
+ /* assume no changes to the output */
+ toggle = FALSE;
+
+ /* From here, a good understanding of the hardware is required */
+ /* to understand what is happening. I won't be able to provide */
+ /* much description to explain it here. */
+
+ /* if the output is pure or the output is poly5 and the poly5 bit */
+ /* is set */
+ if ((audc & NOTPOLY5) || bit5[P5])
+ {
+ /* if the PURE bit is set */
+ if (audc & PURE)
+ {
+ /* then simply toggle the output */
+ toggle = TRUE;
+ }
+ /* otherwise if POLY4 is selected */
+ else if (audc & POLY4)
+ {
+ /* then compare to the poly4 bit */
+ toggle = (bit4[P4] == !(*out_ptr));
+ }
+ else
+ {
+ /* otherwise compare to the poly17 bit */
+ toggle = (bit17[P17] == !(*out_ptr));
+ }
+ }
+
+ /* At this point I haven't emulated the filters. Though I don't
+ expect it to be complicated, I don't believe this feature is
+ used much anyway. I'll work on it later. */
+ if ((next_event == CHAN1) || (next_event == CHAN3))
+ {
+ /* INSERT FILTER HERE */
+ }
+
+ /* if the current output bit has changed */
+ if (toggle)
+ {
+ if (*out_ptr)
+ {
+ /* remove this channel from the signal */
+ cur_val -= *out_ptr;
+
+ /* and turn the output off */
+ *out_ptr = 0;
+ }
+ else
+ {
+ /* turn the output on */
+ *out_ptr = audc & VOLUME_MASK;
+
+ /* and add it to the output signal */
+ cur_val += *out_ptr;
+ }
+ }
+ }
+ else /* otherwise we're processing a sample */
+ {
+ /* adjust the sample counter - note we're using the 24.8 integer
+ which includes an 8 bit fraction for accuracy */
+ *Samp_n_cnt += Samp_n_max;
+
+ /* add the current value to the output buffer */
+ *buffer++ = cur_val << 2;
+
+ /* and indicate one less byte in the buffer */
+ n--;
+ }
+ }
+}
+
+
diff --git a/pokeytest/pokey.h b/pokeytest/pokey.h
new file mode 100644
index 0000000..ee2c6e3
--- /dev/null
+++ b/pokeytest/pokey.h
@@ -0,0 +1,77 @@
+/*****************************************************************************/
+/* */
+/* Module: POKEY Chip Simulator Includes, V1.1 */
+/* Purpose: To emulate the sound generation hardware of the Atari POKEY chip.*/
+/* Author: Ron Fries */
+/* Date: September 22, 1996 */
+/* */
+/*****************************************************************************/
+/* */
+/* License Information and Copyright Notice */
+/* ======================================== */
+/* */
+/* PokeySound is Copyright(c) 1996 by Ron Fries */
+/* */
+/* This library is free software; you can redistribute it and/or modify it */
+/* under the terms of version 2 of the GNU Library General Public License */
+/* as published by the Free Software Foundation. */
+/* */
+/* This library is distributed in the hope that it will be useful, but */
+/* WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library */
+/* General Public License for more details. */
+/* To obtain a copy of the GNU Library General Public License, write to the */
+/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+/* */
+/* Any permitted reproduction of these routines, in whole or in part, must */
+/* bear this legend. */
+/* */
+/*****************************************************************************/
+
+#ifndef _POKEYSOUND_H
+#define _POKEYSOUND_H
+
+#include <stdint.h>
+
+#define int8 int8_t
+#define int16 int16_t
+#define int32 int32_t
+#define uint8 uint8_t
+#define uint16 uint16_t
+#define uint32 uint32_t
+
+#define audf1 (0xd200 + 0x00)
+#define audc1 (0xd200 + 0x01)
+#define audf2 (0xd200 + 0x02)
+#define audc2 (0xd200 + 0x03)
+#define audf3 (0xd200 + 0x04)
+#define audc3 (0xd200 + 0x05)
+#define audf4 (0xd200 + 0x06)
+#define audc4 (0xd200 + 0x07)
+#define audctl (0xd200 + 0x08)
+
+/* CONSTANT DEFINITIONS */
+
+/* As an alternative to using the exact frequencies, selecting a playback
+ frequency that is an exact division of the main clock provides a higher
+ quality output due to less aliasing. For best results, a value of
+ 1787520 MHz is used for the main clock. With this value, both the
+ 64 kHz and 15 kHz clocks are evenly divisible. Selecting a playback
+ frequency that is also a division of the clock provides the best
+ results. The best options are FREQ_64 divided by either 2, 3, or 4.
+ The best selection is based on a trade off between performance and
+ sound quality.
+
+ Of course, using a main clock frequency that is not exact will affect
+ the pitch of the output. With these numbers, the pitch will be low
+ by 0.127%. (More than likely, an actual unit will vary by this much!) */
+
+#define FREQ_17_EXACT 1789790 /* exact 1.79 MHz clock freq */
+#define FREQ_17_APPROX 1787520 /* approximate 1.79 MHz clock freq */
+
+void Pokey_sound_init (uint32 freq17, uint16 playback_freq);
+void Update_pokey_sound (uint16 addr, uint8 val);
+void Pokey_process_2 (register unsigned char *buffer, register uint16 n);
+void Pokey_process (register unsigned char *buffer, register uint16 n);
+
+#endif
diff --git a/pokeytest/pokey11.txt b/pokeytest/pokey11.txt
new file mode 100644
index 0000000..498d768
--- /dev/null
+++ b/pokeytest/pokey11.txt
@@ -0,0 +1,277 @@
+ Atari POKEY Chip Simulator V1.1
+ ===============================
+ by Ron Fries
+ 23 Sep 96
+
+The POKEY Chip Simulator is designed to emulate the functionality of the
+Atari POKEY Chip Hardware through 'C' Sourcecode. I have seen very good
+results. The simulator is able to produce sounds which are essentially
+identical to the original Atari, including the exact distortions and
+pitches.
+
+The simulator is designed to run in a 32-bit environment. Though it can
+compiled and run in a 16-bit environment, it is slow.
+
+
+Features:
+---------
+
+Version 1.1 of the 'POKEY' simulator supports the following functions:
+
+1) All polynomial sound generators:
+ a) 4-bit poly - actual bit pattern determined from sampled sound
+ b) 5-bit poly - actual bit pattern determined from sampled sound
+ c) 17-bit poly - simulated random bit pattern
+ d) 9-bit poly - derived from simulated 17-bit poly
+
+2) Full support of all 'Divide by N' counter clocks:
+ a) 1.79 MHz (high limited to playback sample rate)
+ b) 64 KHz (high limited to playback sample rate)
+ c) 15 KHz
+
+3) Full support of all 'Divide by N' resolutions:
+ a) 8-bit - single channel
+ b) 16-bit - double channel
+
+4) Full support of all distortions
+ a) 5-bit poly, then 17-bit poly
+ b) 5-bit poly only
+ c) 5-bit poly, then 4-bit poly
+ d) 17-bit poly only
+ e) no poly counters (pure tone)
+ f) 5-bit poly only
+
+5) Full support of volume control
+
+6) Full support of all pitches - distortions will vary exactly as the
+ original Atari based on different pitches
+
+7) Accurate pitch generation
+
+8) Support of any playback sample rate (e.g. 22050)
+
+
+The 'POKEY' simulator does not currently support the following functions:
+
+1) High pass filters
+
+
+Though I don't believe adding support for the High-Pass filters is very
+complicated, I decided not to add support right now because I don't
+believe this feature is used much. I'm also not sure how much impact it
+would have on performance.
+
+You'll notice in the code there are two separate versions. The original
+process function, now called Pokey_process_2(), is the non-optimized
+version. I've left it in for reference. The other function,
+Pokey_process(), is optimized. In my tests using the 'Maximize Speed'
+option of the compiler, I've seen very good performance. On my 486DX2-66,
+it typically takes about 0.15 seconds to produce about 3 seconds of audio.
+These times were calculated under WIN95.
+
+One of the unique features of the optimized version is that the processing
+time will vary based on the frequency. Since the routine only calculates
+new output values when a change is sensed, the lower frequencies (which
+change less frequently) will require less processing time.
+
+
+Differences Between the Simulator and the Actual POKEY Chip:
+------------------------------------------------------------
+
+The biggest difference between the simulator and the original hardware is
+that the simulator emulates an 'ideal' POKEY chip. All output from the
+simulator is a based on a precise square wave, whereas the output from the
+original chip has decay. Though the output is slightly different, I
+don't believe this difference is easily discernible.
+
+Another slight difference is the 17-bit/9-bit poly. Since the polynomial
+is large (2^17 bits), I choose to create the sample using a random number
+generator rather than a table. I don't believe this difference is
+significant.
+
+There are also a few differences which are introduced by aliasing. This is
+a direct result of using an output sampling rate which is not identical to
+the original sound rate. It is most evident with high frequencies.
+
+A final difference is the lack of support for the High-Pass Filter
+functionality. I plan to add this in a future release if necessary.
+
+
+Sample/Test Application:
+------------------------
+
+The test program I've distributed is a 16-bit DOS application created with
+the Borland 'C' compiler. The only reason I used 16-bit was because I
+already had a set of working SB drivers in 16-bit. Since the test system
+is dedicated to generating sounds, the performance in 16-bit is more than
+adequate.
+
+
+POKEY11.C
+==========
+
+The POKEY11.C file is the heart of the POKEY Sound Emulation program.
+Although the routines in the file must work together, no other files are
+modules are required for operation. A header file, 'POKEY11.H', has
+been included for use in other modules, and provides the necessary
+function prototypes. I've attempted to make the routines as portable as
+possible, so the file should compile on almost any compiler with little
+or no modification.
+
+I have made some attempts at optimizing the routines, though I am sure
+more optimization can be done. They are currently only available in 'C'.
+I'll be happy to convert them to assembly language if desired. Please feel
+free to send me e-mail at rfries@tcmail.frco.com.
+
+The routines are easy to use. Detailed descriptions on the function calls
+are listed below.
+
+The POKEY11.C module can be compiled in a 32-bit or 16-bit environment.
+Since these routines are optimized for 32-bit use, the code will default
+to 32-bit. To compile in 16-bits, use a command line option to define
+the variable COMP16.
+
+
+GENERAL OVERVIEW
+----------------
+
+On start-up of the system, a single call should be made to Pokey_sound_init.
+This routine will prepare the structures for sound output. This routine
+can be called again if necessary during warm-start or other reset.
+
+Once in the main loop, there are two other functions that will be used.
+Whenever the system needs to write to either the AUDC or AUDF values,
+a call should be made to the Update_pokey_sound routine. This routine will
+take care of updating the internal registers. It will pre-calculate several
+values to help with optimization.
+
+The only other routine that is called is the Pokey_process function. This
+function will fill a audio buffer with a specified number of bytes. This
+function should be called whenever a new audio buffer is required.
+
+For best results, I recommend using at least two output buffers. Using this
+scheme, the sound card can be playing one buffer while the system is filling
+the other.
+
+
+DETAILED FUNCTION DESCRIPTIONS
+------------------------------
+
+Pokey_sound_init(uint32 freq17, uint16 playback_freq)
+--------------------------------------------------------
+
+This function initializes the structures used by the PokeySound routines.
+This function takes two parameters: the main clock frequency and the
+playback frequency.
+
+The main clock frequency is the frequency of the 1.79MHz source clock.
+To provide exact results, freq17 should be set equal to 1789790 Hz. As an
+alternative, freq17 can be set to an approximate frequency of 1787520 Hz.
+Using this approximate frequency will reduce aliasing and thus produce a
+clearer output signal.
+
+A constant has been defined for both of these values for your convenience.
+The names are FREQ_17_EXACT and FREQ_17_APPROX.
+
+The playback frequency is the frequency of the sound playback (the frequency
+used by the sound card). For best results, the playback frequency should
+be an even division of the main clock frequency. Since most of the sounds
+will be generated using the 64kHz clock, I also recommend making the
+playback frequency an even division of the 64kHz clock.
+
+The 64kHz clock is exactly equal to the main clock divided by 28. For
+the playback frequency, I recommend one of the following values:
+
+1) FREQ_17_APPROX / (28*1), which is equal to 63840. Of course, most sound
+ cards can't reproduce this frequency.
+
+2) FREQ_17_APPROX / (28*2), which is equal to 31920. All of the newer cards
+ will support this frequency.
+
+3) FREQ_17_APPROX / (28*3), which is equal to 21280. All of the SB
+ compatibles should support this frequency.
+
+4) FREQ_17_APPROX / (28*4), which is equal to 15960. This may be the
+ best choice, as it offers good sound reproduction with good performance.
+
+Of course, these options also assume you are using the approximate
+frequency for the main clock as well. Any of these choices will offer the
+best results when the main 64kHz clock is used, reasonable results when the
+15kHz clock is selected, and marginal results when the 1.79MHz clock is
+selected (the only way to produce good results in all cases is to set the
+playback frequency to 1.79MHz!)
+
+Feel free to experiment to find other alternatives as well.
+
+This function has no return value (void).
+
+
+Update_pokey_sound (uint16 addr, uint8 val)
+-----------------------------------------
+
+This function should be called each time an AUDC, AUDF or AUDCTL value
+changes. This function takes two parameters: the address to change and
+the new value. The address should be one of the following values:
+
+ Addr Description
+ ------ -----------
+ 0xd200 AUDF1
+ 0xd201 AUDC1
+ 0xd202 AUDF2
+ 0xd203 AUDC2
+ 0xd204 AUDF3
+ 0xd205 AUDC3
+ 0xd206 AUDF4
+ 0xd207 AUDC4
+ 0xd208 AUDCTL
+
+Any values outside of this range will be ignored.
+
+The routine pre-calculates several values that are needed by the
+processing function. This is done to optimize performance.
+
+This function has no return value (void).
+
+
+Pokey_process (unsigned char *buffer, uint16 n)
+---------------------------------------------
+
+This function calculates and fills a buffer with unsigned 8-bit mono audio.
+This function takes two parameters: a pointer to the buffer to fill and
+the size of the buffer (limited to 65535). This function fills the
+buffer based on the requested size and returns. It automatically
+updates the pointers for the next call, so subsequent calls to this function
+will provide a continuous stream of data.
+
+The size of the buffer that is needed depends on the playback frequency.
+It is best to keep the buffer as small as possible to maximize response time
+to changes in the sound. Of course, the minimum size is dependent on
+system and emulator performance.
+
+Selecting the correct buffer size is a careful balance. Selecting a buffer
+size that is too small will produce noticeable clicks in the output, though
+selecting a size that is too large will cause a poor response time and
+possible delays in the system when the new buffer is filled.
+
+This function has no return value (void).
+
+
+License Information and Copyright Notice
+========================================
+
+PokeySound is Copyright(c) 1996 by Ron Fries
+
+This library is free software; you can redistribute it and/or modify it under
+the terms of version 2 of the GNU Library General Public License as published
+by the Free Software Foundation.
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more
+details.
+
+To obtain a copy of the GNU Library General Public License, write to the Free
+Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Any permitted reproduction of these routines, in whole or in part, must bear
+this legend.
diff --git a/pokeytest/pokey11_readme.txt b/pokeytest/pokey11_readme.txt
new file mode 100644
index 0000000..31eac81
--- /dev/null
+++ b/pokeytest/pokey11_readme.txt
@@ -0,0 +1,119 @@
+ ATARI 800 - POKEY SOUND EMULATION LIBRARY V1.1
+ ==============================================
+ September 23, 1996
+
+The Pokey Sound Emulation Library consists of source code and example
+executables that are designed to emulate the sound output characteristics
+of the Atari 800 Pokey chip. Contained in this distribution, you should
+also have the following files:
+
+
+README.TXT - This file
+FILE_ID.DIZ - A quick description of the release.
+SOUND.C - The SB Driver Source Code.
+SOUND.H - The SB Driver Header File which contains the function
+ prototypes.
+SOUND.TXT - A text file which explains the operation of the SB Driver
+ module.
+
+POKEY11.C - The Pokey Sound Emulation Source Code.
+POKEY11.H - The Pokey Sound Emulation Header File which contains the
+ function prototypes.
+POKEY11.TXT - A text file which explains the operation of the Pokey Sound
+ Emulation module.
+SNDTEST.C - The Main Test routine Source Code. Provided as an example
+ of how the routines can be used.
+SNDTEST.EXE - An pre-compiled executable based on the code provided.
+
+
+These routines are available for your use free of charge, provided you give
+me proper recognition, and also provided you don't make a profit off of my
+work. See the GNU Library Public License for more information.
+
+If the final product you are creating will be sold for a profit, please
+contact me at rfries@frmail.frco.com so we can discuss terms.
+
+
+THE LIBRARY
+-----------
+
+The Pokey Sound Emulation software consists of two main files. The
+functions have been divided into these two files depending on your needs.
+If you only need the sound generation routines, the only file you'll need
+to compile and link is the POKEY11.C file. If you need full SB drivers as
+well, then link both the POKEY11.C and the SOUND.C files into your
+application.
+
+The POKEY11.C file was designed to be as portable as possible. You should
+be able to use the file with little or no modification on any 'C' compiler.
+It is currently only available in 'C', though I'll be happy to convert the
+file to Assembly Language if necessary. For best results, this file should
+be compiled in a 32-bit environment with the 'maximize speed' option.
+
+The SOUND.C file was designed to work with the Borland 'C' compiler. It
+may also work directly with other compilers, though some modification may
+be required. The SB driver routines in this file have several limitations
+which are described in detail in the SOUND.TXT file. NOTE: The SB driver
+routines are written in 16-bit.
+
+The POKEY11.C routines should be very stable, though I'm not as confident
+with the SB routines. They work correctly on my system, though I offer no
+guarantees.
+
+Depending on your needs, you will use one of the following algorithms:
+
+
+1) POKEY11.C only - in this case, it is assumed you already have audio
+ driver routines
+
+ The general design is as follows:
+
+ a) During startup, make a single call to Pokey_sound_init()
+ b) Whenever an AUDC or AUDF value changes, call Update_pokey_sound()
+ c) When a new buffer needs to be filled, call Pokey_process()
+
+
+2) POKEY11.C and SOUND.C - using the SB driver routines I've provided
+ (note: the SB driver routines are written for a 16-bit compiler)
+
+ The general design is as follows:
+
+ a) During startup, make a single call to OpenSB()
+ b) During startup, make a single call to Pokey_sound_init()
+ c) Whenever an AUDC or AUDF value changes, call Update_pokey_sound()
+ d) Periodically call FillBuffer()
+ e) During shutdown, make a single call to CloseSB()
+
+
+Each function call has its own set of parameters. Check with the
+POKEY11.TXT and SOUND.TXT files for more information.
+
+The SNDTEST.C file has also been provided as an example. I wrote it mainly
+for my testing, but it should give you a little insight on how to correctly
+use the routines. You can also run the SNDTEST.EXE to play with the sounds.
+
+Since the SNDTEST.EXE program uses my SOUND SB driver routines, it is subject
+to the same limitations. It only supports IRQs 0-7 and DMAs 0-3. It also
+only supports low-speed audio output (max freq is approx. 22KHz), and requires
+that you have the BLASTER environment variable configured.
+
+
+License Information and Copyright Notice
+========================================
+
+PokeySound is Copyright(c) 1996 by Ron Fries
+
+This library is free software; you can redistribute it and/or modify it under
+the terms of version 2 of the GNU Library General Public License as published
+by the Free Software Foundation.
+
+This library is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more
+details.
+
+To obtain a copy of the GNU Library General Public License, write to the Free
+Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Any permitted reproduction of these routines, in whole or in part, must bear
+this legend.
diff --git a/pokeytest/readme.txt b/pokeytest/readme.txt
new file mode 100644
index 0000000..be49295
--- /dev/null
+++ b/pokeytest/readme.txt
@@ -0,0 +1,25 @@
+
+This is an attempt at a sound player for Jumpman Junior, using SDL2 and
+the pokey sound library by Ron Fries.
+
+Usage: ./playsdl2 [-p] addr
+
+Default is to play at NTSC speed, 60 frames/sec. -p means play at PAL
+speed, 50 frames/sec.
+
+addr is a 4-digit hex address from the ROM (../jumpmanjr.rom). Look for
+labels beginning with sfx_ in ../jumpmanjr.dasm. Don't use a leading $,
+although a leading 0x is allowed (but not needed).
+
+Right now this only plays one sound, so you can't play all the parts to
+the 2- and 4-part harmony music yet. Also, parts that repeat will repeat
+forever (press ^C in that case). Eventually this will evolve into the
+sound engine for the SDL2 source port of Jumpman Junior, meaning it'll
+handle up to 4 voices, with priority levels, etc (same as the original).
+
+I tried to use the later 2.3 version of the pokey library, both the
+standalone one and the modified from from the atari800 emulator, but
+couldn't get either to work (no doubt due to my own ignorance). So
+this uses version 1.1 of the library, which should be fine for this
+game since we don't need support for things like volume-only sound or
+high-pass filters.