summaryrefslogtreecommitdiff
path: root/Assembler/Assembler.cs
blob: 14a33fdef21e2df665ecdde8836c6714923ab536 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
namespace Q1Emu.Assembler;

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

public class Assembler
{
    public readonly BinaryWriter Writer;
    public readonly StringReader Reader;

    public Assembler(StringReader reader, BinaryWriter writer)
    {
        this.Reader = reader;
        this.Writer = writer;
    }

    public void Assemble()
    {
        while (this.Reader.ReadLine() is { } line)
        {
            string trimmedLine = line.Trim();
            if (string.IsNullOrEmpty(trimmedLine) || trimmedLine.StartsWith(';'))
                continue;

            this.ParseInstruction(trimmedLine);
        }
    }

    private void ParseInstruction(string instruction)
    {
        string[] parts = instruction.Split(' ', 2);
        string opcode = parts[0].ToUpperInvariant();

        parts = parts.Length > 1 ? parts[1].Split(',') : [];
        if (parts.Length > 2)
            throw new ArgumentException($"Invalid instruction format: {instruction}");

        u8 m1 = 0, m2 = 0;
        if (parts.Length > 0)
        {
            m1 = this.ParseOperand(parts[0]);
            if (parts.Length > 1)
                m2 = this.ParseOperand(parts[1]);
        }

        switch (opcode)
        {
            case "NOP":
                this.Writer.Write((u8) 0x00);
                break;

            default:
                throw new NotSupportedException($"Unsupported opcode: {opcode}");
        }
    }

    private u8 ParseOperand(string operand)
    {
        operand = operand.Trim();

        bool IsRegister(string text)
        {
            if (text.Length > 1 && text[0] == 'V')
                return true;

            string[] hardcodedRegisters =
            [
                "AX",
                "AH",
                "AL",
                "DX",
                "DH",
                "DL",
                "LX",
                "PC",
                "SP",
            ];

            return hardcodedRegisters.Contains(text);
        }

        if (operand.StartsWith("@"))
        {
            return 0x03; // Indirect
        }

        if (operand.StartsWith("[") && operand.EndsWith("]"))
        {
            string inner = operand[1..^1].Trim();
            if (inner.Contains("+"))
                return 0x07; // Register indirect with offset
            
            if (IsRegister(inner))
                return 0x06; // Register indirect

            return 0x02; // Direct
        }

        if (IsRegister(operand))
            return 0x05; // Register

        return 0x01; // Immediate
    }
}