Added support for keyboard shift key down as game port button 2 [TN9].

Refactored memory to use peripheral cards; added place holder for NoSlotClock.

--HG--
extra : convert_revision : svn%3Affd33b8c-2492-42e0-bdc5-587b920b7d6d/trunk%4050808
This commit is contained in:
Sean Fausett 2010-08-27 23:46:11 +00:00
parent 5485e01e02
commit ee4c69dddf
22 changed files with 711 additions and 97 deletions

View File

@ -2,7 +2,7 @@
<Dictionary>
<Acronyms>
<CasingExceptions>
<Acronym>CXXX</Acronym>
<Acronym>Io</Acronym>
<Acronym>RPC</Acronym>
</CasingExceptions>
</Acronyms>

251
Virtu/DiskIIController.cs Normal file
View File

@ -0,0 +1,251 @@
using System.Diagnostics.CodeAnalysis;
using Jellyfish.Library;
using Jellyfish.Virtu.Services;
using Jellyfish.Virtu.Settings;
namespace Jellyfish.Virtu
{
public sealed class DiskIIController : PeripheralCard
{
public DiskIIController(Machine machine) :
base(machine)
{
}
public override void Initialize()
{
var romStream = StorageService.GetResourceStream("Roms/DiskII.rom", 0x0100);
romStream.ReadBlock(_romRegionC1C7, 0x0000, 0x0100);
_drives[0].InsertDisk("Default.dsk", StorageService.GetResourceStream("Disks/Default.dsk", 0x23000), false);
#if WINDOWS
var settings = Machine.Settings.DiskII;
if (settings.Disk1.Name.Length > 0)
{
_drives[0].InsertDisk(settings.Disk1.Name, settings.Disk1.IsWriteProtected);
}
if (settings.Disk2.Name.Length > 0)
{
_drives[1].InsertDisk(settings.Disk2.Name, settings.Disk2.IsWriteProtected);
}
#endif
}
public override void Reset()
{
_phaseStates = 0;
SetMotorOn(false);
SetDriveNumber(0);
_loadMode = false;
_writeMode = false;
}
public override void Uninitialize()
{
Flush();
}
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
public override int ReadIoRegionC0C0(int address)
{
switch (address & 0xF)
{
case 0x0: case 0x1: case 0x2: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7:
SetPhase(address);
break;
case 0x8:
SetMotorOn(false);
break;
case 0x9:
SetMotorOn(true);
break;
case 0xA:
SetDriveNumber(0);
break;
case 0xB:
SetDriveNumber(1);
break;
case 0xC:
_loadMode = false;
if (_motorOn)
{
if (!_writeMode)
{
return _latch = _drives[_driveNumber].Read();
}
else
{
WriteLatch();
}
}
break;
case 0xD:
_loadMode = true;
if (_motorOn && !_writeMode)
{
// write protect is forced if phase 1 is on [F9.7]
_latch &= 0x7F;
if (_drives[_driveNumber].IsWriteProtected ||
(_phaseStates & Phase1On) != 0)
{
_latch |= 0x80;
}
}
break;
case 0xE:
_writeMode = false;
break;
case 0xF:
_writeMode = true;
break;
}
if ((address & 1) == 0)
{
// only even addresses return the latch
if (_motorOn)
{
return _latch;
}
// simple hack to fool DOS SAMESLOT drive spin check (usually at $BD34)
_driveSpin = !_driveSpin;
return _driveSpin ? 0x7E : 0x7F;
}
return ReadFloatingBus();
}
public override int ReadIoRegionC1C7(int address)
{
return _romRegionC1C7[address & 0xFF];
}
public override void WriteIoRegionC0C0(int address, int data)
{
switch (address & 0xF)
{
case 0x0: case 0x1: case 0x2: case 0x3: case 0x4: case 0x5: case 0x6: case 0x7:
SetPhase(address);
break;
case 0x8:
SetMotorOn(false);
break;
case 0x9:
SetMotorOn(true);
break;
case 0xA:
SetDriveNumber(0);
break;
case 0xB:
SetDriveNumber(1);
break;
case 0xC:
_loadMode = false;
if (_writeMode)
{
WriteLatch();
}
break;
case 0xD:
_loadMode = true;
break;
case 0xE:
_writeMode = false;
break;
case 0xF:
_writeMode = true;
break;
}
if (_motorOn && _writeMode)
{
if (_loadMode)
{
// any address writes latch for sequencer LD; OE1/2 irrelevant ['323 datasheet]
_latch = data;
}
}
}
private void WriteLatch()
{
// write protect is forced if phase 1 is on [F9.7]
if ((_phaseStates & Phase1On) == 0)
{
_drives[_driveNumber].Write(_latch);
}
}
private void Flush()
{
_drives[_driveNumber].FlushTrack();
}
private void SetDriveNumber(int driveNumber)
{
if (_driveNumber != driveNumber)
{
Flush();
_driveNumber = driveNumber;
}
}
private void SetMotorOn(bool state)
{
if (_motorOn && !state)
{
Flush();
}
_motorOn = state;
}
private void SetPhase(int address)
{
int phase = (address >> 1) & 0x3;
int state = address & 1;
_phaseStates &= ~(1 << phase);
_phaseStates |= (state << phase);
if (_motorOn)
{
_drives[_driveNumber].ApplyPhaseChange(_phaseStates);
}
}
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
public DiskIIDrive[] Drives { get { return _drives; } }
private const int Phase0On = 1 << 0;
private const int Phase1On = 1 << 1;
private const int Phase2On = 1 << 2;
private const int Phase3On = 1 << 3;
private DiskIIDrive[] _drives = new DiskIIDrive[] { new DiskIIDrive(), new DiskIIDrive() };
private int _latch;
private int _phaseStates;
private bool _motorOn;
private int _driveNumber;
private bool _loadMode;
private bool _writeMode;
private bool _driveSpin;
private byte[] _romRegionC1C7 = new byte[0x0100];
}
}

