parent
af2334c245
commit
4c8b396e53
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 ArthurFerreira2
|
||||
Copyright (c) 2020 Arthur Ferreira
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
#
|
||||
# Cross Platform Makefile
|
||||
# Compatible with MSYS2/MINGW, Ubuntu 14.04.1 and Mac OS X
|
||||
#
|
||||
# You will need SDL2 (http://www.libsdl.org):
|
||||
# Linux:
|
||||
# apt-get install libsdl2-dev
|
||||
# Mac OS X:
|
||||
# brew install sdl2
|
||||
# MSYS2:
|
||||
# pacman -S mingw-w64-i686-SDL2
|
||||
#
|
||||
|
||||
# pick one
|
||||
CXX = g++
|
||||
#CXX = clang++
|
||||
|
||||
EXE = reinette
|
||||
SOURCES = main.cpp puce65c02.cpp mmu.cpp video.cpp disk.cpp speaker.cpp paddles.cpp gui.cpp
|
||||
|
||||
IMGUI_DIR = lib/imgui-1.82
|
||||
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_opengl2.cpp
|
||||
|
||||
ImGuiColorTextEdit_DIR = lib/ImGuiColorTextEdit-master
|
||||
SOURCES += $(ImGuiColorTextEdit_DIR)/TextEditor.cpp
|
||||
|
||||
imfilebrowser_DIR = lib/imgui-filebrowser
|
||||
imgui_club_DIR = lib/imgui_club-master
|
||||
|
||||
|
||||
OBJS = $(addsuffix .o, $(basename $(notdir $(SOURCES))))
|
||||
UNAME_S := $(shell uname -s)
|
||||
|
||||
CXXFLAGS = -I$(IMGUI_DIR) -I$(IMGUI_DIR)/backends -I$(ImGuiColorTextEdit_DIR) -I$(imfilebrowser_DIR) -I$(imgui_club_DIR)
|
||||
# CXXFLAGS += -O3
|
||||
CXXFLAGS += -std=c++17 -lstdc++fs -Wall -Wformat -pedantic -Wpedantic -O3
|
||||
LIBS =
|
||||
|
||||
WIN32-RC = reinette.rc
|
||||
WIN32-RES = reinette.res
|
||||
|
||||
|
||||
##---------------------------------------------------------------------
|
||||
## BUILD FLAGS PER PLATFORM
|
||||
##---------------------------------------------------------------------
|
||||
|
||||
ifeq ($(UNAME_S), Linux) #LINUX
|
||||
ECHO_MESSAGE = "Linux"
|
||||
LIBS += -lGL -ldl `sdl2-config --libs`
|
||||
|
||||
CXXFLAGS += `sdl2-config --cflags`
|
||||
CFLAGS = $(CXXFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME_S), Darwin) #APPLE
|
||||
ECHO_MESSAGE = "Mac OS X"
|
||||
LIBS += -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo `sdl2-config --libs`
|
||||
LIBS += -L/usr/local/lib -L/opt/local/lib
|
||||
|
||||
CXXFLAGS += `sdl2-config --cflags`
|
||||
CXXFLAGS += -I/usr/local/include -I/opt/local/include
|
||||
CFLAGS = $(CXXFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(OS), Windows_NT)
|
||||
ECHO_MESSAGE = "MinGW"
|
||||
# LIBS += -lgdi32 -lopengl32 -limm32 `pkg-config --static --libs sdl2`
|
||||
LIBS += -lgdi32 -lopengl32 -limm32 -lmingw32 --static -Wl,-subsystem,windows -LC:/msys64/mingw64/lib -lmingw32 -lSDL2main -lSDL2 -mwindows -lmingw32 -ldinput8 -lshell32 -lsetupapi -ladvapi32 -luuid -lversion -loleaut32 -lole32 -limm32 -lwinmm -lgdi32 -luser32 -lm -mwindows -Wl,--no-undefined -pipe -Wl,--dynamicbase,--high-entropy-va,--nxcompat,--default-image-base-high
|
||||
|
||||
# CXXFLAGS += `pkg-config --cflags sdl2 `
|
||||
CXXFLAGS += -IC:/msys64/mingw64/include/SDL2
|
||||
CFLAGS = $(CXXFLAGS)
|
||||
endif
|
||||
|
||||
##---------------------------------------------------------------------
|
||||
## BUILD RULES
|
||||
##---------------------------------------------------------------------
|
||||
|
||||
%.o:%.cpp
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
%.o:$(IMGUI_DIR)/%.cpp
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
%.o:$(IMGUI_DIR)/backends/%.cpp
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
%.o:$(ImGuiColorTextEdit_DIR)/%.cpp
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
|
||||
|
||||
all: $(EXE)
|
||||
@echo Build complete for $(ECHO_MESSAGE)
|
||||
|
||||
$(EXE): $(OBJS) $(WIN32-RES)
|
||||
$(CXX) -o $@ $^ $(CXXFLAGS) $(LIBS)
|
||||
|
||||
$(WIN32-RES): $(WIN32-RC)
|
||||
windres -o $@ $^ -O coff
|
||||
|
||||
clean:
|
||||
rm -f $(EXE) $(OBJS) $(WIN32-RES)
|
16
README.md
16
README.md
|
@ -1,2 +1,14 @@
|
|||
# reinetteIIe
|
||||
Reinette IIe, an enhanced Apple //e emulator
|
||||
WORK IN PROGRESS
|
||||
|
||||
|
||||
# reinette IIe
|
||||
|
||||
### reinette gets enhanced !
|
||||
|
||||
![screenshots](animated.gif)
|
||||
|
||||
|
||||
\
|
||||
\
|
||||
\
|
||||
*simplicity is the ultimate sophistication*
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 4.2 MiB |
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* reinette, a french Apple II emulator, using SDL2
|
||||
* and powered by puce65c02 - a WDS 65c02 cpu emulator by the same author
|
||||
* Last modified 1st of July 2021
|
||||
* Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "reinette.h"
|
||||
|
||||
Disk::Disk() {
|
||||
curDrv = 0; // Current Drive - only one can be enabled at a time
|
||||
// TODO : initialize the unit[x] structs
|
||||
}
|
||||
|
||||
|
||||
int Disk::load( char *path, int drive) {
|
||||
FILE *f = fopen(path, "rb"); // open file in read binary mode
|
||||
|
||||
if (!f || fread(unit[drive].data, 1, 232960, f) != 232960) // load it into memory and check size
|
||||
return 0;
|
||||
fclose(f);
|
||||
|
||||
sprintf(unit[drive].pathName, "%s", path); // update floppy image pathName record
|
||||
|
||||
// get filename
|
||||
int i =0, a = 0;
|
||||
while (unit[drive].pathName[i] != 0) { // find start of filename for disk0
|
||||
if (unit[drive].pathName[i] == '/' || unit[drive].pathName[i] == '\\')
|
||||
a = i + 1;
|
||||
i++;
|
||||
}
|
||||
sprintf(unit[drive].fileName, "%s", unit[drive].pathName + a);
|
||||
|
||||
// is the file read only ?
|
||||
f = fopen(path, "ab"); // try to open the file in append binary mode
|
||||
if (f) { // success, file is writable
|
||||
unit[drive].readOnly = false; // update the readOnly flag
|
||||
fclose(f); // and close it untouched
|
||||
} else {
|
||||
unit[drive].readOnly = true; // f is NULL, no writable, no need to close it
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int Disk::save(int drive) {
|
||||
if (!unit[drive].pathName[0]) return 0; // no file loaded into drive
|
||||
if (unit[drive].readOnly) return 0; // file is read only write no aptempted
|
||||
|
||||
FILE *f = fopen(unit[drive].pathName, "wb");
|
||||
|
||||
if (!f) return 0; // could not open the file in write overide binary
|
||||
if (fwrite(unit[drive].data, 1, 232960, f) != 232960) { // failed to write the full file (hdd full ?)
|
||||
fclose(f); // release the ressource
|
||||
return 0;
|
||||
}
|
||||
fclose(f); // success, release the ressource
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int Disk::eject(int drive) {
|
||||
unit[drive].fileName[0] = 0;
|
||||
unit[drive].pathName[0] = 0;
|
||||
unit[drive].readOnly = false;
|
||||
|
||||
for (int i=0; i<232960; i++) // erase data
|
||||
unit[drive].data[i]=0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void Disk::stepMotor(uint16_t address) {
|
||||
static bool phases[2][4] = { 0 }; // phases states (for both drives)
|
||||
static bool phasesB[2][4] = { 0 }; // phases states Before
|
||||
static bool phasesBB[2][4] = { 0 }; // phases states Before Before
|
||||
static int pIdx[2] = { 0 }; // phase index (for both drives)
|
||||
static int pIdxB[2] = { 0 }; // phase index Before
|
||||
static int halfTrackPos[2] = { 0 };
|
||||
|
||||
address &= 7;
|
||||
int phase = address >> 1;
|
||||
|
||||
phasesBB[curDrv][pIdxB[curDrv]] = phasesB[curDrv][pIdxB[curDrv]];
|
||||
phasesB[curDrv][pIdx[curDrv]] = phases[curDrv][pIdx[curDrv]];
|
||||
pIdxB[curDrv] = pIdx[curDrv];
|
||||
pIdx[curDrv] = phase;
|
||||
|
||||
if (!(address & 1)) { // head not moving (PHASE x OFF)
|
||||
phases[curDrv][phase] = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((phasesBB[curDrv][(phase + 1) & 3]) && (--halfTrackPos[curDrv] < 0)) // head is moving in
|
||||
halfTrackPos[curDrv] = 0;
|
||||
|
||||
if ((phasesBB[curDrv][(phase - 1) & 3]) && (++halfTrackPos[curDrv] > 140)) // head is moving out
|
||||
halfTrackPos[curDrv] = 140;
|
||||
|
||||
phases[curDrv][phase] = true; // update track#
|
||||
unit[curDrv].track = (halfTrackPos[curDrv] + 1) / 2;
|
||||
}
|
||||
|
||||
|
||||
void Disk::setDrv(int drv) {
|
||||
unit[drv].motorOn = unit[!drv].motorOn || unit[drv].motorOn; // if any of the motors were ON
|
||||
unit[!drv].motorOn = false; // motor of the other drive is set to OFF
|
||||
curDrv = drv; // set the current drive
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* reinette, a french Apple II emulator, using SDL2
|
||||
* and powered by puce65c02 - a WDS 65c02 cpu emulator by the same author
|
||||
* Last modified 1st of July 2021
|
||||
* Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __DISK_H__
|
||||
#define __DISK_H__
|
||||
|
||||
typedef struct Drive_t {
|
||||
char fileName[400]; // the floppy image file name
|
||||
char pathName[400]; // the full floppy image path name
|
||||
bool readOnly; // based on the image file attributes
|
||||
uint8_t data[232960]; // nibblelized floppy image
|
||||
bool motorOn; // motor status
|
||||
bool writeMode; // writes to file are not implemented
|
||||
uint8_t track; // current track position
|
||||
uint16_t nibble; // ptr to nibble under head position
|
||||
} Drive;
|
||||
|
||||
|
||||
class Disk {
|
||||
public:
|
||||
int curDrv = 0; // Current Drive - only one can be enabled at a time
|
||||
Drive unit[2] = {0}; // two disk ][ drive units
|
||||
|
||||
Disk();
|
||||
~Disk();
|
||||
|
||||
int load(char *filename, int drv);
|
||||
int save(int drive);
|
||||
int eject(int drive);
|
||||
|
||||
void setDrv(int drv);
|
||||
void stepMotor(uint16_t address);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,748 @@
|
|||
/*
|
||||
* reinette, a french Apple II emulator, using SDL2
|
||||
* and powered by puce65c02 - a WDS 65c02 cpu emulator by the same author
|
||||
* Last modified 1st of July 2021
|
||||
* Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "reinette.h"
|
||||
|
||||
Gui::Gui() {
|
||||
frameNumber = 0;
|
||||
screenScale = 2.0f;
|
||||
fps = 60;
|
||||
|
||||
show_about_window = false;
|
||||
show_help_window = false;
|
||||
CRT_is_focused = false;
|
||||
show_crt_window = true;
|
||||
show_control_window = true;
|
||||
show_disks_window = true;
|
||||
show_info_window = true;
|
||||
show_cpu_window = true;
|
||||
show_code_window = true;
|
||||
show_pageZero_window = true;
|
||||
show_stack_window = true;
|
||||
show_ram_window = true;
|
||||
show_aux_window = true;
|
||||
show_rom_window = true;
|
||||
show_editor_window = true;
|
||||
show_ramHeatmap_window = true;
|
||||
show_auxHeatmap_window = true;
|
||||
|
||||
// Setup SDL
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) {
|
||||
printf("Error: %s\n", SDL_GetError());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Setup window
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
wdo = SDL_CreateWindow("Reinette", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1700, 900, window_flags);
|
||||
gl_context = SDL_GL_CreateContext(wdo);
|
||||
SDL_GL_MakeCurrent(wdo, gl_context);
|
||||
SDL_GL_SetSwapInterval(1); // Enable vsync
|
||||
|
||||
SDL_EventState(SDL_DROPFILE, SDL_ENABLE); // ask SDL2 to read dropfile events
|
||||
|
||||
// Setup Dear ImGui context
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
||||
// io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||
// io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||
|
||||
#include "imguiTheme.h"
|
||||
|
||||
// Setup Platform/Renderer backends
|
||||
ImGui_ImplSDL2_InitForOpenGL(wdo, gl_context);
|
||||
ImGui_ImplOpenGL2_Init();
|
||||
|
||||
clear_color = ImVec4((float)51/256, (float)58/256, (float)64/256, 1.00f); // fond de la fenetre principale
|
||||
glGenTextures(1, &screenTexture);
|
||||
glGenTextures(1, &ramHeatmapTexture);
|
||||
glGenTextures(1, &auxHeatmapTexture);
|
||||
|
||||
// editor init
|
||||
auto lang = TextEditor::LanguageDefinition::AppleSoft();
|
||||
editor.SetLanguageDefinition(lang);
|
||||
editor.SetShowWhitespaces(false);
|
||||
editor.SetPalette(TextEditor::GetLightPalette());
|
||||
fileToEdit = "program.bas";
|
||||
std::ifstream t(fileToEdit);
|
||||
if (t.good()) {
|
||||
std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
|
||||
editor.SetText(str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int Gui::newFrame() {
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplOpenGL2_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame(wdo);
|
||||
ImGui::NewFrame();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Gui::getInputs() {
|
||||
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||
|
||||
SDL_bool ctrl, shift, alt;
|
||||
|
||||
alt = SDL_GetModState() & KMOD_ALT ? SDL_TRUE : SDL_FALSE;
|
||||
ctrl = SDL_GetModState() & KMOD_CTRL ? SDL_TRUE : SDL_FALSE;
|
||||
shift = SDL_GetModState() & KMOD_SHIFT ? SDL_TRUE : SDL_FALSE;
|
||||
paddles->PB0 = alt ? 0xFF : 0x00; // update push button 0, Open Apple
|
||||
paddles->PB1 = ctrl ? 0xFF : 0x00; // update push button 1, Solid Apple or option
|
||||
paddles->PB2 = shift ? 0xFF : 0x00; // update push button 2, single-wire Shift-key mod
|
||||
|
||||
|
||||
if (event.type == SDL_QUIT) running = false; // WM sent TERM signal
|
||||
|
||||
if (event.type == SDL_DROPFILE) { // user dropped a file
|
||||
char *filename = event.drop.file; // get full pathname
|
||||
if (!disk->load(filename, alt)) // if ALT : drv 1 else drv 0
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Load", "Not a valid nib file", NULL);
|
||||
SDL_free(filename); // free filename memory
|
||||
paused = false; // might already be the case
|
||||
if (!(alt || ctrl)) { // unless ALT or CTRL were
|
||||
mmu->init();
|
||||
mmu->writeMem(0x3F4,0); // unset the Power-UP byte
|
||||
cpu->RST(); // do a cold reset
|
||||
}
|
||||
}
|
||||
|
||||
if (CRT_is_focused) { // if (!io.WantCaptureKeyboard) {
|
||||
if (event.type == SDL_KEYDOWN) { // a key has been pressed
|
||||
switch (event.key.keysym.sym) {
|
||||
|
||||
// EMULATOR CONTROLS :
|
||||
|
||||
case SDLK_F3: // PASTE text from clipboard
|
||||
if (SDL_HasClipboardText()) {
|
||||
char *clipboardText = SDL_GetClipboardText();
|
||||
int c = 0;
|
||||
while (clipboardText[c]) { // all chars until ascii NUL
|
||||
mmu->KBD = clipboardText[c++] | 0x80; // set bit7
|
||||
if (mmu->KBD == 0x8A) mmu->KBD = 0x8D; // translate Line Feed to Carriage Ret
|
||||
cpu->exec(400000); // give cpu (and applesoft) some cycles to process each char
|
||||
}
|
||||
SDL_free(clipboardText); // release the ressource
|
||||
}
|
||||
break;
|
||||
|
||||
case SDLK_F4: // VOLUME
|
||||
if (shift) speaker->setVolume(volume+10); // increase volume
|
||||
if (ctrl) speaker->setVolume(volume-10); // decrease volume
|
||||
if (!ctrl && !shift) speaker->toggleMute(); // toggle mute / unmute
|
||||
break;
|
||||
|
||||
case SDLK_F5: // JOYSTICK Release Speed
|
||||
if (shift && (paddles->GCReleaseSpeed < 127)) paddles->GCReleaseSpeed += 2; // increase Release Speed
|
||||
if (ctrl && (paddles->GCReleaseSpeed > 1)) paddles->GCReleaseSpeed -= 2; // decrease Release Speed
|
||||
if (!ctrl && !shift) paddles->GCReleaseSpeed = 8; // reset Release Speed to 8
|
||||
break;
|
||||
|
||||
case SDLK_F6: // JOYSTICK Action Speed
|
||||
if (shift && (paddles->GCActionSpeed < 127)) paddles->GCActionSpeed += 2; // increase Action Speed
|
||||
if (ctrl && (paddles->GCActionSpeed > 1)) paddles->GCActionSpeed -= 2; // decrease Action Speed
|
||||
if (!ctrl && !shift) paddles->GCActionSpeed = 8; // reset Action Speed to 8
|
||||
break;
|
||||
|
||||
case SDLK_F7: // ZOOM
|
||||
if (shift && (screenScale <= 3.9f)) screenScale+=.1f; // zoom in
|
||||
if (ctrl && (screenScale > 1.0f)) screenScale-=.1f; // zoom out
|
||||
if (!ctrl && !shift) screenScale = 2.0f; // reset zoom to 2
|
||||
break;
|
||||
|
||||
case SDLK_F10: paused = !paused; break; // toggle pause
|
||||
|
||||
case SDLK_F11: {
|
||||
paused = true;
|
||||
cpu->state = step;
|
||||
} break;
|
||||
|
||||
case SDLK_F12: cpu->RST(); break; // reset
|
||||
|
||||
// EMULATED KEYS :
|
||||
|
||||
case SDLK_a: mmu->KBD = ctrl ? 0x81: 0xC1; break; // a
|
||||
case SDLK_b: mmu->KBD = ctrl ? 0x82: 0xC2; break; // b STX
|
||||
case SDLK_c: mmu->KBD = ctrl ? 0x83: 0xC3; break; // c ETX
|
||||
case SDLK_d: mmu->KBD = ctrl ? 0x84: 0xC4; break; // d EOT
|
||||
case SDLK_e: mmu->KBD = ctrl ? 0x85: 0xC5; break; // e
|
||||
case SDLK_f: mmu->KBD = ctrl ? 0x86: 0xC6; break; // f ACK
|
||||
case SDLK_g: mmu->KBD = ctrl ? 0x87: 0xC7; break; // g BELL
|
||||
case SDLK_h: mmu->KBD = ctrl ? 0x88: 0xC8; break; // h BS
|
||||
case SDLK_i: mmu->KBD = ctrl ? 0x89: 0xC9; break; // i HTAB
|
||||
case SDLK_j: mmu->KBD = ctrl ? 0x8A: 0xCA; break; // j LF
|
||||
case SDLK_k: mmu->KBD = ctrl ? 0x8B: 0xCB; break; // k VTAB
|
||||
case SDLK_l: mmu->KBD = ctrl ? 0x8C: 0xCC; break; // l FF
|
||||
case SDLK_m: mmu->KBD = ctrl ? shift ? 0x9D:0x8D:0xCD; break;// m CR ]
|
||||
case SDLK_n: mmu->KBD = ctrl ? shift ? 0x9E:0x8E:0xCE; break;// n ^
|
||||
case SDLK_o: mmu->KBD = ctrl ? 0x8F: 0xCF; break; // o
|
||||
case SDLK_p: mmu->KBD = ctrl ? shift ? 0x80:0x90:0xD0; break; // p @
|
||||
case SDLK_q: mmu->KBD = ctrl ? 0x91: 0xD1; break; // q
|
||||
case SDLK_r: mmu->KBD = ctrl ? 0x92: 0xD2; break; // r
|
||||
case SDLK_s: mmu->KBD = ctrl ? 0x93: 0xD3; break; // s ESC
|
||||
case SDLK_t: mmu->KBD = ctrl ? 0x94: 0xD4; break; // t
|
||||
case SDLK_u: mmu->KBD = ctrl ? 0x95: 0xD5; break; // u NAK
|
||||
case SDLK_v: mmu->KBD = ctrl ? 0x96: 0xD6; break; // v
|
||||
case SDLK_w: mmu->KBD = ctrl ? 0x97: 0xD7; break; // w
|
||||
case SDLK_x: mmu->KBD = ctrl ? 0x98: 0xD8; break; // x CANCEL
|
||||
case SDLK_y: mmu->KBD = ctrl ? 0x99: 0xD9; break; // y
|
||||
case SDLK_z: mmu->KBD = ctrl ? 0x9A: 0xDA; break; // z
|
||||
case SDLK_LEFTBRACKET: mmu->KBD = ctrl ? 0x9B: 0xDB; break; // [ {
|
||||
case SDLK_BACKSLASH: mmu->KBD = ctrl ? 0x9C: 0xDC; break; // \ |
|
||||
case SDLK_RIGHTBRACKET: mmu->KBD = ctrl ? 0x9D: 0xDD; break; // ] }
|
||||
case SDLK_BACKSPACE: mmu->KBD = ctrl ? 0xDF: 0x88; break; // BS
|
||||
case SDLK_0: mmu->KBD = shift? 0xA9: 0xB0; break; // 0 )
|
||||
case SDLK_1: mmu->KBD = shift? 0xA1: 0xB1; break; // 1 !
|
||||
case SDLK_2: mmu->KBD = shift? 0xC0: 0xB2; break; // 2
|
||||
case SDLK_3: mmu->KBD = shift? 0xA3: 0xB3; break; // 3 #
|
||||
case SDLK_4: mmu->KBD = shift? 0xA4: 0xB4; break; // 4 $
|
||||
case SDLK_5: mmu->KBD = shift? 0xA5: 0xB5; break; // 5 %
|
||||
case SDLK_6: mmu->KBD = shift? 0xDE: 0xB6; break; // 6 ^
|
||||
case SDLK_7: mmu->KBD = shift? 0xA6: 0xB7; break; // 7 &
|
||||
case SDLK_8: mmu->KBD = shift? 0xAA: 0xB8; break; // 8 *
|
||||
case SDLK_9: mmu->KBD = shift? 0xA8: 0xB9; break; // 9 (
|
||||
case SDLK_QUOTE: mmu->KBD = shift? 0xA2: 0xA7; break; // ' "
|
||||
case SDLK_EQUALS: mmu->KBD = shift? 0xAB: 0xBD; break; // = +
|
||||
case SDLK_SEMICOLON: mmu->KBD = shift? 0xBA: 0xBB; break; // ; :
|
||||
case SDLK_COMMA: mmu->KBD = shift? 0xBC: 0xAC; break; // , <
|
||||
case SDLK_PERIOD: mmu->KBD = shift? 0xBE: 0xAE; break; // . >
|
||||
case SDLK_SLASH: mmu->KBD = shift? 0xBF: 0xAF; break; // / ?
|
||||
case SDLK_MINUS: mmu->KBD = shift? 0xDF: 0xAD; break; // - _
|
||||
case SDLK_BACKQUOTE: mmu->KBD = shift? 0xFE: 0xE0; break; // ` ~
|
||||
case SDLK_LEFT: mmu->KBD = 0x88; break; // BS
|
||||
case SDLK_RIGHT: mmu->KBD = 0x95; break; // NAK
|
||||
case SDLK_DOWN: mmu->KBD = 0x8A; break; // LF
|
||||
case SDLK_UP: mmu->KBD = 0x8B; break; // VTAB
|
||||
case SDLK_SPACE: mmu->KBD = 0xA0; break;
|
||||
case SDLK_ESCAPE: mmu->KBD = 0x9B; break; // ESC
|
||||
case SDLK_RETURN: mmu->KBD = 0x8D; break; // CR
|
||||
case SDLK_TAB: mmu->KBD = 0x89; break; // HTAB
|
||||
|
||||
// EMULATED JOYSTICK :
|
||||
|
||||
case SDLK_KP_1: paddles->GCD[0] = -1; paddles->GCA[0] = 1; break; // pdl0 <-
|
||||
case SDLK_KP_3: paddles->GCD[0] = 1; paddles->GCA[0] = 1; break; // pdl0 ->
|
||||
case SDLK_KP_5: paddles->GCD[1] = -1; paddles->GCA[1] = 1; break; // pdl1 <-
|
||||
case SDLK_KP_2: paddles->GCD[1] = 1; paddles->GCA[1] = 1; break; // pdl1 ->
|
||||
}
|
||||
}
|
||||
|
||||
if (event.type == SDL_KEYUP) {
|
||||
switch (event.key.keysym.sym) {
|
||||
case SDLK_KP_1: paddles->GCD[0] = 1; paddles->GCA[0] = 0; break; // pdl0 ->
|
||||
case SDLK_KP_3: paddles->GCD[0] = -1; paddles->GCA[0] = 0; break; // pdl0 <-
|
||||
case SDLK_KP_5: paddles->GCD[1] = 1; paddles->GCA[1] = 0; break; // pdl1 ->
|
||||
case SDLK_KP_2: paddles->GCD[1] = -1; paddles->GCA[1] = 0; break; // pdl1 <-
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int Gui::update() {
|
||||
|
||||
if (ImGui::BeginMainMenuBar()) {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("New", "Ctrl+N")) {
|
||||
// void editor.SetText(const std::string& aText);
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Open", "Ctrl+O")) {
|
||||
// void SetText(const std::string& aText);
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Save", "Ctrl+S")) {
|
||||
// std::string textToSave = editor.GetText();
|
||||
// std::ifstream t(fileToEdit);
|
||||
// if (t.good()) {
|
||||
// std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
|
||||
// editor.SetText(str);
|
||||
// }
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Save As..")) {
|
||||
// TBD
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Quit", "CTRL+Q")) {
|
||||
running = false;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Edit")) {
|
||||
bool ro = editor.IsReadOnly();
|
||||
if (ImGui::MenuItem("Read-only mode", nullptr, &ro))
|
||||
editor.SetReadOnly(ro);
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Undo", "CTRL+Z", nullptr, !ro && editor.CanUndo()))
|
||||
editor.Undo();
|
||||
if (ImGui::MenuItem("Redo", "CTRL+Y", nullptr, !ro && editor.CanRedo()))
|
||||
editor.Redo();
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Copy", "Ctrl-C", nullptr, editor.HasSelection()))
|
||||
editor.Copy();
|
||||
if (ImGui::MenuItem("Cut", "Ctrl-X", nullptr, !ro && editor.HasSelection()))
|
||||
editor.Cut();
|
||||
if (ImGui::MenuItem("Delete", "Del", nullptr, !ro && editor.HasSelection()))
|
||||
editor.Delete();
|
||||
if (ImGui::MenuItem("Paste", "Ctrl-V", nullptr, !ro && ImGui::GetClipboardText() != nullptr))
|
||||
editor.Paste();
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Select all", nullptr, nullptr))
|
||||
editor.SetSelection(TextEditor::Coordinates(), TextEditor::Coordinates(editor.GetTotalLines(), 0));
|
||||
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Dark palette"))
|
||||
editor.SetPalette(TextEditor::GetDarkPalette());
|
||||
if (ImGui::MenuItem("Light palette"))
|
||||
editor.SetPalette(TextEditor::GetLightPalette());
|
||||
if (ImGui::MenuItem("Retro blue palette"))
|
||||
editor.SetPalette(TextEditor::GetRetroBluePalette());
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("CRT", NULL, &show_crt_window);
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem("Page Zero", NULL, &show_pageZero_window);
|
||||
ImGui::MenuItem("Stack", NULL, &show_stack_window);
|
||||
ImGui::MenuItem("Main RAM", NULL, &show_ram_window);
|
||||
ImGui::MenuItem("Aux RAM", NULL, &show_aux_window);
|
||||
ImGui::MenuItem("ROM", NULL, &show_rom_window);
|
||||
ImGui::MenuItem("RAM Heatmap", NULL, &show_ramHeatmap_window);
|
||||
ImGui::MenuItem("AUX Heatmap", NULL, &show_auxHeatmap_window);
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem("Controls", NULL, &show_control_window);
|
||||
ImGui::MenuItem("CPU", NULL, &show_cpu_window);
|
||||
ImGui::MenuItem("Code", NULL, &show_code_window);
|
||||
ImGui::MenuItem("Disk", NULL, &show_disks_window);
|
||||
ImGui::MenuItem("Info", NULL, &show_info_window);
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem("Editor", NULL, &show_editor_window);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Help")) {
|
||||
ImGui::MenuItem("Help", NULL, &show_help_window);
|
||||
ImGui::MenuItem("About", NULL, &show_about_window);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
static MemoryEditor mem_edit_stack;
|
||||
if (show_stack_window) {
|
||||
mem_edit_stack.DrawWindow("STACK", mmu->ram+256, 256);
|
||||
}
|
||||
|
||||
static MemoryEditor mem_edit_pageZero;
|
||||
if (show_pageZero_window) {
|
||||
mem_edit_pageZero.DrawWindow("PAGE ZERO", mmu->ram, 256);
|
||||
}
|
||||
|
||||
static MemoryEditor mem_edit_ram;
|
||||
if (show_ram_window) {
|
||||
mem_edit_ram.DrawWindow("RAM", mmu->ram, RAMSIZE);
|
||||
}
|
||||
|
||||
static MemoryEditor mem_edit_rom;
|
||||
if (show_rom_window) {
|
||||
mem_edit_rom.DrawWindow("ROM", mmu->rom, ROMSIZE);
|
||||
}
|
||||
|
||||
static MemoryEditor mem_edit_aux;
|
||||
if (show_aux_window) {
|
||||
mem_edit_aux.DrawWindow("AUX", mmu->aux, AUXSIZE);
|
||||
}
|
||||
|
||||
if (show_about_window) {
|
||||
if (ImGui::Begin("ABOUT", &show_about_window, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("Reinette 2021 Arthur Ferreira " );
|
||||
ImGui::Separator();
|
||||
ImGui::Text("version : %s", VERSION);
|
||||
ImGui::Text("https://github.com/ArthurFerreira2");
|
||||
ImGui::Text("\nReinette is licensed under the MIT License\nsee LICENSE for more information.\n");
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
if (show_help_window) {
|
||||
if (ImGui::Begin("HELP", &show_help_window, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("Reinette v%s", VERSION);
|
||||
ImGui::Separator();
|
||||
ImGui::Text( "\nctrl F1 writes the changes of the floppy in drive 0"
|
||||
"\nalt F1 writes the changes of the floppy in drive 1"
|
||||
"\n"
|
||||
"\nF3 paste text from clipboard"
|
||||
"\n"
|
||||
"\nF4 mute / un-mute sound"
|
||||
"\nshift F4 increase volume"
|
||||
"\nctrl F4 decrease volume"
|
||||
"\n"
|
||||
"\nF5 reset joystick release speed"
|
||||
"\nshift F5 increase joystick release speed"
|
||||
"\ncrtl F5 decrease joystick release speed"
|
||||
"\n"
|
||||
"\nF6 reset joystick action speed"
|
||||
"\nshift F6 increase joystick action speed"
|
||||
"\ncrtl F6 decrease joystick action speed"
|
||||
"\n"
|
||||
"\nF7 reset the zoom to 2:1"
|
||||
"\nshift F7 increase zoom up to 4:1 max"
|
||||
"\nctrl F7 decrease zoom down to 1:1 pixels"
|
||||
"\n"
|
||||
"\nF8 monochrome / color display (only in HGR)"
|
||||
"\nF9 pause / un-pause the emulator"
|
||||
"\nF10 Not implemented"
|
||||
"\nF11 reset");
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
||||
if (show_ramHeatmap_window) {
|
||||
ImGui::Begin("RAM HEATMAP", &show_ramHeatmap_window);
|
||||
// Adjust the image to the window
|
||||
auto window_size = ImGui::GetWindowSize();
|
||||
auto image_size = ImVec2(window_size.x - 15, window_size.y - 50);
|
||||
// Draw image
|
||||
ImGui::Image((void*)((intptr_t)ramHeatmapTexture), image_size, ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(0,0,0,0));
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (show_auxHeatmap_window) {
|
||||
ImGui::Begin("AUX HEATMAP", &show_auxHeatmap_window);
|
||||
// Adjust the image to the window
|
||||
auto window_size = ImGui::GetWindowSize();
|
||||
auto image_size = ImVec2(window_size.x - 15, window_size.y - 50);
|
||||
// Draw image
|
||||
ImGui::Image((void*)((intptr_t)auxHeatmapTexture), image_size, ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(0,0,0,0));
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (show_control_window) {
|
||||
ImGui::Begin("CONTROLS", &show_control_window);
|
||||
ImGui::SliderInt("GC ACTION", &paddles->GCActionSpeed, 0, 128); // JOYSTICK Action Speed
|
||||
ImGui::SliderInt("GC RELEASE", &paddles->GCReleaseSpeed, 0, 128); // JOYSTICK Release Speed
|
||||
ImGui::Separator();
|
||||
ImGui::SliderFloat("SCALE", &screenScale, 1, 4, "%.1f");
|
||||
if (ImGui::SliderInt("VOLUME", &volume, 0, 127)) speaker->setVolume(volume);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Checkbox("MUTE", &muted)) speaker->toggleMute();
|
||||
ImGui::Separator();
|
||||
if (ImGui::SliderFloat("SPEED", &speed, .0f, 200, "%.4f MHz", ImGuiSliderFlags_Logarithmic));
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("NORMAL")) speed = 1.023f;
|
||||
ImGui::Checkbox("PAUSE", &paused);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("STEP")) {
|
||||
paused = true;
|
||||
cpu->state = step;
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("RESET"))
|
||||
cpu->RST();
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("POWER CYCLE")) {
|
||||
cpu->RST(); // do a cold reset
|
||||
mmu->init();
|
||||
mmu->ram[0x3F4] = 0; // unset the Power-UP byte
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
static std::string floppy1="", floppy2="";
|
||||
|
||||
fileDialog1.Display();
|
||||
if (fileDialog1.HasSelected()) {
|
||||
floppy1 = fileDialog1.GetSelected().filename().string();
|
||||
disk->load((char*)fileDialog1.GetSelected().string().c_str(), 0);
|
||||
fileDialog1.ClearSelected();
|
||||
}
|
||||
|
||||
fileDialog2.Display();
|
||||
if (fileDialog2.HasSelected()) {
|
||||
floppy2 = fileDialog2.GetSelected().filename().string();
|
||||
disk->load((char*)fileDialog2.GetSelected().string().c_str(), 1);
|
||||
fileDialog2.ClearSelected();
|
||||
}
|
||||
|
||||
if (show_disks_window) {
|
||||
ImGui::Begin("DISK ][", &show_disks_window);
|
||||
|
||||
if (ImGui::Button("LOAD FLOPPY #1")) {
|
||||
fileDialog1.SetTitle("Insert floppy into drive #1");
|
||||
fileDialog1.SetTypeFilters({ ".nib", ".dsk", ".woz" });
|
||||
fileDialog1.Open();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("SAVE FLOPPY #1")) {
|
||||
disk->save(0);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("EJECT FLOPPY #1")) {
|
||||
disk->eject(0);
|
||||
}
|
||||
ImGui::Text("Floppy #1 : %s", disk->unit[0].fileName);
|
||||
ImGui::Text("Read %s", disk->unit[0].readOnly ? "Only" : "and Write");
|
||||
ImGui::Text("Status : ");
|
||||
ImGui::SameLine();
|
||||
if (disk->unit[0].motorOn) {
|
||||
if (disk->unit[0].writeMode)
|
||||
ImGui::Text("writting");
|
||||
else
|
||||
ImGui::Text("reading");
|
||||
}
|
||||
else
|
||||
ImGui::Text("idle");
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Button("LOAD FLOPPY #2")) {
|
||||
fileDialog2.SetTitle("Insert floppy into drive #2");
|
||||
fileDialog2.SetTypeFilters({ ".nib", ".dsk", ".woz" });
|
||||
fileDialog2.Open();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("SAVE FLOPPY #2")) {
|
||||
disk->save(1);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("EJECT FLOPPY #2")) {
|
||||
disk->eject(1);
|
||||
}
|
||||
ImGui::Text("Floppy #2 : %s", disk->unit[1].fileName);
|
||||
ImGui::Text("Read %s", disk->unit[1].readOnly ? "Only" : "and Write");
|
||||
ImGui::Text("Status : ");
|
||||
ImGui::SameLine();
|
||||
if (disk->unit[1].motorOn) {
|
||||
if (disk->unit[1].writeMode)
|
||||
ImGui::Text("writting");
|
||||
else
|
||||
ImGui::Text("reading");
|
||||
}
|
||||
else
|
||||
ImGui::Text("idle");
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (show_info_window) {
|
||||
ImGui::Begin("INFO", &show_info_window);
|
||||
// Display FPS
|
||||
ImGui::Text("%.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
|
||||
ImGui::Separator();
|
||||
ImGui::Text("KEY : %02X", mmu->KBD);
|
||||
ImGui::Separator();
|
||||
ImGui::Checkbox("TEXT", &mmu->TEXT);
|
||||
ImGui::Checkbox("MIXED", &mmu->MIXED);
|
||||
ImGui::Checkbox("PAGE2", &mmu->PAGE2);
|
||||
ImGui::Checkbox("HIRES", &mmu->HIRES);
|
||||
ImGui::Checkbox("DHIRES", &mmu->DHIRES);
|
||||
ImGui::Checkbox("COL80", &mmu->COL80);
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Language Card readable : %s", mmu->LCRD ? "True" : "False");
|
||||
ImGui::Text("Language Card writable : %s", mmu->LCWR ? "True" : "False");
|
||||
ImGui::Text("Language Card bank 2 : %s", mmu->LCBK2 ? "Enabled" : "Disabled");
|
||||
ImGui::Text("Language Card prewrite : %s", mmu->LCWFF ? "On" : "Off");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("RAMRD : %s", mmu->RAMRD ? "On" : "Off");
|
||||
ImGui::Text("RAMWRT : %s", mmu->RAMWRT ? "On" : "Off");
|
||||
ImGui::Text("ALTZP : %s", mmu->ALTZP ? "On" : "Off");
|
||||
ImGui::Text("STORE80 : %s", mmu->STORE80 ? "On" : "Off");
|
||||
ImGui::Text("ALTCHARSET : %s", mmu->ALTCHARSET ? "On" : "Off");
|
||||
ImGui::Text("INTCXROM : %s", mmu->INTCXROM ? "On" : "Off");
|
||||
ImGui::Text("SLOTC3ROM : %s", mmu->SLOTC3ROM ? "On" : "Off");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("VERTBLANK : %s", mmu->VERTBLANK ? "On" : "Off");
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (show_cpu_window) {
|
||||
char buffer[1000];
|
||||
cpu->getRegs(buffer);
|
||||
ImGui::Begin("REGISTERS", &show_cpu_window);
|
||||
ImGui::Text("%s", buffer);
|
||||
// ImGui::Text("TICK : %lld", ticks);
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
static char buffer[2000];
|
||||
static char addressString[5];
|
||||
static unsigned long int address = 0xFF00;
|
||||
static bool followPC = true;
|
||||
|
||||
if (show_code_window) {
|
||||
ImGui::Begin("CODE", &show_code_window);
|
||||
if( ImGui::InputTextWithHint("", "address in hex", addressString, IM_ARRAYSIZE(addressString))) {
|
||||
address = strtoul(addressString, NULL, 16);
|
||||
cpu->getCode(address, buffer, 2000, 20);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("PC", &followPC);
|
||||
if (followPC)
|
||||
cpu->getCode(cpu->getPC(), buffer, 2000, 20);
|
||||
ImGui::Text("%s", buffer);
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (show_editor_window) {
|
||||
ImGui::Begin("EDITOR", &show_editor_window, ImGuiWindowFlags_HorizontalScrollbar);
|
||||
ImGui::SetWindowSize(ImVec2(100, 200), ImGuiCond_FirstUseEver);
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("NEW & RUN SELECTION")) {
|
||||
if (editor.HasSelection()) {
|
||||
std::string program = "NEW\n";
|
||||
program += editor.GetSelectedText();
|
||||
program += "\nRUN\n";
|
||||
int c = 0;
|
||||
while (program[c]) { // all chars until ascii NUL
|
||||
mmu->KBD = program[c++] | 0x80; // set bit7
|
||||
if (mmu->KBD == 0x8A) mmu->KBD = 0x8D; // translate Line Feed to Carriage Ret
|
||||
cpu->exec(500000); // give cpu (and applesoft) some cycles to process each char
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("PASTE SELECTION")) {
|
||||
if (editor.HasSelection()) {
|
||||
std::string program = "\n";
|
||||
program += editor.GetSelectedText();
|
||||
program += "\n";
|
||||
int c = 0;
|
||||
while (program[c]) { // all chars until ascii NUL
|
||||
mmu->KBD = program[c++] | 0x80; // set bit7
|
||||
if (mmu->KBD == 0x8A) mmu->KBD = 0x8D; // translate Line Feed to Carriage Ret
|
||||
cpu->exec(500000); // give cpu (and applesoft) some cycles to process each char
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("SEND RESET"))
|
||||
cpu->RST();
|
||||
ImGui::Separator();
|
||||
|
||||
auto cpos = editor.GetCursorPosition();
|
||||
ImGui::Text("%4d/%-4d %4d lines | %s | %s | %s | %s",
|
||||
cpos.mLine + 1,
|
||||
cpos.mColumn + 1,
|
||||
editor.GetTotalLines(),
|
||||
editor.IsOverwrite() ? "Ovr" : "Ins",
|
||||
editor.CanUndo() ? "*" : " ",
|
||||
editor.GetLanguageDefinition().mName.c_str(),
|
||||
fileToEdit.c_str());
|
||||
|
||||
ImGui::Separator();
|
||||
editor.Render("TextEditor");
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (show_crt_window) {
|
||||
ImGui::Begin("CRT", &show_crt_window);
|
||||
// Adjust the window to the image
|
||||
auto image_size = ImVec2(280 * screenScale, 192 * screenScale);
|
||||
ImGui::SetWindowSize(ImVec2(280 * screenScale + 16, 192 * screenScale + 50));
|
||||
// Draw image
|
||||
ImGui::Image((void*)((intptr_t)screenTexture), image_size, ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(0,0,0,0));
|
||||
// get inputs
|
||||
CRT_is_focused = ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows);
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int Gui::render() {
|
||||
// crt
|
||||
if (video->gfxmode == 80) {
|
||||
glBindTexture(GL_TEXTURE_2D, screenTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 560, 384, 0, GL_RGBA, GL_UNSIGNED_BYTE, video->screenPixels80);
|
||||
}
|
||||
else {
|
||||
glBindTexture(GL_TEXTURE_2D, screenTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 280, 192, 0, GL_RGBA, GL_UNSIGNED_BYTE, video->screenPixels);
|
||||
}
|
||||
// RAM heatmap
|
||||
glBindTexture(GL_TEXTURE_2D, ramHeatmapTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, video->ramHeatmap);
|
||||
|
||||
// AUX heatmap
|
||||
glBindTexture(GL_TEXTURE_2D, auxHeatmapTexture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, video->auxHeatmap);
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
|
||||
glViewport(0, 0, (int)ImGui::GetIO().DisplaySize.x, (int)ImGui::GetIO().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_ImplOpenGL2_RenderDrawData(ImGui::GetDrawData());
|
||||
SDL_GL_SwapWindow(wdo);
|
||||
|
||||
if (++frameNumber >= 60) frameNumber = 0; // reset to zero every second
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
Gui::~Gui() {
|
||||
ImGui_ImplOpenGL2_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
SDL_GL_DeleteContext(gl_context);
|
||||
SDL_DestroyWindow(wdo);
|
||||
SDL_AudioQuit();
|
||||
SDL_Quit();
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* reinette, a french Apple II emulator, using SDL2
|
||||
* and powered by puce65c02 - a WDS 65c02 cpu emulator by the same author
|
||||
* Last modified 1st of July 2021
|
||||
* Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _GUI_H
|
||||
#define _GUI_H
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl.h"
|
||||
#include "imgui_impl_opengl2.h"
|
||||
#include "imfilebrowser.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
#include "TextEditor.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL_opengl.h>
|
||||
#include <fstream>
|
||||
|
||||
class Gui {
|
||||
public:
|
||||
int fps;
|
||||
unsigned int frameNumber;
|
||||
|
||||
private:
|
||||
bool show_about_window;
|
||||
bool show_help_window;
|
||||
bool CRT_is_focused;
|
||||
bool show_crt_window;
|
||||
bool show_control_window;
|
||||
bool show_disks_window;
|
||||
bool show_info_window;
|
||||
bool show_cpu_window;
|
||||
bool show_code_window;
|
||||
bool show_pageZero_window;
|
||||
bool show_stack_window;
|
||||
bool show_ram_window;
|
||||
bool show_aux_window;
|
||||
bool show_rom_window;
|
||||
bool show_editor_window;
|
||||
bool show_ramHeatmap_window;
|
||||
bool show_auxHeatmap_window;
|
||||
|
||||
ImVec4 clear_color;
|
||||
uint32_t screenTexture;
|
||||
uint32_t ramHeatmapTexture;
|
||||
uint32_t auxHeatmapTexture;
|
||||
|
||||
SDL_Window* wdo;
|
||||
float screenScale;
|
||||
SDL_GLContext gl_context;
|
||||
|
||||
TextEditor editor;
|
||||
std::string fileToEdit;
|
||||
ImGui::FileBrowser fileDialog1;
|
||||
ImGui::FileBrowser fileDialog2;
|
||||
|
||||
public:
|
||||
Gui();
|
||||
~Gui();
|
||||
void getInputs();
|
||||
int update();
|
||||
int newFrame();
|
||||
int render();
|
||||
int release();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,95 @@
|
|||
[Window][Debug##Default]
|
||||
Pos=579,238
|
||||
Size=476,315
|
||||
Collapsed=0
|
||||
|
||||
[Window][RAM]
|
||||
Pos=8,424
|
||||
Size=546,155
|
||||
Collapsed=0
|
||||
|
||||
[Window][ROM]
|
||||
Pos=8,741
|
||||
Size=546,150
|
||||
Collapsed=0
|
||||
|
||||
[Window][AUX]
|
||||
Pos=8,583
|
||||
Size=546,153
|
||||
Collapsed=0
|
||||
|
||||
[Window][STACK]
|
||||
Pos=8,140
|
||||
Size=512,278
|
||||
Collapsed=0
|
||||
|
||||
[Window][PAGE ZERO]
|
||||
Pos=39,24
|
||||
Size=513,278
|
||||
Collapsed=0
|
||||
|
||||
[Window][INFO]
|
||||
Pos=859,464
|
||||
Size=247,427
|
||||
Collapsed=0
|
||||
|
||||
[Window][REGISTERS]
|
||||
Pos=1111,492
|
||||
Size=216,71
|
||||
Collapsed=0
|
||||
|
||||
[Window][CODE]
|
||||
Pos=1112,571
|
||||
Size=218,320
|
||||
Collapsed=0
|
||||
|
||||
[Window][HELP]
|
||||
Pos=319,106
|
||||
Size=444,407
|
||||
Collapsed=0
|
||||
|
||||
[Window][ABOUT]
|
||||
Pos=234,380
|
||||
Size=311,129
|
||||
Collapsed=0
|
||||
|
||||
[Window][CRT]
|
||||
Pos=561,23
|
||||
Size=576,434
|
||||
Collapsed=0
|
||||
|
||||
[Window][RAM HEATMAP]
|
||||
Pos=561,466
|
||||
Size=289,203
|
||||
Collapsed=0
|
||||
|
||||
[Window][AUX HEATMAP]
|
||||
Pos=559,681
|
||||
Size=295,210
|
||||
Collapsed=0
|
||||
|
||||
[Window][EDITOR]
|
||||
Pos=1142,23
|
||||
Size=550,461
|
||||
Collapsed=0
|
||||
|
||||
[Window][CONTROLS]
|
||||
Pos=1334,492
|
||||
Size=359,206
|
||||
Collapsed=0
|
||||
|
||||
[Window][DISK ][]
|
||||
Pos=1334,708
|
||||
Size=357,183
|
||||
Collapsed=0
|
||||
|
||||
[Window][Insert floppy into drive #2##filebrowser_2012684434080]
|
||||
Pos=500,225
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
||||
[Window][Insert floppy into drive #1##filebrowser_2012684433760]
|
||||
Pos=501,225
|
||||
Size=700,450
|
||||
Collapsed=0
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// Setup Dear ImGui style
|
||||
// ImGui::StyleColorsDark();
|
||||
// or create your own :
|
||||
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
style.Colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||||
style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
|
||||
style.Colors[ImGuiCol_WindowBg] = ImVec4(0.13f, 0.14f, 0.15f, 1.00f);
|
||||
style.Colors[ImGuiCol_ChildBg] = ImVec4(0.13f, 0.14f, 0.15f, 1.00f);
|
||||
style.Colors[ImGuiCol_PopupBg] = ImVec4(0.13f, 0.14f, 0.15f, 1.00f);
|
||||
style.Colors[ImGuiCol_Border] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
|
||||
style.Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
style.Colors[ImGuiCol_FrameBg] = ImVec4(0.25f, 0.25f, 0.25f, 1.00f);
|
||||
style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.38f, 0.38f, 0.38f, 1.00f);
|
||||
style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.67f, 0.67f, 0.67f, 0.39f);
|
||||
style.Colors[ImGuiCol_TitleBg] = ImVec4(0.08f, 0.08f, 0.09f, 1.00f);
|
||||
style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.28f, 0.28f, 0.29f, 1.00f);
|
||||
style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
|
||||
style.Colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);
|
||||
style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);
|
||||
style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);
|
||||
style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);
|
||||
style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);
|
||||
style.Colors[ImGuiCol_CheckMark] = ImVec4(0.11f, 0.64f, 0.92f, 1.00f);
|
||||
style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.11f, 0.64f, 0.92f, 1.00f);
|
||||
style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.08f, 0.50f, 0.72f, 1.00f);
|
||||
style.Colors[ImGuiCol_Button] = ImVec4(0.25f, 0.25f, 0.25f, 1.00f);
|
||||
style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.38f, 0.38f, 0.38f, 1.00f);
|
||||
style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.67f, 0.67f, 0.67f, 0.39f);
|
||||
style.Colors[ImGuiCol_Header] = ImVec4(0.22f, 0.22f, 0.22f, 1.00f);
|
||||
style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.25f, 0.25f, 0.25f, 1.00f);
|
||||
style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.67f, 0.67f, 0.67f, 0.39f);
|
||||
style.Colors[ImGuiCol_Separator] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
|
||||
style.Colors[ImGuiCol_SeparatorHovered] = ImVec4(0.41f, 0.42f, 0.44f, 1.00f);
|
||||
style.Colors[ImGuiCol_SeparatorActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
|
||||
style.Colors[ImGuiCol_ResizeGrip] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.29f, 0.30f, 0.31f, 0.67f);
|
||||
style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
|
||||
style.Colors[ImGuiCol_Tab] = ImVec4(0.08f, 0.08f, 0.09f, 0.83f);
|
||||
style.Colors[ImGuiCol_TabHovered] = ImVec4(0.33f, 0.34f, 0.36f, 0.83f);
|
||||
style.Colors[ImGuiCol_TabActive] = ImVec4(0.23f, 0.23f, 0.24f, 1.00f);
|
||||
style.Colors[ImGuiCol_TabUnfocused] = ImVec4(0.08f, 0.08f, 0.09f, 1.00f);
|
||||
style.Colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.13f, 0.14f, 0.15f, 1.00f);
|
||||
style.Colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
|
||||
style.Colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||||
style.Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||
style.Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
|
||||
style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
|
||||
style.Colors[ImGuiCol_DragDropTarget] = ImVec4(0.11f, 0.64f, 0.92f, 1.00f);
|
||||
style.Colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||||
style.Colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);
|
||||
style.Colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);
|
||||
style.Colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);
|
||||
style.GrabRounding = style.FrameRounding = 2.0f;
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* reinette, a french Apple II emulator, using SDL2
|
||||
* and powered by puce65c02 - a WDS 65c02 cpu emulator by the same author
|
||||
* Last modified 1st of July 2021
|
||||
* Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "reinette.h"
|
||||
#include <iostream>
|
||||
|
||||
// global variables - TODO create a config file and make changes persistant
|
||||
bool muted = false;
|
||||
int volume = 10;
|
||||
bool running = true;
|
||||
bool paused = false;
|
||||
float speed = 1.023f;
|
||||
|
||||
// instanciate all objects, calling their respective constructors
|
||||
Mmu* mmu = new Mmu();
|
||||
puce65c02* cpu = new puce65c02();
|
||||
Video* video = new Video();
|
||||
Disk* disk = new Disk();
|
||||
Speaker* speaker = new Speaker();
|
||||
Paddles* paddles = new Paddles();
|
||||
Gui* gui = new Gui();
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
cpu->RST();
|
||||
if (argc > 1) disk->load(argv[1], 0); // load .nib in parameter into drive 0
|
||||
uint8_t tries = 0; // for disk ][ speed-up
|
||||
|
||||
// main loop
|
||||
while (running) {
|
||||
|
||||
gui->getInputs();
|
||||
paddles->update();
|
||||
gui->newFrame();
|
||||
|
||||
if (!paused) {
|
||||
// dirty hack - fix soon TODO - a better VERTBLANK ... - won't work when stepping through instructions
|
||||
mmu->VERTBLANK = true;
|
||||
cpu->exec((unsigned long long int)(1000000.0 * speed / gui->fps*0.1f)); // the apple II is clocked at 1023000.0 Hhz
|
||||
mmu->VERTBLANK = false;
|
||||
cpu->exec((unsigned long long int)(1000000.0 * speed / gui->fps*0.9f)); // the apple II is clocked at 1023000.0 Hhz
|
||||
|
||||
while (disk->unit[disk->curDrv].motorOn && ++tries) // until motor is off or i reaches 255+1=0
|
||||
cpu->exec(5000); // speed up drive access artificially
|
||||
video->update(); // won't update the video if paused
|
||||
}
|
||||
else if (cpu->state == step) { // paused and user pressed debugNumber
|
||||
cpu->exec(1);
|
||||
cpu->state = run; // still paused
|
||||
video->update(); // update the video after each instruction ...
|
||||
}
|
||||
|
||||
gui->update();
|
||||
gui->render();
|
||||
|
||||
} // while (running)
|
||||
|
||||
return 0;
|
||||
// at this point all destructors were called, properly closing open files and releasing other ressources
|
||||
}
|
|
@ -0,0 +1,620 @@
|
|||
/*
|
||||
* reinette, a french Apple II emulator, using SDL2
|
||||
* and powered by puce65c02 - a WDS 65c02 cpu emulator by the same author
|
||||
* Last modified 1st of July 2021
|
||||
* Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// code is not iso compliant : abuse of range expressions in switch statements
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "reinette.h"
|
||||
|
||||
//========================================== MEMORY MAPPED SOFT SWITCHES HANDLER
|
||||
|
||||
uint8_t Mmu::softSwitches(uint16_t address, uint8_t value, bool WRT) {
|
||||
static uint8_t dLatch = 0; // disk ][ I/O register
|
||||
|
||||
switch (address) {
|
||||
|
||||
// MEMORY MANAGEMENT (and KEYBOARD)
|
||||
case 0xC000: if (WRT) STORE80 = false; else return KBD; break; // cause PAGE2 on to select AUX -- KEYBOARD return key code - if hi-bit is set the key code (7 lo-bits) is valid
|
||||
case 0xC001: if (WRT) STORE80 = true; break; // allow PAGE2 to switch MAIN / AUX
|
||||
case 0xC002: if (WRT) RAMRD = false; break; // read from MAIN
|
||||
case 0xC003: if (WRT) RAMRD = true; break; // read from AUX
|
||||
case 0xC004: if (WRT) RAMWRT = false; break; // write to MAIN
|
||||
case 0xC005: if (WRT) RAMWRT = true; break; // write to AUX
|
||||
case 0xC006: if (WRT) INTCXROM = false; break; // set peripheral roms for peripherals ($C100-$CFFF)
|
||||
case 0xC007: if (WRT) INTCXROM = true; break; // set internal rom for peripherals ($C100-$CFFF)
|
||||
case 0xC008: if (WRT) ALTZP = false; break; // MAIN stack & rero page
|
||||
case 0xC009: if (WRT) ALTZP = true; break; // AUX stack & rero page
|
||||
case 0xC00A: if (WRT) SLOTC3ROM = false; break; // ROM in Slot 3
|
||||
case 0xC00B: if (WRT) SLOTC3ROM = true; break; // ROM in AUX Slot
|
||||
case 0xC00C: if (WRT) COL80 = false; break; // 80 COL OFF -> 40 COL
|
||||
case 0xC00D: if (WRT) COL80 = true; break; // 80 COL ON
|
||||
case 0xC00E: if (WRT) ALTCHARSET = false; break; // primary character set
|
||||
case 0xC00F: if (WRT) ALTCHARSET = true; break; // alternate character set
|
||||
case 0xC010: KBD &= 0x7F; return KBD; // KBDSTROBE, clear hi-bit and return key code
|
||||
|
||||
// SOFT SWITCH STATUS FLAGS
|
||||
case 0xC011: return (0x80 * LCBK2);
|
||||
case 0xC012: return (0x80 * LCRD);
|
||||
case 0xC013: return (0x80 * RAMRD); // 0x80 if reads from AUX
|
||||
case 0xC014: return (0x80 * RAMWRT); // 0x80 if writes from AUX
|
||||
case 0xC015: return (0x80 * INTCXROM);
|
||||
case 0xC016: return (0x80 * ALTZP); // 0x80 if using stack and zero page from AUX
|
||||
case 0xC017: return (0x80 * SLOTC3ROM);
|
||||
case 0xC018: return (0x80 * STORE80); // do we store 80 col page 2 on MAIN or AUX
|
||||
case 0xC019: return (0x80 * VERTBLANK);
|
||||
|
||||
case 0xC01A: return (0x80 * TEXT); // read text switch
|
||||
case 0xC01B: return (0x80 * MIXED); // read mixed switch
|
||||
case 0xC01C: return (0x80 * PAGE2); // read page 2 switch
|
||||
case 0xC01D: return (0x80 * HIRES); // read HiRes switch
|
||||
case 0xC01E: return (0x80 * ALTCHARSET); // alternate character set ?
|
||||
case 0xC01F: return (0x80 * COL80); // 80 columns on ?
|
||||
|
||||
// SOUND
|
||||
case 0xC020: // TAPEOUT (shall we listen it ? - try SAVE from applesoft)
|
||||
case 0xC030: // SPEAKER
|
||||
case 0xC033: speaker->play(); break; // apple invader uses $C033 to output sound !
|
||||
|
||||
// GAME I/O STROBE OUT
|
||||
case 0xC040: break;
|
||||
|
||||
// VIDEO MODES
|
||||
case 0xC050: TEXT = false; video->clearCache(); break; // set Graphics
|
||||
case 0xC051: TEXT = true; video->clearCache(); break; // set Text
|
||||
case 0xC052: MIXED = false; video->clearCache(); break; // set Mixed to off
|
||||
case 0xC053: MIXED = true; video->clearCache(); break; // set Mixed to on
|
||||
case 0xC054: PAGE2 = false; video->clearCache(); break; // select page 1
|
||||
case 0xC055: PAGE2 = true; video->clearCache(); break; // select page 2
|
||||
case 0xC056: HIRES = false; video->clearCache(); break; // set HiRes to off
|
||||
case 0xC057: HIRES = true; video->clearCache(); break; // set HiRes to on
|
||||
|
||||
// ANNUNCIATORS
|
||||
case 0xC058: if (!IOUDIS) AN0 = false; break; // If IOUDIS off: Annunciator 0 Off
|
||||
case 0xC059: if (!IOUDIS) AN0 = true; break; // If IOUDIS off: Annunciator 0 On
|
||||
case 0xC05A: if (!IOUDIS) AN1 = false; break; // If IOUDIS off: Annunciator 1 Off
|
||||
case 0xC05B: if (!IOUDIS) AN1 = true; break; // If IOUDIS off: Annunciator 1 On
|
||||
case 0xC05C: if (!IOUDIS) AN2 = false; break; // If IOUDIS off: Annunciator 2 Off
|
||||
case 0xC05D: if (!IOUDIS) AN2 = true; break; // If IOUDIS off: Annunciator 2 On
|
||||
case 0xC05E: if (!IOUDIS) AN3 = false; DHIRES = true; break; // If IOUDIS off: Annunciator 3 Off
|
||||
case 0xC05F: if (!IOUDIS) AN3 = true; DHIRES = false; break; // If IOUDIS off: Annunciator 2 On
|
||||
|
||||
// TAPE
|
||||
case 0xC060: break; // TAPE IN
|
||||
|
||||
// PADDLES
|
||||
case 0xC061: return paddles->PB0; // Push Button 0 / Open Apple
|
||||
case 0xC062: return paddles->PB1; // Push Button 1 / Solid Apple
|
||||
case 0xC063: return paddles->PB2; // Push Button 2 / Shift
|
||||
case 0xC064: return paddles->read(0); // Paddle 0
|
||||
case 0xC065: return paddles->read(1); // Paddle 1
|
||||
case 0xC066: return paddles->read(2); // Paddle 2
|
||||
case 0xC067: return paddles->read(3); // Paddle 3
|
||||
case 0xC070: paddles->reset(); break; // paddles timer reset
|
||||
|
||||
// IOUDIS
|
||||
case 0xC07E: if (WRT) IOUDIS = false; else return (0x80 * IOUDIS); break;
|
||||
case 0xC07F: if (WRT) IOUDIS = true; else return (0x80 * DHIRES); break;
|
||||
|
||||
// LANGUAGE CARD (used with MAIN and AUX)
|
||||
case 0xC080:
|
||||
case 0xC084: LCBK2 = 1; LCRD = 1; LCWR = 0; LCWFF = 0; break; // LC2RD
|
||||
case 0xC081:
|
||||
case 0xC085: LCBK2 = 1; LCRD = 0; LCWR |= LCWFF; LCWFF = !WRT; break; // LC2WR
|
||||
case 0xC082:
|
||||
case 0xC086: LCBK2 = 1; LCRD = 0; LCWR = 0; LCWFF = 0; break; // ROMONLY2
|
||||
case 0xC083:
|
||||
case 0xC087: LCBK2 = 1; LCRD = 1; LCWR |= LCWFF; LCWFF = !WRT; break; // LC2RW
|
||||
case 0xC088:
|
||||
case 0xC08C: LCBK2 = 0; LCRD = 1; LCWR = 0; LCWFF = 0; break; // LC1RD
|
||||
case 0xC089:
|
||||
case 0xC08D: LCBK2 = 0; LCRD = 0; LCWR |= LCWFF; LCWFF = !WRT; break; // LC1WR
|
||||
case 0xC08A:
|
||||
case 0xC08E: LCBK2 = 0; LCRD = 0; LCWR = 0; LCWFF = 0; break; // ROMONLY1
|
||||
case 0xC08B:
|
||||
case 0xC08F: LCBK2 = 0; LCRD = 1; LCWR |= LCWFF; LCWFF = !WRT; break; // LC1RW
|
||||
|
||||
// SLOT 1
|
||||
case 0xC090 ... 0xC09F: break;
|
||||
|
||||
// SLOT 2
|
||||
case 0xC0A0 ... 0xC0AF: break;
|
||||
|
||||
// SLOT 3
|
||||
case 0xC0B0 ... 0xC0BF: break;
|
||||
|
||||
// SLOT 4
|
||||
case 0xC0C0 ... 0xC0CF: break;
|
||||
|
||||
// SLOT 5
|
||||
case 0xC0D0 ... 0xC0DF: break;
|
||||
|
||||
// SLOT 6 DISK ][
|
||||
case 0xC0E0 ... 0xC0E7: disk->stepMotor(address); break; // MOVE DRIVE HEAD
|
||||
case 0xC0E8: disk->unit[disk->curDrv].motorOn = false; break; // MOTOROFF
|
||||
case 0xC0E9: disk->unit[disk->curDrv].motorOn = true; break; // MOTORON
|
||||
case 0xC0EA: disk->setDrv(0); break; // DRIVE0EN
|
||||
case 0xC0EB: disk->setDrv(1); break; // DRIVE1EN
|
||||
case 0xC0EC: // Shift Data Latch
|
||||
if (disk->unit[disk->curDrv].writeMode) // writting
|
||||
disk->unit[disk->curDrv].data[disk->unit[disk->curDrv].track * 0x1A00 + disk->unit[disk->curDrv].nibble] = dLatch; // good luck gcc
|
||||
else // reading
|
||||
dLatch = disk->unit[disk->curDrv].data[disk->unit[disk->curDrv].track * 0x1A00 + disk->unit[disk->curDrv].nibble]; // easy peasy
|
||||
disk->unit[disk->curDrv].nibble = (disk->unit[disk->curDrv].nibble + 1) % 0x1A00; // turn floppy of 1 nibble
|
||||
return dLatch;
|
||||
case 0xC0ED: dLatch = value; break; // Load Data Latch
|
||||
case 0xC0EE: // latch for READ
|
||||
disk->unit[disk->curDrv].writeMode = false;
|
||||
return disk->unit[disk->curDrv].readOnly ? 0x80 : 0; // check protection
|
||||
case 0xC0EF: disk->unit[disk->curDrv].writeMode = true; break; // latch for WRITE
|
||||
|
||||
// SLOT 7
|
||||
case 0xC0F0 ... 0xC0FF: break;
|
||||
|
||||
}
|
||||
return cpu->ticks%256; // catch all, gives a floating value
|
||||
}
|
||||
|
||||
//================================================================== MEMORY READ
|
||||
|
||||
uint8_t Mmu::readMem(uint16_t address) {
|
||||
|
||||
switch (address) {
|
||||
case 0x0000 ... 0x01FF: // STACK and Zero Page in AUX or MAIN
|
||||
if (ALTZP) {
|
||||
video->auxHeatmap[address] |= 0xFF00FF00;
|
||||
return aux[address];
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return ram[address];
|
||||
break;
|
||||
|
||||
case 0x0200 ... 0x03FF: // MAIN or AUX
|
||||
if (RAMRD) {
|
||||
video->auxHeatmap[address] |= 0xFF00FF00;
|
||||
return aux[address];
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return ram[address];
|
||||
break;
|
||||
|
||||
case 0x0400 ... 0x07FF: // TEXT PAGE 1 in AUX or MAIN
|
||||
if (STORE80) {
|
||||
if (PAGE2) {
|
||||
video->auxHeatmap[address] |= 0xFF00FF00;
|
||||
return aux[address];
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return ram[address];
|
||||
}
|
||||
if (RAMRD) {
|
||||
video->auxHeatmap[address] |= 0xFF00FF00;
|
||||
return aux[address];
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return ram[address];
|
||||
break;
|
||||
|
||||
case 0x0800 ... 0x0BFF: // TEXT PAGE 2 in AUX or MAIN
|
||||
if (RAMRD) {
|
||||
video->auxHeatmap[address] |= 0xFF00FF00;
|
||||
return aux[address];
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return ram[address];
|
||||
break;
|
||||
|
||||
case 0x0C00 ... 0x1FFF: // AUX or MAIN
|
||||
if (RAMRD) {
|
||||
video->auxHeatmap[address] |= 0xFF00FF00;
|
||||
return aux[address];
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return ram[address];
|
||||
break;
|
||||
|
||||
case 0x2000 ... 0x3FFF: // HIRES PAGE 1
|
||||
if (STORE80) {
|
||||
if (PAGE2 && HIRES) {
|
||||
video->auxHeatmap[address] |= 0xFF00FF00;
|
||||
return aux[address];
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return ram[address];
|
||||
}
|
||||
if (RAMRD) {
|
||||
video->auxHeatmap[address] |= 0xFF00FF00;
|
||||
return aux[address];
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return ram[address];
|
||||
break;
|
||||
|
||||
case 0x4000 ... 0x5FFF: // HIRES PAGE 2 in AUX or MAIN
|
||||
if (RAMRD) {
|
||||
video->auxHeatmap[address] |= 0xFF00FF00;
|
||||
return aux[address];
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return ram[address];
|
||||
break;
|
||||
|
||||
case 0x6000 ... 0xBFFF: // AUX or MAIN
|
||||
if (RAMRD) {
|
||||
video->auxHeatmap[address] |= 0xFF00FF00;
|
||||
return aux[address];
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return ram[address];
|
||||
break;
|
||||
|
||||
case 0xC000 ... 0xC0FF: // SOFT SWITCHES
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return softSwitches(address, 0, false);
|
||||
break;
|
||||
|
||||
case 0xC100 ... 0xC1FF: // SLOT 1 ROM or ROM
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
if (INTCXROM) {
|
||||
return rom[address - ROMSTART];
|
||||
}
|
||||
return sl1[address - SL1START];
|
||||
break;
|
||||
|
||||
case 0xC200 ... 0xC2FF: // SLOT 2 ROM or ROM
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
if (INTCXROM) {
|
||||
return rom[address - ROMSTART];
|
||||
}
|
||||
return sl2[address - SL2START];
|
||||
break;
|
||||
|
||||
case 0xC300 ... 0xC3FF: // SLOT 3 ROM or ROM : video
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
if (INTCXROM || !SLOTC3ROM) {
|
||||
return rom[address - ROMSTART];
|
||||
}
|
||||
return sl3[address - SL3START];
|
||||
break;
|
||||
|
||||
case 0xC400 ... 0xC4FF: // SLOT 4 ROM or ROM
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
if (INTCXROM) {
|
||||
return rom[address - ROMSTART];
|
||||
}
|
||||
return sl4[address - SL4START];
|
||||
break;
|
||||
|
||||
case 0xC500 ... 0xC5FF: // SLOT 5 ROM or ROM
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
if (INTCXROM) {
|
||||
return rom[address - ROMSTART];
|
||||
}
|
||||
return sl5[address - SL5START];
|
||||
break;
|
||||
|
||||
case 0xC600 ... 0xC6FF: // SLOT 6 ROM : DISK ][ or ROM
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
if (INTCXROM) {
|
||||
return rom[address - ROMSTART];
|
||||
}
|
||||
return sl6[address - SL6START];
|
||||
break;
|
||||
|
||||
case 0xC700 ... 0xC7FF: // SLOT 7 ROM or ROM
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
if (INTCXROM) {
|
||||
return rom[address - ROMSTART];
|
||||
}
|
||||
return sl7[address - SL7START];
|
||||
break;
|
||||
|
||||
case 0xC800 ... 0xCFFE: // SHARED EXANSION SLOTS ROM AREA or ROM
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
if (INTCXROM || !SLOTC3ROM) {
|
||||
return rom[address - ROMSTART];
|
||||
}
|
||||
return slrom[(address & 0x0F00) >> 2 & 0xF][address - SLROMSTART];
|
||||
break;
|
||||
|
||||
case 0xCFFF: // turn off all slots expansion ROMs - TODO : NEEDS REWORK
|
||||
disk->unit[disk->curDrv].motorOn = false;
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case 0xD000 ... 0xDFFF: // ROM, MAIN-BK1, RAM-BK2, AUX-BK1 or AUX-BK2
|
||||
if (LCRD) {
|
||||
if (LCBK2) {
|
||||
if (ALTZP) {
|
||||
video->auxHeatmap[address] |= 0xFF00FF00;
|
||||
return auxbk2[address - BK2START]; // AUX Bank 2
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return rambk2[address - BK2START]; // MAIN Bank 2
|
||||
}
|
||||
if (ALTZP) {
|
||||
video->auxHeatmap[address] |= 0xFF00FF00;
|
||||
return auxlgc[address - LGCSTART]; // AUX Bank 1
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return ramlgc[address - LGCSTART]; // MAIN Bank 1
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return rom[address - ROMSTART]; // ROM
|
||||
break;
|
||||
|
||||
case 0xE000 ... 0xFFFF: // ROM, MAIN-BK1 or AUX-BK1
|
||||
if (LCRD) {
|
||||
if (ALTZP) {
|
||||
video->auxHeatmap[address] |= 0xFF00FF00;
|
||||
return auxlgc[address - LGCSTART]; // AUX Bank 1
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return ramlgc[address - LGCSTART]; // MAIN Bank 1
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF00FF00;
|
||||
return rom[address - ROMSTART]; // ROM
|
||||
break;
|
||||
}
|
||||
return cpu->ticks%256; // returns a floating value
|
||||
}
|
||||
|
||||
|
||||
//================================================================= MEMORY WRITE
|
||||
|
||||
void Mmu::writeMem(uint16_t address, uint8_t value) {
|
||||
|
||||
switch (address) {
|
||||
case 0x0000 ... 0x01FF: // STACK and Zero Page in AUX or MAIN
|
||||
if (ALTZP) {
|
||||
video->auxHeatmap[address] |= 0xFF0000FF;
|
||||
aux[address] = value;
|
||||
return;
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
ram[address] = value;
|
||||
return;
|
||||
break;
|
||||
|
||||
case 0x0200 ... 0x03FF: // MAIN or AUX
|
||||
if (RAMWRT) {
|
||||
video->auxHeatmap[address] |= 0xFF0000FF;
|
||||
aux[address] = value;
|
||||
return;
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
ram[address] = value;
|
||||
return;
|
||||
break;
|
||||
|
||||
case 0x0400 ... 0x07FF: // TEXT PAGE 1 in AUX or MAIN
|
||||
if (STORE80) {
|
||||
if (PAGE2) {
|
||||
video->auxHeatmap[address] |= 0xFF0000FF;
|
||||
aux[address] = value;
|
||||
return;
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
ram[address] = value;
|
||||
return;
|
||||
}
|
||||
if (RAMWRT) {
|
||||
video->auxHeatmap[address] |= 0xFF0000FF;
|
||||
aux[address] = value;
|
||||
return;
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
ram[address] = value;
|
||||
return;
|
||||
break;
|
||||
|
||||
case 0x0800 ... 0x0BFF: // TEXT PAGE 2 in AUX or MAIN
|
||||
if (RAMWRT) {
|
||||
video->auxHeatmap[address] |= 0xFF0000FF;
|
||||
aux[address] = value;
|
||||
return;
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
ram[address] = value;
|
||||
return;
|
||||
break;
|
||||
|
||||
case 0x0C00 ... 0x1FFF: // MAIN or AUX
|
||||
if (RAMWRT) {
|
||||
video->auxHeatmap[address] |= 0xFF0000FF;
|
||||
aux[address] = value;
|
||||
return;
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
ram[address] = value;
|
||||
return;
|
||||
break;
|
||||
|
||||
case 0x2000 ... 0x3FFF: // HIRES PAGE 1
|
||||
if (STORE80) {
|
||||
if (PAGE2 && HIRES) {
|
||||
video->auxHeatmap[address] |= 0xFF0000FF;
|
||||
aux[address] = value;
|
||||
return;
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
ram[address] = value;
|
||||
return;
|
||||
}
|
||||
if (RAMWRT) {
|
||||
video->auxHeatmap[address] |= 0xFF0000FF;
|
||||
aux[address] = value;
|
||||
return;
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
ram[address] = value;
|
||||
return;
|
||||
break;
|
||||
|
||||
case 0x4000 ... 0x5FFF: // HIRES PAGE 2 in AUX or MAIN
|
||||
if (RAMWRT) {
|
||||
video->auxHeatmap[address] |= 0xFF0000FF;
|
||||
aux[address] = value;
|
||||
return;
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
ram[address] = value;
|
||||
return;
|
||||
break;
|
||||
|
||||
case 0x6000 ... 0xBFFF: // AUX or MAIN
|
||||
if (RAMWRT) {
|
||||
video->auxHeatmap[address] |= 0xFF0000FF;
|
||||
aux[address] = value;
|
||||
return;
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
ram[address] = value;
|
||||
return;
|
||||
break;
|
||||
|
||||
case 0xC000 ... 0xC0FF: // softSwitches
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
softSwitches(address, 0, true);
|
||||
return;
|
||||
break;
|
||||
|
||||
case 0xC100 ... 0xCFFE: // readonly area
|
||||
return;
|
||||
break;
|
||||
|
||||
case 0xCFFF: // turn off all slots expansion ROMs - NEEDS REWORK soft switch ?
|
||||
disk->unit[disk->curDrv].motorOn = false;
|
||||
return;
|
||||
break;
|
||||
|
||||
case 0xD000 ... 0xDFFF: // ROM, MAIN-BK1, RAM-BK2, AUX-BK1 or AUX-BK2
|
||||
if (LCWR) {
|
||||
if (LCBK2) {
|
||||
if (ALTZP) {
|
||||
video->auxHeatmap[address] |= 0xFF0000FF;
|
||||
auxbk2[address - BK2START] = value; // AUX Bank 2
|
||||
return;
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
rambk2[address - BK2START] = value; // MAIN Bank 2
|
||||
return;
|
||||
}
|
||||
if (ALTZP) {
|
||||
video->auxHeatmap[address] |= 0xFF0000FF;
|
||||
auxlgc[address - LGCSTART] = value; // AUX Bank 1
|
||||
return;
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
ramlgc[address - LGCSTART] = value; // MAIN Bank 1
|
||||
return;
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
return;
|
||||
break;
|
||||
|
||||
case 0xE000 ... 0xFFFF: // ROM, MAIN-BK1 or AUX-BK1
|
||||
if (LCWR) {
|
||||
if (ALTZP) {
|
||||
video->auxHeatmap[address] |= 0xFF0000FF;
|
||||
auxlgc[address - LGCSTART] = value; // AUX Bank 1
|
||||
return;
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
ramlgc[address - LGCSTART] = value; // MAIN Bank 1
|
||||
return;
|
||||
}
|
||||
video->ramHeatmap[address] |= 0xFF0000FF;
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Mmu::Mmu() {
|
||||
// TODO : give user the choice of the rom version
|
||||
|
||||
// pick one ROM file
|
||||
// FILE* f = fopen("rom/appleII.rom", "rb"); // load the Apple II ROM
|
||||
// FILE* f = fopen("rom/appleII+.rom", "rb"); // load the Apple II+ ROM
|
||||
// FILE* f = fopen("rom/appleIIe.rom", "rb"); // load the Apple IIe ROM
|
||||
FILE* f = fopen("rom/appleIIee.rom", "rb"); // load the Apple IIee ROM
|
||||
fread(rom, 1, ROMSIZE, f);
|
||||
fclose(f);
|
||||
|
||||
// load DISK][ PROM
|
||||
f = fopen("rom/diskII.rom", "rb"); // load the P5A disk ][ PROM
|
||||
fread(sl6, 1, 256, f);
|
||||
fclose(f);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
Mmu::~Mmu() {
|
||||
// release the memory arrays ?
|
||||
}
|
||||
|
||||
|
||||
void Mmu::init() {
|
||||
KBD = 0; // $C000, $C010 ascii value of keyboard input
|
||||
PAGE2 = false; // $C054 PAGE1 / $C055 PAGE2
|
||||
TEXT = true; // $C050 CLRTEXT / $C051 SETTEXT
|
||||
MIXED = false; // $C052 CLRMIXED / $C053 SETMIXED
|
||||
HIRES = false; // $C056 GR / $C057 HGR
|
||||
DHIRES = false; // 0xC05E / 0xC05F DOUBLE HIRES
|
||||
COL80 = false; // 80 COLUMNS
|
||||
ALTCHARSET = false;
|
||||
LCWR = true; // Language Card writable
|
||||
LCRD = false; // Language Card readable
|
||||
LCBK2 = true; // Language Card bank 2 enabled
|
||||
LCWFF = false; // Language Card pre-write flip flop
|
||||
AN0 = false;
|
||||
AN1 = false;
|
||||
AN2 = true;
|
||||
AN3 = true;
|
||||
|
||||
RAMRD = false;
|
||||
RAMWRT = false;
|
||||
ALTZP = false;
|
||||
STORE80 = false;
|
||||
|
||||
INTCXROM = false; // use slots roms
|
||||
SLOTC3ROM = false; // use AUX Slot rom
|
||||
|
||||
IOUDIS = false;
|
||||
VERTBLANK = true;
|
||||
|
||||
memset(ram, 0, sizeof(ram)); // 48K of MAIN in $000-$BFFF
|
||||
memset(aux, 0, sizeof(aux)); // 48K of AUX memory
|
||||
memset(ramlgc, 0, sizeof(ramlgc)); // MAIN Language Card 12K in $D000-$FFFF
|
||||
memset(rambk2, 0, sizeof(rambk2)); // MAIN bank 2 of Language Card 4K in $D000-$DFFF
|
||||
memset(auxlgc, 0, sizeof(auxlgc)); // AUX Language Card 12K in $D000-$FFFF
|
||||
memset(auxbk2, 0, sizeof(auxbk2)); // AUX bank 2 of Language Card 4K in $D000-$DFFF
|
||||
|
||||
// dirty hacks - fix when I know why
|
||||
ram[0x4D] = 0xAA; // Joust won't work if this memory location equals zero
|
||||
ram[0xD0] = 0xAA; // Planetoids won't work if this memory location equals zero
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* reinette, a french Apple II emulator, using SDL2
|
||||
* and powered by puce65c02 - a WDS 65c02 cpu emulator by the same author
|
||||
* Last modified 1st of July 2021
|
||||
* Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _MEMORY_H
|
||||
#define _MEMORY_H
|
||||
|
||||
// memory layout
|
||||
#define RAMSIZE 0xC000 // 48K
|
||||
#define AUXSIZE 0xC000 // 48K
|
||||
|
||||
// Apple II and II+
|
||||
// #define ROMSTART 0xD000
|
||||
// #define ROMSIZE 0x3000 // 12K
|
||||
|
||||
// Apple IIe and IIee
|
||||
#define ROMSTART 0xC000
|
||||
#define ROMSIZE 0x4000 // 16K
|
||||
|
||||
// language card
|
||||
#define LGCSTART 0xD000
|
||||
#define LGCSIZE 0x3000
|
||||
#define BK2START 0xD000
|
||||
#define BK2SIZE 0x1000
|
||||
|
||||
// slot 1,
|
||||
#define SL1START 0xC100
|
||||
#define SL1SIZE 0x0100
|
||||
// slot 2
|
||||
#define SL2START 0xC200
|
||||
#define SL2SIZE 0x0100
|
||||
// slot 3
|
||||
#define SL3START 0xC300
|
||||
#define SL3SIZE 0x0100
|
||||
// slot 4
|
||||
#define SL4START 0xC400
|
||||
#define SL4SIZE 0x0100
|
||||
// slot 5
|
||||
#define SL5START 0xC500
|
||||
#define SL5SIZE 0x0100
|
||||
// slot 6, diskII
|
||||
#define SL6START 0xC600
|
||||
#define SL6SIZE 0x0100
|
||||
// slot 7
|
||||
#define SL7START 0xC700
|
||||
#define SL7SIZE 0x0100
|
||||
|
||||
|
||||
|
||||
#define SLROMSTART 0xC800 // peripheral-card expansion ROMs -
|
||||
#define SLROMSIZE 0xC800
|
||||
|
||||
|
||||
class Mmu {
|
||||
public:
|
||||
uint8_t ram[RAMSIZE]; // 48K of MAIN in $000-$BFFF
|
||||
uint8_t ramlgc[LGCSIZE]; // MAIN Language Card 12K in $D000-$FFFF
|
||||
uint8_t rambk2[BK2SIZE]; // MAIN bank 2 of Language Card 4K in $D000-$DFFF
|
||||
|
||||
uint8_t aux[AUXSIZE]; // 48K of AUX memory
|
||||
uint8_t auxlgc[LGCSIZE]; // AUX Language Card 12K in $D000-$FFFF
|
||||
uint8_t auxbk2[BK2SIZE]; // AUX bank 2 of Language Card 4K in $D000-$DFFF
|
||||
|
||||
uint8_t rom[ROMSIZE]; // 16K of rom in $C000-$FFFF
|
||||
|
||||
uint8_t sl1[SL1SIZE];
|
||||
uint8_t sl2[SL2SIZE];
|
||||
uint8_t sl3[SL3SIZE];
|
||||
uint8_t sl5[SL5SIZE];
|
||||
uint8_t sl4[SL4SIZE];
|
||||
uint8_t sl6[SL6SIZE]; // P5A disk ][ PROM in slot 6
|
||||
uint8_t sl7[SL7SIZE];
|
||||
uint8_t slrom[8][SLROMSIZE]; // 7x peripheral-card expansion ROMs ( index 0 is not used)
|
||||
|
||||
uint8_t KBD; // $C000, $C010 ascii value of keyboard input
|
||||
|
||||
bool PAGE2; // $C054 PAGE1 / $C055 PAGE2
|
||||
bool TEXT; // $C050 CLRTEXT / $C051 SETTEXT
|
||||
bool MIXED; // $C052 CLRMIXED / $C053 SETMIXED
|
||||
bool HIRES; // $C056 GR / $C057 HGR
|
||||
bool DHIRES; // 0xC05E / 0xC05F DOUBLE HIRES
|
||||
bool COL80; // 80 COLUMNS
|
||||
bool ALTCHARSET;
|
||||
|
||||
bool LCWR ; // Language Card writable
|
||||
bool LCRD ; // Language Card readable
|
||||
bool LCBK2; // Language Card bank 2 enabled
|
||||
bool LCWFF; // Language Card pre-write flip flop
|
||||
|
||||
bool AN0;
|
||||
bool AN1;
|
||||
bool AN2;
|
||||
bool AN3;
|
||||
|
||||
bool RAMRD;
|
||||
bool RAMWRT;
|
||||
bool ALTZP;
|
||||
bool STORE80;
|
||||
bool INTCXROM;
|
||||
bool SLOTC3ROM;
|
||||
bool IOUDIS;
|
||||
bool VERTBLANK;
|
||||
|
||||
Mmu();
|
||||
~Mmu();
|
||||
void init();
|
||||
uint8_t readMem(uint16_t address);
|
||||
void writeMem(uint16_t address, uint8_t value);
|
||||
|
||||
private:
|
||||
uint8_t softSwitches(uint16_t address, uint8_t value, bool WRT);
|
||||
};
|
||||
|
||||
#endif
|
1
nib/128K/Bad Dudes (1988)(Data East - Quicksilver Software)(Disk 1 of 2).nib
generated
Normal file
1
nib/128K/Bad Dudes (1988)(Data East - Quicksilver Software)(Disk 1 of 2).nib
generated
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* reinette, a french Apple II emulator, using SDL2
|
||||
* and powered by puce65c02 - a WDS 65c02 cpu emulator by the same author
|
||||
* Last modified 1st of July 2021
|
||||
* Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "reinette.h"
|
||||
|
||||
Paddles::Paddles() {
|
||||
PB0 = 0; // $C061 Push Button 0 (bit 7) / Open Apple
|
||||
PB1 = 0; // $C062 Push Button 1 (bit 7) / Solid Apple
|
||||
PB2 = 0; // $C063 Push Button 2 (bit 7) / shift mod !!!
|
||||
GCActionSpeed = 8; // Game Controller speed at which it goes to the edges
|
||||
GCReleaseSpeed = 8; // Game Controller speed at which it returns to center
|
||||
GCCrigger = 0; // $C070 the tick at which the GCs were reseted
|
||||
}
|
||||
|
||||
|
||||
void Paddles::reset() {
|
||||
GCC[0] = GCP[0] * GCP[0]; // initialize the countdown for both paddles
|
||||
GCC[1] = GCP[1] * GCP[1]; // to the square of their actuall values (positions)
|
||||
GCC[2] = GCP[2] * GCP[2]; // to the square of their actuall values (positions)
|
||||
GCC[3] = GCP[3] * GCP[3]; // to the square of their actuall values (positions)
|
||||
GCCrigger = cpu->ticks; // records the time this was done
|
||||
}
|
||||
|
||||
|
||||
uint8_t Paddles::read(int pdl) {
|
||||
const float GCFreq = 6.6; // the speed at which the GC values decrease
|
||||
|
||||
GCC[pdl] -= (cpu->ticks - GCCrigger) / GCFreq; // decreases the countdown
|
||||
if (GCC[pdl] <= 0) // timeout
|
||||
return GCC[pdl] = 0; // returns 0
|
||||
return 0x80; // not timeout, return something with the MSB set
|
||||
}
|
||||
|
||||
|
||||
void Paddles::update() {
|
||||
for (int pdl = 0; pdl < 4; pdl++) { // update the two paddles positions
|
||||
if (GCA[pdl]) { // actively pushing the stick
|
||||
GCP[pdl] += GCD[pdl] * GCActionSpeed;
|
||||
if (GCP[pdl] > 255) GCP[pdl] = 255;
|
||||
if (GCP[pdl] < 0) GCP[pdl] = 0;
|
||||
} else { // the stick returns back to center
|
||||
GCP[pdl] += GCD[pdl] * GCReleaseSpeed;
|
||||
if (GCD[pdl] == 1 && GCP[pdl] > 127) GCP[pdl] = 127;
|
||||
if (GCD[pdl] == -1 && GCP[pdl] < 127) GCP[pdl] = 127;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* reinette, a french Apple II emulator, using SDL2
|
||||
* and powered by puce65c02 - a WDS 65c02 cpu emulator by the same author
|
||||
* Last modified 1st of July 2021
|
||||
* Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __PADDLES_H__
|
||||
#define __PADDLES_H__
|
||||
|
||||
class Paddles {
|
||||
public:
|
||||
uint8_t PB0; // $C061 Push Button 0 (bit 7) / Open Apple
|
||||
uint8_t PB1; // $C062 Push Button 1 (bit 7) / Solid Apple
|
||||
uint8_t PB2; // $C063 Push Button 2 (bit 7) / shift mod !!!
|
||||
|
||||
float GCP[4] = { 127.0f, 127.0f, 127.0f, 127.0f }; // GC Position ranging from 0 (left) to 255 right
|
||||
float GCC[4] = { 0.0f }; // $C064 (GC0) and $C065 (GC1) Countdowns
|
||||
|
||||
int GCD[4] = { 0 }; // GC0 and GC1 Directions (left/down or right/up)
|
||||
int GCA[4] = { 0 }; // GC0 and GC1 Action (push or release)
|
||||
|
||||
int GCActionSpeed; // Game Controller speed at which it goes to the edges
|
||||
int GCReleaseSpeed; // Game Controller speed at which it returns to center
|
||||
long long int GCCrigger; // $C070 the tick at which the GCs were reseted
|
||||
|
||||
Paddles();
|
||||
~Paddles();
|
||||
|
||||
void reset();
|
||||
void update();
|
||||
uint8_t read(int pdl);
|
||||
};
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
puce65c02, a WDC 65c02 cpu emulator, based on puce6502 by the same author
|
||||
|
||||
Last modified 1st of July 2021
|
||||
|
||||
Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
This version is slightly modified for reinette IIe, a french Apple IIe
|
||||
emulator using SDL2 (https://github.com/ArthurFerreira2/reinette-IIe).
|
||||
Please download the latest version from
|
||||
https://github.com/ArthurFerreira2/puce65c02
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _PUCE65C02_H
|
||||
#define _PUCE65C02_H
|
||||
|
||||
typedef enum {run, step, stop, wait} status;
|
||||
|
||||
#define CARRY 0x01
|
||||
#define ZERO 0x02
|
||||
#define INTR 0x04
|
||||
#define DECIM 0x08
|
||||
#define BREAK 0x10
|
||||
#define UNDEF 0x20
|
||||
#define OFLOW 0x40
|
||||
#define SIGN 0x80
|
||||
|
||||
typedef struct Pbits_t {
|
||||
uint8_t C : 1; // Carry
|
||||
uint8_t Z : 1; // Zero
|
||||
uint8_t I : 1; // Interupt disabled
|
||||
uint8_t D : 1; // Decimal
|
||||
uint8_t B : 1; // Break
|
||||
uint8_t U : 1; // Undefined
|
||||
uint8_t V : 1; // Overflow
|
||||
uint8_t S : 1; // Sign
|
||||
} Pbits;
|
||||
|
||||
|
||||
class puce65c02 {
|
||||
private:
|
||||
uint16_t PC; // Program Counter
|
||||
uint8_t A, X, Y, SP; // Accumulator, X and y indexes and Stack Pointer
|
||||
union {
|
||||
uint8_t byte;
|
||||
Pbits bits;
|
||||
} P; // Processor Status
|
||||
|
||||
public:
|
||||
unsigned long long int ticks;
|
||||
|
||||
status state;
|
||||
|
||||
puce65c02();
|
||||
~puce65c02();
|
||||
|
||||
void RST();
|
||||
void IRQ();
|
||||
void NMI();
|
||||
uint16_t exec(unsigned long long int cycleCount);
|
||||
|
||||
uint16_t getPC();
|
||||
void setPC(uint16_t address);
|
||||
|
||||
int getRegs(char* buffer);
|
||||
int getCode(uint16_t address, char* buffer, int size, int numLines);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* reinette, a french Apple II emulator, using SDL2
|
||||
* and powered by puce65c02 - a WDS 65c02 cpu emulator by the same author
|
||||
* Last modified 1st of July 2021
|
||||
* Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __REINETTE_H__
|
||||
#define __REINETTE_H__
|
||||
|
||||
#define VERSION "0.8.1"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "puce65c02.h"
|
||||
#include "mmu.h"
|
||||
#include "disk.h"
|
||||
#include "video.h"
|
||||
#include "speaker.h"
|
||||
#include "paddles.h"
|
||||
#include "gui.h"
|
||||
|
||||
|
||||
extern bool muted;
|
||||
extern int volume;
|
||||
extern bool running; // the entire application
|
||||
extern bool paused; // the virtual machine
|
||||
extern float speed;
|
||||
|
||||
extern puce65c02* cpu;
|
||||
extern Mmu* mmu;
|
||||
extern Disk* disk;
|
||||
extern Video* video;
|
||||
extern Speaker* speaker;
|
||||
extern Paddles* paddles;
|
||||
extern Gui* gui;
|
||||
|
||||
#endif
|
Binary file not shown.
After Width: | Height: | Size: 60 KiB |
|
@ -0,0 +1,24 @@
|
|||
1 ICON "reinette.ico"
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 0,7,1,0
|
||||
PRODUCTVERSION 0,7,0,0
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904E4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Arthur Ferreira"
|
||||
VALUE "FileDescription", "Apple II emulator"
|
||||
VALUE "FileVersion", "0.7.1.0"
|
||||
VALUE "InternalName", "reinette"
|
||||
VALUE "LegalCopyright", "Arthur Ferreira"
|
||||
VALUE "OriginalFilename", "reinette.exe"
|
||||
VALUE "ProductName", "reinette"
|
||||
VALUE "ProductVersion", "0.7"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1252
|
||||
END
|
||||
END
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,655 @@
|
|||
notes
|
||||
|
||||
CALL -151
|
||||
|
||||
Emulating an enhanced Apple //e with an Apple IIe Extended 80-Column Text Card, with 64K of additional RAM and a disk ][ interface card in slot 6 with two floppy drives
|
||||
|
||||
|
||||
Problems and bugs :
|
||||
inverted space is normal ....
|
||||
|
||||
|
||||
# Color Bit Pattern
|
||||
|
||||
01 Black 0xFF000000,
|
||||
02 Dark blue 0xFFCD741C,
|
||||
09 Magenta Dk red 0xFF5639E2,
|
||||
Violet purple 0xFFAD6E7E,
|
||||
04 Light blue 0xFFDFB290,
|
||||
05 Brown 0xFF225897,
|
||||
Dark green 0xFF80811F,
|
||||
Gray1 0xFF7A8289,
|
||||
Medium blue 0xFFE4A856,
|
||||
10 Pink 0xFFF0CEFF,
|
||||
Gray2 0xFF8F979E,
|
||||
green 0xFF31C090,
|
||||
13 Orange 0xFF156CEA,
|
||||
Aqua Lt green 0xFFD5D29F,
|
||||
15 Yellow 0xFFA6FDFF,
|
||||
16 White 0xFFFFFFFF
|
||||
|
||||
|
||||
GOOD:
|
||||
const uint32_t dhcolor[16] = {
|
||||
0xFF000000, // 01 Black
|
||||
0xFFCD741C, // 02 Dark blue
|
||||
0xFF80811F, // 03 Dark green
|
||||
0xFFDFB290, // 04 Light blue
|
||||
0xFF225897, // 05 Brown
|
||||
0xFF7A8289, // Gray1
|
||||
0xFF31C090, // green
|
||||
0xFFE4A856, // Medium blue
|
||||
0xFF5639E2, // 09 Magenta Dk red
|
||||
0xFFAD6E7E, // Violet purple
|
||||
0xFF8F979E, // Gray2
|
||||
0xFFD5D29F, // Aqua Lt green
|
||||
0xFF156CEA, // 13 Orange
|
||||
0xFFF0CEFF, // 10 Pink
|
||||
0xFFA6FDFF, // 15 Yellow
|
||||
0xFFFFFFFF // 16 White
|
||||
};
|
||||
|
||||
GOOD:
|
||||
const uint32_t dlcolor[16] = {
|
||||
0xFF000000, // 01 Black
|
||||
0xFFCD741C, // 02 Dark blue
|
||||
0xFF80811F, // 03 Dark green
|
||||
0xFFDFB290, // 04 Light blue
|
||||
0xFF225897, // 05 Brown
|
||||
0xFF7A8289, // Gray1
|
||||
0xFF31C090, // green
|
||||
0xFFE4A856, // Medium blue
|
||||
0xFF5639E2, // 09 Magenta Dk red
|
||||
0xFFAD6E7E, // Violet purple
|
||||
0xFF8F979E, // Gray2
|
||||
0xFFD5D29F, // Aqua Lt green
|
||||
0xFF156CEA, // 13 Orange
|
||||
0xFFF0CEFF, // 10 Pink
|
||||
0xFFA6FDFF, // 15 Yellow
|
||||
0xFFFFFFFF // 16 White
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
# Color Bit Pattern
|
||||
|
||||
1 Black 0000 #121315 0xFF131512, #000000
|
||||
9 Brown 0001 #874912 0xFF491287,
|
||||
5 Dark green 0010 #238681 0xFF868123,
|
||||
13 green 0011 #94C434 0xFFC43494,
|
||||
3 Dark blue 0100 #1E79D2 0xFF79D21E,
|
||||
11 Gray2 0101 #9D9690 0xFF96909D,
|
||||
7 Medium blue 0110 #6BBDF9 0xFFBDF96B,
|
||||
15 Aqua Lt green 0111 #99CDCB 0xFFCDCB99,
|
||||
2 Magenta Dk Red 1000 #E73C5C 0xFF3C5CE7,
|
||||
10 Orange 1001 #EE7420 0xFF7420EE,
|
||||
6 Gray1 1010 #9C9791 0xFF97919C,
|
||||
14 Yellow 1011 #FEFCA5 0xFFFCA5FE,
|
||||
4 Violet purple 1100 #9384C2 0xFF84C293,
|
||||
12 Pink 1101 #F7B5D8 0xFFB5D8F7,
|
||||
8 Light blue 1110 #A2C4F2 0xFFC4F2A2,
|
||||
16 White 1111 #FFFFFF 0xFFFFFFFF
|
||||
|
||||
const uint32_t dhcolor[16] = {
|
||||
0xFF000000, 0xFF5639E2, 0xFFCD741C, 0xFFAD6E7E,
|
||||
0xFF80811F, 0xFF7A8289, 0xFFE4A856, 0xFFDFB290,
|
||||
0xFF225897, 0xFF156CEA, 0xFF8F979E, 0xFFF0CEFF,
|
||||
0xFF31C090, 0xFFA6FDFF, 0xFFD5D29F, 0xFFFFFFFF
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
from aIIinPC
|
||||
const uint32_t hdcolor[16] = {
|
||||
0xFF131512,
|
||||
0xFF3C5CE7,
|
||||
0xFF79D21E,
|
||||
0xFF84C293,
|
||||
0xFF868123,
|
||||
0xFF97919C,
|
||||
0xFFBDF96B,
|
||||
0xFFC4F2A2,
|
||||
0xFF491287,
|
||||
0xFF7420EE,
|
||||
0xFF96909D,
|
||||
0xFFB5D8F7,
|
||||
0xFFC43494,
|
||||
0xFFFCA5FE,
|
||||
0xFFCDCB99,
|
||||
0xFFFFFFFF
|
||||
};
|
||||
|
||||
BAD :
|
||||
const uint32_t color[16] = {
|
||||
0xFF000000, 0xFF5639E2, 0xFF225897, 0xFF156CEA,
|
||||
0xFF80811F, 0xFF7A8289, 0xFF31C090, 0xFFA6FDFF,
|
||||
0xFFCD741C, 0xFFAD6E7E, 0xFF8F979E, 0xFFF0CEFF,
|
||||
0xFFE4A856, 0xFFDFB290, 0xFFD5D29F, 0xFFFFFFFF
|
||||
};
|
||||
|
||||
|
||||
BAD :
|
||||
const uint32_t dhcolor[16] = {
|
||||
0xFF000000, 0xFF225897, 0xFF80811F, 0xFF31C090,
|
||||
0xFFCD741C, 0xFF8F979E, 0xFFE4A856, 0xFFD5D29F,
|
||||
0xFF5639E2, 0xFF156CEA, 0xFF7A8289, 0xFFA6FDFF,
|
||||
0xFFAD6E7E, 0xFFF0CEFF, 0xFFDFB290, 0xFFFFFFFF
|
||||
};
|
||||
|
||||
BAD :
|
||||
const uint32_t dhcolor[16] = {
|
||||
0xFFFFFFFF,
|
||||
0xFFDFB290,
|
||||
0xFFF0CEFF,
|
||||
0xFFAD6E7E,
|
||||
0xFFA6FDFF,
|
||||
0xFF7A8289,
|
||||
0xFF156CEA,
|
||||
0xFF5639E2,
|
||||
0xFFD5D29F,
|
||||
0xFFE4A856,
|
||||
0xFF8F979E,
|
||||
0xFFCD741C,
|
||||
0xFF31C090,
|
||||
0xFF80811F,
|
||||
0xFF225897,
|
||||
0xFF000000
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
ROMS : https://comp.sys.apple2.narkive.com/yOkYv09h/rom-identification
|
||||
|
||||
Apple IIe
|
||||
|
||||
342-0134-A EF
|
||||
342-0135-A CD
|
||||
341-0150-A Keyboard (UK)
|
||||
341-0151-A Keyboard (DE)
|
||||
342-0160 Video (UK)
|
||||
|
||||
Apple //e Enhanced
|
||||
|
||||
342-0303 EF
|
||||
342-0304 CD
|
||||
342-0265 Video (Mousetext US)
|
||||
|
||||
Apple //e Platinum
|
||||
|
||||
342-0349-A CF
|
||||
342-0150-A Keyboard (UK)
|
||||
342-0273-A Video (Mousetext UK)
|
||||
342-0275-A Video (Mousetext DE)
|
||||
|
||||
|
||||
//e and //ee :
|
||||
|
||||
KEYBOARD :
|
||||
|
||||
Three special keys-Control, Shift, and Caps Lock-change the
|
||||
codes generated by the other keys. The Control key is similar to the
|
||||
ASCII CTRL key.
|
||||
Three other keys have special functions: the Reset key, and two keys
|
||||
marked with apples, one outlined (Open Apple) and one solid
|
||||
(Solid Apple). Pressing the Reset key with the Control key depressed
|
||||
resets the Apple Ile, as described in Chapter 4. The Apple keys are
|
||||
connected to the one-bit game inputs, described later in this
|
||||
chapter.
|
||||
On the extended keyboard lie the Solid Apple key Is labeled
|
||||
Option: the Solid Apple and Option keys are functionally
|
||||
Identical. Also note that manuals accompanying products with
|
||||
the Solid Apple labeled as Option may refer to the Open Apple
|
||||
key as simply the Apple key
|
||||
|
||||
$COOO Keyboard data and strobe
|
||||
$C010 Any-key-down flag and dear-strobe switch
|
||||
|
||||
Your program can find out whether any key is down, except the
|
||||
Reset, Control, Shift, Caps Lock, Open Apple, and Solid Apple (or
|
||||
Option, on the extended keyboard Ile) keys, by reading from
|
||||
location $COOO
|
||||
|
||||
The Open Apple and Solid Apple keys are connected to switches 0
|
||||
and 1 of the game I/0 connector inputs. If OA is pressed, switch 0 is
|
||||
"pressed," and if Solid Apple is pressed, switch 1 is "pressed."
|
||||
|
||||
On the extended keyboard lie, the Shift key Is connected to
|
||||
switch 2 of the game 1/0 ports via the X6 jumper (single-wire
|
||||
Shift-key mod jumper).
|
||||
|
||||
Two more special keys are the Apple keys, Open Apple and Solid
|
||||
Apple, located on either side of the Space bar. These keys are
|
||||
connected to the one-bit game inputs, which are described later in
|
||||
this chapter in the section •switch Inputs." Pressing them in
|
||||
combination with the Control and Reset keys causes the built-in
|
||||
firmware to perform special reset and self-test cycles, described
|
||||
with the reset routine in Chapter 4.
|
||||
|
||||
|
||||
VIDEO :
|
||||
composite video compatible with the standard set by the NTSC
|
||||
For a clear 80-column display, you must use a high-resolution video monitor
|
||||
with a bandwidth of 14 MHz or greater.
|
||||
|
||||
When you turn on power or reset the Apple Ile, the 80-column
|
||||
firmware is inactive and the Apple IIe displays the primary
|
||||
character set, even if an 80-column text card is installed. When you
|
||||
activate the 80-column firmware, it switches to the alternate
|
||||
character set.
|
||||
|
||||
|
||||
|
||||
There are actually two versions of 40-column text mode sup-
|
||||
ported by the //e. The "standard" 40-column mode is the one that
|
||||
is usually in effect and is easily identified by its characteristic
|
||||
"checkerboard" cursor that is displayed whenever keyboard in-
|
||||
formation is being requested. The other version is the "special"
|
||||
40-column mode that is available when the //e's 80-column firm-
|
||||
ware is being used; in this mode the cursor is an inverse block that
|
||||
does not flash
|
||||
Once you are in 80-column mode, you can switch between the
|
||||
80-column mode and the special 40-column mode whenever key-
|
||||
board information is being requested by using the two special
|
||||
escape sequences discussed in Chapter 6: ESC 4 and ESC 8. For
|
||||
example, if you are in 80-column mode and you want to enter the
|
||||
special 40-column mode, enter the sequence
|
||||
ESC 4
|
||||
If you want to go in the opposite direction, enter the sequence
|
||||
ESC 8
|
||||
|
||||
|
||||
|
||||
// TODO : implement double hi-res :
|
||||
// high-resolution graphics, 140 by 192, in 6 colors
|
||||
// high-resolution graphics, 280 by 192, in black and white
|
||||
// double high-resolution graphics, 140 by 192, in 16 colors (with optional 64K text card)
|
||||
// double high-resolution graphics, 560 by 192, in black and white (with optional 64K text card)
|
||||
|
||||
===
|
||||
|
||||
integration is Dear IMGUI
|
||||
|
||||
show :
|
||||
|
||||
- registers
|
||||
- live disassembly
|
||||
- memory dump one window per page ?
|
||||
- video memory, page 1, page 2
|
||||
- sound shape
|
||||
- disassembler
|
||||
- joystick setup / live view
|
||||
- live callstack view with support for imported symbols
|
||||
- stepping by cycle / frame
|
||||
- VRAM viewer - HIRES page 1, page 2
|
||||
- audio buffer viewer, with pretty waveform plots
|
||||
- savestate support in the most hacky way possible
|
||||
|
||||
===
|
||||
|
||||
void GUI::renderHoverText(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::TextV(fmt, args);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
===
|
||||
|
||||
float fps = ImGui::GetIO().Framerate;
|
||||
ImGui::Text("FPS: %.1f (%.1fx speed)", fps, fps / 61.0f);
|
||||
|
||||
===
|
||||
|
||||
ImGui::Text("Save state: ");
|
||||
ImGui::PushID(0);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
if (i > 0)
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(std::to_string(i).c_str())) {
|
||||
emu->saveState(i);
|
||||
}
|
||||
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
===
|
||||
|
||||
ImGui::Checkbox("Fast forward", &emu->cpu.fastForward);
|
||||
ImGui::Checkbox("Step mode", &emu->cpu.stepMode);
|
||||
if (ImGui::Button("Step instruction (space)")) {
|
||||
emu->cpu.stepInst = true;
|
||||
}
|
||||
if (ImGui::Button("Step frame (f)")) {
|
||||
emu->cpu.stepFrame = true;
|
||||
}
|
||||
|
||||
===
|
||||
|
||||
ImGui::Text("cycle: %d", emu->cpu.c); // ticks
|
||||
|
||||
===
|
||||
|
||||
if (ImGui::CollapsingHeader("Disassembly @ PC", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
char buf[25 * 20];
|
||||
buf[0] = '\0';
|
||||
emu->cpu.disassemble(emu->cpu.r.pc, 20, buf);
|
||||
ImGui::Text("%s", buf);
|
||||
}
|
||||
|
||||
===
|
||||
|
||||
case SDLK_F1: // SAVES
|
||||
if (ctrl) {
|
||||
if (saveFloppy(0))
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Save", "\nDisk 1 saved back to file\n", NULL);
|
||||
else
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Save", "\nTError while saving Disk 1\n", NULL);
|
||||
} else if (alt) {
|
||||
if (saveFloppy(1))
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Save", "\nDisk 2 saved back to file\n", NULL);
|
||||
else
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Save", "\nError while saving Disk 2\n", NULL);
|
||||
} else {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Save", "CTRL-F1 to save D1\nALT-F1 to save D2\n", NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
===
|
||||
|
||||
case SDLK_F2: { // SCREENSHOTS
|
||||
sshot = SDL_GetWindowSurface(wdo);
|
||||
SDL_RenderReadPixels(rdr, NULL, SDL_GetWindowPixelFormat(wdo), sshot->pixels, sshot->pitch);
|
||||
workDir[workDirSize] = 0;
|
||||
int i = -1, a = 0, b = 0;
|
||||
while (disk[0].filename[++i] != '\0') {
|
||||
if (disk[0].filename[i] == '\\') a = i;
|
||||
if (disk[0].filename[i] == '.') b = i;
|
||||
}
|
||||
strncat(workDir, "screenshots\\", 14);
|
||||
if (a != b) strncat(workDir, disk[0].filename + a, b - a);
|
||||
else strncat(workDir, "no disk", 10);
|
||||
strncat(workDir, ".bmp", 5);
|
||||
SDL_SaveBMP(sshot, workDir);
|
||||
SDL_FreeSurface(sshot);
|
||||
break;
|
||||
}
|
||||
|
||||
===
|
||||
|
||||
if (opened_popup) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
|
||||
===
|
||||
|
||||
ImGui::Begin("RAM", &show_ram_window);
|
||||
if (ImGui::Button("SEARCH")) {};
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("DISASSEMBLE")) {};
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("SET PC")) {};
|
||||
ImGui::Separator();
|
||||
ImGui::End();
|
||||
|
||||
===
|
||||
----------------------------------------------------------------
|
||||
| |
|
||||
| |
|
||||
| Rockwell |
|
||||
| |
|
||||
| 666 5555555 CCCC 000 22222 |
|
||||
| 6 5 C C 0 0 2 2 |
|
||||
| 6 5 C 0 0 0 2 |
|
||||
| 666666 555555 C 0 0 0 222 |
|
||||
| 6 6 5 C 0 0 0 2 |
|
||||
| 6 6 5 C C 0 0 2 |
|
||||
| 66666 555555 CCCC 000 2222222 |
|
||||
| |
|
||||
| 65C02 CMOS MICROPROCESSOR Instruction Set Summary |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| _________ _________ |
|
||||
| _| \__/ |_ ___ |
|
||||
| Vss |_|1 40|_| RES <-- |
|
||||
| _| |_ |
|
||||
| --> RDY |_|2 39|_| CLK2 --> |
|
||||
| _| |_ |
|
||||
| <-- CLK1 |_|3 38|_| NC |
|
||||
| ___ _| |_ |
|
||||
| --> IRQ |_|4 37|_| CLK0 <-- |
|
||||
| _| |_ |
|
||||
| NC |_|5 36|_| NC |
|
||||
| ___ _| |_ |
|
||||
| --> NMI |_|6 35|_| NC |
|
||||
| _| |_ _ |
|
||||
| --> SYNC |_|7 34|_| R/W --> |
|
||||
| _| |_ |
|
||||
| Vcc |_|8 33|_| DB7 <--> |
|
||||
| _| |_ |
|
||||
| <-- A0 |_|9 32|_| DB6 <--> |
|
||||
| _| |_ |
|
||||
| <-- A1 |_|10 65C02 31|_| DB5 <--> |
|
||||
| _| |_ |
|
||||
| <-- A2 |_|11 30|_| DB4 <--> |
|
||||
| _| |_ |
|
||||
| <-- A3 |_|12 29|_| DB3 <--> |
|
||||
| _| |_ |
|
||||
| <-- A4 |_|13 28|_| DB2 <--> |
|
||||
| _| |_ |
|
||||
| <-- A5 |_|14 27|_| DB1 <--> |
|
||||
| _| |_ |
|
||||
| <-- A6 |_|15 26|_| DB0 <--> |
|
||||
| _| |_ |
|
||||
| <-- A7 |_|16 25|_| A15 --> |
|
||||
| _| |_ |
|
||||
| <-- A8 |_|17 24|_| A14 --> |
|
||||
| _| |_ |
|
||||
| <-- A9 |_|18 23|_| A13 --> |
|
||||
| _| |_ |
|
||||
| <-- A10 |_|19 22|_| A12 --> |
|
||||
| _| |_ |
|
||||
| <-- A11 |_|20 21|_| Vss |
|
||||
| |______________________| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
|Written by Jonathan Bowen |
|
||||
| Programming Research Group |
|
||||
| Oxford University Computing Laboratory |
|
||||
| 8-11 Keble Road |
|
||||
| Oxford OX1 3QD |
|
||||
| England |
|
||||
| |
|
||||
| Tel +44-865-273840 |
|
||||
| |
|
||||
|Created November 1984 |
|
||||
|Updated April 1985 |
|
||||
|Issue 1.1 Copyright (C) J.P.Bowen 1985|
|
||||
----------------------------------------------------------------
|
||||
----------------------------------------------------------------
|
||||
|Mnem. |Op|NVBDIZC|A#ZBIRX@|~|Description |Notes |
|
||||
|------+--+-------+--------+-+---------------------+-----------|
|
||||
|ADC s|6D|**---**| XxX XX|4|Add with Carry |A=A+s+C %|
|
||||
|AND s|2D|*----*-| XxX XX|4|Logical AND |A=A&s %|
|
||||
|ASL d|0E|*----**| xx |6|Arith. Shift Left |d={C,d,0}<-|
|
||||
|ASLA |0A|*----**|X |2|Arith. Shift Left |A={C,d,0}<-|
|
||||
|BBRb z|0F|-------| * X |2|Branch if Bit Reset |If s<b>=0 |
|
||||
|BBSb z|8F|-------| * X |2|Branch if Bit Set |If s<b>=1 |
|
||||
|BCC a|90|-------| X |2|Branch if Carry Clear|If C=0(4~)%|
|
||||
|BCS a|B0|-------| X |2|Branch if Carry Set |If C=1(4~)%|
|
||||
|BEQ a|F0|-------| X |2|Branch if Equal |If Z=1(4~)%|
|
||||
|BIT s|2C|**---*-| Xxx |4|Bit Test |A&s $|
|
||||
|BMI a|30|-------| X |2|Branch if Minus |If N=1(4~)%|
|
||||
|BNE a|D0|-------| X |2|Branch if Not Equal |If Z=0(4~)%|
|
||||
|BPL a|10|-------| X |2|Branch if Plus |If N=0(4~)%|
|
||||
|BRA a|80|-------| X |2|Branch Always |PC=a (4~)%|
|
||||
|BRK |00|--+-1--| X |7|Break(-[S]={PC+2,P}) |PC=[FFFEH] |
|
||||
|BVC a|50|-------| X |2|Branch if Overflw Clr|If V=0(4~)%|
|
||||
|BVS a|70|-------| X |2|Branch if Overflw Set|If V=1(4~)%|
|
||||
|CLC |18|------0| X |2|Clear Carry flag |C=0 |
|
||||
|CLD |D8|---0---| X |2|Clear Decimal mode |D=0 |
|
||||
|CLI |58|----0--| X |2|Clear Int. disable |I=0 |
|
||||
|CLV |B8|-0-----| X |2|Clear Overflow flag |V=0 |
|
||||
|CMP s|CD|*----**| XxX XX|4|Compare |A-s |
|
||||
|CPX s|EC|*----**| X** |4|Compare index reg. |X-s |
|
||||
|CPY s|CC|*----**| X** |4|Compare index reg. |Y-s |
|
||||
|DEC d|CE|*----*-| xx |6|Decrement |d=d-1 |
|
||||
|DECA |3A|*----*-|X |6|Decrement Acc. |A=A-1 |
|
||||
|DEX |CA|*----*-| X |2|Decrement index reg. |X=X-1 |
|
||||
|DEY |88|*----*-| X |2|Decrement index reg. |Y=Y-1 |
|
||||
|EOR s|4D|*----*-| XxX XX|4|Logical Exclusive OR |A=Axs %|
|
||||
|INC d|EE|*----*-| xx |6|Increment |d=d+1 |
|
||||
|INCA |1A|*----*-|X |6|Increment Acc. |A=A+1 |
|
||||
|INX |E8|*----*-| X |2|Increment index reg. |X=X+1 |
|
||||
|INY |C8|*----*-| X |2|Increment index reg. |Y=Y+1 |
|
||||
|JMP s|4C|-------| * X|3|Jump |PC=s $|
|
||||
|JSR s|20|-------| * |6|Jump to Subroutine |-[S]=PC+2=s|
|
||||
|LDA s|AD|*----*-| XxX XX|4|Load Accumulator |A=s %|
|
||||
|LDX s|AE|*----*-| Xyy |4|Load index register |X=s $%|
|
||||
|LDY s|AC|*----*-| Xxx |4|Load index register |Y=s %|
|
||||
|LSR d|4E|0----**| xx |6|Logical Shift Right |d=->{0,d,C}|
|
||||
|LSRA |4A|0----**|X |2|Logical Shift Right |A=->{0,A,C}|
|
||||
|NOP |EA|-------| X |2|No Operation | |
|
||||
|ORA s|0D|*----*-| XxX XX|4|Logical Inclusive OR |A=Avs |
|
||||
|PHA |48|-------| X |3|Push Accumulator |-[S]=A |
|
||||
|PHP |08|-------| X |3|Push status register |-[S]=P |
|
||||
|PHX |DA|-------| X |2|Push index register |-[S]=X |
|
||||
|PHY |5A|-------| X |2|Push index register |-[S]=Y |
|
||||
|PLA |68|-------| X |4|Pull Accumulator |A=[S]+ |
|
||||
|PLP |28|*******| X |4|Pull status register |P=[S]+ |
|
||||
|PLX |FA|-------| X |2|Pull index register |X=[S]+ |
|
||||
|PLY |7A|-------| X |2|Pull index register |Y=[S]+ |
|
||||
|RMBb d|07|-------| * |5|Reset Memory Bit |d<b>=0 |
|
||||
|ROL d|2E|*----**| xx |6|Rotate Left |d={C,d}<- |
|
||||
|ROLA |2A|*----**|X |2|Rotate Left Acc. |A={C,A}<- |
|
||||
|ROR d|6E|*----**| xx |6|Rotate Right |d=->{C,d} |
|
||||
|RORA |6A|*----**|X |2|Rotate Right Acc. |A=->{C,A} |
|
||||
|RTI |40|*******| X |6|Return from Interrupt|{PC,P}=[S]+|
|
||||
|RTS |60|-------| X |6|Return from Subr. |PC={[S]+}+1|
|
||||
|SBC s|ED|*----**| XxX XX|4|Subtract with Carry |A=A-s-C %|
|
||||
|SEC |38|------1| X |2|Set Carry flag |C=1 |
|
||||
|SED |F8|---1---| X |2|Set Decimal mode |D=1 |
|
||||
|SEI |78|----1--| X |2|Set Interrupt disable|I=1 |
|
||||
|SMBb d|87|-------| * |5|Set Memory Bit |d<b>=1 |
|
||||
|STA d|8D|-------| xX XX|4|Store Accumulator |d=A |
|
||||
|STX d|8E|-------| y* |4|Store index register |d=X |
|
||||
|STY d|8C|-------| x* |4|Store index register |d=Y |
|
||||
|STZ d|9C|-------| xx |4|Store Zero |d=0 $|
|
||||
|TAX |AA|*----*-| X |2|Transfer Accumulator |X=A |
|
||||
|TAY |A8|*----*-| X |2|Transfer Accumulator |Y=A |
|
||||
|TRB d|1C|**---*-| ** |2|Test and Reset Bits |d=~A&d |
|
||||
|TSB d|0C|**---*-| ** |2|Test and Set Bits |d=Avd |
|
||||
|TSX |BA|*----*-| X |2|Transfer Stack ptr |X=S |
|
||||
|TXA |8A|*----*-| X |2|Transfer index reg. |A=X |
|
||||
|TXS |9A|-------| X |2|Transfer index reg. |S=X |
|
||||
|TYA |98|*----*-| X |2|Transfer index reg. |A=Y |
|
||||
|------+--+-------+--------+-+---------------------------------|
|
||||
| |XX| | |X|Hexadecimal opcode/no. of cycles |
|
||||
----------------------------------------------------------------
|
||||
----------------------------------------------------------------
|
||||
|Mnemonic |NVBDIZC|A#ZBIRX@|Description |
|
||||
|---------+-------+--------+-----------------------------------|
|
||||
| P |-*01+ | |Unaff/affected/reset/set/stack set |
|
||||
| N |N | |Negative status (Bit 7) |
|
||||
| V | V | |Overflow status (Bit 6) |
|
||||
| B | B | |Break command indicator (Bit 4) |
|
||||
| D | D | |Decimal mode control (Bit 3) |
|
||||
| I | I | |Interrupt disable control (Bit 2) |
|
||||
| Z | Z | |Zero status (Bit 1) |
|
||||
| C | C| |Carry status (Bit 0) |
|
||||
|------------------+--------+----------------------------------|
|
||||
| |* |Only non-indexed mode valid |
|
||||
| |x |X and non-indexed mode valid |
|
||||
| |y |Y and non-indexed mode valid |
|
||||
| |X |All modes valid |
|
||||
|-----------------+--------+-----------------------------------|
|
||||
| | |Add XXH to opcode |+XXH| |
|
||||
| | |Subtract XXH from opcode |-XXH| |
|
||||
| | |Add X to number of cycles | |+X|
|
||||
| | |Subtract X from cycles | |-X|
|
||||
|-----------------+--------+---------------------------+----+--|
|
||||
| b | |Bit number (b=0-7) |+b0H| |
|
||||
| A |A |Accumulator | | |
|
||||
| #n | # |Immediate |-0CH|-2|
|
||||
| #n | # | ditto (opcode = XDH) | X9H| 2|
|
||||
| BIT #n | # | ditto (special case) | 89H| 2|
|
||||
| <n | Z |Zero page |-08H|-1|
|
||||
| STZ n | Z | ditto (special case) | 64H| 3|
|
||||
| n | * |Zero page (direct mode) |-08H|-1|
|
||||
| n,X | x |Zero page indexed (X) |+08H|+0|
|
||||
| n,Y | y |Zero Page indexed (Y) |+08H|+0|
|
||||
| >nn | B |Absolute |+00H|+0|
|
||||
| nn | * |Absolute (extended mode) |+00H|+0|
|
||||
| nn,X | x |Absolute indexed (X) |+10H|+0|
|
||||
| nn,Y | y |Absolute indexed (Y) |+0CH|+0|
|
||||
| LDX nn,Y | y | ditto (special case) | BEH| 4|
|
||||
| | I |Implicit | | |
|
||||
| a | R |Relative (PC=PC+1+offset) | |+2|
|
||||
| [nn,X] | x |Indexed indirect (X) |-0CH|+2|
|
||||
| [nn],Y | y |Indirect indexed (Y) |+04H|+1|
|
||||
| [nn] | @|Absolute indirect |+05H|+1|
|
||||
| JMP [nn] | @| ditto (special case) | 6CH| 5|
|
||||
|--------------------------+-----------------------------------|
|
||||
| A |Accumulator (8-bit) |
|
||||
| P |Processor status register (8-bit) |
|
||||
| PC |Program Counter (16-bit) |
|
||||
| S |Stack pointer (9-bit, MSB=1) |
|
||||
| X |Index register X (8-bit) |
|
||||
| Y |Index register Y (8-bit) |
|
||||
|--------------------------+-----------------------------------|
|
||||
| a |Relative address (-128 to +127) |
|
||||
| b |Bit number (0 to 7) |
|
||||
| d |Destination |
|
||||
| n |8-bit expression (0 to 255) |
|
||||
| nn |16-bit expression (0 to 65535) |
|
||||
| s |Source |
|
||||
| z |Zero page, relative address (n,a) |
|
||||
|--------------------------+-----------------------------------|
|
||||
| + - |Arithmetic addition/subtraction |
|
||||
| * / |Arithmetic multiplication/division |
|
||||
| & ~ |Logical AND/NOT |
|
||||
| v x |Logical inclusive/exclusive OR |
|
||||
| <- -> |Rotate left/right |
|
||||
| [ ] |Indirect addressing |
|
||||
| [ ]+ |Post-increment indirect addressing |
|
||||
| -[ ] |Pre-decrement indirect addressing |
|
||||
| { } |Combination of operands |
|
||||
| < > |Bit number |
|
||||
| $ |Special case for addressing mode |
|
||||
| % |~s=~s+1 if crossing page boundary |
|
||||
|--------------------------+-----------------------------------|
|
||||
|0000H to 00FFH |Page 0 (see zero page addressing) |
|
||||
|0100H to 01FFH |Page 1 (stack area, 01FFH = start) |
|
||||
|XX00H to XXFFH |Page n (where n=XXH) |
|
||||
|FFFAH to FFFBH |Non maskable interrupt vector(NMI) |
|
||||
|FFFCH to FFFDH |Reset (RES) vector |
|
||||
|FFFEH to FFFFH |Interrupt Request vector (IRQ) |
|
||||
|FFFEH to FFFFH |Break command vector (see BRK) |
|
||||
----------------------------------------------------------------
|
||||
|
||||
|
||||
===
|
||||
|
||||
|
||||
===
|
||||
|
||||
|
||||
===
|
||||
|
||||
|
||||
===
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* reinette, a french Apple II emulator, using SDL2
|
||||
* and powered by puce65c02 - a WDS 65c02 cpu emulator by the same author
|
||||
* Last modified 1st of July 2021
|
||||
* Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "reinette.h"
|
||||
|
||||
Speaker::Speaker() {
|
||||
if (SDL_Init(SDL_INIT_AUDIO) != 0) {
|
||||
printf("Error: %s\n", SDL_GetError());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
SDL_AudioSpec desired = { 96000, AUDIO_S8, 1, 0, 512, 0, 0, NULL, NULL };
|
||||
audioDevice = SDL_OpenAudioDevice(NULL, 0, &desired, NULL, SDL_FALSE); // get the audio device ID
|
||||
setVolume(volume);
|
||||
SDL_PauseAudioDevice(audioDevice, muted); // unmute it (muted is false)
|
||||
}
|
||||
|
||||
|
||||
void Speaker::play() {
|
||||
static unsigned long long int lastTick = 0LL;
|
||||
static bool SPKR = false; // $C030 Speaker toggle
|
||||
|
||||
SPKR = !SPKR; // toggle speaker state
|
||||
Uint32 length = (int)((double)(cpu->ticks - lastTick) / 10.42f / speed); // 1000000Hz / 96000Hz = 10.4166
|
||||
lastTick = cpu->ticks;
|
||||
if (!muted) {
|
||||
if (length > audioBufferSize)
|
||||
SDL_QueueAudio(audioDevice, audioBuffer[2], audioBufferSize); // silence
|
||||
else
|
||||
SDL_QueueAudio(audioDevice, audioBuffer[SPKR], length | 1); // | 1 TO HEAR HIGH FREQ SOUNDS
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Speaker::toggleMute() {
|
||||
SDL_PauseAudioDevice(audioDevice, muted);
|
||||
SDL_ClearQueuedAudio(audioDevice);
|
||||
}
|
||||
|
||||
|
||||
void Speaker::setVolume(int newVolume) {
|
||||
if (muted) {
|
||||
muted = false;
|
||||
toggleMute();
|
||||
}
|
||||
|
||||
if (newVolume > 127)
|
||||
volume = 127;
|
||||
else if (newVolume < 0)
|
||||
volume = 0;
|
||||
else volume = newVolume;
|
||||
|
||||
for (int i = 0; i < audioBufferSize; i++) { // two audio buffers,
|
||||
audioBuffer[true][i] = (int8_t)volume; // one used when SPKR is true
|
||||
audioBuffer[false][i] = (int8_t)-volume; // the other when SPKR is false
|
||||
audioBuffer[2][i] = 0; // silence
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* reinette, a french Apple II emulator, using SDL2
|
||||
* and powered by puce65c02 - a WDS 65c02 cpu emulator by the same author
|
||||
* Last modified 1st of July 2021
|
||||
* Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __SOUND_H__
|
||||
#define __SOUND_H__
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#define audioBufferSize 32768
|
||||
|
||||
class Speaker {
|
||||
public:
|
||||
Speaker();
|
||||
void play();
|
||||
void toggleMute();
|
||||
void setVolume(int newVolume);
|
||||
|
||||
private:
|
||||
int8_t audioBuffer[3][audioBufferSize];
|
||||
SDL_AudioDeviceID audioDevice;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,561 @@
|
|||
/*
|
||||
* reinette, a french Apple II emulator, using SDL2
|
||||
* and powered by puce65c02 - a WDS 65c02 cpu emulator by the same author
|
||||
* Last modified 1st of July 2021
|
||||
* Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "reinette.h"
|
||||
|
||||
|
||||
Video::Video() {
|
||||
memset(ramHeatmap, 0xFF000000, sizeof(ramHeatmap));
|
||||
memset(auxHeatmap, 0xFF000000, sizeof(auxHeatmap));
|
||||
|
||||
// array from https://github.com/Michaelangel007/apple2_hgr_font_tutorial/
|
||||
const char FONT[] = {
|
||||
0x10, 0x08, 0x36, 0x7F, 0x3F, 0x3F, 0x7E, 0x36, // 0x00 ^@
|
||||
0x10, 0x08, 0x36, 0x41, 0x21, 0x21, 0x4A, 0x36, // 0x01 ^A
|
||||
0x00, 0x00, 0x02, 0x06, 0x0E, 0x1E, 0x36, 0x42, // 0x02 ^B
|
||||
0x7F, 0x22, 0x14, 0x08, 0x08, 0x14, 0x2A, 0x7F, // 0x03 ^C
|
||||
0x00, 0x40, 0x20, 0x11, 0x0A, 0x04, 0x04, 0x00, // 0x04 ^D
|
||||
0x7F, 0x3F, 0x5F, 0x6C, 0x75, 0x7B, 0x7B, 0x7F, // 0x05 ^E
|
||||
0x70, 0x60, 0x7E, 0x31, 0x79, 0x30, 0x3F, 0x02, // 0x06 ^F
|
||||
0x00, 0x18, 0x07, 0x00, 0x07, 0x0C, 0x08, 0x70, // 0x07 ^G
|
||||
0x08, 0x04, 0x02, 0x7F, 0x02, 0x04, 0x08, 0x00, // 0x08 ^H
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, // 0x09 ^I
|
||||
0x08, 0x08, 0x08, 0x08, 0x49, 0x2A, 0x1C, 0x08, // 0x0A ^J
|
||||
0x08, 0x1C, 0x2A, 0x49, 0x08, 0x08, 0x08, 0x08, // 0x0B ^K
|
||||
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0C ^L
|
||||
0x40, 0x40, 0x40, 0x44, 0x46, 0x7F, 0x06, 0x04, // 0x0D ^M
|
||||
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, // 0x0E ^N
|
||||
0x13, 0x18, 0x1C, 0x7E, 0x1C, 0x18, 0x10, 0x6F, // 0x0F ^O
|
||||
0x64, 0x0C, 0x1C, 0x3F, 0x1C, 0x0C, 0x04, 0x7B, // 0x10 ^P
|
||||
0x40, 0x48, 0x08, 0x7F, 0x3E, 0x1C, 0x48, 0x40, // 0x11 ^Q
|
||||
0x40, 0x48, 0x1C, 0x3E, 0x7E, 0x08, 0x48, 0x40, // 0x12 ^R
|
||||
0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, // 0x13 ^S
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7F, // 0x14 ^T
|
||||
0x08, 0x10, 0x20, 0x7F, 0x20, 0x10, 0x08, 0x00, // 0x15 ^U
|
||||
0x2A, 0x55, 0x2A, 0x55, 0x2A, 0x55, 0x2A, 0x55, // 0x16 ^V
|
||||
0x55, 0x2A, 0x55, 0x2A, 0x55, 0x2A, 0x55, 0x2A, // 0x17 ^W
|
||||
0x00, 0x3E, 0x41, 0x01, 0x01, 0x01, 0x7F, 0x00, // 0x18 ^X
|
||||
0x00, 0x00, 0x3F, 0x40, 0x40, 0x40, 0x7F, 0x00, // 0x19 ^Y
|
||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // 0x1A ^Z
|
||||
0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00, // 0x1B ^[
|
||||
0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, // 0x1C ^
|
||||
0x14, 0x14, 0x77, 0x00, 0x77, 0x14, 0x14, 0x00, // 0x1D ^]
|
||||
0x7F, 0x40, 0x40, 0x4C, 0x4C, 0x40, 0x40, 0x7F, // 0x1E ^^
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x1F ^_
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 space
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, // 0x21 !
|
||||
0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x22 "
|
||||
0x14, 0x14, 0x3E, 0x14, 0x3E, 0x14, 0x14, 0x00, // 0x23 #
|
||||
0x08, 0x3C, 0x0A, 0x1C, 0x28, 0x1E, 0x08, 0x00, // 0x24 $
|
||||
0x06, 0x26, 0x10, 0x08, 0x04, 0x32, 0x30, 0x00, // 0x25 %
|
||||
0x04, 0x0A, 0x0A, 0x04, 0x2A, 0x12, 0x2C, 0x00, // 0x26 &
|
||||
0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x27 '
|
||||
0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, 0x00, // 0x28 (
|
||||
0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, // 0x29 )
|
||||
0x08, 0x2A, 0x1C, 0x08, 0x1C, 0x2A, 0x08, 0x00, // 0x2A *
|
||||
0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, // 0x2B +
|
||||
0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x04, 0x00, // 0x2C ,
|
||||
0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, // 0x2D -
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, // 0x2E .
|
||||
0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00, // 0x2F /
|
||||
0x1C, 0x22, 0x32, 0x2A, 0x26, 0x22, 0x1C, 0x00, // 0x30 0
|
||||
0x08, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, // 0x31 1
|
||||
0x1C, 0x22, 0x20, 0x18, 0x04, 0x02, 0x3E, 0x00, // 0x32 2
|
||||
0x3E, 0x20, 0x10, 0x18, 0x20, 0x22, 0x1C, 0x00, // 0x33 3
|
||||
0x10, 0x18, 0x14, 0x12, 0x3E, 0x10, 0x10, 0x00, // 0x34 4
|
||||
0x3E, 0x02, 0x1E, 0x20, 0x20, 0x22, 0x1C, 0x00, // 0x35 5
|
||||
0x38, 0x04, 0x02, 0x1E, 0x22, 0x22, 0x1C, 0x00, // 0x36 6
|
||||
0x3E, 0x20, 0x10, 0x08, 0x04, 0x04, 0x04, 0x00, // 0x37 7
|
||||
0x1C, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x1C, 0x00, // 0x38 8
|
||||
0x1C, 0x22, 0x22, 0x3C, 0x20, 0x10, 0x0E, 0x00, // 0x39 9
|
||||
0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, // 0x3A :
|
||||
0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x04, 0x00, // 0x3B ;
|
||||
0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00, // 0x3C <
|
||||
0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, // 0x3D =
|
||||
0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, // 0x3E >
|
||||
0x1C, 0x22, 0x10, 0x08, 0x08, 0x00, 0x08, 0x00, // 0x3F ?
|
||||
0x1C, 0x22, 0x2A, 0x3A, 0x1A, 0x02, 0x3C, 0x00, // 0x40 @
|
||||
0x08, 0x14, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x00, // 0x41 A
|
||||
0x1E, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x1E, 0x00, // 0x42 B
|
||||
0x1C, 0x22, 0x02, 0x02, 0x02, 0x22, 0x1C, 0x00, // 0x43 C
|
||||
0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, // 0x44 D
|
||||
0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x3E, 0x00, // 0x45 E
|
||||
0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x00, // 0x46 F
|
||||
0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x3C, 0x00, // 0x47 G
|
||||
0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, // 0x48 H
|
||||
0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, // 0x49 I
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, // 0x4A J
|
||||
0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, // 0x4B K
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, // 0x4C L
|
||||
0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, // 0x4D M
|
||||
0x22, 0x22, 0x26, 0x2A, 0x32, 0x22, 0x22, 0x00, // 0x4E N
|
||||
0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, // 0x4F O
|
||||
0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x00, // 0x50 P
|
||||
0x1C, 0x22, 0x22, 0x22, 0x2A, 0x12, 0x2C, 0x00, // 0x51 Q
|
||||
0x1E, 0x22, 0x22, 0x1E, 0x0A, 0x12, 0x22, 0x00, // 0x52 R
|
||||
0x1C, 0x22, 0x02, 0x1C, 0x20, 0x22, 0x1C, 0x00, // 0x53 S
|
||||
0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, // 0x54 T
|
||||
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, // 0x55 U
|
||||
0x22, 0x22, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00, // 0x56 V
|
||||
0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, // 0x57 W
|
||||
0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, // 0x58 X
|
||||
0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, // 0x59 Y
|
||||
0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, // 0x5A Z
|
||||
0x3E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3E, 0x00, // 0x5B [
|
||||
0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, // 0x5C back slash
|
||||
0x3E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3E, 0x00, // 0x5D ]
|
||||
0x00, 0x00, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, // 0x5E ^
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, // 0x5F _
|
||||
0x04, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x60 `
|
||||
0x00, 0x00, 0x1C, 0x20, 0x3C, 0x22, 0x3C, 0x00, // 0x61 a
|
||||
0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, // 0x62 b
|
||||
0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x3C, 0x00, // 0x63 c
|
||||
0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x00, // 0x64 d
|
||||
0x00, 0x00, 0x1C, 0x22, 0x3E, 0x02, 0x3C, 0x00, // 0x65 e
|
||||
0x18, 0x24, 0x04, 0x1E, 0x04, 0x04, 0x04, 0x00, // 0x66 f
|
||||
0x00, 0x00, 0x1C, 0x22, 0x22, 0x3C, 0x20, 0x1C, // 0x67 g
|
||||
0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x00, // 0x68 h
|
||||
0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x1C, 0x00, // 0x69 i
|
||||
0x10, 0x00, 0x18, 0x10, 0x10, 0x10, 0x12, 0x0C, // 0x6A j
|
||||
0x02, 0x02, 0x22, 0x12, 0x0E, 0x12, 0x22, 0x00, // 0x6B k
|
||||
0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, // 0x6C l
|
||||
0x00, 0x00, 0x36, 0x2A, 0x2A, 0x2A, 0x22, 0x00, // 0x6D m
|
||||
0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x00, // 0x6E n
|
||||
0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, // 0x6F o
|
||||
0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, // 0x70 p
|
||||
0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x20, 0x20, // 0x71 q
|
||||
0x00, 0x00, 0x3A, 0x06, 0x02, 0x02, 0x02, 0x00, // 0x72 r
|
||||
0x00, 0x00, 0x3C, 0x02, 0x1C, 0x20, 0x1E, 0x00, // 0x73 s
|
||||
0x04, 0x04, 0x1E, 0x04, 0x04, 0x24, 0x18, 0x00, // 0x74 t
|
||||
0x00, 0x00, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x00, // 0x75 u
|
||||
0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00, // 0x76 v
|
||||
0x00, 0x00, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x00, // 0x77 w
|
||||
0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, // 0x78 x
|
||||
0x00, 0x00, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, // 0x79 y
|
||||
0x00, 0x00, 0x3E, 0x10, 0x08, 0x04, 0x3E, 0x00, // 0x7A z
|
||||
0x38, 0x0C, 0x0C, 0x06, 0x0C, 0x0C, 0x38, 0x00, // 0x7B {
|
||||
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, // 0x7C |
|
||||
0x0E, 0x18, 0x18, 0x30, 0x18, 0x18, 0x0E, 0x00, // 0x7D }
|
||||
0x2C, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x7E ~
|
||||
0x00, 0x2A, 0x14, 0x2A, 0x14, 0x2A, 0x00, 0x00 // 0x7F DEL
|
||||
};
|
||||
|
||||
// Generate the font arrays from the above array
|
||||
int raw;
|
||||
for (int c=0; c<128; c++) {
|
||||
for (int y=0; y<8; y++) {
|
||||
raw = FONT[c*8 + y];
|
||||
for (int x=0; x<7; x++) {
|
||||
if (raw & (1<<x)) {
|
||||
fontInverse[c][y][x]= 0xFF000000;
|
||||
fontNormal[c][y][x]= 0xFFFFFFFF;
|
||||
}
|
||||
else {
|
||||
fontInverse[c][y][x]= 0xFFFFFFFF;
|
||||
fontNormal[c][y][x]= 0xFF000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Video::~Video() {
|
||||
// delete[] fontInverse;
|
||||
// delete[] fontNormal;
|
||||
// ...
|
||||
}
|
||||
|
||||
|
||||
void Video::clearCache() {
|
||||
memset(previousDots, -2, sizeof(previousDots));
|
||||
memset(previousBlocks, -2, sizeof(previousBlocks));
|
||||
memset(previousChars, -2, sizeof(previousChars));
|
||||
memset(previousChars80,-2, sizeof(previousChars80));
|
||||
}
|
||||
|
||||
|
||||
void Video::update() {
|
||||
|
||||
// Note: Colors may vary, depending upon the controls on the monitor or TV set
|
||||
const uint32_t lcolor[16] = { // the 16 low res colors
|
||||
0xFF000000, 0xFF5639E2, 0xFFCD741C, 0xFFAD6E7E,
|
||||
0xFF80811F, 0xFF7A8289, 0xFFE4A856, 0xFFDFB290,
|
||||
0xFF225897, 0xFF156CEA, 0xFF8F979E, 0xFFF0CEFF,
|
||||
0xFF31C090, 0xFFA6FDFF, 0xFFD5D29F, 0xFFFFFFFF
|
||||
};
|
||||
|
||||
const uint32_t hcolor[16] = { // the high res colors (light & dark levels)
|
||||
0xFF000000, 0xFF31C090, 0xFFAD6E7E, 0xFFFFFFFF,
|
||||
0xFF000000, 0xFF156CEA, 0xFFE4A856, 0xFFFFFFFF,
|
||||
0xFF000000, 0xFF56373f, 0xFF196048, 0xFFFFFFFF,
|
||||
0xFF000000, 0xFF72542B, 0xFF0A3675, 0xFFFFFFFF
|
||||
};
|
||||
|
||||
const uint32_t dhcolor[16] = {
|
||||
0xFF000000, 0xFFCD741C, 0xFF80811F, 0xFFDFB290,
|
||||
0xFF225897, 0xFF7A8289, 0xFF31C090, 0xFFE4A856,
|
||||
0xFF5639E2, 0xFFAD6E7E, 0xFF8F979E, 0xFFD5D29F,
|
||||
0xFF156CEA, 0xFFF0CEFF, 0xFFA6FDFF, 0xFFFFFFFF
|
||||
};
|
||||
|
||||
const uint16_t offsetGR[24] = { // base addresses for each line in TEXT or GR
|
||||
0x000, 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380, // lines 0-7
|
||||
0x028, 0x0A8, 0x128, 0x1A8, 0x228, 0x2A8, 0x328, 0x3A8, // lines 8-15
|
||||
0x050, 0x0D0, 0x150, 0x1D0, 0x250, 0x2D0, 0x350, 0x3D0 // lines 16-23
|
||||
};
|
||||
|
||||
const uint16_t offsetHGR[192] = { // base addresses for each line in HGR
|
||||
0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400, 0x1800, 0x1C00, // lines 0-7
|
||||
0x0080, 0x0480, 0x0880, 0x0C80, 0x1080, 0x1480, 0x1880, 0x1C80, // lines 8-15
|
||||
0x0100, 0x0500, 0x0900, 0x0D00, 0x1100, 0x1500, 0x1900, 0x1D00, // lines 16-23
|
||||
0x0180, 0x0580, 0x0980, 0x0D80, 0x1180, 0x1580, 0x1980, 0x1D80,
|
||||
0x0200, 0x0600, 0x0A00, 0x0E00, 0x1200, 0x1600, 0x1A00, 0x1E00,
|
||||
0x0280, 0x0680, 0x0A80, 0x0E80, 0x1280, 0x1680, 0x1A80, 0x1E80,
|
||||
0x0300, 0x0700, 0x0B00, 0x0F00, 0x1300, 0x1700, 0x1B00, 0x1F00,
|
||||
0x0380, 0x0780, 0x0B80, 0x0F80, 0x1380, 0x1780, 0x1B80, 0x1F80,
|
||||
0x0028, 0x0428, 0x0828, 0x0C28, 0x1028, 0x1428, 0x1828, 0x1C28,
|
||||
0x00A8, 0x04A8, 0x08A8, 0x0CA8, 0x10A8, 0x14A8, 0x18A8, 0x1CA8,
|
||||
0x0128, 0x0528, 0x0928, 0x0D28, 0x1128, 0x1528, 0x1928, 0x1D28,
|
||||
0x01A8, 0x05A8, 0x09A8, 0x0DA8, 0x11A8, 0x15A8, 0x19A8, 0x1DA8,
|
||||
0x0228, 0x0628, 0x0A28, 0x0E28, 0x1228, 0x1628, 0x1A28, 0x1E28,
|
||||
0x02A8, 0x06A8, 0x0AA8, 0x0EA8, 0x12A8, 0x16A8, 0x1AA8, 0x1EA8,
|
||||
0x0328, 0x0728, 0x0B28, 0x0F28, 0x1328, 0x1728, 0x1B28, 0x1F28,
|
||||
0x03A8, 0x07A8, 0x0BA8, 0x0FA8, 0x13A8, 0x17A8, 0x1BA8, 0x1FA8,
|
||||
0x0050, 0x0450, 0x0850, 0x0C50, 0x1050, 0x1450, 0x1850, 0x1C50,
|
||||
0x00D0, 0x04D0, 0x08D0, 0x0CD0, 0x10D0, 0x14D0, 0x18D0, 0x1CD0,
|
||||
0x0150, 0x0550, 0x0950, 0x0D50, 0x1150, 0x1550, 0x1950, 0x1D50,
|
||||
0x01D0, 0x05D0, 0x09D0, 0x0DD0, 0x11D0, 0x15D0, 0x19D0, 0x1DD0,
|
||||
0x0250, 0x0650, 0x0A50, 0x0E50, 0x1250, 0x1650, 0x1A50, 0x1E50,
|
||||
0x02D0, 0x06D0, 0x0AD0, 0x0ED0, 0x12D0, 0x16D0, 0x1AD0, 0x1ED0, // lines 168-183
|
||||
0x0350, 0x0750, 0x0B50, 0x0F50, 0x1350, 0x1750, 0x1B50, 0x1F50, // lines 176-183
|
||||
0x03D0, 0x07D0, 0x0BD0, 0x0FD0, 0x13D0, 0x17D0, 0x1BD0, 0x1FD0 // lines 184-191
|
||||
};
|
||||
|
||||
|
||||
// HIGH RES GRAPHICS
|
||||
if (!mmu->TEXT && mmu->HIRES && !mmu->DHIRES) {
|
||||
gfxmode = 40;
|
||||
uint8_t colorIdx; // index to the color array
|
||||
uint16_t word;
|
||||
uint8_t bits[16], bit, pbit, colorSet, even;
|
||||
int vRamBase = 0x2000 + mmu->PAGE2 * 0x2000;
|
||||
int endRaw = mmu->MIXED ? 160 : 192;
|
||||
|
||||
for (int line = 0; line < endRaw; line++) { // for every line
|
||||
for (int col = 0; col < 40; col += 2) { // for every 7 horizontal dots
|
||||
word = (uint16_t)(mmu->ram[vRamBase + offsetHGR[line] + col + 1]) << 8; // store the two next bytes into 'word'
|
||||
word += mmu->ram[vRamBase + offsetHGR[line] + col]; // in reverse order
|
||||
|
||||
if (previousDots[line][col] != word) { // check if this group of 7 dots need a redraw
|
||||
|
||||
int x = col * 7;
|
||||
even = 0;
|
||||
|
||||
for (bit = 0; bit < 16; bit++) // store all bits 'word' into 'bits'
|
||||
bits[bit] = (word >> bit) & 1;
|
||||
colorSet = bits[7] * 4; // select the right color set
|
||||
pbit = previousBit[line][col]; // the bit value of the left dot
|
||||
bit = 0; // starting at 1st bit of 1st byte
|
||||
|
||||
while (bit < 15) { // until we reach bit7 of 2nd byte
|
||||
if (bit == 7) { // moving into the second byte
|
||||
colorSet = bits[15] * 4; // update the color set
|
||||
bit++; // skip bit 7
|
||||
}
|
||||
|
||||
colorIdx = even + colorSet + (bits[bit] << 1) + (pbit);
|
||||
screenPixels[line * 280 + x] = hcolor[colorIdx];
|
||||
|
||||
x++;
|
||||
pbit = bits[bit++]; // proceed to the next pixel
|
||||
even = even ? 0 : 8; // one pixel every two is darker
|
||||
}
|
||||
|
||||
previousDots[line][col] = word; // update the video cache
|
||||
if ((col < 37) && (previousBit[line][col + 2] != pbit)) { // check color franging effect on the dot after
|
||||
previousBit[line][col + 2] = pbit; // set pbit and clear the
|
||||
previousDots[line][col + 2] = -1; // video cache for next dot
|
||||
}
|
||||
} // if (previousDots[line][col] ...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DOUBLE HIGH RES GRAPHICS
|
||||
else if (!mmu->TEXT && mmu->HIRES && mmu->DHIRES) {
|
||||
gfxmode = 80;
|
||||
// int vRamBase = 0x2000 + PAGE2 * 0x2000;
|
||||
int vRamBase = mmu->STORE80 ? 0x2000 : mmu->PAGE2 * 0x2000 + 0x2000; // TODO : CHECK THIS !
|
||||
int endRaw = mmu->MIXED ? 160 : 192;
|
||||
uint32_t color;
|
||||
uint32_t dword;
|
||||
|
||||
for (int line = 0; line < endRaw; line++) {
|
||||
for (int col = 0, x = 0; col < 39; col+=2) {
|
||||
|
||||
int address = vRamBase + offsetHGR[line] + col;
|
||||
|
||||
// combine 4 bytes of memory, from MAIN and AUX
|
||||
dword = (uint32_t)(mmu->aux[address] & 0x7F);
|
||||
dword |= (uint32_t)(mmu->ram[address] & 0x7F) << 7;
|
||||
address++;
|
||||
dword |= (uint32_t)(mmu->aux[address] & 0x7F) << 14;
|
||||
dword |= (uint32_t)(mmu->ram[address] & 0x7F) << 21;
|
||||
|
||||
int offset1 = 560 * line * 2;
|
||||
int offset2 = 560 + offset1;
|
||||
|
||||
for (int p=0; p<7; p++, dword >>= 4) {
|
||||
color = dhcolor[dword & 0x0F];
|
||||
for (int a=0; a<4; a++, x++) { // draw 4x2 pixels
|
||||
screenPixels80[offset1 + x] = color;
|
||||
screenPixels80[offset2 + x] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lOW RES GRAPHICS
|
||||
else if (!mmu->TEXT && !mmu->HIRES && !mmu->DHIRES) { // and not in HIRES
|
||||
gfxmode = 40;
|
||||
int vRamBase = mmu->PAGE2 * 0x0400 + 0x400;
|
||||
int endRaw = mmu->MIXED ? 20 : 24;
|
||||
uint32_t color;
|
||||
for (int col = 0; col < 40; col++) { // for each column
|
||||
int x = col * 7;
|
||||
for (int line = 0; line < endRaw; line++) { // for each row
|
||||
|
||||
glyph = mmu->ram[vRamBase + offsetGR[line] + col]; // read video memory
|
||||
|
||||
if (previousBlocks[line][col] != glyph) { // check if this block need a redraw
|
||||
previousBlocks[line][col] = glyph;
|
||||
|
||||
int y = line * 8; // first block
|
||||
color = lcolor[glyph & 0x0F]; // first nibble
|
||||
for (int a=0; a<7; a++)
|
||||
for (int b=0; b<4; b++)
|
||||
screenPixels[(y+b) * 280 + x + a] = color;
|
||||
|
||||
y += 4; // second block
|
||||
color = lcolor[(glyph & 0xF0) >> 4]; // second nibble
|
||||
for (int a=0; a<7; a++)
|
||||
for (int b=0; b<4; b++)
|
||||
screenPixels[(y+b) * 280 + x + a] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DOUBLE lOW RES GRAPHICS
|
||||
else if (!mmu->TEXT && !mmu->HIRES && mmu->DHIRES) {
|
||||
gfxmode = 80;
|
||||
int vRamBase = mmu->PAGE2 * 0x0400 + 0x400; // TODO : CHECK THIS !
|
||||
int endRaw = mmu->MIXED ? 20 : 24;
|
||||
int x, y;
|
||||
uint32_t color;
|
||||
for (int col = 0; col < 40; col++) { // for each column
|
||||
x = col * 14;
|
||||
for (int line = 0; line < endRaw; line++) { // for each row
|
||||
y = line * 16; // first block
|
||||
|
||||
glyph = mmu->aux[vRamBase + offsetGR[line] + col]; // read AUX video memory
|
||||
color = lcolor[glyph & 0x0F]; // first nibble
|
||||
for (int a=0; a<7; a++)
|
||||
for (int b=0; b<8; b++)
|
||||
screenPixels80[(y+b) * 560 + (x+a)] = color;
|
||||
|
||||
color = lcolor[(glyph & 0xF0) >> 4]; // second nibble
|
||||
for (int a=0; a<7; a++)
|
||||
for (int b=8; b<16; b++)
|
||||
screenPixels80[(y+b) * 560 + (x+a)] = color;
|
||||
|
||||
glyph = mmu->ram[vRamBase + offsetGR[line] + col]; // read MAIN video memory
|
||||
color = lcolor[glyph & 0x0F]; // first nibble
|
||||
for (int a=7; a<14; a++)
|
||||
for (int b=0; b<8; b++)
|
||||
screenPixels80[(y+b) * 560 + (x+a)] = color;
|
||||
|
||||
color = lcolor[(glyph & 0xF0) >> 4]; // second nibble
|
||||
for (int a=7; a<14; a++)
|
||||
for (int b=8; b<16; b++)
|
||||
screenPixels80[(y+b) * 560 + (x+a)] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TEXT 40 COLUMNS
|
||||
if (!mmu->COL80 && (mmu->TEXT || mmu->MIXED)) {
|
||||
gfxmode = 40;
|
||||
int vRamBase = mmu->PAGE2 * 0x0400 + 0x400;
|
||||
int startRaw = mmu->TEXT ? 0 : 20;
|
||||
for (int col = 0; col < 40; col++) { // for each column
|
||||
for (int line = startRaw; line < 24; line++) { // for each row
|
||||
|
||||
glyph = mmu->ram[vRamBase + offsetGR[line] + col]; // read video memory
|
||||
|
||||
if (glyph > 0x7F) glyphAttr = A_NORMAL; // is NORMAL ?
|
||||
else if (glyph < 0x40) glyphAttr = A_INVERSE; // is INVERSE ?
|
||||
else glyphAttr = A_FLASH; // it's FLASH !
|
||||
|
||||
if (previousChars[line][col] != (glyph <<(8*mmu->ALTCHARSET)) || glyphAttr == A_FLASH) { // check if this char need a redraw
|
||||
previousChars[line][col] = (glyph <<(8*mmu->ALTCHARSET));
|
||||
|
||||
glyph &= 0x7F; // unset bit 7
|
||||
if (glyph < 0x20) glyph |= 0x40; // shifts to match the ASCII codes
|
||||
|
||||
if ((!mmu->ALTCHARSET) && (glyphAttr == A_FLASH) && (glyph > 0x5F))
|
||||
glyph &= 0x3F;
|
||||
|
||||
if (mmu->ALTCHARSET && (glyphAttr == A_FLASH)) {
|
||||
if (glyph >= 0x40 && glyph < 0x60) {
|
||||
glyph &= 0x3F;
|
||||
glyphAttr = A_NORMAL;
|
||||
}
|
||||
else if (glyph >= 0x60)
|
||||
glyphAttr = A_INVERSE;
|
||||
}
|
||||
|
||||
if (glyphAttr == A_NORMAL || ((glyphAttr == A_FLASH) && (gui->frameNumber % 30 < 16))) {
|
||||
for (int yy=0; yy<8; yy++)
|
||||
for (int xx=0; xx<7; xx++)
|
||||
screenPixels[(yy+line*8)*280 + (xx+(col*7))] = fontNormal[glyph][yy][xx];
|
||||
}
|
||||
else {
|
||||
for (int yy=0; yy<8; yy++)
|
||||
for (int xx=0; xx<7; xx++)
|
||||
screenPixels[(yy+line*8)*280 + (xx+(col*7))] = fontInverse[glyph][yy][xx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TEXT 80 COLUMNS
|
||||
else if (mmu->COL80 && (mmu->TEXT || mmu->MIXED)) {
|
||||
gfxmode = 80;
|
||||
int startRaw = mmu->TEXT ? 0 : 20;
|
||||
int vRamBase = 0x0400; // + PAGE2 * 0x400;
|
||||
|
||||
for (int col = 0; col < 40; col++) { // for each column
|
||||
for (int line = startRaw; line < 24; line++) { // for each row
|
||||
glyph = mmu->ram[vRamBase + offsetGR[line] + col]; // read video memory in Main memory
|
||||
|
||||
if (glyph > 0x7F) glyphAttr = A_NORMAL; // is NORMAL ?
|
||||
else if (glyph < 0x40) glyphAttr = A_INVERSE; // is INVERSE ?
|
||||
else glyphAttr = A_FLASH; // it's FLASH !
|
||||
|
||||
if (previousChars[line][col] != (glyph <<(8*mmu->ALTCHARSET)) || glyphAttr == A_FLASH) { // check if this char need a redraw
|
||||
previousChars[line][col] = (glyph <<(8*mmu->ALTCHARSET));
|
||||
|
||||
glyph &= 0x7F; // unset bit 7
|
||||
if (glyph < 0x20) glyph |= 0x40; // shifts to match the ASCII codes
|
||||
|
||||
if ((!mmu->ALTCHARSET) && (glyphAttr == A_FLASH) && (glyph > 0x5F))
|
||||
glyph &= 0x3F;
|
||||
|
||||
if (mmu->ALTCHARSET && (glyphAttr == A_FLASH)) {
|
||||
if (glyph >= 0x40 && glyph < 0x60) {
|
||||
glyph &= 0x3F;
|
||||
glyphAttr = A_NORMAL;
|
||||
}
|
||||
else if (glyph >= 0x60)
|
||||
glyphAttr = A_INVERSE;
|
||||
}
|
||||
|
||||
if (glyphAttr == A_NORMAL || ((glyphAttr == A_FLASH) && (gui->frameNumber % 30 < 16))) {
|
||||
for (int yy=0; yy<16; yy+=2)
|
||||
for (int xx=0; xx<7; xx++){
|
||||
screenPixels80[(yy+line*16)*560 + (xx+(((col*2)+1)*7))] = fontNormal[glyph][yy/2][xx];
|
||||
screenPixels80[(1+yy+line*16)*560 + (xx+(((col*2)+1)*7))] = fontNormal[glyph][yy/2][xx];
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int yy=0; yy<16; yy+=2)
|
||||
for (int xx=0; xx<7; xx++){
|
||||
screenPixels80[(yy+line*16)*560 + (xx+(((col*2)+1)*7))] = fontInverse[glyph][yy/2][xx];
|
||||
screenPixels80[(1+yy+line*16)*560 + (xx+(((col*2)+1)*7))] = fontInverse[glyph][yy/2][xx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glyph = mmu->aux[vRamBase + offsetGR[line] + col]; // read video memory in Auxiliary Memory
|
||||
|
||||
if (glyph > 0x7F) glyphAttr = A_NORMAL; // is NORMAL ?
|
||||
else if (glyph < 0x40) glyphAttr = A_INVERSE; // is INVERSE ?
|
||||
else glyphAttr = A_FLASH; // it's FLASH !
|
||||
|
||||
if (previousChars80[line][col] != (glyph <<(8*mmu->ALTCHARSET)) || glyphAttr == A_FLASH) { // check if this char need a redraw
|
||||
previousChars80[line][col] = (glyph <<(8*mmu->ALTCHARSET));
|
||||
|
||||
glyph &= 0x7F; // unset bit 7
|
||||
if (glyph < 0x20) glyph |= 0x40; // shifts to match the ASCII codes
|
||||
|
||||
if ((!mmu->ALTCHARSET) && (glyphAttr == A_FLASH) && (glyph > 0x5F))
|
||||
glyph &= 0x3F;
|
||||
|
||||
if (mmu->ALTCHARSET && (glyphAttr == A_FLASH)) {
|
||||
if (glyph >= 0x40 && glyph < 0x60) {
|
||||
glyph &= 0x3F;
|
||||
glyphAttr = A_NORMAL;
|
||||
}
|
||||
else if (glyph >= 0x60)
|
||||
glyphAttr = A_INVERSE;
|
||||
}
|
||||
|
||||
if (glyphAttr == A_NORMAL || ((glyphAttr == A_FLASH) && (gui->frameNumber % 30 < 16))) {
|
||||
for (int yy=0; yy<16; yy+=2)
|
||||
for (int xx=0; xx<7; xx++) {
|
||||
screenPixels80[(yy+line*16)*560 + (xx+(col*2*7))] = fontNormal[glyph][yy/2][xx];
|
||||
screenPixels80[(1+yy+line*16)*560 + (xx+(col*2*7))] = fontNormal[glyph][yy/2][xx];
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int yy=0; yy<16; yy+=2)
|
||||
for (int xx=0; xx<7; xx++) {
|
||||
screenPixels80[(yy+line*16)*560 + (xx+(col*2*7))] = fontInverse[glyph][yy/2][xx];
|
||||
screenPixels80[(1+yy+line*16)*560 + (xx+(col*2*7))] = fontInverse[glyph][yy/2][xx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update ram & aux HEATMAPS (fade out green and red components)
|
||||
for (int address=0; address<65536; address++){
|
||||
if (ramHeatmap[address] & 0x000000FF)
|
||||
ramHeatmap[address]--;
|
||||
if (ramHeatmap[address] & 0x0000FF00)
|
||||
ramHeatmap[address] -= 256;
|
||||
|
||||
if (auxHeatmap[address] & 0x000000FF)
|
||||
auxHeatmap[address]--;
|
||||
if (auxHeatmap[address] & 0x0000FF00)
|
||||
auxHeatmap[address] -= 256;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* reinette, a french Apple II emulator, using SDL2
|
||||
* and powered by puce65c02 - a WDS 65c02 cpu emulator by the same author
|
||||
* Last modified 1st of July 2021
|
||||
* Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _VIDEO_H
|
||||
#define _VIDEO_H
|
||||
|
||||
class Video {
|
||||
public:
|
||||
int gfxmode;
|
||||
uint8_t glyph; // a TEXT character, or 2 blocks in GR
|
||||
enum characterAttribute { A_NORMAL, A_INVERSE, A_FLASH } glyphAttr; // character attribute in TEXT
|
||||
uint8_t previousBit[192][40] = {0}; // the last bit value of the byte before.
|
||||
|
||||
int previousDots[192][40] = {0}; // check which Hi-Res 7 dots needs redraw
|
||||
int previousBlocks[24][40] = {0}; // check which Lo-Res blocks or text chars needs redraw
|
||||
int previousChars[24][40] = {0}; // check which Lo-Res blocks or text chars needs redraw
|
||||
int previousChars80[24][40] = {0}; // check which Lo-Res blocks or text chars needs redraw
|
||||
|
||||
uint32_t fontNormal[128][8][7]; // normal font
|
||||
uint32_t fontInverse[128][8][7]; // reversed font
|
||||
|
||||
uint32_t screenPixels[280*192] = {0xFF000000};
|
||||
uint32_t screenPixels80[560*384] = {0xFF000000};
|
||||
uint32_t ramHeatmap[256*256] = {0xFF000000};
|
||||
uint32_t auxHeatmap[256*256] = {0xFF000000};
|
||||
|
||||
Video();
|
||||
~Video();
|
||||
|
||||
void update();
|
||||
void clearCache();
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue