aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuan J. Martinez <jjm@usebox.net>2021-04-17 22:05:24 +0100
committerJuan J. Martinez <jjm@usebox.net>2021-04-17 22:05:24 +0100
commita5745813e442b66ae6eed30bba81d1b3dd5cf634 (patch)
treeb302db1780350dc44543b81d49bfc1b2d4dff6a8
downloadbeeper-int-zx-a5745813e442b66ae6eed30bba81d1b3dd5cf634.tar.gz
beeper-int-zx-a5745813e442b66ae6eed30bba81d1b3dd5cf634.zip
Initial public release
-rw-r--r--.gitignore10
-rw-r--r--COPYING20
-rw-r--r--Makefile76
-rw-r--r--README.md77
-rw-r--r--example/test.binbin0 -> 20 bytes
-rw-r--r--example/test.h22
-rw-r--r--example/test.sfx5
-rw-r--r--main.cpp414
-rw-r--r--player.h39
-rw-r--r--player/Makefile20
-rw-r--r--player/README.md13
-rwxr-xr-xplayer/bin2h.py40
-rw-r--r--player/player.z8093
-rw-r--r--sdcc/Makefile13
-rw-r--r--sdcc/README.MD23
-rw-r--r--sdcc/beeper.h49
-rw-r--r--sdcc/beeper.z80158
-rw-r--r--sfx.c356
-rw-r--r--sfx.h46
-rw-r--r--zymosis.c1985
-rw-r--r--zymosis.h214
21 files changed, 3673 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..983f42d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+*.o
+*.swp
+*~
+*.rel
+*.opt
+*.bin
+*.ap
+imgui.ini
+sfxed
+Makefile.deps
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..47867bc
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,20 @@
+Beeper engine
+Copyright (C) 2021 by Juan J. Martinez <jjm@usebox.net>
+
+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.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1f5add0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,76 @@
+# CONFIG
+IMGUI_DIR = $(HOME)/src/imgui
+IMGUI_FILE_DIALOG_DIR = $(HOME)/src/ImGuiFileDialog
+# END OF CONFIG
+
+TAG := $(shell git describe --abbrev=0 --tags ${TAG_COMMIT} 2>/dev/null || true)
+COMMIT := $(shell git rev-parse --short HEAD)
+DATE := $(shell git log -1 --format=%cd --date=format:"%Y%m%d")
+VERSION := $(TAG)
+
+ifeq ($(VERSION),)
+ VERSION := dev-$(COMMIT) ($(DATE))
+else
+ VERSION += ($(DATE))
+endif
+
+CFLAGS = -O2 -s -Wall `sdl2-config --cflags`
+CXXFLAGS = -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends -I$(IMGUI_FILE_DIALOG_DIR)
+CXXFLAGS += -O2 -s -Wall `sdl2-config --cflags` -DAPP_VERSION="\"$(VERSION)\""
+LIBS = -lGL -ldl `sdl2-config --libs`
+
+# cross-build for windows
+ifeq ($(CROSS_BUILD), Win) #LINUX
+ CC = i686-w64-mingw32-gcc
+ CXX = i686-w64-mingw32-g++
+ CFLAGS += -D__USE_MINGW_ANSI_STDIO
+ LIBS = -lgdi32 -lopengl32 -limm32 `sdl2-config --libs --static-libs` -static
+endif
+
+SOURCES = main.cpp sfx.c
+SOURCES += zymosis.c
+SOURCES += $(IMGUI_DIR)/imgui.cpp $(IMGUI_DIR)/imgui_draw.cpp $(IMGUI_DIR)/imgui_tables.cpp $(IMGUI_DIR)/imgui_widgets.cpp
+SOURCES += $(IMGUI_DIR)/backends/imgui_impl_sdl.cpp $(IMGUI_DIR)/backends/imgui_impl_opengl3.cpp
+SOURCES += $(IMGUI_FILE_DIALOG_DIR)/ImGuiFileDialog.cpp
+
+SOURCES += $(IMGUI_DIR)/examples/libs/gl3w/GL/gl3w.c
+CXXFLAGS += -I$(IMGUI_DIR)/examples/libs/gl3w -DIMGUI_IMPL_OPENGL_LOADER_GL3W
+
+BIN = sfxed
+OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES))))
+
+all: $(BIN)
+
+%.o: %.c
+ $(CC) $(CFLAGS) $< -c -o $@
+
+%.o: %.cpp
+ $(CXX) $(CXXFLAGS) $< -c -o $@
+
+%.o:$(IMGUI_DIR)/%.cpp
+ $(CXX) $(CXXFLAGS) -c -o $@ $<
+
+%.o:$(IMGUI_FILE_DIALOG_DIR)/%.cpp
+ $(CXX) $(CXXFLAGS) -c -o $@ $<
+
+%.o:$(IMGUI_DIR)/backends/%.cpp
+ $(CXX) $(CXXFLAGS) -c -o $@ $<
+
+%.o:$(IMGUI_DIR)/examples/libs/gl3w/GL/%.c
+ $(CC) $(CXXFLAGS) -c -o $@ $<
+
+%.o:$(IMGUI_DIR)/examples/libs/glad/src/%.c
+ $(CC) $(CXXFLAGS) -c -o $@ $<
+
+sfxed: $(OBJS)
+ $(CXX) $(OBJS) $(CXXFLAGS) $(LIBS) -o $@
+
+.PHONY: clean
+clean:
+ make -C player clean
+ rm -f $(BIN) $(OBJS) Makefile.deps
+
+Makefile.deps:
+ $(CXX) $(CXXFLAGS) -MM $(SOURCES) > Makefile.deps
+
+include Makefile.deps
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..71d82e2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,77 @@
+# Beeper engine
+
+This is simple beeper engine for the ZX Spectrum 48K (or later), designed to be
+run from an interrupt handler.
+
+This project was inspired by the works of Steve Turner (of Graftgold fame) and
+Shiru. I made this for myself (to be used in my games), and I'm releasing it
+in case it is useful to someone else.
+
+The player supports three types of effects:
+
+* Silence (stops the sound)
+* Tone (square wave)
+* Noise (random)
+
+Tone and noise require:
+
+* the number of frames (ints) to play the effect
+* the frequency; the higher the value, the more time the effect will use in the int
+* an optional slide to apply to the frequency
+* the next sound to play in a chain, or zero to stop (silence)
+
+It is possible to chain multiple sounds and have a loop. To break the loop,
+just play "silence" (queue effect 0) or another effect out of the loop.
+
+The beeper library is documented in `sdcc/beeper.h`, and it provides three
+functions:
+
+* `beeper_init` to set the effect data and initialize the engine
+* `beeper_queue` to schedule an effect to be played starting on next interrupt
+ (0 for silence, the effects start on 1)
+* `beeper_play` to be called from the interrupt handler on each interrupt
+
+The engine comes with a simple GUI editor (`sfxed`) to design the effects on a PC.
+
+There are some binaries on this website: [https://github.com/reidrac/beeper-int-zx](https://github.com/reidrac/beeper-int-zx)
+
+`sfxed` uses the Zymosis Z80 CPU emulation engine to execute the player and
+collect audio samples to be played using SDL. The sound emulation should be
+accurate enough!
+
+The GUI is built with ImGui.
+
+The editor can export the effect data in binary form, so it can be included in
+any assembler project, and as a C include file (for example to be used with
+SDCC).
+
+Check the `example` directory for an example.
+
+## Building the player
+
+Check the README file in `sdcc` directory.
+
+The code is provided in assembler for SDCC, to be built as a library. It should
+be easay to convert to other assemblers.
+
+## Building the editor
+
+Edit the `Makefile` and change the following variables to point to the corerct directory:
+
+- `IMGUI_DIR`: a checkout of ImGui; see: https://github.com/ocornut/imgui
+- `IMGUI_FILE_DIALOG_DIR`: a checkout of ImGuiFileDialog "Lib_Only"; see: https://github.com/aiekick/ImGuiFileDialog
+
+To build on Linux, you'll need:
+- GNU Make, GCC
+- SDL2 for development (e.g. libsdl2-dev in Debian)
+
+When all the requirements are satisfied, just run `make`.
+
+## License
+
+This software is distributed under MIT license. See COPYING file.
+
+**TL;DR**: the only condition is that you are required to preserve the copyright
+and license notices. Licensed works, modifications, and larger works may be
+distributed under different terms and without source code; this includes any game
+made with the help of this software.
diff --git a/example/test.bin b/example/test.bin
new file mode 100644
index 0000000..e906a2e
--- /dev/null
+++ b/example/test.bin
Binary files differ
diff --git a/example/test.h b/example/test.h
new file mode 100644
index 0000000..d59e2b9
--- /dev/null
+++ b/example/test.h
@@ -0,0 +1,22 @@
+#ifndef _SFX_H
+#define _SFX_H
+
+enum sfx_enum {
+ // laser
+ SFX1 = 1,
+ // zap
+ SFX2,
+ // drill
+ SFX3,
+ // explo
+ SFX4,
+};
+
+const struct beeper_sfx sfx_table[] = {
+ { 1, 32, 120, 252, 0 },
+ { 2, 16, 12, 0, 0 },
+ { 1, 32, 1, 0, 0 },
+ { 2, 32, 128, 255, 0 },
+};
+
+#endif /* _SFX_H */
diff --git a/example/test.sfx b/example/test.sfx
new file mode 100644
index 0000000..869ce53
--- /dev/null
+++ b/example/test.sfx
@@ -0,0 +1,5 @@
+;SFXv1
+laser 1 32 120 -4 0
+zap 2 16 12 0 0
+drill 1 32 1 0 0
+explo 2 32 -128 -1 0
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..40c607c
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,414 @@
+
+// sfxed for beeper engine
+// Copyright (C) 2021 by Juan J. Martinez <jjm@usebox.net>
+//
+// 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.
+//
+#include "imgui.h"
+#include "imgui_impl_sdl.h"
+#include "imgui_impl_opengl3.h"
+
+#include "ImGuiFileDialog.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "SDL.h"
+
+#include <GL/gl3w.h>
+
+extern "C" {
+#include "sfx.h"
+}
+
+#define APP_NAME "SFX Editor"
+#define APP_URL "https://github.com/reidrac/beeper-int-zx"
+
+void show_help(char *argv0) {
+ printf("Usage: %s [options] <filename.sfx>\n\n"
+ "Available options:\n"
+ " -h, --help this help text\n"
+ " -V, --version show version and exit\n\n",
+ argv0);
+}
+
+#define MAX_ENTRIES 255
+BeeperSfx sfx[MAX_ENTRIES];
+int entries;
+
+void add_entry(uint8_t index)
+{
+ if (index != entries)
+ for (int i = entries; i > index; i--)
+ {
+ sfx[i].type = sfx[i - 1].type;
+ sfx[i].frames = sfx[i - 1].frames;
+ sfx[i].freq = sfx[i - 1].freq;
+ sfx[i].slide = sfx[i - 1].slide;
+ sfx[i].next = sfx[i - 1].next;
+ strcpy(sfx[i].name, sfx[i - 1].name);
+ if (sfx[i].next)
+ sfx[i].next++;
+ }
+
+ sfx[index].type = 1;
+ sfx[index].frames = 12;
+ sfx[index].freq = 32;
+ sfx[index].slide = 0;
+ sfx[index].next = 0;
+ strcpy(sfx[index].name, "sfx");
+ entries++;
+}
+
+void remove_entry(uint8_t index)
+{
+ if (index + 1 == entries)
+ {
+ entries--;
+ return;
+ }
+
+ for (int i = index + 1; i < entries; i++)
+ {
+ sfx[i - 1].type = sfx[i].type;
+ sfx[i - 1].frames = sfx[i].frames;
+ sfx[i - 1].freq = sfx[i].freq;
+ sfx[i - 1].slide = sfx[i].slide;
+ sfx[i - 1].next = sfx[i].next;
+ strcpy(sfx[i - 1].name, sfx[i].name);
+ }
+
+ entries--;
+ for (int i = 0; i < entries; i++)
+ if (sfx[i].next == index + 1)
+ sfx[i].next = 0;
+ else if (sfx[i].next > index + 1)
+ sfx[i].next--;
+}
+
+int main(int argc, char *argv[])
+{
+ char *filename = NULL;
+ char *error = NULL;
+
+ for (int i = 1; i < argc; i++)
+ if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help"))
+ {
+ show_help(argv[0]);
+ return 0;
+ }
+ else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version"))
+ {
+ printf(APP_NAME " " APP_VERSION "\n"
+ APP_URL "\n");
+ return 0;
+ }
+ else if (!filename)
+ filename = strdup(argv[i]);
+ else
+ {
+ fprintf(stderr, "ERROR: unsupported option '%s', try -h\n", argv[i]);
+ return 1;
+ }
+
+ entries = 0;
+ if (filename)
+ {
+ entries = load_sfx(filename, sfx, MAX_ENTRIES);
+ if (entries == -1)
+ return 1;
+ }
+ else
+ add_entry(0);
+
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO) != 0)
+ {
+ fprintf(stderr, "ERROR: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ // GL 3.0 + GLSL 130
+ const char* glsl_version = "#version 130";
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
+ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
+
+ // Create window with graphics context
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
+ SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
+ SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
+ SDL_Window* window = SDL_CreateWindow(APP_NAME " " APP_VERSION, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 750, 360, window_flags);
+ SDL_GLContext gl_context = SDL_GL_CreateContext(window);
+ SDL_GL_MakeCurrent(window, gl_context);
+ SDL_GL_SetSwapInterval(1); // Enable vsync
+
+ // Initialize OpenGL loader
+ bool err = gl3wInit() != 0;
+ if (err)
+ {
+ fprintf(stderr, "ERROR: failed to initialize OpenGL loader!\n");
+ return 1;
+ }
+
+ // Setup Dear ImGui context
+ IMGUI_CHECKVERSION();
+ ImGui::CreateContext();
+ ImGuiIO& io = ImGui::GetIO();
+ (void)io;
+ io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
+
+ // Setup Dear ImGui style
+ ImGui::StyleColorsDark();
+
+ // Setup Platform/Renderer backends
+ ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
+ ImGui_ImplOpenGL3_Init(glsl_version);
+
+ ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
+
+ const char * types_names[] = { "Silence", "Tone", "Noise" };
+
+ bool done = false;
+ while (!done)
+ {
+ SDL_Event event;
+ while (SDL_PollEvent(&event))
+ {
+ ImGui_ImplSDL2_ProcessEvent(&event);
+ if (event.type == SDL_QUIT)
+ done = true;
+ if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
+ done = true;
+ }
+
+ // Start the Dear ImGui frame
+ ImGui_ImplOpenGL3_NewFrame();
+ ImGui_ImplSDL2_NewFrame(window);
+ ImGui::NewFrame();
+
+ if (ImGuiFileDialog::Instance()->Display("OpenFileDlgKey"))
+ {
+ if (ImGuiFileDialog::Instance()->IsOk())
+ {
+ std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
+ if (filename)
+ free(filename);
+ filename = strdup(filePathName.c_str());
+ entries = load_sfx(filename, sfx, MAX_ENTRIES);
+ if (entries == -1)
+ {
+ entries = 0;
+ add_entry(0);
+ if (filename)
+ free(filename);
+ filename = NULL;
+
+ error = strdup("Failed to load the SFX file!\n\nIt is possible that the file is corrupt or is not a sfx file.");
+ }
+ }
+ ImGuiFileDialog::Instance()->Close();
+ }
+
+ if (ImGuiFileDialog::Instance()->Display("SaveAsFileDlgKey"))
+ {
+ if (ImGuiFileDialog::Instance()->IsOk())
+ {
+ std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
+ if (filename)
+ free(filename);
+ filename = strdup(filePathName.c_str());
+ if (save_sfx(filename, sfx, entries) == -1)
+ error = strdup("Failed to save the SFX file!\n\nPlease double check that you have permissions to save on that location.");
+ }
+ ImGuiFileDialog::Instance()->Close();
+ }
+
+ if (ImGuiFileDialog::Instance()->Display("ExportFileDlgKey"))
+ {
+ if (ImGuiFileDialog::Instance()->IsOk())
+ {
+ std::string filePathName = ImGuiFileDialog::Instance()->GetFilePathName();
+ if (export_sfx((char *)filePathName.c_str(), sfx, entries) == -1)
+ error = strdup("Failed to export!\n\nPlease double check that you have permissions to save on that location.");
+ }
+ ImGuiFileDialog::Instance()->Close();
+ }
+
+ ImGui::Begin(filename ? filename : "<not saved>", NULL, 0);
+
+ if (ImGui::BeginMainMenuBar())
+ {
+ if (ImGui::BeginMenu("File"))
+ {
+ if (ImGui::MenuItem("New"))
+ {
+ if (filename)
+ free(filename);
+ filename = NULL;
+ entries = 0;
+ add_entry(0);
+ }
+ if (ImGui::MenuItem("Open", ""))
+ ImGuiFileDialog::Instance()->OpenDialog("OpenFileDlgKey",
+ "Open File", ".sfx", ".");
+ if (ImGui::MenuItem("Save", "", false, filename != NULL))
+ {
+ if (save_sfx(filename, sfx, entries) == -1)
+ error = strdup("Failed to save the SFX file!\n\nPlease double check that you have permissions to save on that location.");
+ }
+ if (ImGui::MenuItem("Save As"))
+ ImGuiFileDialog::Instance()->OpenDialog("SaveAsFileDlgKey",
+ "Save As", ".sfx", ".");
+
+ ImGui::Separator();
+ if (ImGui::MenuItem("Quit", "Alt+F4"))
+ done = true;
+ ImGui::EndMenu();
+ }
+ if (ImGui::BeginMenu("Export")) {
+ if (ImGui::MenuItem("Binary"))
+ ImGuiFileDialog::Instance()->OpenDialog("ExportFileDlgKey",
+ "Export As", ".bin", ".");
+ if (ImGui::MenuItem("C include"))
+ ImGuiFileDialog::Instance()->OpenDialog("ExportFileDlgKey",
+ "Export As", ".h", ".");
+ ImGui::EndMenu();
+ }
+ ImGui::EndMainMenuBar();
+ }
+
+ if (ImGui::BeginTable("effects table", 8, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg))
+ {
+ ImGui::TableSetupColumn("No.");
+ ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 100.0f);
+ ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 120.0f);
+ ImGui::TableSetupColumn("Frames", ImGuiTableColumnFlags_WidthFixed, 60.0f);
+ ImGui::TableSetupColumn("Freq", ImGuiTableColumnFlags_WidthFixed, 60.0f);
+ ImGui::TableSetupColumn("Slide", ImGuiTableColumnFlags_WidthFixed, 60.0f);
+ ImGui::TableSetupColumn("Next", ImGuiTableColumnFlags_WidthFixed, 60.0f);
+ ImGui::TableHeadersRow();
+
+ for (int i = 0; i < entries; i++)
+ {
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("%02d", i + 1);
+
+ ImGui::PushID(i);
+
+ ImGui::TableNextColumn();
+ ImGui::PushID("name");
+ ImGui::InputText("", sfx[i].name, 9, ImGuiInputTextFlags_CharsNoBlank);
+ ImGui::PopID();
+
+ ImGui::TableNextColumn();
+ ImGui::PushID("type");
+ ImGui::Combo("", &sfx[i].type, types_names, IM_ARRAYSIZE(types_names), IM_ARRAYSIZE(types_names));
+ ImGui::PopID();
+
+ ImGui::TableNextColumn();
+ ImGui::PushID("fames");
+ ImGui::InputScalar("", ImGuiDataType_U8, &sfx[i].frames);
+ ImGui::PopID();
+ ImGui::TableNextColumn();
+ ImGui::PushID("freq");
+ ImGui::InputScalar("", ImGuiDataType_U8, &sfx[i].freq);
+ ImGui::PopID();
+ ImGui::TableNextColumn();
+ ImGui::PushID("slide");
+ ImGui::InputScalar("", ImGuiDataType_S8, &sfx[i].slide);
+ ImGui::PopID();
+ ImGui::TableNextColumn();
+ ImGui::PushID("next");
+ ImGui::InputScalar("", ImGuiDataType_U8, &sfx[i].next);
+ ImGui::PopID();
+ ImGui::TableNextColumn();
+
+ if (sfx[i].frames == 0)
+ sfx[i].frames = 1;
+ if (sfx[i].freq == 0)
+ sfx[i].freq = 1;
+
+ if (sfx[i].next == i + 1 || sfx[i].next > entries)
+ {
+ ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32(255, 0, 0, 100));
+ ImGui::Button("Play");
+ ImGui::PopStyleColor();
+ }
+ else if (ImGui::Button("Play"))
+ play_sfx(i + 1, sfx, entries);
+ ImGui::SameLine();
+ if (entries < MAX_ENTRIES)
+ {
+ if (ImGui::Button("+"))
+ add_entry(i + 1);
+ }
+ if (entries > 1)
+ {
+ ImGui::SameLine();
+ if (ImGui::Button("-"))
+ remove_entry(i);
+ }
+
+ ImGui::PopID();
+ }
+ ImGui::EndTable();
+ }
+
+ if (error && !ImGui::IsPopupOpen("Error"))
+ ImGui::OpenPopup("Error");
+ if (ImGui::BeginPopupModal("Error", NULL, ImGuiWindowFlags_AlwaysAutoResize))
+ {
+ ImGui::Text(error);
+ ImGui::Separator();
+ if (ImGui::Button("OK", ImVec2(120, 0)))
+ {
+ if (error)
+ free(error);
+ error = NULL;
+ ImGui::CloseCurrentPopup();
+ }
+ ImGui::SetItemDefaultFocus();
+ ImGui::EndPopup();
+ }
+
+ ImGui::End();
+
+ // Rendering
+ ImGui::Render();
+ glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
+ glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
+ glClear(GL_COLOR_BUFFER_BIT);
+ ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
+ SDL_GL_SwapWindow(window);
+ }
+
+ // Cleanup
+ ImGui_ImplOpenGL3_Shutdown();
+ ImGui_ImplSDL2_Shutdown();
+ ImGui::DestroyContext();
+
+ SDL_GL_DeleteContext(gl_context);
+ SDL_DestroyWindow(window);
+ SDL_Quit();
+
+ return 0;
+}
diff --git a/player.h b/player.h
new file mode 100644
index 0000000..815edcf
--- /dev/null
+++ b/player.h
@@ -0,0 +1,39 @@
+/* file: player.bin */
+#define PLAYER_LEN 221
+
+#ifdef LOCAL
+const unsigned char player[] = {
+0xf3, 0x31, 0x00, 0x00, 0xfb, 0x21, 0x00, 0x7d,
+0x22, 0xe2, 0x50, 0x21, 0x00, 0xfe, 0x36, 0xfd,
+0x5d, 0x54, 0x13, 0x01, 0x01, 0x01, 0xed, 0xb0,
+0x3e, 0xfe, 0xed, 0x47, 0xed, 0x5e, 0x21, 0xfd,
+0xfd, 0x11, 0x41, 0x50, 0x3e, 0xc3, 0x77, 0x23,
+0x73, 0x23, 0x72, 0xfb, 0x3a, 0xff, 0x7c, 0x6f,
+0xcd, 0x60, 0x50, 0x3a, 0xdd, 0x50, 0xb7, 0x20,
+0xfa, 0x76, 0x76, 0x76, 0x76, 0xd3, 0xff, 0xf3,
+0x76, 0x08, 0xe5, 0xdd, 0xe5, 0xfd, 0xe5, 0xc5,
+0xd5, 0xcd, 0x84, 0x50, 0xd1, 0xc1, 0xfd, 0xe1,
+0xdd, 0xe1, 0xe1, 0x08, 0xfb, 0xc9, 0xf3, 0x22,
+0xe2, 0x50, 0xaf, 0x32, 0xdd, 0x50, 0xfb, 0xc9,
+0xf3, 0x7d, 0xcd, 0x67, 0x50, 0xfb, 0xc9, 0x32,
+0xdd, 0x50, 0xb7, 0xc8, 0x3d, 0x2a, 0xe2, 0x50,
+0x4d, 0x44, 0x26, 0x00, 0x6f, 0x54, 0x5d, 0x29,
+0x29, 0x19, 0x09, 0x11, 0xdd, 0x50, 0x01, 0x05,
+0x00, 0xed, 0xb0, 0xc9, 0x3a, 0xdd, 0x50, 0xb7,
+0xc8, 0x3d, 0x28, 0x18, 0x3d, 0xc0, 0x3a, 0xdf,
+0x50, 0x57, 0x06, 0x00, 0xcd, 0xc8, 0x50, 0xe6,
+0x10, 0xd3, 0xfe, 0x4a, 0x05, 0x28, 0x18, 0x0d,
+0x20, 0xfa, 0x18, 0xf0, 0x3a, 0xdf, 0x50, 0x57,
+0xaf, 0x47, 0xd3, 0xfe, 0xee, 0x10, 0x4a, 0x05,
+0x28, 0x05, 0x0d, 0x20, 0xfa, 0x18, 0xf3, 0x3a,
+0xe1, 0x50, 0x21, 0xde, 0x50, 0x35, 0x28, 0xa7,
+0x3a, 0xe0, 0x50, 0x82, 0x32, 0xdf, 0x50, 0xc9,
+0x21, 0xa1, 0xf3, 0x7c, 0x1f, 0x7d, 0x1f, 0xac,
+0x67, 0x7d, 0x1f, 0x7c, 0x1f, 0xad, 0x6f, 0xac,
+0x67, 0x22, 0xc9, 0x50, 0xc9
+};
+
+#else
+extern const unsigned char player[];
+
+#endif
diff --git a/player/Makefile b/player/Makefile
new file mode 100644
index 0000000..c837be5
--- /dev/null
+++ b/player/Makefile
@@ -0,0 +1,20 @@
+all: player.bin
+
+CC = sdcc
+AS = sdasz80
+AR = sdar
+CFLAGS = -mz80 --Werror --fsigned-char --std-sdcc99 --opt-code-speed
+LDFLAGS = --no-std-crt0 --fomit-frame-pointer
+
+%.rel: %.z80
+ $(AS) -g -o $@ $<
+
+player.bin: player.rel
+ $(CC) $(CFLAGS) $(LDFLAGS) --code-loc 20480 --data-loc 0 -o player.ihx $<
+ hex2bin -p 00 player.ihx
+ ./bin2h.py player.bin player > ../player.h
+
+.PHONY: clean
+clean:
+ rm -f *.rel *.ihx *.bin *.map *.noi *.lk
+
diff --git a/player/README.md b/player/README.md
new file mode 100644
index 0000000..d9f0c96
--- /dev/null
+++ b/player/README.md
@@ -0,0 +1,13 @@
+This builds a player to be run from sfxed emulator.
+
+Changing this is not completely supported, but if you really want to do it, it
+is possible!
+
+## Build instructions
+
+This requires in your PATH:
+
+* SDCC
+* Python 3
+* hex2bin; for example [you can use this version](https://github.com/reidrac/ubox-msx-lib/tree/master/tools/hex2bin-2.0)
+
diff --git a/player/bin2h.py b/player/bin2h.py
new file mode 100755
index 0000000..d0395fd
--- /dev/null
+++ b/player/bin2h.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+
+from argparse import ArgumentParser
+
+__version__ = "1.0"
+
+
+def main():
+
+ parser = ArgumentParser(description="Bin to H converter",
+ epilog="Copyright (C) 2014-2021 Juan J Martinez <jjm@usebox.net>",
+ )
+
+ parser.add_argument("--version", action="version",
+ version="%(prog)s " + __version__)
+ parser.add_argument("file", help="file to convert")
+ parser.add_argument("id", help="variable to use")
+
+ args = parser.parse_args()
+
+ with open(args.file, "rb") as fd:
+ data = bytearray(fd.read())
+
+ data_out = ""
+ for part in range(0, len(data), 8):
+ if data_out:
+ data_out += ",\n"
+ data_out += ', '.join(["0x%02x" % b for b in data[part: part + 8]])
+
+ print("/* file: %s */" % args.file)
+ print("#define %s_LEN %d\n" % (args.id.upper(), len(data)))
+ print("#ifdef LOCAL")
+ print("const unsigned char %s[] = {\n%s\n};\n" % (args.id, data_out))
+ print("#else")
+ print("extern const unsigned char %s[];\n" % args.id)
+ print("#endif")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/player/player.z80 b/player/player.z80
new file mode 100644
index 0000000..d6fed1a
--- /dev/null
+++ b/player/player.z80
@@ -0,0 +1,93 @@
+ISR_TABLE_START = 0xfe00
+ISR_TABLE_START_LO = 0xfe
+ISR_TABLE_VALUE = 0xfd
+ISR_TABLE_START_JP = 0xfdfd
+
+EFX_TABLE_ADDR = 32000
+EFX_IN_ADDR = EFX_TABLE_ADDR - 1
+
+.area _HOME
+.area _CODE
+.area _INITIALIZER
+.area _GSINIT
+.area _GSFINAL
+
+.area _DATA
+.area _INITIALIZED
+.area _BSEG
+.area _BSS
+.area _HEAP
+
+.area _CODE
+
+_main::
+ di
+ ld sp, #0
+ ei
+
+ ld hl, #EFX_TABLE_ADDR
+ ld (sfx_data), hl
+
+ ld hl, #ISR_TABLE_START
+ ld (hl), #ISR_TABLE_VALUE
+ ld e, l
+ ld d, h
+ inc de
+ ld bc, #257
+ ldir
+
+ ld a, #ISR_TABLE_START_LO
+ ld i, a
+ im 2
+
+ ld hl, #ISR_TABLE_START_JP
+ ld de, #isr
+ ld a, #0xc3
+ ld (hl), a
+ inc hl
+ ld (hl), e
+ inc hl
+ ld (hl), d
+ ei
+
+ ld a, (EFX_IN_ADDR)
+ ld l, a
+ call _beeper_queue
+
+wait::
+ ld a, (sfx_type)
+ or a
+ jr nz, wait
+
+ halt
+ halt
+ halt
+ halt
+
+ out (0xff), a
+
+ di
+ halt
+
+isr:
+ ex af,af
+ push hl
+ push ix
+ push iy
+ push bc
+ push de
+
+ call _beeper_play
+
+ pop de
+ pop bc
+ pop iy
+ pop ix
+ pop hl
+ ex af,af
+ ei
+
+ ret
+
+.include "../sdcc/beeper.z80"
+
diff --git a/sdcc/Makefile b/sdcc/Makefile
new file mode 100644
index 0000000..3445ca7
--- /dev/null
+++ b/sdcc/Makefile
@@ -0,0 +1,13 @@
+all: beeper.lib
+
+AS=sdasz80
+AR=sdar
+
+beeper.lib: beeper.z80
+ $(AS) -o $<
+ $(AR) -rcD $@ beeper.rel
+
+.PHONY: clean
+clean:
+ rm -f *.rel *.bin *.lib
+
diff --git a/sdcc/README.MD b/sdcc/README.MD
new file mode 100644
index 0000000..7ec00a3
--- /dev/null
+++ b/sdcc/README.MD
@@ -0,0 +1,23 @@
+This is the main implementation of the beeper engine, to be used with SDCC
+compiler.
+
+The assembler syntax specific to this compiler suite, but it should be
+easy to convert to your favourite assembler.
+
+Feel free to contribute your port!
+
+## Build instructions
+
+Ensure that SDCC is in your path and run `make`.
+
+Include this directory in your include and library paths, and link with the
+beeper library.
+
+For example:
+```
+CFLAGS += -I$(BEEPER_LIB_DIR)
+LDFLAGS += -L$(BEEPER_LIB_DIR) -lbeeper
+```
+
+It is recommended that the beeper code runs from non-contended memory.
+
diff --git a/sdcc/beeper.h b/sdcc/beeper.h
new file mode 100644
index 0000000..fff5dad
--- /dev/null
+++ b/sdcc/beeper.h
@@ -0,0 +1,49 @@
+// Beeper engine
+// Copyright (C) 2021 by Juan J. Martinez <jjm@usebox.net>
+//
+// 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.
+//
+#ifndef _BEEPER_H
+#define _BEEPER_H
+
+#include <stdint.h>
+
+struct beeper_sfx {
+ uint8_t type;
+ uint8_t frames;
+ uint8_t freq;
+ uint8_t slide;
+ uint8_t next;
+};
+
+// to init the beeper engine, provide a pointer to the effect table
+void beeper_init(const struct beeper_sfx *efx_table) __z88dk_fastcall;
+
+// to queue a new effect
+// efx_no is...
+//
+// 0: no effect (stops sound)
+// 1: index 0 of the effect table
+// 2: ...
+//
+void beeper_queue(uint8_t efx_no) __z88dk_fastcall;
+
+// to be called in the INT handler; call beeper_init first!
+void beeper_play();
+#endif
diff --git a/sdcc/beeper.z80 b/sdcc/beeper.z80
new file mode 100644
index 0000000..b5b82e7
--- /dev/null
+++ b/sdcc/beeper.z80
@@ -0,0 +1,158 @@
+; Beeper engine
+; Copyright (C) 2021 by Juan J. Martinez <jjm@usebox.net>
+;
+; 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.
+;
+.globl _beeper_init
+.globl _beeper_queue
+.globl _beeper_play
+
+_beeper_init::
+ di
+ ld (sfx_data), hl
+ xor a
+ ld (sfx_type), a
+ ei
+ ret
+
+_beeper_queue::
+ di
+ ld a, l
+ call queue_next
+ ei
+ ret
+
+queue_next:
+ ld (sfx_type), a
+ or a
+ ret z
+
+ dec a
+
+ ld hl, (sfx_data)
+ ld c, l
+ ld b, h
+
+ ld h, #0
+ ld l, a
+ ld d, h
+ ld e, l
+ add hl, hl
+ add hl, hl
+ add hl, de
+ add hl, bc
+
+ ld de, #sfx_type
+ ld bc, #5
+ ldir
+ ret
+
+_beeper_play::
+ ld a, (sfx_type)
+ or a
+ ret z
+
+ dec a
+ jr z, tone
+
+ dec a
+ ; shouldn't happen!
+ ret nz
+
+ ; noise
+ ld a, (sfx_freq)
+ ld d, a
+
+ ld b, #0
+
+noise_loop:
+ call rnd
+ and #0x10
+ ; FIXME: border ?
+ out (0xfe), a
+
+ ld c, d
+noise_freq_loop:
+ dec b
+ jr z, noise_done
+ dec c
+ jr nz, noise_freq_loop
+ jr noise_loop
+
+tone:
+ ld a, (sfx_freq)
+ ld d, a
+
+ xor a
+ ld b, a
+
+tone_loop:
+ ; FIXME: border ?
+ out (0xfe), a
+ xor #0x10
+
+ ld c, d
+freq_loop:
+ dec b
+ jr z, tone_done
+ dec c
+ jr nz, freq_loop
+ jr tone_loop
+
+tone_done:
+noise_done:
+ ld a, (sfx_next)
+ ld hl, #sfx_frames
+ dec (hl)
+ jr z, queue_next
+
+ ; freq change (slide)
+ ld a, (sfx_freq_chg)
+ add d
+ ld (sfx_freq), a
+
+ ret
+
+rnd:
+ ld hl, #0xf3a1
+ ld a, h
+ rra
+ ld a, l
+ rra
+ xor h
+ ld h, a
+ ld a, l
+ rra
+ ld a, h
+ rra
+ xor l
+ ld l, a
+ xor h
+ ld h, a
+ ld (rnd + 1), hl
+ ret
+
+sfx_type: .ds 1
+sfx_frames: .ds 1
+sfx_freq: .ds 1
+sfx_freq_chg: .ds 1
+sfx_next: .ds 1
+
+sfx_data: .ds 2
+
diff --git a/sfx.c b/sfx.c
new file mode 100644
index 0000000..2008de5
--- /dev/null
+++ b/sfx.c
@@ -0,0 +1,356 @@
+// sfxed for beeper engine
+// Copyright (C) 2021 by Juan J. Martinez <jjm@usebox.net>
+//
+// 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.
+//
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "SDL.h"
+
+#include "zymosis.h"
+
+#define LOCAL
+#include "player.h"
+#undef LOCAL
+
+#include "sfx.h"
+
+#define PLAYER_ADDR 20480
+#define EFX_TABLE_ADDR 32000
+#define EFX_IN_ADDR (EFX_TABLE_ADDR - 1)
+
+// 48K timings
+#define TSTATES_PER_FRAME 69888
+#define TSTATE_STEP 16
+
+uint8_t memory[65536];
+uint8_t sound_state;
+uint8_t exit_state;
+
+#define MAX_SAMPLES (44100*5)
+uint16_t samples[MAX_SAMPLES];
+uint32_t nsamples;
+
+int load_sfx(char *filename, BeeperSfx *table, uint8_t n)
+{
+ FILE *fd;
+ char header[8];
+ uint8_t entries = 0;
+ uint8_t type;
+
+ fd = fopen(filename, "rt");
+ if (!fd)
+ {
+ fprintf(stderr, "ERROR: failed to load %s\n", filename);
+ return -1;
+ }
+
+ if (fgets(header, 7, fd) == NULL || strcmp(header, ";SFXv1"))
+ {
+ fprintf(stderr, "ERROR: %s doesn't look like a valid SFX file\n", filename);
+ return -1;
+ }
+
+ memset(table, 0, sizeof(struct beeper_sfx) * n);
+
+ while (!feof(fd))
+ {
+ if (fscanf(fd, "%8s %hhd %hhd %hhd %hhd %hhd\n",
+ table[entries].name,
+ &type,
+ &table[entries].frames,
+ &table[entries].freq,
+ &table[entries].slide,
+ &table[entries].next
+ ) != 6)
+ {
+ fprintf(stderr, "ERROR: failed to load %s\n", filename);
+ fclose(fd);
+ return -1;
+ }
+ // prevent invalid frequency
+ table[entries].freq = table[entries].freq ? table[entries].freq : 1;
+ // move to int
+ table[entries].type = type < 3 ? type : 0;
+
+ entries++;
+ if (entries == n)
+ {
+ fprintf(stderr, "WARN: read max %d entries\n", entries);
+ break;
+ }
+ }
+
+ fclose(fd);
+
+ return entries;
+}
+
+int save_sfx(char *filename, BeeperSfx *table, uint8_t n)
+{
+ FILE *fd;
+
+ fd = fopen(filename, "wt");
+ if (!fd)
+ {
+ fprintf(stderr, "ERROR: failed to save %s\n", filename);
+ return -1;
+ }
+
+ if (fprintf(fd, ";SFXv1\n") != 7)
+ {
+ fprintf(stderr, "ERROR: failed to write to %s\n", filename);
+ return -1;
+ }
+
+ for (int i = 0; i < n; i++)
+ {
+ if (fprintf(fd, "%s %hhd %hhd %hhd %hhd %hhd\n",
+ table[i].name,
+ table[i].type,
+ table[i].frames,
+ table[i].freq,
+ table[i].slide,
+ table[i].next
+ ) == -1)
+ {
+ fprintf(stderr, "ERROR: failed to write %s\n", filename);
+ fclose(fd);
+ return -1;
+ }
+ }
+
+ fclose(fd);
+
+ return n;
+}
+
+int export_c(char *filename, BeeperSfx *table, uint8_t n)
+{
+ FILE *fd;
+
+ fd = fopen(filename, "wt");
+ if (!fd)
+ {
+ fprintf(stderr, "ERROR: failed to save %s\n", filename);
+ return -1;
+ }
+
+ fprintf(fd, "#ifndef _SFX_H\n#define _SFX_H\n\n");
+
+ fprintf(fd, "enum sfx_enum {\n");
+ for (int i = 0; i < n; i++)
+ {
+ fprintf(fd, "\t// %s\n", table[i].name);
+ if (i == 0)
+ fprintf(fd, "\tSFX%d = 1,\n", i + 1);
+ else
+ fprintf(fd, "\tSFX%d,\n", i + 1);
+ }
+ fprintf(fd, "};\n\n");
+
+ fprintf(fd, "const struct beeper_sfx sfx_table[] = {\n");
+ for (int i = 0; i < n; i++)
+ {
+ if (fprintf(fd, "\t{ %hhu, %hhu, %hhu, %hhu, %hhu },\n",
+ table[i].type,
+ table[i].frames,
+ table[i].freq,
+ table[i].slide,
+ table[i].next
+ ) == -1)
+ {
+ fprintf(stderr, "ERROR: failed to write %s\n", filename);
+ fclose(fd);
+ return -1;
+ }
+ }
+ fprintf(fd, "};\n\n");
+
+ fprintf(fd, "#endif /* _SFX_H */\n");
+
+ fclose(fd);
+
+ return n;
+}
+
+int export_bin(char *filename, BeeperSfx *table, uint8_t n)
+{
+ FILE *fd;
+
+ fd = fopen(filename, "wb");
+ if (!fd)
+ {
+ fprintf(stderr, "ERROR: failed to save %s\n", filename);
+ return -1;
+ }
+
+ for (int i = 0; i < n; i++)
+ {
+ if (fwrite(&table[i].type, 1, 1, fd) != 1
+ || fwrite(&table[i].frames, 1, 1, fd) != 1
+ || fwrite(&table[i].freq, 1, 1, fd) != 1
+ || fwrite(&table[i].slide, 1, 1, fd) != 1
+ || fwrite(&table[i].next, 1, 1, fd) != 1)
+ {
+ fclose(fd);
+ fprintf(stderr, "ERROR: failed to write %s\n", filename);
+ return -1;
+ }
+ }
+
+ fclose(fd);
+
+ return n;
+}
+
+int export_sfx(char *filename, BeeperSfx *table, uint8_t n)
+{
+ if (filename[strlen(filename) - 1] == 'h')
+ return export_c(filename, table, n);
+ else
+ return export_bin(filename, table, n);
+}
+
+void z80_mem_write(Z80Info *z80, uint16_t addr, uint8_t value, Z80MemIOType mio)
+{
+ memory[addr] = value;
+}
+
+uint8_t z80_mem_read(Z80Info *z80, uint16_t addr, Z80MemIOType mio)
+{
+ return memory[addr];
+}
+
+
+uint8_t z80_port_in(Z80Info *z80, uint16_t port, Z80PIOType pio)
+{
+ return 0;
+}
+
+void z80_port_out(Z80Info *z80, uint16_t port, uint8_t value, Z80PIOType pio)
+{
+ switch (port & 0xff)
+ {
+ case 0xfe:
+ sound_state = (value & 16);
+ break;
+
+ default:
+ exit_state = 1;
+ break;
+ }
+}
+
+SDL_AudioDeviceID dev = 0;
+
+uint8_t play_samples()
+{
+ SDL_AudioSpec want, have;
+
+ SDL_memset(&want, 0, sizeof(want));
+ want.freq = 44100;
+ want.format = AUDIO_S16;
+ want.channels = 1;
+ want.samples = 4096;
+
+ if (!dev) {
+ dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
+ if (dev == 0) {
+ fprintf(stderr, "ERROR: failed to open audio: %s", SDL_GetError());
+ return 1;
+ }
+ SDL_PauseAudioDevice(dev, 0);
+ }
+ else
+ SDL_ClearQueuedAudio(dev);
+
+ if (SDL_QueueAudio(dev, samples, nsamples * 2) != 0)
+ fprintf(stderr, "ERROR: playback error: %s\n", SDL_GetError());
+
+ return 0;
+}
+
+int play_sfx(uint8_t index, BeeperSfx *table, uint8_t n)
+{
+ int i;
+ uint32_t states, real, out;
+ int32_t next_int;
+ Z80Info z80;
+ uint8_t *p;
+
+ if (index > n)
+ return 1;
+
+ memset(&z80, 0, sizeof(Z80Info));
+ memset(memory, 0, 65536);
+ memcpy(memory + PLAYER_ADDR, player, PLAYER_LEN);
+
+ p = &memory[EFX_TABLE_ADDR];
+ for (int i = 0; i < n; i++)
+ {
+ *p++ = table[i].type & 0xff;
+ *p++ = table[i].frames;
+ *p++ = table[i].freq;
+ *p++ = table[i].slide;
+ *p++ = table[i].next;
+ }
+ memory[EFX_IN_ADDR] = index;
+
+ Z80_ResetCallbacks(&z80);
+
+ z80.memReadFn = z80_mem_read;
+ z80.memWriteFn = z80_mem_write;
+ z80.portInFn = z80_port_in;
+ z80.portOutFn = z80_port_out;
+
+ Z80_Reset(&z80);
+
+ sound_state = 0;
+ exit_state = 0;
+ nsamples = 0;
+ next_int = TSTATES_PER_FRAME;
+ states = TSTATE_STEP;
+ while (!exit_state)
+ {
+ for (i = 0, out = 0; i < 4; i++)
+ {
+ real = Z80_ExecuteTS(&z80, states);
+ next_int -= real;
+
+ states = TSTATE_STEP + (TSTATE_STEP - real);
+ out += sound_state * 16384 / 16;
+
+ if (next_int < TSTATE_STEP)
+ {
+ next_int += TSTATES_PER_FRAME;
+ Z80_Interrupt(&z80);
+ }
+ }
+
+ samples[nsamples++] = out >> 2;
+
+ if (nsamples > MAX_SAMPLES)
+ break;
+ }
+
+ return play_samples();
+}
diff --git a/sfx.h b/sfx.h
new file mode 100644
index 0000000..95ae833
--- /dev/null
+++ b/sfx.h
@@ -0,0 +1,46 @@
+// sfxed for beeper engine
+// Copyright (C) 2021 by Juan J. Martinez <jjm@usebox.net>
+//
+// 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.
+//
+#ifndef _SFX_H
+#define _SFX_H
+
+#include <stdint.h>
+
+struct beeper_sfx
+{
+ //uint8_t type;
+ int type; // for the combo
+ uint8_t frames;
+ uint8_t freq;
+ uint8_t slide;
+ uint8_t next;
+
+ char name[9];
+};
+
+typedef struct beeper_sfx BeeperSfx;
+
+int load_sfx(char *filename, BeeperSfx *table, uint8_t n);
+int save_sfx(char *filename, BeeperSfx *table, uint8_t n);
+int export_sfx(char *filename, BeeperSfx *table, uint8_t n);
+int play_sfx(uint8_t index, BeeperSfx *table, uint8_t n);
+
+#endif //_SFX_H
diff --git a/zymosis.c b/zymosis.c
new file mode 100644
index 0000000..83ca583
--- /dev/null
+++ b/zymosis.c
@@ -0,0 +1,1985 @@
+/*
+ * Z80 CPU emulation engine v0.0.3b
+ * coded by Ketmar // Vampire Avalon
+ *
+ * This program is free software. It comes without any warranty, to
+ * the extent permitted by applicable law. You can redistribute it
+ * and/or modify it under the terms of the Do What The Fuck You Want
+ * To Public License, Version 2, as published by Sam Hocevar. See
+ * http://sam.zoy.org/wtfpl/COPYING for more details.
+ */
+#include <stdlib.h>
+
+#include "zymosis.h"
+
+
+/******************************************************************************/
+/* some funny tables */
+static int tablesInitialized = 0;
+static uint8_t parityTable[256];
+static uint8_t sz53Table[256]; /* bits 3, 5 and 7 of result, Z flag */
+static uint8_t sz53pTable[256]; /* bits 3, 5 and 7 of result, Z and P flags */
+
+
+/******************************************************************************/
+void Z80_InitTables (void)
+{
+ if (!tablesInitialized)
+ {
+ int f;
+ /***/
+ for (f = 0; f <= 255; ++f)
+ {
+ int n, p;
+ /***/
+ sz53Table[f] = (f & Z80_FLAG_S35);
+ for (n = f, p = 0; n != 0; n >>= 1) p ^= n & 0x01;
+ parityTable[f] = (p ? 0 : Z80_FLAG_PV);
+ sz53pTable[f] = (sz53Table[f] | parityTable[f]);
+ }
+ sz53Table[0] |= Z80_FLAG_Z;
+ sz53pTable[0] |= Z80_FLAG_Z;
+ /***/
+ tablesInitialized = 1;
+ }
+}
+
+
+void Z80_ResetCallbacks (Z80Info *z80)
+{
+ if (!tablesInitialized) Z80_InitTables();
+ z80->memReadFn = NULL;
+ z80->memWriteFn = NULL;
+ z80->contentionFn = NULL;
+ z80->portInFn = NULL;
+ z80->portOutFn = NULL;
+ z80->portContentionFn = NULL;
+ z80->retiFn = NULL;
+ z80->retnFn = NULL;
+ z80->trapEDFn = NULL;
+ z80->pagerFn = NULL;
+ z80->checkBPFn = NULL;
+}
+
+
+/* seems that all regs (and memptr) should be set to 'all 1' here, but i don't care */
+void Z80_Reset (Z80Info *z80)
+{
+ if (!tablesInitialized) Z80_InitTables();
+ z80->bc.w = z80->de.w = z80->hl.w = z80->af.w = z80->sp.w = z80->ix.w = z80->iy.w = 0;
+ z80->bcx.w = z80->dex.w = z80->hlx.w = z80->afx.w = 0;
+ z80->pc = z80->prev_pc = z80->org_pc = 0;
+ z80->memptr.w = 0;
+ z80->regI = z80->regR = 0;
+ z80->iff1 = z80->iff2 = 0;
+ z80->im = 0;
+ z80->halted = 0;
+ z80->prev_was_EIDDR = 0;
+ z80->tstates = 0;
+ z80->dd = &z80->hl;
+}
+
+
+/******************************************************************************/
+#define Z80_EXX(_z80) do { \
+ uint16_t t = (_z80)->bc.w; (_z80)->bc.w = (_z80)->bcx.w; (_z80)->bcx.w = t; \
+ t = (_z80)->de.w; (_z80)->de.w = (_z80)->dex.w; (_z80)->dex.w = t; \
+ t = (_z80)->hl.w; (_z80)->hl.w = (_z80)->hlx.w; (_z80)->hlx.w = t; \
+} while (0)
+
+#define Z80_EXAFAF(_z80) do { \
+ uint16_t t = (_z80)->af.w; (_z80)->af.w = (_z80)->afx.w; (_z80)->afx.w = t; \
+} while (0)
+
+
+/******************************************************************************/
+/* simulate contented memory access */
+/* (tstates = tstates+contention+1)*cnt */
+/* (Z80Info *z80, uint16_t addr, int tstates, Z80MemIOType mio) */
+#define Z80_Contention(_z80,_addr,_tstates,_mio) do { \
+ if ((_z80)->contentionFn != NULL) (_z80)->contentionFn((_z80), (_addr), (_tstates), (_mio)); else (_z80)->tstates += (_tstates); \
+} while (0)
+
+
+#define Z80_ContentionBy1(_z80,_addr,_cnt) do { \
+ if ((z80)->contentionFn != NULL) { \
+ int _f; \
+ for (_f = (_cnt); _f-- > 0; (_z80)->contentionFn((_z80), (_addr), 1, Z80_MREQ_NONE|Z80_MEMIO_OTHER)) ; \
+ } else { \
+ (_z80)->tstates += (_cnt); \
+ } \
+} while (0)
+
+
+#define Z80_ContentionIRBy1(_z80,_cnt) Z80_ContentionBy1((_z80), (((uint16_t)(_z80)->regI)<<8)|((_z80)->regR), (_cnt))
+#define Z80_ContentionPCBy1(_z80,_cnt) Z80_ContentionBy1((_z80), (_z80)->pc, (_cnt))
+
+
+/******************************************************************************/
+static ZYMOSIS_INLINE uint8_t Z80_PortIn (Z80Info *z80, uint16_t port)
+{
+ uint8_t value;
+ /***/
+ if (z80->portContentionFn != NULL)
+ {
+ z80->portContentionFn(z80, port, 1, Z80_PIOFLAG_IN | Z80_PIOFLAG_EARLY);
+ z80->portContentionFn(z80, port, 2, Z80_PIOFLAG_IN);
+ }
+ else
+ {
+ z80->tstates += 3;
+ }
+ value = z80->portInFn(z80, port, Z80_PIO_NORMAL);
+ ++z80->tstates;
+ return value;
+}
+
+
+static ZYMOSIS_INLINE void Z80_PortOut (Z80Info *z80, uint16_t port, uint8_t value)
+{
+ if (z80->portContentionFn != NULL)
+ {
+ z80->portContentionFn(z80, port, 1, Z80_PIOFLAG_EARLY);
+ }
+ else
+ {
+ ++z80->tstates;
+ }
+ z80->portOutFn(z80, port, value, Z80_PIO_NORMAL);
+ if (z80->portContentionFn != NULL)
+ {
+ z80->portContentionFn(z80, port, 2, 0);
+ ++z80->tstates;
+ }
+ else
+ {
+ z80->tstates += 3;
+ }
+}
+
+
+/******************************************************************************/
+#define Z80_PeekBI(_z80,_addr) (_z80)->memReadFn((_z80), (_addr), Z80_MEMIO_OTHER)
+#define Z80_PeekB(_z80,_addr) (_z80)->memReadFn((_z80), (_addr), Z80_MEMIO_DATA)
+/*#define Z80_PeekWI(_z80,_addr) (((uint16_t)Z80_PeekBI((_z80), (_addr)))|(((uint16_t)Z80_PeekBI((_z80), ((_addr)+1)&0xffff))<<8)) */
+
+#define Z80_PokeBI(_z80,_addr,_byte) (_z80)->memWriteFn((_z80), (_addr), (_byte), Z80_MEMIO_OTHER)
+#define Z80_PokeB(_z80,_addr,_byte) (_z80)->memWriteFn((_z80), (_addr), (_byte), Z80_MEMIO_DATA)
+
+/* t1: setting /MREQ & /RD */
+/* t2: memory read */
+/*#define Z80_PeekB3T(_z80,_addr) (Z80_Contention(_z80, (_addr), 3, Z80_MREQ_READ|Z80_MEMIO_DATA), Z80_PeekB(_z80, (_addr))) */
+static ZYMOSIS_INLINE uint8_t Z80_PeekB3T (Z80Info *z80, uint16_t addr)
+{
+ Z80_Contention(z80, addr, 3, Z80_MREQ_READ | Z80_MEMIO_DATA);
+ return Z80_PeekB(z80, addr);
+}
+
+static ZYMOSIS_INLINE uint8_t Z80_PeekB3TA (Z80Info *z80, uint16_t addr)
+{
+ Z80_Contention(z80, addr, 3, Z80_MREQ_READ | Z80_MEMIO_OPCARG);
+ return Z80_PeekB(z80, addr);
+}
+
+/* t1: setting /MREQ & /WR */
+/* t2: memory write */
+#define Z80_PokeB3T(_z80,_addr,_byte) do { \
+ Z80_Contention((_z80), (_addr), 3, Z80_MREQ_WRITE|Z80_MEMIO_DATA); \
+ Z80_PokeB((_z80), (_addr), (_byte)); \
+} while (0)
+
+
+static ZYMOSIS_INLINE uint16_t Z80_PeekW6T (Z80Info *z80, uint16_t addr)
+{
+ uint16_t res = Z80_PeekB3T(z80, addr);
+ return res | (((uint16_t)Z80_PeekB3T(z80, (addr + 1) & 0xffff)) << 8);
+}
+
+static ZYMOSIS_INLINE void Z80_PokeW6T (Z80Info *z80, uint16_t addr, uint16_t value)
+{
+ Z80_PokeB3T(z80, addr, value & 0xff);
+ Z80_PokeB3T(z80, (addr + 1) & 0xffff, (value >> 8) & 0xff);
+}
+
+static ZYMOSIS_INLINE void Z80_PokeW6TInv (Z80Info *z80, uint16_t addr, uint16_t value)
+{
+ Z80_PokeB3T(z80, (addr + 1) & 0xffff, (value >> 8) & 0xff);
+ Z80_PokeB3T(z80, addr, value & 0xff);
+}
+
+static ZYMOSIS_INLINE uint16_t Z80_GetWordPC (Z80Info *z80, int wait1)
+{
+ uint16_t res = Z80_PeekB3TA(z80, z80->pc);
+ /***/
+ z80->pc = (z80->pc + 1) & 0xffff;
+ res |= ((uint16_t)Z80_PeekB3TA(z80, z80->pc)) << 8;
+ if (wait1) Z80_ContentionPCBy1(z80, wait1);
+ z80->pc = (z80->pc + 1) & 0xffff;
+ return res;
+}
+
+static ZYMOSIS_INLINE uint16_t Z80_Pop6T (Z80Info *z80)
+{
+ uint16_t res = Z80_PeekB3T(z80, z80->sp.w);
+ /***/
+ z80->sp.w = (z80->sp.w + 1) & 0xffff;
+ res |= ((uint16_t)Z80_PeekB3T(z80, z80->sp.w)) << 8;
+ z80->sp.w = (z80->sp.w + 1) & 0xffff;
+ return res;
+}
+
+/* 3 T states write high byte of PC to the stack and decrement SP */
+/* 3 T states write the low byte of PC and jump to #0066 */
+static ZYMOSIS_INLINE void Z80_Push6T (Z80Info *z80, uint16_t value)
+{
+ z80->sp.w = (((int32_t)z80->sp.w) - 1) & 0xffff;
+ Z80_PokeB3T(z80, z80->sp.w, (value >> 8) & 0xff);
+ z80->sp.w = (((int32_t)z80->sp.w) - 1) & 0xffff;
+ Z80_PokeB3T(z80, z80->sp.w, value & 0xff);
+}
+
+
+/******************************************************************************/
+static ZYMOSIS_INLINE void Z80_ADC_A (Z80Info *z80, uint8_t b)
+{
+ uint16_t new, o = z80->af.a;
+ /***/
+ z80->af.a = (new = o + b + (z80->af.f & Z80_FLAG_C)) & 0xff; /* Z80_FLAG_C is 0x01, so it's safe */
+ z80->af.f =
+ sz53Table[new & 0xff] |
+ (new > 0xff ? Z80_FLAG_C : 0) |
+ ((o ^ (~b)) & (o ^ new) & 0x80 ? Z80_FLAG_PV : 0) |
+ ((o & 0x0f) + (b & 0x0f) + (z80->af.f & Z80_FLAG_C) >= 0x10 ? Z80_FLAG_H : 0);
+}
+
+static ZYMOSIS_INLINE void Z80_SBC_A (Z80Info *z80, uint8_t b)
+{
+ uint16_t new, o = z80->af.a;
+ /***/
+ z80->af.a = (new = ((int32_t)o - (int32_t)b - (int32_t)(z80->af.f & Z80_FLAG_C)) & 0xffff) & 0xff; /* Z80_FLAG_C is 0x01, so it's safe */
+ z80->af.f =
+ Z80_FLAG_N |
+ sz53Table[new & 0xff] |
+ (new > 0xff ? Z80_FLAG_C : 0) |
+ ((o ^ b) & (o ^ new) & 0x80 ? Z80_FLAG_PV : 0) |
+ ((int32_t)(o & 0x0f) - (int32_t)(b & 0x0f) - (int32_t)(z80->af.f & Z80_FLAG_C) < 0 ? Z80_FLAG_H : 0);
+}
+
+
+static ZYMOSIS_INLINE void Z80_ADD_A (Z80Info *z80, uint8_t b)
+{
+ z80->af.f &= ~Z80_FLAG_C;
+ Z80_ADC_A(z80, b);
+}
+
+static ZYMOSIS_INLINE void Z80_SUB_A (Z80Info *z80, uint8_t b)
+{
+ z80->af.f &= ~Z80_FLAG_C;
+ Z80_SBC_A(z80, b);
+}
+
+static ZYMOSIS_INLINE void Z80_CP_A (Z80Info *z80, uint8_t b)
+{
+ uint8_t o = z80->af.a, new = ((int32_t)o - (int32_t)b) & 0xff;
+ /***/
+ z80->af.f =
+ Z80_FLAG_N |
+ (new & Z80_FLAG_S) |
+ (b & Z80_FLAG_35) |
+ (new == 0 ? Z80_FLAG_Z : 0) |
+ (o < b ? Z80_FLAG_C : 0) |
+ ((o ^ b) & (o ^ new) & 0x80 ? Z80_FLAG_PV : 0) |
+ ((int32_t)(o & 0x0f) - (int32_t)(b & 0x0f) < 0 ? Z80_FLAG_H : 0);
+}
+
+
+#define Z80_AND_A(_z80,_b) ((_z80)->af.f = sz53pTable[(_z80)->af.a&=(_b)]|Z80_FLAG_H)
+#define Z80_OR_A(_z80,_b) ((_z80)->af.f = sz53pTable[(_z80)->af.a|=(_b)])
+#define Z80_XOR_A(_z80,_b) ((_z80)->af.f = sz53pTable[(_z80)->af.a^=(_b)])
+
+
+/* carry unchanged */
+static ZYMOSIS_INLINE uint8_t Z80_DEC8 (Z80Info *z80, uint8_t b)
+{
+ z80->af.f &= Z80_FLAG_C;
+ z80->af.f |= Z80_FLAG_N |
+ (b == 0x80 ? Z80_FLAG_PV : 0) |
+ (b & 0x0f ? 0 : Z80_FLAG_H) |
+ sz53Table[(((int)b) - 1) & 0xff];
+ return (((int)b) - 1) & 0xff;
+}
+
+/* carry unchanged */
+static ZYMOSIS_INLINE uint8_t Z80_INC8 (Z80Info *z80, uint8_t b)
+{
+ z80->af.f &= Z80_FLAG_C;
+ z80->af.f |=
+ (b == 0x7f ? Z80_FLAG_PV : 0) |
+ ((b + 1) & 0x0f ? 0 : Z80_FLAG_H ) |
+ sz53Table[(b + 1) & 0xff];
+ return ((b + 1) & 0xff);
+}
+
+
+/* cyclic, carry reflects shifted bit */
+static ZYMOSIS_INLINE void Z80_RLCA (Z80Info *z80)
+{
+ uint8_t c = ((z80->af.a >> 7) & 0x01);
+ /***/
+ z80->af.a = (z80->af.a << 1) | c;
+ z80->af.f = c | (z80->af.a & Z80_FLAG_35) | (z80->af.f & (Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S));
+}
+
+/* cyclic, carry reflects shifted bit */
+static ZYMOSIS_INLINE void Z80_RRCA (Z80Info *z80)
+{
+ uint8_t c = (z80->af.a & 0x01);
+ /***/
+ z80->af.a = (z80->af.a >> 1) | (c << 7);
+ z80->af.f = c | (z80->af.a & Z80_FLAG_35) | (z80->af.f & (Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S));
+}
+
+
+/* cyclic thru carry */
+static ZYMOSIS_INLINE void Z80_RLA (Z80Info *z80)
+{
+ uint8_t c = ((z80->af.a >> 7) & 0x01);
+ /***/
+ z80->af.a = (z80->af.a << 1) | (z80->af.f & Z80_FLAG_C);
+ z80->af.f = c | (z80->af.a & Z80_FLAG_35) | (z80->af.f & (Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S));
+}
+
+/* cyclic thru carry */
+static ZYMOSIS_INLINE void Z80_RRA (Z80Info *z80)
+{
+ uint8_t c = (z80->af.a & 0x01);
+ /***/
+ z80->af.a = (z80->af.a >> 1) | ((z80->af.f & Z80_FLAG_C) << 7);
+ z80->af.f = c | (z80->af.a & Z80_FLAG_35) | (z80->af.f & (Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S));
+}
+
+/* cyclic thru carry */
+static ZYMOSIS_INLINE uint8_t Z80_RL (Z80Info *z80, uint8_t b)
+{
+ uint8_t c = (b >> 7)&Z80_FLAG_C;
+ /***/
+ z80->af.f = sz53pTable[(b = ((b << 1) & 0xff) | (z80->af.f & Z80_FLAG_C))] | c;
+ return b;
+}
+
+
+static ZYMOSIS_INLINE uint8_t Z80_RR (Z80Info *z80, uint8_t b)
+{
+ uint8_t c = (b & 0x01);
+ /***/
+ z80->af.f = sz53pTable[(b = (b >> 1) | ((z80->af.f & Z80_FLAG_C) << 7))] | c;
+ return b;
+}
+
+/* cyclic, carry reflects shifted bit */
+static ZYMOSIS_INLINE uint8_t Z80_RLC (Z80Info *z80, uint8_t b)
+{
+ uint8_t c = ((b >> 7)&Z80_FLAG_C);
+ /***/
+ z80->af.f = sz53pTable[(b = ((b << 1) & 0xff) | c)] | c;
+ return b;
+}
+
+/* cyclic, carry reflects shifted bit */
+static ZYMOSIS_INLINE uint8_t Z80_RRC (Z80Info *z80, uint8_t b)
+{
+ uint8_t c = (b & 0x01);
+ /***/
+ z80->af.f = sz53pTable[(b = (b >> 1) | (c << 7))] | c;
+ return b;
+}
+
+static ZYMOSIS_INLINE uint8_t Z80_SLA (Z80Info *z80, uint8_t b)
+{
+ uint8_t c = ((b >> 7) & 0x01);
+ /***/
+ z80->af.f = sz53pTable[(b <<= 1)] | c;
+ return b;
+}
+
+static ZYMOSIS_INLINE uint8_t Z80_SRA (Z80Info *z80, uint8_t b)
+{
+ uint8_t c = (b & 0x01);
+ /***/
+ z80->af.f = sz53pTable[(b = (b >> 1) | (b & 0x80))] | c;
+ return b;
+}
+
+static ZYMOSIS_INLINE uint8_t Z80_SLL (Z80Info *z80, uint8_t b)
+{
+ uint8_t c = ((b >> 7) & 0x01);
+ /***/
+ z80->af.f = sz53pTable[(b = (b << 1) | 0x01)] | c;
+ return b;
+}
+
+static ZYMOSIS_INLINE uint8_t Z80_SLR (Z80Info *z80, uint8_t b)
+{
+ uint8_t c = (b & 0x01);
+ /***/
+ z80->af.f = sz53pTable[(b >>= 1)] | c;
+ return b;
+}
+
+
+/* ddvalue+value */
+static ZYMOSIS_INLINE uint16_t Z80_ADD_DD (Z80Info *z80, uint16_t value, uint16_t ddvalue)
+{
+ static const uint8_t hct[8] = { 0, Z80_FLAG_H, Z80_FLAG_H, Z80_FLAG_H, 0, 0, 0, Z80_FLAG_H };
+ uint32_t res = (uint32_t)value + (uint32_t)ddvalue;
+ uint8_t b = ((value & 0x0800) >> 11) | ((ddvalue & 0x0800) >> 10) | ((res & 0x0800) >> 9);
+ /***/
+ z80->memptr.w = (ddvalue + 1) & 0xffff;
+ z80->af.f =
+ (z80->af.f & (Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S)) |
+ (res > 0xffff ? Z80_FLAG_C : 0) |
+ ((res >> 8)&Z80_FLAG_35) |
+ hct[b];
+ return res;
+}
+
+/* ddvalue+value */
+static ZYMOSIS_INLINE uint16_t Z80_ADC_DD (Z80Info *z80, uint16_t value, uint16_t ddvalue)
+{
+ uint8_t c = (z80->af.f & Z80_FLAG_C);
+ uint32_t new = (uint32_t)value + (uint32_t)ddvalue + (uint32_t)c;
+ uint16_t res = (new & 0xffff);
+ /***/
+ z80->memptr.w = (ddvalue + 1) & 0xffff;
+ z80->af.f =
+ ((res >> 8)&Z80_FLAG_S35) |
+ (res == 0 ? Z80_FLAG_Z : 0) |
+ (new > 0xffff ? Z80_FLAG_C : 0) |
+ ((value ^ ((~ddvalue) & 0xffff)) & (value ^ new) & 0x8000 ? Z80_FLAG_PV : 0) |
+ ((value & 0x0fff) + (ddvalue & 0x0fff) + c >= 0x1000 ? Z80_FLAG_H : 0);
+ return res;
+}
+
+/* ddvalue-value */
+static ZYMOSIS_INLINE uint16_t Z80_SBC_DD (Z80Info *z80, uint16_t value, uint16_t ddvalue)
+{
+ uint16_t res;
+ uint8_t tmpB = z80->af.a;
+ /***/
+ z80->memptr.w = (ddvalue + 1) & 0xffff;
+ z80->af.a = ddvalue & 0xff;
+ Z80_SBC_A(z80, value & 0xff);
+ res = z80->af.a;
+ z80->af.a = (ddvalue >> 8) & 0xff;
+ Z80_SBC_A(z80, (value >> 8) & 0xff);
+ res |= (z80->af.a << 8);
+ z80->af.a = tmpB;
+ z80->af.f = (res ? z80->af.f & (~Z80_FLAG_Z) : z80->af.f | Z80_FLAG_Z);
+ return res;
+}
+
+
+static ZYMOSIS_INLINE void Z80_BIT (Z80Info *z80, uint8_t bit, uint8_t num, int mptr)
+{
+ z80->af.f =
+ Z80_FLAG_H |
+ (z80->af.f & Z80_FLAG_C) |
+ (num & Z80_FLAG_35) |
+ (num & (1 << bit) ? 0 : Z80_FLAG_PV | Z80_FLAG_Z) |
+ (bit == 7 ? num&Z80_FLAG_S : 0);
+ if (mptr) z80->af.f = (z80->af.f & ~Z80_FLAG_35) | (z80->memptr.h & Z80_FLAG_35);
+}
+
+
+static ZYMOSIS_INLINE void Z80_DAA (Z80Info *z80)
+{
+ uint8_t tmpI = 0, tmpC = (z80->af.f & Z80_FLAG_C), tmpA = z80->af.a;
+ /***/
+ if ((z80->af.f & Z80_FLAG_H) || (tmpA & 0x0f) > 9) tmpI = 6;
+ if (tmpC != 0 || tmpA > 0x99) tmpI |= 0x60;
+ if (tmpA > 0x99) tmpC = Z80_FLAG_C;
+ if (z80->af.f & Z80_FLAG_N) Z80_SUB_A(z80, tmpI);
+ else Z80_ADD_A(z80, tmpI);
+ z80->af.f = (z80->af.f & ~(Z80_FLAG_C | Z80_FLAG_PV)) | tmpC | parityTable[z80->af.a];
+}
+
+
+static ZYMOSIS_INLINE void Z80_RRD_A (Z80Info *z80)
+{
+ uint8_t tmpB = Z80_PeekB3T(z80, z80->hl.w);
+ /*IOP(4)*/
+ z80->memptr.w = (z80->hl.w + 1) & 0xffff;
+ Z80_ContentionBy1(z80, z80->hl.w, 4);
+ Z80_PokeB3T(z80, z80->hl.w, (z80->af.a << 4) | (tmpB >> 4));
+ z80->af.a = (z80->af.a & 0xf0) | (tmpB & 0x0f);
+ z80->af.f = (z80->af.f & Z80_FLAG_C) | sz53pTable[z80->af.a];
+}
+
+static ZYMOSIS_INLINE void Z80_RLD_A (Z80Info *z80)
+{
+ uint8_t tmpB = Z80_PeekB3T(z80, z80->hl.w);
+ /*IOP(4)*/
+ z80->memptr.w = (z80->hl.w + 1) & 0xffff;
+ Z80_ContentionBy1(z80, z80->hl.w, 4);
+ Z80_PokeB3T(z80, z80->hl.w, (tmpB << 4) | (z80->af.a & 0x0f));
+ z80->af.a = (z80->af.a & 0xf0) | (tmpB >> 4);
+ z80->af.f = (z80->af.f & Z80_FLAG_C) | sz53pTable[z80->af.a];
+}
+
+
+static ZYMOSIS_INLINE void Z80_LD_A_IR (Z80Info *z80, uint8_t ir)
+{
+ z80->af.a = ir;
+ z80->prev_was_EIDDR = -1;
+ Z80_ContentionIRBy1(z80, 1);
+ z80->af.f = sz53Table[z80->af.a] | (z80->af.f & Z80_FLAG_C) | (z80->iff2 ? Z80_FLAG_PV : 0);
+}
+
+
+/******************************************************************************/
+#define INC_R (z80->regR = ((z80->regR+1)&0x7f)|(z80->regR&0x80))
+
+#define SET_TRUE_CC \
+ switch ((opcode>>3)&0x07) { \
+ case 0: trueCC = (z80->af.f&Z80_FLAG_Z) == 0; break; \
+ case 1: trueCC = (z80->af.f&Z80_FLAG_Z) != 0; break; \
+ case 2: trueCC = (z80->af.f&Z80_FLAG_C) == 0; break; \
+ case 3: trueCC = (z80->af.f&Z80_FLAG_C) != 0; break; \
+ case 4: trueCC = (z80->af.f&Z80_FLAG_PV) == 0; break; \
+ case 5: trueCC = (z80->af.f&Z80_FLAG_PV) != 0; break; \
+ case 6: trueCC = (z80->af.f&Z80_FLAG_S) == 0; break; \
+ case 7: trueCC = (z80->af.f&Z80_FLAG_S) != 0; break; \
+ }
+
+#define INC_PC (z80->pc = (z80->pc+1)&0xffff)
+#define DEC_PC (z80->pc = ((int32_t)(z80->pc)-1)&0xffff)
+
+#define INC_W(n) ((n) = ((n)+1)&0xffff)
+#define DEC_W(n) ((n) = ((int32_t)(n)-1)&0xffff)
+
+#define XADD_W(n,v) ((n) = ((n)+v)&0xffff)
+#define XSUB_W(n,v) ((n) = ((int32_t)(n)-v)&0xffff)
+
+#define ZADD_W(n,v) ((n) = ((int32_t)(n)+v)&0xffff)
+
+#define ZADD_WX(n,v) (((int32_t)(n)+v)&0xffff)
+
+#define INC_B(n) ((n) = ((n)+1)&0xff)
+#define DEC_B(n) ((n) = ((int32_t)(n)-1)&0xff)
+
+/* t1: setting /MREQ & /RD */
+/* t2: memory read */
+/* t3, t4: decode command, increment R */
+#define GET_OPCODE(_opc) do { \
+ Z80_Contention(z80, z80->pc, 4, Z80_MREQ_READ|Z80_MEMIO_OPCODE); \
+ if (z80->evenM1 && (z80->tstates&0x01)) ++z80->tstates; \
+ (_opc) = z80->memReadFn(z80, z80->pc, Z80_MEMIO_OPCODE); \
+ z80->pc = (z80->pc+1)&0xffff; \
+ z80->regR = ((z80->regR+1)&0x7f)|(z80->regR&0x80); \
+} while (0)
+
+#define GET_OPCODE_EXT(_opc) do { \
+ Z80_Contention(z80, z80->pc, 4, Z80_MREQ_READ|Z80_MEMIO_OPCEXT); \
+ (_opc) = z80->memReadFn(z80, z80->pc, Z80_MEMIO_OPCEXT); \
+ z80->pc = (z80->pc+1)&0xffff; \
+ z80->regR = ((z80->regR+1)&0x7f)|(z80->regR&0x80); \
+} while (0)
+
+
+#define CBX_REPEATED (opcode&0x10)
+#define CBX_BACKWARD (opcode&0x08)
+
+
+void Z80_Execute (Z80Info *z80)
+{
+ uint8_t opcode;
+ int gotDD, trueCC; /* booleans */
+ int disp;
+ uint8_t tmpB, tmpC, rsrc, rdst;
+ uint16_t tmpW = 0; /* shut up the compiler; it's wrong but stubborn */
+ /***/
+ while (z80->tstates < z80->next_event_tstate)
+ {
+ if (z80->pagerFn != NULL) z80->pagerFn(z80);
+ if (z80->checkBPFn != NULL && z80->checkBPFn(z80)) return;
+ z80->prev_pc = z80->org_pc;
+ z80->org_pc = z80->pc;
+ /* read opcode -- OCR(4) */
+ GET_OPCODE(opcode);
+ z80->prev_was_EIDDR = 0;
+ disp = gotDD = 0;
+ z80->dd = &z80->hl;
+ if (z80->halted)
+ {
+ DEC_W(z80->pc);
+ continue;
+ }
+ /***/
+ if (opcode == 0xdd || opcode == 0xfd)
+ {
+ static const uint32_t withIndexBmp[8] = {0x00, 0x700000, 0x40404040, 0x40bf4040, 0x40404040, 0x40404040, 0x0800, 0x00};
+ /* IX/IY prefix */
+ z80->dd = (opcode == 0xdd ? &z80->ix : &z80->iy);
+ /* read opcode -- OCR(4) */
+ GET_OPCODE_EXT(opcode);
+ /* test if this instruction have (HL) */
+ if (withIndexBmp[opcode >> 5] & (1 << (opcode & 0x1f)))
+ {
+ /* 3rd byte is always DISP here */
+ disp = Z80_PeekB3TA(z80, z80->pc);
+ if (disp > 127) disp -= 256;
+ INC_PC;
+ z80->memptr.w = ZADD_WX(z80->dd->w, disp);
+ }
+ else if (opcode == 0xdd && opcode == 0xfd)
+ {
+ /* double prefix; restart main loop */
+ z80->prev_was_EIDDR = 1;
+ continue;
+ }
+ gotDD = 1;
+ }
+ /* instructions */
+ if (opcode == 0xed)
+ {
+ z80->dd = &z80->hl; /* Á ÎÁÓ -- ÒÁÔØ! */
+ /* read opcode -- OCR(4) */
+ GET_OPCODE_EXT(opcode);
+ switch (opcode)
+ {
+ /* LDI, LDIR, LDD, LDDR */
+ case 0xa0:
+ case 0xb0:
+ case 0xa8:
+ case 0xb8:
+ tmpB = Z80_PeekB3T(z80, z80->hl.w);
+ Z80_PokeB3T(z80, z80->de.w, tmpB);
+ /*MWR(5)*/
+ Z80_ContentionBy1(z80, z80->de.w, 2);
+ DEC_W(z80->bc.w);
+ tmpB = (tmpB + z80->af.a) & 0xff;
+ /***/
+ z80->af.f =
+ (tmpB & Z80_FLAG_3) | (z80->af.f & (Z80_FLAG_C | Z80_FLAG_Z | Z80_FLAG_S)) |
+ (z80->bc.w != 0 ? Z80_FLAG_PV : 0) |
+ (tmpB & 0x02 ? Z80_FLAG_5 : 0);
+ /***/
+ if (CBX_REPEATED)
+ {
+ if (z80->bc.w != 0)
+ {
+ /*IOP(5)*/
+ Z80_ContentionBy1(z80, z80->de.w, 5);
+ /* do it again */
+ XSUB_W(z80->pc, 2);
+ z80->memptr.w = (z80->pc + 1) & 0xffff;
+ }
+ }
+ if (!CBX_BACKWARD)
+ {
+ INC_W(z80->hl.w);
+ INC_W(z80->de.w);
+ }
+ else
+ {
+ DEC_W(z80->hl.w);
+ DEC_W(z80->de.w);
+ }
+ break;
+ /* CPI, CPIR, CPD, CPDR */
+ case 0xa1:
+ case 0xb1:
+ case 0xa9:
+ case 0xb9:
+ /* MEMPTR */
+ if (CBX_REPEATED && (!(z80->bc.w == 1 || Z80_PeekBI(z80, z80->hl.w) == z80->af.a)))
+ {
+ z80->memptr.w = ZADD_WX(z80->org_pc, 1);
+ }
+ else
+ {
+ z80->memptr.w = ZADD_WX(z80->memptr.w, (CBX_BACKWARD ? -1 : 1));
+ }
+ /***/
+ tmpB = Z80_PeekB3T(z80, z80->hl.w);
+ /*IOP(5)*/
+ Z80_ContentionBy1(z80, z80->hl.w, 5);
+ DEC_W(z80->bc.w);
+ /***/
+ z80->af.f =
+ Z80_FLAG_N |
+ (z80->af.f & Z80_FLAG_C) |
+ (z80->bc.w != 0 ? Z80_FLAG_PV : 0) |
+ ((int32_t)(z80->af.a & 0x0f) - (int32_t)(tmpB & 0x0f) < 0 ? Z80_FLAG_H : 0);
+ /***/
+ tmpB = ((int32_t)z80->af.a - (int32_t)tmpB) & 0xff;
+ /***/
+ z80->af.f |=
+ (tmpB == 0 ? Z80_FLAG_Z : 0) |
+ (tmpB & Z80_FLAG_S);
+ /***/
+ if (z80->af.f & Z80_FLAG_H) tmpB = ((uint16_t)tmpB - 1) & 0xff;
+ z80->af.f |= (tmpB & Z80_FLAG_3) | (tmpB & 0x02 ? Z80_FLAG_5 : 0);
+ /***/
+ if (CBX_REPEATED)
+ {
+ /* repeated */
+ if ((z80->af.f & (Z80_FLAG_Z | Z80_FLAG_PV)) == Z80_FLAG_PV)
+ {
+ /*IOP(5)*/
+ Z80_ContentionBy1(z80, z80->hl.w, 5);
+ /* do it again */
+ XSUB_W(z80->pc, 2);
+ }
+ }
+ if (CBX_BACKWARD) DEC_W(z80->hl.w);
+ else INC_W(z80->hl.w);
+ break;
+ /* OUTI, OTIR, OUTD, OTDR */
+ case 0xa3:
+ case 0xb3:
+ case 0xab:
+ case 0xbb:
+ DEC_B(z80->bc.b);
+ /* fallthru */
+ /* INI, INIR, IND, INDR */
+ case 0xa2:
+ case 0xb2:
+ case 0xaa:
+ case 0xba:
+ z80->memptr.w = ZADD_WX(z80->bc.w, (CBX_BACKWARD ? -1 : 1));
+ /*OCR(5)*/
+ Z80_ContentionIRBy1(z80, 1);
+ if (opcode & 0x01)
+ {
+ /* OUT* */
+ tmpB = Z80_PeekB3T(z80, z80->hl.w);/*MRD(3)*/
+ Z80_PortOut(z80, z80->bc.w, tmpB);
+ tmpW = ZADD_WX(z80->hl.w, (CBX_BACKWARD ? -1 : 1));
+ tmpC = (tmpB + tmpW) & 0xff;
+ }
+ else
+ {
+ /* IN* */
+ tmpB = Z80_PortIn(z80, z80->bc.w);
+ Z80_PokeB3T(z80, z80->hl.w, tmpB);/*MWR(3)*/
+ DEC_B(z80->bc.b);
+ if (CBX_BACKWARD) tmpC = ((int32_t)tmpB + (int32_t)z80->bc.c - 1) & 0xff;
+ else tmpC = (tmpB + z80->bc.c + 1) & 0xff;
+ }
+ /***/
+ z80->af.f =
+ (tmpB & 0x80 ? Z80_FLAG_N : 0) |
+ (tmpC < tmpB ? Z80_FLAG_H | Z80_FLAG_C : 0) |
+ parityTable[(tmpC & 0x07)^z80->bc.b] |
+ sz53Table[z80->bc.b];
+ /***/
+ if (CBX_REPEATED)
+ {
+ /* repeating commands */
+ if (z80->bc.b != 0)
+ {
+ uint16_t a = (opcode & 0x01 ? z80->bc.w : z80->hl.w);
+ /***/
+ /*IOP(5)*/
+ Z80_ContentionBy1(z80, a, 5);
+ /* do it again */
+ XSUB_W(z80->pc, 2);
+ }
+ }
+ if (CBX_BACKWARD) DEC_W(z80->hl.w);
+ else INC_W(z80->hl.w);
+ break;
+ /* not strings, but some good instructions anyway */
+ default:
+ if ((opcode & 0xc0) == 0x40)
+ {
+ /* 0x40...0x7f */
+ switch (opcode & 0x07)
+ {
+ /* IN r8,(C) */
+ case 0:
+ z80->memptr.w = ZADD_WX(z80->bc.w, 1);
+ tmpB = Z80_PortIn(z80, z80->bc.w);
+ z80->af.f = sz53pTable[tmpB] | (z80->af.f & Z80_FLAG_C);
+ switch ((opcode >> 3) & 0x07)
+ {
+ case 0:
+ z80->bc.b = tmpB;
+ break;
+ case 1:
+ z80->bc.c = tmpB;
+ break;
+ case 2:
+ z80->de.d = tmpB;
+ break;
+ case 3:
+ z80->de.e = tmpB;
+ break;
+ case 4:
+ z80->hl.h = tmpB;
+ break;
+ case 5:
+ z80->hl.l = tmpB;
+ break;
+ case 7:
+ z80->af.a = tmpB;
+ break;
+ /* 6 affects only flags */
+ }
+ break;
+ /* OUT (C),r8 */
+ case 1:
+ z80->memptr.w = ZADD_WX(z80->bc.w, 1);
+ switch ((opcode >> 3) & 0x07)
+ {
+ case 0:
+ tmpB = z80->bc.b;
+ break;
+ case 1:
+ tmpB = z80->bc.c;
+ break;
+ case 2:
+ tmpB = z80->de.d;
+ break;
+ case 3:
+ tmpB = z80->de.e;
+ break;
+ case 4:
+ tmpB = z80->hl.h;
+ break;
+ case 5:
+ tmpB = z80->hl.l;
+ break;
+ case 7:
+ tmpB = z80->af.a;
+ break;
+ default:
+ tmpB = 0;
+ break; /*6*/
+ }
+ Z80_PortOut(z80, z80->bc.w, tmpB);
+ break;
+ /* SBC HL,rr/ADC HL,rr */
+ case 2:
+ /*IOP(4),IOP(3)*/
+ Z80_ContentionIRBy1(z80, 7);
+ switch ((opcode >> 4) & 0x03)
+ {
+ case 0:
+ tmpW = z80->bc.w;
+ break;
+ case 1:
+ tmpW = z80->de.w;
+ break;
+ case 2:
+ tmpW = z80->hl.w;
+ break;
+ default:
+ tmpW = z80->sp.w;
+ break;
+ }
+ z80->hl.w = (opcode & 0x08 ? Z80_ADC_DD(z80, tmpW, z80->hl.w) : Z80_SBC_DD(z80, tmpW, z80->hl.w));
+ break;
+ /* LD (nn),rr/LD rr,(nn) */
+ case 3:
+ tmpW = Z80_GetWordPC(z80, 0);
+ z80->memptr.w = (tmpW + 1) & 0xffff;
+ if (opcode & 0x08)
+ {
+ /* LD rr,(nn) */
+ switch ((opcode >> 4) & 0x03)
+ {
+ case 0:
+ z80->bc.w = Z80_PeekW6T(z80, tmpW);
+ break;
+ case 1:
+ z80->de.w = Z80_PeekW6T(z80, tmpW);
+ break;
+ case 2:
+ z80->hl.w = Z80_PeekW6T(z80, tmpW);
+ break;
+ case 3:
+ z80->sp.w = Z80_PeekW6T(z80, tmpW);
+ break;
+ }
+ }
+ else
+ {
+ /* LD (nn),rr */
+ switch ((opcode >> 4) & 0x03)
+ {
+ case 0:
+ Z80_PokeW6T(z80, tmpW, z80->bc.w);
+ break;
+ case 1:
+ Z80_PokeW6T(z80, tmpW, z80->de.w);
+ break;
+ case 2:
+ Z80_PokeW6T(z80, tmpW, z80->hl.w);
+ break;
+ case 3:
+ Z80_PokeW6T(z80, tmpW, z80->sp.w);
+ break;
+ }
+ }
+ break;
+ /* NEG */
+ case 4:
+ tmpB = z80->af.a;
+ z80->af.a = 0;
+ Z80_SUB_A(z80, tmpB);
+ break;
+ /* RETI/RETN */
+ case 5:
+ /*RETI: 0x4d, 0x5d, 0x6d, 0x7d*/
+ /*RETN: 0x45, 0x55, 0x65, 0x75*/
+ z80->iff1 = z80->iff2;
+ z80->memptr.w = z80->pc = Z80_Pop6T(z80);
+ if (opcode & 0x08)
+ {
+ /* RETI */
+ if (z80->retiFn != NULL && z80->retiFn(z80, opcode)) return;
+ }
+ else
+ {
+ /* RETN */
+ if (z80->retnFn != NULL && z80->retnFn(z80, opcode)) return;
+ }
+ break;
+ /* IM n */
+ case 6:
+ switch (opcode)
+ {
+ case 0x56:
+ case 0x76:
+ z80->im = 1;
+ break;
+ case 0x5e:
+ case 0x7e:
+ z80->im = 2;
+ break;
+ default:
+ z80->im = 0;
+ break;
+ }
+ break;
+ /* specials */
+ case 7:
+ switch (opcode)
+ {
+ /* LD I,A */
+ case 0x47:
+ /*OCR(5)*/
+ Z80_ContentionIRBy1(z80, 1);
+ z80->regI = z80->af.a;
+ break;
+ /* LD R,A */
+ case 0x4f:
+ /*OCR(5)*/
+ Z80_ContentionIRBy1(z80, 1);
+ z80->regR = z80->af.a;
+ break;
+ /* LD A,I */
+ case 0x57:
+ Z80_LD_A_IR(z80, z80->regI);
+ break;
+ /* LD A,R */
+ case 0x5f:
+ Z80_LD_A_IR(z80, z80->regR);
+ break;
+ /* RRD */
+ case 0x67:
+ Z80_RRD_A(z80);
+ break;
+ /* RLD */
+ case 0x6F:
+ Z80_RLD_A(z80);
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* slt and other traps */
+ if (z80->trapEDFn != NULL && z80->trapEDFn(z80, opcode)) return;
+ }
+ break;
+ }
+ continue;
+ } /* 0xed done */
+ /***/
+ if (opcode == 0xcb)
+ {
+ /* shifts and bit operations */
+ /* read opcode -- OCR(4) */
+ if (!gotDD)
+ {
+ GET_OPCODE_EXT(opcode);
+ }
+ else
+ {
+ Z80_Contention(z80, z80->pc, 3, Z80_MREQ_READ | Z80_MEMIO_OPCEXT);
+ opcode = z80->memReadFn(z80, z80->pc, Z80_MEMIO_OPCEXT);
+ Z80_ContentionPCBy1(z80, 2);
+ INC_PC;
+ }
+ if (gotDD)
+ {
+ tmpW = ZADD_WX(z80->dd->w, disp);
+ tmpB = Z80_PeekB3T(z80, tmpW);
+ Z80_ContentionBy1(z80, tmpW, 1);
+ }
+ else
+ {
+ switch (opcode & 0x07)
+ {
+ case 0:
+ tmpB = z80->bc.b;
+ break;
+ case 1:
+ tmpB = z80->bc.c;
+ break;
+ case 2:
+ tmpB = z80->de.d;
+ break;
+ case 3:
+ tmpB = z80->de.e;
+ break;
+ case 4:
+ tmpB = z80->hl.h;
+ break;
+ case 5:
+ tmpB = z80->hl.l;
+ break;
+ case 6:
+ tmpB = Z80_PeekB3T(z80, z80->hl.w);
+ Z80_Contention(z80, z80->hl.w, 1, Z80_MREQ_READ | Z80_MEMIO_DATA);
+ break;
+ case 7:
+ tmpB = z80->af.a;
+ break;
+ }
+ }
+ switch ((opcode >> 3) & 0x1f)
+ {
+ case 0:
+ tmpB = Z80_RLC(z80, tmpB);
+ break;
+ case 1:
+ tmpB = Z80_RRC(z80, tmpB);
+ break;
+ case 2:
+ tmpB = Z80_RL(z80, tmpB);
+ break;
+ case 3:
+ tmpB = Z80_RR(z80, tmpB);
+ break;
+ case 4:
+ tmpB = Z80_SLA(z80, tmpB);
+ break;
+ case 5:
+ tmpB = Z80_SRA(z80, tmpB);
+ break;
+ case 6:
+ tmpB = Z80_SLL(z80, tmpB);
+ break;
+ case 7:
+ tmpB = Z80_SLR(z80, tmpB);
+ break;
+ default:
+ switch ((opcode >> 6) & 0x03)
+ {
+ case 1:
+ Z80_BIT(z80, (opcode >> 3) & 0x07, tmpB, (gotDD || (opcode & 0x07) == 6));
+ break;
+ case 2:
+ tmpB &= ~(1 << ((opcode >> 3) & 0x07));
+ break; /* RES */
+ case 3:
+ tmpB |= (1 << ((opcode >> 3) & 0x07));
+ break; /* SET */
+ }
+ break;
+ }
+ /***/
+ if ((opcode & 0xc0) != 0x40)
+ {
+ /* BITs are not welcome here */
+ if (gotDD)
+ {
+ /* tmpW was set earlier */
+ if ((opcode & 0x07) != 6) Z80_PokeB3T(z80, tmpW, tmpB);
+ }
+ switch (opcode & 0x07)
+ {
+ case 0:
+ z80->bc.b = tmpB;
+ break;
+ case 1:
+ z80->bc.c = tmpB;
+ break;
+ case 2:
+ z80->de.d = tmpB;
+ break;
+ case 3:
+ z80->de.e = tmpB;
+ break;
+ case 4:
+ z80->hl.h = tmpB;
+ break;
+ case 5:
+ z80->hl.l = tmpB;
+ break;
+ case 6:
+ Z80_PokeB3T(z80, ZADD_WX(z80->dd->w, disp), tmpB);
+ break;
+ case 7:
+ z80->af.a = tmpB;
+ break;
+ }
+ }
+ continue;
+ } /* 0xcb done */
+ /* normal things */
+ switch (opcode & 0xc0)
+ {
+ /* 0x00..0x3F */
+ case 0x00:
+ switch (opcode & 0x07)
+ {
+ /* misc,DJNZ,JR,JR cc */
+ case 0:
+ if (opcode & 0x30)
+ {
+ /* branches */
+ if (opcode & 0x20)
+ {
+ /* JR cc */
+ switch ((opcode >> 3) & 0x03)
+ {
+ case 0:
+ trueCC = (z80->af.f & Z80_FLAG_Z) == 0;
+ break;
+ case 1:
+ trueCC = (z80->af.f & Z80_FLAG_Z) != 0;
+ break;
+ case 2:
+ trueCC = (z80->af.f & Z80_FLAG_C) == 0;
+ break;
+ case 3:
+ trueCC = (z80->af.f & Z80_FLAG_C) != 0;
+ break;
+ default:
+ trueCC = 0;
+ break;
+ }
+ }
+ else
+ {
+ /* DJNZ/JR */
+ if ((opcode & 0x08) == 0)
+ {
+ /* DJNZ */
+ /*OCR(5)*/
+ Z80_ContentionIRBy1(z80, 1);
+ DEC_B(z80->bc.b);
+ trueCC = (z80->bc.b != 0);
+ }
+ else
+ {
+ /* JR */
+ trueCC = 1;
+ }
+ }
+ /***/
+ disp = Z80_PeekB3TA(z80, z80->pc);
+ if (trueCC)
+ {
+ /* execute branch (relative) */
+ /*IOP(5)*/
+ if (disp > 127) disp -= 256;
+ Z80_ContentionPCBy1(z80, 5);
+ INC_PC;
+ ZADD_W(z80->pc, disp);
+ z80->memptr.w = z80->pc;
+ }
+ else
+ {
+ INC_PC;
+ }
+ }
+ else
+ {
+ /* EX AF,AF' or NOP */
+ if (opcode != 0) Z80_EXAFAF(z80);
+ }
+ break;
+ /* LD rr,nn/ADD HL,rr */
+ case 1:
+ if (opcode & 0x08)
+ {
+ /* ADD HL,rr */
+ /*IOP(4),IOP(3)*/
+ Z80_ContentionIRBy1(z80, 7);
+ switch ((opcode >> 4) & 0x03)
+ {
+ case 0:
+ z80->dd->w = Z80_ADD_DD(z80, z80->bc.w, z80->dd->w);
+ break;
+ case 1:
+ z80->dd->w = Z80_ADD_DD(z80, z80->de.w, z80->dd->w);
+ break;
+ case 2:
+ z80->dd->w = Z80_ADD_DD(z80, z80->dd->w, z80->dd->w);
+ break;
+ case 3:
+ z80->dd->w = Z80_ADD_DD(z80, z80->sp.w, z80->dd->w);
+ break;
+ }
+ }
+ else
+ {
+ /* LD rr,nn */
+ tmpW = Z80_GetWordPC(z80, 0);
+ switch ((opcode >> 4) & 0x03)
+ {
+ case 0:
+ z80->bc.w = tmpW;
+ break;
+ case 1:
+ z80->de.w = tmpW;
+ break;
+ case 2:
+ z80->dd->w = tmpW;
+ break;
+ case 3:
+ z80->sp.w = tmpW;
+ break;
+ }
+ }
+ break;
+ /* LD xxx,xxx */
+ case 2:
+ switch ((opcode >> 3) & 0x07)
+ {
+ /* LD (BC),A */
+ case 0:
+ Z80_PokeB3T(z80, z80->bc.w, z80->af.a);
+ z80->memptr.l = (z80->bc.c + 1) & 0xff;
+ z80->memptr.h = z80->af.a;
+ break;
+ /* LD A,(BC) */
+ case 1:
+ z80->af.a = Z80_PeekB3T(z80, z80->bc.w);
+ z80->memptr.w = (z80->bc.w + 1) & 0xffff;
+ break;
+ /* LD (DE),A */
+ case 2:
+ Z80_PokeB3T(z80, z80->de.w, z80->af.a);
+ z80->memptr.l = (z80->de.e+1) & 0xff;
+ z80->memptr.h = z80->af.a;
+ break;
+ /* LD A,(DE) */
+ case 3:
+ z80->af.a = Z80_PeekB3T(z80, z80->de.w);
+ z80->memptr.w = (z80->de.w + 1) & 0xffff;
+ break;
+ /* LD (nn),HL */
+ case 4:
+ tmpW = Z80_GetWordPC(z80, 0);
+ z80->memptr.w = (tmpW + 1) & 0xffff;
+ Z80_PokeW6T(z80, tmpW, z80->dd->w);
+ break;
+ /* LD HL,(nn) */
+ case 5:
+ tmpW = Z80_GetWordPC(z80, 0);
+ z80->memptr.w = (tmpW + 1) & 0xffff;
+ z80->dd->w = Z80_PeekW6T(z80, tmpW);
+ break;
+ /* LD (nn),A */
+ case 6:
+ tmpW = Z80_GetWordPC(z80, 0);
+ z80->memptr.l = (tmpW + 1) & 0xff;
+ z80->memptr.h = z80->af.a;
+ Z80_PokeB3T(z80, tmpW, z80->af.a);
+ break;
+ /* LD A,(nn) */
+ case 7:
+ tmpW = Z80_GetWordPC(z80, 0);
+ z80->memptr.w = (tmpW + 1) & 0xffff;
+ z80->af.a = Z80_PeekB3T(z80, tmpW);
+ break;
+ }
+ break;
+ /* INC rr/DEC rr */
+ case 3:
+ /*OCR(6)*/
+ Z80_ContentionIRBy1(z80, 2);
+ if (opcode & 0x08)
+ {
+ /*DEC*/
+ switch ((opcode >> 4) & 0x03)
+ {
+ case 0:
+ DEC_W(z80->bc.w);
+ break;
+ case 1:
+ DEC_W(z80->de.w);
+ break;
+ case 2:
+ DEC_W(z80->dd->w);
+ break;
+ case 3:
+ DEC_W(z80->sp.w);
+ break;
+ }
+ }
+ else
+ {
+ /*INC*/
+ switch ((opcode >> 4) & 0x03)
+ {
+ case 0:
+ INC_W(z80->bc.w);
+ break;
+ case 1:
+ INC_W(z80->de.w);
+ break;
+ case 2:
+ INC_W(z80->dd->w);
+ break;
+ case 3:
+ INC_W(z80->sp.w);
+ break;
+ }
+ }
+ break;
+ /* INC r8 */
+ case 4:
+ switch ((opcode >> 3) & 0x07)
+ {
+ case 0:
+ z80->bc.b = Z80_INC8(z80, z80->bc.b);
+ break;
+ case 1:
+ z80->bc.c = Z80_INC8(z80, z80->bc.c);
+ break;
+ case 2:
+ z80->de.d = Z80_INC8(z80, z80->de.d);
+ break;
+ case 3:
+ z80->de.e = Z80_INC8(z80, z80->de.e);
+ break;
+ case 4:
+ z80->dd->h = Z80_INC8(z80, z80->dd->h);
+ break;
+ case 5:
+ z80->dd->l = Z80_INC8(z80, z80->dd->l);
+ break;
+ case 6:
+ if (gotDD)
+ {
+ DEC_PC;
+ Z80_ContentionPCBy1(z80, 5);
+ INC_PC;
+ }
+ tmpW = ZADD_WX(z80->dd->w, disp);
+ tmpB = Z80_PeekB3T(z80, tmpW);
+ Z80_ContentionBy1(z80, tmpW, 1);
+ tmpB = Z80_INC8(z80, tmpB);
+ Z80_PokeB3T(z80, tmpW, tmpB);
+ break;
+ case 7:
+ z80->af.a = Z80_INC8(z80, z80->af.a);
+ break;
+ }
+ break;
+ /* DEC r8 */
+ case 5:
+ switch ((opcode >> 3) & 0x07)
+ {
+ case 0:
+ z80->bc.b = Z80_DEC8(z80, z80->bc.b);
+ break;
+ case 1:
+ z80->bc.c = Z80_DEC8(z80, z80->bc.c);
+ break;
+ case 2:
+ z80->de.d = Z80_DEC8(z80, z80->de.d);
+ break;
+ case 3:
+ z80->de.e = Z80_DEC8(z80, z80->de.e);
+ break;
+ case 4:
+ z80->dd->h = Z80_DEC8(z80, z80->dd->h);
+ break;
+ case 5:
+ z80->dd->l = Z80_DEC8(z80, z80->dd->l);
+ break;
+ case 6:
+ if (gotDD)
+ {
+ DEC_PC;
+ Z80_ContentionPCBy1(z80, 5);
+ INC_PC;
+ }
+ tmpW = ZADD_WX(z80->dd->w, disp);
+ tmpB = Z80_PeekB3T(z80, tmpW);
+ Z80_ContentionBy1(z80, tmpW, 1);
+ tmpB = Z80_DEC8(z80, tmpB);
+ Z80_PokeB3T(z80, tmpW, tmpB);
+ break;
+ case 7:
+ z80->af.a = Z80_DEC8(z80, z80->af.a);
+ break;
+ }
+ break;
+ /* LD r8,n */
+ case 6:
+ tmpB = Z80_PeekB3TA(z80, z80->pc);
+ INC_PC;
+ switch ((opcode >> 3) & 0x07)
+ {
+ case 0:
+ z80->bc.b = tmpB;
+ break;
+ case 1:
+ z80->bc.c = tmpB;
+ break;
+ case 2:
+ z80->de.d = tmpB;
+ break;
+ case 3:
+ z80->de.e = tmpB;
+ break;
+ case 4:
+ z80->dd->h = tmpB;
+ break;
+ case 5:
+ z80->dd->l = tmpB;
+ break;
+ case 6:
+ if (gotDD)
+ {
+ DEC_PC;
+ Z80_ContentionPCBy1(z80, 2);
+ INC_PC;
+ }
+ tmpW = ZADD_WX(z80->dd->w, disp);
+ Z80_PokeB3T(z80, tmpW, tmpB);
+ break;
+ case 7:
+ z80->af.a = tmpB;
+ break;
+ }
+ break;
+ /* swim-swim-hungry */
+ case 7:
+ switch ((opcode >> 3) & 0x07)
+ {
+ case 0:
+ Z80_RLCA(z80);
+ break;
+ case 1:
+ Z80_RRCA(z80);
+ break;
+ case 2:
+ Z80_RLA(z80);
+ break;
+ case 3:
+ Z80_RRA(z80);
+ break;
+ case 4:
+ Z80_DAA(z80);
+ break;
+ case 5: /* CPL */
+ z80->af.a ^= 0xff;
+ z80->af.f = (z80->af.a & Z80_FLAG_35) | (Z80_FLAG_N | Z80_FLAG_H) | (z80->af.f & (Z80_FLAG_C | Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S));
+ break;
+ case 6: /* SCF */
+ z80->af.f = (z80->af.f & (Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S)) | (z80->af.a & Z80_FLAG_35) | Z80_FLAG_C;
+ break;
+ case 7: /* CCF */
+ tmpB = z80->af.f & Z80_FLAG_C;
+ z80->af.f = (z80->af.f & (Z80_FLAG_PV | Z80_FLAG_Z | Z80_FLAG_S)) | (z80->af.a & Z80_FLAG_35);
+ z80->af.f |= tmpB ? Z80_FLAG_H : Z80_FLAG_C;
+ break;
+ }
+ break;
+ }
+ break;
+ /* 0x40..0x7F (LD r8,r8) */
+ case 0x40:
+ if (opcode == 0x76)
+ {
+ z80->halted = 1; /* HALT */
+ DEC_W(z80->pc);
+ continue;
+ }
+ rsrc = (opcode & 0x07);
+ rdst = ((opcode >> 3) & 0x07);
+ switch (rsrc)
+ {
+ case 0:
+ tmpB = z80->bc.b;
+ break;
+ case 1:
+ tmpB = z80->bc.c;
+ break;
+ case 2:
+ tmpB = z80->de.d;
+ break;
+ case 3:
+ tmpB = z80->de.e;
+ break;
+ case 4:
+ tmpB = (gotDD && rdst == 6 ? z80->hl.h : z80->dd->h);
+ break;
+ case 5:
+ tmpB = (gotDD && rdst == 6 ? z80->hl.l : z80->dd->l);
+ break;
+ case 6:
+ if (gotDD)
+ {
+ DEC_PC;
+ Z80_ContentionPCBy1(z80, 5);
+ INC_PC;
+ }
+ tmpW = ZADD_WX(z80->dd->w, disp);
+ tmpB = Z80_PeekB3T(z80, tmpW);
+ break;
+ case 7:
+ tmpB = z80->af.a;
+ break;
+ }
+ switch (rdst)
+ {
+ case 0:
+ z80->bc.b = tmpB;
+ break;
+ case 1:
+ z80->bc.c = tmpB;
+ break;
+ case 2:
+ z80->de.d = tmpB;
+ break;
+ case 3:
+ z80->de.e = tmpB;
+ break;
+ case 4:
+ if (gotDD && rsrc == 6) z80->hl.h = tmpB;
+ else z80->dd->h = tmpB;
+ break;
+ case 5:
+ if (gotDD && rsrc == 6) z80->hl.l = tmpB;
+ else z80->dd->l = tmpB;
+ break;
+ case 6:
+ if (gotDD)
+ {
+ DEC_PC;
+ Z80_ContentionPCBy1(z80, 5);
+ INC_PC;
+ }
+ tmpW = ZADD_WX(z80->dd->w, disp);
+ Z80_PokeB3T(z80, tmpW, tmpB);
+ break;
+ case 7:
+ z80->af.a = tmpB;
+ break;
+ }
+ break;
+ /* 0x80..0xBF (ALU A,r8) */
+ case 0x80:
+ switch (opcode & 0x07)
+ {
+ case 0:
+ tmpB = z80->bc.b;
+ break;
+ case 1:
+ tmpB = z80->bc.c;
+ break;
+ case 2:
+ tmpB = z80->de.d;
+ break;
+ case 3:
+ tmpB = z80->de.e;
+ break;
+ case 4:
+ tmpB = z80->dd->h;
+ break;
+ case 5:
+ tmpB = z80->dd->l;
+ break;
+ case 6:
+ if (gotDD)
+ {
+ DEC_PC;
+ Z80_ContentionPCBy1(z80, 5);
+ INC_PC;
+ }
+ tmpW = ZADD_WX(z80->dd->w, disp);
+ tmpB = Z80_PeekB3T(z80, tmpW);
+ break;
+ case 7:
+ tmpB = z80->af.a;
+ break;
+ }
+ switch ((opcode >> 3) & 0x07)
+ {
+ case 0:
+ Z80_ADD_A(z80, tmpB);
+ break;
+ case 1:
+ Z80_ADC_A(z80, tmpB);
+ break;
+ case 2:
+ Z80_SUB_A(z80, tmpB);
+ break;
+ case 3:
+ Z80_SBC_A(z80, tmpB);
+ break;
+ case 4:
+ Z80_AND_A(z80, tmpB);
+ break;
+ case 5:
+ Z80_XOR_A(z80, tmpB);
+ break;
+ case 6:
+ Z80_OR_A(z80, tmpB);
+ break;
+ case 7:
+ Z80_CP_A(z80, tmpB);
+ break;
+ }
+ break;
+ /* 0xC0..0xFF */
+ case 0xC0:
+ switch (opcode & 0x07)
+ {
+ /* RET cc */
+ case 0:
+ Z80_ContentionIRBy1(z80, 1);
+ SET_TRUE_CC
+ if (trueCC) z80->memptr.w = z80->pc = Z80_Pop6T(z80);
+ break;
+ /* POP rr/special0 */
+ case 1:
+ if (opcode & 0x08)
+ {
+ /* special 0 */
+ switch ((opcode >> 4) & 0x03)
+ {
+ /* RET */
+ case 0:
+ z80->memptr.w = z80->pc = Z80_Pop6T(z80);
+ break;
+ /* EXX */
+ case 1:
+ Z80_EXX(z80);
+ break;
+ /* JP (HL) */
+ case 2:
+ z80->pc = z80->dd->w;
+ break;
+ /* LD SP,HL */
+ case 3:
+ /*OCR(6)*/
+ Z80_ContentionIRBy1(z80, 2);
+ z80->sp.w = z80->dd->w;
+ break;
+ }
+ }
+ else
+ {
+ /* POP rr */
+ tmpW = Z80_Pop6T(z80);
+ switch ((opcode >> 4) & 0x03)
+ {
+ case 0:
+ z80->bc.w = tmpW;
+ break;
+ case 1:
+ z80->de.w = tmpW;
+ break;
+ case 2:
+ z80->dd->w = tmpW;
+ break;
+ case 3:
+ z80->af.w = tmpW;
+ break;
+ }
+ }
+ break;
+ /* JP cc,nn */
+ case 2:
+ SET_TRUE_CC
+ z80->memptr.w = Z80_GetWordPC(z80, 0);
+ if (trueCC) z80->pc = z80->memptr.w;
+ break;
+ /* special1/special3 */
+ case 3:
+ switch ((opcode >> 3) & 0x07)
+ {
+ /* JP nn */
+ case 0:
+ z80->memptr.w = z80->pc = Z80_GetWordPC(z80, 0);
+ break;
+ /* OUT (n),A */
+ case 2:
+ tmpW = Z80_PeekB3TA(z80, z80->pc);
+ INC_PC;
+ z80->memptr.l = (tmpW + 1) & 0xff;
+ z80->memptr.h = z80->af.a;
+ tmpW |= (((uint16_t)(z80->af.a)) << 8);
+ Z80_PortOut(z80, tmpW, z80->af.a);
+ break;
+ /* IN A,(n) */
+ case 3:
+ tmpW = (((uint16_t)(z80->af.a)) << 8) | Z80_PeekB3TA(z80, z80->pc);
+ INC_PC;
+ z80->memptr.w = (tmpW + 1) & 0xffff;
+ z80->af.a = Z80_PortIn(z80, tmpW);
+ break;
+ /* EX (SP),HL */
+ case 4:
+ /*SRL(3),SRH(4)*/
+ tmpW = Z80_PeekW6T(z80, z80->sp.w);
+ Z80_ContentionBy1(z80, (z80->sp.w + 1) & 0xffff, 1);
+ /*SWL(3),SWH(5)*/
+ Z80_PokeW6TInv(z80, z80->sp.w, z80->dd->w);
+ Z80_ContentionBy1(z80, z80->sp.w, 2);
+ z80->memptr.w = z80->dd->w = tmpW;
+ break;
+ /* EX DE,HL */
+ case 5:
+ tmpW = z80->de.w;
+ z80->de.w = z80->hl.w;
+ z80->hl.w = tmpW;
+ break;
+ /* DI */
+ case 6:
+ z80->iff1 = z80->iff2 = 0;
+ break;
+ /* EI */
+ case 7:
+ z80->iff1 = z80->iff2 = 1;
+ z80->prev_was_EIDDR = 1;
+ break;
+ }
+ break;
+ /* CALL cc,nn */
+ case 4:
+ SET_TRUE_CC
+ z80->memptr.w = Z80_GetWordPC(z80, trueCC);
+ if (trueCC)
+ {
+ Z80_Push6T(z80, z80->pc);
+ z80->pc = z80->memptr.w;
+ }
+ break;
+ /* PUSH rr/special2 */
+ case 5:
+ if (opcode & 0x08)
+ {
+ if (((opcode >> 4) & 0x03) == 0)
+ {
+ /* CALL */
+ z80->memptr.w = tmpW = Z80_GetWordPC(z80, 1);
+ Z80_Push6T(z80, z80->pc);
+ z80->pc = tmpW;
+ }
+ }
+ else
+ {
+ /* PUSH rr */
+ /*OCR(5)*/
+ Z80_ContentionIRBy1(z80, 1);
+ switch ((opcode >> 4) & 0x03)
+ {
+ case 0:
+ tmpW = z80->bc.w;
+ break;
+ case 1:
+ tmpW = z80->de.w;
+ break;
+ case 2:
+ tmpW = z80->dd->w;
+ break;
+ default:
+ tmpW = z80->af.w;
+ break;
+ }
+ Z80_Push6T(z80, tmpW);
+ }
+ break;
+ /* ALU A,n */
+ case 6:
+ tmpB = Z80_PeekB3TA(z80, z80->pc);
+ INC_PC;
+ switch ((opcode >> 3) & 0x07)
+ {
+ case 0:
+ Z80_ADD_A(z80, tmpB);
+ break;
+ case 1:
+ Z80_ADC_A(z80, tmpB);
+ break;
+ case 2:
+ Z80_SUB_A(z80, tmpB);
+ break;
+ case 3:
+ Z80_SBC_A(z80, tmpB);
+ break;
+ case 4:
+ Z80_AND_A(z80, tmpB);
+ break;
+ case 5:
+ Z80_XOR_A(z80, tmpB);
+ break;
+ case 6:
+ Z80_OR_A(z80, tmpB);
+ break;
+ case 7:
+ Z80_CP_A(z80, tmpB);
+ break;
+ }
+ break;
+ /* RST nnn */
+ case 7:
+ /*OCR(5)*/
+ Z80_ContentionIRBy1(z80, 1);
+ Z80_Push6T(z80, z80->pc);
+ z80->memptr.w = z80->pc = opcode & 0x38;
+ break;
+ }
+ break;
+ } /* end switch */
+ }
+}
+
+
+int32_t Z80_ExecuteStep (Z80Info *z80)
+{
+ int32_t one = z80->next_event_tstate, ots = z80->tstates;
+ /***/
+ z80->next_event_tstate = ots + 1;
+ Z80_Execute(z80);
+ z80->next_event_tstate = one;
+ return z80->tstates - ots;
+}
+
+
+int32_t Z80_ExecuteTS (Z80Info *z80, int32_t tstates)
+{
+ if (tstates > 0)
+ {
+ z80->tstates = 0;
+ z80->next_event_tstate = tstates;
+ Z80_Execute(z80);
+ return z80->tstates;
+ }
+ return 0;
+}
+
+
+/******************************************************************************/
+/* changes z80->tstates if interrupt occurs */
+int Z80_Interrupt (Z80Info *z80)
+{
+ uint16_t a;
+ int ots = z80->tstates;
+ /***/
+ if (z80->prev_was_EIDDR < 0)
+ {
+ z80->prev_was_EIDDR = 0; /* Z80 bug */
+ z80->af.f &= ~Z80_FLAG_PV;
+ }
+ if (z80->prev_was_EIDDR || !z80->iff1) return 0; /* not accepted */
+ if (z80->halted)
+ {
+ z80->halted = 0;
+ INC_PC;
+ }
+ z80->iff1 = z80->iff2 = 0; /* disable interrupts */
+ /***/
+ switch ((z80->im &= 0x03))
+ {
+ case 3: /* ??? */
+ z80->im = 0;
+ case 0: /* take instruction from the bus (for now we assume that reading from bus always returns 0xff) */
+ /* with a CALL nnnn on the data bus, it takes 19 cycles: */
+ /* M1 cycle: 7 T to acknowledge interrupt (where exactly data bus reading occures?) */
+ /* M2 cycle: 3 T to read low byte of 'nnnn' from data bus */
+ /* M3 cycle: 3 T to read high byte of 'nnnn' and decrement SP */
+ /* M4 cycle: 3 T to write high byte of PC to the stack and decrement SP */
+ /* M5 cycle: 3 T to write low byte of PC and jump to 'nnnn' */
+ z80->tstates += 6;
+ case 1: /* just do RST #38 */
+ INC_R;
+ z80->tstates += 7; /* M1 cycle: 7 T to acknowledge interrupt and decrement SP */
+ /* M2 cycle: 3 T states write high byte of PC to the stack and decrement SP */
+ /* M3 cycle: 3 T states write the low byte of PC and jump to #0038 */
+ Z80_Push6T(z80, z80->pc);
+ z80->memptr.w = z80->pc = 0x38;
+ break;
+ case 2:
+ INC_R;
+ z80->tstates += 7; /* M1 cycle: 7 T to acknowledge interrupt and decrement SP */
+ /* M2 cycle: 3 T states write high byte of PC to the stack and decrement SP */
+ /* M3 cycle: 3 T states write the low byte of PC */
+ Z80_Push6T(z80, z80->pc);
+ /* M4 cycle: 3 T to read high byte from the interrupt vector */
+ /* M5 cycle: 3 T to read low byte from bus and jump to interrupt routine */
+ a = (((uint16_t)z80->regI) << 8) | 0xff;
+ z80->memptr.w = z80->pc = Z80_PeekW6T(z80, a);
+ break;
+ }
+ return z80->tstates - ots; /* accepted */
+}
+
+
+int Z80_NMI (Z80Info *z80)
+{
+ int ots = z80->tstates;
+ /***/
+ /* emulate Z80 bug with interrupted LD A,I/R */
+ /*if (z80->prev_was_EIDDR < 0) { z80->prev_was_EIDDR = 0; z80->af.f &= ~Z80_FLAG_PV; }*/
+ /*if (z80->prev_was_EIDDR) return 0;*/
+ z80->prev_was_EIDDR = 0; /* don't care */
+ if (z80->halted)
+ {
+ z80->halted = 0;
+ INC_PC;
+ }
+ INC_R;
+ z80->iff1 = 0; /* IFF2 is not changed */
+ z80->tstates += 5; /* M1 cycle: 5 T states to do an opcode read and decrement SP */
+ /* M2 cycle: 3 T states write high byte of PC to the stack and decrement SP */
+ /* M3 cycle: 3 T states write the low byte of PC and jump to #0066 */
+ Z80_Push6T(z80, z80->pc);
+ z80->memptr.w = z80->pc = 0x66;
+ return z80->tstates - ots;
+}
+
+
+/******************************************************************************/
+uint16_t Z80_Pop (Z80Info *z80)
+{
+ uint16_t res = Z80_PeekBI(z80, z80->sp.w);
+ /***/
+ z80->sp.w = (z80->sp.w + 1) & 0xffff;
+ res |= ((uint16_t)Z80_PeekBI(z80, z80->sp.w)) << 8;
+ z80->sp.w = (z80->sp.w + 1) & 0xffff;
+ return res;
+}
+
+
+void Z80_Push (Z80Info *z80, uint16_t value)
+{
+ z80->sp.w = (((int32_t)z80->sp.w) - 1) & 0xffff;
+ Z80_PokeBI(z80, z80->sp.w, (value >> 8) & 0xff);
+ z80->sp.w = (((int32_t)z80->sp.w) - 1) & 0xffff;
+ Z80_PokeBI(z80, z80->sp.w, value & 0xff);
+}
diff --git a/zymosis.h b/zymosis.h
new file mode 100644
index 0000000..6b2bb9a
--- /dev/null
+++ b/zymosis.h
@@ -0,0 +1,214 @@
+/*
+ * Z80 CPU emulation engine v0.0.3b
+ * coded by Ketmar // Vampire Avalon
+ *
+ * This program is free software. It comes without any warranty, to
+ * the extent permitted by applicable law. You can redistribute it
+ * and/or modify it under the terms of the Do What The Fuck You Want
+ * To Public License, Version 2, as published by Sam Hocevar. See
+ * http://sam.zoy.org/wtfpl/COPYING for more details.
+ */
+#ifndef _ZYMOSIS_H_
+#define _ZYMOSIS_H_
+
+#define ZYMOSIS_LITTLE_ENDIAN
+
+/* define either ZYMOSIS_LITTLE_ENDIAN or ZYMOSIS_BIG_ENDIAN */
+
+#if !defined(ZYMOSIS_LITTLE_ENDIAN) && !defined(ZYMOSIS_BIG_ENDIAN)
+# error wtf?! Zymosis endiannes is not defined!
+#endif
+
+#if defined(ZYMOSIS_LITTLE_ENDIAN) && defined(ZYMOSIS_BIG_ENDIAN)
+# error wtf?! Zymosis endiannes double defined! are you nuts?
+#endif
+
+#if defined(__GNUC__)
+# ifndef ZYMOSIS_PACKED
+# define ZYMOSIS_PACKED __attribute__((packed)) __attribute__((gcc_struct))
+# endif
+# ifndef ZYMOSIS_INLINE
+# define ZYMOSIS_INLINE __inline
+# endif
+#else
+# ifndef ZYMOSIS_PACKED
+# define ZYMOSIS_PACKED
+# endif
+# ifndef ZYMOSIS_INLINE
+# define ZYMOSIS_INLINE
+# endif
+#endif
+
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* flag masks */
+enum {
+ Z80_FLAG_C = 0x01,
+ Z80_FLAG_N = 0x02,
+ Z80_FLAG_PV= 0x04,
+ Z80_FLAG_3 = 0x08,
+ Z80_FLAG_H = 0x10,
+ Z80_FLAG_5 = 0x20,
+ Z80_FLAG_Z = 0x40,
+ Z80_FLAG_S = 0x80,
+
+ Z80_FLAG_35 = Z80_FLAG_3|Z80_FLAG_5,
+ Z80_FLAG_S35 = Z80_FLAG_S|Z80_FLAG_3|Z80_FLAG_5
+};
+
+
+typedef union ZYMOSIS_PACKED {
+ uint16_t w;
+#ifdef ZYMOSIS_LITTLE_ENDIAN
+ struct ZYMOSIS_PACKED { uint8_t c, b; };
+ struct ZYMOSIS_PACKED { uint8_t e, d; };
+ struct ZYMOSIS_PACKED { uint8_t l, h; };
+ struct ZYMOSIS_PACKED { uint8_t f, a; };
+ struct ZYMOSIS_PACKED { uint8_t xl, xh; };
+ struct ZYMOSIS_PACKED { uint8_t yl, yh; };
+#else
+ struct ZYMOSIS_PACKED { uint8_t b, c; };
+ struct ZYMOSIS_PACKED { uint8_t d, e; };
+ struct ZYMOSIS_PACKED { uint8_t h, l; };
+ struct ZYMOSIS_PACKED { uint8_t a, f; };
+ struct ZYMOSIS_PACKED { uint8_t xh, xl; };
+ struct ZYMOSIS_PACKED { uint8_t yh, yl; };
+#endif
+} Z80WordReg;
+
+
+typedef enum {
+ Z80_MEMIO_OPCODE = 0x00, /* reading opcode */
+ Z80_MEMIO_OPCEXT = 0x01, /* 'ext' opcode (after CB/ED/DD/FD prefix) */
+ Z80_MEMIO_OPCARG = 0x02, /* opcode argument (jump destination, register value, etc) */
+ Z80_MEMIO_DATA = 0x03, /* reading/writing data */
+ Z80_MEMIO_OTHER = 0x04, /* other 'internal' reads (for memptr, etc; don't do contention, breakpoints or so) */
+ Z80_MEMIO_MASK = 0x0f,
+ /* values for memory contention */
+ Z80_MREQ_NONE = 0x00,
+ Z80_MREQ_WRITE = 0x10,
+ Z80_MREQ_READ = 0x20,
+ Z80_MREQ_MASK = 0xf0
+} Z80MemIOType;
+
+
+typedef enum {
+ Z80_PIO_NORMAL = 0x00, /* normal call in Z80 execution loop */
+ Z80_PIO_INTERNAL = 0x01, /* call from debugger or other place outside of Z80 execution loop */
+ /* flags for port contention */
+ Z80_PIOFLAG_IN = 0x10, /* doing 'in' if set */
+ Z80_PIOFLAG_EARLY = 0x20 /* 'early' port contetion, if set */
+} Z80PIOType;
+
+
+typedef struct Z80Info Z80Info;
+
+/* will be called when memory contention is necessary */
+/* must increase z80->tstates to at least 'tstates' arg */
+/* mio: Z80_MEMIO_xxx | Z80_MREQ_xxx */
+/* Zymosis will never call this CB for Z80_MEMIO_OTHER memory acces */
+typedef void (*Z80ContentionCB) (Z80Info *z80, uint16_t addr, int tstates, Z80MemIOType mio);
+
+/* will be called when port contention is necessary */
+/* must increase z80->tstates to at least 'tstates' arg */
+/* pio: can contain only Z80_PIOFLAG_xxx flags */
+/* `tstates` is always 1 when Z80_PIOFLAG_EARLY is set and 2 otherwise */
+typedef void (*Z80PortContentionCB) (Z80Info *z80, uint16_t port, int tstates, Z80PIOType pio);
+
+/* miot: only Z80_MEMIO_xxx, no need in masking */
+typedef uint8_t (*Z80MemReadCB) (Z80Info *z80, uint16_t addr, Z80MemIOType miot);
+typedef void (*Z80MemWriteCB) (Z80Info *z80, uint16_t addr, uint8_t value, Z80MemIOType miot);
+
+/* pio: only Z80_PIO_xxx, no need in masking */
+typedef uint8_t (*Z80PortInCB) (Z80Info *z80, uint16_t port, Z80PIOType pio);
+typedef void (*Z80PortOutCB) (Z80Info *z80, uint16_t port, uint8_t value, Z80PIOType pio);
+
+/* return !0 to exit immediately */
+typedef int (*Z80EDTrapCB) (Z80Info *z80, uint8_t trapCode);
+
+/* return !0 to break */
+typedef int (*Z80CheckBPCB) (Z80Info *z80);
+
+typedef void (*Z80PagerCB) (Z80Info *z80);
+
+
+struct Z80Info {
+ /* registers */
+ Z80WordReg bc, de, hl, af, sp, ix, iy;
+ /* alternate registers */
+ Z80WordReg bcx, dex, hlx, afx;
+ Z80WordReg *dd; /* pointer to current HL/IX/IY (inside this struct) for the current command */
+ Z80WordReg memptr;
+ uint16_t pc; /* program counter */
+ uint16_t prev_pc; /* first byte of the previous command */
+ uint16_t org_pc; /* first byte of the current command */
+ uint8_t regI;
+ uint8_t regR;
+ int iff1, iff2; /* boolean */
+ uint8_t im; /* IM (0-2) */
+ int halted; /* boolean; is CPU halted? main progam must manually reset this flag when it's appropriate */
+ int32_t tstates; /* t-states passed from previous interrupt (0-...) */
+ int32_t next_event_tstate; /* Z80Execute() will exit when tstates>=next_event_tstate */
+ int prev_was_EIDDR; /* 1: previous instruction was EI/FD/DD? (they blocks /INT); -1: prev vas LD A,I or LD A,R */
+ /* Zymosis will reset this flag only if it executed at least one instruction */
+ int evenM1; /* boolean; emulate 128K/Scorpion M1 contention? */
+ Z80MemReadCB memReadFn;
+ Z80MemWriteCB memWriteFn;
+ Z80ContentionCB contentionFn; /* can be NULL */
+ /* port I/O functions should add 4 t-states by themselves */
+ Z80PortInCB portInFn; /* in: +3; do read; +1 */
+ Z80PortOutCB portOutFn; /* out: +1; do out; +3 */
+ Z80PortContentionCB portContentionFn; /* can be NULL */
+ /* RETI/RETN traps; called with opcode, *AFTER* iff changed and return address set; return !0 to break execution */
+ Z80EDTrapCB retiFn;
+ Z80EDTrapCB retnFn;
+ /***/
+ Z80EDTrapCB trapEDFn; /* can be NULL */
+ /* called when invalid ED command found */
+ /* PC points to the next instruction */
+ /* trapCode=0xFB: */
+ /* .SLT trap */
+ /* HL: address to load; */
+ /* A: A --> level number */
+ /* return: CARRY complemented --> error */
+ Z80PagerCB pagerFn; /* can be NULL */
+ /* pagerFn is called before fetching opcode to allow, for example, TR-DOS ROM paging in/out */
+ Z80CheckBPCB checkBPFn; /* can be NULL */
+ /* checkBPFn is called just after pagerFn (before fetching opcode) */
+ /* emulator can check various breakpoint conditions there */
+ /* and return non-zero to immediately stop executing and return from Z80_Execute[XXX]() */
+ /***/
+ void *user; /* arbitrary user data */
+};
+
+
+/******************************************************************************/
+/* Z80InitTables() should be called before anyting else! */
+extern void Z80_InitTables (void); /* this will be automatically called by Z80_Reset() */
+
+extern void Z80_ResetCallbacks (Z80Info *z80);
+extern void Z80_Reset (Z80Info *z80);
+extern void Z80_Execute (Z80Info *z80);
+extern int32_t Z80_ExecuteStep (Z80Info *z80); /* returns number of executed ticks */
+extern int Z80_Interrupt (Z80Info *z80); /* !0: interrupt was accepted (returns # of t-states eaten); changes z80->tstates if interrupt occurs */
+extern int Z80_NMI (Z80Info *z80); /* !0: interrupt was accepted (returns # of t-states eaten); changes z80->tstates if interrupt occurs */
+
+/* without contention, using Z80_MEMIO_OTHER */
+extern uint16_t Z80_Pop (Z80Info *z80);
+extern void Z80_Push (Z80Info *z80, uint16_t value);
+
+/* execute at least 'tstates' t-states; return real number of executed t-states */
+/* WARNING: this function resets both z80->tstates and z80->next_event_tstate! */
+extern int32_t Z80_ExecuteTS (Z80Info *z80, int32_t tstates);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif