Dumb calculator example of C zero-runtime
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CFLAGS = -Wall -Wextra -Werror -nostdlib -static -O2 -fno-stack-protector | |
ASFLAGS = -64 | |
LDFLAGS = -m elf_x86_64 | |
all: calc | |
calc: start.o sys.o io.o ops.o main.o | |
$(LD) $(LDFLAGS) $^ -o $@ | |
.phony clean: | |
rm -rf *.o calc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.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