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