Sign in

Sam Abbott-Kennedy

@samothyk.bsky.social
88 followers 277 following 84 posts

[onlookers booing]

PostsRepliesMedia
Sam Abbott-Kennedy @samothyk.bsky.social · 05/09/2026
Work on the ternary fantasy emulator continues. I have a wave channel working like a Gameboy, except it natively supports negative values and has a resolution of 3 trits (27 values) Vs the gb's 4 bits (16 values). Still crunchy but more sonorous than a gameboy
010
Sam Abbott-Kennedy @samothyk.bsky.social · 31/08/2026
Finally working on the ternary fantasy console again and I have audio working. Currently got one channel working with 8 trit period, meaning it can accurately represent a full 8 octaves. Manually changing the pitch by writing to the audio registers with the keyboard for now. #fantasyconsole #ternary
020
Sam Abbott-Kennedy @samothyk.bsky.social · 16/08/2026
Ternary assembly (tasm) is now mature enough to make a ROM to move a square around the screen! #ternary #fantasyconsole
010
Sam Abbott-Kennedy @samothyk.bsky.social · 15/08/2026
Work on an assembler for the ternary bytecode (or I guess, trytecode) has begun, it's so much easier to write programs for the ternary emulator now 😅 Which program is easier to read? 😛 #ternary #assembly
An assembly program that reads:
```
.loop:
    inc a
    jr ceq, .loop
    halt
```The assembled program ROM in balanced heptavigesimal, it reads 
```
RS QQ 0P TT
```
000
Sam Abbott-Kennedy @samothyk.bsky.social · 14/08/2026
IO register is now working, and can be queried trit-by-trit to move a sprite across the screen! No VBLANK yet so there's just a delay of 100 cycles between each update 😅 #ternary #fantasyconsole #emulator
000
Sam Abbott-Kennedy @samothyk.bsky.social · 13/08/2026
Having 3^6 (729) possible opcodes absolutely mogs the gameboy's 2^8 (256)
000
Sam Abbott-Kennedy @samothyk.bsky.social · 13/08/2026
Still loads of room left but the optable for the ternary CPU is filling out nicely! you can do lots of things with just arithmetic, jumps, loads, and tritwise operations. Heavily influenced by the gameboy optable of course gbdev.io/gb-opcodes/o... #ternary #fantasyconsole #emulator
100
Sam Abbott-Kennedy @samothyk.bsky.social · 13/08/2026
Adding input to my ternary console emulator, what's neat is a single trit can represent a whole axis, -1 for left/down, 0 for released, and +1 for right/up. The other buttons are disappointingly binary, however #ternary #emulator #fantasyconsole
A screenshot of my input code for my fantasy ternary console, reading the io register and writing individual trits depending on input.
010
Sam Abbott-Kennedy @samothyk.bsky.social · 04/08/2026
I've gotten to the point where my ternary CPU is good enough that writing roms for it by hand is getting cumbersome. So logically I need to invent an assembly language for it. I have managed to write the rom for a single sprite moving though 🎉 #fantasyconsole #ternary #odinlang
010
Sam Abbott-Kennedy @samothyk.bsky.social · 03/08/2026
I switched the ternary emulator's tilemap to 3 trits per pixel. Now figuring out how to draw tiles with code is much easier. Also this means they can have a colour depth of 27 colours! All with space for more tiles in VRAM than a Gameboy #fantasyconsole #ternary #emulator
A screenshot of my ternary based emulator showing "HELLO WORLD" in the top center along with the base-27 rom code in the backgroundAnother shot of my ternary emulator showing all 27 colours it can represent
000
Sam Abbott-Kennedy @samothyk.bsky.social · 02/08/2026
The "ROM" is behind it, instead of hexadecimal which encodes 4 bits per hex character, I needed to use heptavigesimal (base 27) because it can encode 3 trits per character...
000
Sam Abbott-Kennedy @samothyk.bsky.social · 02/08/2026
Most insane project yet, a ternary CPU based emulator for a console that never existed. I have the CPU and PPU working well enough to transfer tiles to the screen, but working out the ternary encoding of each tile by hand is melting my brain 😅 #fantasyconsole #ternary #odinlang
110
Sam Abbott-Kennedy @samothyk.bsky.social · 23/07/2026
Dabbling in #odinlang once again, I forgot how nice of a language it is. No llm no Google just the docs on one screen and the text editor on the other like god intended
010
Sam Abbott-Kennedy @samothyk.bsky.social · 23/07/2026
Well I'm glad you're still here, and making cool stuff!
110
Sam Abbott-Kennedy @samothyk.bsky.social · 23/07/2026
How on earth do you have so many cool projects?
110
Sam Abbott-Kennedy @samothyk.bsky.social · 18/07/2026
I can recommend posting your stuff to immediately find everything wrong with it, the 4th level hierophant ability completely eclipses the 12 level one for turning undead lmao
000
Sam Abbott-Kennedy @samothyk.bsky.social · 18/07/2026
Continuing OSR-posting, this is the class I'm happiest with (it's also the most complete class besides the Warrior). The Devout covers the cleric/paladin archetypes with a unique casting mechanic that I should really test. Favour rolls feel a bit jank and maths heavy but I like what they're doing
100
Sam Abbott-Kennedy @samothyk.bsky.social · 18/07/2026
This also stops it from being a combat option, so they can't just king Midas an enemy's weapon 😂 I just wanted the option of alching something instead of having to find a merchant. I'm running Arden Vul right now and not having to travel to Gosterwick to sell would be very convenient sometimes 😄
000
Sam Abbott-Kennedy @samothyk.bsky.social · 18/07/2026
Yeah I've nerfed it a bit, now it takes 1 turn (10 mins) to cast and it uses the item's "true" value (i.e. the price in the book). Also it's a 3rd tier spell so they can't cast it endlessly at low levels. Not sure why I'm worrying so much about balance in a game that's not even playtesting yet 😅
100
Sam Abbott-Kennedy @samothyk.bsky.social · 17/07/2026
Another spell I quite like that absolutely cannot be abused in any way, nope not at all
A spell from my WIP TTRPG. The text reads:

