Pissing about with TZX support
This commit is contained in:
@@ -1312,6 +1312,9 @@ namespace Core.Cpu
|
|||||||
case 0x4D: // RETI Does not affect IFF1 or IFF2
|
case 0x4D: // RETI Does not affect IFF1 or IFF2
|
||||||
PC = Pop();
|
PC = Pop();
|
||||||
return 14;
|
return 14;
|
||||||
|
case 0x4F: // LD R, A
|
||||||
|
R = AF.High;
|
||||||
|
return 9;
|
||||||
case 0x51: // OUT (C), D
|
case 0x51: // OUT (C), D
|
||||||
// BC.Word goes to the address bus, D (DE.High) goes to the data bus
|
// BC.Word goes to the address bus, D (DE.High) goes to the data bus
|
||||||
_simpleIoBus.WritePort(BC.Word, DE.High);
|
_simpleIoBus.WritePort(BC.Word, DE.High);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using static Core.Io.TurboSpeedBlock;
|
||||||
//using Core.Io;
|
//using Core.Io;
|
||||||
|
|
||||||
namespace Core.Io
|
namespace Core.Io
|
||||||
@@ -20,7 +21,7 @@ namespace Core.Io
|
|||||||
private List<TzxBlock> _tzxBlocks = new List<TzxBlock>();
|
private List<TzxBlock> _tzxBlocks = new List<TzxBlock>();
|
||||||
|
|
||||||
// State Machine Tracking
|
// State Machine Tracking
|
||||||
private enum TapeState { Idle, Pilot, Sync1, Sync2, Data, Pause }
|
private enum TapeState { Idle, Pilot, Sync1, Sync2, Data, Pause, PureTone, PulseSequence }
|
||||||
private TapeState _state = TapeState.Idle;
|
private TapeState _state = TapeState.Idle;
|
||||||
|
|
||||||
public bool EarBit { get; private set; } = false; // The physical audio signal sent to the CPU
|
public bool EarBit { get; private set; } = false; // The physical audio signal sent to the CPU
|
||||||
@@ -30,6 +31,8 @@ namespace Core.Io
|
|||||||
private int _byteIndex = 0;
|
private int _byteIndex = 0;
|
||||||
private int _bitIndex = 0;
|
private int _bitIndex = 0;
|
||||||
private int _pulseCountForCurrentBit = 0;
|
private int _pulseCountForCurrentBit = 0;
|
||||||
|
private ushort[] _sequencePulses = Array.Empty<ushort>();
|
||||||
|
private int _sequenceIndex = 0;
|
||||||
|
|
||||||
public void LoadTapData(byte[] fileData)
|
public void LoadTapData(byte[] fileData)
|
||||||
{
|
{
|
||||||
@@ -85,8 +88,12 @@ namespace Core.Io
|
|||||||
_currentBlock = _playQueue.Dequeue();
|
_currentBlock = _playQueue.Dequeue();
|
||||||
|
|
||||||
// If it's metadata (like our ArchiveInfoBlock), just skip it and load the next audio block
|
// If it's metadata (like our ArchiveInfoBlock), just skip it and load the next audio block
|
||||||
if (_currentBlock.BlockId == 0x32)
|
if (_currentBlock is ArchiveInfoBlock ||
|
||||||
|
_currentBlock is TextDescriptionBlock ||
|
||||||
|
_currentBlock is GroupStartBlock ||
|
||||||
|
_currentBlock is GroupEndBlock)
|
||||||
{
|
{
|
||||||
|
// It has no audio data! Discard it and recursively pull the next block.
|
||||||
LoadNextBlock();
|
LoadNextBlock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -121,6 +128,70 @@ namespace Core.Io
|
|||||||
_oneLength = turboBlock.OneBitPulseLength;
|
_oneLength = turboBlock.OneBitPulseLength;
|
||||||
_pauseTStates = turboBlock.PauseAfterMs * 3500;
|
_pauseTStates = turboBlock.PauseAfterMs * 3500;
|
||||||
}
|
}
|
||||||
|
else if (_currentBlock is PauseBlock pauseBlock)
|
||||||
|
{
|
||||||
|
if (pauseBlock.PauseDurationMs == 0)
|
||||||
|
{
|
||||||
|
// A duration of 0 literally means "Stop the Tape Deck"
|
||||||
|
Stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, just drop straight into a Pause state!
|
||||||
|
_state = TapeState.Pause;
|
||||||
|
_pauseTStates = pauseBlock.PauseDurationMs * 3500;
|
||||||
|
_pulseTimer = _pauseTStates;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (_currentBlock is PureToneBlock toneBlock)
|
||||||
|
{
|
||||||
|
_state = TapeState.PureTone;
|
||||||
|
_pilotPulsesRemaining = toneBlock.PulseCount;
|
||||||
|
_pilotLength = toneBlock.PulseLength;
|
||||||
|
|
||||||
|
_pulseTimer = _pilotLength; // Start the first pulse
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (_currentBlock is PulseSequenceBlock seqBlock)
|
||||||
|
{
|
||||||
|
_state = TapeState.PulseSequence;
|
||||||
|
_sequencePulses = seqBlock.Pulses;
|
||||||
|
_sequenceIndex = 0;
|
||||||
|
|
||||||
|
// Safety check: only start if the array actually has data
|
||||||
|
if (_sequencePulses.Length > 0)
|
||||||
|
{
|
||||||
|
_pulseTimer = _sequencePulses[_sequenceIndex];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Empty block? Just skip it.
|
||||||
|
_state = TapeState.Idle;
|
||||||
|
LoadNextBlock();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (_currentBlock is PureDataBlock pureDataBlock)
|
||||||
|
{
|
||||||
|
_zeroLength = pureDataBlock.ZeroBitPulseLength;
|
||||||
|
_oneLength = pureDataBlock.OneBitPulseLength;
|
||||||
|
_pauseTStates = pureDataBlock.PauseAfterMs * 3500;
|
||||||
|
if (pureDataBlock.Data.Length == 0)
|
||||||
|
{
|
||||||
|
_state = TapeState.Pause;
|
||||||
|
_pulseTimer = _pauseTStates;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Skip the preamble completely and jump straight to Data processing!
|
||||||
|
_state = TapeState.Data;
|
||||||
|
_byteIndex = 0;
|
||||||
|
_bitIndex = 7;
|
||||||
|
_pulseCountForCurrentBit = 0;
|
||||||
|
|
||||||
|
// Prime the very first data pulse into the timer
|
||||||
|
CalculateNextDataPulse();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Start the very first pilot pulse!
|
// Start the very first pilot pulse!
|
||||||
_pulseTimer = _pilotLength;
|
_pulseTimer = _pilotLength;
|
||||||
@@ -164,37 +235,55 @@ namespace Core.Io
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TapeState.Sync2:
|
case TapeState.Sync2:
|
||||||
|
// --- THE FIX: Check if the block actually has data! ---
|
||||||
|
byte[] upcomingData = ExtractDataFromBlock(_currentBlock);
|
||||||
|
|
||||||
|
if (upcomingData.Length == 0)
|
||||||
|
{
|
||||||
|
// It was just a timing calibration block! Skip Data and go straight to Pause.
|
||||||
|
_state = TapeState.Pause;
|
||||||
|
_pulseTimer += _pauseTStates;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Normal block. Proceed to extract the bits.
|
||||||
_state = TapeState.Data;
|
_state = TapeState.Data;
|
||||||
CalculateNextDataPulse();
|
CalculateNextDataPulse();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TapeState.Data:
|
case TapeState.Data:
|
||||||
_pulseCountForCurrentBit++;
|
_pulseCountForCurrentBit++;
|
||||||
if (_pulseCountForCurrentBit < 2)
|
if (_pulseCountForCurrentBit < 2)
|
||||||
{
|
{
|
||||||
// A bit requires 2 pulses (one high, one low)
|
|
||||||
CalculateNextDataPulse();
|
CalculateNextDataPulse();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Move to next bit
|
|
||||||
_pulseCountForCurrentBit = 0;
|
_pulseCountForCurrentBit = 0;
|
||||||
_bitIndex--;
|
_bitIndex--;
|
||||||
|
|
||||||
|
byte[] dataArray = ExtractDataFromBlock(_currentBlock);
|
||||||
|
|
||||||
|
// --- THE SPEEDLOCK FIX: Used Bits In Last Byte ---
|
||||||
|
byte usedBits = 8;
|
||||||
|
if (_currentBlock is TurboSpeedBlock t && t.UsedBitsInLastByte > 0) usedBits = t.UsedBitsInLastByte;
|
||||||
|
if (_currentBlock is PureDataBlock p && p.UsedBitsInLastByte > 0) usedBits = p.UsedBitsInLastByte;
|
||||||
|
|
||||||
|
// Stop exactly when we've processed the required bits in the final byte!
|
||||||
|
if (_byteIndex == dataArray.Length - 1 && _bitIndex < (8 - usedBits))
|
||||||
|
{
|
||||||
|
_state = TapeState.Pause;
|
||||||
|
_pulseTimer += _pauseTStates;
|
||||||
|
return; // Drop out of AdvanceState immediately
|
||||||
|
}
|
||||||
|
|
||||||
if (_bitIndex < 0)
|
if (_bitIndex < 0)
|
||||||
{
|
{
|
||||||
// Move to next byte
|
|
||||||
_bitIndex = 7;
|
_bitIndex = 7;
|
||||||
_byteIndex++;
|
_byteIndex++;
|
||||||
|
|
||||||
// --- THE FIX ---
|
|
||||||
// Extract the raw byte array from the generic TzxBlock
|
|
||||||
byte[] dataArray = ExtractDataFromBlock(_currentBlock);
|
|
||||||
|
|
||||||
// Now we can safely check the length!
|
|
||||||
if (_byteIndex >= dataArray.Length)
|
if (_byteIndex >= dataArray.Length)
|
||||||
{
|
{
|
||||||
// Block finished! Enter the Pause state and wait for the dynamic delay
|
|
||||||
_state = TapeState.Pause;
|
_state = TapeState.Pause;
|
||||||
_pulseTimer += _pauseTStates;
|
_pulseTimer += _pauseTStates;
|
||||||
return;
|
return;
|
||||||
@@ -207,19 +296,52 @@ namespace Core.Io
|
|||||||
_state = TapeState.Idle;
|
_state = TapeState.Idle;
|
||||||
LoadNextBlock();
|
LoadNextBlock();
|
||||||
break;
|
break;
|
||||||
|
case TapeState.PureTone:
|
||||||
|
_pilotPulsesRemaining--;
|
||||||
|
if (_pilotPulsesRemaining > 0)
|
||||||
|
{
|
||||||
|
_pulseTimer += _pilotLength;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The tone is over! Move immediately to the next block.
|
||||||
|
_state = TapeState.Idle;
|
||||||
|
LoadNextBlock();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TapeState.PulseSequence:
|
||||||
|
_sequenceIndex++;
|
||||||
|
|
||||||
|
if (_sequenceIndex < _sequencePulses.Length)
|
||||||
|
{
|
||||||
|
// Load the length of the next raw pulse
|
||||||
|
_pulseTimer += _sequencePulses[_sequenceIndex];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Array is finished! Move immediately to the next block.
|
||||||
|
_state = TapeState.Idle;
|
||||||
|
LoadNextBlock();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CalculateNextDataPulse()
|
private void CalculateNextDataPulse()
|
||||||
{
|
{
|
||||||
// We need to pull the Data array from the base TzxBlock
|
// 1. Use the helper method to grab the array, which automatically supports PureData blocks too!
|
||||||
byte[] dataArray = Array.Empty<byte>();
|
byte[] dataArray = ExtractDataFromBlock(_currentBlock);
|
||||||
if (_currentBlock is StandardSpeedBlock std) dataArray = std.Data;
|
|
||||||
else if (_currentBlock is TurboSpeedBlock turbo) dataArray = turbo.Data;
|
|
||||||
|
|
||||||
|
// 2. THE BULLETPROOF VEST: Catch zero-length arrays and out-of-bounds indices before they crash the CPU
|
||||||
|
if (dataArray.Length == 0 || _byteIndex >= dataArray.Length)
|
||||||
|
{
|
||||||
|
_state = TapeState.Pause;
|
||||||
|
_pulseTimer += _pauseTStates;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Extract the bit and load the dynamic pulse length into the timer
|
||||||
bool isBitOne = (dataArray[_byteIndex] & (1 << _bitIndex)) != 0;
|
bool isBitOne = (dataArray[_byteIndex] & (1 << _bitIndex)) != 0;
|
||||||
|
|
||||||
// Use the dynamic zero/one lengths!
|
|
||||||
_pulseTimer += isBitOne ? _oneLength : _zeroLength;
|
_pulseTimer += isBitOne ? _oneLength : _zeroLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,8 +362,8 @@ namespace Core.Io
|
|||||||
{
|
{
|
||||||
if (block is StandardSpeedBlock std) return std.Data;
|
if (block is StandardSpeedBlock std) return std.Data;
|
||||||
if (block is TurboSpeedBlock turbo) return turbo.Data;
|
if (block is TurboSpeedBlock turbo) return turbo.Data;
|
||||||
|
if (block is PureDataBlock pureData) return pureData.Data; // <-- Added this line!
|
||||||
|
|
||||||
// If it's metadata or a block type without data, return an empty array
|
|
||||||
return Array.Empty<byte>();
|
return Array.Empty<byte>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,66 @@ namespace Core.Io
|
|||||||
// This is a 24-bit integer in the file, so we store it in a standard 32-bit int
|
// This is a 24-bit integer in the file, so we store it in a standard 32-bit int
|
||||||
public int DataLength { get; set; }
|
public int DataLength { get; set; }
|
||||||
|
|
||||||
|
public byte[] Data { get; set; } = Array.Empty<byte>();
|
||||||
|
// ID 0x30: Text Description
|
||||||
|
public class TextDescriptionBlock : TzxBlock
|
||||||
|
{
|
||||||
|
public override int BlockId => 0x30;
|
||||||
|
public string Description { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID 0x20: Pause / Stop the Tape
|
||||||
|
public class PauseBlock : TzxBlock
|
||||||
|
{
|
||||||
|
public override int BlockId => 0x20;
|
||||||
|
public ushort PauseDurationMs { get; set; } // 0 means "Stop the tape"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID 0x21: Group Start (Used to group blocks in UI tape browsers)
|
||||||
|
public class GroupStartBlock : TzxBlock
|
||||||
|
{
|
||||||
|
public override int BlockId => 0x21;
|
||||||
|
public string GroupName { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID 0x22: Group End
|
||||||
|
public class GroupEndBlock : TzxBlock
|
||||||
|
{
|
||||||
|
public override int BlockId => 0x22;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID 0x12: Pure Tone Block
|
||||||
|
public class PureToneBlock : TzxBlock
|
||||||
|
{
|
||||||
|
public override int BlockId => 0x12;
|
||||||
|
public ushort PulseLength { get; set; }
|
||||||
|
public ushort PulseCount { get; set; }
|
||||||
|
}
|
||||||
|
// ID 0x13: Pulse Sequence Block
|
||||||
|
public class PulseSequenceBlock : TzxBlock
|
||||||
|
{
|
||||||
|
public override int BlockId => 0x13;
|
||||||
|
|
||||||
|
// Number of pulses (1 to 255)
|
||||||
|
public int PulseCount { get; set; }
|
||||||
|
|
||||||
|
// The exact T-State lengths of each pulse
|
||||||
|
public ushort[] Pulses { get; set; } = Array.Empty<ushort>();
|
||||||
|
}
|
||||||
|
// ID 0x14: Pure Data Block
|
||||||
|
public class PureDataBlock : TzxBlock
|
||||||
|
{
|
||||||
|
public override int BlockId => 0x14;
|
||||||
|
|
||||||
|
public ushort ZeroBitPulseLength { get; set; }
|
||||||
|
public ushort OneBitPulseLength { get; set; }
|
||||||
|
public byte UsedBitsInLastByte { get; set; }
|
||||||
|
public ushort PauseAfterMs { get; set; }
|
||||||
|
|
||||||
|
// The return of the 24-bit integer!
|
||||||
|
public int DataLength { get; set; }
|
||||||
|
|
||||||
public byte[] Data { get; set; } = Array.Empty<byte>();
|
public byte[] Data { get; set; } = Array.Empty<byte>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using static Core.Io.TurboSpeedBlock;
|
||||||
|
|
||||||
namespace Core.Io
|
namespace Core.Io
|
||||||
{
|
{
|
||||||
@@ -65,6 +66,66 @@ namespace Core.Io
|
|||||||
|
|
||||||
blocks.Add(turboBlock);
|
blocks.Add(turboBlock);
|
||||||
break;
|
break;
|
||||||
|
case 0x12: // Pure Tone Block
|
||||||
|
blocks.Add(new PureToneBlock
|
||||||
|
{
|
||||||
|
PulseLength = br.ReadUInt16(),
|
||||||
|
PulseCount = br.ReadUInt16()
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 0x13: // Pulse Sequence Block
|
||||||
|
var seqBlock = new PulseSequenceBlock();
|
||||||
|
|
||||||
|
byte rawCount = br.ReadByte();
|
||||||
|
// TZX Spec: A raw count of 0 means 256 pulses!
|
||||||
|
seqBlock.PulseCount = (rawCount == 0) ? 256 : rawCount;
|
||||||
|
|
||||||
|
seqBlock.Pulses = new ushort[seqBlock.PulseCount];
|
||||||
|
for (int i = 0; i < seqBlock.PulseCount; i++)
|
||||||
|
{
|
||||||
|
seqBlock.Pulses[i] = br.ReadUInt16();
|
||||||
|
}
|
||||||
|
|
||||||
|
blocks.Add(seqBlock);
|
||||||
|
break;
|
||||||
|
case 0x14: // Pure Data Block
|
||||||
|
var pureData = new PureDataBlock
|
||||||
|
{
|
||||||
|
ZeroBitPulseLength = br.ReadUInt16(),
|
||||||
|
OneBitPulseLength = br.ReadUInt16(),
|
||||||
|
UsedBitsInLastByte = br.ReadByte(),
|
||||||
|
PauseAfterMs = br.ReadUInt16()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assemble the 24-bit length
|
||||||
|
byte pdLen0 = br.ReadByte();
|
||||||
|
byte pdLen1 = br.ReadByte();
|
||||||
|
byte pdLen2 = br.ReadByte();
|
||||||
|
pureData.DataLength = pdLen0 | (pdLen1 << 8) | (pdLen2 << 16);
|
||||||
|
|
||||||
|
pureData.Data = br.ReadBytes(pureData.DataLength);
|
||||||
|
blocks.Add(pureData);
|
||||||
|
break;
|
||||||
|
case 0x20: // Pause / Stop Tape Block
|
||||||
|
blocks.Add(new PauseBlock { PauseDurationMs = br.ReadUInt16() });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x21: // Group Start Block
|
||||||
|
byte groupNameLength = br.ReadByte();
|
||||||
|
string groupName = System.Text.Encoding.ASCII.GetString(br.ReadBytes(groupNameLength));
|
||||||
|
blocks.Add(new GroupStartBlock { GroupName = groupName });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x22: // Group End Block
|
||||||
|
// This block has no payload data at all! Just the ID.
|
||||||
|
blocks.Add(new GroupEndBlock());
|
||||||
|
break;
|
||||||
|
case 0x30: // Text Description Block
|
||||||
|
byte textLength30 = br.ReadByte();
|
||||||
|
string description = System.Text.Encoding.ASCII.GetString(br.ReadBytes(textLength30));
|
||||||
|
blocks.Add(new TextDescriptionBlock { Description = description });
|
||||||
|
break;
|
||||||
|
|
||||||
case 0x32: // Archive Info Block
|
case 0x32: // Archive Info Block
|
||||||
var archiveBlock = new ArchiveInfoBlock();
|
var archiveBlock = new ArchiveInfoBlock();
|
||||||
|
|
||||||
@@ -76,10 +137,10 @@ namespace Core.Io
|
|||||||
for (int i = 0; i < stringCount; i++)
|
for (int i = 0; i < stringCount; i++)
|
||||||
{
|
{
|
||||||
byte textId = br.ReadByte();
|
byte textId = br.ReadByte();
|
||||||
byte textLength = br.ReadByte();
|
byte textLength32 = br.ReadByte();
|
||||||
|
|
||||||
// Read the raw bytes and convert them to an ASCII string
|
// Read the raw bytes and convert them to an ASCII string
|
||||||
string text = System.Text.Encoding.ASCII.GetString(br.ReadBytes(textLength));
|
string text = System.Text.Encoding.ASCII.GetString(br.ReadBytes(textLength32));
|
||||||
|
|
||||||
archiveBlock.Metadata[textId] = text;
|
archiveBlock.Metadata[textId] = text;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
<None Remove="ROMS\TAP\Treasure Island - Dizzy.tap" />
|
<None Remove="ROMS\TAP\Treasure Island - Dizzy.tap" />
|
||||||
<None Remove="ROMS\TAP\zexall.tap" />
|
<None Remove="ROMS\TAP\zexall.tap" />
|
||||||
<None Remove="ROMS\TZX\Batman - Release 1.tzx" />
|
<None Remove="ROMS\TZX\Batman - Release 1.tzx" />
|
||||||
|
<None Remove="ROMS\TZX\Renegade 2 - Target Renegade - Side 1.tzx" />
|
||||||
|
<None Remove="ROMS\TZX\Renegade 2 - Target Renegade - Side 2.tzx" />
|
||||||
<None Remove="ROMS\TZX\Split Personalities1.tzx" />
|
<None Remove="ROMS\TZX\Split Personalities1.tzx" />
|
||||||
<None Remove="ROMS\TZX\Split Personalities2.tzx" />
|
<None Remove="ROMS\TZX\Split Personalities2.tzx" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -82,6 +84,12 @@
|
|||||||
<EmbeddedResource Include="ROMS\TZX\Batman - Release 1.tzx">
|
<EmbeddedResource Include="ROMS\TZX\Batman - Release 1.tzx">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="ROMS\TZX\Renegade 2 - Target Renegade - Side 1.tzx">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="ROMS\TZX\Renegade 2 - Target Renegade - Side 2.tzx">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="ROMS\TZX\Split Personalities1.tzx">
|
<EmbeddedResource Include="ROMS\TZX\Split Personalities1.tzx">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
|||||||
BIN
Desktop/ROMS/TZX/Renegade 2 - Target Renegade - Side 1.tzx
Normal file
BIN
Desktop/ROMS/TZX/Renegade 2 - Target Renegade - Side 1.tzx
Normal file
Binary file not shown.
BIN
Desktop/ROMS/TZX/Renegade 2 - Target Renegade - Side 2.tzx
Normal file
BIN
Desktop/ROMS/TZX/Renegade 2 - Target Renegade - Side 2.tzx
Normal file
Binary file not shown.
Reference in New Issue
Block a user