add apple2 emu, port of aiie

pull/9/head
jean-marcharvengt 2022-02-06 21:48:25 +01:00
rodzic 065a6e39e0
commit 25e0f243fc
82 zmienionych plików z 29394 dodań i 119632 usunięć

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -5,13 +5,13 @@
// Display
#define TFT_SCLK 13 //27
#define TFT_MOSI 11 //26
#define TFT_SCLK 27
#define TFT_MOSI 26
#define TFT_MISO 255
#define TFT_TOUCH_CS 255
#define TFT_TOUCH_INT 255
#define TFT_DC 9 //23
#define TFT_CS 22 //22 // 255 for LORES ST7789 (NO CS)
#define TFT_DC 23
#define TFT_CS 22 // 255 for LORES ST7789 (NO CS)
#define TFT_RST 255 // 255 for ILI/ST if connected to 3.3V or 24 if really needed

Wyświetl plik

@ -2,7 +2,7 @@
#define _PLATFORM_CONFIG_H_
#define ST7789 1
#define TFTSPI0 1
#define TFTSPI1 1
#define HAS_SND 1
#endif

Wyświetl plik

@ -0,0 +1,361 @@
#include "emuapi.h"
#ifdef HAS_SND
#include "AudioPlaySystem.h"
#include <Arduino.h>
#define SAMPLERATE AUDIO_SAMPLE_RATE_EXACT
#define CLOCKFREQ 985248
#ifndef CUSTOM_SND
PROGMEM static const short square[]={
32767,32767,32767,32767,
32767,32767,32767,32767,
32767,32767,32767,32767,
32767,32767,32767,32767,
32767,32767,32767,32767,
32767,32767,32767,32767,
32767,32767,32767,32767,
32767,32767,32767,32767,
-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,
};
PROGMEM const short noise[] {
-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,
-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,-32767,32767,-32767,
-32767,-32767,32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,32767,-32767,
-32767,-32767,32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,32767,-32767,-32767,32767,32767,-32767,
-32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,32767,-32767,-32767,32767,32767,-32767,
-32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,32767,-32767,32767,32767,32767,-32767,
32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,32767,-32767,32767,32767,32767,-32767,
32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,32767,32767,32767,32767,32767,-32767,
32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,32767,32767,32767,32767,32767,-32767,
32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,-32767,-32767,32767,32767,32767,-32767,-32767,
32767,-32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,-32767,32767,32767,32767,32767,32767,-32767,
32767,-32767,32767,-32767,-32767,32767,32767,-32767,-32767,32767,-32767,32767,32767,32767,-32767,-32767,
32767,32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,-32767,32767,32767,32767,32767,32767,-32767,
32767,-32767,32767,-32767,-32767,32767,32767,-32767,32767,32767,-32767,32767,-32767,32767,-32767,-32767,
32767,32767,-32767,-32767,-32767,32767,-32767,-32767,-32767,-32767,32767,32767,32767,32767,32767,-32767,
32767,-32767,32767,-32767,-32767,32767,32767,32767,32767,32767,-32767,32767,-32767,32767,-32767,-32767,
};
#define NOISEBSIZE 0x100
typedef struct
{
unsigned int spos;
unsigned int sinc;
unsigned int vol;
} Channel;
static Channel chan[6] = {
{0,0,0},
{0,0,0},
{0,0,0},
{0,0,0},
{0,0,0},
{0,0,0} };
#endif
volatile bool playing = false;
static void snd_Reset(void)
{
#ifndef CUSTOM_SND
chan[0].vol = 0;
chan[1].vol = 0;
chan[2].vol = 0;
chan[3].vol = 0;
chan[4].vol = 0;
chan[5].vol = 0;
chan[0].sinc = 0;
chan[1].sinc = 0;
chan[2].sinc = 0;
chan[3].sinc = 0;
chan[4].sinc = 0;
chan[5].sinc = 0;
#endif
}
#ifdef CUSTOM_SND
//extern "C" {
void SND_Process(void *sndbuffer, int sndn);
//}
#endif
FASTRUN void AudioPlaySystem::snd_Mixer(short * stream, int len )
{
if (playing)
{
#ifdef CUSTOM_SND
SND_Process((void*)stream, len);
#else
int i;
long s;
len = len >> 1;
short v0=chan[0].vol;
short v1=chan[1].vol;
short v2=chan[2].vol;
short v3=chan[3].vol;
short v4=chan[4].vol;
short v5=chan[5].vol;
for (i=0;i<len;i++)
{
s =((v0*square[(chan[0].spos>>8)&0x3f])>>11);
s+=((v1*square[(chan[1].spos>>8)&0x3f])>>11);
s+=((v2*square[(chan[2].spos>>8)&0x3f])>>11);
s+=((v3*noise[(chan[3].spos>>8)&(NOISEBSIZE-1)])>>11);
s+=((v4*noise[(chan[4].spos>>8)&(NOISEBSIZE-1)])>>11);
s+=((v5*noise[(chan[5].spos>>8)&(NOISEBSIZE-1)])>>11);
*stream++ = (short)(s);
*stream++ = (short)(s);
chan[0].spos += chan[0].sinc;
chan[1].spos += chan[1].sinc;
chan[2].spos += chan[2].sinc;
chan[3].spos += chan[3].sinc;
chan[4].spos += chan[4].sinc;
chan[5].spos += chan[5].sinc;
}
#endif
}
}
void AudioPlaySystem::begin(void)
{
this->reset();
}
void AudioPlaySystem::start(void)
{
playing = true;
}
void AudioPlaySystem::setSampleParameters(float clockfreq, float samplerate) {
}
void AudioPlaySystem::reset(void)
{
snd_Reset();
}
void AudioPlaySystem::stop(void)
{
//__disable_irq();
playing = false;
//__enable_irq();
}
bool AudioPlaySystem::isPlaying(void)
{
return playing;
}
void AudioPlaySystem::sound(int C, int F, int V) {
#ifndef CUSTOM_SND
if (C < 6) {
chan[C].vol = V;
chan[C].sinc = F>>1;
}
#endif
}
void AudioPlaySystem::step(void) {
}
#ifndef HAS_T4_VGA
/*******************************************************************
Experimental I2S interrupt based sound driver for PCM51xx !!!
*******************************************************************/
FLASHMEM static void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force) // sets PLL4
{
if (!force && (CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return;
CCM_ANALOG_PLL_AUDIO = CCM_ANALOG_PLL_AUDIO_BYPASS | CCM_ANALOG_PLL_AUDIO_ENABLE
| CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 2: 1/4; 1: 1/2; 0: 1/1
| CCM_ANALOG_PLL_AUDIO_DIV_SELECT(nfact);
CCM_ANALOG_PLL_AUDIO_NUM = nmult & CCM_ANALOG_PLL_AUDIO_NUM_MASK;
CCM_ANALOG_PLL_AUDIO_DENOM = ndiv & CCM_ANALOG_PLL_AUDIO_DENOM_MASK;
CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_POWERDOWN;//Switch on PLL
while (!(CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK)) {}; //Wait for pll-lock
const int div_post_pll = 1; // other values: 2,4
CCM_ANALOG_MISC2 &= ~(CCM_ANALOG_MISC2_DIV_MSB | CCM_ANALOG_MISC2_DIV_LSB);
if(div_post_pll>1) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_LSB;
if(div_post_pll>3) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_MSB;
CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_BYPASS;//Disable Bypass
}
#define AUDIO_SAMPLE_RATE_EXACT 11025.0 //44117.64706 //11025.0 //22050.0 //44117.64706 //31778.0
FLASHMEM static void config_sai1()
{
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
double fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = (fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2, true);
// clear SAI1_CLK register locations
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
| CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
n1 = n1 / 2; //Double Speed for TDM
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
| CCM_CS1CDR_SAI1_CLK_PRED(n1 - 1) // &0x07
| CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK
// configure transmitter
int rsync = 0;
int tsync = 1;
I2S1_TMR = 0;
I2S1_TCR1 = I2S_TCR1_RFW(1);
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async;
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1));
I2S1_TCR3 = I2S_TCR3_TCE;
I2S1_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF
| I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S1_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1));
I2S1_RMR = 0;
I2S1_RCR1 = I2S_RCR1_RFW(1);
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async;
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1));
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S1_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1));
//CORE_PIN23_CONFIG = 3; // MCLK
CORE_PIN21_CONFIG = 3; // RX_BCLK
CORE_PIN20_CONFIG = 3; // RX_SYNC
CORE_PIN7_CONFIG = 3; // TX_DATA0
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE ;//<-- not using DMA */;
}
//DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx[1024];
static bool fillfirsthalf = true;
static uint16_t cnt = 0;
static uint16_t sampleBufferSize = 0;
static void (*fillsamples)(short * stream, int len) = nullptr;
static uint32_t * i2s_tx_buffer __attribute__((aligned(32)));
static uint16_t * i2s_tx_buffer16;
static uint16_t * txreg = (uint16_t *)((uint32_t)&I2S1_TDR0 + 2);
FASTRUN void AudioPlaySystem::AUDIO_isr() {
*txreg = i2s_tx_buffer16[cnt];
cnt = cnt + 1;
cnt = cnt & (sampleBufferSize*2-1);
if (cnt == 0) {
fillfirsthalf = false;
NVIC_SET_PENDING(IRQ_SOFTWARE);
}
else if (cnt == sampleBufferSize) {
fillfirsthalf = true;
NVIC_SET_PENDING(IRQ_SOFTWARE);
}
/*
I2S1_TDR0 = i2s_tx_buffer[cnt];
cnt = cnt + 1;
cnt = cnt & (sampleBufferSize-1);
if (cnt == 0) {
fillfirsthalf = false;
NVIC_SET_PENDING(IRQ_SOFTWARE);
}
else if (cnt == sampleBufferSize/2) {
fillfirsthalf = true;
NVIC_SET_PENDING(IRQ_SOFTWARE);
}
*/
}
FASTRUN void AudioPlaySystem::SOFTWARE_isr() {
//Serial.println("x");
if (fillfirsthalf) {
fillsamples((short *)i2s_tx_buffer, sampleBufferSize);
arm_dcache_flush_delete((void*)i2s_tx_buffer, (sampleBufferSize/2)*sizeof(uint32_t));
}
else {
fillsamples((short *)&i2s_tx_buffer[sampleBufferSize/2], sampleBufferSize);
arm_dcache_flush_delete((void*)&i2s_tx_buffer[sampleBufferSize/2], (sampleBufferSize/2)*sizeof(uint32_t));
}
}
// display VGA image
FLASHMEM void AudioPlaySystem::begin_audio(int samplesize, void (*callback)(short * stream, int len))
{
fillsamples = callback;
i2s_tx_buffer = (uint32_t*)malloc(samplesize*sizeof(uint32_t)); //&i2s_tx[0];
if (i2s_tx_buffer == NULL) {
Serial.println("could not allocate audio samples");
return;
}
memset((void*)i2s_tx_buffer,0, samplesize*sizeof(uint32_t));
arm_dcache_flush_delete((void*)i2s_tx_buffer, samplesize*sizeof(uint32_t));
i2s_tx_buffer16 = (uint16_t*)i2s_tx_buffer;
sampleBufferSize = samplesize;
config_sai1();
attachInterruptVector(IRQ_SAI1, AUDIO_isr);
NVIC_ENABLE_IRQ(IRQ_SAI1);
NVIC_SET_PRIORITY(IRQ_QTIMER3, 0); // 0 highest priority, 255 = lowest priority
NVIC_SET_PRIORITY(IRQ_SAI1, 127);
attachInterruptVector(IRQ_SOFTWARE, SOFTWARE_isr);
NVIC_SET_PRIORITY(IRQ_SOFTWARE, 208);
NVIC_ENABLE_IRQ(IRQ_SOFTWARE);
I2S1_TCSR |= 1<<8; // start generating TX FIFO interrupts
Serial.print("Audio sample buffer = ");
Serial.println(samplesize);
}
FLASHMEM void AudioPlaySystem::end_audio()
{
if (i2s_tx_buffer != NULL) {
free(i2s_tx_buffer);
}
}
#endif
#endif

Wyświetl plik

@ -0,0 +1,34 @@
#ifndef audioplaysystem_h_
#define audioplaysystem_h_
#ifdef HAS_SND
#include "platform_config.h"
class AudioPlaySystem
{
public:
AudioPlaySystem(void) { };
void begin(void);
void setSampleParameters(float clockfreq, float samplerate);
void reset(void);
void start(void);
void stop(void);
bool isPlaying(void);
void sound(int C, int F, int V);
void buzz(int size, int val);
void step(void);
static void snd_Mixer(short * stream, int len );
#ifndef HAS_T4_VGA
void begin_audio(int samplesize, void (*callback)(short * stream, int len));
void end_audio();
static void AUDIO_isr(void);
static void SOFTWARE_isr(void);
#endif
};
#endif
#endif

Wyświetl plik

@ -0,0 +1,113 @@
#include "RingBuf.h"
#include <stdlib.h>
RingBuf::RingBuf(int16_t length)
{
this->buffer = (uint8_t *)malloc(length);
this->max = length;
this->fill = 0;
this->ptr = 0;
this->cursor = 0;
}
RingBuf::~RingBuf()
{
free (this->buffer);
}
void RingBuf::clear()
{
this->fill = 0;
}
bool RingBuf::isFull()
{
return (this->max == this->fill);
}
bool RingBuf::hasData()
{
return (this->fill != 0);
}
bool RingBuf::addByte(uint8_t b)
{
if (this->max == this->fill)
return false;
int idx = (this->ptr + this->fill) % this->max;
this->buffer[idx] = b;
this->fill++;
return true;
}
bool RingBuf::replaceByte(uint8_t b)
{
if (cursor < fill) {
buffer[cursor] = b;
cursor++;
if (cursor >= fill) {
cursor = 0;
}
return true;
}
return false;
}
bool RingBuf::addBytes(uint8_t *b, int count)
{
for (int i=0; i<count; i++) {
if (!addByte(b[i]))
return false;
}
return true;
}
uint8_t RingBuf::consumeByte()
{
if (this->fill == 0)
return 0;
uint8_t ret = this->buffer[this->ptr];
this->fill--;
this->ptr++;
this->ptr %= this->max;
return ret;
}
uint8_t RingBuf::peek(int16_t idx)
{
uint16_t p = (this->ptr + idx) % this->max;
return this->buffer[p];
}
int16_t RingBuf::count()
{
return this->fill;
}
uint16_t RingBuf::getPeekCursor()
{
return this->cursor;
}
void RingBuf::setPeekCursor(int16_t idx)
{
this->cursor = idx;
}
void RingBuf::resetPeekCursor()
{
this->cursor = 0;
}
uint8_t RingBuf::peekNext()
{
uint8_t ret = peek(cursor);
cursor++;
if (cursor >= fill) {
cursor = 0;
}
return ret;
}

Wyświetl plik

@ -0,0 +1,34 @@
#ifndef __RINGBUF_H
#define __RINGBUF_H
#include <stdint.h>
class RingBuf {
public:
RingBuf(int16_t length);
~RingBuf();
void clear();
bool isFull();
bool hasData();
bool addByte(uint8_t b);
bool addBytes(uint8_t *b, int count);
bool replaceByte(uint8_t b);
uint8_t consumeByte();
uint8_t peek(int16_t idx);
uint16_t getPeekCursor();
void setPeekCursor(int16_t idx);
void resetPeekCursor();
uint8_t peekNext();
int16_t count();
private:
uint8_t *buffer;
int16_t max;
int16_t ptr;
int16_t fill;
int16_t cursor;
};
#endif

Wyświetl plik

@ -0,0 +1,240 @@
#include <stdio.h>
#include <string.h>
extern "C" {
#include "emuapi.h"
#include "platform_config.h"
}
#include <Arduino.h>
// Apple2 2 emulation includes
#include "bios.h"
#include "cpu.h"
#include "applevm.h"
#include "plf-display.h"
#include "plf-keyboard.h"
#include "plf-speaker.h"
#include "plf-paddles.h"
#include "plf-filemanager.h"
#include "globals.h"
uint32_t nextInstructionMicros;
uint32_t startMicros;
static int ik; // joypad key
static int pik=0;
static int ihk; // I2C keyboard key
static int iusbhk;// USB keyboard key
static int prevhk;
void aiie_Input(int bClick) {
ik = emu_GetPad();
ihk = emu_ReadI2CKeyboard();
}
void emu_KeyboardOnDown(int keymodifer, int key) {
int keyCode = -1; //INV_KEY;
if ((key >=0xc0) && (key <=0xdf)) {
keyCode = ((key-0xc0) & 0x1f) + 0x7f;
}
else {
keyCode = key & 0x7f;
}
//Serial.println(keyCode);
if (keyCode != -1) {
iusbhk = keyCode;
}
}
void emu_KeyboardOnUp(int keymodifer, int key) {
iusbhk = 0;
}
void aiie_Init(void)
{
g_speaker = new PlfSpeaker(0);
// First create the filemanager - the interface to the host file system.
g_filemanager = new PlfFileManager();
// Construct the interface to the host display. This will need the
// VM's video buffer in order to draw the VM, but we don't have that
// yet.
g_display = new PlfDisplay();
// Next create the virtual CPU. This needs the VM's MMU in order to
// run, but we don't have that yet.
g_cpu = new Cpu();
// Create the virtual machine. This may read from g_filemanager to
// get ROMs if necessary. (The actual Apple VM we've built has them
// compiled in, though.) It will create its virutal hardware (MMU,
// video driver, floppy, paddles, whatever).
g_vm = new AppleVM();
// Now that the VM exists and it has created an MMU, we tell the CPU
// how to access memory through the MMU.
g_cpu->SetMMU(g_vm->getMMU());
// And the physical keyboard needs hooks in to the virtual keyboard...
g_keyboard = new PlfKeyboard(g_vm->getKeyboard());
g_paddles = new PlfPaddles(1, 1);
// Now that all the virtual hardware is glued together, reset the VM
g_vm->Reset();
g_display->redraw();
g_cpu->cycles = 0;
startMicros = nextInstructionMicros = micros();
startMicros = 0;
nextInstructionMicros = micros();
#ifdef HAS_SND
emu_sndInit();
#endif
}
void aiie_Start(char * filename)
{
emu_printf("emu starting");
((AppleVM *)g_vm)->insertDisk(0, filename, false);
emu_printf("emu started");
}
static int padx = 128;
static int pady = 128;
static int padxinc = 0;
static int padyinc = 0;
void aiie_Step(void)
{
int n = 1000;
while (n-- > 0) {
//if (micros() >= nextInstructionMicros) {
g_cpu->Run(24);
//g_speaker->beginMixing();
((AppleVM *)g_vm)->cpuMaintenance(g_cpu->cycles);
//g_speaker->maintainSpeaker(g_cpu->cycles);
// The CPU of the Apple //e ran at 1.023 MHz. Adjust when we think
// the next instruction should run based on how long the execution
// was ((1000/1023) * numberOfCycles) - which is about 97.8%.
nextInstructionMicros = startMicros + ((double)g_cpu->cycles * 0.978d);
//}
}
((AppleVM*)g_vm)->disk6->fillDiskBuffer();
g_keyboard->maintainKeyboard();
g_vm->vmdisplay->needsRedraw();
AiieRect what = g_vm->vmdisplay->getDirtyRect();
g_vm->vmdisplay->didRedraw();
g_display->blit(what);
emu_DrawVsync();
int hk=ihk;
if (iusbhk) hk = iusbhk;
if ( (hk) && (hk != prevhk) ) {
if (hk == 10) hk=RET;
else if (hk == 153) hk=UARR; //U L R D
else if (hk == 151) hk=LARR;
else if (hk == 150) hk=RARR;
else if (hk == 152) hk=DARR;
else if (hk == 127) hk=DEL;
prevhk = hk;
g_keyboard->onPress(hk);
// Serial.println(hk);
}
else if ( (prevhk) && (!hk) ) {
g_keyboard->onRelease(prevhk);
// Serial.println("release");
prevhk = 0;
}
int k=ik;
#ifdef TEECOMPUTER
// Ignore joypad if shift/fn is pressed!!!
if ( !(k & MASK_KEY_USER1) && !(k & MASK_KEY_USER2) )
#endif
{
if ( !(pik & MASK_JOY2_BTN) && (k & MASK_JOY2_BTN) ) {
g_keyboard->onPress(LA);
}
else if ( (pik & MASK_JOY2_BTN) && !(k & MASK_JOY2_BTN) ) {
g_keyboard->onRelease(LA);
}
if ( !(pik & MASK_JOY1_BTN) && (k & MASK_JOY1_BTN) ) {
g_keyboard->onPress(RA);
}
else if ( (pik & MASK_JOY1_BTN) && !(k & MASK_JOY1_BTN) ) {
g_keyboard->onRelease(RA);
}
if (k & MASK_JOY2_RIGHT) {
if (padxinc < 0) padxinc = 0;
if (padxinc != +1) padxinc += 1;
if (padx != 255) padx += padxinc;
g_paddles->setPaddle0(padx);
}
else if (k & MASK_JOY2_LEFT) {
if (padxinc > 0) padxinc = 0;
if (padxinc != -1) padxinc -= 1;
if (padx != 0) padx += padxinc;
g_paddles->setPaddle0(padx);
}
if (k & MASK_JOY2_UP) {
if (padyinc < 0) padyinc = 0;
if (padyinc != 1) padyinc += 1;
if (pady != 255) pady += padyinc;
g_paddles->setPaddle1(pady);
}
else if (k & MASK_JOY2_DOWN) {
if (padyinc > 0) padyinc = 0;
if (padyinc != -1) padyinc -= 1;
if (pady != 0) pady += padyinc;
g_paddles->setPaddle1(pady);
}
}
#ifndef TEECOMPUTER
if ( !(pik & MASK_KEY_USER1) && (k & MASK_KEY_USER1) ) {
g_keyboard->onPress('a');
}
else if ( (pik & MASK_KEY_USER1) && !(k & MASK_KEY_USER1) ) {
g_keyboard->onRelease('a');
}
if ( !(pik & MASK_KEY_USER2) && (k & MASK_KEY_USER2) ) {
g_keyboard->onPress('b');
}
else if ( (pik & MASK_KEY_USER2) && !(k & MASK_KEY_USER2) ) {
g_keyboard->onRelease('b');
}
#endif
pik = k;
#ifdef HAS_SND
#endif
}

Wyświetl plik

@ -0,0 +1,4 @@
extern void aiie_Init(void);
extern void aiie_Step(void);
extern void aiie_Start(char * filename);
extern void aiie_Input(int key);

Wyświetl plik

