feat(core/asm): add assembler token, operand, and statement primitives

This commit is contained in:
dualfroz committed 2026-08-24 12:32:10 +02:00
1 parent 8c516cccbf
commit 77e45f92f6
4 files changed
+212

No files matched your search

+16
View File
@@ -0,0 +1,16 @@
using System;
namespace Chip8.Core.Assembly;
public sealed class AsmException : Exception
{
public AsmException(string message, SourcePosition position)
: base("assembly error at " + position + ": " + message)
{
this.reason = message;
this.position = position;
}
public string reason { get; }
public SourcePosition position { get; }
}
+56
View File
@@ -0,0 +1,56 @@
using System.Collections.Generic;
namespace Chip8.Core.Assembly;
public enum OperandKind
{
Register,
RegisterRange,
Immediate,
IndexRegister,
IndexIndirect,
DelayTimer,
SoundTimer,
KeyReg,
FontReg,
BigFontReg,
BcdReg,
RplReg,
}
public sealed class Operand
{
Operand(OperandKind kind, SourcePosition position)
{
this.kind = kind;
this.position = position;
}
public OperandKind kind { get; }
public SourcePosition position { get; }
public int registerIndex { get; private init; }
public int rangeEnd { get; private init; }
public IReadOnlyList<Token> expressionTokens { get; private init; } = System.Array.Empty<Token>();
public static Operand register(int index, SourcePosition position) =>
new(OperandKind.Register, position) { registerIndex = index };
public static Operand registerRange(int start, int end, SourcePosition position) =>
new(OperandKind.RegisterRange, position) { registerIndex = start, rangeEnd = end };
public static Operand immediate(IReadOnlyList<Token> tokens, SourcePosition position) =>
new(OperandKind.Immediate, position) { expressionTokens = tokens };
public static Operand special(OperandKind kind, SourcePosition position) => new(kind, position);
public bool isRegister(int index) => kind == OperandKind.Register && registerIndex == index;
public override string ToString() => kind switch
{
OperandKind.Register => "V" + registerIndex.ToString("X1"),
OperandKind.RegisterRange => "V" + registerIndex.ToString("X1") + "-V" + rangeEnd.ToString("X1"),
OperandKind.Immediate => "imm",
OperandKind.IndexIndirect => "[I]",
_ => kind.ToString(),
};
}
+79
View File
@@ -0,0 +1,79 @@
using System.Collections.Generic;
namespace Chip8.Core.Assembly;
public abstract class Statement
{
protected Statement(SourcePosition position)
{
this.position = position;
}
public SourcePosition position { get; }
}
public sealed class LabelStatement : Statement
{
public LabelStatement(string name, SourcePosition position) : base(position)
{
this.name = name;
}
public string name { get; }
}
public sealed class ConstantStatement : Statement
{
public ConstantStatement(string name, IReadOnlyList<Token> expression, SourcePosition position) : base(position)
{
this.name = name;
this.expression = expression;
}
public string name { get; }
public IReadOnlyList<Token> expression { get; }
}
public sealed class OrgStatement : Statement
{
public OrgStatement(IReadOnlyList<Token> expression, SourcePosition position) : base(position)
{
this.expression = expression;
}
public IReadOnlyList<Token> expression { get; }
}
public sealed class AlignStatement : Statement
{
public AlignStatement(IReadOnlyList<Token> expression, SourcePosition position) : base(position)
{
this.expression = expression;
}
public IReadOnlyList<Token> expression { get; }
}
public sealed class DataStatement : Statement
{
public DataStatement(bool word, IReadOnlyList<IReadOnlyList<Token>> items, SourcePosition position) : base(position)
{
this.word = word;
this.items = items;
}
public bool word { get; }
public IReadOnlyList<IReadOnlyList<Token>> items { get; }
}
public sealed class InstructionStatement : Statement
{
public InstructionStatement(string mnemonic, IReadOnlyList<Operand> operands, SourcePosition position) : base(position)
{
this.mnemonic = mnemonic;
this.operands = operands;
}
public string mnemonic { get; }
public IReadOnlyList<Operand> operands { get; }
}
+61
View File
@@ -0,0 +1,61 @@
namespace Chip8.Core.Assembly;
public enum TokenKind
{
Identifier,
Number,
Comma,
Colon,
Minus,
Plus,
Star,
Slash,
Percent,
Ampersand,
Pipe,
Caret,
Tilde,
ShiftLeft,
ShiftRight,
LeftParen,
RightParen,
LeftBracket,
RightBracket,
EndOfLine,
EndOfFile,
}
public readonly struct SourcePosition
{
public SourcePosition(int line, int column)
{
this.line = line;
this.column = column;
}
public int line { get; }
public int column { get; }
public override string ToString() => "line " + line + ", col " + column;
}
public readonly struct Token
{
public Token(TokenKind kind, string text, int value, SourcePosition position)
{
this.kind = kind;
this.text = text;
this.value = value;
this.position = position;
}
public TokenKind kind { get; }
public string text { get; }
public int value { get; }
public SourcePosition position { get; }
public bool isIdentifier(string name) =>
kind == TokenKind.Identifier && string.Equals(text, name, System.StringComparison.OrdinalIgnoreCase);
public override string ToString() => kind + "('" + text + "')";
}