120
Virtu/DiskIIDrive.cs Normal file
View File

@ -0,0 +1,120 @@
using System;
using System.IO;
using System.Security;
using Jellyfish.Library;
namespace Jellyfish.Virtu
{
public sealed class DiskIIDrive
{
public DiskIIDrive()
{
DriveArmStepDelta[0] = new int[] { 0, 0, 1, 1, 0, 0, 1, 1, -1, -1, 0, 0, -1, -1, 0, 0 }; // phase 0
DriveArmStepDelta[1] = new int[] { 0, -1, 0, -1, 1, 0, 1, 0, 0, -1, 0, -1, 1, 0, 1, 0 }; // phase 1
DriveArmStepDelta[2] = new int[] { 0, 0, -1, -1, 0, 0, -1, -1, 1, 1, 0, 0, 1, 1, 0, 0 }; // phase 2
DriveArmStepDelta[3] = new int[] { 0, 1, 0, 1, -1, 0, -1, 0, 0, 1, 0, 1, -1, 0, -1, 0 }; // phase 3
}
[SecurityCritical]
public void InsertDisk(string fileName, bool isWriteProtected)
{
using (var stream = File.OpenRead(fileName))
{
InsertDisk(fileName, stream, isWriteProtected);
}
}
public void InsertDisk(string name, Stream stream, bool isWriteProtected)
{
FlushTrack();
// TODO handle null param/empty string for eject, or add Eject()
_disk = Disk525.CreateDisk(name, stream.ReadAllBytes(), isWriteProtected);
_trackLoaded = false;
}
public void ApplyPhaseChange(int phaseState)
{
// step the drive head according to stepper magnet changes
int delta = DriveArmStepDelta[_trackNumber & 0x3][phaseState];
if (delta != 0)
{
int newTrackNumber = MathHelpers.Clamp(_trackNumber + delta, 0, TrackNumberMax);
if (newTrackNumber != _trackNumber)
{
FlushTrack();
_trackNumber = newTrackNumber;
_trackOffset = 0;
_trackLoaded = false;
}
}
}
public int Read()
{
if (LoadTrack())
{
int data = _trackData[_trackOffset++];
if (_trackOffset >= Disk525.TrackSize)
{
_trackOffset = 0;
}
return data;
}
return _random.Next(0x01, 0xFF);
}
public void Write(int data)
{
if (LoadTrack())
{
_trackChanged = true;
_trackData[_trackOffset++] = (byte)data;
if (_trackOffset >= Disk525.TrackSize)
{
_trackOffset = 0;
}
}
}
private bool LoadTrack()
{
if (!_trackLoaded && (_disk != null))
{
_disk.ReadTrack(_trackNumber, 0, _trackData);
_trackLoaded = true;
}
return _trackLoaded;
}
public void FlushTrack()
{
if (_trackChanged)
{
_disk.WriteTrack(_trackNumber, 0, _trackData);
_trackChanged = false;
}
}
public bool IsWriteProtected { get { return _disk.IsWriteProtected; } }
private const int TrackNumberMax = 0x44;
private const int PhaseCount = 4;
private readonly int[][] DriveArmStepDelta = new int[PhaseCount][];
private Disk525 _disk;
private bool _trackLoaded;
private bool _trackChanged;
private int _trackNumber;
private int _trackOffset;
private byte[] _trackData = new byte[Disk525.TrackSize];
private Random _random = new Random();
}
}

View File

