blob: f5c53295a61b9c67ff93752bcd943b44ad875c36 [file] [log] [blame] [raw]
/*<empty clipboard>
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 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 "Cpu.h"
#include "roms.h"
#include "Filter.h"
#define TEXTMODE 0x00000000
#define MULTICOLOR 0x00000010
#define GRAPHMODE 0x00000020
#define EXTCOLOR 0x00000040
#define REVERSE 0x00000080
#define ILLEGAL 0x0000000F
static unsigned int VertSubCount;
static int x,tmp;
static unsigned char *VideoBase;
ClockCycle TED::CycleCounter;
static bool ScreenOn, AttribFetch;
static bool SideBorderFlipFlop, CharacterWindow;
static unsigned int BadLine;
static unsigned int ClockingState;
static unsigned int CharacterCount = 0;
static bool VertSubActive;
static unsigned int CharacterPosition;
static unsigned int CharacterPositionReload;
static unsigned int TVScanLineCounter;
static bool HBlanking;
static bool VBlanking;
static bool aligned_write;
static unsigned char *aw_addr_ptr;
static unsigned char aw_value;
static unsigned int ff1d_latch;
TED *TED::instance_;
enum {
TSS = 1 << 1,
TDS = 1 << 2,
TRFSH = 1 << 3,
THALT1 = 1 << 4,
THALT2 = 1 << 5,
THALT3 = 1 << 6,
TDMA = 1 << 7
};
TED::TED() : filter(0), sidCard(0)
{
register unsigned int i;
instance_ = this;
// 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");
// 64 kbytes of memory allocated
RAMMask=0xFFFF;
// 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*312-8;
// setting the CPU to fast mode
fastmode=1;
// initial position of the electron beam (upper left corner)
irqline=VertSubCount=0;
beamy=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( clrbuf, sizeof(DMAbuf), 0);
// 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;
BadLine = 0;
CycleCounter = 0;
oscillatorInit();
memset(protectedPlayerMemory, 0xfe, sizeof(protectedPlayerMemory));
enableSidCard(true);
}
void TED::Reset()
{
// clear RAM with powerup pattern
for (int i=0;i<RAMSIZE;Ram[i++] = (i>>1)<<1==i ? 0 : 0xFF);
// reset oscillators
oscillatorReset();
if (sidCard) sidCard->reset();
lastResetCycle = CycleCounter;
}
void TED::forcedReset()
{
ChangeMemBankSetup(false);
Reset();
}
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++]);
}
void TED::chrtoscreen(int x,int y, char scrchr)
{
register int j, k;
unsigned char *charset = (unsigned char *) kernal+0x1000;
if (isalpha(scrchr)) {
scrchr=toupper(scrchr)-64;
charset+=(scrchr<<3);
for (j=0;j<8;j++)
for (k=0;k<8;k++)
(*(charset+j) & (0x80>>k)) ? screen[(y+j)*456+x+k]=0x00 : screen[(y+j)*456+x+k]=0x71;
return;
}
charset+=(scrchr<<3);
for (j=0;j<8;j++)
for (k=0;k<8;k++)
(*(charset+j) & (0x80>>k)) ? screen[(y+j)*456+x+k]=0x00 : screen[(y+j)*456+x+k]=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[512])
{
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[512])
{
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);
}
void TED::injectCodeToRAM(unsigned int address, unsigned char *from, size_t len)
{
unsigned int bytes = (address + len > 0xffff) ? 0xffff - address : len;
memcpy(actram + (address & 0xffff), from, bytes);
}
void TED::copyToKbBuffer(char *bufferString, unsigned int bufferLength)
{
unsigned int bufferAddress = 0x0527;
if (bufferLength == -1) bufferLength = strlen(bufferString);
for (unsigned int i=0; i < bufferLength; i++)
Write( bufferAddress + i, bufferString[i]);
Write(0xEF, bufferLength);
}
ClockCycle TED::GetClockCount()
{
return CycleCounter;
}
void TED::ChangeMemBankSetup(bool romoff)
{
if (romoff) {
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:
{
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 :
#if 0
if (joyemu)
return keys->feedjoy(Ram[0xFF08])&(keys->feedkey(Ram[0xFD30]));
else
return keys->feedkey(Ram[0xFD30]);
#else
return 0xFF;
#endif
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 0xFF0E :
case 0xFF0F :
case 0xFF10 :
case 0xFF11 :
case 0xFF12 :
case 0xFF13 :
case 0xFF14 :
return Ram[addr];
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)&0xFF;
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:
//return tcbmbus->Read(addr);
//return 0xFE;
return protectedPlayerMemory[addr & 0xfff];
case 0xFD:
switch (addr>>4) {
case 0xFD0: // RS232
return 0xFD;
case 0xFD1: // User port, PIO & 256 RAM expansion
//return (tap->IsButtonPressed()<<2)^0xFF;
return 0xFF;
case 0xFD2: // Speech hardware
case 0xFD4: // SID Card
case 0xFD5:
if (sidCard) {
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::UpdateSerialState(unsigned char portVal)
{
static unsigned char prevVal = 0xC0;
//if ((prevVal ^ portVal)&8)
// tap->SetTapeMotor(CycleCounter, portVal&8);
prevVal = portVal;
}
void TED::Write(unsigned int addr, unsigned char value)
{
switch (addr&0xF000) {
case 0x0000:
switch ( addr & 0xFFFF ) {
case 0:
//fprintf(stderr,"$00 write: %02X\n", value);
prddr = value & 0xDF;
UpdateSerialState(prddr&~prp);
return;
case 1:
//fprintf(stderr,"$01 write: %02X\n", value);
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:
if (sidCard) {
sidCard->write(addr & 0x1f, value);
sidCard->setFrequency(1);
}
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 :
Ram[0xFF06]=value;
// get vertical offset of screen when smooth scroll
vshift = value&0x07;
// Check if screen is turned on
if (value&0x10 && beamy == 0 && !AttribFetch) {
AttribFetch = true;
VertSubCount = 7;
if (vshift != (beamy&7)) {
// FIXME: this is actually delayed by one cycle
if (beamx>4 && beamx<84)
ClockingState = TSS;
}
}
if ( (Ram[0xFF06] ^ value) & 0x1F) {
if (AttribFetch) {
if (vshift == (beamy&7)) {
BadLine |= 1;
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( DMAbuf, clrbuf, oldcount);
memset( DMAbuf+oldcount, idleread, invalidcount);
memcpy( DMAbuf+oldcount+invalidcount, VideoBase + CharacterCount + oldcount
+ invalidcount, newdmacount);
//DoDMA(tmpClrbuf + delay, 0);
ClockingState = THALT1;
} else if (beamx<111 && beamx>=89) {
// FIXME this breaks on FF1E writes
// swap DMA fetch pointers for colour DMA...
if (!(BadLine & 1) && beamx < 94) {
unsigned char *tmpbuf = clrbuf;
clrbuf = tmpClrbuf;
tmpClrbuf = tmpbuf;
}
//BadLine |= 1;
}
} else if (BadLine & 1 /*beamx>=91*/) {
if (beamx>=94) {
BadLine &= ~1;
} else if (beamx >= 91) {
unsigned char *tmpbuf = clrbuf;
clrbuf = tmpClrbuf;
tmpClrbuf = tmpbuf;
BadLine &= ~1;
}
}
}
}
// Delayed DMA?
// check for flat screen (23 rows)
fltscr = !(value&0x08);
// check for extended mode
ecmode=value&EXTCOLOR;
if (ecmode) {
tmp=(0xF800)&RAMMask;
scrattr|=EXTCOLOR;
} else {
tmp=(0xFC00)&RAMMask;
scrattr&=~EXTCOLOR;
}
charrambank=Ram+((Ram[0xFF13]<<8)&tmp);
charrombank=&(RomHi[0][((Ram[0xFF13] & 0x3C)<<8)&tmp]);
// check for graphics mode (5th b14it)
scrattr=(scrattr&~GRAPHMODE)|(value&GRAPHMODE);
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;
if (rvsmode) {
tmp=(0xF800)&RAMMask;
scrattr|=REVERSE;
} else {
tmp=(0xFC00)&RAMMask;
scrattr&=~REVERSE;
}
charrombank=&(RomHi[0][((Ram[0xFF13] & 0x3C)<<8)&tmp]);
charrambank=Ram+((Ram[0xFF13]<<8)&tmp);
cset = charrom ? charrombank : charrambank;
// check for multicolor mode
scrattr=(scrattr&~MULTICOLOR)|(value&0x10);
return;
case 0xFF08 :
Ram[0xFF08] = value;
return;
case 0xFF09 :
// clear the interrupt requester bits
// by writing 1 into them (!!)
Ram[0xFF09]=(Ram[0xFF09]&0x7F)&(~value);
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[0xFF0A]&0x02 ? Ram[0xFF09]|=0x82 : Ram[0xFF09]|=0x02;
irqline = newirqline;
}
}
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;
irqline = newirqline;
}
}
return;
case 0xFF0C :
crsrpos=((value<<8)|(crsrpos&0xFF))&0x3FF;
return;
case 0xFF0D :
crsrpos=value|(crsrpos&0xFF00);
return;
case 0xFF0E :
writeSoundReg(0, value);
Ram[0xFF0E]=value;
return;
case 0xFF0F :
writeSoundReg(1, value);
Ram[0xFF0F]=value;
return;
case 0xFF10 :
writeSoundReg(2, value & 3);
Ram[0xFF10]=value;
return;
case 0xFF11 :
Ram[0xFF11]=value;
writeSoundReg(3, value);
return;
case 0xFF12:
grbank=Ram+((value&0x38)<<10);
if ((value ^ Ram[0xFF12]) & 3)
writeSoundReg(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
fastmode = !(value&0x02);
(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=colorbank=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 :
beamy=((value&0x01)<<8)|(beamy&0xFF);
return;
case 0xFF1D :
beamy=(beamy&0x0100)|value;
return;
case 0xFF1E :
{
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;
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;
crsrphase=(value&0x78)>>3;
return;
case 0xFF3E :
Ram[0xFF13]|=0x01;
RAMenable=false;
ChangeMemBankSetup(RAMenable);
return;
case 0xFF3F :
Ram[0xFF13]&=0xFE;
RAMenable=true;
ChangeMemBankSetup(RAMenable);
return;
}
actram[addr&RAMMask] = value;
return;
case 0xFE:
//tcbmbus->Write(addr,value);
return;
case 0xFD:
switch (addr>>4) {
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:
if (sidCard) {
sidCard->setFrequency(0);
sidCard->write(addr & 0x1f, value);
}
return;
case 0xFDD:
actromlo=&(RomLo[addr&0x03][0]);
actromhi=&(RomHi[(addr&0x0c)>>2][0]);
return;
}
return;
default:
actram[addr&RAMMask] = value;
return;
}
}
return;
}
void TED::dump(void *img)
{
// this is ugly :-P
fwrite(Ram,RAMSIZE,1,(FILE *) img);
fwrite(&RAMenable,sizeof(RAMenable),1,(FILE *) img);
fwrite(&t1start,sizeof(t1start),1, (FILE *) img);
fwrite(&t1on,sizeof(t1on),1, (FILE *) img);
fwrite(&t2on,sizeof(t2on),1, (FILE *) img);
fwrite(&t3on,sizeof(t3on),1, (FILE *) img);
fwrite(&timer1,sizeof(timer1),1, (FILE *) img);
fwrite(&timer2,sizeof(timer2),1, (FILE *) img);
fwrite(&timer3,sizeof(timer3),1, (FILE *) img);
fwrite(&beamx,sizeof(beamx),1, (FILE *) img);
fwrite(&beamy,sizeof(beamy),1, (FILE *) img);
//fwrite(&x,sizeof(x),1, (FILE *) img);
fwrite(&irqline,sizeof(irqline),1, (FILE *) img);
fwrite(&crsrpos,sizeof(crsrpos),1, (FILE *) img);
fwrite(&scrattr,sizeof(scrattr),1, (FILE *) img);
fwrite(&tmp,sizeof(tmp),1, (FILE *) img);
fwrite(&nrwscr,sizeof(nrwscr),1, (FILE *) img);
fwrite(&hshift,sizeof(hshift),1, (FILE *) img);
fwrite(&vshift,sizeof(vshift),1, (FILE *) img);
fwrite(&fltscr,sizeof(fltscr),1, (FILE *) img);
fwrite(&mcol,sizeof(mcol),1, (FILE *) img);
fwrite(chrbuf,40,1, (FILE *) img);
fwrite(clrbuf,40,1, (FILE *) img);
fwrite(&charrom,sizeof(charrom),1, (FILE *) img);
fwrite(&charbank,sizeof(charbank),1, (FILE *) img);
// fwrite(&TEDfreq1,sizeof(TEDfreq1),1, (FILE *) img);
// fwrite(&TEDfreq2,sizeof(TEDfreq2),1, (FILE *) img);
// fwrite(&TEDVolume,sizeof(TEDVolume),1, (FILE *) img);
// fwrite(&TEDDA,sizeof(TEDDA),1, (FILE *) img);
fwrite(&framecol,sizeof(framecol),1, (FILE *) img);
}
void TED::memin(void *img)
{
// this is ugly :-P
fread(Ram,RAMSIZE,1,(FILE *) img);
fread(&RAMenable,sizeof(RAMenable),1,(FILE *) img);
fread(&t1start,sizeof(t1start),1, (FILE *) img);
fread(&t1on,sizeof(t1on),1, (FILE *) img);
fread(&t2on,sizeof(t2on),1, (FILE *) img);
fread(&t3on,sizeof(t3on),1, (FILE *) img);
fread(&timer1,sizeof(timer1),1, (FILE *) img);
fread(&timer2,sizeof(timer2),1, (FILE *) img);
fread(&timer3,sizeof(timer3),1, (FILE *) img);
fread(&beamx,sizeof(beamx),1, (FILE *) img);
fread(&beamy,sizeof(beamy),1, (FILE *) img);
//fread(&x,sizeof(x),1, (FILE *) img);
fread(&irqline,sizeof(irqline),1, (FILE *) img);
fread(&crsrpos,sizeof(crsrpos),1, (FILE *) img);
fread(&scrattr,sizeof(scrattr),1, (FILE *) img);
fread(&tmp,sizeof(tmp),1, (FILE *) img);
fread(&nrwscr,sizeof(nrwscr),1, (FILE *) img);
fread(&hshift,sizeof(hshift),1, (FILE *) img);
fread(&vshift,sizeof(vshift),1, (FILE *) img);
fread(&fltscr,sizeof(fltscr),1, (FILE *) img);
fread(&mcol,sizeof(mcol),1, (FILE *) img);
fread(chrbuf,40,1, (FILE *) img);
fread(clrbuf,40,1, (FILE *) img);
fread(&charrom,sizeof(charrom),1, (FILE *) img);
fread(&charbank,sizeof(charbank),1, (FILE *) img);
// fread(&TEDfreq1,sizeof(TEDfreq1),1, (FILE *) img);
// fread(&TEDfreq2,sizeof(TEDfreq2),1, (FILE *) img);
// fread(&TEDVolume,sizeof(TEDVolume),1, (FILE *) img);
// fread(&TEDDA,sizeof(TEDDA),1, (FILE *) img);
fread(&framecol,sizeof(framecol),1, (FILE *) img);
for (int i=0; i<5; i++)
writeSoundReg(i, Ram[0xFF0E + i]);
beamy=0;
beamx=0;
scrptr=&(screen[0]);
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
mask = cset[((chr&0x7F)<<3)|VertSubCount];
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
mask = cset[(chr<<3)|VertSubCount];
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];
chr = chrbuf[x];
mask = cset[ ((chr&0x3F)<<3)|VertSubCount ];
chr = (chr&0xC0)>>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;
mask = cset[ (chr<<3)|VertSubCount ];
if (charcol&0x08) { // if character is multicolored
mcol[3]=charcol&0xF7;
wbuffer[0] = wbuffer[1] = mcol[ (mask & 0xC0) >> 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;
mask = cset[ (chr<<3)|VertSubCount ];
if ((charcol)&0x08) { // if character is multicolored
mcol[3]=(charcol)&0xF7;
wbuffer[0] = wbuffer[1] = mcol[ (mask & 0xC0) >> 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]&0xF0)>>4)|((clrbuf[x]&0x07)<<4);
mask = grbank[ (((CharacterPosition+x)<<3)&0x1FFF)|VertSubCount ];
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]&0xF0)>>4)|((clrbuf[x]&0x07)<<4);
bmmcol[2]=(chrbuf[x]&0x0F)|(clrbuf[x]&0x70);
mask = grbank[ (((CharacterPosition+x)<<3)&0x1FFF)+VertSubCount ];
wbuffer[0]= wbuffer[1] = bmmcol[ (mask & 0xC0) >> 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(cpuptr->getPC());
}
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];
}
inline void TED::DoDMA( unsigned char *Buf, unsigned int Offset )
{
if ( CharacterCount>=0x03D8 ) {
memcpy( Buf, VideoBase + CharacterCount + Offset, 0x400 - CharacterCount);
memcpy( Buf + 0x400 - CharacterCount, VideoBase + Offset, (CharacterCount + 40)&0x03FF);
} else {
memcpy( Buf, VideoBase + CharacterCount + Offset, 40);
}
}
// main loop of the whole emulation as the TED feeds the CPU with clock cycles
void TED::ted_process(short *buffer, unsigned int count)
{
do {
switch(++beamx) {
default:
break;
case 2:
if (VertSubActive)
VertSubCount = (VertSubCount+1)&7;
break;
case 3:
if (AttribFetch && beamy==0) {
VertSubCount = 7;
}
break;
case 4:
if (AttribFetch) {
BadLine |= ((vshift)&7) == (beamy&7);
if ( BadLine ) {
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)
DoDMA(tmpClrbuf, 0);
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 && VertSubCount == 7 )
CharacterPositionReload = (CharacterPosition + 40)&0x3FF;
break;
case 91:
ClockingState = TRFSH;
break;
case 94:
if (ScreenOn && !nrwscr)
SideBorderFlipFlop = CharacterWindow = false;
break;
case 96:
if (ScreenOn && 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 111:
if ((BadLine&1)) {
BadLine = 2;
VertSubActive = true;
} else if (BadLine&2) {// in the second bad line, we're finished...
BadLine &= ~2;
}
break;
case 102:
ClockingState = fastmode ? TDS : TSS;
break;
case 104:
HBlanking = true;
break;
case 107: // HSYNC start
break;
case 256:
case 128:
beamx = 15;
break;
case 114: // HSYNC end
beamx=0;
// the beam reached a new line
TVScanLineCounter += 1;
if ( TVScanLineCounter >= 340 ) {
TVScanLineCounter = 0;
scrptr=screen;
// render_ok=true;
}
switch (++beamy) {
case 4:
if (!fltscr) ScreenOn = AttribFetch;
break;
case 8:
if (fltscr) ScreenOn = AttribFetch;
break;
case 200:
if (fltscr) ScreenOn = false;
break;
case 204:
if (!fltscr) ScreenOn = false;
break;
case 205:
CharacterCount = 0;
// cursor phase counter in TED register $1F
VertSubActive = false;
if ((++crsrphase&0x0F) == 0x0F)
crsrblinkon ^= 1;
break;
case 251:
VBlanking = true;
break;
case 261: // Vertical retrace
// frame ready...
//render_ok=true;
// reset screen pointer ("TV" electron beam)
scrptr=screen;
TVScanLineCounter = 0;
break;
case 271:
VBlanking = false;
break;
case 512:
case 312:
beamy = 0;
CharacterPosition = CharacterPositionReload = 0;
AttribFetch = (Ram[0xFF06]&0x10) != 0;
}
// is there raster interrupt?
if (beamy == irqline)
Ram[0xFF09] |= Ram[0xFF0A]&0x02 ? 0x82 : 0x02;
}
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
}
if (t3on && !((timer3--)&0xFFFF)) {// Timer3 permitted
timer3=0xFFFF;
Ram[0xFF09] |= Ram[0xFF0A]&0x40 ? 0xC0 : 0x40; // interrupt
}
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 {
if (t1on && !timer1--) { // Timer1 permitted decreased and zero
timer1=(t1start-1)&0xFFFF;
Ram[0xFF09] |= Ram[0xFF0A]&0x08 ? 0x88 : 0x08; // interrupt
}
if (!(HBlanking |VBlanking)) {
if (SideBorderFlipFlop) { // drawing the visible part of the screen
// 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;
}
x = (x + 1) & 0x3F;
}
if (!CharacterWindow) {
// we are on the border area, so use the frame color
*((int*)scrptr) = framecol;
}
}
CycleCounter++;
if (!(CycleCounter & 0x03) && buffer && int(count) > 0) {
static unsigned int remainder = 0;
unsigned int samples = (playbackSpeed + remainder) / 4;
if (samples) {
renderSound(samples, buffer);
if (sidCard) {
short imBuf[128];
sidCard->calcSamples(imBuf, samples);
unsigned int i = samples - 1;
do {
buffer[i] += (short)(int(imBuf[i]) * int(masterVolume) / 10);
}while (i--);
}
storeToBuffer(buffer, samples);
}
remainder = (playbackSpeed + remainder) % 4;
count -= samples;
buffer += samples;
}
}
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 THALT1|1:
case THALT2|1:
case THALT3|1:
cpuptr->stopcycle();
ClockingState<<=1;
break;
}
} while (int(count) > 0 && buffer);
render_ok = false;
}
void TED::enableSidCard(bool enable)
{
if (enable) {
if (sidCard)
return;
sidCard = new SIDsound(SID8580);
sidCard->setSampleRate(TED_SOUND_CLOCK);
} else {
if (!sidCard)
return;
delete sidCard;
sidCard = 0;
}
}
TED::~TED()
{
if (filter)
delete filter;
}
//--------------------------------------------------------------