স্কিপ করে মূল কন্টেন্ট এ যান

3 পোস্টস সঙ্গে ট্যাগ্গেড "Review"

Review Tag

সমস্ত ট্যাগ্স দেখুন

Booting from Scratch in Wave: Printing ‘H’ at 0x7C00

· 3 মিনিট পড়া
LunaStev
Programming Language Engineer

Wave is a language that supports inline assembly. In its current pre-beta stage, it compiles through LLVM. So here's a thought — what if we could write a boot sector in Wave and run it using QEMU?

If this works, we might just be writing the very first line in Wave’s low-level programming history.

Today, we’re going to attempt exactly that: creating a boot sector using only Wave.


At the moment, Wave uses LLVM as a temporary backend. LLVM is a general-purpose compiler toolchain used by languages like C/C++, Rust, and Zig. However, Wave aims to eventually move away from LLVM and build its own dedicated compiler toolchain, called Whale — optimized specifically for Wave and free from the limitations of LLVM.

Of course, before that can happen, Wave’s frontend needs to be fully developed.


In a previous post, I showed how to print “Hello World” using only Wave. This time, we’ll take it one step further and write a boot sector, which runs below the OS level.


Typically, boot sectors and bootloaders are written in raw assembly. But Wave allows inline assembly using the asm {} block, making it possible to implement a boot sector directly in Wave.

The basics of Wave’s inline assembly syntax are explained in my earlier post: Printing Hello World from Scratch in Wave


Here’s the Wave code we’ll be using:

fun main() {
asm {
"mov ah, 0x0e"
"mov al, 0x48"
"int 0x10"
}
}

Before we proceed, let’s break it down a bit.

Code Breakdown

1. mov ah, 0x0e

  • Stores 0x0E in the AH register.
  • In x86 real mode, the int 0x10 BIOS interrupt is used for video services.
  • When AH = 0x0E, it selects the "TTY character output" function.

2. mov al, 0x48

  • Stores 0x48 (ASCII for 'H') in the AL register.
  • This sets the character to be printed.

3. int 0x10

  • Triggers the BIOS video interrupt.
  • AH = 0x0E → TTY mode
  • AL = character to print
  • BL = page number (defaults to 0)
  • As a result, it prints a single 'H' on the screen.

Now that the code is ready, it’s time to compile.

Currently, Wave only compiles to Linux binaries — formats like .img or .exe aren’t directly supported. However, Wave generates a temp.ll file (LLVM IR) when running wavec run main.wave, and we can use that to produce a bootable .img file.

To simplify the process, I’ve written a shell script called build.sh:

#!/bin/bash

set -e

LL_FILE=target/temp.ll
OBJ_FILE=boot.o
BIN_FILE=boot.bin
IMG_FILE=os.img

wavec run main.wave

llc -march=x86 -mattr=+16bit-mode -filetype=obj $LL_FILE -o $OBJ_FILE

ld -m elf_i386 -Ttext 0x7c00 --oformat binary $OBJ_FILE -o $BIN_FILE

echo -ne '\x55\xAA' | dd of=$BIN_FILE bs=1 seek=510 count=2 conv=notrunc

dd if=$BIN_FILE of=$IMG_FILE bs=512 count=1 conv=notrunc

echo "[+] Image created: $IMG_FILE"

Note: You’ll need the LLVM toolchain installed. I recommend using clang 14 for compatibility with Wave.

When you run ./build.sh, you’ll get output like this:

command

The os.img file is now ready.

Let’s boot it using QEMU:

qemu-system-i386 -drive format=raw,file=os.img

You should see the character 'H' printed to the screen like this:

qemu


Wave is still a work in progress, but this experiment shows that it's already capable of writing boot sectors through inline assembly. As the language evolves with more features and syntax improvements, we might one day build entire OS kernels in Wave.

Printing Hello World from Scratch in Wave

· 4 মিনিট পড়া
LunaStev
Programming Language Engineer

Wave fundamentally provides no standard functions out of the box. While println() and print() do currently exist, they are temporary functions intended for testing during development and are not official. The only officially supported built-in function in Wave is import().

But let’s be honest—if you had to build everything from scratch with no foundation, you probably wouldn't want to use the language. Fortunately, Wave supports a standard library, which allows you to use pre-written library functions. However, in bare-metal environments where standard libraries can't be used, you must implement everything yourself, step by step.


Overview

Today, we'll try printing "Hello World" in Wave with nothing but the bare essentials.

The Wave compiler only provides syntactic support—meaning it doesn't include any functional utilities like syscall(). Aside from import(), no functions are provided by default.

Wave supports inline assembly, written using the asm {} block.

Let's quickly go over the basic syntax of inline assembly in Wave.


Inline Assembly Syntax

asm {
"assembly instruction" // actual assembly code line by line
...
in("register") value // input register mapping
out("register") variable // output register mapping
}

1. "...": Assembly Instructions

  • These are raw CPU assembly instructions.
  • One instruction per line, multiple lines allowed.
  • Example: "mov rax, 1", "syscall"

2. in("rdi") s: Passing Input

  • This means the Wave variable s will be loaded into the rdi register.
  • On x86-64, rdi is the standard register for the first syscall argument.

in("register") expression -> Loads the given expression into the specified register.

3. out("rax") ret: Receiving Output

  • Syscall return values are typically stored in the rax register.
  • out("rax") ret means: assign the value in rax to the Wave variable ret.

out("register") variable -> Reads the register's value into the specified Wave variable.


Now let’s implement Hello World from scratch.

This is only supported in Wave version v0.1.3-pre-beta-nightly-2025-07-11 and later. The stable release is v0.1.3-pre-beta. Although major syntax changes are unlikely beyond this version, there may still be some minor differences. For best compatibility, it's recommended to use the v0.1.3-pre-beta family.

The first thing we need is a way to measure the length of a string.

Most programming languages use a len() function to calculate string length. As mentioned earlier, Wave provides no built-in functions other than import(), so we’ll implement our own len() function manually.

fun len(s: str) -> i32 {
var count: i32 = 0;
while (s[count] != 0) {
count = count + 1;
}
return count;
}

This function would typically belong in the standard library and is used to measure the length of a null-terminated string.


fun println_s(s: ptr<i8>) {
var l: i32 = len(s);
var ret: ptr<i8>;
asm {
"mov rax, 1"
"syscall"
in("rdi") 1
in("rsi") s
in("rdx") l
out("rax") ret
}

var nl: ptr<i8> = "\n";
var one: i32 = 1;
asm {
"mov rax, 1"
"syscall"
in("rdi") 1
in("rsi") nl
in("rdx") one
out("rax") ret
}
}

Wave currently includes a temporary testing function called println(), but to avoid confusion, we’ll define our own version called println_s() here.


import("println");

fun main() {
println_s("Hello World");
}

We use the import() function to load the println.wave file. If your main.wave file already contains the len() and println_s() functions, you won’t need to use import().


Running this will result in:

hello

You’ll see the Hello World output printed like this.


Wave doesn’t have a complete standard library yet, but the potential is definitely there. By taking full advantage of the compiler’s low-level capabilities, you can already build impressive programs—even today.


Site: https://wave-lang.dev/ GitHub: https://github.com/LunaStev/Wave Discord: https://discord.com/invite/Kuk2qXFjc5

Wave Language Performance Benchmark: Comparison with C and Rust

· 5 মিনিট পড়া
LunaStev
Programming Language Engineer

Wave is not aiming to be a "C replacement" like Zig, but it is an independent low-level language distinct from C. It has been under active development for about six months now.

Currently in its pre-beta phase, Wave still has several bugs and limitations—but that also means there's significant room for improvement. The potential is definitely there.

According to the roadmap, the frontend of Wave will be completed during the pre-beta stage. In the next phase, alpha, development will begin on Wave’s custom compiler toolchain called Whale, with the goal of moving away from LLVM. The current plan is for Whale to fully replace LLVM and become a highly optimized and independent compiler toolchain tailored specifically for Wave.