@ -42,7 +42,7 @@ namespace Jellyfish.Virtu
{
var settings = Machine.Settings.GamePort;
return (_gamePortService.IsButton2Down ||
return (_gamePortService.IsButton2Down || _keyboardService.IsShiftKeyDown || // [TN9]
(settings.UseKeyboard && (settings.Key.Button2 > 0) && _keyboardService.IsKeyDown(settings.Key.Button2)));
}

View File

@ -19,13 +19,24 @@ namespace Jellyfish.Virtu
Cpu = new Cpu(this);
Memory = new Memory(this);
DiskII = new DiskII(this);
Keyboard = new Keyboard(this);
GamePort = new GamePort(this);
Cassette = new Cassette(this);
Speaker = new Speaker(this);
Video = new Video(this);
Components = new Collection<MachineComponent> { Cpu, Memory, DiskII, Keyboard, GamePort, Cassette, Speaker, Video };
NoSlotClock = new NoSlotClock();
var emptySlot = new PeripheralCard(this);
Slot1 = emptySlot;
Slot2 = emptySlot;
Slot3 = emptySlot;
Slot4 = emptySlot;
Slot5 = emptySlot;
Slot6 = new DiskIIController(this);
Slot7 = emptySlot;
Slots = new Collection<PeripheralCard> { null, Slot1, Slot2, Slot3, Slot4, Slot5, Slot6, Slot7 };
Components = new Collection<MachineComponent> { Cpu, Memory, Keyboard, GamePort, Cassette, Speaker, Video, Slot1, Slot2, Slot3, Slot4, Slot5, Slot6, Slot7 };
Thread = new Thread(Run) { Name = "Machine" };
}
@ -76,6 +87,20 @@ namespace Jellyfish.Virtu
}
}
public DiskIIController FindDiskIIController()
{
for (int i = 7; i >= 1; i--)
{
var diskII = Slots[i] as DiskIIController;
if (diskII != null)
{
return diskII;
}
}
return null;
}
private void Run() // machine thread
{
Components.ForEach(component => component.Initialize());
@ -108,12 +133,22 @@ namespace Jellyfish.Virtu
public Cpu Cpu { get; private set; }
public Memory Memory { get; private set; }
public DiskII DiskII { get; private set; }
public Keyboard Keyboard { get; private set; }
public GamePort GamePort { get; private set; }
public Cassette Cassette { get; private set; }
public Speaker Speaker { get; private set; }
public Video Video { get; private set; }
public NoSlotClock NoSlotClock { get; private set; }
public PeripheralCard Slot1 { get; private set; }
public PeripheralCard Slot2 { get; private set; }
public PeripheralCard Slot3 { get; private set; }
public PeripheralCard Slot4 { get; private set; }
public PeripheralCard Slot5 { get; private set; }
public PeripheralCard Slot6 { get; private set; }
public PeripheralCard Slot7 { get; private set; }
public Collection<PeripheralCard> Slots { get; private set; }
public Collection<MachineComponent> Components { get; private set; }
public Thread Thread { get; private set; }

View File

@ -81,12 +81,12 @@ namespace Jellyfish.Virtu
public override void Initialize()
{
_diskII = Machine.DiskII;
_keyboard = Machine.Keyboard;
_gamePort = Machine.GamePort;
_cassette = Machine.Cassette;
_speaker = Machine.Speaker;
_video = Machine.Video;
_noSlotClock = Machine.NoSlotClock;
var romStream = StorageService.GetResourceStream("Roms/AppleIIe.rom", 0x4000);
romStream.Seek(0x0100, SeekOrigin.Current);
@ -94,9 +94,6 @@ namespace Jellyfish.Virtu
romStream.ReadBlock(_romRegionD0DF, 0x0000, 0x1000);
romStream.ReadBlock(_romRegionE0FF, 0x0000, 0x2000);
romStream = StorageService.GetResourceStream("Roms/DiskII.rom", 0x0100);
romStream.ReadBlock(_romExternalRegionC1CF, 0x0500, 0x0100);
if ((ReadRomRegionE0FF(0xFBB3) == 0x06) && (ReadRomRegionE0FF(0xFBBF) == 0xC1))
{
Monitor = MonitorType.Standard;
@ -105,14 +102,12 @@ namespace Jellyfish.Virtu
{
Monitor = MonitorType.Enhanced;
}
Buffer.BlockCopy(_romInternalRegionC1CF, 0x0700, _romExternalRegionC1CF, 0x0700, 0x0800);
}
public override void Reset() // [7-3]
{
ResetState(State80Col | State80Store | StateAltChrSet | StateAltZP | StateBank1 | StateHRamRd | StateHRamPreWrt | StateHRamWrt | // HRamWrt' [5-23]
StateHires | StatePage2 | StateRamRd | StateRamWrt | StateSlotC3Rom | StateIntCXRom | StateAn0 | StateAn1 | StateAn2 | StateAn3);
StateHires | StatePage2 | StateRamRd | StateRamWrt | StateIntCXRom | StateSlotC3Rom | StateIntC8Rom | StateAn0 | StateAn1 | StateAn2 | StateAn3);
SetState(StateDRes); // An3' -> DRes [8-20]
MapRegion0001();
@ -125,8 +120,7 @@ namespace Jellyfish.Virtu
public int Read(int address)
{
int region = PageRegion[address >> 8];
return (region == RegionC0C0) ? ReadIoC0XX(address) : _regionRead[region][address - RegionBaseAddress[region]];
return ((address & 0xF000) != 0xC000) ? _regionRead[region][address - RegionBaseAddress[region]] : ReadIoRegionC0CF(address);
}
public int ReadZeroPage(int address)
@ -154,9 +148,29 @@ namespace Jellyfish.Virtu
#endregion
#region Read Actions
private int ReadIoRegionC0CF(int address)
{
switch (address & 0xFF00)
{
case 0xC000:
return ReadIoRegionC0C0(address);
case 0xC100: case 0xC200: case 0xC400: case 0xC500: case 0xC600: case 0xC700:
return ReadIoRegionC1C7(address);
case 0xC300:
return ReadIoRegionC3C3(address);
case 0xC800: case 0xC900: case 0xCA00: case 0xCB00: case 0xCC00: case 0xCD00: case 0xCE00: case 0xCF00:
return ReadIoRegionC8CF(address);
}
return _video.ReadFloatingBus();
}
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
[SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode")]
private int ReadIoC0XX(int address)
private int ReadIoRegionC0C0(int address)
{
switch (address)
{
@ -181,13 +195,13 @@ namespace Jellyfish.Virtu
return SetBit7(_keyboard.ReadLatch(), IsRamWriteAux);
case 0xC015:
return SetBit7(_keyboard.ReadLatch(), IsRomCXXXInternal);
return SetBit7(_keyboard.ReadLatch(), IsRomC1CFInternal);
case 0xC016:
return SetBit7(_keyboard.ReadLatch(), IsZeroPageAux);
case 0xC017:
return SetBit7(_keyboard.ReadLatch(), IsRomC3XXExternal);
return SetBit7(_keyboard.ReadLatch(), IsRomC3C3External);
case 0xC018:
return SetBit7(_keyboard.ReadLatch(), Is80Store);
@ -296,37 +310,62 @@ namespace Jellyfish.Virtu
case 0xC090: case 0xC091: case 0xC092: case 0xC093: case 0xC094: case 0xC095: case 0xC096: case 0xC097: // slot1
case 0xC098: case 0xC099: case 0xC09A: case 0xC09B: case 0xC09C: case 0xC09D: case 0xC09E: case 0xC09F:
break;
return Machine.Slot1.ReadIoRegionC0C0(address);
case 0xC0A0: case 0xC0A1: case 0xC0A2: case 0xC0A3: case 0xC0A4: case 0xC0A5: case 0xC0A6: case 0xC0A7: // slot2
case 0xC0A8: case 0xC0A9: case 0xC0AA: case 0xC0AB: case 0xC0AC: case 0xC0AD: case 0xC0AE: case 0xC0AF:
break;
return Machine.Slot2.ReadIoRegionC0C0(address);
case 0xC0B0: case 0xC0B1: case 0xC0B2: case 0xC0B3: case 0xC0B4: case 0xC0B5: case 0xC0B6: case 0xC0B7: // slot3
case 0xC0B8: case 0xC0B9: case 0xC0BA: case 0xC0BB: case 0xC0BC: case 0xC0BD: case 0xC0BE: case 0xC0BF:
break;
return Machine.Slot3.ReadIoRegionC0C0(address);
case 0xC0C0: case 0xC0C1: case 0xC0C2: case 0xC0C3: case 0xC0C4: case 0xC0C5: case 0xC0C6: case 0xC0C7: // slot4
case 0xC0C8: case 0xC0C9: case 0xC0CA: case 0xC0CB: case 0xC0CC: case 0xC0CD: case 0xC0CE: case 0xC0CF:
break;
return Machine.Slot4.ReadIoRegionC0C0(address);
case 0xC0D0: case 0xC0D1: case 0xC0D2: case 0xC0D3: case 0xC0D4: case 0xC0D5: case 0xC0D6: case 0xC0D7: // slot5
case 0xC0D8: case 0xC0D9: case 0xC0DA: case 0xC0DB: case 0xC0DC: case 0xC0DD: case 0xC0DE: case 0xC0DF:
break;
return Machine.Slot5.ReadIoRegionC0C0(address);
case 0xC0E0: case 0xC0E1: case 0xC0E2: case 0xC0E3: case 0xC0E4: case 0xC0E5: case 0xC0E6: case 0xC0E7: // slot6
case 0xC0E8: case 0xC0E9: case 0xC0EA: case 0xC0EB: case 0xC0EC: case 0xC0ED: case 0xC0EE: case 0xC0EF:
return _diskII.Read(address);
return Machine.Slot6.ReadIoRegionC0C0(address);
case 0xC0F0: case 0xC0F1: case 0xC0F2: case 0xC0F3: case 0xC0F4: case 0xC0F5: case 0xC0F6: case 0xC0F7: // slot7
case 0xC0F8: case 0xC0F9: case 0xC0FA: case 0xC0FB: case 0xC0FC: case 0xC0FD: case 0xC0FE: case 0xC0FF:
break;
return Machine.Slot7.ReadIoRegionC0C0(address);
default:
throw new ArgumentOutOfRangeException("address");
}
return _video.ReadFloatingBus(); // [5-40]
return _video.ReadFloatingBus();
}
private int ReadIoRegionC1C7(int address)
{
_slotRegionC8CF = (address >> 8) & 0x07;
return IsRomC1CFInternal ? _romInternalRegionC1CF[address - 0xC100] : Machine.Slots[_slotRegionC8CF].ReadIoRegionC1C7(address);
}
private int ReadIoRegionC3C3(int address)
{
_slotRegionC8CF = (address >> 8) & 0x07;
if (!IsRomC3C3External)
{
SetRomC8CF(true); // $C3XX sets IntC8Rom; inhibits I/O Strobe' [5-28, 7-21]
}
return _noSlotClock.Read(address, (IsRomC1CFInternal || !IsRomC3C3External) ? _romInternalRegionC1CF[address - 0xC100] : Machine.Slot3.ReadIoRegionC1C7(address));
}
private int ReadIoRegionC8CF(int address)
{
if (address == 0xCFFF)
{
SetRomC8CF(false); // $CFFF resets IntC8Rom [5-28, 7-21]
}
return (IsRomC1CFInternal || IsRomC8CFInternal) ? _romInternalRegionC1CF[address - 0xC100] : Machine.Slots[_slotRegionC8CF].ReadIoRegionC8CF(address);
}
[SuppressMessage("Microsoft.Usage", "CA2233:OperationsShouldNotOverflow", MessageId = "address-512")]
@ -351,7 +390,7 @@ namespace Jellyfish.Virtu
#region Write Actions
[SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
[SuppressMessage("Microsoft.Maintainability", "CA1505:AvoidUnmaintainableCode")]
private void WriteIoC0XX(int address, byte data)
private void WriteIoRegionC0C0(int address, byte data)
{
switch (address)
{
@ -368,7 +407,7 @@ namespace Jellyfish.Virtu
break;
case 0xC006: case 0xC007:
SetRomCXXX(TestBit(address, 0));
SetRomC1CF(TestBit(address, 0));
break;
case 0xC008: case 0xC009:
@ -376,7 +415,7 @@ namespace Jellyfish.Virtu
break;
case 0xC00A: case 0xC00B:
SetRomC3XX(TestBit(address, 0));
SetRomC3C3(TestBit(address, 0));
break;
case 0xC00C: case 0xC00D: // [7-5]
@ -455,31 +494,37 @@ namespace Jellyfish.Virtu
case 0xC090: case 0xC091: case 0xC092: case 0xC093: case 0xC094: case 0xC095: case 0xC096: case 0xC097: // slot1
case 0xC098: case 0xC099: case 0xC09A: case 0xC09B: case 0xC09C: case 0xC09D: case 0xC09E: case 0xC09F:
Machine.Slot1.WriteIoRegionC0C0(address, data);
break;
case 0xC0A0: case 0xC0A1: case 0xC0A2: case 0xC0A3: case 0xC0A4: case 0xC0A5: case 0xC0A6: case 0xC0A7: // slot2
case 0xC0A8: case 0xC0A9: case 0xC0AA: case 0xC0AB: case 0xC0AC: case 0xC0AD: case 0xC0AE: case 0xC0AF:
Machine.Slot2.WriteIoRegionC0C0(address, data);
break;
case 0xC0B0: case 0xC0B1: case 0xC0B2: case 0xC0B3: case 0xC0B4: case 0xC0B5: case 0xC0B6: case 0xC0B7: // slot3
case 0xC0B8: case 0xC0B9: case 0xC0BA: case 0xC0BB: case 0xC0BC: case 0xC0BD: case 0xC0BE: case 0xC0BF:
Machine.Slot3.WriteIoRegionC0C0(address, data);
break;
case 0xC0C0: case 0xC0C1: case 0xC0C2: case 0xC0C3: case 0xC0C4: case 0xC0C5: case 0xC0C6: case 0xC0C7: // slot4
case 0xC0C8: case 0xC0C9: case 0xC0CA: case 0xC0CB: case 0xC0CC: case 0xC0CD: case 0xC0CE: case 0xC0CF:
Machine.Slot4.WriteIoRegionC0C0(address, data);
break;
case 0xC0D0: case 0xC0D1: case 0xC0D2: case 0xC0D3: case 0xC0D4: case 0xC0D5: case 0xC0D6: case 0xC0D7: // slot5
case 0xC0D8: case 0xC0D9: case 0xC0DA: case 0xC0DB: case 0xC0DC: case 0xC0DD: case 0xC0DE: case 0xC0DF:
Machine.Slot5.WriteIoRegionC0C0(address, data);
break;
case 0xC0E0: case 0xC0E1: case 0xC0E2: case 0xC0E3: case 0xC0E4: case 0xC0E5: case 0xC0E6: case 0xC0E7: // slot6
case 0xC0E8: case 0xC0E9: case 0xC0EA: case 0xC0EB: case 0xC0EC: case 0xC0ED: case 0xC0EE: case 0xC0EF:
_diskII.Write(address, data);
Machine.Slot6.WriteIoRegionC0C0(address, data);
break;
case 0xC0F0: case 0xC0F1: case 0xC0F2: case 0xC0F3: case 0xC0F4: case 0xC0F5: case 0xC0F6: case 0xC0F7: // slot7
case 0xC0F8: case 0xC0F9: case 0xC0FA: case 0xC0FB: case 0xC0FC: case 0xC0FD: case 0xC0FE: case 0xC0FF:
Machine.Slot7.WriteIoRegionC0C0(address, data);
break;
default:
@ -487,6 +532,41 @@ namespace Jellyfish.Virtu
}
}
private void WriteIoRegionC1C7(int address, byte data)
{
_slotRegionC8CF = (address >> 8) & 0x07;
if (!IsRomC1CFInternal)
{
Machine.Slots[_slotRegionC8CF].WriteIoRegionC1C7(address, data);
}
}
private void WriteIoRegionC3C3(int address, byte data)
{
_slotRegionC8CF = (address >> 8) & 0x07;
if (!IsRomC3C3External)
{
SetRomC8CF(true); // $C3XX sets IntC8Rom; inhibits I/O Strobe' [5-28, 7-21]
}
else if (!IsRomC1CFInternal)
{
Machine.Slot3.WriteIoRegionC1C7(address, data);
}
_noSlotClock.Write(address);
}
private void WriteIoRegionC8CF(int address, byte data)
{
if (address == 0xCFFF)
{
SetRomC8CF(false); // $CFFF resets IntC8Rom [5-28, 7-21]
}
if (!IsRomC1CFInternal && !IsRomC8CFInternal)
{
Machine.Slots[_slotRegionC8CF].WriteIoRegionC8CF(address, data);
}
}
private void WriteRamMode0MainRegion0407(int address, byte data)
{
if (_ramMainRegion02BF[address - 0x0200] != data)
@ -991,7 +1071,7 @@ namespace Jellyfish.Virtu
}
}
private void WriteRomRegionC1FF(int address, byte data)
private void WriteRomRegionD0FF(int address, byte data)
{
}
#endregion
@ -1100,7 +1180,7 @@ namespace Jellyfish.Virtu
private void MapRegionC0CF()
{
_regionRead[RegionC0C0] = null;
if (IsRomCXXXInternal)
if (IsRomC1CFInternal)
{
_regionRead[RegionC1C7] = _romInternalRegionC1CF;
_regionRead[RegionC3C3] = _romInternalRegionC1CF;
@ -1109,17 +1189,17 @@ namespace Jellyfish.Virtu
else
{
_regionRead[RegionC1C7] = _romExternalRegionC1CF;
_regionRead[RegionC3C3] = IsRomC3XXExternal ? _romExternalRegionC1CF : _romInternalRegionC1CF;
_regionRead[RegionC8CF] = _romExternalRegionC1CF;
_regionRead[RegionC3C3] = IsRomC3C3External ? _romExternalRegionC1CF : _romInternalRegionC1CF;
_regionRead[RegionC8CF] = !IsRomC8CFInternal ? _romExternalRegionC1CF : _romInternalRegionC1CF;
}
_regionWrite[RegionC0C0] = null;
_regionWrite[RegionC1C7] = null;
_regionWrite[RegionC3C3] = null;
_regionWrite[RegionC8CF] = null;
_writeRegion[RegionC0C0] = WriteIoC0XX;
_writeRegion[RegionC1C7] = WriteRomRegionC1FF;
_writeRegion[RegionC3C3] = WriteRomRegionC1FF;
_writeRegion[RegionC8CF] = WriteRomRegionC1FF;
_writeRegion[RegionC0C0] = WriteIoRegionC0C0;
_writeRegion[RegionC1C7] = WriteIoRegionC1C7;
_writeRegion[RegionC3C3] = WriteIoRegionC3C3;
_writeRegion[RegionC8CF] = WriteIoRegionC8CF;
}
private void MapRegionD0FF()
@ -1161,8 +1241,8 @@ namespace Jellyfish.Virtu
{
_regionWrite[RegionD0DF] = null;
_regionWrite[RegionE0FF] = null;
_writeRegion[RegionD0DF] = WriteRomRegionC1FF;
_writeRegion[RegionE0FF] = WriteRomRegionC1FF;
_writeRegion[RegionD0DF] = WriteRomRegionD0FF;
_writeRegion[RegionE0FF] = WriteRomRegionD0FF;
}
}
@ -1315,7 +1395,16 @@ namespace Jellyfish.Virtu
}
}
private void SetRomC3XX(bool value)
private void SetRomC1CF(bool value)
{
if (!TestState(StateIntCXRom, value))
{
SetState(StateIntCXRom, value);
MapRegionC0CF();
}
}
private void SetRomC3C3(bool value)
{
if (!TestState(StateSlotC3Rom, value))
{
@ -1324,11 +1413,11 @@ namespace Jellyfish.Virtu
}
}
private void SetRomCXXX(bool value)
private void SetRomC8CF(bool value)
{
if (!TestState(StateIntCXRom, value))
if (!TestState(StateIntC8Rom, value))
{
SetState(StateIntCXRom, value);
SetState(StateIntC8Rom, value);
MapRegionC0CF();
}
}
@ -1427,8 +1516,9 @@ namespace Jellyfish.Virtu
public bool IsRamWriteAux { get { return TestState(StateRamWrt); } }
public bool IsRamWriteAuxRegion0407 { get { return Is80Store ? IsPage2 : IsRamWriteAux; } }
public bool IsRamWriteAuxRegion203F { get { return TestState(State80Store | StateHires, State80Store | StateHires) ? IsPage2 : IsRamWriteAux; } }
public bool IsRomC3XXExternal { get { return TestState(StateSlotC3Rom); } }
public bool IsRomCXXXInternal { get { return TestState(StateIntCXRom); } }
public bool IsRomC1CFInternal { get { return TestState(StateIntCXRom); } }
public bool IsRomC3C3External { get { return TestState(StateSlotC3Rom); } }
public bool IsRomC8CFInternal { get { return TestState(StateIntC8Rom); } }
public bool IsText { get { return TestState(StateText); } }
public bool IsVideoPage2 { get { return TestState(State80Store | StatePage2, StatePage2); } } // 80Store inhibits video Page2 [5-7, 8-19]
public bool IsZeroPageAux { get { return TestState(StateAltZP); } }
@ -1436,14 +1526,15 @@ namespace Jellyfish.Virtu
public MonitorType Monitor { get; private set; }
public int VideoMode { get { return StateVideoMode[_state & StateVideo]; } }
private DiskII _diskII;
private Keyboard _keyboard;
private GamePort _gamePort;
private Cassette _cassette;
private Speaker _speaker;
private Video _video;
private NoSlotClock _noSlotClock;
private int _state;
private int _slotRegionC8CF;
private byte[] _zeroPage;
private byte[][] _regionRead = new byte[RegionCount][];

View File

@ -83,11 +83,12 @@ namespace Jellyfish.Virtu
private const int StateRamRd = 0x002000;
private const int StateRamWrt = 0x004000;
private const int StateSlotC3Rom = 0x008000;
private const int StateIntCXRom = 0x010000;
private const int StateAn0 = 0x020000;
private const int StateAn1 = 0x040000;
private const int StateAn2 = 0x080000;
private const int StateAn3 = 0x100000;
private const int StateIntC8Rom = 0x010000; // [5-28]
private const int StateIntCXRom = 0x020000;
private const int StateAn0 = 0x040000;
private const int StateAn1 = 0x080000;
private const int StateAn2 = 0x100000;
private const int StateAn3 = 0x200000;
private const int StateVideo = State80Col | StateText | StateMixed | StateHires | StateDRes;
private const int StateVideoModeCount = 32;

18
Virtu/NoSlotClock.cs Normal file
View File

@ -0,0 +1,18 @@
namespace Jellyfish.Virtu
{
public sealed class NoSlotClock
{
public NoSlotClock()
{
}
public int Read(int address, int data)
{
return data;
}
public void Write(int address)
{
}
}
}

48
Virtu/PeripheralCard.cs Normal file
View File

@ -0,0 +1,48 @@
namespace Jellyfish.Virtu
{
public class PeripheralCard : MachineComponent
{
public PeripheralCard(Machine machine) :
base(machine)
{
}
public virtual int ReadIoRegionC0C0(int address)
{
// read Device Select' address $C0nX; n = slot number + 8
return ReadFloatingBus();
}
public virtual int ReadIoRegionC1C7(int address)
{
// read I/O Select' address $CsXX; s = slot number
return ReadFloatingBus();
}
public virtual int ReadIoRegionC8CF(int address)
{
// read I/O Strobe' address $C800-$CFFF
return ReadFloatingBus();
}
public virtual void WriteIoRegionC0C0(int address, int data)
{
// write Device Select' address $C0nX; n = slot number + 8
}
public virtual void WriteIoRegionC1C7(int address, int data)
{
// write I/O Select' address $CsXX; s = slot number
}
public virtual void WriteIoRegionC8CF(int address, int data)
{
// write I/O Strobe' address $C800-$CFFF
}
protected int ReadFloatingBus()
{
return Machine.Video.ReadFloatingBus();
}
}
}

View File

@ -28,6 +28,9 @@
}
public bool IsAnyKeyDown { get; protected set; }
public bool IsControlKeyDown { get; protected set; }
public bool IsShiftKeyDown { get; protected set; }
public bool IsOpenAppleKeyDown { get; protected set; }
public bool IsCloseAppleKeyDown { get; protected set; }

