Add placeholder implementation of Linux backend
[rrq/jonasforth.git] / os / uefi.asm
1 ;; vim: syntax=fasm
2
3 format pe64 dll efi
4 entry main
5
6 ;; EFI struct definitions {{{
7
8 EFI_NOT_READY = 0x8000_0000_0000_0000 or 6
9
10 ;; Based on https://wiki.osdev.org/Uefi.inc
11 macro struct name {
12   virtual at 0
13     name name
14   end virtual
15 }
16
17 struc EFI_TABLE_HEADER {
18   dq ?
19   dd ?
20   dd ?
21   dd ?
22   dd ?
23 }
24
25 struc EFI_SYSTEM_TABLE {
26   .Hdr EFI_TABLE_HEADER
27   .FirmwareVendor dq ? ; CHAR16*
28   .FirmwareRevision dd ? ; UINT32
29   align 8
30   .ConsoleInHandle dq ? ; EFI_HANDLE
31   .ConIn dq ? ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL*
32   .ConsoleOutHandle dq ? ; EFI_HANDLE
33   .ConOut dq ? ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL*
34   ; ...
35 }
36 struct EFI_SYSTEM_TABLE
37
38 struc EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
39   .Reset dq ? ; EFI_TEXT_RESET
40   .OutputString dq ? ; EFI_TEXT_STRING
41   ; ...
42 }
43 struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
44
45 struc EFI_SIMPLE_TEXT_INPUT_PROTOCOL {
46   .Reset dq ? ; EFI_INPUT_RESET
47   .ReadKeyStroke dq ? ; EFI_INPUT_READ_KEY
48   ; ...
49 }
50 struct EFI_SIMPLE_TEXT_INPUT_PROTOCOL
51
52 struc EFI_INPUT_KEY {
53   .ScanCode dw ? ; UINT16
54   .UnicodeChar dw ? ; CHAR16
55   align 8
56 }
57 struct EFI_INPUT_KEY
58
59 ;; }}}
60
61 macro os_code_section {
62   section '.text' code readable executable
63 }
64
65 macro os_data_section {
66   section '.data' readable writable
67 }
68
69 section '.text' code executable readable
70
71 os_initialize:
72   ; At program startup, RDX contains an EFI_SYSTEM_TABLE*.
73   mov [system_table], rdx
74   ret
75
76 os_print_string:
77   ;; We take an input string of bytes without any terminator. We need to turn
78   ;; this string into a string of words, terminated by a null character.
79
80   mov rdi, .output_buffer ; Current location in output string
81
82 .copy_byte:
83   ;; When there are no characters left in the input string, we are done.
84   cmp rdx, 0
85   je .done
86
87   ;; Load byte from input string
88   mov al, byte [rcx]
89
90   ;; Copy byte to output string
91
92   cmp al, $A
93   jne .not_newline
94 .newline:
95   ;; It's a newline; replace it with '\r\n' in output string.
96   mov byte [rdi], $D
97   inc rdi
98   mov byte [rdi], 0
99   inc rdi
100   mov byte [rdi], $A
101   inc rdi
102   mov byte [rdi], 0
103   inc rdi
104   jmp .pop
105
106 .not_newline:
107   ;; Not a newline, proceed as normal:
108   mov byte [rdi], al
109   inc rdi
110
111   ;; The output string has words rather than bytes for charactesr, so we need
112   ;; to add an extra zero:
113   mov byte [rdi], 0
114   inc rdi
115
116 .pop:
117   ;; We finished copying character to output string, so pop it from the input
118   ;; string.
119   inc rcx
120   dec rdx
121
122   jmp .copy_byte
123 .done:
124   ;; Append a final null-word:
125   mov word [rdi], 0
126
127   ; At this point we have our null-terminated word-string at .output_buffer. Now
128   ; we just need to print it.
129
130   mov rcx, [system_table]                                       ; EFI_SYSTEM_TABLE* rcx
131   mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]                      ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* rcx
132   mov rdx, .output_buffer
133   mov rbx, [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString] ; EFI_TEXT_STRING rbx
134   sub rsp, 32
135   call rbx
136   add rsp, 32
137   ret
138
139 os_read_char:
140 .read_key:
141   mov rcx, [system_table]                                       ; EFI_SYSTEM_TABLE* rcx
142   mov rcx, [rcx + EFI_SYSTEM_TABLE.ConIn]                       ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL* rcx
143   mov rbx, [rcx + EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke] ; EFI_INPUT_READ_KEY rbx
144   mov rdx, input_key                                            ; EFI_INPUT_KEY* rdx
145   sub rsp, 32
146   call rbx
147   add rsp, 32
148
149   mov r8, EFI_NOT_READY
150   cmp rax, r8
151   je .read_key
152
153   movzx rax, word [input_key.UnicodeChar]
154
155   ;; Special handling of enter (UEFI gives us '\r', but we want '\n'.)
156   cmp ax, $D
157   jne .no_enter
158   mov al, $A
159 .no_enter:
160
161   push rax
162   ;; Print the character
163   mov [char_buffer], al
164   mov rcx, char_buffer
165   mov rdx, 1
166   call os_print_string
167   pop rax
168
169   ret
170
171 ;; Terminate with the given error code.
172 ;;
173 ;; Inputs:
174 ;; - RCX = Error code
175 os_terminate:
176   mov rcx, terminated_msg
177   mov rdx, terminated_msg.len
178   call os_print_string
179   jmp $
180
181 section '.data' readable writable
182
183 system_table dq ? ; EFI_SYSTEM_TABLE*
184
185 terminated_msg db 0xD, 0xA, '(The program has terminated.)', 0xD, 0xA
186 .len = $ - terminated_msg
187
188 os_print_string.output_buffer rq 0x400
189
190 char_buffer db ?
191
192 input_key EFI_INPUT_KEY