fec --emit-asm -> wasm -> wlink -> .exe. 툴체인은 고정된 Open Watcom 그대로다. 레지스터 할당기가 없다. 임시값마다 스택 슬롯을 주고, 명령마다 피연산자를 고정 레지스터로 읽어 계산하고 다시 저장한다. 느린 코드지만 명백히 옳은 코드이고, 옳은 것이 먼저다. 나중에 할당기를 끼워도 나머지는 모른다 -- 임시값이 어디 사는지만 바뀐다. 런타임 fec/rt/start.asm 은 진입 스텁과 fe_trap 이다. trap 은 이유와 파일과 줄을 stderr 에 쓰고 3으로 끝낸다. 처음으로 Ferro 프로그램이 실행됐다: 1..10 합 -> 55 (7*6-2)/4 -> 10 루프+호출+분기 -> 1 tests/build.py 가 컴파일하고 링크하고 돌린다.
135 lines
3.3 KiB
NASM
135 lines
3.3 KiB
NASM
; Ferro runtime: process entry and the trap handler.
|
|
;
|
|
; The entry point calls the program's `main` and hands its result to
|
|
; ExitProcess, so a Ferro program is an ordinary console executable.
|
|
; `fe_trap` prints where the program stopped and why, then exits 3.
|
|
|
|
.386
|
|
.model flat
|
|
|
|
extern _ExitProcess@4 : near
|
|
extern _GetStdHandle@4 : near
|
|
extern _WriteFile@20 : near
|
|
extern fe_main_ : near
|
|
|
|
_DATA segment dword public 'DATA'
|
|
|
|
reasons dd offset r_bounds, offset r_overflow, offset r_divide
|
|
dd offset r_unreach, offset r_explicit
|
|
r_bounds db 'index out of bounds',0
|
|
r_overflow db 'integer overflow',0
|
|
r_divide db 'divide by zero',0
|
|
r_unreach db 'reached unreachable code',0
|
|
r_explicit db 'trap',0
|
|
r_unknown db 'trap',0
|
|
prefix db 'ferro: ',0
|
|
at_word db ' at ',0
|
|
colon db ':',0
|
|
newline db 13,10,0
|
|
numbuf db 16 dup(0)
|
|
written dd 0
|
|
|
|
_DATA ends
|
|
|
|
_TEXT segment dword public 'CODE'
|
|
|
|
; write_cstr(esi = pointer to a NUL-terminated string) -> void
|
|
write_cstr proc near
|
|
push ebp
|
|
mov ebp, esp
|
|
push ebx
|
|
push esi
|
|
push edi
|
|
mov edi, esi
|
|
xor ecx, ecx
|
|
count_loop:
|
|
cmp byte ptr [edi], 0
|
|
je count_done
|
|
inc edi
|
|
inc ecx
|
|
jmp count_loop
|
|
count_done:
|
|
test ecx, ecx
|
|
je write_done
|
|
push -11 ; STD_ERROR_HANDLE
|
|
call _GetStdHandle@4
|
|
push 0 ; lpOverlapped
|
|
push offset written
|
|
push ecx
|
|
push esi
|
|
push eax
|
|
call _WriteFile@20
|
|
write_done:
|
|
pop edi
|
|
pop esi
|
|
pop ebx
|
|
mov esp, ebp
|
|
pop ebp
|
|
ret
|
|
write_cstr endp
|
|
|
|
; write_uint(eax = value) -> void
|
|
write_uint proc near
|
|
push ebp
|
|
mov ebp, esp
|
|
push ebx
|
|
mov edi, offset numbuf + 15
|
|
mov byte ptr [edi], 0
|
|
mov ebx, 10
|
|
digit_loop:
|
|
xor edx, edx
|
|
div ebx
|
|
add dl, '0'
|
|
dec edi
|
|
mov [edi], dl
|
|
test eax, eax
|
|
jnz digit_loop
|
|
mov esi, edi
|
|
call write_cstr
|
|
pop ebx
|
|
mov esp, ebp
|
|
pop ebp
|
|
ret
|
|
write_uint endp
|
|
|
|
; fe_trap(reason, file, line) -- cdecl, never returns
|
|
public fe_trap
|
|
fe_trap proc near
|
|
push ebp
|
|
mov ebp, esp
|
|
mov esi, offset prefix
|
|
call write_cstr
|
|
mov eax, [ebp+8] ; reason
|
|
cmp eax, 5
|
|
jb reason_ok
|
|
mov esi, offset r_unknown
|
|
jmp reason_write
|
|
reason_ok:
|
|
mov esi, [reasons + eax*4]
|
|
reason_write:
|
|
call write_cstr
|
|
mov esi, offset at_word
|
|
call write_cstr
|
|
mov esi, [ebp+12] ; file
|
|
call write_cstr
|
|
mov esi, offset colon
|
|
call write_cstr
|
|
mov eax, [ebp+16] ; line
|
|
call write_uint
|
|
mov esi, offset newline
|
|
call write_cstr
|
|
push 3
|
|
call _ExitProcess@4
|
|
fe_trap endp
|
|
|
|
public fe_start_
|
|
fe_start_ proc near
|
|
call fe_main_
|
|
push eax
|
|
call _ExitProcess@4
|
|
fe_start_ endp
|
|
|
|
_TEXT ends
|
|
|
|
end fe_start_
|