/* * Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ package jace.hardware.mockingboard; import jace.core.Computer; import jace.core.Device; /** * Implementation of 6522 VIA chip * * @author Brendan Robert (BLuRry) brendan.robert@gmail.com */ public abstract class R6522 extends Device { public R6522(Computer computer) { super(computer); timer1freerun = true; timer1running = true; timer1latch = 0x1fff; timer1interruptEnabled = false; setRun(true); } // 6522 VIA // http://www.applevault.com/twiki/Main/Mockingboard/6522.pdf // I/O registers public static enum Register { ORB(0), // Output Register B ORA(1), // Output Register A DDRB(2),// Data direction reg B DDRA(3),// Data direction reg A T1CL(4),// T1 low-order latches (low-order counter for read operations) T1CH(5),// T1 high-order counter T1LL(6),// T1 low-order latches T1LH(7),// T1 high-order latches T2CL(8),// T2 low-order latches (low-order counter for read operations) T2CH(9),// T2 high-order counter SR(10),// Shift register ACR(11),// Aux control register PCR(12),// Perripheral control register IFR(13),// Interrupt flag register IER(14),// Interrupt enable register ORAH(15);// Output Register A (no handshake) int val; Register(int v) { val = v; } static public Register fromInt(int i) { for (Register r : Register.values()) { if (r.val == i) { return r; } } return null; } } // state variables public int oraReg = 0; public int iraReg = 0; public int orbReg = 0; public int irbReg = 0; // DDRA and DDRB must be set to output for mockingboard to do anything // Common values for this are FF for DDRA and 7 for DDRB // DDRB bits 0-2 are used to control AY chips but bits 3-7 are not connected. // that's why it is common to see mockingboard drivers init the port with a 7 public int dataDirectionA = 0; public int dataDirectionB = 0; // Though this is necessary for a complete emulation of the 6522, it isn't needed by the mockingboard // set by bit 0 of ACR // public boolean latchEnabledA = false; // set by bit 1 of ACR // public boolean latchEnabledB = false; //Bits 2,3,4 of ACR // static public enum ShiftRegisterControl { // interruptDisabled(0), // shiftInT2(4), // shiftIn02(8), // shiftInExt(12), // shiftOutFree(16), // shiftOutT2(20), // shiftOut02(24), // shiftOutExt(28); // // int val; // private ShiftRegisterControl(int v) { // val = v; // } // // public static ShiftRegisterControl fromBits(int b) { // b=b&28; // for (ShiftRegisterControl s : values()) { // if (s.val == b) return s; // } // return null; // } // } // public ShiftRegisterControl shiftMode = ShiftRegisterControl.interruptDisabled; // //Bit 5 of ACR (false = timed interrupt, true = count down pulses on PB6) // public boolean t2countPulses = false; // //Bit 6 of ACR (true = continuous, false = one-shot) // public boolean t1continuous = false; // //Bit 7 of ACR (true = enable PB7, false = interruptDisabled) // public boolean t1enablePB7 = false; // // NOTE: Mockingboard did not use PB6 or PB7, they are not connected to anything public boolean timer1interruptEnabled = true; public boolean timer1IRQ = false; // True if timer interrupt flag is set public int timer1latch = 0; public int timer1counter = 0; public boolean timer1freerun = false; public boolean timer1running = false; public boolean timer2interruptEnabled = true; public boolean timer2IRQ = false; // True if timer interrupt flag is set public int timer2latch = 0; public int timer2counter = 0; public boolean timer2running = false; @Override protected String getDeviceName() { return "6522 VIA Chip"; } @Override public void tick() { if (timer1running) { timer1counter--; if (timer1counter < 0) { timer1counter = timer1latch; if (!timer1freerun) { timer1running = false; } if (timer1interruptEnabled) { // System.out.println("Timer 1 generated interrupt"); timer1IRQ = true; computer.getCpu().generateInterrupt(); } } } if (timer2running) { timer2counter--; if (timer2counter < 0) { timer2running = false; timer2counter = timer2latch; if (timer2interruptEnabled) { timer2IRQ = true; computer.getCpu().generateInterrupt(); } } } if (!timer1running && !timer2running) { setRun(false); } } @Override public void attach() { // Start chip } @Override public void reconfigure() { // Reset } public void writeRegister(int reg, int val) { int value = val & 0x0ff; Register r = Register.fromInt(reg); // System.out.println("Writing "+(value&0x0ff)+" to register "+r.toString()); switch (r) { case ORB: if (dataDirectionB == 0) { break; } sendOutputB(value & dataDirectionB); break; case ORA: // case ORAH: if (dataDirectionA == 0) { break; } sendOutputA(value & dataDirectionA); break; case DDRB: dataDirectionB = value; break; case DDRA: dataDirectionA = value; break; case T1CL: case T1LL: timer1latch = (timer1latch & 0x0ff00) | value; break; case T1CH: timer1latch = (timer1latch & 0x0ff) | (value << 8); timer1IRQ = false; timer1counter = timer1latch; timer1running = true; setRun(true); break; case T1LH: timer1latch = (timer1latch & 0x0ff) | (value << 8); timer1IRQ = false; break; case T2CL: timer2latch = (timer2latch & 0x0ff00) | value; break; case T2CH: timer2latch = (timer2latch & 0x0ff) | (value << 8); timer2IRQ = false; timer2counter = timer2latch; timer2running = true; setRun(true); break; case SR: // SHIFT REGISTER NOT IMPLEMENTED break; case ACR: // SHIFT REGISTER NOT IMPLEMENTED timer1freerun = (value & 64) != 0; if (timer1freerun) { timer1running = true; setRun(true); } break; case PCR: // TODO: Implement if Votrax (SSI) is to be supported break; case IFR: if ((value & 64) != 0) { timer1IRQ = false; } if ((value & 32) != 0) { timer2IRQ = false; } break; case IER: boolean enable = (value & 128) != 0; if ((value & 64) != 0) { timer1interruptEnabled = enable; } if ((value & 32) != 0) { timer2interruptEnabled = enable; } break; default: } } // Whatever uses 6522 will want to know when it is outputting values // So to hook that in, these abstract methods will be defined as appropriate public abstract void sendOutputA(int value); public abstract void sendOutputB(int value); public int readRegister(int reg) { Register r = Register.fromInt(reg); // System.out.println("Reading register "+r.toString()); switch (r) { case ORB: if (dataDirectionB == 0x0ff) { break; } return receiveOutputB() & (dataDirectionB ^ 0x0ff); case ORA: case ORAH: if (dataDirectionA == 0x0ff) { break; } return receiveOutputA() & (dataDirectionA ^ 0x0ff); case DDRB: return dataDirectionB; case DDRA: return dataDirectionA; case T1CL: timer1IRQ = false; return timer1counter & 0x0ff; case T1CH: return (timer1counter & 0x0ff00) >> 8; case T1LL: return timer1latch & 0x0ff; case T1LH: return (timer1latch & 0x0ff00) >> 8; case T2CL: timer2IRQ = false; return timer2counter & 0x0ff; case T2CH: return (timer2counter & 0x0ff00) >> 8; case SR: // SHIFT REGISTER NOT IMPLEMENTED return 0; case ACR: // SHIFT REGISTER NOT IMPLEMENTED if (timer1freerun) { return 64; } return 0; case PCR: break; case IFR: int val = 0; if (timer1IRQ) { val |= 64; } if (timer2IRQ) { val |= 32; } if (val != 0) { val |= 128; } return val; case IER: val = 128; if (timer1interruptEnabled) { val |= 64; } if (timer2interruptEnabled) { val |= 32; } return val; } return 0; } public abstract int receiveOutputA(); public abstract int receiveOutputB(); }