View File

@ -128,15 +128,15 @@
<Compile Include="..\DiskDsk.cs">
<Link>Core\DiskDsk.cs</Link>
</Compile>
<Compile Include="..\DiskII.cs">
<Link>Core\DiskII.cs</Link>
<Compile Include="..\DiskIIController.cs">
<Link>Core\DiskIIController.cs</Link>
</Compile>
<Compile Include="..\DiskIIDrive.cs">
<Link>Core\DiskIIDrive.cs</Link>
</Compile>
<Compile Include="..\DiskNib.cs">
<Link>Core\DiskNib.cs</Link>
</Compile>
<Compile Include="..\Drive525.cs">
<Link>Core\Drive525.cs</Link>
</Compile>
<Compile Include="..\GamePort.cs">
<Link>Core\GamePort.cs</Link>
</Compile>
@ -164,6 +164,12 @@
<Compile Include="..\MemoryData.cs">
<Link>Core\MemoryData.cs</Link>
</Compile>
<Compile Include="..\NoSlotClock.cs">
<Link>Core\NoSlotClock.cs</Link>
</Compile>
<Compile Include="..\PeripheralCard.cs">
<Link>Core\PeripheralCard.cs</Link>
</Compile>
<Compile Include="..\Properties\Strings.Designer.cs">
<Link>Properties\Strings.Designer.cs</Link>
<AutoGen>True</AutoGen>

