Merge branch 'mockingboard'

Conflicts:
	src/cpu.S
	src/misc.c
This commit is contained in:
Aaron Culliney 2013-11-12 23:45:05 -08:00
commit 679d119486
30 changed files with 25890 additions and 272 deletions

1041
src/AY8910.c Normal file

File diff suppressed because it is too large Load Diff

100
src/AY8910.h Normal file
View File

@ -0,0 +1,100 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 2 or later (your choice) as published by the Free Software
* Foundation.
*
* THERE ARE NO WARRANTIES WHATSOEVER.
*
*/
#ifndef AY8910_H
#define AY8910_H
#define MAX_8910 4
//-------------------------------------
// MAME interface
void _AYWriteReg(int chip, int r, int v);
//void AY8910_write_ym(int chip, int addr, int data);
void AY8910_reset(int chip);
void AY8910Update(int chip, INT16** buffer, int nNumSamples);
void AY8910_InitAll(int nClock, int nSampleRate);
void AY8910_InitClock(int nClock);
BYTE* AY8910_GetRegsPtr(UINT uChip);
void AY8910UpdateSetCycles();
//-------------------------------------
// FUSE stuff
typedef ULONG libspectrum_dword;
typedef UCHAR libspectrum_byte;
typedef SHORT libspectrum_signed_word;
struct CAY8910;
/* max. number of sub-frame AY port writes allowed;
* given the number of port writes theoretically possible in a
* 50th I think this should be plenty.
*/
#define AY_CHANGE_MAX 8000
//class CAY8910
//{
//public:
void CAY8910_init(struct CAY8910 *_this);
void sound_ay_init(struct CAY8910 *_this);
void sound_init(struct CAY8910 *_this, const char *device);
void sound_ay_write(struct CAY8910 *_this, int reg, int val, libspectrum_dword now);
void sound_ay_reset(struct CAY8910 *_this);
void sound_frame(struct CAY8910 *_this);
BYTE* GetAYRegsPtr(struct CAY8910 *_this);
void SetCLK(double CLK);
//private:
// void sound_end(struct CAY8910 *_this, void);
// void sound_ay_overlay(struct CAY8910 *_this, void);
typedef struct ay_change_tag
{
libspectrum_dword tstates;
unsigned short ofs;
unsigned char reg, val;
} ay_change_tag;
//private:
typedef struct CAY8910
{
/* foo_subcycles are fixed-point with low 16 bits as fractional part.
* The other bits count as the chip does.
*/
unsigned int ay_tone_tick[3], ay_tone_high[3], ay_noise_tick;
unsigned int ay_tone_subcycles, ay_env_subcycles;
unsigned int ay_env_internal_tick, ay_env_tick;
unsigned int ay_tick_incr;
unsigned int ay_tone_period[3], ay_noise_period, ay_env_period;
//static int beeper_last_subpos[2] = { 0, 0 };
/* Local copy of the AY registers */
libspectrum_byte sound_ay_registers[16];
ay_change_tag ay_change[ AY_CHANGE_MAX ];
int ay_change_count;
// statics from sound_ay_overlay()
int rng;
int noise_toggle;
int env_first, env_rev, env_counter;
} CAY8910;
// Vars shared between all AY's
extern double m_fCurrentCLK_AY8910;
#endif

View File

@ -61,40 +61,26 @@ CC = gcc
DEBUGGER_O =
JOYSTICK_O = joystick.o
PACKAGE = apple2-emul
PROGS = xapple2 xapple2-80col
PROGS = xapple2-80col
VERSION = 0.7.4
noinst_HEADERS = apple2.h debug.h disk.h interface.h keys.h misc.h video.h cpu.h glue.h gluepro.h prefs.h timing.h
noinst_HEADERS = apple2.h debug.h disk.h interface.h keys.h misc.h video.h cpu.h glue.h glue-prologue.h prefs.h timing.h
SUFFIXES = .l -80.o
man_MANS = apple2.6
EXTRA_PROGRAMS = apple2 xapple2 xapple2-80col
bin_PROGRAMS = xapple2 xapple2-80col
EXTRA_apple2_SOURCES = debugger.c opcodes.c debug.c joystick.c
apple2_SOURCES = cpu.S memory.S display.S glue.S keys.c prefs.c disk.c interface.c misc.c soundcore.c soundcore-alsa.c speaker.c font.c svideo.c cpu-supp.c vidsup.c timing.c
EXTRA_PROGRAMS = xapple2-80col
bin_PROGRAMS = xapple2-80col
apple2_LDADD = joystick.o -lvga
apple2_DEPENDENCIES = joystick.o
EXTRA_xapple2_SOURCES = debugger.c opcodes.c debug.c joystick.c
xapple2_SOURCES = cpu.S memory.S display.S glue.S keys.c prefs.c disk.c interface.c misc.c soundcore.c soundcore-alsa.c speaker.c font.c xvideo.c cpu-supp.c vidsup.c timing.c
xapple2_LDADD = joystick.o -L/usr/lib/i386-linux-gnu -lX11 -lXext -lrt -lasound
xapple2_DEPENDENCIES = joystick.o
EXTRA_xapple2_80col_SOURCES = debugger.c opcodes.c debug.c joystick.c
xapple2_80col_SOURCES = cpu.S memory.S glue.S keys.c prefs.c disk.c font.c cpu-supp.c misc.c soundcore.c soundcore-alsa.c speaker.c interface.c timing.c
xapple2_80col_SOURCES = cpu.S memory.S glue.S keys.c prefs.c disk.c font.c cpu-supp.c misc.c win-shim.c soundcore.c soundcore-openal.c alhelpers.c speaker.c AY8910.c mockingboard.c interface.c timing.c
xapple2_80col_LDADD = joystick.o vidsup-80.o xvideo-80.o display-80.o -L/usr/lib/i386-linux-gnu -lX11 -lXext -lrt -lasound
xapple2_80col_LDADD = joystick.o vidsup-80.o xvideo-80.o display-80.o -L/usr/lib/i386-linux-gnu -lX11 -lXext -lrt -lopenal -lpthread
xapple2_80col_DEPENDENCIES = joystick.o vidsup-80.o xvideo-80.o display-80.o
@ -119,11 +105,7 @@ X_CFLAGS =
X_LIBS =
X_EXTRA_LIBS =
X_PRE_LIBS = -lSM -lICE
apple2_OBJECTS = cpu.o memory.o display.o glue.o keys.o prefs.o disk.o debugger.o opcodes.o debug.o interface.o misc.o soundcore.o soundcore-alsa.o speaker.o font.o svideo.o cpu-supp.o vidsup.o timing.o
apple2_LDFLAGS =
xapple2_OBJECTS = cpu.o memory.o display.o glue.o keys.o prefs.o disk.o debugger.o opcodes.o debug.o interface.o misc.o soundcore.o soundcore-alsa.o speaker.o font.o xvideo.o cpu-supp.o vidsup.o timing.o
xapple2_LDFLAGS =
xapple2_80col_OBJECTS = cpu.o memory.o glue.o keys.o prefs.o disk.o debugger.o opcodes.o debug.o font.o cpu-supp.o misc.o soundcore.o soundcore-alsa.o speaker.o interface.o timing.o
xapple2_80col_OBJECTS = cpu.o memory.o glue.o keys.o prefs.o disk.o debugger.o opcodes.o debug.o font.o cpu-supp.o misc.o AY8910.o mockingboard.o win-shim.o soundcore.o soundcore-openal.o alhelpers.o speaker.o interface.o timing.o
xapple2_80col_LDFLAGS =
genfont_SOURCES = genfont.c
genfont_OBJECTS = genfont.o
@ -393,7 +375,7 @@ mostlyclean distclean maintainer-clean
font.c : font.txt genfont
./genfont < $< > $@
glue.S : disk.c misc.c
glue.S : disk.c misc.c mockingboard.c
$(srcdir)/genglue $^ > $@
%-80.o: %.c

19620
src/SSI263Phonemes.h Normal file

File diff suppressed because it is too large Load Diff

380
src/alhelpers.c Normal file
View File

