Show older

My core compiler is just under 500 lines of code - it's been creeping closer and closer with every new feature but it's still below the threshold - and there is a part of me that is like, significantly worried about letting it get any bigger than that. Like, the core is complicated enough, and I have the capability to add pretty much arbitrary compiler extensions in modules, so I feel a weird pull to do whatever it takes to keep that file under 500loc.

whooooops, tried to write a small loop with my lisp compiler but it generated so much code inside the loop that the target of my branch instruction looped around and became negative

this will actually be a fairly obnoxious problem to solve! shit!

I have implemented The Shitty Fix for my compiler issue, which is to just always do long branches, at the cost of making the generated code bigger and slower than it usually needs to be. But, make it correct first, make it fast later, or never. I can always inline assembly for the cases where this really matters!

My extremely-unoptimized tile-drawing routine seems to be drawing the pixels I want it to draw! Unfortunately my palette is... really not right.

It feels awkward to have implemented a compiler for a language with no name. Like it's "a lisp" insofar as it's prefix notation and uses nested parenthesis for everything, but there are no lists or symbols at runtime like you might expect. The language is a lot closer in semantics to something like C, if C only had two data types.

Internally it's called "ssc", for "sufficiently stupid/simple compiler" (as opposed to the mythical "sufficiently smart compiler" that will magically optimize high-level languages to efficient low-level code). That's not really a name for a LANGUAGE tho. I wish I had a better name!

This is me procrastinating on actually making improvements to it, or otherwise working on my game, btw

I kind of like “Llissp” - low-level, interactive, simple, single-pass

Perhaps “Llissp” could be written as “ỻlißp”, to emphasize that it is an unholy mashup of other languages

Goddammit. So I've been fighting for days to wrap my head around the Apple IIgs graphics architecture for days. The IIgs has no graphics architecture, just an insanely, insanely slow framebuffer and some palette registers. There's no graphics coprocessor, there's no page-flipping, there's no hardware scrolling, there's just 32000 bytes of pixels, that the processor slows down to 1MHz to write.

Lying in bed, trying to sleep, I suddenly realize that the tricks that people do to make this go fast boil down to the following:
* The fastest way the processor can write data to sequential areas of memory is to push data onto the stack.
* Therefore the best thing to do is to change the processor's stack pointer to point at the area of graphics memory you want to modify, and then run some code that pushes the values you want into memory.

I had kind of been thinking of these tricks as "oh maybe if I _really_ need it I can dip my toe into this, but I don't _really_ need to be that fast."

But if I commit to this pattern, for everything? I can factor it out. One routine that does all the tricky shit; temporarily disabling interrupts, mucking with the stack pointer, all of it, and then jumps to the precompiled code to draw whatever needs drawing.

The only thing the precompiled code needs to do is to JUMP BACK DIRECTLY, because there's no processor stack to return with.

But with this design, I have a completely flexible "drawing object" capability. I can draw tiles, I can draw sprites, I can do weird custom non-bitmap shapes, I can do the weird thing the IIgs needs where I write the data in memory that's already there to commit it to main graphics memory (PEI slamming).

So now, instead of sleeping I am writing it up in the hopes that if I write it down, my brain will let me go to bed.

I really hope it doesn't make me implement it tonight, it's late.

Try not to think about how it's not REALLY that much code...

Welp, I did it and it works. I wrote the fast-draw code skeleton, and then I wrote a tile & sprite compiler. I was originally going to separate them but then I realized that a compiled tile is just a compiled sprite that doesn't have any transparent pixels. You can see the green splat draws on top of the red smiley face. It's as fast as I've seen anything draw on the Apple IIgs. I'm pleased with myself.

it's slowly dawning on me that, given the graphics architecture of the IIgs, and the flexibility of the sprite compiler I just implemented, there is not really any technical advantage to doing a flat tile-based design. I could just as easily do something from an isometric perspective, or put big weirdly-shaped objects in my levels.

I'm not sure if I have the _artistic_ chops to pull off these more ambitious ideas, but the tech? no problem.

IDK, there's a part of me that I can't seem to quite keep quiet that really wants it to look like a significant upgrade from the EGA version. but also, this is a one-person spare-time project, and I am not gonna spend huge amounts of time creating graphic assets.

(yes I realize how absurd it sounds that "build a compiler from scratch" is totally within reasonable scope but "draw a lot of pictures" has me worried)