View File

@ -73,7 +73,11 @@ namespace Jellyfish.Virtu
using (var stream = dialog.File.OpenRead())
{
_machine.Pause();
_machine.DiskII.Drives[drive].InsertDisk(dialog.File.Name, stream, false);
var diskII = _machine.FindDiskIIController();
if (diskII != null)
{
diskII.Drives[drive].InsertDisk(dialog.File.Name, stream, false);
}
_machine.Unpause();
}
}

View File

@ -113,15 +113,15 @@
<Compile Include="..\..\DiskDsk.cs">
<Link>Core\DiskDsk.cs</Link>
</Compile>
<Compile Include="..\..\DiskII.cs">
<Link>Core\DiskII.cs</Link>
<Compile Include="..\..\DiskIIController.cs">
<Link>Core\DiskIIController.cs</Link>
</Compile>
<Compile Include="..\..\DiskIIDrive.cs">
<Link>Core\DiskIIDrive.cs</Link>
</Compile>
<Compile Include="..\..\DiskNib.cs">
<Link>Core\DiskNib.cs</Link>
</Compile>
<Compile Include="..\..\Drive525.cs">
<Link>Core\Drive525.cs</Link>
</Compile>
<Compile Include="..\..\GamePort.cs">
<Link>Core\GamePort.cs</Link>
</Compile>
@ -149,6 +149,12 @@
<Compile Include="..\..\MemoryData.cs">
<Link>Core\MemoryData.cs</Link>
</Compile>
<Compile Include="..\..\NoSlotClock.cs">
<Link>Core\NoSlotClock.cs</Link>
</Compile>
<Compile Include="..\..\PeripheralCard.cs">
<Link>Core\PeripheralCard.cs</Link>
</Compile>
<Compile Include="..\..\Properties\Strings.Designer.cs">
<Link>Properties\Strings.Designer.cs</Link>
<AutoGen>True</AutoGen>

