Implement subroutine to read character
[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
15 ;; #region Structs
16
17 EFI_NOT_READY = 0x8000_0000_0000_0000 or 6
18
19 ;; Based on https://wiki.osdev.org/Uefi.inc
20 macro struct name {
21   virtual at 0
22     name name
23   end virtual
24 }
25
26 struc EFI_TABLE_HEADER {
27   dq ?
28   dd ?
29   dd ?
30   dd ?
31   dd ?
32 }
33
34 struc EFI_SYSTEM_TABLE {
35   .Hdr EFI_TABLE_HEADER
36   .FirmwareVendor dq ? ; CHAR16*
37   .FirmwareRevision dd ? ; UINT32
38   align 8
39   .ConsoleInHandle dq ? ; EFI_HANDLE
40   .ConIn dq ? ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL*
41   .ConsoleOutHandle dq ? ; EFI_HANDLE
42   .ConOut dq ? ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL*
43   ; ...
44 }
45 struct EFI_SYSTEM_TABLE
46
47 struc EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
48   .Reset dq ? ; EFI_TEXT_RESET
49   .OutputString dq ? ; EFI_TEXT_STRING
50   ; ...
51 }
52 struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
53
54 struc EFI_SIMPLE_TEXT_INPUT_PROTOCOL {
55   .Reset dq ? ; EFI_INPUT_RESET
56   .ReadKeyStroke dq ? ; EFI_INPUT_READ_KEY
57   ; ...
58 }
59 struct EFI_SIMPLE_TEXT_INPUT_PROTOCOL
60
61 struc EFI_INPUT_KEY {
62   .ScanCode dw ? ; UINT16
63   .UnicodeChar dw ? ; CHAR16
64   align 8
65 }
66 struct EFI_INPUT_KEY
67
68 ;; #endregion
69
70 section '.text' code executable readable
71
72 main:
73   ; At program startup, RDX contains an EFI_SYSTEM_TABLE*.
74   mov [system_table], rdx
75
76   mov rcx, hello_string
77   mov rdx, hello_string.len
78   call print_string
79
80   mov rcx, char_buffer
81   call read_char
82
83   mov rcx, char_buffer
84   mov rdx, 1
85   call print_string
86
87   mov rcx, hello_string
88   mov rdx, hello_string.len
89   call print_string
90
91   ret
92
93 ;; Print a string of the given length.
94 ;;
95 ;; Inputs:
96 ;;  - RCX = String buffer
97 ;;  - RDX = String length
98 print_string:
99   mov r8, rcx
100   mov r9, rdx
101
102   mov r10, r9
103   add r10, r10
104
105   ; We take an input string of bytes without any terminator. We need to turn
106   ; this string into a string of words, terminated by a null character.
107   mov rcx, 0
108   mov rsi, 0
109 .copy_byte:
110   cmp rcx, r10
111   je .done
112
113   mov al, byte [r8 + rsi]
114   lea rdx, [.output_buffer + rcx]
115   mov byte [rdx], al
116   inc rcx
117   inc rsi
118
119   lea rdx, [.output_buffer + rcx]
120   mov byte [rdx], 0
121   inc rcx
122
123   jmp .copy_byte
124 .done:
125   lea rdx, [.output_buffer + r10]
126   mov byte [rdx], 0
127
128   ; At this point we have our null-terminated word-string at .output_buffer. Now
129   ; we just need to print it.
130
131   mov rcx, [system_table]                                       ; EFI_SYSTEM_TABLE* rcx
132   mov rcx, [rcx + EFI_SYSTEM_TABLE.ConOut]                      ; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* rcx
133   mov rdx, .output_buffer
134   mov rbx, [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString] ; EFI_TEXT_STRING rbx
135   sub rsp, 32
136   call rbx
137   add rsp, 32
138   ret
139
140 ;; Read a character as an ASCII byte into the given buffer.
141 ;;
142 ;; Inputs:
143 ;; - RCX = Character buffer (1 byte)
144 read_char:
145   mov r15, rcx
146 .read_key:
147   mov rcx, [system_table] ; EFI_SYSTEM_TABLE* rcx
148   mov rcx, [rcx + EFI_SYSTEM_TABLE.ConIn] ; EFI_SIMPLE_TEXT_INPUT_PROTOCOL* rcx
149   mov rbx, [rcx + EFI_SIMPLE_TEXT_INPUT_PROTOCOL.ReadKeyStroke] ; EFI_INPUT_READ_KEY rbx
150   mov rdx, input_key ; EFI_INPUT_KEY* rdx
151   sub rsp, 32
152   call rbx
153   add rsp, 32
154
155   mov r8, EFI_NOT_READY
156   cmp rax, r8
157   je .read_key
158
159   mov ax, [input_key.UnicodeChar]
160   mov [r15], al
161
162   ret
163
164 section '.data' readable writable
165
166 system_table dq ?  ; EFI_SYSTEM_TABLE*
167
168 hello_string db 'Hello, world!', 0xD, 0xA, 'Here is some more text.', 0xD, 0xA
169 .len = $ - hello_string
170
171 print_string.output_buffer rq 0x400
172
173 char_buffer db ?
174
175 input_key EFI_INPUT_KEY