431a64c9d1267c846c47279630a680b34dff7e69
[rrq/jonasforth.git] / uefi / main.asm
1 ;; vim: syntax=fasm
2
3 format pe64 dll efi
4 entry main
5
6 ;; [TODO] We need to provide the following:
7 ;; - [X] Print a string of a given length
8 ;; - [ ] Print a single character
9 ;; - [ ] Terminate the program (? - What should this do?)
10 ;; - [X] Read a single character
11 ;;       - This should allow the user to type in a string, and then feed the
12 ;;         buffer to us one character at a time.
13 ;;       - [ ] We want to show the user's input on the screen while reading
14 ;; - [ ] Read a file that was bundled with the program
15 ;;       - It looks like we can use EFI_LOAD_FILE_PROTOCOL.LoadFile() to load
16 ;;         a file into a buffer. In order to be able to use this, we need to
17 ;;         have some way of interpreting a static buffer instead of reading as
18 ;;         we go.
19
20 ;; EFI struct definitions {{{
21
22 EFI_NOT_READY = 0x8000_0000_0000_0000 or 6
23
24 ;; Based on https://wiki.osdev.org/Uefi.inc
25 macro struct name {
26   virtual at 0
27     name name
28   end virtual
29 }
30
31 struc EFI_TABLE_HEADER {
32   dq ?
33   dd ?
34   dd ?
35   dd ?
36   dd ?
37 }
38
39 struc EFI_SYSTEM_TABLE {
40   .Hdr EFI_TABLE_HEADER
41   .FirmwareVendor dq ? ; CHAR16*
42   .FirmwareRevision dd ? ; UINT32
43   align 8
44   .ConsoleInHandle dq ? ; EFI_HANDLE
45   .ConIn dq ? ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL*
46   .ConsoleOutHandle dq ? ; EFI_HANDLE
47   .ConOut dq ? ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL*
48   ; ...
49 }
50 struct EFI_SYSTEM_TABLE
51
52 struc EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
53   .Reset dq ? ; EFI_TEXT_RESET
54   .OutputString dq ? ; EFI_TEXT_STRING
55   ; ...
56 }
57 struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
58
59 struc EFI_SIMPLE_TEXT_INPUT_PROTOCOL {
60   .Reset dq ? ; EFI_INPUT_RESET
61   .ReadKeyStroke dq ? ; EFI_INPUT_READ_KEY
62   ; ...
63 }
64 struct EFI_SIMPLE_TEXT_INPUT_PROTOCOL
65
66 struc EFI_INPUT_KEY {
67   .ScanCode dw ? ; UINT16
68   .UnicodeChar dw ? ; CHAR16
69   align 8
70 }
71 struct EFI_INPUT_KEY
72
73 ;; }}}
74
75 section '.text' code executable readable
76
77 main:
78   ; At program startup, RDX contains an EFI_SYSTEM_TABLE*.
79   mov [system_table], rdx
80
81   mov rcx, hello_string
82   mov rdx, hello_string.len
83   call print_string
84
85   mov rcx, char_buffer
86   call read_char
87
88   mov rcx, char_buffer
89   mov rdx, 1
90   call print_string
91
92   mov rcx, hello_string
93   mov rdx, hello_string.len
94   call print_string
95
96   ret
97
98 ;; Print a string of the given length.
99 ;;
100 ;; Inputs:
101 ;;  - RCX = String buffer
102 ;;  - RDX = String length
103 print_string:
104   mov r8, rcx
105   mov r9, rdx
106
107   mov r10, r9
108   add r10, r10
109
110   ; We take an input string of bytes without any terminator. We need to turn
111   ; this string into a string of words, terminated by a null character.
112   mov rcx, 0
113   mov rsi, 0
114 .copy_byte:
115   cmp rcx, r10
116   je .done
117
118   mov al, byte [r8 + rsi]
119   lea rdx, [.output_buffer + rcx]
120   mov byte [rdx], al
121   inc rcx
122   inc rsi
123
124   lea rdx, [.output_buffer + rcx]
125   mov byte [rdx], 0
126   inc rcx
127
128   jmp .copy_byte
129 .done:
130   lea rdx, [.output_buffer + r10]
131   mov byte [rdx], 0
132
133   ; At this point we have our null-terminated word-string at .output_buffer. Now
134   ; we just need to print it.
135
136   mov rcx, [system_table]                                       ; EFI_SYSTEM_TABLE* rcx
137   mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]                      ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* rcx
138   mov rdx, .output_buffer
139   mov rbx, [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString] ; EFI_TEXT_STRING rbx
140   sub rsp, 32
141   call rbx
142   add rsp, 32
143   ret
144
145 ;; Read a character as an ASCII byte into the given buffer.
146 ;;
147 ;; Inputs:
148 ;; - RCX = Character buffer (1 byte)
149 read_char:
150   mov r15, rcx
151 .read_key:
152   mov rcx, [system_table] ; EFI_SYSTEM_TABLE* rcx
153   mov rcx, [rcx + EFI_SYSTEM_TABLE.ConIn] ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL* rcx
154   mov rbx, [rcx + EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke] ; EFI_INPUT_READ_KEY rbx
155   mov rdx, input_key ; EFI_INPUT_KEY* rdx
156   sub rsp, 32
157   call rbx
158   add rsp, 32
159
160   mov r8, EFI_NOT_READY
161   cmp rax, r8
162   je .read_key
163
164   mov ax, [input_key.UnicodeChar]
165   mov [r15], al
166
167   ret
168
169 section '.data' readable writable
170
171 system_table dq ?  ; EFI_SYSTEM_TABLE*
172
173 hello_string db 'Hello, world!', 0xD, 0xA, 'Here is some more text.', 0xD, 0xA
174 .len = $ - hello_string
175
176 print_string.output_buffer rq 0x400
177
178 char_buffer db ?
179
180 input_key EFI_INPUT_KEY