.equ DATA_STACK_DEPTH, 128
.equ RETURN_STACK_DEPTH, 1024
.equ MEMORY_SIZE, 1000000
.equ MAX_STRING_LENGTH, 1024
.equ MAX_OPEN_FILES, 8
.section .bss
.lcomm stats, 64 .lcomm termios_old, 36 .lcomm termios, 36
.lcomm buffer, 1
.lcomm winsize, 8 .lcomm filename, 4
.lcomm data, DATA_STACK_DEPTH*4
.lcomm return, RETURN_STACK_DEPTH*4
.lcomm ports, 5*4
.lcomm memory, MEMORY_SIZE*4
.lcomm str, MAX_STRING_LENGTH
.lcomm input, MAX_OPEN_FILES*4
.section .data
arg_error:
.byte 19
.ascii "Usage: ngaro image\n"
file_error:
.byte 23
.ascii "Cannot open image file\n"
stack_error:
.byte 16
.ascii "Stack underflow\n"
clear_screen:
.byte 10
.ascii "\033[2J\033[1;1H"
instr:
.long ins_nop, ins_lit, ins_dup, ins_drop, ins_swap, ins_push
.long ins_pop, ins_loop, ins_jump, ins_return, ins_lt_jump
.long ins_gt_jump, ins_ne_jump, ins_eq_jump, ins_fetch, ins_store
.long ins_add, ins_subtract, ins_multiply, ins_divmod, ins_and
.long ins_or, ins_xor, ins_shl, ins_shr, ins_zero_exit
.long ins_inc, ins_dec, ins_in, ins_out, ins_wait
file_modes:
.long 0, 01101, 02101, 2 input_id:
.long 0
.section .text
.globl _start
_start:
movl $54, %eax movl $0, %ebx movl $0x5401, %ecx leal termios_old, %edx int $0x80
popl %eax
cmpl $2, %eax
je argc_ok
leal arg_error, %ecx
call write_str
jmp quit
argc_ok:
popl %eax popl %ebx movl %ebx, filename
movl $5, %eax movl $0, %ecx movl $0644, %edx int $0x80
testl %eax, %eax
jge open_ok
leal file_error, %ecx
call write_str
jmp quit
open_ok:
movl %eax, %ebx
movl $108, %eax leal stats, %ecx int $0x80
movl $3, %eax leal memory, %ecx movl stats+20, %edx int $0x80
movl $6, %eax int $0x80
movl $54, %eax movl $0, %ebx movl $0x5401, %ecx leal termios, %edx int $0x80
movl $0xfff5, %edi andl %edi, termios+12
movl $54, %eax movl $0, %ebx movl $0x5402, %ecx leal termios, %edx int $0x80
vm_start:
movl $0, %ebp movl $-1, %edi movl $-1, %esi movl $0, input movl $0, input_id vm_loop:
cmp $(MEMORY_SIZE/4), %ebp
jge vm_start movl memory(,%ebp,4), %eax
cmpl $30, %eax
jg implicit_call
call *instr(,%eax,4)
incl %ebp jmp vm_loop
implicit_call:
incl %esi
movl %ebp, return(,%esi,4)
movl %eax, %ebp
jmp vm_loop
quit:
movl $54, %eax movl $0, %ebx movl $0x5402, %ecx leal termios_old, %edx int $0x80
movl $1, %eax movl $0, %ebx int $0x80
write_str: movl $4, %eax movl $1, %ebx xorl %edx, %edx
movb (%ecx), %dl incl %ecx int $0x80
ret
stack_underflow:
leal stack_error, %ecx
call write_str
ret
convert_str: xorl %eax, %eax
movl data(,%edi,4), %ebx
xorl %ecx, %ecx
convert_loop:
movb memory(,%ebx,4), %al
movb %al, str(%ecx)
incl %ebx
incl %ecx
testb %al, %al
jne convert_loop
ret
revert_str: xorl %eax, %eax
movl data(,%edi,4), %ecx
revert_loop:
movb (%ebx), %al
movb %al, memory(,%ecx,4)
incl %ebx
incl %ecx
testb %al, %al
jne revert_loop
ret
get_window_size:
movl $54, %eax movl $0, %ebx movl $0x5413, %ecx leal winsize, %edx int $0x80
ret
.macro check_data_1
testl %edi, %edi
jl stack_underflow
.endm
.macro check_data_2
testl %edi, %edi
jle stack_underflow
.endm
.macro check_return_1
testl %esi, %esi
jl stack_underflow
.endm
.macro jump_if test
check_data_2
subl $2, %edi
movl data+4(,%edi,4), %eax
cmpl %eax, data+8(,%edi,4)
\test ins_jump_\test
incl %ebp
ret
ins_jump_\test:
movl memory+4(,%ebp,4), %ebp
decl %ebp
ret
.endm
.macro port_neq n branch k=0
movl ports+\n*4, %eax
cmpl $\k, %eax
jne \branch
.endm
.macro port_eq n branch k=0
movl ports+\n*4, %eax
cmpl $\k, %eax
je \branch
.endm
.macro set_port n k
movl \k, ports+\n*4
.endm
ins_nop:
ret
ins_lit:
incl %ebp
movl memory(,%ebp,4), %eax
incl %edi
movl %eax, data(,%edi,4)
ret
ins_dup:
check_data_1
movl data(,%edi,4), %eax
incl %edi
movl %eax, data(,%edi,4)
ret
ins_drop:
check_data_1
decl %edi
ret
ins_swap:
check_data_2
movl data-4(,%edi,4), %eax
xchgl data(,%edi,4), %eax
movl %eax, data-4(,%edi,4)
ret
ins_push:
check_data_1
incl %esi
movl data(,%edi,4), %eax
movl %eax, return(,%esi,4)
decl %edi
ret
ins_pop:
check_return_1
incl %edi
movl return(,%esi,4), %eax
movl %eax, data(,%edi,4)
decl %esi
ret
ins_loop:
check_data_1
decl data(,%edi,4) jle ins_loop_branch
movl memory+4(,%ebp,4), %ebp
decl %ebp
ret
ins_loop_branch:
incl %ebp
decl %edi
ret
ins_jump:
movl memory+4(,%ebp,4), %ebp
decl %ebp
ret
ins_return:
check_return_1
movl return(,%esi,4), %ebp
decl %esi
ret
ins_lt_jump:
jump_if jl
ins_gt_jump:
jump_if jg
ins_ne_jump:
jump_if jne
ins_eq_jump:
jump_if je
ins_fetch:
check_data_1
movl data(,%edi,4), %eax
movl memory(,%eax,4), %ebx
movl %ebx, data(,%edi,4)
ret
ins_store:
check_data_2
movl data(,%edi,4), %eax
decl %edi
movl data(,%edi,4), %ebx
decl %edi
movl %ebx, memory(,%eax,4)
ret
ins_add:
check_data_2
movl data(,%edi,4), %eax
decl %edi
addl %eax, data(,%edi,4)
ret
ins_subtract:
check_data_2
movl data(,%edi,4), %eax
decl %edi
subl %eax, data(,%edi,4)
ret
ins_multiply:
check_data_2
movl data(,%edi,4), %eax
decl %edi
imull data(,%edi,4)
movl %eax, data(,%edi,4)
ret
ins_divmod:
check_data_2
movl data-4(,%edi,4), %eax
xorl %edx, %edx
testl %eax, %eax
jl dividend_negative
idivl data(,%edi,4)
movl %edx, data-4(,%edi,4)
movl %eax, data(,%edi,4)
ret
dividend_negative:
negl %eax
idivl data(,%edi,4)
negl %eax
negl %edx
movl %edx, data-4(,%edi,4)
movl %eax, data(,%edi,4)
ret
ins_and:
check_data_2
movl data(,%edi,4), %eax
decl %edi
andl %eax, data(,%edi,4)
ret
ins_or:
check_data_2
movl data(,%edi,4), %eax
decl %edi
orl %eax, data(,%edi,4)
ret
ins_xor:
check_data_2
movl data(,%edi,4), %eax
decl %edi
xorl %eax, data(,%edi,4)
ret
ins_shl:
check_data_2
movl data(,%edi,4), %ecx
decl %edi
shll %cl, data(,%edi,4)
ret
ins_shr:
check_data_2
movl data(,%edi,4), %ecx
decl %edi
shrl %cl, data(,%edi,4)
ret
ins_zero_exit:
check_data_1
movl data(,%edi,4), %eax
testl %eax, %eax
je ins_zero_exit_branch
ret
ins_zero_exit_branch:
decl %edi
movl return(,%esi,4), %ebp
decl %esi
ret
ins_inc:
check_data_1
incl data(,%edi,4)
ret
ins_dec:
check_data_1
decl data(,%edi,4)
ret
ins_in:
check_data_1
movl data(,%edi,4), %eax
movl ports(,%eax,4), %ebx
movl %ebx, data(,%edi,4)
movl $0, ports(,%eax,4)
ret
ins_out:
check_data_2
movl data(,%edi,4), %eax
decl %edi
movl data(,%edi,4), %ebx
decl %edi
movl %ebx, ports(,%eax,4)
set_port 0 $0
ret
ins_wait:
port_neq 0 no_port
port1: port_neq 1 port2 1
set_port 0 $0
input_again:
movl $3, %eax movl input_id, %ecx
movl input(,%ecx,4), %ebx leal buffer, %ecx movl $1, %edx int $0x80
testl %eax, %eax
jne input_ok
movl input_id, %eax
testl %eax, %eax
je end_of_input
movl input(,%eax,4), %ebx movl $6, %eax int $0x80
decl input_id
jmp input_again
end_of_input:
popl %eax jmp quit
input_ok:
xorl %eax, %eax
movb buffer, %al
set_port 1 %eax
port2: port_neq 2 port4 1
set_port 0 $0
check_data_1
cmpl $0, data(,%edi,4)
jge port2_char
leal clear_screen, %ecx
call write_str
jmp port2_done
port2_char:
movl $4, %eax movl $1, %ebx leal data(,%edi,4), %ecx movl $1, %edx
int $0x80
port2_done:
set_port 2 $0
decl %edi
port4: port_eq 4 port5
set_port 0 $0
port4_p1: cmpl $1, %eax
jne port4_p2
movl $5, %eax movl filename, %ebx movl $01101, %ecx movl $0644, %edx int $0x80
testl %eax, %eax
jl cannot_save
movl %eax, %ebx movl $4, %eax leal memory, %ecx movl memory+12, %edx shll $2, %edx int $0x80
movl $6, %eax int $0x80
cannot_save:
set_port 4 $0
jmp port5
port4_p2: cmpl $2, %eax
jne port4_1
check_data_1
call convert_str
decl %edi
movl $5, %eax leal str, %ebx movl $0, %ecx movl $0644, %edx int $0x80
testl %eax, %eax
jl cannot_include
incl input_id
movl input_id, %ebx
movl %eax, input(,%ebx,4)
cannot_include:
set_port 4 $0
jmp port5
port4_1: cmpl $-1, %eax
jne port4_2
check_data_2
movl data(,%edi,4), %edx
decl %edi
call convert_str
movl file_modes(,%edx,4), %ecx leal str, %ebx movl $5, %eax movl $0644, %edx int $0x80
decl %edi
testl %eax, %eax
jge file_open_ok
set_port 4 $0
jmp port5
file_open_ok:
set_port 4 %eax
jmp port5
port4_2: cmpl $-2, %eax
jne port4_3
check_data_1
movl $3, %eax movl data(,%edi,4), %ebx leal buffer, %ecx movl $1, %edx int $0x80
decl %edi
testl %eax, %eax
jge read_byte_ok
set_port 4 $0
jmp port5
read_byte_ok:
xorl %eax, %eax
movb buffer, %al
set_port 4 %eax
jmp port5
port4_3: cmpl $-3, %eax
jne port4_4
check_data_2
movl $4, %eax movl data(,%edi,4), %ebx movl data-4(,%edi,4), %ecx
movb %cl, buffer
leal buffer, %ecx movl $1, %edx int $0x80
subl $2, %edi
testl %eax, %eax
jge write_byte_ok
set_port 4 $0
jmp port5
write_byte_ok:
set_port 4 $1
jmp port5
port4_4: cmpl $-4, %eax
jne port4_5
check_data_1
movl $6, %eax movl data(,%edi,4), %ebx int $0x80
decl %edi
set_port 4 %eax
jmp port5
port4_5: cmpl $-5, %eax
jne port4_6
check_data_1
movl $19, %eax movl data(,%edi,4), %ebx movl $0, %ecx movl $1, %edx int $0x80
decl %edi
set_port 4 %eax
jmp port5
port4_6: cmpl $-6, %eax
jne port4_7
check_data_2
movl $19, %eax movl data(,%edi,4), %ebx movl data-4(,%edi,4), %ecx movl $0, %edx int $0x80
subl $2, %edi
set_port 4 %eax
jmp port5
port4_7: cmpl $-7, %eax
jne port4_8
check_data_1
movl $108, %eax movl data(,%edi,4), %ebx leal stats, %ecx int $0x80
decl %edi
testl %eax, %eax
jge file_size_ok
set_port 4 $0
jmp port5
file_size_ok:
movl stats+20, %eax
set_port 4 %eax
jmp port5
port4_8: cmpl $-8, %eax
jne port5
check_data_1
call convert_str
movl $10, %eax leal str, %ebx int $0x80
decl %edi
testl %eax, %eax
je delete_file_ok
set_port 4 $0
jmp port5
delete_file_ok:
set_port 4 $-1
port5: port_eq 5 no_port
set_port 0 $0
port5_1: cmpl $-1, %eax
jne port5_5
set_port 5 $MEMORY_SIZE
jmp no_port
port5_5: cmpl $-5, %eax
jne port5_6
movl %edi, %ebx
incl %ebx
set_port 5 %ebx
jmp no_port
port5_6: cmpl $-6, %eax
jne port5_8
movl %esi, %ebx
incl %ebx
set_port 5 %ebx
jmp no_port
port5_8: cmpl $-8, %eax
jne port5_9
movl $13, %eax movl $0, %ebx
int $0x80
set_port 5 %eax
jmp no_port
port5_9: cmpl $-9, %eax
jne port5_10
popl %eax jmp quit
port5_10: cmpl $-10, %eax
jne port5_11
check_data_2
call convert_str
decl %edi
movl %esp, %edx
popl %eax popl %eax env_loop: popl %ebx
testl %ebx, %ebx
je env_not_found
xorl %ecx, %ecx
compare_loop: movb str(%ecx), %al
testb %al, %al
je str_ended
cmpb $61, (%ebx) je env_loop cmpb (%ebx), %al
jne env_loop incl %ebx
incl %ecx
jmp compare_loop
str_ended:
cmpb $61, (%ebx) jne env_loop incl %ebx
call revert_str
jmp env_end
env_not_found:
movl data(,%edi,4), %ecx
movb $0, memory(,%ecx,4)
env_end:
movl %edx, %esp
decl %edi
set_port 5 $0
jmp no_port
port5_11: cmpl $-11, %eax
jne port5_12
call get_window_size
xorl %eax, %eax
movw winsize, %ax
set_port 5 %eax
jmp no_port
port5_12: cmpl $-11, %eax
jne port5_end
call get_window_size
xorl %eax, %eax
movw winsize+2, %ax
set_port 5 %eax
jmp no_port
port5_end:
set_port 5 $0
no_port:
ret