@ -0,0 +1,628 @@
#include <ctype.h> // isgraph
#include <stdlib.h> // calloc
#include <string.h> // strlen
#include "appledisplay.h"
#include "applemmu.h" // for switch constants
#include "font.h"
/* Fourpossible Hi-Res color-drawing modes..
MONOCHROME: show all the pixels, but only in green;
BLACKANDWHITE: monochrome, but use B&W instead of B&G;
NTSCLIKE: reduce the resolution to 140 pixels wide, similar to how an NTSC monitor would blend it
PERFECTCOLOR: as the Apple RGB monitor shows it, which means you can't have a solid color field
*/
#define extendDirtyRect(x,y) { \
if (dirtyRect.left > x) { \
dirtyRect.left = x; \
} \
if (dirtyRect.right < x) { \
dirtyRect.right = x; \
} \
if (dirtyRect.top > y) { \
dirtyRect.top = y; \
} \
if (dirtyRect.bottom < y) { \
dirtyRect.bottom = y; \
} \
}
#if DISPLAYRUN == 512
#define drawPixel(c, x, y) { \
uint16_t idx = (((y) << 9) + (x)) >> 1; \
if ((x) & 1) { \
videoBuffer[idx] = (videoBuffer[idx] & 0xF0) | (c); \
} else { \
videoBuffer[idx] = (videoBuffer[idx] & 0x0F) | ((c) << 4); \
} \
}
#define draw2Pixels(cAB, x, y) { \
videoBuffer[(((y) <<9) + (x)) >> 1] = cAB; \
}
#else
#define drawPixel(c, x, y) { \
uint16_t idx = ((y) * DISPLAYRUN + (x)) / 2; \
if ((x) & 1) { \
videoBuffer[idx] = (videoBuffer[idx] & 0xF0) | (c); \
} else { \
videoBuffer[idx] = (videoBuffer[idx] & 0x0F) | ((c) << 4); \
} \
}
#define draw2Pixels(cAB, x, y) { \
videoBuffer[((y) * DISPLAYRUN + (x)) /2] = cAB; \
}
#endif
#define DrawLoresPixelAt(c, x, y) { \
uint8_t pixel = c & 0x0F; \
for (uint8_t y2 = 0; y2<4; y2++) { \
for (int8_t x2 = 6; x2>=0; x2--) { \
drawPixel(pixel, x*7+x2, y*8+y2); \
} \
} \
pixel = (c >> 4); \
for (uint8_t y2 = 4; y2<8; y2++) { \
for (int8_t x2 = 6; x2>=0; x2--) { \
drawPixel(pixel, x*7+x2, y*8+y2); \
} \
} \
}
#include "globals.h"
AppleDisplay::AppleDisplay(uint8_t *vb) : VMDisplay(vb)
{
this->switches = NULL;
this->dirty = true;
this->dirtyRect.left = this->dirtyRect.top = 0;
this->dirtyRect.right = 279;
this->dirtyRect.bottom = 191;
textColor = g_displayType == m_monochrome?c_green:c_white;
}
AppleDisplay::~AppleDisplay()
{
}
bool AppleDisplay::deinterlaceAddress(uint16_t address, uint8_t *row, uint8_t *col)
{
if (address >= 0x800 && address < 0xC00) {
address -= 0x400;
}
uint8_t block = (address >> 7) - 0x08;
uint8_t blockOffset = (address & 0x00FF) - ((block & 0x01) ? 0x80 : 0x00);
if (blockOffset < 0x28) {
*row = block;
*col = blockOffset;
} else if (blockOffset < 0x50) {
*row = block + 8;
*col = blockOffset - 0x28;
} else {
*row = block + 16;
*col = blockOffset - 0x50;
}
return true;
}
// calculate x/y pixel offsets from a memory address.
// Note that this is the first of 7 pixels that will be affected by this write;
// we'll need to update all 7 starting at this x.
bool AppleDisplay::deinterlaceHiresAddress(uint16_t address, uint8_t *row, uint16_t *col)
{
// each row is 40 bytes, for 7 pixels each, totalling 128
// pixels wide.
// They are grouped in to 3 "runs" of 40-byte blocks, where
// each group is 64 lines after the one before.
// Then repeat at +400, +800, +c00, +1000, +1400, +1800, +1c00 for
// the other 7 pixels tall.
// Repeat the whole shebang at +0x80, +0x100, +0x180, ... to +280
// for each 8-pixel tall group.
// There are 8 bytes at the end of each run that we ignore. Skip them.
if ((address & 0x07f) >= 0x78 &&
(address & 0x7f) <= 0x7f) {
*row = 255;
*col = 65535;
return false;
}
*row = ((address & 0x380) >> 4) +
((address & 0x1c00)>>10) +
64 * ((address & 0x7f) / 40);
*col = ((address & 0x7f) % 40) * 7;
return true;
}
// return a pointer to the right glyph, and set *invert appropriately
const unsigned char *AppleDisplay::xlateChar(uint8_t c, bool *invert)
{
if (c <= 0x3F) {
// 0-3f: inverted @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ !"#$%&'()*+,-./0123456789:;<=>?
// (same w/o mousetext, actually)
*invert = true;
return &ucase_glyphs[c * 8];
} else if (c <= 0x5F) {
// 40-5f: normal mousetext
// (these are flashing @ABCDEFG..[\]^_ when not in mousetext mode)
if ((*switches) & S_ALTCH) {
*invert = false;
return &mousetext_glyphs[(c - 0x40) * 8];
} else {
*invert = true;
return &ucase_glyphs[(c - 0x40) * 8];
}
} else if (c <= 0x7F) {
// 60-7f: inverted `abcdefghijklmnopqrstuvwxyz{|}~*
// (these are flashing (sp)!"#$%...<=>? when not in mousetext)
if ((*switches) & S_ALTCH) {
*invert = true;
return &lcase_glyphs[(c - 0x60) * 8];
} else {
*invert = true;
return &ucase_glyphs[((c-0x60) + 0x20) * 8];
}
} else if (c <= 0xBF) {
// 80-BF: normal @ABCD... <=>? in both character sets
*invert = false;
return &ucase_glyphs[(c - 0x80) * 8];
} else if (c <= 0xDF) {
// C0-DF: normal @ABCD...Z[\]^_ in both character sets
*invert = false;
return &ucase_glyphs[(c - 0xC0) * 8];
} else {
// E0- : normal `abcdef... in both character sets
*invert = false;
return &lcase_glyphs[(c - 0xE0) * 8];
}
/* NOTREACHED */
}
inline void AppleDisplay::Draw14DoubleHiresPixelsAt(uint16_t addr)
{
// We will consult 4 bytes (2 in main, 2 in aux) for any single-byte
// write. Align to the first byte in that series based on what
// address we were given...
addr &= ~0x01;
// Figure out the position of that address on the "normal" hires screen
uint8_t row;
uint16_t col;
deinterlaceHiresAddress(addr, &row, &col);
if (row >= 160 &&
((*switches) & S_MIXED)) {
// displaying text, so don't have to draw this line
return;
}
// Make sure it's a valid graphics area, not a dead hole
if (col <= 280 && row <= 192) {
// Grab the 4 bytes we care about
uint8_t b1A = mmu->readDirect(addr, 0);
uint8_t b2A = mmu->readDirect(addr+1, 0);
uint8_t b1B = mmu->readDirect(addr, 1);
uint8_t b2B = mmu->readDirect(addr+1, 1);
// Construct the 28 bit wide bitstream, like we do for the simpler 14 Hires pixel draw
uint32_t bitTrain = b2A & 0x7F;
bitTrain <<= 7;
bitTrain |= (b2B & 0x7F);
bitTrain <<= 7;
bitTrain |= (b1A & 0x7F);
bitTrain <<= 7;
bitTrain |= (b1B & 0x7F);
// Now we pop groups of 4 bits off the bottom and draw our
// NTSC-style-only color. The display for this project only has
// 320 columns, so it's silly to try to do 560 columns of
// monochrome; and likewise, we can't do "perfect" representation
// of shifted color pixels. So NTSC it is, and we'll draw two screen
// pixels for every color.
for (int8_t xoff = 0; xoff < 14; xoff += 2) {
drawPixel(bitTrain & 0x0F, col+xoff, row);
drawPixel(bitTrain & 0x0F, col+xoff+1, row);
bitTrain >>= 4;
}
}
}
// Whenever we change a byte, it's possible that it will have an affect on the byte next to it -
// because between two bytes there is a shared bit.
// FIXME: what happens when the high bit of the left doesn't match the right? Which high bit does
// the overlap bit get?
inline void AppleDisplay::Draw14HiresPixelsAt(uint16_t addr)
{
uint8_t row;
uint16_t col;
deinterlaceHiresAddress(addr, &row, &col);
if (row >= 160 &&
((*switches) & S_MIXED)) {
return;
}
if (col <= 280 && row <= 192) {
/*
The high bit only selects the color palette.
There are only really two bits here, and they can be one of six colors.
color highbit even odd restriction
black x 0x80,0x00
green 0 0x2A 0x55 odd only
violet 0 0x55 0x2A even only
white x 0xFF,0x7F
orange 1 0xAA 0xD5 odd only
blue 1 0xD5 0xAA even only
in other words, we can look at the pixels in pairs and we get
00 black
01 green/orange
10 violet/blue
11 white
When the horizontal byte number is even, we ignore the last
bit. When the horizontal byte number is odd, we use that dropped
bit.
So each even byte turns in to 3 bits; and each odd byte turns in
to 4. Our effective output is therefore 140 pixels (half the
actual B&W resolution).
(Note that I swap 0x02 and 0x01 below, because we're running the
bit train backward, so the bits are reversed.)
*/
uint8_t b1 = mmu->read(addr);
uint8_t b2 = mmu->read(addr+1);
// Used for color modes...
bool highBitOne = (b1 & 0x80);
bool highBitTwo = (b2 & 0x80);
uint16_t bitTrain = (b1 & 0x7F) | ((b2 & 0x7F) << 7);
for (int8_t xoff = 0; xoff < 14; xoff += 2) {
if (g_displayType == m_monochrome) {
draw2Pixels(((bitTrain & 0x01 ? c_green : c_black) << 4) |
(bitTrain & 0x02 ? c_green : c_black),
col+xoff, row);
} else if (g_displayType == m_blackAndWhite) {
draw2Pixels(((bitTrain & 0x01 ? c_white : c_black) << 4) |
(bitTrain & 0x02 ? c_white : c_black),
col+xoff, row);
} else if (g_displayType == m_ntsclike) {
// Use the NTSC-like color mode, where we're only 140 pixels wide.
bool highBitSet = (xoff >= 7 ? highBitTwo : highBitOne);
uint8_t color;
switch (bitTrain & 0x03) {
case 0x00:
color = c_black;
break;
case 0x02:
color = (highBitSet ? c_orange : c_green);
break;
case 0x01:
color = (highBitSet ? c_medblue : c_purple);
break;
case 0x03:
color = c_white;
break;
}
draw2Pixels( (color << 4) | color, col+xoff, row );
} else {
// Use the "perfect" color mode, like the Apple RGB monitor showed.
bool highBitSet = (xoff >= 7 ? highBitTwo : highBitOne);
uint8_t color;
switch (bitTrain & 0x03) {
case 0x00:
color = c_black;
break;
case 0x02:
color = (highBitSet ? c_orange : c_green);
break;
case 0x01:
color = (highBitSet ? c_medblue : c_purple);
break;
case 0x03:
color = c_white;
break;
}
uint16_t twoColors;
if (color == c_black || color == c_white || bitTrain & 0x01) {
twoColors = color;
} else {
twoColors = c_black;
}
twoColors <<= 4;
if (color == c_black || color == c_white || bitTrain & 0x02) {
twoColors |= color;
} else {
twoColors |= c_black;
}
draw2Pixels(twoColors, col+xoff, row);
}
bitTrain >>= 2;
}
}
}
void AppleDisplay::redraw80ColumnText(uint8_t startingY)
{
uint8_t row, col;
col = -1; // will force us to deinterlaceAddress()
bool invert;
const uint8_t *cptr;
// FIXME: is there ever a case for 0x800, like in redraw40ColumnText?
uint16_t start = 0x400;
// Every time through this loop, we increment the column. That's going to be correct most of the time.
// Sometimes we'll get beyond the end (40 columns), and wind up on another line 8 rows down.
// Sometimes we'll get beyond the end, and we'll wind up in unused RAM.
// But this is an optimization (for speed) over just calling DrawCharacter() for every one.
for (uint16_t addr = start; addr <= start + 0x3FF; addr++,col++) {
if (col > 39 || row > 23) {
// Could be blanking space; we'll try to re-confirm...
deinterlaceAddress(addr, &row, &col);
}
// Only draw onscreen locations
if (row >= startingY && col <= 39 && row <= 23) {
// Even characters are in bank 0 ram. Odd characters are in bank
// 1 ram. Technically, this would need 560 columns to work
// correctly - and I don't have that, so it's going to be a bit
// wonky.
//
// First pass: draw two pixels on top of each other, clearing
// only if both are black. This would be blocky but probably
// passable if it weren't for the fact that characters are 7
// pixels wide, so we wind up sharing a half-pixel between two
// characters. So we'll render these as 3-pixel-wide characters
// and make sure they always even-align the drawing on the left
// side so we don't overwrite every other one on the left or
// right side.
// Draw the first of two characters
cptr = xlateChar(mmu->readDirect(addr, 1), &invert);
for (uint8_t y2 = 0; y2<8; y2++) {
uint8_t d = *(cptr + y2);
for (uint8_t x2 = 0; x2 <= 7; x2+=2) {
uint16_t basex = ((col * 2) * 7) & 0xFFFE; // even aligned
bool pixelOn = ( (d & (1<<x2)) | (d & (1<<(x2+1))) );
if (pixelOn) {
uint8_t val = (invert ? c_black : textColor);
drawPixel(val, (basex+x2)/2, row*8+y2);
} else {
uint8_t val = (invert ? textColor : c_black);
drawPixel(val, (basex+x2)/2, row*8+y2);
}
}
}
// Draw the second of two characters
cptr = xlateChar(mmu->readDirect(addr, 0), &invert);
for (uint8_t y2 = 0; y2<8; y2++) {
uint8_t d = *(cptr + y2);
for (uint8_t x2 = 0; x2 <= 7; x2+=2) {
uint16_t basex = ((col * 2 + 1) * 7) & 0xFFFE; // even aligned -- +1 for the second character
bool pixelOn = ( (d & (1<<x2)) | (d & (1<<(x2+1))) );
if (pixelOn) {
uint8_t val = (invert ? c_black : textColor);
drawPixel(val, (basex+x2)/2, row*8+y2);
} else {
uint8_t val = (invert ? textColor : c_black);
drawPixel(val, (basex+x2)/2, row*8+y2);
}
}
}
}
}
}
void AppleDisplay::redraw40ColumnText(uint8_t startingY)
{
bool invert;
uint16_t start = ((*switches) & S_PAGE2) ? 0x800 : 0x400;
uint8_t row, col;
col = -1; // will force us to deinterlaceAddress()
// Every time through this loop, we increment the column. That's going to be correct most of the time.
// Sometimes we'll get beyond the end (40 columns), and wind up on another line 8 rows down.
// Sometimes we'll get beyond the end, and we'll wind up in unused RAM.
// But this is an optimization (for speed) over just calling DrawCharacter() for every one.
for (uint16_t addr = start; addr <= start + 0x3FF; addr++,col++) {
if (col > 39 || row > 23) {
// Could be blanking space; we'll try to re-confirm...
deinterlaceAddress(addr, &row, &col);
}
// Only draw onscreen locations
if (row >= startingY && col <= 39 && row <= 23) {
const uint8_t *cptr = xlateChar(mmu->read(addr), &invert);
for (uint8_t y2 = 0; y2<8; y2++) {
uint8_t d = *(cptr + y2);
for (uint8_t x2 = 0; x2 < 7; x2++) {
if (d & 1) {
uint8_t val = (invert ? c_black : textColor);
drawPixel(val, col*7+x2, row*8+y2);
} else {
uint8_t val = (invert ? textColor : c_black);
drawPixel(val, col*7+x2, row*8+y2);
}
d >>= 1;
}
}
}
}
}
void AppleDisplay::redrawHires()
{
uint16_t start = ((*switches) & S_PAGE2) ? 0x4000 : 0x2000;
if ((*switches) & S_80STORE) {
// Apple IIe, technical nodes #3: 80STORE must be OFF to display Page 2
start = 0x2000;
}
// FIXME: check MIXED & don't redraw the lower area if it's set
for (uint16_t addr = start; addr <= start + 0x1FFF; addr+=2) {
if ((*switches) & S_DHIRES) {
// FIXME: inline & optimize
Draw14DoubleHiresPixelsAt(addr);
} else {
// FIXME: inline & optimize
Draw14HiresPixelsAt(addr);
}
}
}
void AppleDisplay::redrawLores()
{
// FIXME: can make more efficient by checking S_MIXED for lower bound
if (((*switches) & S_80COL) && ((*switches) & S_DHIRES)) {
for (uint16_t addr = 0x400; addr <= 0x400 + 0x3ff; addr++) {
uint8_t row, col;
deinterlaceAddress(addr, &row, &col);
if (col <= 39 && row <= 23) {
Draw80LoresPixelAt(mmu->readDirect(addr, 0), col, row, 1);
Draw80LoresPixelAt(mmu->readDirect(addr, 1), col, row, 0);
}
}
} else {
uint16_t start = ((*switches) & S_PAGE2) ? 0x800 : 0x400;
for (uint16_t addr = start; addr <= start + 0x3FF; addr++) {
uint8_t row, col;
deinterlaceAddress(addr, &row, &col);
if (col <= 39 && row <= 23) {
DrawLoresPixelAt(mmu->read(addr), col, row);
}
}
}
}
void AppleDisplay::modeChange()
{
dirty = true;
}
void AppleDisplay::Draw80LoresPixelAt(uint8_t c, uint8_t x, uint8_t y, uint8_t offset)
{
// Just like 80-column text, this has a minor problem; we're taking
// a 7-pixel-wide space and dividing it in half. Here I'm drawing
// every other column 1 pixel narrower (the ">= offset" in the for
// loop condition).
//
// Make those ">= 0" and change the "*7" to "*8" and you've got
// 320-pixel-wide slightly distorted but cleaner double-lores...
if (!offset) {
// The colors in every other column are swizzled. Un-swizzle.
c = ((c & 0x77) << 1) | ((c & 0x88) >> 3);
}
uint8_t pixel = c & 0x0F;
for (uint8_t y2 = 0; y2<4; y2++) {
for (int8_t x2 = 3; x2>=offset; x2--) {
drawPixel(pixel, x*7+x2+offset*3, y*8+y2);
}
}
pixel = (c >> 4);
for (uint8_t y2 = 4; y2<8; y2++) {
for (int8_t x2 = 3; x2>=offset; x2--) {
drawPixel(pixel, x*7+x2+offset*3, y*8+y2);
}
}
}
void AppleDisplay::setSwitches(uint16_t *switches)
{
dirty = true;
dirtyRect.left = 0;
dirtyRect.right = 279;
dirtyRect.top = 0;
dirtyRect.bottom = 191;
this->switches = switches;
}
AiieRect AppleDisplay::getDirtyRect()
{
return dirtyRect;
}
bool AppleDisplay::needsRedraw()
{
if (dirty) {
// Figure out what graphics mode we're in and redraw it in its entirety.
if ((*switches) & S_TEXT) {
if ((*switches) & S_80COL) {
redraw80ColumnText(0);
} else {
redraw40ColumnText(0);
}
return true;
}
// Not text mode - what mode are we in?
if ((*switches) & S_HIRES) {
redrawHires();
} else {
redrawLores();
}
// Mixed graphics modes: draw text @ bottom
if ((*switches) & S_MIXED) {
if ((*switches) & S_80COL) {
redraw80ColumnText(20);
} else {
redraw40ColumnText(20);
}
}
}
return dirty;
}
void AppleDisplay::didRedraw()
{
dirty = false;
}
void AppleDisplay::displayTypeChanged()
{
textColor = g_displayType == m_monochrome?c_green:c_white;
}

Wyświetl plik

@ -0,0 +1,82 @@
#ifndef __APPLEDISPLAY_H
#define __APPLEDISPLAY_H
#ifdef TEENSYDUINO
#include <Arduino.h>
#else
#include <stdlib.h>
#include <stdint.h>
#endif
#include "vmdisplay.h"
enum {
c_black = 0,
c_magenta = 1,
c_darkblue = 2,
c_purple = 3,
c_darkgreen = 4,
c_darkgrey = 5,
c_medblue = 6,
c_lightblue = 7,
c_brown = 8,
c_orange = 9,
c_lightgray = 10,
c_pink = 11,
c_green = 12,
c_yellow = 13,
c_aqua = 14,
c_white = 15
};
enum {
m_blackAndWhite = 0,
m_monochrome = 1,
m_ntsclike = 2,
m_perfectcolor = 3
};
class AppleMMU;
class AppleDisplay : public VMDisplay{
public:
AppleDisplay(uint8_t *vb);
virtual ~AppleDisplay();
virtual bool needsRedraw();
virtual void didRedraw();
virtual AiieRect getDirtyRect();
void modeChange(); // FIXME: rename 'redraw'?
void setSwitches(uint16_t *switches);
void writeLores(uint16_t address, uint8_t v);
void writeHires(uint16_t address, uint8_t v);
void displayTypeChanged();
private:
bool deinterlaceAddress(uint16_t address, uint8_t *row, uint8_t *col);
bool deinterlaceHiresAddress(uint16_t address, uint8_t *row, uint16_t *col);
void Draw14DoubleHiresPixelsAt(uint16_t addr);
void Draw14HiresPixelsAt(uint16_t addr);
void Draw80LoresPixelAt(uint8_t c, uint8_t x, uint8_t y, uint8_t offset);
const unsigned char *xlateChar(uint8_t c, bool *invert);
void redraw40ColumnText(uint8_t startingY);
void redraw80ColumnText(uint8_t startingY);
void redrawHires();
void redrawLores();
private:
volatile bool dirty;
AiieRect dirtyRect;
uint16_t *switches; // pointer to the MMU's switches
uint16_t textColor;
};
#endif

Wyświetl plik

@ -0,0 +1,370 @@
#include "applekeyboard.h"
#include "physicalkeyboard.h" // for LA/RA constants
#include "applemmu.h"
#include "globals.h"
// How many CPU cycles before we begin repeating a key?
#define STARTREPEAT 700000
// How many CPU cycles between repeats of a key?
#define REPEATAGAIN 66667
AppleKeyboard::AppleKeyboard(AppleMMU *m)
{
this->mmu = m;
for (uint16_t i=0; i<sizeof(keysDown); i++) {
keysDown[i] = false;
}
anyKeyIsDown = false;
startRepeatTimer = 0;
repeatTimer = 0;
capsLockEnabled = true;
}
AppleKeyboard::~AppleKeyboard()
{
}
bool AppleKeyboard::isVirtualKey(uint8_t kc)
{
if (kc >= 0x81 && kc <= 0x97) {
return true;
}
return false;
}
// apply the apple keymap.
// FIXME: easier with an array, but is that better?
uint8_t AppleKeyboard::translateKeyWithModifiers(uint8_t k)
{
// tolower, so we know what we're working with...
if (k >= 'A' && k <= 'Z') {
k = k - 'A' + 'a';
}
if (keysDown[_CTRL]) {
if (k >= 'a' && k <= 'z') {
return k - 'a' + 1;
}
// FIXME: any other control keys honored on the //e keyboard?
}
if (capsLockEnabled && k >= 'a' && k <= 'z') {
return k - 'a' + 'A';
}
if (keysDown[LSHFT] || keysDown[RSHFT]) {
if (k >= 'a' && k <= 'z') {
return k - 'a' + 'A';
}
switch (k) {
case '1':
return '!';
case '2':
return '@';
case '3':
return '#';
case '4':
return '$';
case '5':
return '%';
case '6':
return '^';
case '7':
return '&';
case '8':
return '*';
case '9':
return '(';
case '0':
return ')';
case '-':
return '_';
case '=':
return '+';
case '[':
return '{';
case ']':
return '}';
case '\\':
return '|';
case '`':
return '~';
case ';':
return ':';
case '\'':
return '"';
case ',':
return '<';
case '.':
return '>';
case '/':
return '?';
}
// FIXME: what the heck is it? I guess we don't need to shift it?
}
// And if we fall through, then just return it as-is
return k;
}
// apply the apple keymap.
// FIXME: easier with an array, but is that better?
uint8_t AppleKeyboard::translateKeyWithModifiers(uint8_t k, uint8_t m)
{
// tolower, so we know what we're working with...
if (k >= 'A' && k <= 'Z') {
k = k - 'A' + 'a';
}
if (m & (USB_LEFT_CTRL | USB_RIGHT_CTRL | USB_LEFT_GUI | USB_RIGHT_GUI)) {
if (k >= 'a' && k <= 'z') {
return k - 'a' + 1;
}
// FIXME: any other control keys honored on the //e keyboard?
}
if (capsLockEnabled && k >= 'a' && k <= 'z') {
return k - 'a' + 'A';
}
if (m & (USB_LEFT_SHIFT | USB_RIGHT_SHIFT)) {
if (k >= 'a' && k <= 'z') {
return k - 'a' + 'A';
}
switch (k) {
case '1':
return '!';
case '2':
return '@';
case '3':
return '#';
case '4':
return '$';
case '5':
return '%';
case '6':
return '^';
case '7':
return '&';
case '8':
return '*';
case '9':
return '(';
case '0':
return ')';
case '-':
return '_';
case '=':
return '+';
case '[':
return '{';
case ']':
return '}';
case '\\':
return '|';
case '`':
return '~';
case ';':
return ':';
case '\'':
return '"';
case ',':
return '<';
case '.':
return '>';
case '/':
return '?';
}
// FIXME: what the heck is it? I guess we don't need to shift it?
}
// And if we fall through, then just return it as-is
return k;
}
void AppleKeyboard::keyDepressed(uint8_t k)
{
keysDown[k] = true;
// If it's not a virtual key, then set the anyKeyDown flag
// (the VM will see this as a keyboard key)
if (!isVirtualKey(k)) {
if (!anyKeyIsDown) {
mmu->setKeyDown(true);
anyKeyIsDown = true;
}
keyThatIsRepeating = translateKeyWithModifiers(k);
startRepeatTimer = g_cpu->cycles + STARTREPEAT;
mmu->keyboardInput(keyThatIsRepeating);
} else if (k == LA) {
// Special handling: apple keys
mmu->isOpenApplePressed = true;
return;
} else if (k == RA) {
// Special handling: apple keys
mmu->isClosedApplePressed = true;
return;
} else if (k == JOY2) {
// Special handling: apple keys
mmu->isButton2Pressed = true;
return;
} else if (k == LOCK) {
// Special handling: caps lock
capsLockEnabled = !capsLockEnabled;
g_keyboard->setCaps(capsLockEnabled);
return;
}
}
void AppleKeyboard::keyReleased(uint8_t k)
{
keysDown[k] = false;
// Special handling: apple keys
if (k == LA) {
mmu->isOpenApplePressed = false;
return;
}
if (k == RA) {
mmu->isClosedApplePressed = false;
return;
}
if (k == JOY2) {
mmu->isButton2Pressed = false;
return;
}
if (k == LOCK) {
// Nothing to do when the caps lock key is released.
return;
}
if (anyKeyIsDown) {
anyKeyIsDown = false;
for (uint16_t i=0; i<sizeof(keysDown); i++) {
if (keysDown[i] && !isVirtualKey(i)) {
anyKeyIsDown = true;
break;
}
}
if (!anyKeyIsDown) {
mmu->setKeyDown(false);
}
}
}
void AppleKeyboard::keyDepressed(uint8_t k, uint8_t m)
{
keysDown[k] = true;
// If it's not a virtual key, then set the anyKeyDown flag
// (the VM will see this as a keyboard key)
if (k && !isVirtualKey(k)) {
if (!anyKeyIsDown) {
mmu->setKeyDown(true);
anyKeyIsDown = true;
}
keyThatIsRepeating = translateKeyWithModifiers(k, m);
startRepeatTimer = g_cpu->cycles + STARTREPEAT;
mmu->keyboardInput(keyThatIsRepeating);
} else if (k == LA) {
// Special handling: apple keys
mmu->isOpenApplePressed = true;
return;
} else if (k == RA) {
// Special handling: apple keys
mmu->isClosedApplePressed = true;
return;
} else if (k == JOY2) {
// Special handling: apple keys
mmu->isButton2Pressed = true;
return;
} else if (k == LOCK) {
// Special handling: caps lock
capsLockEnabled = !capsLockEnabled;
g_keyboard->setCaps(capsLockEnabled);
return;
} else if (k == SYSRQ) {
// Special handling: System request
biosRequest = true;
return;
}
}
void AppleKeyboard::keyReleased(uint8_t k, uint8_t m)
{
keysDown[k] = false;
// Special handling: apple keys
if (k == LA) {
mmu->isOpenApplePressed = false;
return;
}
if (k == RA) {
mmu->isClosedApplePressed = false;
return;
}
if (k == JOY2) {
mmu->isButton2Pressed = false;
return;
}
if (k == LOCK) {
// Nothing to do when the caps lock key is released.
return;
}
if (anyKeyIsDown) {
anyKeyIsDown = false;
for (uint16_t i=0; i<sizeof(keysDown); i++) {
if (keysDown[i] && !isVirtualKey(i)) {
anyKeyIsDown = true;
break;
}
}
if (!anyKeyIsDown) {
mmu->setKeyDown(false);
}
}
}
bool AppleKeyboard::getAnnunciator(uint8_t index){
return mmu->annunciators[index];
}
void AppleKeyboard::setButton(uint8_t index, bool val){
if (index == 0) mmu->isOpenApplePressed = val;
else if (index == 1) mmu->isClosedApplePressed = val;
else if (index == 2) mmu->isButton2Pressed = val;
}
void AppleKeyboard::setButtons(bool b0, bool b1, bool b2) {
mmu->isOpenApplePressed = b0;
mmu->isClosedApplePressed = b1;
mmu->isButton2Pressed = b2;
}
void AppleKeyboard::maintainKeyboard(uint32_t cycleCount)
{
if (anyKeyIsDown) {
if (startRepeatTimer) {
if (cycleCount >= startRepeatTimer) {
// waiting to start repeating
startRepeatTimer = 0;
repeatTimer = 0;
// Will fall through...
} else {
// Don't fall through; not time to start repeating yet
return;
}
}
// already repeating; keep it up
if (cycleCount >= repeatTimer) {
mmu->keyboardInput(keyThatIsRepeating);
repeatTimer = cycleCount + REPEATAGAIN;
}
}
}

Wyświetl plik

@ -0,0 +1,64 @@
#ifndef __APPLEKEYBOARD_H
#define __APPLEKEYBOARD_H
#include <stdint.h>
#include "vmkeyboard.h"
#include "applemmu.h"
extern void biosInterrupt();
class AppleKeyboard : public VMKeyboard {
public:
AppleKeyboard(AppleMMU *m);
virtual ~AppleKeyboard();
virtual void keyDepressed(uint8_t k);
virtual void keyReleased(uint8_t k);
virtual void keyDepressed(uint8_t k, uint8_t m);
virtual void keyReleased(uint8_t k, uint8_t m);
virtual void setButton(uint8_t index, bool val);
virtual void setButtons(bool b0, bool b1, bool b2);
virtual bool getAnnunciator(uint8_t index);
virtual void maintainKeyboard(uint32_t cycleCount);
protected:
bool isVirtualKey(uint8_t kc);
uint8_t translateKeyWithModifiers(uint8_t k);
uint8_t translateKeyWithModifiers(uint8_t k, uint8_t m);
private:
AppleMMU *mmu;
bool capsLockEnabled;
// This is a trade-off. I'm choosing speed over RAM size. If we need
// to reclaim RAM, we can get some bytes here at the expense of speed.
// These are flags for whether or not each of the individual keys are
// down, so that we can repeat appropriately. We're tracking state
// of all of the keys because of special modifier key situations.
// It's lazily using 256 bytes instead of whatever 62 we'd actually need.
bool keysDown[256];
bool anyKeyIsDown;
// While one - and only one - key is down, we repeat keypresses
// after about "534 to 801 milliseconds" (UTA2E, p. 7-15); and then
// while repeating, we send that keypress (reset keystrobe) about 15
// times a second (every 66667-ish CPU cycles).
//
// startRepeatTimer is the time (in CPU clock cycles) when we will
// start repeating the key that's currently down (note: rollover
// happens every 4925 seconds because it's a 32-bit counter, which means
// that roughly once every 82 minutes it's possible that a key will begin
// repeating early).
//
// keyThatIsRepeating is set to the actual key pressed.
// repeatTimer is the cpu cycle count at which we would repeat again.
// (It also has the rollover problem once every 82 minutes.)
uint32_t startRepeatTimer;
uint8_t keyThatIsRepeating;
uint32_t repeatTimer;
};
#endif

Wyświetl plik

