Thank you. My favorite reasons for projects are "just because I wanted to" and "because someone told me I shouldn't", and this one ticks both boxes, haha.
Oh wow! Your project was actually really inspiring to me, thank you for making this. I was really impressed reading through it a while back. Is it alright if I add a link to your repo in my README?
Woah! I honestly feel more intimidated writing a CGI script in assembly than I was writing a server, lol. CGI support has been on my mind for a couple weeks, but I haven't really dug into it yet. I'd love to see yours if it's hosted anywhere! Could be a great reference when I do.
Really? It's a bit of a nonsense that I did so long ago so it's weird to hear someone interested in it...
The script has been lost to time. I wrote it 5+ computers ago and I don't even know where input that backup...
The overall gist is that CGI Bin specification sets Environmental variables, STDIN and STDOUT to various values. A minimal pure assembly that writes <h1> Hello World </h1> over stdout is your minimalist CGI Script.
A bit of research into what those STDIN/Environmental variables is needed for more. I knew this may e 20+ years ago but have long forgotten....
With access to the various input parameters offered over CGI, you can easily access form data (buttons and whatever clicked by the user). Use some smart file writing to store sessions and off you go....
-------
Maybe start with a Perl CGI tutorial. Then go backwards to C, and finally raw assembly by hand
Aw, that’s too bad. Sounds like it was a really fun project.
Thanks for the tips on CGI! Definitely going to look into it more. The server-side execution of CGI scripts definitely interests me more than the CGI scripts themselves, so I’ll probably just look for some existing (simple) CGI scripts and work on building the env vars and executing them.
Implementing a web server that can do CGI is actually probably easier than writing a CGI app. All you have to do for your server is set some environment variables and then spawn the executable.
Yeah, that's what I realized during this, too. You need to be much more explicit, but the way any given function works isn't fundamentally different. "strlen" will always iterate through a string searching for a NULL byte whether it's in C, Rust, Assembly, or whatever other language. I think it can feel almost more straightforward than other languages, since you're laying out exactly what the CPU needs to do, in what exact order.
> "strlen" will always iterate through a string searching for a NULL byte whether it's in C, Rust, Assembly
Not all languages use NULL terminated strings. I think Rust actually stores the string length alongside a pointer to the start of the string data. You can do the same in C, but you'd have to do it manually using a struct. In assembly you could do the same thing since you get to decide basically everything.
Pascal stores the length of the string next to the string, but the other thing of note is with the advent of Unicode, strlen is strlen, but often not actually the number you need/want.
Arguably a POSIX assembly programmer would also naturally keep strings along with their size, either explicitly or known implicitly, more similar to Pascal, not null-terminated, as that's how syscalls expect it. Null-terminated would chiefly be a libc-ism.
I argue that strlen is actually still quite often the number you need+want, because possibly the main use case for it is determining how many bytes long a string is.
It is, it just isn't always any more. When printing the string to the screen, the multibyte sequences need to be accounted for, like an emoji that's 2 bytes but only renders as one character too the screen.
Agreed. And super cool project. After seeing Matt Godbolts Advent of Compiler Optimisations in December I decided to do AoC in assembly. Was the most fun I had in years even though I didn't finish all days!
And super educational. Since then I've been pondering which problems require dropping down to the assembly level. E.g. implementing a JIT compiler, a coroutine runtime, etc.
It's a HTML browser for Pi Pico2, CLI, meant to support my in-house project running on a mesh of Pico2's. I really wanted to use RISC-V and it needed a webhook that serves a page on PIO wake. We are at the browser is written about 60%. The server is now already handled ;P I found this awesome project someone posted on HN. When I complete my project the browser will be released alongside. You can very likely reproduce it with less than a handful of prompts. One thing I really do believe, ideas are going to be the next open-source. LLM's can make ideas into things.
Yeah, I know MacOS syscalls aren't stable. Interesting point about Go, I hadn't heard about that. Unfortunately I'm a masochist though, and want to avoid libSystem.dylib as much as possible. The only reason I link against it at all is because MacOS requires it for executables to run, I never actually call into it. Figured I'd just update the syscall numbers if/when they change.
Honestly haven't benchmarked it, but I would imagine ymawky would be considerably slower than most fully-featured web servers. ymawky uses fork-per-connection, which is fundamentally slower than what production servers like nginx or Apache use. nginx uses event-driven IO (kqueue/epoll), which can handle thousands of concurrent connections without the overhead of forking the process on each request. Apache uses pools of threads which handle multiple connections without needing to be spawned per-request. A head-to-head against any other web server would mostly measure "fork-per-connection vs event loop/thread pools", which assembly has nothing to do with.
In a comparison between a similar fork-per-connection server written in C and this, I would imagine the throughput would be about the same, because the bottleneck in this model is fork() itself rather than the actual code. It probably matters more for binary size and startup time than requests/sec. Would be fun to actually benchmark, though.
Woah, that's really cool! I'm glad you did that even if you didn't need to. I honestly think everyone needs to write more assembly, because it's so much cooler.
Honestly, just reading existing assembly to get a feel for how it works, and then violently googling everything that goes wrong. The ARM Architecture Reference Manual (aka "The ARM ARM") ended up being really helpful for looking up what specific instructions do and how they're called. Another really helpful tool is writing something in C/C++, and compiling with "gcc -O1 -S file.c" to see the assembly gcc generated. It helps to mess around a lot with smaller programs in gdb or lldb.
reply