From 7f96fc5e7f412a66d460dcaa82bbdbcaea08f360 Mon Sep 17 00:00:00 2001 From: Melodi <62381002+Melodi17@users.noreply.github.com> Date: Thu, 28 Aug 2025 12:35:24 +1000 Subject: everything is finally at peace --- Emulator/Chip/HIDDevice.cs | 15 ++- Emulator/DisplayEngine.cs | 17 +++- Emulator/Program.cs | 2 +- Orchestrator/Program.cs | 7 +- ROMs/_old/hello_world.asm | 42 ++++++++ ROMs/_old/hello_world.q1 | Bin 0 -> 2151 bytes ROMs/_old/manual.q1 | Bin 0 -> 10 bytes ROMs/_old/write_display_direct.asm | 21 ++++ ROMs/_old/write_display_direct.q1 | Bin 0 -> 51 bytes ROMs/hello_world.asm | 42 -------- ROMs/hello_world.q1 | Bin 2151 -> 0 bytes ROMs/hid.asm | 117 ----------------------- ROMs/hid.c | 34 ------- ROMs/hid.q1 | Bin 417 -> 0 bytes ROMs/manual.q1 | Bin 10 -> 0 bytes ROMs/paint.asm | 191 +++++++++++++++++++++++++++++++++++++ ROMs/paint.c | 55 +++++++++++ ROMs/paint.q1 | Bin 0 -> 676 bytes ROMs/write_display_direct.asm | 21 ---- ROMs/write_display_direct.q1 | Bin 51 -> 0 bytes RegionVisualiser/output.txt | 14 ++- 21 files changed, 356 insertions(+), 222 deletions(-) create mode 100644 ROMs/_old/hello_world.asm create mode 100644 ROMs/_old/hello_world.q1 create mode 100644 ROMs/_old/manual.q1 create mode 100644 ROMs/_old/write_display_direct.asm create mode 100644 ROMs/_old/write_display_direct.q1 delete mode 100644 ROMs/hello_world.asm delete mode 100644 ROMs/hello_world.q1 delete mode 100644 ROMs/hid.asm delete mode 100644 ROMs/hid.c delete mode 100644 ROMs/hid.q1 delete mode 100644 ROMs/manual.q1 create mode 100644 ROMs/paint.asm create mode 100644 ROMs/paint.c create mode 100644 ROMs/paint.q1 delete mode 100644 ROMs/write_display_direct.asm delete mode 100644 ROMs/write_display_direct.q1 diff --git a/Emulator/Chip/HIDDevice.cs b/Emulator/Chip/HIDDevice.cs index 87bb2c3..c8a7d04 100644 --- a/Emulator/Chip/HIDDevice.cs +++ b/Emulator/Chip/HIDDevice.cs @@ -1,5 +1,7 @@ namespace Q1.Emulator.Chip; +using System; + public class HIDDevice : RamDevice { public HIDDevice(u16 start) : base(start, HIDDevice.CalculateSize()) { } @@ -24,7 +26,7 @@ public class HIDDevice : RamDevice int bit = scanCode & 7; // keyCode % 8 ushort addr = (ushort) (4 + index); - u8 value = this.Read(addr); + u8 value = this.Read((u16) (this.AddressableStart + addr)); if (pressed) value |= (u8) (1 << bit); @@ -33,4 +35,15 @@ public class HIDDevice : RamDevice this.Write((u16) (this.AddressableStart + addr), value); } + + public bool GetKeyboard(int scanCode) + { + int index = scanCode >> 3; + int bit = scanCode & 7; + + ushort addr = (ushort) (4 + index); + byte value = this.Read((u16) (this.AddressableStart + addr)); + + return (value & (1 << bit)) != 0; + } } \ No newline at end of file diff --git a/Emulator/DisplayEngine.cs b/Emulator/DisplayEngine.cs index e5d508e..7aa8e2e 100644 --- a/Emulator/DisplayEngine.cs +++ b/Emulator/DisplayEngine.cs @@ -17,6 +17,7 @@ public class DisplayEngine : Game private SpriteBatch _spriteBatch; private Texture2D _texture; + private Keys[]? _oldKeys = null; public int Scaling { get; } public int Width { get; } public int Height { get; } @@ -66,11 +67,25 @@ public class DisplayEngine : Game var mouse = Mouse.GetState(); u16 mousePos = (u16) (((mouse.Y / this.Scaling) * this.Width) + (mouse.X / this.Scaling)); - this._hid.SetMouse(mousePos, + this._hid.SetMouse( + mousePos, mouse.LeftButton == ButtonState.Pressed, mouse.RightButton == ButtonState.Pressed, mouse.MiddleButton == ButtonState.Pressed); + Keys[] keys = keyboard.GetPressedKeys(); + foreach (Keys newKey in keys.Except(this._oldKeys ?? [])) + { + this._hid.SetKeyboard((u8) newKey, true); + } + + foreach (Keys oldKey in this._oldKeys?.Except(keys) ?? []) + { + this._hid.SetKeyboard((u8) oldKey, false); + } + + this._oldKeys = keys; + if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || keyboard.IsKeyDown(Keys.Escape) || this._checkAbort()) diff --git a/Emulator/Program.cs b/Emulator/Program.cs index 26f8c26..65bf068 100644 --- a/Emulator/Program.cs +++ b/Emulator/Program.cs @@ -74,7 +74,7 @@ public class Program { display.OutputTexture = tex; Console.WriteLine("Display surface initialized."); - }, () => display.Clock(), () => false); + }, () => display.Clock(), () => cpu.AbortRequested); game.Run(); Console.WriteLine("Display surface closed. Waiting for CPU thread to finish..."); diff --git a/Orchestrator/Program.cs b/Orchestrator/Program.cs index 837eac0..a59127b 100644 --- a/Orchestrator/Program.cs +++ b/Orchestrator/Program.cs @@ -9,7 +9,12 @@ class Program { static void Main(string[] args) { - Parser.Default.ParseArguments(args).WithParsed(Main); + Parser p = new(with => + { + with.CaseInsensitiveEnumValues = true; + }); + + p.ParseArguments(args).WithParsed(Main); } private static void Main(Options options) diff --git a/ROMs/_old/hello_world.asm b/ROMs/_old/hello_world.asm new file mode 100644 index 0000000..1d812fb --- /dev/null +++ b/ROMs/_old/hello_world.asm @@ -0,0 +1,42 @@ +.org $1FFF +JMP program + +font: + .include "font.bin" + +program: + MOV 0, V0 ; Load character #12 + MOV 0, V1 ; X position + MOV 0, V2 ; Y position + MOV 1, V3 ; Color (1 = white) + CALL write_char ; Call function to write character + SUS + +write_char: + ; V0: Character to write + ; V1: X position + ; V2: Y position + ; V3: Color + ; Write a character to the screen at (V1, V2) with color V3 + + MUL V0, 64 ; V0 = Character * 64 (character size) + MOV AX, V0 ; Store result back into character + + MUL V2, 64 + ADD AX, V1 + MOV AX, V4 ; V4 = X position + Y position * 64 (screen index) + + MOV V4, [$EFFF] ; Debug + + MOV [V0 + $2003], V5 ; Read character (1FFF + 4 since jump address is 1FFF) + MOV V5, [$EFFF] ; Debug + AND V5, $01 ; Mask to get first bit (color) + MOV DX, V5 + MOV V5, [$EFFF] ; Debug + MUL V5, V3 ; V5 = Character * Color (color index) + MOV AX, V5 ; Store result into V5 + + MOV V5, [$EFFF] ; Debug + + MOVB V5, [V4 + $402F] ; Write character to screen memory + RET \ No newline at end of file diff --git a/ROMs/_old/hello_world.q1 b/ROMs/_old/hello_world.q1 new file mode 100644 index 0000000..fd66ffe Binary files /dev/null and b/ROMs/_old/hello_world.q1 differ diff --git a/ROMs/_old/manual.q1 b/ROMs/_old/manual.q1 new file mode 100644 index 0000000..b166c7a Binary files /dev/null and b/ROMs/_old/manual.q1 differ diff --git a/ROMs/_old/write_display_direct.asm b/ROMs/_old/write_display_direct.asm new file mode 100644 index 0000000..9fde417 --- /dev/null +++ b/ROMs/_old/write_display_direct.asm @@ -0,0 +1,21 @@ +.org $1FFF + +program: +MOV $0000, V0 +MUL 32, 64 ; Set AX to 32 * 64 +MOV AX, V1 ; Copy AX to V1 +MOV 1, V2 ; Color value 1 + +reset: +MOV 0, V0 +INC V2 ; Increment color value + +draw: +GT V0, V1 ; V0 > V1? +BZ ; If number exceeds, branch to reset +JMP reset + +MOVB V2, [V0+$402F] ; Set pixel at V0 to color 6 +INC V0 ; Increment V0 + +JMP draw ; Repeat drawing \ No newline at end of file diff --git a/ROMs/_old/write_display_direct.q1 b/ROMs/_old/write_display_direct.q1 new file mode 100644 index 0000000..4041649 Binary files /dev/null and b/ROMs/_old/write_display_direct.q1 differ diff --git a/ROMs/hello_world.asm b/ROMs/hello_world.asm deleted file mode 100644 index 1d812fb..0000000 --- a/ROMs/hello_world.asm +++ /dev/null @@ -1,42 +0,0 @@ -.org $1FFF -JMP program - -font: - .include "font.bin" - -program: - MOV 0, V0 ; Load character #12 - MOV 0, V1 ; X position - MOV 0, V2 ; Y position - MOV 1, V3 ; Color (1 = white) - CALL write_char ; Call function to write character - SUS - -write_char: - ; V0: Character to write - ; V1: X position - ; V2: Y position - ; V3: Color - ; Write a character to the screen at (V1, V2) with color V3 - - MUL V0, 64 ; V0 = Character * 64 (character size) - MOV AX, V0 ; Store result back into character - - MUL V2, 64 - ADD AX, V1 - MOV AX, V4 ; V4 = X position + Y position * 64 (screen index) - - MOV V4, [$EFFF] ; Debug - - MOV [V0 + $2003], V5 ; Read character (1FFF + 4 since jump address is 1FFF) - MOV V5, [$EFFF] ; Debug - AND V5, $01 ; Mask to get first bit (color) - MOV DX, V5 - MOV V5, [$EFFF] ; Debug - MUL V5, V3 ; V5 = Character * Color (color index) - MOV AX, V5 ; Store result into V5 - - MOV V5, [$EFFF] ; Debug - - MOVB V5, [V4 + $402F] ; Write character to screen memory - RET \ No newline at end of file diff --git a/ROMs/hello_world.q1 b/ROMs/hello_world.q1 deleted file mode 100644 index fd66ffe..0000000 Binary files a/ROMs/hello_world.q1 and /dev/null differ diff --git a/ROMs/hid.asm b/ROMs/hid.asm deleted file mode 100644 index 0e3600f..0000000 --- a/ROMs/hid.asm +++ /dev/null @@ -1,117 +0,0 @@ - jmp _main -_setPixel_int_x__int_y__int_c: - pop V0 - pop [$71FF] - pop [$7201] - pop [$7203] - push V0 - mov $402F, [$7205] - push [$7201] - mov 128, V1 - pop V0 - mul V0, V1 - push AX - mov [$71FF], V1 - pop V0 - add V0, V1 - mov AX, [$7207] - push [$7205] - mov [$7207], V1 - pop V0 - mul V1, 1 - add AX, V0 - mov AX, V0 - movb [$7203], [V0] - mov 0, V0 - ret - -_setPixelIndex_int_i__int_c: - pop V0 - pop [$7209] - pop [$720B] - push V0 - mov $402F, [$720D] - push [$720D] - mov [$7209], V1 - pop V0 - mul V1, 1 - add AX, V0 - mov AX, V0 - movb [$720B], [V0] - mov 0, V0 - ret - -_clear_int_c: - pop V0 - pop [$720F] - push V0 - mov $402F, [$7211] - push 64 - mov 128, V1 - pop V0 - mul V0, V1 - mov AX, [$7213] - mov 0, [$7215] -_body_1: - push [$7215] - mov [$7213], V1 - pop V0 - lt V0, V1 - not LX - bz - jmp _end_2 - push [$7211] - mov [$7215], V1 - pop V0 - mul V1, 1 - add AX, V0 - mov AX, V0 - movb [$720F], [V0] - push [$7215] - inc [$7215] - pop V0 - jmp _body_1 -_end_2: - mov 0, V0 - ret - -_main: -_body_3: - not 1 - bz - jmp _end_4 - mov $602F, [$7217] - push [$7217] - mov 0, V1 - pop V0 - mul V1, 2 - add AX, V0 - mov AX, V0 - mov [V0], [$7219] - push [$7217] - mov 1, V1 - pop V0 - mul V1, 2 - add AX, V0 - mov AX, V0 - mov [V0], [$721B] - push [$721B] - push 1 - mov 1, V1 - pop V0 - cmp V0, V1 - mov LX, V1 - pop V0 - and V0, V1 - not DX - bz - jmp _end_5 - push 1 - push [$7219] - call _setPixelIndex_int_i__int_c -_end_5: - jmp _body_3 -_end_4: - mov 0, V0 - ret - diff --git a/ROMs/hid.c b/ROMs/hid.c deleted file mode 100644 index 615808a..0000000 --- a/ROMs/hid.c +++ /dev/null @@ -1,34 +0,0 @@ -int setPixel(int x, int y, int c) -{ - char* screenStart = 0x402F; - int i = (y * 128) + x; - screenStart[i] = c; -} - -int setPixelIndex(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 main() -{ - while (1) - { - int* hid = 0x602F; - int mouseIndex = hid[0]; - int mouseState = hid[1]; - if (mouseState & 1 == 1) - setPixelIndex(mouseIndex, 1); - } -} \ No newline at end of file diff --git a/ROMs/hid.q1 b/ROMs/hid.q1 deleted file mode 100644 index e188e33..0000000 Binary files a/ROMs/hid.q1 and /dev/null differ diff --git a/ROMs/manual.q1 b/ROMs/manual.q1 deleted file mode 100644 index b166c7a..0000000 Binary files a/ROMs/manual.q1 and /dev/null differ diff --git a/ROMs/paint.asm b/ROMs/paint.asm new file mode 100644 index 0000000..2f023b7 --- /dev/null +++ b/ROMs/paint.asm @@ -0,0 +1,191 @@ + jmp _main +_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 + +_setPixelIndex_int_i__int_c: ; int setPixelIndex(int i, int c) + pop V0 ; preserve function return address + pop [$7209] ; param int i + pop [$720B] ; param int c + push V0 ; restore function return address + mov $402F, [$720D] ; int screenStart = $402F + push [$720D] ; store index obj + mov [$7209], 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 [$720B], [V0] ; [V0] = [$720B] + mov 0, V0 ; fallback return 0 + ret + +_clear_int_c: ; int clear(int c) + pop V0 ; preserve function return address + pop [$720F] ; param int c + push V0 ; restore function return address + mov $402F, [$7211] ; 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, [$7213] ; int screenSize = AX + mov 0, [$7215] ; int i = 0 +_body_1: + push [$7215] ; left operand + mov [$7213], 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 + mov [$7215], 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 [$720F], [V0] ; [V0] = [$720F] + push [$7215] ; i++ + inc [$7215] + pop V0 + jmp _body_1 +_end_2: + mov 0, V0 ; fallback return 0 + ret + +_getKeyState_int_scanCode: ; int getKeyState(int scanCode) + pop V0 ; preserve function return address + pop [$7217] ; param int scanCode + push V0 ; restore function return address + mov $6033, [$7219] ; int hid = $6033 + push [$7217] ; left operand + mov 8, V1 ; right operand + pop V0 ; get left operand back + div V0, V1 ; compute division + mov AX, [$721B] ; int index = AX + push [$7217] ; left operand + mov 8, V1 ; right operand + pop V0 ; get left operand back + mod V0, V1 ; compute modulus + mov AX, [$721D] ; int bit = AX + push [$7219] ; store index obj + push [$721B] ; 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], [$721F] ; int val = [V0] + push [$721F] ; left operand + push 1 ; left operand + mov [$721D], 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 + +_main: ; int main() + mov 0, [$7221] ; int color = 0 +_body_3: + not 1 + bz + jmp _end_4 ; if while condition fails, jump to end + mov $602F, [$7223] ; int hid = $602F + push [$7223] ; store index obj + mov 0, 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], [$7225] ; int mouseIndex = [V0] + push [$7223] ; store index obj + mov 1, 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], [$7227] ; int mouseState = [V0] + push [$7227] ; left operand + mov 1, V1 ; right operand + pop V0 ; get left operand back + and V0, V1 ; compute and + not DX + bz ; check if condition + jmp _end_5 ; if succeeds, continue, else jump to end + push [$7221] ; left operand + mov 3, V1 ; right operand + pop V0 ; get left operand back + mod V0, V1 ; compute modulus + push AX ; left operand + mov 1, V1 ; right operand + pop V0 ; get left operand back + add V0, V1 ; compute addition + push AX ; arg int i + push [$7225] ; arg int c + call _setPixelIndex_int_i__int_c; int setPixelIndex(int i, int c) +_end_5: + push 67 ; arg int scanCode + call _getKeyState_int_scanCode; int getKeyState(int scanCode) + not V0 + bz ; check if condition + jmp _end_6 ; if succeeds, continue, else jump to end + push 8 ; arg int c + call _clear_int_c ; int clear(int c) +_end_6: + push 65 ; arg int scanCode + call _getKeyState_int_scanCode; int getKeyState(int scanCode) + not V0 + bz ; check if condition + jmp _end_7 ; if succeeds, continue, else jump to end + push [$7221] ; i++ + inc [$7221] + pop V0 +_body_8: + push 65 ; arg int scanCode + call _getKeyState_int_scanCode; int getKeyState(int scanCode) + not V0 + bz + jmp _end_9 ; if while condition fails, jump to end + jmp _body_8 +_end_9: +_end_7: + jmp _body_3 +_end_4: + mov 0, V0 ; fallback return 0 + ret + diff --git a/ROMs/paint.c b/ROMs/paint.c new file mode 100644 index 0000000..443719a --- /dev/null +++ b/ROMs/paint.c @@ -0,0 +1,55 @@ +int setPixel(int x, int y, int c) +{ + char* screenStart = 0x402F; + int i = (y * 128) + x; + screenStart[i] = c; +} + +int setPixelIndex(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 main() +{ + int color = 0; + while (1) + { + int* hid = 0x602F; + int mouseIndex = hid[0]; + int mouseState = hid[1]; + + if (mouseState & 1) + setPixelIndex(mouseIndex, (color % 3) + 1); + + if (getKeyState(67)) + clear(8); + + if (getKeyState(65)) + { + color++; + while (getKeyState(65)) { } + } + } +} \ No newline at end of file diff --git a/ROMs/paint.q1 b/ROMs/paint.q1 new file mode 100644 index 0000000..98929bb Binary files /dev/null and b/ROMs/paint.q1 differ diff --git a/ROMs/write_display_direct.asm b/ROMs/write_display_direct.asm deleted file mode 100644 index 9fde417..0000000 --- a/ROMs/write_display_direct.asm +++ /dev/null @@ -1,21 +0,0 @@ -.org $1FFF - -program: -MOV $0000, V0 -MUL 32, 64 ; Set AX to 32 * 64 -MOV AX, V1 ; Copy AX to V1 -MOV 1, V2 ; Color value 1 - -reset: -MOV 0, V0 -INC V2 ; Increment color value - -draw: -GT V0, V1 ; V0 > V1? -BZ ; If number exceeds, branch to reset -JMP reset - -MOVB V2, [V0+$402F] ; Set pixel at V0 to color 6 -INC V0 ; Increment V0 - -JMP draw ; Repeat drawing \ No newline at end of file diff --git a/ROMs/write_display_direct.q1 b/ROMs/write_display_direct.q1 deleted file mode 100644 index 4041649..0000000 Binary files a/ROMs/write_display_direct.q1 and /dev/null differ diff --git a/RegionVisualiser/output.txt b/RegionVisualiser/output.txt index ecf83cc..10d7970 100644 --- a/RegionVisualiser/output.txt +++ b/RegionVisualiser/output.txt @@ -1,15 +1,21 @@ + 0x0000 ────────────────────────────────────────────────────── [ Unmapped / Reserved (8 KB) ] 0x1FFF ── ┌─────────────────── ROM (8 KB) ───────────────────┐ │ ProgramStart: 0x1FFF │ └──────────────────────────────────────────────────┘ 0x3FFF ── ┌─────────────── Display (8.05 KB) ────────────────┐ - │ Palette: 16 colors │ - │ Offset: 0x402F │ + │ Palette : 16 colors │ + │ Offset : 0x402F │ │ Dimensions: 128x64 │ └──────────────────────────────────────────────────┘ - 0x602F ────────────────────────────────────────────────────── - [ Unmapped / Reserved (3.95 KB) ] + 0x602F ── ┌───────────── HID Module (260 bytes) ─────────────┐ + │ Mouse IDX : 0x602F │ + │ Mouse state: 0x6031 │ + │ Keyboard SK: 0x6033 │ + └──────────────────────────────────────────────────┘ + 0x6133 ────────────────────────────────────────────────────── + [ Unmapped / Reserved (3.7 KB) ] 0x6FFF ── ┌───────────────── Memory (16 KB) ─────────────────┐ │ Stack: 0x6FFF-0x71FF (512b) │ │ Free : 0x71FF │ -- cgit v1.3.1