├── LibZealCompiler ├── CodeGeneration │ ├── CpuCodeGenerator.cs │ └── SfcRomWriter.cs ├── Data │ ├── ArgumentSize.cs │ ├── CpuAddressingMode.cs │ ├── CpuAssumeAddressingAttribute.cs │ ├── CpuInstructionStatement.cs │ ├── CpuInstructions.cs │ ├── InstructionArgument.cs │ ├── OpcodeAttribute.cs │ ├── RomHeader.cs │ ├── Scope.cs │ ├── Statement.cs │ └── Vectors.cs ├── Helper │ ├── CpuAddressConverter.cs │ └── EnumHelper.cs ├── LibZealCompiler.csproj ├── Parser │ ├── CompilerErrorException.cs │ ├── ErrorMessage.cs │ ├── ZealCpu.g4 │ ├── ZealCpu.g4.lexer.cs │ ├── ZealCpu.g4.parser.cs │ └── ZealCpuDriver.cs ├── Pass │ ├── CpuPass.cs │ ├── ParseCpuPass.cs │ └── SecondCpuPass.cs ├── Properties │ └── AssemblyInfo.cs └── packages.config ├── README.md ├── ZealCompiler.sln ├── ZealCompiler ├── App.config ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── ZealCompiler.csproj └── ZealCompilerUnitTests ├── CpuCodeGeneratorTest.cs ├── CpuParseTest.cs ├── Extensions └── StringExtensions.cs ├── Properties └── AssemblyInfo.cs ├── ScopeTest.cs ├── SemanticErrorTest.cs ├── ZealCompilerUnitTests.csproj └── packages.config /LibZealCompiler/CodeGeneration/CpuCodeGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using Zeal.Compiler.Data; 6 | using Zeal.Compiler.Helper; 7 | 8 | namespace Zeal.Compiler.CodeGeneration 9 | { 10 | public class CpuCodeGenerator 11 | { 12 | private Stream _stream; 13 | 14 | public List Instructions 15 | { 16 | get; 17 | set; 18 | } 19 | 20 | public Scope Scope 21 | { 22 | private get; 23 | set; 24 | } 25 | 26 | public RomHeader Header 27 | { 28 | private get; 29 | set; 30 | } 31 | 32 | public CpuCodeGenerator(Stream stream) 33 | { 34 | _stream = stream; 35 | } 36 | 37 | public void Generate() 38 | { 39 | foreach(var instruction in Instructions) 40 | { 41 | var opcodes = instruction.Opcode.GetAttributes(); 42 | 43 | var opcode = opcodes.Where(x => x.AddressingMode == instruction.AddressingMode).First(); 44 | 45 | switch(instruction.AddressingMode) 46 | { 47 | // Instruction size of 1 48 | case CpuAddressingMode.Implied: 49 | _stream.WriteByte(opcode.Opcode); 50 | break; 51 | case CpuAddressingMode.Immediate: 52 | case CpuAddressingMode.Direct: 53 | case CpuAddressingMode.Absolute: 54 | case CpuAddressingMode.Relative: 55 | { 56 | _stream.WriteByte(opcode.Opcode); 57 | 58 | if (instruction.Arguments[0] is NumberInstructionArgument) 59 | { 60 | var numberArgument = instruction.Arguments[0] as NumberInstructionArgument; 61 | 62 | if (numberArgument.Size == ArgumentSize.Word 63 | || numberArgument.Size == ArgumentSize.LongWord) 64 | { 65 | writeWord(numberArgument.Number); 66 | } 67 | else 68 | { 69 | _stream.WriteByte((byte)numberArgument.Number); 70 | } 71 | } 72 | else if (instruction.Arguments[0] is LabelInstructionArgument) 73 | { 74 | var labelArgument = instruction.Arguments[0] as LabelInstructionArgument; 75 | long labelPhysicalAddress = Scope.AddressFor(labelArgument.Label); 76 | 77 | if (instruction.AddressingMode == CpuAddressingMode.Relative) 78 | { 79 | byte relativeAddress = Convert.ToByte((labelPhysicalAddress - (_stream.Position + 1)) & 0xFF); 80 | _stream.WriteByte(relativeAddress); 81 | } 82 | else if (instruction.AddressingMode == CpuAddressingMode.Absolute) 83 | { 84 | int ramAddress = CpuAddressConverter.PhysicalToRAM((int)labelPhysicalAddress, Header.MapMode, Header.RomSpeed); 85 | writeWord(ramAddress & 0xFFFF); 86 | } 87 | } 88 | break; 89 | } 90 | } 91 | } 92 | } 93 | 94 | private void writeWord(int number) 95 | { 96 | _stream.WriteByte((byte)(number & 0xFF)); 97 | _stream.WriteByte((byte)(number >> 8)); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /LibZealCompiler/CodeGeneration/SfcRomWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Zeal.Compiler.Parser; 8 | using Zeal.Compiler.Helper; 9 | using Zeal.Compiler.Data; 10 | 11 | namespace Zeal.Compiler.CodeGeneration 12 | { 13 | public static class SfcRomHeader 14 | { 15 | public const int CatridgeHeader = 0x00FFC0; 16 | public const int Checksum = 0x00FFDC; 17 | 18 | public const int NativeVectors = 0x00FFE4; 19 | 20 | public const int EmulationCopVector = 0x00FFF4; 21 | public const int EmulationVectors = 0x00FFF8; 22 | } 23 | 24 | public class SfcRomWriter 25 | { 26 | private Stream _writer; 27 | 28 | public ZealCpuDriver Driver { get; set; } 29 | 30 | private Scope GlobalScope 31 | { 32 | get 33 | { 34 | return Driver.GlobalScope; 35 | } 36 | } 37 | 38 | public SfcRomWriter(Stream stream) 39 | { 40 | _writer = stream; 41 | } 42 | 43 | public void Write() 44 | { 45 | writeCatridgeHeader(); 46 | writeVectors(); 47 | } 48 | 49 | public void ComputeChecksum() 50 | { 51 | _writer.Seek(0, SeekOrigin.End); 52 | 53 | long fileSize = _writer.Position; 54 | 55 | byte[] allBytes = new byte[fileSize]; 56 | 57 | _writer.Seek(0, SeekOrigin.Begin); 58 | 59 | _writer.Read(allBytes, 0, allBytes.Length); 60 | 61 | uint checksum = 0; 62 | 63 | for(int i=0; i 0) 107 | { 108 | chipset = 0x2; 109 | } 110 | 111 | _writer.WriteByte(chipset); 112 | 113 | // Write ROM size 114 | byte romSize = (byte)Math.Log(32, 2); 115 | _writer.WriteByte(romSize); 116 | 117 | // Write RAM size 118 | _writer.WriteByte((byte)Math.Log(Driver.Header.SramSize, 2)); 119 | 120 | // Write Country 121 | _writer.WriteByte((byte)Driver.Header.Country); 122 | 123 | // Write Developer ID 124 | _writer.WriteByte((byte)Driver.Header.Developer); 125 | 126 | // Write ROM version 127 | _writer.WriteByte((byte)Driver.Header.Version); 128 | 129 | // Write initial checksum complement 130 | _writer.WriteByte(0xFF); 131 | _writer.WriteByte(0xFF); 132 | 133 | // Write initial checksum 134 | _writer.WriteByte(0); 135 | _writer.WriteByte(0); 136 | } 137 | 138 | private void writeVectors() 139 | { 140 | MapMode map = Driver.Header.MapMode; 141 | 142 | _writer.Seek(CpuAddressConverter.RAMToPhysical(SfcRomHeader.NativeVectors, map), SeekOrigin.Begin); 143 | 144 | using (BinaryWriter binWriter = new BinaryWriter(_writer)) 145 | { 146 | Vectors vectors = Driver.Vectors; 147 | RomSpeed speed = Driver.Header.RomSpeed; 148 | 149 | // ========================== 150 | // = Native (65816 vectors) = 151 | // ========================== 152 | 153 | // Use BRK version for COP vector 154 | int brk = (int)GlobalScope.AddressFor(vectors.BRK); 155 | brk = CpuAddressConverter.PhysicalToRAM(brk, map, speed); 156 | 157 | binWriter.Write((ushort)brk); 158 | 159 | // BRK vector 160 | binWriter.Write((ushort)brk); 161 | 162 | // Use BRK for ABORT vector 163 | binWriter.Write((ushort)brk); 164 | 165 | // NMI vector 166 | int nmi = (int)GlobalScope.AddressFor(vectors.NMI); 167 | nmi = CpuAddressConverter.PhysicalToRAM(nmi, map, speed); 168 | 169 | binWriter.Write((ushort)nmi); 170 | 171 | // RESET vector 172 | int reset = (int)GlobalScope.AddressFor(vectors.Reset); 173 | reset = CpuAddressConverter.PhysicalToRAM(reset, map, speed); 174 | 175 | binWriter.Write((ushort)reset); 176 | 177 | // IRQ vector 178 | int irq = (int)GlobalScope.AddressFor(vectors.IRQ); 179 | irq = CpuAddressConverter.PhysicalToRAM(irq, map, speed); 180 | 181 | binWriter.Write((ushort)irq); 182 | 183 | // ============================ 184 | // = Emulation (6502 vectors) = 185 | // ============================ 186 | 187 | // COP vector 188 | binWriter.Seek(CpuAddressConverter.RAMToPhysical(SfcRomHeader.EmulationCopVector, map), SeekOrigin.Begin); 189 | 190 | binWriter.Write((ushort)brk); 191 | 192 | // Rest of 6502 vectors 193 | binWriter.Seek(CpuAddressConverter.RAMToPhysical(SfcRomHeader.EmulationVectors, map), SeekOrigin.Begin); 194 | 195 | // ABORT 196 | binWriter.Write((ushort)brk); 197 | 198 | // NMI 199 | binWriter.Write((ushort)nmi); 200 | 201 | // RESET 202 | binWriter.Write((ushort)reset); 203 | 204 | // IRQ 205 | binWriter.Write((ushort)irq); 206 | } 207 | } 208 | } 209 | } -------------------------------------------------------------------------------- /LibZealCompiler/Data/ArgumentSize.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Zeal.Compiler.Data 8 | { 9 | public enum ArgumentSize 10 | { 11 | Byte, 12 | Word, 13 | LongWord 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LibZealCompiler/Data/CpuAddressingMode.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace Zeal.Compiler.Data 4 | { 5 | public enum CpuAddressingMode 6 | { 7 | [Description("implied")] 8 | Implied, 9 | [Description("immediate")] 10 | Immediate, 11 | [Description("relative")] 12 | Relative, 13 | [Description("relative long")] 14 | RelativeLong, 15 | [Description("direct")] 16 | Direct, 17 | [Description("direct indexed X")] 18 | DirectIndexedX, 19 | [Description("direct indexed Y")] 20 | DirectIndexedY, 21 | [Description("direct indirect")] 22 | DirectIndirect, 23 | [Description("direct indexed indirect")] 24 | DirectIndexedIndirect, 25 | [Description("direct indirect long")] 26 | DirectIndirectLong, 27 | [Description("direct indirect indexed long")] 28 | DirectIndirectIndexedLong, 29 | [Description("absolute")] 30 | Absolute, 31 | [Description("absolute indexed X")] 32 | AbsoluteIndexedX, 33 | [Description("absolute indexed Y")] 34 | AbsoluteIndexedY, 35 | [Description("absolute long")] 36 | AbsoluteLong, 37 | [Description("absolute indexed long")] 38 | AbsoluteIndexedLong, 39 | [Description("stack relative")] 40 | StackRelative, 41 | [Description("stack relative indirect indexed")] 42 | StackRelativeIndirectIndexed, 43 | [Description("absolute indirect")] 44 | AbsoluteIndirect, 45 | [Description("absolute indirect")] 46 | AbsoluteIndirectLong, 47 | [Description("absolute indexed indirect")] 48 | AbsoluteIndexedIndirect, 49 | [Description("block move")] 50 | BlockMove 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /LibZealCompiler/Data/CpuAssumeAddressingAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Zeal.Compiler.Data 8 | { 9 | public class CpuAssumeAddressingAttribute : Attribute 10 | { 11 | public CpuAddressingMode Addressing { get; private set; } 12 | 13 | public CpuAssumeAddressingAttribute(CpuAddressingMode addressing) 14 | { 15 | Addressing = addressing; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /LibZealCompiler/Data/CpuInstructionStatement.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Zeal.Compiler.Data 4 | { 5 | public class CpuInstructionStatement : Statement 6 | { 7 | private List _arguments = new List(); 8 | 9 | public CpuInstructions Opcode { get; set; } 10 | public CpuAddressingMode AddressingMode { get; set; } 11 | 12 | public List Arguments 13 | { 14 | get 15 | { 16 | return _arguments; 17 | } 18 | } 19 | 20 | public CpuInstructionStatement() 21 | : base() 22 | { 23 | } 24 | 25 | public override long ComputeSize() 26 | { 27 | long totalSize = 1; 28 | 29 | foreach (var argument in Arguments) 30 | { 31 | long argSize = argument.ComputeSize(); 32 | if (argSize == -1) 33 | { 34 | if (AddressingMode == CpuAddressingMode.Relative) 35 | { 36 | totalSize += 1; 37 | } 38 | else if (AddressingMode == CpuAddressingMode.Absolute) 39 | { 40 | totalSize += 2; 41 | } 42 | else if (AddressingMode == CpuAddressingMode.AbsoluteLong) 43 | { 44 | totalSize += 3; 45 | } 46 | } 47 | else 48 | { 49 | totalSize += argSize; 50 | } 51 | } 52 | 53 | return totalSize; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /LibZealCompiler/Data/CpuInstructions.cs: -------------------------------------------------------------------------------- 1 | using Zeal.Compiler.CodeGeneration; 2 | 3 | namespace Zeal.Compiler.Data 4 | { 5 | public enum CpuInstructions 6 | { 7 | [Opcode(CpuAddressingMode.Immediate, 0x69)] 8 | [Opcode(CpuAddressingMode.Direct, 0x65)] 9 | [Opcode(CpuAddressingMode.Absolute, 0x6D)] 10 | adc, 11 | 12 | [Opcode(CpuAddressingMode.Immediate, 0x29)] 13 | [Opcode(CpuAddressingMode.Direct, 0x25)] 14 | [Opcode(CpuAddressingMode.Absolute, 0x2D)] 15 | and, 16 | 17 | [Opcode(0x0A)] 18 | [Opcode(CpuAddressingMode.Direct, 0x06)] 19 | [Opcode(CpuAddressingMode.Absolute, 0x0E)] 20 | asl, 21 | 22 | [CpuAssumeAddressing(CpuAddressingMode.Relative)] 23 | [Opcode(CpuAddressingMode.Relative, 0x90)] 24 | bcc, 25 | [CpuAssumeAddressing(CpuAddressingMode.Relative)] 26 | [Opcode(CpuAddressingMode.Relative, 0xB0)] 27 | bcs, 28 | [CpuAssumeAddressing(CpuAddressingMode.Relative)] 29 | [Opcode(CpuAddressingMode.Relative, 0xF0)] 30 | beq, 31 | 32 | [Opcode(CpuAddressingMode.Immediate, 0x89)] 33 | [Opcode(CpuAddressingMode.Direct, 0x24)] 34 | [Opcode(CpuAddressingMode.Absolute, 0x2C)] 35 | bit, 36 | 37 | [CpuAssumeAddressing(CpuAddressingMode.Relative)] 38 | [Opcode(CpuAddressingMode.Relative, 0x30)] 39 | bmi, 40 | 41 | [CpuAssumeAddressing(CpuAddressingMode.Relative)] 42 | [Opcode(CpuAddressingMode.Relative, 0xD0)] 43 | bne, 44 | 45 | [CpuAssumeAddressing(CpuAddressingMode.Relative)] 46 | [Opcode(CpuAddressingMode.Relative, 0x10)] 47 | bpl, 48 | 49 | [CpuAssumeAddressing(CpuAddressingMode.Relative)] 50 | [Opcode(CpuAddressingMode.Relative, 0x80)] 51 | bra, 52 | 53 | [Opcode(0x00)] 54 | brk, 55 | 56 | brl, 57 | 58 | [CpuAssumeAddressing(CpuAddressingMode.Relative)] 59 | [Opcode(CpuAddressingMode.Relative, 0x50)] 60 | bvc, 61 | 62 | [CpuAssumeAddressing(CpuAddressingMode.Relative)] 63 | [Opcode(CpuAddressingMode.Relative, 0x70)] 64 | bvs, 65 | 66 | [Opcode(0x18)] 67 | clc, 68 | 69 | [Opcode(0xD8)] 70 | cld, 71 | 72 | [Opcode(0x58)] 73 | cli, 74 | 75 | [Opcode(0xB8)] 76 | clv, 77 | 78 | [Opcode(CpuAddressingMode.Immediate, 0xC9)] 79 | [Opcode(CpuAddressingMode.Direct, 0xC5)] 80 | [Opcode(CpuAddressingMode.Absolute, 0xCD)] 81 | cmp, 82 | 83 | cop, 84 | 85 | [Opcode(CpuAddressingMode.Immediate, 0xE0)] 86 | [Opcode(CpuAddressingMode.Direct, 0xE4)] 87 | [Opcode(CpuAddressingMode.Absolute, 0xEC)] 88 | cpx, 89 | 90 | [Opcode(CpuAddressingMode.Immediate, 0xC0)] 91 | [Opcode(CpuAddressingMode.Direct, 0xC4)] 92 | [Opcode(CpuAddressingMode.Absolute, 0xCC)] 93 | cpy, 94 | 95 | [Opcode(0x3A)] 96 | [Opcode(CpuAddressingMode.Direct, 0xC6)] 97 | [Opcode(CpuAddressingMode.Absolute, 0xCE)] 98 | dec, 99 | 100 | [Opcode(0xCA)] 101 | dex, 102 | 103 | [Opcode(0x88)] 104 | dey, 105 | 106 | [Opcode(CpuAddressingMode.Immediate, 0x49)] 107 | [Opcode(CpuAddressingMode.Direct, 0x45)] 108 | [Opcode(CpuAddressingMode.Absolute, 0x4D)] 109 | eor, 110 | 111 | [Opcode(0x1A)] 112 | [Opcode(CpuAddressingMode.Direct, 0xE6)] 113 | [Opcode(CpuAddressingMode.Absolute, 0xEE)] 114 | inc, 115 | 116 | [Opcode(0xE8)] 117 | inx, 118 | 119 | [Opcode(0xC8)] 120 | iny, 121 | 122 | [Opcode(CpuAddressingMode.Absolute, 0x4C)] 123 | jmp, 124 | 125 | jml, 126 | 127 | [Opcode(CpuAddressingMode.Absolute, 0x20)] 128 | jsr, 129 | 130 | jsl, 131 | 132 | [Opcode(CpuAddressingMode.Immediate, 0xA9)] 133 | [Opcode(CpuAddressingMode.Direct, 0xA5)] 134 | [Opcode(CpuAddressingMode.Absolute, 0xAD)] 135 | lda, 136 | 137 | [Opcode(CpuAddressingMode.Immediate, 0xA2)] 138 | [Opcode(CpuAddressingMode.Direct, 0xA6)] 139 | [Opcode(CpuAddressingMode.Absolute, 0xAE)] 140 | [Opcode(CpuAddressingMode.AbsoluteLong, 0xAF)] 141 | ldx, 142 | 143 | [Opcode(CpuAddressingMode.Immediate, 0xA0)] 144 | [Opcode(CpuAddressingMode.Direct, 0xA4)] 145 | [Opcode(CpuAddressingMode.Absolute, 0xAC)] 146 | ldy, 147 | 148 | [Opcode(0x4A)] 149 | [Opcode(CpuAddressingMode.Direct, 0x46)] 150 | [Opcode(CpuAddressingMode.Absolute, 0x4E)] 151 | lsr, 152 | 153 | mvn, 154 | mvp, 155 | 156 | [Opcode(0xEA)] 157 | nop, 158 | 159 | [Opcode(CpuAddressingMode.Immediate, 0x09)] 160 | [Opcode(CpuAddressingMode.Direct, 0x05)] 161 | [Opcode(CpuAddressingMode.Absolute, 0x0D)] 162 | ora, 163 | 164 | pea, 165 | per, 166 | 167 | [Opcode(0x48)] 168 | pha, 169 | 170 | [Opcode(0x8B)] 171 | phb, 172 | 173 | [Opcode(0x0B)] 174 | phd, 175 | 176 | [Opcode(0x4B)] 177 | phk, 178 | 179 | [Opcode(0x08)] 180 | php, 181 | 182 | [Opcode(0xDA)] 183 | phx, 184 | 185 | [Opcode(0x5A)] 186 | phy, 187 | 188 | [Opcode(0x68)] 189 | pla, 190 | 191 | [Opcode(0xAB)] 192 | plb, 193 | 194 | [Opcode(0x2B)] 195 | pld, 196 | 197 | [Opcode(0x28)] 198 | plp, 199 | 200 | [Opcode(0xFA)] 201 | plx, 202 | 203 | [Opcode(0x7A)] 204 | ply, 205 | 206 | [Opcode(CpuAddressingMode.Immediate, 0xC2)] 207 | rep, 208 | 209 | [Opcode(0x2A)] 210 | [Opcode(CpuAddressingMode.Direct, 0x26)] 211 | [Opcode(CpuAddressingMode.Absolute, 0x2E)] 212 | rol, 213 | 214 | [Opcode(0x6A)] 215 | [Opcode(CpuAddressingMode.Direct, 0x66)] 216 | [Opcode(CpuAddressingMode.Absolute, 0x6E)] 217 | ror, 218 | 219 | [Opcode(0x40)] 220 | rti, 221 | 222 | [Opcode(0x6B)] 223 | rtl, 224 | 225 | [Opcode(0x60)] 226 | rts, 227 | 228 | [Opcode(CpuAddressingMode.Immediate, 0xE9)] 229 | [Opcode(CpuAddressingMode.Direct, 0xE5)] 230 | [Opcode(CpuAddressingMode.Absolute, 0xED)] 231 | sbc, 232 | 233 | [Opcode(0x38)] 234 | sec, 235 | 236 | [Opcode(0xF8)] 237 | sed, 238 | 239 | [Opcode(0x78)] 240 | sei, 241 | 242 | [Opcode(CpuAddressingMode.Immediate, 0xE2)] 243 | sep, 244 | 245 | [Opcode(CpuAddressingMode.Direct, 0x85)] 246 | [Opcode(CpuAddressingMode.Absolute, 0x8D)] 247 | [Opcode(CpuAddressingMode.AbsoluteLong, 0x8F)] 248 | sta, 249 | 250 | [Opcode(0xDB)] 251 | stp, 252 | 253 | [Opcode(CpuAddressingMode.Direct, 0x86)] 254 | [Opcode(CpuAddressingMode.Absolute, 0x8E)] 255 | stx, 256 | 257 | [Opcode(CpuAddressingMode.Direct, 0x84)] 258 | [Opcode(CpuAddressingMode.Absolute, 0x8C)] 259 | sty, 260 | 261 | [Opcode(CpuAddressingMode.Direct, 0x64)] 262 | [Opcode(CpuAddressingMode.Absolute, 0x9C)] 263 | stz, 264 | 265 | [Opcode(0xAA)] 266 | tax, 267 | 268 | [Opcode(0xA8)] 269 | tay, 270 | 271 | [Opcode(0x5B)] 272 | tcd, 273 | 274 | [Opcode(0x1B)] 275 | tcs, 276 | 277 | [Opcode(0x7B)] 278 | tdc, 279 | 280 | [Opcode(CpuAddressingMode.Direct, 0x14)] 281 | [Opcode(CpuAddressingMode.Absolute, 0x1C)] 282 | trb, 283 | 284 | [Opcode(CpuAddressingMode.Direct, 0x04)] 285 | [Opcode(CpuAddressingMode.Absolute, 0x0C)] 286 | tsb, 287 | 288 | [Opcode(0x3B)] 289 | tsc, 290 | 291 | [Opcode(0xBA)] 292 | tsx, 293 | 294 | [Opcode(0x8A)] 295 | txa, 296 | 297 | [Opcode(0x9A)] 298 | txs, 299 | 300 | [Opcode(0x9B)] 301 | txy, 302 | 303 | [Opcode(0x98)] 304 | tya, 305 | 306 | [Opcode(0xBB)] 307 | tyx, 308 | 309 | [Opcode(0xCB)] 310 | wai, 311 | 312 | [Opcode(0xEB)] 313 | xba, 314 | 315 | [Opcode(0xFB)] 316 | xce 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /LibZealCompiler/Data/InstructionArgument.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Zeal.Compiler.Data 4 | { 5 | public class InstructionArgument 6 | { 7 | public virtual long ComputeSize() 8 | { 9 | return -1; 10 | } 11 | } 12 | 13 | public class NumberInstructionArgument : InstructionArgument 14 | { 15 | public int Number { get; set; } 16 | public ArgumentSize Size { get; set; } 17 | 18 | public NumberInstructionArgument(int value, ArgumentSize size) 19 | { 20 | Number = value; 21 | Size = size; 22 | } 23 | 24 | public override long ComputeSize() 25 | { 26 | switch (Size) 27 | { 28 | case ArgumentSize.Byte: 29 | return 1; 30 | case ArgumentSize.Word: 31 | return 2; 32 | case ArgumentSize.LongWord: 33 | return 3; 34 | } 35 | 36 | return -1; 37 | } 38 | } 39 | 40 | public class LabelInstructionArgument : InstructionArgument 41 | { 42 | public string Label { get; set; } 43 | 44 | public LabelInstructionArgument(string label) 45 | { 46 | Label = label; 47 | } 48 | 49 | public override long ComputeSize() 50 | { 51 | return base.ComputeSize(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /LibZealCompiler/Data/OpcodeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Zeal.Compiler.Data; 3 | 4 | namespace Zeal.Compiler.Data 5 | { 6 | [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] 7 | public class OpcodeAttribute : Attribute 8 | { 9 | public byte Opcode { get; private set; } 10 | public CpuAddressingMode AddressingMode { get; private set; } 11 | 12 | public OpcodeAttribute(byte opcode) 13 | { 14 | AddressingMode = CpuAddressingMode.Implied; 15 | Opcode = opcode; 16 | } 17 | 18 | public OpcodeAttribute(CpuAddressingMode addressing, byte opcode) 19 | { 20 | AddressingMode = addressing; 21 | Opcode = opcode; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LibZealCompiler/Data/RomHeader.cs: -------------------------------------------------------------------------------- 1 | namespace Zeal.Compiler.Data 2 | { 3 | public enum RomSpeed 4 | { 5 | SlowROM, 6 | FastROM 7 | } 8 | 9 | public enum MapMode 10 | { 11 | LoROM, 12 | HiROM 13 | } 14 | 15 | public enum Country 16 | { 17 | Japan, 18 | NorthAmerica, 19 | Europe, 20 | Sweden, 21 | Finland, 22 | Denmark, 23 | France, 24 | Holland, 25 | Spain, 26 | Germany, 27 | Italy, 28 | China, 29 | Indonesia, 30 | SouthKorea, 31 | Common, 32 | Canada, 33 | Brazil, 34 | Australia 35 | } 36 | 37 | public class RomHeader 38 | { 39 | public string CartridgeName { get; set; } 40 | public RomSpeed RomSpeed { get; set; } 41 | public MapMode MapMode { get; set; } 42 | public uint SramSize { get; set; } 43 | public Country Country { get; set; } 44 | public uint Developer { get; set; } 45 | public uint Version { get; set; } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /LibZealCompiler/Data/Scope.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Zeal.Compiler.Data 5 | { 6 | public enum ScopeType 7 | { 8 | Scope, 9 | Procedure, 10 | Interrupt 11 | } 12 | 13 | public class Scope 14 | { 15 | private List _statements = new List(); 16 | private Dictionary _labels = new Dictionary(); 17 | private List _children = new List(); 18 | 19 | public string Name { get; set; } 20 | public ScopeType Type { get; set; } 21 | 22 | public List Statements 23 | { 24 | get 25 | { 26 | return _statements; 27 | } 28 | } 29 | 30 | internal Scope GetScope(string text) 31 | { 32 | foreach(var child in _children) 33 | { 34 | if (child.Name == text) 35 | { 36 | return child; 37 | } 38 | } 39 | 40 | return null; 41 | } 42 | 43 | public Dictionary Labels 44 | { 45 | get 46 | { 47 | return _labels; 48 | } 49 | } 50 | 51 | public Scope Parent 52 | { 53 | get; 54 | internal set; 55 | } 56 | 57 | public List Children 58 | { 59 | get 60 | { 61 | return _children; 62 | } 63 | } 64 | 65 | public void Add(Scope scope) 66 | { 67 | scope.Parent = this; 68 | 69 | _children.Add(scope); 70 | } 71 | 72 | public long AddressFor(string label) 73 | { 74 | var currentScope = this; 75 | 76 | while (currentScope != null) 77 | { 78 | long result = -1; 79 | if (currentScope.Labels.TryGetValue(label, out result)) 80 | { 81 | return result; 82 | } 83 | 84 | currentScope = currentScope.Parent; 85 | } 86 | 87 | return -1; 88 | } 89 | 90 | public bool IsLabelValid(string label) 91 | { 92 | var currentScope = this; 93 | while (currentScope != null) 94 | { 95 | if (currentScope.Labels.ContainsKey(label)) 96 | { 97 | return true; 98 | } 99 | 100 | currentScope = currentScope.Parent; 101 | } 102 | 103 | return false; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /LibZealCompiler/Data/Statement.cs: -------------------------------------------------------------------------------- 1 | namespace Zeal.Compiler.Data 2 | { 3 | public class Statement 4 | { 5 | public string AssociatedLabel { get; set; } 6 | 7 | public int Line { get; set; } 8 | public int Column { get; set; } 9 | 10 | public Statement() 11 | { 12 | AssociatedLabel = null; 13 | } 14 | 15 | public virtual long ComputeSize() 16 | { 17 | return 0; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /LibZealCompiler/Data/Vectors.cs: -------------------------------------------------------------------------------- 1 | namespace Zeal.Compiler.Data 2 | { 3 | public class Vectors 4 | { 5 | public string BRK { get; set; } 6 | public string IRQ { get; set; } 7 | public string NMI { get; set; } 8 | public string Reset { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /LibZealCompiler/Helper/CpuAddressConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Zeal.Compiler.Data; 3 | 4 | namespace Zeal.Compiler.Helper 5 | { 6 | public static class CpuAddressConverter 7 | { 8 | public static int RAMToPhysical(int address, MapMode mode) 9 | { 10 | switch(mode) 11 | { 12 | case MapMode.LoROM: 13 | return ((address & 0x7F0000) >> 1) | (address & 0x7FFF); 14 | case MapMode.HiROM: 15 | return address & 0x3FFFFF; 16 | } 17 | 18 | return address; 19 | } 20 | 21 | public static int PhysicalToRAM(int address, MapMode mode, RomSpeed romSpeed) 22 | { 23 | int bank = 0; 24 | switch(mode) 25 | { 26 | case MapMode.LoROM: 27 | { 28 | switch (romSpeed) 29 | { 30 | case RomSpeed.SlowROM: 31 | bank = 0x00; 32 | break; 33 | case RomSpeed.FastROM: 34 | bank = 0x80; 35 | break; 36 | } 37 | 38 | int result = (bank << 16) 39 | | ((address & 0xFF8000) << 1) 40 | | (address & 0xFFFF) 41 | | 0x8000 42 | ; 43 | 44 | return result; 45 | } 46 | case MapMode.HiROM: 47 | { 48 | switch (romSpeed) 49 | { 50 | case RomSpeed.SlowROM: 51 | case RomSpeed.FastROM: 52 | bank = 0xC0; 53 | break; 54 | } 55 | 56 | return (bank << 16) | (address & 0xFFFFFF); 57 | } 58 | } 59 | 60 | return address; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /LibZealCompiler/Helper/EnumHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Zeal.Compiler.Helper 5 | { 6 | static class EnumHelper 7 | { 8 | public static T GetAttribute(this Enum value) where T : Attribute 9 | { 10 | return GetAttributes(value).FirstOrDefault(); 11 | } 12 | 13 | public static T[] GetAttributes(this Enum value) where T : Attribute 14 | { 15 | var type = value.GetType(); 16 | var memberInfo = type.GetMember(value.ToString())[0]; 17 | var attributes = memberInfo.GetCustomAttributes(typeof(T), false) as T[]; 18 | return attributes; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /LibZealCompiler/LibZealCompiler.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {58DC5B8A-BD9E-4F08-A898-14331DF4FA9B} 9 | Library 10 | Properties 11 | Zeal.Compiler 12 | LibZealCompiler 13 | v4.5.2 14 | 512 15 | 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | ..\packages\Antlr4.Runtime.4.5-alpha003\lib\net45\Antlr4.Runtime.dll 38 | True 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | ZealCpu.g4 69 | 70 | 71 | ZealCpu.g4 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | Designer 82 | 83 | 84 | MSBuild:Compile 85 | Zeal.Compiler.Parser 86 | 87 | 88 | 89 | 90 | 91 | 92 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 93 | 94 | 95 | 96 | 97 | 98 | 105 | -------------------------------------------------------------------------------- /LibZealCompiler/Parser/CompilerErrorException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Zeal.Compiler.Parser 4 | { 5 | public class CompilerErrorException : Exception 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /LibZealCompiler/Parser/ErrorMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Zeal.Compiler.Parser 2 | { 3 | public class ErrorMessage 4 | { 5 | public string SourceFile { get; set; } 6 | public string Message { get; set; } 7 | public int Line { get; set; } 8 | public int Column { get; set; } 9 | public int StartToken { get; set; } 10 | public int EndToken { get; set; } 11 | public string Context { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /LibZealCompiler/Parser/ZealCpu.g4: -------------------------------------------------------------------------------- 1 | grammar ZealCpu; 2 | 3 | // ========= 4 | // = Lexer = 5 | // ========= 6 | HEADER: 'header'; 7 | VECTORS: 'vectors'; 8 | PROCEDURE: 'procedure'; 9 | INTERRUPT: 'interrupt'; 10 | 11 | ADC: 'adc'; 12 | AND: 'and'; 13 | ASL: 'asl'; 14 | BCC: 'bcc'; 15 | BCS: 'bcs'; 16 | BEQ: 'beq'; 17 | BIT: 'bit'; 18 | BMI: 'bmi'; 19 | BNE: 'bne'; 20 | BPL: 'bpl'; 21 | BRA: 'bra'; 22 | BRK: 'brk'; 23 | BRL: 'brl'; 24 | BVC: 'bvc'; 25 | BVS: 'bvs'; 26 | CLC: 'clc'; 27 | CLD: 'cld'; 28 | CLI: 'cli'; 29 | CLV: 'clv'; 30 | CMP: 'cmp'; 31 | COP: 'cop'; 32 | CPX: 'cpx'; 33 | CPY: 'cpy'; 34 | DEC: 'dec'; 35 | DEX: 'dex'; 36 | DEY: 'dey'; 37 | EOR: 'eor'; 38 | INC: 'inc'; 39 | INX: 'inx'; 40 | INY: 'iny'; 41 | JMP: 'jmp'; 42 | JML: 'jml'; 43 | JSR: 'jsr'; 44 | JSL: 'jsl'; 45 | LDA: 'lda'; 46 | LDX: 'ldx'; 47 | LDY: 'ldy'; 48 | LSR: 'lsr'; 49 | MVN: 'mvn'; 50 | MVP: 'mvp'; 51 | NOP: 'nop'; 52 | ORA: 'ora'; 53 | PEA: 'pea'; 54 | PER: 'per'; 55 | PHA: 'pha'; 56 | PHB: 'phb'; 57 | PHD: 'phd'; 58 | PHK: 'phk'; 59 | PHP: 'php'; 60 | PHX: 'phx'; 61 | PHY: 'phy'; 62 | PLA: 'pla'; 63 | PLB: 'plb'; 64 | PLD: 'pld'; 65 | PLP: 'plp'; 66 | PLX: 'plx'; 67 | PLY: 'ply'; 68 | REP: 'rep'; 69 | ROL: 'rol'; 70 | ROR: 'ror'; 71 | RTI: 'rti'; 72 | RTL: 'rtl'; 73 | RTS: 'rts'; 74 | SBC: 'sbc'; 75 | SEC: 'sec'; 76 | SED: 'sed'; 77 | SEI: 'sei'; 78 | SEP: 'sep'; 79 | STA: 'sta'; 80 | STP: 'stp'; 81 | STX: 'stx'; 82 | STY: 'sty'; 83 | STZ: 'stz'; 84 | TAX: 'tax'; 85 | TAY: 'tay'; 86 | TCD: 'tcd'; 87 | TCS: 'tcs'; 88 | TDC: 'tdc'; 89 | TRB: 'trb'; 90 | TSB: 'tsb'; 91 | TSC: 'tsc'; 92 | TSX: 'tsx'; 93 | TXA: 'txa'; 94 | TXS: 'txs'; 95 | TXY: 'txy'; 96 | TYA: 'tya'; 97 | TYX: 'tyx'; 98 | WAI: 'wai'; 99 | XBA: 'xba'; 100 | XCE: 'xce'; 101 | 102 | IDENTIFIER: [a-zA-Z_][a-zA-Z0-9_]* ; 103 | 104 | STRING_LITERAL: '"' .*? '"' ; 105 | HEX_LITERAL: '$' [0-9a-fA-F]+ ; 106 | INTEGER_LITERAL: '-'? [0-9]+ ; 107 | BINARY_LITERAL: '%' [0-1]+; 108 | 109 | LINE_COMMENT: ';' ~[\r\n]* -> skip; 110 | WHITESPACE : (' '|'\t'|'\n'|'\r')+ -> channel(HIDDEN) ; 111 | 112 | // ========== 113 | // = Parser = 114 | // ========== 115 | root 116 | : ( 117 | headerDeclaration 118 | | vectorsDeclaration 119 | | procedureDeclaration 120 | | interruptDeclaration 121 | )* 122 | ; 123 | 124 | headerDeclaration 125 | : HEADER '{' 126 | headerInfo* 127 | '}' 128 | ; 129 | 130 | headerInfo 131 | : headerType=IDENTIFIER '=' headerValue=literal 132 | ; 133 | 134 | vectorsDeclaration 135 | : VECTORS '{' 136 | vectorInfo* 137 | '}' 138 | ; 139 | 140 | vectorInfo 141 | : vectorType=IDENTIFIER '=' labelName=IDENTIFIER 142 | ; 143 | 144 | procedureDeclaration 145 | : PROCEDURE name=IDENTIFIER '{' 146 | statement* 147 | '}' 148 | ; 149 | 150 | interruptDeclaration 151 | : INTERRUPT name=IDENTIFIER '{' 152 | statement* 153 | '}' 154 | ; 155 | 156 | statement 157 | : instructionStatement 158 | ; 159 | 160 | instructionStatement 161 | : label? opcode argument? 162 | ; 163 | 164 | opcode 165 | : ADC 166 | | AND 167 | | ASL 168 | | BCC 169 | | BCS 170 | | BEQ 171 | | BIT 172 | | BMI 173 | | BNE 174 | | BPL 175 | | BRA 176 | | BRK 177 | | BRL 178 | | BVC 179 | | BVS 180 | | CLC 181 | | CLD 182 | | CLI 183 | | CLV 184 | | CMP 185 | | COP 186 | | CPX 187 | | CPY 188 | | DEC 189 | | DEX 190 | | DEY 191 | | EOR 192 | | INC 193 | | INX 194 | | INY 195 | | JMP 196 | | JML 197 | | JSR 198 | | JSL 199 | | LDA 200 | | LDX 201 | | LDY 202 | | LSR 203 | | MVN 204 | | MVP 205 | | NOP 206 | | ORA 207 | | PEA 208 | | PER 209 | | PHA 210 | | PHB 211 | | PHD 212 | | PHK 213 | | PHP 214 | | PHX 215 | | PHY 216 | | PLA 217 | | PLB 218 | | PLD 219 | | PLP 220 | | PLX 221 | | PLY 222 | | REP 223 | | ROL 224 | | ROR 225 | | RTI 226 | | RTL 227 | | RTS 228 | | SBC 229 | | SEC 230 | | SED 231 | | SEI 232 | | SEP 233 | | STA 234 | | STP 235 | | STX 236 | | STY 237 | | STZ 238 | | TAX 239 | | TAY 240 | | TCD 241 | | TCS 242 | | TDC 243 | | TRB 244 | | TSB 245 | | TSC 246 | | TSX 247 | | TXA 248 | | TXS 249 | | TXY 250 | | TYA 251 | | TYX 252 | | WAI 253 | | XBA 254 | | XCE 255 | ; 256 | 257 | argument 258 | : argumentLiteral # Address 259 | | '#' numberLiteral # Immediate 260 | ; 261 | 262 | literal 263 | : STRING_LITERAL 264 | | IDENTIFIER 265 | | numberLiteral 266 | ; 267 | 268 | argumentLiteral 269 | : IDENTIFIER 270 | | numberLiteral 271 | ; 272 | 273 | numberLiteral 274 | : HEX_LITERAL 275 | | INTEGER_LITERAL 276 | | BINARY_LITERAL 277 | ; 278 | 279 | label: 280 | IDENTIFIER ':' 281 | ; 282 | -------------------------------------------------------------------------------- /LibZealCompiler/Parser/ZealCpu.g4.lexer.cs: -------------------------------------------------------------------------------- 1 | namespace Zeal.Compiler.Parser 2 | { 3 | partial class ZealCpuLexer 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /LibZealCompiler/Parser/ZealCpu.g4.parser.cs: -------------------------------------------------------------------------------- 1 | namespace Zeal.Compiler.Parser 2 | { 3 | partial class ZealCpuParser 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /LibZealCompiler/Parser/ZealCpuDriver.cs: -------------------------------------------------------------------------------- 1 | using Antlr4.Runtime; 2 | using Antlr4.Runtime.Tree; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using Zeal.Compiler.Data; 7 | using Zeal.Compiler.Pass; 8 | 9 | namespace Zeal.Compiler.Parser 10 | { 11 | class CpuErrorListener : BaseErrorListener 12 | { 13 | private ZealCpuDriver _driver; 14 | 15 | public CpuErrorListener(ZealCpuDriver driver) 16 | { 17 | _driver = driver; 18 | } 19 | 20 | public override void SyntaxError(IRecognizer recognizer, IToken offendingSymbol, int line, int charPositionInLine, string msg, RecognitionException e) 21 | { 22 | ErrorMessage errorMessage = new ErrorMessage(); 23 | 24 | CommonTokenStream tokens = (CommonTokenStream)recognizer.InputStream; 25 | 26 | errorMessage.SourceFile = _driver.SourceFilePath; 27 | errorMessage.Context = tokens.TokenSource.InputStream.ToString().Split('\n')[line - 1].Replace('\t', ' ').TrimEnd(); 28 | errorMessage.Line = line; 29 | errorMessage.Column = charPositionInLine; 30 | errorMessage.Message = msg; 31 | errorMessage.StartToken = offendingSymbol.StartIndex; 32 | errorMessage.EndToken = offendingSymbol.StopIndex; 33 | 34 | _driver.Errors.Add(errorMessage); 35 | } 36 | } 37 | 38 | public class ZealCpuDriver 39 | { 40 | private RomHeader _header = new RomHeader(); 41 | private List _scopes = new List(); 42 | private Scope _globalScope = new Scope(); 43 | private List _errors = new List(); 44 | 45 | private ZealCpuLexer _lexer; 46 | private CommonTokenStream _tokenStream; 47 | private ZealCpuParser _parser; 48 | private ZealCpuParser.RootContext _rootTree; 49 | 50 | public RomHeader Header 51 | { 52 | get 53 | { 54 | return _header; 55 | } 56 | } 57 | 58 | public Vectors Vectors 59 | { 60 | get; 61 | set; 62 | } 63 | 64 | public Scope GlobalScope 65 | { 66 | get 67 | { 68 | return _globalScope; 69 | } 70 | } 71 | 72 | public List Errors 73 | { 74 | get 75 | { 76 | return _errors; 77 | } 78 | } 79 | 80 | internal string SourceFilePath { get; set; } 81 | 82 | public ZealCpuDriver(string inputFile) 83 | : this(new AntlrFileStream(inputFile)) 84 | { 85 | SourceFilePath = Path.GetFullPath(inputFile); 86 | } 87 | 88 | public ZealCpuDriver(Stream stream) 89 | : this(new AntlrInputStream(stream)) 90 | { 91 | } 92 | 93 | private ZealCpuDriver(ICharStream antlrInputStream) 94 | { 95 | _lexer = new ZealCpuLexer(antlrInputStream); 96 | _tokenStream = new CommonTokenStream(_lexer); 97 | _parser = new ZealCpuParser(_tokenStream); 98 | _parser.RemoveErrorListeners(); 99 | _parser.AddErrorListener(new CpuErrorListener(this)); 100 | } 101 | 102 | public void Parse() 103 | { 104 | _rootTree = _parser.root(); 105 | 106 | ParseTreeWalker walker = new ParseTreeWalker(); 107 | walker.Walk(new ParseCpuPass(this), _rootTree); 108 | 109 | if (Errors.Count > 0) 110 | { 111 | throw new CompilerErrorException(); 112 | } 113 | 114 | resolveLabels(); 115 | verifyBranchLength(); 116 | 117 | if (Errors.Count > 0) 118 | { 119 | throw new CompilerErrorException(); 120 | } 121 | } 122 | 123 | public void SecondPass() 124 | { 125 | if (Vectors == null) 126 | { 127 | ErrorMessage error = new ErrorMessage(); 128 | error.SourceFile = SourceFilePath; 129 | error.Message = "Required vectors statement not found."; 130 | Errors.Add(error); 131 | } 132 | 133 | ParseTreeWalker walker = new ParseTreeWalker(); 134 | walker.Walk(new SecondCpuPass(this), _rootTree); 135 | 136 | if (Errors.Count > 0) 137 | { 138 | throw new CompilerErrorException(); 139 | } 140 | } 141 | 142 | private void resolveLabels() 143 | { 144 | long physicalAddress = 0; 145 | 146 | Scope parentScope = _globalScope; 147 | foreach (var scope in parentScope.Children) 148 | { 149 | parentScope.Labels[scope.Name] = physicalAddress; 150 | 151 | foreach (var instruction in scope.Statements) 152 | { 153 | if (!String.IsNullOrEmpty(instruction.AssociatedLabel)) 154 | { 155 | scope.Labels[instruction.AssociatedLabel] = physicalAddress; 156 | } 157 | 158 | physicalAddress += instruction.ComputeSize(); 159 | } 160 | } 161 | } 162 | 163 | private void verifyBranchLength() 164 | { 165 | long physicalAddress = 0; 166 | 167 | Scope parentScope = _globalScope; 168 | foreach (var scope in parentScope.Children) 169 | { 170 | foreach (var instruction in scope.Statements) 171 | { 172 | if (instruction is CpuInstructionStatement) 173 | { 174 | var cpuInstruction = instruction as CpuInstructionStatement; 175 | if (cpuInstruction.AddressingMode == CpuAddressingMode.Relative) 176 | { 177 | int instructionSize = (int)instruction.ComputeSize(); 178 | 179 | if (cpuInstruction.Arguments[0] is LabelInstructionArgument) 180 | { 181 | var labelArgument = cpuInstruction.Arguments[0] as LabelInstructionArgument; 182 | int labelAddress = (int)scope.AddressFor(labelArgument.Label); 183 | 184 | int branchLength = labelAddress - ((int)physicalAddress + instructionSize); 185 | if (branchLength > sbyte.MaxValue || branchLength < sbyte.MinValue) 186 | { 187 | ErrorMessage error = new ErrorMessage(); 188 | error.SourceFile = SourceFilePath; 189 | error.Message = String.Format("Branch label '{0}' is too far away. Consider reducing the distance or use a jmp.", labelArgument.Label); 190 | error.Line = instruction.Line; 191 | error.Column = instruction.Column; 192 | 193 | Errors.Add(error); 194 | } 195 | } 196 | } 197 | } 198 | 199 | physicalAddress += instruction.ComputeSize(); 200 | } 201 | } 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /LibZealCompiler/Pass/CpuPass.cs: -------------------------------------------------------------------------------- 1 | using Antlr4.Runtime; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Zeal.Compiler.Parser; 8 | 9 | namespace Zeal.Compiler.Pass 10 | { 11 | class CpuPass : ZealCpuBaseListener 12 | { 13 | protected ZealCpuDriver _driver; 14 | 15 | public CpuPass(ZealCpuDriver driver) 16 | { 17 | _driver = driver; 18 | } 19 | 20 | protected void addErrorMesage(string message, IToken offendingToken) 21 | { 22 | ErrorMessage error = new ErrorMessage(); 23 | error.SourceFile = _driver.SourceFilePath; 24 | error.Message = message; 25 | 26 | if (offendingToken != null) 27 | { 28 | error.Context = offendingToken.InputStream.ToString().Split('\n')[offendingToken.Line - 1].Replace('\t', ' ').TrimEnd(); 29 | error.Line = offendingToken.Line; 30 | error.Column = offendingToken.Column; 31 | error.StartToken = offendingToken.StartIndex; 32 | error.EndToken = offendingToken.StopIndex; 33 | } 34 | 35 | _driver.Errors.Add(error); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LibZealCompiler/Pass/ParseCpuPass.cs: -------------------------------------------------------------------------------- 1 | using Antlr4.Runtime.Misc; 2 | using Antlr4.Runtime.Tree; 3 | using System; 4 | using System.Linq; 5 | using System.Globalization; 6 | using Zeal.Compiler.Data; 7 | using Zeal.Compiler.Parser; 8 | using Zeal.Compiler.Helper; 9 | using System.ComponentModel; 10 | 11 | namespace Zeal.Compiler.Pass 12 | { 13 | class ParseCpuPass : CpuPass 14 | { 15 | private Scope _currentScope; 16 | private CpuInstructionStatement _currentInstruction; 17 | 18 | public ParseCpuPass(ZealCpuDriver driver) 19 | : base(driver) 20 | { 21 | } 22 | 23 | public override void ExitHeaderDeclaration([NotNull] ZealCpuParser.HeaderDeclarationContext context) 24 | { 25 | foreach (var info in context.headerInfo()) 26 | { 27 | switch (info.headerType.Text) 28 | { 29 | case "CartridgeName": 30 | if (info.headerValue.STRING_LITERAL() == null) 31 | { 32 | addErrorMesage("CartridgeName excepts a string literal.", info.headerValue.Start); 33 | } 34 | else 35 | { 36 | _driver.Header.CartridgeName = parseStringLiteral(info.headerValue.STRING_LITERAL()); 37 | } 38 | break; 39 | case "RomSpeed": 40 | { 41 | RomSpeed speed = RomSpeed.SlowROM; 42 | if (Enum.TryParse(info.headerValue.IDENTIFIER().GetText(), out speed)) 43 | { 44 | _driver.Header.RomSpeed = speed; 45 | } 46 | else 47 | { 48 | string validInput = String.Join(", ", Enum.GetNames(typeof(RomSpeed))); 49 | addErrorMesage(String.Format("'{0}' is not a valid RomSpeed. Valid inputs are: {1}", info.headerValue.IDENTIFIER().GetText(), validInput), info.headerValue.IDENTIFIER().Symbol); 50 | } 51 | break; 52 | } 53 | case "MapMode": 54 | { 55 | MapMode mode = MapMode.LoROM; 56 | if (Enum.TryParse(info.headerValue.IDENTIFIER().GetText(), out mode)) 57 | { 58 | _driver.Header.MapMode = mode; 59 | } 60 | else 61 | { 62 | string validInput = String.Join(", ", Enum.GetNames(typeof(MapMode))); 63 | addErrorMesage(String.Format("'{0}' is not a valid MapMode. Valid inputs are: {1}", info.headerValue.IDENTIFIER().GetText(), validInput), info.headerValue.IDENTIFIER().Symbol); 64 | } 65 | break; 66 | } 67 | case "SramSize": 68 | if (info.headerValue.numberLiteral() == null) 69 | { 70 | addErrorMesage("SramSize expects a number literal.", info.headerValue.Start); 71 | } 72 | else 73 | { 74 | _driver.Header.SramSize = (uint)parseNumberLiteral(info.headerValue.numberLiteral()); 75 | } 76 | break; 77 | case "Country": 78 | { 79 | Country country = Country.Japan; 80 | if (Enum.TryParse(info.headerValue.IDENTIFIER().GetText(), out country)) 81 | { 82 | _driver.Header.Country = country; 83 | } 84 | else 85 | { 86 | string validInput = String.Join(", ", Enum.GetNames(typeof(Country))); 87 | addErrorMesage(String.Format("'{0}' is not a valid Country. Valid inputs are: {1}", info.headerValue.IDENTIFIER().GetText(), validInput), info.headerValue.IDENTIFIER().Symbol); 88 | } 89 | break; 90 | } 91 | case "Developer": 92 | { 93 | if (info.headerValue.numberLiteral() == null) 94 | { 95 | addErrorMesage("Developer expects a number literal.", info.headerValue.Start); 96 | } 97 | else 98 | { 99 | _driver.Header.Developer = (uint)parseNumberLiteral(info.headerValue.numberLiteral()); 100 | } 101 | break; 102 | } 103 | case "Version": 104 | { 105 | if (info.headerValue.numberLiteral() == null) 106 | { 107 | addErrorMesage("Version expects a number literal.", info.headerValue.Start); 108 | } 109 | else 110 | { 111 | _driver.Header.Version = (uint)parseNumberLiteral(info.headerValue.numberLiteral()); 112 | } 113 | break; 114 | } 115 | default: 116 | addErrorMesage(String.Format("'{0}' is not a valid entry name for the header statement.", info.headerType.Text), info.headerType); 117 | break; 118 | } 119 | } 120 | } 121 | 122 | public override void ExitVectorsDeclaration([NotNull] ZealCpuParser.VectorsDeclarationContext context) 123 | { 124 | _driver.Vectors = new Vectors(); 125 | 126 | foreach (var info in context.vectorInfo()) 127 | { 128 | switch (info.vectorType.Text) 129 | { 130 | case "BRK": 131 | _driver.Vectors.BRK = info.labelName.Text; 132 | break; 133 | case "IRQ": 134 | _driver.Vectors.IRQ = info.labelName.Text; 135 | break; 136 | case "NMI": 137 | _driver.Vectors.NMI = info.labelName.Text; 138 | break; 139 | case "Reset": 140 | _driver.Vectors.Reset = info.labelName.Text; 141 | break; 142 | default: 143 | addErrorMesage(String.Format("'{0}' is not a valid entry name for the vectors statement.", info.vectorType.Text), info.vectorType); 144 | break; 145 | } 146 | } 147 | 148 | if (String.IsNullOrEmpty(_driver.Vectors.BRK)) 149 | { 150 | addErrorMesage("BRK vector should be defined.", context.Start); 151 | } 152 | if (String.IsNullOrEmpty(_driver.Vectors.IRQ)) 153 | { 154 | addErrorMesage("IRQ vector should be defined.", context.Start); 155 | } 156 | if (String.IsNullOrEmpty(_driver.Vectors.NMI)) 157 | { 158 | addErrorMesage("NMI vector should be defined.", context.Start); 159 | } 160 | if (String.IsNullOrEmpty(_driver.Vectors.Reset)) 161 | { 162 | addErrorMesage("Reset vector should be defined.", context.Start); 163 | } 164 | } 165 | 166 | public override void EnterProcedureDeclaration([NotNull] ZealCpuParser.ProcedureDeclarationContext context) 167 | { 168 | _currentScope = new Scope(); 169 | _currentScope.Name = context.name.Text; 170 | _currentScope.Type = ScopeType.Procedure; 171 | } 172 | 173 | public override void ExitProcedureDeclaration([NotNull] ZealCpuParser.ProcedureDeclarationContext context) 174 | { 175 | _driver.GlobalScope.Add(_currentScope); 176 | _currentScope = null; 177 | } 178 | 179 | public override void EnterInterruptDeclaration([NotNull] ZealCpuParser.InterruptDeclarationContext context) 180 | { 181 | _currentScope = new Scope(); 182 | _currentScope.Name = context.name.Text; 183 | _currentScope.Type = ScopeType.Interrupt; 184 | } 185 | 186 | public override void ExitInterruptDeclaration([NotNull] ZealCpuParser.InterruptDeclarationContext context) 187 | { 188 | var rtiInstruction = new CpuInstructionStatement(); 189 | rtiInstruction.Opcode = CpuInstructions.rti; 190 | rtiInstruction.AddressingMode = CpuAddressingMode.Implied; 191 | 192 | _currentScope.Statements.Add(rtiInstruction); 193 | 194 | _driver.GlobalScope.Add(_currentScope); 195 | _currentScope = null; 196 | } 197 | 198 | public override void EnterInstructionStatement([NotNull] ZealCpuParser.InstructionStatementContext context) 199 | { 200 | _currentInstruction = new CpuInstructionStatement(); 201 | 202 | _currentInstruction.Line = context.Start.Line; 203 | _currentInstruction.Column = context.Start.Column; 204 | 205 | CpuInstructions opcode; 206 | if (Enum.TryParse(context.opcode().GetText(), out opcode)) 207 | { 208 | _currentInstruction.Opcode = opcode; 209 | } 210 | } 211 | 212 | public override void ExitAddress([NotNull] ZealCpuParser.AddressContext context) 213 | { 214 | var numberLiteral = context.argumentLiteral().numberLiteral(); 215 | InstructionArgument argument = null; 216 | 217 | if (numberLiteral != null) 218 | { 219 | argument = parseNumberArgument(numberLiteral); 220 | 221 | if (((NumberInstructionArgument)argument).Size == ArgumentSize.Word) 222 | { 223 | _currentInstruction.AddressingMode = CpuAddressingMode.Absolute; 224 | } 225 | else if (((NumberInstructionArgument)argument).Size == ArgumentSize.LongWord) 226 | { 227 | _currentInstruction.AddressingMode = CpuAddressingMode.AbsoluteLong; 228 | } 229 | else 230 | { 231 | _currentInstruction.AddressingMode = CpuAddressingMode.Direct; 232 | } 233 | } 234 | 235 | var identifierLitteral = context.argumentLiteral().IDENTIFIER(); 236 | if (identifierLitteral != null) 237 | { 238 | argument = new LabelInstructionArgument(identifierLitteral.GetText()); 239 | _currentInstruction.AddressingMode = CpuAddressingMode.Absolute; 240 | } 241 | 242 | if (argument != null) 243 | { 244 | _currentInstruction.Arguments.Add(argument); 245 | } 246 | } 247 | 248 | public override void ExitImmediate([NotNull] ZealCpuParser.ImmediateContext context) 249 | { 250 | var numberLiteral = context.numberLiteral(); 251 | if (numberLiteral != null) 252 | { 253 | _currentInstruction.Arguments.Add(parseNumberArgument(numberLiteral)); 254 | 255 | _currentInstruction.AddressingMode = CpuAddressingMode.Immediate; 256 | } 257 | } 258 | 259 | public override void ExitInstructionStatement([NotNull] ZealCpuParser.InstructionStatementContext context) 260 | { 261 | if (_currentInstruction.Arguments.Count == 0) 262 | { 263 | _currentInstruction.AddressingMode = CpuAddressingMode.Implied; 264 | } 265 | 266 | var assumeAddressingAttribute = _currentInstruction.Opcode.GetAttribute(); 267 | if (assumeAddressingAttribute != null) 268 | { 269 | _currentInstruction.AddressingMode = assumeAddressingAttribute.Addressing; 270 | } 271 | 272 | var labelContext = context.label(); 273 | if (labelContext != null) 274 | { 275 | _currentInstruction.AssociatedLabel = labelContext.IDENTIFIER().GetText(); 276 | } 277 | 278 | var opcodeAttributes = EnumHelper.GetAttributes(_currentInstruction.Opcode).Where(x => x.AddressingMode == _currentInstruction.AddressingMode).ToArray(); 279 | if (opcodeAttributes == null || (opcodeAttributes != null && opcodeAttributes.Length == 0)) 280 | { 281 | string descriptionAddressingMode = EnumHelper.GetAttribute(_currentInstruction.AddressingMode).Description; 282 | addErrorMesage(String.Format("opcode '{0}' does not support {1} addressing mode.", _currentInstruction.Opcode, descriptionAddressingMode), context.opcode().Start); 283 | } 284 | 285 | _currentScope.Statements.Add(_currentInstruction); 286 | _currentInstruction = null; 287 | } 288 | 289 | private string parseStringLiteral(ITerminalNode node) 290 | { 291 | return node.GetText().Trim('"'); 292 | } 293 | 294 | private int parseNumberLiteral(ZealCpuParser.NumberLiteralContext context) 295 | { 296 | return parseNumberArgument(context).Number; 297 | } 298 | 299 | private NumberInstructionArgument parseNumberArgument(ZealCpuParser.NumberLiteralContext context) 300 | { 301 | int result = 0; 302 | ArgumentSize size = ArgumentSize.Byte; 303 | 304 | switch (context.Start.Type) 305 | { 306 | case ZealCpuParser.HEX_LITERAL: 307 | { 308 | string hexText = context.HEX_LITERAL().GetText().Substring(1); 309 | if (Int32.TryParse(hexText, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out result)) 310 | { 311 | if (hexText.Length > 4 && hexText.Length <= 6) 312 | { 313 | size = ArgumentSize.LongWord; 314 | } 315 | else if (hexText.Length > 2 && hexText.Length <= 4) 316 | { 317 | size = ArgumentSize.Word; 318 | } 319 | } 320 | break; 321 | } 322 | case ZealCpuParser.INTEGER_LITERAL: 323 | if (Int32.TryParse(context.INTEGER_LITERAL().GetText(), out result)) 324 | { 325 | if (result > ushort.MaxValue) 326 | { 327 | size = ArgumentSize.LongWord; 328 | } 329 | else if (result > byte.MaxValue) 330 | { 331 | size = ArgumentSize.Word; 332 | } 333 | } 334 | break; 335 | case ZealCpuParser.BINARY_LITERAL: 336 | { 337 | string binaryLiteral = context.BINARY_LITERAL().GetText().Substring(1); 338 | int stringLength = binaryLiteral.Length - 1; 339 | for (int i = 0; i <= stringLength; ++i) 340 | { 341 | if (binaryLiteral[stringLength - i] == '1') 342 | { 343 | result |= (1 << i); 344 | } 345 | } 346 | if (binaryLiteral.Length > 16 && binaryLiteral.Length <= 24) 347 | { 348 | size = ArgumentSize.LongWord; 349 | } 350 | else if (binaryLiteral.Length > 8 && binaryLiteral.Length <= 16) 351 | { 352 | size = ArgumentSize.Word; 353 | } 354 | break; 355 | } 356 | } 357 | 358 | return new NumberInstructionArgument(result, size); 359 | } 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /LibZealCompiler/Pass/SecondCpuPass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Antlr4.Runtime.Misc; 7 | using Zeal.Compiler.Parser; 8 | using Zeal.Compiler.Data; 9 | 10 | namespace Zeal.Compiler.Pass 11 | { 12 | class SecondCpuPass : CpuPass 13 | { 14 | private Scope _currentScope; 15 | 16 | public SecondCpuPass(ZealCpuDriver driver) 17 | : base(driver) 18 | { 19 | } 20 | 21 | public override void EnterProcedureDeclaration([NotNull] ZealCpuParser.ProcedureDeclarationContext context) 22 | { 23 | _currentScope = _driver.GlobalScope.GetScope(context.name.Text); 24 | } 25 | 26 | public override void EnterInterruptDeclaration([NotNull] ZealCpuParser.InterruptDeclarationContext context) 27 | { 28 | _currentScope = _driver.GlobalScope.GetScope(context.name.Text); 29 | } 30 | 31 | public override void ExitVectorInfo([NotNull] ZealCpuParser.VectorInfoContext context) 32 | { 33 | if (!_driver.GlobalScope.IsLabelValid(context.labelName.Text)) 34 | { 35 | addErrorMesage(String.Format("Label '{0}' not found for vector {1}.", context.labelName.Text, context.vectorType.Text), context.labelName); 36 | } 37 | } 38 | 39 | public override void ExitArgumentLiteral([NotNull] ZealCpuParser.ArgumentLiteralContext context) 40 | { 41 | if (_currentScope != null && context.IDENTIFIER() != null) 42 | { 43 | string labelName = context.IDENTIFIER().GetText(); 44 | if (!_currentScope.IsLabelValid(labelName)) 45 | { 46 | addErrorMesage(String.Format("Label '{0}' not found.", labelName), context.IDENTIFIER().Symbol); 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /LibZealCompiler/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("LibZealCompiler")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("LibZealCompiler")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("58dc5b8a-bd9e-4f08-a898-14331df4fa9b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /LibZealCompiler/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # compiler 2 | Compiler for the Zeal 65816 (and hopefully SPC700) high-level assembly language 3 | -------------------------------------------------------------------------------- /ZealCompiler.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZealCompiler", "ZealCompiler\ZealCompiler.csproj", "{C97F37AE-1059-43A8-BFF1-279C85462A21}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibZealCompiler", "LibZealCompiler\LibZealCompiler.csproj", "{58DC5B8A-BD9E-4F08-A898-14331DF4FA9B}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZealCompilerUnitTests", "ZealCompilerUnitTests\ZealCompilerUnitTests.csproj", "{6E1C4D2E-A3BC-4D42-A00B-0937AAB939AB}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {C97F37AE-1059-43A8-BFF1-279C85462A21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {C97F37AE-1059-43A8-BFF1-279C85462A21}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {C97F37AE-1059-43A8-BFF1-279C85462A21}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {C97F37AE-1059-43A8-BFF1-279C85462A21}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {58DC5B8A-BD9E-4F08-A898-14331DF4FA9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {58DC5B8A-BD9E-4F08-A898-14331DF4FA9B}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {58DC5B8A-BD9E-4F08-A898-14331DF4FA9B}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {58DC5B8A-BD9E-4F08-A898-14331DF4FA9B}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {6E1C4D2E-A3BC-4D42-A00B-0937AAB939AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {6E1C4D2E-A3BC-4D42-A00B-0937AAB939AB}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {6E1C4D2E-A3BC-4D42-A00B-0937AAB939AB}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {6E1C4D2E-A3BC-4D42-A00B-0937AAB939AB}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /ZealCompiler/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ZealCompiler/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Zeal.Compiler.CodeGeneration; 5 | using Zeal.Compiler.Data; 6 | using Zeal.Compiler.Parser; 7 | 8 | namespace ZealCompiler 9 | { 10 | class Program 11 | { 12 | static void printErrorMessage(ErrorMessage error) 13 | { 14 | Console.Error.WriteLine("{0}({1},{2}): error: {3}", error.SourceFile, error.Line, error.Column, error.Message); 15 | if (!String.IsNullOrEmpty(error.Context)) 16 | { 17 | Console.Error.WriteLine(error.Context); 18 | for (int i = 0; i < error.Column; ++i) 19 | { 20 | Console.Error.Write(' '); 21 | } 22 | for(int i=error.StartToken; i<=error.EndToken; ++i) 23 | { 24 | Console.Error.Write("^"); 25 | } 26 | Console.Error.WriteLine(); 27 | } 28 | } 29 | 30 | static int Main(string[] args) 31 | { 32 | if (args.Length < 1) 33 | { 34 | Console.Error.WriteLine("No source file provided."); 35 | return 1; 36 | } 37 | 38 | if (Path.GetExtension(args[0]) != ".zcpu") 39 | { 40 | Console.Error.WriteLine("The source file is in the wrong format. The source file must ends with .zcpu."); 41 | return 1; 42 | } 43 | 44 | ZealCpuDriver driver = new ZealCpuDriver(args[0]); 45 | try 46 | { 47 | driver.Parse(); 48 | driver.SecondPass(); 49 | } 50 | catch(CompilerErrorException) 51 | { 52 | foreach (var error in driver.Errors) 53 | { 54 | printErrorMessage(error); 55 | } 56 | 57 | #if DEBUG 58 | Console.Read(); 59 | #endif 60 | 61 | return 1; 62 | } 63 | 64 | FileStream outputRom = new FileStream(Path.ChangeExtension(args[0], ".sfc"), FileMode.Create); 65 | 66 | CpuCodeGenerator codeGenerator = new CpuCodeGenerator(outputRom); 67 | codeGenerator.Header = driver.Header; 68 | 69 | foreach (var scope in driver.GlobalScope.Children) 70 | { 71 | codeGenerator.Scope = scope; 72 | 73 | List instructions = new List(); 74 | 75 | foreach (var statement in scope.Statements) 76 | { 77 | if (statement is CpuInstructionStatement) 78 | { 79 | instructions.Add((CpuInstructionStatement)statement); 80 | } 81 | } 82 | 83 | codeGenerator.Instructions = instructions; 84 | codeGenerator.Generate(); 85 | } 86 | 87 | SfcRomWriter romWriter = new SfcRomWriter(outputRom); 88 | romWriter.Driver = driver; 89 | romWriter.Write(); 90 | 91 | outputRom.Close(); 92 | 93 | using (FileStream newRom = new FileStream(Path.ChangeExtension(args[0], ".sfc"), FileMode.Open)) 94 | { 95 | SfcRomWriter checksumWriter = new SfcRomWriter(newRom); 96 | checksumWriter.Driver = driver; 97 | checksumWriter.ComputeChecksum(); 98 | } 99 | 100 | return 0; 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /ZealCompiler/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("ZealCompiler")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("ZealCompiler")] 12 | [assembly: AssemblyCopyright("Copyright © 2015")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("c97f37ae-1059-43a8-bff1-279c85462a21")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /ZealCompiler/ZealCompiler.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C97F37AE-1059-43A8-BFF1-279C85462A21} 8 | Exe 9 | Properties 10 | Zeal.Compiler 11 | zealc 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | ..\..\bin\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | ..\..\bin\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | {58dc5b8a-bd9e-4f08-a898-14331df4fa9b} 55 | LibZealCompiler 56 | 57 | 58 | 59 | 66 | -------------------------------------------------------------------------------- /ZealCompilerUnitTests/CpuCodeGeneratorTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using Xunit; 5 | using Zeal.Compiler.CodeGeneration; 6 | using Zeal.Compiler.Data; 7 | using Zeal.Compiler.Parser; 8 | using Zeal.Compiler.UnitTests.Extensions; 9 | 10 | namespace Zeal.Compiler.UnitTests 11 | { 12 | public class CpuCodeGeneratorTest 13 | { 14 | [Theory] 15 | [InlineData(CpuInstructions.asl, 0x0A)] 16 | [InlineData(CpuInstructions.brk, 0x00)] 17 | [InlineData(CpuInstructions.clc, 0x18)] 18 | [InlineData(CpuInstructions.cld, 0xD8)] 19 | [InlineData(CpuInstructions.cli, 0x58)] 20 | [InlineData(CpuInstructions.clv, 0xB8)] 21 | [InlineData(CpuInstructions.dec, 0x3A)] 22 | [InlineData(CpuInstructions.dex, 0xCA)] 23 | [InlineData(CpuInstructions.dey, 0x88)] 24 | [InlineData(CpuInstructions.inc, 0x1A)] 25 | [InlineData(CpuInstructions.inx, 0xE8)] 26 | [InlineData(CpuInstructions.iny, 0xC8)] 27 | [InlineData(CpuInstructions.lsr, 0x4A)] 28 | [InlineData(CpuInstructions.nop, 0xEA)] 29 | [InlineData(CpuInstructions.pha, 0x48)] 30 | [InlineData(CpuInstructions.phb, 0x8B)] 31 | [InlineData(CpuInstructions.phd, 0x0B)] 32 | [InlineData(CpuInstructions.phk, 0x4B)] 33 | [InlineData(CpuInstructions.php, 0x08)] 34 | [InlineData(CpuInstructions.phx, 0xDA)] 35 | [InlineData(CpuInstructions.phy, 0x5A)] 36 | [InlineData(CpuInstructions.pla, 0x68)] 37 | [InlineData(CpuInstructions.plb, 0xAB)] 38 | [InlineData(CpuInstructions.pld, 0x2B)] 39 | [InlineData(CpuInstructions.plp, 0x28)] 40 | [InlineData(CpuInstructions.plx, 0xFA)] 41 | [InlineData(CpuInstructions.ply, 0x7A)] 42 | [InlineData(CpuInstructions.rol, 0x2A)] 43 | [InlineData(CpuInstructions.ror, 0x6A)] 44 | [InlineData(CpuInstructions.rti, 0x40)] 45 | [InlineData(CpuInstructions.rtl, 0x6B)] 46 | [InlineData(CpuInstructions.rts, 0x60)] 47 | [InlineData(CpuInstructions.sec, 0x38)] 48 | [InlineData(CpuInstructions.sed, 0xF8)] 49 | [InlineData(CpuInstructions.sei, 0x78)] 50 | [InlineData(CpuInstructions.stp, 0xDB)] 51 | [InlineData(CpuInstructions.tax, 0xAA)] 52 | [InlineData(CpuInstructions.tay, 0xA8)] 53 | [InlineData(CpuInstructions.tcd, 0x5B)] 54 | [InlineData(CpuInstructions.tcs, 0x1B)] 55 | [InlineData(CpuInstructions.tdc, 0x7B)] 56 | [InlineData(CpuInstructions.tsc, 0x3B)] 57 | [InlineData(CpuInstructions.tsx, 0xBA)] 58 | [InlineData(CpuInstructions.txa, 0x8A)] 59 | [InlineData(CpuInstructions.txs, 0x9A)] 60 | [InlineData(CpuInstructions.txy, 0x9B)] 61 | [InlineData(CpuInstructions.tya, 0x98)] 62 | [InlineData(CpuInstructions.tyx, 0xBB)] 63 | [InlineData(CpuInstructions.wai, 0xCB)] 64 | [InlineData(CpuInstructions.xba, 0xEB)] 65 | [InlineData(CpuInstructions.xce, 0xFB)] 66 | public void ShouldGenerateImpliedInstruction(CpuInstructions opcodeEnum, byte finalOpcode) 67 | { 68 | CpuInstructionStatement instruction = new CpuInstructionStatement(); 69 | instruction.AddressingMode = CpuAddressingMode.Implied; 70 | instruction.Opcode = opcodeEnum; 71 | 72 | List instructions = new List(); 73 | instructions.Add(instruction); 74 | 75 | MemoryStream memoryStream = new MemoryStream(8); 76 | CpuCodeGenerator generator = new CpuCodeGenerator(memoryStream); 77 | generator.Instructions = instructions; 78 | generator.Generate(); 79 | 80 | Assert.Equal(finalOpcode, memoryStream.GetBuffer()[0]); 81 | } 82 | 83 | [Theory] 84 | // Enum, opcode, value 85 | [InlineData(CpuInstructions.adc, 0x69, 34)] 86 | [InlineData(CpuInstructions.adc, 0x69, 1024)] 87 | [InlineData(CpuInstructions.and, 0x29, 34)] 88 | [InlineData(CpuInstructions.and, 0x29, 1024)] 89 | [InlineData(CpuInstructions.bit, 0x89, 34)] 90 | [InlineData(CpuInstructions.bit, 0x89, 1024)] 91 | [InlineData(CpuInstructions.cmp, 0xC9, 34)] 92 | [InlineData(CpuInstructions.cmp, 0xC9, 1024)] 93 | [InlineData(CpuInstructions.cpx, 0xE0, 34)] 94 | [InlineData(CpuInstructions.cpx, 0xE0, 1024)] 95 | [InlineData(CpuInstructions.cpy, 0xC0, 34)] 96 | [InlineData(CpuInstructions.cpy, 0xC0, 1024)] 97 | [InlineData(CpuInstructions.eor, 0x49, 34)] 98 | [InlineData(CpuInstructions.eor, 0x49, 1024)] 99 | [InlineData(CpuInstructions.lda, 0xA9, 34)] 100 | [InlineData(CpuInstructions.lda, 0xA9, 1024)] 101 | [InlineData(CpuInstructions.ldx, 0xA2, 34)] 102 | [InlineData(CpuInstructions.ldx, 0xA2, 1024)] 103 | [InlineData(CpuInstructions.ldy, 0xA0, 34)] 104 | [InlineData(CpuInstructions.ldy, 0xA0, 1024)] 105 | [InlineData(CpuInstructions.ora, 0x09, 34)] 106 | [InlineData(CpuInstructions.ora, 0x09, 1024)] 107 | [InlineData(CpuInstructions.rep, 0xC2, 34)] 108 | [InlineData(CpuInstructions.sbc, 0xE9, 34)] 109 | [InlineData(CpuInstructions.sbc, 0xE9, 1024)] 110 | [InlineData(CpuInstructions.sep, 0xE2, 34)] 111 | public void ShouldGenerateImmediateInstruction(CpuInstructions opcodeEnum, byte finalOpcode, int value) 112 | { 113 | CpuInstructionStatement instruction = new CpuInstructionStatement(); 114 | instruction.AddressingMode = CpuAddressingMode.Immediate; 115 | instruction.Opcode = opcodeEnum; 116 | if (value > byte.MaxValue) 117 | { 118 | instruction.Arguments.Add(new NumberInstructionArgument(value, ArgumentSize.Word)); 119 | } 120 | else 121 | { 122 | instruction.Arguments.Add(new NumberInstructionArgument(value, ArgumentSize.Byte)); 123 | } 124 | 125 | List instructions = new List(); 126 | instructions.Add(instruction); 127 | 128 | MemoryStream memoryStream = new MemoryStream(8); 129 | CpuCodeGenerator generator = new CpuCodeGenerator(memoryStream); 130 | generator.Instructions = instructions; 131 | generator.Generate(); 132 | 133 | Assert.Equal(finalOpcode, memoryStream.GetBuffer()[0]); 134 | Assert.Equal((byte)(value & 0xFF), memoryStream.GetBuffer()[1]); 135 | Assert.Equal((byte)(value >> 8), memoryStream.GetBuffer()[2]); 136 | } 137 | 138 | [Theory] 139 | // Enum, opcode, value 140 | [InlineData(CpuInstructions.adc, 0x65, 0x34)] 141 | [InlineData(CpuInstructions.and, 0x25, 0x34)] 142 | [InlineData(CpuInstructions.asl, 0x06, 0x34)] 143 | [InlineData(CpuInstructions.bit, 0x24, 0x34)] 144 | [InlineData(CpuInstructions.cmp, 0xC5, 0x34)] 145 | [InlineData(CpuInstructions.cpx, 0xE4, 0x34)] 146 | [InlineData(CpuInstructions.cpy, 0xC4, 0x34)] 147 | [InlineData(CpuInstructions.dec, 0xC6, 0x34)] 148 | [InlineData(CpuInstructions.eor, 0x45, 0x34)] 149 | [InlineData(CpuInstructions.inc, 0xE6, 0x34)] 150 | [InlineData(CpuInstructions.lda, 0xA5, 0x34)] 151 | [InlineData(CpuInstructions.ldx, 0xA6, 0x34)] 152 | [InlineData(CpuInstructions.ldy, 0xA4, 0x34)] 153 | [InlineData(CpuInstructions.lsr, 0x46, 0x34)] 154 | [InlineData(CpuInstructions.ora, 0x05, 0x34)] 155 | [InlineData(CpuInstructions.rol, 0x26, 0x34)] 156 | [InlineData(CpuInstructions.ror, 0x66, 0x34)] 157 | [InlineData(CpuInstructions.sbc, 0xE5, 0x34)] 158 | [InlineData(CpuInstructions.sta, 0x85, 0x34)] 159 | [InlineData(CpuInstructions.stx, 0x86, 0x34)] 160 | [InlineData(CpuInstructions.sty, 0x84, 0x34)] 161 | [InlineData(CpuInstructions.stz, 0x64, 0x34)] 162 | [InlineData(CpuInstructions.trb, 0x14, 0x34)] 163 | [InlineData(CpuInstructions.tsb, 0x04, 0x34)] 164 | public void ShouldGenerateDirectInstruction(CpuInstructions opcodeEnum, byte finalOpcode, int value) 165 | { 166 | CpuInstructionStatement instruction = new CpuInstructionStatement(); 167 | instruction.AddressingMode = CpuAddressingMode.Direct; 168 | instruction.Opcode = opcodeEnum; 169 | instruction.Arguments.Add(new NumberInstructionArgument(value, ArgumentSize.Byte)); 170 | 171 | List instructions = new List(); 172 | instructions.Add(instruction); 173 | 174 | MemoryStream memoryStream = new MemoryStream(8); 175 | CpuCodeGenerator generator = new CpuCodeGenerator(memoryStream); 176 | generator.Instructions = instructions; 177 | generator.Generate(); 178 | 179 | Assert.Equal(finalOpcode, memoryStream.GetBuffer()[0]); 180 | Assert.Equal((byte)(value & 0xFF), memoryStream.GetBuffer()[1]); 181 | } 182 | 183 | [Theory] 184 | // Enum, opcode, value 185 | [InlineData(CpuInstructions.adc, 0x6D, 0x34FF)] 186 | [InlineData(CpuInstructions.and, 0x2D, 0x34FF)] 187 | [InlineData(CpuInstructions.asl, 0x0E, 0x34FF)] 188 | [InlineData(CpuInstructions.bit, 0x2C, 0x34FF)] 189 | [InlineData(CpuInstructions.cmp, 0xCD, 0x34FF)] 190 | [InlineData(CpuInstructions.cpx, 0xEC, 0x34FF)] 191 | [InlineData(CpuInstructions.cpy, 0xCC, 0x34FF)] 192 | [InlineData(CpuInstructions.dec, 0xCE, 0x34FF)] 193 | [InlineData(CpuInstructions.eor, 0x4D, 0x34FF)] 194 | [InlineData(CpuInstructions.inc, 0xEE, 0x34FF)] 195 | [InlineData(CpuInstructions.jmp, 0x4C, 0x34FF)] 196 | [InlineData(CpuInstructions.jsr, 0x20, 0x34FF)] 197 | [InlineData(CpuInstructions.lda, 0xAD, 0x34FF)] 198 | [InlineData(CpuInstructions.ldx, 0xAE, 0x34FF)] 199 | [InlineData(CpuInstructions.ldy, 0xAC, 0x34FF)] 200 | [InlineData(CpuInstructions.lsr, 0x4E, 0x34FF)] 201 | [InlineData(CpuInstructions.ora, 0x0D, 0x34FF)] 202 | [InlineData(CpuInstructions.rol, 0x2E, 0x34FF)] 203 | [InlineData(CpuInstructions.ror, 0x6E, 0x34FF)] 204 | [InlineData(CpuInstructions.sbc, 0xED, 0x34FF)] 205 | [InlineData(CpuInstructions.sta, 0x8D, 0x34FF)] 206 | [InlineData(CpuInstructions.stx, 0x8E, 0x34FF)] 207 | [InlineData(CpuInstructions.sty, 0x8C, 0x34FF)] 208 | [InlineData(CpuInstructions.stz, 0x9C, 0x34FF)] 209 | [InlineData(CpuInstructions.trb, 0x1C, 0x34FF)] 210 | [InlineData(CpuInstructions.tsb, 0x0C, 0x34FF)] 211 | public void ShouldGenerateAbsoluteInstruction(CpuInstructions opcodeEnum, byte finalOpcode, int value) 212 | { 213 | CpuInstructionStatement instruction = new CpuInstructionStatement(); 214 | instruction.AddressingMode = CpuAddressingMode.Absolute; 215 | instruction.Opcode = opcodeEnum; 216 | instruction.Arguments.Add(new NumberInstructionArgument(value, ArgumentSize.Word)); 217 | 218 | List instructions = new List(); 219 | instructions.Add(instruction); 220 | 221 | MemoryStream memoryStream = new MemoryStream(8); 222 | CpuCodeGenerator generator = new CpuCodeGenerator(memoryStream); 223 | generator.Instructions = instructions; 224 | generator.Generate(); 225 | 226 | Assert.Equal(finalOpcode, memoryStream.GetBuffer()[0]); 227 | Assert.Equal((byte)(value & 0xFF), memoryStream.GetBuffer()[1]); 228 | Assert.Equal((byte)(value >> 8), memoryStream.GetBuffer()[2]); 229 | } 230 | 231 | [Fact] 232 | public void ShouldGenerateRelativeInstructionsWithLabel() 233 | { 234 | string input = @"procedure Test 235 | { 236 | php 237 | 238 | backwardBranch: 239 | sec 240 | bvs backwardBranch 241 | lda #$03 242 | bra forwardBranch 243 | tax 244 | tay 245 | 246 | forwardBranch: 247 | rts 248 | } 249 | "; 250 | 251 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 252 | driver.Parse(); 253 | 254 | MemoryStream memoryStream = new MemoryStream(32); 255 | CpuCodeGenerator generator = new CpuCodeGenerator(memoryStream); 256 | generator.Instructions = driver.GlobalScope.Children[0].Statements.Where(x => x is CpuInstructionStatement).Select(x => x as CpuInstructionStatement).ToList(); 257 | generator.Scope = driver.GlobalScope.Children[0]; 258 | generator.Generate(); 259 | 260 | Assert.Equal(0xFD, memoryStream.GetBuffer()[3]); 261 | Assert.Equal(0x02, memoryStream.GetBuffer()[7]); 262 | } 263 | 264 | [Fact] 265 | public void ShouldGenerateAbsoluteInstructionsWithLabel() 266 | { 267 | string input = @"procedure Test 268 | { 269 | php 270 | 271 | mainLoop: 272 | jmp mainLoop 273 | } 274 | "; 275 | 276 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 277 | driver.Parse(); 278 | 279 | RomHeader fakeHeader = new RomHeader(); 280 | fakeHeader.MapMode = MapMode.LoROM; 281 | fakeHeader.RomSpeed = RomSpeed.SlowROM; 282 | 283 | MemoryStream memoryStream = new MemoryStream(32); 284 | CpuCodeGenerator generator = new CpuCodeGenerator(memoryStream); 285 | generator.Instructions = driver.GlobalScope.Children[0].Statements.Where(x => x is CpuInstructionStatement).Select(x => x as CpuInstructionStatement).ToList(); 286 | generator.Scope = driver.GlobalScope.Children[0]; 287 | generator.Header = fakeHeader; 288 | generator.Generate(); 289 | 290 | Assert.Equal(0x01, memoryStream.GetBuffer()[2]); 291 | Assert.Equal(0x80, memoryStream.GetBuffer()[3]); 292 | } 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /ZealCompilerUnitTests/CpuParseTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | using Zeal.Compiler.Data; 4 | using Zeal.Compiler.Parser; 5 | using Zeal.Compiler.UnitTests.Extensions; 6 | 7 | namespace Zeal.Compiler.UnitTests 8 | { 9 | public class CpuParseTest 10 | { 11 | const string ProcedureTemplate = @"procedure Test 12 | {{ 13 | {0} 14 | }}"; 15 | 16 | [Fact] 17 | public void ShouldParseHeaderInfo() 18 | { 19 | const string input = @" 20 | header 21 | { 22 | CartridgeName = ""HELLO WORLD SNES"" 23 | RomSpeed = FastROM 24 | MapMode = HiROM 25 | SramSize = 32 26 | Country = NorthAmerica 27 | Developer = $1A 28 | Version = %1010 29 | } 30 | "; 31 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 32 | driver.Parse(); 33 | 34 | Assert.Equal("HELLO WORLD SNES", driver.Header.CartridgeName); 35 | Assert.Equal(RomSpeed.FastROM, driver.Header.RomSpeed); 36 | Assert.Equal(MapMode.HiROM, driver.Header.MapMode); 37 | Assert.Equal(32u, driver.Header.SramSize); 38 | Assert.Equal(Country.NorthAmerica, driver.Header.Country); 39 | Assert.Equal(0x1Au, driver.Header.Developer); 40 | Assert.Equal(10u, driver.Header.Version); 41 | } 42 | 43 | [Fact] 44 | public void ShouldParseVectorsInfo() 45 | { 46 | const string input = @" 47 | vectors 48 | { 49 | BRK = BrkVector 50 | IRQ = IrqVector 51 | NMI = NmiVector 52 | Reset = Main 53 | } 54 | "; 55 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 56 | driver.Parse(); 57 | 58 | Assert.Equal("BrkVector", driver.Vectors.BRK); 59 | Assert.Equal("IrqVector", driver.Vectors.IRQ); 60 | Assert.Equal("NmiVector", driver.Vectors.NMI); 61 | Assert.Equal("Main", driver.Vectors.Reset); 62 | } 63 | 64 | [Fact] 65 | public void ShouldParseProcedure() 66 | { 67 | const string input = @"procedure Test 68 | { 69 | }"; 70 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 71 | driver.Parse(); 72 | 73 | Assert.Equal("Test", driver.GlobalScope.Children[0].Name); 74 | Assert.Equal(ScopeType.Procedure, driver.GlobalScope.Children[0].Type); 75 | } 76 | 77 | [Fact] 78 | public void ShouldParseInterrupt() 79 | { 80 | const string input = @"interrupt EmptyVector 81 | { 82 | }"; 83 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 84 | driver.Parse(); 85 | 86 | Assert.Equal("EmptyVector", driver.GlobalScope.Children[0].Name); 87 | Assert.Equal(ScopeType.Interrupt, driver.GlobalScope.Children[0].Type); 88 | } 89 | 90 | [Fact] 91 | public void ShouldAddRTIWhenParsingInterrupt() 92 | { 93 | const string input = @"interrupt NMI 94 | { 95 | php 96 | pha 97 | 98 | pla 99 | plp 100 | }"; 101 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 102 | driver.Parse(); 103 | 104 | var cpuInstruction = driver.GlobalScope.Children[0].Statements[driver.GlobalScope.Children[0].Statements.Count - 1] as CpuInstructionStatement; 105 | 106 | Assert.Equal("NMI", driver.GlobalScope.Children[0].Name); 107 | Assert.Equal(ScopeType.Interrupt, driver.GlobalScope.Children[0].Type); 108 | Assert.Equal(CpuInstructions.rti, cpuInstruction.Opcode); 109 | Assert.Equal(CpuAddressingMode.Implied, cpuInstruction.AddressingMode); 110 | } 111 | 112 | [Fact] 113 | public void ShouldParseLabel() 114 | { 115 | const string input = @"procedure Test 116 | { 117 | php 118 | pha 119 | 120 | mainLoop: 121 | lda $2007 122 | jmp mainLoop 123 | 124 | exit: 125 | rts 126 | }"; 127 | 128 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 129 | driver.Parse(); 130 | 131 | var thirdInstruction = driver.GlobalScope.Children[0].Statements[2]; 132 | var fiveInstruction = driver.GlobalScope.Children[0].Statements[4]; 133 | 134 | Assert.Equal("mainLoop", thirdInstruction.AssociatedLabel); 135 | Assert.Equal("exit", fiveInstruction.AssociatedLabel); 136 | } 137 | 138 | [Fact] 139 | public void ShouldParseLabelArgument() 140 | { 141 | string input = @"procedure Test 142 | { 143 | jmp mainLoop 144 | }"; 145 | 146 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 147 | driver.Parse(); 148 | 149 | var instruction = driver.GlobalScope.Children[0].Statements[0] as CpuInstructionStatement; 150 | var argument = instruction.Arguments[0] as LabelInstructionArgument; 151 | 152 | Assert.Equal("mainLoop", argument.Label); 153 | } 154 | 155 | [Theory] 156 | [InlineData("jmp mainLoop")] 157 | [InlineData("jsr SomeFunction")] 158 | public void ShouldMarkAbsoluteLabelArgument(string instructionText) 159 | { 160 | string input = String.Format(ProcedureTemplate, instructionText); 161 | 162 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 163 | driver.Parse(); 164 | 165 | var instruction = driver.GlobalScope.Children[0].Statements[0] as CpuInstructionStatement; 166 | 167 | Assert.Equal(CpuAddressingMode.Absolute, instruction.AddressingMode); 168 | } 169 | 170 | [Theory] 171 | [InlineData("bcc Label")] 172 | [InlineData("bcs Label")] 173 | [InlineData("beq Label")] 174 | [InlineData("bmi Label")] 175 | [InlineData("bne Label")] 176 | [InlineData("bpl Label")] 177 | [InlineData("bra Label")] 178 | [InlineData("bvc Label")] 179 | [InlineData("bvs Label")] 180 | public void ShouldMarkRelativeLabelArgument(string instructionText) 181 | { 182 | string input = String.Format(ProcedureTemplate, instructionText); 183 | 184 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 185 | driver.Parse(); 186 | 187 | var instruction = driver.GlobalScope.Children[0].Statements[0] as CpuInstructionStatement; 188 | 189 | Assert.Equal(CpuAddressingMode.Relative, instruction.AddressingMode); 190 | } 191 | 192 | [Theory] 193 | [InlineData("clc", CpuInstructions.clc)] 194 | [InlineData("cld", CpuInstructions.cld)] 195 | [InlineData("cli", CpuInstructions.cli)] 196 | [InlineData("clv", CpuInstructions.clv)] 197 | [InlineData("dex", CpuInstructions.dex)] 198 | [InlineData("dey", CpuInstructions.dey)] 199 | [InlineData("inc", CpuInstructions.inc)] 200 | [InlineData("inx", CpuInstructions.inx)] 201 | [InlineData("iny", CpuInstructions.iny)] 202 | [InlineData("nop", CpuInstructions.nop)] 203 | [InlineData("sec", CpuInstructions.sec)] 204 | [InlineData("sed", CpuInstructions.sed)] 205 | [InlineData("sei", CpuInstructions.sei)] 206 | [InlineData("stp", CpuInstructions.stp)] 207 | [InlineData("tax", CpuInstructions.tax)] 208 | [InlineData("tay", CpuInstructions.tay)] 209 | [InlineData("tcd", CpuInstructions.tcd)] 210 | [InlineData("tcs", CpuInstructions.tcs)] 211 | [InlineData("tdc", CpuInstructions.tdc)] 212 | [InlineData("tsc", CpuInstructions.tsc)] 213 | [InlineData("tsx", CpuInstructions.tsx)] 214 | [InlineData("txa", CpuInstructions.txa)] 215 | [InlineData("txs", CpuInstructions.txs)] 216 | [InlineData("txy", CpuInstructions.txy)] 217 | [InlineData("tya", CpuInstructions.tya)] 218 | [InlineData("tyx", CpuInstructions.tyx)] 219 | [InlineData("wai", CpuInstructions.wai)] 220 | [InlineData("xba", CpuInstructions.xba)] 221 | [InlineData("xce", CpuInstructions.xce)] 222 | public void ShouldParseImpliedInstructions(string opcodeText, CpuInstructions opcodeEnum) 223 | { 224 | string input = String.Format(ProcedureTemplate, opcodeText); 225 | 226 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 227 | driver.Parse(); 228 | 229 | CpuInstructionStatement instructionStatement = driver.GlobalScope.Children[0].Statements[0] as CpuInstructionStatement; 230 | Assert.Equal(opcodeEnum, instructionStatement.Opcode); 231 | Assert.Equal(CpuAddressingMode.Implied, instructionStatement.AddressingMode); 232 | } 233 | 234 | [Theory] 235 | [InlineData("rep #$38", CpuInstructions.rep, 0x38)] 236 | [InlineData("ldx #$1FFF", CpuInstructions.ldx, 0x1FFF)] 237 | public void ShouldParseImmediateInstructions(string instruction, CpuInstructions opcodeEnum, int value) 238 | { 239 | string input = String.Format(ProcedureTemplate, instruction); 240 | 241 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 242 | driver.Parse(); 243 | 244 | CpuInstructionStatement instructionStatement = driver.GlobalScope.Children[0].Statements[0] as CpuInstructionStatement; 245 | Assert.Equal(opcodeEnum, instructionStatement.Opcode); 246 | Assert.Equal(CpuAddressingMode.Immediate, instructionStatement.AddressingMode); 247 | 248 | var numberArgument = instructionStatement.Arguments[0] as NumberInstructionArgument; 249 | Assert.Equal(value, numberArgument.Number); 250 | } 251 | 252 | [Theory] 253 | [InlineData("sta $00", CpuInstructions.sta, 0)] 254 | [InlineData("lda $02", CpuInstructions.lda, 2)] 255 | public void ShouldParseDirectInstructions(string instruction, CpuInstructions opcodeEnum, int value) 256 | { 257 | string input = String.Format(ProcedureTemplate, instruction); 258 | 259 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 260 | driver.Parse(); 261 | 262 | CpuInstructionStatement instructionStatement = driver.GlobalScope.Children[0].Statements[0] as CpuInstructionStatement; 263 | Assert.Equal(opcodeEnum, instructionStatement.Opcode); 264 | Assert.Equal(CpuAddressingMode.Direct, instructionStatement.AddressingMode); 265 | 266 | var numberArgument = instructionStatement.Arguments[0] as NumberInstructionArgument; 267 | Assert.Equal(value, numberArgument.Number); 268 | } 269 | 270 | [Theory] 271 | [InlineData("sta $2100", CpuInstructions.sta, 0x2100)] 272 | public void ShouldParseAbsoluteInstructions(string instruction, CpuInstructions opcodeEnum, int value) 273 | { 274 | string input = String.Format(ProcedureTemplate, instruction); 275 | 276 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 277 | driver.Parse(); 278 | 279 | CpuInstructionStatement instructionStatement = driver.GlobalScope.Children[0].Statements[0] as CpuInstructionStatement; 280 | Assert.Equal(opcodeEnum, instructionStatement.Opcode); 281 | Assert.Equal(CpuAddressingMode.Absolute, instructionStatement.AddressingMode); 282 | 283 | var numberArgument = instructionStatement.Arguments[0] as NumberInstructionArgument; 284 | Assert.Equal(value, numberArgument.Number); 285 | } 286 | 287 | [Theory] 288 | [InlineData("lda #$1", 0x1, ArgumentSize.Byte)] 289 | [InlineData("lda #$01", 0x1, ArgumentSize.Byte)] 290 | [InlineData("lda #1", 0x1, ArgumentSize.Byte)] 291 | [InlineData("lda #255", 255, ArgumentSize.Byte)] 292 | [InlineData("lda #%0001", 1, ArgumentSize.Byte)] 293 | [InlineData("lda #$0001", 0x1, ArgumentSize.Word)] 294 | [InlineData("lda #$100", 0x100, ArgumentSize.Word)] 295 | [InlineData("lda #$00FF", 0xFF, ArgumentSize.Word)] 296 | [InlineData("lda #256", 256, ArgumentSize.Word)] 297 | [InlineData("lda #%000000000001", 1, ArgumentSize.Word)] 298 | [InlineData("sta $000001", 1, ArgumentSize.LongWord)] 299 | [InlineData("sta 65537", 65537, ArgumentSize.LongWord)] 300 | [InlineData("sta %100000000000000000000000", 0x800000, ArgumentSize.LongWord)] 301 | [InlineData("sta %000000000000000000000001", 1, ArgumentSize.LongWord)] 302 | public void ShouldParseArgumentSizeCorrectly(string instruction, int value, ArgumentSize size) 303 | { 304 | string input = String.Format(ProcedureTemplate, instruction); 305 | 306 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 307 | driver.Parse(); 308 | 309 | CpuInstructionStatement instructionStatement = driver.GlobalScope.Children[0].Statements[0] as CpuInstructionStatement; 310 | 311 | var argument = instructionStatement.Arguments[0] as NumberInstructionArgument; 312 | 313 | Assert.Equal(value, argument.Number); 314 | Assert.Equal(size, argument.Size); 315 | } 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /ZealCompilerUnitTests/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | 4 | namespace Zeal.Compiler.UnitTests.Extensions 5 | { 6 | static class StringExtensions 7 | { 8 | public static MemoryStream ToMemoryStream(this string input) 9 | { 10 | return new MemoryStream(Encoding.UTF8.GetBytes(input)); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ZealCompilerUnitTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("ZealCompilerUnitTests")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("ZealCompilerUnitTests")] 12 | [assembly: AssemblyCopyright("Copyright © 2015")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("6e1c4d2e-a3bc-4d42-a00b-0937aab939ab")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /ZealCompilerUnitTests/ScopeTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | using Zeal.Compiler.Parser; 8 | using Zeal.Compiler.UnitTests.Extensions; 9 | 10 | namespace Zeal.Compiler.UnitTests 11 | { 12 | public class ScopeTest 13 | { 14 | [Fact] 15 | public void ShoulResolveLabelsToAddress() 16 | { 17 | string input = @"procedure Test 18 | { 19 | php 20 | pha 21 | 22 | mainLoop: 23 | lda $2007 24 | jmp mainLoop 25 | bra exit 26 | 27 | exit: 28 | rts 29 | } 30 | 31 | interrupt EmptyVector 32 | { 33 | } 34 | "; 35 | 36 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 37 | driver.Parse(); 38 | 39 | Assert.Equal(0, driver.GlobalScope.AddressFor("Test")); 40 | Assert.Equal(11, driver.GlobalScope.AddressFor("EmptyVector")); 41 | 42 | var testScope = driver.GlobalScope.Children[0]; 43 | Assert.Equal(2, testScope.AddressFor("mainLoop")); 44 | Assert.Equal(10, testScope.AddressFor("exit")); 45 | } 46 | 47 | [Fact] 48 | public void ShouldResolveLabelsOutsideScope() 49 | { 50 | const string input = 51 | @" 52 | vectors 53 | { 54 | BRK = EmptyVector 55 | IRQ = EmptyVector 56 | NMI = EmptyVector 57 | Reset = Main 58 | } 59 | 60 | procedure Main 61 | { 62 | jsr Test 63 | } 64 | 65 | procedure Test 66 | { 67 | php 68 | rep #$30 69 | pha 70 | 71 | lda #$03 72 | 73 | pla 74 | plp 75 | rts 76 | } 77 | 78 | interrupt EmptyVector 79 | { 80 | } 81 | "; 82 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 83 | 84 | driver.Parse(); 85 | driver.SecondPass(); 86 | 87 | var mainProcedure = driver.GlobalScope.Children[0]; 88 | Assert.Equal(true, mainProcedure.IsLabelValid("Test")); 89 | Assert.Equal(3, mainProcedure.AddressFor("Test")); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /ZealCompilerUnitTests/SemanticErrorTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | using Zeal.Compiler.Parser; 8 | using Zeal.Compiler.UnitTests.Extensions; 9 | 10 | namespace Zeal.Compiler.UnitTests 11 | { 12 | public class SemanticErrorTest 13 | { 14 | [Fact] 15 | public void ShouldFailOnInvalidHeaderEntryName() 16 | { 17 | const string input = @" 18 | header 19 | { 20 | CartName = ""HELLO WORLD"" 21 | } 22 | 23 | vectors 24 | { 25 | 26 | } 27 | 28 | procedure Main 29 | { 30 | sei 31 | clc 32 | xce 33 | } 34 | "; 35 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 36 | Assert.Throws(() => driver.Parse()); 37 | } 38 | 39 | [Theory] 40 | [InlineData("Slowrom")] 41 | [InlineData("Fastrom")] 42 | [InlineData("ExHirom")] 43 | [InlineData("SlowROM")] 44 | public void MapModeShouldBeValid(string mapMode) 45 | { 46 | const string inputTemplate = @" 47 | header 48 | {{ 49 | MapMode = {0} 50 | }} 51 | 52 | vectors 53 | {{ 54 | 55 | }} 56 | 57 | procedure Main 58 | {{ 59 | sei 60 | clc 61 | xce 62 | }} 63 | "; 64 | string input = String.Format(inputTemplate, mapMode); 65 | 66 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 67 | Assert.Throws(() => driver.Parse()); 68 | } 69 | 70 | [Theory] 71 | [InlineData("slowrom")] 72 | [InlineData("fastrom")] 73 | [InlineData("LoROM")] 74 | [InlineData("HiROM")] 75 | public void RomSpeedShouldBeValid(string romSpeed) 76 | { 77 | const string inputTemplate = @" 78 | header 79 | {{ 80 | RomSpeed = {0} 81 | }} 82 | 83 | vectors 84 | {{ 85 | 86 | }} 87 | 88 | procedure Main 89 | {{ 90 | sei 91 | clc 92 | xce 93 | }} 94 | "; 95 | string input = String.Format(inputTemplate, romSpeed); 96 | 97 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 98 | Assert.Throws(() => driver.Parse()); 99 | } 100 | 101 | [Theory] 102 | [InlineData("Canadia")] 103 | [InlineData("USA")] 104 | [InlineData("Deuchland")] 105 | [InlineData("Kebec")] 106 | public void CountryShouldBeValid(string country) 107 | { 108 | const string inputTemplate = @" 109 | header 110 | {{ 111 | Country = {0} 112 | }} 113 | 114 | vectors 115 | {{ 116 | 117 | }} 118 | 119 | procedure Main 120 | {{ 121 | sei 122 | clc 123 | xce 124 | }} 125 | "; 126 | string input = String.Format(inputTemplate, country); 127 | 128 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 129 | Assert.Throws(() => driver.Parse()); 130 | } 131 | 132 | [Theory] 133 | [InlineData("2")] 134 | [InlineData("HelloWorld")] 135 | [InlineData("$2A")] 136 | [InlineData("%010101011")] 137 | public void CartridgeNameExceptsAStringLiteral(string invalidCartridgeName) 138 | { 139 | const string inputTemplate = @" 140 | header 141 | {{ 142 | CartridgeName = {0} 143 | }} 144 | 145 | vectors 146 | {{ 147 | 148 | }} 149 | 150 | procedure Main 151 | {{ 152 | sei 153 | clc 154 | xce 155 | }} 156 | "; 157 | string input = String.Format(inputTemplate, invalidCartridgeName); 158 | 159 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 160 | Assert.Throws(() => driver.Parse()); 161 | } 162 | 163 | [Theory] 164 | [InlineData("HelloWorld")] 165 | [InlineData("\"Hi\"")] 166 | public void SramSizeExceptsANumberiteral(string invalidSramSize) 167 | { 168 | const string inputTemplate = @" 169 | header 170 | {{ 171 | SramSize = {0} 172 | }} 173 | 174 | vectors 175 | {{ 176 | 177 | }} 178 | 179 | procedure Main 180 | {{ 181 | sei 182 | clc 183 | xce 184 | }} 185 | "; 186 | string input = String.Format(inputTemplate, invalidSramSize); 187 | 188 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 189 | Assert.Throws(() => driver.Parse()); 190 | } 191 | 192 | [Theory] 193 | [InlineData("HelloWorld")] 194 | [InlineData("\"Hi\"")] 195 | public void DeveloperExceptsANumberiteral(string invalidDeveloper) 196 | { 197 | const string inputTemplate = @" 198 | header 199 | {{ 200 | Developer = {0} 201 | }} 202 | 203 | vectors 204 | {{ 205 | 206 | }} 207 | 208 | procedure Main 209 | {{ 210 | sei 211 | clc 212 | xce 213 | }} 214 | "; 215 | string input = String.Format(inputTemplate, invalidDeveloper); 216 | 217 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 218 | Assert.Throws(() => driver.Parse()); 219 | } 220 | 221 | [Theory] 222 | [InlineData("HelloWorld")] 223 | [InlineData("\"Hi\"")] 224 | public void VersionExceptsANumberiteral(string invalidVersion) 225 | { 226 | const string inputTemplate = @" 227 | header 228 | {{ 229 | Version = {0} 230 | }} 231 | 232 | vectors 233 | {{ 234 | 235 | }} 236 | 237 | procedure Main 238 | {{ 239 | sei 240 | clc 241 | xce 242 | }} 243 | "; 244 | string input = String.Format(inputTemplate, invalidVersion); 245 | 246 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 247 | Assert.Throws(() => driver.Parse()); 248 | } 249 | 250 | [Fact] 251 | public void VectorsIsRequired() 252 | { 253 | const string input = @" 254 | header 255 | { 256 | CartridgeName = ""HELLO WORLD SNES"" 257 | RomSpeed = SlowROM 258 | MapMode = LoROM 259 | SramSize = 0 260 | Country = Japan 261 | Developer = 0 262 | Version = 0 263 | } 264 | 265 | procedure Main 266 | { 267 | sei 268 | clc 269 | xce 270 | } 271 | " 272 | ; 273 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 274 | driver.Parse(); 275 | Assert.Throws(() => driver.SecondPass()); 276 | Assert.Equal(1, driver.Errors.Count); 277 | } 278 | 279 | [Fact] 280 | public void ShouldFailOnInvalidVectorsEntryName() 281 | { 282 | const string input = @" 283 | vectors 284 | { 285 | NormalNMI = Main 286 | } 287 | 288 | procedure Main 289 | { 290 | sei 291 | clc 292 | xce 293 | } 294 | " 295 | ; 296 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 297 | Assert.Throws(() => driver.Parse()); 298 | } 299 | 300 | [Fact] 301 | public void VectorsShouldBeFullyPopulated() 302 | { 303 | const string input = @" 304 | vectors 305 | { 306 | Reset = Main 307 | } 308 | 309 | procedure Main 310 | { 311 | sei 312 | clc 313 | xce 314 | } 315 | " 316 | ; 317 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 318 | Assert.Throws(() => driver.Parse()); 319 | } 320 | 321 | [Fact] 322 | public void ShouldFailOnInvalidVectorsLabel() 323 | { 324 | const string input = @" 325 | vectors 326 | { 327 | BRK = EmptyVector 328 | NMI = EmptyVector 329 | IRQ = EmptyVector 330 | Reset = NotValidReset 331 | } 332 | 333 | procedure Main 334 | { 335 | sei 336 | clc 337 | xce 338 | } 339 | 340 | interrupt EmptyVector 341 | { 342 | } 343 | "; 344 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 345 | driver.Parse(); 346 | Assert.Throws(() => driver.SecondPass()); 347 | } 348 | 349 | [Fact] 350 | public void ShouldFailOnInvalidInstructionLabel() 351 | { 352 | const string input = @" 353 | vectors 354 | { 355 | BRK = EmptyVector 356 | NMI = EmptyVector 357 | IRQ = EmptyVector 358 | Reset = Main 359 | } 360 | 361 | procedure Main 362 | { 363 | sei 364 | clc 365 | xce 366 | mainLoop: 367 | jmp infinite 368 | } 369 | 370 | interrupt EmptyVector 371 | { 372 | } 373 | "; 374 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 375 | driver.Parse(); 376 | Assert.Throws(() => driver.SecondPass()); 377 | } 378 | 379 | [Fact] 380 | public void ImpliedInstructionsShouldNotHaveArguments() 381 | { 382 | const string input = @" 383 | vectors 384 | { 385 | BRK = EmptyVector 386 | NMI = EmptyVector 387 | IRQ = EmptyVector 388 | Reset = Main 389 | } 390 | 391 | procedure Main 392 | { 393 | sei #$1A 394 | } 395 | 396 | interrupt EmptyVector 397 | { 398 | } 399 | "; 400 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 401 | Assert.Throws(() => driver.Parse()); 402 | } 403 | 404 | [Fact] 405 | public void InstructionsShouldHaveArguments() 406 | { 407 | const string input = @" 408 | vectors 409 | { 410 | BRK = EmptyVector 411 | NMI = EmptyVector 412 | IRQ = EmptyVector 413 | Reset = Main 414 | } 415 | 416 | procedure Main 417 | { 418 | lda 419 | } 420 | 421 | interrupt EmptyVector 422 | { 423 | } 424 | "; 425 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 426 | Assert.Throws(() => driver.Parse()); 427 | } 428 | 429 | [Theory] 430 | [InlineData("stz $7E2345")] 431 | public void CheckInvalidAddressingMode(string instruction) 432 | { 433 | const string inputTemplate = @" 434 | vectors 435 | {{ 436 | BRK = EmptyVector 437 | NMI = EmptyVector 438 | IRQ = EmptyVector 439 | Reset = Main 440 | }} 441 | 442 | procedure Main 443 | {{ 444 | {0} 445 | }} 446 | 447 | interrupt EmptyVector 448 | {{ 449 | }} 450 | "; 451 | string input = String.Format(inputTemplate, instruction); 452 | 453 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 454 | Assert.Throws(() => driver.Parse()); 455 | } 456 | 457 | [Fact] 458 | public void ShouldFailOnBranchTooLong() 459 | { 460 | const string input = @" 461 | vectors 462 | { 463 | BRK = EmptyVector 464 | NMI = EmptyVector 465 | IRQ = EmptyVector 466 | Reset = Main 467 | } 468 | 469 | procedure Main 470 | { 471 | bra forwardLabel 472 | backwardLabel: 473 | sta $7E0000 474 | sta $7E0000 475 | sta $7E0000 476 | sta $7E0000 477 | sta $7E0000 478 | sta $7E0000 479 | sta $7E0000 480 | sta $7E0000 481 | sta $7E0000 482 | sta $7E0000 483 | sta $7E0000 484 | sta $7E0000 485 | sta $7E0000 486 | sta $7E0000 487 | sta $7E0000 488 | sta $7E0000 489 | sta $7E0000 490 | sta $7E0000 491 | sta $7E0000 492 | sta $7E0000 493 | sta $7E0000 494 | sta $7E0000 495 | sta $7E0000 496 | sta $7E0000 497 | sta $7E0000 498 | sta $7E0000 499 | sta $7E0000 500 | sta $7E0000 501 | sta $7E0000 502 | sta $7E0000 503 | sta $7E0000 504 | sta $7E0000 505 | sta $7E0000 506 | sta $7E0000 507 | sta $7E0000 508 | sta $7E0000 509 | sta $7E0000 510 | sta $7E0000 511 | sta $7E0000 512 | forwardLabel: 513 | bra backwardLabel 514 | } 515 | 516 | interrupt EmptyVector 517 | { 518 | } 519 | "; 520 | 521 | ZealCpuDriver driver = new ZealCpuDriver(input.ToMemoryStream()); 522 | Assert.Throws(() => driver.Parse()); 523 | Assert.Equal(2, driver.Errors.Count); 524 | } 525 | } 526 | } 527 | -------------------------------------------------------------------------------- /ZealCompilerUnitTests/ZealCompilerUnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {6E1C4D2E-A3BC-4D42-A00B-0937AAB939AB} 9 | Library 10 | Properties 11 | Zeal.Compiler.UnitTests 12 | ZealCompilerUnitTests 13 | v4.5.2 14 | 512 15 | 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll 46 | True 47 | 48 | 49 | ..\packages\xunit.assert.2.1.0\lib\portable-net45+win8+wp8+wpa81\xunit.assert.dll 50 | True 51 | 52 | 53 | ..\packages\xunit.extensibility.core.2.1.0\lib\portable-net45+win8+wp8+wpa81\xunit.core.dll 54 | True 55 | 56 | 57 | ..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll 58 | True 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | {58dc5b8a-bd9e-4f08-a898-14331df4fa9b} 75 | LibZealCompiler 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 85 | 86 | 87 | 88 | 95 | -------------------------------------------------------------------------------- /ZealCompilerUnitTests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | --------------------------------------------------------------------------------