Make S" work inside compiled words
[rrq/jonasforth.git] / 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 uefi_initialize:
64   ; At program startup, RDX contains an EFI_SYSTEM_TABLE*.
65   mov [system_table], rdx
66   ret
67
68 ;; Print a string of the given length.
69 ;;
70 ;; Inputs:
71 ;;  - RCX = String buffer
72 ;;  - RDX = String length
73 uefi_print_string:
74   ;; We take an input string of bytes without any terminator. We need to turn
75   ;; this string into a string of words, terminated by a null character.
76
77   mov rdi, .output_buffer ; Current location in output string
78
79 .copy_byte:
80   ;; When there are no characters left in the input string, we are 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 we need
109   ;; 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 the input
115   ;; 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 .output_buffer. Now
125   ; we just need to print it.
126
127   mov rcx, [system_table]                                       ; EFI_SYSTEM_TABLE* rcx
128   mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]                      ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* rcx
129   mov rdx, .output_buffer
130   mov rbx, [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString] ; EFI_TEXT_STRING rbx
131   sub rsp, 32
132   call rbx
133   add rsp, 32
134   ret
135
136 ;; Read a character as an ASCII byte into the given buffer.
137 ;;
138 ;; Inputs:
139 ;; - RCX = Character buffer (1 byte)
140 uefi_read_char:
141   mov r15, rcx
142 .read_key:
143   mov rcx, [system_table]                                       ; EFI_SYSTEM_TABLE* rcx
144   mov rcx, [rcx + EFI_SYSTEM_TABLE.ConIn]                       ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL* rcx
145   mov rbx, [rcx + EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke] ; EFI_INPUT_READ_KEY rbx
146   mov rdx, input_key                                            ; EFI_INPUT_KEY* rdx
147   sub rsp, 32
148   call rbx
149   add rsp, 32
150
151   mov r8, EFI_NOT_READY
152   cmp rax, r8
153   je .read_key
154
155   mov ax, [input_key.UnicodeChar]
156   mov [r15], al
157
158   ;; Special handling of enter (UEFI gives us '\r', but we want '\n'.)
159   cmp ax, $D
160   jne .no_enter
161   mov byte [r15], $A
162 .no_enter:
163
164   ;; Print the character
165   mov rcx, r15
166   mov rdx, 1
167   call uefi_print_string
168
169   ret
170
171 ;; Terminate with the given error code.
172 ;;
173 ;; Inputs:
174 ;; - RCX = Error code
175 uefi_terminate:
176   mov rcx, terminated_msg
177   mov rdx, terminated_msg.len
178   call uefi_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 uefi_print_string.output_buffer rq 0x400
189
190 char_buffer db ?
191
192 input_key EFI_INPUT_KEY