@ -0,0 +1,995 @@
#ifdef TEENSYDUINO
#include <Arduino.h>
#define println(x) Serial.println(x)
#else
#include <stdio.h>
#include <unistd.h>
#define println(x) {}
#endif
#include "applemmu.h"
#include "applemmu-rom.h"
#include "physicalspeaker.h"
#include "cpu.h"
#include "globals.h"
// apple //e memory map
/*
page 0x00: zero page (straight ram)
page 0x01: stack (straight ram)
page 0x02:
page 0x03:
text/lores page 1: 0x0400 - 0x7FF
text/lores page 2: 0x0800 - 0xBFF
pages 0x0C - 0x1F: straight ram
hires page 1: pages 0x20 - 0x3F
hires page 2: pages 0x40 - 0x5F
pages 0x60 - 0xBF: straight ram
page 0xc0: I/O switches
pages 0xc1 - 0xcf: slot ROMs
pages 0xd0 - 0xdf: Basic ROM
pages 0xe0 - 0xff: monitor ROM
*/
AppleMMU::AppleMMU(AppleDisplay *display)
{
anyKeyDown = false;
keyboardStrobe = 0x00;
isOpenApplePressed = false;
isClosedApplePressed = false;
isButton2Pressed = false;
for (int8_t i=0; i<=7; i++) {
slots[i] = NULL;
}
for (int8_t i=0; i<3; i++) {
annunciators[i] = false;
}
allocateMemory();
this->display = display;
this->display->setSwitches(&switches);
resetRAM(); // initialize RAM, load ROM
}
AppleMMU::~AppleMMU()
{
delete display;
// FIXME: clean up the memory we allocated
}
void AppleMMU::Reset()
{
resetRAM();
resetDisplay(); // sets the switches properly
}
uint8_t AppleMMU::read(uint16_t address)
{
if (address >= 0xC000 &&
address <= 0xC0FF) {
return readSwitches(address);
}
// If C800-CFFF isn't latched to a slot ROM, and we try to
// access a slot's memory space from C100-C7FF, then we need
// to latch in the slot's ROM.
if (slotLatch == -1 && address >= 0xc100 && address <= 0xc7ff) {
slotLatch = (address >> 8) & 0x07;
if (slotLatch == 3 && slot3rom) {
// Back off: UTA2E p. 5-28: don't latch in slot 3 ROM while
// the slot3rom flag is enabled
// fixme
slotLatch = 3;
} else {
updateMemoryPages();
}
}
// If we access CFFF, that unlatches slot ROM.
if (address == 0xCFFF) {
slotLatch = -1;
updateMemoryPages();
}
uint8_t res = readPages[address >> 8][address & 0xFF];
return res;
}
// Bypass MMU and read directly from a given page - also bypasses switches
uint8_t AppleMMU::readDirect(uint16_t address, uint8_t fromPage)
{
return ramPages[address >> 8][fromPage][address & 0xFF];
}
// Bypass MMU and write directly to a given page - also bypasses switches
void AppleMMU::writeDirect(uint16_t address, uint8_t fromPage, uint8_t val)
{
ramPages[address >> 8][fromPage][address & 0xFF] = val;
}
void AppleMMU::write(uint16_t address, uint8_t v)
{
if (address >= 0xC000 &&
address <= 0xC0FF) {
return writeSwitches(address, v);
}
// Don't allow writes to ROM
// Hard ROM, I/O, slots, whatnot
if (address >= 0xC100 && address <= 0xCFFF)
return;
// Bank-switched ROM/RAM areas
if (address >= 0xD000 && address <= 0xFFFF && !writebsr) {
return;
}
writePages[address >> 8][address & 0xFF] = v;
if (address >= 0x400 &&
address <= 0x7FF) {
// If it's text mode, or mixed mode, or lores graphics mode, then update.
if ((switches & S_TEXT) || (switches & S_MIXED) || (!(switches & S_HIRES))) {
// Force a redraw
display->modeChange();
}
return;
}
if (address >= 0x2000 &&
address <= 0x5FFF) {
if (switches & S_HIRES) {
// Force a redraw
display->modeChange();
}
}
}
// FIXME: this is no longer "MMU", is it?
void AppleMMU::resetDisplay()
{
updateMemoryPages();
display->modeChange();
}
void AppleMMU::handleMemorySwitches(uint16_t address, uint16_t lastSwitch)
{
// many of these are spelled out here:
// http://apple2.org.za/gswv/a2zine/faqs/csa2pfaq.html
switch (address) {
// These are write-only and perform no action on read
case 0xC000: // CLR80STORE
switches &= ~S_80STORE;
break;
case 0xC001: // SET80STORE
switches |= S_80STORE;
break;
case 0xC002: // CLRAUXRD read from main 48k RAM
auxRamRead = false;
break;
case 0xC003: // SETAUXRD read from aux/alt 48k
auxRamRead = true;
break;
case 0xC004: // CLRAUXWR write to main 48k RAM
auxRamWrite = false;
break;
case 0xC005: // SETAUXWR write to aux/alt 48k
auxRamWrite = true;
break;
case 0xC006: // CLRCXROM use ROM on cards
intcxrom = false;
break;
case 0xC007: // SETCXROM use internal ROM
intcxrom = true;
break;
case 0xC008: // CLRAUXZP use main zero page, stack, LC
altzp = false;
break;
case 0xC009: // SETAUXZP use alt zero page, stack, LC
altzp = true;
break;
case 0xC00A: // CLRC3ROM use internal slot 3 ROM
slot3rom = false;
break;
case 0xC00B: // SETC3ROM use external slot 3 ROM
slot3rom = true;
break;
// Registers C080 - C08F control bank switching.
case 0xC080:
case 0xC081:
case 0xC082:
case 0xC083:
case 0xC084:
case 0xC085:
case 0xC086:
case 0xC087:
case 0xC088:
case 0xC089:
case 0xC08A:
case 0xC08B:
case 0xC08C:
case 0xC08D:
case 0xC08E:
case 0xC08F:
// Per ITA2E, p. 286:
// (address & 0x08) controls whether or not we are selecting from bank2. Per table 8-2,
// bank2 is active if address & 0x08 is zero. So if the bit is on, it's bank 1.
bank2 = (address & 0x08) ? false : true;
// (address & 0x04) is unused.
// (address & 0x02) is read-select: if it is set the same as
// (address & 0x01) then readbsr is true.
readbsr = ((address & 0x02) >> 1) == (address & 0x01);
// (address & 0x01) is write-select: if 1, we write BSR RAM; if 0, we write ROM.
// But it's a little more complicated than readbsr.
// Per UTA2E p. 5-23:
// "Writing to high RAM is enabled when the HRAMWRT' soft switch
// is reset. ... It is reset by even read access or any write
// access in the $C08X range. HRAMWRT' is reset by odd read
// access in the $C08X range when PRE-WRITE is set. It is set by
// even access in the CC08X range. Any other type of access
// causes HRAMWRT' to hold its current state."
if (address & 0x01) {
if (preWriteFlag)
writebsr = 1;
// Per UTA2E, p. 5-23: any other preWriteFlag leaves writebsr unchanged.
} else {
writebsr = false;
}
break;
}
updateMemoryPages();
}
// many (most? all?) switches are documented here:
// http://apple2.org.za/gswv/a2zine/faqs/csa2pfaq.html
uint8_t AppleMMU::readSwitches(uint16_t address)
{
static uint16_t lastReadSwitch = 0x0000;
static uint16_t thisReadSwitch = 0x0000;
lastReadSwitch = thisReadSwitch;
thisReadSwitch = address;
// If this is a read for any of the slot switches, and we have
// hardware in that slot, then return its result.
if (address >= 0xC090 && address <= 0xC0FF) {
for (uint8_t i=1; i<=7; i++) {
if (address >= (0xC080 | (i << 4)) &&
address <= (0xC08F | (i << 4))) {
if (slots[i]) {
return slots[i]->readSwitches(address & ~(0xC080 | (i<<4)));
}
else
return FLOATING;
}
}
}
switch (address) {
case 0xC010:
// consume the keyboard strobe flag
keyboardStrobe &= 0x7F;
return (anyKeyDown ? 0x80 : 0x00);
case 0xC080:
case 0xC081:
case 0xC082:
case 0xC083:
case 0xC084:
case 0xC085:
case 0xC086:
case 0xC087:
case 0xC088:
case 0xC089:
case 0xC08A:
case 0xC08B:
case 0xC08C:
case 0xC08D:
case 0xC08E:
case 0xC08F:
// but read does affect these, same as write
handleMemorySwitches(address, lastReadSwitch);
// UTA2E, p. 5-23: preWrite is set by odd read access, and reset
// by even read access
preWriteFlag = (address & 0x01);
break;
case 0xC00C: // CLR80VID disable 80-col video mode
if (switches & S_80COL) {
switches &= ~S_80COL;
resetDisplay();
}
break;
case 0xC00D: // SET80VID enable 80-col video mode
if (!(switches & S_80COL)) {
switches |= S_80COL;
resetDisplay();
}
break;
case 0xC00E: // CLRALTCH use main char set - norm LC, flash UC
switches &= ~S_ALTCH;
break;
case 0xC00F: // SETALTCH use alt char set - norm inverse, LC; no flash
switches |= S_ALTCH;
break;
case 0xC011: // RDLCBNK2
return bank2 ? 0x80 : 0x00;
case 0xC012: // RDLCRAM
return readbsr ? 0x80 : 0x00;
case 0xC013: // RDRAMRD
return auxRamRead ? 0x80 : 0x00;
case 0xC014: // RDRAMWR
return auxRamWrite ? 0x80 : 0x00;
case 0xC015: // RDCXROM
return intcxrom ? 0x80 : 0x00;
case 0xC016: // RDAUXZP
return altzp ? 0x80 : 0x00;
case 0xC017: // RDC3ROM
return slot3rom ? 0x80 : 0x00;
case 0xC018: // RD80COL
return (switches & S_80STORE) ? 0x80 : 0x00;
case 0xC019: // RDVBLBAR -- vertical blanking, for 4550 cycles of every 17030
// Should return 0 for 4550 of 17030 cycles. Since we're not really
// running full speed video, instead, I'm returning 0 for 4096 (2^12)
// of every 16384 (2^14) cycles; the math is easier.
if ((g_cpu->cycles & 0x3000) == 0x3000) {
return 0x00;
} else {
return 0xFF; // FIXME: is 0xFF correct? Or 0x80?
}
case 0xC01A: // RDTEXT
return ( (switches & S_TEXT) ? 0x80 : 0x00 );
case 0xC01B: // RDMIXED
return ( (switches & S_MIXED) ? 0x80 : 0x00 );
case 0xC01C: // RDPAGE2
return ( (switches & S_PAGE2) ? 0x80 : 0x00 );
case 0xC01D: // RDHIRES
return ( (switches & S_HIRES) ? 0x80 : 0x00 );
case 0xC01E: // RDALTCH
return ( (switches & S_ALTCH) ? 0x80 : 0x00 );
case 0xC01F: // RD80VID
return ( (switches & S_80COL) ? 0x80 : 0x00 );
case 0xC030: // SPEAKER
g_speaker->toggle();
break;
case 0xC050: // CLRTEXT
if (switches & S_TEXT) {
switches &= ~S_TEXT;
resetDisplay();
}
return FLOATING;
case 0xC051: // SETTEXT
if (!(switches & S_TEXT)) {
switches |= S_TEXT;
resetDisplay();
}
return FLOATING;
case 0xC052: // CLRMIXED
if (switches & S_MIXED) {
switches &= ~S_MIXED;
resetDisplay();
}
return FLOATING;
case 0xC053: // SETMIXED
if (!(switches & S_MIXED)) {
switches |= S_MIXED;
resetDisplay();
}
return FLOATING;
case 0xC054: // PAGE1
if (switches & S_PAGE2) {
switches &= ~S_PAGE2;
if (!(switches & S_80COL)) {
resetDisplay();
} else {
updateMemoryPages();
}
}
return FLOATING;
case 0xC055: // PAGE2
if (!(switches & S_PAGE2)) {
switches |= S_PAGE2;
if (!(switches & S_80COL)) {
resetDisplay();
} else {
updateMemoryPages();
}
}
return FLOATING;
case 0xC056: // CLRHIRES
if (switches & S_HIRES) {
switches &= ~S_HIRES;
resetDisplay();
}
return FLOATING;
case 0xC057: // SETHIRES
if (!(switches & S_HIRES)) {
switches |= S_HIRES;
resetDisplay();
}
return FLOATING;
case 0xC058: // annunciator 0 off
annunciators[0] = false;
g_keyboard->setAnnunciators();
return FLOATING;
case 0xC059: // annunciator 0 on
annunciators[0] = true;
g_keyboard->setAnnunciators();
return FLOATING;
case 0xC05A: // annunciator 1 off
annunciators[1] = false;
g_keyboard->setAnnunciators();
return FLOATING;
case 0xC05B: // annunciator 1 on
annunciators[1] = true;
g_keyboard->setAnnunciators();
return FLOATING;
case 0xC05C: // annunciator 2 off
annunciators[2] = false;
g_keyboard->setAnnunciators();
return FLOATING;
case 0xC05D: // annunciator 2 on
annunciators[2] = true;
g_keyboard->setAnnunciators();
return FLOATING;
case 0xC05E: // DHIRES ON
if (!(switches & S_DHIRES)) {
switches |= S_DHIRES;
resetDisplay();
}
return FLOATING;
case 0xC05F: // DHIRES OFF
if (switches & S_DHIRES) {
switches &= ~S_DHIRES;
resetDisplay();
}
return FLOATING;
// paddles
case 0xC061: // OPNAPPLE
return isOpenApplePressed ? 0x80 : 0x00;
case 0xC062: // CLSAPPLE
return isClosedApplePressed ? 0x80 : 0x00;
case 0xC063: // Button 2
return isButton2Pressed ? 0x80 : 0x00;
case 0xC070: // PDLTRIG
// It doesn't matter if we update readPages or writePages, because 0xC0
// has only one page.
readPages[0xC0][0x64] = readPages[0xC0][0x65] = 0xFF;
g_keyboard->startReading();
g_paddles->startReading();
return FLOATING;
}
if (address >= 0xc000 && address <= 0xc00f) {
// This is the keyboardStrobe support referenced in the switch statement above.
return keyboardStrobe;
}
return readPages[address >> 8][address & 0xFF];
}
void AppleMMU::writeSwitches(uint16_t address, uint8_t v)
{
// fixme: combine these with the last read switch
static uint16_t lastWriteSwitch = 0x0000;
static uint16_t thisWriteSwitch = 0x0000;
lastWriteSwitch = thisWriteSwitch;
thisWriteSwitch = address;
// If this is a write for any of the slot switches, and we have
// hardware in that slot, then return its result.
if (address >= 0xC090 && address <= 0xC0FF) {
for (uint8_t i=1; i<=7; i++) {
if (address >= (0xC080 | (i << 4)) &&
address <= (0xC08F | (i << 4))) {
if (slots[i]) {
slots[i]->writeSwitches(address & ~(0xC080 | (i<<4)), v);
return;
}
}
}
}
switch (address) {
case 0xC010:
case 0xC011: // Per Understanding the Apple //e, p. 7-3:
case 0xC012: // a write to any $C01x address causes
case 0xC013: // a clear of the keyboard strobe.
case 0xC014:
case 0xC015:
case 0xC016:
case 0xC017:
case 0xC018:
case 0xC019:
case 0xC01A:
case 0xC01B:
case 0xC01C:
case 0xC01D:
case 0xC01E:
case 0xC01F:
keyboardStrobe &= 0x7F;
return;
case 0xC050: // graphics mode
if (switches & S_TEXT) {
switches &= ~S_TEXT;
resetDisplay();
}
return;
case 0xC051:
if (!(switches & S_TEXT)) {
switches |= S_TEXT;
resetDisplay();
}
return;
case 0xC052: // "no mixed"
if (switches & S_MIXED) {
switches &= ~S_MIXED;
resetDisplay();
}
return;
case 0xC053: // "mixed"
if (!(switches & S_MIXED)) {
switches |= S_MIXED;
resetDisplay();
}
return;
case 0xC054: // page2 off
if (switches & S_PAGE2) {
switches &= ~S_PAGE2;
if (!(switches & S_80COL)) {
resetDisplay();
} else {
updateMemoryPages();
}
}
return;
case 0xC055: // page2 on
if (!(switches & S_PAGE2)) {
switches |= S_PAGE2;
if (!(switches & S_80COL)) {
resetDisplay();
} else {
updateMemoryPages();
}
}
return;
case 0xC056: // hires off
if (switches & S_HIRES) {
switches &= ~S_HIRES;
resetDisplay();
}
return;
case 0xC057: // hires on
if (!(switches & S_HIRES)) {
switches |= S_HIRES;
resetDisplay();
}
return;
case 0xC058: // annunciator 0 off
annunciators[0] = false;
g_keyboard->setAnnunciators();
return;
case 0xC059: // annunciator 0 on
annunciators[0] = true;
g_keyboard->setAnnunciators();
return;
case 0xC05A: // annunciator 1 off
annunciators[1] = false;
g_keyboard->setAnnunciators();
return;
case 0xC05B: // annunciator 1 on
annunciators[1] = true;
g_keyboard->setAnnunciators();
return;
case 0xC05C: // annunciator 2 off
annunciators[2] = false;
g_keyboard->setAnnunciators();
return;
case 0xC05D: // annunciator 2 on
annunciators[2] = true;
g_keyboard->setAnnunciators();
return;
case 0xC05E: // DHIRES ON
if (!(switches & S_DHIRES)) {
switches |= S_DHIRES;
resetDisplay();
}
return;
case 0xC05F: // DHIRES OFF
if (switches & S_DHIRES) {
switches &= ~S_DHIRES;
resetDisplay();
}
return;
// paddles
case 0xC070:
g_keyboard->startReading();
g_paddles->startReading();
writePages[0xC0][0x64] = writePages[0xC0][0x65] = 0xFF;
break;
case 0xC080:
case 0xC081:
case 0xC082:
case 0xC083:
case 0xC084:
case 0xC085:
case 0xC086:
case 0xC087:
case 0xC088:
case 0xC089:
case 0xC08A:
case 0xC08B:
case 0xC08C:
case 0xC08D:
case 0xC08E:
case 0xC08F:
// UTA2E, p. 5-23: preWrite is reset by any write access to these
preWriteFlag = 0;
// fall through...
case 0xC000:
case 0xC001:
case 0xC002:
case 0xC003:
case 0xC004:
case 0xC005:
case 0xC006:
case 0xC007:
case 0xC008:
case 0xC009:
case 0xC00A:
case 0xC00B:
handleMemorySwitches(address, lastWriteSwitch);
break;
case 0xC00C: // CLR80VID disable 80-col video mode
if (switches & S_80COL) {
switches &= ~S_80COL;
resetDisplay();
}
break;
case 0xC00D: // SET80VID enable 80-col video mode
if (!(switches & S_80COL)) {
switches |= S_80COL;
resetDisplay();
}
break;
case 0xC00E: // CLRALTCH use main char set - norm LC, flash UC
switches &= ~S_ALTCH;
break;
case 0xC00F: // SETALTCH use alt char set - norm inverse, LC; no flash
switches |= S_ALTCH;
break;
}
}
void AppleMMU::keyboardInput(uint8_t v)
{
keyboardStrobe = v | 0x80;
anyKeyDown = true;
}
void AppleMMU::setKeyDown(bool isTrue)
{
anyKeyDown = isTrue;
}
void AppleMMU::triggerPaddleTimer(uint8_t paddle)
{
writePages[0xC0][0x64 + paddle] = 0x00;
}
void AppleMMU::resetRAM()
{
switches = S_TEXT;
// Per UTA2E, p. 5-23:
// When a system reset occurs, all MMU soft switches are reset (turned off).
bank2 = false;
auxRamRead = auxRamWrite = false;
readbsr = writebsr = false;
altzp = false;
intcxrom = false;
slot3rom = false;
slotLatch = -1;
preWriteFlag = false;
// Clear all the pages
for (uint8_t i=0; i<0xFF; i++) {
for (uint8_t j=0; j<5; j++) {
if (ramPages[i][j]) {
for (uint16_t k=0; k<0x100; k++) {
ramPages[i][j][k] = 0;
}
}
}
// and set our expectation of what we're reading from/writing to
readPages[i] = writePages[i] = ramPages[i][0];
}
// Load system ROM
for (uint16_t i=0x80; i<=0xFF; i++) {
for (uint16_t k=0; k<0x100; k++) {
uint16_t idx = ((i-0x80) << 8) | k;
#ifdef TEENSYDUINO
uint8_t v = pgm_read_byte(&romData[idx]);
#else
uint8_t v = romData[idx];
#endif
for (int j=0; j<5; j++) {
// For the ROM section from 0xc100 .. 0xcfff, we load in to
// an alternate page space (INTCXROM).
if (i >= 0xc1 && i <= 0xcf) {
// If we want to convince the VM we've got 128k of RAM, we
// need to load C3 ROM in page 0 (but not 1, meaning there's
// a board installed); and C800.CFFF in both page [0] and [1]
// (meaning there's an extended 80-column ROM available,
// that is also physically in the slot).
// Everything else goes in page [1].
if (i == 0xc3)
ramPages[i][0][k] = v;
else if (i >= 0xc8)
ramPages[i][0][k] = ramPages[i][1][k] = v;
else
ramPages[i][1][k] = v;
} else {
// Everything else goes in page 0.
ramPages[i][0][k] = v;
}
}
}
}
// have each slot load its ROM
for (uint8_t slotnum = 0; slotnum <= 7; slotnum++) {
if (slots[slotnum]) {
slots[slotnum]->loadROM(ramPages[0xC0 + slotnum][0]);
}
}
// update the memory read/write flags &c. Not strictly necessary, if
// we're really setting all the RAM flags to the right default
// settings above - but better safe than sorry?
updateMemoryPages();
}
void AppleMMU::setSlot(int8_t slotnum, Slot *peripheral)
{
slots[slotnum] = peripheral;
if (slots[slotnum]) {
slots[slotnum]->loadROM(ramPages[0xC0 + slotnum][0]);
}
}
void AppleMMU::allocateMemory()
{
for (uint16_t i=0; i<0xC0; i++) {
for (uint8_t j=0; j<2; j++) {
ramPages[i][j] = (uint8_t *)malloc(0x100);
if (ramPages[i][j] == NULL) println("MMU Out of memory!");
}
for (uint8_t j=2; j<5; j++) {
ramPages[i][j] = NULL;
}
readPages[i] = ramPages[i][0];
writePages[i] = ramPages[i][0];
}
for (uint16_t i=0xC0; i<0x100; i++) {
for (uint8_t j=0; j<4; j++) {
ramPages[i][j] = (uint8_t *)malloc(0x100);
if (ramPages[i][j] == NULL) println("MMU Out of memory!");
}
for (uint8_t j=4; j<5; j++) {
ramPages[i][j] = NULL;
}
readPages[i] = ramPages[i][0];
writePages[i] = ramPages[i][0];
}
}
void AppleMMU::updateMemoryPages()
{
if (auxRamRead) {
for (uint8_t idx = 0x02; idx < 0xc0; idx++) {
readPages[idx] = ramPages[idx][1];
}
} else {
for (uint8_t idx = 0x02; idx < 0xc0; idx++) {
readPages[idx] = ramPages[idx][0];
}
}
if (auxRamWrite) {
for (uint8_t idx = 0x02; idx < 0xc0; idx++) {
writePages[idx] = ramPages[idx][1];
}
} else {
for (uint8_t idx = 0x02; idx < 0xc0; idx++) {
writePages[idx] = ramPages[idx][0];
}
}
if (switches & S_80STORE) {
// When S_80STORE is on, we switch 400-800 and 2000-4000 based on S_PAGE2.
// The behavior is different based on whether HIRESON/OFF is set.
if (switches & S_PAGE2) {
// Regardless of HIRESON/OFF, pages 0x400-0x7ff are switched on S_PAGE2
for (uint8_t idx = 0x04; idx < 0x08; idx++) {
readPages[idx] = ramPages[idx][1];
writePages[idx] = ramPages[idx][1];
}
// but 2000-3fff switches based on S_PAGE2 only if HIRES is on.
// HIRESOFF: 400-7ff doesn't switch based on read/write flags
// b/c it switches based on S_PAGE2 instead
// HIRESON: 400-800, 2000-3fff doesn't switch
// b/c they switch based on S_PAGE2 instead
// If HIRES is on, then we honor the PAGE2 setting; otherwise, we don't
for (uint8_t idx = 0x20; idx < 0x40; idx++) {
readPages[idx] = ramPages[idx][(switches & S_HIRES) ? 1 : 0];
writePages[idx] = ramPages[idx][(switches & S_HIRES) ? 1 : 0];
}
} else {
for (uint8_t idx = 0x04; idx < 0x08; idx++) {
readPages[idx] = ramPages[idx][0];
writePages[idx] = ramPages[idx][0];
}
for (uint8_t idx = 0x20; idx < 0x40; idx++) {
readPages[idx] = ramPages[idx][0];
writePages[idx] = ramPages[idx][0];
}
}
}
if (intcxrom) {
for (uint8_t idx = 0xc1; idx < 0xd0; idx++) {
readPages[idx] = ramPages[idx][1];
}
} else {
for (uint8_t idx = 0xc1; idx < 0xd0; idx++) {
readPages[idx] = ramPages[idx][0];
}
if (slot3rom) {
readPages[0xc3] = ramPages[0xc3][1];
for (int i=0xc8; i<=0xcf; i++) {
readPages[i] = ramPages[i][1];
}
}
}
// If slotLatch is set (!= -1), then we are mapping 2k of ROM
// for a given peripheral to C800..CFFF.
if (slotLatch != -1) {
// FIXME: the only peripheral we support this with right now is
// the 80-column card.
if (slotLatch == 3) {
for (int i=0xc8; i <= 0xcf; i++) {
readPages[i] = ramPages[i][1];
}
}
}
// set zero-page & stack pages based on altzp flag
if (altzp) {
for (uint8_t idx = 0x00; idx < 0x02; idx++) {
readPages[idx] = ramPages[idx][1];
writePages[idx] = ramPages[idx][1];
}
} else {
for (uint8_t idx = 0x00; idx < 0x02; idx++) {
readPages[idx] = ramPages[idx][0];
writePages[idx] = ramPages[idx][0];
}
}
// Set bank-switched ram reading from readbsr & bank2
if (readbsr) {
// 0xD0 - 0xE0 has 4 possible banks:
if (!bank2) {
// Bank 1 RAM: either in main RAM (1) or in the extended memory
// card (3):
for (uint8_t idx = 0xd0; idx < 0xe0; idx++) {
readPages[idx] = ramPages[idx][altzp ? 3 : 1];
}
} else {
// Bank 2 RAM: either in main RAM (2) or in the extended memory
// card (4):
for (uint8_t idx = 0xd0; idx < 0xe0; idx++) {
readPages[idx] = ramPages[idx][altzp ? 4 : 2];
}
}
// ... but 0xE0 - 0xFF has just the motherboard RAM (1) and
// extended memory card RAM (2):
for (uint16_t idx = 0xe0; idx < 0x100; idx++) {
readPages[idx] = ramPages[idx][altzp ? 2 : 1];
}
} else {
// Built-in ROM
for (uint16_t idx = 0xd0; idx < 0x100; idx++) {
readPages[idx] = ramPages[idx][0];
}
}
if (writebsr) {
if (!bank2) {
for (uint8_t idx = 0xd0; idx < 0xe0; idx++) {
writePages[idx] = ramPages[idx][altzp ? 3 : 1];
}
} else {
for (uint8_t idx = 0xd0; idx < 0xe0; idx++) {
writePages[idx] = ramPages[idx][altzp ? 4 : 2];
}
}
for (uint16_t idx = 0xe0; idx < 0x100; idx++) {
writePages[idx] = ramPages[idx][altzp ? 2 : 1];
}
} else {
for (uint16_t idx = 0xd0; idx < 0x100; idx++) {
writePages[idx] = ramPages[idx][0];
}
}
}

Wyświetl plik

@ -0,0 +1,99 @@
#ifndef __APPLEMMU_H
#define __APPLEMMU_H
#include <stdlib.h>
#include "appledisplay.h"
#include "Slot.h"
#include "mmu.h"
// when we read a nondeterministic result, we return FLOATING. Maybe
// some day we can come back here and figure out how to return what
// the Apple would have.
#define FLOATING 0
// Switches activated by various memory locations
enum {
S_TEXT = 0x0001,
S_MIXED = 0x0002,
S_HIRES = 0x0004,
S_PAGE2 = 0x0008,
S_80COL = 0x0010,
S_ALTCH = 0x0020,
S_80STORE = 0x0040,
S_DHIRES = 0x0080
};
typedef bool (*callback_t)(void *);
class AppleVM;
class AppleMMU : public MMU {
friend class AppleVM;
public:
AppleMMU(AppleDisplay *display);
virtual ~AppleMMU();
virtual uint8_t read(uint16_t address);
virtual uint8_t readDirect(uint16_t address, uint8_t fromPage);
virtual void write(uint16_t address, uint8_t v);
virtual void writeDirect(uint16_t address, uint8_t fromPage, uint8_t val);
virtual void Reset();
void keyboardInput(uint8_t v);
void setKeyDown(bool isTrue);
void triggerPaddleTimer(uint8_t paddle);
void resetRAM(); // used by BIOS on cold boot
void setSlot(int8_t slotnum, Slot *peripheral);
bool annunciators[3];
void resetDisplay();
void updateMemoryPages();
protected:
void allocateMemory();
uint8_t readSwitches(uint16_t address);
void writeSwitches(uint16_t address, uint8_t v);
void handleMemorySwitches(uint16_t address, uint16_t lastSwitch);
private:
AppleDisplay *display;
public: // 'public' for debugging
uint16_t switches;
bool auxRamRead;
bool auxRamWrite;
bool bank2;
bool readbsr;
bool writebsr;
bool altzp;
bool intcxrom;
bool slot3rom;
int8_t slotLatch;
bool preWriteFlag; // see UTA2E p. 5-23
Slot *slots[8]; // slots 0-7
uint8_t *ramPages[0x100][5];
uint8_t *readPages[0x100];
uint8_t *writePages[0x100];
uint8_t keyboardStrobe;
bool anyKeyDown;
public:
// FIXME: build a private API for these
bool isOpenApplePressed;
bool isClosedApplePressed;
bool isButton2Pressed;
};
#endif

Wyświetl plik

@ -0,0 +1,100 @@
#include "applevm.h"
#include "filemanager.h"
#include "cpu.h"
#include "appledisplay.h"
#include "applekeyboard.h"
#include "physicalkeyboard.h"
#include "globals.h"
AppleVM::AppleVM()
{
// FIXME: all this typecasting makes me knife-stabby
vmdisplay = new AppleDisplay(videoBuffer);
mmu = new AppleMMU((AppleDisplay *)vmdisplay);
vmdisplay->SetMMU((AppleMMU *)mmu);
disk6 = new DiskII((AppleMMU *)mmu);
((AppleMMU *)mmu)->setSlot(6, disk6);
keyboard = new AppleKeyboard((AppleMMU *)mmu);
#ifdef TEENSYDUINO
teensyClock = new TeensyClock((AppleMMU *)mmu);
((AppleMMU *)mmu)->setSlot(7, teensyClock);
#else
plfClock = new PlfClock((AppleMMU *)mmu);
((AppleMMU *)mmu)->setSlot(7, plfClock);
#endif
}
AppleVM::~AppleVM()
{
#ifdef TEENSYDUINO
delete teensyClock;
#else
delete plfClock;
#endif
delete disk6;
}
// fixme: make member vars
unsigned long paddleCycleTrigger[2] = {0, 0};
void AppleVM::triggerPaddleInCycles(uint8_t paddleNum,uint16_t cycleCount)
{
paddleCycleTrigger[paddleNum] = cycleCount + g_cpu->cycles;
}
void AppleVM::cpuMaintenance(uint32_t cycles)
{
for (uint8_t i=0; i<2; i++) {
if (paddleCycleTrigger[i] && cycles >= paddleCycleTrigger[i]) {
((AppleMMU *)mmu)->triggerPaddleTimer(i);
paddleCycleTrigger[i] = 0;
}
}
keyboard->maintainKeyboard(cycles);
}
void AppleVM::Reset()
{
disk6->Reset();
//((AppleMMU *)mmu)->resetRAM();
mmu->Reset();
g_cpu->pc = (((AppleMMU *)mmu)->read(0xFFFD) << 8) | ((AppleMMU *)mmu)->read(0xFFFC);
// give the keyboard a moment to depress keys upon startup
keyboard->maintainKeyboard(0);
}
void AppleVM::Monitor()
{
g_cpu->pc = 0xff69; // "call -151"
((AppleMMU *)mmu)->readSwitches(0xC054); // make sure we're in page 1
((AppleMMU *)mmu)->readSwitches(0xC056); // and that hires is off
((AppleMMU *)mmu)->readSwitches(0xC051); // and text mode is on
}
const char *AppleVM::DiskName(uint8_t drivenum)
{
return disk6->DiskName(drivenum);
}
void AppleVM::ejectDisk(uint8_t drivenum, bool drawIt)
{
disk6->ejectDisk(drivenum, drawIt);
}
void AppleVM::insertDisk(uint8_t drivenum, const char *filename, bool drawIt)
{
disk6->insertDisk(drivenum, filename, drawIt);
}
VMKeyboard * AppleVM::getKeyboard()
{
return keyboard;
}

Wyświetl plik

