4ec1b570946570b09d078b3ad22a0341a566da58
[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 ;;
74 ;; [TODO] Handle newlines correctly. (I.e. translate '\n' to '\r\n'.)
75 uefi_print_string:
76   mov r8, rcx
77   mov r9, rdx
78
79   mov r10, r9
80   add r10, r10
81
82   ; We take an input string of bytes without any terminator. We need to turn
83   ; this string into a string of words, terminated by a null character.
84   mov rcx, 0
85   mov rsi, 0
86 .copy_byte:
87   cmp rcx, r10
88   je .done
89
90   mov al, byte [r8 + rsi]
91   lea rdx, [.output_buffer + rcx]
92   mov byte [rdx], al
93   inc rcx
94   inc rsi
95
96   lea rdx, [.output_buffer + rcx]
97   mov byte [rdx], 0
98   inc rcx
99
100   jmp .copy_byte
101 .done:
102   lea rdx, [.output_buffer + r10]
103   mov byte [rdx], 0
104
105   ; At this point we have our null-terminated word-string at .output_buffer. Now
106   ; we just need to print it.
107
108   mov rcx, [system_table]                                       ; EFI_SYSTEM_TABLE* rcx
109   mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]                      ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* rcx
110   mov rdx, .output_buffer
111   mov rbx, [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString] ; EFI_TEXT_STRING rbx
112   sub rsp, 32
113   call rbx
114   add rsp, 32
115   ret
116
117 ;; Read a character as an ASCII byte into the given buffer.
118 ;;
119 ;; Inputs:
120 ;; - RCX = Character buffer (1 byte)
121 ;;
122 ;; [TODO] Handle enter key correctly (should return '\n').
123 uefi_read_char:
124   mov r15, rcx
125 .read_key:
126   mov rcx, [system_table]                                       ; EFI_SYSTEM_TABLE* rcx
127   mov rcx, [rcx + EFI_SYSTEM_TABLE.ConIn]                       ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL* rcx
128   mov rbx, [rcx + EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke] ; EFI_INPUT_READ_KEY rbx
129   mov rdx, input_key                                            ; EFI_INPUT_KEY* rdx
130   sub rsp, 32
131   call rbx
132   add rsp, 32
133
134   mov r8, EFI_NOT_READY
135   cmp rax, r8
136   je .read_key
137
138   mov ax, [input_key.UnicodeChar]
139   mov [r15], al
140
141   ;; Print the character
142   mov rcx, r15
143   mov rdx, 1
144   call uefi_print_string
145
146   ret
147
148 ;; Terminate with the given error code.
149 ;;
150 ;; Inputs:
151 ;; - RCX = Error code
152 uefi_terminate:
153   mov rcx, terminated_msg
154   mov rdx, terminated_msg.len
155   call uefi_print_string
156   jmp $
157
158 section '.data' readable writable
159
160 system_table dq ? ; EFI_SYSTEM_TABLE*
161
162 terminated_msg db 0xD, 0xA, '(The program has terminated.)', 0xD, 0xA
163 .len = $ - terminated_msg
164
165 uefi_print_string.output_buffer rq 0x400
166
167 char_buffer db ?
168
169 input_key EFI_INPUT_KEY