| #include <stdio.h> |
| #include <string.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 NEWSDMA 1 |
| |
| #define RASTERX2TVCOL(X) (X < 400 ? X + 104 : X - 400) |
| #define SET_BITS(REG, VAL) { \ |
| unsigned int i = 7; \ |
| do { \ |
| REG = ((VAL) & (1 << i)) == (1 << i); \ |
| } while(i--); \ |
| } |
| |
| #define MOB_DO_PIXEL(X, COLOR) \ |
| do { \ |
| if (!(out[X] & 0x80)) { \ |
| if (!(out[X] & 0x40)) { \ |
| if (!spriteBckgCollReg) { \ |
| vicReg[0x19] |= ((vicReg[0x1A] & 2) << 6) | 2; \ |
| checkIRQflag(); \ |
| } \ |
| spriteBckgCollReg |= six; \ |
| if (!priority) out[X] = COLOR; \ |
| } else \ |
| out[X] = 0x40 | COLOR;\ |
| } \ |
| } while(0); |
| |
| #define STOP_SPRITE_DMA(X) \ |
| do { \ |
| spriteDMAmask &= ~(1 << X); \ |
| if (!spriteDMAmask) \ |
| vicBusAccessCycleStart = 0; \ |
| } while(0); |
| |
| #if NEWSDMA |
| #define DO_SPRITE_DMA(X) \ |
| do { \ |
| if (mob[X].dmaState) { \ |
| unsigned int &dc = mob[X].dataCount; \ |
| unsigned int &dcReload = mob[X].dataCountReload; \ |
| unsigned char *sData = vicBase + mob[X].dataAddress + dcReload; \ |
| unsigned char *sBuf = mob[X].sdb[0].shiftRegBuf; \ |
| sBuf[0] = sData[0]; \ |
| sBuf[1] = sData[1]; \ |
| sBuf[2] = sData[2]; \ |
| dc = dcReload + 3; \ |
| } \ |
| } while (0); |
| #else |
| #define DO_SPRITE_DMA(X) ; |
| #endif |
| |
| #define MOB_READ_ADDRESS(X) mob[X].dataAddress = (VideoBase[0x03F8 + X] << 6); // if (mob[X].dmaState) |
| |
| static unsigned char cycleLookup[][128] = { |
| // SCREEN: |===========0102030405060708091011121314151617181920212223242526272829303132333435363738391111========= |
| // coordinate: 111111111111111111111111111111 |
| // 0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000011111111112222222222 |
| // 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 |
| // beamX: |
| // 11111111111111111111111111 |
| // 000000000011111111112222220000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999 |
| // 012345678901234567890123450123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 |
| // 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 " |
| { "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 gsisis0sis1sis2sis3sis4sis5sis6sis7sisr r r "}, |
| // bad line |
| //"33i344i455i566i677i7r r*r*r*rcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcg i i 00i011i122i2"} |
| { "r*rcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgcgsisis0sis1sis2sis3sis4sis5sis6sis7sisr r*r*"} |
| }; |
| |
| static unsigned char prevY; |
| |
| unsigned int Vic2mem::CIA::refCount = 0; |
| |
| Vic2mem::Vic2mem() : gamepin(1), exrom(1) |
| { |
| instance_ = this; |
| setId("VIC2"); |
| if (!sidCard) |
| enableSidCard(true, 0); |
| sidCard->setFrequency(VIC_SOUND_CLOCK); |
| sidCard->setModel(SID6581); |
| masterClock = VIC_REAL_CLOCK_M10; |
| colorRAM = new unsigned char[0x0400]; |
| actram = Ram; |
| loadroms(); |
| chrbuf = DMAbuf; |
| // for sideborder effects prefill excess area with space (workaround) |
| memset(chrbuf + 40, 32, 24); |
| // setting screen memory pointer |
| scrptr = screen; |
| // important for sprite-bg collisions: fill blank area with border black |
| memset(screen, 0x80, VIC_PIXELS_PER_ROW * 312); |
| TVScanLineCounter = 0; |
| beamy = beamx = 0; |
| framecol = 0x80808080; |
| // |
| mobExtCol[0] = 0xFF; |
| unsigned int i; |
| for(i = 0; i < 256; i++) { |
| collisionLookup[i] = i; |
| } |
| for(i = 0; i < 8; i++) { |
| collisionLookup[1ULL << i] = 0; |
| mob[i].sdb[0].dwSrDmaBuf = mob[i].sdb[1].dwSrDmaBuf = 0; |
| } |
| // |
| irqFlag = 0; |
| vicBase = Ram; |
| charrombank = charRomC64; |
| charrom = false; |
| tap->mem=this; |
| keys64 = new KEYS64; |
| // CIA's |
| cia[0].setIrqCallback(setCiaIrq, this); |
| // |
| Reset(true); |
| // remove TED sound (inherited) from the list |
| SoundSource::remove(this); |
| } |
| |
| Vic2mem::~Vic2mem() |
| { |
| delete[] colorRAM; |
| delete keys64; |
| } |
| |
| void Vic2mem::Reset(bool clearmem) |
| { |
| if (clearmem) { |
| for (int i=0;i<RAMSIZE;Ram[i] = (i>>1)<<1==i ? 0 : 0xFF, i++); |
| loadroms(); |
| mem_8000_bfff = rom[0]; |
| mem_c000_ffff = rom[0] + 0x4000; |
| } |
| // empty collision buffers |
| memset(spriteCollisions, 0, sizeof(spriteCollisions)); |
| memset(spriteBckgColl, 0, sizeof(spriteBckgColl)); |
| spriteBckgCollReg = spriteCollisionReg = 0; |
| // |
| vicBusAccessCycleStart = spriteDMAmask = 0; |
| for (int i = 0; i < 8; i++) { |
| mob[i].dataCount = 0; |
| mob[i].dataCountReload = 0; |
| mob[i].reloadFlipFlop = 0; |
| mob[i].x = 0; |
| mob[i].y = 0; |
| mob[i].dmaState = false; |
| mob[i].rendering = false; |
| mob[i].enabled = 0; |
| } |
| // |
| soundReset(); |
| cia[0].reset(); |
| cia[1].reset(); |
| vicReg[0x19] = 0; |
| prp = 7; |
| prddr = 0; |
| } |
| |
| void Vic2mem::dumpState() |
| { |
| // always called during end of screen (X=100; Y=0) |
| saveVar(Ram, RAMSIZE); |
| saveVar(&prp, sizeof(prp)); |
| saveVar(&prddr, sizeof(prddr)); |
| saveVar(serialPort, sizeof(serialPort[0])); |
| saveVar(colorRAM, 0x0400); |
| saveVar(&beamx, sizeof(beamx)); |
| saveVar(&beamy, sizeof(beamy)); |
| 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(&charrom, sizeof(charrom)); |
| saveVar(&charbank, sizeof(charbank)); |
| saveVar(&framecol, sizeof(framecol)); |
| // |
| saveVar(&vicReg, sizeof(vicReg) / sizeof(vicReg[0])); |
| saveVar(&cia[0].reg, sizeof(cia[0].reg) / sizeof(cia[0].reg[0])); |
| saveVar(&cia[1].reg, sizeof(cia[1].reg) / sizeof(cia[1].reg[0])); |
| } |
| |
| void Vic2mem::readState() |
| { |
| readVar(Ram, RAMSIZE); |
| readVar(&prp, sizeof(prp)); |
| readVar(&prddr, sizeof(prddr)); |
| readVar(serialPort, sizeof(serialPort[0])); |
| readVar(colorRAM, 0x0400); |
| readVar(&beamx, sizeof(beamx)); |
| readVar(&beamy, sizeof(beamy)); |
| 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(&charrom, sizeof(charrom)); |
| readVar(&charbank, sizeof(charbank)); |
| readVar(&framecol, sizeof(framecol)); |
| // |
| readVar(&vicReg, sizeof(vicReg) / sizeof(vicReg[0])); |
| readVar(&cia[0].reg, sizeof(cia[0].reg) / sizeof(cia[0].reg[0])); |
| readVar(&cia[1].reg, sizeof(cia[1].reg) / sizeof(cia[1].reg[0])); |
| // |
| for (unsigned int i = 0; i < 16; i++) { |
| cia[0].write(i, cia[0].reg[i]); |
| cia[1].write(i, cia[1].reg[i]); |
| } |
| for (unsigned int i = 0; i < 0x30; i++) { |
| Write(0xD000 + i, vicReg[i]); |
| } |
| Write(0, prddr); |
| Write(1, prp); |
| } |
| |
| void Vic2mem::loadromfromfile(int nr, char fname[512], unsigned int offset) |
| { |
| FILE *img; |
| |
| if (img = fopen(fname, "rb")) { |
| // note: this is only the minimum! |
| const unsigned int crthdrsize = 64; |
| unsigned char crtheader[crthdrsize]; |
| |
| size_t r = fread(crtheader, 1, crthdrsize, img); |
| if (!strncmp((char*)crtheader, "C64 CARTRIDGE", 13)) { |
| const unsigned int chiphdrsize = 16; |
| unsigned char chipheader[chiphdrsize]; |
| unsigned int size, loadaddress; |
| const unsigned int crtversionMain = crtheader[0x14]; |
| const unsigned int crtversionSub = crtheader[0x15]; |
| const unsigned int crtType = crtheader[0x17] | (crtheader[0x16] << 8); |
| |
| fprintf(stderr, "CRT image version: %u.%u, type: %u\n", crtversionMain, crtversionSub, crtType); |
| |
| r = fread(chipheader, 1, chiphdrsize, img); |
| if (!strncmp((char*)chipheader, "CHIP", 4)) { |
| loadaddress = chipheader[0x0D] | (chipheader[0x0C] << 8); |
| size = chipheader[0x0F] | (chipheader[0x0E] << 8); |
| exrom = crtheader[0x18]; |
| gamepin = crtheader[0x19]; |
| if (size <= 0x2000) |
| offset = loadaddress & 0x3000; |
| // load ROM/CRT file |
| r = fread(rom[nr] + offset, size, 1, img); |
| fprintf(stderr, " CHIP data loaded: %04X-%04X EXROM:%u GAME:%u\n", loadaddress, loadaddress + size - 1, exrom, gamepin); |
| fclose(img); |
| // |
| changeMemoryBank(prp | ~prddr, exrom, gamepin); |
| Reset(0); |
| cpuptr->Reset(); |
| return; |
| } |
| } |
| } |
| memset(rom[nr] + offset, 0, ROMSIZE); |
| bool restart = !(exrom & gamepin); |
| exrom = gamepin = 1; |
| changeMemoryBank(prp | ~prddr, exrom, gamepin); |
| if (restart) { |
| Reset(1); |
| cpuptr->Reset(); |
| } |
| } |
| |
| void Vic2mem::loadroms() |
| { |
| memcpy(rom[0], basicRomC64, basicRomC64_size); |
| memcpy(rom[0] + 0x4000, kernalRomC64, kernalRomC64_size); |
| mem_8000_bfff = rom[0]; |
| mem_8000_9fff = Ram + 0x8000; |
| mem_c000_ffff = rom[0] + 0x4000; |
| #if FAST_BOOT |
| // TODO: check ROM pattern |
| 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::setCpuPtr(CPU *cpu) |
| { |
| cpuptr = cpu; |
| cia[1].setIrqCallback(setCiaNmi, cpuptr); |
| } |
| |
| void Vic2mem::copyToKbBuffer(const char *text, unsigned int length) |
| { |
| if (!length) |
| length = (unsigned int) strlen(text); |
| Write(0xc6, length); |
| while (length--) |
| Write(0x0277 + length, text[length]); |
| } |
| |
| Color Vic2mem::getColor(unsigned int ix) |
| { |
| const double bsat = 45.0; |
| const Color color[16] = { |
| { 0, 0, 0 }, { 0, 5.0, 0 }, |
| #ifndef MOS6569R1 |
| #if 0 // speculative |
| { 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 } |
| #else // measured |
| #if 0 // my TV card ~TED hues |
| { 96, 2.9375, bsat },{ 283, 3.875, bsat }, |
| { 56, 3.125, bsat },{ 245, 3.5, bsat },{ 350, 2.75, bsat },{ 167, 4.25, bsat }, |
| { 126, 3.125, bsat },{ 147, 2.75, bsat },{ 96, 3.5, bsat },{ 0, 2.9375, 0 }, |
| { 0, 3.41, 0 },{ 245, 4.25, bsat },{ 350, 3.41, bsat },{ 0, 3.875, 0 } |
| #else // screenshot |
| { 96, 2.9375, bsat },{ 283, 3.875, bsat }, |
| { 55, 3.125, bsat },{ 241, 3.5, bsat },{ 347, 2.75, bsat },{ 167, 4.25, bsat }, |
| { 129, 3.125, bsat },{ 148, 2.75, bsat },{ 96, 3.5, bsat },{ 0, 2.9375, 0 }, |
| { 0, 3.41, 0 },{ 241, 4.25, bsat },{ 347, 3.41, bsat },{ 0, 3.875, 0 } |
| #endif |
| #endif |
| #else // 6569R1 |
| { 96, 3.0, bsat },{ 282, 4.5, bsat }, |
| { 55, 3.5, bsat },{ 245, 3.5, bsat },{ 350, 3.0, bsat },{ 167, 4.5, bsat }, |
| { 126, 3.5, bsat },{ 140, 3.0, bsat },{ 96, 3.5, bsat },{ 0, 3.0, 0 }, |
| { 0, 3.5, 0 },{ 245, 4.5, bsat },{ 350, 3.5, bsat },{ 0, 4.5, 0 } |
| #endif |
| }; |
| return color[ix & 0xF]; |
| } |
| |
| void Vic2mem::soundReset() |
| { |
| if (sidCard) |
| sidCard->reset(); |
| } |
| |
| void Vic2mem::CIA::reset() |
| { |
| pra = prb = 0; |
| ddra = ddrb = 0; |
| icr = 0; |
| irq_mask = 0; |
| ta = tb = taFeed = tbFeed = 0; |
| latcha = latchb = 0xFFFF; |
| tbReload = 0; |
| cra = crb = 0; |
| prbTimerOut = 0; |
| prbTimerMode = 0; |
| prbTimerToggle = 0x80; |
| sdrShiftCnt = 0; |
| // ToD |
| todCount = 60 * 60 * 50; // set to 1hr at reset |
| alarmCount = -1; |
| tod.latched = false; |
| tod.halt = 1; |
| todIn = 60; |
| tod.ampm = 0; |
| pendingIrq = false; |
| } |
| |
| inline void Vic2mem::CIA::setIRQflag(unsigned int mask) |
| { |
| if (mask) { |
| if (!(icr & 0x80)) { |
| #if 1 |
| pendingIrq = true; |
| #else |
| icr |= 0x80; |
| irqCallback(callBackParam); |
| #endif |
| } |
| } |
| } |
| |
| 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); |
| } |
| |
| inline void Vic2mem::CIA::setTimerMode(const unsigned int flag, const unsigned int tv, unsigned int cr) |
| { |
| if (cr & 2) { |
| prbTimerMode |= flag; // PB6 shows timer underflow state |
| if (cr & 4) { // On a timer overflow, PBx is inverted? |
| prbTimerOut = (prbTimerOut & ~flag) | (prbTimerToggle & flag); |
| } else { |
| if (!tv) { |
| prbTimerOut |= flag; |
| } else { |
| prbTimerOut &= ~flag; |
| } |
| } |
| } |
| else |
| prbTimerMode &= ~flag; |
| } |
| |
| 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 only if stopped |
| if (!(cra & 1)) { |
| taReload = 1; |
| } |
| break; |
| |
| case 0x06: |
| latchb = (latchb & 0xFF00) | value; |
| break; |
| |
| case 0x07: |
| latchb = (latchb & 0xFF) | (value << 8); |
| // Reload timer B only if stopped |
| if (!(crb & 1)) { |
| tbReload = 1; |
| } |
| break; |
| |
| case 0x08: |
| if (crb & 0x80) { |
| frames2tod(alarmCount, alm, todIn); |
| alm.tenths = value & 0x0F; |
| alarmCount = tod2frames(alm); |
| } else { |
| if (!tod.halt) |
| frames2tod(todCount, tod, todIn); |
| tod.tenths = value & 0x0F; |
| if (!tod.halt) |
| todCount = tod2frames(tod); |
| } |
| tod.halt = false; |
| break; |
| |
| case 0x09: |
| if (crb & 0x80) { |
| frames2tod(alarmCount, alm, todIn); |
| alm.sec = value & 0x7F; |
| alarmCount = tod2frames(alm); |
| } else { |
| if (!tod.halt) |
| frames2tod(todCount, tod, todIn); |
| tod.sec = value & 0x7F; |
| if (!tod.halt) |
| todCount = tod2frames(tod); |
| } |
| break; |
| |
| case 0x0A: |
| if (crb & 0x80) { |
| frames2tod(alarmCount, alm, todIn); |
| alm.min = value & 0x7F; |
| alarmCount = tod2frames(alm); |
| } else { |
| if (!tod.halt) |
| frames2tod(todCount, tod, todIn); |
| tod.min = value & 0x7F; |
| if (!tod.halt) |
| todCount = tod2frames(tod); |
| } |
| break; |
| |
| case 0x0B: |
| if (crb & 0x80) { |
| frames2tod(alarmCount, alm, todIn); |
| alm.hr = value & 0x9F; |
| alarmCount = tod2frames(alm); |
| } else { |
| if (!tod.halt) |
| 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 & 0x1F); |
| else |
| irq_mask &= ~(value & 0x1F); |
| setIRQflag(icr & irq_mask); |
| break; |
| |
| case 0x0E: |
| // rising edge of CRA0 sets PB6 toggle |
| if (!(cra & 1) && (value & 1)) |
| prbTimerToggle |= 0x40; |
| cra = value & 0xEF; |
| if (!(value & 1)) |
| taFeed = 0; |
| // Forced reload |
| if (value & 0x10) { |
| taReload = 1; |
| } |
| // set Timer A mode |
| setTimerMode(0x40, ta, cra); |
| // ToD clock rate |
| todIn = value & 0x80 ? 50 : 60; |
| break; |
| |
| case 0x0F: |
| // rising edge of CRB0 sets PB7 toggle |
| if (!(crb & 1) && (value & 1)) |
| prbTimerToggle |= 0x80; |
| crb = value & 0xEF; |
| if (!(crb & 1)) |
| tbFeed = 0; |
| // Forced reload |
| if (value & 0x10) { |
| tbReload = 1; |
| } |
| // set Timer B mode |
| setTimerMode(0x80, tb, crb); |
| break; |
| } |
| reg[addr] = value; |
| } |
| |
| unsigned char Vic2mem::CIA::read(unsigned int addr) |
| { |
| addr &= 0x0F; |
| switch (addr) { |
| case 0x00: |
| return pra | ~ddra; |
| case 0x01: |
| { |
| unsigned char retval; |
| retval = ((prb | ~ddrb) & ~prbTimerMode) | (prbTimerOut & prbTimerMode); |
| 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: |
| if (!tod.halt) { |
| frames2tod(todCount, tod, todIn); |
| } |
| tod.latched = true; |
| todLatch = tod; |
| return todLatch.hr | tod.ampm; |
| case 0x0C: |
| return sdr; |
| case 0x0D: |
| { |
| unsigned char retval = icr & 0x9F; |
| icr = 0; |
| pendingIrq = false; |
| return retval; |
| } |
| case 0x0E: |
| return cra; |
| case 0x0F: |
| return crb; |
| } |
| return reg[addr]; |
| } |
| |
| void Vic2mem::CIA::checkTimerAUnderflow() |
| { |
| if (!ta && (taFeed & 1)) { |
| icr |= 0x01; // Set timer A IRQ flag |
| setIRQflag(icr & irq_mask); // FIXME, 1 cycle delay |
| if (crb & 0x40) { // cascaded timer? CNT pin is high by default |
| tbFeed |= 1; |
| countTimerB(-1); |
| } |
| prbTimerToggle ^= 0x40; // PRA7 underflow count toggle |
| // timer A output to PB6? |
| if (cra & 2) { |
| // set PRA6 high for one clock cycle |
| if (cra & 4) { |
| prbTimerOut ^= 0x40; // toggle PRB6 between 1 and 0 |
| } |
| else { |
| prbTimerOut |= 0x40; // set high for one clock |
| } |
| } |
| //prbTimerOut = (prbTimerOut & ~0x40) | (prbTimerToggle & 0x40); |
| if (cra & 8) { // One-shot? |
| cra &= 0xFE; // Stop timer |
| taFeed = 0; |
| } |
| taReload = 1; |
| } |
| } |
| |
| void Vic2mem::CIA::checkTimerBUnderflow(const int cascaded) |
| { |
| if (tb == cascaded && (tbFeed & 1)) { |
| icr |= 0x02; // Set timer B IRQ flag |
| setIRQflag(icr & irq_mask); // FIXME, 1 cycle delay on later CIA's |
| prbTimerToggle ^= 0x80; // PRB7 underflow count toggle |
| // timer A output to PB6? |
| if (crb & 2) { |
| // set PRB7 high for one clock cycle |
| if (crb & 4) { |
| prbTimerOut ^= 0x80; // toggle PRB7 between 1 and 0 |
| } |
| else { |
| prbTimerOut |= 0x80; // set high for one clock |
| } |
| } |
| //prbTimerOut = (prbTimerOut & ~0x80) | (prbTimerToggle & 0x80); |
| if (crb & 8) {// One-shot? |
| crb &= 0xFE; // Stop timer |
| tbFeed = 0; |
| } |
| // Reload from latch if not cascading |
| tbReload = 1; |
| } |
| } |
| |
| void Vic2mem::CIA::countTimerB(int cascaded) |
| { |
| tb -= (tbFeed & 1); |
| tbFeed = (tbFeed >> 1) | ((crb & 1) << 1); |
| // Underflow? |
| checkTimerBUnderflow(cascaded); |
| if (tbReload) { |
| tbReload = 0; |
| // Reload from latch |
| tb = latchb; |
| // skip decrement in next cycle |
| tbFeed &= ~1; |
| } |
| } |
| |
| void Vic2mem::CIA::countTimers() |
| { |
| if (pendingIrq) { |
| icr |= 0x80; |
| irqCallback(callBackParam); |
| pendingIrq = false; |
| } |
| if ((cra & 0x40) && sdrShiftCnt) { |
| sdrShiftCnt -= 1; |
| if (!sdrShiftCnt) { |
| icr |= 8; |
| setIRQflag(icr & irq_mask); |
| } |
| } |
| if (!(cra & 4)) { |
| prbTimerOut &= ~0x40; // reset PRB6 |
| } |
| if ((cra & 0x20) == 0x00) { |
| ta -= (taFeed & 1); |
| taFeed = (taFeed >> 1) | ((cra & 1) << 1); |
| // Underflow? |
| checkTimerAUnderflow(); |
| if (taReload) { |
| taReload = 0; |
| // Reload from latch |
| ta = latcha; |
| // skip decrement in next cycle |
| taFeed &= ~1; |
| } |
| } |
| if (!(crb & 4)) { |
| prbTimerOut &= ~0x80; // reset PRB7 |
| } |
| if (!(crb & 0x40)) { // TimerB counting phi clock cycles? |
| countTimerB(0); |
| } |
| } |
| |
| 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; |
| // 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 cset:%04X(%u) in line:%03i pra:%02X ddra:%02X vic18:%02X\n", |
| vicBank, vmOffset, cSetOffset, cset != charrambank, beamy, cia[1].pra, cia[1].ddra, vicReg[0x18]); |
| #endif |
| } |
| |
| void Vic2mem::changeMemoryBank(unsigned int port, unsigned int ex, unsigned int game) |
| { |
| // TODO: make it a table |
| mem_8000_bfff = ((port & 3) == 3) ? rom[0] : Ram + 0xa000; // a000..bfff |
| mem_c000_ffff = ((port & 2) == 2) ? rom[0] + 0x4000 : Ram + 0xe000; // e000..ffff |
| charrom = (!(port & 4) && (port & 3)); |
| mem_8000_9fff = Ram + 0x8000; |
| mem_1000_3fff = Ram; |
| // Ultimax mode? |
| if (exrom && !gamepin) { |
| mem_8000_9fff = rom[1]; |
| mem_c000_ffff = rom[1] + 0x2000; |
| mem_1000_3fff = rom[1]; |
| // HACK! Cart ROM is mirrored at lowest VIC bank in Ultimax mode |
| memcpy(Ram + 0x1000, rom[1] + 0x1000, 0x3000); |
| changeCharsetBank(); |
| } else if (!exrom) { |
| if ((port & 3) == 3) { |
| mem_8000_9fff = rom[1]; |
| } |
| if (!gamepin && (port & 2)) { |
| mem_8000_bfff = rom[1] + 0x2000; |
| } |
| } |
| } |
| |
| void Vic2mem::setCiaIrq(void *param) |
| { |
| Vic2mem *mh = reinterpret_cast<Vic2mem*>(param); |
| mh->irqFlag |= 0x40; |
| //fprintf(stderr, "CIA1 irq @ PC=%04X @ cycle=%i\n", mh->cpuptr->getPC(), CycleCounter); |
| } |
| |
| void Vic2mem::setCiaNmi(void *param) |
| { |
| CPU *cpu = reinterpret_cast<CPU*>(param); |
| cpu->triggerNmi(); |
| } |
| |
| inline void Vic2mem::checkIRQflag() |
| { |
| irqFlag |= (vicReg[0x19] & 0x80); |
| } |
| |
| void Vic2mem::doDelayedDMA() |
| { |
| if (attribFetch) { |
| bool nowBadLine = (vshift == (beamy & 7)) & (beamy != 247); |
| if (nowBadLine) { |
| if (!BadLine && (beamx <= 86 || beamx >= 124)) { |
| int delay; |
| int illegalRead; |
| |
| vicBusAccessCycleStart = CycleCounter; |
| BadLine = 1; |
| if (!VertSubActive) { |
| // FIXME one cycle delay |
| VertSubActive = true; |
| delay = (beamx <= 86) ? ((beamx) >> 1) + 0 : (beamx - 124) >> 0; |
| } else { |
| delay = 0; |
| } |
| if (delay <= 40) { |
| delayedDMA = true; |
| dmaCount = 40 - delay; |
| if (CharacterPosition + dmaCount >= 0x0400) { |
| memcpy(chrbuf, VideoBase + CharacterPosition, 0x400 - CharacterPosition); |
| memcpy(chrbuf + 0x400 - CharacterPosition, VideoBase, (CharacterPosition + dmaCount) & 0x03FF); |
| } else { |
| memcpy(chrbuf, VideoBase + CharacterPosition, dmaCount); |
| } |
| } else { |
| dmaCount = 0; |
| } |
| //fprintf(stderr, "Delayed DMA:%02i count:%i @ XSCR=%i X=%i Y=%i(%02X) YSCR=%0X @ PC=%04X\n", delay, |
| // dmaCount, hshift, beamx, beamy, beamy, vshift, cpuptr->getPC()); |
| } else { |
| //fprintf(stderr, "Bad line (DMAdelay:%i) @ XSCR=%i X=%i Y=%i(%02X) @ PC=%04X\n", delayedDMA, |
| // hshift, beamx, beamy, beamy, cpuptr->getPC()); |
| BadLine = 1; |
| VertSubActive = true; |
| } |
| } else { |
| BadLine = 0; |
| //fprintf(stderr, "Bad line stopped @ XSCR=%i X=%i Y=%i(%02X) VSC=%02X DMAC=%i @ PC=%04X\n", |
| // hshift, beamx, beamy, beamy, vertSubCount, dmaCount, cpuptr->getPC()); |
| } |
| } |
| } |
| |
| void Vic2mem::UpdateSerialState(unsigned char newPort) |
| { |
| static unsigned char prevPort = 0x01; |
| if (prevPort ^ newPort) { |
| serialPort[0] = ((newPort << 2) & 0x80) // DATA OUT -> DATA IN |
| | ((newPort << 2) & 0x40) // CLK OUT -> CLK IN |
| | ((newPort << 1) & 0x10); // ATN OUT -> ATN IN (drive) |
| updateSerialDevices(serialPort[0]); |
| prevPort = newPort; |
| #if LOG_SERIAL |
| fprintf(stderr, "$DD00 write : %02X @ PC=%04X in cycle:%llu\n", value, cpuptr->getPC(), CycleCounter); |
| fprintf(stderr, " serial port written: %02X.\n", serialPort[0]); |
| #endif |
| } |
| } |
| |
| // 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) | ((portState | 0x17) & ~prddr & 0xDF & (!(tap->IsButtonPressed()) << 4)); |
| default: |
| return actram[addr & 0xFFFF]; |
| } |
| default: |
| return actram[addr & 0xFFFF]; |
| case 0x8000: |
| case 0x9000: |
| return mem_8000_9fff[addr & 0x1FFF]; |
| 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) && !(exrom & ~gamepin)) |
| return actram[addr & 0xFFFF]; |
| else if (charrom && !(exrom & ~gamepin)) { |
| return charRomC64[addr & 0x0FFF]; |
| } else { |
| switch ( addr >> 8 ) { |
| case 0xD0: // VIC2 |
| case 0xD1: |
| case 0xD2: |
| case 0xD3: |
| addr &= 0x3F; |
| switch (addr) { |
| case 0x11: |
| return (vicReg[0x11] & 0x7f) | ((beamy & 0x100) >> 1); |
| case 0x12: |
| return beamy & 0xFF; |
| case 0x13: // LPX |
| return lpLatchX; |
| case 0x14: // LPY |
| return lpLatchY; |
| 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 0x1E: |
| { // sprite-sprite collision |
| unsigned char rv = spriteCollisionReg; |
| spriteCollisionReg = 0; |
| return rv; |
| } |
| case 0x1F: |
| { // sprite-background collision |
| unsigned char rv = spriteBckgCollReg; |
| spriteBckgCollReg = 0; |
| return rv; |
| } |
| case 0x20: |
| return framecol | 0xF0; |
| case 0x21: |
| case 0x22: |
| case 0x23: |
| case 0x24: |
| return ecol[(addr & 0x3F) - 0x21] | 0xF0; |
| case 0x25: |
| case 0x26: |
| return mobExtCol[((addr - 0x25) << 1) + 1] | 0xF0; |
| case 0x27: |
| case 0x28: |
| case 0x29: |
| case 0x2A: |
| case 0x2B: |
| case 0x2C: |
| case 0x2D: |
| case 0x2E: |
| return vicReg[addr] | 0xF0; |
| case 0x2F: // unconnected |
| return 0xFF; |
| } |
| return vicReg[addr]; |
| case 0xD4: // SID |
| case 0xD5: |
| case 0xD6: |
| case 0xD7: |
| if (sidCard) { |
| flushBuffer(CycleCounter, VIC_SOUND_CLOCK); |
| return sidCard->read(addr & 0x1F); |
| } |
| return 0xD4; |
| 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: |
| retval = cia[0].read(0) |
| & keys64->getJoyState(1) |
| & keys64->feedKeyColumn((cia[0].prb | ~cia[0].ddrb) & keys64->getJoyState(0)); |
| return retval; |
| case 0x01: // port B usually not driven low by port A. |
| #if 1 |
| { |
| static unsigned char oldRetval = 0xFF; |
| retval = ((keys64->feedkey((cia[0].pra | ~cia[0].ddra) & keys64->getJoyState(1)) ) // | (cia[0].read(1) & 0xC0) |
| & ~cia[0].ddrb) |
| | (cia[0].read(1) & cia[0].ddrb); |
| if ((oldRetval & 0x10) && !(retval & 0x10)) |
| latchCounters(); |
| oldRetval = retval; |
| } |
| #else |
| retval = cia[0].read(1) |
| & (keys64->feedkey(cia[0].read(0)) & keys64->getJoyState(1) | cia[0].ddrb); |
| #endif |
| //fprintf(stderr, "$Kb(%02X,%02X) read: %02X\n", cia[0].pra, cia[0].ddra, retval); |
| return retval; |
| case 0x0D: |
| retval = cia[0].read(0x0D); |
| irqFlag &= ~0x40; |
| break; |
| default: |
| retval = cia[0].read(addr); |
| } |
| /*fprintf(stderr, "CIA1(%02X) read:%02X @ PC=%04X @ cycle=%i\n", addr & 0x1f, retval, |
| cpuptr->getPC(), CycleCounter);*/ |
| 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(); |
| /*fprintf(stderr, "CIA2(%02X) read:%02X @ PC=%04X @ cycle=%lli\n", addr & 0x1f, retval, |
| cpuptr->getPC(), CycleCounter);*/ |
| return retval; |
| } |
| default: |
| ; |
| } |
| /*fprintf(stderr, "CIA2(%02X) read:%02X @ PC=%04X @ cycle=%lli\n", addr & 0x1f, cia[1].read(addr & 0xf), |
| cpuptr->getPC(), CycleCounter);*/ |
| return cia[1].read(addr); |
| default: // open address space |
| return cpuptr->getcins();// beamy ^ beamx;//actram[addr & 0xFFFF]; |
| } |
| } |
| } |
| } |
| |
| void Vic2mem::Write(unsigned int addr, unsigned char value) |
| { |
| switch (addr & 0xF000) { |
| case 0x0000: |
| { |
| unsigned char port; |
| switch ( addr & 0xFFFF ) { |
| case 0: |
| prddr = value; |
| goto skip; |
| |
| case 1: |
| if ((prp ^ value) & 0x20) |
| tap->setTapeMotor(CycleCounter, !(value & 0x20)); |
| prp = value; |
| skip: |
| portState = (portState & ~prddr) | (prp & 0xC8 & prddr); |
| port = prp | ~prddr; |
| changeMemoryBank(port, exrom, gamepin); |
| return; |
| default: |
| actram[addr & 0xFFFF] = value; |
| } |
| } |
| return; |
| default: |
| actram[addr & 0xFFFF] = value; |
| return; |
| case 0xD000: |
| if (!((prp | ~prddr) & 3) && !(exrom & ~gamepin)) { // should be read(1) |
| actram[addr & 0xFFFF] = value; |
| } else if (!charrom || (exrom & ~gamepin)) { |
| //unsigned int i; |
| switch ( addr >> 8 ) { |
| case 0xD0: // VIC2 |
| case 0xD1: |
| case 0xD2: |
| case 0xD3: |
| addr &= 0x3F; |
| switch (addr) { |
| case 0x12: |
| if ((irqline ^ value) & 0xFF ) |
| { |
| /*fprintf(stderr, "Raster IRQ set to %03i(%03X) @ PC=0%04X @ cycle=%i\n", |
| irqline, value, cpuptr->getPC(), CycleCounter); */ |
| irqline = (irqline & 0x100) | value; |
| if (beamy == irqline) { |
| vicReg[0x19] |= ((vicReg[0x1A] & 1) << 7) | 1; |
| checkIRQflag(); |
| } |
| } |
| break; |
| case 0x11: |
| // raster IRQ line |
| if (((irqline >> 1) ^ value) & 0x80 ) |
| { |
| irqline = (irqline & 0xFF) | ((value & 0x80) << 1); |
| if (beamy == irqline) { |
| vicReg[0x19] |= ((vicReg[0x1A] & 1) << 7) | 1; |
| 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 |
| // check for graphics mode (5th b14it) |
| scrattr = (scrattr & ~(GRAPHMODE|EXTCOLOR))|(value & (GRAPHMODE|EXTCOLOR)); |
| // Check if screen is turned on |
| if (value & 0x10 && beamy == 48 && !attribFetch) { |
| attribFetch = true; |
| } else if (attribFetch && ((fltscr && beamy == 48+7) || (!fltscr && beamy == 48+3))) { |
| ScreenOn = true; |
| } else if ((beamy == 48+199 && fltscr) || (beamy == 48+203 && !fltscr)) { |
| ScreenOn = false; |
| } |
| doDelayedDMA(); |
| //fprintf(stderr, "d011: %02X @ X=%03i @ Y=%03i($%03X) PC=%04X\n", value, beamx, beamy, beamy, cpuptr->getPC()); |
| break; |
| case 0x16: |
| // check for narrow screen (38 columns) |
| nrwscr = value & 0x08; |
| // get horizontal offset of screen when smooth scroll |
| if (CharacterWindow) |
| doXscrollChange(hshift, value & 0x07); |
| hshift = value & 0x07; |
| scrattr = (scrattr & ~(MULTICOLOR)) | (value & (MULTICOLOR)); |
| //fprintf(stderr, "$D016 write: %02X @ PC=%04X @ X=%03i @ Y=%03i\n", value, cpuptr->getPC(), beamx, beamy); |
| 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; |
| irqFlag |= 0x80; |
| } else { |
| vicReg[0x19] &= 0x7F; |
| irqFlag &= ~0x80; |
| } |
| //fprintf(stderr, "IRQ ack. write:%02X value:%02X @ PC=%04X @ cycle=%i\n", |
| // value, vicReg[0x19], cpuptr->getPC(), CycleCounter); |
| return; |
| case 0x1a: |
| // check if we have a pending IRQ |
| if ((vicReg[0x19]) & 0x0F & value) { |
| vicReg[0x19] |= 0x80; |
| irqFlag |= 0x80; |
| } else { |
| vicReg[0x19] &= 0x7F; |
| irqFlag &= ~0x80; |
| } |
| break; |
| case 0x20: |
| // distinguish border in the rendered screen with 0x80 |
| value = (value & 0x0F) | 0x80; |
| framecol = (value << 24) | (value << 16) | (value << 8) | value; |
| break; |
| case 0x21: |
| ecol[0] = bmmcol[0] = mcol[0] = (value & 0x0F) | 0x40; |
| break; |
| case 0x22: // '01' counts as background as well |
| ecol[1] = mcol[1] = (value & 0x0F) | 0x40; |
| 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 = (mob[addr >> 1].x & 0x0100) | value; |
| //fprintf(stderr, "Sprite%i:%i m_x: %u\n", addr >> 1, value, mob[addr >> 1].x); |
| break; |
| case 0x01: |
| case 0x03: |
| case 0x05: |
| case 0x07: |
| case 0x09: |
| case 0x0B: |
| case 0x0D: |
| case 0x0F: |
| mob[addr >> 1].y = (value); |
| //fprintf(stderr, "Sprite%i:%i : %u\n", addr >> 1, value, mob[addr >> 1].y); |
| break; |
| case 0x10: |
| { |
| unsigned int i = 7; |
| do { |
| mob[i].x = ((mob[i].x & 0xFF) | ((value << (8 - i)) & 0x100)); |
| //fprintf(stderr, "Sprite%i:%i m8x: %u\n", i, value, mob[i].x); |
| } while (i--); |
| } |
| break; |
| case 0x15: |
| SET_BITS(mob[i].enabled, value); |
| break; |
| case 0x17: |
| // sprite crunch? |
| if (beamx == 2) { |
| unsigned int i = 7; |
| do { |
| unsigned int bit = (1 << i); |
| unsigned int newBitOn = value & bit; |
| if ((vicReg[0x17] & bit) && !newBitOn) { |
| unsigned int &dcReload = mob[i].dataCountReload; |
| unsigned int dc = (dcReload + 3) & 0x3F; |
| dcReload = (0x2A & dcReload & dc) | (0x15 & (dcReload | dc)); |
| } |
| mob[i].expandY = mob[i].reloadFlipFlop = !!newBitOn; |
| } while(i--); |
| } else |
| { |
| SET_BITS(mob[i].expandY, value); |
| SET_BITS(mob[i].reloadFlipFlop, 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) << 1) + 1] = 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: |
| if (sidCard) { |
| flushBuffer(CycleCounter, VIC_SOUND_CLOCK); |
| 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 & LP irq |
| case 1: |
| case 3: |
| { |
| unsigned char oldPortOut = cia[0].prb | ~cia[0].ddrb; |
| cia[0].write(addr, value); |
| unsigned char newPortOut = cia[0].prb | ~cia[0].ddrb; |
| if ((oldPortOut & 0x10) && !(newPortOut & 0x10)) { |
| latchCounters(); |
| } |
| } |
| return; |
| case 0: |
| cia[0].write(addr, value); |
| return; |
| default:; |
| } |
| //fprintf(stderr, "CIA1(%02X) write: %02X @ PC=%04X\n", addr & 0x0f, value, cpuptr->getPC()); |
| cia[0].write(addr, value); |
| return; |
| case 0xDD: // CIA2 |
| switch (addr & 0x0F) { |
| case 2: |
| cia[1].write(2, value); |
| UpdateSerialState(~cia[1].pra & cia[1].ddra); |
| return; |
| case 0: |
| cia[1].write(0, value & 0x3F); |
| // VIC base |
| changeCharsetBank(); |
| // serial IEC |
| UpdateSerialState(~cia[1].pra & cia[1].ddra); |
| return; |
| default: |
| break; |
| } |
| //fprintf(stderr, "CIA2(%02X) write: %02X @ PC=%04X\n", addr & 0x0f, value, cpuptr->getPC()); |
| 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::latchCounters() |
| { |
| // once per frame only |
| if (!lpLatched) { |
| lpLatched = true; |
| lpLatchX = beamx << 1; |
| lpLatchY = beamy; |
| vicReg[0x19] |= ((vicReg[0x1A] & 8) << 4) | 8; |
| checkIRQflag(); |
| } |
| } |
| |
| void Vic2mem::doHRetrace() |
| { |
| static unsigned char *sPtr = scrptr; |
| //if (vicReg[0x15]) |
| drawSpritesPerLine(sPtr); |
| // the beam reached a new line |
| sPtr = scrptr; |
| } |
| |
| inline void Vic2mem::newLine() |
| { |
| prevY = beamy; |
| beamy += 1; |
| switch (beamy) { |
| |
| case 48: |
| attribFetch = (vicReg[0x11] & 0x10) != 0; |
| break; |
| |
| case 48 + 3: |
| if (!fltscr && attribFetch) ScreenOn = true; |
| break; |
| |
| case 48 + 7: |
| if (fltscr && attribFetch) ScreenOn = true; |
| break; |
| |
| case 199 + 48: |
| if (fltscr) ScreenOn = false; |
| break; |
| |
| case 203 + 48: |
| if (!fltscr) ScreenOn = false; |
| break; |
| |
| case 204 + 48: |
| //VertSubActive = false; |
| break; |
| |
| case 250 + 48: // VIC article: 300 |
| VBlanking = true; |
| break; |
| |
| case 0: |
| case 512: |
| case 312: // Vertical retrace |
| if (!attribFetch) { |
| endOfScreen = true; |
| } |
| beamy = 0; |
| CharacterPositionReload = 0; |
| doVRetrace(); |
| // CIA ToD count @ 50/60 Hz |
| cia[0].todUpdate(); |
| cia[1].todUpdate(); |
| // skip checking raster IRQ |
| return; |
| |
| case 6: |
| VBlanking = false; |
| break; |
| } |
| // is there raster interrupt? |
| if (beamy == irqline) { |
| vicReg[0x19] |= ((vicReg[0x1A] & 1) << 7) | 1; |
| 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, VIC_SOUND_CLOCK); |
| if (attribFetch) { |
| BadLine = (vshift == (beamy & 7)); |
| if (BadLine) { |
| VertSubActive = true; |
| } |
| } |
| MOB_READ_ADDRESS(3); |
| DO_SPRITE_DMA(3); |
| STOP_SPRITE_DMA(2); |
| break; |
| |
| case 102: |
| if (endOfScreen) { |
| // is there raster interrupt? line 0 IRQ is delayed by 0 cycle |
| if (0 == irqline) { |
| vicReg[0x19] |= ((vicReg[0x1A] & 1) << 7) | 1; |
| checkIRQflag(); |
| } |
| lpLatched = false; |
| endOfScreen = false; |
| dmaCount = 0; |
| } |
| checkSpriteDMA(5); |
| break; |
| |
| case 104: |
| MOB_READ_ADDRESS(4); |
| DO_SPRITE_DMA(4); |
| STOP_SPRITE_DMA(3); |
| break; |
| |
| case 106: |
| checkSpriteDMA(6); |
| break; |
| |
| case 108: |
| MOB_READ_ADDRESS(5); |
| DO_SPRITE_DMA(5); |
| STOP_SPRITE_DMA(4); |
| break; |
| |
| case 110: |
| checkSpriteDMA(7); |
| break; |
| |
| case 112: |
| MOB_READ_ADDRESS(6); |
| DO_SPRITE_DMA(6); |
| STOP_SPRITE_DMA(5); |
| break; |
| |
| case 116: |
| MOB_READ_ADDRESS(7); |
| DO_SPRITE_DMA(7); |
| STOP_SPRITE_DMA(6); |
| break; |
| |
| case 120: |
| // Stop sprite DMA |
| vicBusAccessCycleStart = 0; |
| spriteDMAmask = 0; |
| for (unsigned int i = 0; i < 8; i++) { |
| mob[i].sdb[1].dwSrDmaBuf = mob[i].sdb[0].dwSrDmaBuf; |
| } |
| break; |
| |
| case 122: |
| HBlanking = false; |
| if (beamy == 247) { |
| attribFetch = false; |
| } |
| if (BadLine) |
| vicBusAccessCycleStart = CycleCounter; |
| //fprintf(stderr, "Line %03i - AttribFetch:%i Badline:%i VSA:%i VSC:%i Screen:%i Y=%03X YSCR=%X\n", beamy, attribFetch, |
| // BadLine, VertSubActive, vertSubCount, ScreenOn, beamy, vshift); |
| break; |
| |
| case 126: |
| beamx = 0; |
| case 0: |
| if (BadLine && !delayedDMA) { |
| vertSubCount = 0; |
| } |
| CharacterPosition = CharacterPositionReload; |
| break; |
| |
| case 2: |
| if (BadLine && !delayedDMA) { |
| if (CharacterPosition >= 0x03d9) { |
| memcpy(chrbuf, VideoBase + CharacterPosition, 0x400 - CharacterPosition); |
| memcpy(chrbuf + 0x400 - CharacterPosition, VideoBase, (CharacterPosition + 40) & 0x03FF); |
| } else { |
| memcpy(chrbuf, VideoBase + CharacterPosition, 40); |
| } |
| //dmaCount = 40; |
| } |
| break; |
| |
| case 4: |
| stopSpriteDMA(); |
| break; |
| |
| case 6: |
| if (ScreenOn) { |
| SideBorderFlipFlop = true; |
| if (nrwscr) { |
| CharacterWindow = true; |
| if (hshift) |
| doXscrollChange(0, hshift); |
| } |
| x = 0; |
| } |
| break; |
| |
| case 8: |
| if (ScreenOn && !nrwscr) { |
| CharacterWindow = true; |
| } |
| break; |
| |
| case 82: |
| checkSpriteEnable(); |
| // On bad line with sprite 0 on, all CPU cycles are stolen |
| if (!checkSpriteDMA(0)) |
| vicBusAccessCycleStart = 0; |
| break; |
| |
| case 84: |
| if (VertSubActive && !delayedDMA && !dmaCount) |
| dmaCount = 40; |
| if (!nrwscr) |
| SideBorderFlipFlop = CharacterWindow = false; |
| break; |
| |
| case 86: |
| if (nrwscr) |
| SideBorderFlipFlop = CharacterWindow = false; |
| checkSpriteDMA(1); |
| break; |
| |
| case 88: |
| if (vertSubCount == 7) {// FIXME |
| CharacterPositionReload = (CharacterPosition + dmaCount) & 0x3FF; |
| dmaCount = 0; |
| VertSubActive = false; |
| } |
| if (BadLine) { |
| BadLine = 0; |
| delayedDMA = false; |
| VertSubActive = true; |
| } |
| if (VertSubActive) |
| vertSubCount = (vertSubCount + 1) & 7; |
| // |
| spriteReloadCounters(); |
| MOB_READ_ADDRESS(0); |
| DO_SPRITE_DMA(0); |
| break; |
| |
| case 90: |
| checkSpriteDMA(2); |
| break; |
| |
| case 92: |
| MOB_READ_ADDRESS(1); |
| DO_SPRITE_DMA(1); |
| STOP_SPRITE_DMA(0); |
| break; |
| |
| case 94: |
| checkSpriteDMA(3); |
| break; |
| |
| case 96: |
| MOB_READ_ADDRESS(2); |
| DO_SPRITE_DMA(2); |
| STOP_SPRITE_DMA(1); |
| break; |
| |
| case 98: |
| checkSpriteDMA(4); |
| HBlanking = true; |
| break; |
| } |
| |
| // CPU clocking |
| if (!vicBusAccessCycleStart) |
| cpuptr->process(); |
| else if (CycleCounter - vicBusAccessCycleStart < 3) |
| cpuptr->stopcycle(); |
| |
| // 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; |
| } |
| } |
| scrptr += 8; |
| // |
| cia[0].countTimers(); |
| cia[1].countTimers(); |
| // |
| CycleCounter += 1; |
| // |
| if (tap->isMotorOn()) { |
| if (tap->getFallingEdgeState(CycleCounter)) { |
| cia[0].icr |= 0x10; |
| cia[0].setIRQflag(cia[0].icr & cia[0].irq_mask); |
| tap->resetFallingEdge(CycleCounter); |
| } |
| } |
| // |
| 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); |
| } |
| |
| inline void Vic2mem::doXscrollChange(unsigned int oldXscr, unsigned int newXscr) |
| { |
| if (newXscr > oldXscr) { |
| unsigned char a; |
| switch (scrattr) { |
| default: |
| a = mcol[0]; |
| break; |
| case GRAPHMODE: |
| a = (chrbuf[x] & 0x0F) | 0x40; |
| break; |
| } |
| memset(scrptr + oldXscr, a, newXscr - oldXscr); |
| } |
| } |
| |
| inline unsigned char Vic2mem::readFloatingBus(unsigned int adr) |
| { |
| unsigned int prevcycle = (124 + beamx) % 126; |
| unsigned char c = cycleLookup[BadLine][prevcycle]; |
| switch (c) { |
| default: |
| case 'p': |
| return cpuptr->getcins(); |
| break; |
| } |
| } |
| |
| // renders hires text |
| inline void Vic2mem::hi_text() |
| { |
| unsigned char charcol; |
| unsigned char mask; |
| unsigned char *wbuffer = scrptr + hshift; |
| |
| if (VertSubActive) { |
| charcol = colorRAM[(CharacterPosition + x) & 0x03FF] & 0x0F; |
| mask = cset[(chrbuf[x] << 3) | vertSubCount]; |
| } else { |
| charcol = 0; |
| mask = vicBase[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) { |
| charcol = colorRAM[(CharacterPosition + x) & 0x03FF] & 0x0F; |
| chr = chrbuf[x]; |
| mask = cset[((chr & 0x3F) << 3) | vertSubCount]; |
| chr >>= 6; |
| } else { |
| mask = vicBase[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) & 0x03FF] & 0x0F; |
| chr = chrbuf[x]; |
| mask = cset[(chr << 3) | vertSubCount]; |
| } else { |
| mask = vicBase[0x3FFF]; |
| charcol = 0; |
| } |
| |
| if (charcol & 8) { // if character is multicolored |
| |
| mcol[3] = charcol & 0x07; |
| |
| 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 Vic2mem::hi_bitmap() |
| { |
| unsigned char mask; |
| unsigned char *wbuffer = scrptr + hshift; |
| unsigned char hcol0; |
| unsigned char hcol1; |
| |
| if (VertSubActive) { |
| // get the actual color attributes |
| hcol0 = (chrbuf[x] & 0x0F) | 0x40; |
| hcol1 = chrbuf[x] >> 4; |
| mask = grbank[(((CharacterPosition + x) << 3) & 0x1FF8) | vertSubCount]; |
| } else { |
| hcol0 = 0x40; |
| hcol1 = 0; |
| mask = vicBase[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; |
| |
| if (VertSubActive) { |
| unsigned int cp = (CharacterPosition + x) & 0x03FF; |
| bmmcol[1] = (chrbuf[x] >> 4) | 0x40; |
| bmmcol[2] = chrbuf[x] & 0x0F; |
| bmmcol[3] = colorRAM[cp] & 0x0F; |
| mask = grbank[(cp << 3) | vertSubCount]; |
| } else {// FIXME |
| bmmcol[1] = 0x40; |
| bmmcol[2] = bmmcol[3] = 0; |
| mask = vicBase[0x3FFF]; |
| } |
| |
| 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]; |
| } |
| |
| // when multi and extended color modes are all on the screen is blank |
| inline void Vic2mem::mcec() |
| { |
| unsigned char *wbuffer = scrptr + hshift; |
| unsigned char mask, charcol; |
| unsigned char imcol[4] = { 0x40, 0x40, 0, 0 }; |
| |
| if (VertSubActive) { |
| mask = cset[((chrbuf[x] & 0x3F) << 3) | vertSubCount]; |
| charcol = colorRAM[(CharacterPosition + x) & 0x03FF] & 0x0F; |
| } else { |
| mask = vicBase[0x39FF]; |
| charcol = 0; |
| } |
| |
| if (charcol & 8) { |
| wbuffer[0] = wbuffer[1] = imcol[mask >> 6]; |
| wbuffer[2] = wbuffer[3] = imcol[(mask & 0x30) >> 4]; |
| wbuffer[4] = wbuffer[5] = imcol[(mask & 0x0C) >> 2]; |
| wbuffer[6] = wbuffer[7] = imcol[mask & 0x03]; |
| } else { |
| wbuffer[0] = (mask & 0x80) ? 0 : 0x40; |
| wbuffer[1] = (mask & 0x40) ? 0 : 0x40; |
| wbuffer[2] = (mask & 0x20) ? 0 : 0x40; |
| wbuffer[3] = (mask & 0x10) ? 0 : 0x40; |
| wbuffer[4] = (mask & 0x08) ? 0 : 0x40; |
| wbuffer[5] = (mask & 0x04) ? 0 : 0x40; |
| wbuffer[6] = (mask & 0x02) ? 0 : 0x40; |
| wbuffer[7] = (mask & 0x01) ? 0 : 0x40; |
| } |
| } |
| |
| // renders hires bitmap graphics |
| inline void Vic2mem::hi_bmec() |
| { |
| unsigned char mask; |
| unsigned char *wbuffer = scrptr + hshift; |
| unsigned char hcol0 = 0x40; |
| unsigned char hcol1 = 0; |
| |
| if (VertSubActive) { |
| mask = grbank[(((CharacterPosition + x) << 3) & 0x19FF) | vertSubCount]; |
| } else { |
| mask = vicBase[0x39FF]; |
| } |
| |
| 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; |
| } |
| |
| inline void Vic2mem::mc_bmec() |
| { |
| unsigned char mask; |
| unsigned char *wbuffer = scrptr + hshift; |
| unsigned char imcol[4] = { 0x40, 0x40, 0, 0 }; |
| |
| if (VertSubActive) { |
| unsigned int cp = (CharacterPosition + x) & 0x039F; |
| mask = grbank[(cp << 3) | vertSubCount]; |
| } else { |
| mask = vicBase[0x39FF]; |
| } |
| |
| wbuffer[0]= wbuffer[1] = imcol[mask >> 6]; |
| wbuffer[2]= wbuffer[3] = imcol[(mask & 0x30) >> 4 ]; |
| wbuffer[4]= wbuffer[5] = imcol[(mask & 0x0C) >> 2 ]; |
| wbuffer[6]= wbuffer[7] = imcol[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; |
| // illegal modes |
| case GRAPHMODE|EXTCOLOR: |
| hi_bmec(); |
| break; |
| case GRAPHMODE|EXTCOLOR|MULTICOLOR: |
| mc_bmec(); |
| break; |
| case EXTCOLOR|MULTICOLOR: |
| default: |
| mcec(); |
| break; |
| } |
| } |
| |
| void Vic2mem::renderSprite(unsigned char *in, unsigned char *out, Mob &m, unsigned int cx, const unsigned int six) |
| { |
| unsigned int i; |
| const unsigned int priority = m.priority; |
| |
| if (!m.multicolor) { |
| const unsigned char spc = m.color; |
| if (m.expandX) { |
| // sprite X expansion, hires mode |
| for(i = 0; i < 3; i++, out += 16, cx += 16) { |
| unsigned char data = in[i]; |
| if (data & 0x80) { |
| spriteCollisions[cx] |= six; |
| spriteCollisions[cx + 1] |= six; |
| MOB_DO_PIXEL(0, spc); |
| MOB_DO_PIXEL(1, spc); |
| } |
| if (data & 0x40) { |
| spriteCollisions[cx + 2] |= six; |
| spriteCollisions[cx + 3] |= six; |
| MOB_DO_PIXEL(2, spc); |
| MOB_DO_PIXEL(3, spc); |
| } |
| if (data & 0x20) { |
| spriteCollisions[cx + 4] |= six; |
| spriteCollisions[cx + 5] |= six; |
| MOB_DO_PIXEL(4, spc); |
| MOB_DO_PIXEL(5, spc); |
| } |
| if (data & 0x10) { |
| spriteCollisions[cx + 6] |= six; |
| spriteCollisions[cx + 7] |= six; |
| MOB_DO_PIXEL(6, spc); |
| MOB_DO_PIXEL(7, spc); |
| } |
| if (data & 0x08) { |
| spriteCollisions[cx + 8] |= six; |
| spriteCollisions[cx + 9] |= six; |
| MOB_DO_PIXEL(8, spc); |
| MOB_DO_PIXEL(9, spc); |
| } |
| if (data & 0x04) { |
| spriteCollisions[cx + 10] |= six; |
| spriteCollisions[cx + 11] |= six; |
| MOB_DO_PIXEL(10, spc); |
| MOB_DO_PIXEL(11, spc); |
| } |
| if (data & 0x02) { |
| spriteCollisions[cx + 12] |= six; |
| spriteCollisions[cx + 13] |= six; |
| MOB_DO_PIXEL(12, spc); |
| MOB_DO_PIXEL(13, spc); |
| } |
| if (data & 0x01) { |
| spriteCollisions[cx + 14] |= six; |
| spriteCollisions[cx + 15] |= six; |
| MOB_DO_PIXEL(14, spc); |
| MOB_DO_PIXEL(15, spc); |
| } |
| } |
| } else { |
| // sprite, normal size, hires mode |
| for(i = 0; i < 3; i++, out += 8, cx += 8) { |
| const unsigned char data = in[i]; |
| if (data & 0x80) { |
| spriteCollisions[cx] |= six; |
| MOB_DO_PIXEL(0, spc); |
| } |
| if (data & 0x40) { |
| spriteCollisions[cx + 1] |= six; |
| MOB_DO_PIXEL(1, spc); |
| } |
| if (data & 0x20) { |
| spriteCollisions[cx + 2] |= six; |
| MOB_DO_PIXEL(2, spc); |
| } |
| if (data & 0x10) { |
| spriteCollisions[cx + 3] |= six; |
| MOB_DO_PIXEL(3, spc); |
| } |
| if (data & 0x08) { |
| spriteCollisions[cx + 4] |= six; |
| MOB_DO_PIXEL(4, spc); |
| } |
| if (data & 0x04) { |
| spriteCollisions[cx + 5] |= six; |
| MOB_DO_PIXEL(5, spc); |
| } |
| if (data & 0x02) { |
| spriteCollisions[cx + 6] |= six; |
| MOB_DO_PIXEL(6, spc); |
| } |
| if (data & 0x01) { |
| spriteCollisions[cx + 7] |= six; |
| MOB_DO_PIXEL(7, spc); |
| } |
| } |
| } |
| } else { |
| mobExtCol[2] = m.color; |
| if (m.expandX) { |
| unsigned int cDword = (six << 24) | (six << 16) | (six << 8) | six; |
| // sprite X expansion, multi mode |
| for(i = 0; i < 3; i++, out += 16, cx += 16) { |
| const unsigned char data = in[i]; |
| unsigned int bitlet = data >> 6; |
| if (bitlet) { |
| *((unsigned int*)(spriteCollisions + cx)) |= cDword; |
| MOB_DO_PIXEL(0, mobExtCol[bitlet]); |
| MOB_DO_PIXEL(1, mobExtCol[bitlet]); |
| MOB_DO_PIXEL(2, mobExtCol[bitlet]); |
| MOB_DO_PIXEL(3, mobExtCol[bitlet]); |
| } |
| bitlet = data & 0x30; |
| if (bitlet) { |
| *((unsigned int*)(spriteCollisions + cx + 4)) |= cDword; |
| MOB_DO_PIXEL(4, mobExtCol[bitlet >> 4]); |
| MOB_DO_PIXEL(5, mobExtCol[bitlet >> 4]); |
| MOB_DO_PIXEL(6, mobExtCol[bitlet >> 4]); |
| MOB_DO_PIXEL(7, mobExtCol[bitlet >> 4]); |
| } |
| bitlet = data & 0x0C; |
| if (bitlet) { |
| *((unsigned int*)(spriteCollisions + cx + 8)) |= cDword; |
| MOB_DO_PIXEL(8, mobExtCol[bitlet >> 2]); |
| MOB_DO_PIXEL(9, mobExtCol[bitlet >> 2]); |
| MOB_DO_PIXEL(10, mobExtCol[bitlet >> 2]); |
| MOB_DO_PIXEL(11, mobExtCol[bitlet >> 2]); |
| } |
| bitlet = data & 0x03; |
| if (bitlet) { |
| *((unsigned int*)(spriteCollisions + cx + 12)) |= cDword; |
| MOB_DO_PIXEL(12, mobExtCol[bitlet]); |
| MOB_DO_PIXEL(13, mobExtCol[bitlet]); |
| MOB_DO_PIXEL(14, mobExtCol[bitlet]); |
| MOB_DO_PIXEL(15, mobExtCol[bitlet]); |
| } |
| } |
| } else { |
| // normal size, multicolor |
| for(i = 0; i < 3; i++, out += 8, cx += 8) { |
| const unsigned char data = in[i]; |
| unsigned int bitlet = data >> 6; |
| if (bitlet) { |
| spriteCollisions[cx] |= six; |
| spriteCollisions[cx + 1] |= six; |
| MOB_DO_PIXEL(0, mobExtCol[bitlet]); |
| MOB_DO_PIXEL(1, mobExtCol[bitlet]); |
| } |
| bitlet = data & 0x30; |
| if (bitlet) { |
| spriteCollisions[cx + 2] |= six; |
| spriteCollisions[cx + 3] |= six; |
| MOB_DO_PIXEL(2, mobExtCol[bitlet >> 4]); |
| MOB_DO_PIXEL(3, mobExtCol[bitlet >> 4]); |
| } |
| bitlet = data & 0x0C; |
| if (bitlet) { |
| spriteCollisions[cx + 4] |= six; |
| spriteCollisions[cx + 5] |= six; |
| MOB_DO_PIXEL(4, mobExtCol[bitlet >> 2]); |
| MOB_DO_PIXEL(5, mobExtCol[bitlet >> 2]); |
| } |
| bitlet = data & 0x03; |
| if (bitlet) { |
| spriteCollisions[cx + 6] |= six; |
| spriteCollisions[cx + 7] |= six; |
| MOB_DO_PIXEL(6, mobExtCol[bitlet]); |
| MOB_DO_PIXEL(7, mobExtCol[bitlet]); |
| } |
| } |
| } |
| } |
| } |
| |
| inline void Vic2mem::checkSpriteEnable() |
| { |
| #if NEWSDMA |
| unsigned int i = 7; |
| do { |
| if (mob[i].enabled && mob[i].y == beamy /*&& !mob[i].dmaState*/) { |
| mob[i].dmaState = true; |
| mob[i].dataCountReload = 0; |
| mob[i].dataCount = 0; |
| mob[i].reloadFlipFlop = mob[i].expandY ^ 1; |
| } |
| } while (i--); |
| #endif |
| } |
| |
| inline bool Vic2mem::checkSpriteDMA(unsigned int i) |
| { |
| #if !NEWSDMA |
| if (mob[i].enabled && mob[i].y == prevY && !mob[i].dmaState) { |
| mob[i].dmaState = true; |
| mob[i].dataCountReload = 0; |
| mob[i].dataCount = 0; |
| mob[i].reloadFlipFlop = mob[i].expandY; |
| } |
| #endif |
| if (mob[i].dmaState) { |
| spriteDMAmask |= (1 << i); |
| if (!vicBusAccessCycleStart) |
| vicBusAccessCycleStart = CycleCounter; |
| return true; |
| } |
| return false; |
| } |
| |
| inline void Vic2mem::stopSpriteDMA() |
| { |
| #if NEWSDMA |
| unsigned int i = 7; |
| |
| do { |
| unsigned int &dc = mob[i].dataCount; |
| // check end of sprite DMA |
| if (mob[i].dataCountReload == 0x3F) { |
| mob[i].dmaState = mob[i].rendering = false; |
| mob[i].dataCountReload = dc; |
| } |
| } while (i--); |
| #endif |
| } |
| |
| inline void Vic2mem::spriteReloadCounters() |
| { |
| #if NEWSDMA |
| unsigned int i = 7; |
| |
| do { |
| if (mob[i].dmaState) { |
| unsigned int &dc = mob[i].dataCount; |
| unsigned int &flipFlop = mob[i].reloadFlipFlop; |
| |
| flipFlop ^= 1; |
| if (flipFlop) { |
| mob[i].dataCountReload = dc; |
| // set flipflop to Y expension bit |
| flipFlop = mob[i].expandY; |
| } |
| } |
| } while (i--); |
| #endif |
| } |
| |
| inline void Vic2mem::drawSpritesPerLine(unsigned char *lineBuf) |
| { |
| unsigned int i = 7; |
| |
| do { |
| #if !NEWSDMA |
| if (mob[i].dmaState) { |
| unsigned int &dc = mob[i].dataCount; |
| unsigned int &dcReload = mob[i].dataCountReload; |
| unsigned int &flipFlop = mob[i].reloadFlipFlop; |
| |
| flipFlop ^= 1; |
| if (flipFlop) { |
| dcReload = dc; |
| // set flipflop to Y expension bit |
| flipFlop = mob[i].expandY; |
| } |
| dc = (dcReload + 3) & 0x3F; |
| const unsigned int tvX = RASTERX2TVCOL(mob[i].x); |
| unsigned char *d = vicBase + mob[i].dataAddress + dcReload; |
| unsigned char *p = lineBuf + tvX; |
| renderSprite(d, p, mob[i], tvX, 1 << i); |
| // check end of sprite DMA |
| if (dc == 0x3F) { |
| mob[i].dmaState = false; |
| dcReload = dc; |
| } |
| #else |
| if (mob[i].rendering) { |
| if (mob[i].x < VIC_PIXELS_PER_ROW) { |
| const unsigned int tvX = RASTERX2TVCOL(mob[i].x); |
| unsigned char *d = mob[i].sdb[1].shiftRegBuf; |
| unsigned char *p = lineBuf + tvX; |
| renderSprite(d, p, mob[i], tvX, 1 << i); |
| } |
| if (!mob[i].dmaState) { |
| mob[i].rendering = false; |
| } |
| } else if (mob[i].dmaState) { |
| mob[i].rendering = true; |
| #endif |
| } |
| } while (i--); |
| // check collisions |
| for(i = 0; i < VIC_PIXELS_PER_ROW; i++) { |
| if (spriteCollisions[i]) { |
| const unsigned char newReg = spriteCollisionReg | collisionLookup[spriteCollisions[i]]; |
| if (!spriteCollisionReg && newReg) { |
| vicReg[0x19] |= ((vicReg[0x1A] & 4) << 5) | 4; |
| checkIRQflag(); |
| } |
| spriteCollisionReg = newReg; |
| spriteCollisions[i] = 0; |
| } |
| } |
| } |