@ -0,0 +1,46 @@
#ifndef __APPLEVM_H
#define __APPLEVM_H
#include "cpu.h"
#include "appledisplay.h"
#include "diskii.h"
#include "vmkeyboard.h"
#undef TEENSYDUINO
#ifdef TEENSYDUINO
#include "teensy-clock.h"
#else
#include "plf-clock.h"
#endif
#include "vm.h"
class AppleVM : public VM {
public:
AppleVM();
virtual ~AppleVM();
void cpuMaintenance(uint32_t cycles);
virtual void Reset();
void Monitor();
virtual void triggerPaddleInCycles(uint8_t paddleNum,uint16_t cycleCount);
const char *DiskName(uint8_t drivenum);
void ejectDisk(uint8_t drivenum, bool drawIt = true);
void insertDisk(uint8_t drivenum, const char *filename, bool drawIt = true);
virtual VMKeyboard *getKeyboard();
DiskII *disk6;
protected:
VMKeyboard *keyboard;
#ifdef TEENSYDUINO
TeensyClock *teensyClock;
#else
PlfClock *plfClock;
#endif
};
#endif

Wyświetl plik

@ -0,0 +1,137 @@
// SmallFont.c from UTFT library
// Font Size : 8x12
// Memory usage : 1144 bytes
// # characters : 95
#ifndef TEENSYDUINO
#define PROGMEM
#endif
// xsize: 8; ysize: 0x0C; offset: 0x20; numchars: 0x5F
const uint8_t BiosFont[1441] PROGMEM={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // <Space>
0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x00, // !
0x00,0x28,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // "
0x00,0x00,0x28,0x28,0xFC,0x28,0x50,0xFC,0x50,0x50,0x00,0x00, // #
0x00,0x20,0x78,0xA8,0xA0,0x60,0x30,0x28,0xA8,0xF0,0x20,0x00, // $
0x00,0x00,0x48,0xA8,0xB0,0x50,0x28,0x34,0x54,0x48,0x00,0x00, // %
0x00,0x00,0x20,0x50,0x50,0x78,0xA8,0xA8,0x90,0x6C,0x00,0x00, // &
0x00,0x40,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // '
0x00,0x04,0x08,0x10,0x10,0x10,0x10,0x10,0x10,0x08,0x04,0x00, // (
0x00,0x40,0x20,0x10,0x10,0x10,0x10,0x10,0x10,0x20,0x40,0x00, // )
0x00,0x00,0x00,0x20,0xA8,0x70,0x70,0xA8,0x20,0x00,0x00,0x00, // *
0x00,0x00,0x20,0x20,0x20,0xF8,0x20,0x20,0x20,0x00,0x00,0x00, // +
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x80, // ,
0x00,0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00, // -
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, // .
0x00,0x08,0x10,0x10,0x10,0x20,0x20,0x40,0x40,0x40,0x80,0x00, // /
0x00,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00, // 0
0x00,0x00,0x20,0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, // 1
0x00,0x00,0x70,0x88,0x88,0x10,0x20,0x40,0x80,0xF8,0x00,0x00, // 2
0x00,0x00,0x70,0x88,0x08,0x30,0x08,0x08,0x88,0x70,0x00,0x00, // 3
0x00,0x00,0x10,0x30,0x50,0x50,0x90,0x78,0x10,0x18,0x00,0x00, // 4
0x00,0x00,0xF8,0x80,0x80,0xF0,0x08,0x08,0x88,0x70,0x00,0x00, // 5
0x00,0x00,0x70,0x90,0x80,0xF0,0x88,0x88,0x88,0x70,0x00,0x00, // 6
0x00,0x00,0xF8,0x90,0x10,0x20,0x20,0x20,0x20,0x20,0x00,0x00, // 7
0x00,0x00,0x70,0x88,0x88,0x70,0x88,0x88,0x88,0x70,0x00,0x00, // 8
0x00,0x00,0x70,0x88,0x88,0x88,0x78,0x08,0x48,0x70,0x00,0x00, // 9
0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x20,0x00,0x00, // :
0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x20,0x00, // ;
0x00,0x04,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x04,0x00,0x00, // <
0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0x00,0x00, // =
0x00,0x40,0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x40,0x00,0x00, // >
0x00,0x00,0x70,0x88,0x88,0x10,0x20,0x20,0x00,0x20,0x00,0x00, // ?
0x00,0x00,0x70,0x88,0x98,0xA8,0xA8,0xB8,0x80,0x78,0x00,0x00, // @
0x00,0x00,0x20,0x20,0x30,0x50,0x50,0x78,0x48,0xCC,0x00,0x00, // A
0x00,0x00,0xF0,0x48,0x48,0x70,0x48,0x48,0x48,0xF0,0x00,0x00, // B
0x00,0x00,0x78,0x88,0x80,0x80,0x80,0x80,0x88,0x70,0x00,0x00, // C
0x00,0x00,0xF0,0x48,0x48,0x48,0x48,0x48,0x48,0xF0,0x00,0x00, // D
0x00,0x00,0xF8,0x48,0x50,0x70,0x50,0x40,0x48,0xF8,0x00,0x00, // E
0x00,0x00,0xF8,0x48,0x50,0x70,0x50,0x40,0x40,0xE0,0x00,0x00, // F
0x00,0x00,0x38,0x48,0x80,0x80,0x9C,0x88,0x48,0x30,0x00,0x00, // G
0x00,0x00,0xCC,0x48,0x48,0x78,0x48,0x48,0x48,0xCC,0x00,0x00, // H
0x00,0x00,0xF8,0x20,0x20,0x20,0x20,0x20,0x20,0xF8,0x00,0x00, // I
0x00,0x00,0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x90,0xE0,0x00, // J
0x00,0x00,0xEC,0x48,0x50,0x60,0x50,0x50,0x48,0xEC,0x00,0x00, // K
0x00,0x00,0xE0,0x40,0x40,0x40,0x40,0x40,0x44,0xFC,0x00,0x00, // L
0x00,0x00,0xD8,0xD8,0xD8,0xD8,0xA8,0xA8,0xA8,0xA8,0x00,0x00, // M
0x00,0x00,0xDC,0x48,0x68,0x68,0x58,0x58,0x48,0xE8,0x00,0x00, // N
0x00,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00, // O
0x00,0x00,0xF0,0x48,0x48,0x70,0x40,0x40,0x40,0xE0,0x00,0x00, // P
0x00,0x00,0x70,0x88,0x88,0x88,0x88,0xE8,0x98,0x70,0x18,0x00, // Q
0x00,0x00,0xF0,0x48,0x48,0x70,0x50,0x48,0x48,0xEC,0x00,0x00, // R
0x00,0x00,0x78,0x88,0x80,0x60,0x10,0x08,0x88,0xF0,0x00,0x00, // S
0x00,0x00,0xF8,0xA8,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, // T
0x00,0x00,0xCC,0x48,0x48,0x48,0x48,0x48,0x48,0x30,0x00,0x00, // U
0x00,0x00,0xCC,0x48,0x48,0x50,0x50,0x30,0x20,0x20,0x00,0x00, // V
0x00,0x00,0xA8,0xA8,0xA8,0x70,0x50,0x50,0x50,0x50,0x00,0x00, // W
0x00,0x00,0xD8,0x50,0x50,0x20,0x20,0x50,0x50,0xD8,0x00,0x00, // X
0x00,0x00,0xD8,0x50,0x50,0x20,0x20,0x20,0x20,0x70,0x00,0x00, // Y
0x00,0x00,0xF8,0x90,0x10,0x20,0x20,0x40,0x48,0xF8,0x00,0x00, // Z
0x00,0x38,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x38,0x00, // [
0x00,0x40,0x40,0x40,0x20,0x20,0x10,0x10,0x10,0x08,0x00,0x00, // <Backslash>
0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x70,0x00, // ]
0x00,0x20,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ^
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC, // _
0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // '
0x00,0x00,0x00,0x00,0x00,0x30,0x48,0x38,0x48,0x3C,0x00,0x00, // a
0x00,0x00,0xC0,0x40,0x40,0x70,0x48,0x48,0x48,0x70,0x00,0x00, // b
0x00,0x00,0x00,0x00,0x00,0x38,0x48,0x40,0x40,0x38,0x00,0x00, // c
0x00,0x00,0x18,0x08,0x08,0x38,0x48,0x48,0x48,0x3C,0x00,0x00, // d
0x00,0x00,0x00,0x00,0x00,0x30,0x48,0x78,0x40,0x38,0x00,0x00, // e
0x00,0x00,0x1C,0x20,0x20,0x78,0x20,0x20,0x20,0x78,0x00,0x00, // f
0x00,0x00,0x00,0x00,0x00,0x3C,0x48,0x30,0x40,0x78,0x44,0x38, // g
0x00,0x00,0xC0,0x40,0x40,0x70,0x48,0x48,0x48,0xEC,0x00,0x00, // h
0x00,0x00,0x20,0x00,0x00,0x60,0x20,0x20,0x20,0x70,0x00,0x00, // i
0x00,0x00,0x10,0x00,0x00,0x30,0x10,0x10,0x10,0x10,0x10,0xE0, // j
0x00,0x00,0xC0,0x40,0x40,0x5C,0x50,0x70,0x48,0xEC,0x00,0x00, // k
0x00,0x00,0xE0,0x20,0x20,0x20,0x20,0x20,0x20,0xF8,0x00,0x00, // l
0x00,0x00,0x00,0x00,0x00,0xF0,0xA8,0xA8,0xA8,0xA8,0x00,0x00, // m
0x00,0x00,0x00,0x00,0x00,0xF0,0x48,0x48,0x48,0xEC,0x00,0x00, // n
0x00,0x00,0x00,0x00,0x00,0x30,0x48,0x48,0x48,0x30,0x00,0x00, // o
0x00,0x00,0x00,0x00,0x00,0xF0,0x48,0x48,0x48,0x70,0x40,0xE0, // p
0x00,0x00,0x00,0x00,0x00,0x38,0x48,0x48,0x48,0x38,0x08,0x1C, // q
0x00,0x00,0x00,0x00,0x00,0xD8,0x60,0x40,0x40,0xE0,0x00,0x00, // r
0x00,0x00,0x00,0x00,0x00,0x78,0x40,0x30,0x08,0x78,0x00,0x00, // s
0x00,0x00,0x00,0x20,0x20,0x70,0x20,0x20,0x20,0x18,0x00,0x00, // t
0x00,0x00,0x00,0x00,0x00,0xD8,0x48,0x48,0x48,0x3C,0x00,0x00, // u
0x00,0x00,0x00,0x00,0x00,0xEC,0x48,0x50,0x30,0x20,0x00,0x00, // v
0x00,0x00,0x00,0x00,0x00,0xA8,0xA8,0x70,0x50,0x50,0x00,0x00, // w
0x00,0x00,0x00,0x00,0x00,0xD8,0x50,0x20,0x50,0xD8,0x00,0x00, // x
0x00,0x00,0x00,0x00,0x00,0xEC,0x48,0x50,0x30,0x20,0x20,0xC0, // y
0x00,0x00,0x00,0x00,0x00,0x78,0x10,0x20,0x20,0x78,0x00,0x00, // z
0x00,0x18,0x10,0x10,0x10,0x20,0x10,0x10,0x10,0x10,0x18,0x00, // {
0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, // |
0x00,0x60,0x20,0x20,0x20,0x10,0x20,0x20,0x20,0x20,0x60,0x00, // }
0x40,0xA4,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ~
0x00,0x00,0x00,0x20,0x60,0xFF,0xFF,0x60,0x20,0x00,0x00,0x00, //0x7F, 'bkspace'
0x00,0x00,0xE0,0x89,0x92,0xD2,0x8A,0x8A,0x8A,0xD1,0x00,0x00, //0x80, 'esc'
0x00,0x00,0xE0,0x42,0x52,0x4B,0x5B,0x5B,0x5B,0x4A,0x00,0x00, //0x81, 'tab'
0x00,0x00,0x40,0xA1,0x89,0x8D,0x89,0x89,0xA9,0x45,0x00,0x00, //0x82, 'ctl'
0x00,0x00,0x40,0xA8,0x88,0x4C,0x2A,0x2A,0xAA,0x4A,0x00,0x00, //0x83, 'sh'
0x00,0x00,0x14,0xA4,0x26,0xB4,0xA4,0xA4,0xA4,0xA2,0x00,0x00, //0x84, 'ift'
0x00,0x06,0x08,0x6E,0x99,0x83,0x84,0x84,0xC3,0x66,0x3C,0x00, //0x85, 'lapple'
0x00,0x06,0x08,0x6E,0xFF,0xFF,0xFC,0xFC,0xFF,0x7E,0x3C,0x00, //0x86, 'rapple'
0x00,0x00,0x00,0x70,0x8F,0x8F,0x8F,0x70,0x00,0x00,0x00,0x00, //0x87, 'ljoy'
0x00,0x00,0x00,0x0E,0xF1,0xF1,0xF1,0x0E,0x00,0x00,0x00,0x00, //0x88, 'rjoy'
0x00,0x00,0x38,0x44,0x44,0x44,0x38,0x38,0x38,0x38,0x00,0x00, //0x89, 'ujoy'
0x00,0x00,0x38,0x38,0x38,0x38,0x44,0x44,0x44,0x38,0x00,0x00, //0x8A, 'djoy'
0x00,0x00,0x03,0x0F,0x39,0xE1,0xE1,0x39,0x0F,0x03,0x00,0x00, //0x8B, 'lkey'
0x00,0x00,0xC0,0xF0,0x9C,0x87,0x87,0x9C,0xF0,0xC0,0x00,0x00, //0x8C, 'rkey'
0x00,0x00,0x18,0x18,0x3C,0x24,0x66,0x42,0xC3,0xFF,0x00,0x00, //0x8D, 'ukey'
0x00,0x00,0xFF,0xC3,0x42,0x66,0x24,0x3C,0x18,0x18,0x00,0x00, //0x8E, 'dkey'
0x00,0x00,0x03,0x03,0x23,0x63,0xFF,0xFE,0x60,0x20,0x00,0x00, //0x8F, 'ent'
0x00,0x00,0x40,0xA0,0x80,0x9B,0x55,0x35,0x33,0xB1,0x51,0x00, //0x90, 'srq'
0x00,0x00,0x40,0xAC,0x8A,0x4A,0x2C,0x28,0xA8,0x48,0x00,0x00, //0x91, 'sp'
0x00,0x00,0x00,0x8B,0x52,0x52,0xD3,0xD2,0xD2,0x4B,0x00,0x00, //0x92, 'ace'
0x00,0x00,0x80,0x84,0x8A,0x8A,0x8A,0x8A,0x8A,0xE4,0x00,0x00, //0x93, 'lo'
0x00,0x00,0x00,0x4A,0xAA,0x8C,0x8C,0x8A,0xAA,0x4A,0x00,0x00, //0x94, 'ck'
0x00,0x00,0x90,0x90,0xD5,0xB5,0x95,0x95,0x95,0x92,0x00,0x00, //0x95, 'nu'
0x00,0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x77,0x00,0x00, //0x96, 'll'
0x00,0x00,0xFF,0x81,0x82,0x99,0x99,0x81,0x99,0xFF,0x00,0x00, //0x97, 'disk'
0x00,
};

Wyświetl plik

@ -0,0 +1,59 @@
#ifndef __BIOS_H
#define __BIOS_H
//#include <Arduino.h>
#include <stdint.h>
#define BIOS_MAXFILES 10 // number of files in a page of listing
#define BIOS_MAXPATH 127 // maximum length of a single filename that we'll support
class BIOS {
public:
BIOS();
~BIOS();
// return true if a persistent change needs to be stored in EEPROM
bool runUntilDone();
bool updateDiagnostics();
private:
uint8_t GetAction(int8_t &prevAction);
bool isActionActive(int8_t action);
void DrawMainMenu(int8_t selection);
void UpdateMainMenu(int8_t selection);
void DrawMenuItem(int8_t item, int8_t selection);
void ClearMenuItem(int8_t item);
void drawKeyboard();
void drawInfo();
void drawThumb();
bool stickTrim();
bool stickSpeed();
void WarmReset();
void ColdReboot();
bool SelectDiskImage();
void DrawDiskName(uint8_t index, uint8_t page, int8_t selection);
void DrawDiskNames(uint8_t page, int8_t selection, bool force);
uint8_t GatherFilenames(uint8_t pageOffset);
bool HasNextPage(uint8_t pageOffset);
// bool GetFilename(uint8_t pageOffset, uint8_t index);
void stripDirectory();
bool saveState();
bool restoreState();
private:
void biosIdle();
void drawBiosScreen(int8_t prevAction);
int8_t selectedFile;
char fileDirectory[BIOS_MAXFILES][BIOS_MAXPATH+1];
int8_t fileCount;
bool hasNextPage;
bool inMainMenu;
// char fileName[BIOS_MAXPATH + 1];
char rootPath[255-BIOS_MAXPATH];
int8_t dirPage;
};
#endif

Plik diff jest za duży Load Diff

Wyświetl plik

@ -0,0 +1,77 @@
#ifndef __CPU_H
#define __CPU_H
#include <stdlib.h>
#include <stdint.h>
class MMU;
// Flags (P) register bit definitions.
// Negative
#define F_N (1<<7)
// Overflow
#define F_V (1<<6)
#define F_UNK (1<<5) // What the heck is this?
// Break
#define F_B (1<<4)
// Decimal
#define F_D (1<<3)
// Interrupt Disable
#define F_I (1<<2)
// Zero
#define F_Z (1<<1)
// Carry
#define F_C (1<<0)
class Cpu {
public:
Cpu();
~Cpu();
void Reset();
void nmi();
void rst();
void brk();
void irq();
uint8_t Run(uint8_t numSteps);
uint8_t step();
uint8_t X();
uint8_t Y();
uint8_t A();
uint16_t PC();
uint8_t SP();
uint8_t P();
void stageIRQ();
protected:
// Stack manipulation
void pushS8(uint8_t b);
void pushS16(uint16_t w);
uint8_t popS8();
uint16_t popS16();
public:
void SetMMU(MMU *mmu) { this->mmu = mmu; }
public:
uint16_t pc;
uint8_t sp;
uint8_t a;
uint8_t x;
uint8_t y;
uint8_t flags;
uint64_t cycles;
bool irqPending;
MMU *mmu;
};
#endif

Wyświetl plik

@ -0,0 +1,16 @@
#ifndef TEENSYDUINO
#define PROGMEM
#endif
static uint8_t romData[256] PROGMEM =
{
162,32,160,0,162,3,134,60,138,10,36,60,240,16,5,60,73,255,41,126,176,8,74,208,251,152,157,86,3,200,232,
16,229,32,88,255,186,189,0,1,10,10,10,10,133,43,170,189,142,192,189,140,192,189,138,192,189,137,192,160,
80,189,128,192,152,41,3,10,5,43,170,189,129,192,169,86,32,168,252,136,16,235,133,38,133,61,133,65,169,
8,133,39,24,8,189,140,192,16,251,73,213,208,247,189,140,192,16,251,201,170,208,243,234,189,140,192,16,
251,201,150,240,9,40,144,223,73,173,240,37,208,217,160,3,133,64,189,140,192,16,251,42,133,60,189,140,
192,16,251,37,60,136,208,236,40,197,61,208,190,165,64,197,65,208,184,176,183,160,86,132,60,188,140,192,
16,251,89,214,2,164,60,136,153,0,3,208,238,132,60,188,140,192,16,251,89,214,2,164,60,145,38,200,208,239,
188,140,192,16,251,89,214,2,208,135,160,0,162,86,202,48,251,177,38,94,0,3,42,94,0,3,42,145,38,200,208,
238,230,39,230,61,165,61,205,0,8,166,43,144,219,76,1,8,0,0,0,0,0
};

Wyświetl plik

@ -0,0 +1,528 @@
#include "diskii.h"
#ifdef TEENSYDUINO
#include <Arduino.h>
#define println(x) Serial.println(x)
#else
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define println(x) {}
#endif
#include "applemmu.h" // for FLOATING
#include "globals.h"
#include "diskii-rom.h"
DiskII::DiskII(AppleMMU *mmu)
{
this->trackBuffer = new RingBuf(NIBTRACKSIZE);
this->rawTrackBuffer = (uint8_t *)malloc(4096);
if (this->rawTrackBuffer == NULL) println("DiskII Out of memory!");
this->mmu = mmu;
curTrack = 0;
trackDirty = false;
trackToRead = -1;
trackToFlush = -1;
writeMode = false;
writeProt = false; // FIXME: expose an interface to this
readWriteLatch = 0x00;
disk[0] = disk[1] = -1;
indicatorIsOn[0] = indicatorIsOn[1] = 0;
selectedDisk = 0;
diskType[0] = diskType[1] = dosDisk;
}
DiskII::~DiskII()
{
delete this->trackBuffer; this->trackBuffer = NULL;
//free(this->rawTrackBuffer); this->rawTrackBuffer = NULL;
}
void DiskII::Reset()
{
curTrack = 0;
trackDirty = false;
writeMode = false;
writeProt = false; // FIXME: expose an interface to this
readWriteLatch = 0x00;
ejectDisk(0);
ejectDisk(1);
}
void DiskII::checkFlush(int8_t track)
{
if (trackDirty && trackToFlush == -1) {
diskToFlush = selectedDisk;
trackToFlush = track;
trackDirty = false; // just so we don't overwrite disk/track to flush before continuing...
}
}
uint8_t DiskII::readSwitches(uint8_t s)
{
switch (s) {
case 0x00: // change stepper motor phase
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
step(s);
break;
case 0x08: // drive off
indicatorIsOn[selectedDisk] = 99;
g_display->setDriveIndicator(selectedDisk, false); // FIXME: after a spell...
checkFlush(curTrack);
break;
case 0x09: // drive on
indicatorIsOn[selectedDisk] = 100;
g_display->setDriveIndicator(selectedDisk, true);
break;
case 0x0A: // select drive 1
select(0);
break;
case 0x0B: // select drive 2
select(1);
break;
case 0x0C: // shift one read or write byte
readWriteLatch = readOrWriteByte();
break;
case 0x0D: // load data register (latch)
// This is complex and incomplete. cf. Logic State Sequencer,
// UTA2E, p. 9-14
if (!writeMode && indicatorIsOn[selectedDisk]) {
if (isWriteProtected())
readWriteLatch |= 0x80;
else
readWriteLatch &= 0x7F;
}
break;
case 0x0E: // set read mode
setWriteMode(false);
// FIXME: with this shortcut here, disk access speeds up ridiculously.
// Is this breaking anything?
return ( (readOrWriteByte() & 0x7F) |
(isWriteProtected() ? 0x80 : 0x00) );
break;
case 0x0F: // set write mode
setWriteMode(true);
break;
}
// FIXME: improve the spin-down here. We need a CPU cycle callback
// for some period of time instead of this silly decrement counter.
if (!indicatorIsOn[selectedDisk]) {
// printf("Unexpected read while disk isn't on?\n");
indicatorIsOn[selectedDisk] = 100;
g_display->setDriveIndicator(selectedDisk, true);
}
if (indicatorIsOn[selectedDisk] > 0 && indicatorIsOn[selectedDisk] < 100) {
// slowly spin it down...
if (--indicatorIsOn[selectedDisk] == 0) {
g_display->setDriveIndicator(selectedDisk, false);
}
}
// Any even address read returns the readWriteLatch (UTA2E Table 9.1,
// p. 9-12, note 2)
return (s & 1) ? FLOATING : readWriteLatch;
}
void DiskII::writeSwitches(uint8_t s, uint8_t v)
{
switch (s) {
case 0x00: // change stepper motor phase
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
step(s);
break;
case 0x08: // drive off
break;
case 0x09: // drive on
break;
case 0x0A: // select drive 1
select(0);
break;
case 0x0B: // select drive 2
select(1);
break;
case 0x0C: // shift one read or write byte
readOrWriteByte();
break;
case 0x0D: // drive write
break;
case 0x0E: // set read mode
setWriteMode(false);
break;
case 0x0F: // set write mode
setWriteMode(true);
break;
}
// All writes update the latch
if (writeMode) {
readWriteLatch = v;
}
}
// where phase is the address poked & 0x07 (e.g. "0xC0E0 & 0x07")
void DiskII::step(uint8_t phase)
{
static int mag[4] = { 0,0,0,0 };
static int pmag[4] = { 0, 0, 0, 0 };
static int ppmag[4] = { 0, 0, 0, 0 };
static int pnum = 0;
static int ppnum = 0;
static int trackPos = 0;
static int prevTrack = 0;
// phase &= 7;
int magnet_number = phase >> 1;
// shuffle data down
ppmag[ppnum] = pmag[ppnum];
ppnum = pnum;
pmag[pnum] = mag[pnum];
pnum = magnet_number;
if ((phase & 1) == 0) {
mag[magnet_number] = 0;
} else {
if (ppmag[(magnet_number + 1) & 3]) {
if (--trackPos < 0) {
trackPos = 0;
// recalibrate...
}
}
if (ppmag[(magnet_number - 1) & 3]) {
// FIXME: don't go beyond the end of the media. For a 35-track disk, that's 68 == ((35-1) * 2).
if (++trackPos > 68) {
trackPos = 68;
}
}
mag[magnet_number] = 1;
}
curTrack = (trackPos + 1) / 2;
if (curTrack != prevTrack) {
// We're about to change tracks - be sure to flush the track if we've written to it
checkFlush(prevTrack);
// step to the appropriate track
prevTrack = curTrack;
// mark it to be read
trackToRead = curTrack;
}
}
bool DiskII::isWriteProtected()
{
return (writeProt ? 0xFF : 0x00);
}
void DiskII::setWriteMode(bool enable)
{
writeMode = enable;
}
static uint8_t _lc(char c)
{
if (c >= 'A' && c <= 'Z') {
c = c - 'A' + 'a';
}
return c;
}
static bool _endsWithI(const char *s1, const char *s2)
{
if (strlen(s2) > strlen(s1)) {
return false;
}
const char *p = &s1[strlen(s1)-1];
int16_t l = strlen(s2)-1;
while (l >= 0) {
if (_lc(*p--) != _lc(s2[l]))
return false;
l--;
}
return true;
}
void DiskII::insertDisk(int8_t driveNum, const char *filename, bool drawIt)
{
ejectDisk(driveNum);
disk[driveNum] = g_filemanager->openFile(filename);
if (drawIt)
g_display->drawDriveDoor(driveNum, false);
if (_endsWithI(filename, ".nib")) {
diskType[driveNum] = nibDisk;
} else if (_endsWithI(filename, ".po")) {
diskType[driveNum] = prodosDisk;
} else {
diskType[driveNum] = dosDisk;
#ifndef TEENSYDUINO
// debugging: make a nib copy of the image to play with
// convertDskToNib("/tmp/debug.nib");
#endif
}
}
void DiskII::ejectDisk(int8_t driveNum, bool drawIt)
{
if (disk[driveNum] != -1) {
g_filemanager->closeFile(disk[driveNum]);
disk[driveNum] = -1;
if (drawIt) g_display->drawDriveDoor(driveNum, true);
}
}
void DiskII::select(int8_t which)
{
if (which != 0 && which != 1)
return;
if (which != selectedDisk) {
indicatorIsOn[selectedDisk] = 0;
g_display->setDriveIndicator(selectedDisk, false);
checkFlush(curTrack);
}
// set the selected disk drive
selectedDisk = which;
}
uint8_t DiskII::readOrWriteByte()
{
if (disk[selectedDisk] == -1) {
return GAP;
}
if (writeMode && !writeProt) {
if (!trackBuffer->hasData()) {
// Error: writing to empty track buffer? That's a raw write w/o
// knowing where we are on the disk.
return GAP;
}
trackDirty = true;
// It's possible that a badly behaving OS could try to write more
// data than we have buffer to handle. Don't let it. We should
// only need something like 500 bytes, at worst. In the typical
// case, we're talking about something like
//
// ~5 bytes of GAP
// 3 bytes of sector prolog
// 2 bytes of volume
// 2 bytes of track
// 2 bytes of sector
// 2 bytes of checksum
// 2 bytes of epilog
// ~5 bytes of GAP
// 3 bytes of data prolog
// 342 bytes of GRP-encoded (6:2) data
// 1 byte of checksum
// 3 bytes of epilog
// 1 byte of GAP
// == 373 bytes
//
// ... so if we get up to the full 1024 we've allocated, there's
// something suspicious happening.
if (readWriteLatch < 0x96) {
// Can't write a de-nibblized byte...
g_display->debugMsg("DII: bad write");
return 0;
}
trackBuffer->replaceByte(readWriteLatch);
return 0;
}
// trackToRead is -1 when we have a filled buffer, or we have no data at all.
// trackToRead is != -1 when we're flushing our buffer and re-filling it.
//
// Don't fill it right here, b/c we don't want to bog down the CPU
// thread/ISR.
if (trackToRead == curTrack) {// waiting for a read to complete
return GAP;
}
if ((trackToRead != -1) || !trackBuffer->hasData()) {
checkFlush(curTrack);
// Need to read in a track of data and nibblize it. We'll return 0xFF
// until that completes.
// This might update trackToRead with a different track than the
// one we're reading. When we finish the read, we'll need to check
// to be sure that we're still trying to read the same track that
// we started with.
trackToRead = curTrack;
// While we're waiting for the sector to come around, we'll return
// GAP bytes.
return GAP;
}
return trackBuffer->peekNext();
}
void DiskII::fillDiskBuffer()
{
if (trackToFlush != -1) {
flushTrack(trackToFlush, diskToFlush); // in case it's dirty: flush before changing drives
trackBuffer->clear();
trackToFlush = -1;
}
// No work to do if trackToRead is -1
if (trackToRead == -1)
return;
trackDirty = false;
trackBuffer->clear();
int8_t trackWeAreReading = trackToRead;
int8_t diskWeAreUsing = selectedDisk;
trackBuffer->clear();
trackBuffer->setPeekCursor(0);
if (diskType[diskWeAreUsing] == nibDisk) {
// Read one nibblized sector at a time and jam it in trackBuf
// directly. We don't read the whole track at once only because
// of RAM constraints on the Teensy. There's no reason we
// couldn't, though, if RAM weren't at a premium.
for (int i=0; i<2; i++) {
g_filemanager->seekBlock(disk[diskWeAreUsing], trackWeAreReading * 16 + (i * 8), diskType[diskWeAreUsing] == nibDisk);
if (!g_filemanager->readBlocks(disk[diskWeAreUsing], rawTrackBuffer, 8, diskType[diskWeAreUsing] == nibDisk)) {
// FIXME: error handling?
trackToRead = -1;
return;
}
trackBuffer->addBytes(rawTrackBuffer, 416 * 8);
}
} else {
// It's a .dsk / .po disk image. Read the whole track in to
// rawTrackBuffer and nibblize it.
g_filemanager->seekBlock(disk[diskWeAreUsing], trackWeAreReading * 16, diskType[diskWeAreUsing] == nibDisk);
if (!g_filemanager->readTrack(disk[diskWeAreUsing], rawTrackBuffer, diskType[diskWeAreUsing] == nibDisk)) {
// FIXME: error handling?
trackToRead = -1;
return;
}
nibblizeTrack(trackBuffer, rawTrackBuffer, diskType[diskWeAreUsing], curTrack);
}
// Make sure we're still intending to read the track we just read
if (trackWeAreReading != trackToRead ||
diskWeAreUsing != selectedDisk) {
// Abort and let it start over next time
return;
}
// Buffer is full, we're done - reset trackToRead and that will let the reads reach the CPU!
trackToRead = -1;
}
const char *DiskII::DiskName(int8_t num)
{
if (disk[num] != -1)
return g_filemanager->fileName(disk[num]);
return "";
}
void DiskII::loadROM(uint8_t *toWhere)
{
#ifdef TEENSYDUINO
println("loading DiskII rom");
for (uint16_t i=0; i<=0xFF; i++) {
toWhere[i] = pgm_read_byte(&romData[i]);
}
#else
printf("loading DiskII rom\n");
memcpy(toWhere, romData, 256);
#endif
}
void DiskII::flushTrack(int8_t track, int8_t sel)
{
// safety check: if we're write-protected, then how did we get here?
if (writeProt) {
g_display->debugMsg("Write Protected");
return;
}
if (!trackBuffer->hasData()) {
// Dunno what happened - we're writing but haven't initialized the sector buffer?
return;
}
if (diskType[sel] == nibDisk) {
// Write the whole track out exactly as we've got it. Hopefully
// someone has re-calcuated appropriate checksums on it...
g_display->debugMsg("Not writing Nib image");
return;
}
nibErr e = denibblizeTrack(trackBuffer, rawTrackBuffer, diskType[sel], curTrack);
switch (e) {
case errorShortTrack:
g_display->debugMsg("DII: short track");
trackBuffer->clear();
return;
case errorMissingSectors:
g_display->debugMsg("DII: missing sectors");
trackBuffer->clear();
break;
case errorNone:
break;
}
// ok, write the track!
g_filemanager->seekBlock(disk[sel], track * 16);
g_filemanager->writeTrack(disk[sel], rawTrackBuffer);
}