View File

@ -44,11 +44,12 @@ namespace Jellyfish.Virtu.Services
}
var modifiers = System.Windows.Input.Keyboard.Modifiers;
bool control = ((modifiers & ModifierKeys.Control) != 0);
IsControlKeyDown = ((modifiers & ModifierKeys.Control) != 0);
IsShiftKeyDown = ((modifiers & ModifierKeys.Shift) != 0);
IsOpenAppleKeyDown = ((modifiers & ModifierKeys.Alt) != 0) || IsKeyDown(Key.NumPad0);
IsCloseAppleKeyDown = ((modifiers & ModifierKeys.Windows) != 0) || IsKeyDown(Key.Decimal);
IsResetKeyDown = control && IsKeyDown(Key.Back);
IsResetKeyDown = IsControlKeyDown && IsKeyDown(Key.Back);
base.Update();
}

View File

@ -90,7 +90,7 @@ namespace Jellyfish.Virtu
}
}
public int ReadFloatingBus()
public int ReadFloatingBus() // [5-40]
{
// derive scanner counters from current cycles into frame; assumes hcount and vcount preset at start of frame [3-13, 3-15, 3-16]
int cycles = _cyclesPerVSync - Machine.Events.FindEvent(_resetVSyncEvent);

View File

@ -118,15 +118,15 @@
<Compile Include="..\DiskDsk.cs">
<Link>Core\DiskDsk.cs</Link>
</Compile>
<Compile Include="..\DiskII.cs">
<Link>Core\DiskII.cs</Link>
<Compile Include="..\DiskIIController.cs">
<Link>Core\DiskIIController.cs</Link>
</Compile>
<Compile Include="..\DiskIIDrive.cs">
<Link>Core\DiskIIDrive.cs</Link>
</Compile>
<Compile Include="..\DiskNib.cs">
<Link>Core\DiskNib.cs</Link>
</Compile>
<Compile Include="..\Drive525.cs">
<Link>Core\Drive525.cs</Link>
</Compile>
<Compile Include="..\GamePort.cs">
<Link>Core\GamePort.cs</Link>
</Compile>
@ -154,6 +154,12 @@
<Compile Include="..\MemoryData.cs">
<Link>Core\MemoryData.cs</Link>
</Compile>
<Compile Include="..\NoSlotClock.cs">
<Link>Core\NoSlotClock.cs</Link>
</Compile>
<Compile Include="..\PeripheralCard.cs">
<Link>Core\PeripheralCard.cs</Link>
</Compile>
<Compile Include="..\Properties\Strings.Designer.cs">
<Link>Properties\Strings.Designer.cs</Link>
<AutoGen>True</AutoGen>

View File

@ -75,15 +75,19 @@ namespace Jellyfish.Virtu
using (var stream = File.OpenRead(dialog.FileName))
{
_machine.Pause();
_machine.DiskII.Drives[drive].InsertDisk(dialog.FileName, stream, false);
var settings = _machine.Settings.DiskII;
if (drive == 0)
var diskII = _machine.FindDiskIIController();
if (diskII != null)
{
settings.Disk1.Name = dialog.FileName;
}
else
{
settings.Disk2.Name = dialog.FileName;
diskII.Drives[drive].InsertDisk(dialog.FileName, stream, false);
var settings = _machine.Settings.DiskII;
if (drive == 0)
{
settings.Disk1.Name = dialog.FileName;
}
else
{
settings.Disk2.Name = dialog.FileName;
}
}
_machine.Unpause();
}

View File

@ -44,11 +44,12 @@ namespace Jellyfish.Virtu.Services
}
}
bool control = ((keyboard.Modifiers & ModifierKeys.Control) != 0);
IsControlKeyDown = ((keyboard.Modifiers & ModifierKeys.Control) != 0);
IsShiftKeyDown = ((keyboard.Modifiers & ModifierKeys.Shift) != 0);
IsOpenAppleKeyDown = keyboard.IsKeyDown(Key.LeftAlt) || IsKeyDown(Key.NumPad0);
IsCloseAppleKeyDown = keyboard.IsKeyDown(Key.RightAlt) || IsKeyDown(Key.Decimal);
IsResetKeyDown = control && keyboard.IsKeyDown(Key.Back);
IsResetKeyDown = IsControlKeyDown && keyboard.IsKeyDown(Key.Back);
base.Update();
}

View File

@ -122,15 +122,15 @@
<Compile Include="..\DiskDsk.cs">
<Link>Core\DiskDsk.cs</Link>
</Compile>
<Compile Include="..\DiskII.cs">
<Link>Core\DiskII.cs</Link>
<Compile Include="..\DiskIIController.cs">
<Link>Core\DiskIIController.cs</Link>
</Compile>
<Compile Include="..\DiskIIDrive.cs">
<Link>Core\DiskIIDrive.cs</Link>
</Compile>
<Compile Include="..\DiskNib.cs">
<Link>Core\DiskNib.cs</Link>
</Compile>
<Compile Include="..\Drive525.cs">
<Link>Core\Drive525.cs</Link>
</Compile>
<Compile Include="..\GamePort.cs">
<Link>Core\GamePort.cs</Link>
</Compile>
@ -158,6 +158,12 @@
<Compile Include="..\MemoryData.cs">
<Link>Core\MemoryData.cs</Link>
</Compile>
<Compile Include="..\NoSlotClock.cs">
<Link>Core\NoSlotClock.cs</Link>
</Compile>
<Compile Include="..\PeripheralCard.cs">
<Link>Core\PeripheralCard.cs</Link>
</Compile>
<Compile Include="..\Properties\Strings.Designer.cs">
<Link>Properties\Strings.Designer.cs</Link>
<AutoGen>True</AutoGen>

