mirror of https://github.com/JorjBauer/aiie.git
No-Slot clock conversion
This commit is contained in:
parent
c2d5166272
commit
f848a0cbef
2
Makefile
2
Makefile
|
@ -7,7 +7,7 @@ CXXFLAGS=-Wall -I/usr/include/SDL2 -I .. -I . -I apple -I nix -I sdl -I/usr/loca
|
||||||
|
|
||||||
TSRC=cpu.cpp util/testharness.cpp
|
TSRC=cpu.cpp util/testharness.cpp
|
||||||
|
|
||||||
COMMONOBJS=cpu.o apple/appledisplay.o apple/applekeyboard.o apple/applemmu.o apple/applevm.o apple/diskii.o apple/nibutil.o LRingBuffer.o globals.o apple/parallelcard.o apple/fx80.o lcg.o apple/hd32.o images.o apple/appleui.o vmram.o bios.o
|
COMMONOBJS=cpu.o apple/appledisplay.o apple/applekeyboard.o apple/applemmu.o apple/applevm.o apple/diskii.o apple/nibutil.o LRingBuffer.o globals.o apple/parallelcard.o apple/fx80.o lcg.o apple/hd32.o images.o apple/appleui.o vmram.o bios.o apple/noslotclock.o
|
||||||
|
|
||||||
FBOBJS=linuxfb/linux-speaker.o linuxfb/fb-display.o linuxfb/linux-keyboard.o linuxfb/fb-paddles.o nix/nix-filemanager.o linuxfb/aiie.o linuxfb/linux-printer.o nix/nix-clock.o
|
FBOBJS=linuxfb/linux-speaker.o linuxfb/fb-display.o linuxfb/linux-keyboard.o linuxfb/fb-paddles.o nix/nix-filemanager.o linuxfb/aiie.o linuxfb/linux-printer.o nix/nix-clock.o
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,12 @@
|
||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
|
||||||
|
#ifdef TEENSYDUINO
|
||||||
|
#include "teensy-clock.h"
|
||||||
|
#else
|
||||||
|
#include "nix-clock.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
// Serializing token for MMU data
|
// Serializing token for MMU data
|
||||||
#define MMUMAGIC 'M'
|
#define MMUMAGIC 'M'
|
||||||
|
|
||||||
|
@ -141,6 +147,12 @@ AppleMMU::AppleMMU(AppleDisplay *display)
|
||||||
this->display = display;
|
this->display = display;
|
||||||
this->display->setSwitches(&switches);
|
this->display->setSwitches(&switches);
|
||||||
resetRAM(); // initialize RAM, load ROM
|
resetRAM(); // initialize RAM, load ROM
|
||||||
|
|
||||||
|
#ifdef TEENSYDUINO
|
||||||
|
clock = new TeensyClock((AppleMMU *)this);
|
||||||
|
#else
|
||||||
|
clock = new NixClock((AppleMMU *)this);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
AppleMMU::~AppleMMU()
|
AppleMMU::~AppleMMU()
|
||||||
|
@ -221,6 +233,11 @@ void AppleMMU::Reset()
|
||||||
|
|
||||||
uint8_t AppleMMU::read(uint16_t address)
|
uint8_t AppleMMU::read(uint16_t address)
|
||||||
{
|
{
|
||||||
|
uint8_t rv = 0;
|
||||||
|
if (handleNoSlotClock(address, &rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
if (address >= 0xC000 &&
|
if (address >= 0xC000 &&
|
||||||
address <= 0xC0FF) {
|
address <= 0xC0FF) {
|
||||||
return readSwitches(address);
|
return readSwitches(address);
|
||||||
|
@ -261,6 +278,10 @@ uint8_t AppleMMU::readDirect(uint16_t address, uint8_t fromPage)
|
||||||
|
|
||||||
void AppleMMU::write(uint16_t address, uint8_t v)
|
void AppleMMU::write(uint16_t address, uint8_t v)
|
||||||
{
|
{
|
||||||
|
if (handleNoSlotClock(address, NULL)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (address >= 0xC000 &&
|
if (address >= 0xC000 &&
|
||||||
address <= 0xC0FF) {
|
address <= 0xC0FF) {
|
||||||
return writeSwitches(address, v);
|
return writeSwitches(address, v);
|
||||||
|
@ -297,6 +318,26 @@ void AppleMMU::write(uint16_t address, uint8_t v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AppleMMU::handleNoSlotClock(uint16_t address, uint8_t *rv)
|
||||||
|
{
|
||||||
|
uint8_t ah = address >> 8;
|
||||||
|
if ( ((!intcxrom || !slot3rom) && (ah == 0xc3)) ||
|
||||||
|
(ah == 0xc8) ) {
|
||||||
|
if (rv) {
|
||||||
|
// It's a read attempt - we want a return value.
|
||||||
|
*rv = 0;
|
||||||
|
if (clock->read(address, rv)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clock->write(address);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: this is no longer "MMU", is it?
|
// FIXME: this is no longer "MMU", is it?
|
||||||
void AppleMMU::resetDisplay()
|
void AppleMMU::resetDisplay()
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "appledisplay.h"
|
#include "appledisplay.h"
|
||||||
#include "slot.h"
|
#include "slot.h"
|
||||||
#include "mmu.h"
|
#include "mmu.h"
|
||||||
|
#include "noslotclock.h"
|
||||||
|
|
||||||
// when we read a nondeterministic result, we return FLOATING. Maybe
|
// when we read a nondeterministic result, we return FLOATING. Maybe
|
||||||
// some day we can come back here and figure out how to return what
|
// some day we can come back here and figure out how to return what
|
||||||
|
@ -56,6 +57,8 @@ class AppleMMU : public MMU {
|
||||||
void setAppleKey(int8_t which, bool isDown);
|
void setAppleKey(int8_t which, bool isDown);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
bool handleNoSlotClock(uint16_t address, uint8_t *rv);
|
||||||
|
|
||||||
void resetDisplay();
|
void resetDisplay();
|
||||||
uint8_t readSwitches(uint16_t address);
|
uint8_t readSwitches(uint16_t address);
|
||||||
void writeSwitches(uint16_t address, uint8_t v);
|
void writeSwitches(uint16_t address, uint8_t v);
|
||||||
|
@ -85,6 +88,8 @@ class AppleMMU : public MMU {
|
||||||
uint16_t writePages[0x100];
|
uint16_t writePages[0x100];
|
||||||
|
|
||||||
bool anyKeyDown;
|
bool anyKeyDown;
|
||||||
|
|
||||||
|
NoSlotClock *clock;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,25 +26,12 @@ AppleVM::AppleVM()
|
||||||
parallel = new ParallelCard();
|
parallel = new ParallelCard();
|
||||||
((AppleMMU *)mmu)->setSlot(1, parallel);
|
((AppleMMU *)mmu)->setSlot(1, parallel);
|
||||||
|
|
||||||
#ifdef TEENSYDUINO
|
|
||||||
teensyClock = new TeensyClock((AppleMMU *)mmu);
|
|
||||||
((AppleMMU *)mmu)->setSlot(5, teensyClock);
|
|
||||||
#else
|
|
||||||
nixClock = new NixClock((AppleMMU *)mmu);
|
|
||||||
((AppleMMU *)mmu)->setSlot(5, nixClock);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
hd32 = new HD32((AppleMMU *)mmu);
|
hd32 = new HD32((AppleMMU *)mmu);
|
||||||
((AppleMMU *)mmu)->setSlot(7, hd32);
|
((AppleMMU *)mmu)->setSlot(7, hd32);
|
||||||
}
|
}
|
||||||
|
|
||||||
AppleVM::~AppleVM()
|
AppleVM::~AppleVM()
|
||||||
{
|
{
|
||||||
#ifdef TEENSYDUINO
|
|
||||||
delete teensyClock;
|
|
||||||
#else
|
|
||||||
delete nixClock;
|
|
||||||
#endif
|
|
||||||
delete disk6;
|
delete disk6;
|
||||||
delete parallel;
|
delete parallel;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,6 @@
|
||||||
#include "hd32.h"
|
#include "hd32.h"
|
||||||
#include "vmkeyboard.h"
|
#include "vmkeyboard.h"
|
||||||
#include "parallelcard.h"
|
#include "parallelcard.h"
|
||||||
#ifdef TEENSYDUINO
|
|
||||||
#include "teensy-clock.h"
|
|
||||||
#else
|
|
||||||
#include "nix-clock.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
class AppleVM : public VM {
|
class AppleVM : public VM {
|
||||||
|
@ -44,11 +39,6 @@ class AppleVM : public VM {
|
||||||
protected:
|
protected:
|
||||||
VMKeyboard *keyboard;
|
VMKeyboard *keyboard;
|
||||||
ParallelCard *parallel;
|
ParallelCard *parallel;
|
||||||
#ifdef TEENSYDUINO
|
|
||||||
TeensyClock *teensyClock;
|
|
||||||
#else
|
|
||||||
NixClock *nixClock;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
#include "noslotclock.h"
|
||||||
|
#include "applemmu.h" // for FLOATING
|
||||||
|
|
||||||
|
#define initSequence 0x5CA33AC55CA33AC5LL
|
||||||
|
|
||||||
|
/* The no-slot clock works like this...
|
||||||
|
*
|
||||||
|
* The NSC is installed in some bank of ROM memory. For our instance,
|
||||||
|
* it's 0xC300 or 0xC800. When a read or write occurs in these pages
|
||||||
|
* - and ROM isn't switched out or whatever - then this driver is
|
||||||
|
* invoked.
|
||||||
|
*
|
||||||
|
* (It's the job of applemmu to decide if the right address is being
|
||||||
|
* called to invoke the read or write here.)
|
||||||
|
*
|
||||||
|
* To get the clock to respond, we need to first pass it the init
|
||||||
|
* Sequence (above). The NSC gets one bit at a time from watching the
|
||||||
|
* transactions on the bus. So first the driver reads or writes to
|
||||||
|
* memory address (e.g.) 0xC800 or 0xC801, depending on whether it
|
||||||
|
* wants to send a 0 or 1 bit; and then, if the NSC sees all of the
|
||||||
|
* correct bits for the init sequence, it allows responses when
|
||||||
|
* reading from memory (e.g.) 0xC804. These responses are, again, one
|
||||||
|
* bit at a time of the current date and time.
|
||||||
|
*/
|
||||||
|
|
||||||
|
NoSlotClock::NoSlotClock(AppleMMU *mmu)
|
||||||
|
{
|
||||||
|
this->mmu = mmu;
|
||||||
|
|
||||||
|
compareReg = initSequence;
|
||||||
|
clockReg = 0x00LL;
|
||||||
|
clockRegPtr = compareRegPtr = 0;
|
||||||
|
|
||||||
|
regEnabled = false;
|
||||||
|
writeEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
NoSlotClock::~NoSlotClock()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NoSlotClock::read(uint8_t s, uint8_t *d)
|
||||||
|
{
|
||||||
|
if (s & 0x04) {
|
||||||
|
return doRead(d);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
doWrite(s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NoSlotClock::write(uint8_t s)
|
||||||
|
{
|
||||||
|
if (s & 0x04) {
|
||||||
|
doRead(0);
|
||||||
|
} else {
|
||||||
|
doWrite(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NoSlotClock::doRead(uint8_t *d)
|
||||||
|
{
|
||||||
|
if (!regEnabled) {
|
||||||
|
compareReg = initSequence;
|
||||||
|
compareRegPtr = 0;
|
||||||
|
writeEnabled = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d) {
|
||||||
|
*d = (clockReg & 0x01) ?
|
||||||
|
((*d) | 1) :
|
||||||
|
((*d) & ~1);
|
||||||
|
}
|
||||||
|
clockRegPtr++;
|
||||||
|
clockReg >>= 1;
|
||||||
|
if (clockRegPtr == 64) {
|
||||||
|
regEnabled = false;
|
||||||
|
clockRegPtr = 0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NoSlotClock::doWrite(uint8_t address)
|
||||||
|
{
|
||||||
|
if (!writeEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!regEnabled) {
|
||||||
|
if ((compareReg & 0x01) == (address & 0x01)) {
|
||||||
|
compareRegPtr++;
|
||||||
|
compareReg >>= 1;
|
||||||
|
if (compareRegPtr == 64) {
|
||||||
|
regEnabled = true;
|
||||||
|
|
||||||
|
compareRegPtr = 0;
|
||||||
|
compareReg = initSequence;
|
||||||
|
|
||||||
|
populateClockRegister();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
writeEnabled = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The NSC driver is writing a new clock time to the clock...
|
||||||
|
clockRegPtr++;
|
||||||
|
clockReg >>= 1;
|
||||||
|
if (address & 0x01) {
|
||||||
|
clockReg |= 0x8000000000000000LL;
|
||||||
|
}
|
||||||
|
if (clockRegPtr == 64) {
|
||||||
|
regEnabled = false;
|
||||||
|
|
||||||
|
// The clockReg should now contain a BCD4 packed date like
|
||||||
|
// 0x1708071521140200
|
||||||
|
// ... 2017, August 07, <day of week?>; 21:14:02.00
|
||||||
|
// where that <day of week> is clearly suspect. Probably because 2017
|
||||||
|
// was too far in the future when this driver was written...
|
||||||
|
|
||||||
|
clockRegPtr = 0;
|
||||||
|
|
||||||
|
updateClockFromRegister();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NoSlotClock::writeNibble(uint8_t n)
|
||||||
|
{
|
||||||
|
for (uint8_t i=0; i<4; i++) {
|
||||||
|
clockReg <<= 1;
|
||||||
|
if (n & 0x08) {
|
||||||
|
clockReg |= 1;
|
||||||
|
}
|
||||||
|
n <<= 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
#ifndef __NSCLOCK_H
|
||||||
|
#define __NSCLOCK_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
class AppleMMU;
|
||||||
|
|
||||||
|
class NoSlotClock {
|
||||||
|
public:
|
||||||
|
NoSlotClock(AppleMMU *mmu);
|
||||||
|
virtual ~NoSlotClock();
|
||||||
|
|
||||||
|
bool read(uint8_t s, uint8_t *data);
|
||||||
|
void write(uint8_t s);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool doRead(uint8_t *d);
|
||||||
|
void doWrite(uint8_t address);
|
||||||
|
void writeNibble(uint8_t n);
|
||||||
|
|
||||||
|
virtual void populateClockRegister() = 0;
|
||||||
|
virtual void updateClockFromRegister() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AppleMMU *mmu;
|
||||||
|
|
||||||
|
uint64_t clockReg;
|
||||||
|
uint64_t compareReg;
|
||||||
|
uint8_t clockRegPtr;
|
||||||
|
uint8_t compareRegPtr;
|
||||||
|
|
||||||
|
bool regEnabled;
|
||||||
|
bool writeEnabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,141 +1,62 @@
|
||||||
#include <string.h> // memset
|
#include <string.h> // memset
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "noslotclock.h"
|
||||||
|
|
||||||
#include "nix-clock.h"
|
#include "nix-clock.h"
|
||||||
#include "applemmu.h" // for FLOATING
|
|
||||||
|
|
||||||
/*
|
NixClock::NixClock(AppleMMU *mmu) : NoSlotClock(mmu)
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
NixClock::NixClock(AppleMMU *mmu)
|
|
||||||
{
|
|
||||||
this->mmu = mmu;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NixClock::~NixClock()
|
NixClock::~NixClock()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NixClock::Serialize(int8_t fd)
|
void NixClock::populateClockRegister()
|
||||||
{
|
{
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NixClock::Deserialize(int8_t fd)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NixClock::Reset()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t NixClock::readSwitches(uint8_t s)
|
|
||||||
{
|
|
||||||
// When any switch is read, we'll put the current time in the prodos time buffer
|
|
||||||
time_t lt;
|
time_t lt;
|
||||||
time(<);
|
time(<);
|
||||||
struct tm *ct = localtime(<);
|
struct tm *ct = localtime(<);
|
||||||
|
|
||||||
// Put the date/time in the official ProDOS buffer
|
clockReg = 0x0;
|
||||||
uint8_t prodosOut[4];
|
|
||||||
timeToProDOS(ct->tm_year+1900,
|
|
||||||
ct->tm_mon,
|
|
||||||
ct->tm_mday,
|
|
||||||
ct->tm_hour,
|
|
||||||
ct->tm_min,
|
|
||||||
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
|
// BCD, 4 bits per digit.
|
||||||
// year, which it also consumes
|
|
||||||
char ts[18];
|
|
||||||
sprintf(ts, "%.2d,%.2d,%.2d,%.2d,%.2d,%.2d",
|
|
||||||
ct->tm_mon,
|
|
||||||
ct->tm_wday,
|
|
||||||
ct->tm_mday,
|
|
||||||
ct->tm_hour,
|
|
||||||
ct->tm_min,
|
|
||||||
ct->tm_sec);
|
|
||||||
|
|
||||||
uint8_t i = 0;
|
ct->tm_year %= 100; // must be 00-99
|
||||||
while (ts[i]) {
|
writeNibble(ct->tm_year / 10);
|
||||||
mmu->write(0x200 + i, ts[i] | 0x80);
|
writeNibble(ct->tm_year % 10);
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FLOATING;
|
ct->tm_mon++; // 1 = January
|
||||||
|
writeNibble(ct->tm_mon / 10);
|
||||||
|
writeNibble(ct->tm_mon % 10);
|
||||||
|
|
||||||
|
writeNibble(ct->tm_mday / 10);
|
||||||
|
writeNibble(ct->tm_mday % 10);// day of month, 1-31
|
||||||
|
|
||||||
|
writeNibble(0);
|
||||||
|
writeNibble(ct->tm_wday + 1); // day of week, 1-7
|
||||||
|
|
||||||
|
writeNibble(ct->tm_hour / 10);
|
||||||
|
writeNibble(ct->tm_hour % 10);
|
||||||
|
|
||||||
|
writeNibble(ct->tm_min / 10);
|
||||||
|
writeNibble(ct->tm_min % 10);
|
||||||
|
|
||||||
|
writeNibble(ct->tm_sec / 10); // tens of seconds
|
||||||
|
writeNibble(ct->tm_sec % 10); // ones of seconds, 00-99
|
||||||
|
|
||||||
|
writeNibble(0); // ones of milliseconds, 00-99
|
||||||
|
writeNibble(0); // tens of milliseconds
|
||||||
}
|
}
|
||||||
|
|
||||||
void NixClock::writeSwitches(uint8_t s, uint8_t v)
|
void NixClock::updateClockFromRegister()
|
||||||
{
|
{
|
||||||
// printf("unimplemented write to the clock - 0x%X\n", v);
|
// The clockReg should now contain a BCD4 packed date like
|
||||||
|
// 0x1708071521140200
|
||||||
|
// ... 2017, August 07, <day of week?>; 21:14:02.00
|
||||||
|
// where that <day of week> is clearly suspect. Probably because 2017
|
||||||
|
// was too far in the future when this driver was written...
|
||||||
|
|
||||||
|
printf(">> Got a request to set clock: 0x%llX\n", clockReg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: this assumes slot #5
|
|
||||||
void NixClock::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 c500 becomes
|
|
||||||
//
|
|
||||||
// C500: 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 $C510
|
|
||||||
toWhere[0x09] = 0x10;
|
|
||||||
toWhere[0x0A] = 0xC5;
|
|
||||||
|
|
||||||
// 0x0b: write
|
|
||||||
toWhere[0x0B] = 0x8D; // STA $C0D0 (slot 5's first switch)
|
|
||||||
toWhere[0x0C] = 0xD0;
|
|
||||||
toWhere[0x0D] = 0xC0;
|
|
||||||
toWhere[0x0E] = 0x60; // RTS
|
|
||||||
|
|
||||||
// simple read
|
|
||||||
toWhere[0x10] = 0xAD; // LDA $C0D0 (slot 5's first switch)
|
|
||||||
toWhere[0x11] = 0xD0;
|
|
||||||
toWhere[0x12] = 0xC0;
|
|
||||||
toWhere[0x13] = 0x60; // RTS
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -4,28 +4,17 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "slot.h"
|
#include "noslotclock.h"
|
||||||
#include "applemmu.h"
|
|
||||||
|
|
||||||
// Simple clock for *nix
|
class NixClock : public NoSlotClock {
|
||||||
|
|
||||||
class NixClock : public Slot {
|
|
||||||
public:
|
public:
|
||||||
NixClock(AppleMMU *mmu);
|
NixClock(AppleMMU *mmu);
|
||||||
virtual ~NixClock();
|
virtual ~NixClock();
|
||||||
|
|
||||||
virtual bool Serialize(int8_t fd);
|
protected:
|
||||||
virtual bool Deserialize(int8_t fd);
|
virtual void populateClockRegister();
|
||||||
|
virtual void updateClockFromRegister();
|
||||||
|
|
||||||
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
|
#endif
|
||||||
|
|
|
@ -1,136 +1,62 @@
|
||||||
#include <string.h> // memset
|
#include <string.h> // memset
|
||||||
#include <TimeLib.h>
|
#include <TimeLib.h>
|
||||||
|
|
||||||
|
#include "noslotclock.h"
|
||||||
|
|
||||||
#include "teensy-clock.h"
|
#include "teensy-clock.h"
|
||||||
#include "applemmu.h" // for FLOATING
|
|
||||||
|
|
||||||
/*
|
TeensyClock::TeensyClock(AppleMMU *mmu) : NoSlotClock(mmu)
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
TeensyClock::TeensyClock(AppleMMU *mmu)
|
|
||||||
{
|
|
||||||
this->mmu = mmu;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TeensyClock::~TeensyClock()
|
TeensyClock::~TeensyClock()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TeensyClock::Serialize(int8_t fd)
|
void TeensyClock::populateClockRegister()
|
||||||
{
|
{
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TeensyClock::Deserialize(int8_t fd)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TeensyClock::Reset()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TeensyClock::readSwitches(uint8_t s)
|
|
||||||
{
|
|
||||||
// When any switch is read, we'll put the current time in the prodos time buffer
|
|
||||||
|
|
||||||
tmElements_t tm;
|
tmElements_t tm;
|
||||||
breakTime(now(), tm);
|
breakTime(now(), tm);
|
||||||
|
|
||||||
// Put the date/time in the official ProDOS buffer
|
tm.Year %= 100; // 00-99
|
||||||
uint8_t prodosOut[4];
|
tm.Month++; // 1-12
|
||||||
timeToProDOS(tm.Year, tm.Month, tm.Day, tm.Hour, tm.Minute, prodosOut);
|
tm.Wday++; // 1-7, where 1 = Sunday
|
||||||
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
|
writeNibble(tm.Year / 10); // 00-99
|
||||||
// year, which it also consumes
|
writeNibble(tm.Year % 10);
|
||||||
char ts[18];
|
writeNibble(tm.Month / 10); // 1-12
|
||||||
sprintf(ts, "%.2d,%.2d,%.2d,%.2d,%.2d,%.2d",
|
writeNibble(tm.Month % 10);
|
||||||
tm.Month,
|
writeNibble(tm.Day / 10); // 1-31
|
||||||
tm.Wday - 1, // Sunday should be 0, not 1
|
writeNibble(tm.Day % 10);
|
||||||
tm.Day,
|
writeNibble(0);
|
||||||
tm.Hour,
|
writeNibble(tm.Wday); // 1-7, where 1 = Sunday
|
||||||
tm.Minute,
|
writeNibble(tm.Hour / 10);
|
||||||
tm.Second);
|
writeNibble(tm.Hour % 10);
|
||||||
|
writeNibble(tm.Minute / 10);
|
||||||
uint8_t i = 0;
|
writeNibble(tm.Minute % 10);
|
||||||
while (ts[i]) {
|
writeNibble(tm.Second / 10);
|
||||||
mmu->write(0x200 + i, ts[i] | 0x80);
|
writeNibble(tm.Second % 10);
|
||||||
i++;
|
writeNibble(0); // 00-99 milliseconds
|
||||||
}
|
writeNibble(0);
|
||||||
|
|
||||||
return FLOATING;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TeensyClock::writeSwitches(uint8_t s, uint8_t v)
|
static uint8_t bcdToDecimal(uint8_t v)
|
||||||
{
|
{
|
||||||
// printf("unimplemented write to the clock - 0x%X\n", v);
|
return ((v & 0x0F) + (((v & 0xF0) >> 4) * 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: this assumes slot #5
|
|
||||||
void TeensyClock::loadROM(uint8_t *toWhere)
|
void TeensyClock::updateClockFromRegister()
|
||||||
{
|
{
|
||||||
memset(toWhere, 0xEA, 256); // fill the page with NOPs
|
uint8_t hours, minutes, seconds, days, months;
|
||||||
|
uint16_t years;
|
||||||
|
|
||||||
// ProDOS only needs these 4 bytes to recognize that a clock is present
|
years = bcdToDecimal(clockReg & 0xFF00000000000000LL >> 56) + 2000;
|
||||||
toWhere[0x00] = 0x08; // PHP
|
months = bcdToDecimal(clockReg & 0x00FF000000000000LL >> 48) - 1;
|
||||||
toWhere[0x02] = 0x28; // PLP
|
days = bcdToDecimal(clockReg & 0x0000FF0000000000LL >> 40);
|
||||||
toWhere[0x04] = 0x58; // CLI
|
hours = bcdToDecimal(clockReg & 0x00000000FF000000LL >> 24);
|
||||||
toWhere[0x06] = 0x70; // BVS
|
minutes = bcdToDecimal(clockReg & 0x0000000000FF0000LL >> 16);
|
||||||
|
seconds = bcdToDecimal(clockReg & 0x000000000000FF00LL >> 8);
|
||||||
|
|
||||||
// Pad out those bytes so they will return control well. The program
|
setTime(hours, minutes, seconds, days, months, years);
|
||||||
// at c500 becomes
|
|
||||||
//
|
|
||||||
// C500: 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 $C510
|
|
||||||
toWhere[0x09] = 0x10;
|
|
||||||
toWhere[0x0A] = 0xC5;
|
|
||||||
|
|
||||||
// 0x0b: write
|
|
||||||
toWhere[0x0B] = 0x8D; // STA $C0D0 (slot 5's first switch)
|
|
||||||
toWhere[0x0C] = 0xD0;
|
|
||||||
toWhere[0x0D] = 0xC0;
|
|
||||||
toWhere[0x0E] = 0x60; // RTS
|
|
||||||
|
|
||||||
// simple read
|
|
||||||
toWhere[0x10] = 0xAD; // LDA $C0D0 (slot 5's first switch)
|
|
||||||
toWhere[0x11] = 0xD0;
|
|
||||||
toWhere[0x12] = 0xC0;
|
|
||||||
toWhere[0x13] = 0x60; // RTS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,33 +1,20 @@
|
||||||
#ifndef __TEENSYCLOCK_H
|
#ifndef __TEENSYCLOCK_H
|
||||||
#define __TEENSYCLOCK_H
|
#define __TEENSYCLOCK_H
|
||||||
|
|
||||||
#ifdef TEENSYDUINO
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#else
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "slot.h"
|
|
||||||
#include "applemmu.h"
|
#include "applemmu.h"
|
||||||
|
|
||||||
class TeensyClock : public Slot {
|
#include "NoSlotClock.h"
|
||||||
|
|
||||||
|
class TeensyClock : public NoSlotClock {
|
||||||
public:
|
public:
|
||||||
TeensyClock(AppleMMU *mmu);
|
TeensyClock(AppleMMU *mmu);
|
||||||
virtual ~TeensyClock();
|
virtual ~TeensyClock();
|
||||||
|
|
||||||
virtual bool Serialize(int8_t fd);
|
protected:
|
||||||
virtual bool Deserialize(int8_t fd);
|
virtual void populateClockRegister();
|
||||||
|
virtual void updateClockFromRegister();
|
||||||
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
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue