diff --git a/assets/data/programs.json b/assets/data/programs.json index 038515d..978ec2a 100644 --- a/assets/data/programs.json +++ b/assets/data/programs.json @@ -781,6 +781,15 @@ "machineType": "VIC44", "ram": "RAM_UNEXPANDED" }, + { + "name": "VIC44K", + "displayName": "VIC 44K", + "filePath": "", + "fileType": "ROM", + "iconPath": "screenshots/V/VIC44/VIC44.png", + "machineType": "VIC44K", + "ram": "RAM_UNEXPANDED" + }, { "name": "Whack-E", "filePath": "https://archive.org/download/tosec-20161111-commodore-vic-20_20170305/TOSEC.2016.11.11.Commodore.VIC20.AlphaBot.zip/TOSEC.2016.11.11.Commodore.VIC20.AlphaBot%2FCommodore%20VIC20%20-%20Games%20-%20%5BPRG%5D%2FWhack-E%20%282009%29%28McCrea%2C%20Steve%29.zip", diff --git a/assets/roms/vic_44k_basic.rom b/assets/roms/vic_44k_basic.rom new file mode 100644 index 0000000..038f86e Binary files /dev/null and b/assets/roms/vic_44k_basic.rom differ diff --git a/assets/roms/vic_44k_kernal.rom b/assets/roms/vic_44k_kernal.rom new file mode 100644 index 0000000..85f95ee Binary files /dev/null and b/assets/roms/vic_44k_kernal.rom differ diff --git a/core/src/main/java/emu/jvic/JVic.java b/core/src/main/java/emu/jvic/JVic.java index 99c5c3f..93653eb 100644 --- a/core/src/main/java/emu/jvic/JVic.java +++ b/core/src/main/java/emu/jvic/JVic.java @@ -172,6 +172,8 @@ private void applyTvConfig(AppConfigItem appConfigItem, String tv) { case "VIC44": appConfigItem.setMachineType("VIC44"); break; + case "VIC44K": + appConfigItem.setMachineType("VIC44K"); default: break; } diff --git a/core/src/main/java/emu/jvic/JVicRunner.java b/core/src/main/java/emu/jvic/JVicRunner.java index 96bb166..1dcef12 100644 --- a/core/src/main/java/emu/jvic/JVicRunner.java +++ b/core/src/main/java/emu/jvic/JVicRunner.java @@ -192,6 +192,48 @@ public MachineInputProcessor getMachineInputProcessor() { return (machineScreen != null? machineScreen.getMachineInputProcessor() : null); } + public byte[] loadKernalRom(MachineType machineType) { + byte[] kernalRom = null; + + switch (machineType) { + case NTSC: + kernalRom = Gdx.files.internal("roms/kernal_ntsc.rom").readBytes(); + break; + case PAL: + kernalRom = Gdx.files.internal("roms/kernal_pal.rom").readBytes(); + break; + case VIC44: + kernalRom = Gdx.files.internal("roms/vic_44_kernal.rom").readBytes(); + break; + case VIC44K: + kernalRom = Gdx.files.internal("roms/vic_44k_kernal.rom").readBytes(); + break; + } + + return kernalRom; + } + + public byte[] loadBasicRom(MachineType machineType) { + byte[] basicRom = null; + + switch (machineType) { + case NTSC: + basicRom = Gdx.files.internal("roms/basic.rom").readBytes(); + break; + case PAL: + basicRom = Gdx.files.internal("roms/basic.rom").readBytes(); + break; + case VIC44: + basicRom = Gdx.files.internal("roms/vic_44_basic.rom").readBytes(); + break; + case VIC44K: + basicRom = Gdx.files.internal("roms/vic_44k_basic.rom").readBytes(); + break; + } + + return basicRom; + } + public abstract void start(AppConfigItem appConfigItem); public abstract void reset(); diff --git a/core/src/main/java/emu/jvic/Machine.java b/core/src/main/java/emu/jvic/Machine.java index b7da7a6..2601619 100644 --- a/core/src/main/java/emu/jvic/Machine.java +++ b/core/src/main/java/emu/jvic/Machine.java @@ -22,6 +22,7 @@ import emu.jvic.sound.libgdx.GdxSoundGenerator; import emu.jvic.video.Vic; import emu.jvic.video.Vic44; +import emu.jvic.video.Vic44k; import emu.jvic.video.Vic6560; import emu.jvic.video.Vic6561; @@ -138,6 +139,8 @@ public Callable> init( // Create the VIC chip and configure it as per the current TV type. if (machineType.isVIC44()) { vic = new Vic44(pixelData, machineType, snapshot); + } else if (machineType.isVIC44K()) { + vic = new Vic44k(pixelData, machineType, snapshot); } else if (machineType.isNTSC()) { vic = new Vic6560(pixelData, machineType, snapshot); } else { diff --git a/core/src/main/java/emu/jvic/MachineScreen.java b/core/src/main/java/emu/jvic/MachineScreen.java index b687774..2610009 100644 --- a/core/src/main/java/emu/jvic/MachineScreen.java +++ b/core/src/main/java/emu/jvic/MachineScreen.java @@ -157,6 +157,7 @@ public MachineScreen(JVic jvic, JVicRunner jvicRunner, DialogHandler dialogHandl createScreenResourcesForMachineType(MachineType.PAL); createScreenResourcesForMachineType(MachineType.NTSC); createScreenResourcesForMachineType(MachineType.VIC44); + createScreenResourcesForMachineType(MachineType.VIC44K); warpSpeedIcon = new Texture("png/warp_speed_icon.png"); cameraIcon = new Texture("png/camera_icon.png"); diff --git a/core/src/main/java/emu/jvic/MachineType.java b/core/src/main/java/emu/jvic/MachineType.java index 1f3d51c..96af504 100644 --- a/core/src/main/java/emu/jvic/MachineType.java +++ b/core/src/main/java/emu/jvic/MachineType.java @@ -11,7 +11,9 @@ public enum MachineType { NTSC(1022727, 260, 263, 199, 252, 49, 8, 60), - VIC44(1108405, 568, 312, 440, 272, 103, 31, 50); + VIC44(1108405, 568, 312, 440, 272, 103, 31, 50), + + VIC44K(1108405, 568, 312, 440, 272, 103, 31, 50); private int cyclesPerSecond; private int totalScreenWidth; @@ -147,4 +149,8 @@ public boolean isNTSC() { public boolean isVIC44() { return equals(VIC44); } + + public boolean isVIC44K() { + return equals(VIC44K); + } } diff --git a/core/src/main/java/emu/jvic/memory/Vic20Memory.java b/core/src/main/java/emu/jvic/memory/Vic20Memory.java index 32b4262..1ece37e 100644 --- a/core/src/main/java/emu/jvic/memory/Vic20Memory.java +++ b/core/src/main/java/emu/jvic/memory/Vic20Memory.java @@ -75,111 +75,164 @@ public Vic20Memory(Cpu6502 cpu, Vic vic, Via6522 via1, Via6522 via2, int ramExpa */ private void initVicMemory(Vic vic, Via6522 via1, Via6522 via2, byte[] basicRom, byte[] kernalRom, byte[] charRom) { - // This 1K of RAM is always present. - mapChipToMemory(new VicBusRamChip(), 0x0000, 0x03FF); - - // The next 3K of memory may have RAM or may be unconnected. - mapChipToMemory((ramExpansion & RAM_1) != 0 ? new RamChip() : new UnconnectedMemory(), 0x0400, 0x07FF); - mapChipToMemory((ramExpansion & RAM_2) != 0 ? new RamChip() : new UnconnectedMemory(), 0x0800, 0x0BFF); - mapChipToMemory((ramExpansion & RAM_3) != 0 ? new RamChip() : new UnconnectedMemory(), 0x0C00, 0x0FFF); - - // This 4K of RAM is always present. - mapChipToMemory(new VicBusRamChip(), 0x1000, 0x1FFF); - - // The next three 8K blocks may have RAM or may be unconnected. - mapChipToMemory((ramExpansion & BLK_1) != 0 ? new RamChip() : new UnconnectedMemory(), 0x2000, 0x3FFF); - mapChipToMemory((ramExpansion & BLK_2) != 0 ? new RamChip() : new UnconnectedMemory(), 0x4000, 0x5FFF); - mapChipToMemory((ramExpansion & BLK_3) != 0 ? new RamChip() : new UnconnectedMemory(), 0x6000, 0x7FFF); - - mapChipToMemory(new VicBusRomChip(), 0x8000, 0x8FFF, charRom); - + if (machineType.isVIC44K()) { + initVic44KMemory(vic, via1, via2); + + } else { + // This 1K of RAM is always present. + mapChipToMemory(new VicBusRamChip(), 0x0000, 0x03FF); + + // The next 3K of memory may have RAM or may be unconnected. + mapChipToMemory((ramExpansion & RAM_1) != 0 ? new RamChip() : new UnconnectedMemory(), 0x0400, 0x07FF); + mapChipToMemory((ramExpansion & RAM_2) != 0 ? new RamChip() : new UnconnectedMemory(), 0x0800, 0x0BFF); + mapChipToMemory((ramExpansion & RAM_3) != 0 ? new RamChip() : new UnconnectedMemory(), 0x0C00, 0x0FFF); + + // This 4K of RAM is always present. + mapChipToMemory(new VicBusRamChip(), 0x1000, 0x1FFF); + + // The next three 8K blocks may have RAM or may be unconnected. + mapChipToMemory((ramExpansion & BLK_1) != 0 ? new RamChip() : new UnconnectedMemory(), 0x2000, 0x3FFF); + mapChipToMemory((ramExpansion & BLK_2) != 0 ? new RamChip() : new UnconnectedMemory(), 0x4000, 0x5FFF); + mapChipToMemory((ramExpansion & BLK_3) != 0 ? new RamChip() : new UnconnectedMemory(), 0x6000, 0x7FFF); + + mapChipToMemory(new VicBusRomChip(), 0x8000, 0x8FFF, charRom); + + // These are the standard locations for the VIC, VIA1 and VIA2 chips. + mapChipToMemory(vic, 0x9000, 0x900F); + mapChipToMemory(via1, 0x9110, 0x911F); + mapChipToMemory(via2, 0x9120, 0x912F); + + // The rest of the $9XXX address space, other than colour RAM, is not fully + // decoded. Every address in that range is going to select at least one of + // the VIC, VIA1 or VIA2 chips though, but sometimes it will select 2 or 3 + // of them at once!! So we emulate this behaviour below. + NotFullyDecodedMemory vicVia1 = new NotFullyDecodedMemory(new MemoryMappedChip[] { vic, via1 }); + NotFullyDecodedMemory vicVia2 = new NotFullyDecodedMemory(new MemoryMappedChip[] { vic, via2 }); + NotFullyDecodedMemory via1Via2 = new NotFullyDecodedMemory(new MemoryMappedChip[] { via1, via2 }); + NotFullyDecodedMemory vicVia1Via2 = new NotFullyDecodedMemory(new MemoryMappedChip[] { vic, via1, via2 }); + + mapChipToMemory(vicVia1, 0x9010, 0x901F); + mapChipToMemory(vicVia2, 0x9020, 0x902F); + mapChipToMemory(vicVia1Via2, 0x9030, 0x903F); + mapChipToMemory(vic, 0x9040, 0x904F); + mapChipToMemory(vicVia1, 0x9050, 0x905F); + mapChipToMemory(vicVia2, 0x9060, 0x906F); + mapChipToMemory(vicVia1Via2, 0x9070, 0x907F); + mapChipToMemory(vic, 0x9080, 0x908F); + mapChipToMemory(vicVia1, 0x9090, 0x909F); + mapChipToMemory(vicVia2, 0x90A0, 0x90AF); + mapChipToMemory(vicVia1Via2, 0x90B0, 0x90BF); + mapChipToMemory(vic, 0x90C0, 0x90CF); + mapChipToMemory(vicVia1, 0x90D0, 0x90DF); + mapChipToMemory(vicVia2, 0x90E0, 0x90EF); + mapChipToMemory(vicVia1Via2, 0x90F0, 0x90FF); + + mapChipToMemory(vic, 0x9100, 0x910F); // Unconnected VIC address bus space. + mapChipToMemory(via1Via2, 0x9130, 0x913F); + mapChipToMemory(vic, 0x9140, 0x914F); // Unconnected VIC address bus space. + mapChipToMemory(via1, 0x9150, 0x915F); + mapChipToMemory(via2, 0x9160, 0x916F); + mapChipToMemory(via1Via2, 0x9170, 0x917F); + mapChipToMemory(vic, 0x9180, 0x918F); // Unconnected VIC address bus space. + mapChipToMemory(via1, 0x9190, 0x919F); + mapChipToMemory(via2, 0x91A0, 0x91AF); + mapChipToMemory(via1Via2, 0x91B0, 0x91BF); + mapChipToMemory(vic, 0x91C0, 0x91CF); // Unconnected VIC address bus space. + mapChipToMemory(via1, 0x91D0, 0x91DF); + mapChipToMemory(via2, 0x91E0, 0x91EF); + mapChipToMemory(via1Via2, 0x91F0, 0x91FF); + mapChipToMemory(vic, 0x9200, 0x920F); // Unconnected VIC address bus space. + mapChipToMemory(via1, 0x9210, 0x921F); + mapChipToMemory(via2, 0x9220, 0x922F); + mapChipToMemory(via1Via2, 0x9230, 0x923F); + mapChipToMemory(vic, 0x9240, 0x924F); // Unconnected VIC address bus space. + mapChipToMemory(via1, 0x9250, 0x925F); + mapChipToMemory(via2, 0x9260, 0x926F); + mapChipToMemory(via1Via2, 0x9270, 0x927F); + mapChipToMemory(vic, 0x9280, 0x928F); // Unconnected VIC address bus space. + mapChipToMemory(via1, 0x9290, 0x929F); + mapChipToMemory(via2, 0x92A0, 0x92AF); + mapChipToMemory(via1Via2, 0x92B0, 0x92BF); + mapChipToMemory(vic, 0x92C0, 0x92CF); // Unconnected VIC address bus space. + mapChipToMemory(via1, 0x92D0, 0x92DF); + mapChipToMemory(via2, 0x92E0, 0x92EF); + mapChipToMemory(via1Via2, 0x92F0, 0x92FF); + mapChipToMemory(vic, 0x9300, 0x930F); // Unconnected VIC address bus space. + mapChipToMemory(via1, 0x9310, 0x931F); + mapChipToMemory(via2, 0x9320, 0x932F); + mapChipToMemory(via1Via2, 0x9330, 0x933F); + mapChipToMemory(vic, 0x9340, 0x934F); // Unconnected VIC address bus space. + mapChipToMemory(via1, 0x9350, 0x935F); + mapChipToMemory(via2, 0x9360, 0x936F); + mapChipToMemory(via1Via2, 0x9370, 0x937F); + mapChipToMemory(vic, 0x9380, 0x938F); // Unconnected VIC address bus space. + mapChipToMemory(via1, 0x9390, 0x939F); + mapChipToMemory(via2, 0x93A0, 0x93AF); + mapChipToMemory(via1Via2, 0x93B0, 0x93BF); + mapChipToMemory(vic, 0x93C0, 0x93CF); // Unconnected VIC address bus space. + mapChipToMemory(via1, 0x93D0, 0x93DF); + mapChipToMemory(via2, 0x93E0, 0x93EF); + mapChipToMemory(via1Via2, 0x93F0, 0x93FF); + + // The colour RAM lives at this spot by default. + mapChipToMemory(new NibbleRamChip(), 0x9400, 0x97FF); + + mapChipToMemory(vic, 0x9800, 0x9FFF); // Unconnected VIC address bus space. + + // It is possible to connect RAM in this block, even though it would normally be + // a cartridge ROM. + mapChipToMemory((ramExpansion & BLK_5) != 0 ? new RamChip() : new UnconnectedMemory(), 0xA000, 0xBFFF); + } + + mapChipToMemory(new RomChip(), 0xC000, 0xDFFF, basicRom); + mapChipToMemory(new RomChip(), 0xE000, 0xFFFF, kernalRom); + + if (machineType.isVIC44() || machineType.isVIC44K()) { + vic.setCharRom(charRom); + } + } + + /** + * Initialises the memory for the VIC44K machine. + * + * @param vic The VIC chip to map to memory. + * @param via1 The VIA #1 chip to map to memory. + * @param via2 The VIA #2 chip to map to memory. + */ + private void initVic44KMemory(Vic vic, Via6522 via1, Via6522 via2) { + // All Expansion RAM in the VIC44K (BLK5/BLK1/BLK2/BLK3) + mapChipToMemory(new RamChip(), 0x0000, 0x7FFF); + + mapChipToMemory(new VicBusRamChip(), 0x8000, 0xB7FF); + + mapChipToMemory(new NibbleRamChip(), 0xB800, 0xBBFF); + // These are the standard locations for the VIC, VIA1 and VIA2 chips. - mapChipToMemory(vic, 0x9000, 0x900F); - mapChipToMemory(via1, 0x9110, 0x911F); - mapChipToMemory(via2, 0x9120, 0x912F); - - // The rest of the $9XXX address space, other than colour RAM, is not fully - // decoded. Every address in that range is going to select at least one of - // the VIC, VIA1 or VIA2 chips though, but sometimes it will select 2 or 3 - // of them at once!! So we emulate this behaviour below. + mapChipToMemory(vic, 0xBC00, 0xBC0F); + mapChipToMemory(via1, 0xBD10, 0xBD1F); + mapChipToMemory(via2, 0xBD20, 0xBD2F); + NotFullyDecodedMemory vicVia1 = new NotFullyDecodedMemory(new MemoryMappedChip[] { vic, via1 }); NotFullyDecodedMemory vicVia2 = new NotFullyDecodedMemory(new MemoryMappedChip[] { vic, via2 }); NotFullyDecodedMemory via1Via2 = new NotFullyDecodedMemory(new MemoryMappedChip[] { via1, via2 }); NotFullyDecodedMemory vicVia1Via2 = new NotFullyDecodedMemory(new MemoryMappedChip[] { vic, via1, via2 }); - - mapChipToMemory(vicVia1, 0x9010, 0x901F); - mapChipToMemory(vicVia2, 0x9020, 0x902F); - mapChipToMemory(vicVia1Via2, 0x9030, 0x903F); - mapChipToMemory(vic, 0x9040, 0x904F); - mapChipToMemory(vicVia1, 0x9050, 0x905F); - mapChipToMemory(vicVia2, 0x9060, 0x906F); - mapChipToMemory(vicVia1Via2, 0x9070, 0x907F); - mapChipToMemory(vic, 0x9080, 0x908F); - mapChipToMemory(vicVia1, 0x9090, 0x909F); - mapChipToMemory(vicVia2, 0x90A0, 0x90AF); - mapChipToMemory(vicVia1Via2, 0x90B0, 0x90BF); - mapChipToMemory(vic, 0x90C0, 0x90CF); - mapChipToMemory(vicVia1, 0x90D0, 0x90DF); - mapChipToMemory(vicVia2, 0x90E0, 0x90EF); - mapChipToMemory(vicVia1Via2, 0x90F0, 0x90FF); - mapChipToMemory(vic, 0x9100, 0x910F); // Unconnected VIC address bus space. - mapChipToMemory(via1Via2, 0x9130, 0x913F); - mapChipToMemory(vic, 0x9140, 0x914F); // Unconnected VIC address bus space. - mapChipToMemory(via1, 0x9150, 0x915F); - mapChipToMemory(via2, 0x9160, 0x916F); - mapChipToMemory(via1Via2, 0x9170, 0x917F); - mapChipToMemory(vic, 0x9180, 0x918F); // Unconnected VIC address bus space. - mapChipToMemory(via1, 0x9190, 0x919F); - mapChipToMemory(via2, 0x91A0, 0x91AF); - mapChipToMemory(via1Via2, 0x91B0, 0x91BF); - mapChipToMemory(vic, 0x91C0, 0x91CF); // Unconnected VIC address bus space. - mapChipToMemory(via1, 0x91D0, 0x91DF); - mapChipToMemory(via2, 0x91E0, 0x91EF); - mapChipToMemory(via1Via2, 0x91F0, 0x91FF); - mapChipToMemory(vic, 0x9200, 0x920F); // Unconnected VIC address bus space. - mapChipToMemory(via1, 0x9210, 0x921F); - mapChipToMemory(via2, 0x9220, 0x922F); - mapChipToMemory(via1Via2, 0x9230, 0x923F); - mapChipToMemory(vic, 0x9240, 0x924F); // Unconnected VIC address bus space. - mapChipToMemory(via1, 0x9250, 0x925F); - mapChipToMemory(via2, 0x9260, 0x926F); - mapChipToMemory(via1Via2, 0x9270, 0x927F); - mapChipToMemory(vic, 0x9280, 0x928F); // Unconnected VIC address bus space. - mapChipToMemory(via1, 0x9290, 0x929F); - mapChipToMemory(via2, 0x92A0, 0x92AF); - mapChipToMemory(via1Via2, 0x92B0, 0x92BF); - mapChipToMemory(vic, 0x92C0, 0x92CF); // Unconnected VIC address bus space. - mapChipToMemory(via1, 0x92D0, 0x92DF); - mapChipToMemory(via2, 0x92E0, 0x92EF); - mapChipToMemory(via1Via2, 0x92F0, 0x92FF); - mapChipToMemory(vic, 0x9300, 0x930F); // Unconnected VIC address bus space. - mapChipToMemory(via1, 0x9310, 0x931F); - mapChipToMemory(via2, 0x9320, 0x932F); - mapChipToMemory(via1Via2, 0x9330, 0x933F); - mapChipToMemory(vic, 0x9340, 0x934F); // Unconnected VIC address bus space. - mapChipToMemory(via1, 0x9350, 0x935F); - mapChipToMemory(via2, 0x9360, 0x936F); - mapChipToMemory(via1Via2, 0x9370, 0x937F); - mapChipToMemory(vic, 0x9380, 0x938F); // Unconnected VIC address bus space. - mapChipToMemory(via1, 0x9390, 0x939F); - mapChipToMemory(via2, 0x93A0, 0x93AF); - mapChipToMemory(via1Via2, 0x93B0, 0x93BF); - mapChipToMemory(vic, 0x93C0, 0x93CF); // Unconnected VIC address bus space. - mapChipToMemory(via1, 0x93D0, 0x93DF); - mapChipToMemory(via2, 0x93E0, 0x93EF); - mapChipToMemory(via1Via2, 0x93F0, 0x93FF); - - // The colour RAM lives at this spot by default. - mapChipToMemory(new NibbleRamChip(), 0x9400, 0x97FF); - - mapChipToMemory(vic, 0x9800, 0x9FFF); // Unconnected VIC address bus space. - - // It is possible to connect RAM in this block, even though it would normally be - // a cartridge ROM. - mapChipToMemory((ramExpansion & BLK_5) != 0 ? new RamChip() : new UnconnectedMemory(), 0xA000, 0xBFFF); - - mapChipToMemory(new RomChip(), 0xC000, 0xDFFF, basicRom); - mapChipToMemory(new RomChip(), 0xE000, 0xFFFF, kernalRom); + + for (int i=0xBC00; i<=0xBFFF; i++) { + boolean isVic = ((i & 0xFF00) == 0xBC00); + boolean isVia1 = ((i & 0x0030) == 0x0010); + boolean isVia2 = ((i & 0x0030) == 0x0020); + + if (isVic && !isVia1 && !isVia2) mapChipToMemory(vic, i, i); + if (isVic && isVia1 && !isVia2) mapChipToMemory(vicVia1, i, i); + if (isVic && !isVia1 && isVia2) mapChipToMemory(vicVia2, i, i); + if (isVic && isVia1 && isVia2) mapChipToMemory(vicVia1Via2, i, i); + if (!isVic && isVia1 && !isVia2) mapChipToMemory(via1, i, i); + if (!isVic && !isVia1 && isVia2) mapChipToMemory(via2, i, i); + if (!isVic && isVia1 && isVia2) mapChipToMemory(via1Via2, i, i); + + // TODO: Does this need vic bus version of unconnected memory? + if (!isVic && !isVia1 && !isVia2) mapChipToMemory(new UnconnectedMemory(), i, i); + } } /** diff --git a/core/src/main/java/emu/jvic/video/Vic.java b/core/src/main/java/emu/jvic/video/Vic.java index b679b06..7e7e608 100644 --- a/core/src/main/java/emu/jvic/video/Vic.java +++ b/core/src/main/java/emu/jvic/video/Vic.java @@ -42,8 +42,8 @@ public abstract class Vic extends MemoryMappedChip { /** * A lookup table to map between the VIC chip's memory addresses and VIC 20 memory map. */ - protected final static int[] VIC_MEM_TABLE = new int[0x4000]; - { + protected final int[] VIC_MEM_TABLE = new int[0x4000]; + private void buildMemTable() { for (int i=0; i<0x2000; i++) { VIC_MEM_TABLE[i] = 0x8000 + i; } @@ -53,22 +53,24 @@ public abstract class Vic extends MemoryMappedChip { } // VIC chip memory mapped registers. - protected static final int VIC_REG_0 = 0x9000; // ABBBBBBB A=Interlace B=Screen Origin X (4 pixels granularity) - protected static final int VIC_REG_1 = 0x9001; // CCCCCCCC C=Screen Origin Y (2 pixel granularity) - protected static final int VIC_REG_2 = 0x9002; // HDDDDDDD D=Number of Columns - protected static final int VIC_REG_3 = 0x9003; // GEEEEEEF E=Number of Rows F=Double Size Chars - protected static final int VIC_REG_4 = 0x9004; // GGGGGGGG G=Raster Line - protected static final int VIC_REG_5 = 0x9005; // HHHHIIII H=Screen Mem Addr I=Char Mem Addr - protected static final int VIC_REG_6 = 0x9006; // JJJJJJJJ Light pen X - protected static final int VIC_REG_7 = 0x9007; // KKKKKKKK Light pen Y - protected static final int VIC_REG_8 = 0x9008; // LLLLLLLL Paddle X - protected static final int VIC_REG_9 = 0x9009; // MMMMMMMM Paddle Y - protected static final int VIC_REG_10 = 0x900A; // NRRRRRRR Sound voice 1 - protected static final int VIC_REG_11 = 0x900B; // OSSSSSSS Sound voice 2 - protected static final int VIC_REG_12 = 0x900C; // PTTTTTTT Sound voice 3 - protected static final int VIC_REG_13 = 0x900D; // QUUUUUUU Noise voice - protected static final int VIC_REG_14 = 0x900E; // WWWWVVVV W=Auxiliary colour V=Volume control - protected static final int VIC_REG_15 = 0x900F; // XXXXYZZZ X=Background colour Y=Reverse Z=Border colour + protected static final int VIC_REG_START_ADDR = 0x9000; + + protected int VIC_REG_0; // ABBBBBBB A=Interlace B=Screen Origin X (4 pixels granularity) + protected int VIC_REG_1; // CCCCCCCC C=Screen Origin Y (2 pixel granularity) + protected int VIC_REG_2; // HDDDDDDD D=Number of Columns + protected int VIC_REG_3; // GEEEEEEF E=Number of Rows F=Double Size Chars + protected int VIC_REG_4; // GGGGGGGG G=Raster Line + protected int VIC_REG_5; // HHHHIIII H=Screen Mem Addr I=Char Mem Addr + protected int VIC_REG_6; // JJJJJJJJ Light pen X + protected int VIC_REG_7; // KKKKKKKK Light pen Y + protected int VIC_REG_8; // LLLLLLLL Paddle X + protected int VIC_REG_9; // MMMMMMMM Paddle Y + protected int VIC_REG_10; // NRRRRRRR Sound voice 1 + protected int VIC_REG_11; // OSSSSSSS Sound voice 2 + protected int VIC_REG_12; // PTTTTTTT Sound voice 3 + protected int VIC_REG_13; // QUUUUUUU Noise voice + protected int VIC_REG_14; // WWWWVVVV W=Auxiliary colour V=Volume control + protected int VIC_REG_15; // XXXXYZZZ X=Background colour Y=Reverse Z=Border colour // Constants for the fetch state of the vic_core1_loop. protected static final int FETCH_OUTSIDE_MATRIX = 0; @@ -165,12 +167,42 @@ public Vic(PixelData pixelData, MachineType machineType, Snapshot snapshot) { this.pixelData = pixelData; this.machineType = machineType; + buildMemTable(); + initRegNumbers(); reset(); if (snapshot != null) { loadSnapshot(snapshot); } } + + protected void initRegNumbers() { + initRegNumbers(VIC_REG_START_ADDR); + } + + /** + * Initialises the VIC register numbers based on the base address. + * + * @param baseAddress + */ + protected void initRegNumbers(int baseAddress) { + VIC_REG_0 = baseAddress + 0; + VIC_REG_1 = baseAddress + 1; + VIC_REG_2 = baseAddress + 2; + VIC_REG_3 = baseAddress + 3; + VIC_REG_4 = baseAddress + 4; + VIC_REG_5 = baseAddress + 5; + VIC_REG_6 = baseAddress + 6; + VIC_REG_7 = baseAddress + 7; + VIC_REG_8 = baseAddress + 8; + VIC_REG_9 = baseAddress + 9; + VIC_REG_10 = baseAddress + 10; + VIC_REG_11 = baseAddress + 11; + VIC_REG_12 = baseAddress + 12; + VIC_REG_13 = baseAddress + 13; + VIC_REG_14 = baseAddress + 14; + VIC_REG_15 = baseAddress + 15; + } /** * Initialises the VIC chip with the state stored in the Snapshot. @@ -222,76 +254,7 @@ public int readMemory(int address) { // Handle all VIC chip memory address ranges, including undocumented ones. address = (address & 0xFF0F); - - switch (address) { - case VIC_REG_0: - value = mem[address]; - break; - - case VIC_REG_1: - value = mem[address]; - break; - - case VIC_REG_2: - value = mem[address]; - break; - - case VIC_REG_3: - value = mem[address]; - break; - - case VIC_REG_4: - value = mem[address]; - break; - - case VIC_REG_5: - value = mem[address]; - break; - - case VIC_REG_6: - value = mem[address]; - break; - - case VIC_REG_7: - value = mem[address]; - break; - - case VIC_REG_8: - value = mem[address]; - break; - - case VIC_REG_9: - value = mem[address]; - break; - - case VIC_REG_10: - value = mem[address]; - break; - - case VIC_REG_11: - value = mem[address]; - break; - - case VIC_REG_12: - value = mem[address]; - break; - - case VIC_REG_13: - value = mem[address]; - break; - - case VIC_REG_14: - value = mem[address]; - break; - - case VIC_REG_15: - value = mem[address]; - break; - - default: - value = charData & 0xFF; - } - + value = mem[address]; memory.setLastBusData(value); return value; @@ -309,63 +272,14 @@ public void writeMemory(int address, int value) { // This is how the VIC chip is mapped, i.e. each register to multiple addresses. address = address & 0xFF0F; - switch (address) { - case VIC_REG_0: // $9000 Left margin, or horizontal origin (4 pixel granularity) - mem[address] = value; + switch (address & 0xF) { + case 4: + case 6: + case 7: + case 8: + case 9: break; - - case VIC_REG_1: // $9001 Top margin, or vertical origin (2 pixel granularity) - mem[address] = value; - break; - - case VIC_REG_2: // $9002 Video Matrix Columns, Video and colour memory - mem[address] = value; - break; - - case VIC_REG_3: // $9003 Video Matrix Rows, Character size - mem[address] = value; - break; - - case VIC_REG_4: // $9004 Raster line counter (READ ONLY) - break; - - case VIC_REG_5: // $9005 Video matrix and char generator base address control - mem[address] = value; - break; - - case VIC_REG_6: // $9006 Light pen X (READ ONLY) - break; - - case VIC_REG_7: // $9007 Light pen Y (READ ONLY) - break; - - case VIC_REG_8: // $9008 Paddle X (READ ONLY) - break; - - case VIC_REG_9: // $9009 Paddle Y (READ ONLY) - break; - - case VIC_REG_10: // $900A Bass sound switch and frequency - mem[address] = value; - break; - - case VIC_REG_11: // $900B Alto sound switch and frequency - mem[address] = value; - break; - - case VIC_REG_12: // $900C Soprano sound switch and frequency - mem[address] = value; - break; - - case VIC_REG_13: // $900D Noise sound switch and frequency - mem[address] = value; - break; - - case VIC_REG_14: // $900E Auxiliary Colour, Master Volume - mem[address] = value; - break; - - case VIC_REG_15: // $900F Screen and Border Colours, Reverse Video + default: mem[address] = value; break; } @@ -384,6 +298,14 @@ protected void pio_sm_put(int pio, int sm, int pixel) { } } + /** + * Used by VIC44 modes. + * + * @param charRom + */ + public void setCharRom(byte[] charRom) { + } + /** * Emulates a single cycle of the VIC chip. * diff --git a/core/src/main/java/emu/jvic/video/Vic44.java b/core/src/main/java/emu/jvic/video/Vic44.java index 6246d22..a3d5819 100644 --- a/core/src/main/java/emu/jvic/video/Vic44.java +++ b/core/src/main/java/emu/jvic/video/Vic44.java @@ -91,6 +91,12 @@ public class Vic44 extends Vic { private int[] pal_palette = pal_palette_e; private int[] pal_trunc_palette = pal_palette; + private static final int VIC44_COLOUR_RAM_BASE_ADDRESS = 0x9400; + private int colourRamBaseAddress; + + // PIVIC has the char ROM internally cached, so we simulate that. + private byte[] charRom; + /** * Constructor for Vic44. * @@ -99,7 +105,20 @@ public class Vic44 extends Vic { * @param snapshot Optional snapshot of the machine state to start with. */ public Vic44(PixelData pixelData, MachineType machineType, Snapshot snapshot) { + this(pixelData, machineType, snapshot, VIC44_COLOUR_RAM_BASE_ADDRESS); + } + + /** + * Constructor for Vic44. + * + * @param pixelData Interface to the platform specific mechanism for writing pixels. + * @param machineType The type of machine, PAL or NTSC. + * @param snapshot Optional snapshot of the machine state to start with. + * @param colourRamBaseAddress The base address for colour RAM. + */ + public Vic44(PixelData pixelData, MachineType machineType, Snapshot snapshot, int colourRamBaseAddress) { super(pixelData, machineType, snapshot); + this.colourRamBaseAddress = colourRamBaseAddress; } /** @@ -825,20 +844,16 @@ public boolean emulateCycle() { int screenAddress = screen_mem_start + videoMatrixCounter; switch ((screenAddress >> 10) & 0xF) { - case 4: - case 5: - case 6: - case 7: - case 9: - case 10: - case 11: - // Unconnected memory, so VIC chip sees what CPU put on bus. - cellIndex = memory.getLastBusData(); + case 0: + case 1: + case 2: + case 3: + // PIVIC has char ROM embedded. Not useful to have screen mem here though. + cellIndex = charRom[screenAddress]; break; - default: + // All other VIC chip read address go to main memory. cellIndex = mem[VIC_MEM_TABLE[screenAddress & 0x3FFF]]; - memory.setLastBusData(cellIndex); break; } @@ -846,7 +861,7 @@ public boolean emulateCycle() { // index also happens to automatically fetch the foreground colour from the // Colour Matrix via the top 4 lines of the data bus (DB8-DB11), which are // wired directly from colour RAM in to the VIC chip. - colourData = mem[0x9400 + (screenAddress & 0x3ff)]; + colourData = mem[colourRamBaseAddress + (screenAddress & 0x3ff)]; // Output the 1st pixel of next character. Note that this is not the character // that relates to the cell index and colour data fetched above. @@ -879,20 +894,16 @@ public boolean emulateCycle() { charDataOffset = char_mem_start + (cellIndex << char_size_shift) + cellDepthCounter; switch ((charDataOffset >> 10) & 0xF) { - case 4: - case 5: - case 6: - case 7: - case 9: - case 10: - case 11: - // Unconnected memory, so VIC chip sees what CPU put on bus. - charDataLatch = memory.getLastBusData(); + case 0: + case 1: + case 2: + case 3: + // PIVIC has char ROM embedded. + charDataLatch = charRom[charDataOffset]; break; default: // Fetch cell data, initially latched to the side until it is needed. charDataLatch = mem[VIC_MEM_TABLE[(charDataOffset & 0x3FFF)]]; - memory.setLastBusData(charDataLatch); break; } @@ -991,4 +1002,13 @@ public boolean emulateCycle() { return frameRenderComplete; } + + /** + * For the VIC44 and VIC44K, the char ROM data is embedded and used directly. + * + * @param charRom The content of the char ROM to use. + */ + public void setCharRom(byte[] charRom) { + this.charRom = charRom; + } } diff --git a/core/src/main/java/emu/jvic/video/Vic44k.java b/core/src/main/java/emu/jvic/video/Vic44k.java new file mode 100644 index 0000000..a1d9963 --- /dev/null +++ b/core/src/main/java/emu/jvic/video/Vic44k.java @@ -0,0 +1,53 @@ +package emu.jvic.video; + +import emu.jvic.MachineType; +import emu.jvic.PixelData; +import emu.jvic.snap.Snapshot; + +public class Vic44k extends Vic44 { + + private static final int VIC44K_COLOUR_RAM_BASE_ADDRESS = 0xB800; + private static final int VIC44K_REGISTER_BASE_ADDRESS = 0xBC00; + + /** + * Constructor for Vic4k. + * + * @param pixelData Interface to the platform specific mechanism for writing pixels. + * @param machineType The type of machine, PAL or NTSC. + * @param snapshot Optional snapshot of the machine state to start with. + */ + public Vic44k(PixelData pixelData, MachineType machineType, Snapshot snapshot) { + super(pixelData, machineType, snapshot, VIC44K_COLOUR_RAM_BASE_ADDRESS); + + // VIC chip addresses VIC 20 addresses and their normal usage + // + // $0000 $A000 1K RAM [Char ROM during VIC cycle] + // $0400 $A400 1K RAM [Char ROM during VIC cycle] + // $0800 $A800 1K RAM [Char ROM during VIC cycle] + // $0C00 $AC00 1K RAM [Char ROM during VIC cycle] + // $1000 $B000 1K RAM + // $1400 $B400 1K RAM Default Screen memory + // $1800 $B800 Colour RAM (fixed location) + // $1C00 $BC00 VIC and VIA chips + // + // $2000 $8000 1K RAM + // $2400 $8400 1K RAM + // $2800 $8800 1K RAM + // $2C00 $8C00 1K RAM + // $3000 $9000 1K RAM + // $3400 $9400 1K RAM + // $3800 $9800 1K RAM + // $3C00 $9C00 1K RAM + + for (int i=0; i<0x2000; i++) { + VIC_MEM_TABLE[i] = 0xA000 + i; + } + for (int i=0x2000; i<0x4000; i++) { + VIC_MEM_TABLE[i] = 0x8000 + i; + } + } + + protected void initRegNumbers() { + initRegNumbers(VIC44K_REGISTER_BASE_ADDRESS); + } +} diff --git a/html/src/main/java/emu/jvic/gwt/GwtJVicRunner.java b/html/src/main/java/emu/jvic/gwt/GwtJVicRunner.java index 959373c..3a11f25 100644 --- a/html/src/main/java/emu/jvic/gwt/GwtJVicRunner.java +++ b/html/src/main/java/emu/jvic/gwt/GwtJVicRunner.java @@ -112,16 +112,10 @@ private ArrayBuffer convertProgramToArrayBuffer(Program program, AppConfigItem a int index = 0; MachineType machineType = MachineType.valueOf(appConfigItem.getMachineType()); - byte[] basicRom = (machineType.equals(MachineType.VIC44)? - Gdx.files.internal("roms/vic_44_basic.rom").readBytes() : - Gdx.files.internal("roms/basic.rom").readBytes()); + byte[] basicRom = loadBasicRom(machineType); byte[] dos1541Rom = Gdx.files.internal("roms/dos1541.rom").readBytes(); byte[] charRom = Gdx.files.internal("roms/char.rom").readBytes(); - byte[] kernalRom = (machineType.equals(MachineType.VIC44)? - Gdx.files.internal("roms/vic_44_kernal.rom").readBytes() : - (machineType.equals(MachineType.NTSC)? - Gdx.files.internal("roms/kernal_ntsc.rom").readBytes() : - Gdx.files.internal("roms/kernal_pal.rom").readBytes())); + byte[] kernalRom = loadKernalRom(machineType); for (int i=0; i < basicRom.length; index++, i++) { programUint8Array.set(index, (basicRom[i] & 0xFF)); diff --git a/html/src/main/java/emu/jvic/gwt/GwtSoundGenerator.java b/html/src/main/java/emu/jvic/gwt/GwtSoundGenerator.java index 2931c76..5e15d74 100644 --- a/html/src/main/java/emu/jvic/gwt/GwtSoundGenerator.java +++ b/html/src/main/java/emu/jvic/gwt/GwtSoundGenerator.java @@ -20,8 +20,8 @@ public class GwtSoundGenerator extends SoundGenerator { // Number of samples to queue before being output to the audio hardware. public static final int SAMPLE_LATENCY = 3072; - private static final int VIC_REG_10 = 0x900A; - private static final int VIC_REG_14 = 0x900E; + private int VIC_REG_10 = 0x900A; + private int VIC_REG_14 = 0x900E; private int cyclesPerSample; private AudioDevice audioDevice; @@ -88,6 +88,11 @@ public void initSound(MachineType machineType) { voiceCounters = new int[4]; voiceShiftRegisters = new int[4]; voiceClockDividerTriggers = new int[] { 0xF, 0x7, 0x3, 0x1 }; + + if (machineType.isVIC44K()) { + VIC_REG_10 = 0xBC0A; + VIC_REG_14 = 0xBC0E; + } } /** diff --git a/lwjgl3/src/main/java/emu/jvic/lwjgl3/DesktopJVicRunner.java b/lwjgl3/src/main/java/emu/jvic/lwjgl3/DesktopJVicRunner.java index c5d49be..e3f2b27 100644 --- a/lwjgl3/src/main/java/emu/jvic/lwjgl3/DesktopJVicRunner.java +++ b/lwjgl3/src/main/java/emu/jvic/lwjgl3/DesktopJVicRunner.java @@ -69,16 +69,10 @@ private void runProgram(AppConfigItem appConfigItem, Program program) { machine = new Machine(soundGenerator, keyboardMatrix, pixelData); // Load the ROM files. - byte[] basicRom = (machineType.equals(MachineType.VIC44)? - Gdx.files.internal("roms/vic_44_basic.rom").readBytes() : - Gdx.files.internal("roms/basic.rom").readBytes()); + byte[] basicRom = loadBasicRom(machineType); byte[] dos1541Rom = Gdx.files.internal("roms/dos1541.rom").readBytes(); byte[] charRom = Gdx.files.internal("roms/char.rom").readBytes(); - byte[] kernalRom = (machineType.equals(MachineType.VIC44)? - Gdx.files.internal("roms/vic_44_kernal.rom").readBytes() : - (machineType.equals(MachineType.NTSC)? - Gdx.files.internal("roms/kernal_ntsc.rom").readBytes() : - Gdx.files.internal("roms/kernal_pal.rom").readBytes())); + byte[] kernalRom = loadKernalRom(machineType); Queue autoRunCmdQueue = null; Callable> autoLoadProgram = machine.init( diff --git a/lwjgl3/src/main/java/emu/jvic/lwjgl3/DesktopProgramLoader.java b/lwjgl3/src/main/java/emu/jvic/lwjgl3/DesktopProgramLoader.java index efe70c7..1763707 100644 --- a/lwjgl3/src/main/java/emu/jvic/lwjgl3/DesktopProgramLoader.java +++ b/lwjgl3/src/main/java/emu/jvic/lwjgl3/DesktopProgramLoader.java @@ -32,7 +32,11 @@ public void fetchProgram(AppConfigItem appConfigItem, Consumer programC byte[] data = null; try { - if (!appConfigItem.getFilePath().startsWith("http")) { + if ((appConfigItem.getFilePath() == null) || + (appConfigItem.getFilePath().trim().equals(""))) { + // Ignore. Nothing to load. + } + else if (!appConfigItem.getFilePath().startsWith("http")) { FileHandle fileHandle = null; if ("ABSOLUTE".equals(appConfigItem.getFileType())) { fileHandle = Gdx.files.absolute(appConfigItem.getFilePath()); diff --git a/lwjgl3/src/main/java/emu/jvic/lwjgl3/DesktopSoundGenerator.java b/lwjgl3/src/main/java/emu/jvic/lwjgl3/DesktopSoundGenerator.java index afbe570..5753325 100644 --- a/lwjgl3/src/main/java/emu/jvic/lwjgl3/DesktopSoundGenerator.java +++ b/lwjgl3/src/main/java/emu/jvic/lwjgl3/DesktopSoundGenerator.java @@ -16,8 +16,8 @@ public class DesktopSoundGenerator extends SoundGenerator { private static final int SAMPLE_RATE = 22050; - private static final int VIC_REG_10 = 0x900A; - private static final int VIC_REG_14 = 0x900E; + private int VIC_REG_10 = 0x900A; + private int VIC_REG_14 = 0x900E; private int cyclesPerSample; private byte[] sampleBuffer; @@ -54,6 +54,11 @@ public void initSound(MachineType machineType) { voiceCounters = new int[4]; voiceShiftRegisters = new int[4]; voiceClockDividerTriggers = new int[] { 0xF, 0x7, 0x3, 0x1 }; + + if (machineType.isVIC44K()) { + VIC_REG_10 = 0xBC0A; + VIC_REG_14 = 0xBC0E; + } } @Override