-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathboot.asm
316 lines (247 loc) · 9.82 KB
/
boot.asm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
bits 16 ; Cuz real mode.
org 0x7C00 ; Cuz this is where our sector is loaded in memory.
WIDTH equ 320
HEIGHT equ 200
FB_ADDR equ 0xA000
DECOMPRESSED_ADDR equ 0x8000
VGA_PALETTE_BASE equ 0x40 ; 0x20 looks alright too. 0x30 and 0x50 are weird though.
SNOW equ 0x0F
main:
; Set video mode.
mov ah, 0x00 ; Set video mode.
mov al, 0x13 ; 320x200, 256 colours, as god intended.
int 0x10 ; BIOS video interrupt.
; Load next sector into the memory right after this one.
mov ah, 0x02 ; Read sectors.
mov al, 1 ; Read one sector.
xor ch, ch ; Cylinder 0.
mov cl, 2 ; Sector 2 (boot sector is in sector 1).
xor dh, dh ; Head 0.
; dl should already be set to the boot drive by the BIOS.
mov bx, 0x7E00 ; Load into memory right after boot sector.
int 0x13 ; BIOS disk interrupt.
jc read_error
; Decompress image.
; We write words each time as a cheap way to stretch the image to double its height.
mov si, graphic_i0 ; Pointer to current run.
mov ax, DECOMPRESSED_ADDR
mov es, ax ; Destination for decompressed image.
mov ax, 0x0101 ; State of the current run (0 is black, 1 is white).
; Start at 1 because we will immediately be flipped back to black.
xor di, di ; Destination offset for the decompressed image.
xor ch, ch ; We only want to use the lower 8 bits of cx (counter for rep).
decompress_loop:
mov cl, [si] ; Get the next run length.
and cl, 0xF0 ; Mask first nibble.
shr cl, 4 ; Shift first nibble to the right.
xor ax, 0x0101 ; Flip the colour.
rep stosw ; Write run to the destination offset (es:di).
add di, cx ; Increment the destination offset by the run length.
mov cl, [si] ; Get the next run length.
and cl, 0x0F ; Mask second nibble.
xor ax, 0x0101 ; Flip the colour.
rep stosw ; Write run to the destination offset (es:di).
add di, cx ; Increment the destination offset by the run length.
inc si ; Increment to the next run.
cmp si, graphic_i0 + GRAPHIC_I0_SIZE
mov bx, graphic_i512
cmovz si, bx ; If we've reached the end of the first set of runs, switch to the second set.
cmp si, graphic_i512 + GRAPHIC_I512_SIZE ; If we've reached the end of the second set of runs, we're done.
jnz decompress_loop
; Start draw loop.
xor cx, cx
draw_loop:
; Draw the image to the screen.
mov ax, FB_ADDR
mov es, ax
mov ax, DECOMPRESSED_ADDR
mov ds, ax
mov bx, 0 ; X coordinate.
mov si, 0 ; Graphic index.
graphic_loop_x:
mov ax, 0 ; Y coordinate.
graphic_loop_y:
; If byte in the decompressed image is 0, it is transparent.
cmp byte [ds:si], 0
jz transparent
; Find index on screen to draw pixel to.
; Don't forget to center the graphic.
mov di, ax
add di, (HEIGHT / 2) - (GRAPHIC_H / 2)
imul di, WIDTH
add di, (WIDTH / 2) - (GRAPHIC_W / 2)
add di, bx
; Calculate colour of the pixel, using the following formula: (X + Y + time) % 16 + VGA_PALETTE_BASE.
mov dx, ax
add dx, bx
add dx, cx
and dl, 0x0F ; Mask out the high nibble, equivalent to a mod 16 but without clobbering up registers.
add dl, VGA_PALETTE_BASE ; Base VGA colour in palette.
mov byte [es:di], dl ; Actually write the pixel colour to the framebuffer.
transparent:
inc si ; We must always increment our decompressed image index.
; Increment Y coordinate and break out if we've reached the end of our graphic.
inc ax
cmp ax, GRAPHIC_H
jnz graphic_loop_y
; Increment X coordinate and break out if we've reached the end of our graphic.
inc bx
cmp bx, GRAPHIC_W
jnz graphic_loop_x
; Already push cx here so we can use it for other shit.
push cx
; Iterate snow simulation.
mov ax, HEIGHT - 2 ; Y coordinate. Start at the bottom or snowflakes which have already been processed will be processed again on the next layer down.
snow_loop_y:
mov bx, 0 ; X coordinate.
snow_loop_x:
; Find index in the framebuffer for this coordinate.
mov di, ax
imul di, WIDTH
add di, bx
; Is there snow here? If not, just skip to the next pixel.
mov cl, [es:di]
cmp cl, SNOW
jnz skip
; If we've reached this point, there is snow at this pixel.
; The first thing we always need to do is check the pixel right underneath.
; If there is nothing, simply fall.
mov si, di ; Store the previous position of the snowflake for later.
add di, WIDTH ; Go down one row.
mov cl, [es:di]
test cl, cl
jz fall
; If we're not on top of snow, then "stick".
cmp cl, SNOW
jnz skip
; If we can't go directly underneath, try going to the right.
inc di
mov cl, [es:di]
test cl, cl
jz fall
; If we can't go to the right either, try going to the left.
sub di, 2
mov cl, [es:di]
test cl, cl
jz fall
jmp skip
fall:
mov byte [es:si], 0 ; Clear the previous position the snowflake was at.
mov byte [es:di], SNOW ; Draw a new snowflake right underneath.
skip:
; Increment X coordinate and break out if we've reached the end of the screen.
inc bx
cmp bx, WIDTH
jnz snow_loop_x
; Decrement Y coordinate and break out if we've reached the end of the screen.
dec ax
cmp ax, -1
jnz snow_loop_y
; Spawn a new snow particle at a random spot at the top of the screen.
; Use the PIT's channel 0 as a "random" value.
; XXX I don't think QEMU supports rdrand, so we're not using that.
; XXX Also, I wasn't able to read anything from 0x40:0x6C in the BDA as @fuz's answer suggested :(
xor dx, dx
in ax, 0x40
mov cx, WIDTH
div cx ; Divide dx:ax by WIDTH. Remainder will be stored in dx.
mov di, dx
mov byte [es:di], SNOW
; Wait for a bit before jumping to the top of the draw loop.
mov ah, 0x86 ; Elapsed time wait.
mov cx, 0 ; Higher 16 bits of the wait time.
mov dx, 30000 ; Lower 16 bits of the wait time. Wait for 30 ms.
int 0x15 ; BIOS system services.
pop cx
; Finally, do it all over again.
inc cx
jmp draw_loop
read_error:
; Clear screen to red to indicate there was an error.
mov ax, FB_ADDR
mov es, ax ; Set destination to video memory.
xor di, di ; Set destination offset to 0.
mov al, 0x04 ; Fill with red colour.
mov cx, WIDTH * HEIGHT ; Count until the whole screen is filled.
rep stosb
jmp $ ; Hang.
graphic_i512:
db 0xF0, 0xC7, 0xF0, 0xF0, 0xD6, 0xF0, 0xF0, 0xF4,
db 0xF0, 0xF0,
GRAPHIC_I0_SIZE equ 512
GRAPHIC_I512_SIZE equ 10
GRAPHIC_W equ 132
GRAPHIC_H equ 50 * 2
riddle:
db "Les paroles de The Riddle, de Nik Kershaw: "
db "J'ai deux bras forts, les benedictions de Babylon (skr). "
db "Il est temps de porter sur, et essayer pour peches. "
db "Le seul indice que je peux te donner, sont les 2 prochains octets:"
times 510 - ($ - $$) db 0x69 ; Pad remaining space with 69s.
dw 0xAA55 ; Boot signature.
graphic_i0:
db 0x42, 0xD2, 0xF0, 0xF0, 0x15, 0xD2, 0xF0, 0xF1,
db 0x23, 0xD1, 0xF0, 0xE1, 0x33, 0xD1, 0xF0, 0xE1,
db 0x32, 0xE1, 0xF0, 0xD1, 0xF0, 0x42, 0xF0, 0xDF,
db 0x06, 0xF0, 0xEF, 0x06, 0xF0, 0xEF, 0x05, 0xF0,
db 0xFF, 0x04, 0xF0, 0xF0, 0x3E, 0xF0, 0xF0, 0xF0,
db 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x77, 0xF0,
db 0xF0, 0xCA, 0xF0, 0xF0, 0x9B, 0xF0, 0xF0, 0x9C,
db 0xF0, 0xE2, 0x62, 0x92, 0xF0, 0xE3, 0x52, 0xA1,
db 0xF0, 0xF2, 0x53, 0x73, 0xF0, 0xF0, 0x11, 0x6C,
db 0xB5, 0xF1, 0x6B, 0xB7, 0xD2, 0x79, 0x51, 0x52,
db 0x33, 0x52, 0x52, 0xA5, 0x72, 0x41, 0x43, 0x52,
db 0x43, 0x71, 0xF1, 0x32, 0x34, 0x52, 0x24, 0x82,
db 0xE1, 0x31, 0x52, 0x66, 0xA5, 0xB1, 0x31, 0xA7,
db 0xC7, 0x82, 0x22, 0x78, 0xEA, 0x42, 0x33, 0x27,
db 0x32, 0xF0, 0x1D, 0x49, 0x62, 0xF0, 0x49, 0x68,
db 0x62, 0xF0, 0x75, 0x7C, 0x22, 0xF0, 0x74, 0x8F,
db 0x01, 0xEA, 0xBF, 0x03, 0xB7, 0xF0, 0x1F, 0x04,
db 0xF0, 0xF0, 0x5F, 0xF0, 0xF0, 0x8D, 0x98, 0xF0,
db 0x8A, 0x8A, 0xF0, 0x82, 0x16, 0x7C, 0xF0, 0x72,
db 0x42, 0x83, 0x27, 0xF0, 0x72, 0x51, 0x72, 0x41,
db 0x33, 0xF0, 0xF0, 0x72, 0x31, 0x52, 0x71, 0xF0,
db 0xE6, 0x52, 0x72, 0xF0, 0xE4, 0x62, 0x6F, 0x0A,
db 0x73, 0x71, 0x7F, 0x0A, 0xF0, 0x11, 0x9F, 0x09,
db 0x81, 0xF0, 0x2F, 0x09, 0x7A, 0xBF, 0x05, 0x9C,
db 0xF0, 0xF0, 0x8C, 0xF0, 0xF0, 0x8C, 0xF0, 0x56,
db 0xF0, 0x72, 0xF0, 0x3A, 0xF0, 0x51, 0xF0, 0x3C,
db 0x91, 0x81, 0xF0, 0x3E, 0x8C, 0xFF, 0x8C, 0xF3,
db 0x41, 0x26, 0x7C, 0xF2, 0x42, 0x44, 0x7C, 0xE2,
db 0x51, 0x63, 0xF0, 0x21, 0xF3, 0x32, 0x63, 0x71,
db 0xF0, 0xB6, 0x73, 0x72, 0x82, 0xF5, 0x82, 0x83,
db 0x54, 0xF5, 0x82, 0x85, 0x22, 0xF0, 0x43, 0x91,
db 0x98, 0xF0, 0xF0, 0x11, 0xB7, 0xF0, 0x41, 0xF0,
db 0xA7, 0xF0, 0x22, 0xB3, 0xA8, 0xF5, 0x75, 0x83,
db 0x26, 0xE7, 0x43, 0xA2, 0x64, 0xF7, 0x23, 0xB1,
db 0x92, 0xFA, 0xF0, 0xF0, 0xC9, 0xF0, 0xF0, 0xC9,
db 0xF0, 0xF0, 0xD9, 0xF0, 0xF0, 0xAB, 0xF0, 0xF0,
db 0x73, 0x38, 0xF0, 0x21, 0xF0, 0x14, 0x66, 0x44,
db 0x92, 0xE3, 0xA4, 0x36, 0x91, 0xF0, 0xD2, 0x31,
db 0x33, 0x91, 0xF0, 0xE1, 0x31, 0x23, 0xA1, 0xF0,
db 0xF0, 0x21, 0x41, 0xA2, 0xA2, 0x31, 0xF0, 0x11,
db 0xD3, 0xA4, 0x2F, 0x2F, 0x01, 0xB4, 0x1F, 0x02,
db 0x15, 0xF0, 0x74, 0x1F, 0x02, 0x17, 0xF0, 0x54,
db 0x2F, 0x01, 0x28, 0xF0, 0x42, 0x4E, 0x58, 0xF0,
db 0xF0, 0xE8, 0xF0, 0xF0, 0xE8, 0xF0, 0x61, 0x82,
db 0xC8, 0xF0, 0x25, 0x72, 0xD7, 0xF7, 0x71, 0x5F,
db 0x01, 0xE9, 0x52, 0x2F, 0x04, 0xCB, 0x42, 0x12,
db 0xE4, 0xC2, 0x37, 0x23, 0x11, 0xF0, 0x22, 0xC2,
db 0x4B, 0x11, 0xF0, 0x22, 0xD1, 0x12, 0x29, 0x21,
db 0x94, 0x51, 0xD4, 0x38, 0x22, 0x68, 0xF0, 0x33,
db 0x46, 0x32, 0x5A, 0xF0, 0xB2, 0xBC, 0xF0, 0xF0,
db 0x82, 0x82, 0xF0, 0xF0, 0x72, 0xA1, 0xF0, 0xF0,
db 0x81, 0x92, 0xF0, 0xF0, 0x8C, 0xF0, 0xF0, 0x8B,
db 0xF0, 0xF0, 0xA9, 0xF0, 0xF0, 0xC7, 0xF0, 0xF0,
db 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x88,
db 0xF0, 0xE2, 0xAA, 0x9A, 0x84, 0x33, 0x2B, 0x8F,
db 0x02, 0x16, 0x23, 0x23, 0x27, 0x7F, 0x02, 0x16,
db 0x31, 0x31, 0x41, 0x33, 0x8A, 0x84, 0x72, 0x31,
db 0x52, 0xF0, 0xC2, 0x43, 0x25, 0x52, 0xF0, 0xF0,
db 0x33, 0x24, 0x62, 0xF0, 0xF0, 0x41, 0x33, 0x71,
db 0xF0, 0xF0, 0xF0, 0x31, 0xF0, 0xF0, 0x41, 0xF0,
db 0xF0, 0xF0, 0x4F, 0x03, 0xF0, 0xF0, 0x2F, 0x03,
db 0xF0, 0xF0, 0x2F, 0x03, 0xF0, 0xF0, 0x2F, 0x03,
db 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
db 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x23, 0x22, 0xF0,
times 1024 - ($ - $$) db 0x69 ; Pad remaining space with 69s (though there shouldn't be any).