Wyświetl plik

@ -0,0 +1,71 @@
#ifndef __DISKII_H
#define __DISKII_H
#ifdef TEENSYDUINO
#include <Arduino.h>
#else
#include <stdint.h>
#include <stdio.h>
#endif
#include "filemanager.h"
#include "applemmu.h"
#include "Slot.h"
#include "RingBuf.h"
#include "nibutil.h"
class DiskII : public Slot {
public:
DiskII(AppleMMU *mmu);
virtual ~DiskII();
virtual void Reset(); // used by BIOS cold-boot
virtual uint8_t readSwitches(uint8_t s);
virtual void writeSwitches(uint8_t s, uint8_t v);
virtual void loadROM(uint8_t *toWhere);
void insertDisk(int8_t driveNum, const char *filename, bool drawIt = true);
void ejectDisk(int8_t driveNum, bool drawIt = true);
const char *DiskName(int8_t num);
void flushTrack(int8_t track, int8_t sel);
void fillDiskBuffer(); // called from main loop
private:
void step(uint8_t phase);
bool isWriteProtected();
void setWriteMode(bool enable);
void select(int8_t which); // 0 or 1 for drives 1 and 2, respectively
uint8_t readOrWriteByte();
void checkFlush(int8_t track);
#ifndef TEENSYDUINO
void convertDskToNib(const char *outFN);
#endif
private:
volatile uint8_t curTrack;
volatile bool trackDirty; // does this track need flushing to disk?
uint8_t readWriteLatch;
RingBuf *trackBuffer; // nibblized data
uint8_t *rawTrackBuffer; // not nibblized data
bool writeMode;
bool writeProt;
AppleMMU *mmu;
int8_t disk[2];
volatile uint8_t indicatorIsOn[2];
uint8_t diskType[2];
volatile int8_t trackToRead; // -1 when we're idle; not -1 when we need to read a track.
volatile int8_t selectedDisk;
volatile int8_t trackToFlush; // -1 when there's none
volatile int8_t diskToFlush; // which selected disk are we writing to?
};
#endif

Plik diff jest za duży Load Diff

Wyświetl plik

@ -0,0 +1,214 @@
#ifndef EMUAPI_H
#define EMUAPI_H
#include "platform_config.h"
#define CUSTOM_SND 1
//#define TIMER_REND 1
#define EXTRA_HEAP 0x10
// Title: < >
#define TITLE " Apple2 Emulator "
#define ROMSDIR "apple2"
#define emu_Init(ROM) { aiie_Init(); aiie_Start(ROM);}
#define emu_Step(x) { aiie_Step(); }
#define emu_Input(x) { aiie_Input(x); }
#define MAX_FILENAME_PATH 64
#define NB_FILE_HANDLER 4
#define PALETTE_SIZE 256
#define VID_FRAME_SKIP 0x0
#define TFT_VBUFFER_YCROP 0
#define SINGLELINE_RENDERING 1
#define R32(rgb) ((rgb>>16)&0xff)
#define G32(rgb) ((rgb>>8)&0xff)
#define B32(rgb) (rgb & 0xff)
#define ACTION_NONE 0
#define ACTION_MAXKBDVAL 16
#define ACTION_EXITKBD 128
#define ACTION_RUN1 129
#define ACTION_RUN2 130
#define ACTION_RUN3 131
#ifdef KEYMAP_PRESENT
#define keylables_map0_0 (char *)"qwertyuiop\x1a"
#define keylables_map0_1 (char *)" asdfghjkl\x19"
#define keylables_map0_2 (char *)" zxcvbnm,.;/"
#define keylables_map0_3 (char *)" +\x10-"
const unsigned short key_map0[] = {
'q','w','e','r','t','y','u','i','o','p',127, //lowecase
0,'a','s','d','f','g','h','j','k','l',10,
0,'z','x','c','v','b','n','m',',','.',';','/',
0,0,0,0, //U L R D
0,'+',' ','-'
};
#define keylables_map1_0 (char *)"1234567890 "
#define keylables_map1_1 (char *)" "
#define keylables_map1_2 (char *)" "
#define keylables_map1_3 (char *)" "
const unsigned short key_map1[] = {
'1','2','3','4','5','6','7','8','9','0',0, // digit keys
0, 0,0,0,0,0,0,0,0,0,0,
0, 0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,
0,0,' ',0
};
#define keylables_map2_0 (char *)"!\"#$%^&*()@"
#define keylables_map2_1 (char *)" "
#define keylables_map2_2 (char *)" <>:?"
#define keylables_map2_3 (char *)" =\x10_"
const unsigned short key_map2[] = {
'!','"','#','$','%','^','&','*','(',')','@', // shiftothers
0, 0,0,0,0,0,0,0,0,0,0,
0, 0,0,0,0,0,0,0,'<','>',':','?',
153,151,150,152, //U L R D
0,'=',' ','_'
};
#define keylables_map3_0 (char *)"\x11\x12\x13\x14\x15\x16\x17\x18 "
#define keylables_map3_1 (char *)" "
#define keylables_map3_2 (char *)" "
#define keylables_map3_3 (char *)" "
const unsigned short key_map3[] = {
129,130,131,132,133,134,135,136,0,0,0, // function keys
0, 0,0,0,0,0,0,0,0,0,0,
0, 0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,
0,0,' ',0
};
#define keylables_map4_0 (char *)"QWERTYUIOP@"
#define keylables_map4_1 (char *)" ASDFGHJKL\x19"
#define keylables_map4_2 (char *)" ZXCVBNM<>:?"
#define keylables_map4_3 (char *)" =\x10_"
const unsigned short key_map4[] = {
'Q','W','E','R','T','Y','U','I','O','P','@', //shift uppercase
0,'A','S','D','F','G','H','J','K','L',10,
0,'Z','X','C','V','B','N','M','<','>',':','?',
0,0,0,0,
0,'=',' ','_'
};
#define keylables_map5_0 (char *)" "
#define keylables_map5_1 (char *)" "
#define keylables_map5_2 (char *)" "
#define keylables_map5_3 (char *)" "
const unsigned short key_map5[] = {
0,0,0,0,0,0,0,0,0,0,0, // extra keys
0, 0,0,0,0,0,0,0,0,0,0,
0, 0,0,0,0,0,0,0,0,0,0,0,
153,151,150,152, //U L R D
0,0,' ',0
};
const unsigned short matkeys[] = {
0x004,0x008,0x108,0x104,0x208,0x204,0x308,0x304,0x408,0x404,0x410, // row 1
0x502,0x002,0x020,0x102,0x120,0x202,0x220,0x302,0x320,0x402,0x420, // row 2
0x508,0x001,0x040,0x101,0x140,0x201,0x240,0x210,0x340,0x301,0x401,0x440, // row 3
0x504,0x520,0x540,0x501, // UP LEFT RIGHT DOWN
0x510,0x010,0x110,0x310, // row 4
};
#endif
#define MASK_JOY2_RIGHT 0x0001
#define MASK_JOY2_LEFT 0x0002
#define MASK_JOY2_UP 0x0004
#define MASK_JOY2_DOWN 0x0008
#define MASK_JOY2_BTN 0x0010
#define MASK_KEY_USER1 0x0020
#define MASK_KEY_USER2 0x0040
#define MASK_KEY_USER3 0x0080
#define MASK_JOY1_RIGHT 0x0100
#define MASK_JOY1_LEFT 0x0200
#define MASK_JOY1_UP 0x0400
#define MASK_JOY1_DOWN 0x0800
#define MASK_JOY1_BTN 0x1000
#define MASK_KEY_USER4 0x2000
#ifdef __cplusplus
extern "C" {
#endif
extern void emu_init(void);
extern void emu_start(void);
extern void emu_printf(const char * text);
extern void emu_printi(int val);
extern void emu_printh(int val);
extern void * emu_Malloc(unsigned int size);
extern void * emu_MallocI(unsigned int size);
extern void emu_Free(void * pt);
extern int emu_FileOpen(const char * filepath, const char * mode);
extern int emu_FileRead(void * buf, int size, int handler);
extern int emu_FileGetc(int handler);
extern int emu_FileSeek(int handler, int seek, int origin);
extern int emu_FileTell(int handler);
extern void emu_FileClose(int handler);
extern unsigned int emu_FileSize(const char * filepath);
extern unsigned int emu_LoadFile(const char * filepath, void * buf, int size);
extern unsigned int emu_LoadFileSeek(const char * filepath, void * buf, int size, int seek);
extern void emu_SetPaletteEntry(unsigned char r, unsigned char g, unsigned char b, int index);
extern void emu_DrawScreen(unsigned char * VBuf, int width, int height, int stride);
extern void emu_DrawLine(unsigned char * VBuf, int width, int height, int line);
extern void emu_DrawLine16(unsigned short * VBuf, int width, int height, int line);
extern void emu_DrawLine8(unsigned char * VBuf, int width, int height, int line);
extern void emu_CopyLine(int width, int height, int ysrc, int ydst);
extern void emu_DrawVsync(void);
extern int emu_FrameSkip(void);
extern void * emu_LineBuffer(int line);
extern void emu_tweakVideo(int shiftdelta, int numdelta, int denomdelta);
extern bool menuActive(void);
extern char * menuSelection(void);
extern char * menuSecondSelection(void);
extern void toggleMenu(bool on);
extern int handleMenu(unsigned short bClick);
extern int handleOSKB(void);
extern void toggleOSKB(bool forceon);
extern void emu_InitJoysticks(void);
extern int emu_SwapJoysticks(int statusOnly);
extern unsigned short emu_DebounceLocalKeys(void);
extern int emu_ReadKeys(void);
extern int emu_GetPad(void);
extern int emu_GetMouse(int *x, int *y, int *buts);
extern int emu_MouseDetected(void);
extern int emu_KeyboardDetected(void);
extern int emu_ReadAnalogJoyX(int min, int max);
extern int emu_ReadAnalogJoyY(int min, int max);
extern int emu_ReadI2CKeyboard(void);
extern unsigned char emu_ReadI2CKeyboard2(int row);
extern void emu_KeyboardOnUp(int keymodifer, int key);
extern void emu_KeyboardOnDown(int keymodifer, int key);
extern void emu_MidiOnDataReceived(unsigned char data);
extern void emu_sndPlaySound(int chan, int volume, int freq);
extern void emu_sndPlayBuzz(int size, int val);
extern void emu_sndInit();
extern void emu_resetus(void);
extern int emu_us(void);
extern int emu_setKeymap(int index);
#ifdef __cplusplus
}
#endif
#endif

Wyświetl plik

@ -0,0 +1,34 @@
#ifndef __FILEMANAGER_H
#define __FILEMANAGER_H
#include <stdint.h>
#include "bios.h"
#define MAXFILES 4 // how many results we can simultaneously manage
#define DIRPAGESIZE 10 // how many results in one readDir
#ifndef MAXPATH
#define MAXPATH 255
#endif
class FileManager {
public:
virtual ~FileManager() {};
virtual int8_t openFile(const char *name) = 0;
virtual void closeFile(int8_t fd) = 0;
virtual const char *fileName(int8_t fd) = 0;
virtual int8_t readDir(const char *where, const char *suffix, char fileDirectory[BIOS_MAXFILES][BIOS_MAXPATH+1], int16_t startIdx, uint16_t maxlen) = 0;
virtual int16_t readDir(const char *where, const char *suffix, char *outputFN, int16_t startIdx, uint16_t maxlen) = 0;
virtual void seekBlock(int8_t fd, uint16_t block, bool isNib = false) = 0;
virtual bool readTrack(int8_t fd, uint8_t *toWhere, bool isNib = false) = 0;
virtual bool readBlock(int8_t fd, uint8_t *toWhere, bool isNib = false) = 0;
virtual bool readBlocks(int8_t fd, uint8_t *toWhere, uint8_t blocks, bool isNib = false) = 0;
virtual bool writeBlock(int8_t fd, uint8_t *fromWhere, bool isNib = false) = 0;
virtual bool writeTrack(int8_t fd, uint8_t *fromWhere, bool isNib = false) = 0;
virtual bool readState(int8_t fd) = 0;
virtual bool writeState(int8_t fd) = 0;
};
#endif

Wyświetl plik

