| #include <stdio.h> |
| #include <memory.h> |
| #include "vic2mem.h" |
| #include "c64rom.h" |
| #include "Sid.h" |
| #include "Clockable.h" |
| #include "video.h" |
| #include "keys64.h" |
| #include "sound.h" |
| #include "tape.h" |
| |
| #define FAST_BOOT 1 |
| #define VRETRACE_LINE 265 |
| #define BEAMY2RASTER(X) (X < VRETRACE_LINE ? X + (312 - VRETRACE_LINE) : X - VRETRACE_LINE) |
| #define RASTER2BEAMY(X) (X < (312 - VRETRACE_LINE) ? VRETRACE_LINE + X : X - (312 - VRETRACE_LINE)) |
| |
| #define SET_BITS(REG, VAL) { \ |
| unsigned int i = 7; \ |
| do { \ |
| REG = ((VAL) & (1 << i)) == (1 << i); \ |
| } while(i--); \ |
| } |
| |
| static unsigned char cycleLookup[][128] = { |
| // SCREEN: |===========0102030405060708091011121314151617181920212223242526272829303132333435363738391111========= |
| // coordinate: 111111111111111111111111111111 |
| //0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000011111111112222222222 |
| //0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 |
| // beamX: |
| //11111111111111111111111111 |
| //000000000011111111112222220000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999 |
| //012345678901234567890123450123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 |
| // NO SPRITES NO BADLINE |
| {"3 i 4 i 5 i 6 i 7 i r r r r r g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g g i i 0 i 1 i 2 i "}, |
| // no sprites, bad line |
| {"3 i 4 i 5 i 6 i 7 i r r*r*r*rcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcg i i 0 i 1 i 2 i "} |
| }; |
| |
| unsigned int Vic2mem::CIA::refCount = 0; |
| |
| Vic2mem::Vic2mem() |
| { |
| enableSidCard(true, 0); |
| sidCard->setFrequency(1); |
| sidCard->setModel(SID6581R1); |
| actram = Ram; |
| loadroms(); |
| chrbuf = DMAbuf; |
| clrbuf = DMAbuf + 64; |
| tmpClrbuf = DMAbuf + 128; |
| // setting screen memory pointer |
| scrptr = screen; |
| // pointer of the end of the screen memory |
| endptr = scrptr + VIC_PIXELS_PER_ROW; |
| framecol = 0; |
| crsrblinkon = false; |
| vicBase = Ram; |
| charrombank = charRomC64; |
| charrom = false; |
| tap = new TAP; |
| keys64 = new KEYS64; |
| Reset(true); |
| } |
| |
| Vic2mem::~Vic2mem() |
| { |
| delete keys64; |
| delete tap; |
| } |
| |
| void Vic2mem::Reset(bool clearmem) |
| { |
| if (clearmem) { |
| for (int i=0;i<RAMSIZE;Ram[i] = (i>>1)<<1==i ? 0 : 0xFF, i++); |
| loadroms(); |
| } |
| soundReset(); |
| cia[0].reset(); |
| cia[1].reset(); |
| vicReg[0x19] = 0; |
| prp = 7; |
| prddr = 0; |
| } |
| |
| void Vic2mem::loadroms() |
| { |
| memcpy(RomLo[0], basicRomC64, basicRomC64_size); |
| memcpy(RomHi[0], kernalRomC64, kernalRomC64_size); |
| mem_8000_bfff = RomLo[0]; |
| mem_c000_ffff = RomHi[0]; |
| #if FAST_BOOT |
| //if (memcmp()) // TODO |
| unsigned char patch[] = { 0xA0, 0xA0, 0xA2, 0x00, 0x84, 0xC1, 0x86, 0xC2 }; |
| memset(mem_c000_ffff + 0x1D68, 0xEA, 0x24); |
| memcpy(mem_c000_ffff + 0x1D68, patch, sizeof(patch)); |
| #endif |
| } |
| |
| void Vic2mem::copyToKbBuffer(const char *text, unsigned int length) |
| { |
| if (!length) |
| length = strlen(text); |
| Write(0xc6, length); |
| while (length--) |
| Write(0x0277 + length, text[length]); |
| } |
| |
| Color Vic2mem::getColor(unsigned int ix) |
| { |
| const double bsat = 45.0; |
| Color color[16] = { |
| { 0, 0, 0 }, { 0, 5.0, 0 }, { 112.5, 2.9375, bsat }, { 292.5, 3.875, bsat }, |
| { 45, 3.125, bsat }, { 225, 3.5, bsat }, { 0, 2.75, bsat }, { 180, 4.25, bsat}, |
| { 135, 3.125, bsat }, { 157.5, 2.75, bsat }, { 112.5, 3.5, bsat }, { 0, 2.9375, 0 }, |
| { 0, 3.41, 0 }, { 225, 4.25, bsat }, { 0, 3.41, bsat }, { 0, 3.875, 0 } |
| }; |
| return color[ix & 0xF]; |
| } |
| |
| void Vic2mem::soundReset() |
| { |
| sidCard->reset(); |
| } |
| |
| void Vic2mem::CIA::reset() |
| { |
| pra = prb = 0; |
| ddra = ddrb = 0; |
| icr = 0; |
| irq_mask = 0; |
| ta = tb = latcha = latchb = 0; |
| cra = crb = 0; |
| // ToD |
| todCount = 60 * 60 * 50; // set to 1hr at reset |
| alarmCount = -1; |
| tod.latched = false; |
| tod.halt = 1; |
| todIn = 60; |
| tod.ampm = 0; |
| } |
| |
| void Vic2mem::CIA::setIRQflag(unsigned int mask) |
| { |
| if (mask & 0x1F) { |
| icr |= 0x80; |
| } else { |
| icr &= 0x7F; |
| } |
| } |
| |
| unsigned int Vic2mem::CIA::bcd2hex(unsigned int bcd) |
| { |
| return (((bcd & 0xf0) >> 4) * 10) + (bcd & 0xf); |
| } |
| |
| unsigned int Vic2mem::CIA::hex2bcd(unsigned int hex) |
| { |
| return ((hex / 10) << 4) + (hex % 10); |
| } |
| |
| // called after each new frame |
| void Vic2mem::CIA::todUpdate() |
| { |
| if (!tod.halt) { |
| todCount += 1; |
| if (alarmCount == todCount) { |
| // set alarm IRQ |
| icr |= 4; |
| setIRQflag(irq_mask & icr); |
| } |
| if (todCount == 12 * 60 * 60 * 50) {// 12 AM/PM |
| tod.ampm ^= 0x80; |
| todCount = 0; |
| } |
| #if 0 |
| TOD time; |
| frames2tod(todCount, time, todIn); |
| // if (!(todCount % 2000)) |
| fprintf(stderr, "Count:%09u Time: %02xh:%02Xm:%02Xs:%02Xths.\n", todCount, time.hr, time.min, time.sec, time.tenths); |
| #endif |
| } |
| } |
| |
| unsigned int Vic2mem::CIA::tod2frames(TOD &todin) |
| { |
| unsigned int newmsec = |
| bcd2hex(todin.hr) * 180000 + |
| bcd2hex(todin.min) * 3000 + |
| bcd2hex(todin.sec) * 50 + |
| bcd2hex(todin.tenths) * 5; |
| return newmsec; |
| } |
| |
| void Vic2mem::CIA::frames2tod(unsigned int frames, TOD &todout, unsigned int frq) |
| { |
| unsigned int hours = frames * frq / 180000 / 50; |
| frames = frames - hours * 180000; |
| unsigned int minutes = frames / 3000; |
| frames = frames - minutes * 3000; |
| unsigned int seconds = frames / 50; |
| frames = frames - seconds * 50; |
| unsigned int tenths = frames / 5; |
| |
| todout.hr = hex2bcd(hours); |
| todout.min = hex2bcd(minutes); |
| todout.sec = hex2bcd(seconds); |
| todout.tenths = hex2bcd(tenths); |
| } |
| |
| void Vic2mem::CIA::write(unsigned int addr, unsigned char value) |
| { |
| //fprintf(stderr, "$(%04X) CIA%i write : %02X @ PC=%04X\n", addr, refCount, value, theTed->cpuptr->getPC()); |
| addr &= 0xF; |
| switch (addr) { |
| case 0x00: |
| pra = value; |
| break; |
| |
| case 0x01: |
| prb = value; |
| break; |
| |
| case 0x02: |
| ddra = value; |
| break; |
| |
| case 0x03: |
| ddrb = value; |
| break; |
| |
| case 0x04: |
| latcha = (latcha & 0xFF00) | value; |
| break; |
| |
| case 0x05: |
| latcha = (latcha & 0xFF) | (value << 8); |
| // Reload timer A if stopped |
| if (!(cra & 1)) |
| ta = latcha; |
| break; |
| |
| case 0x06: |
| latchb = (latchb & 0xFF00) | value; |
| break; |
| |
| case 0x07: |
| latchb = (latchb & 0xFF) | (value << 8); |
| // Reload timer B if stopped |
| if (!(crb & 1)) |
| tb = latchb; |
| break; |
| |
| case 0x08: |
| if (crb & 0x80) { |
| frames2tod(alarmCount, alm, todIn); |
| alm.tenths = value & 0x0F; |
| alarmCount = tod2frames(alm); |
| } else { |
| frames2tod(todCount, tod, todIn); |
| tod.tenths = value & 0x0F; |
| todCount = tod2frames(tod); |
| } |
| tod.halt = false; |
| break; |
| |
| case 0x09: |
| if (crb & 0x80) { |
| frames2tod(alarmCount, alm, todIn); |
| alm.sec = value & 0x7F; |
| alarmCount = tod2frames(alm); |
| } else { |
| frames2tod(todCount, tod, todIn); |
| tod.sec = value & 0x7F; |
| todCount = tod2frames(tod); |
| } |
| break; |
| |
| case 0x0A: |
| if (crb & 0x80) { |
| frames2tod(alarmCount, alm, todIn); |
| alm.min = value & 0x7F; |
| alarmCount = tod2frames(alm); |
| } else { |
| frames2tod(todCount, tod, todIn); |
| tod.min = value & 0x7F; |
| todCount = tod2frames(tod); |
| } |
| break; |
| |
| case 0x0B: |
| if (crb & 0x80) { |
| frames2tod(alarmCount, alm, todIn); |
| alm.hr = value & 0x9F; |
| alarmCount = tod2frames(alm); |
| } else { |
| frames2tod(todCount, tod, todIn); |
| tod.hr = value & 0x9F; |
| todCount = tod2frames(tod); |
| } |
| tod.halt = true; |
| break; |
| |
| case 0x0C: |
| sdr = value; |
| sdrShiftCnt = 8; |
| break; |
| |
| case 0x0D: |
| if (value & 0x80) |
| irq_mask |= value & 0x9F; |
| else |
| irq_mask &= ~value; |
| setIRQflag(icr & irq_mask); |
| break; |
| |
| case 0x0E: |
| cra = value & 0xEF; |
| // ToD clock rate |
| todIn = value & 0x80 ? 50 : 60; |
| if (value & 0x10) // Forced reload |
| ta = latcha; |
| break; |
| |
| case 0x0F: |
| crb = value & 0xEF; |
| if (value & 0x10) // Forced reload |
| tb = latchb; |
| break; |
| } |
| reg[addr] = value; |
| } |
| |
| unsigned char Vic2mem::CIA::read(unsigned int addr) |
| { |
| addr &= 0x0F; |
| switch (addr) { |
| case 0x00: |
| //return (pra & ddra) | ( 0xff & ~ddra); |
| return pra | ~ddra; |
| case 0x01: |
| { |
| unsigned char retval; |
| retval = (prb & ddrb) |
| | (0xff & ~ddrb); |
| return retval; |
| } |
| case 0x02: |
| return ddra; |
| case 0x03: |
| return ddrb; |
| case 0x04: |
| return ta & 0xFF; |
| case 0x05: |
| return ta >> 8; |
| case 0x06: |
| return tb & 0xFF; |
| case 0x07: |
| return tb >> 8; |
| case 0x08: |
| if (tod.latched) { |
| tod.latched = false; |
| return todLatch.sec; |
| } else { |
| frames2tod(todCount, tod, todIn); |
| return tod.tenths; |
| } |
| case 0x09: |
| if (tod.latched) |
| return todLatch.sec; |
| else { |
| frames2tod(todCount, tod, todIn); |
| return tod.sec; |
| } |
| case 0x0A: |
| if (tod.latched) |
| return todLatch.min; |
| else { |
| frames2tod(todCount, tod, todIn); |
| return tod.min; |
| } |
| case 0x0B: |
| frames2tod(todCount, tod, todIn); |
| tod.latched = true; |
| todLatch = tod; |
| return todLatch.hr | tod.ampm; |
| case 0x0C: |
| return sdr; |
| case 0x0D: |
| { |
| unsigned char retval = icr; |
| icr = 0; |
| //setIRQflag(0); |
| return retval & 0x9F; |
| } |
| case 0x0E: |
| return cra; |
| case 0x0F: |
| return crb; |
| } |
| return reg[addr]; |
| } |
| |
| void Vic2mem::CIA::countTimers() |
| { |
| if ((cra & 0x40) && sdrShiftCnt) { |
| sdrShiftCnt -= 1; |
| if (!sdrShiftCnt) { |
| icr |= 8; |
| setIRQflag(icr & irq_mask); |
| } |
| } |
| if ((cra & 0x20) == 0x00 && cra & 1 ) { |
| if (!ta--) { |
| icr |= 0x01; // Set timer A IRQ flag |
| setIRQflag(icr & irq_mask); // FIXME, 1 cycle delay |
| prbTimerToggle ^= 0x40; // PRA7 underflow count toggle |
| // timer A output to PB6? |
| if (cra & 2) { |
| // set PRA6 high for one clock |
| if (cra & 4) { |
| prbTimerOut ^= 0x40; // toggle PRA6 between 1 and 0 |
| } else { |
| prbTimerOut |= 0x40; // set high for one clock |
| } |
| } |
| if (cra & 8) // One-shot? |
| cra &= 0xFE; // Stop timer |
| // Reload from latch |
| ta = latcha; |
| } |
| } |
| if ((crb & 0x20) == 0x00 && crb & 1) { |
| if (!tb--) { |
| icr |= 0x02; // Set timer B IRQ flag |
| setIRQflag(icr & irq_mask); // FIXME, 1 cycle delay |
| prbTimerToggle ^= 0x80; // PRB7 underflow count toggle |
| // timer A output to PRB6? |
| if (crb & 2) { |
| // set PRB7 high for one clock |
| if (crb & 4) { |
| prbTimerOut ^= 0x80; // toggle PRB7 between 1 and 0 |
| } else { |
| prbTimerOut |= 0x80; // set high for one clock |
| } |
| } |
| if (crb & 8) // One-shot? |
| crb &= 0xFE; // Stop timer |
| // Reload from latch |
| tb = latchb; |
| } |
| } |
| } |
| |
| void Vic2mem::changeCharsetBank() |
| { |
| const unsigned int vicBank = (((cia[1].pra | ~cia[1].ddra) ^ 0xFF) & 3) << 14; |
| vicBase = Ram + vicBank; |
| // video matrix base address |
| const unsigned int vmOffset = ((vicReg[0x18] & 0xF0) << 6); |
| VideoBase = vicBase + vmOffset; |
| // Sprite data addresses |
| unsigned int i = 7; |
| do { |
| mob[i].address = VideoBase + 0x3F8 + i; |
| } while (i--); |
| // character bitmap data |
| const unsigned int cSetOffset = ((vicReg[0x18] & 0x0E) << 10); |
| charrambank = vicBase + cSetOffset; |
| |
| cset = (!(vicBank & 0x4000) && ((cSetOffset & 0x3000) == 0x1000)) // 4 or 6 |
| ? charrombank + (cSetOffset & 0x0800) : charrambank; |
| grbank = vicBase + ((vicReg[0x18] & 8) << 10); |
| #if 0 |
| fprintf(stderr, "VIC bank: %04X, matrix:%04X(%u) in line:%03i pra:%02X ddra:%02X vic18:%02X\n", |
| vicBank, cSetOffset, cset != charrambank, beamy, cia[1].pra, cia[1].ddra, vicReg[0x18]); |
| #endif |
| } |
| |
| void Vic2mem::checkIRQflag() |
| { |
| irqFlag = (cia[0].icr | vicReg[0x19]) & 0x80; |
| } |
| |
| void Vic2mem::doDelayedDMA() |
| { |
| if (attribFetch) { |
| if (vshift == (beamy & 7)) { |
| BadLine = 1; |
| // Delayed DMA? |
| if (beamx >=2 && beamx < 82) { |
| unsigned char idleread = Read((cpuptr->getPC()+1) & 0xFFFF); |
| unsigned int delay = (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, chrbuf, oldcount); |
| memset(tmpClrbuf + oldcount, idleread, invalidcount); |
| memcpy(tmpClrbuf + oldcount + invalidcount, VideoBase + CharacterCount + oldcount |
| + invalidcount, newdmacount); |
| BadLine = 1; |
| delayedDMA = true; |
| } else if (BadLine) { |
| BadLine = 0; |
| } |
| } |
| } |
| } |
| |
| // read memory through memory decoder |
| unsigned char Vic2mem::Read(unsigned int addr) |
| { |
| switch (addr & 0xF000) { |
| case 0x0000: |
| switch (addr & 0xFFFF) { |
| case 0: |
| return prddr; |
| case 1: |
| return prp | ~prddr; // (!tap->IsButtonPressed() << 4) |
| default: |
| return actram[addr & 0xFFFF]; |
| } |
| default: |
| return actram[addr & 0xFFFF]; |
| case 0xA000: |
| case 0xB000: |
| return mem_8000_bfff[addr & 0x1FFF]; |
| case 0xE000: |
| case 0xF000: |
| return mem_c000_ffff[addr & 0x1FFF]; |
| case 0xD000: |
| if (!((prp | ~prddr) & 3)) |
| return actram[addr & 0xFFFF]; |
| else if (charrom) { |
| return charRomC64[addr & 0x0FFF]; |
| } else { |
| switch ( addr >> 8 ) { |
| case 0xD0: // VIC2 |
| case 0xD1: |
| case 0xD2: |
| case 0xD3: |
| addr &= 0x3F; |
| switch (addr) { |
| case 0x12: |
| return (BEAMY2RASTER(beamy)) & 0xFF; |
| case 0x11: |
| return (vicReg[0x11] & 0x7f) | (((BEAMY2RASTER(beamy)) & 0x100) >> 1); |
| case 0x13: // LPX |
| return beamx << 1; |
| case 0x16: |
| return vicReg[0x16] | 0xC0; |
| case 0x18: |
| return vicReg[0x18] | 1; |
| case 0x19: |
| return vicReg[0x19] | 0x70; |
| case 0x1A: |
| return vicReg[0x1A] | 0xF0; |
| case 0x20: |
| return framecol | 0xF0; |
| case 0x21: |
| case 0x22: |
| case 0x23: |
| case 0x24: |
| return ecol[(addr & 0x3F) - 0x21] | 0xF0; |
| } |
| return vicReg[addr]; |
| case 0xD4: // SID |
| case 0xD5: |
| case 0xD6: |
| case 0xD7: |
| return sidCard->read(addr & 0x1F); |
| case 0xD8: // Color RAM |
| case 0xD9: |
| case 0xDA: |
| case 0xDB: |
| return colorRAM[addr & 0x03FF]; |
| case 0xDC: // CIA1 |
| { |
| unsigned char retval; |
| switch (addr & 0x0F) { |
| case 0x00: |
| return keys64->getJoyState(1); |
| case 0x01: |
| retval = keys64->feedkey(cia[0].pra | ~cia[0].ddra); |
| //fprintf(stderr, "$Kb(%02X,%02X) read: %02X\n", cia[0].pra, cia[0].ddra, retval); |
| break; |
| case 0x0D: |
| checkIRQflag(); |
| default: |
| retval = cia[0].read(addr); |
| } |
| return retval; |
| } |
| case 0xDD: // CIA2 |
| switch (addr & 0x0F) { |
| case 0: |
| return (readBus() & 0xC0) | (cia[1].read(0) & 0x3F); |
| case 0xD: |
| { |
| unsigned char retval = cia[1].read(0xD); |
| cpuptr->clearNmi(); |
| return retval; |
| } |
| default: |
| ; |
| } |
| return cia[1].read(addr); |
| default: // open address space |
| return beamy ^ beamx;//actram[addr & 0xFFFF]; |
| } |
| } |
| } |
| } |
| |
| void Vic2mem::Write(unsigned int addr, unsigned char value) |
| { |
| switch (addr & 0xF000) { |
| case 0x0000: |
| switch ( addr & 0xFFFF ) { |
| case 0: |
| prddr = value & 0xDF; |
| return; |
| case 1: |
| if ((prp ^ value) & 8) |
| tap->SetTapeMotor(CycleCounter, value & 8); |
| prp = value; |
| mem_8000_bfff = ((prp & 3) == 3) ? RomLo[0] : Ram + 0xa000; // a000..bfff |
| mem_c000_ffff = ((prp & 2) == 2) ? RomHi[0] : Ram + 0xe000; // e000..ffff |
| charrom = (!(prp & 4) && (prp & 3)); |
| return; |
| default: |
| actram[addr & 0xFFFF] = value; |
| } |
| return; |
| default: |
| actram[addr & 0xFFFF] = value; |
| return; |
| case 0xD000: |
| if (!((prp | ~prddr) & 3)) { // should be read(1) |
| actram[addr & 0xFFFF] = value; |
| } else if (!charrom) { |
| //unsigned int i; |
| switch ( addr >> 8 ) { |
| case 0xD0: // VIC2 |
| addr &= 0x3F; |
| switch (addr) { |
| case 0x12: |
| irqline = RASTER2BEAMY(((BEAMY2RASTER(irqline) & 0x100) | value)); |
| if (beamy == irqline) { |
| vicReg[0x19] |= (vicReg[0x1A] & 1) ? 0x81 : 0x01; |
| checkIRQflag(); |
| } |
| break; |
| case 0x11: |
| // raster IRQ line |
| irqline = RASTER2BEAMY(((BEAMY2RASTER(irqline) & 0xFF) |
| | ((value & 0x80) << 1))); |
| if (beamy == irqline) { |
| vicReg[0x19] |= (vicReg[0x1A] & 1) ? 0x81 : 0x01; |
| checkIRQflag(); |
| } |
| // 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)); |
| // Check if screen is turned on |
| if (value & 0x10 && !beamy && !attribFetch) { |
| attribFetch = true; |
| vertSubCount = 7; |
| } else if (attribFetch && ((fltscr && beamy == 8) || (!fltscr && beamy == 4))) { |
| ScreenOn = true; |
| } else if ((beamy == 200 && fltscr) || (beamy == 204 && !fltscr)) { |
| ScreenOn = false; |
| } |
| //doDelayedDMA(); |
| break; |
| case 0x16: |
| // check for narrow screen (38 columns) |
| nrwscr = value & 0x08; |
| // get horizontal offset of screen when smooth scroll |
| hshift = value & 0x07; |
| scrattr = (scrattr & ~(MULTICOLOR)) | (value & (MULTICOLOR)); |
| break; |
| case 0x18: |
| vicReg[0x18] = value; |
| changeCharsetBank(); |
| break; |
| case 0x19: |
| vicReg[0x19] &= (0x0F & ~value); |
| // check if we have a pending IRQ |
| if ((vicReg[0x1a]) & 0x0F & vicReg[0x19]) |
| vicReg[0x19] |= 0x80; |
| else |
| vicReg[0x19] &= 0x7F; |
| checkIRQflag(); |
| return; |
| case 0x1a: |
| // check if we have a pending IRQ |
| if ((vicReg[0x19]) & 0x0F & value) |
| vicReg[0x19] |= 0x80; |
| else |
| vicReg[0x19] &= 0x7F; |
| checkIRQflag(); |
| break; |
| case 0x20: |
| value &= 0x0F; |
| framecol=(value<<24)|(value<<16)|(value<<8)|value; |
| break; |
| case 0x21: |
| ecol[0]=bmmcol[0]=mcol[0]=value&0x0F; |
| break; |
| case 0x22: |
| ecol[1]=bmmcol[3]=mcol[1]=value&0x0F; |
| break; |
| case 0x23: |
| ecol[2]=mcol[2]=value&0x0F; |
| break; |
| case 0x24: |
| ecol[3]=value&0x0F; |
| break; |
| // sprites |
| case 0x00: |
| case 0x02: |
| case 0x04: |
| case 0x06: |
| case 0x08: |
| case 0x0A: |
| case 0x0C: |
| case 0x0E: |
| mob[addr >> 1].x = value; |
| break; |
| case 0x01: |
| case 0x03: |
| case 0x05: |
| case 0x07: |
| case 0x09: |
| case 0x0B: |
| case 0x0D: |
| case 0x0F: |
| mob[addr >> 1].y = (mob[addr >> 1].y & 0x100) | value; |
| break; |
| case 0x10: |
| { |
| unsigned int ix = addr >> 1; |
| unsigned int i = 7; |
| do { |
| mob[ix].y = (mob[ix].y & 0xFF) | ((value << (i - 6)) & 0x100); |
| } while (i--); |
| } |
| break; |
| case 0x15: |
| SET_BITS(mob[i].enabled, value); |
| break; |
| case 0x1B: |
| SET_BITS(mob[i].priority, value); |
| break; |
| case 0x1C: |
| SET_BITS(mob[i].multicolor, value); |
| break; |
| case 0x1D: |
| SET_BITS(mob[i].expandX, value); |
| break; |
| case 0x25: |
| case 0x26: |
| mobExtCol[addr - 0x25] = value & 0x0F; |
| break; |
| case 0x27: |
| case 0x28: |
| case 0x29: |
| case 0x2A: |
| case 0x2B: |
| case 0x2C: |
| case 0x2D: |
| case 0x2E: |
| mob[addr - 0x27].color = value & 0x0F; |
| break; |
| } |
| vicReg[addr] = value; |
| return; |
| case 0xD4: // SID |
| case 0xD5: |
| case 0xD6: |
| case 0xD7: |
| sidCard->write(addr & 0x1f, value); |
| return; |
| case 0xD8: // Color RAM |
| case 0xD9: |
| case 0xDA: |
| case 0xDB: |
| colorRAM[addr & 0x03FF] = value; |
| return; |
| case 0xDC: // CIA1 |
| switch (addr & 0x0F) { |
| // key matrix row select |
| case 0: |
| break; |
| } |
| cia[0].write(addr, value); |
| return; |
| case 0xDD: // CIA2 |
| switch (addr & 0x0F) { |
| case 2: |
| case 0: |
| cia[1].write(addr, value); |
| // VIC base |
| changeCharsetBank(); |
| // serial IEC |
| { |
| static unsigned char prevPort = 0x01; |
| if ((prevPort ^ cia[1].pra) & 0x38) { |
| unsigned char port = ~cia[1].pra & 0x38; |
| serialPort[0] = ((port << 2) & 0x80) // DATA OUT -> DATA IN |
| | ((port << 2) & 0x40) // CLK OUT -> CLK IN |
| | ((port << 1) & 0x10); // ATN OUT -> ATN IN (drive) |
| updateSerialDevices(serialPort[0]); |
| prevPort = cia[1].pra; |
| #if LOG_SERIAL |
| fprintf(stderr, "$DD00 write : %02X @ PC=%04X\n", value, cpuptr->getPC()); |
| fprintf(stderr, "$DD00 written: %02X.\n", serialPort[0]); |
| #endif |
| } |
| } |
| return; |
| default: |
| break; |
| } |
| cia[1].write(addr, value); |
| return; |
| default: // $DExx/$DFxx open I/O |
| //actram[addr & 0xFFFF] = value; |
| return; |
| } |
| } else { |
| actram[addr & 0xFFFF] = value; |
| } |
| return; |
| } |
| } |
| |
| void Vic2mem::doHRetrace() |
| { |
| // the beam reached a new line |
| TVScanLineCounter += 1; |
| if ( TVScanLineCounter >= 340 ) { |
| doVRetrace(); |
| } |
| scrptr = screen + TVScanLineCounter * VIC_PIXELS_PER_ROW; |
| endptr = scrptr + VIC_PIXELS_PER_ROW; |
| } |
| |
| inline void Vic2mem::newLine() |
| { |
| beamy += 1; |
| ff1d_latch = beamy; |
| switch (beamy) { |
| |
| case 4: |
| if (!fltscr && attribFetch) ScreenOn = true; |
| break; |
| |
| case 8: |
| if (fltscr && attribFetch) ScreenOn = true; |
| break; |
| |
| case 200: |
| if (fltscr) ScreenOn = false; |
| break; |
| |
| case 204: |
| if (!fltscr) ScreenOn = false; |
| break; |
| |
| case 205: |
| CharacterCount = 0; |
| VertSubActive = false; |
| break; |
| |
| case 251: |
| VBlanking = true; |
| break; |
| |
| case VRETRACE_LINE: // Vertical retrace 261 or 265? |
| doVRetrace(); |
| // CIA ToD count @ 50 Hz |
| cia[0].todUpdate(); |
| cia[1].todUpdate(); |
| break; |
| |
| case 271: |
| VBlanking = false; |
| break; |
| |
| case 512: |
| case 312: |
| beamy = 0; |
| CharacterPositionReload = 0; |
| if (!attribFetch) { |
| endOfScreen = true; |
| } |
| attribFetch = (vicReg[0x11] & 0x10) != 0; |
| // skip checking raster IRQ |
| return; |
| } |
| // is there raster interrupt? |
| if (!endOfScreen && beamy == irqline) { |
| vicReg[0x19] |= (vicReg[0x1A] & 1) ? 0x81 : 0x01; |
| checkIRQflag(); |
| } |
| } |
| |
| void Vic2mem::ted_process(const unsigned int continuous) |
| { |
| loop_continuous = continuous; |
| do { |
| beamx += 2; |
| switch(beamx) { |
| |
| default: |
| break; |
| case 100: |
| // the beam reached a new line |
| doHRetrace(); |
| newLine(); |
| flushBuffer(CycleCounter); |
| break; |
| |
| case 102: |
| if (VertSubActive) |
| vertSubCount = (vertSubCount+1)&7; |
| if (endOfScreen) { |
| vertSubCount = 7; |
| // is there raster interrupt? line 0 IRQ is delayed by 0 cycle |
| if (beamy == irqline) { |
| vicReg[0x19] |= (vicReg[0x1A] & 1) ? 0x81 : 0x01; |
| checkIRQflag(); |
| } |
| endOfScreen = false; |
| } |
| break; |
| |
| case 122: |
| HBlanking = false; |
| break; |
| |
| case 124: |
| if (attribFetch) { |
| BadLine = (vshift == (beamy & 7)) & (beamy != 203); |
| if (BadLine) { |
| vertSubCount = 7; |
| } |
| if (beamy == 203) { |
| attribFetch = false; |
| } |
| } |
| break; |
| |
| case 0: |
| case 126: |
| if (VertSubActive) |
| CharacterPosition = CharacterPositionReload; |
| beamx = 0; |
| break; |
| |
| case 2: |
| if (BadLine) { |
| if (!delayedDMA) |
| doDMA(tmpClrbuf, 0); |
| } |
| break; |
| |
| case 6: |
| if (ScreenOn) { |
| SideBorderFlipFlop = true; |
| memset(scrptr, mcol[0], hshift); |
| if (nrwscr) |
| CharacterWindow = true; |
| x = 0; |
| } |
| break; |
| |
| case 8: |
| if (ScreenOn && !nrwscr) { |
| CharacterWindow = true; |
| } |
| break; |
| |
| case 82: |
| if (VertSubActive && vertSubCount == 6) |
| CharacterCount = (CharacterCount + 40) & 0x3FF; |
| break; |
| |
| case 84: |
| if ( VertSubActive && charPosLatchFlag) // FIXME |
| CharacterPositionReload = (CharacterPosition + 40) & 0x3FF; |
| if (!nrwscr) |
| SideBorderFlipFlop = CharacterWindow = false; |
| break; |
| |
| case 86: |
| if (nrwscr) |
| SideBorderFlipFlop = CharacterWindow = false; |
| break; |
| |
| case 98: |
| HBlanking = true; |
| break; |
| |
| case 94: // $BC (376) |
| charPosLatchFlag = vertSubCount == 6; |
| break; |
| |
| case 96: |
| if (BadLine) { |
| VertSubActive = true; |
| BadLine = 0; |
| // swap DMA pointers |
| unsigned char *tmpbuf = chrbuf; |
| chrbuf = tmpClrbuf; |
| tmpClrbuf = tmpbuf; |
| } |
| break; |
| |
| case 256: |
| case 128: // overflow |
| beamx = 0; |
| break; |
| } |
| // drawing the visible part of the screen |
| if (!(HBlanking |VBlanking)) { |
| if (SideBorderFlipFlop) { |
| // 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; |
| *((int*)(scrptr + 4)) = framecol; |
| } |
| } |
| unsigned char cycleChr = cycleLookup[BadLine][beamx]; |
| // sprites |
| if (cycleChr >= '0' && cycleChr <= '9') { |
| unsigned int spX = cycleChr - '0'; |
| if (mob[spX].enabled) { |
| mob[spX].address = vicBase ; |
| } |
| } |
| // |
| if (scrptr != endptr) |
| scrptr += 8; |
| else |
| doHRetrace(); |
| // |
| checkIRQflag(); |
| cia[0].countTimers(); |
| cia[1].countTimers(); |
| cycleChr = cycleLookup[BadLine][beamx|1]; |
| switch (cycleChr) { |
| case ' ': |
| cpuptr->process(); |
| break; |
| case '*': |
| cpuptr->stopcycle(); |
| default:; |
| } |
| // |
| CycleCounter += 1; |
| |
| unsigned int i = 0; |
| while (Clockable::itemHeap[i]) { |
| Clockable *c = Clockable::itemHeap[i]; |
| while (c->ClockCount >= VIC_REAL_CLOCK_M10) { |
| c->ClockCount -= VIC_REAL_CLOCK_M10; |
| c->Clock(); |
| } |
| c->ClockCount += c->ClockRate; |
| i++; |
| } |
| } while (loop_continuous); |
| |
| loop_continuous = false; |
| } |
| |
| // renders hires text |
| inline void Vic2mem::hi_text() |
| { |
| unsigned char chr; |
| unsigned char charcol; |
| unsigned char mask; |
| unsigned char *wbuffer = scrptr + hshift; |
| |
| if (VertSubActive) { |
| charcol = colorRAM[CharacterPosition + x]; |
| // get the actual physical character column |
| chr = chrbuf[x]; |
| mask = cset[(chr << 3) | vertSubCount]; |
| } else { |
| charcol = 0; |
| mask = Read(0x3FFF); |
| } |
| |
| 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 Vic2mem::ec_text() |
| { |
| unsigned char charcol; |
| unsigned char chr; |
| unsigned char mask; |
| unsigned char *wbuffer = scrptr + hshift; |
| |
| if (VertSubActive) { |
| // get the actual physical character column |
| charcol = colorRAM[CharacterPosition + x]; |
| chr = chrbuf[x]; |
| mask = cset[((chr & 0x3F) << 3) | vertSubCount]; |
| chr >>= 6; |
| } else { |
| mask = Read(0x39FF); |
| charcol = chr = 0; |
| } |
| |
| 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 with reverse bit set |
| inline void Vic2mem::mc_text() |
| { |
| unsigned char charcol; |
| unsigned char chr; |
| unsigned char *wbuffer = scrptr + hshift; |
| unsigned char mask; |
| |
| if (VertSubActive) { |
| charcol = colorRAM[CharacterPosition + x]; |
| chr = chrbuf[x]; |
| mask = cset[(chr << 3) | vertSubCount]; |
| } else { |
| mask = Read(0x3FFF); |
| charcol = 0; |
| } |
| |
| if (charcol & 8) { // if character is multicolored |
| |
| mcol[3] = charcol & 0x07; |
| |
| 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]; |
| } |
| } |
| |
| // when multi and extended color modes are all on the screen is blank |
| inline void Vic2mem::mcec() |
| { |
| //unsigned char charcol = colorRAM[CharacterPosition + x]; |
| unsigned char chr = chrbuf[x]; |
| unsigned char *wbuffer = scrptr + hshift; |
| unsigned char mask; |
| |
| if (VertSubActive) |
| mask = cset[((chr & 0x3F) << 3) | vertSubCount]; |
| else |
| mask = Read(0x3FFF); |
| |
| memset(wbuffer, mask & 0, 8); |
| } |
| |
| // renders hires bitmap graphics |
| inline void Vic2mem::hi_bitmap() |
| { |
| unsigned char mask; |
| unsigned char *wbuffer = scrptr + hshift; |
| // get the actual color attributes |
| unsigned char hcol0; |
| unsigned char hcol1; |
| |
| if (VertSubActive) { |
| // get the actual color attributes |
| hcol0 = chrbuf[x] & 0x0F; |
| hcol1 = chrbuf[x] >> 4; |
| mask = grbank[(((CharacterPosition + x) << 3) & 0x1FFF) | vertSubCount]; |
| } else { |
| hcol0 = hcol1 = 0; |
| mask = Read(0x3FFF); |
| } |
| |
| wbuffer[0] = (mask & 0x80) ? hcol1 : hcol0; |
| wbuffer[1] = (mask & 0x40) ? hcol1 : hcol0; |
| wbuffer[2] = (mask & 0x20) ? hcol1 : hcol0; |
| wbuffer[3] = (mask & 0x10) ? hcol1 : hcol0; |
| wbuffer[4] = (mask & 0x08) ? hcol1 : hcol0; |
| wbuffer[5] = (mask & 0x04) ? hcol1 : hcol0; |
| wbuffer[6] = (mask & 0x02) ? hcol1 : hcol0; |
| wbuffer[7] = (mask & 0x01) ? hcol1 : hcol0; |
| } |
| |
| // renders multicolor bitmap graphics |
| inline void Vic2mem::mc_bitmap() |
| { |
| unsigned char mask; |
| unsigned char *wbuffer = scrptr + hshift; |
| // get the actual color attributes |
| bmmcol[1] = chrbuf[x] >> 4; |
| bmmcol[2] = chrbuf[x] & 0x0F; |
| bmmcol[3] = colorRAM[CharacterPosition + x] & 0x0F; |
| |
| if (VertSubActive) |
| mask = grbank[(((CharacterPosition + x) << 3) & 0x1FFF) | vertSubCount]; |
| else // FIXME |
| mask = Read(0x3FFF); |
| |
| 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]; |
| } |
| |
| inline void Vic2mem::render() |
| { |
| // call the relevant rendering function |
| switch (scrattr) { |
| case 0: |
| hi_text(); |
| break; |
| case MULTICOLOR : |
| mc_text(); |
| break; |
| case EXTCOLOR : |
| ec_text(); |
| break; |
| case GRAPHMODE : |
| hi_bitmap(); |
| break; |
| case GRAPHMODE|MULTICOLOR : |
| mc_bitmap(); |
| break; |
| default: |
| mcec(); |
| break; |
| } |
| } |