@ -0,0 +1,380 @@
/*
* OpenAL Helpers
*
* Copyright (c) 2011 by Chris Robinson <chris.kcat@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* This file contains routines to help with some menial OpenAL-related tasks,
* such as opening a device and setting up a context, closing the device and
* destroying its context, converting between frame counts and byte lengths,
* finding an appropriate buffer format, and getting readable strings for
* channel configs and sample types. */
#include <stdio.h>
#include "AL/al.h"
#include "AL/alc.h"
#include "AL/alext.h"
#include "alhelpers.h"
#include "common.h"
/* InitAL opens the default device and sets up a context using default
* attributes, making the program ready to call OpenAL functions. */
ALCcontext* InitAL(void)
{
ALCdevice *device;
ALCcontext *ctx;
/* Open and initialize a device with default settings */
device = alcOpenDevice(NULL);
if(!device)
{
ERRLOG("Could not open a device!");
return NULL;
}
ctx = alcCreateContext(device, NULL);
if(ctx == NULL || alcMakeContextCurrent(ctx) == ALC_FALSE)
{
if(ctx != NULL)
{
alcDestroyContext(ctx);
}
alcCloseDevice(device);
ERRLOG("Could not set a context!");
return NULL;
}
LOG("Opened \"%s\"", alcGetString(device, ALC_DEVICE_SPECIFIER));
return ctx;
}
/* CloseAL closes the device belonging to the current context, and destroys the
* context. */
void CloseAL(void)
{
ALCdevice *device;
ALCcontext *ctx;
ctx = alcGetCurrentContext();
if(ctx == NULL)
{
return;
}
device = alcGetContextsDevice(ctx);
alcMakeContextCurrent(NULL);
alcDestroyContext(ctx);
alcCloseDevice(device);
}
/* GetFormat retrieves a compatible buffer format given the channel config and
* sample type. If an alIsBufferFormatSupportedSOFT-compatible function is
* provided, it will be called to find the closest-matching format from
* AL_SOFT_buffer_samples. Returns AL_NONE (0) if no supported format can be
* found. */
ALenum GetFormat(ALenum channels, ALenum type, LPALISBUFFERFORMATSUPPORTEDSOFT palIsBufferFormatSupportedSOFT)
{
ALenum format = AL_NONE;
/* If using AL_SOFT_buffer_samples, try looking through its formats */
if(palIsBufferFormatSupportedSOFT)
{
/* AL_SOFT_buffer_samples is more lenient with matching formats. The
* specified sample type does not need to match the returned format,
* but it is nice to try to get something close. */
if(type == AL_UNSIGNED_BYTE_SOFT || type == AL_BYTE_SOFT)
{
if(channels == AL_MONO_SOFT) format = AL_MONO8_SOFT;
else if(channels == AL_STEREO_SOFT) format = AL_STEREO8_SOFT;
else if(channels == AL_QUAD_SOFT) format = AL_QUAD8_SOFT;
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_8_SOFT;
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_8_SOFT;
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_8_SOFT;
}
else if(type == AL_UNSIGNED_SHORT_SOFT || type == AL_SHORT_SOFT)
{
if(channels == AL_MONO_SOFT) format = AL_MONO16_SOFT;
else if(channels == AL_STEREO_SOFT) format = AL_STEREO16_SOFT;
else if(channels == AL_QUAD_SOFT) format = AL_QUAD16_SOFT;
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_16_SOFT;
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_16_SOFT;
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_16_SOFT;
}
else if(type == AL_UNSIGNED_BYTE3_SOFT || type == AL_BYTE3_SOFT ||
type == AL_UNSIGNED_INT_SOFT || type == AL_INT_SOFT ||
type == AL_FLOAT_SOFT || type == AL_DOUBLE_SOFT)
{
if(channels == AL_MONO_SOFT) format = AL_MONO32F_SOFT;
else if(channels == AL_STEREO_SOFT) format = AL_STEREO32F_SOFT;
else if(channels == AL_QUAD_SOFT) format = AL_QUAD32F_SOFT;
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_32F_SOFT;
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_32F_SOFT;
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_32F_SOFT;
}
if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format))
format = AL_NONE;
/* A matching format was not found or supported. Try 32-bit float. */
if(format == AL_NONE)
{
if(channels == AL_MONO_SOFT) format = AL_MONO32F_SOFT;
else if(channels == AL_STEREO_SOFT) format = AL_STEREO32F_SOFT;
else if(channels == AL_QUAD_SOFT) format = AL_QUAD32F_SOFT;
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_32F_SOFT;
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_32F_SOFT;
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_32F_SOFT;
if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format))
format = AL_NONE;
}
/* 32-bit float not supported. Try 16-bit int. */
if(format == AL_NONE)
{
if(channels == AL_MONO_SOFT) format = AL_MONO16_SOFT;
else if(channels == AL_STEREO_SOFT) format = AL_STEREO16_SOFT;
else if(channels == AL_QUAD_SOFT) format = AL_QUAD16_SOFT;
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_16_SOFT;
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_16_SOFT;
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_16_SOFT;
if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format))
format = AL_NONE;
}
/* 16-bit int not supported. Try 8-bit int. */
if(format == AL_NONE)
{
if(channels == AL_MONO_SOFT) format = AL_MONO8_SOFT;
else if(channels == AL_STEREO_SOFT) format = AL_STEREO8_SOFT;
else if(channels == AL_QUAD_SOFT) format = AL_QUAD8_SOFT;
else if(channels == AL_5POINT1_SOFT) format = AL_5POINT1_8_SOFT;
else if(channels == AL_6POINT1_SOFT) format = AL_6POINT1_8_SOFT;
else if(channels == AL_7POINT1_SOFT) format = AL_7POINT1_8_SOFT;
if(format != AL_NONE && !palIsBufferFormatSupportedSOFT(format))
format = AL_NONE;
}
return format;
}
/* We use the AL_EXT_MCFORMATS extension to provide output of Quad, 5.1,
* and 7.1 channel configs, AL_EXT_FLOAT32 for 32-bit float samples, and
* AL_EXT_DOUBLE for 64-bit float samples. */
if(type == AL_UNSIGNED_BYTE_SOFT)
{
if(channels == AL_MONO_SOFT)
format = AL_FORMAT_MONO8;
else if(channels == AL_STEREO_SOFT)
format = AL_FORMAT_STEREO8;
else if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
{
if(channels == AL_QUAD_SOFT)
format = alGetEnumValue("AL_FORMAT_QUAD8");
else if(channels == AL_5POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_51CHN8");
else if(channels == AL_6POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_61CHN8");
else if(channels == AL_7POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_71CHN8");
}
}
else if(type == AL_SHORT_SOFT)
{
if(channels == AL_MONO_SOFT)
format = AL_FORMAT_MONO16;
else if(channels == AL_STEREO_SOFT)
format = AL_FORMAT_STEREO16;
else if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
{
if(channels == AL_QUAD_SOFT)
format = alGetEnumValue("AL_FORMAT_QUAD16");
else if(channels == AL_5POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_51CHN16");
else if(channels == AL_6POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_61CHN16");
else if(channels == AL_7POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_71CHN16");
}
}
else if(type == AL_FLOAT_SOFT && alIsExtensionPresent("AL_EXT_FLOAT32"))
{
if(channels == AL_MONO_SOFT)
format = alGetEnumValue("AL_FORMAT_MONO_FLOAT32");
else if(channels == AL_STEREO_SOFT)
format = alGetEnumValue("AL_FORMAT_STEREO_FLOAT32");
else if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
{
if(channels == AL_QUAD_SOFT)
format = alGetEnumValue("AL_FORMAT_QUAD32");
else if(channels == AL_5POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_51CHN32");
else if(channels == AL_6POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_61CHN32");
else if(channels == AL_7POINT1_SOFT)
format = alGetEnumValue("AL_FORMAT_71CHN32");
}
}
else if(type == AL_DOUBLE_SOFT && alIsExtensionPresent("AL_EXT_DOUBLE"))
{
if(channels == AL_MONO_SOFT)
format = alGetEnumValue("AL_FORMAT_MONO_DOUBLE");
else if(channels == AL_STEREO_SOFT)
format = alGetEnumValue("AL_FORMAT_STEREO_DOUBLE");
}
/* NOTE: It seems OSX returns -1 from alGetEnumValue for unknown enums, as
* opposed to 0. Correct it. */
if(format == -1)
format = 0;
return format;
}
void AL_APIENTRY wrap_BufferSamples(ALuint buffer, ALuint samplerate,
ALenum internalformat, ALsizei samples,
ALenum channels, ALenum type,
const ALvoid *data)
{
alBufferData(buffer, internalformat, data,
FramesToBytes(samples, channels, type),
samplerate);
}
const char *ChannelsName(ALenum chans)
{
switch(chans)
{
case AL_MONO_SOFT: return "Mono";
case AL_STEREO_SOFT: return "Stereo";
case AL_REAR_SOFT: return "Rear";
case AL_QUAD_SOFT: return "Quadraphonic";
case AL_5POINT1_SOFT: return "5.1 Surround";
case AL_6POINT1_SOFT: return "6.1 Surround";
case AL_7POINT1_SOFT: return "7.1 Surround";
}
return "Unknown Channels";
}
const char *TypeName(ALenum type)
{
switch(type)
{
case AL_BYTE_SOFT: return "S8";
case AL_UNSIGNED_BYTE_SOFT: return "U8";
case AL_SHORT_SOFT: return "S16";
case AL_UNSIGNED_SHORT_SOFT: return "U16";
case AL_INT_SOFT: return "S32";
case AL_UNSIGNED_INT_SOFT: return "U32";
case AL_FLOAT_SOFT: return "Float32";
case AL_DOUBLE_SOFT: return "Float64";
}
return "Unknown Type";
}
const char *FormatName(ALenum format)
{
switch (format)
{
case AL_FORMAT_MONO8: return "MONO8";
case AL_FORMAT_MONO16: return "MONO16";
case AL_FORMAT_STEREO8: return "STEREO8";
case AL_FORMAT_STEREO16: return "STEREO16";
case AL_FORMAT_QUAD8: return "QUAD8";
case AL_FORMAT_QUAD16: return "QUAD16";
case AL_FORMAT_QUAD32: return "QUAD32";
case AL_FORMAT_REAR8: return "REAR8";
case AL_FORMAT_REAR16: return "REAR16";
case AL_FORMAT_REAR32: return "REAR32";
case AL_FORMAT_51CHN8: return "51CHN8";
case AL_FORMAT_51CHN16: return "51CHN16";
case AL_FORMAT_51CHN32: return "51CHN32";
case AL_FORMAT_61CHN8: return "61CHN8";
case AL_FORMAT_61CHN16: return "61CHN16";
case AL_FORMAT_61CHN32: return "61CHN32";
case AL_FORMAT_71CHN8: return "71CHN8";
case AL_FORMAT_71CHN16: return "71CHN16";
case AL_FORMAT_71CHN32: return "71CHN32";
case AL_FORMAT_WAVE_EXT: return "WAVE_EXT";
case AL_FORMAT_IMA_ADPCM_MONO16_EXT: return "IMA_ADPCM_MONO16_EXT";
case AL_FORMAT_IMA_ADPCM_STEREO16_EXT: return "IMA_ADPCM_STEREO16_EXT";
case AL_FORMAT_VORBIS_EXT: return "VORBIS_EXT";
case AL_FORMAT_MONO_FLOAT32: return "MONO_FLOAT32";
case AL_FORMAT_STEREO_FLOAT32: return "STEREO_FLOAT32";
case AL_FORMAT_MONO_DOUBLE_EXT: return "MONO_DOUBLE_EXT";
case AL_FORMAT_STEREO_DOUBLE_EXT: return "STEREO_DOUBLE_EXT";
case AL_FORMAT_MONO_MULAW_EXT: return "MONO_MULAW(_EXT)";
//case AL_FORMAT_MONO_MULAW: return "MONO_MULAW";
case AL_FORMAT_STEREO_MULAW_EXT: return "STEREO_MULAW(_EXT)";
//case AL_FORMAT_STEREO_MULAW: return "STEREO_MULAW";
case AL_FORMAT_QUAD8_LOKI: return "QUAD8_LOKI";
case AL_FORMAT_QUAD16_LOKI: return "QUAD16_LOKI";
case AL_FORMAT_QUAD_MULAW: return "QUAD_MULAW";
case AL_FORMAT_REAR_MULAW: return "REAR_MULAW";
case AL_FORMAT_51CHN_MULAW: return "51CHN_MULAW";
case AL_FORMAT_61CHN_MULAW: return "61CHN_MULAW";
case AL_FORMAT_71CHN_MULAW: return "71CHN_MULAW";
case AL_FORMAT_MONO_IMA4: return "MONO_IMA4";
case AL_FORMAT_STEREO_IMA4: return "STEREO_IMA4";
}
return "Unknown Format";
}
ALsizei FramesToBytes(ALsizei size, ALenum channels, ALenum type)
{
switch(channels)
{
case AL_MONO_SOFT: size *= 1; break;
case AL_STEREO_SOFT: size *= 2; break;
case AL_REAR_SOFT: size *= 2; break;
case AL_QUAD_SOFT: size *= 4; break;
case AL_5POINT1_SOFT: size *= 6; break;
case AL_6POINT1_SOFT: size *= 7; break;
case AL_7POINT1_SOFT: size *= 8; break;
}
switch(type)
{
case AL_BYTE_SOFT: size *= sizeof(ALbyte); break;
case AL_UNSIGNED_BYTE_SOFT: size *= sizeof(ALubyte); break;
case AL_SHORT_SOFT: size *= sizeof(ALshort); break;
case AL_UNSIGNED_SHORT_SOFT: size *= sizeof(ALushort); break;
case AL_INT_SOFT: size *= sizeof(ALint); break;
case AL_UNSIGNED_INT_SOFT: size *= sizeof(ALuint); break;
case AL_FLOAT_SOFT: size *= sizeof(ALfloat); break;
case AL_DOUBLE_SOFT: size *= sizeof(ALdouble); break;
}
return size;
}
ALsizei BytesToFrames(ALsizei size, ALenum channels, ALenum type)
{
return size / FramesToBytes(1, channels, type);
}

47
src/alhelpers.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef ALHELPERS_H
#define ALHELPERS_H
#ifndef _WIN32
#include <unistd.h>
#else
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Some helper functions to get the name from the channel and type enums. */
const char *ChannelsName(ALenum chans);
const char *TypeName(ALenum type);
const char *FormatName(ALenum format);
/* Helpers to convert frame counts and byte lengths. */
ALsizei FramesToBytes(ALsizei size, ALenum channels, ALenum type);
ALsizei BytesToFrames(ALsizei size, ALenum channels, ALenum type);
/* Retrieves a compatible buffer format given the channel configuration and
* sample type. If an alIsBufferFormatSupportedSOFT-compatible function is
* provided, it will be called to find the closest-matching format from
* AL_SOFT_buffer_samples. Returns AL_NONE (0) if no supported format can be
* found. */
ALenum GetFormat(ALenum channels, ALenum type, LPALISBUFFERFORMATSUPPORTEDSOFT palIsBufferFormatSupportedSOFT);
/* Loads samples into a buffer using the standard alBufferData call, but with a
* LPALBUFFERSAMPLESSOFT-compatible prototype. Assumes internalformat is valid
* for alBufferData, and that channels and type match it. */
void AL_APIENTRY wrap_BufferSamples(ALuint buffer, ALuint samplerate,
ALenum internalformat, ALsizei samples,
ALenum channels, ALenum type,
const ALvoid *data);
/* Easy device init/deinit functions. */
ALCcontext* InitAL(void);
void CloseAL(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* ALHELPERS_H */

View File

@ -12,7 +12,7 @@
#ifndef _COMMON_H_
#define _COMMON_H_
#if defined(__GNUC__)
#if defined(__GNUC__) && !defined(_GNU_SOURCE)
# define _GNU_SOURCE
#endif

View File

@ -14,26 +14,33 @@
*
*/
#include <string.h>
#include <stdlib.h>
#include "common.h"
#include "cpu.h"
#include "mockingboard.h"
/* different than in defs.h! */
#define C_Flag_6502 0x1
#define X_Flag_6502 0x20
#define I_Flag_6502 0x4
#define V_Flag_6502 0x40
#define B_Flag_6502 0x10
#define D_Flag_6502 0x8
#define Z_Flag_6502 0x2
#define N_Flag_6502 0x80
// These match the bit positions of the 6502 P-register, they are not the same as in cpu.h -- see note there
#define C_Flag_6502 0x1 // [C]arry
#define X_Flag_6502 0x20 // [X]tra (reserved)...
#define I_Flag_6502 0x4 // [I]nterrupt
#define V_Flag_6502 0x40 // o[V]erfly
#define B_Flag_6502 0x10 // [B]reak
#define D_Flag_6502 0x8 // [D]ecimal
#define Z_Flag_6502 0x2 // [Z]ero
#define N_Flag_6502 0x80 // [N]egative
static void initialize_code_tables(void)
struct cpu65_state cpu65_current;
struct cpu65_extra cpu65_debug;
int16_t cpu65_cycle_count;
int16_t cpu65_cycles_to_execute;
uint8_t cpu65__signal;
static pthread_mutex_t irq_mutex = PTHREAD_MUTEX_INITIALIZER;
// NOTE: currently this is a conversion table between i386 flags <-> 6502 P register
static void initialize_code_tables()
{
int i;
for (i = 0; i < 256; i++)
for (unsigned i = 0; i < 256; i++)
{
unsigned char val = 0;
@ -97,11 +104,12 @@ void cpu65_set(int flags)
{
memcpy(cpu65__opcodes,cpu65__nmos,1024);
}
break;
case CPU65_C02:
memcpy(cpu65__opcodes,cpu65__cmos,1024);
break;
default:
abort();
}
@ -111,7 +119,16 @@ void cpu65_set(int flags)
void cpu65_interrupt(int reason)
{
cpu65__signal = reason;
pthread_mutex_lock(&irq_mutex);
cpu65__signal |= reason;
pthread_mutex_unlock(&irq_mutex);
}
void cpu65_uninterrupt(int reason)
{
pthread_mutex_lock(&irq_mutex);
cpu65__signal &= ~reason;
pthread_mutex_unlock(&irq_mutex);
}
void cpu65_set_stepping(int flag)

103
src/cpu.S
View File

@ -25,12 +25,6 @@
.comm SN(cpu65_flags_encode),256
.comm SN(cpu65_flags_decode),256
.comm SN(cpu65__opcodes),1024
.comm SN(cpu65__signal),1
.comm SN(cpu65_do_reboot),1
.comm SN(cpu65_cycle_count),2
.comm SN(cpu65_cycles_to_execute),2
.comm SN(cpu65_debug),7
.comm SN(cpu65_current),7
#define DebugCurrEA SN(cpu65_debug)
#define DebugCurrByte SN(cpu65_debug)+2
@ -114,7 +108,7 @@
movb Y_Reg, SN(cpu65_current)+5; \
movb SP_Reg_L, SN(cpu65_current)+6;
/* Replace CPU state when being called from C.
/* Restore CPU state when being called from C.
*
* The xorls clear the high parts of the registers
* Note: dependent on register assignment
@ -124,7 +118,7 @@
* polluting this module with Apple-specific stuff. But we need to do
* it, else aux-stack using programs will crash when debugged.)
*/
#define ReplaceState \
#define RestoreState \
xorl %eax, %eax; \
xorl %ebx, %ebx; \
xorl %ecx, %ecx; \
@ -836,7 +830,7 @@ op_BRK:
movb SN(cpu65_flags_encode)(,%eax,1), %al
Push(%al)
orb $I_Flag, F_Reg
movw $0xFFFE, EffectiveAddr
movw $0xFFFE, EffectiveAddr // ROM interrupt vector
GetFromEA_W
movw %ax, PC_Reg
Continue
@ -2672,56 +2666,63 @@ continue:
movb DebugCurrOpcode, %al
movb SN(cpu65__opcycles)(,%eax,1), %al
addb DebugCycleCount, %al
addw %ax, SN(cpu65_cycle_count) // TODO: cycle counting is slightly incorrect, it should be done earlier per instruction ...
subw %ax, SN(cpu65_cycles_to_execute) // but in practice this may not matter ...
jle 1f
addw %ax, SN(cpu65_cycle_count) // TODO: cycle counting is slightly incorrect, it should be done earlier per instruction ...
subw %ax, SN(cpu65_cycles_to_execute) // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ orly?
jle exit_cpu65_run
xorl %eax, %eax
orb SN(cpu65__signal), %ah
orb SN(cpu65__signal), %al
jnz exception
JumpNextInstruction
1: SaveState
popal
ret
1: JumpNextInstruction
/* -------------------------------------------------------------------------
Exception handler
Exception handlers
------------------------------------------------------------------------- */
exception: cmpb $RebootSig, %ah
jz ex_reboot
cmpb $ResetSig, %ah
jz ex_reset
jmp ex_step
ex_step: movb $0, SN(cpu65__signal)
xorl %eax, %eax
SaveState
call SN(c_stepping_yield)
ReplaceState
JumpNextInstruction
ex_reboot: movb $0, SN(cpu65__signal) // EXIT CPURUN
movb $1, SN(cpu65_do_reboot)
popal
ret
exception: testb $RebootSig, %al
jnz emul_reinit
testb $ResetSig, %al
jnz ex_reset
testb $DebugStepSig, %al
jnz ex_step
jmp ex_irq
ex_reset: movb $0, SN(cpu65__signal)
movw $0xfffc, EffectiveAddr
movw $0xFFFC, EffectiveAddr // ROM reset vector
GetFromEA_W
movw %ax, PC_Reg
xorb %ah, %ah
JumpNextInstruction
ex_irq: testb $I_Flag, F_Reg // Already interrupted?
jz 1f
JumpNextInstruction // Yes (ignored) ...
1: movw PC_Reg, %ax // No (handle IRQ) ...
Push(%ah)
Push(%al)
orb $X_Flag, F_Reg
xorl %eax,%eax
movb F_Reg, %al
movb SN(cpu65_flags_encode)(,%eax,1), %al
Push(%al)
orb $(B_Flag | I_Flag), F_Reg
//andb $~D_Flag, F_Reg // AppleWin clears Decimal bit?
andl $0xFFFF, EffectiveAddr_E// HACK FIXME : there is a bug somewhere that is occasionally corrupting EffectiveAddr_E
movw $0xFFFE, EffectiveAddr // ROM interrupt vector
GetFromEA_W
movw %ax, PC_Reg
xorb %ah, %ah
JumpNextInstruction
/* -------------------------------------------------------------------------
CPU thread run "loop"
CPU thread main entry and exit points
------------------------------------------------------------------------- */
E(cpu65_run)
pushal // ENTER CPURUN
cmpb $0, SN(cpu65_do_reboot)
cmpb $0, SN(emul_reinitialize)
jnz 1f
ReplaceState
RestoreState
JumpNextInstruction
1: movb $0, SN(cpu65_do_reboot)
1: movb $0, SN(emul_reinitialize)
/* Zero all registers, as well as the unused 32-bit parts
* of variables. (which may need to be kept 0)
*
@ -2735,12 +2736,32 @@ E(cpu65_run)
movl $0x1FF, %edx # Stack pointer
jmp ex_reset
exit_cpu65_run: SaveState // Return to timing loop ...
popal
ret
emul_reinit: movb $0, SN(cpu65__signal) // EXIT CPURUN
movb $1, SN(emul_reinitialize)
popal
ret
/* -------------------------------------------------------------------------
Debugger hooks
------------------------------------------------------------------------- */
ex_step: orb $~DebugStepSig, SN(cpu65__signal)
xorl %eax, %eax
SaveState
call SN(c_stepping_yield)
RestoreState
JumpNextInstruction
E(cpu65_direct_write)
/* NB: dependent on register choices */
pushl %edi
movl 8(%esp),%edi
movl 12(%esp),%eax
call * SN(cpu65_vmem)+4(,EffectiveAddr_E,8)
call *SN(cpu65_vmem)+4(,EffectiveAddr_E,8)
popl %edi
ret

View File

@ -14,6 +14,9 @@
*
*/
#ifndef __CPU_H_
#define __CPU_H_
#ifndef __ASSEMBLER__
#include <sys/types.h>
#include <stdint.h>
@ -59,6 +62,7 @@ extern void cpu65_set(int flags);
/* Interrupt the processor */
extern void cpu65_interrupt(int reason);
extern void cpu65_uninterrupt(int reason);
extern void cpu65_run(void);
@ -75,13 +79,17 @@ extern unsigned char cpu65_flags_decode[256];
extern int16_t cpu65_cycle_count;
extern int16_t cpu65_cycles_to_execute;
extern uint8_t cpu65_do_reboot;
extern uint8_t emul_reinitialize;
#endif /* !__ASSEMBLER__ */
#define RebootSig 0x01
#define ResetSig 0x02
#define DebugStepSig 0x04
#define IRQ6522 0x08
#define IRQSpeech 0x10
#define IRQSSC 0x20
#define IRQMouse 0x40
/* Note: These are *not* the bit positions used for the flags in the P
* register of a real 6502. Rather, they have been distorted so that C,
@ -111,12 +119,13 @@ extern uint8_t cpu65_do_reboot;
#define X_Reg %bl /* 6502 X register in %bl */
#define Y_Reg %bh /* 6502 Y register in %bh */
#define XY_Regs_32 %ebx /* 6502 X&Y flags */
#define A_Reg %cl /* 6502 A register in %cl */
#define F_Reg %ch /* 6502 flags in %ch */
#define FF_Reg %ecx /* 6502 flags for bt */
#define SP_Reg %edx /* 6502 Stack pointer */
#define FF_Reg %ecx /* 6502 F&A flags */
#define SP_Reg_L %dl /* 6502 Stack pointer low */
#define SP_Reg_H %dh /* 6502 Stack pointer high */
#define SP_Reg %edx /* 6502 Stack pointer */
#define PC_Reg %si /* 6502 Program Counter */
#define PC_Reg_E %esi /* 6502 Program Counter */
#define EffectiveAddr %di /* Effective address */
@ -129,8 +138,9 @@ extern void *const cpu65__nmos[256];
extern void *const cpu65__nmosbrk[256];
extern void *const cpu65__cmos[256];
extern char cpu65__opcycles[256];// cycle counter
extern uint8_t cpu65__opcycles[256];// cycle counter
extern unsigned char cpu65__signal;
extern uint8_t cpu65__signal;
#endif /* !__ASSEMBLER__ */
#endif // whole file

View File

@ -18,30 +18,32 @@
typedef struct IDirectSoundBuffer {
void *implementation_specific;
void *_this;
HRESULT (*SetVolume)(LONG lVolume);
HRESULT (*SetVolume)(void* _this, LONG lVolume);
HRESULT (*GetVolume)(LPLONG lplVolume);
HRESULT (*GetVolume)(void* _this, LPLONG lplVolume);
HRESULT (*GetCurrentPosition)(LPDWORD lpdwCurrentPlayCursor, LPDWORD lpdwCurrentWriteCursor);
HRESULT (*GetCurrentPosition)(void* _this, LPDWORD lpdwCurrentPlayCursor, LPDWORD lpdwCurrentWriteCursor);
HRESULT (*SetCurrentPosition)(DWORD dwNewPosition);
HRESULT (*Stop)();
HRESULT (*Stop)(void* _this);
// This method restores the memory allocation for a lost sound buffer for the specified DirectSoundBuffer object.
HRESULT (*Restore)();
HRESULT (*Restore)(void *_this);
HRESULT (*Play)(DWORD dwReserved1, DWORD dwReserved2, DWORD dwFlags);
HRESULT (*Play)(void* _this, DWORD dwReserved1, DWORD dwReserved2, DWORD dwFlags);
// This method obtains a valid write pointer to the sound buffer's audio data
HRESULT (*Lock)(DWORD dwWriteCursor, DWORD dwWriteBytes, LPVOID* lplpvAudioPtr1, LPDWORD lpdwAudioBytes1, LPVOID* lplpvAudioPtr2, LPDWORD lpdwAudioBytes2, DWORD dwFlags);
HRESULT (*Lock)(void* _this, DWORD dwWriteCursor, DWORD dwWriteBytes, LPVOID* lplpvAudioPtr1, LPDWORD lpdwAudioBytes1, LPVOID* lplpvAudioPtr2, LPDWORD lpdwAudioBytes2, DWORD dwFlags);
// This method releases a locked sound buffer.
HRESULT (*Unlock)(LPVOID lpvAudioPtr1, DWORD dwAudioBytes1, LPVOID lpvAudioPtr2, DWORD dwAudioBytes2);
HRESULT (*Unlock)(void* _this, LPVOID lpvAudioPtr1, DWORD dwAudioBytes1, LPVOID lpvAudioPtr2, DWORD dwAudioBytes2);
HRESULT (*GetStatus)(LPDWORD lpdwStatus);
HRESULT (*GetStatus)(void* _this, LPDWORD lpdwStatus);
HRESULT (*UnlockStaticBuffer)(void* _this, DWORD dwAudioBytes);
HRESULT (*Replay)(void* _this);
} IDirectSoundBuffer, *LPDIRECTSOUNDBUFFER, **LPLPDIRECTSOUNDBUFFER;
@ -124,6 +126,7 @@ typedef struct IDirectSoundBuffer {
#define DSBSTATUS_BUFFERLOST 0x00000002
#define DSBSTATUS_LOOPING 0x00000004
#define DSBSTATUS_PLAYING 0x00000001
#define _DSBSTATUS_NOTPLAYING 0x08000000
#define DSBPLAY_LOOPING 0x00000001
#define DSBVOLUME_MIN -10000
#define DSBVOLUME_MAX 0
@ -136,7 +139,10 @@ static inline bool SUCCEEDED(HRESULT x) { return x == DS_OK; }
#define WAVE_FORMAT_PCM 0x0001
#define DSBCAPS_GETCURRENTPOSITION2 0x00010000
#define DSBCAPS_STICKYFOCUS 0x00004000
#define DSBCAPS_LOCSOFTWARE 0x00000008
#define DSBCAPS_CTRLVOLUME 0x00000080
#define DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100
typedef struct {
WORD wFormatTag;

View File

@ -1,5 +1,5 @@
#! /bin/sh
echo '/* Automatically Generated -- do not edit */'
echo '#include "gluepro.h"'
echo '#include "glue-prologue.h"'
grep -E -h '^(GLUE_)|(#if)|(#endif)|(#else)|(#elif)' $*
exit 0

View File

@ -45,14 +45,15 @@ E(func) addl SN(pointer),%edi; \
1: ret;
// TODO FIXME : implement CDECL prologue/epilogues...
#define GLUE_C_WRITE(func) \
E(func) pushl %eax; \
andl $0xff,%eax; \
pushl %ecx; \
pushl %edx; \
andl $0xff,%eax; \
pushl %eax; \
pushl %edi; \
call SN(unglued_##func); \
call SN(c_##func); \
popl %edx; /* dummy */ \
popl %edx; /* dummy */ \
popl %edx; \
@ -60,16 +61,17 @@ E(func) pushl %eax; \
popl %eax; \
ret; \
// TODO FIXME : implement CDECL prologue/epilogues...
#define GLUE_C_READ(func) \
E(func) pushl %eax; \
E(func) /*pushl %eax;*/ \
pushl %ecx; \
pushl %edx; \
pushl %edi; \
call SN(unglued_##func); \
movb %al,12(%esp); \
call SN(c_##func); \
/*movb %al,12(%esp);*/ \
popl %edx; /* dummy */ \
popl %edx; \
popl %ecx; \
popl %eax; \
/*popl %eax;*/ \
ret;

View File

@ -21,8 +21,10 @@
#define GLUE_BANK_MAYBEWRITE(func,pointer)
#define GLUE_C_WRITE(func) \
void unglued_##func(int ea, unsigned char d) /* you complete definition */
extern void func(); \
void c_##func(uint16_t ea, uint8_t b) /* you complete definition */
#define GLUE_C_READ(func) \
unsigned char unglued_##func(int ea) /* you complete definition */
extern void func(); \
uint8_t c_##func(uint16_t ea) /* you complete definition */

View File

@ -36,6 +36,7 @@
#include "timing.h"
#include "speaker.h"
#include "soundcore.h"
#include "mockingboard.h"
/* ----------------------------------
internal apple2 variables
@ -484,6 +485,7 @@ void c_initialize_tables() {
// HACK TODO FIXME : this needs to be tied to the UI/configuration system (once we have more/conflicting options)
mb_io_initialize(4, 5); /* Mockingboard(s) and/or Phasor in slots 4 & 5 */
disk_io_initialize(6); /* Put a Disk ][ Controller in slot 6 */
}
@ -669,8 +671,6 @@ void reinitialize(void)
{
int i;
cpu65_do_reboot=1;
/* reset the watchpoints and breakpoints */
for (i=0; i<MAX_BRKPTS; i++)
{
@ -718,11 +718,12 @@ static void c_initialize_firsttime()
// TODO FIXME : sound system never released ...
DSInit();
SpkrInitialize();
//MB_Initialize();
MB_Initialize();
reinitialize();
}
// HACK FIXME TODO : candidate for GLUE_C_READ(...)
void c_read_random() {
static unsigned int seed=0;
if (!seed) {

View File

@ -50,7 +50,7 @@ unsigned char apple_ii_64k[2][65536]; /* 128k memory */
unsigned char language_card[2][8192], language_banks[2][8192];
/* misc stuff */
unsigned char random_value;
uint8_t random_value;
/* global ref to commandline args */
char **argv;

2267
src/mockingboard.c Normal file

File diff suppressed because it is too large Load Diff

121
src/mockingboard.h Normal file
View File

@ -0,0 +1,121 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 2 or later (your choice) as published by the Free Software
* Foundation.
*
* THERE ARE NO WARRANTIES WHATSOEVER.
*
*/
#ifndef _MOCKINGBOARD_H__
#define _MOCKINGBOARD_H__
#ifdef APPLE2IX
#include "win-shim.h"
#include "peripherals.h"
extern bool g_bDisableDirectSoundMockingboard;
typedef struct
{
union
{
struct
{
BYTE l;
BYTE h;
};
USHORT w;
};
} IWORD;
typedef struct
{
BYTE ORB; // $00 - Port B
BYTE ORA; // $01 - Port A (with handshaking)
BYTE DDRB; // $02 - Data Direction Register B
BYTE DDRA; // $03 - Data Direction Register A
//
// $04 - Read counter (L) / Write latch (L)
// $05 - Read / Write & initiate count (H)
// $06 - Read / Write & latch (L)
// $07 - Read / Write & latch (H)
// $08 - Read counter (L) / Write latch (L)
// $09 - Read counter (H) / Write latch (H)
IWORD TIMER1_COUNTER;
IWORD TIMER1_LATCH;
IWORD TIMER2_COUNTER;
IWORD TIMER2_LATCH;
//
BYTE SERIAL_SHIFT; // $0A
BYTE ACR; // $0B - Auxiliary Control Register
BYTE PCR; // $0C - Peripheral Control Register
BYTE IFR; // $0D - Interrupt Flag Register
BYTE IER; // $0E - Interrupt Enable Register
BYTE ORA_NO_HS; // $0F - Port A (without handshaking)
} SY6522;
typedef struct
{
BYTE DurationPhonome;
BYTE Inflection; // I10..I3
BYTE RateInflection;
BYTE CtrlArtAmp;
BYTE FilterFreq;
//
BYTE CurrentMode; // b7:6=Mode; b0=D7 pin (for IRQ)
} SSI263A;
extern SS_CARDTYPE g_Slot4; // Mockingboard, Z80, Mouse in slot4
extern SS_CARDTYPE g_Slot5; // Mockingboard, Z80 in slot5
#define MB_UNITS_PER_CARD 2
typedef struct
{
SY6522 RegsSY6522;
BYTE RegsAY8910[16];
SSI263A RegsSSI263;
BYTE nAYCurrentRegister;
bool bTimer1IrqPending;
bool bTimer2IrqPending;
bool bSpeechIrqPending;
} MB_Unit;
typedef struct
{
SS_CARD_HDR Hdr;
MB_Unit Unit[MB_UNITS_PER_CARD];
} SS_CARD_MOCKINGBOARD;
#endif
extern bool g_bMBTimerIrqActive;
#ifdef _DEBUG
extern UINT32 g_uTimer1IrqCount; // DEBUG
#endif
void MB_Initialize();
void MB_Reinitialize();
void MB_Destroy();
void MB_Reset();
void MB_InitializeIO(LPBYTE pCxRomPeripheral, UINT uSlot4, UINT uSlot5);
void MB_Mute();
void MB_Demute();
void MB_StartOfCpuExecute();
void MB_EndOfVideoFrame();
void MB_UpdateCycles(ULONG uExecutedCycles);
SS_CARDTYPE MB_GetSoundcardType();
void MB_SetSoundcardType(SS_CARDTYPE NewSoundcardType);
double MB_GetFramePeriod();
bool MB_IsActive();
DWORD MB_GetVolume();
void MB_SetVolume(DWORD dwVolume, DWORD dwVolumeMax);
DWORD MB_GetSnapshot(SS_CARD_MOCKINGBOARD* pSS, DWORD dwSlot);
DWORD MB_SetSnapshot(SS_CARD_MOCKINGBOARD* pSS, DWORD dwSlot);
#ifdef APPLE2IX
void mb_io_initialize(unsigned int slot4, unsigned int slot5);
#endif
#endif // whole file

57
src/peripherals.h Normal file
View File

@ -0,0 +1,57 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 2 or later (your choice) as published by the Free Software
* Foundation.
*
* THERE ARE NO WARRANTIES WHATSOEVER.
*
*/
#ifndef _PERIPHERALS_H_
#define _PERIPHERALS_H_
#include "cpu.h"
#include "win-shim.h"
typedef enum eIRQSRC {
IS_6522=0x08, // NOTE : matches IRQ... defines in cpu.h
IS_SPEECH=0x10,
IS_SSC=0x20,
IS_MOUSE=0x40
} eIRQSRC;
typedef enum SS_CARDTYPE
{
CT_Empty = 0,
CT_Disk2, // Apple Disk][
CT_SSC, // Apple Super Serial Card
CT_MockingboardC, // Soundcard
CT_GenericPrinter,
CT_GenericHDD, // Hard disk
CT_GenericClock,
CT_MouseInterface,
CT_Z80,
CT_Phasor, // Soundcard
CT_Echo, // Soundcard
CT_SAM, // Soundcard: Software Automated Mouth
} SS_CARDTYPE;
typedef struct
{
DWORD dwLength; // Byte length of this unit struct
DWORD dwVersion;
} SS_UNIT_HDR;
typedef struct
{
SS_UNIT_HDR UnitHdr;
DWORD dwType; // SS_CARDTYPE
DWORD dwSlot; // [1..7]
} SS_CARD_HDR;
void CpuIrqAssert(eIRQSRC irq_source);
void CpuIrqDeassert(eIRQSRC irq_source);
#endif

View File

@ -15,52 +15,43 @@
// HACK : this is an ugly shoehorning into DirectSound assumptions... Needta rework this once I have a better
// understanding of the workings of the sound system :)
static int resample = 1; /* enable alsa-lib resampling */
static int period_event = 0; /* produce poll event after each period */
static snd_pcm_format_t format = SND_PCM_FORMAT_S16;
static int format_bits = 0;
static int bytes_per_sample = 0;
static int shift_per_sample = 0;
static snd_pcm_uframes_t buffer_size;
static snd_pcm_uframes_t period_size;
static snd_pcm_hw_params_t *hwparams = NULL;
static snd_pcm_sw_params_t *swparams = NULL;
extern SoundSystemStruct *g_lpDS; // HACK
snd_pcm_uframes_t play_offset;
static long alsa_create_sound_buffer(ALSABufferParamsStruct *params_struct, ALSASoundBufferStruct **soundbuf_struct, void *extra_data);
static long alsa_destroy_sound_buffer(ALSASoundBufferStruct **soundbuf_struct);
long SoundSystemCreate(const char *sound_device, SoundSystemStruct **sound_struct)
{
// ugly assumption : this sets the extern g_lpDS ...
assert(*sound_struct == NULL);
snd_pcm_t *handle = NULL;
int err = -1;
if ((*sound_struct = malloc(sizeof(SoundSystemStruct))) == NULL)
{
ERRLOG("Not enough memory");
return -1;
}
do {
if ((*sound_struct = malloc(sizeof(SoundSystemStruct))) == NULL)
{
ERRLOG("Not enough memory");
break;
}
if ((err = snd_pcm_open(&handle, sound_device, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
if ((err = snd_pcm_open(&handle, sound_device, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
{
ERRLOG("Playback open error: %s", snd_strerror(err));
break;
}
(*sound_struct)->implementation_specific = handle;
(*sound_struct)->CreateSoundBuffer = alsa_create_sound_buffer;
(*sound_struct)->DestroySoundBuffer = alsa_destroy_sound_buffer;
return 0;
} while(0);
// ERRQUIT
if (*sound_struct)
{
ERRLOG("Playback open error: %s", snd_strerror(err));
Free(*sound_struct);
return -1;
}
(*sound_struct)->implementation_specific = handle;
(*sound_struct)->CreateSoundBuffer = alsa_create_sound_buffer;
(*sound_struct)->DestroySoundBuffer = alsa_destroy_sound_buffer;
return 0;
return -1;
}
long SoundSystemDestroy(SoundSystemStruct **sound_struct)
@ -81,7 +72,6 @@ long SoundSystemDestroy(SoundSystemStruct **sound_struct)
ERRLOG("snd_pcm_drop: %s", snd_strerror(err));
}
(*sound_struct)->implementation_specific = NULL;
Free(*sound_struct);
return 0;
@ -204,7 +194,7 @@ static long _alsa_create_volume_refs(snd_mixer_t **handle, snd_mixer_selem_id_t
return err ?: -1;
}
static long alsa_get_volume(long *volume)
static long alsa_get_volume(void *_this, long *volume)
{
assert(volume != NULL);
@ -241,7 +231,7 @@ static long alsa_get_volume(long *volume)
return err;
}
static long alsa_set_volume(long volume)
static long alsa_set_volume(void *_this, long volume)
{
// UNIMPLEMENTED
return 0;
@ -291,34 +281,28 @@ static int xrun_recovery(snd_pcm_t *handle, int err)
return err;
}
static long alsa_set_position(unsigned long write_cursor)
{
// UNIMPLEMENTED
assert(false);
return 0;
}
static long alsa_stop()
static long alsa_stop(void *_this)
{
// this is a no-op at the moment, just let the sound flow!
return 0;
}
static long alsa_restore()
static long alsa_restore(void *_this)
{
return 0;
}
static long alsa_play(unsigned long reserved1, unsigned long reserved2, unsigned long flags)
static long alsa_play(void *_this, unsigned long reserved1, unsigned long reserved2, unsigned long flags)
{
// this is a no-op presumably because all the alsa setup give us a buffer ready to play
return 0;
}
// returns buffer position in bytes
static long alsa_get_position(unsigned long *play_cursor, unsigned long *unused_write_cursor)
static long alsa_get_position(void *_this, unsigned long *play_cursor, unsigned long *unused_write_cursor)
{
snd_pcm_t *handle = (snd_pcm_t*)g_lpDS->implementation_specific;
ALSAExtras *extras = (ALSAExtras*)_this;
snd_pcm_t *handle = extras->handle;
snd_pcm_sframes_t avail = 0;
long err = 0;
@ -368,7 +352,7 @@ static long alsa_get_position(unsigned long *play_cursor, unsigned long *unused_
if (avail < 1024) // HACK MAGICK CONSTANT
{
ERRLOG("OOPS avail(%ld) < period_size(%lu) ... ", avail, period_size);
ERRLOG("OOPS avail(%ld) < 1024 ... ", avail);
ERRLOG("performing snd_pcm_wait() ...");
err = snd_pcm_wait(handle, -1);
if (err < 0)
@ -383,8 +367,8 @@ static long alsa_get_position(unsigned long *play_cursor, unsigned long *unused_
break;
}
avail = buffer_size - avail;
*play_cursor = avail<<shift_per_sample;
avail = extras->buffer_size - avail;
*play_cursor = avail<<(extras->shift_per_sample);
return 0;
} while (0);
@ -394,19 +378,20 @@ static long alsa_get_position(unsigned long *play_cursor, unsigned long *unused_
// HACK NOTE : audio_ptr2 is unused
// DS->Lock()
static long alsa_begin(unsigned long unused, unsigned long write_bytes, void **audio_ptr1, unsigned long *audio_bytes1, void **unused_audio_ptr2, unsigned long *audio_bytes2, unsigned long flags)
static long alsa_begin(void *_this, unsigned long unused, unsigned long write_bytes, void **audio_ptr1, unsigned long *audio_bytes1, void **unused_audio_ptr2, unsigned long *audio_bytes2, unsigned long flags_unused)
{
int err = 0;
ALSAExtras *extras = (ALSAExtras*)_this;
const snd_pcm_channel_area_t *areas;
uint8_t *bytes = NULL;
snd_pcm_uframes_t offset_frames = 0;
snd_pcm_uframes_t size_frames = write_bytes>>shift_per_sample; // HACK : assuming 16bit samples
snd_pcm_uframes_t size_frames = write_bytes>>(extras->shift_per_sample); // HACK : assuming 16bit samples
if (write_bytes == 0)
{
size_frames = buffer_size;
size_frames = extras->buffer_size;
}
*audio_ptr1 = NULL;
@ -418,7 +403,7 @@ static long alsa_begin(unsigned long unused, unsigned long write_bytes, void **a
*audio_bytes2 = 0;
do {
snd_pcm_t *handle = (snd_pcm_t*)g_lpDS->implementation_specific;
snd_pcm_t *handle = extras->handle;
while ((err = snd_pcm_mmap_begin(handle, &areas, &offset_frames, &size_frames)) < 0)
{
if ((err = xrun_recovery(handle, err)) < 0)
@ -434,12 +419,12 @@ static long alsa_begin(unsigned long unused, unsigned long write_bytes, void **a
assert((area.first % 8) == 0);
bytes = (((uint8_t *)area.addr) + (area.first>>3));
assert(format_bits == area.step);
assert(bytes_per_sample == (area.step>>3));
bytes += offset_frames * bytes_per_sample;
assert(extras->format_bits == area.step);
assert(extras->bytes_per_sample == (area.step>>3));
bytes += offset_frames * extras->bytes_per_sample;
*audio_ptr1 = (void*)bytes;
*audio_bytes1 = size_frames<<shift_per_sample;
*audio_bytes1 = size_frames<<(extras->shift_per_sample);
*audio_bytes2 = offset_frames;
return 0;
@ -449,15 +434,16 @@ static long alsa_begin(unsigned long unused, unsigned long write_bytes, void **a
}
// DS->Unlock()
static long alsa_commit(void *unused_audio_ptr1, unsigned long audio_bytes1, void *unused_audio_ptr2, unsigned long audio_bytes2)
static long alsa_commit(void *_this, void *unused_audio_ptr1, unsigned long audio_bytes1, void *unused_audio_ptr2, unsigned long audio_bytes2)
{
ALSAExtras *extras = (ALSAExtras*)_this;
assert(unused_audio_ptr2 == NULL);
int err = 0;
snd_pcm_uframes_t size_frames = audio_bytes1>>shift_per_sample;
snd_pcm_uframes_t size_frames = audio_bytes1>>(extras->shift_per_sample);
snd_pcm_uframes_t offset_frames = audio_bytes2;
do {
snd_pcm_t *handle = (snd_pcm_t*)g_lpDS->implementation_specific;
snd_pcm_t *handle = extras->handle;
err = snd_pcm_mmap_commit(handle, offset_frames, size_frames);
if (err < 0 || (snd_pcm_uframes_t)err != size_frames)
{
@ -484,18 +470,33 @@ static long alsa_commit(void *unused_audio_ptr1, unsigned long audio_bytes1, voi
return err ?: -1;
}
static long alsa_get_status(unsigned long *status)
static long alsa_get_status(void *_this, unsigned long *status)
{
// this is actually tested in the alsa_get_position() call
snd_pcm_t *handle = ((ALSAExtras*)_this)->handle;
if (snd_pcm_state(handle) == SND_PCM_STATE_RUNNING)
{
*status = DSBSTATUS_PLAYING;
}
else
{
*status = _DSBSTATUS_NOTPLAYING;
}
return 0;
}
// ----------------------------------------------------------------------------
static int set_hwparams(snd_pcm_t *handle, ALSABufferParamsStruct *params_struct)
static int set_hwparams(ALSAExtras *extras, ALSABufferParamsStruct *params_struct)
{
int err = 0;
int resample = 1; // enable alsa-lib resampling
snd_pcm_t *handle = extras->handle;
snd_pcm_hw_params_t *hwparams = extras->hwparams;
/* choose all parameters */
if ((err = snd_pcm_hw_params_any(handle, hwparams)) < 0)
{
@ -517,15 +518,16 @@ static int set_hwparams(snd_pcm_t *handle, ALSABufferParamsStruct *params_struct
return err;
}
extras->format = SND_PCM_FORMAT_S16;
/* set the sample format as signed 16bit samples */
if ((err = snd_pcm_hw_params_set_format(handle, hwparams, format)) < 0)
if ((err = snd_pcm_hw_params_set_format(handle, hwparams, extras->format)) < 0)
{
ERRLOG("Sample format not available for playback: %s", snd_strerror(err));
return err;
}
format_bits = snd_pcm_format_physical_width(format);
bytes_per_sample = format_bits / 8;
shift_per_sample = (bytes_per_sample>>1); // HACK : ASSUMES 16bit samples ...
extras->format_bits = snd_pcm_format_physical_width(extras->format);
extras->bytes_per_sample = extras->format_bits / 8;
extras->shift_per_sample = (extras->bytes_per_sample>>1); // HACK : ASSUMES 16bit samples ...
/* set the count of channels */
if ((err = snd_pcm_hw_params_set_channels(handle, hwparams, params_struct->lpwfxFormat->nChannels)) < 0)
@ -548,13 +550,13 @@ static int set_hwparams(snd_pcm_t *handle, ALSABufferParamsStruct *params_struct
return -EINVAL;
}
/* set the buffer time */
/* set the buffer size */
int ignored;
buffer_size = params_struct->dwBufferBytes;
err = snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &buffer_size);
extras->buffer_size = params_struct->dwBufferBytes;
err = snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &(extras->buffer_size));
if (err < 0)
{
ERRLOG("Unable to set buffer size %d for playback: %s", (int)buffer_size, snd_strerror(err));
ERRLOG("Unable to set buffer size %d for playback: %s", (int)extras->buffer_size, snd_strerror(err));
}
snd_pcm_uframes_t size;
@ -563,17 +565,17 @@ static int set_hwparams(snd_pcm_t *handle, ALSABufferParamsStruct *params_struct
ERRLOG("Unable to get buffer size for playback: %s", snd_strerror(err));
return err;
}
if (size != buffer_size)
if (size != extras->buffer_size)
{
ERRLOG("Note: buffer_size fetch mismatch using %d -- (%d requested)", (int)size, (int)buffer_size);
buffer_size = size;
ERRLOG("Note: buffer_size fetch mismatch using %d -- (%d requested)", (int)size, (int)extras->buffer_size);
extras->buffer_size = size;
}
period_size = buffer_size / 4;
err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &period_size, &ignored);
extras->period_size = (extras->buffer_size)>>3;
err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &(extras->period_size), &ignored);
if (err < 0)
{
ERRLOG("Unable to set period size %d for playback: %s", (int)period_size, snd_strerror(err));
ERRLOG("Unable to set period size %d for playback: %s", (int)extras->period_size, snd_strerror(err));
}
if ((err = snd_pcm_hw_params_get_period_size(hwparams, &size, &ignored)) < 0)
@ -581,10 +583,10 @@ static int set_hwparams(snd_pcm_t *handle, ALSABufferParamsStruct *params_struct
ERRLOG("Unable to get period size for playback: %s", snd_strerror(err));
return err;
}
if (size != period_size)
if (size != extras->period_size)
{
ERRLOG("Note: period_size fetch mismatch using %d -- (%d requested)", (int)size, (int)period_size);
period_size = size;
ERRLOG("Note: period_size fetch mismatch using %d -- (%d requested)", (int)size, (int)extras->period_size);
extras->period_size = size;
}
/* write the parameters to device */
@ -597,10 +599,14 @@ static int set_hwparams(snd_pcm_t *handle, ALSABufferParamsStruct *params_struct
return 0;
}
static int set_swparams(snd_pcm_t *handle, ALSABufferParamsStruct *params_struct)
static int set_swparams(ALSAExtras *extras, ALSABufferParamsStruct *params_struct)
{
int err = -1;
int period_event = 0; // produce poll event after each period
snd_pcm_t *handle = extras->handle;
snd_pcm_sw_params_t *swparams = extras->swparams;
/* get the current swparams */
if ((err = snd_pcm_sw_params_current(handle, swparams)) < 0)
{
@ -610,7 +616,7 @@ static int set_swparams(snd_pcm_t *handle, ALSABufferParamsStruct *params_struct
/* start the transfer when the buffer is almost full: */
/* (buffer_size / avail_min) * avail_min */
if ((err = snd_pcm_sw_params_set_start_threshold(handle, swparams, (buffer_size / period_size) * period_size)) < 0)
if ((err = snd_pcm_sw_params_set_start_threshold(handle, swparams, (extras->buffer_size / extras->period_size) * extras->period_size)) < 0)
{
ERRLOG("Unable to set start threshold mode for playback: %s", snd_strerror(err));
return err;
@ -618,7 +624,7 @@ static int set_swparams(snd_pcm_t *handle, ALSABufferParamsStruct *params_struct
/* allow the transfer when at least period_size samples can be processed */
/* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
if ((err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_event ? buffer_size : period_size)) < 0)
if ((err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_event ? extras->buffer_size : extras->period_size)) < 0)
{
ERRLOG("Unable to set avail min for playback: %s", snd_strerror(err));
return err;
@ -652,45 +658,23 @@ static long alsa_create_sound_buffer(ALSABufferParamsStruct *params_struct, ALSA
snd_pcm_t *handle = (snd_pcm_t*)(sound_struct->implementation_specific);
assert(handle != NULL);
ALSAExtras *extras = NULL;
int err = -1;
#if 0
ALSASoundStructExtras *extras = NULL;
#endif
if (hwparams == NULL)
{
snd_pcm_hw_params_alloca(&hwparams);
}
if (swparams == NULL)
{
snd_pcm_sw_params_alloca(&swparams);
}
do {
#if 0
if ((extras = malloc(sizeof(ALSASoundStructExtras))) == NULL)
if ((extras = calloc(1, sizeof(ALSAExtras))) == NULL)
{
ERRLOG("Not enough memory");
break;
}
signed short *samples = NULL;
if ((samples = malloc((period_size * format_bits) / 8)) == NULL)
{
ERRLOG("Not enough memory");
break;
}
snd_pcm_channel_area_t *area;
if ((area = malloc(sizeof(snd_pcm_channel_area_t))) == NULL)
{
ERRLOG("Not enough memory");
break;
}
area->addr = samples;
area->first = 0;
area->step = format_bits;
#endif
extras->handle = handle;
snd_pcm_hw_params_alloca(&(extras->hwparams));
snd_pcm_sw_params_alloca(&(extras->swparams));
if ((*soundbuf_struct = malloc(sizeof(ALSASoundBufferStruct))) == NULL)
{
@ -698,17 +682,10 @@ static long alsa_create_sound_buffer(ALSABufferParamsStruct *params_struct, ALSA
break;
}
#if 0
extras->hwparams = hwparams;
extras->swparams = swparams;
extras->area = area;
(*soundbuf_struct)->implementation_specific = extras;
#endif
(*soundbuf_struct)->_this = extras;
(*soundbuf_struct)->SetVolume = alsa_set_volume;
(*soundbuf_struct)->GetVolume = alsa_get_volume;
(*soundbuf_struct)->GetCurrentPosition = alsa_get_position;
(*soundbuf_struct)->SetCurrentPosition = alsa_set_position;
(*soundbuf_struct)->Stop = alsa_stop;
(*soundbuf_struct)->Restore = alsa_restore;
(*soundbuf_struct)->Play = alsa_play;
@ -718,13 +695,13 @@ static long alsa_create_sound_buffer(ALSABufferParamsStruct *params_struct, ALSA
// configuration ...
if ((err = set_hwparams(handle, params_struct)) < 0)
if ((err = set_hwparams(extras, params_struct)) < 0)
{
ERRLOG("Setting of hwparams failed: %s", snd_strerror(err));
break;
}
if ((err = set_swparams(handle, params_struct)) < 0)
if ((err = set_swparams(extras, params_struct)) < 0)
{
ERRLOG("Setting of swparams failed: %s", snd_strerror(err));
break;
@ -743,20 +720,14 @@ static long alsa_create_sound_buffer(ALSABufferParamsStruct *params_struct, ALSA
} while(0);
if (extras)
{
Free(extras);
}
if (*soundbuf_struct)
{
alsa_destroy_sound_buffer(soundbuf_struct);
}
else
{
#if 0
if (hwparams) { Free(hwparams); }
if (swparams) { Free(swparams); }
if (area) { Free(area); }
if (samples) { Free(samples); }
if (extras) { Free(extras); }
#endif
}
return err ?: -1;
}

View File

@ -27,10 +27,19 @@ typedef struct IDirectSoundBuffer ALSASoundBufferStruct;
typedef struct DSBUFFERDESC ALSABufferParamsStruct;
typedef struct {
snd_pcm_t *handle;
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
snd_pcm_channel_area_t *area;
} ALSASoundStructExtras;
snd_pcm_format_t format;
int format_bits;
int bytes_per_sample;
int shift_per_sample;
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size;
} ALSAExtras;
#endif /* whole file */

781
src/soundcore-openal.c Normal file
View File

@ -0,0 +1,781 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 2 or later (your choice) as published by the Free Software
* Foundation.
*
* THERE ARE NO WARRANTIES WHATSOEVER.
*
*/
// HACK NOTE: DO NOT REFACTOR (yet) ...
// OK this is more hackish than it needs to be because it's attempting to mimic a DirectSound backend ...Oh God, Why?...
// Here I must confess that because of general ignorance of the mockingboard and other soundcard code at this time,
// there is a need to track any changes/fixes implemented in AppleWin...
#include "soundcore-openal.h"
#include "alhelpers.h"
LPALBUFFERSAMPLESSOFT alBufferSamplesSOFT = wrap_BufferSamples;
LPALISBUFFERFORMATSUPPORTEDSOFT alIsBufferFormatSupportedSOFT = NULL;
static long OpenALCreateSoundBuffer(ALBufferParamsStruct *params, ALSoundBufferStruct **soundbuf_struct, void *extra_data);
static long OpenALDestroySoundBuffer(ALSoundBufferStruct **soundbuf_struct);
// ----------------------------------------------------------------------------
// uthash
static ALPlayBuf *PlaylistEnqueue(ALVoice *voice, ALuint bytes)
{
ALPlayBuf *node = voice->avail_buffers;
if (node == NULL)
{
ERRLOG("OOPS, sound playback overflow!");
return NULL;
}
//LOG("Really enqueing OpenAL buffer %u", node->bufid);
ALPlayBuf *node2 = NULL;
HASH_FIND_INT(voice->queued_buffers, &node->bufid, node2);
if (node2 != NULL)
{
ERRLOG("OOPS, confused ... ALPlayBuf already added!");
return NULL;
}
// remove from list / place on hash
voice->avail_buffers = node->_avail_next;
node->_avail_next = NULL;
HASH_ADD_INT(voice->queued_buffers, bufid, node);
node->bytes = bytes;
voice->_queued_total_bytes += node->bytes;
voice->index = 0;
assert(voice->_queued_total_bytes > 0);
#if 0
ALPlayBuf *tmp = voice->queued_buffers;
unsigned int count = HASH_COUNT(voice->queued_buffers);
LOG("\t(numqueued: %d)", count);
for (unsigned int i = 0; i < count; i++, tmp = tmp->hh.next)
{
LOG("\t(bufid : %u)", tmp->bufid);
}
#endif
return node;
}
static ALPlayBuf *PlaylistGet(ALVoice *voice, ALuint bufid)
{
ALPlayBuf *node = NULL;
HASH_FIND_INT(voice->queued_buffers, &bufid, node);
return node;
}
static void PlaylistDequeue(ALVoice *voice, ALPlayBuf *node)
{
//LOG("Dequeing OpenAL buffer %u", node->bufid);
// remove from hash / place on list
HASH_DEL(voice->queued_buffers, node); // remove from hash
node->_avail_next = voice->avail_buffers;
voice->avail_buffers = node;
voice->_queued_total_bytes -= node->bytes;
assert(voice->_queued_total_bytes >= 0);
node->bytes = 0;
#if 0
ALPlayBuf *tmp = voice->queued_buffers;
unsigned int count = HASH_COUNT(voice->queued_buffers);
LOG("\t(numqueued: %d)", count);
for (unsigned int i = 0; i < count; i++, tmp = tmp->hh.next)
{
LOG("\t(bufid : %u)", tmp->bufid);
}
#endif
}
// ----------------------------------------------------------------------------
long SoundSystemCreate(const char *sound_device, SoundSystemStruct **sound_struct)
{
assert(*sound_struct == NULL);
int err = -1;
ALCcontext *ctx = NULL;
do {
if ((ctx = InitAL()) == NULL)
{
ERRLOG("OOPS, OpenAL initialize failed");
break;
}
if (alIsExtensionPresent("AL_SOFT_buffer_samples"))
{
LOG("AL_SOFT_buffer_samples supported, good!");
alBufferSamplesSOFT = alGetProcAddress("alBufferSamplesSOFT");
alIsBufferFormatSupportedSOFT = alGetProcAddress("alIsBufferFormatSupportedSOFT");
}
else
{
LOG("WARNING - AL_SOFT_buffer_samples extension not supported... Proceeding anyway...");
}
if ((*sound_struct = malloc(sizeof(SoundSystemStruct))) == NULL)
{
ERRLOG("OOPS, Not enough memory");
break;
}
(*sound_struct)->implementation_specific = ctx;
(*sound_struct)->CreateSoundBuffer = OpenALCreateSoundBuffer;
(*sound_struct)->DestroySoundBuffer = OpenALDestroySoundBuffer;
return 0;
} while(0);
// ERRQUIT
if (*sound_struct)
{
Free(*sound_struct);
}
return -1;
}
long SoundSystemDestroy(SoundSystemStruct **sound_struct)
{
// ugly assumption : this sets the extern g_lpDS ...
assert(*sound_struct != NULL);
ALCcontext *ctx = (ALCcontext*) (*sound_struct)->implementation_specific;
assert(ctx != NULL);
(*sound_struct)->implementation_specific = NULL;
Free(*sound_struct);
CloseAL();
return 0;
}
long SoundSystemEnumerate(char ***device_list, const int limit)
{
assert(*device_list == NULL);
*device_list = malloc(sizeof(char*)*2);
(*device_list)[0] = strdup("unused-by-OpenAL");
unsigned int num_devices = 1;
(*device_list)[num_devices] = NULL; // sentinel
return num_devices;
}
// ----------------------------------------------------------------------------
/* Destroys a voice object, deleting the source and buffers. No error handling
* since these calls shouldn't fail with a properly-made voice object. */
static void DeleteVoice(ALVoice *voice)
{
alDeleteSources(1, &voice->source);
if (alGetError() != AL_NO_ERROR)
{
ERRLOG("OOPS, Failed to delete source");
}
if (voice->data)
{
Free(voice->data);
}
ALPlayBuf *node = NULL;
while (voice->avail_buffers)
{
node = voice->avail_buffers;
alDeleteBuffers(1, &node->bufid);
voice->avail_buffers = node->_avail_next;
free(node);
}
ALPlayBuf *tmp = NULL;
HASH_ITER(hh, voice->queued_buffers, node, tmp) {
HASH_DEL(voice->queued_buffers, node);
alDeleteBuffers(1, &node->bufid);
if (alGetError() != AL_NO_ERROR)
{
ERRLOG("OOPS, Failed to delete object IDs");
}
free(node);
}
memset(voice, 0, sizeof(*voice));
free(voice);
}
/* Creates a new voice object, and allocates the needed OpenAL source and
* buffer objects. Error checking is simplified for the purposes of this
* example, and will cause an abort if needed. */
static ALVoice *NewVoice(ALBufferParamsStruct *params)
{
ALVoice *voice = NULL;
do {
voice = calloc(1, sizeof(*voice));
if (voice == NULL)
{
ERRLOG("OOPS, Out of memory!");
break;
}
ALuint buffers[OPENAL_NUM_BUFFERS];
alGenBuffers(OPENAL_NUM_BUFFERS, buffers);
if (alGetError() != AL_NO_ERROR)
{
ERRLOG("OOPS, Could not create buffers");
break;
}
alGenSources(1, &voice->source);
if (alGetError() != AL_NO_ERROR)
{
ERRLOG("OOPS, Could not create source");
break;
}
// Set parameters so mono sources play out the front-center speaker and won't distance attenuate.
alSource3i(voice->source, AL_POSITION, 0, 0, -1);
if (alGetError() != AL_NO_ERROR)
{
ERRLOG("OOPS, Could not set AL_POSITION source parameter");
break;
}
alSourcei(voice->source, AL_SOURCE_RELATIVE, AL_TRUE);
if (alGetError() != AL_NO_ERROR)
{
ERRLOG("OOPS, Could not set AL_SOURCE_RELATIVE source parameter");
break;
}
alSourcei(voice->source, AL_ROLLOFF_FACTOR, 0);
if (alGetError() != AL_NO_ERROR)
{
ERRLOG("OOPS, Could not set AL_ROLLOFF_FACTOR source parameter");
break;
}
#if 0
alSourcei(voice->source, AL_STREAMING, AL_TRUE);
if (alGetError() != AL_NO_ERROR)
{
ERRLOG("OOPS, Could not set AL_STREAMING source parameter");
break;
}
#endif
voice->avail_buffers = NULL;
for (unsigned int i=0; i<OPENAL_NUM_BUFFERS; i++)
{
ALPlayBuf immutableNode = { .bufid = buffers[i] };
ALPlayBuf *node = calloc(1, sizeof(ALPlayBuf));
if (!node)
{
ERRLOG("OOPS, Not enough memory");
break;
}
memcpy(node, &immutableNode, sizeof(ALPlayBuf));
node->_avail_next = voice->avail_buffers;
voice->avail_buffers = node;
}
voice->rate = params->lpwfxFormat->nSamplesPerSec;
// Emulator supports only mono and stereo
if (params->lpwfxFormat->nChannels == 2)
{
voice->channels = AL_STEREO_SOFT;
}
else
{
voice->channels = AL_MONO_SOFT;
}
voice->type = AL_SHORT_SOFT; // signed 16bit
voice->format = GetFormat(voice->channels, voice->type, alIsBufferFormatSupportedSOFT);
if (voice->format == 0)
{
ERRLOG("OOPS, Unsupported format (%s, %s)", ChannelsName(voice->channels), TypeName(voice->type));
break;
}
/* Allocate enough space for the temp buffer, given the format */
//voice->buffersize = FramesToBytes(params->dwBufferBytes, voice->channels, voice->type);
voice->buffersize = params->dwBufferBytes;
voice->data = malloc(voice->buffersize);
if (voice->data == NULL)
{
ERRLOG("OOPS, Error allocating %d bytes", voice->buffersize);
break;
}
LOG("\tType : 0x%08x", voice->type);
LOG("\tRate : 0x%08x", voice->rate);
LOG("\tChannels : 0x%08x", voice->channels);
LOG("\tFormat : 0x%08x", voice->format);
LOG("\tbuffersize : %d", voice->buffersize);
LOG("\tSOFT : %s", alIsBufferFormatSupportedSOFT ? "YES" : "NO");
return voice;
} while(0);
// ERR
if (voice)
{
DeleteVoice(voice);
}
return NULL;
}
// ----------------------------------------------------------------------------
static long ALGetVolume(void *_this, long *volume)
{
LOG("ALGetVolume ...");
if (volume)
{
*volume = 0;
}
return 0;
}
static long ALSetVolume(void *_this, long volume)
{
LOG("ALSetVolume ...");
return 0;
}
static long ALStop(void *_this)
{
LOG("ALStop ...");
return 0;
}
static long ALRestore(void *_this)
{
LOG("ALRestore ...");
return 0;
}
static long ALPlay(void *_this, unsigned long reserved1, unsigned long reserved2, unsigned long flags)
{
LOG("ALPlay ...");
#if 0
ALVoice *voice = (ALVoice*)_this;
int err = 0;
// Rewind the source position and clear the buffer queue
alSourceRewind(voice->source);
if((err = alGetError()) != AL_NO_ERROR)
{
ERRLOG("OOPS, alSourceRewind : 0x%08x", err);
return err;
}
alSourcei(voice->source, AL_BUFFER, 0);
if((err = alGetError()) != AL_NO_ERROR)
{
ERRLOG("OOPS, alSourcei : 0x%08x", err);
return err;
}
if (false) {
// fill buffer queue with quiet
memset(voice->data, 0x0, voice->buffersize);
int half_buffers = (OPENAL_NUM_BUFFERS>>1);
for (size_t i=0; i<half_buffers; i++)
{
alBufferSamplesSOFT(voice->buffers[i], voice->rate, voice->format,
BytesToFrames(voice->buffersize, voice->channels, voice->type),
voice->channels, voice->type, voice->data);
err = alGetError();
if (err != AL_NO_ERROR)
{
LOG("Error buffering initial samples : 0x%08x", err);
return err;
}
}
alSourceQueueBuffers(voice->source, OPENAL_NUM_BUFFERS, voice->buffers);
err = alGetError();
if (err != AL_NO_ERROR)
{
LOG("Error queueing initial buffers : 0x%08x", err);
return err;
}
alSourcePlay(voice->source);
err = alGetError();
if (err != AL_NO_ERROR)
{
LOG("Error starting playback : 0x%08x", err);
return err;
}
}
#endif
return 0;
}
static long _ALProcessPlayBuffers(ALVoice *voice, ALuint *bytes_queued)
{
ALint processed = 0;
int err = 0;
alGetSourcei(voice->source, AL_BUFFERS_PROCESSED, &processed);
if ((err = alGetError()) != AL_NO_ERROR)
{
ERRLOG("OOPS, error in checking processed buffers : 0x%08x", err);
return err;
}
if ((processed == 0) && (HASH_COUNT(voice->queued_buffers) >= OPENAL_NUM_BUFFERS))
{
LOG("All audio buffers processing...");
}
while (processed > 0)
{
--processed;
ALuint bufid = 0;
alSourceUnqueueBuffers(voice->source, 1, &bufid);
if ((err = alGetError()) != AL_NO_ERROR)
{
ERRLOG("OOPS, OpenAL error dequeuing buffer : 0x%08x", err);
return err;
}
//LOG("Attempting to dequeue %u ...", bufid);
ALPlayBuf *node = PlaylistGet(voice, bufid);
if (!node)
{
ERRLOG("OOPS, OpenAL bufid %u not found in playlist...", bufid);
ALPlayBuf *tmp = voice->queued_buffers;
unsigned int count = HASH_COUNT(voice->queued_buffers);
LOG("\t(numqueued: %d)", count);
for (unsigned int i = 0; i < count; i++, tmp = tmp->hh.next)
{
LOG("\t(bufid : %u)", tmp->bufid);
}
continue;
}
PlaylistDequeue(voice, node);
}
ALint play_offset = 0;
alGetSourcei(voice->source, AL_BYTE_OFFSET, &play_offset);
if ((err = alGetError()) != AL_NO_ERROR)
{
ERRLOG("OOPS, alGetSourcei AL_BYTE_OFFSET : 0x%08x", err);
return err;
}
assert((play_offset >= 0) && (play_offset < voice->buffersize));
long q = voice->_queued_total_bytes/* + voice->index*/ - play_offset;
assert(q >= 0);
*bytes_queued = (ALuint)q;
return 0;
}
// returns queued+working sound buffer size in bytes
static long ALGetPosition(void *_this, unsigned long *bytes_queued, unsigned long *unused_write_cursor)
{
ALVoice *voice = (ALVoice*)_this;
*bytes_queued = 0;
*unused_write_cursor = 0;
ALuint queued = 0;
int err = _ALProcessPlayBuffers(voice, &queued);
if (err)
{
return err;
}
static int last_queued = 0;
if (queued != last_queued)
{
last_queued = queued;
//LOG("OpenAL bytes queued : %u", queued);
}
*bytes_queued = queued + voice->index;
return 0;
}
// DS->Lock()
static long ALBegin(void *_this, unsigned long unused, unsigned long write_bytes, void **audio_ptr1, unsigned long *audio_bytes1, void **unused_audio_ptr2, unsigned long *unused_audio_bytes2, unsigned long flags_unused)
{
ALVoice *voice = (ALVoice*)_this;
if (unused_audio_ptr2)
{
*unused_audio_ptr2 = NULL;
}
if (write_bytes == 0)
{
write_bytes = voice->buffersize;
}
ALsizei remaining = voice->buffersize - voice->index;
if (write_bytes > remaining)
{
write_bytes = remaining;
}
*audio_ptr1 = voice->data+voice->index;
*audio_bytes1 = write_bytes;
return 0;
}
static long _ALSubmitBufferToOpenAL(ALVoice *voice, ALint state)
{
int err =0;
ALPlayBuf *node = PlaylistEnqueue(voice, voice->index);
if (!node)
{
return -1;
}
//LOG("Enqueing OpenAL buffer %u (%u bytes)", node->bufid, node->bytes);
alBufferSamplesSOFT(node->bufid, voice->rate, voice->format,
BytesToFrames(node->bytes, voice->channels, voice->type),
voice->channels, voice->type, voice->data);
if ((err = alGetError()) != AL_NO_ERROR)
{
PlaylistDequeue(voice, node);
ERRLOG("OOPS, Error alBufferSamplesSOFT : 0x%08x", err);
return err;
}
alSourceQueueBuffers(voice->source, 1, &node->bufid);
if ((err = alGetError()) != AL_NO_ERROR)
{
PlaylistDequeue(voice, node);
ERRLOG("OOPS, Error buffering data : 0x%08x", err);
return err;
}
if ((state != AL_PLAYING) && (state != AL_PAUSED))
{
LOG("Restarting playback (was 0x%08x) ...", state);
alSourcePlay(voice->source);
if ((err = alGetError()) != AL_NO_ERROR)
{
LOG("Error starting playback : 0x%08x", err);
return err;
}
}
return 0;
}
// DS->Unlock()
static long ALCommit(void *_this, void *unused_audio_ptr1, unsigned long audio_bytes1, void *unused_audio_ptr2, unsigned long unused_audio_bytes2)
{
ALVoice *voice = (ALVoice*)_this;
int err = 0;
ALuint bytes_queued = 0;
err = _ALProcessPlayBuffers(voice, &bytes_queued);
if (err)
{
return err;
}
ALint state = 0;
alGetSourcei(voice->source, AL_SOURCE_STATE, &state);
if ((err = alGetError()) != AL_NO_ERROR)
{
ERRLOG("OOPS, Error checking source state : 0x%08x", err);
return err;
}
voice->index += audio_bytes1;
if ((state != AL_PLAYING) && (state != AL_PAUSED))
{
// OpenAL not playing -- potentially refill queue with quiet samples ...
#if 0
// NOTE : seen this fail when gdb-ing
assert(HASH_COUNT(voice->queued_buffers) == 0);
assert(voice->_queued_total_bytes == 0);
assert(bytes_queued == 0);
#endif
int quiet_size = (voice->buffersize>>2/*quarter buffersize*/) - voice->index;
if (quiet_size > 0)
{
LOG("quiet samples...");
memmove(voice->data + quiet_size, voice->data, voice->index);
memset(voice->data, 0x0, quiet_size);
voice->index += quiet_size;
}
// ... and OpenAL playback will be restarted below ...
}
if (voice->index > voice->buffersize)
{
voice->index = 0;
ERRLOG("OOPS, overflow in queued sound data");
return -1;
}
if (bytes_queued >= (voice->buffersize>>2)/*quarter buffersize*/)
{
// keep accumulating data into working buffer
return 0;
}
if (HASH_COUNT(voice->queued_buffers) >= (OPENAL_NUM_BUFFERS))
{
LOG("no free audio buffers"); // keep accumulating ...
return 0;
}
// ---------------------------
// Submit working buffer to OpenAL
err = _ALSubmitBufferToOpenAL(voice, state);
if (err)
{
return err;
}
return 0;
}
// HACK Part I : done once for mockingboard that has semiauto repeating phonemes ...
static long ALCommitStaticBuffer(void *_this, unsigned long audio_bytes1)
{
ALVoice *voice = (ALVoice*)_this;
voice->replay_index = audio_bytes1;
return 0;
}
// HACK Part II : replay mockingboard phoneme ...
static long ALReplay(void *_this)
{
ALVoice *voice = (ALVoice*)_this;
voice->index = voice->replay_index;
int err = 0;
ALint state = 0;
alGetSourcei(voice->source, AL_SOURCE_STATE, &state);
if ((err = alGetError()) != AL_NO_ERROR)
{
ERRLOG("OOPS, Error checking source state : 0x%08x", err);
return err;
}
err = _ALSubmitBufferToOpenAL(voice, state);
if (err)
{
return err;
}
return 0;
}
static long ALGetStatus(void *_this, unsigned long *status)
{
ALVoice* voice = (ALVoice*)_this;
int err = 0;
ALint state = 0;
alGetSourcei(voice->source, AL_SOURCE_STATE, &state);
if ((err = alGetError()) != AL_NO_ERROR)
{
ERRLOG("OOPS, Error checking source state : 0x%08x", err);
return err;
}
if (state == AL_PLAYING)
{
*status = DSBSTATUS_PLAYING;
}
else
{
*status = _DSBSTATUS_NOTPLAYING;
}
return 0;
}
static long OpenALCreateSoundBuffer(ALBufferParamsStruct *params, ALSoundBufferStruct **soundbuf_struct, void *extra_data)
{
LOG("OpenALCreateSoundBuffer ...");
assert(*soundbuf_struct == NULL);
const SoundSystemStruct *sound_struct = (SoundSystemStruct*)extra_data;
ALCcontext *ctx = (ALCcontext*)(sound_struct->implementation_specific);
assert(ctx != NULL);
ALVoice *voice = NULL;
int err = -1;
do {
if ((voice = NewVoice(params)) == NULL)
{
ERRLOG("OOPS, Cannot create new voice");
break;
}
if ((*soundbuf_struct = malloc(sizeof(ALSoundBufferStruct))) == NULL)
{
ERRLOG("OOPS, Not enough memory");
break;
}
(*soundbuf_struct)->_this = voice;
(*soundbuf_struct)->SetVolume = ALSetVolume;
(*soundbuf_struct)->GetVolume = ALGetVolume;
(*soundbuf_struct)->GetCurrentPosition = ALGetPosition;
(*soundbuf_struct)->Stop = ALStop;
(*soundbuf_struct)->Restore = ALRestore;
(*soundbuf_struct)->Play = ALPlay;
(*soundbuf_struct)->Lock = ALBegin;
(*soundbuf_struct)->Unlock = ALCommit;
(*soundbuf_struct)->GetStatus = ALGetStatus;
// mockingboard-specific hacks
(*soundbuf_struct)->UnlockStaticBuffer = ALCommitStaticBuffer;
(*soundbuf_struct)->Replay = ALReplay;
return 0;
} while(0);
if (*soundbuf_struct)
{
OpenALDestroySoundBuffer(soundbuf_struct);
}
else if (voice)
{
DeleteVoice(voice);
}
return -1;
}
static long OpenALDestroySoundBuffer(ALSoundBufferStruct **soundbuf_struct)
{
LOG("OpenALDestroySoundBuffer ...");
ALVoice *voice = (*soundbuf_struct)->_this;
DeleteVoice(voice);
Free(*soundbuf_struct);
return 0;
}

83
src/soundcore-openal.h Normal file
View File

@ -0,0 +1,83 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 2 or later (your choice) as published by the Free Software
* Foundation.
*
* THERE ARE NO WARRANTIES WHATSOEVER.
*
*/
#ifndef _SOUNDCORE_OPENAL_H_
#define _SOUNDCORE_OPENAL_H_
#include "common.h"
#include "soundcore.h"
#include "uthash.h"
#include <AL/al.h>
#include <AL/alc.h>
#include <AL/alext.h>
#undef DSBVOLUME_MIN
#define DSBVOLUME_MIN 0
#undef DSBVOLUME_MAX
#define DSBVOLUME_MAX 100
typedef struct IDirectSoundBuffer ALSoundBufferStruct;
typedef struct DSBUFFERDESC ALBufferParamsStruct;
/*
DWORD dwSize;
DWORD dwFlags;
DWORD dwBufferBytes;
DWORD dwReserved;
LPWAVEFORMATEX lpwfxFormat
{
WORD wFormatTag;
WORD nChannels;
DWORD nSamplesPerSec;
DWORD nAvgBytesPerSec;
WORD nBlockAlign;
WORD wBitsPerSample;
WORD cbSize;
}
*/
struct ALPlayBuf;
typedef struct ALPlayBuf {
const ALuint bufid; // the hash id
ALuint bytes; // bytes to play
UT_hash_handle hh; // make this struct hashable
struct ALPlayBuf *_avail_next;
} ALPlayBuf;
#define OPENAL_NUM_BUFFERS 4
typedef struct ALVoice {
ALuint source;
// playing data
ALPlayBuf *queued_buffers;
ALint _queued_total_bytes; // a maximum estimate -- actual value depends on OpenAL query
// working data buffer
ALbyte *data;
ALsizei index; // working buffer byte index
ALsizei buffersize; // working buffer size (and OpenAL buffersize)
// available buffers
ALPlayBuf *avail_buffers;
ALsizei replay_index;
// sample parameters
ALenum format;
ALenum channels;
ALenum type;
ALuint rate;
} ALVoice;
#endif /* whole file */

View File

@ -162,8 +162,12 @@ bool DSGetLock(LPDIRECTSOUNDBUFFER pVoice, DWORD dwOffset, DWORD dwBytes,
SHORT** ppDSLockedBuffer0, DWORD* pdwDSLockedBufferSize0,
SHORT** ppDSLockedBuffer1, DWORD* pdwDSLockedBufferSize1)
{
DWORD nStatus;
DWORD nStatus = 0;
#ifdef APPLE2IX
HRESULT hr = pVoice->GetStatus(pVoice->_this, &nStatus);
#else
HRESULT hr = pVoice->GetStatus(&nStatus);
#endif
if(hr != DS_OK)
return false;
@ -171,7 +175,11 @@ bool DSGetLock(LPDIRECTSOUNDBUFFER pVoice, DWORD dwOffset, DWORD dwBytes,
{
do
{
#ifdef APPLE2IX
hr = pVoice->Restore(pVoice->_this);
#else
hr = pVoice->Restore();
#endif
if(hr == DSERR_BUFFERLOST)
Sleep(10);
}
@ -181,7 +189,11 @@ bool DSGetLock(LPDIRECTSOUNDBUFFER pVoice, DWORD dwOffset, DWORD dwBytes,
// Get write only pointer(s) to sound buffer
if(dwBytes == 0)
{
#ifdef APPLE2IX
if(FAILED(hr = pVoice->Lock(pVoice->_this, 0, 0,
#else
if(FAILED(hr = pVoice->Lock(0, 0,
#endif
(void**)ppDSLockedBuffer0, pdwDSLockedBufferSize0,
(void**)ppDSLockedBuffer1, pdwDSLockedBufferSize1,
DSBLOCK_ENTIREBUFFER)))
@ -189,7 +201,11 @@ bool DSGetLock(LPDIRECTSOUNDBUFFER pVoice, DWORD dwOffset, DWORD dwBytes,
}
else
{
#ifdef APPLE2IX
if(FAILED(hr = pVoice->Lock(pVoice->_this, dwOffset, dwBytes,
#else
if(FAILED(hr = pVoice->Lock(dwOffset, dwBytes,
#endif
(void**)ppDSLockedBuffer0, pdwDSLockedBufferSize0,
(void**)ppDSLockedBuffer1, pdwDSLockedBufferSize1,
0)))
@ -227,6 +243,7 @@ HRESULT DSGetSoundBuffer(VOICE* pVoice, DWORD dwFlags, DWORD dwBufferSize, DWORD
if (pVoice->lpDSBvoice)
{
g_lpDS->DestroySoundBuffer(&pVoice->lpDSBvoice);
//DSReleaseSoundBuffer(pVoice);
}
HRESULT hr = g_lpDS->CreateSoundBuffer(&dsbdesc, &pVoice->lpDSBvoice, g_lpDS);
#else
@ -277,17 +294,13 @@ void DSReleaseSoundBuffer(VOICE* pVoice)
bool DSZeroVoiceBuffer(PVOICE Voice, char* pszDevName, DWORD dwBufferSize)
{
#ifdef APPLE2IX
// HACK FIXME is this function necessary?
return true;
#endif
DWORD dwDSLockedBufferSize = 0; // Size of the locked DirectSound buffer
SHORT* pDSLockedBuffer;
#ifdef APPLE2IX
DWORD argX = 0;
HRESULT hr = Voice->lpDSBvoice->Stop();
HRESULT hr = Voice->lpDSBvoice->Stop(Voice->lpDSBvoice->_this);
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "%s: DSStop failed (%08X)\n",pszDevName,hr);
@ -308,7 +321,7 @@ bool DSZeroVoiceBuffer(PVOICE Voice, char* pszDevName, DWORD dwBufferSize)
memset(pDSLockedBuffer, 0x00, dwDSLockedBufferSize);
#ifdef APPLE2IX
hr = Voice->lpDSBvoice->Unlock((void*)pDSLockedBuffer, dwDSLockedBufferSize, NULL, argX);
hr = Voice->lpDSBvoice->Unlock(Voice->lpDSBvoice->_this, (void*)pDSLockedBuffer, dwDSLockedBufferSize, NULL, argX);
#else
hr = Voice->lpDSBvoice->Unlock((void*)pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0);
#endif
@ -318,7 +331,11 @@ bool DSZeroVoiceBuffer(PVOICE Voice, char* pszDevName, DWORD dwBufferSize)
return false;
}
#ifdef APPLE2IX
hr = Voice->lpDSBvoice->Play(Voice->lpDSBvoice->_this,0,0,0);
#else
hr = Voice->lpDSBvoice->Play(0,0,DSBPLAY_LOOPING);
#endif
if(FAILED(hr))
{
if(g_fh) fprintf(g_fh, "%s: DSPlay failed (%08X)\n",pszDevName,hr);
@ -353,7 +370,11 @@ bool DSZeroVoiceWritableBuffer(PVOICE Voice, char* pszDevName, DWORD dwBufferSiz
if(pDSLockedBuffer1)
memset(pDSLockedBuffer1, 0x00, dwDSLockedBufferSize1);
#ifdef APPLE2IX
hr = Voice->lpDSBvoice->Unlock(Voice->lpDSBvoice->_this, (void*)pDSLockedBuffer0, dwDSLockedBufferSize0,
#else
hr = Voice->lpDSBvoice->Unlock((void*)pDSLockedBuffer0, dwDSLockedBufferSize0,
#endif
(void*)pDSLockedBuffer1, dwDSLockedBufferSize1);
if(FAILED(hr))
{
@ -542,6 +563,7 @@ void SoundCore_SetFade(eFADE FadeType)
// If AppleWin started by double-clicking a .dsk, then our window won't have focus when volumes are set (so gets ignored).
// Subsequent setting (to the same volume) will get ignored, as DirectSound thinks that volume is already set.
#ifndef APPLE2IX
void SoundCore_TweakVolumes()
{
for (UINT i=0; i<g_uNumVoices; i++)
@ -550,6 +572,7 @@ void SoundCore_TweakVolumes()
g_pVoices[i]->lpDSBvoice->SetVolume(g_pVoices[i]->nVolume);
}
}
#endif
//-----------------------------------------------------------------------------

View File

@ -31,6 +31,9 @@
//#define RIFF_SPKR
//#define RIFF_MB
#ifdef APPLE2IX
extern bool g_bDisableDirectSound;
#endif
typedef struct
{
@ -91,7 +94,6 @@ extern bool g_bDSAvailable;
#ifdef APPLE2IX
typedef struct IDirectSound SoundSystemStruct;
int SoundSystemInitialize();
long SoundSystemCreate(const char *sound_device, SoundSystemStruct **sound_struct);
long SoundSystemDestroy(SoundSystemStruct **sound_struct);
long SoundSystemEnumerate(char ***sound_devices, const int maxcount);

View File

@ -72,7 +72,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#define SOUND_WAVE 3
#ifdef APPLE2IX
extern bool g_bDisableDirectSound; // soundcore.h
#define g_nSPKR_NumChannels 1
#else
static const unsigned short g_nSPKR_NumChannels = 1;
@ -817,7 +816,11 @@ static ULONG Spkr_SubmitWaveBuffer_FullSpeed(short* pSpeakerBuffer, ULONG nNumSa
//bool bBufferError = false;
DWORD dwCurrentPlayCursor, dwCurrentWriteCursor;
#ifdef APPLE2IX
HRESULT hr = SpeakerVoice.lpDSBvoice->GetCurrentPosition(SpeakerVoice.lpDSBvoice->_this, &dwCurrentPlayCursor, &dwCurrentWriteCursor);
#else
HRESULT hr = SpeakerVoice.lpDSBvoice->GetCurrentPosition(&dwCurrentPlayCursor, &dwCurrentWriteCursor);
#endif
if(FAILED(hr))
return nNumSamples;
@ -967,7 +970,11 @@ static ULONG Spkr_SubmitWaveBuffer_FullSpeed(short* pSpeakerBuffer, ULONG nNumSa
}
// Commit sound buffer
#ifdef APPLE2IX
hr = SpeakerVoice.lpDSBvoice->Unlock(SpeakerVoice.lpDSBvoice->_this, (void*)pDSLockedBuffer0, dwDSLockedBufferSize0,
#else
hr = SpeakerVoice.lpDSBvoice->Unlock((void*)pDSLockedBuffer0, dwDSLockedBufferSize0,
#endif
(void*)pDSLockedBuffer1, dwDSLockedBufferSize1);
if(FAILED(hr))
return nNumSamples;
@ -1013,7 +1020,11 @@ static ULONG Spkr_SubmitWaveBuffer(short* pSpeakerBuffer, ULONG nNumSamples)
bool bBufferError = false;
DWORD dwCurrentPlayCursor, dwCurrentWriteCursor;
#ifdef APPLE2IX
HRESULT hr = SpeakerVoice.lpDSBvoice->GetCurrentPosition(SpeakerVoice.lpDSBvoice->_this, &dwCurrentPlayCursor, &dwCurrentWriteCursor);
#else
HRESULT hr = SpeakerVoice.lpDSBvoice->GetCurrentPosition(&dwCurrentPlayCursor, &dwCurrentWriteCursor);
#endif
if(FAILED(hr))
return nNumSamples;
@ -1147,7 +1158,11 @@ static ULONG Spkr_SubmitWaveBuffer(short* pSpeakerBuffer, ULONG nNumSamples)
}
// Commit sound buffer
#ifdef APPLE2IX
hr = SpeakerVoice.lpDSBvoice->Unlock(SpeakerVoice.lpDSBvoice->_this, (void*)pDSLockedBuffer0, dwDSLockedBufferSize0,
#else
hr = SpeakerVoice.lpDSBvoice->Unlock((void*)pDSLockedBuffer0, dwDSLockedBufferSize0,
#endif
(void*)pDSLockedBuffer1, dwDSLockedBufferSize1);
if(FAILED(hr))
return nNumSamples;
@ -1166,7 +1181,11 @@ void Spkr_Mute()
{
if(SpeakerVoice.bActive && !SpeakerVoice.bMute)
{
#ifdef APPLE2IX
SpeakerVoice.lpDSBvoice->SetVolume(SpeakerVoice.lpDSBvoice->_this, DSBVOLUME_MIN);
#else
SpeakerVoice.lpDSBvoice->SetVolume(DSBVOLUME_MIN);
#endif
SpeakerVoice.bMute = true;
}
}
@ -1175,7 +1194,11 @@ void Spkr_Demute()
{
if(SpeakerVoice.bActive && SpeakerVoice.bMute)
{
#ifdef APPLE2IX
SpeakerVoice.lpDSBvoice->SetVolume(SpeakerVoice.lpDSBvoice->_this, SpeakerVoice.nVolume);
#else
SpeakerVoice.lpDSBvoice->SetVolume(SpeakerVoice.nVolume);
#endif
SpeakerVoice.bMute = false;
}
}
@ -1222,7 +1245,11 @@ void SpkrSetVolume(DWORD dwVolume, DWORD dwVolumeMax)
SpeakerVoice.nVolume = NewVolume(dwVolume, dwVolumeMax);
if(SpeakerVoice.bActive)
#ifdef APPLE2IX
SpeakerVoice.lpDSBvoice->SetVolume(SpeakerVoice.lpDSBvoice->_this, SpeakerVoice.nVolume);
#else
SpeakerVoice.lpDSBvoice->SetVolume(SpeakerVoice.nVolume);
#endif
}
//=============================================================================
@ -1245,15 +1272,12 @@ bool Spkr_DSInit()
return false;
}
#ifdef APPLE2IX
// HACK FIXME TODO : need to revisit whether the below is necessary or not....
SpeakerVoice.bActive = true;
#else
if(!DSZeroVoiceBuffer(&SpeakerVoice, "Spkr", g_dwDSSpkrBufferSize))
return false;
SpeakerVoice.bActive = true;
#ifndef APPLE2IX
// Volume might've been setup from value in Registry
if(!SpeakerVoice.nVolume)
SpeakerVoice.nVolume = DSBVOLUME_MAX;
@ -1271,12 +1295,9 @@ bool Spkr_DSInit()
Sleep(200);
hr = SpeakerVoice.lpDSBvoice->GetCurrentPosition(&dwCurrentPlayCursor, &dwCurrentWriteCursor);
#ifndef APPLE2IX
char szDbg[100];
sprintf(szDbg, "[DSInit] PC=%08X, WC=%08X, Diff=%08X\n", dwCurrentPlayCursor, dwCurrentWriteCursor, dwCurrentWriteCursor-dwCurrentPlayCursor); OutputDebugString(szDbg);
#endif
}
#endif
return true;
}
@ -1285,7 +1306,11 @@ void Spkr_DSUninit()
{
if(SpeakerVoice.lpDSBvoice && SpeakerVoice.bActive)
{
#ifdef APPLE2IX
SpeakerVoice.lpDSBvoice->Stop(SpeakerVoice.lpDSBvoice->_this);
#else
SpeakerVoice.lpDSBvoice->Stop();
#endif
SpeakerVoice.bActive = false;
}

View File

@ -21,6 +21,7 @@
#include "cpu.h"
#include "speaker.h"
#include "keys.h"
#include "mockingboard.h"
#define EXECUTION_PERIOD_NSECS 1000000 // AppleWin: nExecutionPeriodUsec
@ -33,6 +34,8 @@ static bool alt_speed_enabled = false;
double cpu_scale_factor = 1.0;
double cpu_altscale_factor = 1.0;
uint8_t emul_reinitialize;
static unsigned int g_nCyclesExecuted; // # of cycles executed up to last IO access
// -----------------------------------------------------------------------------
@ -168,6 +171,8 @@ void cpu_thread(void *dummyptr) {
LOG("cpu_thread : begin main loop ...");
clock_gettime(CLOCK_MONOTONIC, &t0);
emul_reinitialize = 1;
do {
// -LOCK----------------------------------------------------------------------------------------- SAMPLE ti
pthread_mutex_lock(&interface_mutex);
@ -195,9 +200,11 @@ void cpu_thread(void *dummyptr) {
cpu65_cycle_count = 0;
g_nCyclesExecuted = 0;
//MB_StartOfCpuExecute();
MB_StartOfCpuExecute();
cpu65_run(); // run emulation for cpu65_cycles_to_execute cycles ...
cycles_adjust = cpu65_cycles_to_execute; // counter is decremented in cpu65_run()
if (cycles_adjust < 0)
{
@ -205,7 +212,7 @@ void cpu_thread(void *dummyptr) {
}
unsigned int uExecutedCycles = cpu65_cycle_count;
//MB_UpdateCycles(uExecutedCycles); // Update 6522s (NB. Do this before updating g_nCumulativeCycles below)
MB_UpdateCycles(uExecutedCycles); // Update 6522s (NB. Do this before updating g_nCumulativeCycles below)
// N.B.: IO calls that depend on accurate timing will update g_nCyclesExecuted
const unsigned int nRemainingCycles = uExecutedCycles - g_nCyclesExecuted;
@ -216,6 +223,9 @@ void cpu_thread(void *dummyptr) {
SpkrUpdate(uExecutedCycles); // play audio
}
// N.B.: technically this is not the end of the video frame...
MB_EndOfVideoFrame();
clock_gettime(CLOCK_MONOTONIC, &tj);
pthread_mutex_unlock(&interface_mutex);
// -UNLOCK--------------------------------------------------------------------------------------- SAMPLE tj
@ -253,7 +263,7 @@ void cpu_thread(void *dummyptr) {
LOG("tick (%ld . %ld) real: (%ld . %ld)", t0.tv_sec, t0.tv_nsec, ti.tv_sec, ti.tv_nsec);
}
#endif
} while (!cpu65_do_reboot);
} while (!emul_reinitialize);
reinitialize();
} while (1);

948
src/uthash.h Normal file
View File

@ -0,0 +1,948 @@
/*
Copyright (c) 2003-2013, Troy D. Hanson http://troydhanson.github.com/uthash/
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef UTHASH_H
#define UTHASH_H
#include <string.h> /* memcmp,strlen */
#include <stddef.h> /* ptrdiff_t */
#include <stdlib.h> /* exit() */
/* These macros use decltype or the earlier __typeof GNU extension.
As decltype is only available in newer compilers (VS2010 or gcc 4.3+
when compiling c++ source) this code uses whatever method is needed
or, for VS2008 where neither is available, uses casting workarounds. */
#ifdef _MSC_VER /* MS compiler */
#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
#define DECLTYPE(x) (decltype(x))
#else /* VS2008 or older (or VS2010 in C mode) */
#define NO_DECLTYPE
#define DECLTYPE(x)
#endif
#else /* GNU, Sun and other compilers */
#define DECLTYPE(x) (__typeof(x))
#endif
#ifdef NO_DECLTYPE
#define DECLTYPE_ASSIGN(dst,src) \
do { \
char **_da_dst = (char**)(&(dst)); \
*_da_dst = (char*)(src); \
} while(0)
#else
#define DECLTYPE_ASSIGN(dst,src) \
do { \
(dst) = DECLTYPE(dst)(src); \
} while(0)
#endif
/* a number of the hash function use uint32_t which isn't defined on win32 */
#ifdef _MSC_VER
typedef unsigned int uint32_t;
typedef unsigned char uint8_t;
#else
#include <inttypes.h> /* uint32_t */
#endif
#define UTHASH_VERSION 1.9.8
#ifndef uthash_fatal
#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */
#endif
#ifndef uthash_malloc
#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
#endif
#ifndef uthash_free
#define uthash_free(ptr,sz) free(ptr) /* free fcn */
#endif
#ifndef uthash_noexpand_fyi
#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
#endif
#ifndef uthash_expand_fyi
#define uthash_expand_fyi(tbl) /* can be defined to log expands */
#endif
/* initial number of buckets */
#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */
#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */
#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */
/* calculate the element whose hash handle address is hhe */
#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
#define HASH_FIND(hh,head,keyptr,keylen,out) \
do { \
unsigned _hf_bkt,_hf_hashv; \
out=NULL; \
if (head) { \
HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \
if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \
HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \
keyptr,keylen,out); \
} \
} \
} while (0)
#ifdef HASH_BLOOM
#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM)
#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0)
#define HASH_BLOOM_MAKE(tbl) \
do { \
(tbl)->bloom_nbits = HASH_BLOOM; \
(tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \
memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \
(tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
} while (0)
#define HASH_BLOOM_FREE(tbl) \
do { \
uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
} while (0)
#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8)))
#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8)))
#define HASH_BLOOM_ADD(tbl,hashv) \
HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
#define HASH_BLOOM_TEST(tbl,hashv) \
HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
#else
#define HASH_BLOOM_MAKE(tbl)
#define HASH_BLOOM_FREE(tbl)
#define HASH_BLOOM_ADD(tbl,hashv)
#define HASH_BLOOM_TEST(tbl,hashv) (1)
#define HASH_BLOOM_BYTELEN 0
#endif
#define HASH_MAKE_TABLE(hh,head) \
do { \
(head)->hh.tbl = (UT_hash_table*)uthash_malloc( \
sizeof(UT_hash_table)); \
if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \
memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \
(head)->hh.tbl->tail = &((head)->hh); \
(head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
(head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
(head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
(head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \
memset((head)->hh.tbl->buckets, 0, \
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
HASH_BLOOM_MAKE((head)->hh.tbl); \
(head)->hh.tbl->signature = HASH_SIGNATURE; \
} while(0)
#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add)
#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \
do { \
replaced=NULL; \
HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \
if (replaced!=NULL) { \
HASH_DELETE(hh,head,replaced); \
}; \
HASH_ADD(hh,head,fieldname,keylen_in,add); \
} while(0)
#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
do { \
unsigned _ha_bkt; \
(add)->hh.next = NULL; \
(add)->hh.key = (char*)(keyptr); \
(add)->hh.keylen = (unsigned)(keylen_in); \
if (!(head)) { \
head = (add); \
(head)->hh.prev = NULL; \
HASH_MAKE_TABLE(hh,head); \
} else { \
(head)->hh.tbl->tail->next = (add); \
(add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
(head)->hh.tbl->tail = &((add)->hh); \
} \
(head)->hh.tbl->num_items++; \
(add)->hh.tbl = (head)->hh.tbl; \
HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \
(add)->hh.hashv, _ha_bkt); \
HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \
HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \
HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \
HASH_FSCK(hh,head); \
} while(0)
#define HASH_TO_BKT( hashv, num_bkts, bkt ) \
do { \
bkt = ((hashv) & ((num_bkts) - 1)); \
} while(0)
/* delete "delptr" from the hash table.
* "the usual" patch-up process for the app-order doubly-linked-list.
* The use of _hd_hh_del below deserves special explanation.
* These used to be expressed using (delptr) but that led to a bug
* if someone used the same symbol for the head and deletee, like
* HASH_DELETE(hh,users,users);
* We want that to work, but by changing the head (users) below
* we were forfeiting our ability to further refer to the deletee (users)
* in the patch-up process. Solution: use scratch space to
* copy the deletee pointer, then the latter references are via that
* scratch pointer rather than through the repointed (users) symbol.
*/
#define HASH_DELETE(hh,head,delptr) \
do { \
unsigned _hd_bkt; \
struct UT_hash_handle *_hd_hh_del; \
if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \
uthash_free((head)->hh.tbl->buckets, \
(head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
HASH_BLOOM_FREE((head)->hh.tbl); \
uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
head = NULL; \
} else { \
_hd_hh_del = &((delptr)->hh); \
if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \
(head)->hh.tbl->tail = \
(UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
(head)->hh.tbl->hho); \
} \
if ((delptr)->hh.prev) { \
((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \
(head)->hh.tbl->hho))->next = (delptr)->hh.next; \
} else { \
DECLTYPE_ASSIGN(head,(delptr)->hh.next); \
} \
if (_hd_hh_del->next) { \
((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \
(head)->hh.tbl->hho))->prev = \
_hd_hh_del->prev; \
} \
HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
(head)->hh.tbl->num_items--; \
} \
HASH_FSCK(hh,head); \
} while (0)
/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
#define HASH_FIND_STR(head,findstr,out) \
HASH_FIND(hh,head,findstr,strlen(findstr),out)
#define HASH_ADD_STR(head,strfield,add) \
HASH_ADD(hh,head,strfield,strlen(add->strfield),add)
#define HASH_REPLACE_STR(head,strfield,add,replaced) \
HASH_REPLACE(hh,head,strfield,strlen(add->strfield),add,replaced)
#define HASH_FIND_INT(head,findint,out) \
HASH_FIND(hh,head,findint,sizeof(int),out)
#define HASH_ADD_INT(head,intfield,add) \
HASH_ADD(hh,head,intfield,sizeof(int),add)
#define HASH_REPLACE_INT(head,intfield,add,replaced) \
HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
#define HASH_FIND_PTR(head,findptr,out) \
HASH_FIND(hh,head,findptr,sizeof(void *),out)
#define HASH_ADD_PTR(head,ptrfield,add) \
HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
#define HASH_REPLACE_PTR(head,ptrfield,add) \
HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
#define HASH_DEL(head,delptr) \
HASH_DELETE(hh,head,delptr)
/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
* This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
*/
#ifdef HASH_DEBUG
#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
#define HASH_FSCK(hh,head) \
do { \
unsigned _bkt_i; \
unsigned _count, _bkt_count; \
char *_prev; \
struct UT_hash_handle *_thh; \
if (head) { \
_count = 0; \
for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \
_bkt_count = 0; \
_thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
_prev = NULL; \
while (_thh) { \
if (_prev != (char*)(_thh->hh_prev)) { \
HASH_OOPS("invalid hh_prev %p, actual %p\n", \
_thh->hh_prev, _prev ); \
} \
_bkt_count++; \
_prev = (char*)(_thh); \
_thh = _thh->hh_next; \
} \
_count += _bkt_count; \
if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
HASH_OOPS("invalid bucket count %d, actual %d\n", \
(head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
} \
} \
if (_count != (head)->hh.tbl->num_items) { \
HASH_OOPS("invalid hh item count %d, actual %d\n", \
(head)->hh.tbl->num_items, _count ); \
} \
/* traverse hh in app order; check next/prev integrity, count */ \
_count = 0; \
_prev = NULL; \
_thh = &(head)->hh; \
while (_thh) { \
_count++; \
if (_prev !=(char*)(_thh->prev)) { \
HASH_OOPS("invalid prev %p, actual %p\n", \
_thh->prev, _prev ); \
} \
_prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
_thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \
(head)->hh.tbl->hho) : NULL ); \
} \
if (_count != (head)->hh.tbl->num_items) { \
HASH_OOPS("invalid app item count %d, actual %d\n", \
(head)->hh.tbl->num_items, _count ); \
} \
} \
} while (0)
#else
#define HASH_FSCK(hh,head)
#endif
/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
* the descriptor to which this macro is defined for tuning the hash function.
* The app can #include <unistd.h> to get the prototype for write(2). */
#ifdef HASH_EMIT_KEYS
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
do { \
unsigned _klen = fieldlen; \
write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
write(HASH_EMIT_KEYS, keyptr, fieldlen); \
} while (0)
#else
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
#endif
/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
#ifdef HASH_FUNCTION
#define HASH_FCN HASH_FUNCTION
#else
#define HASH_FCN HASH_JEN
#endif
/* The Bernstein hash function, used in Perl prior to v5.6 */
#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _hb_keylen=keylen; \
char *_hb_key=(char*)(key); \
(hashv) = 0; \
while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \
bkt = (hashv) & (num_bkts-1); \
} while (0)
/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
* http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _sx_i; \
char *_hs_key=(char*)(key); \
hashv = 0; \
for(_sx_i=0; _sx_i < keylen; _sx_i++) \
hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
bkt = hashv & (num_bkts-1); \
} while (0)
#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _fn_i; \
char *_hf_key=(char*)(key); \
hashv = 2166136261UL; \
for(_fn_i=0; _fn_i < keylen; _fn_i++) \
hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \
bkt = hashv & (num_bkts-1); \
} while(0)
#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _ho_i; \
char *_ho_key=(char*)(key); \
hashv = 0; \
for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
hashv += _ho_key[_ho_i]; \
hashv += (hashv << 10); \
hashv ^= (hashv >> 6); \
} \
hashv += (hashv << 3); \
hashv ^= (hashv >> 11); \
hashv += (hashv << 15); \
bkt = hashv & (num_bkts-1); \
} while(0)
#define HASH_JEN_MIX(a,b,c) \
do { \
a -= b; a -= c; a ^= ( c >> 13 ); \
b -= c; b -= a; b ^= ( a << 8 ); \
c -= a; c -= b; c ^= ( b >> 13 ); \
a -= b; a -= c; a ^= ( c >> 12 ); \
b -= c; b -= a; b ^= ( a << 16 ); \
c -= a; c -= b; c ^= ( b >> 5 ); \
a -= b; a -= c; a ^= ( c >> 3 ); \
b -= c; b -= a; b ^= ( a << 10 ); \
c -= a; c -= b; c ^= ( b >> 15 ); \
} while (0)
#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned _hj_i,_hj_j,_hj_k; \
unsigned char *_hj_key=(unsigned char*)(key); \
hashv = 0xfeedbeef; \
_hj_i = _hj_j = 0x9e3779b9; \
_hj_k = (unsigned)(keylen); \
while (_hj_k >= 12) { \
_hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
+ ( (unsigned)_hj_key[2] << 16 ) \
+ ( (unsigned)_hj_key[3] << 24 ) ); \
_hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
+ ( (unsigned)_hj_key[6] << 16 ) \
+ ( (unsigned)_hj_key[7] << 24 ) ); \
hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
+ ( (unsigned)_hj_key[10] << 16 ) \
+ ( (unsigned)_hj_key[11] << 24 ) ); \
\
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
\
_hj_key += 12; \
_hj_k -= 12; \
} \
hashv += keylen; \
switch ( _hj_k ) { \
case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \
case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \
case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \
case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \
case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \
case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \
case 5: _hj_j += _hj_key[4]; \
case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \
case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \
case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \
case 1: _hj_i += _hj_key[0]; \
} \
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
bkt = hashv & (num_bkts-1); \
} while(0)
/* The Paul Hsieh hash function */
#undef get16bits
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
#define get16bits(d) (*((const uint16_t *) (d)))
#endif
#if !defined (get16bits)
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
+(uint32_t)(((const uint8_t *)(d))[0]) )
#endif
#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \
do { \
unsigned char *_sfh_key=(unsigned char*)(key); \
uint32_t _sfh_tmp, _sfh_len = keylen; \
\
int _sfh_rem = _sfh_len & 3; \
_sfh_len >>= 2; \
hashv = 0xcafebabe; \
\
/* Main loop */ \
for (;_sfh_len > 0; _sfh_len--) { \
hashv += get16bits (_sfh_key); \
_sfh_tmp = (uint32_t)(get16bits (_sfh_key+2)) << 11 ^ hashv; \
hashv = (hashv << 16) ^ _sfh_tmp; \
_sfh_key += 2*sizeof (uint16_t); \
hashv += hashv >> 11; \
} \
\
/* Handle end cases */ \
switch (_sfh_rem) { \
case 3: hashv += get16bits (_sfh_key); \
hashv ^= hashv << 16; \
hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)] << 18); \
hashv += hashv >> 11; \
break; \
case 2: hashv += get16bits (_sfh_key); \
hashv ^= hashv << 11; \
hashv += hashv >> 17; \
break; \
case 1: hashv += *_sfh_key; \
hashv ^= hashv << 10; \
hashv += hashv >> 1; \
} \
\
/* Force "avalanching" of final 127 bits */ \
hashv ^= hashv << 3; \
hashv += hashv >> 5; \
hashv ^= hashv << 4; \
hashv += hashv >> 17; \
hashv ^= hashv << 25; \
hashv += hashv >> 6; \
bkt = hashv & (num_bkts-1); \
} while(0)
#ifdef HASH_USING_NO_STRICT_ALIASING
/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads.
* For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
* MurmurHash uses the faster approach only on CPU's where we know it's safe.
*
* Note the preprocessor built-in defines can be emitted using:
*
* gcc -m64 -dM -E - < /dev/null (on gcc)
* cc -## a.c (where a.c is a simple test file) (Sun Studio)
*/
#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86))
#define MUR_GETBLOCK(p,i) p[i]
#else /* non intel */
#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0)
#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1)
#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2)
#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3)
#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL))
#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__))
#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24))
#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16))
#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8))
#else /* assume little endian non-intel */
#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24))
#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16))
#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8))
#endif
#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \
(MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \
(MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \
MUR_ONE_THREE(p))))
#endif
#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
#define MUR_FMIX(_h) \
do { \
_h ^= _h >> 16; \
_h *= 0x85ebca6b; \
_h ^= _h >> 13; \
_h *= 0xc2b2ae35l; \
_h ^= _h >> 16; \
} while(0)
#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \
do { \
const uint8_t *_mur_data = (const uint8_t*)(key); \
const int _mur_nblocks = (keylen) / 4; \
uint32_t _mur_h1 = 0xf88D5353; \
uint32_t _mur_c1 = 0xcc9e2d51; \
uint32_t _mur_c2 = 0x1b873593; \
uint32_t _mur_k1 = 0; \
const uint8_t *_mur_tail; \
const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \
int _mur_i; \
for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \
_mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \
_mur_k1 *= _mur_c1; \
_mur_k1 = MUR_ROTL32(_mur_k1,15); \
_mur_k1 *= _mur_c2; \
\
_mur_h1 ^= _mur_k1; \
_mur_h1 = MUR_ROTL32(_mur_h1,13); \
_mur_h1 = _mur_h1*5+0xe6546b64; \
} \
_mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \
_mur_k1=0; \
switch((keylen) & 3) { \
case 3: _mur_k1 ^= _mur_tail[2] << 16; \
case 2: _mur_k1 ^= _mur_tail[1] << 8; \
case 1: _mur_k1 ^= _mur_tail[0]; \
_mur_k1 *= _mur_c1; \
_mur_k1 = MUR_ROTL32(_mur_k1,15); \
_mur_k1 *= _mur_c2; \
_mur_h1 ^= _mur_k1; \
} \
_mur_h1 ^= (keylen); \
MUR_FMIX(_mur_h1); \
hashv = _mur_h1; \
bkt = hashv & (num_bkts-1); \
} while(0)
#endif /* HASH_USING_NO_STRICT_ALIASING */
/* key comparison function; return 0 if keys equal */
#define HASH_KEYCMP(a,b,len) memcmp(a,b,len)
/* iterate over items in a known bucket to find desired item */
#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \
do { \
if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \
else out=NULL; \
while (out) { \
if ((out)->hh.keylen == keylen_in) { \
if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \
} \
if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \
else out = NULL; \
} \
} while(0)
/* add an item to a bucket */
#define HASH_ADD_TO_BKT(head,addhh) \
do { \
head.count++; \
(addhh)->hh_next = head.hh_head; \
(addhh)->hh_prev = NULL; \
if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \
(head).hh_head=addhh; \
if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \
&& (addhh)->tbl->noexpand != 1) { \
HASH_EXPAND_BUCKETS((addhh)->tbl); \
} \
} while(0)
/* remove an item from a given bucket */
#define HASH_DEL_IN_BKT(hh,head,hh_del) \
(head).count--; \
if ((head).hh_head == hh_del) { \
(head).hh_head = hh_del->hh_next; \
} \
if (hh_del->hh_prev) { \
hh_del->hh_prev->hh_next = hh_del->hh_next; \
} \
if (hh_del->hh_next) { \
hh_del->hh_next->hh_prev = hh_del->hh_prev; \
}
/* Bucket expansion has the effect of doubling the number of buckets
* and redistributing the items into the new buckets. Ideally the
* items will distribute more or less evenly into the new buckets
* (the extent to which this is true is a measure of the quality of
* the hash function as it applies to the key domain).
*
* With the items distributed into more buckets, the chain length
* (item count) in each bucket is reduced. Thus by expanding buckets
* the hash keeps a bound on the chain length. This bounded chain
* length is the essence of how a hash provides constant time lookup.
*
* The calculation of tbl->ideal_chain_maxlen below deserves some
* explanation. First, keep in mind that we're calculating the ideal
* maximum chain length based on the *new* (doubled) bucket count.
* In fractions this is just n/b (n=number of items,b=new num buckets).
* Since the ideal chain length is an integer, we want to calculate
* ceil(n/b). We don't depend on floating point arithmetic in this
* hash, so to calculate ceil(n/b) with integers we could write
*
* ceil(n/b) = (n/b) + ((n%b)?1:0)
*
* and in fact a previous version of this hash did just that.
* But now we have improved things a bit by recognizing that b is
* always a power of two. We keep its base 2 log handy (call it lb),
* so now we can write this with a bit shift and logical AND:
*
* ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
*
*/
#define HASH_EXPAND_BUCKETS(tbl) \
do { \
unsigned _he_bkt; \
unsigned _he_bkt_i; \
struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
_he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \
memset(_he_new_buckets, 0, \
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
tbl->ideal_chain_maxlen = \
(tbl->num_items >> (tbl->log2_num_buckets+1)) + \
((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \
tbl->nonideal_items = 0; \
for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \
{ \
_he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \
while (_he_thh) { \
_he_hh_nxt = _he_thh->hh_next; \
HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \
_he_newbkt = &(_he_new_buckets[ _he_bkt ]); \
if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \
tbl->nonideal_items++; \
_he_newbkt->expand_mult = _he_newbkt->count / \
tbl->ideal_chain_maxlen; \
} \
_he_thh->hh_prev = NULL; \
_he_thh->hh_next = _he_newbkt->hh_head; \
if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \
_he_thh; \
_he_newbkt->hh_head = _he_thh; \
_he_thh = _he_hh_nxt; \
} \
} \
uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
tbl->num_buckets *= 2; \
tbl->log2_num_buckets++; \
tbl->buckets = _he_new_buckets; \
tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \
(tbl->ineff_expands+1) : 0; \
if (tbl->ineff_expands > 1) { \
tbl->noexpand=1; \
uthash_noexpand_fyi(tbl); \
} \
uthash_expand_fyi(tbl); \
} while(0)
/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
/* Note that HASH_SORT assumes the hash handle name to be hh.
* HASH_SRT was added to allow the hash handle name to be passed in. */
#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
#define HASH_SRT(hh,head,cmpfcn) \
do { \
unsigned _hs_i; \
unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
if (head) { \
_hs_insize = 1; \
_hs_looping = 1; \
_hs_list = &((head)->hh); \
while (_hs_looping) { \
_hs_p = _hs_list; \
_hs_list = NULL; \
_hs_tail = NULL; \
_hs_nmerges = 0; \
while (_hs_p) { \
_hs_nmerges++; \
_hs_q = _hs_p; \
_hs_psize = 0; \
for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \
_hs_psize++; \
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
((void*)((char*)(_hs_q->next) + \
(head)->hh.tbl->hho)) : NULL); \
if (! (_hs_q) ) break; \
} \
_hs_qsize = _hs_insize; \
while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \
if (_hs_psize == 0) { \
_hs_e = _hs_q; \
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
((void*)((char*)(_hs_q->next) + \
(head)->hh.tbl->hho)) : NULL); \
_hs_qsize--; \
} else if ( (_hs_qsize == 0) || !(_hs_q) ) { \
_hs_e = _hs_p; \
if (_hs_p){ \
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
((void*)((char*)(_hs_p->next) + \
(head)->hh.tbl->hho)) : NULL); \
} \
_hs_psize--; \
} else if (( \
cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
) <= 0) { \
_hs_e = _hs_p; \
if (_hs_p){ \
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
((void*)((char*)(_hs_p->next) + \
(head)->hh.tbl->hho)) : NULL); \
} \
_hs_psize--; \
} else { \
_hs_e = _hs_q; \
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
((void*)((char*)(_hs_q->next) + \
(head)->hh.tbl->hho)) : NULL); \
_hs_qsize--; \
} \
if ( _hs_tail ) { \
_hs_tail->next = ((_hs_e) ? \
ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \
} else { \
_hs_list = _hs_e; \
} \
if (_hs_e) { \
_hs_e->prev = ((_hs_tail) ? \
ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \
} \
_hs_tail = _hs_e; \
} \
_hs_p = _hs_q; \
} \
if (_hs_tail){ \
_hs_tail->next = NULL; \
} \
if ( _hs_nmerges <= 1 ) { \
_hs_looping=0; \
(head)->hh.tbl->tail = _hs_tail; \
DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \
} \
_hs_insize *= 2; \
} \
HASH_FSCK(hh,head); \
} \
} while (0)
/* This function selects items from one hash into another hash.
* The end result is that the selected items have dual presence
* in both hashes. There is no copy of the items made; rather
* they are added into the new hash through a secondary hash
* hash handle that must be present in the structure. */
#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
do { \
unsigned _src_bkt, _dst_bkt; \
void *_last_elt=NULL, *_elt; \
UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
if (src) { \
for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
_src_hh; \
_src_hh = _src_hh->hh_next) { \
_elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
if (cond(_elt)) { \
_dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \
_dst_hh->key = _src_hh->key; \
_dst_hh->keylen = _src_hh->keylen; \
_dst_hh->hashv = _src_hh->hashv; \
_dst_hh->prev = _last_elt; \
_dst_hh->next = NULL; \
if (_last_elt_hh) { _last_elt_hh->next = _elt; } \
if (!dst) { \
DECLTYPE_ASSIGN(dst,_elt); \
HASH_MAKE_TABLE(hh_dst,dst); \
} else { \
_dst_hh->tbl = (dst)->hh_dst.tbl; \
} \
HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \
(dst)->hh_dst.tbl->num_items++; \
_last_elt = _elt; \
_last_elt_hh = _dst_hh; \
} \
} \
} \
} \
HASH_FSCK(hh_dst,dst); \
} while (0)
#define HASH_CLEAR(hh,head) \
do { \
if (head) { \
uthash_free((head)->hh.tbl->buckets, \
(head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \
HASH_BLOOM_FREE((head)->hh.tbl); \
uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
(head)=NULL; \
} \
} while(0)
#define HASH_OVERHEAD(hh,head) \
(size_t)((((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \
((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \
(sizeof(UT_hash_table)) + \
(HASH_BLOOM_BYTELEN)))
#ifdef NO_DECLTYPE
#define HASH_ITER(hh,head,el,tmp) \
for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \
el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL))
#else
#define HASH_ITER(hh,head,el,tmp) \
for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \
el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL))
#endif
/* obtain a count of items in the hash */
#define HASH_COUNT(head) HASH_CNT(hh,head)
#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0)
typedef struct UT_hash_bucket {
struct UT_hash_handle *hh_head;
unsigned count;
/* expand_mult is normally set to 0. In this situation, the max chain length
* threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
* the bucket's chain exceeds this length, bucket expansion is triggered).
* However, setting expand_mult to a non-zero value delays bucket expansion
* (that would be triggered by additions to this particular bucket)
* until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
* (The multiplier is simply expand_mult+1). The whole idea of this
* multiplier is to reduce bucket expansions, since they are expensive, in
* situations where we know that a particular bucket tends to be overused.
* It is better to let its chain length grow to a longer yet-still-bounded
* value, than to do an O(n) bucket expansion too often.
*/
unsigned expand_mult;
} UT_hash_bucket;
/* random signature used only to find hash tables in external analysis */
#define HASH_SIGNATURE 0xa0111fe1
#define HASH_BLOOM_SIGNATURE 0xb12220f2
typedef struct UT_hash_table {
UT_hash_bucket *buckets;
unsigned num_buckets, log2_num_buckets;
unsigned num_items;
struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
/* in an ideal situation (all buckets used equally), no bucket would have
* more than ceil(#items/#buckets) items. that's the ideal chain length. */
unsigned ideal_chain_maxlen;
/* nonideal_items is the number of items in the hash whose chain position
* exceeds the ideal chain maxlen. these items pay the penalty for an uneven
* hash distribution; reaching them in a chain traversal takes >ideal steps */
unsigned nonideal_items;
/* ineffective expands occur when a bucket doubling was performed, but
* afterward, more than half the items in the hash had nonideal chain
* positions. If this happens on two consecutive expansions we inhibit any
* further expansion, as it's not helping; this happens when the hash
* function isn't a good fit for the key domain. When expansion is inhibited
* the hash will still work, albeit no longer in constant time. */
unsigned ineff_expands, noexpand;
uint32_t signature; /* used only to find hash tables in external analysis */
#ifdef HASH_BLOOM
uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
uint8_t *bloom_bv;
char bloom_nbits;
#endif
} UT_hash_table;
typedef struct UT_hash_handle {
struct UT_hash_table *tbl;
void *prev; /* prev element in app order */
void *next; /* next element in app order */
struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
struct UT_hash_handle *hh_next; /* next hh in bucket order */
void *key; /* ptr to enclosing struct's key */
unsigned keylen; /* enclosing struct's key len */
unsigned hashv; /* result of hash-fcn(key) */
} UT_hash_handle;
#endif /* UTHASH_H */

65
src/win-shim.c Normal file
View File

@ -0,0 +1,65 @@
/*
* Apple // emulator for *nix
*
* This software package is subject to the GNU General Public License
* version 2 or later (your choice) as published by the Free Software
* Foundation.
*
* THERE ARE NO WARRANTIES WHATSOEVER.
*
*/
#include "common.h"
#include "win-shim.h"
pthread_t CreateThread(void* unused_lpThreadAttributes, int unused_dwStackSize, LPTHREAD_START_ROUTINE lpStartRoutine, LPVOID lpParameter, DWORD unused_dwCreationFlags, LPDWORD lpThreadId)
{
pthread_t a_thread = 0;
int err = 0;
if ((err = pthread_create(&a_thread, NULL, lpStartRoutine, lpParameter)))
{
ERRLOG("pthread_create");
}
return a_thread;
}
bool SetThreadPriority(pthread_t thread, int unused_nPriority)
{
// assuming time critical ...
int policy = sched_getscheduler(getpid());
int prio = 0;
if ((prio = sched_get_priority_max(policy)) < 0)
{
ERRLOG("OOPS sched_get_priority_max");
return 0;
}
int err = 0;
if ((err = pthread_setschedprio(thread, prio)))
{
ERRLOG("OOPS pthread_setschedprio");
return 0;
}
return 1;
}
bool GetExitCodeThread(pthread_t thread, unsigned long *lpExitCode)
{
if (pthread_tryjoin_np(thread, NULL))
{
if (lpExitCode)
{
*lpExitCode = STILL_ACTIVE;
}
}
else if (lpExitCode)
{
*lpExitCode = 0;
}
return 1;
}

View File

@ -12,9 +12,12 @@
#ifndef _WINSHIM_H_
#define _WINSHIM_H_
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "common.h"
/*
* This is mostly a shim for Windows-related stuff but also contains some AppleWin-isms
*
*/
// 2013/09/19 - http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx
@ -23,15 +26,21 @@ typedef unsigned long ULONG;
typedef long LONG;
typedef long HRESULT;
typedef unsigned int UINT;
typedef uint32_t UINT32;
typedef uint64_t UINT64;
typedef bool BOOL;
typedef char TCHAR;
typedef uint8_t UCHAR;
typedef int16_t INT16;
typedef short SHORT;
typedef unsigned short USHORT;
typedef unsigned short WORD;
typedef unsigned char BYTE;
typedef long *LPLONG;
typedef void *LPVOID;
typedef void *LPDVOID;
typedef char *LPBYTE;
typedef DWORD *LPDWORD;
typedef char *GUID; // HACK
@ -48,6 +57,8 @@ typedef void *HWND; // HACK
typedef int64_t __int64;
typedef void* HANDLE;
#define VOID void
// unneeded ???
@ -64,7 +75,7 @@ extern FILE *g_fh;
#define _strdup strdup
#define _ASSERT assert
#define Sleep(x) sleep(x)
#define Sleep(x) usleep(x)
typedef void *IUnknown;
@ -75,5 +86,21 @@ typedef void *IUnknown;
#define MessageBox(window, message, group, flags) LOG("%s", message)
#define INFINITE 0
#define WAIT_OBJECT_0 0x00000000L
#define LogFileOutput(...) LOG(__VA_ARGS__)
typedef LPVOID (*LPTHREAD_START_ROUTINE)(LPVOID unused);
pthread_t CreateThread(void* unused_lpThreadAttributes, int unused_dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD unused_dwCreationFlags, LPDWORD lpThreadId);
#define THREAD_PRIORITY_NORMAL 0
#define THREAD_PRIORITY_TIME_CRITICAL 15
bool SetThreadPriority(pthread_t hThread, int nPriority);
#define STILL_ACTIVE 259
bool GetExitCodeThread(pthread_t hThread, unsigned long *lpExitCode);
#endif /* whole file */