@ -0,0 +1,335 @@
const unsigned char ucase_glyphs[512] =
{
/* : 0x40 @ */
0x1c, 0x22, 0x2a, 0x3a, 0x1a, 0x02, 0x3c, 0x00,
/* : 0x41 A */
0x08, 0x14, 0x22, 0x22, 0x3e, 0x22, 0x22, 0x00,
/* : 0x42 B */
0x1e, 0x22, 0x22, 0x1e, 0x22, 0x22, 0x1e, 0x00,
/* : 0x43 C */
0x1c, 0x22, 0x02, 0x02, 0x02, 0x22, 0x1c, 0x00,
/* : 0x44 D */
0x1e, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1e, 0x00,
/* : 0x45 E */
0x3e, 0x02, 0x02, 0x1e, 0x02, 0x02, 0x3e, 0x00,
/* : 0x46 F */
0x3e, 0x02, 0x02, 0x1e, 0x02, 0x02, 0x02, 0x00,
/* : 0x47 G */
0x3c, 0x02, 0x02, 0x02, 0x32, 0x22, 0x3c, 0x00,
/* : 0x48 H */
0x22, 0x22, 0x22, 0x3e, 0x22, 0x22, 0x22, 0x00,
/* : 0x49 I */
0x1c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c, 0x00,
/* : 0x4a */
0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x1c, 0x00,
/* : 0x4b */
0x22, 0x12, 0x0a, 0x06, 0x0a, 0x12, 0x22, 0x00,
/* : 0x4c */
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3e, 0x00,
/* : 0x4d */
0x22, 0x36, 0x2a, 0x2a, 0x22, 0x22, 0x22, 0x00,
/* : 0x4e */
0x22, 0x22, 0x26, 0x2a, 0x32, 0x22, 0x22, 0x00,
/* : 0x4f */
0x1c, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c, 0x00,
/* : 0x50 */
0x1e, 0x22, 0x22, 0x1e, 0x02, 0x02, 0x02, 0x00,
/* : 0x51 */
0x1c, 0x22, 0x22, 0x22, 0x2a, 0x12, 0x2c, 0x00,
/* : 0x52 */
0x1e, 0x22, 0x22, 0x1e, 0x0a, 0x12, 0x22, 0x00,
/* : 0x53 */
0x1c, 0x22, 0x02, 0x1c, 0x20, 0x22, 0x1c, 0x00,
/* : 0x54 */
0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00,
/* : 0x55 */
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c, 0x00,
/* : 0x56 */
0x22, 0x22, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00,
/* : 0x57 */
0x22, 0x22, 0x22, 0x2a, 0x2a, 0x36, 0x22, 0x00,
/* : 0x58 */
0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00,
/* : 0x59 */
0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00,
/* : 0x5a */
0x3e, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3e, 0x00,
/* : 0x5b */
0x3e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3e, 0x00,
/* : 0x5c */
0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00,
/* : 0x5d */
0x3e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3e, 0x00,
/* : 0x5e */
0x00, 0x00, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00,
/* : 0x5f */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00,
/* : 0x20 space */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* : 0x21 ! */
0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00,
/* : 0x22 */
0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
/* : 0x23 */
0x14, 0x14, 0x3e, 0x14, 0x3e, 0x14, 0x14, 0x00,
/* : 0x24 */
0x08, 0x3c, 0x0a, 0x1c, 0x28, 0x1e, 0x08, 0x00,
/* : 0x25 */
0x06, 0x26, 0x10, 0x08, 0x04, 0x32, 0x30, 0x00,
/* : 0x26 */
0x04, 0x0a, 0x0a, 0x04, 0x2a, 0x12, 0x2c, 0x00,
/* : 0x27 */
0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
/* : 0x28 */
0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, 0x00,
/* : 0x29 */
0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00,
/* : 0x2a */
0x08, 0x2a, 0x1c, 0x08, 0x1c, 0x2a, 0x08, 0x00,
/* : 0x2b */
0x00, 0x08, 0x08, 0x3e, 0x08, 0x08, 0x00, 0x00,
/* : 0x2c */
0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x04, 0x00,
/* : 0x2d */
0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00,
/* : 0x2e */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
/* : 0x2f */
0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00,
/* : 0x30 */
0x1c, 0x22, 0x32, 0x2a, 0x26, 0x22, 0x1c, 0x00,
/* : 0x31 */
0x08, 0x0c, 0x08, 0x08, 0x08, 0x08, 0x1c, 0x00,
/* : 0x32 */
0x1c, 0x22, 0x20, 0x18, 0x04, 0x02, 0x3e, 0x00,
/* : 0x33 */
0x3e, 0x20, 0x10, 0x18, 0x20, 0x22, 0x1c, 0x00,
/* : 0x34 */
0x10, 0x18, 0x14, 0x12, 0x3e, 0x10, 0x10, 0x00,
/* : 0x35 */
0x3e, 0x02, 0x1e, 0x20, 0x20, 0x22, 0x1c, 0x00,
/* : 0x36 */
0x38, 0x04, 0x02, 0x1e, 0x22, 0x22, 0x1c, 0x00,
/* : 0x37 */
0x3e, 0x20, 0x10, 0x08, 0x04, 0x04, 0x04, 0x00,
/* : 0x38 */
0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x1c, 0x00,
/* : 0x39 */
0x1c, 0x22, 0x22, 0x3c, 0x20, 0x10, 0x0e, 0x00,
/* : 0x3a */
0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00,
/* : 0x3b */
0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x04, 0x00,
/* : 0x3c */
0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00,
/* : 0x3d */
0x00, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x00, 0x00,
/* : 0x3e */
0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00,
/* : 0x3f */
0x1c, 0x22, 0x10, 0x08, 0x08, 0x00, 0x08, 0x00
};
const unsigned char lcase_glyphs[256] =
{
/* : 0x60 */
0x02, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
/* : 0x61 */
0x00, 0x00, 0x1c, 0x20, 0x3c, 0x22, 0x3c, 0x00,
/* : 0x62 */
0x02, 0x02, 0x1e, 0x22, 0x22, 0x22, 0x1e, 0x00,
/* : 0x63 */
0x00, 0x00, 0x3c, 0x02, 0x02, 0x02, 0x3c, 0x00,
/* : 0x64 */
0x20, 0x20, 0x3c, 0x22, 0x22, 0x22, 0x3c, 0x00,
/* : 0x65 */
0x00, 0x00, 0x1c, 0x22, 0x3e, 0x02, 0x3c, 0x00,
/* : 0x66 */
0x18, 0x24, 0x04, 0x0e, 0x04, 0x04, 0x04, 0x00,
/* : 0x67 */
0x00, 0x00, 0x1c, 0x22, 0x22, 0x3c, 0x20, 0x1c,
/* : 0x68 */
0x02, 0x02, 0x1e, 0x22, 0x22, 0x22, 0x22, 0x00,
/* : 0x69 */
0x08, 0x00, 0x0c, 0x08, 0x08, 0x08, 0x1c, 0x00,
/* : 0x6a */
0x10, 0x00, 0x18, 0x10, 0x10, 0x10, 0x12, 0x0c,
/* : 0x6b */
0x02, 0x02, 0x22, 0x12, 0x0e, 0x12, 0x22, 0x00,
/* : 0x6c */
0x0c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1c, 0x00,
/* : 0x6d */
0x00, 0x00, 0x16, 0x2a, 0x2a, 0x2a, 0x2a, 0x00,
/* : 0x6e */
0x00, 0x00, 0x1e, 0x22, 0x22, 0x22, 0x22, 0x00,
/* : 0x6f */
0x00, 0x00, 0x1c, 0x22, 0x22, 0x22, 0x1c, 0x00,
/* : 0x70 */
0x00, 0x00, 0x1e, 0x22, 0x22, 0x1e, 0x02, 0x02,
/* : 0x71 */
0x00, 0x00, 0x3c, 0x22, 0x22, 0x3c, 0x20, 0x20,
/* : 0x72 */
0x00, 0x00, 0x3a, 0x06, 0x02, 0x02, 0x02, 0x00,
/* : 0x73 */
0x00, 0x00, 0x3c, 0x02, 0x1c, 0x20, 0x1e, 0x00,
/* : 0x74 */
0x04, 0x04, 0x3e, 0x04, 0x04, 0x24, 0x18, 0x00,
/* : 0x75 */
0x00, 0x00, 0x22, 0x22, 0x22, 0x32, 0x2c, 0x00,
/* : 0x76 */
0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00,
/* : 0x77 */
0x00, 0x00, 0x22, 0x2a, 0x2a, 0x2a, 0x14, 0x00,
/* : 0x78 */
0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00,
/* : 0x79 */
0x00, 0x00, 0x22, 0x22, 0x22, 0x3c, 0x20, 0x1c,
/* : 0x7a */
0x00, 0x00, 0x3e, 0x10, 0x08, 0x04, 0x3e, 0x00,
/* : 0x7b */
0x38, 0x0c, 0x0c, 0x06, 0x0c, 0x0c, 0x38, 0x00,
/* : 0x7c */
0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x08, 0x00,
/* : 0x7d */
0x0e, 0x18, 0x18, 0x30, 0x18, 0x18, 0x0e, 0x00,
/* : 0x7e */
0x2c, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* : 0x7f */
0x00, 0x2a, 0x14, 0x2a, 0x14, 0x2a, 0x00, 0x00
};
const unsigned char mousetext_glyphs[256] =
{
/* : 0x00 */
0x10, 0x08, 0x36, 0x7f, 0x3f, 0x3f, 0x7e, 0x36,
/* : 0x01 */
0x10, 0x08, 0x36, 0x41, 0x21, 0x21, 0x4a, 0x36,
/* : 0x02 */
0x00, 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x36, 0x42,
/* : 0x03 */
0x7f, 0x22, 0x14, 0x08, 0x08, 0x14, 0x22, 0x7f,
/* : 0x04 */
0x00, 0x40, 0x20, 0x11, 0x0a, 0x04, 0x04, 0x00,
/* : 0x05 */
0x7f, 0x3f, 0x5f, 0x6e, 0x75, 0x7b, 0x7b, 0x7f,
/* : 0x06 */
0x70, 0x60, 0x7e, 0x31, 0x79, 0x30, 0x3f, 0x02,
/* : 0x07 */
0x00, 0x18, 0x07, 0x00, 0x07, 0x0c, 0x08, 0x70,
/* : 0x08 */
0x08, 0x04, 0x02, 0x7f, 0x02, 0x04, 0x08, 0x00,
/* : 0x09 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a,
/* : 0x0a */
0x08, 0x08, 0x08, 0x08, 0x49, 0x2a, 0x1c, 0x08,
/* : 0x0b */
0x08, 0x1c, 0x2a, 0x49, 0x08, 0x08, 0x08, 0x08,
/* : 0x0c */
0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* : 0x0d */
0x40, 0x40, 0x40, 0x44, 0x46, 0x7f, 0x06, 0x04,
/* : 0x0e */
0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
/* : 0x0f */
0x13, 0x18, 0x1c, 0x7e, 0x1c, 0x18, 0x10, 0x6f,
/* : 0x10 */
0x64, 0x0c, 0x1c, 0x3f, 0x1c, 0x0c, 0x04, 0x7b,
/* : 0x11 */
0x40, 0x48, 0x08, 0x7f, 0x3e, 0x1c, 0x48, 0x40,
/* : 0x12 */
0x40, 0x48, 0x1c, 0x3e, 0x7f, 0x08, 0x48, 0x40,
/* : 0x13 */
0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00,
/* : 0x14 */
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7f,
/* : 0x15 */
0x08, 0x10, 0x20, 0x7f, 0x20, 0x10, 0x08, 0x00,
/* : 0x16 */
0x2a, 0x55, 0x2a, 0x55, 0x2a, 0x55, 0x2a, 0x55,
/* : 0x17 */
0x55, 0x2a, 0x55, 0x2a, 0x55, 0x2a, 0x55, 0x2a,
/* : 0x18 */
0x00, 0x3e, 0x41, 0x01, 0x01, 0x01, 0x7f, 0x00,
/* : 0x19 */
0x00, 0x00, 0x3f, 0x40, 0x40, 0x40, 0x7f, 0x00,
/* : 0x1a */
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
/* : 0x1b */
0x08, 0x1c, 0x3e, 0x7f, 0x3e, 0x1c, 0x08, 0x00,
/* : 0x1c */
0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f,
/* : 0x1d */
0x14, 0x14, 0x77, 0x00, 0x77, 0x14, 0x14, 0x00,
/* : 0x1e */
0x7f, 0x40, 0x40, 0x4c, 0x4c, 0x40, 0x40, 0x7f,
/* : 0x1f */
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
};
const unsigned char interface_glyphs[256] =
{
/* : 0x00 ----------------------- menu borders */
0x00, 0x00, 0x00, 0x00, 0x78, 0x08, 0x08, 0x08,
/* : 0x01 */
0x00, 0x00, 0x00, 0x00, 0x0f, 0x08, 0x08, 0x08,
/* : 0x02 */
0x08, 0x08, 0x08, 0x08, 0x78, 0x00, 0x00, 0x00,
/* : 0x03 */
0x08, 0x08, 0x08, 0x08, 0x0f, 0x00, 0x00, 0x00,
/* : 0x04 */
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
/* : 0x05 */
0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
/* : 0x06 */
0x08, 0x08, 0x08, 0x08, 0x78, 0x08, 0x08, 0x08,
/* : 0x07 */
0x08, 0x08, 0x08, 0x08, 0x0f, 0x08, 0x08, 0x08,
/* : 0x08 */
0x00, 0x00, 0x00, 0x00, 0x7f, 0x08, 0x08, 0x08,
/* : 0x09 */
0x08, 0x08, 0x08, 0x08, 0x7f, 0x00, 0x00, 0x00,
/* : 0x0A */
0x08, 0x08, 0x08, 0x08, 0x7f, 0x08, 0x08, 0x08,
/* : 0x0B ----------------------- disk icon quad */
0x00, 0x7e, 0x02, 0x02, 0x02, 0x42, 0x22, 0x22,
/* : 0x0C */
0x00, 0x7f, 0x40, 0x20, 0x40, 0x43, 0x44, 0x44,
/* : 0x0D */
0x22, 0x42, 0x02, 0x02, 0x02, 0x02, 0x7e, 0x00,
/* : 0x0E */
0x44, 0x43, 0x40, 0x41, 0x41, 0x41, 0x7e, 0x00,
/* : 0x0F ----------------------- unlock icon */
0x1c, 0x24, 0x04, 0x3e, 0x22, 0x22, 0x2a, 0x3e,
/* : 0x10 ----------------------- reverse return arrow */
0x01, 0x01, 0x01, 0x11, 0x31, 0x7f, 0x30, 0x10,
/* : 0x11 ----------------------- mini-spacebar visual */
0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x3e, 0x00,
/* : 0x12 ----------------------- glyph_joystick */
0x08, 0x08, 0x08, 0x77, 0x08, 0x08, 0x08, 0x08,
/* : 0x13 ----------------------- glyph_ctrl */
0x08, 0x1c, 0x3e, 0x63, 0x7b, 0x63, 0x7f, 0x00,
/* : 0x14 ----------------------- glyph_lowercase */
0x7f, 0x63, 0x5f, 0x43, 0x5d, 0x43, 0x7f, 0x00,
/* : 0x15 ----------------------- glyph_uppercase */
0x77, 0x6b, 0x5d, 0x41, 0x5d, 0x5d, 0x7f, 0x00,
/* : 0x16 ----------------------- glyph_showalt */
0x7f, 0x77, 0x77, 0x41, 0x77, 0x77, 0x7f, 0x00,
/* : 0x17 ----------------------- glyph_backspace */
0x00, 0x08, 0x04, 0x7e, 0x04, 0x08, 0x00, 0x00,
/* : 0x18 ----------------------- glyph_joystick_kpad */
0x08, 0x2a, 0x08, 0x77, 0x08, 0x2a, 0x08, 0x08,
/* : 0x19 ----------------------- glyph_leftspace */
0x00, 0x7e, 0x02, 0x42, 0x42, 0x42, 0x02, 0x7e,
/* : 0x1A ----------------------- glyph_midspace */
0x00, 0x7f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x7f,
/* : 0x1B ----------------------- glyph_rightspace */
0x00, 0x3f, 0x20, 0x21, 0x21, 0x21, 0x20, 0x3f,
/* : 0x1C ----------------------- glyph_esc */
0x7f, 0x63, 0x5d, 0x41, 0x7d, 0x43, 0x7f, 0x00,
/* : 0x1D ----------------------- glyph_return left */
0x00, 0x00, 0x00, 0x10, 0x18, 0x7c, 0x18, 0x10,
/* : 0x1E ----------------------- glyph_return right */
0x20, 0x20, 0x20, 0x20, 0x20, 0x3f, 0x00, 0x00,
/* : 0x1F ----------------------- glyph_nonactionable */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

Wyświetl plik

@ -0,0 +1,148 @@
// Font: c64_lower.64c
PROGMEM const unsigned char font8x8[128][8] =
{
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F
{ 0x7f, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7f, 0x00 }, // Space // 0x10
{ 0x00, 0x27, 0x31, 0x27, 0x21, 0x71, 0x00, 0x00 }, // F1 // 0x11
{ 0x00, 0x77, 0x41, 0x77, 0x11, 0x71, 0x00, 0x00 }, // F2
{ 0x00, 0x77, 0x41, 0x77, 0x41, 0x71, 0x00, 0x00 }, // F3
{ 0x00, 0x17, 0x51, 0x77, 0x41, 0x41, 0x00, 0x00 }, // F4
{ 0x00, 0x77, 0x11, 0x77, 0x41, 0x71, 0x00, 0x00 }, // F5
{ 0x00, 0x77, 0x11, 0x77, 0x51, 0x71, 0x00, 0x00 }, // F6
{ 0x00, 0x77, 0x41, 0x47, 0x41, 0x41, 0x00, 0x00 }, // F7
{ 0x00, 0x77, 0x51, 0x77, 0x51, 0x71, 0x00, 0x00 }, // F8 // 0x18
{ 0x00, 0x00, 0x20, 0x24, 0x3e, 0x04, 0x00, 0x00 }, // Return // 0x19
{ 0x00, 0x59, 0x4b, 0x5b, 0x4b, 0xd9, 0x00, 0x00 }, // Del // 0x1A
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019
//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space)
{ 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!)
{ 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (")
{ 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#)
{ 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($)
{ 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%)
{ 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&)
{ 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (')
{ 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (()
{ 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ())
{ 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*)
{ 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,)
{ 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.)
{ 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/)
{ 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0)
{ 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1)
{ 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2)
{ 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3)
{ 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4)
{ 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5)
{ 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6)
{ 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7)
{ 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8)
{ 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9)
{ 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:)
{ 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (//)
{ 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<)
{ 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=)
{ 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>)
{ 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?)
{ 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@)
{ 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A)
{ 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B)
{ 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C)
{ 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D)
{ 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E)
{ 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F)
{ 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G)
{ 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H)
{ 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I)
{ 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J)
{ 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K)
{ 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L)
{ 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M)
{ 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N)
{ 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O)
{ 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P)
{ 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q)
{ 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R)
{ 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S)
{ 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T)
{ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U)
{ 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V)
{ 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W)
{ 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X)
{ 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y)
{ 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z)
{ 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([)
{ 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\)
{ 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (])
{ 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_)
{ 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`)
{ 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a)
{ 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b)
{ 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c)
{ 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d)
{ 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e)
{ 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f)
{ 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g)
{ 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h)
{ 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i)
{ 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j)
{ 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k)
{ 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l)
{ 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m)
{ 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n)
{ 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o)
{ 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p)
{ 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q)
{ 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r)
{ 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s)
{ 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t)
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u)
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v)
{ 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w)
{ 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x)
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y)
{ 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z)
{ 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({)
{ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|)
{ 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (})
{ 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~)
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // U+007F
};

Wyświetl plik

@ -0,0 +1,19 @@
#include "globals.h"
FileManager *g_filemanager = NULL;
Cpu *g_cpu = NULL;
VM *g_vm = NULL;
PhysicalDisplay *g_display = NULL;
PhysicalKeyboard *g_keyboard = NULL;
PhysicalSpeaker *g_speaker = NULL;
PhysicalPaddles *g_paddles = NULL;
PhysicalPrinter *g_printer = NULL;
uint16_t g_battery = 0;
uint16_t g_charge = 0;
int16_t g_volume = 15;
int8_t g_displayType = 3; // FIXME m_perfectcolor
uint8_t g_joyTrimX = 127;
uint8_t g_joyTrimY = 127;
uint8_t g_joySpeed = 5;
uint8_t g_screenSync = true;
bool biosRequest = false;

Wyświetl plik

@ -0,0 +1,28 @@
#include <stdint.h>
#include "filemanager.h"
#include "cpu.h"
#include "vm.h"
#include "physicaldisplay.h"
#include "physicalkeyboard.h"
#include "physicalspeaker.h"
#include "physicalpaddles.h"
#include "physicalprinter.h"
extern FileManager *g_filemanager;
extern Cpu *g_cpu;
extern VM *g_vm;
extern PhysicalDisplay *g_display;
extern PhysicalKeyboard *g_keyboard;
extern PhysicalSpeaker *g_speaker;
extern PhysicalPaddles *g_paddles;
extern PhysicalPrinter *g_printer;
extern uint16_t g_battery;
extern uint16_t g_charge;
extern int16_t g_volume;
extern int8_t g_displayType;
extern uint8_t g_joyTrimX;
extern uint8_t g_joyTrimY;
extern uint8_t g_joySpeed;
extern uint8_t g_screenSync;
extern bool biosRequest;

Wyświetl plik

@ -0,0 +1,124 @@
#ifndef IOPINS_H
#define IOPINS_H
#include "platform_config.h"
#ifdef TEECOMPUTER
// Teecomputer layout
// VGA
// R 3 2K
// R 4 1K
// R 33 500
// G 11 2K
// G 13 1K
// G 2 500
// B 10 820
// B 12 390
// HSYNC 15 82
// VSYNC 8 82
// Display
#define TFT_SCLK 27
#define TFT_MOSI 26
#define TFT_MISO 255
#define TFT_TOUCH_CS 255
#define TFT_TOUCH_INT 255
#define TFT_DC 23
#define TFT_CS 22 // 255 for LORES ST7789 (NO CS)
#define TFT_RST 255 // 255 for ILI/ST if connected to 3.3V or 24 if really needed
// SD
#define SD_CS BUILTIN_SDCARD
// Audio
#define AUDIO_I2S_DIN 7
#define AUDIO_I2S_BCK 21
#define AUDIO_I2S_LCK 20
// Keyboard matrix
#define KLED 14
//Cols (out)
//pico 1,2,3,4,5,14
//teen 16,6,24,25,28,31
#define KCOLOUT1 16
#define KCOLOUT2 6
#define KCOLOUT3 24
#define KCOLOUT4 25
#define KCOLOUT5 28
#define KCOLOUT6 31
//Rows (in)
//pico 9,8,6,15,7,22
//teen 19,18,17,5,29,30,32 //5,6,16,17,18,19
#define KROWIN1 19
#define KROWIN2 18
#define KROWIN3 17
#define KROWIN4 5
#define KROWIN5 29
#define KROWIN6 30
#define KROWIN7 32
#define PIN_KEY_USER1 41
#define PIN_KEY_USER2 40
// Second joystick (external)
#define PIN_JOY1_BTN 34
#define PIN_JOY1_1 35 // UP
#define PIN_JOY1_2 36 // DOWN
#define PIN_JOY1_3 38 // RIGHT
#define PIN_JOY1_4 37 // LEFT
#else
// Original Layout
#define TFT_SCLK 13
#define TFT_MOSI 11
#define TFT_MISO 12
#define TFT_TOUCH_CS 255
#define TFT_TOUCH_INT 255
#define TFT_DC 9
#define TFT_CS 22 // 255 for LORES ST7789 (NO CS)
#define TFT_RST 23 // 255 for ILI/ST if connected to 3.3V
// SD
#define SD_CS BUILTIN_SDCARD
// I2C keyboard
#define I2C_SCL_IO 19
#define I2C_SDA_IO 18
// Analog joystick (primary) for JOY2 and 5 extra buttons
#ifdef HAS_T4_VGA
#define PIN_JOY2_A1X A3
#define PIN_JOY2_A2Y A2
#define PIN_JOY2_BTN 14
#define PIN_KEY_USER1 22
#define PIN_KEY_USER2 23
// Second joystick
#define PIN_JOY1_BTN 34
#define PIN_JOY1_1 35 // UP
#define PIN_JOY1_2 36 // DOWN
#define PIN_JOY1_3 38 // RIGHT
#define PIN_JOY1_4 37 // LEFT
#else
#define PIN_JOY2_A1X A1
#define PIN_JOY2_A2Y A2
#define PIN_JOY2_BTN 17
#define PIN_KEY_USER1 3 //34
#define PIN_KEY_USER2 4 //35
// Second joystick
#define PIN_JOY1_BTN 2
#define PIN_JOY1_1 14 // UP
#define PIN_JOY1_2 7 // DOWN
#define PIN_JOY1_3 6 // RIGHT
#define PIN_JOY1_4 5 // LEFT
#endif
#endif
#endif

Wyświetl plik

@ -0,0 +1,33 @@
#include "lcg.h"
/* Dead simple 8-bit Linear Congruential Generator (PRNG).
*
* An LCG is defined as
*
* X(n+1) = ( a * X(n) + c ) % m
*
* In this implementation:
* a = 2^7 = 128
* c = 251
* m = 256
*
* Like all LCGs, the low-order bits of this cycle quickly. The
* high-order bits have better (longer) periods.
*/
LCG::LCG(uint16_t s)
{
seed = s;
}
void LCG::srnd(uint16_t s)
{
seed = s;
}
uint8_t LCG::rnd()
{
seed = (seed << 7) - seed + 251;
return (uint8_t)(seed + (seed>>8));
}

Wyświetl plik

@ -0,0 +1,17 @@
#ifndef __LCG_H
#define __LCG_H
#include <stdint.h>
class LCG {
public:
LCG(uint16_t s);
void srnd(uint16_t s);
uint8_t rnd();
private:
uint16_t seed;
};
#endif

Wyświetl plik

@ -0,0 +1,18 @@
#ifndef __MMU_H
#define __MMU_H
#include <stdint.h>
class MMU {
public:
virtual ~MMU() {}
virtual void Reset() = 0;
virtual uint8_t read(uint16_t mem) = 0;
virtual void write(uint16_t mem, uint8_t val) = 0;
virtual uint8_t readDirect(uint16_t address, uint8_t fromPage) = 0;
virtual void writeDirect(uint16_t address, uint8_t fromPage, uint8_t val) = 0;
};
#endif

Wyświetl plik

@ -0,0 +1,331 @@
#include "nibutil.h"
#ifdef TEENSYDUINO
#include <Arduino.h>
#else
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#endif
// Long gaps are more "correct" in the sense that they're
// nib-disk-like; but they mean the VM has to chew on a lot of disk
// gaps to find the real data, which takes a noticeable amount of
// time. With this off, we present a minimum number of gaps (that
// hopefully aren't too short for the ROM to be able to write
// correctly)
//#define LONGGAPS
#define DISK_VOLUME 254
// dos 3.3 to physical sector conversion
const static uint8_t dephys[16] = {
0x00, 0x07, 0x0e, 0x06, 0x0d, 0x05, 0x0c, 0x04,
0x0b, 0x03, 0x0a, 0x02, 0x09, 0x01, 0x08, 0x0f };
// Prodos to physical sector conversion
const uint8_t deProdosPhys[] = {
0x00, 0x08, 0x01, 0x09, 0x02, 0x0a, 0x03, 0x0b,
0x04, 0x0c, 0x05, 0x0d, 0x06, 0x0e, 0x07, 0x0f };
const static uint8_t _trans[64] = {0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff};
const static uint8_t _detrans[0x80] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x08, 0x0C, 0x00, 0x10, 0x14, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x20,
0x00, 0x00, 0x00, 0x24, 0x28, 0x2C, 0x30, 0x34,
0x00, 0x00, 0x38, 0x3C, 0x40, 0x44, 0x48, 0x4C,
0x00, 0x50, 0x54, 0x58, 0x5C, 0x60, 0x64, 0x68,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x6C, 0x00, 0x70, 0x74, 0x78,
0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x80, 0x84,
0x00, 0x88, 0x8C, 0x90, 0x94, 0x98, 0x9C, 0xA0,
0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0xA8, 0xAC,
0x00, 0xB0, 0xB4, 0xB8, 0xBC, 0xC0, 0xC4, 0xC8,
0x00, 0x00, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC, 0xE0,
0x00, 0xE4, 0xE8, 0xEC, 0xF0, 0xF4, 0xF8, 0xFC };
void nibblizeTrack(RingBuf *trackBuffer, uint8_t *rawTrackBuffer,
uint8_t diskType, int8_t track)
{
int checksum;
for (uint8_t sector=0; sector<16; sector++) {
for (uint8_t i=0;
#ifdef LONGGAPS
i < (sector==0 ? 0x63 : 0x13);
#else
i < 8;
#endif
i++) {
trackBuffer->addByte(GAP);
}
trackBuffer->addByte(0xD5); // prolog
trackBuffer->addByte(0xAA);
trackBuffer->addByte(0x96);
trackBuffer->addByte(nib1(DISK_VOLUME));
trackBuffer->addByte(nib2(DISK_VOLUME));
trackBuffer->addByte(nib1(track));
trackBuffer->addByte(nib2(track));
trackBuffer->addByte(nib1(sector));
trackBuffer->addByte(nib2(sector));
checksum = DISK_VOLUME ^ track ^ sector;
trackBuffer->addByte(nib1(checksum));
trackBuffer->addByte(nib2(checksum));
trackBuffer->addByte(0xDE); // epilog
trackBuffer->addByte(0xAA);
trackBuffer->addByte(0xEB); // Not strictly necessary, but the DiskII controller does it, so we will too.
// The DiskII controller puts out 5 GAP bytes here.
for (uint8_t i=0; i<5; i++) {
trackBuffer->addByte(GAP);
}
trackBuffer->addByte(0xD5); // data prolog
trackBuffer->addByte(0xAA);
trackBuffer->addByte(0xAD);
uint8_t physicalSector = (diskType == prodosDisk ? deProdosPhys[sector] : dephys[sector]);
encodeData(trackBuffer, &rawTrackBuffer[physicalSector * 256]);
trackBuffer->addByte(0xDE); // data epilog
trackBuffer->addByte(0xAA);
trackBuffer->addByte(0xEB);
// trackBuffer->addByte(GAP);
}
}
#define SIXBIT_SPAN 0x56 // 86 bytes
// Pop the next 343 bytes off of trackBuffer, which should be 342
// 6:2-bit GCR encoded values, which we decode back in to 256 8-byte
// output values; and one checksum byte.
//
// Return true if we've successfully consumed 343 bytes from
// trackBuf. This reads from the circular buffer trackBuffer, so if
// there's not enough data there, the results are somewhat
// unpredictable.
bool decodeData(RingBuf *trackBuffer, uint16_t startAt, uint8_t *output)
{
// Basic check that there's enough buffer data in trackBuffer. Note
// that we're not checking it against startAt; we could be wrapping
// around.
if (trackBuffer->count() < 343)
return false;
static uint8_t workbuf[342];
for (int i=0; i<342; i++) {
uint8_t in = trackBuffer->peek(startAt++) & 0x7F; // strip high bit
workbuf[i] = _detrans[in];
}
// fixme: collapse this in to the previous loop
uint8_t prev = 0;
for (int i=0; i<342; i++) {
workbuf[i] = prev ^ workbuf[i];
prev = workbuf[i];
}
// Put the checksum on the track - only necessary if we're about to
// write the nibblized version of the track back out
/* uint16_t cursor = trackBuffer->Cursor();
trackBuffer->setPeekCursor(startAt++);
trackBuffer->replaceByte(prev); // 'prev' holds the checksum
trackBuffer->setPeekCursor(cursor); // put it back where we found it
*/
// Start with all of the bytes with 6 bits of data
for (uint16_t i=0; i<256; i++) {
output[i] = workbuf[SIXBIT_SPAN + i] & 0xFC; // 6 bits
}
// Then pull in all of the 2-bit values, which are stuffed 3 to a byte. That gives us
// 4 bits more than we need - the last two skip two of the bits.
for (uint8_t i=0; i<SIXBIT_SPAN; i++) {
// This byte (workbuf[i]) has 2 bits for each of 3 output bytes:
// i, SIXBIT_SPAN+i, and 2*SIXBIT_SPAN+i
uint8_t thisbyte = workbuf[i];
output[ i] |= ((thisbyte & 0x08) >> 3) | ((thisbyte & 0x04) >> 1);
output[ SIXBIT_SPAN + i] |= ((thisbyte & 0x20) >> 5) | ((thisbyte & 0x10) >> 3);
if (i < SIXBIT_SPAN-2) {
output[2*SIXBIT_SPAN + i] |= ((thisbyte & 0x80) >> 7) | ((thisbyte & 0x40) >> 5);
}
}
// FIXME: check or update the checksum?
return true;
}
void encodeData(RingBuf *trackBuffer, uint8_t *data)
{
int16_t i;
int ptr2 = 0;
int ptr6 = 0x56;
static int nibbles[0x156];
for (i=0; i<0x156; i++) {
nibbles[i] = 0;
}
int idx2 = 0x55;
for (int idx6 = 0x101; idx6 >= 0; idx6--) {
int val6 = data[idx6 & 0xFF];
int val2 = nibbles[ptr2 + idx2];
val2 = (val2 << 1) | (val6 & 1);
val6 >>= 1;
val2 = (val2 << 1) | (val6 & 1);
val6 >>= 1;
// There are 2 "extra" bytes of 2-bit data that we ignore here.
if (ptr6 + idx6 < 0x156) {
nibbles[ptr6 + idx6] = val6;
}
if (ptr2 + idx2 < 0x156) {
nibbles[ptr2 + idx2] = val2;
}
if (--idx2 < 0) {
idx2 = 0x55;
}
}
int lastv = 0;
for (int idx = 0; idx < 0x156; idx++) {
int val = nibbles[idx];
trackBuffer->addByte(_trans[lastv ^ val]);
lastv = val;
}
trackBuffer->addByte(_trans[lastv]);
}
nibErr denibblizeTrack(RingBuf *trackBuffer, uint8_t *rawTrackBuffer,
uint8_t diskType, int8_t track)
{
// We can't tell exactly what the length should be, b/c there might
// be varying numbers of GAP bytes. But we can tell, generally, that
// this is the minimum acceptable length that might hold all the
// track data.
if (trackBuffer->count() < 16*MINNIBSECTORSIZE) {
return errorShortTrack;
}
// bitmask of the sectors that we've found while decoding. We should
// find all 16.
uint16_t sectorsUpdated = 0;
// loop through the data twice, so we make sure we read anything
// that crosses the end/start boundary
// FIXME: if this approach works, we probably want 1/16th extra, not 2*
for (uint16_t i=0; i<2*trackBuffer->count(); ) {
// Find the prolog
while (trackBuffer->peek(i++) != 0xD5)
;
if (trackBuffer->peek(i++) != 0xAA) {
continue;
}
if (trackBuffer->peek(i++) != 0x96) {
continue;
}
// And now we should be in the header section
uint8_t volumeID = denib(trackBuffer->peek(i),
trackBuffer->peek(i+1));
i += 2;
uint8_t trackID = denib(trackBuffer->peek(i),
trackBuffer->peek(i+1));
i += 2;
uint8_t sectorNum = denib(trackBuffer->peek(i),
trackBuffer->peek(i+1));
i += 2;
uint8_t headerChecksum = denib(trackBuffer->peek(i),
trackBuffer->peek(i+1));
i += 2;
if (headerChecksum != (volumeID ^ trackID ^ sectorNum)) {
continue;
}
// check for the epilog
if (trackBuffer->peek(i++) != 0xDE) {
continue;
}
if (trackBuffer->peek(i++) != 0xAA) {
continue;
}
// Skip to the data prolog
while (trackBuffer->peek(i++) != 0xD5)
;
if (trackBuffer->peek(i++) != 0xAA) {
continue;
}
if (trackBuffer->peek(i++) != 0xAD) {
continue;
}
// Decode the data in to a temporary buffer: we don't want to overwrite
// something valid with partial data
uint8_t output[256];
if (!decodeData(trackBuffer, i, output)) {
continue;
}
i += 343;
// Check the data epilog
if (trackBuffer->peek(i++) != 0xDE) {
continue;
}
if (trackBuffer->peek(i++) != 0xAA) {
continue;
}
if (trackBuffer->peek(i++) != 0xEB) {
continue;
}
// We've got a whole block! Put it in the rawTrackBuffer and mark
// the bit for it in sectorsUpdated.
// FIXME: if trackID != curTrack, that's an error?
uint8_t targetSector;
if (diskType == prodosDisk) {
targetSector = deProdosPhys[sectorNum];
} else {
targetSector = dephys[sectorNum];
}
memcpy(&rawTrackBuffer[targetSector * 256],
output,
256);
sectorsUpdated |= (1 << sectorNum);
}
// Check that we found all of the sectors for this track
if (sectorsUpdated != 0xFFFF) {
return errorMissingSectors;
}
return errorNone;
}

Wyświetl plik

@ -0,0 +1,44 @@
#ifdef TEENSYDUINO
#include <Arduino.h>
#else
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#endif
#include "RingBuf.h"
#define NIBTRACKSIZE 0x1A00
// Minimum viable nibblized sector size. With GAP bytes, could be much longer.
#define MINNIBSECTORSIZE (343 + 13 + 3)
#define nib1(a) (((a & 0xAA) >> 1) | 0xAA)
#define nib2(b) (((b & 0x55) ) | 0xAA)
#define denib(a, b) ((((a) & ~0xAA) << 1) | ((b) & ~0xAA))
#define GAP 0xFF
enum {
dosDisk = 0,
prodosDisk = 1,
nibDisk = 2
};
enum nibErr {
errorNone = 0,
errorShortTrack = 1,
errorMissingSectors = 2
};
void nibblizeTrack(RingBuf *trackBuffer, uint8_t *rawTrackBuffer,
uint8_t diskType, int8_t track);
nibErr denibblizeTrack(RingBuf *trackBuffer, uint8_t *rawTrackBuffer,
uint8_t diskType, int8_t track);
bool decodeData(RingBuf *trackBuffer, uint16_t startAt, uint8_t *output);
void encodeData(RingBuf *trackBuffer, uint8_t *data);

Wyświetl plik

@ -0,0 +1,27 @@
#ifndef __PHYSICALDISPLAY_H
#define __PHYSICALDISPLAY_H
#include <string.h> // strncpy
#include "vmdisplay.h" // FIXME: for AiieRect
class PhysicalDisplay {
public:
PhysicalDisplay() { overlayMessage[0] = '\0'; }
virtual ~PhysicalDisplay() {};
virtual void redraw() = 0; // total redraw, assuming nothing
virtual void blit(AiieRect r) = 0; // redraw just the VM display area
virtual void drawDriveDoor(uint8_t which, bool isOpen) = 0;
virtual void setDriveIndicator(uint8_t which, bool isRunning) = 0;
virtual void drawBatteryStatus(uint8_t percent) = 0;
virtual void debugMsg(const char *msg) { strncpy(overlayMessage, msg, sizeof(overlayMessage)); }
protected:
char overlayMessage[40];
};
#endif

Wyświetl plik

@ -0,0 +1,85 @@
#ifndef __PHYSICALKEYBOARD_H
#define __PHYSICALKEYBOARD_H
#include <stdint.h>
#include "vmkeyboard.h"
#define ESC 0x1B
#define DEL 0x7F
#define RET 0x0D
#define TAB 0x09
#define LARR 0x08 // control-H
#define RARR 0x15 // control-U
#define DARR 0x0A
#define UARR 0x0B
// Virtual keys
#define _CTRL 0x81
#define LSHFT 0x82
#define RSHFT 0x83
#define LOCK 0x84 // caps lock
#define LA 0x85 // left (open) apple, aka paddle0 button
#define RA 0x86 // right (closed) apple aka paddle1 button
#define SYSRQ 0x87
//Panel keys
#define LJOY 0x88
#define RJOY 0x89
#define UJOY 0x8A
#define DJOY 0x8B
#define BUT1 0x8C
#define BUT2 0x8D
#define JOYKEY 0x8E
#define NOTEKEY 0x8F
#define LWHTKEY 0x90
#define UKEY 0x91
#define RWHTKEY 0x92
#define ENTKEY 0x93
#define LKEY 0x94
#define DKEY 0x95
#define RKEY 0x96
//Special keys
#define JOY2 0x97
//USB modifiers
#define USB_LEFT_CTRL 0x01
#define USB_LEFT_SHIFT 0x02
#define USB_LEFT_ALT 0x04
#define USB_LEFT_GUI 0x08
#define USB_RIGHT_CTRL 0x10
#define USB_RIGHT_SHIFT 0x20
#define USB_RIGHT_ALT 0x40
#define USB_RIGHT_GUI 0x80
#define JOY_MODE_ANA_ABS 0
#define JOY_MODE_ANA_REL 1
#define JOY_MODE_JOYPORT1 2
#define JOY_MODE_JOYPORT2 3
class PhysicalKeyboard {
public:
PhysicalKeyboard(VMKeyboard *k) { this->vmkeyboard = k; }
virtual ~PhysicalKeyboard() {};
virtual void maintainKeyboard() = 0;
virtual bool kbhit() = 0;
virtual uint8_t read() = 0;
//Key joystick
virtual void startReading() = 0;
virtual uint8_t getMapping(uint8_t key) = 0;
virtual void setMapping(uint8_t key, uint8_t val) = 0;
virtual void setJoymode(uint8_t mode) = 0;
virtual void setAnnunciators() = 0;
virtual void setCaps(bool enabled) = 0;
virtual void onPress(int unicode);
virtual void onRelease(int unicode);
virtual void pressedKey(uint8_t key, uint8_t mod);
virtual void releasedKey(uint8_t key, uint8_t mod);
protected:
VMKeyboard *vmkeyboard;
};
#endif

Wyświetl plik

@ -0,0 +1,18 @@
#ifndef __PHYSICALPADDLES_H
#define __PHYSICALPADDLES_H
#include <stdint.h>
class PhysicalPaddles {
public:
virtual ~PhysicalPaddles() {}
virtual void startReading() = 0;
virtual void setPaddle0(uint8_t val);
virtual void setPaddle1(uint8_t val);
virtual uint8_t paddle0() = 0;
virtual uint8_t paddle1() = 0;
};
#endif

Wyświetl plik

@ -0,0 +1,13 @@
#ifndef __PHYSICALPRINTER_H
#define __PHYSICALPRINTER_H
class PhysicalPrinter {
public:
virtual ~PhysicalPrinter() {}
// must be 960 pixels wide (120 bytes)
virtual void addLine(uint8_t *rowOfBits) = 0;
virtual void update() = 0;
virtual void moveDownPixels(uint8_t p) = 0;
};
#endif

Wyświetl plik

@ -0,0 +1,17 @@
#ifndef __PHYSICALSPEAKER_H
#define __PHYSICALSPEAKER_H
#include <stdint.h>
class PhysicalSpeaker {
public:
virtual ~PhysicalSpeaker() {}
virtual void toggle() = 0;
virtual void maintainSpeaker(uint32_t c) = 0;
virtual void beginMixing() = 0;
virtual void mixOutput(uint8_t v) = 0;
};
#endif

Wyświetl plik

@ -0,0 +1,39 @@
#ifndef _PLATFORM_CONFIG_H_
#define _PLATFORM_CONFIG_H_
#define TEECOMPUTER 1
#ifdef TEECOMPUTER
//#define ILI9341 1
//#define ST7789 1
//#define TFTSPI1 1
#define HAS_T4_VGA 1
//#define HAS_SND 1
#define HAS_USBKEY 1
#define INVX 1
#else
#define HAS_T4_VGA 1
//#define INVX 1
#define INVY 1
#define HAS_SND 1
#define HAS_USBKEY 1
#endif
//#define ILI9341 1
//#define ST7789 1
//#define SWAP_JOYSTICK 1
//#define LOHRES 1
//#define ROTATE_SCREEN 1
//#define EXTERNAL_SD 1
//#define USE_SDFAT 1
//#define SD_FAT_TYPE 1
//#define USE_SDFS 1
//#define SDFSDEV "1:"
#endif

Wyświetl plik

@ -0,0 +1,133 @@
#include <string.h> // memset
//#include <TimeLib.h>
#include "plf-clock.h"
#include "applemmu.h" // for FLOATING
/*
* http://apple2online.com/web_documents/prodos_technical_notes.pdf
*
* When ProDOS calls a clock card, the card deposits an ASCII string
* in the GETLN input buffer in the form: 07,04,14,22,46,57. The
* string translates as the following:
*
* 07 = the month, July
* 04 = the day of the week (00 = Sun)
* 14 = the date (00 to 31)
* 22 = the hour (00 to 23)
* 46 = the minute (00 to 59)
* 57 = the second (00 to 59)
*/
static void timeToProDOS(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute,
uint8_t proDOStimeOut[4])
{
proDOStimeOut[0] = ((year % 100) << 1) | (month >> 3);
proDOStimeOut[1] = ((month & 0x0F) << 5) | (day & 0x1F);
proDOStimeOut[2] = hour & 0x1F;
proDOStimeOut[3] = minute & 0x3F;
}
PlfClock::PlfClock(AppleMMU *mmu)
{
this->mmu = mmu;
}
PlfClock::~PlfClock()
{
}
void PlfClock::Reset()
{
}
uint8_t PlfClock::readSwitches(uint8_t s)
{
// When any switch is read, we'll put the current time in the prodos time buffer
// JMH tmElements_t tm;
// JMH breakTime(now(), tm);
// Put the date/time in the official ProDOS buffer
uint8_t prodosOut[4];
// JMH timeToProDOS(tm.Year, tm.Month, tm.Day, tm.Hour, tm.Minute, prodosOut);
timeToProDOS(1985, 12, 02, 10, 45, prodosOut);
mmu->write(0xBF90, prodosOut[0]);
mmu->write(0xBF91, prodosOut[1]);
mmu->write(0xBF92, prodosOut[2]);
mmu->write(0xBF93, prodosOut[3]);
// and also generate a date/time that contains seconds, but not a
// year, which it also consumes
char ts[18];
// JMH sprintf(ts, "%.2d,%.2d,%.2d,%.2d,%.2d,%.2d",
// tm.Month,
// tm.Wday - 1, // Sunday should be 0, not 1
// tm.Day,
// tm.Hour,
// tm.Minute,
// tm.Second);
sprintf(ts, "%.2d,%.2d,%.2d,%.2d,%.2d,%.2d",
2,
4 - 1, // Sunday should be 0, not 1
12,
10,
45,
0);
uint8_t i = 0;
while (ts[i]) {
mmu->write(0x200 + i, ts[i] | 0x80);
i++;
}
return FLOATING;
}
void PlfClock::writeSwitches(uint8_t s, uint8_t v)
{
// printf("unimplemented write to the clock - 0x%X\n", v);
}
// FIXME: this assumes slot #7
void PlfClock::loadROM(uint8_t *toWhere)
{
memset(toWhere, 0xEA, 256); // fill the page with NOPs
// ProDOS only needs these 4 bytes to recognize that a clock is present
toWhere[0x00] = 0x08; // PHP
toWhere[0x02] = 0x28; // PLP
toWhere[0x04] = 0x58; // CLI
toWhere[0x06] = 0x70; // BVS
// Pad out those bytes so they will return control well. The program
// at c700 becomes
//
// C700: PHP ; push to stack
// NOP ; filler (filled in by memory clear)
// PLP ; pop from stack
// RTS ; return
// CLI ; required to detect driver, but not used
// NOP ; filled in by memory clear
// BVS ; required to detect driver, but not used
toWhere[0x03] = 0x60; // RTS
// And it needs a small routing here to read/write it:
// 0x08: read
toWhere[0x08] = 0x4C; // JMP $C710
toWhere[0x09] = 0x10;
toWhere[0x0A] = 0xC7;
// 0x0b: write
toWhere[0x0B] = 0x8D; // STA $C0F0 (slot 7's first switch)
toWhere[0x0C] = 0xF0;
toWhere[0x0D] = 0xC0;
toWhere[0x0E] = 0x60; // RTS
// simple read
toWhere[0x10] = 0xAD; // LDA $C0F0 (slot 7's first switch)
toWhere[0x11] = 0xF0;
toWhere[0x12] = 0xC0;
toWhere[0x13] = 0x60; // RTS
}

Wyświetl plik

@ -0,0 +1,26 @@
#ifndef __TEENSYCLOCK_H
#define __TEENSYCLOCK_H
#include <stdint.h>
#include <stdio.h>
#include "slot.h"
#include "applemmu.h"
class PlfClock : public Slot {
public:
PlfClock(AppleMMU *mmu);
virtual ~PlfClock();
virtual void Reset();
virtual uint8_t readSwitches(uint8_t s);
virtual void writeSwitches(uint8_t s, uint8_t v);
virtual void loadROM(uint8_t *toWhere);
private:
AppleMMU *mmu;
};
#endif

Wyświetl plik

@ -0,0 +1,117 @@
#include "plf-display.h"
extern "C" {
#include "emuapi.h"
#include "platform_config.h"
}
#include <Arduino.h>
#include "bios-font.h"
#ifdef HAS_T4_VGA
#include "vga_t_dma.h"
typedef uint8_t Pixel;
#define RGBVAL(r,g,b) RGBVAL8(r,g,b)
#else
#include "tft_t_dma.h"
typedef uint16_t Pixel;
#define RGBVAL(r,g,b) RGBVAL16(r,g,b)
#endif
#include "globals.h"
#include "applevm.h"
#define BLACK RGBVAL(0,0,0) //0x0000 // 0 black
#define MAGENTA RGBVAL(192,0,48) //0xC006 // 1 magenta
#define DARK_BLUE RGBVAL(0,0,128) //0x0010 // 2 dark blue
#define PURPLE RGBVAL(160,52,168) //0xA1B5 // 3 purple
#define DARK_GREEN RGBVAL(0,144,0) //0x0480 // 4 dark green
#define DARK_GREY RGBVAL(104,104,104) //0x6B4D // 5 dark grey
#define BLUE RGBVAL(24,112,248) //0x1B9F // 6 med blue
#define LIGHT_BLUE RGBVAL(8,188,232) //0x0DFD // 7 light blue
#define BROWN RGBVAL(144,84,40) //0x92A5 // 8 brown
#define ORANGE RGBVAL(248,24,40) //0xF8C5 // 9 orange
#define LIGHT_GREY RGBVAL(144,168,168) //0x9555 // 10 light gray
#define PINK RGBVAL(248,156,144) //0xFCF2 // 11 pink
#define GREEN RGBVAL(0,252,0) //0x07E0 // 12 green
#define YELLOW RGBVAL(248,252,0) //0xFFE0 // 13 yellow
#define AQUA RGBVAL(128,252,128) //0x87F0 // 14 aqua
#define WHITE RGBVAL(248,252,248) //0xFFFF // 15 white
// RGB map of each of the lowres colors
const Pixel loresPixelColors[16] = {
BLACK, // 0 black
MAGENTA, // 1 magenta
DARK_BLUE, // 2 dark blue
PURPLE, // 3 purple
DARK_GREEN, // 4 dark green
DARK_GREY, // 5 dark grey
BLUE, // 6 med blue
LIGHT_BLUE, // 7 light blue
BROWN, // 8 brown
ORANGE, // 9 orange
LIGHT_GREY, // 10 light gray
PINK, // 11 pink
GREEN, // 12 green
YELLOW, // 13 yellow
AQUA, // 14 aqua
WHITE // 15 white
};
Pixel backgroundColor;
PlfDisplay::PlfDisplay()
{
/*
for (int i = 0; i<16; i++) {
uint16_t val = loresPixelColors[i];
uint16_t r = ((val >> (6+5))&0x1f)<<3;
uint16_t g = ((val >> (5))&0x3f)<<2;
uint16_t b = ((val)&0x1f)<<3;
printf("c %i RGBVAL(%u,%u,%u)\n", i,r,g,b);
}
*/
backgroundColor = DARK_BLUE;
}
PlfDisplay::~PlfDisplay()
{
}
void PlfDisplay::redraw()
{
}
void PlfDisplay::blit(AiieRect r)
{
uint8_t *videoBuffer = g_vm->videoBuffer; // FIXME: poking deep
uint16_t pixel;
for (uint8_t y=r.top; y<=r.bottom; y++) {
Pixel * scrlinept = (Pixel *)emu_LineBuffer(y);
scrlinept += (TFT_WIDTH - (r.right - r.left))/2;
for (uint16_t x=r.left; x<=r.right; x++) {
pixel = y * (DISPLAYRUN >> 1) + (x >> 1);
uint8_t colorIdx;
if (!(x & 0x01)) {
colorIdx = videoBuffer[pixel] >> 4;
} else {
colorIdx = videoBuffer[pixel] & 0x0F;
}
scrlinept[x] = loresPixelColors[colorIdx];
}
}
}
void PlfDisplay::drawDriveDoor(uint8_t which, bool isOpen)
{
}
void PlfDisplay::setDriveIndicator(uint8_t which, bool isRunning)
{
}
void PlfDisplay::drawBatteryStatus(uint8_t percent)
{
}

Wyświetl plik

@ -0,0 +1,25 @@
#ifndef __PLF_DISPLAY_H
#define __PLF_DISPLAY_H
#include <stdint.h>
#include "physicaldisplay.h"
class UTFT;
class BIOS;
class PlfDisplay : public PhysicalDisplay {
friend class BIOS;
public:
PlfDisplay();
virtual ~PlfDisplay();
virtual void blit(AiieRect r);
virtual void redraw();
virtual void drawDriveDoor(uint8_t which, bool isOpen);
virtual void setDriveIndicator(uint8_t which, bool isRunning);
virtual void drawBatteryStatus(uint8_t percent);
};
#endif

Wyświetl plik

@ -0,0 +1,232 @@
#include <string.h> // strcpy
#include <sys/types.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include "emuapi.h"
#include "plf-filemanager.h"
PlfFileManager::PlfFileManager()
{
numCached = 0;
}
PlfFileManager::~PlfFileManager()
{
}
int8_t PlfFileManager::openFile(const char *name)
{
//emu_printf("openFile");
//emu_printf(name);
// See if there's a hole to re-use...
for (int i=0; i<numCached; i++) {
if (cachedNames[i][0] == '\0') {
strncpy(cachedNames[i], name, MAXPATH-1);
cachedNames[i][MAXPATH-1] = '\0'; // safety: ensure string terminator
fileSeekPositions[i] = 0;
return i;
}
}
// check for too many open files
if (numCached >= MAXFILES)
return -1;
// No, so we'll add it to the end
strncpy(cachedNames[numCached], name, MAXPATH-1);
cachedNames[numCached][MAXPATH-1] = '\0'; // safety: ensure string terminator
fileSeekPositions[numCached] = 0;
numCached++;
return numCached-1;
}
void PlfFileManager::closeFile(int8_t fd)
{
// invalid fd provided?
if (fd < 0 || fd >= numCached)
return;
// clear the name
cachedNames[fd][0] = '\0';
}
const char *PlfFileManager::fileName(int8_t fd)
{
if (fd < 0 || fd >= numCached)
return NULL;
return cachedNames[fd];
}
int8_t PlfFileManager::readDir(const char *where, const char *suffix, char fileDirectory[BIOS_MAXFILES][BIOS_MAXPATH+1], int16_t startIdx, uint16_t maxlen)
{
emu_printf("readdir should not be called!!!");
return 0;
}
// suffix may be comma-separated
int16_t PlfFileManager::readDir(const char *where, const char *suffix, char *outputFN, int16_t startIdx, uint16_t maxlen)
{
emu_printf("readdir should not be called!!!");
return 0;
}
void PlfFileManager::seekBlock(int8_t fd, uint16_t block, bool isNib)
{
//emu_printf("seekBlock\n");
//emu_printi(block);
if (fd < 0 || fd >= numCached)
return;
if (isNib) {
fileSeekPositions[fd] = block * 416;
} else {
fileSeekPositions[fd] = block * 256;
}
}
bool PlfFileManager::readTrack(int8_t fd, uint8_t *toWhere, bool isNib)
{
if (fd < 0 || fd >= numCached)
return false;
if (cachedNames[fd][0] == 0)
return false;
//printf("readTrack %s %d %lu\n", cachedNames[fd], isNib, (unsigned long)toWhere);
//emu_printf("readTrack");
// open, seek, read, close.
bool ret = false;
int f = emu_FileOpen(cachedNames[fd], "r+b");
emu_FileSeek(f, fileSeekPositions[fd], 0);
if (isNib) {
ret = (emu_FileRead((char*)toWhere, 0x1A00, f) == 0x1A00);
} else {
int read = emu_FileRead((char*)toWhere, 256 * 16, f);
//printf("track read %d\n", read);
ret = ( read == 256 * 16);
}
//emu_printf("track read\n");
emu_FileClose(f);
return ret;
}
bool PlfFileManager::readBlock(int8_t fd, uint8_t *toWhere, bool isNib)
{
//emu_printf("readBlock");
// open, seek, read, close.
if (fd < 0 || fd >= numCached)
return false;
if (cachedNames[fd][0] == 0)
return false;
// open, seek, read, close.
bool ret = false;
int f = emu_FileOpen(cachedNames[fd], "r+b");
if (f != -1) {
emu_FileSeek(f, fileSeekPositions[fd], SEEK_SET);
if (isNib) {
ret = (emu_FileRead(toWhere, 416, f) == 416);
} else {
ret = (emu_FileRead(toWhere, 256, f) == 256);
}
emu_FileClose(f);
}
return ret;
}
bool PlfFileManager::writeBlock(int8_t fd, uint8_t *fromWhere, bool isNib)
{
emu_printf("writeBlock should not be called");
#ifdef unused
// open, seek, write, close.
if (fd < 0 || fd >= numCached)
return false;
if (cachedNames[fd][0] == 0)
return false;
// don't know how to do this without seeking through the nibblized
// track data, so just give up for now
if (isNib)
return false;
// open, seek, write, close.
int ffd = open(cachedNames[fd], O_WRONLY);
if (ffd != -1) {
if (lseek(ffd, fileSeekPositions[fd], SEEK_SET) != fileSeekPositions[fd]) {
printf("ERROR: failed to seek to %lu\n", fileSeekPositions[fd]);
return false;
}
if (write(ffd, fromWhere, 256) != 256) {
printf("ERROR: failed to write 256 bytes\n");
return false;
}
close(ffd);
}
#endif
return true;
}
bool PlfFileManager::readState(int8_t fd) {
return false;
}
bool PlfFileManager::writeState(int8_t fd){
return false;
}
bool PlfFileManager::readBlocks(int8_t fd, uint8_t *toWhere, uint8_t blocks, bool isNib)
{
emu_printf("readBlocks should not be called\n");
return false;
}
bool PlfFileManager::writeTrack(int8_t fd, uint8_t *fromWhere, bool isNib)
{
emu_printf("writeTrackshould not be called");
#ifdef unused
// open, seek, write, close.
if (fd < 0 || fd >= numCached)
return false;
if (cachedNames[fd][0] == 0)
return false;
// open, seek, write, close.
int ffd = open(cachedNames[fd], O_WRONLY);
if (ffd != -1) {
if (lseek(ffd, fileSeekPositions[fd], SEEK_SET) != fileSeekPositions[fd]) {
printf("ERROR: failed to seek to %lu\n", fileSeekPositions[fd]);
return false;
}
int16_t wrsize = 256 * 16;
if (isNib)
wrsize = 0x1A00;
if (write(ffd, fromWhere, wrsize) != wrsize) {
printf("ERROR: failed to write bytes\n");
return false;
}
close(ffd);
}
#endif
return true;
}

Wyświetl plik

@ -0,0 +1,35 @@
#ifndef __PLFFILEMANAGER_H
#define __PLFFILEMANAGER_H
#include "filemanager.h"
#include <stdint.h>
class PlfFileManager : public FileManager {
public:
PlfFileManager();
virtual ~PlfFileManager();
virtual int8_t openFile(const char *name);
virtual void closeFile(int8_t fd);
virtual const char *fileName(int8_t fd);
virtual int8_t readDir(const char *where, const char *suffix, char fileDirectory[BIOS_MAXFILES][BIOS_MAXPATH+1], int16_t startIdx, uint16_t maxlen);
virtual int16_t readDir(const char *where, const char *suffix, char *outputFN, int16_t startIdx, uint16_t maxlen);
virtual void seekBlock(int8_t fd, uint16_t block, bool isNib = false);
virtual bool readTrack(int8_t fd, uint8_t *toWhere, bool isNib = false);
virtual bool readBlock(int8_t fd, uint8_t *toWhere, bool isNib = false);
virtual bool readBlocks(int8_t fd, uint8_t *toWhere, uint8_t blocks, bool isNib = false);
virtual bool writeBlock(int8_t fd, uint8_t *fromWhere, bool isNib = false);
virtual bool writeTrack(int8_t fd, uint8_t *fromWhere, bool isNib = false);
virtual bool readState(int8_t fd);
virtual bool writeState(int8_t fd);
private:
int8_t numCached;
char cachedNames[MAXFILES][MAXPATH];
unsigned long fileSeekPositions[MAXFILES];
};
#endif

Wyświetl plik

@ -0,0 +1,533 @@
#include "plf-keyboard.h"
#include "globals.h"
//#include <Keypad.h>
#include "RingBuf.h"
#define byte unsigned char
const byte ROWS = 3;
const byte COLS = 7;
//Panel keys and joystick
static unsigned char keys[ROWS][COLS] = {
{ BUT1, LJOY, UJOY, RJOY, DJOY },
{ BUT2, ENTKEY, LKEY, DKEY, RKEY },
{ JOYKEY, NOTEKEY, LWHTKEY, UKEY, RWHTKEY }
};
uint8_t rowsPins[ROWS] = { 24, 25, 26 };
uint8_t colsPins[COLS] = { 27, 28, 29, 30, 31 };
//Keypad keypad(makeKeymap(keys), rowsPins, colsPins, ROWS, COLS);
RingBuf bufferUsb(10); // 10 keys should be plenty, right?
static uint8_t panelBIOS[15] = { LARR, RARR, UARR, DARR, RET, ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9' };
uint8_t panelMap[15] = { LJOY, RJOY, UJOY, DJOY, LA, RA, 'j', 'k', '1', UARR, '2', RET, LARR, DARR, RARR };
//PS2 101 key keyboard layout
const int ps2KeyLayout[102] =
{
0, 0, 0, 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', RET, ESC,
DEL, TAB, ' ', '-', '=', '[', ']','\\', '?', ';','\'', '`', ',', '.',
'/',LOCK, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
SYSRQ, 0, 0, 0, 0, 0, 0, 0, 0,RARR,LARR,DARR,UARR, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, LA, RA
};
static uint8_t shiftedNumber[] = { '<', // ,
'_', // -
'>', // .
'?', // /
')', // 0
'!', // 1
'@', // 2
'#', // 3
'$', // 4
'%', // 5
'^', // 6
'&', // 7
'*', // 8
'(', // 9
0, // (: is not a key)
':' // ;
};
//bool buffered;
bool leftShiftPressed;
bool rightShiftPressed;
bool ctrlPressed;
bool capsLock;
bool leftApplePressed;
bool rightApplePressed;
int8_t numPressed;
bool buffered;
VMKeyboard *vmKeyboard;
int16_t paddle0;
int16_t paddle1;
uint8_t currentJoyMode = JOY_MODE_ANA_ABS;
PlfKeyboard::PlfKeyboard(VMKeyboard *k) : PhysicalKeyboard(k)
{
//keypad.setDebounceTime(5);
//myusb.begin();
//keyboard1.attachPress(onPress);
//keyboard1.attachRelease(onRelease);
//keyboard1.rawOnly(true);
leftShiftPressed = false;
rightShiftPressed = false;
ctrlPressed = false;
capsLock = true;
leftApplePressed = false;
rightApplePressed = false;
buffered = false;
vmKeyboard = k;
numPressed = 0;
paddle0 = 127;
paddle1 = 127;
}
PlfKeyboard::~PlfKeyboard()
{
}
uint8_t PlfKeyboard::getMapping(uint8_t key) {
return panelMap[key];
}
void PlfKeyboard::setMapping(uint8_t key, uint8_t val) {
panelMap[key] = val;
}
void PlfKeyboard::onPress(int unicode)
{
/*
uint8_t modifiers = 0; //keyboard1.getModifiers();
uint8_t key = unicode & 0x7f; //keyboard1.getOemKey();
if (key > 101) key = 101;
if (buffered) {
pressedKey(ps2KeyLayout[key], modifiers);
} else {
vmKeyboard->keyDepressed(ps2KeyLayout[key], modifiers);
}
*/
if (unicode <= 0x7f) {
int8_t modifiers = 0;
vmKeyboard->keyDepressed(unicode & 0x7f, modifiers);
}
else {
vmKeyboard->keyDepressed(unicode);
}
}
void PlfKeyboard::onRelease(int unicode)
{
/*
uint8_t key = unicode & 0x7f; //keyboard1.getOemKey();
uint8_t modifiers = 0; //keyboard1.getModifiers();
if (key > 101) key = 101;
if (buffered) {
releasedKey(ps2KeyLayout[key], modifiers);
} else {
vmKeyboard->keyReleased(ps2KeyLayout[key], modifiers);
}
*/
if (unicode <= 0x7f) {
int8_t modifiers = 0;
vmKeyboard->keyReleased(unicode & 0x7f, modifiers);
}
else {
vmKeyboard->keyReleased(unicode);
}
}
void PlfKeyboard::pressedKey(uint8_t key, uint8_t mod)
{
numPressed++;
if (key != SYSRQ && key & 0x80) {
// it's a modifier key.
switch (key) {
case _CTRL:
ctrlPressed = 1;
break;
case LSHFT:
leftShiftPressed = 1;
break;
case RSHFT:
rightShiftPressed = 1;
break;
case LOCK:
capsLock = !capsLock;
break;
case LA:
leftApplePressed = 1;
break;
case RA:
rightApplePressed = 1;
break;
}
return;
}
if (key == ' ' || key == DEL || key == ESC || key == RET || key == TAB || key == SYSRQ) {
//Serial.println((int)key);
bufferUsb.addByte(key);
return;
}
if (key >= 'a' &&
key <= 'z') {
if (ctrlPressed) {
bufferUsb.addByte(key - 'a' + 1);
return;
}
if (leftShiftPressed || rightShiftPressed || capsLock) {
bufferUsb.addByte(key - 'a' + 'A');
return;
}
bufferUsb.addByte(key);
return;
}
// FIXME: can we control-shift?
if (key >= ',' && key <= ';') {
if (leftShiftPressed || rightShiftPressed) {
bufferUsb.addByte(shiftedNumber[key - ',']);
return;
}
bufferUsb.addByte(key);
return;
}
if (leftShiftPressed || rightShiftPressed) {
uint8_t ret = 0;
switch (key) {
case '=':
ret = '+';
break;
case '[':
ret = '{';
break;
case ']':
ret = '}';
break;
case '\\':
ret = '|';
break;
case '\'':
ret = '"';
break;
case '`':
ret = '~';
break;
}
if (ret) {
bufferUsb.addByte(ret);
return;
}
}
// Everything else falls through.
bufferUsb.addByte(key);
}
void PlfKeyboard::releasedKey(uint8_t key, uint8_t mod)
{
numPressed--;
if (key & 0x80) {
// it's a modifier key.
switch (key) {
case _CTRL:
ctrlPressed = 0;
break;
case LSHFT:
leftShiftPressed = 0;
break;
case RSHFT:
rightShiftPressed = 0;
break;
case LA:
leftApplePressed = 0;
break;
case RA:
rightApplePressed = 0;
break;
}
}
}
bool PlfKeyboard::kbhit()
{
//USB keyboard
if (!buffered) {
bufferUsb.clear();
buffered = true;
}
//myusb.Task();
//Front panel keys
/*
if (keypad.getKeys()) {
for (int i=0; i<LIST_MAX; i++) {
if ( keypad.key[i].stateChanged ) {
switch (keypad.key[i].kstate) {
case PRESSED:
pressedKey(panelBIOS[keypad.key[i].kchar - 0x88], 0);
break;
case RELEASED:
releasedKey(panelBIOS[keypad.key[i].kchar - 0x88], 0);
break;
case HOLD:
case IDLE:
break;
}
}
}
}
*/
// For debugging: also allow USB serial to act as a keyboard
//if (Serial.available()) {
// bufferUsb.addByte(Serial.read());
//}
return bufferUsb.hasData();
}
uint8_t PlfKeyboard::read()
{
if (bufferUsb.hasData()) {
return bufferUsb.consumeByte();
}
return 0;
}
//Set joystick mode to analog, joyport1 or joyport2
void PlfKeyboard::setJoymode(uint8_t mode) {
currentJoyMode = mode;
if (mode == JOY_MODE_ANA_ABS || mode == JOY_MODE_ANA_REL)
//Regular button interface
vmKeyboard->setButtons(false, false, false);
else if (mode == JOY_MODE_JOYPORT1 || mode == JOY_MODE_JOYPORT2)
//Joyport inputs are active low
vmKeyboard->setButtons(true, true, true);
}
//Joyport stick switches
bool stick_up = false;
bool stick_down = false;
bool stick_left = false;
bool stick_right = false;
bool stick_trig = false;
//Set joystick input buttons based on annunciator outputs
// Emulates joyport circuit
// http://lukazi.blogspot.nl/2009/04/game-controller-atari-joysticks.html
void updateJoyport() {
if (currentJoyMode != JOY_MODE_ANA_ABS && currentJoyMode != JOY_MODE_ANA_REL) {
if ((!vmKeyboard->getAnnunciator(0) && currentJoyMode == JOY_MODE_JOYPORT1) || //Joystick 1 enabled
(vmKeyboard->getAnnunciator(0) && currentJoyMode == JOY_MODE_JOYPORT2)) { //Joystick 2 enabled
vmKeyboard->setButton(0, !stick_trig); //Button
if (!vmKeyboard->getAnnunciator(1)) { //Joystick L/R or U/D
vmKeyboard->setButton(1, !stick_left);
vmKeyboard->setButton(2, !stick_right);
} else {
vmKeyboard->setButton(1, !stick_up);
vmKeyboard->setButton(2, !stick_down);
}
}
else vmKeyboard->setButtons(true, true, true); //No joystick
}
}
//Called from MMU when game port annunciators are changed
void PlfKeyboard::setAnnunciators() {
updateJoyport();
}
void updateRelStick() {
if (currentJoyMode == JOY_MODE_ANA_REL) {
//Relative Joystick Emulation
if (stick_left) {
paddle0 -= g_joySpeed;
if (paddle0 < 0) paddle0 = 0;
}
else if (stick_right) {
paddle0 += g_joySpeed;
if (paddle0 > 255) paddle0 = 255;
}
if (stick_up) {
paddle1 -= g_joySpeed;
if (paddle1 < 0) paddle1 = 0;
}
else if (stick_down) {
paddle1 += g_joySpeed;
if (paddle1 > 255) paddle1 = 255;
}
}
}
//Joystick emulation
// Returns true if key was handled by stick
bool pressStick(uint8_t key) {
//Joyport emulation
if (currentJoyMode != JOY_MODE_ANA_ABS) {
if (key == LJOY) {
stick_left = true;
stick_right = false;
updateJoyport();
return true;
}
if (key == RJOY) {
stick_left = false;
stick_right = true;
updateJoyport();
return true;
}
if (key == UJOY) {
stick_up = true;
stick_down = false;
updateJoyport();
return true;
}
if (key == DJOY) {
stick_up = false;
stick_down = true;
updateJoyport();
return true;
}
if (key == LA && currentJoyMode != JOY_MODE_ANA_REL) {
stick_trig = true;
updateJoyport();
return true;
}
if (key == RA && currentJoyMode != JOY_MODE_ANA_REL) {
return true;
}
} else {
//Absolute Analog stick emulation
if (key == LJOY) {
paddle0 = 0;
return true;
}
if (key == RJOY) {
paddle0 = 255;
return true;
}
if (key == UJOY) {
paddle1 = 0;
return true;
}
if (key == DJOY) {
paddle1 = 255;
return true;
}
}
return false;
}
bool releaseStick(uint8_t key) {
//Joyport emulation
if (currentJoyMode != JOY_MODE_ANA_ABS) {
if (key == LJOY || key == RJOY) {
stick_left = false;
stick_right = false;
updateJoyport();
return true;
}
if (key == UJOY || key == DJOY) {
stick_up = false;
stick_down = false;
updateJoyport();
return true;
}
if (key == LA && currentJoyMode != JOY_MODE_ANA_REL) {
stick_trig = false;
updateJoyport();
return true;
}
if (key == RA && currentJoyMode != JOY_MODE_ANA_REL) {
return true;
}
} else {
//Absolute Analog stick emulation
if (key == LJOY || key == RJOY) {
paddle0 = g_joyTrimX;
return true;
}
if (key == UJOY || key == DJOY) {
paddle1 = g_joyTrimY;
return true;
}
}
return false;
}
bool capsLed = false;
// This is a non-buffered interface to the physical keyboard, as used
// by the VM.
void PlfKeyboard::maintainKeyboard()
{
static bool oldCapsLed = false;
if (oldCapsLed != capsLed) {
oldCapsLed = capsLed;
//if (keyboard1.connected()) keyboard1.capsLock(capsLed);
}
// Serial.println("maintain");
buffered = false;
//myusb.Task();
updateRelStick();
/*
if (keypad.getKeys()) {
for (int i=0; i<LIST_MAX; i++) {
if ( keypad.key[i].stateChanged ) {
uint8_t kchar = panelMap[keypad.key[i].kchar - 0x88];
switch (keypad.key[i].kstate) {
case PRESSED:
if (!pressStick(kchar))
vmkeyboard->keyDepressed(kchar);
break;
case RELEASED:
if (!releaseStick(kchar))
vmkeyboard->keyReleased(kchar);
break;
case HOLD:
case IDLE:
break;
}
}
}
}
*/
// For debugging: also allow USB serial to act as a keyboard
/*
if (Serial.available()) {
int c = Serial.read();
vmkeyboard->keyDepressed(c);
vmkeyboard->keyReleased(c);
}
*/
}
void PlfKeyboard::startReading()
{
g_vm->triggerPaddleInCycles(0, 12 * paddle0);
g_vm->triggerPaddleInCycles(1, 12 * paddle1);
}
void PlfKeyboard::setCaps(bool enabled) {
capsLed = enabled;
//Serial.print("Set Leds:");
//Serial.println(enabled?"On":"Off");
}

Wyświetl plik

@ -0,0 +1,37 @@
#ifndef __PLF_KEYBOARD_H
#define __PLF_KEYBOARD_H
#include "physicalkeyboard.h"
//void onPress(int unicode);
//void onRelease(int unicode);
class PlfKeyboard : public PhysicalKeyboard {
public:
PlfKeyboard(VMKeyboard *k);
virtual ~PlfKeyboard();
// Interface used by the VM...
virtual void maintainKeyboard();
// Interface used by the BIOS...
virtual bool kbhit();
virtual uint8_t read();
//Key joystick
virtual void startReading();
virtual void setJoymode(uint8_t mode);
virtual void setAnnunciators();
virtual void setCaps(bool enabled);
virtual void onPress(int unicode);
virtual void onRelease(int unicode);
virtual void pressedKey(uint8_t key, uint8_t mod);
virtual void releasedKey(uint8_t key, uint8_t mod);
//Panel mapping
virtual uint8_t getMapping(uint8_t key);
virtual void setMapping(uint8_t key, uint8_t val);
};
#endif

Wyświetl plik

@ -0,0 +1,64 @@
#include "plf-paddles.h"
/* C061: Open Apple (Paddle 0 button pressed if &= 0x80)
* C062: Closed Apple (Paddle 1 button pressed if &= 0x80)
* C064: PADDLE0 (sets bit 0x80 when value reached, increments of 11 us)
* C065: PADDLE1 (sets bit 0x80 when value reached, increments of 11 us)
* C070: "start reading paddle data" - "may take up to 3 milliseconds"
*/
#include "globals.h"
static uint8_t val0=128;
static uint8_t val1=128;
PlfPaddles::PlfPaddles(bool p0rev, bool p1rev)
{
this->p0rev = p0rev;
this->p1rev = p1rev;
}
PlfPaddles::~PlfPaddles()
{
}
void PlfPaddles::setPaddle0(uint8_t val)
{
val0 = val;
}
void PlfPaddles::setPaddle1(uint8_t val)
{
val1 = val;
}
uint8_t PlfPaddles::paddle0()
{
uint8_t raw = val0; // 255: LEFT, 0:RIGHT
if (p0rev) {
raw = 255 - raw;
}
return raw;
}
uint8_t PlfPaddles::paddle1()
{
uint8_t raw = val1;
if (p1rev) {
raw = 255 - raw;
}
return raw;
}
void PlfPaddles::startReading()
{
g_vm->triggerPaddleInCycles(0, 12 * paddle0());
g_vm->triggerPaddleInCycles(1, 12 * paddle1());
}
void PlfPaddles::setRev(bool p0rev, bool p1rev)
{
this->p0rev = p0rev;
this->p1rev = p1rev;
}

Wyświetl plik

@ -0,0 +1,20 @@
#include <Arduino.h>
#include "physicalpaddles.h"
class PlfPaddles : public PhysicalPaddles {
public:
PlfPaddles(bool p0rev, bool p1rev);
virtual ~PlfPaddles();
void setRev(bool p0rev, bool p1rev);
virtual void setPaddle0(uint8_t val);
virtual void setPaddle1(uint8_t val);
virtual uint8_t paddle0();
virtual uint8_t paddle1();
virtual void startReading();
bool p0rev;
bool p1rev;
};

Wyświetl plik

@ -0,0 +1,46 @@
#include "plf-speaker.h"
#include "globals.h"
PlfSpeaker::PlfSpeaker(uint8_t pinNum) : PhysicalSpeaker()
{
toggleState = false;
needsToggle = false;
speakerPin = pinNum;
mixerValue = numMixed = 0;
}
PlfSpeaker::~PlfSpeaker()
{
}
void PlfSpeaker::toggle()
{
needsToggle = true;
}
void PlfSpeaker::maintainSpeaker(uint32_t c)
{
if (needsToggle) {
toggleState = !toggleState;
needsToggle = false;
}
mixerValue += (toggleState ? 0x1FF : 0x00);
mixerValue >>= (16-g_volume);
}
void PlfSpeaker::beginMixing()
{
mixerValue = 0;
numMixed = 0;
}
void PlfSpeaker::mixOutput(uint8_t v)
{
mixerValue += v;
numMixed++;
}

Wyświetl plik

@ -0,0 +1,27 @@
#ifndef __PLF_SPEAKER_H
#define __PLF_SPEAKER_H
#include "physicalspeaker.h"
class PlfSpeaker : public PhysicalSpeaker {
public:
PlfSpeaker(uint8_t pinNum);
virtual ~PlfSpeaker();
virtual void toggle();
virtual void maintainSpeaker(uint32_t c);
virtual void beginMixing();
virtual void mixOutput(uint8_t v);
private:
uint8_t speakerPin;
bool toggleState;
bool needsToggle;
uint32_t mixerValue;
uint8_t numMixed;
};
#endif

Wyświetl plik

@ -0,0 +1,24 @@
#ifndef __SLOT_H
#define __SLOT_H
#ifdef TEENSYDUINO
#include <Arduino.h>
#else
#include <stdint.h>
#include <stdio.h>
#endif
class Slot {
public:
virtual ~Slot() {};
virtual void Reset() = 0; // for use at cold-boot
virtual uint8_t readSwitches(uint8_t s) = 0;
virtual void writeSwitches(uint8_t s, uint8_t v) = 0;
virtual void loadROM(uint8_t *toWhere) = 0;
};
#endif

Wyświetl plik

@ -0,0 +1,204 @@
extern "C" {
#include "emuapi.h"
#include "iopins.h"
}
#include "aiie.h"
#ifdef HAS_T4_VGA
#include "vga_t_dma.h"
TFT_T_DMA tft;
#else
#include "tft_t_dma.h"
TFT_T_DMA tft = TFT_T_DMA(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO, TFT_TOUCH_CS, TFT_TOUCH_INT);
#endif
bool vgaMode = false;
static unsigned char palette8[PALETTE_SIZE];
static unsigned short palette16[PALETTE_SIZE];
static IntervalTimer myTimer;
volatile boolean vbl=true;
static int skip=0;
static elapsedMicros tius;
static void vblCount() {
if (vbl) {
vbl = false;
} else {
vbl = true;
}
}
void emu_SetPaletteEntry(unsigned char r, unsigned char g, unsigned char b, int index)
{
if (index<PALETTE_SIZE) {
//Serial.println("%d: %d %d %d\n", index, r,g,b);
palette8[index] = RGBVAL8(r,g,b);
palette16[index] = RGBVAL16(r,g,b);
}
}
void emu_DrawVsync(void)
{
volatile boolean vb=vbl;
skip += 1;
skip &= VID_FRAME_SKIP;
if (!vgaMode) {
#ifdef HAS_T4_VGA
tft.waitSync();
#else
while (vbl==vb) {};
#endif
}
}
void emu_DrawLine(unsigned char * VBuf, int width, int height, int line)
{
if (!vgaMode) {
#ifdef HAS_T4_VGA
tft.writeLine(width,1,line, VBuf, palette8);
#else
tft.writeLine(width,1,line, VBuf, palette16);
#endif
}
}
void emu_DrawLine8(unsigned char * VBuf, int width, int height, int line)
{
if (!vgaMode) {
if (skip==0) {
#ifdef HAS_T4_VGA
tft.writeLine(width,height,line, VBuf);
#endif
}
}
}
void emu_DrawLine16(unsigned short * VBuf, int width, int height, int line)
{
if (!vgaMode) {
if (skip==0) {
#ifdef HAS_T4_VGA
tft.writeLine16(width,height,line, VBuf);
#else
tft.writeLine(width,height,line, VBuf);
#endif
}
}
}
void emu_DrawScreen(unsigned char * VBuf, int width, int height, int stride)
{
if (!vgaMode) {
if (skip==0) {
#ifdef HAS_T4_VGA
tft.writeScreen(width,height-TFT_VBUFFER_YCROP,stride, VBuf+(TFT_VBUFFER_YCROP/2)*stride, palette8);
#else
tft.writeScreen(width,height-TFT_VBUFFER_YCROP,stride, VBuf+(TFT_VBUFFER_YCROP/2)*stride, palette16);
#endif
}
}
}
int emu_FrameSkip(void)
{
return skip;
}
void * emu_LineBuffer(int line)
{
if (!vgaMode) {
return (void*)tft.getLineBuffer(line);
}
}
// ****************************************************
// the setup() method runs once, when the sketch starts
// ****************************************************
void setup() {
#ifdef HAS_T4_VGA
tft.begin(VGA_MODE_320x240);
// NVIC_SET_PRIORITY(IRQ_QTIMER3, 0);
#else
tft.begin();
#endif
emu_init();
}
// ****************************************************
// the loop() method runs continuously
// ****************************************************
void loop(void)
{
if (menuActive()) {
uint16_t bClick = emu_DebounceLocalKeys();
int action = handleMenu(bClick);
char * filename = menuSelection();
if (action == ACTION_RUN1) {
toggleMenu(false);
vgaMode = false;
emu_start();
emu_Init(filename);
tft.fillScreenNoDma( RGBVAL16(0x00,0x00,0x00) );
tft.startDMA();
myTimer.begin(vblCount, 20000); //to run every 20ms
}
delay(20);
}
else {
uint16_t bClick = emu_DebounceLocalKeys();
emu_Input(bClick);
emu_Step();
delay(10);
//uint16_t bClick = emu_DebounceLocalKeys();
//if (bClick & MASK_KEY_USER1) {
// emu_Input(bClick);
//}
}
}
#ifdef HAS_SND
#include "AudioPlaySystem.h"
AudioPlaySystem mymixer;
void emu_sndInit() {
Serial.println("sound init");
#ifdef HAS_T4_VGA
tft.begin_audio(256, mymixer.snd_Mixer);
#else
mymixer.begin_audio(256, mymixer.snd_Mixer);
#endif
// sgtl5000_1.enable();
// sgtl5000_1.volume(0.6);
mymixer.start();
}
void emu_sndPlaySound(int chan, int volume, int freq)
{
if (chan < 6) {
mymixer.sound(chan, freq, volume);
}
/*
Serial.print(chan);
Serial.print(":" );
Serial.print(volume);
Serial.print(":" );
Serial.println(freq);
*/
}
void emu_sndPlayBuzz(int size, int val) {
mymixer.buzz(size,val);
//Serial.print((val==1)?1:0);
//Serial.print(":");
//Serial.println(size);
}
#endif

Wyświetl plik

@ -0,0 +1,234 @@
/*
Based on C64 ILI9341 dma driver from Frank Bösing, 2017
*/
#ifndef _TFT_T_DMAH_
#define _TFT_T_DMAH_
#ifdef __cplusplus
#include <Arduino.h>
#include <SPI.h>
#include <DMAChannel.h>
#endif
#include "tft_t_dma_config.h"
#define RGBVAL32(r,g,b) ( (r<<16) | (g<<8) | b )
#define RGBVAL16(r,g,b) ( (((r>>3)&0x1f)<<11) | (((g>>2)&0x3f)<<5) | (((b>>3)&0x1f)<<0) )
#define RGBVAL8(r,g,b) ( (((r>>5)&0x07)<<5) | (((g>>5)&0x07)<<2) | (((b>>6)&0x3)<<0) )
#define R16(rgb) ((rgb>>8)&0xf8)
#define G16(rgb) ((rgb>>3)&0xfc)
#define B16(rgb) ((rgb<<3)&0xf8)
#define PAL_COLOR_MASK 0xff
#ifdef LOHRES
#define TFT_WIDTH 240
#define TFT_REALWIDTH 240
#else
#define TFT_WIDTH 320
#define TFT_REALWIDTH 320
#endif
#define TFT_HEIGHT 240
#define TFT_REALHEIGHT 240
//#define WIDTH 272
//#define HEIGHT 228
#define LINES_PER_BLOCK 64
#define NR_OF_BLOCK 4
#define SCREEN_DMA_NUM_SETTINGS NR_OF_BLOCK
#ifdef ILI9341
#define ILI9341_NOP 0x00
#define ILI9341_SWRESET 0x01
#define ILI9341_RDDID 0x04
#define ILI9341_RDDST 0x09
#define ILI9341_SLPIN 0x10
#define ILI9341_SLPOUT 0x11
#define ILI9341_PTLON 0x12
#define ILI9341_NORON 0x13
#define ILI9341_RDMODE 0x0A
#define ILI9341_RDMADCTL 0x0B
#define ILI9341_RDPIXFMT 0x0C
#define ILI9341_RDIMGFMT 0x0D
#define ILI9341_RDSELFDIAG 0x0F
#define ILI9341_INVOFF 0x20
#define ILI9341_INVON 0x21
#define ILI9341_GAMMASET 0x26
#define ILI9341_DISPOFF 0x28
#define ILI9341_DISPON 0x29
#define ILI9341_CASET 0x2A
#define ILI9341_PASET 0x2B
#define ILI9341_RAMWR 0x2C
#define ILI9341_RAMRD 0x2E
#define ILI9341_PTLAR 0x30
#define ILI9341_MADCTL 0x36
#define ILI9341_VSCRSADD 0x37
#define ILI9341_PIXFMT 0x3A
#define ILI9341_FRMCTR1 0xB1
#define ILI9341_FRMCTR2 0xB2
#define ILI9341_FRMCTR3 0xB3
#define ILI9341_INVCTR 0xB4
#define ILI9341_DFUNCTR 0xB6
#define ILI9341_PWCTR1 0xC0
#define ILI9341_PWCTR2 0xC1
#define ILI9341_PWCTR3 0xC2
#define ILI9341_PWCTR4 0xC3
#define ILI9341_PWCTR5 0xC4
#define ILI9341_VMCTR1 0xC5
#define ILI9341_VMCTR2 0xC7
#define ILI9341_RDID1 0xDA
#define ILI9341_RDID2 0xDB
#define ILI9341_RDID3 0xDC
#define ILI9341_RDID4 0xDD
#define ILI9341_GMCTRP1 0xE0
#define ILI9341_GMCTRN1 0xE1
#define ILI9341_MADCTL_MY 0x80
#define ILI9341_MADCTL_MX 0x40
#define ILI9341_MADCTL_MV 0x20
#define ILI9341_MADCTL_ML 0x10
#define ILI9341_MADCTL_RGB 0x00
#define ILI9341_MADCTL_BGR 0x08
#define ILI9341_MADCTL_MH 0x04
#define TFT_CASET ILI9341_CASET
#define TFT_PASET ILI9341_PASET
#define TFT_RAMWR ILI9341_RAMWR
#define TFT_MADCTL ILI9341_MADCTL
#endif
#ifdef ST7789
#define ST7735_NOP 0x00
#define ST7735_SWRESET 0x01
#define ST7735_RDDID 0x04
#define ST7735_RDDST 0x09
#define ST7735_SLPIN 0x10
#define ST7735_SLPOUT 0x11
#define ST7735_PTLON 0x12
#define ST7735_NORON 0x13
#define ST7735_INVOFF 0x20
#define ST7735_INVON 0x21
#define ST7735_DISPOFF 0x28
#define ST7735_DISPON 0x29
#define ST7735_CASET 0x2A
#define ST7735_RASET 0x2B
#define ST7735_RAMWR 0x2C
#define ST7735_RAMRD 0x2E
#define ST7735_PTLAR 0x30
#define ST7735_COLMOD 0x3A
#define ST7735_MADCTL 0x36
#define ST7735_FRMCTR1 0xB1
#define ST7735_FRMCTR2 0xB2
#define ST7735_FRMCTR3 0xB3
#define ST7735_INVCTR 0xB4
#define ST7735_DISSET5 0xB6
#define ST7735_PWCTR1 0xC0
#define ST7735_PWCTR2 0xC1
#define ST7735_PWCTR3 0xC2
#define ST7735_PWCTR4 0xC3
#define ST7735_PWCTR5 0xC4
#define ST7735_VMCTR1 0xC5
#define ST7735_RDID1 0xDA
#define ST7735_RDID2 0xDB
#define ST7735_RDID3 0xDC
#define ST7735_RDID4 0xDD
#define ST7735_PWCTR6 0xFC
#define ST7735_GMCTRP1 0xE0
#define ST7735_GMCTRN1 0xE1
#define ST77XX_MADCTL_MY 0x80
#define ST77XX_MADCTL_MX 0x40
#define ST77XX_MADCTL_MV 0x20
#define ST77XX_MADCTL_ML 0x10
#define ST77XX_MADCTL_RGB 0x00
#define ST77XX_MADCTL_BGR 0x08
#define ST77XX_MADCTL_MH 0x04
#define TFT_CASET ST7735_CASET
#define TFT_PASET ST7735_RASET
#define TFT_RAMWR ST7735_RAMWR
#define TFT_MADCTL ST7735_MADCTL
#endif
#ifdef __cplusplus
class TFT_T_DMA
{
public:
TFT_T_DMA(uint8_t _CS, uint8_t _DC, uint8_t _RST = 255, uint8_t _MOSI=11, uint8_t _SCLK=13, uint8_t _MISO=12, uint8_t touch_cs=38, uint8_t touch_irq=37);
void setArea(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2);
void begin(void);
void flipscreen(bool flip);
boolean isflipped(void);
void startDMA(void);
void stopDMA();
int get_frame_buffer_size(int *width, int *height);
// Touch screen functions
#define TOUCH_ENABLED() ((_touch_cs != 255) && (_touch_irq != 255))
bool isTouching(void) { return ((!TOUCH_ENABLED())?false:(digitalRead(_touch_irq) == LOW)); }
void readRaw(uint16_t * oX, uint16_t * oY, uint16_t * oZ);
void readCal(uint16_t * oX, uint16_t * oY, uint16_t * oZ);
void callibrateTouch(uint16_t xMin,uint16_t yMin,uint16_t xMax,uint16_t yMax);
// NoDMA functions
void writeScreenNoDma(const uint16_t *pcolors);
void fillScreenNoDma(uint16_t color);
void drawTextNoDma(int16_t x, int16_t y, const char * text, uint16_t fgcolor, uint16_t bgcolor, bool doublesize);
void drawRectNoDma(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void drawSpriteNoDma(int16_t x, int16_t y, const uint16_t *bitmap);
void drawSpriteNoDma(int16_t x, int16_t y, const uint16_t *bitmap, uint16_t croparx, uint16_t cropary, uint16_t croparw, uint16_t croparh);
// DMA functions
uint16_t * getLineBuffer(int j);
void writeScreen(int width, int height, int stride, uint8_t *buffer, uint16_t *palette16);
void writeLine(int width, int height, int stride, uint8_t *buffer, uint16_t *palette16);
void writeLine(int width, int height, int y, uint16_t *buf);
void fillScreen(uint16_t color);
void drawText(int16_t x, int16_t y, const char * text, uint16_t fgcolor, uint16_t bgcolor, bool doublesize);
void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void drawSprite(int16_t x, int16_t y, const uint16_t *bitmap);
void drawSprite(int16_t x, int16_t y, const uint16_t *bitmap, uint16_t croparx, uint16_t cropary, uint16_t croparw, uint16_t croparh);
protected:
uint8_t _rst, _cs, _dc;
uint8_t _miso, _mosi, _sclk;
uint8_t _touch_irq=255, _touch_cs=255;
bool flipped=false;
void wait(void);
void enableTouchIrq();
};
#endif
#endif

Wyświetl plik

@ -0,0 +1,14 @@
#include "platform_config.h"
//#define ST7789 1
//#define ILI9341 1
#define TFT_LINEARINT 1
#define LINEARINT_HACK 1
//#define FLIP_SCREEN 1
//#define TFT_DEBUG 1
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
//#define TFT_STATICFB 1
#endif

Wyświetl plik

@ -0,0 +1,53 @@
/*
Wrapping class to extend VGA_T4 to TFT_T_DMA
*/
#ifndef _VGA_T_DMAH_
#define _VGA_T_DMAH_
#ifdef __cplusplus
#include <VGA_t4.h>
#endif
#define RGBVAL16(r,g,b) ( (((r>>5)&0x07)<<5) | (((g>>5)&0x07)<<2) | (((b>>6)&0x3)<<0) )
#define RGBVAL8(r,g,b) ( (((r>>5)&0x07)<<5) | (((g>>5)&0x07)<<2) | (((b>>6)&0x3)<<0) )
#define TFT_WIDTH 320
#define TFT_REALWIDTH 320
#define TFT_HEIGHT 240
#define TFT_REALHEIGHT 240
#ifdef __cplusplus
class TFT_T_DMA: public VGA_T4
{
public:
// Fake touch screen functions
bool isTouching(void) { return false; }
void readRaw(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { }
void readCal(uint16_t * oX, uint16_t * oY, uint16_t * oZ) { };
void callibrateTouch(uint16_t xMin,uint16_t yMin,uint16_t xMax,uint16_t yMax) { }
// fake DMA functions
void startDMA(void) { };
void stopDMA(void) { };
// fake no DMA functions
void writeScreenNoDma(const vga_pixel *pcolors) { writeScreen(pcolors); }
void fillScreenNoDma(vga_pixel color) { clear(color); }
void drawTextNoDma(int16_t x, int16_t y, const char * text, vga_pixel fgcolor, vga_pixel bgcolor, bool doublesize) { drawText(x,y,text,fgcolor,bgcolor,doublesize); }
void drawRectNoDma(int16_t x, int16_t y, int16_t w, int16_t h, vga_pixel color) { drawRect(x, y, w, h, color); }
void drawSpriteNoDma(int16_t x, int16_t y, const uint16_t *bitmap) { drawSprite(x, y, bitmap); }
void drawSpriteNoDma(int16_t x, int16_t y, const uint16_t *bitmap, uint16_t croparx, uint16_t cropary, uint16_t croparw, uint16_t croparh) { drawSprite(x, y, bitmap, croparx, cropary, croparw, croparh); }
};
#endif
#endif

Wyświetl plik

@ -0,0 +1,34 @@
#ifndef __VM_H
#define __VM_H
#include <stdint.h>
#include <stdlib.h> // for calloc
#include "mmu.h"
#include "vmdisplay.h"
#include "vmkeyboard.h"
#define DISPLAYWIDTH 320
#define DISPLAYHEIGHT 240
#define DISPLAYRUN 320 // how wide each row is in pixels in the buffer (for faster math)
class VM {
public:
VM() { mmu=NULL; vmdisplay = NULL; videoBuffer = (uint8_t *)calloc(DISPLAYRUN * DISPLAYHEIGHT / 2, 1); hasIRQ = false;}
virtual ~VM() { if (mmu) delete mmu; if (vmdisplay) delete vmdisplay; free(videoBuffer); }
virtual void SetMMU(MMU *mmu) { this->mmu = mmu; }
virtual MMU *getMMU() { return mmu; }
virtual VMKeyboard *getKeyboard() = 0;
virtual void Reset() = 0;
virtual void triggerPaddleInCycles(uint8_t paddleNum, uint16_t cycleCount) = 0;
uint8_t *videoBuffer;
VMDisplay *vmdisplay;
MMU *mmu;
bool hasIRQ;
};
#endif

Wyświetl plik

@ -0,0 +1,28 @@
#ifndef __VMDISPLAY_H
#define __VMDISPLAY_H
class MMU;
typedef struct {
uint8_t top;
uint16_t left;
uint8_t bottom;
uint16_t right;
} AiieRect;
class VMDisplay {
public:
VMDisplay(uint8_t *vb) { videoBuffer = vb; }
virtual ~VMDisplay() { videoBuffer = NULL; };
virtual void SetMMU(MMU *m) { mmu = m; }
virtual bool needsRedraw() = 0;
virtual void didRedraw() = 0;
virtual AiieRect getDirtyRect() = 0;
MMU *mmu;
uint8_t *videoBuffer;
};
#endif

Wyświetl plik

@ -0,0 +1,20 @@
#ifndef __VMKEYBOARD_H
#define __VMKEYBOARD_H
#include <stdint.h>
class VMKeyboard {
public:
virtual ~VMKeyboard() {}
virtual void keyDepressed(uint8_t k) = 0;
virtual void keyReleased(uint8_t k) = 0;
virtual void keyDepressed(uint8_t k, uint8_t m) = 0;
virtual void keyReleased(uint8_t k, uint8_t m) = 0;
virtual void setButton(uint8_t index, bool val) = 0;
virtual void setButtons(bool b0, bool b1, bool b2) = 0;
virtual bool getAnnunciator(uint8_t index) = 0;
virtual void maintainKeyboard(uint32_t cycleCount) = 0;
};
#endif

Wyświetl plik

@ -0,0 +1,83 @@
#include "widgets.h"
#include "globals.h"
Widgets::Widgets()
{
}
Widgets::~Widgets()
{
}
void Widgets::drawBatteryText() {
/*
uint16_t back = BLACK;
char buf[10];
if (g_charge > 15) {
sprintf(buf, "Charge"); //charging
back = BLUE;
}
else {
if (g_battery > 1000) {
int bat = g_battery;
if (bat > 4200) bat = 4200;
if (bat < 3000) bat = 3000;
if (bat > 3400) back = DARK_GREEN; //Good
else if (bat > 3200) back = BROWN; //Low
else back = ORANGE; //Expired
bat = map(bat, 3000, 4200, 0, 100);
sprintf(buf, "%d%%", bat);
}
else {
sprintf(buf, "Full"); //Fully charged (off)
back = DARK_BLUE;
}
}
*/
//g_display->fillRoundRect(battX, battY, 53, 20, 5, back);
//g_display->drawRoundRect(battX, battY, 53, 20, 5, battColor);
//g_display->drawRoundRect(battX+52, battY+7, 5, 6, 2, battColor);
//g_display->setBackground(back);
//g_display->drawString(M_NORMAL, battX+2, battY+4, " ");
//g_display->drawString(M_NORMAL, (battX+27) - (strlen(buf) * 4), battY+4, buf);
//g_display->setBackground(DARK_BLUE);
}
void Widgets::drawBattery(int16_t x, int16_t y, uint16_t color) {
battX = x;
battY = y;
battColor = color;
drawBatteryText();
}
//Caption 1 or 2 chars long
void Widgets::drawCaptionText(uint8_t style, uint16_t x, uint16_t y, const char* str) {
//uint8_t len = strlen(str)>1?4:0;
//g_display->drawString(style, x-len, y, str);
}
void Widgets::drawKey (uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color, const char* str) {
//g_display->drawRoundRect(x, y, w, h, 2, color);
//g_display->fillRoundRect(x+2, y+2, w-4, h-4, 4, BLACK);
//g_display->drawRoundRect(x+2, y+2, w-4, h-4, 4, color);
//drawCaptionText(M_HIGHLIGHT, x+12, y+5, str);
}
void Widgets::drawButton (uint16_t x, uint16_t y, uint16_t color, const char* str) {
//g_display->drawCircle(x+14, y+14, 14, color);
//g_display->fillCircle(x+14, y+14, 12, BLACK);
//g_display->drawCircle(x+14, y+14, 12, color);
//drawCaptionText(M_HIGHLIGHT, x+10, y+8, str);
}
void Widgets::drawStick (uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color, const char* str0, const char* str1, const char* str2, const char* str3) {
//g_display->fillRoundRect(x, y, w, h, 6, BLACK);
//g_display->drawRoundRect(x, y, w, h, 6, color);
//g_display->drawCircle(x+(w/2), y+(h/2), 3, color);
//uint8_t len = strlen(str0)>1?8:0;
//g_display->drawString(M_HIGHLIGHT, x+(w/2)-14-len, y+(h/2)-6, str0);
//g_display->drawString(M_HIGHLIGHT, x+(w/2)+6, y+(h/2)-6, str1);
//drawCaptionText(M_HIGHLIGHT, x+(w/2)-3, y+(h/2)-16, str2);
//drawCaptionText(M_HIGHLIGHT, x+(w/2)-3, y+(h/2)+4, str3);
}

Wyświetl plik

@ -0,0 +1,29 @@
#ifndef __WIDGETS_H
#define __WIDGETS_H
#ifdef TEENSYDUINO
#include <Arduino.h>
#else
#include <stdint.h>
#endif
class Widgets {
public:
Widgets();
~Widgets();
void drawBattery(int16_t x, int16_t y, uint16_t color);
void drawBatteryText();
void drawCaptionText(uint8_t style, uint16_t x, uint16_t y, const char* str);
void drawKey (uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color, const char* str);
void drawButton (uint16_t x, uint16_t y, uint16_t color, const char* str);
void drawStick (uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t color, const char* str0, const char* str1, const char* str2, const char* str3);
private:
int16_t battX;
int16_t battY;
int16_t battColor;
};
#endif