Here's a fun fact: Bret Victor, famous among "future of programming" types for visionary talks like "Inventing on Principle" and "The Humane Representation of Thought", as well as his work on, was once a prolific Apple IIgs developer that made games like "Surfburgers"

In the shower this morning, thinking about adding an intermediate representation to my single-pass compiler, for optimization reasons

Two things my codegen does now that bug me and I could fix with this:
1. Conditional branches always generate the code for a long jump, because there’s no way for the compiler to know how many bytes of code it’s going to need to jump over
2. Pointless shuffling of 32-bit values into the LONG “register” just to move them back out to where they _really_ belong

On the other hand… it works fine the way it is, and is not currently causing me any practical problems, it just bugs me aesthetically when I’m stepping through the code in the debugger

I loooove the feeling of "oh I wish my tool did X" and then fixing it without even closing the tab I was doing my work in

today's project: implementing a parser for the cc65 library format, with the goal of allowing Honeylisp to import the ip65 networking stack

unsurprisingly, reverse engineering a linker written in C, so that I can re-implement it in fennel, kind of sucks. there are too many things! I don't want to untangle all of these things!!

My end goal is just to write a tiny debug stub that lets me read and write to memory over ethernet, so maybe I should just make that a standalone program written in C?

Alternately, there is an object format for 6502 (o65) that is simple (because it’s designed with the assumption that loaders will be implemented for the 6502) which I could make Honeylisp optionally _export_ to, as the cc65 toolchain knows how to deal with them.

Interop is complicated, that’s why Honeylisp generally doesn’t

urgh ok so
ip65 is a full TCP/IP implementation built for the 6502. The ethernet card I have is the Uthernet II, which basically just directly exposes the W5100 chip to the Apple II.

This chip has a full TCP/IP stack on it already that ip65 ignores! It runs the thing in raw ethernet mode and adds a bunch of overhead! My inner Chuck Moore can't deal with this.

The thing I want to build is a tiny little stub program that listens on a socket and accepts a handful of simple commands - mostly just "write these bytes to this memory address". The only thing missing from the chip that I might actually want is a DHCP client for assigning an IP address. I don't even need DNS.

But I don't even need that! This is for my personal use on my personal network! I can just hardcode the network config! Why am I making this so complicated?

oh oof I thought MAME emulated the Uthernet II but it actually emulates its predecessor, which uses a totally different chip that doesn't have TCP/IP support in hardware. So if I go down this road I am doing it without an emulator.

I have a habit of writing All The Code before I test Any Of The Code; generally I've done enough thinking about how I want to structure things and making sure the details are right before I start writing that this allows me to have Time Focussed On Getting The Code Out Of My Brain be separate from Time Focussed On Finding The Bugs. Not for everyone but it generally works for me.

In practice, this means that I have written a debug stub that _should_ allow me to bootstrap my live dev environment on an actual Apple IIgs over ethernet. I wrote the hardware driver from scratch and have not even made sure it compiles. I'm using several untested capabilities of my compiler, so the code generation may be wrong. I will only able to test this on real hardware and am totally unfamiliar with the debugging tools there.

This will be fine.

Compiler grew to more than 500 lines of code, but I knew there was some cleanup to make things simpler and more consistent, so I wasn't sweating it. Did the cleanup, and now the compiler is 502 lines of code. I find this unreasonably irritating.

My debug stub runs on hardware, and isn't failing like it fails on MAME with no emulated Uthernet II card, but it also doesn't appear to be getting any of the packets I'm sending it. I'd do some printf debugging but I haven't implemented strings yet.

right, time to implement string literals so I can do debug output *implements "proper" macros and rewrites how constants work* wait what was I doing

I sent a UDP packet from my laptop to the IIgs! My server thinks there’s a second package filled with garbage but the first one is handled correctly I think!

I'm actually able to have a really fast development cycle thanks to ADTPro Virtual Drive, which allows me to mount a disk image over ethernet! Any changes I make to the disk image are immediately visible, and when I hit Ctrl-Reset to break out of my program, it resets the ethernet card. So even if I _terribly_ fuck up its state I can immediately try again.


Ahahaha there was a bug in my assembler! Just pushed live code to Apple IIgs RAM over Ethernet and jumped to it!!

I think maybe the most important lesson I learned from my time working in Forth is that imperfect things are often _more_ valuable than perfect things.

Working on live code reload over Ethernet and realizing that, two years ago, I would have been stressing out so much about how to handle a million possible edge cases that I wouldn't have even gotten started. Now I feel like, this small piece that handles one case will probably be enough, and if not, then it's a good start.

