From 33e1c5fc6e37549b4eba833ae7e534f9da250b10 Mon Sep 17 00:00:00 2001 From: Melodi <62381002+Melodi17@users.noreply.github.com> Date: Thu, 7 Aug 2025 00:02:00 +1000 Subject: added binary, logical and bitwise operators to compiler --- Assembler/Assembler.cs | 2 +- Compiler/CCompilerVisitor.cs | 208 ++++++++++++++++++++++++++++++------------- Emulator/Chip/Q1Cpu.cs | 8 +- Emulator/Program.cs | 16 ++-- ROMs/hello_c.asm | 11 +-- ROMs/hello_c.c | 2 +- ROMs/hello_c.q1 | Bin 22 -> 22 bytes 7 files changed, 163 insertions(+), 84 deletions(-) diff --git a/Assembler/Assembler.cs b/Assembler/Assembler.cs index 7c172e7..d2f79ed 100644 --- a/Assembler/Assembler.cs +++ b/Assembler/Assembler.cs @@ -29,7 +29,7 @@ public class Assembler private void ResolveLabels() { - int offset = 0; + int offset = Q1Cpu.ProgramStart; int pos = 0; foreach (string line in this.InputLines) { diff --git a/Compiler/CCompilerVisitor.cs b/Compiler/CCompilerVisitor.cs index cee69f4..7bbbeda 100644 --- a/Compiler/CCompilerVisitor.cs +++ b/Compiler/CCompilerVisitor.cs @@ -2,19 +2,37 @@ namespace Q1.Compiler; public class CCompilerVisitor : CGrammarBaseVisitor { - private List Instructions = new(); + private int _branchCount = 1; + private List _instructions = new(); + private string GetBranchLabel(string type) + { + return $"_{type}_{_branchCount++}"; + } + private void Instruction(string instruction) + { + this._instructions.Add($" {instruction}"); + } + private void Blank() + { + this._instructions.Add(""); + } + private void Label(string name) + { + this._instructions.Add($"{name}:"); + } + public IEnumerable Compile(CGrammarParser.ProgramContext program) { this.Visit(program); - return this.Instructions; + return this._instructions; } public override string? VisitFunction(CGrammarParser.FunctionContext context) { string functionName = context.ID().GetText(); - Instructions.Add($"_{functionName}:"); + this.Label($"_{functionName}"); this.VisitChildren(context); - this.Instructions.Add(""); + this.Blank(); return null; } @@ -29,14 +47,14 @@ public class CCompilerVisitor : CGrammarBaseVisitor string? expression = this.Visit(context.expression()); if (expression != null) { - this.Instructions.Add($"MOV {expression}, V0"); - Instructions.Add($"RET"); + this.Instruction($"mov {expression}, V0"); + this.Instruction($"ret"); } else throw new CompilerException("Return value was not an expression."); } else - Instructions.Add("RET"); + this.Instruction("ret"); return null; } @@ -55,7 +73,7 @@ public class CCompilerVisitor : CGrammarBaseVisitor if (value == null) throw new CompilerException("Value for negating was not an expression."); - this.Instructions.Add($"NOT {value}"); + this.Instruction($"not {value}"); return "LX"; } @@ -65,108 +83,170 @@ public class CCompilerVisitor : CGrammarBaseVisitor if (value == null) throw new CompilerException("Value for inversion was not an expression."); - this.Instructions.Add($"INV {value}"); + this.Instruction($"inv {value}"); return "DX"; } - public override string? VisitAddExpression(CGrammarParser.AddExpressionContext context) + private void BaseBinaryOperation(CGrammarParser.ExpressionContext leftExpr, CGrammarParser.ExpressionContext rightExpr, string operation, string operationFriendlyName) { - string? left = this.Visit(context.left); - string? right = this.Visit(context.right); + string? left = this.Visit(leftExpr); + if (left == null) + throw new CompilerException($"Left operand for {operationFriendlyName} was not an expression."); + + this.Instruction($"push {left}"); - if (left == null || right == null) - throw new CompilerException("Left or right operand for addition was not an expression."); + string? right = this.Visit(rightExpr); + if (right == null) + throw new CompilerException($"Right operand for {operationFriendlyName} was not an expression."); - this.Instructions.Add($"ADD {left}, {right}"); + this.Instruction($"mov {right}, V1"); + this.Instruction($"pop V0"); + this.Instruction($"{operation} V0, V1"); + } + public override string? VisitAddExpression(CGrammarParser.AddExpressionContext context) + { + BaseBinaryOperation(context.left, context.right, "add", "addition"); return "AX"; } public override string? VisitSubtractExpression(CGrammarParser.SubtractExpressionContext context) { - string? left = this.Visit(context.left); - string? right = this.Visit(context.right); - - if (left == null || right == null) - throw new CompilerException("Left or right operand for subtraction was not an expression."); - - this.Instructions.Add($"SUB {left}, {right}"); + BaseBinaryOperation(context.left, context.right, "sub", "subtraction"); return "AX"; } public override string? VisitMultiplyExpression(CGrammarParser.MultiplyExpressionContext context) { - string? left = this.Visit(context.left); - string? right = this.Visit(context.right); - - if (left == null || right == null) - throw new CompilerException("Left or right operand for multiplication was not an expression."); - - this.Instructions.Add($"MUL {left}, {right}"); + BaseBinaryOperation(context.left, context.right, "mul", "multiplication"); return "AX"; } public override string? VisitDivideExpression(CGrammarParser.DivideExpressionContext context) { - string? left = this.Visit(context.left); - string? right = this.Visit(context.right); - - if (left == null || right == null) - throw new CompilerException("Left or right operand for division was not an expression."); - - this.Instructions.Add($"DIV {left}, {right}"); + BaseBinaryOperation(context.left, context.right, "div", "division"); return "AX"; } public override string? VisitModulusExpression(CGrammarParser.ModulusExpressionContext context) { - string? left = this.Visit(context.left); - string? right = this.Visit(context.right); - - if (left == null || right == null) - throw new CompilerException("Left or right operand for modulus was not an expression."); - - this.Instructions.Add($"MOD {left}, {right}"); + BaseBinaryOperation(context.left, context.right, "mod", "modulus"); return "AX"; } - public override string? VisitBitwiseXorExpression(CGrammarParser.BitwiseXorExpressionContext context) - { - throw new NotImplementedException(); - } - public override string? VisitLessThanOrEqualExpression(CGrammarParser.LessThanOrEqualExpressionContext context) + public override string? VisitEqualExpression(CGrammarParser.EqualExpressionContext context) { - throw new NotImplementedException(); + BaseBinaryOperation(context.left, context.right, "cmp", "equality check"); + return "LX"; } + public override string? VisitNotEqualExpression(CGrammarParser.NotEqualExpressionContext context) { - throw new NotImplementedException(); + BaseBinaryOperation(context.left, context.right, "cmp", "inequality check"); + this.Instruction($"not"); // lx is implicit + return "LX"; } + public override string? VisitGreaterThanExpression(CGrammarParser.GreaterThanExpressionContext context) { - throw new NotImplementedException(); + BaseBinaryOperation(context.left, context.right, "gt", "greater than check"); + return "LX"; + } + + public override string? VisitLessThanExpression(CGrammarParser.LessThanExpressionContext context) + { + BaseBinaryOperation(context.left, context.right, "lt", "less than check"); + return "LX"; } + public override string? VisitLogicalOrExpression(CGrammarParser.LogicalOrExpressionContext context) { - throw new NotImplementedException(); + string? left = this.Visit(context.left); + if (left == null) + throw new CompilerException("Left operand for logical OR was not an expression."); + + string clauseBranch = this.GetBranchLabel("clause"); + string endBranch = this.GetBranchLabel("end"); + + this.Instruction($"cmp {left}, 0"); + this.Instruction("bz"); // implicit LX + this.Instruction($"jmp {clauseBranch}"); // only if bz is not 0 + this.Instruction("mov 1, V0"); + this.Instruction($"jmp {endBranch}"); + this.Label(clauseBranch); + + string? right = this.Visit(context.right); + if (right == null) + throw new CompilerException("Right operand for logical OR was not an expression."); + + this.Instruction($"cmp {right}, 0"); + this.Instruction("not"); + this.Instruction("mov LX, V0"); + this.Label(endBranch); + + return "V0"; } - public override string? VisitGreaterThanOrEqualExpression(CGrammarParser.GreaterThanOrEqualExpressionContext context) + public override string? VisitLogicalAndExpression(CGrammarParser.LogicalAndExpressionContext context) { - throw new NotImplementedException(); + string? left = this.Visit(context.left); + if (left == null) + throw new CompilerException("Left operand for logical OR was not an expression."); + + string clauseBranch = this.GetBranchLabel("clause"); + string endBranch = this.GetBranchLabel("end"); + + this.Instruction($"cmp {left}, 0"); + this.Instruction("not"); + this.Instruction("bz"); // implicit LX + this.Instruction($"jmp {clauseBranch}"); // only if bz is not 0 + this.Instruction("mov 0, V0"); + this.Instruction($"jmp {endBranch}"); + this.Label(clauseBranch); + + string? right = this.Visit(context.right); + if (right == null) + throw new CompilerException("Right operand for logical OR was not an expression."); + + this.Instruction($"cmp {right}, 0"); + this.Instruction("not"); + this.Instruction("mov LX, V0"); + this.Label(endBranch); + + return "V0"; } - public override string? VisitBitwiseOrExpression(CGrammarParser.BitwiseOrExpressionContext context) + + public override string? VisitGreaterThanOrEqualExpression(CGrammarParser.GreaterThanOrEqualExpressionContext context) { - throw new NotImplementedException(); + BaseBinaryOperation(context.left, context.right, "cmp", "equality check"); + string endBranch = this.GetBranchLabel("end"); + + this.Instruction("bz"); + this.Instruction($"jmp {endBranch}"); + this.Instruction($"gt V0, V1"); + this.Label(endBranch); + + return "LX"; } - public override string? VisitBitwiseAndExpression(CGrammarParser.BitwiseAndExpressionContext context) + public override string? VisitLessThanOrEqualExpression(CGrammarParser.LessThanOrEqualExpressionContext context) { - throw new NotImplementedException(); + BaseBinaryOperation(context.left, context.right, "cmp", "equality check"); + string endBranch = this.GetBranchLabel("end"); + + this.Instruction("bz"); + this.Instruction($"jmp {endBranch}"); + this.Instruction($"lt V0, V1"); + this.Label(endBranch); + + return "LX"; } - public override string? VisitLessThanExpression(CGrammarParser.LessThanExpressionContext context) + public override string? VisitBitwiseXorExpression(CGrammarParser.BitwiseXorExpressionContext context) { - throw new NotImplementedException(); + this.BaseBinaryOperation(context.left, context.right, "xor", "exclusive or"); + return "DX"; } - public override string? VisitLogicalAndExpression(CGrammarParser.LogicalAndExpressionContext context) + public override string? VisitBitwiseOrExpression(CGrammarParser.BitwiseOrExpressionContext context) { - throw new NotImplementedException(); + this.BaseBinaryOperation(context.left, context.right, "or", "or"); + return "DX"; } - public override string? VisitEqualExpression(CGrammarParser.EqualExpressionContext context) + public override string? VisitBitwiseAndExpression(CGrammarParser.BitwiseAndExpressionContext context) { - throw new NotImplementedException(); + this.BaseBinaryOperation(context.left, context.right, "and", "and"); + return "DX"; } } \ No newline at end of file diff --git a/Emulator/Chip/Q1Cpu.cs b/Emulator/Chip/Q1Cpu.cs index 16e1e14..d1fcf7a 100644 --- a/Emulator/Chip/Q1Cpu.cs +++ b/Emulator/Chip/Q1Cpu.cs @@ -5,8 +5,9 @@ using Assembler; public partial class Q1Cpu { - public const u16 StackStart = 0x2FFF; - public const u16 StackLength = 16 * 2; // 16 16-bit values + public const u16 StackStart = 0x2FFF; + public const u16 StackLength = 16 * 2; // 16 16-bit values + public const u16 ProgramStart = 0x1FFF; public Random Random; @@ -18,7 +19,6 @@ public partial class Q1Cpu public u16 PC { get; set; } // Program counter public u16[] V { get; set; } // Variable registers (64 total) - public u16 I { get; set; } // Index register public u16 Cycles { get; set; } // Number of cycles public Bus Bus { get; set; } @@ -38,7 +38,7 @@ public partial class Q1Cpu this.SP = Q1Cpu.StackStart; this.PushStack(0xFFFF); - this.I = 0; + this.PC = Q1Cpu.ProgramStart; } public void Clock() diff --git a/Emulator/Program.cs b/Emulator/Program.cs index cd6a8c0..3104fa9 100644 --- a/Emulator/Program.cs +++ b/Emulator/Program.cs @@ -20,9 +20,7 @@ internal class Program Bus bus = new(u16.MinValue, u16.MaxValue); RamDevice ram = new(0x1FFF, 1024 * 8); File.ReadAllBytes(options.InputFile).CopyTo(ram.Memory, 0); - - cpu.PC = ram.AddressableStart; - + DisplayDevice display = new(16, 64, 32, 0x3FFF); display.SetPalette([ Color.Red, @@ -55,13 +53,13 @@ internal class Program cpu.Reset(); while (!cpu.AbortRequested) { - for (int i = 0; i < 64; i++) - cpu.Clock(); - - Thread.Sleep(1); + // for (int i = 0; i < 64; i++) + // cpu.Clock(); + // + // Thread.Sleep(1); // - // cpu.Clock(); - // Thread.Sleep(1000); + cpu.Clock(); + Thread.Sleep(1000); } Console.WriteLine($"Exit code: 0x{cpu.V[0]:X4}"); }); diff --git a/ROMs/hello_c.asm b/ROMs/hello_c.asm index 292b99a..8cdf58b 100644 --- a/ROMs/hello_c.asm +++ b/ROMs/hello_c.asm @@ -1,7 +1,8 @@ _main: -ADD 1, 2 -SUB 4, 1 -MUL AX, AX -MOV AX, V0 -RET + push 0 + mov 1, V1 + pop V0 + or V0, V1 + mov DX, V0 + ret diff --git a/ROMs/hello_c.c b/ROMs/hello_c.c index 46eab8d..1f79a53 100644 --- a/ROMs/hello_c.c +++ b/ROMs/hello_c.c @@ -1,4 +1,4 @@ int main() { - return (1 + 2) * (4 - 1); + return 0 | 1; } \ No newline at end of file diff --git a/ROMs/hello_c.q1 b/ROMs/hello_c.q1 index deffc80..5e4d71e 100644 Binary files a/ROMs/hello_c.q1 and b/ROMs/hello_c.q1 differ -- cgit v1.3.1