View File

@ -128,15 +128,15 @@
<Compile Include="..\DiskDsk.cs">
<Link>Core\DiskDsk.cs</Link>
</Compile>
<Compile Include="..\DiskII.cs">
<Link>Core\DiskII.cs</Link>
<Compile Include="..\DiskIIController.cs">
<Link>Core\DiskIIController.cs</Link>
</Compile>
<Compile Include="..\DiskIIDrive.cs">
<Link>Core\DiskIIDrive.cs</Link>
</Compile>
<Compile Include="..\DiskNib.cs">
<Link>Core\DiskNib.cs</Link>
</Compile>
<Compile Include="..\Drive525.cs">
<Link>Core\Drive525.cs</Link>
</Compile>
<Compile Include="..\GamePort.cs">
<Link>Core\GamePort.cs</Link>
</Compile>
@ -164,6 +164,12 @@
<Compile Include="..\MemoryData.cs">
<Link>Core\MemoryData.cs</Link>
</Compile>
<Compile Include="..\NoSlotClock.cs">
<Link>Core\NoSlotClock.cs</Link>
</Compile>
<Compile Include="..\PeripheralCard.cs">
<Link>Core\PeripheralCard.cs</Link>
</Compile>
<Compile Include="..\Properties\Strings.Designer.cs">
<Link>Properties\Strings.Designer.cs</Link>
<AutoGen>True</AutoGen>

View File

@ -132,15 +132,15 @@
<Compile Include="..\DiskDsk.cs">
<Link>Core\DiskDsk.cs</Link>
</Compile>
<Compile Include="..\DiskII.cs">
<Link>Core\DiskII.cs</Link>
<Compile Include="..\DiskIIController.cs">
<Link>Core\DiskIIController.cs</Link>
</Compile>
<Compile Include="..\DiskIIDrive.cs">
<Link>Core\DiskIIDrive.cs</Link>
</Compile>
<Compile Include="..\DiskNib.cs">
<Link>Core\DiskNib.cs</Link>
</Compile>
<Compile Include="..\Drive525.cs">
<Link>Core\Drive525.cs</Link>
</Compile>
<Compile Include="..\GamePort.cs">
<Link>Core\GamePort.cs</Link>
</Compile>
@ -168,6 +168,12 @@
<Compile Include="..\MemoryData.cs">
<Link>Core\MemoryData.cs</Link>
</Compile>
<Compile Include="..\NoSlotClock.cs">
<Link>Core\NoSlotClock.cs</Link>
</Compile>
<Compile Include="..\PeripheralCard.cs">
<Link>Core\PeripheralCard.cs</Link>
</Compile>
<Compile Include="..\Properties\Strings.Designer.cs">
<Link>Properties\Strings.Designer.cs</Link>
<AutoGen>True</AutoGen>

View File

@ -67,11 +67,12 @@ namespace Jellyfish.Virtu.Services
}
}
bool control = IsKeyDown(Keys.LeftControl) || IsKeyDown(Keys.RightControl);
IsControlKeyDown = IsKeyDown(Keys.LeftControl) || IsKeyDown(Keys.RightControl);
IsShiftKeyDown = IsKeyDown(Keys.LeftShift) || IsKeyDown(Keys.RightShift);
IsOpenAppleKeyDown = IsKeyDown(Keys.LeftAlt) || IsKeyDown(Keys.NumPad0) || (gamePadState.Buttons.LeftShoulder == ButtonState.Pressed);
IsCloseAppleKeyDown = IsKeyDown(Keys.RightAlt) || IsKeyDown(Keys.Decimal) || (gamePadState.Buttons.RightShoulder == ButtonState.Pressed);
IsResetKeyDown = (control && IsKeyDown(Keys.Back)) || (gamePadControl && (gamePadState.Buttons.Start == ButtonState.Pressed));
IsResetKeyDown = (IsControlKeyDown && IsKeyDown(Keys.Back)) || (gamePadControl && (gamePadState.Buttons.Start == ButtonState.Pressed));
base.Update();
}