Skip to content

Instantly share code, notes, and snippets.

@magnickolas
Created Oct 15, 2020
Embed
What would you like to do?
Dumb calculator example of C zero-runtime
#include "./io.h"
#include "./sys.h"
int getc() {
char c;
return read(1, &c) == 1 ? c : EOF;
}
int is_digit(char c) {
return c >= '0' && c <= '9';
}
int read_int(int* err) {
char c;
while ((c = getc()) != EOF && c != '-' && c != '+' && !is_digit(c));
int is_negative = 0;
while (c == '-' || c == '+') {
if (c == '-') {
is_negative = !is_negative;
}
c = getc();
}
int x = 0;
if (err && !is_digit(c)) {
*err = 1;
}
while (is_digit(c)) {
x = x * 10 + (c - '0');
c = getc();
}
if (is_negative) {
x = -x;
}
return x;
}
int digits_num(int x) {
int num = 0;
do {
num++;
x /= 10;
} while (x);
return num;
}
void write_int(int x) {
char buf[12];
int chars_num = digits_num(x);
if (x < 0) {
chars_num++;
buf[0] = '-';
x = -x;
}
buf[chars_num] = '\0';
do {
buf[--chars_num] = '0' + x % 10;
x /= 10;
} while (x);
write(buf);
}
#ifndef IO_H_
#define IO_H_
#define EOF (-1)
int getc();
int is_digit(char);
int read_int(int*);
int digits_num(int);
void write_int(int);
#endif //IO_H_
#include "./sys.h"
#include "./io.h"
#include "./ops.h"
void print_usage() {
write(
"Usage:\n"
" ./calc [+|-|*|/]\n"
);
}
void print_empty_list_error() {
write(
"Error: empty list\n"
);
}
int main(int argc, char* argv[]) {
if (argc != 2) {
print_usage();
return 1;
}
if (strlen(argv[1]) != 1) {
print_usage();
return 1;
}
char op = argv[1][0];
switch (op) {
case '+': break;
case '-': break;
case '*': break;
case '/': break;
default: print_usage();
return 1;
}
const int MAXN = 1000;
int xs[MAXN];
int err = 0;
int result = read_int(&err);
if (err) {
print_empty_list_error();
return 2;
}
binary_op bin_op = parse_op(op);
while (!err) {
xs[0] = result;
int n;
for (n = 1; n < MAXN; n++) {
xs[n] = read_int(&err);
if (err) break;
}
result = accumulate(n, xs, bin_op);
}
write_int(result);
write("\n");
return 0;
}
CFLAGS = -Wall -Wextra -Werror -nostdlib -static -O2
ASFLAGS = -64
LDFLAGS = -m elf_x86_64 -nodefaultlibs
all: calc
calc: start.o sys.o io.o ops.o main.o
$(LD) $(LDFLAGS) $^ -o $@
.phony clean:
rm -rf *.o calc
#include "./ops.h"
static int sum(int x, int y) {
return x + y;
}
static int sub(int x, int y) {
return x - y;
}
static int mult(int x, int y) {
return x * y;
}
static int div(int x, int y) {
return x / y;
}
int accumulate(ssize_t n, int xs[], binary_op op) {
int result = xs[0];
for (ssize_t i = 1; i < n; i++) {
result = op(result, xs[i]);
}
return result;
}
binary_op parse_op(char op) {
switch (op) {
case '+': return sum;
case '-': return sub;
case '*': return mult;
case '/': return div;
default: return 0;
}
}
#ifndef OPS_H_
#define OPS_H_
#include "./sys.h"
typedef int (*binary_op)(int, int);
int accumulate(ssize_t, int[], binary_op);
binary_op parse_op(char);
#endif //OPS_H_
.section .text
.globl _start
_start:
xorl %ebp, %ebp # mark the end of a stack frame
movl (%rsp), %edi # argc
leaq 8(%rsp), %rsi # argv
xorl %eax, %eax
call main
movl %eax, %edi # exit code
movl $60, %eax # exit syscall num
syscall
#ifndef SYS_H_
#define SYS_H_
typedef long unsigned int ssize_t;
ssize_t strlen(const char*);
ssize_t read(ssize_t, char*);
ssize_t write(const char*);
#endif //SYS_H_
.section .text
.globl write
.globl read
.globl strlen
# char* (%rdi)
strlen:
xorl %eax, %eax
cmpb $0, (%rdi)
je .strlen_ret
.strlen_loop:
addq $1, %rax
cmpb $0, (%rdi,%rax)
jne .strlen_loop
.strlen_ret:
ret
# char* (%rdi)
write:
call strlen
movq %rdi, %rsi
movq %rax, %rdx # number of bytes to output
movq $1, %rax # write syscall num
movq $1, %rdi # stdout file descriptor
syscall
ret
# int64 (%rdi)
# char* (%rsi)
read:
movq %rdi, %rdx
xorq %rax, %rax
xorq %rdi, %rdi
syscall
ret
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment