├── LICENSE.txt ├── Makefile ├── README.md ├── analyzer.py ├── compiler.py ├── lexer.py ├── parser.py ├── symtable.py ├── syntree.py ├── test-programs ├── elementary │ ├── arithmetic.expected │ ├── arithmetic.wend │ ├── helloworld.expected │ ├── helloworld.wend │ ├── int-overflow.expected │ ├── int-overflow.wend │ ├── overload.expected │ ├── overload.wend │ ├── scope.expected │ └── scope.wend ├── gfx │ ├── README.md │ ├── breakout.c │ ├── breakout.wend │ ├── fire.c │ ├── fire.wend │ ├── fire_cursed.c │ ├── mandelbrot.c │ ├── metaballs.c │ ├── metaballs.wend │ ├── race.c │ ├── race.wend │ └── raytracer.wend ├── nontrivial │ ├── bitwise.expected │ ├── bitwise.wend │ ├── mandelbrot.expected │ ├── mandelbrot.wend │ ├── sqrt.expected │ ├── sqrt.wend │ ├── trig-hp12c.expected │ └── trig-hp12c.wend └── simple │ ├── eight-queens.expected │ ├── eight-queens.wend │ ├── fixed-point.expected │ ├── fixed-point.wend │ ├── mutual-recursion.expected │ ├── mutual-recursion.wend │ ├── popcount.expected │ ├── popcount.wend │ ├── sopfr.expected │ └── sopfr.wend ├── transasm.py └── transasm_recipe.py /LICENSE.txt: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := bash 2 | .ONESHELL: 3 | .SHELLFLAGS := -eu -o pipefail -c 4 | .DELETE_ON_ERROR: 5 | MAKEFLAGS += --warn-undefined-variables 6 | MAKEFLAGS += --no-builtin-rules 7 | 8 | BUILDDIR = build 9 | WENDDIR = test-programs 10 | TESTS = $(shell find $(WENDDIR) -name '*.wend' -not -path '*/gfx/*') 11 | 12 | GFXDIR = $(WENDDIR)/gfx 13 | GFXASM = $(patsubst $(GFXDIR)/%.wend, $(BUILDDIR)/%.s, $(wildcard $(GFXDIR)/*.wend)) 14 | GFXOBJ = $(patsubst $(GFXDIR)/%.wend, $(BUILDDIR)/%.o, $(wildcard $(GFXDIR)/*.wend)) 15 | GFXEXE = $(patsubst $(GFXDIR)/%.wend, $(BUILDDIR)/%.exe, $(wildcard $(GFXDIR)/*.wend)) 16 | 17 | .PHONY: all test clean 18 | 19 | all: test gfx 20 | 21 | $(BUILDDIR): 22 | @mkdir -p $(BUILDDIR) 23 | 24 | test: $(BUILDDIR) 25 | @for WEND in $(TESTS) ; do \ 26 | SRCDIR=$$(dirname $$WEND) ; \ 27 | DSTDIR=$$(dirname $$WEND | sed s/$(WENDDIR)/$(BUILDDIR)/) ; \ 28 | mkdir -p $$DSTDIR ; \ 29 | echo -n Testing $$WEND... ;\ 30 | EXP=$$(echo $$WEND|sed s/\.wend/\.expected/) ; \ 31 | ASM=$$DSTDIR/$$(basename $$WEND|sed s/\.wend/\.s/) ; \ 32 | OBJ=$$DSTDIR/$$(basename $$WEND|sed s/\.wend/\.o/) ; \ 33 | ELF=$$DSTDIR/$$(basename $$WEND|sed s/\.wend//) ; \ 34 | python3 compiler.py $$WEND > $$ASM ; \ 35 | as --march=i386 --32 -gstabs -o $$OBJ $$ASM ; \ 36 | ld -m elf_i386 $$OBJ -o $$ELF ; \ 37 | $$ELF | diff $$EXP - ; \ 38 | echo ' ok' ; \ 39 | done 40 | 41 | $(BUILDDIR)/%.exe: $(BUILDDIR)/%.o 42 | ld -m elf_i386 $< -o $@ 43 | 44 | $(BUILDDIR)/%.o: $(BUILDDIR)/%.s 45 | as --march=i386 --32 -o $@ $< 46 | 47 | $(BUILDDIR)/%.s: $(GFXDIR)/%.wend 48 | python3 compiler.py $< > $@ 49 | 50 | gfx: $(BUILDDIR) $(GFXASM) $(GFXOBJ) $(GFXEXE) 51 | 52 | clean: 53 | rm -rf $(BUILDDIR) 54 | find . -type f -name *.pyc -delete 55 | find . -type d -name __pycache__ -delete 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TinyCompiler - a 500-ish lines of code compiler in a weekend 2 | 3 | Have you ever wondered how a compiler works, but you never found courage to find out? 4 | Then this project is for you (**N.B.: a detailed description is available [here](https://ssloy.github.io/tinycompiler/)**). 5 | 6 | I have never had the chance to look under the hood either, but one week-end I have decided to to write a translator from the esoteric programming language *wend* (short for week-end), 7 | which I just invented myself, into regular GNU assembly. 8 | The goal is to keep the code as tiny as possible, 500-ish lines of python sounds great. 9 | 10 | Here is a program that uses virtually all concepts in Wend: 11 | ```cpp 12 | main() { 13 | // square root of a fixed-point number 14 | // stored in a 32 bit integer variable, shift is the precision 15 | 16 | int sqrt(int n, int shift) { 17 | int x; 18 | int x_old; 19 | int n_one; 20 | 21 | if n > 2147483647/shift { // pay attention to potential overflows 22 | return 2 * sqrt(n / 4, shift); 23 | } 24 | x = shift; // initial guess 1.0, can do better, but oh well 25 | n_one = n * shift; // need to compensate for fixp division 26 | while true { 27 | x_old = x; 28 | x = (x + n_one / x) / 2; 29 | if abs(x - x_old) <= 1 { 30 | return x; 31 | } 32 | } 33 | } 34 | 35 | int abs(int x) { 36 | if x < 0 { 37 | return -x; 38 | } else { 39 | return x; 40 | } 41 | } 42 | 43 | // 25735 is approximately equal to pi * 8192; 44 | // expected value of the output is sqrt(pi) * 8192 approx 14519 45 | 46 | println sqrt(25735, 8192); 47 | } 48 | ``` 49 | 50 | ## run tests 51 | ```sh 52 | make test 53 | ``` 54 | 55 | ## Graphics! 56 | It is so dull to compute Fibonacci numbers, so here are more eyecandy examples for our compiler, check [test-programs/gfx/*.wend](https://github.com/ssloy/tinycompiler/tree/main/test-programs/gfx) files. 57 | ```sh 58 | make gfx 59 | ``` 60 | ### Mandelbrot set 61 | 62 | 63 | ### Ray tracer 64 | ![](https://ssloy.github.io/tinycompiler/home/raytracer.png) 65 | 66 | ### Zero-player breakout game 67 | ![](https://ssloy.github.io/tinycompiler/home/breakout.gif) 68 | 69 | ### Fire 70 | ![](https://ssloy.github.io/tinycompiler/home/fire.gif) 71 | 72 | ### Sunset race 73 | ![](https://ssloy.github.io/tinycompiler/home/sunset-race.gif) 74 | 75 | ### Metaballs 76 | ![](https://ssloy.github.io/tinycompiler/home/metaballs.gif) 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /analyzer.py: -------------------------------------------------------------------------------- 1 | from syntree import * 2 | from symtable import * 3 | 4 | def decorate(ast): 5 | if not isinstance(ast, Function) or ast.name != 'main' or ast.deco['type'] != Type.VOID or len(ast.args)>0: 6 | raise Exception('Cannot find a valid entry point') 7 | symtable = SymbolTable() 8 | symtable.add_fun(ast.name, [], ast.deco) 9 | ast.deco['strings'] = set() # collection of constant strings from the program 10 | process_scope(ast, symtable) 11 | process_scope(ast, symtable) 12 | ast.deco['scope_cnt'] = symtable.scope_cnt # total number of functions, necessary for the static scope display table allocation 13 | 14 | def process_scope(fun, symtable): 15 | symtable.push_scope(fun.deco) 16 | for v in fun.args: # process function arguments 17 | symtable.add_var(v.name, v.deco) 18 | for v in fun.var: # process local variables 19 | symtable.add_var(v.name, v.deco) 20 | for f in fun.fun: # process nested functions: first add function symbols to the table 21 | symtable.add_fun(f.name, [v.deco['type'] for v in f.args], f.deco) 22 | for f in fun.fun: # then process nested function bodies 23 | process_scope(f, symtable) 24 | for s in fun.body: # process the list of statements 25 | process_instruction(s, symtable) 26 | symtable.pop_scope() 27 | 28 | def process_instruction(n, symtable): 29 | match n: 30 | case Print(): # no type checking is necessary 31 | process_instruction(n.expr, symtable) 32 | case Return(): 33 | if n.expr is None: return # TODO semantic check for return; in non-void functions 34 | process_instruction(n.expr, symtable) 35 | if symtable.ret_stack[-1]['type'] != n.expr.deco['type']: 36 | raise Exception('Incompatible types in return statement, line %s', n.deco['lineno']) 37 | case Assign(): 38 | process_instruction(n.expr, symtable) 39 | n.deco |= symtable.find_var(n.name) 40 | if n.deco['type'] != n.expr.deco['type']: 41 | raise Exception('Incompatible types in assignment statement, line %s', n.deco['lineno']) 42 | case While(): 43 | process_instruction(n.expr, symtable) 44 | if n.expr.deco['type'] != Type.BOOL: 45 | raise Exception('Non-boolean expression in while statement, line %s', n.deco['lineno']) 46 | for s in n.body: 47 | process_instruction(s, symtable) 48 | case IfThenElse(): 49 | process_instruction(n.expr, symtable) 50 | if n.expr.deco['type'] != Type.BOOL: 51 | raise Exception('Non-boolean expression in if statement, line %s', n.deco['lineno']) 52 | for s in n.ibody + n.ebody: 53 | process_instruction(s, symtable) 54 | case ArithOp(): 55 | process_instruction(n.left, symtable) 56 | process_instruction(n.right, symtable) 57 | if n.left.deco['type'] != Type.INT or n.right.deco['type'] != Type.INT: 58 | raise Exception('Arithmetic operation over non-integer type in line %s', n.deco['lineno']) 59 | case LogicOp(): 60 | process_instruction(n.left, symtable) 61 | process_instruction(n.right, symtable) 62 | if (n.left.deco['type'] != n.right.deco['type']) or \ 63 | (n.op in ['<=', '<', '>=', '>'] and n.left.deco['type'] != Type.INT) or \ 64 | (n.op in ['&&', '||'] and n.left.deco['type'] != Type.BOOL): 65 | raise Exception('Boolean operation over incompatible types in line %s', n.deco['lineno']) 66 | case Var(): # no type checking is necessary 67 | n.deco |= symtable.find_var(n.name) 68 | case FunCall(): 69 | for s in n.args: 70 | process_instruction(s, symtable) 71 | n.deco |= symtable.find_fun(n.name, [ a.deco['type'] for a in n.args ]) 72 | case String(): # no type checking is necessary 73 | symtable.ret_stack[1]['strings'].add((n.deco['label'], n.value)) 74 | case Integer() | Boolean(): pass # no type checking is necessary 75 | case other: raise Exception('Unknown instruction', n) 76 | -------------------------------------------------------------------------------- /compiler.py: -------------------------------------------------------------------------------- 1 | import io, sys 2 | from lexer import WendLexer 3 | from parser import WendParser 4 | from analyzer import decorate 5 | from transasm import transasm 6 | 7 | if len(sys.argv)!=2: 8 | sys.exit('Usage: compiler.py path/source.wend') 9 | try: 10 | f = open(sys.argv[1], 'r') 11 | tokens = WendLexer().tokenize(f.read()) 12 | ast = WendParser().parse(tokens) 13 | decorate(ast) 14 | print(transasm(ast)) 15 | except Exception as e: 16 | print(e) 17 | -------------------------------------------------------------------------------- /lexer.py: -------------------------------------------------------------------------------- 1 | class Token: 2 | def __init__(self, t, v, l=None): 3 | self.type, self.value, self.lineno = t, v, (l or 0) 4 | 5 | def __repr__(self): 6 | return f'Token(type={self.type!r}, value={self.value!r}, lineno={self.lineno!r})' 7 | 8 | class WendLexer: 9 | keywords = {'true':'BOOLEAN','false':'BOOLEAN','print':'PRINT','println':'PRINT','int':'TYPE','bool':'TYPE','if':'IF','else':'ELSE','while':'WHILE','return':'RETURN'} 10 | double_char = {'==':'COMP', '<=':'COMP', '>=':'COMP', '!=':'COMP', '&&':'AND', '||':'OR'} 11 | single_char = {'=':'ASSIGN','<':'COMP', '>':'COMP', '!':'NOT', '+':'PLUS', '-':'MINUS', '/':'DIVIDE', '*':'TIMES', '%':'MOD','(':'LPAREN',')':'RPAREN', '{':'BEGIN', '}':'END', ';':'SEMICOLON', ',':'COMMA'} 12 | tokens = {'ID', 'STRING', 'INTEGER'} | { v for k, v in keywords.items() | double_char.items() | single_char.items() } 13 | 14 | def tokenize(self, text): 15 | lineno, idx, state, accum = 0, 0, 0, '' 16 | while idx0: # chomp those symbols from the stack 118 | chew = stack[-chomp:] 119 | del stack[-chomp:] 120 | stack.append(self.grammar[rule.rule][2](chew)) # put AST node back on the stack 121 | return stack[0] # normally we have only one symbol left on the stack 122 | 123 | def parse(self, tokens): 124 | return self.build_syntree( self.recognize(tokens) ) 125 | -------------------------------------------------------------------------------- /symtable.py: -------------------------------------------------------------------------------- 1 | class SymbolTable(): 2 | def __init__(self): 3 | self.variables = [{}] # stack of variable symbol tables 4 | self.functions = [{}] # stack of function symbol tables 5 | self.ret_stack = [ None ] # stack of enclosing function symbols, useful for return statements 6 | self.scope_cnt = 0 # global scope counter for the display table allocation 7 | 8 | def add_fun(self, name, argtypes, deco): # a function can be identified by its name and a list of argument types, e.g. 9 | signature = (name, *argtypes) # fun foo(x:bool, y:int) : int {...} has ('foo',Type.BOOL,Type.INT) signature 10 | if signature in self.functions[-1]: 11 | raise Exception('Double declaration of the function %s %s' % (signature[0], signature[1:])) 12 | self.functions[-1][signature] = deco 13 | deco['scope'] = self.scope_cnt # id for the function block in the scope display table 14 | self.scope_cnt += 1 15 | 16 | def add_var(self, name, deco): 17 | if name in self.variables[-1]: 18 | raise Exception('Double declaration of the variable %s' % name) 19 | self.variables[-1][name] = deco 20 | deco['scope'] = self.ret_stack[-1]['scope'] # pointer to the display entry 21 | deco['offset'] = self.ret_stack[-1]['var_cnt'] # id of the variable in the corresponding stack frame 22 | self.ret_stack[-1]['var_cnt'] += 1 23 | 24 | def push_scope(self, deco): 25 | self.variables.append({}) 26 | self.functions.append({}) 27 | self.ret_stack.append(deco) 28 | deco['var_cnt'] = 0 # reset the per scope variable counter 29 | 30 | def pop_scope(self): 31 | self.variables.pop() 32 | self.functions.pop() 33 | self.ret_stack.pop() 34 | 35 | def find_var(self, name): 36 | for i in reversed(range(len(self.variables))): 37 | if name in self.variables[i]: 38 | return self.variables[i][name] 39 | raise Exception('No declaration for the variable %s' % name) 40 | 41 | def find_fun(self, name, argtypes): 42 | signature = (name, *argtypes) 43 | for i in reversed(range(len(self.functions))): 44 | if signature in self.functions[i]: 45 | return self.functions[i][signature] 46 | raise Exception('No declaration for the function %s' % signature[0], signature[1:]) 47 | -------------------------------------------------------------------------------- /syntree.py: -------------------------------------------------------------------------------- 1 | class LabelFactory: # this is a suffix to add to all function names 2 | counter = 0 # in particular, it is useful for function overloading 3 | @staticmethod # it is also useful for different goto labels (loops, conditional statements etc) in assembly code 4 | def cur_label(): 5 | return "uniqstr%d" % LabelFactory.counter 6 | def new_label(): 7 | LabelFactory.counter += 1 8 | return "uniqstr%d" % LabelFactory.counter 9 | 10 | class Type: 11 | VOID = 0 12 | INT = 1 13 | BOOL = 2 14 | STRING = 3 15 | 16 | class Function: 17 | def __init__(self, name, args, var, fun, body, deco): 18 | self.name = name # function name, string 19 | self.args = args # function arguments, list of tuples (name, type) 20 | self.var = var # local variables, list of tuples (name, type) 21 | self.fun = fun # nested functions, list of Function nodes 22 | self.body = body # function body, list of statement nodes (Print/Return/Assign/While/IfThenElse/FunCall) 23 | self.deco = deco | {'label' : name+'_'+LabelFactory.new_label()} # decoration dictionary to be filled by the parser (line number) and by the semantic analyzer (return type, scope id etc) 24 | 25 | # statements 26 | class Print: 27 | def __init__(self, expr, newline, deco): 28 | self.expr, self.newline, self.deco = expr, newline, deco 29 | 30 | class Return: 31 | def __init__(self, expr, deco): 32 | self.expr, self.deco = expr, deco 33 | 34 | class Assign: 35 | def __init__(self, name, expr, deco): 36 | self.name, self.expr, self.deco = name, expr, deco 37 | 38 | class While: 39 | def __init__(self, expr, body, deco): 40 | self.expr, self.body, self.deco = expr, body, deco 41 | 42 | class IfThenElse: 43 | def __init__(self, expr, ibody, ebody, deco): 44 | self.expr, self.ibody, self.ebody, self.deco = expr, ibody, ebody, deco 45 | 46 | # expressions 47 | class ArithOp: 48 | def __init__(self, op, left, right, deco): 49 | self.op, self.left, self.right, self.deco = op, left, right, deco | {'type' : Type.INT} 50 | 51 | class LogicOp: 52 | def __init__(self, op, left, right, deco): 53 | self.op, self.left, self.right, self.deco = op, left, right, deco | {'type' : Type.BOOL} 54 | 55 | class Integer: 56 | def __init__(self, value, deco): 57 | self.value, self.deco = value, deco | {'type' : Type.INT} 58 | 59 | class Boolean: 60 | def __init__(self, value, deco): 61 | self.value, self.deco = value, deco | {'type' : Type.BOOL} 62 | 63 | class String: 64 | def __init__(self, value, deco): 65 | self.value, self.deco = value, deco | {'type' : Type.STRING, 'label' : LabelFactory.new_label() } 66 | 67 | class Var: 68 | def __init__(self, name, deco): 69 | self.name, self.deco = name, deco 70 | 71 | class FunCall: # depending on the context, a function call can be a statement or an expression 72 | def __init__(self, name, args, deco): 73 | self.name, self.args, self.deco = name, args, deco 74 | -------------------------------------------------------------------------------- /test-programs/elementary/arithmetic.expected: -------------------------------------------------------------------------------- 1 | -7 2 | -17 3 | 1 4 | 15 5 | false 6 | true 7 | true 8 | false 9 | true 10 | false 11 | true 12 | -------------------------------------------------------------------------------- /test-programs/elementary/arithmetic.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | println +3 + 5 * -2; 3 | println 3 - 4 * 5; 4 | println 3 - 10 / 5; 5 | println (-2+3*4)+5/(7-6)%8; 6 | println 5<3; 7 | println 3==3; 8 | println 3 * (4 + 5) / 7 == 3; 9 | println true && false; 10 | println true && false || true; 11 | println !true; 12 | println 3<=3 && 3>=3; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /test-programs/elementary/helloworld.expected: -------------------------------------------------------------------------------- 1 | hello //world 2 | -------------------------------------------------------------------------------- /test-programs/elementary/helloworld.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | println "hello //world"; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test-programs/elementary/int-overflow.expected: -------------------------------------------------------------------------------- 1 | -2147483648 2 | 2147483647 3 | -------------------------------------------------------------------------------- /test-programs/elementary/int-overflow.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | println 256*256*256*128; // one overflow 3 | println 256*256*256*128 - 1; // two overflows 4 | } 5 | 6 | -------------------------------------------------------------------------------- /test-programs/elementary/overload.expected: -------------------------------------------------------------------------------- 1 | 0 2 | 1 3 | 2 4 | -------------------------------------------------------------------------------- /test-programs/elementary/overload.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | int main(int x) { 3 | return x; 4 | } 5 | 6 | int main(int x, int y) { 7 | return x + y; 8 | } 9 | 10 | int main(bool x) { 11 | if x { 12 | return 0; 13 | } 14 | return 2; 15 | } 16 | 17 | println main(0); 18 | println main(0, 1); 19 | println main(false); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /test-programs/elementary/scope.expected: -------------------------------------------------------------------------------- 1 | 3 2 | -------------------------------------------------------------------------------- /test-programs/elementary/scope.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | int x; 3 | 4 | f(int y) { 5 | g(int y) { 6 | x = y + 1; 7 | } 8 | 9 | x = y; 10 | g(x + 1); 11 | } 12 | 13 | x = 0; 14 | f(x + 1); 15 | println x; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /test-programs/gfx/README.md: -------------------------------------------------------------------------------- 1 | # Graphics! 2 | It is so dull to compute Fibonacci numbers, so here are more eyecandy examples for tinycompiler. 3 | 4 | ### build wend version: 5 | ```sh 6 | git clone https://github.com/ssloy/tinycompiler.git && 7 | cd tinycompiler && 8 | make gfx 9 | ``` 10 | 11 | ### build C version: 12 | ```sh 13 | git clone https://github.com/ssloy/tinycompiler.git && 14 | cd tinycompiler/test-programs/gfx/ && 15 | gcc breakout.c -o breakout && 16 | gcc fire.c -o fire && 17 | gcc mandelbrot.c -o mandelbrot && 18 | gcc metaballs.c -o metaballs -lm && 19 | gcc race.c -o race -lm 20 | ``` 21 | 22 | ### Mandelbrot set 23 | 24 | 25 | ### Ray tracer 26 | ![](https://ssloy.github.io/tinycompiler/home/raytracer.png) 27 | 28 | ### Zero-player breakout game 29 | ![](https://ssloy.github.io/tinycompiler/home/breakout.gif) 30 | 31 | ### Fire 32 | ![](https://ssloy.github.io/tinycompiler/home/fire.gif) 33 | 34 | ### Sunset race 35 | ![](https://ssloy.github.io/tinycompiler/home/sunset-race.gif) 36 | 37 | ### Metaballs 38 | ![](https://ssloy.github.io/tinycompiler/home/metaballs.gif) 39 | -------------------------------------------------------------------------------- /test-programs/gfx/breakout.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // for usleep() 4 | 5 | #define ABS(a) (((a) < 0) ? -(a) : (a)) 6 | #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) 7 | 8 | const char* palette[2] = { "187;204;51", "213;48;49" }; 9 | struct Ball { int x[2], v[2]; } balls[2]; 10 | bool bricks[48*48]; // The battlefield. The brick grid is 16x16, but it is zoomed x4 for more fluid motion 11 | 12 | int xor_bricks(int x, int y, bool ball) { // flip a 4x4 block of the battlefield 13 | int hit = -1; // N.B. the block is not aligned to the brick grid 14 | for (int i=0; i<16; i++) { 15 | int idx = x+i%4+(y+i/4)*48; 16 | if ((bricks[idx] ^= true) == ball) hit = idx; // if a ball hits a brick, return the brick position 17 | } 18 | return hit; 19 | } 20 | 21 | int main() { 22 | balls[0].x[0] = 3; balls[0].x[1] = 3; // initial position and speed for two balls 23 | balls[0].v[0] = 1; balls[0].v[1] = 3; 24 | balls[1].x[0] = 41; balls[1].x[1] = 43; 25 | balls[1].v[0] = -1; balls[1].v[1] = -3; 26 | 27 | for (int i=0; i<48*48; i++) 28 | bricks[i] = i<48*48/2; // initialize the battlefield 29 | 30 | printf("\033[2J"); // clear screen 31 | for (;;) { 32 | printf("\033[H"); // home 33 | for (int b=0; b<2; b++) // for each ball 34 | for (int d=0; d<2; d++ ) // for each coordinate 35 | for (int i=0; i 0 ? 1 : -1; 37 | if (balls[b].x[d]<0 || balls[b].x[d]>48-4) { // bounce the ball off the walls 38 | balls[b].x[d] = CLAMP(balls[b].x[d], 0, 48-4); 39 | balls[b].v[d] = -balls[b].v[d]; 40 | } 41 | int hit = xor_bricks(balls[b].x[0], balls[b].x[1], !b); // draw the ball and check if it hits a brick 42 | xor_bricks(balls[b].x[0], balls[b].x[1], !b); // immediately clear the ball 43 | if (hit!=-1) { // if we hit a brick 44 | xor_bricks(((hit%48)/4)*4, ((hit/48)/4)*4, !b); // snap the hit to the brick grid and break the brick 45 | balls[b].v[d] = -balls[b].v[d]; // bounce the ball off the brick 46 | balls[b].x[d] += balls[b].v[d] > 0 ? 1 : -1; 47 | } 48 | } 49 | 50 | for (int b=0; b<2; b++) // imprint the balls into the battlefield 51 | xor_bricks(balls[b].x[0], balls[b].x[1], !b); 52 | for (int j=0; j<48; j+=2) { // show the battlefield 53 | for (int i=0; i<48; i++) { 54 | printf("\033[48;2;%sm",palette[bricks[i + (j+0)*48]]); // set background color 55 | printf("\033[38;2;%sm",palette[bricks[i + (j+1)*48]]); // set foreground color 56 | printf("\xE2\x96\x83"); // half-block Unicode symbol 57 | } 58 | printf("\033[49m\n"); 59 | } 60 | for (int b=0; b<2; b++) // clear the balls from the battlefield 61 | xor_bricks(balls[b].x[0], balls[b].x[1], !b); 62 | usleep(1000000/50); // fps 63 | } 64 | return 0; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /test-programs/gfx/fire.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define WIDTH 80 7 | #define HEIGHT 25 8 | #define FPS 30 9 | 10 | const char* palette[256] = { 11 | #define ANSIRGB(R,G,B) "\033[48;2;" #R ";" #G ";" #B "m " 12 | ANSIRGB( 0, 0, 0), ANSIRGB( 0, 4, 4), ANSIRGB( 0, 16, 20), ANSIRGB( 0, 28, 36), 13 | ANSIRGB( 0, 32, 44), ANSIRGB( 0, 36, 48), ANSIRGB( 60, 24, 32), ANSIRGB(100, 16, 16), 14 | ANSIRGB(132, 12, 12), ANSIRGB(160, 8, 8), ANSIRGB(192, 8, 8), ANSIRGB(220, 4, 4), 15 | ANSIRGB(252, 0, 0), ANSIRGB(252, 0, 0), ANSIRGB(252, 12, 0), ANSIRGB(252, 28, 0), 16 | ANSIRGB(252, 40, 0), ANSIRGB(252, 52, 0), ANSIRGB(252, 64, 0), ANSIRGB(252, 80, 0), 17 | ANSIRGB(252, 92, 0), ANSIRGB(252, 104, 0), ANSIRGB(252, 116, 0), ANSIRGB(252, 132, 0), 18 | ANSIRGB(252, 144, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 160, 0), 19 | ANSIRGB(252, 160, 0), ANSIRGB(252, 164, 0), ANSIRGB(252, 168, 0), ANSIRGB(252, 168, 0), 20 | ANSIRGB(252, 172, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 180, 0), 21 | ANSIRGB(252, 180, 0), ANSIRGB(252, 184, 0), ANSIRGB(252, 188, 0), ANSIRGB(252, 188, 0), 22 | ANSIRGB(252, 192, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 200, 0), 23 | ANSIRGB(252, 204, 0), ANSIRGB(252, 204, 0), ANSIRGB(252, 208, 0), ANSIRGB(252, 212, 0), 24 | ANSIRGB(252, 212, 0), ANSIRGB(252, 216, 0), ANSIRGB(252, 220, 0), ANSIRGB(252, 220, 0), 25 | ANSIRGB(252, 224, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 232, 0), 26 | ANSIRGB(252, 232, 0), ANSIRGB(252, 236, 0), ANSIRGB(252, 240, 0), ANSIRGB(252, 240, 0), 27 | ANSIRGB(252, 244, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 252, 0), 28 | #define W ANSIRGB(252,252,252) 29 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 30 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 31 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 32 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 33 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 34 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 35 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 36 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W 37 | #undef W 38 | #undef ANSIRGB 39 | }; 40 | 41 | static uint8_t fire[WIDTH * HEIGHT]; 42 | 43 | void line_blur(int offset, int step, int nsteps) { 44 | uint8_t circ[3] = {0, fire[offset], fire[offset+step]}; 45 | uint8_t beg = 1; 46 | for (int i=0; i0) 72 | fire[i]--; 73 | 74 | for (int i = 0; i 2 | #include 3 | #include 4 | #include 5 | 6 | #define WIDTH 80 7 | #define HEIGHT 25 8 | #define FPS 30 9 | 10 | const char* palette[256] = { 11 | #define ANSIRGB(R,G,B) "\033[48;2;" #R ";" #G ";" #B "m " 12 | ANSIRGB( 0, 0, 0), ANSIRGB( 0, 4, 4), ANSIRGB( 0, 16, 20), ANSIRGB( 0, 28, 36), 13 | ANSIRGB( 0, 32, 44), ANSIRGB( 0, 36, 48), ANSIRGB( 60, 24, 32), ANSIRGB(100, 16, 16), 14 | ANSIRGB(132, 12, 12), ANSIRGB(160, 8, 8), ANSIRGB(192, 8, 8), ANSIRGB(220, 4, 4), 15 | ANSIRGB(252, 0, 0), ANSIRGB(252, 0, 0), ANSIRGB(252, 12, 0), ANSIRGB(252, 28, 0), 16 | ANSIRGB(252, 40, 0), ANSIRGB(252, 52, 0), ANSIRGB(252, 64, 0), ANSIRGB(252, 80, 0), 17 | ANSIRGB(252, 92, 0), ANSIRGB(252, 104, 0), ANSIRGB(252, 116, 0), ANSIRGB(252, 132, 0), 18 | ANSIRGB(252, 144, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 156, 0), ANSIRGB(252, 160, 0), 19 | ANSIRGB(252, 160, 0), ANSIRGB(252, 164, 0), ANSIRGB(252, 168, 0), ANSIRGB(252, 168, 0), 20 | ANSIRGB(252, 172, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 176, 0), ANSIRGB(252, 180, 0), 21 | ANSIRGB(252, 180, 0), ANSIRGB(252, 184, 0), ANSIRGB(252, 188, 0), ANSIRGB(252, 188, 0), 22 | ANSIRGB(252, 192, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 196, 0), ANSIRGB(252, 200, 0), 23 | ANSIRGB(252, 204, 0), ANSIRGB(252, 204, 0), ANSIRGB(252, 208, 0), ANSIRGB(252, 212, 0), 24 | ANSIRGB(252, 212, 0), ANSIRGB(252, 216, 0), ANSIRGB(252, 220, 0), ANSIRGB(252, 220, 0), 25 | ANSIRGB(252, 224, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 228, 0), ANSIRGB(252, 232, 0), 26 | ANSIRGB(252, 232, 0), ANSIRGB(252, 236, 0), ANSIRGB(252, 240, 0), ANSIRGB(252, 240, 0), 27 | ANSIRGB(252, 244, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 248, 0), ANSIRGB(252, 252, 0), 28 | #define W ANSIRGB(252,252,252) 29 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 30 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 31 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 32 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 33 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 34 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 35 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, 36 | W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W 37 | #undef W 38 | #undef ANSIRGB 39 | }; 40 | 41 | #define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) 42 | #define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) 43 | #define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) 44 | #define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__))) 45 | #define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__))) 46 | #define EVAL5(...) __VA_ARGS__ 47 | 48 | #define CONCAT(a,b) a##b 49 | 50 | #define IF_ELSE(b) CONCAT(IF_,b) 51 | #define IF_0(...) ELSE_0 52 | #define IF_1(...) __VA_ARGS__ ELSE_1 53 | #define ELSE_0(...) __VA_ARGS__ 54 | #define ELSE_1(...) 55 | 56 | #define SECOND(a, b, ...) b 57 | #define TEST(...) SECOND(__VA_ARGS__, 0) 58 | #define ISZERO(n) TEST(ISZERO_ ## n) 59 | #define ISZERO_0 ~, 1 60 | 61 | #define DEC(n) CONCAT(DEC_, n) 62 | #define DEC_0 0 63 | #define DEC_1 0 64 | #define DEC_2 1 65 | #define DEC_3 2 66 | #define DEC_4 3 67 | #define DEC_5 4 68 | #define DEC_6 5 69 | #define DEC_7 6 70 | #define DEC_8 7 71 | #define DEC_9 8 72 | #define DEC_10 9 73 | #define DEC_11 10 74 | 75 | #define EMPTY() 76 | #define DELAY(id) id EMPTY() 77 | #define DELAY2(...) __VA_ARGS__ DELAY(EMPTY)() 78 | 79 | #define DEPTH 11 80 | #define FIRE(d,id) \ 81 | IF_ELSE(ISZERO(d)) \ 82 | ( uint8_t id; ) \ 83 | ( \ 84 | DELAY2(FIRE_)( DEC(d), id##0 ) \ 85 | DELAY2(FIRE_)( DEC(d), id##1 ) \ 86 | ) 87 | #define FIRE_(...) DELAY(FIRE)(__VA_ARGS__) 88 | EVAL(FIRE(DEPTH,fire)) 89 | 90 | uint8_t get_fire(int i) { 91 | #define GETTER(d,m,id) \ 92 | IF_ELSE(ISZERO(d)) \ 93 | ( return id; ) \ 94 | ( \ 95 | if (i<(m)) \ 96 | DELAY2(GETTER_)(DEC(d), ((m)-(1<0) 145 | set_fire(i, get_fire(i)-1); 146 | 147 | for (int i = 0; i 2 | #include 3 | #include 4 | 5 | int main() { 6 | char c[] = ".,'~=+:;[/<&?oxOX# "; 7 | int32_t l = strlen(c); 8 | 9 | int32_t w = 80; 10 | int32_t h = 25; 11 | int32_t s = 1<<13; 12 | 13 | for (int32_t y=0; y 2 | #include 3 | #include 4 | 5 | #define FPS 24 6 | #define WIDTH 80 7 | #define HEIGHT 50 8 | #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) 9 | 10 | float iTime = 0; 11 | 12 | void mainImage(int fragCoord_x, int fragCoord_y) { // kinda shadertoy naming :) 13 | float u = (2.*fragCoord_x - WIDTH )/HEIGHT; 14 | float v = (2.*fragCoord_y - HEIGHT)/HEIGHT; 15 | float d1 = .6/sqrt(pow(sin(iTime*.5) -u, 2) + pow(sin(iTime*.5)-v, 2)); // linear motion 16 | float d2 = .6/sqrt(pow(sin(iTime*.5) -u, 2) + pow(cos(iTime*.5)-v, 2)); // circular motion 17 | float d3 = .6/sqrt(pow(sin(iTime*.25)-u, 2) + pow(sin(iTime) -v, 2)); // wave 18 | float sdf = d1 + d2 + d3 - 2.2; // metaballs signed distance function 19 | float fragColor_r = 255*1.7*(sdf+1); // orange halo (red and green channels) 20 | float fragColor_g = 255*0.8*(sdf+1); 21 | float fragColor_b = 255*(sdf<0 ? 0 : 1); // cold-white metaballs (step function) 22 | printf("%d;%d;%d", CLAMP((int)fragColor_r,0,255), CLAMP((int)fragColor_g, 0, 255), CLAMP((int)fragColor_b, 0, 255)); 23 | } 24 | 25 | int main() { 26 | printf("\033[2J\033[?25l"); // clear screen and hide cursor 27 | for (;;) { 28 | printf("\033[H"); // home 29 | for (int j = 0; j0 { sign = 1; } else { sign = -1; x = -x; } // 13 | while x>+79060768 { x = x - 105414357; } // reduce the argument to the acceptable range 14 | if x>26353589 { return sign*cos24(x - 26353589); } // 15 | return sign*(x/4096)*((16777216 + (((x/4096)*(x/4096))/4096)*((((x/4096)*(x/4096))/131 - 2785856)/4096))/4096); 16 | } 17 | 18 | // cos(x) = 1 + x^2 * (0.03705 * x^2 - 0.49670)) 19 | // this formula works pretty well in the range [-pi/2, +pi/2] 20 | int cos24(int x) { 21 | if x<0 { x = -x; } // 22 | while x>79060768 { x = x - 105414357; } // reduce the argument to the acceptable range 23 | if x>26353589 { return -sin24(x - 26353589); } // 24 | return 16777216 + (((x/4096)*(x/4096))/4096)*((((x/4096)*(x/4096))/27 - 8333243)/4096); 25 | } 26 | 27 | // square root of a fixed-point number 28 | // stored in a 32 bit integer variable, shift is the precision 29 | int sqrt(int n, int shift) { 30 | int x; 31 | int x_old; 32 | int n_one; 33 | 34 | int abs(int x) { 35 | if x < 0 { 36 | return -x; 37 | } else { 38 | return x; 39 | } 40 | } 41 | 42 | if n > 2147483647/shift { // pay attention to potential overflows 43 | return 2 * sqrt(n / 4, shift); 44 | } 45 | x = shift; // initial guess 1.0, can do better, but oh well 46 | n_one = n * shift; // need to compensate for fixp division 47 | while true { 48 | x_old = x; 49 | x = (x + n_one / x) / 2; 50 | if abs(x - x_old) <= 1 { 51 | return x; 52 | } 53 | } 54 | } 55 | 56 | mainImage(int fragCoord_x, int fragCoord_y) { // kinda shadertoy naming :) 57 | int u; int v; 58 | int fragColor_r; int fragColor_g; int fragColor_b; 59 | int sdf; 60 | int a; int b; int c; int d; int e; 61 | int d1; int d2; int d3; 62 | 63 | int CLAMP(int x, int low, int high) { 64 | if x>high { 65 | return high; 66 | } 67 | if xPOW2_24*100 { // 100 approx 32 pi :) 118 | iTime = 0; 119 | } 120 | } 121 | } 122 | 123 | -------------------------------------------------------------------------------- /test-programs/gfx/race.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define FPS 24 6 | #define MULTI 2 7 | #define WIDTH (80*MULTI) 8 | #define HEIGHT (50*MULTI) 9 | #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) 10 | float iTime = 0; 11 | 12 | void mainImage(int fragCoord_x, int fragCoord_y, int *fragColor_r, int *fragColor_g, int *fragColor_b) { // kinda shadertoy naming :) 13 | float u = (fragCoord_x - WIDTH/2.)/HEIGHT; 14 | float v = -(fragCoord_y - HEIGHT/2.)/HEIGHT; 15 | float horizon = .32; 16 | float persp = 1./((horizon+.05)-v); 17 | float t = sin(iTime/4.); 18 | float t3 = t*t*t; // cubic law allows for longer straight roads 19 | float x = pow(t3 + u*persp - .05*t3*persp*persp, 2.); 20 | float y = 2.*persp+20.*iTime; // x,y are texture coordinates 21 | int dash = sin(y)>0; // a bit overkill to use sine function here, simple mod/fmod would do well 22 | 23 | if (v>horizon) { 24 | *fragColor_r = 255*(1.-v); // sunset sky 25 | *fragColor_g = 128; 26 | *fragColor_b = 178; 27 | } else { // actual texture to map 28 | *fragColor_r = *fragColor_g = *fragColor_b = 128; // asphalt 29 | if (x>4.) { // grass 30 | *fragColor_r = 0; 31 | *fragColor_g = 178; 32 | *fragColor_b = 0; 33 | } else if (x>2.) { // red-white curb 34 | *fragColor_r = 255; 35 | *fragColor_g = dash ? 255 : 0; 36 | *fragColor_b = dash ? 255 : 0; 37 | } else if (x<.015 && !dash) // central line 38 | *fragColor_r = *fragColor_g = *fragColor_b = 255; 39 | *fragColor_r = CLAMP(*fragColor_r*(1-v), 0, 255); // headlights 40 | *fragColor_g = CLAMP(*fragColor_g*(1-v), 0, 255); 41 | *fragColor_b = CLAMP(*fragColor_b*(1-v), 0, 255); 42 | } 43 | } 44 | 45 | void multisample(int fragCoord_x, int fragCoord_y) { 46 | int fragColor_r = 0, fragColor_g = 0, fragColor_b = 0; 47 | int r, g, b; 48 | for (int i=0; i0) { sign = 1; } else { sign = -1; x = -x; } // 13 | while (x>79060768) { x = x - 105414357; } // reduce the argument to the acceptable range 14 | if (x>26353589) { return sign*sin24(52707179 - x); } // 15 | return sign*(x/4096)*((16777216 + (((x/4096)*(x/4096))/4096)*((((x/4096)*(x/4096))/131 - 2785856)/4096))/4096); 16 | } 17 | 18 | int mul24(int a, int b) { 19 | return (a/4096)*(b/4096); 20 | } 21 | 22 | int div24(int a, int b) { 23 | return (a/(b/4096))*4096; 24 | } 25 | 26 | mainImage(int fragCoord_x, int fragCoord_y) { 27 | int u; int v; 28 | int horizon; 29 | int persp; 30 | 31 | int t; int t3; 32 | int x; int y; 33 | bool band; 34 | 35 | int CLAMP(int x, int low, int high) { 36 | if x>high { 37 | return high; 38 | } 39 | if xhorizon) { 51 | fragColor_r = (255*((POW2_24-v)/4096))/4096; // sky 52 | fragColor_g = 128; 53 | fragColor_b = 178; 54 | } else { 55 | persp = div24(POW2_24, horizon + POW2_24/13 - v); 56 | t = sin24(iTime/4); 57 | t3 = mul24(mul24(t, t), t); 58 | x = t3 + mul24(u, persp) - mul24(mul24(t3/10, persp), persp); 59 | if (x>10*POW2_24 || x<-10*POW2_24) { // ugly hack to avoid overflow: if x is large, it is grass 60 | fragColor_r = 0; 61 | fragColor_g = 178; 62 | fragColor_b = 0; 63 | } else { 64 | x = mul24(x, x); 65 | y = 2*persp+ 20*iTime; 66 | band = sin24(y)>0; 67 | if (x>4*POW2_24) { // grass 68 | fragColor_r = 0; 69 | fragColor_g = 178; 70 | fragColor_b = 0; 71 | } else { 72 | if (x>2*POW2_24) { // red-white curb 73 | if (band) { 74 | fragColor_r = 255; 75 | fragColor_g = 255; 76 | fragColor_b = 255; 77 | } else { 78 | fragColor_r = 255; 79 | fragColor_g = 0; 80 | fragColor_b = 0; 81 | } 82 | } else { // road 83 | fragColor_r = 128; 84 | fragColor_g = 128; 85 | fragColor_b = 128; // asphalt 86 | if (xPOW2_24*100 { // 100 approx 32 pi :) 144 | iTime = 0; 145 | } 146 | } 147 | } 148 | 149 | -------------------------------------------------------------------------------- /test-programs/gfx/raytracer.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | // _ _ 3 | // ___ ___ _ __ ___| |_ __ _ _ __ | |_ ___ 4 | // / __/ _ \| '_ \/ __| __/ _` | '_ \| __/ __| 5 | // | (_| (_) | | | \__ \ || (_| | | | | |_\__ \ 6 | // \___\___/|_| |_|___/\__\__,_|_| |_|\__|___/ 7 | 8 | int width; 9 | int height; 10 | int rays; 11 | 12 | int box1min_x; int box1min_y; int box1min_z; 13 | int box1max_x; int box1max_y; int box1max_z; 14 | int box1col_r; int box1col_g; int box1col_b; 15 | 16 | int box2min_x; int box2min_y; int box2min_z; 17 | int box2max_x; int box2max_y; int box2max_z; 18 | int box2col_r; int box2col_g; int box2col_b; 19 | 20 | int sph1c_x; int sph1c_y; int sph1c_z; int sph1_r; 21 | int sph1col_r; int sph1col_g; int sph1col_b; 22 | 23 | int sph2c_x; int sph2c_y; int sph2c_z; int sph2_r; 24 | int sph2col_r; int sph2col_g; int sph2col_b; 25 | 26 | int sph3c_x; int sph3c_y; int sph3c_z; int sph3_r; 27 | int sph3col_r; int sph3col_g; int sph3col_b; 28 | 29 | // _ _ _ 30 | // __ ____ _ _ __(_) __ _| |__ | | ___ ___ 31 | // \ \ / / _` | '__| |/ _` | '_ \| |/ _ \/ __| 32 | // \ V / (_| | | | | (_| | |_) | | __/\__ \ 33 | // \_/ \__,_|_| |_|\__,_|_.__/|_|\___||___/ 34 | 35 | int rand; // random number generator static variable 36 | int i; int j; int k; 37 | 38 | int tmp1; int tmp2; int tmp3; int tmp4; 39 | 40 | int color_r; int color_g; int color_b; 41 | int color_acc_r; int color_acc_g; int color_acc_b; 42 | 43 | int rayorg_x; int rayorg_y; int rayorg_z; // current ray to trace 44 | int raydir_x; int raydir_y; int raydir_z; 45 | int point_x; int point_y; int point_z; // intersection point 46 | int normal_x; int normal_y; int normal_z; // normal at the point 47 | 48 | // ____ __ _ __ _ _ _ _ _ 49 | // / ___| ___ / _| |_ / _| | ___ __ _| |_(_)_ __ __ _ _ __ ___ (_)_ __ | |_ 50 | // \___ \ / _ \| |_| __| | |_| |/ _ \ / _` | __| | '_ \ / _` | | '_ \ / _ \| | '_ \| __| 51 | // ___) | (_) | _| |_ | _| | (_) | (_| | |_| | | | | (_| | | |_) | (_) | | | | | |_ 52 | // |____/ \___/|_| \__| |_| |_|\___/ \__,_|\__|_|_| |_|\__, | | .__/ \___/|_|_| |_|\__| 53 | // |___/ |_| 54 | 55 | int fp32_normalize(int sign, int exponent, int mantissa) { 56 | if (mantissa == 0) { 57 | return 0; 58 | } 59 | 60 | while (mantissa < 8388608 && exponent > -127) { // mantissa > 0 61 | mantissa = mantissa * 2; 62 | exponent = exponent - 1; 63 | } 64 | while (mantissa >= 16777216) { 65 | mantissa = mantissa / 2; 66 | exponent = exponent + 1; 67 | } 68 | 69 | if (sign<0) { 70 | sign = -2147483648; 71 | } else { 72 | sign = 0; 73 | } 74 | exponent = (exponent + 127) * 8388608; // add the bias and shift the exponent to the position 75 | mantissa = mantissa % 8388608; // clear the hidden bit (it will implicitly be there but not stored) 76 | return sign + exponent + mantissa; 77 | } 78 | 79 | int fp32_from_int(int integer) { 80 | if (integer>0) { 81 | return fp32_normalize( 1, 23, integer); 82 | } else { 83 | return fp32_normalize(-1, 23, -integer); 84 | } 85 | } 86 | 87 | int fp32_sign(int fp) { 88 | if (fp>=0) { 89 | return 1; 90 | } else { 91 | return -1; 92 | } 93 | } 94 | 95 | int fp32_exponent(int fp) { 96 | if (fp<0) { // clear the MSB 97 | fp = (fp + 1073741824) + 1073741824; // avoid overflow 98 | } 99 | return fp/8388608 - 127; 100 | } 101 | 102 | int fp32_mantissa(int fp) { // the mantissa is in [1..2), i.e. the hidden bit included 103 | int exponent; 104 | int mantissa; 105 | if (fp<0) { // clear the MSB 106 | fp = (fp + 1073741824) + 1073741824; // avoid overflow 107 | } 108 | exponent = fp / 8388608; 109 | mantissa = fp % 8388608; 110 | if (exponent==0 && mantissa==0) { 111 | return 0; 112 | } 113 | return 8388608 + mantissa; 114 | } 115 | 116 | int fp32_to_int(int fp) { 117 | int sign; 118 | int exponent; 119 | int mantissa; 120 | 121 | sign = fp32_sign(fp); 122 | exponent = fp32_exponent(fp); 123 | mantissa = fp32_mantissa(fp); 124 | 125 | if (mantissa==0) { 126 | return 0; 127 | } 128 | 129 | while (exponent!=0) { 130 | if (exponent > 0) { 131 | if (mantissa < 1073741824) { 132 | mantissa = mantissa * 2; 133 | } else { 134 | if (sign<0) { 135 | return -2147483648; 136 | } else { 137 | return 2147483647; 138 | } 139 | } 140 | exponent = exponent - 1; 141 | } else { 142 | mantissa = mantissa / 2; 143 | exponent = exponent + 1; 144 | } 145 | } 146 | return sign*mantissa; 147 | } 148 | 149 | int fp32_flip_sign(int fp) { 150 | if (fp >= 0) { 151 | return (fp - 1073741824) - 1073741824; 152 | } else { 153 | return (fp + 1073741824) + 1073741824; 154 | } 155 | } 156 | 157 | int fp32_add(int a, int b) { 158 | int sign_a; 159 | int sign_b; 160 | int exponent_a; 161 | int exponent_b; 162 | int mantissa_a; 163 | int mantissa_b; 164 | int mantissa_ab; 165 | int sign_ab; 166 | 167 | sign_a = fp32_sign(a); 168 | sign_b = fp32_sign(b); 169 | exponent_a = fp32_exponent(a); 170 | exponent_b = fp32_exponent(b); 171 | mantissa_a = fp32_mantissa(a); 172 | mantissa_b = fp32_mantissa(b); 173 | 174 | while (exponent_a > exponent_b) { 175 | mantissa_b = mantissa_b / 2; 176 | exponent_b = exponent_b + 1; 177 | } 178 | while (exponent_a < exponent_b) { 179 | mantissa_a = mantissa_a / 2; 180 | exponent_a = exponent_a + 1; 181 | } 182 | 183 | sign_ab = sign_a; 184 | if (sign_a==sign_b) { 185 | mantissa_ab = mantissa_a + mantissa_b; 186 | } else { 187 | mantissa_ab = mantissa_a - mantissa_b; 188 | if (mantissa_ab<0) { 189 | sign_ab = -sign_a; 190 | mantissa_ab = -mantissa_ab; 191 | } 192 | } 193 | return fp32_normalize(sign_ab, exponent_a, mantissa_ab); 194 | } 195 | 196 | int fp32_sub(int a, int b) { 197 | return fp32_add(a, fp32_flip_sign(b)); 198 | } 199 | 200 | int fp32_mul(int a, int b) { 201 | int sign_a; int exponent_a; int mantissa_a; 202 | int sign_b; int exponent_b; int mantissa_b; 203 | int a_hi; int a_lo; int b_hi; int b_lo; 204 | int hihi; int hilo; int lohi; int lolo; 205 | int mantissa_low; int mantissa_high; 206 | 207 | sign_a = fp32_sign(a); 208 | sign_b = fp32_sign(b); 209 | exponent_a = fp32_exponent(a); 210 | exponent_b = fp32_exponent(b); 211 | mantissa_a = fp32_mantissa(a); 212 | mantissa_b = fp32_mantissa(b); 213 | 214 | a_hi = mantissa_a / 4096; // multiply 2 24-bit mantissas 215 | a_lo = mantissa_a % 4096; // into two 24-bit halves mantissa_high, mantissa_low 216 | b_hi = mantissa_b / 4096; 217 | b_lo = mantissa_b % 4096; 218 | hihi = a_hi * b_hi; 219 | hilo = a_hi * b_lo; 220 | lohi = a_lo * b_hi; 221 | lolo = a_lo * b_lo; 222 | mantissa_low = lolo + (hilo%4096 + lohi%4096)*4096; 223 | mantissa_high = hihi + hilo/4096 + lohi/4096 + mantissa_low/16777216; 224 | mantissa_low = mantissa_low % 16777216; 225 | 226 | return fp32_normalize(sign_a*sign_b, exponent_a + exponent_b + 1, mantissa_high); 227 | } 228 | 229 | int fp32_div(int a, int b) { 230 | int sign_a; int exponent_a; int mantissa_a; 231 | int sign_b; int exponent_b; int mantissa_b; 232 | int exponent; int mantissa; int remainder; 233 | 234 | sign_a = fp32_sign(a); 235 | sign_b = fp32_sign(b); 236 | exponent_a = fp32_exponent(a); 237 | exponent_b = fp32_exponent(b); 238 | mantissa_a = fp32_mantissa(a); 239 | mantissa_b = fp32_mantissa(b); 240 | 241 | if (mantissa_a == 0) { 242 | return 0; 243 | } 244 | 245 | mantissa = mantissa_a / mantissa_b; 246 | remainder = mantissa_a % mantissa_b; 247 | exponent = exponent_a - exponent_b - 1 + 24; 248 | 249 | if (mantissa == 0) { // TODO: meh 250 | mantissa = 1; 251 | exponent = exponent - 1; 252 | remainder = remainder * 2; 253 | if (remainder >= mantissa_b) { 254 | remainder = remainder - mantissa_b; 255 | } 256 | } 257 | while (mantissa < 8388608) { 258 | mantissa = mantissa * 2; 259 | remainder = remainder * 2; 260 | exponent = exponent - 1; 261 | if (remainder >= mantissa_b) { 262 | mantissa = mantissa + 1; 263 | remainder = remainder - mantissa_b; 264 | } 265 | } 266 | 267 | return fp32_normalize(sign_a*sign_b, exponent, mantissa); 268 | } 269 | 270 | int fp32_abs(int fp) { 271 | if (fp >= 0) { 272 | return fp; 273 | } else { 274 | return (fp + 1073741824) + 1073741824; 275 | } 276 | } 277 | 278 | int fp32_sqrt(int fp) { 279 | int x; 280 | int x_old; 281 | 282 | x = fp; 283 | while (true) { 284 | x_old = x; 285 | x = fp32_div(fp32_add(x, fp32_div(fp, x)), fp32_from_int(2)); 286 | if (x>=x_old && x-x_old<=1) ||(x<=x_old && x_old-x<=1) { // 1 ulp difference 287 | return x; 288 | } 289 | } 290 | } 291 | 292 | // LCM pseudo-random number generator. 293 | // Outputs numbers from 0..65535 inclusive. 294 | 295 | int rand() { 296 | rand = (rand*25173 + 13849) % 65536; 297 | return rand; 298 | } 299 | 300 | // (-1..1) random floating point number 301 | int fp32_rand() { 302 | return fp32_div(fp32_from_int(2*rand()-32768), fp32_from_int(32768)); 303 | } 304 | 305 | // ____ _ _ 306 | // | _ \ __ _ _ _ | |_ _ __ __ _ ___(_)_ __ __ _ 307 | // | |_) / _` | | | | | __| '__/ _` |/ __| | '_ \ / _` | 308 | // | _ < (_| | |_| | | |_| | | (_| | (__| | | | | (_| | 309 | // |_| \_\__,_|\__, | \__|_| \__,_|\___|_|_| |_|\__, | 310 | // |___/ |___/ 311 | 312 | bool point_inside_rectangle(int x, int y, int xmin, int ymin, int xmax, int ymax) { 313 | return fp32_sub(x, xmin)>=0 && fp32_sub(x, xmax)<=0 && fp32_sub(y, ymin)>=0 && fp32_sub(y, ymax)<=0; 314 | } 315 | 316 | bool ray_box_intersect(int boxmin_x, int boxmin_y, int boxmin_z, int boxmax_x, int boxmax_y, int boxmax_z) { 317 | int d; 318 | int side; 319 | 320 | normal_x = 0; normal_y = 0; normal_z = 0; 321 | if fp32_sub(fp32_abs(raydir_x), 981668463)>0 { // |x|<0.001 322 | if raydir_x>0 { 323 | side = boxmin_x; 324 | normal_x = fp32_from_int(-1); 325 | } else { 326 | side = boxmax_x; 327 | normal_x = fp32_from_int(1); 328 | } 329 | d = fp32_div(fp32_sub(side,rayorg_x),raydir_x); 330 | if d>0 { 331 | point_x = fp32_add(rayorg_x, fp32_mul(raydir_x, d)); 332 | point_y = fp32_add(rayorg_y, fp32_mul(raydir_y, d)); 333 | point_z = fp32_add(rayorg_z, fp32_mul(raydir_z, d)); 334 | if point_inside_rectangle(point_y, point_z, boxmin_y, boxmin_z, boxmax_y, boxmax_z) { 335 | return true; 336 | } 337 | } 338 | } 339 | 340 | normal_x = 0; normal_y = 0; normal_z = 0; 341 | if fp32_sub(fp32_abs(raydir_y), 981668463)>0 { // |y|<0.001 342 | if raydir_y>0 { 343 | side = boxmin_y; 344 | normal_y = fp32_from_int(-1); 345 | } else { 346 | side = boxmax_y; 347 | normal_y = fp32_from_int(1); 348 | } 349 | d = fp32_div(fp32_sub(side,rayorg_y),raydir_y); 350 | if d>0 { 351 | point_x = fp32_add(rayorg_x, fp32_mul(raydir_x, d)); 352 | point_y = fp32_add(rayorg_y, fp32_mul(raydir_y, d)); 353 | point_z = fp32_add(rayorg_z, fp32_mul(raydir_z, d)); 354 | if point_inside_rectangle(point_x, point_z, boxmin_x, boxmin_z, boxmax_x, boxmax_z) { 355 | return true; 356 | } 357 | } 358 | } 359 | 360 | normal_x = 0; normal_y = 0; normal_z = 0; 361 | if fp32_sub(fp32_abs(raydir_z), 981668463)>0 { // |z|<0.001 362 | if raydir_z>0 { 363 | side = boxmin_z; 364 | normal_z = fp32_from_int(-1); 365 | } else { 366 | side = boxmax_z; 367 | normal_z = fp32_from_int(1); 368 | } 369 | d = fp32_div(fp32_sub(side,rayorg_z),raydir_z); 370 | if d>0 { 371 | point_x = fp32_add(rayorg_x, fp32_mul(raydir_x, d)); 372 | point_y = fp32_add(rayorg_y, fp32_mul(raydir_y, d)); 373 | point_z = fp32_add(rayorg_z, fp32_mul(raydir_z, d)); 374 | if point_inside_rectangle(point_x, point_y, boxmin_x, boxmin_y, boxmax_x, boxmax_y) { 375 | return true; 376 | } 377 | } 378 | } 379 | return false; 380 | } 381 | 382 | bool ray_sphere_intersect(int sphc_x, int sphc_y, int sphc_z, int sph_r) { 383 | int displacement_x; int displacement_y; int displacement_z; 384 | int proj; 385 | int discriminant; 386 | int factor; 387 | 388 | displacement_x = fp32_sub(sphc_x, rayorg_x); 389 | displacement_y = fp32_sub(sphc_y, rayorg_y); 390 | displacement_z = fp32_sub(sphc_z, rayorg_z); 391 | proj = fp32_add( 392 | fp32_add( 393 | fp32_mul(displacement_x, raydir_x), 394 | fp32_mul(displacement_y, raydir_y)), 395 | fp32_mul(displacement_z, raydir_z)); 396 | discriminant = fp32_add( 397 | fp32_mul(sph_r, sph_r), 398 | fp32_sub( 399 | fp32_mul(proj, proj), 400 | fp32_add( 401 | fp32_add( 402 | fp32_mul(displacement_x, displacement_x), 403 | fp32_mul(displacement_y, displacement_y)) , 404 | fp32_mul(displacement_z, displacement_z)))); 405 | 406 | 407 | if discriminant<0 { return false; } 408 | factor = fp32_sub(proj, fp32_sqrt(discriminant)); 409 | if (factor<0) { return false; } 410 | point_x = fp32_add(rayorg_x, fp32_mul(raydir_x, factor)); 411 | point_y = fp32_add(rayorg_y, fp32_mul(raydir_y, factor)); 412 | point_z = fp32_add(rayorg_z, fp32_mul(raydir_z, factor)); 413 | normal_x = fp32_div(fp32_sub(sphc_x, point_x), sph_r); 414 | normal_y = fp32_div(fp32_sub(sphc_y, point_y), sph_r); 415 | normal_z = fp32_div(fp32_sub(sphc_z, point_z), sph_r); 416 | return true; 417 | } 418 | 419 | trace(int depth) { 420 | int tmp_dist; 421 | int best_dist; 422 | int best_point_x; int best_point_y; int best_point_z; 423 | int best_normal_x; int best_normal_y; int best_normal_z; 424 | int best_color_r; int best_color_g; int best_color_b; 425 | 426 | if depth>4 { 427 | color_r = (color_r *102)/255; 428 | color_g = (color_g *102)/255; 429 | color_b = (color_b *102)/255; 430 | return; 431 | } 432 | 433 | best_dist = fp32_from_int(-1); 434 | 435 | if ray_box_intersect(box1min_x, box1min_y, box1min_z, box1max_x, box1max_y, box1max_z) { 436 | best_color_r = box1col_r; 437 | best_color_g = box1col_g; 438 | best_color_b = box1col_b; 439 | best_normal_x = normal_x; 440 | best_normal_y = normal_y; 441 | best_normal_z = normal_z; 442 | best_point_x = point_x; 443 | best_point_y = point_y; 444 | best_point_z = point_z; 445 | best_dist = fp32_add(fp32_add( 446 | fp32_mul(fp32_sub(point_x, rayorg_x), fp32_sub(point_x, rayorg_x)), 447 | fp32_mul(fp32_sub(point_y, rayorg_y), fp32_sub(point_y, rayorg_y))), 448 | fp32_mul(fp32_sub(point_z, rayorg_z), fp32_sub(point_z, rayorg_z))); 449 | } 450 | 451 | if ray_box_intersect(box2min_x, box2min_y, box2min_z, box2max_x, box2max_y, box2max_z) { 452 | tmp_dist = fp32_add(fp32_add( 453 | fp32_mul(fp32_sub(point_x, rayorg_x), fp32_sub(point_x, rayorg_x)), 454 | fp32_mul(fp32_sub(point_y, rayorg_y), fp32_sub(point_y, rayorg_y))), 455 | fp32_mul(fp32_sub(point_z, rayorg_z), fp32_sub(point_z, rayorg_z))); 456 | if fp32_sub(best_dist, tmp_dist)>0 || best_dist<0 { 457 | best_dist = tmp_dist; 458 | best_color_r = box2col_r; 459 | best_color_g = box2col_g; 460 | best_color_b = box2col_b; 461 | best_normal_x = normal_x; 462 | best_normal_y = normal_y; 463 | best_normal_z = normal_z; 464 | best_point_x = point_x; 465 | best_point_y = point_y; 466 | best_point_z = point_z; 467 | } 468 | } 469 | 470 | if ray_sphere_intersect(sph1c_x, sph1c_y, sph1c_z, sph1_r) { 471 | tmp_dist = fp32_add(fp32_add( 472 | fp32_mul(fp32_sub(point_x, rayorg_x), fp32_sub(point_x, rayorg_x)), 473 | fp32_mul(fp32_sub(point_y, rayorg_y), fp32_sub(point_y, rayorg_y))), 474 | fp32_mul(fp32_sub(point_z, rayorg_z), fp32_sub(point_z, rayorg_z))); 475 | if fp32_sub(best_dist, tmp_dist)>0 || best_dist<0 { 476 | best_dist = tmp_dist; 477 | best_color_r = sph1col_r; 478 | best_color_g = sph1col_g; 479 | best_color_b = sph1col_b; 480 | best_normal_x = normal_x; 481 | best_normal_y = normal_y; 482 | best_normal_z = normal_z; 483 | best_point_x = point_x; 484 | best_point_y = point_y; 485 | best_point_z = point_z; 486 | } 487 | } 488 | 489 | if ray_sphere_intersect(sph2c_x, sph2c_y, sph2c_z, sph2_r) { 490 | tmp_dist = fp32_add(fp32_add( 491 | fp32_mul(fp32_sub(point_x, rayorg_x), fp32_sub(point_x, rayorg_x)), 492 | fp32_mul(fp32_sub(point_y, rayorg_y), fp32_sub(point_y, rayorg_y))), 493 | fp32_mul(fp32_sub(point_z, rayorg_z), fp32_sub(point_z, rayorg_z))); 494 | if fp32_sub(best_dist, tmp_dist)>0 || best_dist<0 { 495 | best_dist = tmp_dist; 496 | best_color_r = sph2col_r; 497 | best_color_g = sph2col_g; 498 | best_color_b = sph2col_b; 499 | best_normal_x = normal_x; 500 | best_normal_y = normal_y; 501 | best_normal_z = normal_z; 502 | best_point_x = point_x; 503 | best_point_y = point_y; 504 | best_point_z = point_z; 505 | } 506 | } 507 | 508 | if ray_sphere_intersect(sph3c_x, sph3c_y, sph3c_z, sph3_r) { 509 | tmp_dist = fp32_add(fp32_add( 510 | fp32_mul(fp32_sub(point_x, rayorg_x), fp32_sub(point_x, rayorg_x)), 511 | fp32_mul(fp32_sub(point_y, rayorg_y), fp32_sub(point_y, rayorg_y))), 512 | fp32_mul(fp32_sub(point_z, rayorg_z), fp32_sub(point_z, rayorg_z))); 513 | if fp32_sub(best_dist, tmp_dist)>0 || best_dist<0 { 514 | return; 515 | } 516 | } 517 | 518 | if best_dist>=0 { 519 | color_r = (color_r * best_color_r)/255; 520 | color_g = (color_g * best_color_g)/255; 521 | color_b = (color_b * best_color_b)/255; 522 | 523 | rayorg_x = best_point_x; 524 | rayorg_y = best_point_y; 525 | rayorg_z = best_point_z; 526 | 527 | // reflect 528 | tmp1 = fp32_mul( 529 | fp32_from_int(2), 530 | fp32_add(fp32_add( 531 | fp32_mul(raydir_x, best_normal_x), 532 | fp32_mul(raydir_y, best_normal_y)), 533 | fp32_mul(raydir_z, best_normal_z))); 534 | 535 | raydir_x = fp32_add(fp32_sub(raydir_x, fp32_mul(best_normal_x,tmp1)), fp32_div(fp32_rand(), fp32_from_int(12))); 536 | raydir_y = fp32_add(fp32_sub(raydir_y, fp32_mul(best_normal_y,tmp1)), fp32_div(fp32_rand(), fp32_from_int(12))); 537 | raydir_z = fp32_add(fp32_sub(raydir_z, fp32_mul(best_normal_z,tmp1)), fp32_div(fp32_rand(), fp32_from_int(12))); 538 | 539 | tmp1 = fp32_sqrt( // the norm 540 | fp32_add( 541 | fp32_add( 542 | fp32_mul(raydir_x, raydir_x), 543 | fp32_mul(raydir_y, raydir_y)), 544 | fp32_mul(raydir_z, raydir_z)) 545 | ); 546 | raydir_x = fp32_div(raydir_x, tmp1); // normalize the ray direction 547 | raydir_y = fp32_div(raydir_y, tmp1); 548 | raydir_z = fp32_div(raydir_z, tmp1); 549 | 550 | trace(depth+1); 551 | } else { 552 | color_r = (color_r *102)/255; 553 | color_g = (color_g *102)/255; 554 | color_b = (color_b *102)/255; 555 | } 556 | } 557 | 558 | width = 320; 559 | height = 240; 560 | rays = 30; 561 | rand = 1337; // the seed for the random number generator 562 | 563 | box1min_x = fp32_from_int(30); box1min_y = fp32_from_int(-40); box1min_z = fp32_from_int(110); 564 | box1max_x = fp32_from_int(70); box1max_y = fp32_from_int( 20); box1max_z = fp32_from_int(130); 565 | box1col_r = 102; box1col_g = 179; box1col_b = 255; 566 | 567 | box2min_x = fp32_from_int( 0); box2min_y = fp32_from_int(20); box2min_z = fp32_from_int( 60); 568 | box2max_x = fp32_from_int(110); box2max_y = fp32_from_int(23); box2max_z = fp32_from_int(160); 569 | box2col_r = 179; box2col_g = 179; box2col_b = 102; 570 | 571 | sph1c_x = fp32_from_int(60); sph1c_y = fp32_from_int(0); sph1c_z = fp32_from_int(70); sph1_r = fp32_from_int(20); 572 | sph1col_r = 255; sph1col_g = 102; sph1col_b = 153; 573 | 574 | sph2c_x = fp32_from_int(28); sph2c_y = fp32_from_int(11); sph2c_z = fp32_from_int(70); sph2_r = fp32_from_int(9); 575 | sph2col_r = 255; sph2col_g = 255; sph2col_b = 77; 576 | 577 | sph3c_x = fp32_from_int(50); sph3c_y = fp32_from_int(-100); sph3c_z = fp32_from_int(-70); sph3_r = fp32_from_int(80); 578 | sph3col_r = 255; sph3col_g = 255; sph3col_b = 255; 579 | 580 | print "P3\n"; print width; print " "; print height; print "\n255\n"; 581 | 582 | j = 0; 583 | while j0 || b>0 { 27 | if a % 2 == 1 && b % 2 == 1 { 28 | result = result + pow; 29 | } 30 | a = a / 2; 31 | b = b / 2; 32 | pow = pow * 2; 33 | } 34 | return result; 35 | } 36 | 37 | // __ ______ _____ 38 | // \ \ / / __ \| __ \ 39 | // \ V / | | | |__) | 40 | // > <| | | | _ / 41 | // / . \ |__| | | \ \ 42 | // /_/ \_\____/|_| \_\ 43 | 44 | int xor(int a, int b) { 45 | return a - and(a,b) + b - and(a,b); 46 | } 47 | 48 | // ____ _____ 49 | // / __ \| __ \ 50 | // | | | | |__) | 51 | // | | | | _ / 52 | // | |__| | | \ \ 53 | // \____/|_| \_\ 54 | 55 | int or(int a, int b) { 56 | return xor(xor(a,b),and(a,b)); 57 | } 58 | 59 | // _ _ ____ _______ 60 | // | \ | |/ __ \__ __| 61 | // | \| | | | | | | 62 | // | . ` | | | | | | 63 | // | |\ | |__| | | | 64 | // |_| \_|\____/ |_| 65 | 66 | int not(int a) { 67 | return -1 - a; 68 | } 69 | 70 | // _ _ _ _ 71 | // | | | | (_) | | 72 | // _ __ _ __ ___| |_| |_ _ _ _ __ _ __ _ _ __ | |_ 73 | // | '_ \| '__/ _ \ __| __| | | | | '_ \| '__| | '_ \| __| 74 | // | |_) | | | __/ |_| |_| |_| | | |_) | | | | | | | |_ 75 | // | .__/|_| \___|\__|\__|\__, | | .__/|_| |_|_| |_|\__| 76 | // | | __/ | | | 77 | // |_| |___/ |_| 78 | 79 | padprint(int a) { 80 | int n; 81 | int nspaces; 82 | 83 | n = a; 84 | nspaces = 16; 85 | if (n<=0) { 86 | nspaces = nspaces - 1; 87 | } 88 | while n!=0 { 89 | n = n / 10; 90 | nspaces = nspaces - 1; 91 | } 92 | n = 0; 93 | while n _ < | __/ _ \/ __| __| / __/ _` / __|/ _ \/ __| 104 | // | (_) | | || __/\__ \ |_ | (_| (_| \__ \ __/\__ \ 105 | // \___/ \__\___||___/\__| \___\__,_|___/\___||___/ 106 | 107 | int test(int i) { 108 | if (i<4) { 109 | if (i<2) { 110 | if (i<1) { 111 | return -1804289383; 112 | } else { 113 | return 1681692777; 114 | } 115 | } else { 116 | if (i<3) { 117 | return 1957747793; 118 | } else { 119 | return -719885386; 120 | } 121 | } 122 | } else { 123 | if (i<6) { 124 | if (i<5) { 125 | return 596516649; 126 | } else { 127 | return 1025202362; 128 | } 129 | } else { 130 | if (i<7) { 131 | return 783368690; 132 | } else { 133 | return -2044897763; 134 | } 135 | } 136 | } 137 | } 138 | 139 | // _ _ _ _ _ 140 | // | | | | | | | | 141 | // _ __ _ _ _ __ | |_ ___ ___| |_ ___| | | | 142 | // | '__| | | | '_ \ | __/ _ \/ __| __/ __| | | | 143 | // | | | |_| | | | | | || __/\__ \ |_\__ \_|_|_| 144 | // |_| \__,_|_| |_| \__\___||___/\__|___(_|_|_) 145 | 146 | // _ _ _____ 147 | // /\ | \ | | __ \ 148 | // / \ | \| | | | | 149 | // / /\ \ | . ` | | | | 150 | // / ____ \| |\ | |__| | 151 | // /_/ \_\_| \_|_____/ 152 | 153 | print " bitwise and"; 154 | i = 0; 155 | while i<8 { 156 | padprint(test(i)); 157 | i = i + 1; 158 | } 159 | print "\n"; 160 | i = 0; 161 | while i<8 { 162 | padprint(test(i)); 163 | j = 0; 164 | while j<8 { 165 | padprint(and(test(i), test(j))); 166 | j = j + 1; 167 | } 168 | print "\n"; 169 | i = i + 1; 170 | } 171 | print "\n"; 172 | 173 | // ____ _____ 174 | // / __ \| __ \ 175 | // | | | | |__) | 176 | // | | | | _ / 177 | // | |__| | | \ \ 178 | // \____/|_| \_\ 179 | 180 | print " bitwise or"; 181 | i = 0; 182 | while i<8 { 183 | padprint(test(i)); 184 | i = i + 1; 185 | } 186 | print "\n"; 187 | i = 0; 188 | while i<8 { 189 | padprint(test(i)); 190 | j = 0; 191 | while j<8 { 192 | padprint(or(test(i), test(j))); 193 | j = j + 1; 194 | } 195 | print "\n"; 196 | i = i + 1; 197 | } 198 | print "\n"; 199 | 200 | // __ ______ _____ 201 | // \ \ / / __ \| __ \ 202 | // \ V / | | | |__) | 203 | // > <| | | | _ / 204 | // / . \ |__| | | \ \ 205 | // /_/ \_\____/|_| \_\ 206 | 207 | print " bitwise xor"; 208 | i = 0; 209 | while i<8 { 210 | padprint(test(i)); 211 | i = i + 1; 212 | } 213 | print "\n"; 214 | i = 0; 215 | while i<8 { 216 | padprint(test(i)); 217 | j = 0; 218 | while j<8 { 219 | padprint(xor(test(i), test(j))); 220 | j = j + 1; 221 | } 222 | print "\n"; 223 | i = i + 1; 224 | } 225 | print "\n"; 226 | 227 | // _ _ ____ _______ 228 | // | \ | |/ __ \__ __| 229 | // | \| | | | | | | 230 | // | . ` | | | | | | 231 | // | |\ | |__| | | | 232 | // |_| \_|\____/ |_| 233 | 234 | print " "; 235 | i = 0; 236 | while i<8 { 237 | padprint(test(i)); 238 | i = i + 1; 239 | } 240 | print "\n bitwise not"; 241 | j = 0; 242 | while j<8 { 243 | padprint(not(test(j))); 244 | j = j + 1; 245 | } 246 | print "\n"; 247 | } 248 | 249 | -------------------------------------------------------------------------------- /test-programs/nontrivial/mandelbrot.expected: -------------------------------------------------------------------------------- 1 | ...............,,,,,,,,,,,,,,,,,,''''''''''''''''''''''''''''''',,,,,,,,,,,,,,,, 2 | ............,,,,,,,,,,,,''''''''''''''''''''''''''''~~~~~~==~~~~~'''''''',,,,,,, 3 | ..........,,,,,,,,,''''''''''''''''''''''''''~~~~~~~~~==++<&:+++=~~~~~~''''''',, 4 | ........,,,,,,,'''''''''''''''''''''''''~~~~~~~~~~~====+::;/&O/[:+==~~~~~~~''''' 5 | .......,,,,'''''''''''''''''''''''''~~~~~~~~~~~~====++:? x O=18) { print " "; } 38 | } 39 | 40 | l = 19; 41 | w = 80; 42 | h = 25; 43 | s = 8192; 44 | 45 | y = 0; 46 | while (y 2147483647/shift { // pay attention to potential overflows 11 | return 2 * sqrt(n / 4, shift); 12 | } 13 | x = shift; // initial guess 1.0, can do better, but oh well 14 | n_one = n * shift; // need to compensate for fixp division 15 | while true { 16 | x_old = x; 17 | x = (x + n_one / x) / 2; 18 | if abs(x - x_old) <= 1 { 19 | return x; 20 | } 21 | } 22 | } 23 | 24 | int abs(int x) { 25 | if x < 0 { 26 | return -x; 27 | } else { 28 | return x; 29 | } 30 | } 31 | 32 | // 25735 is approximately equal to pi * 8192; 33 | // expected value of the output is sqrt(pi) * 8192 approx 14519 34 | 35 | println sqrt(25735, 8192); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /test-programs/nontrivial/trig-hp12c.expected: -------------------------------------------------------------------------------- 1 | pi/2 approx 1.57079631 2 | sin(pi/2) approx 1.00000286 3 | cos(pi/2) approx 0.00037169 4 | -------------------------------------------------------------------------------- /test-programs/nontrivial/trig-hp12c.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | int PI_2; // pi/2 in fixed point precision 3 | int pow2_24; // 2^24 precision 4 | 5 | print_fixed_point(int x, int shift) { 6 | int decpr; 7 | int i; 8 | 9 | if x<0 { 10 | print "-"; 11 | x = -x; 12 | } 13 | 14 | print x / shift; 15 | print "."; 16 | 17 | decpr = 0; // number of meaningful digits after the decimal point 18 | i = shift; 19 | while i>0 { 20 | decpr = decpr + 1; 21 | i = i / 10; 22 | } 23 | 24 | i = 0; 25 | x = (x % shift) * 10; 26 | while i0 { sign = 1; } else { sign = -1; x = -x; } // 39 | while x>+79060768 { x = x - 105414357; } // reduce the argument to the acceptable range 40 | if x>26353589 { return sign*sin24(52707179 - x); } // 41 | return sign*(x/4096)*((16777216 + (((x/4096)*(x/4096))/4096)*((((x/4096)*(x/4096))/131 - 2785856)/4096))/4096); 42 | } 43 | 44 | // cos(x) = 1 + x^2 * (0.03705 * x^2 - 0.49670)) 45 | // this formula works pretty well in the range [-pi/2, +pi/2] 46 | int cos24(int x) { 47 | if x<0 { x = -x; } // 48 | while x>79060768 { x = x - 105414357; } // reduce the argument to the acceptable range 49 | if x>26353589 { return -cos24(52707179 - x); } // 50 | return 16777216 + (((x/4096)*(x/4096))/4096)*((((x/4096)*(x/4096))/27 - 8333243)/4096); 51 | } 52 | 53 | PI_2 = 26353589; 54 | pow2_24 = 16777216; 55 | print "pi/2 approx "; print_fixed_point(PI_2, pow2_24); 56 | print "sin(pi/2) approx "; print_fixed_point(sin24(PI_2), pow2_24); 57 | print "cos(pi/2) approx "; print_fixed_point(cos24(PI_2), pow2_24); 58 | } 59 | 60 | -------------------------------------------------------------------------------- /test-programs/simple/eight-queens.expected: -------------------------------------------------------------------------------- 1 | Q....... 2 | ......Q. 3 | ....Q... 4 | .......Q 5 | .Q...... 6 | ...Q.... 7 | .....Q.. 8 | ..Q..... 9 | -------------------------------------------------------------------------------- /test-programs/simple/eight-queens.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | bool board0; bool board1; bool board2; bool board3; bool board4; bool board5; bool board6; bool board7; bool board8; bool board9; bool board10; bool board11; bool board12; bool board13; bool board14; bool board15; bool board16; bool board17; bool board18; bool board19; bool board20; bool board21; bool board22; bool board23; bool board24; bool board25; bool board26; bool board27; bool board28; bool board29; bool board30; bool board31; bool board32; bool board33; bool board34; bool board35; bool board36; bool board37; bool board38; bool board39; bool board40; bool board41; bool board42; bool board43; bool board44; bool board45; bool board46; bool board47; bool board48; bool board49; bool board50; bool board51; bool board52; bool board53; bool board54; bool board55; bool board56; bool board57; bool board58; bool board59; bool board60; bool board61; bool board62; bool board63; 3 | int i; int j; 4 | 5 | // getter-setter for the board 6 | bool board(int i) { 7 | if i < 32 { if i < 16 { if i < 8 { if i < 4 { if i < 2 { if i < 1 { return board0; } else { return board1; }} else { if i < 3 { return board2; } else { return board3; }}} else { if i < 6 { if i < 5 { return board4; } else { return board5; }} else { if i < 7 { return board6; } else { return board7; }}}} else { if i < 12 { if i < 10 { if i < 9 { return board8; } else { return board9; }} else { if i < 11 { return board10; } else { return board11; }}} else { if i < 14 { if i < 13 { return board12; } else { return board13; }} else { if i < 15 { return board14; } else { return board15; }}}}} else { if i < 24 { if i < 20 { if i < 18 { if i < 17 { return board16; } else { return board17; }} else { if i < 19 { return board18; } else { return board19; }}} else { if i < 22 { if i < 21 { return board20; } else { return board21; }} else { if i < 23 { return board22; } else { return board23; }}}} else { if i < 28 { if i < 26 { if i < 25 { return board24; } else { return board25; }} else { if i < 27 { return board26; } else { return board27; }}} else { if i < 30 { if i < 29 { return board28; } else { return board29; }} else { if i < 31 { return board30; } else { return board31; }}}}}} else { if i < 48 { if i < 40 { if i < 36 { if i < 34 { if i < 33 { return board32; } else { return board33; }} else { if i < 35 { return board34; } else { return board35; }}} else { if i < 38 { if i < 37 { return board36; } else { return board37; }} else { if i < 39 { return board38; } else { return board39; }}}} else { if i < 44 { if i < 42 { if i < 41 { return board40; } else { return board41; }} else { if i < 43 { return board42; } else { return board43; }}} else { if i < 46 { if i < 45 { return board44; } else { return board45; }} else { if i < 47 { return board46; } else { return board47; }}}}} else { if i < 56 { if i < 52 { if i < 50 { if i < 49 { return board48; } else { return board49; }} else { if i < 51 { return board50; } else { return board51; }}} else { if i < 54 { if i < 53 { return board52; } else { return board53; }} else { if i < 55 { return board54; } else { return board55; }}}} else { if i < 60 { if i < 58 { if i < 57 { return board56; } else { return board57; }} else { if i < 59 { return board58; } else { return board59; }}} else { if i < 62 { if i < 61 { return board60; } else { return board61; }} else { if i < 63 { return board62; } else { return board63; }}}}}} 8 | } 9 | 10 | board(int i, bool v) { 11 | if i < 32 { if i < 16 { if i < 8 { if i < 4 { if i < 2 { if i < 1 { board0 = v; } else { board1 = v; }} else { if i < 3 { board2 = v; } else { board3 = v; }}} else { if i < 6 { if i < 5 { board4 = v; } else { board5 = v; }} else { if i < 7 { board6 = v; } else { board7 = v; }}}} else { if i < 12 { if i < 10 { if i < 9 { board8 = v; } else { board9 = v; }} else { if i < 11 { board10 = v; } else { board11 = v; }}} else { if i < 14 { if i < 13 { board12 = v; } else { board13 = v; }} else { if i < 15 { board14 = v; } else { board15 = v; }}}}} else { if i < 24 { if i < 20 { if i < 18 { if i < 17 { board16 = v; } else { board17 = v; }} else { if i < 19 { board18 = v; } else { board19 = v; }}} else { if i < 22 { if i < 21 { board20 = v; } else { board21 = v; }} else { if i < 23 { board22 = v; } else { board23 = v; }}}} else { if i < 28 { if i < 26 { if i < 25 { board24 = v; } else { board25 = v; }} else { if i < 27 { board26 = v; } else { board27 = v; }}} else { if i < 30 { if i < 29 { board28 = v; } else { board29 = v; }} else { if i < 31 { board30 = v; } else { board31 = v; }}}}}} else { if i < 48 { if i < 40 { if i < 36 { if i < 34 { if i < 33 { board32 = v; } else { board33 = v; }} else { if i < 35 { board34 = v; } else { board35 = v; }}} else { if i < 38 { if i < 37 { board36 = v; } else { board37 = v; }} else { if i < 39 { board38 = v; } else { board39 = v; }}}} else { if i < 44 { if i < 42 { if i < 41 { board40 = v; } else { board41 = v; }} else { if i < 43 { board42 = v; } else { board43 = v; }}} else { if i < 46 { if i < 45 { board44 = v; } else { board45 = v; }} else { if i < 47 { board46 = v; } else { board47 = v; }}}}} else { if i < 56 { if i < 52 { if i < 50 { if i < 49 { board48 = v; } else { board49 = v; }} else { if i < 51 { board50 = v; } else { board51 = v; }}} else { if i < 54 { if i < 53 { board52 = v; } else { board53 = v; }} else { if i < 55 { board54 = v; } else { board55 = v; }}}} else { if i < 60 { if i < 58 { if i < 57 { board56 = v; } else { board57 = v; }} else { if i < 59 { board58 = v; } else { board59 = v; }}} else { if i < 62 { if i < 61 { board60 = v; } else { board61 = v; }} else { if i < 63 { board62 = v; } else { board63 = v; }}}}}} 12 | } 13 | 14 | bool valid_position(int row, int col) { // Is it safe to plase a queen in this position? 15 | int r; int c; // The board is filled left-to-right (it contains col-1 queens), 16 | c = 0; // so we need to check to the left of row,col position. 17 | while c=0 && r>=0 { 25 | if board(c+r*8) { // Check the diagonal. 26 | return false; 27 | } 28 | r = r - 1; 29 | c = c - 1; 30 | } 31 | r = row; c = col; 32 | while r<8 && c>=0 { 33 | if board(c+r*8) { // Check the anti-diagonal. 34 | return false; 35 | } 36 | r = r + 1; 37 | c = c - 1; 38 | } 39 | return true; 40 | } 41 | 42 | bool solve(int col) { // Place a queen on the first column, 43 | int row; // then proceed to the next column and place 44 | if col == 8 { // a queen in the first safe row of that column. 45 | return true; // Rinse, repeat. 46 | } 47 | row = 0; 48 | while row<8 { 49 | if valid_position(row, col) { 50 | board(col+row*8, true); 51 | if solve(col+1) { 52 | return true; 53 | } 54 | board(col+row*8, false); 55 | } 56 | row = row + 1; 57 | } 58 | return false; 59 | } 60 | 61 | i = 0; 62 | while i<64 { 63 | board(i, false); 64 | i = i + 1; 65 | } 66 | solve(0); 67 | j = 0; 68 | while j<8 { 69 | i = 0; 70 | while i<8 { 71 | if board(i+j*8) { 72 | print "Q"; 73 | } else { 74 | print "."; 75 | } 76 | i = i + 1; 77 | } 78 | println ""; 79 | j = j + 1; 80 | } 81 | } 82 | 83 | -------------------------------------------------------------------------------- /test-programs/simple/fixed-point.expected: -------------------------------------------------------------------------------- 1 | -3.14158 2 | -------------------------------------------------------------------------------- /test-programs/simple/fixed-point.wend: -------------------------------------------------------------------------------- 1 | main() { 2 | 3 | // This program outputs a decimal representation of a signed 32-bit fixed point number. 4 | // It prints the number one character at a time without ever printing an integer type expression, 5 | // like we would make it with aid of _write system call. 6 | 7 | print_fixed_point(int x, int shift) { 8 | int decpr; 9 | int i; 10 | 11 | int decpr(int shift) { // number of meaningful digits after the decimal point 12 | int decpr; 13 | decpr = 0; 14 | while shift>0 { 15 | decpr = decpr + 1; 16 | shift = shift / 10; 17 | } 18 | return decpr; 19 | } 20 | 21 | print_digit(int d) { 22 | if d==0 { 23 | print "0"; 24 | } else { 25 | if d==1 { 26 | print "1"; 27 | } else { 28 | if d==2 { 29 | print "2"; 30 | } else { 31 | if d==3 { 32 | print "3"; 33 | } else { 34 | if d==4 { 35 | print "4"; 36 | } else { 37 | if d==5 { 38 | print "5"; 39 | } else { 40 | if d==6 { 41 | print "6"; 42 | } else { 43 | if d==7 { 44 | print "7"; 45 | } else { 46 | if d==8 { 47 | print "8"; 48 | } else { 49 | if d==9 { 50 | print "9"; 51 | } else { 52 | print "invalid input"; 53 | }}}}}}}}}} 54 | } 55 | 56 | print_integer(int x) { 57 | if x>0 { 58 | print_integer(x / 10); 59 | print_digit(x % 10); // recursion to reverse the print 60 | } 61 | } 62 | 63 | if x<0 { 64 | print "-"; 65 | x = -x; 66 | } 67 | 68 | print_integer(x / shift); 69 | print "."; 70 | 71 | decpr = decpr(shift); 72 | i = 0; 73 | x = (x % shift) * 10; 74 | while i=':'jge', '>':'jg', '==':'je', '!=':'jne'} 52 | if n.op in pyeq1: 53 | return args + f'\t{pyeq1[n.op]} %ebx, %eax\n' 54 | elif n.op in pyeq2: 55 | return args + f'\tcmp %ebx, %eax\n\tmovl $1, %eax\n\t{pyeq2[n.op]} 1f\n\txorl %eax, %eax\n1:\n' 56 | elif n.op=='/': 57 | return args + '\tcdq\n\tidivl %ebx, %eax\n' 58 | elif n.op=='%': 59 | return args + '\tcdq\n\tidivl %ebx, %eax\n\tmovl %edx, %eax\n' 60 | raise Exception('Unknown binary operation') 61 | case Integer() | Boolean(): 62 | return f'\tmovl ${int(n.value)}, %eax\n' 63 | case Var(): 64 | return templates['var'].format(scope = n.deco['scope']*4, variable = n.deco['offset']*4) 65 | case FunCall(): 66 | return templates['funcall'].format(allocargs = ''.join(['%s\tpushl %%eax\n' % stat(a) for a in n.args]), 67 | varsize = n.deco['var_cnt']*4, 68 | disphead = n.deco['var_cnt']*4 + len(n.args)*4 - 4, 69 | scope = n.deco['scope']*4, 70 | funlabel = n.deco['label']) 71 | case other: raise Exception('Unknown instruction', n) 72 | -------------------------------------------------------------------------------- /transasm_recipe.py: -------------------------------------------------------------------------------- 1 | templates = { 2 | 'ascii' : '''{label}: .ascii "{string}" 3 | {label}_len = . - {label} 4 | ''', 5 | 'var' : ''' movl display+{scope}, %eax 6 | movl -{variable}(%eax), %eax 7 | ''', 8 | 'print_linebreak' : ''' pushl $10 # '\\n' 9 | movl $4, %eax # write system call 10 | movl $1, %ebx # stdout 11 | leal 0(%esp), %ecx # address of the character 12 | movl $1, %edx # one byte 13 | int $0x80 # make system call 14 | addl $4, %esp 15 | ''', 16 | 'print_int' : '''{expr} 17 | pushl %eax 18 | call print_int32 19 | addl $4, %esp 20 | ''', 21 | 'print_string' : ''' movl $4, %eax 22 | movl $1, %ebx 23 | movl ${label}, %ecx 24 | movl ${label}_len, %edx 25 | int $0x80 26 | ''', 27 | 'print_bool' : '''{expr} 28 | movl $truestr, %ecx 29 | movl $truestr_len, %edx 30 | test %eax, %eax 31 | jnz 0f 32 | movl $falsestr, %ecx 33 | movl $falsestr_len, %edx 34 | 0: movl $4, %eax 35 | movl $1, %ebx 36 | int $0x80 37 | ''', 38 | 'assign' : '''{expression} 39 | pushl %eax 40 | movl display+{scope}, %eax 41 | popl %ebx 42 | movl %ebx, -{variable}(%eax) 43 | ''', 44 | 'ifthenelse' : '''{condition} 45 | test %eax, %eax 46 | jz {label1} 47 | {ibody} 48 | jmp {label2} 49 | {label1}: 50 | {ebody} 51 | {label2}: 52 | ''', 53 | 'while' : '''{label1}: 54 | {condition} 55 | test %eax, %eax 56 | jz {label2} 57 | {body} 58 | jmp {label1} 59 | {label2}: 60 | ''', 61 | 'funcall' : ''' pushl display+{scope} 62 | {allocargs} 63 | subl ${varsize}, %esp 64 | leal {disphead}(%esp), %eax 65 | movl %eax, display+{scope} 66 | call {funlabel} 67 | movl display+{scope}, %esp 68 | addl $4, %esp 69 | popl display+{scope} 70 | ''', 71 | 'program' : '''.global _start 72 | .data 73 | {strings} 74 | truestr: .ascii "true" 75 | truestr_len = . - truestr 76 | falsestr: .ascii "false" 77 | falsestr_len = . - falsestr 78 | .align 2 79 | display: .skip {display_size} 80 | .text 81 | _start: 82 | leal -4(%esp), %eax 83 | movl %eax, display+{offset} 84 | subl ${varsize}, %esp # allocate locals 85 | call {main} 86 | addl ${varsize}, %esp # deallocate locals 87 | _end: # do not care about clearing the stack 88 | movl $1, %eax # _exit system call (check asm/unistd_32.h for the table) 89 | movl $0, %ebx # error code 0 90 | int $0x80 # make system call 91 | {functions} 92 | print_int32: 93 | movl 4(%esp), %eax # the number to print 94 | cdq 95 | xorl %edx, %eax 96 | subl %edx, %eax # abs(%eax) 97 | pushl $10 # base 10 98 | movl %esp, %ecx # buffer for the string to print 99 | subl $16, %esp # max 10 digits for a 32-bit number (keep %esp dword-aligned) 100 | 0: xorl %edx, %edx # %edx = 0 101 | divl 16(%esp) # %eax = %edx:%eax/10 ; %edx = %edx:%eax % 10 102 | decl %ecx # allocate one more digit 103 | addb $48, %dl # %edx += '0' # 0,0,0,0,0,0,0,0,0,0,'1','2','3','4','5','6' 104 | movb %dl, (%ecx) # store the digit # ^ ^ ^ 105 | test %eax, %eax # # %esp %ecx (after) %ecx (before) 106 | jnz 0b # until %eax==0 # <----- %edx = 6 -----> 107 | cmp %eax, 24(%esp) # if the number is negative 108 | jge 0f 109 | decl %ecx # allocate one more character 110 | movb $45, 0(%ecx) # '-' 111 | 0: movl $4, %eax # write system call 112 | movl $1, %ebx # stdout 113 | leal 16(%esp), %edx # the buffer to print 114 | subl %ecx, %edx # number of digits 115 | int $0x80 # make system call 116 | addl $20, %esp # deallocate the buffer 117 | ret 118 | '''} 119 | --------------------------------------------------------------------------------