initial commit

first public release
This commit is contained in:
ArthurFerreira2 2021-07-07 17:00:47 +02:00 committed by GitHub
parent af2334c245
commit 4c8b396e53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 6475 additions and 3 deletions

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2021 ArthurFerreira2 Copyright (c) 2020 Arthur Ferreira
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

104
Makefile Normal file
View File

@ -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)

View File

@ -1,2 +1,14 @@
# reinetteIIe WORK IN PROGRESS
Reinette IIe, an enhanced Apple //e emulator
# reinette IIe
### reinette gets enhanced !
![screenshots](animated.gif)
\
\
\
*simplicity is the ultimate sophistication*

BIN
animated.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

129
disk.cpp Normal file
View File

@ -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
}

57
disk.h Normal file
View File

@ -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

748
gui.cpp Normal file
View File

@ -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();
}

91
gui.h Normal file
View File

@ -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

95
imgui.ini Normal file
View File

@ -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

54
imguiTheme.h Normal file
View File

@ -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;

83
main.cpp Normal file
View File

@ -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
}

620
mmu.cpp Normal file
View File

@ -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
}

135
mmu.h Normal file
View File

@ -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

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

1
nib/Lode Runner (Changable Params).nib generated Normal file

File diff suppressed because one or more lines are too long

1
nib/Ms. Pac-Man.nib generated Normal file

File diff suppressed because one or more lines are too long

1
nib/demo/appleii-megademo.nib generated Normal file

File diff suppressed because one or more lines are too long

1
nib/demo/fakepal128.nib generated Normal file

File diff suppressed because one or more lines are too long

1
nib/demo/oldskool.nib generated Normal file

File diff suppressed because one or more lines are too long

1
nib/demo/outline2021.nib generated Normal file

File diff suppressed because one or more lines are too long

1
nib/demo/sierzoom128.nib generated Normal file

File diff suppressed because one or more lines are too long

1
nib/diag/A2eDiagnostics_v2.1.nib generated Normal file

File diff suppressed because one or more lines are too long

1
nib/dolores/DLAMPCORE11.1.nib generated Normal file

File diff suppressed because one or more lines are too long

1
nib/dolores/DOLORES12_0_beta.nib generated Normal file

File diff suppressed because one or more lines are too long

1
nib/dolores/LODELO33.nib generated Normal file

File diff suppressed because one or more lines are too long

1
nib/dos/DOS3.3 Blank.nib generated Normal file

File diff suppressed because one or more lines are too long

1
nib/dos/PRODOS-8 v4.0.2 System.nib generated Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

69
paddles.cpp Normal file
View File

@ -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;
}
}
}

53
paddles.h Normal file
View File

@ -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

2639
puce65c02.cpp Normal file

File diff suppressed because it is too large Load Diff

90
puce65c02.h Normal file
View File

@ -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

56
reinette.h Normal file
View File

@ -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

BIN
reinette.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

24
reinette.rc Normal file
View File

@ -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

BIN
rom/appleII+.rom Normal file

Binary file not shown.

BIN
rom/appleII.rom Normal file

Binary file not shown.

BIN
rom/appleIIe.rom Normal file

Binary file not shown.

BIN
rom/appleIIee.rom Normal file

Binary file not shown.

BIN
rom/diskII.rom Normal file

Binary file not shown.

655
scratch.cpp Normal file
View File

@ -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) |
----------------------------------------------------------------
===
===
===
===

79
speaker.cpp Normal file
View File

@ -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
}
}

45
speaker.h Normal file
View File

@ -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

561
video.cpp Normal file
View File

@ -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;
}
}

56
video.h Normal file
View File

@ -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