blob: 8328e4e5daa27173e234af293725623f842206c0 [file] [log] [blame] [raw]
/*
YAPE - Yet Another Plus/4 Emulator
The program emulates the Commodore 264 family of 8 bit microcomputers
This program is free software, you are welcome to distribute it,
and/or modify it under certain conditions. For more information,
read 'Copying'.
(c) 2000, 2001, 2004, 2005, 2015 Attila Grósz
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <ctype.h>
#include "tedmem.h"
#include "Sid.h"
#include "sound.h"
#include "cpu.h"
#include "keyboard.h"
#include "tape.h"
#include "roms.h"
#include "tcbm.h"
#include "Clockable.h"
#include "video.h"
#define RETRACESCANLINEMAX 360
#define RETRACESCANLINEMIN (SCR_VSIZE - (RETRACESCANLINEMAX - SCR_VSIZE))
unsigned int TED::vertSubCount;
int TED::x;
unsigned char *TED::VideoBase;
unsigned int TED::masterClock;
ClockCycle TED::CycleCounter;
bool TED::ScreenOn, TED::attribFetch;
bool TED::displayEnable;
bool TED::SideBorderFlipFlop, TED::CharacterWindow;
unsigned int TED::BadLine;
unsigned int TED::clockingState;
unsigned int TED::CharacterCount = 0;
bool TED::VertSubActive;
unsigned int TED::CharacterPosition;
unsigned int TED::CharacterPositionReload;
//unsigned int TED::CharacterPositionCount;
unsigned int TED::TVScanLineCounter;
bool TED::HBlanking;
bool TED::VBlanking;
bool TED::aligned_write;
unsigned char *TED::aw_addr_ptr;
unsigned char TED::aw_value;
unsigned int TED::ff1d_latch;
bool TED::charPosLatchFlag;
bool TED::endOfScreen;
bool TED::delayedDMA;
TED *TED::instance_;
unsigned int TED::retraceScanLine;
char TED::romlopath[4][260];
char TED::romhighpath[4][260];
unsigned int TED::bigram, TED::bramsm;
// 64 kbytes of memory allocated by default
unsigned int TED::RAMMask = 0xFFFF;
unsigned int TED::sidCardEnabled;
rvar_t TED::tedSettings[] = {
//{ "Sid card", "SidCardEnabled", TED::toggleSidCard, &TED::sidCardEnabled, RVAR_TOGGLE, NULL },
//{ "rom c0 low", "ROMC0LOW", NULL, TED::romlopath[0], RVAR_STRING },
//{ "rom c1 low", "ROMC1LOW", NULL, TED::romlopath[1], RVAR_STRING },
//{ "rom c2 low", "ROMC2LOW", NULL, TED::romlopath[2], RVAR_STRING },
//{ "rom c3 low", "ROMC3LOW", NULL, TED::romlopath[3], RVAR_STRING },
//{ "rom c0 hi", "ROMC0HIGH", NULL, TED::romhighpath[0], RVAR_STRING },
//{ "rom c1 hi", "ROMC1HIGH", NULL, TED::romhighpath[1], RVAR_STRING },
//{ "rom c2 hi", "ROMC2HIGH", NULL, TED::romhighpath[2], RVAR_STRING },
{ "C264 RAM mask", "RamMask", TED::flipRamMask, &TED::RAMMask, RVAR_HEX, NULL },
//{ "C264 RAM expansion", "256KBRAM", TED::bigram, &TED::bigram, RVAR_TOGGLE },
{ "", "", NULL, NULL, RVAR_NULL, NULL }
};
enum {
TSS = 1 << 1,
TDS = 1 << 2,
TRFSH = 1 << 3,
THALT1 = 1 << 4,
THALT2 = 1 << 5,
THALT3 = 1 << 6,
TDMA = 1 << 7,
TDMADELAY = 1 << 8,
TSSDELAY = 1 << 9,
TDSDELAY = 1 << 10,
TRFSHDELAY = 1 << 11
};
TED::TED() : sidCard(0), SaveState()
{
unsigned int i;
instance_ = this;
masterClock = TED_REAL_CLOCK_M10;
setId("TED0");
// clearing cartdridge ROMs
for (i=0;i<4;++i) {
memset(&(RomHi[i]),0,ROMSIZE);
memset(&(RomLo[i]),0,ROMSIZE);
memset(romlopath,0,sizeof(romlopath));
memset(romhighpath,0,sizeof(romhighpath));
};
// default ROM sets
strcpy(romlopath[0],"BASIC");
strcpy(romhighpath[0],"KERNAL");
// actual ram bank pointer default setting
actram=Ram;
// setting screen memory pointer
scrptr=screen;
// pointer of the end of the screen memory
endptr=scrptr + 456;
// setting the CPU to fast mode
fastmode=1;
// initial position of the electron beam (upper left corner)
irqline=vertSubCount=0;
beamy = ff1d_latch = 0;
beamx=0;
hshift = 0;
scrblank= false;
charrombank=charrambank=cset=VideoBase=Ram;
scrattr=0;
timer1=timer2=timer3=0;
chrbuf = DMAbuf;
clrbuf = DMAbuf + 64;
tmpClrbuf = DMAbuf + 128;
memset(DMAbuf, 0, sizeof(DMAbuf));
// create an instance of the keyboard class
keys = new KEYS;
tap = new TAP;
// setting the TAP::mem pointer to this MEM class
tap->mem=this;
tcbmbus = NULL;
crsrblinkon = false;
VertSubActive = false;
CharacterPositionReload = CharacterPosition = 0;
SideBorderFlipFlop = false;
render_ok = false;
displayEnable = false;
irqFlag = 0;
BadLine = 0;
CycleCounter = 0;
retraceScanLine = RETRACESCANLINEMAX;
tedSoundInit(sampleRate);
if (enableSidCard(true, 0)) {
//sidCard->setModel(SID8580DB);
}
}
void TED::flipRamMask(void *none)
{
if (RAMMask == 0xFFFF) RAMMask = 0x3FFF;
else if (RAMMask == 0x7FFF) RAMMask = 0xFFFF;
else RAMMask = 0x7FFF;
TED *ted = instance_;
// only reset if C264
if (ted->getCyclesPerRow() == SCR_HSIZE) {
ted->cpuptr->Reset();
ted->Reset(true);
}
}
void TED::soundReset()
{
if (sidCard) sidCard->reset();
}
void TED::Reset(bool clearmem)
{
RAMenable = false;
loadroms();
ChangeMemBankSetup();
// this should not be HERE, but where else could I've put it??
tap->rewind();
// clear RAM with powerup pattern
if (clearmem)
for (int i=0;i<RAMSIZE;Ram[i] = (i>>1)<<1==i ? 0 : 0xFF, i++);
soundReset();
fastmode = 2;
}
void TED::showled(int x, int y, unsigned char onoff)
{
unsigned int j, k;
char out = 0;
unsigned char *charset = (unsigned char *) (kernal+0x1000+(0x51<<3));
unsigned int offset = y * getCyclesPerRow() + x;
// if either drive motor is on or LED is on, but motor has priority
if (onoff) {
out = (onoff & 0x04) ? 0x55 : 0x52;
for (j = 0; j < 8; j++)
for (k = 0; k < 8; k++)
screen[offset + j * getCyclesPerRow() + k] = (charset[j] & (0x80 >> k)) ? out : 0x71;
}
}
void TED::texttoscreen(int x,int y, const char *scrtxt)
{
register int i =0;
while (scrtxt[i]!=0) {
chrtoscreen(x+i*8,y,scrtxt[i]);
i++;
}
}
void TED::copyToKbBuffer(const char *text, unsigned int length)
{
if (!length)
length = (unsigned int) strlen(text);
Write(0xEF, length);
while (length--)
Write(0x0527+length, text[length]);
}
void TED::chrtoscreen(int x,int y, unsigned int scrchr)
{
unsigned int j, k;
const unsigned int CPR = getCyclesPerRow();
unsigned char *charset = (unsigned char *) kernal + 0x1000;
if (isalpha(scrchr)) {
scrchr = (scrchr & ~0xE0) | ((scrchr & 0x20) << 2);
}
charset += (scrchr << 3);
for (j=0;j<8;j++)
for (k=0;k<8;k++)
screen[(y+j)*CPR+x+k] = (*(charset+j) & (0x80>>k)) ? 0x00 : 0x71;
}
void TED::loadroms()
{
for (int i=0;i<4;i++) {
loadhiromfromfile(i,romhighpath[i]);
loadloromfromfile(i,romlopath[i]);
}
mem_8000_bfff = actromlo = &(RomLo[0][0]);
mem_fc00_fcff = mem_c000_ffff = actromhi = &(RomHi[0][0]);
}
void TED::loadloromfromfile(int nr, char fname[256])
{
FILE *img;
if ((fname[0]!='\0')) {
if ((img = fopen(fname, "rb"))) {
// load low ROM file
fread(&(RomLo[nr]),ROMSIZE,1,img);
fclose(img);
return;
}
switch (nr) {
case 0: memcpy(&(RomLo[0]),basic,ROMSIZE);
break;
case 1: if (!strncmp(fname,"3PLUS1LOW",9))
memcpy(&(RomLo[1]),plus4lo,ROMSIZE);
else
memset(&(RomLo[1]),0,ROMSIZE);
break;
default : memset(&(RomLo[nr]),0,ROMSIZE);
}
} else
memset(&(RomLo[nr]),0,ROMSIZE);
}
void TED::loadhiromfromfile(int nr, char fname[256])
{
FILE *img;
if ((fname[0]!='\0')) {
if ((img = fopen(fname, "rb"))) {
// load high ROM file
fread(&(RomHi[nr]),ROMSIZE,1,img);
fclose(img);
return;
}
switch (nr) {
case 0:
memcpy(&(RomHi[0]),kernal,ROMSIZE);
break;
case 1: if (!strncmp(fname,"3PLUS1HIGH",10))
memcpy(&(RomHi[1]),plus4hi,ROMSIZE);
else
memset(&(RomHi[1]),0,ROMSIZE);
break;
default : memset(&(RomHi[nr]),0,ROMSIZE);
}
} else
memset(&(RomHi[nr]),0,ROMSIZE);
}
ClockCycle TED::GetClockCount()
{
return CycleCounter;
}
void TED::ChangeMemBankSetup()
{
if (RAMenable) {
mem_8000_bfff = actram + (0x8000 & RAMMask);
mem_fc00_fcff = mem_c000_ffff = actram + (0xC000 & RAMMask);
} else {
mem_8000_bfff = actromlo;
mem_c000_ffff = actromhi;
mem_fc00_fcff = &(RomHi[0][0]);
}
}
unsigned char TED::Read(unsigned int addr)
{
switch ( addr & 0xF000 ) {
case 0x0000:
switch ( addr & 0xFFFF ) {
case 0:
return prddr;
case 1:
{
#if 0
CSerial *serialDevice = getRoot();
while (serialDevice) {
// Half cycle delay elapsed?
unsigned int devNr = serialDevice->getDeviceNumber();
if (devNr >= 8 && Clockable::item[devNr]->getClockCount() > 15000000) {
serialDevice->UpdateSerialState(serialPort[0]);
}
serialDevice = serialDevice->getNext();
}
#endif
unsigned char retval =
(readBus()&0xC0)
|(tap->ReadCSTIn(CycleCounter)&0x10);
return (prp&prddr)|(retval&~prddr);
}
default:
return actram[addr&0xFFFF];
}
break;
case 0x1000:
case 0x2000:
case 0x3000:
return actram[addr&0xFFFF];
case 0x4000:
case 0x5000:
case 0x6000:
case 0x7000:
return actram[addr&RAMMask];
case 0x8000:
case 0x9000:
case 0xA000:
case 0xB000:
return mem_8000_bfff[addr&0x3FFF];
case 0xC000:
case 0xD000:
case 0xE000:
return mem_c000_ffff[addr&0x3FFF];
case 0xF000:
switch ( addr >> 8 ) {
case 0xFF:
switch (addr) {
case 0xFF00 : return timer1&0xFF;
case 0xFF01 : return timer1>>8;
case 0xFF02 : return timer2&0xFF;
case 0xFF03 : return timer2>>8;
case 0xFF04 : return timer3&0xFF;
case 0xFF05 : return timer3>>8;
case 0xFF06 : return Ram[0xFF06];
case 0xFF07 : return Ram[0xFF07];
case 0xFF08 :
return keys->readLatch();
case 0xFF09 : return Ram[0xFF09]|(0x25);
case 0xFF0A : return Ram[0xFF0A]|(0xA0);
case 0xFF0B : return irqline & 0xFF;
case 0xFF0C : return ((crsrpos>>8)&0x03)|0xFC;
case 0xFF0D : return crsrpos&0xFF;
case 0xFF10 : return Ram[0xFF10] | 0x7C;
case 0xFF12 : return Ram[0xFF12] | 0xC0;
case 0xFF0E :
case 0xFF0F :
case 0xFF11 :
case 0xFF13 :
return Ram[addr];
case 0xFF14 : return Ram[addr]|0x07; // lowest 3 bits are always high
case 0xFF15 : return ecol[0]|0x80; // The highest bit is not used and so always 1
case 0xFF16 : return ecol[1]|0x80; // A few games (Rockman) used it...
case 0xFF17 : return ecol[2]|0x80;
case 0xFF18 : return ecol[3]|0x80;
case 0xFF19 : return (framecol&0xFF)|0x80;
case 0xFF1A : return (CharacterPositionReload >> 8) | 0xFC;
case 0xFF1B : return CharacterPositionReload & 0xFF;
case 0xFF1C : return (beamy>>8)|0xFE;
case 0xFF1D : return beamy&0xFF; /// 1-8. bit of the rasterline counter
case 0xFF1E : return ((98+beamx)<<1)%228; // raster column
case 0xFF1F : return 0x80|(crsrphase<<3)|vertSubCount;
default:
return mem_c000_ffff[addr&0x3FFF];
}
break;
case 0xFE:
if (tcbmbus)
return tcbmbus->Read(addr);
else
return addr >> 8; // FIXME
case 0xFD:
switch (addr>>4) {
case 0xFD0: // RS232
return 0xFD;
case 0xFD1: // User port, PIO & 256 RAM expansion
return (tap->IsButtonPressed()<<2)^0xFF;
case 0xFD2: // Speech hardware
return addr >> 8;
case 0xFD4: // SID Card
case 0xFD5:
case 0xFE8: // FIXME never taken
case 0xFE9:
if (sidCard) {
flushBuffer(CycleCounter, TED_SOUND_CLOCK);
return sidCard->read(addr & 0x1f);
}
return 0xFD;
case 0xFD3:
return Ram[0xFD30];
}
return 0xFD;
case 0xFC:
return mem_fc00_fcff[addr&0x3FFF];
default:
return mem_c000_ffff[addr&0x3FFF];
}
}
//fprintf(stderr,"Unhandled read %04X\n", addr);
return 0;
}
void TED::updateSerialDevices(unsigned char newAtn)
{
// Let all devices know about the new serial state
CSerial *SerialDevChain = CSerial::getRoot();
while (SerialDevChain) {
SerialDevChain->UpdateSerialState(newAtn);
SerialDevChain = SerialDevChain->getNext();
}
}
void TED::UpdateSerialState(unsigned char portVal)
{
static unsigned char prevVal = 0x00;
if ((prevVal ^ portVal) & 8)
tap->SetTapeMotor(CycleCounter, portVal&8);
if ((prevVal ^ portVal) & 7 ) { // serial lines changed
serialPort[0] = ((portVal << 7) & 0x80) // DATA OUT -> DATA IN
| ((portVal << 5) & 0x40) // CLK OUT -> CLK IN
| ((portVal << 2) & 0x10); // ATN OUT -> ATN IN (drive)
updateSerialDevices(serialPort[0]);
#if LOG_SERIAL
fprintf(stderr, "$01 write : %02X @ PC=%04X\n", portVal, cpuptr->getPC());
fprintf(stderr, "$01 written: %02X @ PC=%04X.\n", serialPort[0], cpuptr->getPC());
#endif
}
prevVal = portVal;
}
void TED::changeCharsetBank()
{
unsigned int tmp;
if (rvsmode || ecmode) {
tmp = 0xF800 & RAMMask;
} else {
tmp = 0xFC00 & RAMMask;
}
charrombank = &(RomHi[0][((Ram[0xFF13] & 0x3C)<<8) & tmp]);
charrambank = Ram + ((Ram[0xFF13]<<8)&tmp);
cset = charrom ? charrombank : charrambank;
}
void TED::Write(unsigned int addr, unsigned char value)
{
unsigned int tmp;
switch (addr&0xF000) {
case 0x0000:
switch ( addr & 0xFFFF ) {
case 0:
prddr = value & 0xDF;
UpdateSerialState(prddr & ~prp);
return;
case 1:
prp = value;
UpdateSerialState(prddr & ~prp);
return;
default:
actram[addr & 0xFFFF] = value;
}
return;
case 0x1000:
case 0x2000:
case 0x3000:
actram[addr&0xFFFF] = value;
return;
case 0xD000:
case 0x4000:
case 0x5000:
case 0x6000:
case 0x7000:
case 0x8000:
case 0x9000:
case 0xA000:
case 0xB000:
case 0xC000:
case 0xE000:
actram[addr&RAMMask] = value;
return;
case 0xF000:
switch ( addr >> 8 ) {
case 0xFF:
switch (addr) {
case 0xFF00:
t1on=false; // Timer1 disabled
t1start=(t1start & 0xFF00)|value;
timer1=(timer1 & 0xFF00)|value;
return;
case 0xFF01:
t1on=true; // Timer1 enabled
t1start=(t1start & 0xFF)|(value<<8);
timer1=(timer1 & 0x00FF)|(value<<8);
return;
case 0xFF02 :
t2on=false; // Timer2 disabled
timer2=(timer2 & 0xFF00)|value;
return;
case 0xFF03:
t2on=true; // Timer2 enabled
timer2=(timer2&0x00FF)|(value<<8);
return;
case 0xFF04:
t3on=false; // Timer3 disabled
timer3=(timer3&0xFF00)|value;
return;
case 0xFF05:
t3on=true; // Timer3 enabled
timer3=(timer3&0x00FF)|(value<<8);
return;
case 0xFF06:
/*fprintf(stderr, "%04X write %02X in cycle %d, in line: %d\n", addr,
value, beamx, beamy);*/
Ram[0xFF06]=value;
// get vertical offset of screen when smooth scroll
vshift = value&0x07;
// check for flat screen (23 rows)
fltscr = !(value&0x08);
// check for extended mode
ecmode = value & EXTCOLOR;
// check for graphics mode (5th b14it)
scrattr = (scrattr & ~(GRAPHMODE|EXTCOLOR))|(value & (GRAPHMODE|EXTCOLOR));
changeCharsetBank();
// Check if screen is turned on
displayEnable = !!(value & 0x10);
if (!attribFetch && displayEnable && beamy == 0) {
attribFetch = true;
vertSubCount = 7;
if (vshift != (ff1d_latch & 7))
{
if (beamx > 4 && beamx < 84)
clockingState = TSSDELAY;
}
} else if (attribFetch && ((fltscr && ff1d_latch == 8) || (!fltscr && ff1d_latch == 4))) {
ScreenOn = true;
} else if ((ff1d_latch == 200 && fltscr) || (ff1d_latch == 204 && !fltscr)) {
ScreenOn = false;
}
if ( (Ram[0xFF06] ^ value) & 0x1F)
return;
if (attribFetch) {
if (vshift == (ff1d_latch & 7)) {
// Delayed DMA?
if (beamx>=3 && beamx<89) {
unsigned char idleread = Read((cpuptr->getPC()+1)&0xFFFF);
unsigned int delay = (BadLine & 2) ? 0 : (beamx - 1) >> 1;
unsigned int invalidcount = (delay > 3) ? 3 : delay;
unsigned int invalidpos = delay - invalidcount;
invalidcount = (invalidcount < 40-invalidpos) ? invalidcount : 40-invalidpos;
unsigned int newdmapos = (invalidpos+invalidcount < 40) ? invalidpos+invalidcount : 40;
unsigned int newdmacount = 40 - newdmapos ;
unsigned int oldcount = 40 - newdmacount - invalidcount;
memcpy(tmpClrbuf, clrbuf, oldcount);
memset(tmpClrbuf + oldcount, idleread, invalidcount);
memcpy(tmpClrbuf + oldcount + invalidcount, VideoBase + CharacterCount + oldcount
+ invalidcount, newdmacount);
BadLine |= 1;
delayedDMA = true;
if (!(BadLine & 2))
clockingState = TDMADELAY;
} else if (beamx<111 && beamx>=89) {
// FIXME this breaks on FF1E writes
BadLine |= 1;
}
} else if (BadLine & 1) {
if (beamx>=94) {
BadLine &= ~1;
} else if (beamx >= 91) {
unsigned char *tmpbuf = clrbuf;
clrbuf = tmpClrbuf;
tmpClrbuf = tmpbuf;
BadLine &= ~1;
}
}
}
return;
case 0xFF07:
Ram[0xFF07]=value;
// check for narrow screen (38 columns)
nrwscr=value&0x08;
// get horizontal offset of screen when smooth scroll
hshift=value&0x07;
// check for reversed mode
rvsmode = value & 0x80;
// check for multicolor mode
scrattr = (scrattr & ~(MULTICOLOR|REVERSE)) | (value & (MULTICOLOR|REVERSE));
changeCharsetBank();
return;
case 0xFF08:
keys->latch(Ram[0xfd30], Ram[0xFF08] = value);
return;
case 0xFF09:
// clear the interrupt requester bits
// by writing 1 into them (!!)
Ram[0xFF09]=(Ram[0xFF09]&0x7F)&(~value);
// check if we have a pending IRQ
if (Ram[0xFF0A]&0x5E&(Ram[0xFF09]|4)) {
Ram[0xFF09] |= 0x80;
irqFlag = 0x80;
} else {
Ram[0xFF09] &= 0x7F;
irqFlag = 0;
}
return;
case 0xFF0A:
{
Ram[0xFF0A]=value;
// change the raster irq line
unsigned int newirqline = (irqline&0xFF)|((value&0x01)<<8);
if (newirqline != irqline) {
if (beamy == newirqline) {
Ram[0xFF09]|=0x02;
}
irqline = newirqline;
}
// check if we have a pending IRQ m
if ((Ram[0xFF09]|4) & 0x5E & value) {
Ram[0xFF09] |= 0x80;
irqFlag = 0x80;
} else {
Ram[0xFF09] &= 0x7F;
irqFlag = 0;
}
}
return;
case 0xFF0B:
{
Ram[0xFF0B]=value;
unsigned int newirqline = value|(irqline&0x0100);
if (newirqline != irqline) {
if (beamy == newirqline) {
Ram[0xFF0A]&0x02 ? Ram[0xFF09]|=0x82 : Ram[0xFF09]|=0x02;
irqFlag |= Ram[0xFF09] & 0x80;
}
irqline = newirqline;
}
}
return;
case 0xFF0C:
crsrpos=((value<<8)|(crsrpos&0xFF))&0x3FF;
return;
case 0xFF0D:
crsrpos=value|(crsrpos&0xFF00);
return;
case 0xFF0E:
if (value != Ram[0xFF0E]) {
writeSoundReg(CycleCounter, 0, value);
Ram[0xFF0E]=value;
}
return;
case 0xFF0F:
if (value != Ram[0xFF0F]) {
writeSoundReg(CycleCounter, 1, value);
Ram[0xFF0F]=value;
}
return;
case 0xFF10:
if (value != Ram[0xFF10]) {
writeSoundReg(CycleCounter, 2, value & 3);
Ram[0xFF10]=value;
}
return;
case 0xFF11:
if (value != Ram[0xFF11]) {
writeSoundReg(CycleCounter, 3, value);
Ram[0xFF11]=value;
}
return;
case 0xFF12:
grbank=Ram+((value&0x38)<<10);
if ((value ^ Ram[0xFF12]) & 3)
writeSoundReg(CycleCounter, 4, value & 3);
// if the 2nd bit is set the chars are read from ROM
charrom=(value&0x04) != 0;
if (charrom && Ram[0xFF13] < 0x80)
scrattr|=ILLEGAL;
else {
scrattr&=~ILLEGAL;
cset = charrom ? charrombank : charrambank;
}
Ram[0xFF12]=value;
return;
case 0xFF13:
// the 0th bit is not writable, it indicates if the ROMs are on
Ram[0xFF13]=(value&0xFE)|(Ram[0xFF13]&0x01);
// bit 1 is the fast/slow mode switch
if ((fastmode ^ value) & 2) {
fastmode = (value & 2) ^ 2;
if (!fastmode) {
if (clockingState == TDS) {
clockingState = TSSDELAY;
} else if (clockingState == TRFSH) {
clockingState = TSS;
}
} else {
if (clockingState == TSS) {
clockingState = TDSDELAY;
}
}
}
(ecmode || rvsmode) ? tmp=(0xF800)&RAMMask : tmp=(0xFC00)&RAMMask;
charbank = ((value)<<8)&tmp;
charrambank=Ram+charbank;
charrombank=&(RomHi[0][charbank & 0x3C00]);
if (charrom && value<0x80)
scrattr|=ILLEGAL;
else {
scrattr&=~ILLEGAL;
(charrom) ? cset = charrombank : cset = charrambank;
}
return;
case 0xFF14:
Ram[0xFF14]=value;
VideoBase = Ram+(((value&0xF8)<<8)&RAMMask);
return;
case 0xFF15:
ecol[0]=bmmcol[0]=mcol[0]=value&0x7F;
return;
case 0xFF16:
ecol[1]=bmmcol[3]=mcol[1]=value&0x7F;
return;
case 0xFF17:
ecol[2]=mcol[2]=value&0x7F;
return;
case 0xFF18:
ecol[3]=value&0x7F;
return;
case 0xFF19:
value &= 0x7F;
framecol=(value<<24)|(value<<16)|(value<<8)|value;
return;
case 0xFF1A:
CharacterPositionReload = (CharacterPositionReload & 0xFF) | ((value&3)<<8);
return;
case 0xFF1B:
CharacterPositionReload = (CharacterPositionReload & 0x300) | value;
return;
case 0xFF1C:
/*fprintf(stderr, "%04X write %d (%02X) in line: %d (%03X) @ TV line %d @PC=%04X in cycle %llu \n",
addr, value, value, beamy, beamy, TVScanLineCounter, cpuptr->getPC(), CycleCounter);*/
beamy=((value&0x01)<<8)|(beamy&0xFF);
return;
case 0xFF1D:
/*fprintf(stderr, "%04X write %d (%02X) in line: %d (%03X) @ TV line %d @PC=%04X in cycle %llu \n",
addr, value, value, beamy, beamy, TVScanLineCounter, cpuptr->getPC(), CycleCounter);*/
beamy=(beamy&0x0100)|value;
return;
case 0xFF1E:
{
/*fprintf(stderr, "%04X write %02X in cycle %d, in line: %d\n", addr,
value ^ 0xFF, beamx, beamy);*/
unsigned int low_x = beamx&1;
// lowest 2 bits are not writable
// inverted value must be written
unsigned int new_beamx=((~value))&0xFC;
new_beamx >>= 1;
if (new_beamx < 114)
new_beamx>=98 ? new_beamx -= 98 : new_beamx += 16;
// writes are aligned to single clock cycles
if (low_x) {
aligned_write = true;
aw_addr_ptr = (unsigned char*)(&beamx);
aw_value = new_beamx;
} else {
beamx = new_beamx;
}
}
return;
case 0xFF1F :
vertSubCount=value&0x07;
if ((crsrphase & 0x0F) == 0x0F) {
if (value != 0x78) crsrblinkon = !crsrblinkon;
}
crsrphase=(value&0x78)>>3;
return;
case 0xFF3E :
Ram[0xFF13]|=0x01;
RAMenable=false;
ChangeMemBankSetup();
return;
case 0xFF3F :
Ram[0xFF13]&=0xFE;
RAMenable=true;
ChangeMemBankSetup();
return;
}
actram[addr&RAMMask] = value;
return;
case 0xFE:
case 0xFD:
switch (addr>>4) {
default:
case 0xFD0: // RS232
case 0xFD1: // User port, PIO & 256 RAM expansion
case 0xFD2: // Speech hardware
return;
case 0xFD3:
Ram[0xFD30] = value;
return;
case 0xFD4: // SID Card
case 0xFD5:
case 0xFE8:
case 0xFE9:
if (sidCard) {
flushBuffer(CycleCounter, TED_SOUND_CLOCK);
sidCard->write(addr & 0x1f, value);
}
return;
case 0xFDD:
actromlo=&(RomLo[addr&0x03][0]);
actromhi=&(RomHi[(addr&0x0c)>>2][0]);
return;
case 0xFEC:
case 0xFED:
case 0xFEE:
case 0xFEF:
if (tcbmbus)
tcbmbus->Write(addr,value);
return;
}
return;
default:
actram[addr&RAMMask] = value;
return;
}
}
return;
}
void TED::dumpState()
{
// this is ugly :-P
saveVar(Ram,RAMSIZE);
saveVar(&prp, sizeof(prp));
saveVar(&prddr, sizeof(prddr));
saveVar(serialPort, sizeof(serialPort[0]));
saveVar(&RAMenable,sizeof(RAMenable));
saveVar(&t1start,sizeof(t1start));
saveVar(&t1on,sizeof(t1on));
saveVar(&t2on,sizeof(t2on));
saveVar(&t3on,sizeof(t3on));
saveVar(&timer1,sizeof(timer1));
saveVar(&timer2,sizeof(timer2));
saveVar(&timer3,sizeof(timer3));
saveVar(&beamx,sizeof(beamx));
saveVar(&beamy,sizeof(beamy));
//fwrite(&x,sizeof(x));
saveVar(&irqline,sizeof(irqline));
saveVar(&crsrpos,sizeof(crsrpos));
saveVar(&scrattr,sizeof(scrattr));
saveVar(&nrwscr,sizeof(nrwscr));
saveVar(&hshift,sizeof(hshift));
saveVar(&vshift,sizeof(vshift));
saveVar(&fltscr,sizeof(fltscr));
saveVar(&mcol,sizeof(mcol));
saveVar(chrbuf,40);
saveVar(clrbuf,40);
saveVar(&charrom,sizeof(charrom));
saveVar(&charbank,sizeof(charbank));
saveVar(&framecol,sizeof(framecol));
}
void TED::readState()
{
// this is ugly :-P
readVar(Ram,RAMSIZE);
readVar(&prp, sizeof(prp));
readVar(&prddr, sizeof(prddr));
readVar(serialPort, sizeof(serialPort[0]));
readVar(&RAMenable,sizeof(RAMenable));
readVar(&t1start,sizeof(t1start));
readVar(&t1on,sizeof(t1on));
readVar(&t2on,sizeof(t2on));
readVar(&t3on,sizeof(t3on));
readVar(&timer1,sizeof(timer1));
readVar(&timer2,sizeof(timer2));
readVar(&timer3,sizeof(timer3));
readVar(&beamx,sizeof(beamx));
readVar(&beamy,sizeof(beamy));
//readVar(&x,sizeof(x));
readVar(&irqline,sizeof(irqline));
readVar(&crsrpos,sizeof(crsrpos));
readVar(&scrattr,sizeof(scrattr));
readVar(&nrwscr,sizeof(nrwscr));
readVar(&hshift,sizeof(hshift));
readVar(&vshift,sizeof(vshift));
readVar(&fltscr,sizeof(fltscr));
readVar(&mcol,sizeof(mcol));
readVar(chrbuf,40);
readVar(clrbuf,40);
readVar(&charrom,sizeof(charrom));
readVar(&charbank,sizeof(charbank));
readVar(&framecol,sizeof(framecol));
for (int i=0; i<5; i++)
writeSoundReg(0, i, Ram[0xFF0E + i]);
beamy=0;
beamx=0;
scrptr=screen;
charrambank=Ram+charbank;
charrombank=&(RomHi[0][charbank & 0x3C00]);
(charrom) ? cset = charrombank : cset = charrambank;
}
// when multi and extended color modes are all on the screen is blank
inline void TED::mcec()
{
memset( scrptr, 0, 8);
}
// renders hires text with reverse (128 chars)
inline void TED::hi_text()
{
unsigned char chr;
unsigned char charcol;
unsigned char mask;
unsigned char *wbuffer = scrptr + hshift;
// get the actual physical character column
charcol=clrbuf[x];
chr=chrbuf[x];
if ((charcol)&0x80 && !crsrblinkon)
mask = 00;
else if (VertSubActive)
mask = cset[((chr&0x7F)<<3)|vertSubCount];
else
mask = Read(0xFFFF);
if (chr&0x80)
mask ^= 0xFF;
if (crsrpos==((CharacterPosition+x)&0x3FF) && crsrblinkon )
mask ^= 0xFF;
wbuffer[0] = (mask & 0x80) ? charcol : mcol[0];
wbuffer[1] = (mask & 0x40) ? charcol : mcol[0];
wbuffer[2] = (mask & 0x20) ? charcol : mcol[0];
wbuffer[3] = (mask & 0x10) ? charcol : mcol[0];
wbuffer[4] = (mask & 0x08) ? charcol : mcol[0];
wbuffer[5] = (mask & 0x04) ? charcol : mcol[0];
wbuffer[6] = (mask & 0x02) ? charcol : mcol[0];
wbuffer[7] = (mask & 0x01) ? charcol : mcol[0];
}
// renders text without the reverse (all 256 chars)
inline void TED::rv_text()
{
unsigned char chr;
unsigned char charcol;
unsigned char mask;
unsigned char *wbuffer = scrptr + hshift;
// get the actual physical character column
charcol=clrbuf[x];
chr=chrbuf[x];
if ((charcol)&0x80 && !crsrblinkon)
mask = 00;
else if (VertSubActive)
mask = cset[(chr<<3)|vertSubCount];
else
mask = Read(0xFFFF);
if (crsrpos==((CharacterPosition+x)&0x3FF) && crsrblinkon )
mask ^= 0xFF;
wbuffer[0] = (mask & 0x80) ? charcol : mcol[0];
wbuffer[1] = (mask & 0x40) ? charcol : mcol[0];
wbuffer[2] = (mask & 0x20) ? charcol : mcol[0];
wbuffer[3] = (mask & 0x10) ? charcol : mcol[0];
wbuffer[4] = (mask & 0x08) ? charcol : mcol[0];
wbuffer[5] = (mask & 0x04) ? charcol : mcol[0];
wbuffer[6] = (mask & 0x02) ? charcol : mcol[0];
wbuffer[7] = (mask & 0x01) ? charcol : mcol[0];
}
// renders extended color text
inline void TED::ec_text()
{
unsigned char charcol;
unsigned char chr;
unsigned char mask;
unsigned char *wbuffer = scrptr + hshift;
// get the actual physical character column
charcol = clrbuf[x] & 0x7F;
chr = chrbuf[x];
if (VertSubActive)
mask = cset[ ((chr & 0x3F) << 3)|vertSubCount ];
else
mask = Read(0xFFFF);
chr >>= 6;
wbuffer[0] = (mask & 0x80) ? charcol : ecol[chr];
wbuffer[1] = (mask & 0x40) ? charcol : ecol[chr];
wbuffer[2] = (mask & 0x20) ? charcol : ecol[chr];
wbuffer[3] = (mask & 0x10) ? charcol : ecol[chr];
wbuffer[4] = (mask & 0x08) ? charcol : ecol[chr];
wbuffer[5] = (mask & 0x04) ? charcol : ecol[chr];
wbuffer[6] = (mask & 0x02) ? charcol : ecol[chr];
wbuffer[7] = (mask & 0x01) ? charcol : ecol[chr];
}
// renders multicolor text
inline void TED::mc_text_rvs()
{
unsigned char chr = chrbuf[x];
unsigned char charcol = clrbuf[x];
unsigned char *wbuffer = scrptr + hshift;
unsigned char mask;
if (VertSubActive)
mask = cset[(chr << 3) | vertSubCount];
else
mask = Read(0xFFFF);
if (charcol&0x08) { // if character is multicolored
mcol[3]=charcol & 0xF7;
wbuffer[0] = wbuffer[1] = mcol[mask >> 6];
wbuffer[2] = wbuffer[3] = mcol[(mask & 0x30) >> 4];
wbuffer[4] = wbuffer[5] = mcol[(mask & 0x0C) >> 2];
wbuffer[6] = wbuffer[7] = mcol[mask & 0x03];
} else { // this is a normally colored character
wbuffer[0] = (mask & 0x80) ? charcol : mcol[0];
wbuffer[1] = (mask & 0x40) ? charcol : mcol[0];
wbuffer[2] = (mask & 0x20) ? charcol : mcol[0];
wbuffer[3] = (mask & 0x10) ? charcol : mcol[0];
wbuffer[4] = (mask & 0x08) ? charcol : mcol[0];
wbuffer[5] = (mask & 0x04) ? charcol : mcol[0];
wbuffer[6] = (mask & 0x02) ? charcol : mcol[0];
wbuffer[7] = (mask & 0x01) ? charcol : mcol[0];
}
}
// renders multicolor text with reverse bit set
inline void TED::mc_text()
{
unsigned char charcol = clrbuf[x];
unsigned char chr = chrbuf[x] & 0x7F;
unsigned char *wbuffer = scrptr + hshift;
unsigned char mask;
if (VertSubActive)
mask = cset[(chr << 3) | vertSubCount];
else
mask = Read(0xFFFF);
if ((charcol)&0x08) { // if character is multicolored
mcol[3] = charcol & 0xF7;
wbuffer[0] = wbuffer[1] = mcol[mask >> 6];
wbuffer[2] = wbuffer[3] = mcol[(mask & 0x30) >> 4];
wbuffer[4] = wbuffer[5] = mcol[(mask & 0x0C) >> 2];
wbuffer[6] = wbuffer[7] = mcol[mask & 0x03];
} else { // this is a normally colored character
wbuffer[0] = (mask & 0x80) ? charcol : mcol[0];
wbuffer[1] = (mask & 0x40) ? charcol : mcol[0];
wbuffer[2] = (mask & 0x20) ? charcol : mcol[0];
wbuffer[3] = (mask & 0x10) ? charcol : mcol[0];
wbuffer[4] = (mask & 0x08) ? charcol : mcol[0];
wbuffer[5] = (mask & 0x04) ? charcol : mcol[0];
wbuffer[6] = (mask & 0x02) ? charcol : mcol[0];
wbuffer[7] = (mask & 0x01) ? charcol : mcol[0];
}
}
// renders hires bitmap graphics
inline void TED::hi_bitmap()
{
unsigned char mask;
unsigned char *wbuffer = scrptr + hshift;
// get the actual color attributes
hcol[0] = (chrbuf[x] & 0x0F) | (clrbuf[x] & 0x70);
hcol[1] = (chrbuf[x] >> 4) | ((clrbuf[x] & 0x07) << 4);
if (VertSubActive)
mask = grbank[(((CharacterPosition + x) << 3) & 0x1FFF) | vertSubCount];
else
mask = Read(0xFFFF);
wbuffer[0] = (mask & 0x80) ? hcol[1] : hcol[0];
wbuffer[1] = (mask & 0x40) ? hcol[1] : hcol[0];
wbuffer[2] = (mask & 0x20) ? hcol[1] : hcol[0];
wbuffer[3] = (mask & 0x10) ? hcol[1] : hcol[0];
wbuffer[4] = (mask & 0x08) ? hcol[1] : hcol[0];
wbuffer[5] = (mask & 0x04) ? hcol[1] : hcol[0];
wbuffer[6] = (mask & 0x02) ? hcol[1] : hcol[0];
wbuffer[7] = (mask & 0x01) ? hcol[1] : hcol[0];
}
// renders multicolor bitmap graphics
inline void TED::mc_bitmap()
{
unsigned char mask;
unsigned char *wbuffer = scrptr + hshift;
// get the actual color attributes
bmmcol[1] = (chrbuf[x] >> 4)|((clrbuf[x] & 0x07) << 4);
bmmcol[2] = (chrbuf[x] & 0x0F)|(clrbuf[x] & 0x70);
if (VertSubActive)
mask = grbank[ (((CharacterPosition+x)<<3)&0x1FFF)+vertSubCount ];
else
mask = Read(0xFFFF);
wbuffer[0]= wbuffer[1] = bmmcol[ mask >> 6 ];
wbuffer[2]= wbuffer[3] = bmmcol[ (mask & 0x30) >> 4 ];
wbuffer[4]= wbuffer[5] = bmmcol[ (mask & 0x0C) >> 2 ];
wbuffer[6]= wbuffer[7] = bmmcol[ mask & 0x03 ];
}
// "illegal" mode: when $FF13 points to an illegal ROM address
// the current data on the bus is displayed
inline void TED::illegalbank()
{
unsigned char chr = chrbuf[x];
unsigned char charcol = clrbuf[x];
unsigned char mask;
unsigned char *wbuffer = scrptr + hshift;
if (charcol&0x80 && crsrblinkon)
mask = 00;
else {
if (BadLine==1)
mask = clrbuf[x];
else if (BadLine==2)
mask = chrbuf[x];
else
mask = Read(VertSubActive ? cpuptr->getPC() : 0xFFFF);
}
if (chr&0x80)
mask ^= 0xFF;
if (crsrpos==((CharacterPosition+x)&0x3FF) && crsrblinkon)
mask ^= 0xFF;
wbuffer[0] = (mask & 0x80) ? charcol : mcol[0];
wbuffer[1] = (mask & 0x40) ? charcol : mcol[0];
wbuffer[2] = (mask & 0x20) ? charcol : mcol[0];
wbuffer[3] = (mask & 0x10) ? charcol : mcol[0];
wbuffer[4] = (mask & 0x08) ? charcol : mcol[0];
wbuffer[5] = (mask & 0x04) ? charcol : mcol[0];
wbuffer[6] = (mask & 0x02) ? charcol : mcol[0];
wbuffer[7] = (mask & 0x01) ? charcol : mcol[0];
}
void TED::doDMA( unsigned char *Buf, unsigned int Offset )
{
if (CharacterCount>=0x03d9) {
memcpy( Buf, VideoBase + CharacterCount + Offset, 0x400 - CharacterCount);
memcpy( Buf + 0x400 - CharacterCount, VideoBase + Offset, (CharacterCount + 40)&0x03FF);
} else {
memcpy( Buf, VideoBase + CharacterCount + Offset, 40);
}
}
void TED::doVRetrace()
{
// frame ready...
loop_continuous = 0;
// reset screen pointer ("TV" electron beam)
TVScanLineCounter = 0;
scrptr = screen;
retraceScanLine = RETRACESCANLINEMAX;
VBlanking = false;
}
void TED::doHRetrace()
{
// the beam reached a new line
TVScanLineCounter += 1;
if ( TVScanLineCounter >= RETRACESCANLINEMAX || TVScanLineCounter >= retraceScanLine) {
if (TVScanLineCounter > RETRACESCANLINEMIN)
doVRetrace();
}
scrptr = screen + TVScanLineCounter * SCR_HSIZE;
endptr = scrptr + SCR_HSIZE;
}
inline void TED::render()
{
// call the relevant rendering function
switch (scrattr) {
case 0:
hi_text();
break;
case REVERSE :
rv_text();
break;
case MULTICOLOR|REVERSE :
mc_text_rvs();
break;
case MULTICOLOR :
mc_text();
break;
case EXTCOLOR|REVERSE :
case EXTCOLOR :
ec_text();
break;
case GRAPHMODE|REVERSE :
case GRAPHMODE :
hi_bitmap();
break;
case EXTCOLOR|MULTICOLOR :
case GRAPHMODE|EXTCOLOR :
case GRAPHMODE|MULTICOLOR|EXTCOLOR :
case REVERSE|MULTICOLOR|EXTCOLOR :
case GRAPHMODE|MULTICOLOR|EXTCOLOR|REVERSE :
mcec();
break;
case GRAPHMODE|MULTICOLOR :
case GRAPHMODE|MULTICOLOR|REVERSE :
mc_bitmap();
break;
default:
illegalbank();
break;
}
}
inline void TED::newLine()
{
beamx = 0;
beamy = ff1d_latch;
doHRetrace();
flushBuffer(CycleCounter, TED_SOUND_CLOCK);
switch (beamy) {
case 4:
if (!fltscr && displayEnable) ScreenOn = true;
break;
case 8:
if (fltscr && displayEnable) ScreenOn = true;
break;
case 200:
if (fltscr) ScreenOn = false;
break;
case 204:
if (!fltscr) ScreenOn = false;
break;
case 205:
CharacterCount = 0;
VertSubActive = false;
// cursor phase counter in TED register $1F
if ((++crsrphase&0x0F) == 0x0F)
crsrblinkon ^= 1;
break;
case 226: // NTSC
VBlanking = !!(Ram[0xFF07] & 0x40);
break;
case 229: // NTSC
if ((Ram[0xFF07] & 0x40))
retraceScanLine = TVScanLineCounter + 20;
break;
case 247: // NTSC
if (Ram[0xFF07] & 0x40)
VBlanking = false;
break;
case 251:
VBlanking = !(Ram[0xFF07] & 0x40);
break;
case 254:
// Schedule vertical retrace @ 274
if (!(Ram[0xFF07] & 0x40))
retraceScanLine = TVScanLineCounter + 20;
break;
case 261:
break;
case 274:
if (!(Ram[0xFF07] & 0x40))
VBlanking = false;
break;
case 512:
case 312:
beamy = ff1d_latch = 0;
CharacterPositionReload = 0;
if (displayEnable) {
if (!attribFetch) {
attribFetch = true;
endOfScreen = true;
}
}
}
// is there raster interrupt?
if (beamy == irqline) {
Ram[0xFF09] |= Ram[0xFF0A]&0x02 ? 0x82 : 0x02;
irqFlag |= Ram[0xFF09] & 0x80;
}
}
// main loop of the whole emulation as the TED feeds the CPU with clock cycles
void TED::ted_process(const unsigned int continuous)
{
loop_continuous = continuous;
do {
switch(++beamx) {
default:
break;
case 2:
if (VertSubActive)
vertSubCount = (vertSubCount+1)&7;
break;
case 3:
if (endOfScreen) {
vertSubCount = 7;
endOfScreen = false;
}
break;
case 4:
if (attribFetch) {
BadLine |= (vshift == (ff1d_latch & 7)) & (ff1d_latch != 203);
if ( BadLine ) {
if (clockingState != TDMADELAY) clockingState = THALT1;
} else
clockingState = TSS;
if (beamy==203) {
attribFetch = false;
if (!(BadLine & 2)) clockingState = TSS;
}
}
break;
case 8:
HBlanking = false;
break;
case 10:
if (VertSubActive)
CharacterPosition = CharacterPositionReload;
break;
case 16:
if (ScreenOn) {
SideBorderFlipFlop = true;
memset( scrptr, mcol[0], hshift);
if (nrwscr)
CharacterWindow = true;
x = 0;
}
if (BadLine & 1) {
if (!delayedDMA)
doDMA(tmpClrbuf, 0);
else
delayedDMA = false;
}
if (BadLine & 2)
doDMA(chrbuf, (BadLine & 1) ? 0 : 0x400);
break;
case 18:
if (ScreenOn && !nrwscr) {
CharacterWindow = true;
}
break;
case 89:
if (VertSubActive && vertSubCount == 6)
CharacterCount = (CharacterCount + 40)&0x3FF;
break;
case 90:
if ( VertSubActive && charPosLatchFlag) // FIXME
CharacterPositionReload = (CharacterPosition + x + 3)&0x3FF;
break;
case 91:
clockingState = TRFSH;
break;
case 94:
if (!nrwscr)
SideBorderFlipFlop = CharacterWindow = false;
break;
case 96:
if (nrwscr)
SideBorderFlipFlop = CharacterWindow = false;
// FIXME this breaks on FF1E writes
if (BadLine & 1) {
// swap DMA fetch pointers for colour DMA...
unsigned char *tmpbuf = clrbuf;
clrbuf = tmpClrbuf;
tmpClrbuf = tmpbuf;
}
break;
case 101:
clockingState = fastmode ? TDS : TSS;
break;
case 104:
HBlanking = true;
break;
case 107: // HSYNC start
break;
case 110: // $BC (376)
charPosLatchFlag = vertSubCount == 6;
break;
case 111:
if (BadLine & 1) {
BadLine = 2;
VertSubActive = true;
} else if (BadLine & 2) {// in the second bad line, we're finished...
BadLine &= ~2;
}
break;
case 113:
ff1d_latch = beamy + 1;
break;
case 114: // HSYNC end
newLine();
break;
case 127:
beamx = 15;
doHRetrace();
break;
}
if (beamx&1) { // perform these only in every second cycle
if (t2on && !((timer2--)&0xFFFF)) {// Timer2 permitted
timer2=0xFFFF;
Ram[0xFF09] |= Ram[0xFF0A]&0x10 ? 0x90 : 0x10; // interrupt
irqFlag |= Ram[0xFF09] & 0x80;
}
if (t3on && !((timer3--)&0xFFFF)) {// Timer3 permitted
timer3=0xFFFF;
Ram[0xFF09] |= Ram[0xFF0A]&0x40 ? 0xC0 : 0x40; // interrupt
irqFlag |= Ram[0xFF09] & 0x80;
}
if (!CharacterWindow && !HBlanking && !VBlanking) {
// we are on the border area, so use the frame color
*((int*)(scrptr+4)) = framecol;
}
if (scrptr != endptr)
scrptr+=8;
else
doHRetrace();
} else {
if (t1on && !timer1--) { // Timer1 permitted decreased and zero
timer1=(t1start-1)&0xFFFF;
Ram[0xFF09] |= Ram[0xFF0A]&0x08 ? 0x88 : 0x08; // interrupt
irqFlag |= Ram[0xFF09] & 0x80;
}
if (!(HBlanking||VBlanking)) {
if (SideBorderFlipFlop) { // drawing the visible part of the screen
// call the relevant rendering function
render();
x = (x + 1) & 0x3F;
}
if (!CharacterWindow) {
// we are on the border area, so use the frame color
*((int*)scrptr) = framecol;
}
}
}
if (aligned_write) {
*aw_addr_ptr = aw_value;
aligned_write = false;
}
switch (clockingState|(beamx&1)) {
case TRFSH|1:
case TSS|1:
case TDS|1:
case TDS:
cpuptr->process();
break;
case TDMADELAY|1:
cpuptr->process();
clockingState = THALT1;
break;
case THALT1|1:
case THALT2|1:
case THALT3|1:
cpuptr->stopcycle();
clockingState<<=1;
break;
case TSSDELAY:
clockingState = TSS;
case TSSDELAY|1:
cpuptr->process();
break;
case TDSDELAY|1:
clockingState = TDS;
cpuptr->process();
break;
case TDSDELAY:
clockingState = TDS;
break;
case TRFSH:
break;
}
CycleCounter++;
#if 1
unsigned int i = 0;
while (Clockable::itemHeap[i]) {
Clockable *c = Clockable::itemHeap[i];
while (c->ClockCount >= TED_REAL_CLOCK_M10) {
c->ClockCount -= TED_REAL_CLOCK_M10;
c->Clock();
}
c->ClockCount += c->ClockRate;
i++;
}
#endif
} while (loop_continuous);
}
bool TED::enableSidCard(bool enable, unsigned int disableMask)
{
if (enable) {
if (sidCard) {
return true;
}
sidCard = new SIDsound(SID8580DB, disableMask);
sidCard->setSampleRate(SAMPLE_FREQ);
sidCard->setFrequency(TED_SOUND_CLOCK / 2);
sidCardEnabled = 1;
} else {
if (!sidCard)
return false;
delete sidCard;
sidCard = 0;
sidCardEnabled = 0;
}
return false;
}
SIDsound *TED::getSidCard()
{
return sidCard;
}
Color TED::getColor(unsigned int ix)
{
Color color;
unsigned int code = ix & 0x0F;
int thishue = hue(code);
double thisluma = luma(ix >> 4);
if (thishue < 0) {
color.saturation = 0;
color.hue = 0;
} else {
color.saturation = 45.0;
color.hue = thishue;
}
if (thishue == -2) // black
color.luma = 0;
else
color.luma = thisluma;
return color;
}
TED::~TED()
{
if (keys) {
delete keys;
keys = NULL;
}
if (tap) {
delete tap;
tap = NULL;
}
if (sidCard)
enableSidCard(false, 0);
}
//--------------------------------------------------------------
// TED - fast emulation (line based) FIXME: could be made much faster
//--------------------------------------------------------------
enum {
CLK_BORDER = 0,
CLK_DMA,
CLK_SCREEN
};
static bool externalFetchWindow;
TEDFAST::TEDFAST() : TED()
{
unsigned int i = 0;
endOfDMA = false;
//emulationLevel = 0;
Clockable *device = Clockable::itemHeap[0];
while (device) {
device->ClockCount = 0;
device = Clockable::itemHeap[++i];
}
}
inline void TEDFAST::dmaLineBased()
{
static bool endOfDMA;
if (attribFetch) {
bool bad_line = (vshift == (beamy & 7));
// First line after second line of DMA
if (endOfDMA) {
endOfDMA = false;
clockingState = CLK_SCREEN;
}
// Check second line DMA
if (BadLine & 2) {
unsigned int offset;
unsigned char *tmp = tmpClrbuf;
tmpClrbuf = clrbuf;
clrbuf = tmp;
offset = bad_line ? 0 : 0x400;
doDMA(chrbuf, offset);
endOfDMA = true;
BadLine &= ~2;
externalFetchWindow = true;
VertSubActive = true;
clockingState = CLK_DMA;
}
// Check first line DMA
if (bad_line) {
BadLine |= 2;
// idleState = false;
clockingState = CLK_DMA;
doDMA(tmpClrbuf, 0);
}
if (externalFetchWindow) {
vertSubCount = (vertSubCount + 1) & 7;
if (6 == vertSubCount) {
CharacterCount = (CharacterCount + 40) & 0x3FF;
charPosLatchFlag = true;
} else if (0 == vertSubCount) {
if (charPosLatchFlag) {
CharacterPosition = (CharacterPositionReload + 40) & 0x3FF;
CharacterPositionReload = CharacterPosition;
charPosLatchFlag = false;
}
}
}
if (beamy == 203) {
if (BadLine & 2) {
BadLine = 0;
endOfDMA = false;
}
attribFetch = false;
}
}
if (beamy == 204) {
clockingState = CLK_BORDER;
externalFetchWindow = false; // -?
} else if (beamy == 311 /*TED::RasterLinesPerFrame*/) {
CharacterPositionReload = CharacterPosition = 0;
charPosLatchFlag = false;
if (endOfScreen) {
vertSubCount = 7;
endOfScreen = false;
}
}
}
inline void TEDFAST::countTimers(unsigned int clocks)
{
int newTimerValue;
if (t1on) { // Timer1 permitted decreased and zero
newTimerValue = (int) timer1 - (int) clocks;
if (newTimerValue <= 0) {
if (t1start >= clocks)
timer1 = (t1start + newTimerValue - 1)&0xFFFF;
Ram[0xFF09] |= Ram[0xFF0A]&0x08 ? 0x88 : 0x08; // interrupt
irqFlag |= Ram[0xFF09] & 0x80;
} else {
timer1 = newTimerValue;
}
}
if (t2on) {
newTimerValue = (int) timer2 - (int) clocks;
if (newTimerValue <= 0) { // Timer2 permitted
Ram[0xFF09] |= Ram[0xFF0A]&0x10 ? 0x90 : 0x10; // interrupt
irqFlag |= Ram[0xFF09] & 0x80;
}
timer2 = newTimerValue & 0xFFFF;
}
if (t3on) {
newTimerValue = (int) timer3 - (int) clocks;
if (newTimerValue <= 0) {// Timer3 permitted
Ram[0xFF09] |= Ram[0xFF0A]&0x40 ? 0xC0 : 0x40; // interrupt
irqFlag |= Ram[0xFF09] & 0x80;
}
timer3 = newTimerValue & 0xFFFF;
}
// checkIrq();
}
void TEDFAST::renderLine()
{
if (ScreenOn) {
unsigned char *screen;
scrptr += 4 * 8;
DRAW_BORDER(0,framecol);
DRAW_BORDER(4,framecol);
DRAW_BORDER(8,framecol);
DRAW_BORDER(12,framecol);
DRAW_BORDER(16,framecol);
DRAW_BORDER(20,framecol);
DRAW_BORDER(24,framecol);
DRAW_BORDER(28,framecol);
scrptr += 4 * 8;
screen = scrptr;
for(x = 0; x < 40; x++) {
//(this->*scrmode)();
render();
scrptr += 8;
}
if (!nrwscr) {
unsigned char bc = framecol & 0xFF;
memset(screen, bc, 8);
memset(screen + 312, bc, 8);
} else if (hshift) {
unsigned char a;
switch (scrattr) {
case 0:
case REVERSE :
a = mcol[0];
break;
case GRAPHMODE:
case GRAPHMODE|REVERSE:
a = (clrbuf[39]&0x70)+(chrbuf[39]&0x0F);
break;
default:
a = ecol[0];
}
memset( screen, a, hshift);
}
DRAW_BORDER(0,framecol);
DRAW_BORDER(4,framecol);
DRAW_BORDER(8,framecol);
DRAW_BORDER(12,framecol);
DRAW_BORDER(16,framecol);
DRAW_BORDER(20,framecol);
DRAW_BORDER(24,framecol);
DRAW_BORDER(28,framecol);
} else {
unsigned char bc = framecol & 0xFF;
memset(scrptr + 4 * 8, bc, 384);
}
}
const unsigned int clocksPerLine[6] = { 109, 22, 65, 57, 14, 57 };
void TEDFAST::ted_process(const unsigned int continuous)
{
loop_continuous = continuous;
Clockable *drive = Clockable::itemHeap[0];
if (drive) {
for (;loop_continuous;) {
const unsigned int clkIx = clocksPerLine[clockingState + fastmode ? 0 : 3];
ff1d_latch = (beamy + 1) & 0x1FF;
newLine();
dmaLineBased();
//if (isFrameRendered()) {
renderLine();
//}
// Drives
const unsigned int driveStepFactor = 1;
const int driveCycles = 64; // 312 * 50 * 64 = 998400 ~= 1 MHz
int machineCycles = clkIx;
do {
unsigned int i = 0;
cpuptr->process();
Clockable *device = drive;
do {
device->ClockCount += driveCycles;
while (device->ClockCount >= clkIx) { // clkIx 57
device->Clock(driveStepFactor);
device->ClockCount -= clkIx; // clkIx 57
}
device = Clockable::itemHeap[++i];
} while (device);
} while (--machineCycles);
countTimers(57);
CycleCounter += 114;
}
} else {
for (;loop_continuous;) {
const unsigned int clkIx = clocksPerLine[clockingState + fastmode ? 0 : 3];
ff1d_latch = (beamy + 1) & 0x1FF;
newLine();
dmaLineBased();
//if (isFrameRendered()) {
renderLine();
//}
cpuptr->process(clkIx);
countTimers(57);
CycleCounter += 114;
}
}
}
void TEDFAST::process_debug(unsigned int continuous)
{
TED::ted_process(continuous);
}
unsigned char TEDFAST::getHorizontalCount()
{
unsigned int cyclesPerLine = clocksPerLine[clockingState + fastmode ? 0 : 3];
return ((98 + ((cyclesPerLine - cpuptr->getRemainingCycles()) * 114
/ cyclesPerLine)) << 1) % 228;
}