4f8ce56ef437f18b34fce4314a9d389a4500ecb2
[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 section '.text' code executable readable
62
63 os_initialize:
64   ; At program startup, RDX contains an EFI_SYSTEM_TABLE*.
65   mov [system_table], rdx
66   ret
67
68 os_print_string:
69   ;; We take an input string of bytes without any terminator. We need to turn
70   ;; this string into a string of words, terminated by a null character.
71
72   mov rdi, .output_buffer ; Current location in output string
73
74 .copy_byte:
75   ;; When there are no characters left in the input string, we are done.
76   cmp rdx, 0
77   je .done
78
79   ;; Load byte from input string
80   mov al, byte [rcx]
81
82   ;; Copy byte to output string
83
84   cmp al, $A
85   jne .not_newline
86 .newline:
87   ;; It's a newline; replace it with '\r\n' in output string.
88   mov byte [rdi], $D
89   inc rdi
90   mov byte [rdi], 0
91   inc rdi
92   mov byte [rdi], $A
93   inc rdi
94   mov byte [rdi], 0
95   inc rdi
96   jmp .pop
97
98 .not_newline:
99   ;; Not a newline, proceed as normal:
100   mov byte [rdi], al
101   inc rdi
102
103   ;; The output string has words rather than bytes for charactesr, so we need
104   ;; to add an extra zero:
105   mov byte [rdi], 0
106   inc rdi
107
108 .pop:
109   ;; We finished copying character to output string, so pop it from the input
110   ;; string.
111   inc rcx
112   dec rdx
113
114   jmp .copy_byte
115 .done:
116   ;; Append a final null-word:
117   mov word [rdi], 0
118
119   ; At this point we have our null-terminated word-string at .output_buffer. Now
120   ; we just need to print it.
121
122   mov rcx, [system_table]                                       ; EFI_SYSTEM_TABLE* rcx
123   mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]                      ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* rcx
124   mov rdx, .output_buffer
125   mov rbx, [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString] ; EFI_TEXT_STRING rbx
126   sub rsp, 32
127   call rbx
128   add rsp, 32
129   ret
130
131 os_read_char:
132   mov r15, rcx
133 .read_key:
134   mov rcx, [system_table]                                       ; EFI_SYSTEM_TABLE* rcx
135   mov rcx, [rcx + EFI_SYSTEM_TABLE.ConIn]                       ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL* rcx
136   mov rbx, [rcx + EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke] ; EFI_INPUT_READ_KEY rbx
137   mov rdx, input_key                                            ; EFI_INPUT_KEY* rdx
138   sub rsp, 32
139   call rbx
140   add rsp, 32
141
142   mov r8, EFI_NOT_READY
143   cmp rax, r8
144   je .read_key
145
146   mov ax, [input_key.UnicodeChar]
147   mov [r15], al
148
149   ;; Special handling of enter (UEFI gives us '\r', but we want '\n'.)
150   cmp ax, $D
151   jne .no_enter
152   mov byte [r15], $A
153 .no_enter:
154
155   ;; Print the character
156   mov rcx, r15
157   mov rdx, 1
158   call os_print_string
159
160   ret
161
162 ;; Terminate with the given error code.
163 ;;
164 ;; Inputs:
165 ;; - RCX = Error code
166 os_terminate:
167   mov rcx, terminated_msg
168   mov rdx, terminated_msg.len
169   call os_print_string
170   jmp $
171
172 section '.data' readable writable
173
174 system_table dq ? ; EFI_SYSTEM_TABLE*
175
176 terminated_msg db 0xD, 0xA, '(The program has terminated.)', 0xD, 0xA
177 .len = $ - terminated_msg
178
179 os_print_string.output_buffer rq 0x400
180
181 char_buffer db ?
182
183 input_key EFI_INPUT_KEY