aboutsummaryrefslogtreecommitdiff
path: root/pu.c
blob: 4e93a31cc9d749178a8d16e1ec377787a75502e8 (plain) (blame)
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
153
154
155
156
157
158
159
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#pragma clang diagnostic ignored "-Wreturn-type"
#pragma clang diagnostic ignored "-Wc23-extensions"

// clang-format off
#define S 65536
#define H !--c?
#define J while
m[S],c,a;pu(){J((c=m[m[7]++]))H*m=m[m[7]++]:c--<9?!a?m[c]=*m:0:8>(c-=8)?*m=m[c]:!(c-=8)?m[m[1]]=*m:H*m=a:H a+=*m:H a-=*m:H a=!a:H a=0:0;}
// clang-format on

// a is acc
// first 8 memory bytes are registers actually
// c is pc, read from m[7]

// --- opcodes ---
// 0 = halt DONE
// 1 = const in r0 DONE
// 2...9 = r0 in rX (only if acc is zero) DONE
// 10...17 = rX in r0 DONE
// 18 = r0 in m[r1] DONE
// 19 = acc in r0 DONE
// 20 = acc += r0 DONE
// 21 = acc -= r0 DONE
// 22 = acc = !acc DONE
// 23 = acc = 0 DONE
// for ops 2..9 and 10..17 it basically works like this: 2 handles r0, 3 r1, 4 r2, etc. 10 handles
// r0, 11 r1, 12 r2 and so on.

// compiler!
// the switch skips whitespace. then, the beautiful loop thingymajigy emits bytecode.

// clang-format off
#define W case
#define Q goto M;
char R[]={
#embed"t"
};K;char*as(char*I,int*O){char*V=R;J((K=*I)){V=R;switch(K){W 32:W 10:W 9:I++;Q;W 35:J(*++I!=10);Q}strncmp("con ",I,4)?:({*O++=1,*O++=atoi(I+=4);J(*I>47&*I<58)I++;Q});J(*V)V+=3,(strncmp(V-3,I,3)?:({*O++=*V;I+=3;Q})),V+=2;return I;M:}return 0;}
// clang-format on

#undef Q
#undef H
#undef W
#undef S
#undef J

#ifndef __WRAPPER

// runs pu until exit code != 2,
// that exit code is an interrupt for talking to the emulated hardware
int emu() {
    while (1) {
        pu();
        int e_code = m[ 0 ];

        if (e_code != 2) return e_code;

        // handle hardware interrupts
        int int_code = m[ 1 ];

        // 0: output string
        if (int_code == 0) {
            int ptr  = m[ 2 ];
            int size = m[ 3 ], i = 0;

            for (; i < size; i++) putchar(m[ ptr++ ]);
            write(STDOUT_FILENO, &m[ ptr ], size);
        }

        // 1: input character
        else if (int_code == 1) {
            m[ 2 ] = getchar();
        }

        else {
            // unknown interrupt. what to do?
        }

        m[ 0 ] = 0;
        m[ 7 ]++;
    }
}

char R_backup[] = "hlt\0 ar1\2 ar2\3 ar3\4 ar4\5 ar5\6 ar6\7 ar7\x8 ar8\x9 r1a\xA r2a\xB r3a\xC "
                  "r4a\xD r5a\xE r6a\xF r7a\20 r8a\21 mem\22 aca\23 add\24 sub\25 not\26 clr\27";

int main(int argc, char **argv) {
    if (access("t", F_OK)) {
        // patch the instruction table incase you removed it.
        printf("you've deleted t! i'll write it again for you.\n\n");
        FILE *fp = fopen("t", "w+");
        fwrite(R_backup, 1, sizeof(R_backup), fp);
        fclose(fp);
        memcpy(R, R_backup, sizeof(R_backup));
    }

    if (argc == 1) {
        printf("Usage:\n    %s <filename>", argv[ 0 ]);
        exit(1);
    }

    // read code
    FILE *fp = fopen(argv[ 1 ], "r");
    fseek(fp, 0, SEEK_END);
    int sz = ftell(fp);
    rewind(fp);
    char code[ sz + 1 ];
    fread(code, 1, sz, fp);
    code[ sz ] = 0;
    fclose(fp);

    // set pc
    m[ 7 ] = 1024;
    // assemble
    char *fail_point = as(code, &m[ 1024 ]);

    if (fail_point != 0) {
        int error_pos = (int) (fail_point - code);
        int i;

        int line = 1, col = 1;
        for (i = 0; i < error_pos && i < sz; i++) {
            if (code[ i ] == '\n') {
                line++;
                col = 1;
            } else {
                col++;
            }
        }

        fprintf(
            stderr, "Assembly failed at line %d, column %d (position %d):\n", line, col, error_pos);

        int line_start = error_pos;
        while (line_start > 0 && code[ line_start - 1 ] != '\n') { line_start--; }

        int line_end = error_pos;
        while (line_end < sz && code[ line_end ] != '\n' && code[ line_end ] != '\0') {
            line_end++;
        }

        fprintf(stderr, "  ");
        for (i = line_start; i < line_end; i++) { fputc(code[ i ], stderr); }
        fprintf(stderr, "\n");

        fprintf(stderr, "  ");
        for (i = line_start; i < error_pos; i++) { fputc(' ', stderr); }
        fprintf(stderr, "^ parsing stopped here\n");

        exit(1);
    }

    // run emulation
    emu();
}
#endif