mirror of
https://github.com/ArthurFerreira2/reinette-II-plus.git
synced 2024-12-27 13:29:16 +00:00
stable version
This commit is contained in:
parent
5596f245fb
commit
8a6f8af6e8
2
Makefile
2
Makefile
@ -1,5 +1,5 @@
|
|||||||
CC = gcc
|
CC = gcc
|
||||||
FLAGS = -std=c99 -pedantic -Wpedantic -Wall -O3
|
FLAGS = -std=c11 -pedantic -Wpedantic -Wall -O3
|
||||||
|
|
||||||
LIBS = -lSDL2
|
LIBS = -lSDL2
|
||||||
# comment these two lines if you are under Linux :
|
# comment these two lines if you are under Linux :
|
||||||
|
@ -24,7 +24,7 @@ After [reinette](https://github.com/ArthurFerreira2/reinette) (Apple 1 emulator)
|
|||||||
* easy screenshot
|
* easy screenshot
|
||||||
|
|
||||||
|
|
||||||
It uses the same MOS 6502 CPU emulator as her sisters (now christened [puce6502](https://github.com/ArthurFerreira2/puce6502)).\
|
It uses an optimized and accurate MOS 6502 CPU emulator (now christened [puce6502](https://github.com/ArthurFerreira2/puce6502)).\
|
||||||
You only need SDL2 to compile it. (I'm not using SDL_Mixer, but only the native SDL2 audio functions)
|
You only need SDL2 to compile it. (I'm not using SDL_Mixer, but only the native SDL2 audio functions)
|
||||||
|
|
||||||
This emulator is not accurate in many ways and does not compete with
|
This emulator is not accurate in many ways and does not compete with
|
||||||
@ -66,9 +66,7 @@ Use the functions keys to control the emulator itself :
|
|||||||
* F7 : reset the zoom to 2:1
|
* F7 : reset the zoom to 2:1
|
||||||
* shift F7 : increase zoom up to 8:1 max
|
* shift F7 : increase zoom up to 8:1 max
|
||||||
* ctrl F7 : decrease zoom down to 1:1 pixels
|
* ctrl F7 : decrease zoom down to 1:1 pixels
|
||||||
* F8 : monochrome / color display (only in HGR mode)
|
* F10 : pause / un-pause the emulator
|
||||||
* F9 : pause / un-pause the emulator
|
|
||||||
* F10 : break
|
|
||||||
* F11 : reset
|
* F11 : reset
|
||||||
* F12 : about, help
|
* F12 : about, help
|
||||||
|
|
||||||
|
2522
puce6502.c
2522
puce6502.c
File diff suppressed because it is too large
Load Diff
33
puce6502.h
33
puce6502.h
@ -1,10 +1,9 @@
|
|||||||
/*
|
/*
|
||||||
|
|
||||||
Puce6502 - MOS 6502 cpu emulator
|
Puce6502 - MOS 6502 cpu emulator
|
||||||
Last modified 1st of August 2020
|
Last modified 1st of August 2020
|
||||||
Copyright (c) 2018 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
Copyright (c) 2018 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||||
|
|
||||||
This version has been modified for reinette II plus, a french Apple II plus
|
This version has been modified for Reinette II plus, a french Apple II plus
|
||||||
emulator using SDL2 (https://github.com/ArthurFerreira2/reinette-II-plus).
|
emulator using SDL2 (https://github.com/ArthurFerreira2/reinette-II-plus).
|
||||||
|
|
||||||
Please download the latest version from
|
Please download the latest version from
|
||||||
@ -27,9 +26,9 @@
|
|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#ifndef _PUCE6502_H
|
#ifndef _PUCE6502_H
|
||||||
#define _PUCE6502_H
|
#define _PUCE6502_H
|
||||||
|
|
||||||
@ -37,26 +36,16 @@ typedef unsigned char uint8_t;
|
|||||||
typedef unsigned short uint16_t;
|
typedef unsigned short uint16_t;
|
||||||
typedef enum { false, true } bool;
|
typedef enum { false, true } bool;
|
||||||
|
|
||||||
#define RAMSIZE 0xC000
|
extern unsigned long long int ticks;
|
||||||
#define ROMSTART 0xD000
|
|
||||||
#define ROMSIZE 0x3000
|
|
||||||
#define LGCSTART 0xD000
|
|
||||||
#define LGCSIZE 0x3000
|
|
||||||
#define BK2START 0xD000
|
|
||||||
#define BK2SIZE 0x1000
|
|
||||||
#define SL6START 0xC600
|
|
||||||
#define SL6SIZE 0x0100
|
|
||||||
|
|
||||||
uint8_t ram[RAMSIZE]; // 48K of ram in $000-$BFFF
|
uint16_t puce6502Exec(unsigned long long int cycleCount);
|
||||||
uint8_t rom[ROMSIZE]; // 12K of rom in $D000-$FFFF
|
void puce6502RST();
|
||||||
uint8_t lgc[LGCSIZE]; // Language Card 12K in $D000-$FFFF
|
void puce6502IRQ();
|
||||||
uint8_t bk2[BK2SIZE]; // bank 2 of Language Card 4K in $D000-$DFFF
|
void puce6502NMI();
|
||||||
uint8_t sl6[SL6SIZE]; // P5A disk ][ PROM in slot 6
|
|
||||||
|
|
||||||
long long int ticks;
|
// void printRegs();
|
||||||
|
// void dasm(uint16_t address);
|
||||||
void puce6502Exec(long long int cycleCount);
|
// void setPC(uint16_t address);
|
||||||
void puce6502Reset();
|
// uint16_t getPC();
|
||||||
void puce6502Break();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
560
reinetteII+.c
560
reinetteII+.c
@ -1,53 +1,77 @@
|
|||||||
/*
|
/*
|
||||||
reinette II plus, a french Apple II emulator, using SDL2
|
* Reinette II plus, a french Apple II emulator, using SDL2
|
||||||
and powered by puce6502 - a MOS 6502 cpu emulator by the same author
|
* and powered by puce6502 - a MOS 6502 cpu emulator by the same author
|
||||||
Last modified 5th of September 2020
|
* Last modified 21st of June 2021
|
||||||
Copyright (c) 2020 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
* Copyright (c) 2020 Arthur Ferreira (arthur.ferreira2@gmail.com)
|
||||||
|
*
|
||||||
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
|
||||||
in the Software without restriction, including without limitation the rights
|
* in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
The above copyright notice and this permission notice shall be included in
|
* The above copyright notice and this permission notice shall be included in
|
||||||
all copies or substantial portions of the Software.
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
* 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
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include "puce6502.h"
|
#include "puce6502.h"
|
||||||
|
|
||||||
|
|
||||||
|
// memory layout
|
||||||
|
#define RAMSIZE 0xC000
|
||||||
|
#define ROMSTART 0xD000
|
||||||
|
#define ROMSIZE 0x3000
|
||||||
|
uint8_t ram[RAMSIZE]; // 48K of ram in $000-$BFFF
|
||||||
|
uint8_t rom[ROMSIZE]; // 12K of rom in $D000-$FFFF
|
||||||
|
|
||||||
|
// language card
|
||||||
|
#define LGCSTART 0xD000
|
||||||
|
#define LGCSIZE 0x3000
|
||||||
|
#define BK2START 0xD000
|
||||||
|
#define BK2SIZE 0x1000
|
||||||
|
uint8_t lgc[LGCSIZE]; // Language Card 12K in $D000-$FFFF
|
||||||
|
uint8_t bk2[BK2SIZE]; // bank 2 of Language Card 4K in $D000-$DFFF
|
||||||
|
|
||||||
|
// disk ][ prom
|
||||||
|
#define SL6START 0xC600
|
||||||
|
#define SL6SIZE 0x0100
|
||||||
|
uint8_t sl6[SL6SIZE]; // P5A disk ][ prom in slot 6
|
||||||
|
|
||||||
|
|
||||||
//================================================================ SOFT SWITCHES
|
//================================================================ SOFT SWITCHES
|
||||||
|
|
||||||
uint8_t KBD = 0; // $C000, $C010 ascii value of keyboard input
|
uint8_t KBD = 0; // $C000, $C010 ascii value of keyboard input
|
||||||
bool TEXT = true; // $C050 CLRTEXT / $C051 SETTEXT
|
bool TEXT = true; // $C050 CLRTEXT / $C051 SETTEXT
|
||||||
bool MIXED = false; // $C052 CLRMIXED / $C053 SETMIXED
|
bool MIXED = false; // $C052 CLRMIXED / $C053 SETMIXED
|
||||||
uint8_t PAGE = 1; // $C054 PAGE1 / $C055 PAGE2
|
bool PAGE2 = false; // $C054 PAGE2 off / $C055 PAGE2 on
|
||||||
bool HIRES = false; // $C056 GR / $C057 HGR
|
bool HIRES = false; // $C056 GR / $C057 HGR
|
||||||
bool LCWR = true; // Language Card writable
|
bool LCWR = true; // Language Card writable
|
||||||
bool LCRD = false; // Language Card readable
|
bool LCRD = false; // Language Card readable
|
||||||
bool LCBK2 = true; // Language Card bank 2 enabled
|
bool LCBK2 = true; // Language Card bank 2 enabled
|
||||||
bool LCWFF = false; // Language Card pre-write flip flop
|
bool LCWFF = false; // Language Card pre-write flip flop
|
||||||
|
|
||||||
|
|
||||||
//====================================================================== PADDLES
|
//====================================================================== PADDLES
|
||||||
|
|
||||||
uint8_t PB0 = 0; // $C061 Push Button 0 (bit 7) / Open Apple
|
uint8_t PB0 = 0; // $C061 Push Button 0 (bit 7) / Open Apple
|
||||||
uint8_t PB1 = 0; // $C062 Push Button 1 (bit 7) / Solid Apple
|
uint8_t PB1 = 0; // $C062 Push Button 1 (bit 7) / Solid Apple
|
||||||
uint8_t PB2 = 0; // $C063 Push Button 2 (bit 7) / shift mod !!!
|
uint8_t PB2 = 0; // $C063 Push Button 2 (bit 7) / shift mod !!!
|
||||||
float GCP[2] = {127, 127}; // GC Position ranging from 0 (left) to 255 right
|
float GCP[2] = { 127.0f, 127.0f }; // GC Position ranging from 0 (left) to 255 right
|
||||||
float GCC[2] = {0}; // $C064 (GC0) and $C065 (GC1) Countdowns
|
float GCC[2] = { 0.0f }; // $C064 (GC0) and $C065 (GC1) Countdowns
|
||||||
int GCD[2] = { 0 }; // GC0 and GC1 Directions (left/down or right/up)
|
int GCD[2] = { 0 }; // GC0 and GC1 Directions (left/down or right/up)
|
||||||
int GCA[2] = { 0 }; // GC0 and GC1 Action (push or release)
|
int GCA[2] = { 0 }; // GC0 and GC1 Action (push or release)
|
||||||
uint8_t GCActionSpeed = 8; // Game Controller speed at which it goes to the edges
|
uint8_t GCActionSpeed = 8; // Game Controller speed at which it goes to the edges
|
||||||
@ -62,18 +86,20 @@ inline static void resetPaddles(){
|
|||||||
|
|
||||||
inline static uint8_t readPaddle(int pdl) {
|
inline static uint8_t readPaddle(int pdl) {
|
||||||
const float GCFreq = 6.6; // the speed at which the GC values decrease
|
const float GCFreq = 6.6; // the speed at which the GC values decrease
|
||||||
|
|
||||||
GCC[pdl] -= (ticks - GCCrigger) / GCFreq; // decreases the countdown
|
GCC[pdl] -= (ticks - GCCrigger) / GCFreq; // decreases the countdown
|
||||||
if (GCC[pdl] <= 0) // timeout
|
if (GCC[pdl] <= 0) // timeout
|
||||||
return(GCC[pdl] = 0); // returns 0
|
return GCC[pdl] = 0; // returns 0
|
||||||
return(0x80); // not timeout, return something with the MSB set
|
return 0x80; // not timeout, return something with the MSB set
|
||||||
}
|
}
|
||||||
|
|
||||||
//======================================================================== AUDIO
|
|
||||||
|
//====================================================================== SPEAKER
|
||||||
|
|
||||||
#define audioBufferSize 4096 // found to be large enought
|
#define audioBufferSize 4096 // found to be large enought
|
||||||
Sint8 audioBuffer[2][audioBufferSize] = {0}; // see main() for more details
|
Sint8 audioBuffer[2][audioBufferSize] = { 0 }; // see in main() for more details
|
||||||
SDL_AudioDeviceID audioDevice;
|
SDL_AudioDeviceID audioDevice;
|
||||||
bool muted = false; // mute/unmute
|
bool muted = false; // mute/unmute switch
|
||||||
|
|
||||||
static void playSound() {
|
static void playSound() {
|
||||||
static long long int lastTick = 0LL;
|
static long long int lastTick = 0LL;
|
||||||
@ -81,13 +107,14 @@ static void playSound(){
|
|||||||
|
|
||||||
if (!muted) {
|
if (!muted) {
|
||||||
SPKR = !SPKR; // toggle speaker state
|
SPKR = !SPKR; // toggle speaker state
|
||||||
Uint32 length = (ticks - lastTick) / 10.65625; // 1023000Hz / 96000Hz = 10.65625
|
Uint32 length = (int)((double)(ticks - lastTick) / 10.65625f); // 1023000Hz / 96000Hz = 10.65625
|
||||||
lastTick = ticks;
|
lastTick = ticks;
|
||||||
if (length > audioBufferSize) length = audioBufferSize;
|
if (length > audioBufferSize) length = audioBufferSize;
|
||||||
SDL_QueueAudio(audioDevice, audioBuffer[SPKR], length | 1); // | 1 TO HEAR HIGH FREQ SOUNDS
|
SDL_QueueAudio(audioDevice, audioBuffer[SPKR], length | 1); // | 1 TO HEAR HIGH FREQ SOUNDS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//====================================================================== DISK ][
|
//====================================================================== DISK ][
|
||||||
|
|
||||||
int curDrv = 0; // Current Drive - only one can be enabled at a time
|
int curDrv = 0; // Current Drive - only one can be enabled at a time
|
||||||
@ -104,22 +131,24 @@ struct drive{
|
|||||||
|
|
||||||
|
|
||||||
int insertFloppy(SDL_Window *wdo, char *filename, int drv) {
|
int insertFloppy(SDL_Window *wdo, char *filename, int drv) {
|
||||||
|
|
||||||
FILE *f = fopen(filename, "rb"); // open file in read binary mode
|
FILE *f = fopen(filename, "rb"); // open file in read binary mode
|
||||||
if (!f || fread(disk[drv].data, 1, 232960, f) != 232960) // load it into memory and check size
|
if (!f || fread(disk[drv].data, 1, 232960, f) != 232960) // load it into memory and check size
|
||||||
return(0);
|
return 0;
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
sprintf(disk[drv].filename, "%s", filename); // update disk filename record
|
sprintf(disk[drv].filename, "%s", filename); // update disk filename record
|
||||||
|
|
||||||
f = fopen(filename, "ab"); // try to open the file in append binary mode
|
f = fopen(filename, "ab"); // try to open the file in append binary mode
|
||||||
if (!f){ // success, file is writable
|
if (f) { // success, file is writable
|
||||||
disk[drv].readOnly = true; // update the readOnly flag
|
disk[drv].readOnly = false; // update the readOnly flag
|
||||||
fclose(f); // and close it untouched
|
fclose(f); // and close it untouched
|
||||||
|
} else {
|
||||||
|
disk[drv].readOnly = true; // f is NULL, no writable, no need to close it
|
||||||
}
|
}
|
||||||
else disk[drv].readOnly = false; // f is NULL, no writable, no need to close it
|
|
||||||
|
|
||||||
char title[1000]; // UPDATE WINDOW TITLE
|
char title[1000]; // UPDATE WINDOW TITLE
|
||||||
int i, a, b;
|
int i, a, b;
|
||||||
|
|
||||||
i = a = 0;
|
i = a = 0;
|
||||||
while (disk[0].filename[i] != 0) // find start of filename for disk0
|
while (disk[0].filename[i] != 0) // find start of filename for disk0
|
||||||
if (disk[0].filename[i++] == '\\') a = i;
|
if (disk[0].filename[i++] == '\\') a = i;
|
||||||
@ -127,11 +156,10 @@ int insertFloppy(SDL_Window *wdo, char *filename, int drv){
|
|||||||
while (disk[1].filename[i] != 0) // find start of filename for disk1
|
while (disk[1].filename[i] != 0) // find start of filename for disk1
|
||||||
if (disk[1].filename[i++] == '\\') b = i;
|
if (disk[1].filename[i++] == '\\') b = i;
|
||||||
|
|
||||||
sprintf(title, "reinette II+ D1: %s D2: %s", \
|
sprintf(title, "Reinette ][+ D1: %s D2: %s", disk[0].filename + a, disk[1].filename + b);
|
||||||
disk[0].filename + a, disk[1].filename + b);
|
|
||||||
SDL_SetWindowTitle(wdo, title); // updates window title
|
SDL_SetWindowTitle(wdo, title); // updates window title
|
||||||
|
|
||||||
return(1);
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -140,13 +168,14 @@ int saveFloppy(int drive){
|
|||||||
if (disk[drive].readOnly) return 0; // file is read only write no aptempted
|
if (disk[drive].readOnly) return 0; // file is read only write no aptempted
|
||||||
|
|
||||||
FILE *f = fopen(disk[drive].filename, "wb");
|
FILE *f = fopen(disk[drive].filename, "wb");
|
||||||
if (!f) return(0); // could not open the file in write overide binary
|
if (!f) return 0; // could not open the file in write overide binary
|
||||||
|
|
||||||
if (fwrite(disk[drive].data, 1, 232960, f) != 232960) { // failed to write the full file (disk full ?)
|
if (fwrite(disk[drive].data, 1, 232960, f) != 232960) { // failed to write the full file (disk full ?)
|
||||||
fclose(f); // release the ressource
|
fclose(f); // release the ressource
|
||||||
return(0);
|
return 0;
|
||||||
}
|
}
|
||||||
fclose(f); // success, release the ressource
|
fclose(f); // success, release the ressource
|
||||||
return (1);
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -179,25 +208,25 @@ void stepMotor(uint16_t address){
|
|||||||
|
|
||||||
phases[curDrv][phase] = true; // update track#
|
phases[curDrv][phase] = true; // update track#
|
||||||
disk[curDrv].track = (halfTrackPos[curDrv] + 1) / 2;
|
disk[curDrv].track = (halfTrackPos[curDrv] + 1) / 2;
|
||||||
disk[curDrv].nibble = 0; // not sure this is necessary ?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void setDrv(bool drv){
|
inline void setDrv(int drv) {
|
||||||
disk[drv].motorOn = disk[!drv].motorOn || disk[drv].motorOn; // if any of the motors were ON
|
disk[drv].motorOn = disk[!drv].motorOn || disk[drv].motorOn; // if any of the motors were ON
|
||||||
disk[!drv].motorOn = false; // motor of the other drive is set to OFF
|
disk[!drv].motorOn = false; // motor of the other drive is set to OFF
|
||||||
curDrv = drv; // set the current drive
|
curDrv = drv; // set the current drive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//========================================== MEMORY MAPPED SOFT SWITCHES HANDLER
|
//========================================== MEMORY MAPPED SOFT SWITCHES HANDLER
|
||||||
// this function is called from readMem and writeMem in puce6502
|
// this function is called from readMem and writeMem
|
||||||
// it complements both functions when address 1is between 0xC000 and 0xCFFF
|
// it complements both functions when address is in page $C0
|
||||||
uint8_t softSwitches(uint16_t address, uint8_t value, bool WRT) {
|
uint8_t softSwitches(uint16_t address, uint8_t value, bool WRT) {
|
||||||
static uint8_t dLatch = 0; // disk ][ I/O register
|
static uint8_t dLatch = 0; // disk ][ I/O register
|
||||||
|
|
||||||
switch (address) {
|
switch (address) {
|
||||||
case 0xC000: return(KBD); // KEYBOARD
|
case 0xC000: return KBD; // KEYBOARD
|
||||||
case 0xC010: KBD &= 0x7F; return(KBD); // KBDSTROBE
|
case 0xC010: KBD &= 0x7F; return KBD; // KBDSTROBE
|
||||||
|
|
||||||
case 0xC020: // TAPEOUT (shall we listen it ? - try SAVE from applesoft)
|
case 0xC020: // TAPEOUT (shall we listen it ? - try SAVE from applesoft)
|
||||||
case 0xC030: // SPEAKER
|
case 0xC030: // SPEAKER
|
||||||
@ -207,46 +236,19 @@ uint8_t softSwitches(uint16_t address, uint8_t value, bool WRT){
|
|||||||
case 0xC051: TEXT = true; break; // Text
|
case 0xC051: TEXT = true; break; // Text
|
||||||
case 0xC052: MIXED = false; break; // Mixed off
|
case 0xC052: MIXED = false; break; // Mixed off
|
||||||
case 0xC053: MIXED = true; break; // Mixed on
|
case 0xC053: MIXED = true; break; // Mixed on
|
||||||
case 0xC054: PAGE = 1; break; // Page 1
|
case 0xC054: PAGE2 = false; break; // PAGE2 off
|
||||||
case 0xC055: PAGE = 2; break; // Page 2
|
case 0xC055: PAGE2 = true; break; // PAGE2 on
|
||||||
case 0xC056: HIRES = false; break; // HiRes off
|
case 0xC056: HIRES = false; break; // HiRes off
|
||||||
case 0xC057: HIRES = true; break; // HiRes on
|
case 0xC057: HIRES = true; break; // HiRes on
|
||||||
|
|
||||||
case 0xC061: return(PB0); // Push Button 0
|
case 0xC061: return PB0; // Push Button 0
|
||||||
case 0xC062: return(PB1); // Push Button 1
|
case 0xC062: return PB1; // Push Button 1
|
||||||
case 0xC063: return(PB2); // Push Button 2
|
case 0xC063: return PB2; // Push Button 2
|
||||||
case 0xC064: return(readPaddle(0)); // Paddle 0
|
case 0xC064: return readPaddle(0); // Paddle 0
|
||||||
case 0xC065: return(readPaddle(1)); // Paddle 1
|
case 0xC065: return readPaddle(1); // Paddle 1
|
||||||
case 0xC066: return(readPaddle(0)); // Paddle 2 -- not implemented
|
|
||||||
case 0xC067: return(readPaddle(1)); // Paddle 3 -- not implemented
|
|
||||||
|
|
||||||
case 0xC070: resetPaddles(); break; // paddle timer RST
|
case 0xC070: resetPaddles(); break; // paddle timer RST
|
||||||
|
|
||||||
case 0xC0E0 ... 0xC0E7: stepMotor(address); break; // MOVE DRIVE HEAD
|
|
||||||
|
|
||||||
case 0xCFFF:
|
|
||||||
case 0xC0E8: disk[curDrv].motorOn = false; break; // MOTOROFF
|
|
||||||
case 0xC0E9: disk[curDrv].motorOn = true; break; // MOTORON
|
|
||||||
|
|
||||||
case 0xC0EA: setDrv(0); break; // DRIVE0EN
|
|
||||||
case 0xC0EB: setDrv(1); break; // DRIVE1EN
|
|
||||||
|
|
||||||
case 0xC0EC: // Shift Data Latch
|
|
||||||
if (disk[curDrv].writeMode) // writting
|
|
||||||
disk[curDrv].data[disk[curDrv].track*0x1A00+disk[curDrv].nibble]=dLatch;
|
|
||||||
else // reading
|
|
||||||
dLatch=disk[curDrv].data[disk[curDrv].track*0x1A00+disk[curDrv].nibble];
|
|
||||||
disk[curDrv].nibble = (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[curDrv].writeMode = false;
|
|
||||||
return(disk[curDrv].readOnly ? 0x80 : 0); // check protection
|
|
||||||
|
|
||||||
case 0xC0EF: disk[curDrv].writeMode = true; break; // latch for WRITE
|
|
||||||
|
|
||||||
case 0xC080: // LANGUAGE CARD :
|
case 0xC080: // LANGUAGE CARD :
|
||||||
case 0xC084: LCBK2 = 1; LCRD = 1; LCWR = 0; LCWFF = 0; break; // LC2RD
|
case 0xC084: LCBK2 = 1; LCRD = 1; LCWR = 0; LCWFF = 0; break; // LC2RD
|
||||||
case 0xC081:
|
case 0xC081:
|
||||||
@ -263,8 +265,89 @@ uint8_t softSwitches(uint16_t address, uint8_t value, bool WRT){
|
|||||||
case 0xC08E: LCBK2 = 0; LCRD = 0; LCWR = 0; LCWFF = 0; break; // ROMONLY1
|
case 0xC08E: LCBK2 = 0; LCRD = 0; LCWR = 0; LCWFF = 0; break; // ROMONLY1
|
||||||
case 0xC08B:
|
case 0xC08B:
|
||||||
case 0xC08F: LCBK2 = 0; LCRD = 1; LCWR |= LCWFF; LCWFF = !WRT; break; // LC1RW
|
case 0xC08F: LCBK2 = 0; LCRD = 1; LCWR |= LCWFF; LCWFF = !WRT; break; // LC1RW
|
||||||
|
|
||||||
|
case 0xC0E0:
|
||||||
|
case 0xC0E1:
|
||||||
|
case 0xC0E2:
|
||||||
|
case 0xC0E3:
|
||||||
|
case 0xC0E4:
|
||||||
|
case 0xC0E5:
|
||||||
|
case 0xC0E6:
|
||||||
|
case 0xC0E7: stepMotor(address); break; // MOVE DRIVE HEAD
|
||||||
|
|
||||||
|
case 0xCFFF:
|
||||||
|
case 0xC0E8: disk[curDrv].motorOn = false; break; // MOTOROFF
|
||||||
|
case 0xC0E9: disk[curDrv].motorOn = true; break; // MOTORON
|
||||||
|
|
||||||
|
case 0xC0EA: setDrv(0); break; // DRIVE0EN
|
||||||
|
case 0xC0EB: setDrv(1); break; // DRIVE1EN
|
||||||
|
|
||||||
|
case 0xC0EC: // Shift Data Latch
|
||||||
|
if (disk[curDrv].writeMode) // writting
|
||||||
|
disk[curDrv].data[disk[curDrv].track*0x1A00+disk[curDrv].nibble]=dLatch;// good luck gcc
|
||||||
|
else // reading
|
||||||
|
dLatch=disk[curDrv].data[disk[curDrv].track*0x1A00+disk[curDrv].nibble];// easy peasy
|
||||||
|
disk[curDrv].nibble = (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[curDrv].writeMode = false;
|
||||||
|
return disk[curDrv].readOnly ? 0x80 : 0; // check protection
|
||||||
|
|
||||||
|
case 0xC0EF: disk[curDrv].writeMode = true; break; // latch for WRITE
|
||||||
|
}
|
||||||
|
return ticks % 0xFF; // catch all, gives a 'floating' value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//======================================================================= MEMORY
|
||||||
|
// these two functions are imported into puce6502.c
|
||||||
|
|
||||||
|
uint8_t readMem(uint16_t address) {
|
||||||
|
if (address < RAMSIZE)
|
||||||
|
return ram[address]; // RAM
|
||||||
|
|
||||||
|
if (address >= ROMSTART) {
|
||||||
|
if (!LCRD)
|
||||||
|
return rom[address - ROMSTART]; // ROM
|
||||||
|
|
||||||
|
if (LCBK2 && (address < 0xE000))
|
||||||
|
return bk2[address - BK2START]; // BK2
|
||||||
|
|
||||||
|
return lgc[address - LGCSTART]; // LC
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((address & 0xFF00) == SL6START)
|
||||||
|
return sl6[address - SL6START]; // disk][
|
||||||
|
|
||||||
|
if ((address & 0xF000) == 0xC000)
|
||||||
|
return softSwitches(address, 0, false); // Soft Switches
|
||||||
|
|
||||||
|
return ticks & 0xFF; // catch all, gives a 'floating' value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void writeMem(uint16_t address, uint8_t value) {
|
||||||
|
if (address < RAMSIZE) {
|
||||||
|
ram[address] = value; // RAM
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LCWR && (address >= ROMSTART)) {
|
||||||
|
if (LCBK2 && (address < 0xE000)) {
|
||||||
|
bk2[address - BK2START] = value; // BK2
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lgc[address - LGCSTART] = value; // LC
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((address & 0xF000) == 0xC000) {
|
||||||
|
softSwitches(address, value, true); // Soft Switches
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return(ticks%256); // catch all
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -272,71 +355,49 @@ uint8_t softSwitches(uint16_t address, uint8_t value, bool WRT){
|
|||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
char workDir[1000]; // find the working directory
|
|
||||||
int workDirSize = 0, i = 0;
|
|
||||||
while (argv[0][i] != '\0'){
|
|
||||||
workDir[i] = argv[0][i];
|
|
||||||
if (argv[0][++i] == '\\') workDirSize = i + 1; // find the last '/' if any
|
|
||||||
}
|
|
||||||
|
|
||||||
// SDL INITIALIZATION
|
//========================================================= SDL INITIALIZATION
|
||||||
|
|
||||||
double fps = 60, frameTime = 0, frameDelay = 1000.0 / 60.0; // targeting 60 FPS
|
|
||||||
Uint32 frameStart = 0, frame = 0;
|
|
||||||
int zoom = 2;
|
int zoom = 2;
|
||||||
uint8_t tries = 0; // disk ][ speed-up
|
uint8_t tries = 0; // for disk ][ speed-up access
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
SDL_bool paused = false, running = true, ctrl, shift, alt;
|
SDL_bool running = true, paused = false, ctrl = false, shift = false, alt = false;
|
||||||
|
|
||||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
|
||||||
printf("failed to initialize SDL2 : %s", SDL_GetError());
|
printf("failed to initialize SDL2 : %s", SDL_GetError());
|
||||||
return(-1);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_EventState(SDL_DROPFILE, SDL_ENABLE); // ask SDL2 to read dropfile events
|
SDL_Window *wdo = SDL_CreateWindow("Reinette ][+", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 280 * zoom, 192 * zoom, SDL_WINDOW_OPENGL);
|
||||||
|
SDL_Renderer *rdr = SDL_CreateRenderer(wdo, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||||
SDL_Window *wdo = SDL_CreateWindow("reinette II+", SDL_WINDOWPOS_CENTERED, \
|
|
||||||
SDL_WINDOWPOS_CENTERED, 280*zoom, 192*zoom, SDL_WINDOW_OPENGL);
|
|
||||||
SDL_Surface *sshot; // used later for the screenshots
|
|
||||||
SDL_Renderer *rdr = SDL_CreateRenderer(wdo, -1, SDL_RENDERER_ACCELERATED); // | SDL_RENDERER_PRESENTVSYNC);
|
|
||||||
SDL_SetRenderDrawBlendMode(rdr, SDL_BLENDMODE_NONE); // SDL_BLENDMODE_BLEND);
|
SDL_SetRenderDrawBlendMode(rdr, SDL_BLENDMODE_NONE); // SDL_BLENDMODE_BLEND);
|
||||||
|
SDL_EventState(SDL_DROPFILE, SDL_ENABLE); // ask SDL2 to read dropfile events
|
||||||
SDL_RenderSetScale(rdr, zoom, zoom);
|
SDL_RenderSetScale(rdr, zoom, zoom);
|
||||||
|
SDL_Surface *sshot; // used later for the screenshots
|
||||||
|
|
||||||
// SDL AUDIO INITIALIZATION
|
|
||||||
|
//=================================================== SDL AUDIO INITIALIZATION
|
||||||
|
|
||||||
SDL_AudioSpec desired = { 96000, AUDIO_S8, 1, 0, 4096, 0, 0, NULL, NULL };
|
SDL_AudioSpec desired = { 96000, AUDIO_S8, 1, 0, 4096, 0, 0, NULL, NULL };
|
||||||
audioDevice = SDL_OpenAudioDevice(NULL, 0, &desired, NULL, SDL_FALSE); // get the audio device ID
|
audioDevice = SDL_OpenAudioDevice(NULL, 0, &desired, NULL, SDL_FALSE); // get the audio device ID
|
||||||
SDL_PauseAudioDevice(audioDevice, muted); // unmute it (muted is false)
|
SDL_PauseAudioDevice(audioDevice, muted); // unmute it (muted is false)
|
||||||
uint8_t volume = 2;
|
uint8_t volume = 4;
|
||||||
|
|
||||||
for (int i=1; i<audioBufferSize; i++){ // two audio buffers,
|
for (int i = 0; i < audioBufferSize; i++) { // two audio buffers,
|
||||||
audioBuffer[true][i] = volume; // one used when SPKR is true
|
audioBuffer[true][i] = volume; // one used when SPKR is true
|
||||||
audioBuffer[false][i] = -volume; // the other when SPKR is false
|
audioBuffer[false][i] = -volume; // the other when SPKR is false
|
||||||
}
|
}
|
||||||
|
|
||||||
// LOAD NORMAL AND REVERSE CHARACTERS BITMAPS
|
|
||||||
|
|
||||||
SDL_Surface *tmpSurface;
|
//===================================== VARIABLES USED IN THE VIDEO PRODUCTION
|
||||||
workDir[workDirSize] = '\0';
|
|
||||||
tmpSurface = SDL_LoadBMP(strncat(workDir, "assets/font-normal.bmp", 23)); // load the normal font
|
|
||||||
SDL_Texture *normCharTexture = SDL_CreateTextureFromSurface(rdr, tmpSurface);
|
|
||||||
SDL_FreeSurface(tmpSurface);
|
|
||||||
|
|
||||||
workDir[workDirSize] = '\0';
|
int TextCache[24][40] = { 0 };
|
||||||
tmpSurface = SDL_LoadBMP(strncat(workDir, "assets/font-reverse.bmp", 24)); // load the reverse font
|
int LoResCache[24][40] = { 0 };
|
||||||
SDL_Texture *revCharTexture = SDL_CreateTextureFromSurface(rdr, tmpSurface);
|
int HiResCache[192][40] = { 0 }; // check which Hi-Res 7 dots needs redraw
|
||||||
SDL_FreeSurface(tmpSurface);
|
|
||||||
|
|
||||||
// VARIABLES USED IN THE VIDEO PRODUCTION
|
|
||||||
|
|
||||||
uint16_t vRamBase = 0x0400; // can be $400, $800, $2000 or $4000
|
|
||||||
uint16_t previousDots[192][40] = {0}; // check which Hi-Res 7 dots needs redraw
|
|
||||||
uint8_t previousBit[192][40] = { 0 }; // the last bit value of the byte before.
|
uint8_t previousBit[192][40] = { 0 }; // the last bit value of the byte before.
|
||||||
uint8_t lineLimit; // the line limit between GR and TEXT
|
|
||||||
uint8_t glyph; // a TEXT character, or 2 blocks in GR
|
|
||||||
uint8_t colorIdx = 0; // to index the color arrays
|
|
||||||
bool monochrome = false; // starting in color
|
|
||||||
enum characterAttribute { A_NORMAL, A_INVERSE, A_FLASH } glyphAttr; // character attribute in TEXT
|
enum characterAttribute { A_NORMAL, A_INVERSE, A_FLASH } glyphAttr; // character attribute in TEXT
|
||||||
|
uint8_t flashCycle = 0; // TEXT cursor flashes at 2Hz
|
||||||
|
|
||||||
SDL_Rect drvRect[2] = { { 272, 188, 4, 4 }, { 276, 188, 4, 4 } }; // disk drive status squares
|
SDL_Rect drvRect[2] = { { 272, 188, 4, 4 }, { 276, 188, 4, 4 } }; // disk drive status squares
|
||||||
SDL_Rect pixelGR = { 0, 0, 7, 4 }; // a block in LoRes
|
SDL_Rect pixelGR = { 0, 0, 7, 4 }; // a block in LoRes
|
||||||
@ -353,18 +414,21 @@ int main(int argc, char *argv[]){
|
|||||||
{ 0, 0, 0 }, { 226, 57, 86 }, { 28, 116, 205 }, { 126, 110, 173 },
|
{ 0, 0, 0 }, { 226, 57, 86 }, { 28, 116, 205 }, { 126, 110, 173 },
|
||||||
{ 31, 129, 128 }, { 137, 130, 122 }, { 86, 168, 228 }, { 144, 178, 223 },
|
{ 31, 129, 128 }, { 137, 130, 122 }, { 86, 168, 228 }, { 144, 178, 223 },
|
||||||
{ 151, 88, 34 }, { 234, 108, 21 }, { 158, 151, 143 }, { 255, 206, 240 },
|
{ 151, 88, 34 }, { 234, 108, 21 }, { 158, 151, 143 }, { 255, 206, 240 },
|
||||||
{144, 192, 49}, {255, 253, 166}, {159, 210,213}, {255, 255, 255}};
|
{ 144, 192, 49 }, { 255, 253, 166 }, { 159, 210, 213 }, { 255, 255, 255 }
|
||||||
|
};
|
||||||
|
|
||||||
const int hcolor[16][3] = { // the high res colors (2 light levels)
|
const int hcolor[16][3] = { // the high res colors (2 light levels)
|
||||||
{ 0, 0, 0 }, { 144, 192, 49 }, { 126, 110, 173 }, { 255, 255, 255 },
|
{ 0, 0, 0 }, { 144, 192, 49 }, { 126, 110, 173 }, { 255, 255, 255 },
|
||||||
{ 0, 0, 0 }, { 234, 108, 21 }, { 86, 168, 228 }, { 255, 255, 255 },
|
{ 0, 0, 0 }, { 234, 108, 21 }, { 86, 168, 228 }, { 255, 255, 255 },
|
||||||
{ 0, 0, 0 }, { 63, 55, 86 }, { 72, 96, 25 }, { 255, 255, 255 },
|
{ 0, 0, 0 }, { 63, 55, 86 }, { 72, 96, 25 }, { 255, 255, 255 },
|
||||||
{ 0, 0, 0}, { 43, 84, 114}, {117, 54, 10}, {255, 255, 255}};
|
{ 0, 0, 0 }, { 43, 84, 114 }, { 117, 54, 10 }, { 255, 255, 255 }
|
||||||
|
};
|
||||||
|
|
||||||
const int offsetGR[24] = { // helper for TEXT and GR video generation
|
const int offsetGR[24] = { // helper for TEXT and GR video generation
|
||||||
0x000, 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380, // lines 0-7
|
0x0000, 0x0080, 0x0100, 0x0180, 0x0200, 0x0280, 0x0300, 0x0380, // lines 0-7
|
||||||
0x028, 0x0A8, 0x128, 0x1A8, 0x228, 0x2A8, 0x328, 0x3A8, // lines 8-15
|
0x0028, 0x00A8, 0x0128, 0x01A8, 0x0228, 0x02A8, 0x0328, 0x03A8, // lines 8-15
|
||||||
0x050, 0x0D0, 0x150, 0x1D0, 0x250, 0x2D0, 0x350, 0x3D0}; // lines 16-23
|
0x0050, 0x00D0, 0x0150, 0x01D0, 0x0250, 0x02D0, 0x0350, 0x03D0 // lines 16-23
|
||||||
|
};
|
||||||
|
|
||||||
const int offsetHGR[192] = { // helper for HGR video generation
|
const int offsetHGR[192] = { // helper for HGR video generation
|
||||||
0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400, 0x1800, 0x1C00, // lines 0-7
|
0x0000, 0x0400, 0x0800, 0x0C00, 0x1000, 0x1400, 0x1800, 0x1C00, // lines 0-7
|
||||||
@ -390,19 +454,42 @@ int main(int argc, char *argv[]){
|
|||||||
0x0250, 0x0650, 0x0A50, 0x0E50, 0x1250, 0x1650, 0x1A50, 0x1E50,
|
0x0250, 0x0650, 0x0A50, 0x0E50, 0x1250, 0x1650, 0x1A50, 0x1E50,
|
||||||
0x02D0, 0x06D0, 0x0AD0, 0x0ED0, 0x12D0, 0x16D0, 0x1AD0, 0x1ED0, // lines 168-183
|
0x02D0, 0x06D0, 0x0AD0, 0x0ED0, 0x12D0, 0x16D0, 0x1AD0, 0x1ED0, // lines 168-183
|
||||||
0x0350, 0x0750, 0x0B50, 0x0F50, 0x1350, 0x1750, 0x1B50, 0x1F50, // lines 176-183
|
0x0350, 0x0750, 0x0B50, 0x0F50, 0x1350, 0x1750, 0x1B50, 0x1F50, // lines 176-183
|
||||||
0x03D0, 0x07D0, 0x0BD0, 0x0FD0, 0x13D0, 0x17D0, 0x1BD0, 0x1FD0}; // lines 184-191
|
0x03D0, 0x07D0, 0x0BD0, 0x0FD0, 0x13D0, 0x17D0, 0x1BD0, 0x1FD0 // lines 184-191
|
||||||
|
};
|
||||||
|
|
||||||
// VM INITIALIZATION
|
|
||||||
|
//================================= LOAD NORMAL AND REVERSE CHARACTERS BITMAPS
|
||||||
|
|
||||||
|
char workDir[1000]; // find the working directory
|
||||||
|
int workDirSize = 0, i = 0;
|
||||||
|
while (argv[0][i] != '\0') {
|
||||||
|
workDir[i] = argv[0][i];
|
||||||
|
if (argv[0][++i] == '\\') workDirSize = i + 1; // find the last '/' if any
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Surface *tmpSurface;
|
||||||
|
workDir[workDirSize] = 0;
|
||||||
|
tmpSurface = SDL_LoadBMP(strncat(workDir, "assets/font-normal.bmp", 23)); // load the normal font
|
||||||
|
SDL_Texture *normCharTexture = SDL_CreateTextureFromSurface(rdr, tmpSurface);
|
||||||
|
SDL_FreeSurface(tmpSurface);
|
||||||
|
|
||||||
|
workDir[workDirSize] = 0;
|
||||||
|
tmpSurface = SDL_LoadBMP(strncat(workDir, "assets/font-reverse.bmp", 24)); // load the reverse font
|
||||||
|
SDL_Texture *revCharTexture = SDL_CreateTextureFromSurface(rdr, tmpSurface);
|
||||||
|
SDL_FreeSurface(tmpSurface);
|
||||||
|
|
||||||
|
|
||||||
|
//================================================================== LOAD ROMS
|
||||||
|
|
||||||
workDir[workDirSize] = 0;
|
workDir[workDirSize] = 0;
|
||||||
FILE *f = fopen(strncat(workDir, "rom/appleII+.rom", 17), "rb"); // load the Apple II+ ROM
|
FILE *f = fopen(strncat(workDir, "rom/appleII+.rom", 17), "rb"); // load the Apple II+ ROM
|
||||||
if (!f) {
|
if (!f) {
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error", "Could not locate appleII+.rom in the rom folder", NULL);
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error", "Could not locate appleII+.rom in the rom folder", NULL);
|
||||||
return(1); // exit
|
return 1; // exit
|
||||||
}
|
}
|
||||||
if (fread(rom, 1, ROMSIZE, f) != ROMSIZE) { // the file is too small
|
if (fread(rom, 1, ROMSIZE, f) != ROMSIZE) { // the file is too small
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error", "appleII+.rom should be exactly 12 KB", NULL);
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error", "appleII+.rom should be exactly 12 KB", NULL);
|
||||||
return(1); // exit
|
return 1; // exit
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
@ -410,37 +497,41 @@ int main(int argc, char *argv[]){
|
|||||||
f = fopen(strncat(workDir, "rom/diskII.rom", 15), "rb"); // load the P5A disk ][ PROM
|
f = fopen(strncat(workDir, "rom/diskII.rom", 15), "rb"); // load the P5A disk ][ PROM
|
||||||
if (!f) {
|
if (!f) {
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error", "Could not locate diskII.rom in the rom folder", NULL);
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error", "Could not locate diskII.rom in the rom folder", NULL);
|
||||||
return(1); // exit
|
return 1; // exit
|
||||||
}
|
}
|
||||||
if (fread(sl6, 1, 256, f) != 256) { // file too small
|
if (fread(sl6, 1, 256, f) != 256) { // file too small
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error", "diskII.rom should be exactly 256 bytes", NULL);
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error", "diskII.rom should be exactly 256 bytes", NULL);
|
||||||
return(1); // exit
|
return 1; // exit
|
||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
if (argc > 1) insertFloppy(wdo, argv[1], 0); // load .nib in parameter into drive 0
|
|
||||||
|
|
||||||
for (int i=0; i<RAMSIZE; i++) ram[i] = 0xAA; // Joust and Planetoids won't work if page zero is zeroided
|
//========================================================== VM INITIALIZATION
|
||||||
|
|
||||||
puce6502Reset(); // reset the 6502
|
if (argc > 1) insertFloppy(wdo, argv[1], 0); // load floppy if provided at command line
|
||||||
|
|
||||||
|
// reset the CPU
|
||||||
|
puce6502RST(); // reset the 6502
|
||||||
|
|
||||||
|
// dirty hack, fix soon... if I understand why
|
||||||
|
ram[0x4D] = 0xAA; // Joust crashes if this memory location equals zero
|
||||||
|
ram[0xD0] = 0xAA; // Planetoids won't work if this memory location equals zero
|
||||||
|
|
||||||
|
|
||||||
//================================================================== MAIN LOOP
|
//================================================================== MAIN LOOP
|
||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
|
|
||||||
frameStart = SDL_GetTicks(); // start of a new frame
|
if (!paused) { // the apple II is clocked at 1023000.0 Hhz
|
||||||
|
puce6502Exec(17050); // execute instructions for 1/60 of a second
|
||||||
if (!paused){
|
|
||||||
puce6502Exec((long long int)(1023000.0 / fps)); // using actualized frame rate
|
|
||||||
while (disk[curDrv].motorOn && ++tries) // until motor is off or i reaches 255+1=0
|
while (disk[curDrv].motorOn && ++tries) // until motor is off or i reaches 255+1=0
|
||||||
puce6502Exec(5000); // artificial drive speed up
|
puce6502Exec(5000); // speed up drive access artificially
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//=============================================================== USER INPUT
|
//=============================================================== USER INPUT
|
||||||
|
|
||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
|
|
||||||
alt = SDL_GetModState() & KMOD_ALT ? true : false;
|
alt = SDL_GetModState() & KMOD_ALT ? true : false;
|
||||||
ctrl = SDL_GetModState() & KMOD_CTRL ? true : false;
|
ctrl = SDL_GetModState() & KMOD_CTRL ? true : false;
|
||||||
shift = SDL_GetModState() & KMOD_SHIFT ? true : false;
|
shift = SDL_GetModState() & KMOD_SHIFT ? true : false;
|
||||||
@ -453,42 +544,43 @@ int main(int argc, char *argv[]){
|
|||||||
// if (event.type == SDL_WINDOWEVENT) { // pause if the window loses focus
|
// if (event.type == SDL_WINDOWEVENT) { // pause if the window loses focus
|
||||||
// if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
|
// if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
|
||||||
// paused = true;
|
// paused = true;
|
||||||
// if (event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
|
// else if (event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
|
||||||
// paused = false;
|
// paused = false;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (event.type == SDL_DROPFILE) { // user dropped a file
|
if (event.type == SDL_DROPFILE) { // user dropped a file
|
||||||
char *filename = event.drop.file; // get full pathname
|
char *filename = event.drop.file; // get full pathname
|
||||||
if (!insertFloppy(wdo, filename, alt)) // if ALT : drv 1 else drv 0
|
if (!insertFloppy(wdo, filename, alt)) // if ALT is pressed : drv 1 else drv 0
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Load", "Not a valid nib file", NULL);
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Load", "Not a valid nib file", NULL);
|
||||||
SDL_free(filename); // free filename memory
|
SDL_free(filename); // free filename memory
|
||||||
paused = false; // might already be the case
|
paused = false; // might already be the case
|
||||||
if (!(alt || ctrl)){ // unless ALT or CTRL were
|
|
||||||
|
if (!(alt || ctrl)) { // if ALT or CTRL were not pressed
|
||||||
ram[0x3F4] = 0; // unset the Power-UP byte
|
ram[0x3F4] = 0; // unset the Power-UP byte
|
||||||
puce6502Reset(); // do a cold reset
|
puce6502RST(); // do a cold reset
|
||||||
|
memset(ram, 0, sizeof(ram));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type == SDL_KEYDOWN) // a key has been pressed
|
if (event.type == SDL_KEYDOWN) { // a key has been pressed
|
||||||
switch (event.key.keysym.sym) {
|
switch (event.key.keysym.sym) {
|
||||||
|
|
||||||
// EMULATOR CONTROL :
|
// EMULATOR CONTROLS :
|
||||||
|
|
||||||
case SDLK_F1: // SAVES
|
case SDLK_F1: // SAVES
|
||||||
if (ctrl) {
|
if (ctrl) {
|
||||||
if (saveFloppy(0))
|
if (saveFloppy(0))
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Save", "\nDisk 1 saved back to file\n", NULL);
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Save", "\nDisk 1 saved back to file\n", NULL);
|
||||||
else
|
else
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Save", "\nThere was an error while saving Disk 1\n", NULL);
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Save", "\nTError while saving Disk 1\n", NULL);
|
||||||
}
|
} else if (alt) {
|
||||||
else if (alt){
|
|
||||||
if (saveFloppy(1))
|
if (saveFloppy(1))
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Save", "\nDisk 2 saved back to file\n", NULL);
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Save", "\nDisk 2 saved back to file\n", NULL);
|
||||||
else
|
else
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Save", "\nThere was an error while saving Disk 2\n", NULL);
|
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);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, "Save", "ctrl F1 to save D1\nalt F1 to save D2\n", NULL);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
@ -501,14 +593,16 @@ int main(int argc, char *argv[]){
|
|||||||
if (disk[0].filename[i] == '\\') a = i;
|
if (disk[0].filename[i] == '\\') a = i;
|
||||||
if (disk[0].filename[i] == '.') b = i;
|
if (disk[0].filename[i] == '.') b = i;
|
||||||
}
|
}
|
||||||
strncat(workDir, "screenshots", 13);
|
strncat(workDir, "screenshots\\", 14);
|
||||||
if (a != b) strncat(workDir, disk[0].filename + a, b - a);
|
if (a != b)
|
||||||
else strncat(workDir,"\\no disk", 10);
|
strncat(workDir, disk[0].filename + a, b - a);
|
||||||
|
else
|
||||||
|
strncat(workDir, "no disk", 10);
|
||||||
strncat(workDir, ".bmp", 5);
|
strncat(workDir, ".bmp", 5);
|
||||||
SDL_SaveBMP(sshot, workDir);
|
SDL_SaveBMP(sshot, workDir);
|
||||||
SDL_FreeSurface(sshot);
|
SDL_FreeSurface(sshot);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case SDLK_F3: // PASTE text from clipboard
|
case SDLK_F3: // PASTE text from clipboard
|
||||||
if (SDL_HasClipboardText()) {
|
if (SDL_HasClipboardText()) {
|
||||||
@ -527,7 +621,7 @@ int main(int argc, char *argv[]){
|
|||||||
if (shift && (volume < 120)) volume++; // increase volume
|
if (shift && (volume < 120)) volume++; // increase volume
|
||||||
if (ctrl && (volume > 0)) volume--; // decrease volume
|
if (ctrl && (volume > 0)) volume--; // decrease volume
|
||||||
if (!ctrl && !shift) muted = !muted; // toggle mute / unmute
|
if (!ctrl && !shift) muted = !muted; // toggle mute / unmute
|
||||||
for (int i=1; i<audioBufferSize; i++){ // update the audio buffers,
|
for (int i = 0; i < audioBufferSize; i++) { // update the audio buffers,
|
||||||
audioBuffer[true][i] = volume; // one used when SPKR is true
|
audioBuffer[true][i] = volume; // one used when SPKR is true
|
||||||
audioBuffer[false][i] = -volume; // the other when SPKR is false
|
audioBuffer[false][i] = -volume; // the other when SPKR is false
|
||||||
}
|
}
|
||||||
@ -553,37 +647,42 @@ int main(int argc, char *argv[]){
|
|||||||
SDL_RenderSetScale(rdr, zoom, zoom); // update renderer size
|
SDL_RenderSetScale(rdr, zoom, zoom); // update renderer size
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDLK_F8: monochrome = !monochrome; break; // toggle monochrome for HGR mode
|
case SDLK_F10: paused = !paused; break; // toggle pause
|
||||||
case SDLK_F9: paused = !paused; break; // toggle pause
|
|
||||||
case SDLK_F10: puce6502Break(); break; // simulate a break
|
case SDLK_F11: puce6502RST(); break; // simulate a reset
|
||||||
case SDLK_F11: puce6502Reset(); break; // reset
|
|
||||||
|
|
||||||
case SDLK_F12: // help box
|
case SDLK_F12: // help box
|
||||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Help",
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "Help",
|
||||||
"~ reinette II plus v0.4b ~ \
|
"\tReinette ][ plus v0.4.8\n"
|
||||||
\n\nctrl F1\twrites the changes of the floppy in drive 0 \
|
"\n"
|
||||||
\nalt F1\twrites the changes of the floppy in drive 1 \
|
"ctrl F1\twrites the changes of the floppy in drive 0\n"
|
||||||
\n\nF2\tsave a screenshot into the screenshots directory \
|
"alt F1\twrites the changes of the floppy in drive 1\n"
|
||||||
\nF3\tpaste text from clipboard \
|
"\n"
|
||||||
\n\nF4\tmute / un-mute sound \
|
"F2\tsave a screenshot into the screenshots directory\n"
|
||||||
\nshift F4\tincrease volume \
|
"F3\tpaste text from clipboard\n"
|
||||||
\nctrl F4\tdecrease volume \
|
"\n"
|
||||||
\n\nF5\treset joystick release speed \
|
"F4\tmute / un-mute sound\n"
|
||||||
\nshift F5\tincrease joystick release speed \
|
"shift F4\tincrease volume\n"
|
||||||
\ncrtl F5\tdecrease joystick release speed \
|
"ctrl F4\tdecrease volume\n"
|
||||||
\n\nF6\treset joystick action speed \
|
"\n"
|
||||||
\nshift F6\tincrease joystick action speed \
|
"F5\treset joystick release speed\n"
|
||||||
\ncrtl F6\tdecrease joystick action speed \
|
"shift F5\tincrease joystick release speed\n"
|
||||||
\n\nF7\treset the zoom to 2:1 \
|
"crtl F5\tdecrease joystick release speed\n"
|
||||||
\nshift F7\tincrease zoom up to 8:1 max \
|
"\n"
|
||||||
\nctrl F7\tdecrease zoom down to 1:1 pixels \
|
"F6\treset joystick action speed\n"
|
||||||
\nF8\tmonochrome / color display (only in HGR) \
|
"shift F6\tincrease joystick action speed\n"
|
||||||
\nF9\tpause / un-pause the emulator \
|
"crtl F6\tdecrease joystick action speed\n"
|
||||||
\n\nF10\tload binary file into $8000 \
|
"\n"
|
||||||
\nF11\treset \
|
"F7\treset zoom to 2:1\n"
|
||||||
\nctrl F11\tbreak \
|
"shift F7\tincrease zoom up to 8:1\n"
|
||||||
\n\nF12\tthis help \
|
"ctrl F7\tdecrease zoom down to 1:1\n"
|
||||||
\n\nmore information at github.com/ArthurFerreira2", NULL);
|
"\n"
|
||||||
|
"F10\tpause / un-pause the emulator\n"
|
||||||
|
"F11\treset\n"
|
||||||
|
"\n"
|
||||||
|
"F12\tthis help\n"
|
||||||
|
"\n"
|
||||||
|
"More information at github.com/ArthurFerreira2\n", NULL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// EMULATED KEYS :
|
// EMULATED KEYS :
|
||||||
@ -649,8 +748,9 @@ int main(int argc, char *argv[]){
|
|||||||
case SDLK_KP_5: GCD[1] = -1; GCA[1] = 1; break; // pdl1 <-
|
case SDLK_KP_5: GCD[1] = -1; GCA[1] = 1; break; // pdl1 <-
|
||||||
case SDLK_KP_2: GCD[1] = 1; GCA[1] = 1; break; // pdl1 ->
|
case SDLK_KP_2: GCD[1] = 1; GCA[1] = 1; break; // pdl1 ->
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (event.type == SDL_KEYUP)
|
if (event.type == SDL_KEYUP) {
|
||||||
switch (event.key.keysym.sym) {
|
switch (event.key.keysym.sym) {
|
||||||
case SDLK_KP_1: GCD[0] = 1; GCA[0] = 0; break; // pdl0 ->
|
case SDLK_KP_1: GCD[0] = 1; GCA[0] = 0; break; // pdl0 ->
|
||||||
case SDLK_KP_3: GCD[0] = -1; GCA[0] = 0; break; // pdl0 <-
|
case SDLK_KP_3: GCD[0] = -1; GCA[0] = 0; break; // pdl0 <-
|
||||||
@ -658,31 +758,32 @@ int main(int argc, char *argv[]){
|
|||||||
case SDLK_KP_2: GCD[1] = -1; GCA[1] = 0; break; // pdl1 <-
|
case SDLK_KP_2: GCD[1] = -1; GCA[1] = 0; break; // pdl1 <-
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int pdl = 0; pdl < 2; pdl++) { // update the two paddles positions
|
for (int pdl = 0; pdl < 2; pdl++) { // update the two paddles positions
|
||||||
if (GCA[pdl]) { // actively pushing the stick
|
if (GCA[pdl]) { // actively pushing the stick
|
||||||
GCP[pdl] += GCD[pdl] * GCActionSpeed;
|
GCP[pdl] += GCD[pdl] * GCActionSpeed;
|
||||||
if (GCP[pdl] > 255) GCP[pdl] = 255;
|
if (GCP[pdl] > 255) GCP[pdl] = 255;
|
||||||
if (GCP[pdl] < 0) GCP[pdl] = 0;
|
if (GCP[pdl] < 0) GCP[pdl] = 0;
|
||||||
}
|
} else { // the stick is return back to center
|
||||||
else { // the stick is return back to center
|
|
||||||
GCP[pdl] += GCD[pdl] * GCReleaseSpeed;
|
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;
|
||||||
if (GCD[pdl] == -1 && GCP[pdl] < 127) GCP[pdl] = 127;
|
if (GCD[pdl] == -1 && GCP[pdl] < 127) GCP[pdl] = 127;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//============================================================= VIDEO OUTPUT
|
//============================================================= VIDEO OUTPUT
|
||||||
|
|
||||||
// HIGH RES GRAPHICS
|
// HIGH RES GRAPHICS
|
||||||
|
|
||||||
if (!TEXT && HIRES) {
|
if (!TEXT && HIRES) {
|
||||||
uint16_t word;
|
uint16_t word;
|
||||||
uint8_t bits[16], bit, pbit, colorSet, even;
|
uint8_t bits[16], bit, pbit, colorSet, even;
|
||||||
vRamBase = PAGE * 0x2000; // PAGE is 1 or 2
|
uint16_t vRamBase = 0x2000 + PAGE2 * 0x2000;
|
||||||
lineLimit = MIXED ? 160 : 192;
|
uint8_t lastLine = MIXED ? 160 : 192;
|
||||||
|
uint8_t colorIdx = 0; // to index the color arrays
|
||||||
|
|
||||||
for (int line=0; line<lineLimit; line++){ // for every line
|
for (int line = 0; line < lastLine; line++) { // for every line
|
||||||
for (int col = 0; col < 40; col += 2) { // for every 7 horizontal dots
|
for (int col = 0; col < 40; col += 2) { // for every 7 horizontal dots
|
||||||
int x = col * 7;
|
int x = col * 7;
|
||||||
even = 0;
|
even = 0;
|
||||||
@ -690,8 +791,8 @@ int main(int argc, char *argv[]){
|
|||||||
word = (uint16_t)(ram[vRamBase + offsetHGR[line] + col + 1]) << 8; // store the two next bytes into 'word'
|
word = (uint16_t)(ram[vRamBase + offsetHGR[line] + col + 1]) << 8; // store the two next bytes into 'word'
|
||||||
word += ram[vRamBase + offsetHGR[line] + col]; // in reverse order
|
word += ram[vRamBase + offsetHGR[line] + col]; // in reverse order
|
||||||
|
|
||||||
if (previousDots[line][col] != word || !frame){ // check if this group of 7 dots need a redraw
|
if (HiResCache[line][col] != word || !flashCycle) { // check if this group of 7 dots need a redraw
|
||||||
// or refresh the full screen every 1/2 second
|
|
||||||
for (bit=0; bit < 16; bit++) // store all bits 'word' into 'bits'
|
for (bit=0; bit < 16; bit++) // store all bits 'word' into 'bits'
|
||||||
bits[bit] = (word >> bit) & 1;
|
bits[bit] = (word >> bit) & 1;
|
||||||
colorSet = bits[7] * 4; // select the right color set
|
colorSet = bits[7] * 4; // select the right color set
|
||||||
@ -703,83 +804,84 @@ int main(int argc, char *argv[]){
|
|||||||
colorSet = bits[15] * 4; // update the color set
|
colorSet = bits[15] * 4; // update the color set
|
||||||
bit++; // skip bit 7
|
bit++; // skip bit 7
|
||||||
}
|
}
|
||||||
if (monochrome)
|
|
||||||
colorIdx = bits[bit] * 3; // black if bit==0, white if bit==1
|
|
||||||
else
|
|
||||||
colorIdx = even + colorSet + (bits[bit] << 1) + (pbit);
|
colorIdx = even + colorSet + (bits[bit] << 1) + (pbit);
|
||||||
SDL_SetRenderDrawColor(rdr, hcolor[colorIdx][0], \
|
SDL_SetRenderDrawColor(rdr, hcolor[colorIdx][0], hcolor[colorIdx][1], hcolor[colorIdx][2], SDL_ALPHA_OPAQUE);
|
||||||
hcolor[colorIdx][1], hcolor[colorIdx][2], SDL_ALPHA_OPAQUE);
|
|
||||||
SDL_RenderDrawPoint(rdr, x++, line);
|
SDL_RenderDrawPoint(rdr, x++, line);
|
||||||
pbit = bits[bit++]; // proceed to the next pixel
|
pbit = bits[bit++]; // proceed to the next pixel
|
||||||
even = even ? 0 : 8; // one pixel every two is darker
|
even = even ? 0 : 8; // one pixel every two is darker
|
||||||
}
|
}
|
||||||
|
|
||||||
previousDots[line][col] = word; // update the video cache
|
HiResCache[line][col] = word; // update the video cache
|
||||||
if ((col < 37) && (previousBit[line][col + 2] != pbit)) { // check color franging effect on the dot after
|
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
|
previousBit[line][col + 2] = pbit; // set pbit and clear the
|
||||||
previousDots[line][col + 2] = -1; // video cache for next dot
|
HiResCache[line][col + 2] = -1; // video cache for next dot
|
||||||
}
|
}
|
||||||
} // if (previousDots[line][col] ...
|
} // if (HiResCache[line][col] ...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// lOW RES GRAPHICS
|
// lOW RES GRAPHICS
|
||||||
|
|
||||||
else if (!TEXT) { // and not in HIRES
|
else if (!TEXT) { // and not in HIRES
|
||||||
vRamBase = PAGE * 0x0400;
|
uint16_t vRamBase = 0x400 + PAGE2 * 0x0400;
|
||||||
lineLimit = MIXED ? 20 : 24;
|
uint8_t lastLine = MIXED ? 20 : 24;
|
||||||
|
uint8_t glyph; // 2 blocks in GR
|
||||||
|
uint8_t colorIdx = 0; // to index the color arrays
|
||||||
|
|
||||||
for (int col = 0; col < 40; col++) { // for each column
|
for (int col = 0; col < 40; col++) { // for each column
|
||||||
pixelGR.x = col * 7;
|
pixelGR.x = col * 7;
|
||||||
for (int line=0; line<lineLimit; line++){ // for each row
|
for (int line = 0; line < lastLine; line++) { // for each row
|
||||||
pixelGR.y = line * 8; // first block
|
pixelGR.y = line * 8; // first block
|
||||||
|
|
||||||
glyph = ram[vRamBase + offsetGR[line] + col]; // read video memory
|
glyph = ram[vRamBase + offsetGR[line] + col]; // read video memory
|
||||||
|
if (LoResCache[line][col] != glyph || !flashCycle) {
|
||||||
|
LoResCache[line][col] = glyph;
|
||||||
|
|
||||||
colorIdx = glyph & 0x0F; // first nibble
|
colorIdx = glyph & 0x0F; // first nibble
|
||||||
SDL_SetRenderDrawColor(rdr, color[colorIdx][0], \
|
SDL_SetRenderDrawColor(rdr, color[colorIdx][0], color[colorIdx][1], color[colorIdx][2], SDL_ALPHA_OPAQUE);
|
||||||
color[colorIdx][1], color[colorIdx][2], SDL_ALPHA_OPAQUE);
|
|
||||||
SDL_RenderFillRect(rdr, &pixelGR);
|
SDL_RenderFillRect(rdr, &pixelGR);
|
||||||
|
|
||||||
pixelGR.y += 4; // second block
|
pixelGR.y += 4; // second block
|
||||||
colorIdx = (glyph & 0xF0) >> 4; // second nibble
|
colorIdx = (glyph & 0xF0) >> 4; // second nibble
|
||||||
SDL_SetRenderDrawColor(rdr, color[colorIdx][0], \
|
SDL_SetRenderDrawColor(rdr, color[colorIdx][0], color[colorIdx][1], color[colorIdx][2], SDL_ALPHA_OPAQUE);
|
||||||
color[colorIdx][1], color[colorIdx][2], SDL_ALPHA_OPAQUE);
|
|
||||||
SDL_RenderFillRect(rdr, &pixelGR);
|
SDL_RenderFillRect(rdr, &pixelGR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TEXT 40 COLUMNS
|
// TEXT 40 COLUMNS
|
||||||
|
if (TEXT || MIXED) { // not Full Graphics
|
||||||
if (TEXT || MIXED){
|
uint16_t vRamBase = 0x400 +PAGE2 * 0x0400;
|
||||||
vRamBase = PAGE * 0x0400;
|
uint8_t firstLine = TEXT ? 0 : 20;
|
||||||
lineLimit = TEXT ? 0 : 20;
|
uint8_t glyph; // a TEXT character
|
||||||
|
|
||||||
for (int col = 0; col < 40; col++) { // for each column
|
for (int col = 0; col < 40; col++) { // for each column
|
||||||
dstRect.x = col * 7;
|
dstRect.x = col * 7;
|
||||||
for (int line=lineLimit; line<24; line++){ // for each row
|
for (int line = firstLine; line < 24; line++) { // for each row
|
||||||
dstRect.y = line * 8;
|
dstRect.y = line * 8;
|
||||||
|
|
||||||
glyph = ram[vRamBase + offsetGR[line] + col]; // read video memory
|
glyph = ram[vRamBase + offsetGR[line] + col]; // read video memory
|
||||||
|
|
||||||
if (glyph > 0x7F) glyphAttr = A_NORMAL; // is NORMAL ?
|
if (glyph > 0x7F) glyphAttr = A_NORMAL; // is NORMAL ?
|
||||||
else if (glyph < 0x40) glyphAttr = A_INVERSE; // is INVERSE ?
|
else if (glyph < 0x40) glyphAttr = A_INVERSE; // is INVERSE ?
|
||||||
else glyphAttr = A_FLASH; // it's FLASH !
|
else glyphAttr = A_FLASH; // it's FLASH !
|
||||||
|
|
||||||
glyph &= 0x7F; // unset bit 7
|
if (glyphAttr==A_FLASH || TextCache[line][col]!=glyph || !flashCycle){
|
||||||
|
TextCache[line][col] = glyph;
|
||||||
|
|
||||||
|
glyph &= 0x7F; // unset bit 7
|
||||||
if (glyph > 0x5F) glyph &= 0x3F; // shifts to match
|
if (glyph > 0x5F) glyph &= 0x3F; // shifts to match
|
||||||
if (glyph < 0x20) glyph |= 0x40; // the ASCII codes
|
if (glyph < 0x20) glyph |= 0x40; // the ASCII codes
|
||||||
|
|
||||||
if (glyphAttr == A_NORMAL || (glyphAttr == A_FLASH && frame < 15))
|
if (glyphAttr==A_NORMAL || (glyphAttr==A_FLASH && flashCycle<15))
|
||||||
SDL_RenderCopy(rdr, normCharTexture, &charRects[glyph], &dstRect);
|
SDL_RenderCopy(rdr, normCharTexture, &charRects[glyph], &dstRect);
|
||||||
else
|
else
|
||||||
SDL_RenderCopy(rdr, revCharTexture, &charRects[glyph], &dstRect);
|
SDL_RenderCopy(rdr, revCharTexture, &charRects[glyph], &dstRect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//====================================================== DISPLAY DISK STATUS
|
//====================================================== DISPLAY DISK STATUS
|
||||||
|
|
||||||
@ -788,27 +890,21 @@ int main(int argc, char *argv[]){
|
|||||||
SDL_SetRenderDrawColor(rdr, 255, 0, 0, 85); // red for writes
|
SDL_SetRenderDrawColor(rdr, 255, 0, 0, 85); // red for writes
|
||||||
else
|
else
|
||||||
SDL_SetRenderDrawColor(rdr, 0, 255, 0, 85); // green for reads
|
SDL_SetRenderDrawColor(rdr, 0, 255, 0, 85); // green for reads
|
||||||
|
|
||||||
SDL_RenderFillRect(rdr, &drvRect[curDrv]); // square actually
|
SDL_RenderFillRect(rdr, &drvRect[curDrv]); // square actually
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//========================================================= SDL RENDER FRAME
|
//========================================================= SDL RENDER FRAME
|
||||||
|
|
||||||
if (++frame > 30) frame = 0;
|
if (++flashCycle == 30) // increase cursor flash cycle
|
||||||
|
flashCycle = 0; // reset to zero every half second
|
||||||
frameTime = SDL_GetTicks() - frameStart; // frame duration
|
|
||||||
if (frameTime < frameDelay){ // do we have time ?
|
|
||||||
SDL_Delay(frameDelay - frameTime); // wait 'vsync'
|
|
||||||
SDL_RenderPresent(rdr); // swap buffers
|
SDL_RenderPresent(rdr); // swap buffers
|
||||||
frameTime = SDL_GetTicks() - frameStart; // update frameTime
|
|
||||||
} // else skip the frame
|
|
||||||
fps = 1000.0 / frameTime; // calculate the actual frame rate
|
|
||||||
|
|
||||||
} // while (running)
|
} // while (running)
|
||||||
|
|
||||||
|
|
||||||
//================================================ RELEASE RESSOURSES AND EXIT
|
//================================================ RELEASE RESSOURSES AND EXIT
|
||||||
|
|
||||||
SDL_AudioQuit();
|
SDL_AudioQuit();
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
return(0);
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
1 ICON "assets/reinetteII+.ico"
|
1 ICON "assets/reinetteII+.ico"
|
||||||
1 VERSIONINFO
|
1 VERSIONINFO
|
||||||
FILEVERSION 0,4,1,0
|
FILEVERSION 0,4,8,0
|
||||||
PRODUCTVERSION 0,4,0,0
|
PRODUCTVERSION 0,4,8,0
|
||||||
BEGIN
|
BEGIN
|
||||||
BLOCK "StringFileInfo"
|
BLOCK "StringFileInfo"
|
||||||
BEGIN
|
BEGIN
|
||||||
@ -9,12 +9,12 @@ BEGIN
|
|||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Arthur Ferreira"
|
VALUE "CompanyName", "Arthur Ferreira"
|
||||||
VALUE "FileDescription", "Apple II+ emulator"
|
VALUE "FileDescription", "Apple II+ emulator"
|
||||||
VALUE "FileVersion", "0.4.1.0"
|
VALUE "FileVersion", "0.4.8.0"
|
||||||
VALUE "InternalName", "reinette II plus"
|
VALUE "InternalName", "reinette II plus"
|
||||||
VALUE "LegalCopyright", "Arthur Ferreira"
|
VALUE "LegalCopyright", "Arthur Ferreira"
|
||||||
VALUE "OriginalFilename", "reinetteII+.exe"
|
VALUE "OriginalFilename", "reinetteII+.exe"
|
||||||
VALUE "ProductName", "reinette II plus"
|
VALUE "ProductName", "reinette II plus"
|
||||||
VALUE "ProductVersion", "0.4"
|
VALUE "ProductVersion", "0.4.8.0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
BIN
reinetteII+.res
Normal file
BIN
reinetteII+.res
Normal file
Binary file not shown.
BIN
screenshots/Ms. Pac-Man.bmp
Normal file
BIN
screenshots/Ms. Pac-Man.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 630 KiB |
Binary file not shown.
Before Width: | Height: | Size: 630 KiB After Width: | Height: | Size: 630 KiB |
Loading…
Reference in New Issue
Block a user