mirror of
https://github.com/JorjBauer/aiie.git
synced 2024-12-11 18:49:16 +00:00
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
|
||||
|
||||
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
|
||||
|
||||
|
@ -14,6 +14,12 @@
|
||||
|
||||
#include "globals.h"
|
||||
|
||||
#ifdef TEENSYDUINO
|
||||
#include "teensy-clock.h"
|
||||
#else
|
||||
#include "nix-clock.h"
|
||||
#endif
|
||||
|
||||
// Serializing token for MMU data
|
||||
#define MMUMAGIC 'M'
|
||||
|
||||
@ -141,6 +147,12 @@ AppleMMU::AppleMMU(AppleDisplay *display)
|
||||
this->display = display;
|
||||
this->display->setSwitches(&switches);
|
||||
resetRAM(); // initialize RAM, load ROM
|
||||
|
||||
#ifdef TEENSYDUINO
|
||||
clock = new TeensyClock((AppleMMU *)this);
|
||||
#else
|
||||
clock = new NixClock((AppleMMU *)this);
|
||||
#endif
|
||||
}
|
||||
|
||||
AppleMMU::~AppleMMU()
|
||||
@ -221,6 +233,11 @@ void AppleMMU::Reset()
|
||||
|
||||
uint8_t AppleMMU::read(uint16_t address)
|
||||
{
|
||||
uint8_t rv = 0;
|
||||
if (handleNoSlotClock(address, &rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (address >= 0xC000 &&
|
||||
address <= 0xC0FF) {
|
||||
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)
|
||||
{
|
||||
if (handleNoSlotClock(address, NULL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (address >= 0xC000 &&
|
||||
address <= 0xC0FF) {
|
||||
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?
|
||||
void AppleMMU::resetDisplay()
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "appledisplay.h"
|
||||
#include "slot.h"
|
||||
#include "mmu.h"
|
||||
#include "noslotclock.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
|
||||
@ -56,6 +57,8 @@ class AppleMMU : public MMU {
|
||||
void setAppleKey(int8_t which, bool isDown);
|
||||
|
||||
protected:
|
||||
bool handleNoSlotClock(uint16_t address, uint8_t *rv);
|
||||
|
||||
void resetDisplay();
|
||||
uint8_t readSwitches(uint16_t address);
|
||||
void writeSwitches(uint16_t address, uint8_t v);
|
||||
@ -85,6 +88,8 @@ class AppleMMU : public MMU {
|
||||
uint16_t writePages[0x100];
|
||||
|
||||
bool anyKeyDown;
|
||||
|
||||
NoSlotClock *clock;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -26,25 +26,12 @@ AppleVM::AppleVM()
|
||||
parallel = new ParallelCard();
|
||||
((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);
|
||||
((AppleMMU *)mmu)->setSlot(7, hd32);
|
||||
}
|
||||
|
||||
AppleVM::~AppleVM()
|
||||
{
|
||||
#ifdef TEENSYDUINO
|
||||
delete teensyClock;
|
||||
#else
|
||||
delete nixClock;
|
||||
#endif
|
||||
delete disk6;
|
||||
delete parallel;
|
||||
}
|
||||
|
@ -7,11 +7,6 @@
|
||||
#include "hd32.h"
|
||||
#include "vmkeyboard.h"
|
||||
#include "parallelcard.h"
|
||||
#ifdef TEENSYDUINO
|
||||
#include "teensy-clock.h"
|
||||
#else
|
||||
#include "nix-clock.h"
|
||||
#endif
|
||||
|
||||
#include "vm.h"
|
||||
class AppleVM : public VM {
|
||||
@ -44,11 +39,6 @@ class AppleVM : public VM {
|
||||
protected:
|
||||
VMKeyboard *keyboard;
|
||||
ParallelCard *parallel;
|
||||
#ifdef TEENSYDUINO
|
||||
TeensyClock *teensyClock;
|
||||
#else
|
||||
NixClock *nixClock;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
138
apple/noslotclock.cpp
Normal file
138
apple/noslotclock.cpp
Normal file
@ -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;
|
||||
}
|
||||
}
|
37
apple/noslotclock.h
Normal file
37
apple/noslotclock.h
Normal file
@ -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 <time.h>
|
||||
|
||||
#include "noslotclock.h"
|
||||
|
||||
#include "nix-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])
|
||||
NixClock::NixClock(AppleMMU *mmu) : NoSlotClock(mmu)
|
||||
{
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
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(<);
|
||||
struct tm *ct = localtime(<);
|
||||
|
||||
// Put the date/time in the official ProDOS buffer
|
||||
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]);
|
||||
clockReg = 0x0;
|
||||
|
||||
// and also generate a date/time that contains seconds, but not a
|
||||
// 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);
|
||||
// BCD, 4 bits per digit.
|
||||
|
||||
uint8_t i = 0;
|
||||
while (ts[i]) {
|
||||
mmu->write(0x200 + i, ts[i] | 0x80);
|
||||
i++;
|
||||
}
|
||||
ct->tm_year %= 100; // must be 00-99
|
||||
writeNibble(ct->tm_year / 10);
|
||||
writeNibble(ct->tm_year % 10);
|
||||
|
||||
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 <stdio.h>
|
||||
|
||||
#include "slot.h"
|
||||
#include "applemmu.h"
|
||||
#include "noslotclock.h"
|
||||
|
||||
// Simple clock for *nix
|
||||
|
||||
class NixClock : public Slot {
|
||||
class NixClock : public NoSlotClock {
|
||||
public:
|
||||
NixClock(AppleMMU *mmu);
|
||||
virtual ~NixClock();
|
||||
|
||||
virtual bool Serialize(int8_t fd);
|
||||
virtual bool Deserialize(int8_t fd);
|
||||
protected:
|
||||
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
|
||||
|
@ -1,136 +1,62 @@
|
||||
#include <string.h> // memset
|
||||
#include <TimeLib.h>
|
||||
|
||||
#include "noslotclock.h"
|
||||
|
||||
#include "teensy-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])
|
||||
TeensyClock::TeensyClock(AppleMMU *mmu) : NoSlotClock(mmu)
|
||||
{
|
||||
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()
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
breakTime(now(), tm);
|
||||
|
||||
// Put the date/time in the official ProDOS buffer
|
||||
uint8_t prodosOut[4];
|
||||
timeToProDOS(tm.Year, tm.Month, tm.Day, tm.Hour, tm.Minute, prodosOut);
|
||||
mmu->write(0xBF90, prodosOut[0]);
|
||||
mmu->write(0xBF91, prodosOut[1]);
|
||||
mmu->write(0xBF92, prodosOut[2]);
|
||||
mmu->write(0xBF93, prodosOut[3]);
|
||||
tm.Year %= 100; // 00-99
|
||||
tm.Month++; // 1-12
|
||||
tm.Wday++; // 1-7, where 1 = Sunday
|
||||
|
||||
// and also generate a date/time that contains seconds, but not a
|
||||
// year, which it also consumes
|
||||
char ts[18];
|
||||
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);
|
||||
|
||||
uint8_t i = 0;
|
||||
while (ts[i]) {
|
||||
mmu->write(0x200 + i, ts[i] | 0x80);
|
||||
i++;
|
||||
}
|
||||
|
||||
return FLOATING;
|
||||
writeNibble(tm.Year / 10); // 00-99
|
||||
writeNibble(tm.Year % 10);
|
||||
writeNibble(tm.Month / 10); // 1-12
|
||||
writeNibble(tm.Month % 10);
|
||||
writeNibble(tm.Day / 10); // 1-31
|
||||
writeNibble(tm.Day % 10);
|
||||
writeNibble(0);
|
||||
writeNibble(tm.Wday); // 1-7, where 1 = Sunday
|
||||
writeNibble(tm.Hour / 10);
|
||||
writeNibble(tm.Hour % 10);
|
||||
writeNibble(tm.Minute / 10);
|
||||
writeNibble(tm.Minute % 10);
|
||||
writeNibble(tm.Second / 10);
|
||||
writeNibble(tm.Second % 10);
|
||||
writeNibble(0); // 00-99 milliseconds
|
||||
writeNibble(0);
|
||||
}
|
||||
|
||||
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
|
||||
toWhere[0x00] = 0x08; // PHP
|
||||
toWhere[0x02] = 0x28; // PLP
|
||||
toWhere[0x04] = 0x58; // CLI
|
||||
toWhere[0x06] = 0x70; // BVS
|
||||
years = bcdToDecimal(clockReg & 0xFF00000000000000LL >> 56) + 2000;
|
||||
months = bcdToDecimal(clockReg & 0x00FF000000000000LL >> 48) - 1;
|
||||
days = bcdToDecimal(clockReg & 0x0000FF0000000000LL >> 40);
|
||||
hours = bcdToDecimal(clockReg & 0x00000000FF000000LL >> 24);
|
||||
minutes = bcdToDecimal(clockReg & 0x0000000000FF0000LL >> 16);
|
||||
seconds = bcdToDecimal(clockReg & 0x000000000000FF00LL >> 8);
|
||||
|
||||
// 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
|
||||
setTime(hours, minutes, seconds, days, months, years);
|
||||
}
|
||||
|
||||
|
@ -1,33 +1,20 @@
|
||||
#ifndef __TEENSYCLOCK_H
|
||||
#define __TEENSYCLOCK_H
|
||||
|
||||
#ifdef TEENSYDUINO
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include "slot.h"
|
||||
#include "applemmu.h"
|
||||
|
||||
class TeensyClock : public Slot {
|
||||
#include "NoSlotClock.h"
|
||||
|
||||
class TeensyClock : public NoSlotClock {
|
||||
public:
|
||||
TeensyClock(AppleMMU *mmu);
|
||||
virtual ~TeensyClock();
|
||||
|
||||
virtual bool Serialize(int8_t fd);
|
||||
virtual bool Deserialize(int8_t fd);
|
||||
|
||||
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;
|
||||
protected:
|
||||
virtual void populateClockRegister();
|
||||
virtual void updateClockFromRegister();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user