add apple2 emu, port of aiie
This commit is contained in:
parent
065a6e39e0
commit
25e0f243fc
82 changed files with 29394 additions and 119632 deletions
BIN
MCUME_teensy41/.DS_Store
vendored
BIN
MCUME_teensy41/.DS_Store
vendored
Binary file not shown.
BIN
MCUME_teensy41/audiovideotest/.DS_Store
vendored
BIN
MCUME_teensy41/audiovideotest/.DS_Store
vendored
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
8327
MCUME_teensy41/bin/TEECOMPUTER/TFT/teensyaiie.ino.hex
Normal file
8327
MCUME_teensy41/bin/TEECOMPUTER/TFT/teensyaiie.ino.hex
Normal file
File diff suppressed because it is too large
Load diff
8328
MCUME_teensy41/bin/TEECOMPUTER/VGA/teensyaiie.ino.hex
Normal file
8328
MCUME_teensy41/bin/TEECOMPUTER/VGA/teensyaiie.ino.hex
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Binary file not shown.
BIN
MCUME_teensy41/schematics/.DS_Store
vendored
BIN
MCUME_teensy41/schematics/.DS_Store
vendored
Binary file not shown.
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
#define _PLATFORM_CONFIG_H_
|
||||
|
||||
#define ST7789 1
|
||||
#define TFTSPI0 1
|
||||
#define TFTSPI1 1
|
||||
#define HAS_SND 1
|
||||
|
||||
#endif
|
||||
361
MCUME_teensy41/teensyaiie/AudioPlaySystem.cpp
Normal file
361
MCUME_teensy41/teensyaiie/AudioPlaySystem.cpp
Normal file
|
|
@ -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
|
||||
34
MCUME_teensy41/teensyaiie/AudioPlaySystem.h
Normal file
34
MCUME_teensy41/teensyaiie/AudioPlaySystem.h
Normal file
|
|
@ -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
|
||||
113
MCUME_teensy41/teensyaiie/RingBuf.cpp
Executable file
113
MCUME_teensy41/teensyaiie/RingBuf.cpp
Executable file
|
|
@ -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;
|
||||
}
|
||||
34
MCUME_teensy41/teensyaiie/RingBuf.h
Executable file
34
MCUME_teensy41/teensyaiie/RingBuf.h
Executable file
|
|
@ -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
|
||||
240
MCUME_teensy41/teensyaiie/aiie.cpp
Normal file
240
MCUME_teensy41/teensyaiie/aiie.cpp
Normal file
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
4
MCUME_teensy41/teensyaiie/aiie.h
Normal file
4
MCUME_teensy41/teensyaiie/aiie.h
Normal file
|
|
@ -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);
|
||||
628
MCUME_teensy41/teensyaiie/appledisplay.cpp
Executable file
628
MCUME_teensy41/teensyaiie/appledisplay.cpp
Executable file
|
|
@ -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;
|
||||
}
|
||||
82
MCUME_teensy41/teensyaiie/appledisplay.h
Executable file
82
MCUME_teensy41/teensyaiie/appledisplay.h
Executable file
|
|
@ -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
|
||||
370
MCUME_teensy41/teensyaiie/applekeyboard.cpp
Executable file
370
MCUME_teensy41/teensyaiie/applekeyboard.cpp
Executable file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
64
MCUME_teensy41/teensyaiie/applekeyboard.h
Executable file
64
MCUME_teensy41/teensyaiie/applekeyboard.h
Executable file
|
|
@ -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
|
||||
1146
MCUME_teensy41/teensyaiie/applemmu-rom.h
Executable file
1146
MCUME_teensy41/teensyaiie/applemmu-rom.h
Executable file
File diff suppressed because it is too large
Load diff
995
MCUME_teensy41/teensyaiie/applemmu.cpp
Normal file
995
MCUME_teensy41/teensyaiie/applemmu.cpp
Normal file
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
99
MCUME_teensy41/teensyaiie/applemmu.h
Executable file
99
MCUME_teensy41/teensyaiie/applemmu.h
Executable file
|
|
@ -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
|
||||
100
MCUME_teensy41/teensyaiie/applevm.cpp
Executable file
100
MCUME_teensy41/teensyaiie/applevm.cpp
Executable file
|
|
@ -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;
|
||||
}
|
||||
46
MCUME_teensy41/teensyaiie/applevm.h
Normal file
46
MCUME_teensy41/teensyaiie/applevm.h
Normal file
|
|
@ -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
|
||||
137
MCUME_teensy41/teensyaiie/bios-font.h
Executable file
137
MCUME_teensy41/teensyaiie/bios-font.h
Executable file
|
|
@ -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,
|
||||
|
||||
};
|
||||
|
||||
59
MCUME_teensy41/teensyaiie/bios.h
Executable file
59
MCUME_teensy41/teensyaiie/bios.h
Executable file
|
|
@ -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
|
||||
1066
MCUME_teensy41/teensyaiie/cpu.cpp
Executable file
1066
MCUME_teensy41/teensyaiie/cpu.cpp
Executable file
File diff suppressed because it is too large
Load diff
77
MCUME_teensy41/teensyaiie/cpu.h
Executable file
77
MCUME_teensy41/teensyaiie/cpu.h
Executable file
|
|
@ -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
|
||||
16
MCUME_teensy41/teensyaiie/diskii-rom.h
Executable file
16
MCUME_teensy41/teensyaiie/diskii-rom.h
Executable file
|
|
@ -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
|
||||
};
|
||||
528
MCUME_teensy41/teensyaiie/diskii.cpp
Executable file
528
MCUME_teensy41/teensyaiie/diskii.cpp
Executable file
|
|
@ -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);
|
||||
}
|
||||
|
||||
71
MCUME_teensy41/teensyaiie/diskii.h
Executable file
71
MCUME_teensy41/teensyaiie/diskii.h
Executable file
|
|
@ -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
|
||||
1659
MCUME_teensy41/teensyaiie/emuapi.cpp
Normal file
1659
MCUME_teensy41/teensyaiie/emuapi.cpp
Normal file
File diff suppressed because it is too large
Load diff
214
MCUME_teensy41/teensyaiie/emuapi.h
Normal file
214
MCUME_teensy41/teensyaiie/emuapi.h
Normal file
|
|
@ -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
|
||||
34
MCUME_teensy41/teensyaiie/filemanager.h
Executable file
34
MCUME_teensy41/teensyaiie/filemanager.h
Executable file
|
|
@ -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
|
||||
335
MCUME_teensy41/teensyaiie/font.h
Executable file
335
MCUME_teensy41/teensyaiie/font.h
Executable file
|
|
@ -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
|
||||
};
|
||||
148
MCUME_teensy41/teensyaiie/font8x8.h
Normal file
148
MCUME_teensy41/teensyaiie/font8x8.h
Normal file
|
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
19
MCUME_teensy41/teensyaiie/globals.cpp
Normal file
19
MCUME_teensy41/teensyaiie/globals.cpp
Normal file
|
|
@ -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;
|
||||
28
MCUME_teensy41/teensyaiie/globals.h
Normal file
28
MCUME_teensy41/teensyaiie/globals.h
Normal file
|
|
@ -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;
|
||||
124
MCUME_teensy41/teensyaiie/iopins.h
Normal file
124
MCUME_teensy41/teensyaiie/iopins.h
Normal file
|
|
@ -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
|
||||
33
MCUME_teensy41/teensyaiie/lcg.cpp
Executable file
33
MCUME_teensy41/teensyaiie/lcg.cpp
Executable file
|
|
@ -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));
|
||||
}
|
||||
17
MCUME_teensy41/teensyaiie/lcg.h
Executable file
17
MCUME_teensy41/teensyaiie/lcg.h
Executable file
|
|
@ -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
|
||||
18
MCUME_teensy41/teensyaiie/mmu.h
Executable file
18
MCUME_teensy41/teensyaiie/mmu.h
Executable file
|
|
@ -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
|
||||
331
MCUME_teensy41/teensyaiie/nibutil.cpp
Executable file
331
MCUME_teensy41/teensyaiie/nibutil.cpp
Executable file
|
|
@ -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;
|
||||
}
|
||||
|
||||
44
MCUME_teensy41/teensyaiie/nibutil.h
Executable file
44
MCUME_teensy41/teensyaiie/nibutil.h
Executable file
|
|
@ -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);
|
||||
|
||||
27
MCUME_teensy41/teensyaiie/physicaldisplay.h
Normal file
27
MCUME_teensy41/teensyaiie/physicaldisplay.h
Normal file
|
|
@ -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
|
||||
85
MCUME_teensy41/teensyaiie/physicalkeyboard.h
Executable file
85
MCUME_teensy41/teensyaiie/physicalkeyboard.h
Executable file
|
|
@ -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
|
||||
18
MCUME_teensy41/teensyaiie/physicalpaddles.h
Normal file
18
MCUME_teensy41/teensyaiie/physicalpaddles.h
Normal file
|
|
@ -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
|
||||
13
MCUME_teensy41/teensyaiie/physicalprinter.h
Executable file
13
MCUME_teensy41/teensyaiie/physicalprinter.h
Executable file
|
|
@ -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
|
||||
17
MCUME_teensy41/teensyaiie/physicalspeaker.h
Normal file
17
MCUME_teensy41/teensyaiie/physicalspeaker.h
Normal file
|
|
@ -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
|
||||
39
MCUME_teensy41/teensyaiie/platform_config.h
Normal file
39
MCUME_teensy41/teensyaiie/platform_config.h
Normal file
|
|
@ -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
|
||||
133
MCUME_teensy41/teensyaiie/plf-clock.cpp
Executable file
133
MCUME_teensy41/teensyaiie/plf-clock.cpp
Executable file
|
|
@ -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
|
||||
}
|
||||
|
||||
26
MCUME_teensy41/teensyaiie/plf-clock.h
Executable file
26
MCUME_teensy41/teensyaiie/plf-clock.h
Executable file
|
|
@ -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
|
||||
117
MCUME_teensy41/teensyaiie/plf-display.cpp
Normal file
117
MCUME_teensy41/teensyaiie/plf-display.cpp
Normal file
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
25
MCUME_teensy41/teensyaiie/plf-display.h
Executable file
25
MCUME_teensy41/teensyaiie/plf-display.h
Executable file
|
|
@ -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
|
||||
232
MCUME_teensy41/teensyaiie/plf-filemanager.cpp
Normal file
232
MCUME_teensy41/teensyaiie/plf-filemanager.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
35
MCUME_teensy41/teensyaiie/plf-filemanager.h
Executable file
35
MCUME_teensy41/teensyaiie/plf-filemanager.h
Executable file
|
|
@ -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
|
||||
533
MCUME_teensy41/teensyaiie/plf-keyboard.cpp
Normal file
533
MCUME_teensy41/teensyaiie/plf-keyboard.cpp
Normal file
|
|
@ -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");
|
||||
}
|
||||
37
MCUME_teensy41/teensyaiie/plf-keyboard.h
Executable file
37
MCUME_teensy41/teensyaiie/plf-keyboard.h
Executable file
|
|
@ -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
|
||||
64
MCUME_teensy41/teensyaiie/plf-paddles.cpp
Normal file
64
MCUME_teensy41/teensyaiie/plf-paddles.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
20
MCUME_teensy41/teensyaiie/plf-paddles.h
Normal file
20
MCUME_teensy41/teensyaiie/plf-paddles.h
Normal file
|
|
@ -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;
|
||||
};
|
||||
46
MCUME_teensy41/teensyaiie/plf-speaker.cpp
Executable file
46
MCUME_teensy41/teensyaiie/plf-speaker.cpp
Executable file
|
|
@ -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++;
|
||||
}
|
||||
|
||||
27
MCUME_teensy41/teensyaiie/plf-speaker.h
Executable file
27
MCUME_teensy41/teensyaiie/plf-speaker.h
Executable file
|
|
@ -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
|
||||
24
MCUME_teensy41/teensyaiie/slot.h
Executable file
24
MCUME_teensy41/teensyaiie/slot.h
Executable file
|
|
@ -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
|
||||
204
MCUME_teensy41/teensyaiie/teensyaiie.ino
Normal file
204
MCUME_teensy41/teensyaiie/teensyaiie.ino
Normal file
|
|
@ -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
|
||||
1243
MCUME_teensy41/teensyaiie/tft_t_dma.cpp
Normal file
1243
MCUME_teensy41/teensyaiie/tft_t_dma.cpp
Normal file
File diff suppressed because it is too large
Load diff
234
MCUME_teensy41/teensyaiie/tft_t_dma.h
Normal file
234
MCUME_teensy41/teensyaiie/tft_t_dma.h
Normal file
|
|
@ -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
|
||||
|
||||
|
||||
14
MCUME_teensy41/teensyaiie/tft_t_dma_config.h
Normal file
14
MCUME_teensy41/teensyaiie/tft_t_dma_config.h
Normal file
|
|
@ -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
|
||||
|
||||
53
MCUME_teensy41/teensyaiie/vga_t_dma.h
Normal file
53
MCUME_teensy41/teensyaiie/vga_t_dma.h
Normal file
|
|
@ -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
|
||||
34
MCUME_teensy41/teensyaiie/vm.h
Executable file
34
MCUME_teensy41/teensyaiie/vm.h
Executable file
|
|
@ -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
|
||||
28
MCUME_teensy41/teensyaiie/vmdisplay.h
Executable file
28
MCUME_teensy41/teensyaiie/vmdisplay.h
Executable file
|
|
@ -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
|
||||
20
MCUME_teensy41/teensyaiie/vmkeyboard.h
Executable file
20
MCUME_teensy41/teensyaiie/vmkeyboard.h
Executable file
|
|
@ -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
|
||||
83
MCUME_teensy41/teensyaiie/widgets.cpp
Executable file
83
MCUME_teensy41/teensyaiie/widgets.cpp
Executable file
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
29
MCUME_teensy41/teensyaiie/widgets.h
Executable file
29
MCUME_teensy41/teensyaiie/widgets.h
Executable file
|
|
@ -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
|
||||
Loading…
Reference in a new issue