However, Wave also faces some clear challenges:

  • Lack of ecosystem: While the concept of Wave has been around for over a year, actual development only began six months ago. As a result, there is no standard library, and there are practically no real-world programs built with it. While algorithmic code is possible, practical application-level development is not. As of version v0.1.2-pre-beta, even basic input functions like input() are not yet implemented.
  • Lack of contributors: This is a common issue with all new programming languages. In the very early stages, it's normal to have zero contributors. Often, a single developer drives the entire project based on their own philosophy. That’s just how it is. Compared to existing utility tools, new programming languages attract attention much more slowly.

Wave is a compiled, low-level language and shouldn’t be compared to Python or JavaScript. It’s not in the same category, and Wave isn’t trying to compete with them either. If we had to choose competitors, C and Rust would be the most appropriate comparisons.

As mentioned earlier, Wave is still in its early stages, so it’s only natural that it’s significantly slower than both C and Rust. However, running benchmarks and comparing results is a meaningful exercise—it provides motivation and insight into potential optimizations.


benchmark

The graph above shows a performance comparison between Wave, C, and Rust. The benchmark measures how long it takes to run a simple string length function 100 million times.

All three languages were tested using AOT (ahead-of-time) compilation. Note that Wave does not and will not support JIT; it will be AOT-only by design.

Here are the source codes used for each language:

fun len(s: str) -> i32 {
var count: i32 = 0;
while (s[count] != 0) {
count = count + 1;
}
return count;
}

fun main() {
var message: str = "hello, world!";
var result: i32 = 0;
var i: i32 = 0;
while (i < 100000000) {
result = result + len(message);
i = i + 1;
}
println("Wave Result: {}", result);
}

Wave does not yet have a time-related library, so execution time was measured using the Linux time command. Please keep that in mind when interpreting the results.


#include <stdio.h>
#include <time.h>

int len(const char* s) {
int count = 0;
while (s[count] != 0) {
count++;
}
return count;
}

int main() {
const char* message = "hello, world!";
int result = 0;

clock_t start = clock();
for (int i = 0; i < 100000000; ++i) {
result += len(message);
}
clock_t end = clock();

double elapsed = (double)(end - start) / CLOCKS_PER_SEC;
printf("C Result: %d\n", result);
printf("C Time: %.3f seconds\n", elapsed);
return 0;
}

C is a time-tested language that has been the foundation of many systems and is deeply optimized. It is, without question, one of the most respected and traditional languages in software development.


use std::time::Instant;
use std::hint::black_box;

fn len(s: &str) -> i32 {
let bytes = s.as_bytes();
let mut count: i32 = 0;
while count < bytes.len() as i32 && bytes[count as usize] != 0 {
count += 1;
}
count
}

fn main() {
let message = black_box("hello, world!");
let mut result = 0;

let start = Instant::now();
for _ in 0..100_000_000 {
result += len(message);
}
let duration = start.elapsed();

println!("Rust Result: {}", result);
println!("Rust Time: {:.3?}", duration);
}

Rust is the language used to implement Wave itself, so of course it's included in the benchmark. Although Rust has only been around for about a decade, it has grown rapidly and now has a thriving ecosystem. While it hasn’t reached the level of C yet, it is without a doubt one of the most promising modern systems languages.


Some might look at this benchmark and say, "Wow, Wave is incredibly slow!"—and yes, it is slow right now. But don’t forget: Wave is only six months into development. Rust reached its first stable release after 10 years of development.

Wave is about 5× slower than C, and about 4.2× slower than Rust. However, considering that Wave is still a very young AOT-compiled language, this level of performance is already quite impressive. Reaching Rust-level performance won't happen overnight, but the potential is clearly there.


Site: https://wave-lang.dev/ GitHub: https://github.com/LunaStev/Wave Discord: https://discord.com/invite/Kuk2qXFjc5