Multitasking-aware hot code reload for Llissp is mostly working in MAME. There's still a bug with my integration that means it only works once for some reason I'll have to dig into, but the main concept - rewrite each task's callstack to repoint to the moved code - WORKS. I can literally change a function that is in the middle of being executed and it'll return to the new code. (And hotswapping will fail with an editor message when things have changed too much, rather than a crash on device.)

And I am _very_ close to having it work seamlessly over ethernet as well - I mostly just have to implement batched read/write calls.

Starting to feel like compilers shouldn't produce binary machine code executables, or object files, they should produce arbitrarily queryable databases

of course, this kind of magic is only tractable because I have purposely made my ABI extremely dumb

every now and then I remember I'm doing something that, as far as I know, nobody else has ever done

like, I have never seen a livecoding environment that wasn't built on an interpreted language

Fixed hot code reloading not working after the first time in MAME - the fix was literally deleting a single question mark, augh. If you set a breakpoint at the same address twice, my integration was assuming execution had never unpaused. I'd written code to deal with it, it just had a typo.

The bug's been around since Neu] [ower, but I guess I just didn't run into it as often.

Just had my first successful hot code reload on real hardware, hot DAMN

Really need to make a call with Neut GS - do I wanna continue with the isometric style (in which case I need to radically retool my art workflow and build a much more sophisticated graphics engine) or do I wanna just do a similar top-down style as Neut Tower, which is easy and I know how to do it?

On the one hand, learning and stretching my abilities is part of the point
On the other hand, I haven't touched this project in three weeks because of this impasse

Was reading about how the Filmation engine on the ZX Spectrum worked last night and I think it shook the cobwebs loose a little

Finally started drawing on graph paper to try to figure out what the constraints of doing isometric art are, and what it buys me. I think I’d want to add a vertical element, the ability to have multi-levelled puzzles - otherwise what’s the point? - but it simply can’t work with one flat map turned 45 degrees.

Ok so I sketched things out on paper and I have a scheme for how to organize everything so it visually works. It should be pretty simple to implement, in the end.

However, as part of this, I am using multiple different sizes of tile, and apparently I have built the assumption that all tiles are of uniform size pretty deep into my editor tooling :/

So my current task is a bunch of tedious untangling, which is boring and therefore going slowly

I also have, like, one “special effect” that I still have to work out how on Earth I’m gonna implement. My entire design kind of depends on it right now.

Past few days of muddling have finally started to coalesce into an implementation strategy; finally I feel like I understand all the parts and how they need to change. I still kinda feel like I'm changing things haphazardly in random places but at least it feels like I'm making the RIGHT changes haphazardly in random places, and that maybe soon stuff is gonna start working again.

Progress on my IIgs project remains extremely slooow. Still haven't recovered my sense of "here is the next chunk to tackle" which has guided me through all my personal work the past couple of years. Refactoring my map editor hasn't been particularly fun and it still needs a lot of tweaks to be usable. But I'm showing up almost every day and drawing a tile or two at least.

My worst habit is that when I sit down to do a thing without knowing the next step I want to take on is, instead of trying to figure that out and doing the work to clarify my plan, I invariably just stare at the internet for hours, occasionally poking at the actual work

It's not even that it's "unproductive" - if the right thing for me is to not work on the project and play video games, then I should do that! But I don't take the time to figure it out

I have been aware of this tendency for years, and have repeatedly experienced "figuring out the plan" reducing my level of stress and making me happier. And yet! I just. Don't.

@SpindleyQ The trouble with building your own tools is never knowing if the bug is in what you're building or the builder itself...

@DHeadshot the great part about it is that you know it all backwards and forward and can go in and fix it immediately, though!

The bug actually turned out to be with the reference documentation I had used to create the opcode table, which had two of the JMP addressing modes reversed

@SpindleyQ Assuming you didn't make the tool years ago and have forgotten how it works in the meantime.

@SpindleyQ What were you reading? I'm curious about Filmation engine internals

Sign in to participate in the conversation

Hi! Game Making Social is a part of the Fediverse dedicated to being a well-moderated, cosy, friendly place to talk and share stuff about amateur videogame making, and everything surrounding that.

It's kinda an offshoot of Game Making Tools, which is a wiki(+) for a similar audience.

Game makers, game writers, game curators, etc. etc. most welcome!

Please read the rules before signing-up :)

PS: We have Animal Crossing, LSD, and Klik & Play emoji :3