Knick-Knack
Class: Arcanist, Knave, Occultist
Duration: 3 turns
Range: Self/Melee
Conjure a simple mundane item worth
20GP or less. Once the spell wears off
the item vanishes.
100
Sam Abbott-Kennedy @samothyk.bsky.social · 17/07/2026
In my everlasting quest to start new projects I'm trying to design a sort of OSR TTRPG. I'm having fun with the spells section, can you guess which games are inspiring me? #OSR #TTRPG
231
Sam Abbott-Kennedy @samothyk.bsky.social · 20/05/2026
010
Sam Abbott-Kennedy @samothyk.bsky.social · 08/05/2026
The Firered romhack with only gen 5 pokemon is still going. The increased level curve and power creep is really refreshing. Also figured out how to change the overworld pokemon sprites! #pokemon #romhack
010
Sam Abbott-Kennedy @samothyk.bsky.social · 06/04/2026
It's surprisingly refreshing, the higher power from the 4 generations of scope creep means everything is stronger, and because everything in unova evolves so much later there needs to be more XP everywhere raising the power level even more. It's quite fun tbh!
000
Sam Abbott-Kennedy @samothyk.bsky.social · 06/04/2026
Working on something completely different, what if the unova dex was the OG 151 #pokemon #romhack #frlg
120
Sam Abbott-Kennedy @samothyk.bsky.social · 28/03/2026
I'm extremely jealous, good luck!
010
Sam Abbott-Kennedy @samothyk.bsky.social · 28/03/2026
Ooh what's the brew?
100
Sam Abbott-Kennedy @samothyk.bsky.social · 20/03/2026
Spent a few days playing with the game of life program, fixed a lot of bugs in the language doing so. I should make more projects like this to stress test the language, any ideas for what else I should try coding? #compiler #programming #raylib
060
Sam Abbott-Kennedy @samothyk.bsky.social · 18/03/2026
The code is a bit longer than the usual snippets I post, if you're curious what it looks like: pastebin.com/shtUPVki
pastebin.com
import "raylib.os"let width: const i32 = 1280let height: const i32 = 720 - Pastebin.com
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
000
Sam Abbott-Kennedy @samothyk.bsky.social · 18/03/2026
Managed to build a cheeky little game of life simulator in my programming language! #programming #compiler #raylib
140
Sam Abbott-Kennedy @samothyk.bsky.social · 17/03/2026
Anonymous structs have blown open a whole new slew of features. Multiple return values and tuples basically for free #compiler #programming
A snippet of my programming language showing anonymous structs, multiple return values, and tuples
```
fn multiple_returns(): struct { u32, u32 } {
    return { 6, 7 }
}

//accept as anonymous struct
fn foo(struct { a: u32, b: u32}) {
    println(a)
    println(b)
}

//accept as tuple
fn bar(tup: struct { u32, u32 }) {
    println(tup.0)
    println(tup.1)
}

fn main(): u32 {
    let x = multiple_returns()
    foo(x)
    bar(x)

    return 0
}
```
031
Sam Abbott-Kennedy @samothyk.bsky.social · 17/03/2026
Been noodling on how my language will handle anonymous structs. They're not done yet but I think they'll be pretty loose, able to coerce between compatible named structs. Plus they'll basically act like tuples once you can create anonymous struct literals, which is nice! #programming #compiler
A snippet of code in my programming language showing coercion between a named struct and an unnamed struct, as well as promotion of an anonymous struct's fields to the containing struct:
```
struct Foo {
    { a: u32, b: u32 }
}

struct Pair { a: u32, b: u32 }

fn foo({a: u32, b: u32}, c: u32) {
    println(a, b, c)
}

fn main(): u32 {
    let pair = Pair { 1, 2 }
    foo(pair, 3) //named struct coerced to anonymous struct

    let f = Foo { pair }

    println(f.a)
    println(f.b)

    return 0
}
```The output:
```
1, 2, 3
1
2
```
010
Sam Abbott-Kennedy @samothyk.bsky.social · 15/03/2026
Gave in and added function receivers, and I think they look pretty swanky. Just means that &self is automagically converted into a parameter `self: &Vec`
A snippet in my language showing method receivers on a struct `CPU`:
```
struct CPU {
    clock: u64 = 0,
    //registers
    AF: u16 = 0x01b0,
    BC: u16 = 0x0013,
    DE: u16 = 0x00D8,
    HL: u16 = 0x014D,
    SP: u16 = 0xfffe, //Stack pointer
    PC: u16 = 0x0100, //Program counter
}

fn CPU.execute(&self, opcode: u8) {
    switch (opcode) {
        0x00 => println("NOP"),
        0x01 => println("Load N16"),
        // etc for the other 0xFD opcodes...
        _ => println("unknown"),
    }
}
```
010
Sam Abbott-Kennedy @samothyk.bsky.social · 15/03/2026
For instance, for this small program to compile I had to add hexadecimal integer literals, and fix struct defaults not being recognised in imported types
000
Sam Abbott-Kennedy @samothyk.bsky.social · 15/03/2026
Getting to the point where I can write semi-serious programs in my language. It's a great way to find a bunch of subtle bugs and missing functionality. E.g. here's a ~totally functioning~ gameboy emulator #programming #compiler #raylib
Two files in my language showing a barebones gameboy CPU and raylib draw loop with the gameboy window running, showing a blank screen
170
Sam Abbott-Kennedy @samothyk.bsky.social · 14/03/2026
Had to implement some name mangling when emitting functions to lay the groundwork for interfaces, and I noticed this gave me function overloads for free. Take that, C! #compiler #programming
A program in my language showing function overloads:
```
struct Vec2 { x: u32, y: u32 }
struct Vec3 { x: u32, y: u32, z: u32 }

fn add(a: Vec2, b: Vec2): Vec2 {
    return Vec2 { a.x + b.x, a.y + b.y }
}

fn add(a: Vec3, b: Vec3): Vec3 {
    return Vec3 { a.x + b.x, a.y + b.y, a.z + b.z }
}

fn main(): u32 {
    let v2a = Vec2 { 3, 2 }
    let v2b = Vec2 { 4, 3 }
    let v2c = add(v2a, v2b)
    println(v2c)

    let v3a = Vec3 { 3, 2, 1 }
    let v3b = Vec3 { 2, 1, 1 }
    let v3c: Vec3 = add(v3a, v3b)
    println(v3c)

    return 0
}
```The output of the program:
```
{ 7, 5 }
{ 5, 3, 2 }
```
010
Sam Abbott-Kennedy @samothyk.bsky.social · 12/03/2026
Sounds like a game mechanic waiting to happen 😜
110
Sam Abbott-Kennedy @samothyk.bsky.social · 12/03/2026
It's all coming together. Now I have unions I can implement the optional type ?T which is just a tagged union. In addition I could implement rust style try-unwrap of optionals basically for free with all the parts I have! Here opt_return only prints x if it's some, else it returns early! #compiler
A snippet of my programming language showing optionals and early returns on optionals:
```
fn maybe(b: bool): ?u32 {
    if (b) return 10
    return none
}

fn opt_return(b: bool): ?u32 {
    let x = maybe(b)
    println(x?) //if x is none, return here, else print the value of x (10)
    println("x was some!")
    return x
}

fn main(): u32 {
    let x = opt_return(true)

    if (x) |value| {
        println(value + 10)
    } else println("x was none")

    let y = opt_return(false)
    if (y) |value| {
        println(value + 10)
    } else println("y was none")

    return 0
}
```The output of the program:
```
10
x was some!
20
y was none
```
030
Sam Abbott-Kennedy @samothyk.bsky.social · 11/03/2026
Now with the fancy struct syntax, implementing tagged unions was relatively easy. Also got rid of all those pesky semicolons messing up the place #compiler
010
Sam Abbott-Kennedy @samothyk.bsky.social · 10/03/2026
Blew apart my struct and function parsing to allow default values and named arguments. Pretty swanky imo #compiler
010
Sam Abbott-Kennedy @samothyk.bsky.social · 09/03/2026
Output of the program:
000
Sam Abbott-Kennedy @samothyk.bsky.social · 09/03/2026
Not sure it's the greatest use of it but the new `with` statement works nicely with raylib 😅 #raylib #compiler
110
Sam Abbott-Kennedy @samothyk.bsky.social · 09/03/2026
Added a defer statement to my language, but I didn't like how the resource acquisition and disposal had to be separated. So I added a with statement that just desugars to defer, and I kinda like it! #compiler
A snippet of my programming language showing the defer statement:
```
fn main() {
    let f = os::open_file("test.txt");
    defer os::close(f);
    
    for _ in 0..15 {
        os::write(f, "Hello once again from osmium!\n");
    }
}
```A snippet from my programming language showing the same code but using a `with` statement:
```
fn main() {
    with let f = os::open_file("test.txt"); { println("closing file!"); os::close(f); } {
        for _ in 0..15 {
            os::write(f, "Hello once again from osmium!\n");
        }
        println("done writing!");
    }
    println("end of main!");
}
```The output of the with statement variant showing the order in which things are run:
```
done writing!
closing file!
end of main!
```
030
Sam Abbott-Kennedy @samothyk.bsky.social · 08/03/2026
Starting on the memory management for my language. Nothing fancy but just a few more guard rails than C. Every pointer has an "origin": Stack, Param, Heap, or Global. Only Heap can be freed, and Stack can't be returned from functions. Maybe this will cause problems but we'll see! #compiler #llvm
A program in my programming language showing pointer allocation and illegal freeing:
```
fn bad(ptr: &u32) {
    free ptr;
}

fn main() {
    let x = new u32;
    free x;

    let i = 10;
    let p = &i;
    free p;
}
```The error messages from compiling the program:
```
Error: Cannot free a pointer with lifetime `Param` at src\main\resources\examples\memory.os:2:5
2 |     free ptr;
  |     ^^^^^^^^^
Error: Cannot free a pointer with lifetime `Stack` at src\main\resources\examples\memory.os:11:5
11 |     free p;
   |     ^^^^^^^
```
110
Sam Abbott-Kennedy @samothyk.bsky.social · 04/03/2026
Osmium Raylib ffi speedrun #compiler #llvm #raylib
020
Sam Abbott-Kennedy @samothyk.bsky.social · 04/03/2026
Yeah like that
Using FFI and modules to make an os.os file that's imported to write to a file:
`os.os`:
```
extern fn exit(status: u32);
extern struct FILE();

extern fn fopen(filename: &const char, mode: &const char): &FILE;
extern fn fputs(s: &const char, file: &FILE): u32;
extern fn fclose(file: &FILE);

struct File(handle: &FILE) {
    fn write(self: File, s: string): u32 {
        return fputs(s.data, self.handle);
    }

    fn close(self: File) {
        fclose(self.handle);
    }
}

fn open_file(filename: string): File {
    return File(fopen(filename.data, "w".data));
}
```
files.os:
```
import "core/os.os";

fn main() {
    let f = os::open_file("test.txt");
    for _ in 0..10 {
        os::write(f, "Hello again from osmium!\n");
    }
    os::close(f);
}
```
test.txt:
```
Hello again from osmium!
Hello again from osmium!
Hello again from osmium!
Hello again from osmium!
Hello again from osmium!
Hello again from osmium!
Hello again from osmium!
Hello again from osmium!
Hello again from osmium!
Hello again from osmium!
```
010
Sam Abbott-Kennedy @samothyk.bsky.social · 04/03/2026
Decided the next logical step from modules was ffi, surprisingly straightforward to implement! Now I can tuck all that nasty ffi code in a libc.os or something #compiler
A program in my programming language showing FFI:
```
extern fn exit(status: u32);
extern struct FILE();

extern fn fopen(filename: &const char, mode: &const char): &FILE;
extern fn fputs(s: &const char, file: &FILE): u32;
extern fn fclose(file: &FILE);

fn main() {
    let file = fopen("test.txt".data, "w".data);
    if file == null {
        println("Could not open file!");
        exit(1);
    }

    for _ in 0..10 {
        fputs("Hello from osmium!\n".data, file);
    }
    fclose(file);
}
```The output in a new file `test.txt`
```
Hello from osmium!
Hello from osmium!
Hello from osmium!
Hello from osmium!
Hello from osmium!
Hello from osmium!
Hello from osmium!
Hello from osmium!
Hello from osmium!
Hello from osmium!
```
110
Sam Abbott-Kennedy @samothyk.bsky.social · 04/03/2026
The way it works is horribly inefficient, it takes the main file and parses it and gathers every type and function declared. Then it takes every import statement, finds the file it points to and then repeats the process recursively to build a graph. Then the graph is DFS'd to emit one llvm file!
000
Sam Abbott-Kennedy @samothyk.bsky.social · 04/03/2026
I managed to implement a somewhat halfway decent module system. I'm not thrilled that the import needs the relative path but it'll do for now. Plus it handles the same file being imported multiple times across the compilation graph 😌 #compiler #llvm
A picture of 3 separate files in my programming language that form a dependency chain. main.os imports io.os and math.os, math.os imports io.os.
main.os:
```
import "math.os";
import "io.os";

fn main() {
    io::print_msg("Hello from main.os!");
    var v = math::Vec(1.0, 2.0);
    println(math::dot(v, v));
    var x = math::pow(2.0, 3);

    println(x);

    math::greet();
}
```
math.os:
```
import "io.os";

struct Vec(x: f32, y: f32) {
    fn dot(v1: Vec, v2: Vec): f32 {
        return v1.x * v2.x + v1.y * v2.y;
    }
}

fn pow(x: f32, p: u32): f32 {
    for _ in 1..p x = x * x;
    return x;
}

fn greet() {
    io::print_msg("Hello from math.os!");
}
```
io.os:
```
fn print_msg(msg: string) {
    println(msg);
}
```The output of the program:
```
Hello from main.os!
5.000000
16.000000
Hello from math.os!
```
120
Sam Abbott-Kennedy @samothyk.bsky.social · 01/03/2026
Added strings to my language, since they're just backed by a slice of u8's all the regular slice operations just worked out of the box Again nothing real languages don't do but it's so satisfying seeing it work! #compiler #llvm
A program in my programming language showing string slicing:
```
fn main() {
    var s: string = "Hello from osmium!";
    println(s);
    println(s[6..=10]);

    var sl = s[8..17];
    println(sl);

    for c in s[0..5] {
        print(c);
        print(' ');
    }
}
```The output of the program:
```
Hello from osmium!
from 
om osmium
H e l l o 
```
040