summaryrefslogtreecommitdiff
path: root/Q1.core/Arch/Chip.cs
blob: 69a087773d7a3f343e8fdadf386b78af8170966f (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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
namespace Q1.core.Arch;

// Big endian
public class Chip
{
    public IAddressable Bus;
    public u16 Pc, Sp;
    public u16 Ax, Dx, Lx;
    
    private u16[] _registers;

    public void Reset()
    {
        this._registers = new u16[64];
        this.Pc = ChipLayout.PROGRAM_START;
        this.Sp = ChipLayout.STACK_START;
    }

    public void Clock()
    {
        this.Bus.Clock();
        
        this.Fetch(out u8 opcode, out u8 m1, out u8 m2, out bool word);

        switch (m1, m2)
        {
            case (0x00, 0x00):
                InstructionSet.Lookup[opcode].Implicit.Execute(this, word);
                break;
            
            case (_, 0x00):
                InstructionSet.Lookup[opcode].SimpleAddressing.Execute(this, m1, word);
                break;
            
            case (0x00, 0x01):
                InstructionSet.Lookup[opcode].ExtendedImplicit.Execute(this, word);
                break;
            
            case (_, _):
                InstructionSet.Lookup[opcode].ExtendedAddressing.Execute(this, m1, m2, word);
                break;
        }
    }
    
    /// [opcode: 7 bits] [word: 1 bits] [m1: 4 bits] [m2: 4 bits]
    public void Fetch(out u8 opcode, out u8 m1, out u8 m2, out bool word)
    { 
        u16 instruction = this.Bus.ReadWord(this.Pc);
        this.Pc += 2;

        opcode = (u8) ((instruction >> 8) & 0xFE); // 128 opcodes
        word = ((instruction >> 8) & 0x1) != 0; // flag for word mode
        
        m1 = (u8) ((instruction >> 4) & 0x0F); // 16 modes
        m2 = (u8) (instruction        & 0x0F); // 16 modes
    }

    public void SkipInstruction()
    {
        this.Fetch(out u8 _, out u8 m1, out u8 m2, out bool word);
        this.Pc += (u8)(Size(m1, word) + Size(m2, word));
    }

    public u16 Load(u8 mode, bool word)
    {
        AddressingMode am = AddressingModes.Lookup[mode];
        return word ? am.LoadWord(this) : am.LoadByte(this);
    }
    
    public void Store(u8 mode, u16 value, bool word)
    {
        AddressingMode am = AddressingModes.Lookup[mode];
        if (word) am.StoreWord(this, value);
        else am.StoreByte(this, (u8)(value & 0xFF));
    }
    
    public u8 Size(u8 mode, bool word)
    {
        AddressingMode am = AddressingModes.Lookup[mode];
        return word ? am.SizeW : am.SizeB;
    }
    
    public void Push(u16 value)
    {
        if (this.Sp + 2 > ChipLayout.STACK_START + ChipLayout.STACK_SIZE) 
            throw new InvalidOperationException("Stack overflow");
        
        this.Bus.WriteWord(this.Sp, value);

        this.Sp += 2;
    }
    
    public u16 Pop()
    {
        if (this.Sp - 2 < ChipLayout.STACK_START) 
            throw new InvalidOperationException("Stack underflow");
        
        this.Sp -= 2;
        u16 value = this.Bus.ReadWord(this.Sp);
        
        return value;
    }
    
    public u16 GetRegister(u8 index)
    {
        if (index < 64) return this._registers[index];
        
        if (index == 0x40) return this.Ax;
        if (index == 0x41) return (u16) (this.Ax >> 8); // High byte of Ax
        if (index == 0x42) return (u16) (this.Ax & 0xFF); // Low byte of Ax
        
        if (index == 0x44) return this.Dx;
        if (index == 0x45) return (u16) (this.Dx >> 8); // High byte of Dx
        if (index == 0x46) return (u16) (this.Dx & 0xFF); // Low byte of Dx
        
        if (index == 0x48) return this.Lx;
        
        if (index == 0x4C) return this.Pc;
        if (index == 0x4D) return this.Sp;
        
        throw new InvalidOperationException($"Invalid register index: {index}");
    }
    
    public void SetRegister(u8 index, u16 value)
    {
        if (index < 64) this._registers[index] = value;
        
        else if (index == 0x40) this.Ax = value;
        else if (index == 0x41) this.Ax = (u16)((this.Ax & 0x00FF) | ((value & 0xFF) << 8)); // Set high byte of Ax
        else if (index == 0x42) this.Ax = (u16)((this.Ax & 0xFF00) | (value & 0xFF)); // Set low byte of Ax
        
        else if (index == 0x44) this.Dx = value;
        else if (index == 0x45) this.Dx = (u16)((this.Dx & 0x00FF) | ((value & 0xFF) << 8)); // Set high byte of Dx
        else if (index == 0x46) this.Dx = (u16)((this.Dx & 0xFF00) | (value & 0xFF)); // Set low byte of Dx
        
        else if (index == 0x48) this.Lx = value;
        
        else if (index == 0x4C) this.Pc = value;
        else if (index == 0x4D) this.Sp = value;
        
        else throw new InvalidOperationException($"Invalid register index: {index}");
    }
    public void Interrupt(u16 code)
    {
        if (code >= ChipLayout.IVT_COUNT)
            throw new InvalidOperationException($"Invalid interrupt code: {code}");
        
        this.Push(this.Pc);
        u16 interruptVectorAddress = (u16) (ChipLayout.IVT_START + code * 2);
        this.Pc = this.Bus.ReadWord(interruptVectorAddress);
    }
}