summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMelodi <62381002+Melodi17@users.noreply.github.com>2025-08-28 14:44:51 +1000
committerMelodi <62381002+Melodi17@users.noreply.github.com>2025-08-28 14:44:51 +1000
commitcf7e621c104cb4aea801392585d2dcc9335bc013 (patch)
treeee4aef401c399995968afe217d06557587707c51
parent7f96fc5e7f412a66d460dcaa82bbdbcaea08f360 (diff)
added preprocessor (wip)
-rw-r--r--Compiler/Preprocessor.cs59
-rw-r--r--Compiler/Program.cs24
-rw-r--r--Compiler/visitors/Main.cs2
-rw-r--r--Emulator/DisplayEngine.cs1
-rw-r--r--Q1.sln.DotSettings.user4
-rw-r--r--ROMs/hello_c.c2
-rw-r--r--ROMs/lib.c42
-rw-r--r--ROMs/test_lib.c7
-rw-r--r--ROMs/typewriter.asm387
-rw-r--r--ROMs/typewriter.c107
-rw-r--r--ROMs/typewriter.q1bin0 -> 3412 bytes
11 files changed, 632 insertions, 3 deletions
diff --git a/Compiler/Preprocessor.cs b/Compiler/Preprocessor.cs
new file mode 100644
index 0000000..01c14ab
--- /dev/null
+++ b/Compiler/Preprocessor.cs
@@ -0,0 +1,59 @@
+namespace Q1.Compiler;
+
+using System.Text.RegularExpressions;
+
+public static class Preprocessor
+{
+ private static readonly Dictionary<string, string> Defines = new();
+
+ public static string Process(string filePath)
+ {
+ List<string> lines = [];
+ ProcessFile(filePath, lines, []);
+ return string.Join(Environment.NewLine, lines);
+ }
+
+ private static void ProcessFile(string filePath, List<string> output, HashSet<string> includeGuard)
+ {
+ if (includeGuard.Contains(Path.GetFullPath(filePath)))
+ return; // prevent recursive includes
+
+ includeGuard.Add(Path.GetFullPath(filePath));
+
+ foreach (string rawLine in File.ReadAllLines(filePath))
+ {
+ string line = rawLine.Trim();
+
+ if (line.StartsWith("#include"))
+ {
+ // simple format: #include "file"
+ Match match = Regex.Match(line, "#include\\s+\"([^\"]+)\"");
+ if (match.Success)
+ {
+ string includePath = Path.Combine(Path.GetDirectoryName(filePath)!, match.Groups[1].Value);
+ ProcessFile(includePath, output, includeGuard);
+ }
+ continue;
+ }
+ else if (line.StartsWith("#define"))
+ {
+ // simple format: #define NAME VALUE
+ Match match = Regex.Match(line, "#define\\s+(\\w+)\\s+(.*)");
+ if (match.Success)
+ Defines[match.Groups[1].Value] = match.Groups[2].Value;
+ continue;
+ }
+ else if (line.StartsWith("#load"))
+ {
+ output.Add(line.Replace("#load", "#.include"));
+ continue;
+ }
+
+ // expand macros
+ foreach (KeyValuePair<string, string> kvp in Defines)
+ line = line.Replace(kvp.Key, kvp.Value);
+
+ output.Add(line);
+ }
+ }
+} \ No newline at end of file
diff --git a/Compiler/Program.cs b/Compiler/Program.cs
index 3a277a1..09f7127 100644
--- a/Compiler/Program.cs
+++ b/Compiler/Program.cs
@@ -2,6 +2,7 @@
using System.Text;
using Antlr4.Runtime;
+using Antlr4.Runtime.Misc;
using CommandLine;
using visitors;
using Parser = CommandLine.Parser;
@@ -26,7 +27,7 @@ public class Program
try
{
- AntlrFileStream inputStream = new(options.InputFile);
+ ICharStream inputStream = new PreprocessingCharStream(options.InputFile);
CGrammarLexer lexer = new(inputStream);
lexer.RemoveErrorListeners();
@@ -48,4 +49,25 @@ public class Program
Console.WriteLine($"{ex.Message}");
}
}
+}
+
+public class PreprocessingCharStream : ICharStream
+{
+ private readonly AntlrInputStream inner;
+
+ public PreprocessingCharStream(string filePath)
+ {
+ string preprocessed = Preprocessor.Process(filePath);
+ inner = new AntlrInputStream(preprocessed);
+ }
+
+ public int Index => inner.Index;
+ public int Size => inner.Size;
+ public string SourceName => inner.SourceName;
+ public void Consume() => inner.Consume();
+ public int LA(int i) => inner.LA(i);
+ public int Mark() => inner.Mark();
+ public void Release(int marker) => inner.Release(marker);
+ public void Seek(int index) => inner.Seek(index);
+ public string GetText(Interval interval) => inner.GetText(interval);
} \ No newline at end of file
diff --git a/Compiler/visitors/Main.cs b/Compiler/visitors/Main.cs
index cbd468f..3b32399 100644
--- a/Compiler/visitors/Main.cs
+++ b/Compiler/visitors/Main.cs
@@ -152,7 +152,7 @@ public partial class CCompilerVisitor : CGrammarBaseVisitor<object?>
.TrimEnd('\n')
.Trim();
- this.Instruction("." + directive);
+ this.Instruction(directive);
}
return base.VisitErrorNode(node);
diff --git a/Emulator/DisplayEngine.cs b/Emulator/DisplayEngine.cs
index 7aa8e2e..1e50809 100644
--- a/Emulator/DisplayEngine.cs
+++ b/Emulator/DisplayEngine.cs
@@ -77,6 +77,7 @@ public class DisplayEngine : Game
foreach (Keys newKey in keys.Except(this._oldKeys ?? []))
{
this._hid.SetKeyboard((u8) newKey, true);
+ Console.WriteLine((u8) newKey);
}
foreach (Keys oldKey in this._oldKeys?.Except(keys) ?? [])
diff --git a/Q1.sln.DotSettings.user b/Q1.sln.DotSettings.user
index 9d9941a..982881d 100644
--- a/Q1.sln.DotSettings.user
+++ b/Q1.sln.DotSettings.user
@@ -1,8 +1,12 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAbstractParseTreeVisitor_00601_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F09190d4d899c4e68827ef2ee0d8f96282ee00_003Fdb_003F1fe7d371_003FAbstractParseTreeVisitor_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABinaryWriter_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fb5ea9bfdffa3204580857642ef06a8c5c8bf9e1e0cba3035b23ebccbdf92_003FBinaryWriter_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
+ <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ABufferedTokenStream_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F09190d4d899c4e68827ef2ee0d8f96282ee00_003Fd9_003F3b2cdef9_003FBufferedTokenStream_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AConsoleColor_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc77a01c848704f55b83cd099c11adf1535800_003Fe5_003Fa0e095e5_003FConsoleColor_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Ahello_005Fc_002Easm_002Fl_003AROMs_003Fhello_005Fc_002Easm/@EntryIndexedValue">ExplicitlyExcluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003Ahello_005Fc_002Ec_002Fl_003AROMs_003Fhello_005Fc_002Ec/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AIParseTree_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F09190d4d899c4e68827ef2ee0d8f96282ee00_003F10_003F6709d45b_003FIParseTree_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
+ <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AITokenSource_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F09190d4d899c4e68827ef2ee0d8f96282ee00_003Feb_003Fbfeed1b2_003FITokenSource_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
+ <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AITokenStream_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F09190d4d899c4e68827ef2ee0d8f96282ee00_003F70_003Ffcaa3e03_003FITokenStream_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
+ <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALexer_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F09190d4d899c4e68827ef2ee0d8f96282ee00_003F1e_003F6f12b962_003FLexer_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AParserRuleContext_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F09190d4d899c4e68827ef2ee0d8f96282ee00_003Fa2_003F4aba81d4_003FParserRuleContext_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary> \ No newline at end of file
diff --git a/ROMs/hello_c.c b/ROMs/hello_c.c
index 9cf7a96..6be9385 100644
--- a/ROMs/hello_c.c
+++ b/ROMs/hello_c.c
@@ -1,4 +1,4 @@
-#include "ROMs/font.bin"
+#load "font.bin"
int drawChar(int ch, int x, int y, int color)
{
diff --git a/ROMs/lib.c b/ROMs/lib.c
new file mode 100644
index 0000000..32378c0
--- /dev/null
+++ b/ROMs/lib.c
@@ -0,0 +1,42 @@
+int setPixel(int x, int y, int c)
+{
+ char* screenStart = 0x402F;
+ int i = (y * 128) + x;
+ screenStart[i] = c;
+}
+
+int setPixel(int i, int c)
+{
+ char* screenStart = 0x402F;
+ screenStart[i] = c;
+}
+
+int clear(int c)
+{
+ char* screenStart = 0x402F;
+ char screenSize = 64 * 128;
+ for (int i = 0; i < screenSize; i++)
+ {
+ screenStart[i] = c;
+ }
+}
+
+int getKeyState(int scanCode)
+{
+ char* hid = 0x6033;
+ int index = scanCode / 8;
+ int bit = scanCode % 8;
+
+ int val = hid[index - 1];
+ return (val & (1 << bit)) != 0;
+}
+
+int getMouseIndex()
+{
+ return *(0x602F);
+}
+
+int getMouseState()
+{
+ return *(0x602F + 2);
+} \ No newline at end of file
diff --git a/ROMs/test_lib.c b/ROMs/test_lib.c
new file mode 100644
index 0000000..e779c2c
--- /dev/null
+++ b/ROMs/test_lib.c
@@ -0,0 +1,7 @@
+int main()
+{
+ while (1)
+ {
+
+ }
+} \ No newline at end of file
diff --git a/ROMs/typewriter.asm b/ROMs/typewriter.asm
new file mode 100644
index 0000000..df4e7ca
--- /dev/null
+++ b/ROMs/typewriter.asm
@@ -0,0 +1,387 @@
+ jmp _main
+ .include "font.bin"
+_setPixel_int_x__int_y__int_c:; int setPixel(int x, int y, int c)
+ pop V0 ; preserve function return address
+ pop [$71FF] ; param int x
+ pop [$7201] ; param int y
+ pop [$7203] ; param int c
+ push V0 ; restore function return address
+ mov $402F, [$7205] ; int screenStart = $402F
+ push [$7201] ; left operand
+ mov 128, V1 ; right operand
+ pop V0 ; get left operand back
+ mul V0, V1 ; compute multiplication
+ push AX ; left operand
+ mov [$71FF], V1 ; right operand
+ pop V0 ; get left operand back
+ add V0, V1 ; compute addition
+ mov AX, [$7207] ; int i = AX
+ push [$7205] ; store index obj
+ mov [$7207], V1 ; store indexer
+ pop V0 ; restore index obj
+ mul V1, 1 ; multiply indexer by 1
+ add AX, V0 ; add indexer and index obj
+ mov AX, V0 ; move to general purpose register
+ movb [$7203], [V0] ; [V0] = [$7203]
+ mov 0, V0 ; fallback return 0
+ ret
+
+_drawChar_int_ch__int_x__int_y__int_color:; int drawChar(int ch, int x, int y, int color)
+ pop V0 ; preserve function return address
+ pop [$7209] ; param int ch
+ pop [$720B] ; param int x
+ pop [$720D] ; param int y
+ pop [$720F] ; param int color
+ push V0 ; restore function return address
+ ; adjust for jmp instruction
+ push $1FFF ; left operand
+ mov 2, V1 ; right operand
+ pop V0 ; get left operand back
+ add V0, V1 ; compute addition
+ mov AX, [$7211] ; int fontStart = AX
+ push [$7209] ; left operand
+ mov 8, V1 ; right operand
+ pop V0 ; get left operand back
+ mul V0, V1 ; compute multiplication
+ mov AX, [$7213] ; int charStart = AX
+ mov 0, [$7215] ; int yOff = 0
+_body_1:
+ push [$7215] ; left operand
+ mov 8, V1 ; right operand
+ pop V0 ; get left operand back
+ lt V0, V1 ; compute less than check
+ not LX
+ bz
+ jmp _end_2 ; if for condition fails, jump to end
+ push [$7211] ; store index obj
+ push [$7213] ; left operand
+ mov [$7215], V1 ; right operand
+ pop V0 ; get left operand back
+ add V0, V1 ; compute addition
+ mov AX, V1 ; store indexer
+ pop V0 ; restore index obj
+ mul V1, 1 ; multiply indexer by 1
+ add AX, V0 ; add indexer and index obj
+ mov AX, V0 ; move to general purpose register
+ mov [V0], [$7217] ; int row = [V0]
+ mov 0, [$7219] ; int xOff = 0
+_body_3:
+ push [$7219] ; left operand
+ mov 8, V1 ; right operand
+ pop V0 ; get left operand back
+ lt V0, V1 ; compute less than check
+ not LX
+ bz
+ jmp _end_4 ; if for condition fails, jump to end
+ push [$7217] ; left operand
+ push 7 ; left operand
+ mov [$7219], V1 ; right operand
+ pop V0 ; get left operand back
+ sub V0, V1 ; compute subtraction
+ mov AX, V1 ; right operand
+ pop V0 ; get left operand back
+ shr V0, V1 ; compute right shift
+ push DX ; left operand
+ mov 1, V1 ; right operand
+ pop V0 ; get left operand back
+ and V0, V1 ; compute and
+ mov DX, [$721B] ; int bit = DX
+ bz [$721B] ; check ternary condition
+ jmp _then_5
+ mov [$720F], V0 ; store ternary fail result
+ jmp _end_6
+_then_5:
+ mov 8, V0 ; store ternary success result
+_end_6:
+ mov V0, [$721D] ; int pix = V0
+ push [$721D] ; arg int x
+ push [$720D] ; left operand
+ mov [$7215], V1 ; right operand
+ pop V0 ; get left operand back
+ add V0, V1 ; compute addition
+ push AX ; arg int y
+ push [$720B] ; left operand
+ mov [$7219], V1 ; right operand
+ pop V0 ; get left operand back
+ add V0, V1 ; compute addition
+ push AX ; arg int c
+ call _setPixel_int_x__int_y__int_c; int setPixel(int x, int y, int c)
+ push [$7219] ; i++
+ inc [$7219]
+ pop V0
+ jmp _body_3
+_end_4:
+ push [$7215] ; i++
+ inc [$7215]
+ pop V0
+ jmp _body_1
+_end_2:
+ mov 0, V0 ; fallback return 0
+ ret
+
+_clear_int_c: ; int clear(int c)
+ pop V0 ; preserve function return address
+ pop [$721F] ; param int c
+ push V0 ; restore function return address
+ mov $402F, [$7221] ; int screenStart = $402F
+ push 64 ; left operand
+ mov 128, V1 ; right operand
+ pop V0 ; get left operand back
+ mul V0, V1 ; compute multiplication
+ mov AX, [$7223] ; int screenSize = AX
+ mov 0, [$7225] ; int i = 0
+_body_7:
+ push [$7225] ; left operand
+ mov [$7223], V1 ; right operand
+ pop V0 ; get left operand back
+ lt V0, V1 ; compute less than check
+ not LX
+ bz
+ jmp _end_8 ; if for condition fails, jump to end
+ push [$7221] ; store index obj
+ mov [$7225], V1 ; store indexer
+ pop V0 ; restore index obj
+ mul V1, 1 ; multiply indexer by 1
+ add AX, V0 ; add indexer and index obj
+ mov AX, V0 ; move to general purpose register
+ movb [$721F], [V0] ; [V0] = [$721F]
+ push [$7225] ; i++
+ inc [$7225]
+ pop V0
+ jmp _body_7
+_end_8:
+ mov 0, V0 ; fallback return 0
+ ret
+
+_getKeyState_int_scanCode: ; int getKeyState(int scanCode)
+ pop V0 ; preserve function return address
+ pop [$7227] ; param int scanCode
+ push V0 ; restore function return address
+ mov $6033, [$7229] ; int hid = $6033
+ push [$7227] ; left operand
+ mov 8, V1 ; right operand
+ pop V0 ; get left operand back
+ div V0, V1 ; compute division
+ mov AX, [$722B] ; int index = AX
+ push [$7227] ; left operand
+ mov 8, V1 ; right operand
+ pop V0 ; get left operand back
+ mod V0, V1 ; compute modulus
+ mov AX, [$722D] ; int bit = AX
+ push [$7229] ; store index obj
+ push [$722B] ; left operand
+ mov 1, V1 ; right operand
+ pop V0 ; get left operand back
+ sub V0, V1 ; compute subtraction
+ mov AX, V1 ; store indexer
+ pop V0 ; restore index obj
+ mul V1, 1 ; multiply indexer by 1
+ add AX, V0 ; add indexer and index obj
+ mov AX, V0 ; move to general purpose register
+ mov [V0], [$722F] ; int val = [V0]
+ push [$722F] ; left operand
+ push 1 ; left operand
+ mov [$722D], V1 ; right operand
+ pop V0 ; get left operand back
+ shl V0, V1 ; compute left shift
+ mov DX, V1 ; right operand
+ pop V0 ; get left operand back
+ and V0, V1 ; compute and
+ push DX ; left operand
+ mov 0, V1 ; right operand
+ pop V0 ; get left operand back
+ cmp V0, V1 ; compute inequality check
+ not
+ mov LX, V0 ; return value
+ ret
+ mov 0, V0 ; fallback return 0
+ ret
+
+_print_int*_text__int_len__int_x__int_y__int_color:; int print(int* text, int len, int x, int y, int color)
+ pop V0 ; preserve function return address
+ pop [$7231] ; param int* text
+ pop [$7233] ; param int len
+ pop [$7235] ; param int x
+ pop [$7237] ; param int y
+ pop [$7239] ; param int color
+ push V0 ; restore function return address
+ mov 0, [$723B] ; int i = 0
+_body_9:
+ push [$723B] ; left operand
+ mov [$7233], V1 ; right operand
+ pop V0 ; get left operand back
+ lt V0, V1 ; compute less than check
+ not LX
+ bz
+ jmp _end_10 ; if for condition fails, jump to end
+ push [$7231] ; store index obj
+ mov [$723B], V1 ; store indexer
+ pop V0 ; restore index obj
+ mul V1, 2 ; multiply indexer by 2
+ add AX, V0 ; add indexer and index obj
+ mov AX, V0 ; move to general purpose register
+ mov [V0], [$723D] ; int ch = [V0]
+ push [$7239] ; arg int ch
+ push [$7237] ; arg int x
+ push [$7235] ; left operand
+ push [$723B] ; left operand
+ mov 8, V1 ; right operand
+ pop V0 ; get left operand back
+ mul V0, V1 ; compute multiplication
+ mov AX, V1 ; right operand
+ pop V0 ; get left operand back
+ add V0, V1 ; compute addition
+ push AX ; arg int y
+ push [$723D] ; arg int color
+ call _drawChar_int_ch__int_x__int_y__int_color; int drawChar(int ch, int x, int y, int color)
+ push [$723B] ; i++
+ inc [$723B]
+ pop V0
+ jmp _body_9
+_end_10:
+ mov 0, V0 ; fallback return 0
+ ret
+
+_main: ; int main()
+ mov 0, [$723F] ; int x = 0
+ mov 0, [$7241] ; int y = 0
+_body_11:
+ not 1
+ bz
+ jmp _end_12 ; if while condition fails, jump to end
+ mov 0, [$7243] ; int sk = 0
+_body_13:
+ push [$7243] ; left operand
+ mov 256, V1 ; right operand
+ pop V0 ; get left operand back
+ lt V0, V1 ; compute less than check
+ not LX
+ bz
+ jmp _end_14 ; if for condition fails, jump to end
+ push [$7243] ; arg int scanCode
+ call _getKeyState_int_scanCode; int getKeyState(int scanCode)
+ not V0
+ bz ; check if condition
+ jmp _end_15 ; if succeeds, continue, else jump to end
+_body_16:
+ push [$7243] ; arg int scanCode
+ call _getKeyState_int_scanCode; int getKeyState(int scanCode)
+ not V0
+ bz
+ jmp _end_17 ; if while condition fails, jump to end
+ jmp _body_16
+_end_17:
+ ; Newline pressed
+ push [$7243] ; left operand
+ mov 13, V1 ; right operand
+ pop V0 ; get left operand back
+ cmp V0, V1 ; compute equality check
+ not LX
+ bz ; check if condition
+ jmp _end_18 ; if succeeds, continue, else jump to end
+ push [$7241] ; left operand
+ mov 8, V1 ; right operand
+ pop V0 ; get left operand back
+ add V0, V1 ; compute addition
+ push AX ; computation results
+ pop V1 ; get computation results back
+ mov V1, [$7241] ; store back into left param
+ mov 0, [$723F] ; [$723F] = 0
+ jmp _body_13 ; continue
+_end_18:
+ ; Backspace pressed
+ push [$7243] ; left operand
+ mov 8, V1 ; right operand
+ pop V0 ; get left operand back
+ cmp V0, V1 ; compute equality check
+ not LX
+ bz ; check if condition
+ jmp _end_19 ; if succeeds, continue, else jump to end
+ push [$723F] ; left operand
+ mov 8, V1 ; right operand
+ pop V0 ; get left operand back
+ sub V0, V1 ; compute subtraction
+ push AX ; computation results
+ pop V1 ; get computation results back
+ mov V1, [$723F] ; store back into left param
+ push [$723F] ; left operand
+ mov 0, V1 ; right operand
+ pop V0 ; get left operand back
+ lt V0, V1 ; compute less than check
+ not LX
+ bz ; check if condition
+ jmp _end_20 ; if succeeds, continue, else jump to end
+ mov 0, [$723F] ; [$723F] = 0
+_end_20:
+ mov 32, [$7247]
+ mov $7247, [$7245] ; int text = $7247
+ push 1 ; arg int* text
+ push [$7241] ; arg int len
+ push [$723F] ; arg int x
+ push 1 ; arg int y
+ push [$7245] ; arg int color
+ call _print_int*_text__int_len__int_x__int_y__int_color; int print(int* text, int len, int x, int y, int color)
+ jmp _body_13 ; continue
+_end_19:
+ mov [$7243], [$724B]
+ mov $724B, [$7249] ; int text = $724B
+ push 1 ; arg int* text
+ push [$7241] ; arg int len
+ push [$723F] ; arg int x
+ push 1 ; arg int y
+ push [$7249] ; arg int color
+ call _print_int*_text__int_len__int_x__int_y__int_color; int print(int* text, int len, int x, int y, int color)
+ push [$723F] ; left operand
+ mov 8, V1 ; right operand
+ pop V0 ; get left operand back
+ add V0, V1 ; compute addition
+ push AX ; computation results
+ pop V1 ; get computation results back
+ mov V1, [$723F] ; store back into left param
+_end_15:
+ push [$7243] ; i++
+ inc [$7243]
+ pop V0
+ jmp _body_13
+_end_14:
+ push [$723F] ; left operand
+ mov 128, V1 ; right operand
+ pop V0 ; get left operand back
+ cmp V0, V1 ; compute equality check
+ bz
+ jmp _end_21
+ gt V0, V1
+_end_21:
+ not LX
+ bz ; check if condition
+ jmp _end_22 ; if succeeds, continue, else jump to end
+ mov 0, [$723F] ; [$723F] = 0
+ push [$7241] ; left operand
+ mov 8, V1 ; right operand
+ pop V0 ; get left operand back
+ add V0, V1 ; compute addition
+ push AX ; computation results
+ pop V1 ; get computation results back
+ mov V1, [$7241] ; store back into left param
+_end_22:
+ push [$7241] ; left operand
+ mov 64, V1 ; right operand
+ pop V0 ; get left operand back
+ cmp V0, V1 ; compute equality check
+ bz
+ jmp _end_23
+ gt V0, V1
+_end_23:
+ not LX
+ bz ; check if condition
+ jmp _end_24 ; if succeeds, continue, else jump to end
+ mov 0, [$723F] ; [$723F] = 0
+ mov 0, [$7241] ; [$7241] = 0
+ push 8 ; arg int c
+ call _clear_int_c ; int clear(int c)
+_end_24:
+ jmp _body_11
+_end_12:
+ mov 0, V0 ; fallback return 0
+ ret
+
diff --git a/ROMs/typewriter.c b/ROMs/typewriter.c
new file mode 100644
index 0000000..0886532
--- /dev/null
+++ b/ROMs/typewriter.c
@@ -0,0 +1,107 @@
+#load "font.bin"
+
+int drawChar(int ch, int x, int y, int color)
+{
+ // adjust for jmp instruction
+ char* fontStart = 0x1FFF + 2;
+ int charStart = ch * 8;
+
+ for (int yOff = 0; yOff < 8; yOff++)
+ {
+ int row = fontStart[charStart + yOff];
+
+ for (int xOff = 0; xOff < 8; xOff++)
+ {
+ int bit = (row >> (7 - xOff)) & 1;
+ int pix = bit ? 8 : color;
+ setPixel(x + xOff, y + yOff, pix);
+ }
+ }
+}
+
+int setPixel(int x, int y, int c)
+{
+ char* screenStart = 0x402F;
+ int i = (y * 128) + x;
+ screenStart[i] = c;
+}
+
+int clear(int c)
+{
+ char* screenStart = 0x402F;
+ char screenSize = 64 * 128;
+ for (int i = 0; i < screenSize; i++)
+ {
+ screenStart[i] = c;
+ }
+}
+
+int getKeyState(int scanCode)
+{
+ char* hid = 0x6033;
+ int index = scanCode / 8;
+ int bit = scanCode % 8;
+
+ int val = hid[index - 1];
+ return (val & (1 << bit)) != 0;
+}
+
+int print(int* text, int len, int x, int y, int color)
+{
+ for (int i = 0; i < len; i++)
+ {
+ int ch = text[i];
+ drawChar(ch, x + (i * 8), y, color);
+ }
+}
+
+int main()
+{
+ int x = 0;
+ int y = 0;
+ while (1)
+ {
+ for (int sk = 0; sk < 256; sk++)
+ {
+ if (getKeyState(sk))
+ {
+ while (getKeyState(sk)) { }
+ // Newline pressed
+ if (sk == 13)
+ {
+ y += 8;
+ x = 0;
+ continue;
+ }
+ // Backspace pressed
+ if (sk == 8)
+ {
+ x -= 8;
+ if (x < 0)
+ x = 0;
+ int* text = " ";
+ print(text, 1, x, y, 1);
+ continue;
+ }
+
+ int* text = new int[] { sk };
+ print(text, 1, x, y, 1);
+ x += 8;
+ }
+ }
+
+ if (x >= 128)
+ {
+ x = 0;
+ y += 8;
+ }
+
+ if (y >= 64)
+ {
+ x = 0;
+ y = 0;
+ clear(8);
+ }
+
+ }
+} \ No newline at end of file
diff --git a/ROMs/typewriter.q1 b/ROMs/typewriter.q1
new file mode 100644
index 0000000..ab14869
--- /dev/null
+++ b/ROMs/typewriter.q1
Binary files differ