/
floppyloader.asm
333 lines (261 loc) · 10 KB
/
floppyloader.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
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
[bits 16]
[org 0]
[cpu 8086] ; Attempt to stop me from accidentally using 286 instructions
; Where we'll load in the stage2
; Since we're located at 07C0:0000 and we're loading at 0100:000
; That leaves us 0x6C00 bytes or 27648 bytes for the stage 2 loader
%define STAGE_SEGMENT 0x100
; We want to load the root directory above this loader
%define ROOT_DIR_SEGMENT 0x7e0
%define FAT_SEGMENT 0x7e0
%define FLOPPY_DRIVE 0x00
%define DIRECTORY_ENTRIES 224
%define BYTES_PER_ENTRY 32
%define ROOT_DIR_SECTORS ( (DIRECTORY_ENTRIES * BYTES_PER_ENTRY) / 512 )
%define BYTES_PER_SECTOR 512
%define TOTAL_SECTORS 2880
%define RESERVED_SECTORS 1
%define SECTORS_PER_CLUSTER 1
%define SECTORS_PER_FAT 9
%define NUM_FATS 2
%define NUM_HEADS 2
%define SECTORS_PER_TRACK 18
%define MEDIA_DESCRIPTOR 0xf0
%define TRACKS_PER_SIDE ((TOTAL_SECTORS / SECTORS_PER_TRACK)/2)
%define FAT_SECTOR RESERVED_SECTORS
%define ROOT_DIR_SECTOR ( FAT_SECTOR + (SECTORS_PER_FAT * NUM_FATS) )
%define FILENAME_LENGTH 11
;
; FAT Boot Record for a 1.44mb floppy
;
jmp short init ; Bytes 0-1 - Jump over boot record
nop ; Byte 2
db "real_os_" ; Bytes 3-10 - OEM Identifier
dw BYTES_PER_SECTOR ; Bytes 11-12 - Bytes per Sector
db SECTORS_PER_CLUSTER ; Byte 13 - Sectors per Cluster
dw RESERVED_SECTORS ; Bytes 14-15 - Reserved Sectors (This one)
db NUM_FATS ; Byte 16 - Number of FATs
dw DIRECTORY_ENTRIES ; Bytes 17-18 - Root Directory Entries
dw TOTAL_SECTORS ; Bytes 19-20 - Total Number of Sectors
db MEDIA_DESCRIPTOR ; Byte 21 - Media Descriptor Type
dw SECTORS_PER_FAT ; Bytes 22-23 - Sectors per FAT
dw SECTORS_PER_TRACK ; Bytes 24-25 - Sectors per Track
dw NUM_HEADS ; Bytes 26-27 - Number of Heads/Sides
dd 0 ; Bytes 28-31 - Number of Hidden Sectors
dd 0 ; Bytes 32-35 - Large Amt Sectors (unused)
;
; FAT12 Extended Boot Record
;
db 0x00 ; Byte 36 - Drive Number
db 0x00 ; Byte 37 - Reserved
db 0x29 ; Byte 38 - Signature (0x28 or 0x29)
dd 0xDEADBEEF ; Bytes 39-42 - Volume ID
db "RealOS " ; Bytes 43-53 - Volume Label (space padded)
db "FAT12 " ; Bytes 54-61 - System Identifier
; Make sure that cs=0x7c0 so we know where we are
; Most bioses boot to 0:0x7c00 but some boot to 0x7c0:0
; We do this to be consistant
init:
jmp 0x7c0:start
start:
cli
mov ax, cs ;
mov ds, ax ; Make all our segments the same
mov es, ax ;
; Set up the stack to be the top of the first segment
mov ax, 0
mov ss, ax
mov sp, 0xFFFF
sti
; Bios stores the boot drive in dl for us, save it
mov [bootDrive], dl
mov ah, 00h ; Set Video Mode
mov al, 03h ; Mode 3 - 80x25 text, 16 color
int 10h
mov si, msgEnter ; Print our entrance message
call printStr
driveReset:
mov ah, 0 ; Initialize drive
mov dl, [bootDrive] ; Boot Drive
int 13h
jc driveReset ; If there was an error, try again
; Read in the directory entry
mov ax, ROOT_DIR_SEGMENT
mov es, ax ; Segment to load at
xor bx, bx ; Offset to load at
mov ax, ROOT_DIR_SECTOR ; Start sector
mov cl, ROOT_DIR_SECTORS ; Sectors to load
call readFloppy
; Look through the directory entries for Stage BIN
mov si, stageFileName ; First operand offset (segment in ds)
mov di, 0 ; Start at the first entry
mov cx, FILENAME_LENGTH
mov al, DIRECTORY_ENTRIES
cmpEntry:
push di ; Save the start of the current
repe cmpsb ; Compare the strings
je foundStage
nextEntry:
pop di ; Restore DI
add di, BYTES_PER_ENTRY ; Move to the next entry
mov si, stageFileName
mov cx, FILENAME_LENGTH
dec al
jnz cmpEntry
; Could not find the stage
mov si, msgNoStage
call printStr
mov ah, 0
int 16h ; Wait for a keypress
int 19h ; Reboot
foundStage:
pop di
mov ax, WORD [es:di+26] ; Read in the starting cluster
mov WORD [startingCluster], ax ; Keep it safe
; We don't need the size since we'll just load and follow the fat
; mov bx, WORD [es:di+28] ; Read in the low half of the size
; mov dx, WORD [es:di+30] ; Read in the high half of the size
mov si, msgFoundStage
call printStr
; Read in the FAT
mov ax, FAT_SEGMENT
mov es, ax ; Segment to load at
mov ax, FAT_SECTOR ; Start sector
xor bx, bx ; Offset to load at
mov cl, SECTORS_PER_FAT ; Sectors to load
call readFloppy
mov ax, STAGE_SEGMENT
mov es, ax ; Segment to load to
mov ax, WORD [startingCluster] ; Get the cluster to start at
xor bx, bx ; Start at offset 0
.next:
; Now convert the cluster to a logical sector
push ax ; Save current cluster
sub ax, 2 ; Subtract 2 to zero the cluster number
mov cx, SECTORS_PER_CLUSTER
mul cx ; Multiply ax by cx into ax
add ax, ROOT_DIR_SECTOR + ROOT_DIR_SECTORS
mov cl, SECTORS_PER_CLUSTER ; Read in one sector
call readFloppy
add bx, SECTORS_PER_CLUSTER * BYTES_PER_SECTOR ; Move forward in ram
pop ax ; Get current cluster
call nextCluster ; Find the next cluster
cmp ax, 0xFF0
jb .next ; If it's a valid sector, keep going
; We're done loading, jump to the Stage
mov bl, BYTE [bootDrive]
jmp STAGE_SEGMENT:0
haltLoop:
jmp haltLoop
; Get the next cluster from the FAT
; Arugments:
; ax - Current Cluster Number
; Return:
; ax - Next Cluster Number (if >= ff8 then there is no next cluster)
nextCluster:
push bx ; Save the register's we're changing
push cx
push dx
push es
; Since each cluster entry is 12 bytes we need to multiply by 1.5
mov bx, ax ; Make a copy
shr bx, 1 ; Divide by 2
add bx, ax ; Now we have cluster * 1.5
mov cx, ax ; Save the original cluster number
mov ax, FAT_SEGMENT
mov es, ax ; We're reading from the fat segment
mov dx, WORD [es:bx] ; Read in 2 bytes from the fat
test cx, 1 ; Check if the cluster number is even
jnz .odd
.even:
and dx, 0x0FFF ; Mask off the upper 4 bits
jmp .found
.odd:
shr dx, 1 ; Shift down to keep only the upper 12 bits
shr dx, 1
shr dx, 1
shr dx, 1
.found:
mov ax, dx ; Our return value
pop es
pop dx
pop cx
pop bx ; Restore the register's
ret ; Return
; Print's a message at the current cursor position to the screen
; Arguments:
; si - Address of string
printStr:
;pusha ; Save all GP registers
push ax
push si
.loop:
lodsb ; Read character from string into AL
or al, al ; Set flags based on al
jz .done ; Finish on a null character
mov ah, 0x0e ; BIOS - Write Character
int 10h ;
jmp .loop
.done:
;popa ; Restore GP registers
pop si
pop ax
ret
; Read sectors from the floppy disk to es:0
; Arguments:
; es - Segment to load into
; bx - Offset to load at
; ax - Sector to load (cylinder/head calculated)
; cl - Number of segments to load
readFloppy:
;pusha ; Save GP Registers
push ax
push bx
push cx
push dx
push bx ; Save offset
mov bl, cl ; Save number of segments
; Convert LBA/logical sector to CHS
mov cx, SECTORS_PER_TRACK
xor dx, dx ; Clear dx since we're dividing dx:ax by bx
div cx ; Divide Sector / Sectors per track
inc dx ; Remainder + 1 is the sector
push dx ; Save the sector
xor dx, dx
mov cx, NUM_HEADS ; Now divide the last result by the number
div cx ; of heads
pop cx ; Sector in cl
mov dh, dl ; Head number
mov ch, al ; Track Number
mov di, 5 ; 5 retries
.found:
mov al, bl ; Number of sectors to read
pop bx ; Offset to load at
mov dl, [bootDrive] ; Read from the drive
mov ah, 02h ; We want to read from the drive
int 13h
jnc .success
dec di
jnz .found ; Try again
; Failed too many times, error
mov si, diskError
call printStr ; Print error message
mov ah, 0
int 16h ; Wait for any key
int 19h ; Reset
.success:
;popa ; Restore registers
pop dx
pop cx
pop bx
pop ax
ret ; Return
msgEnter db "Floppy Bootloader",10,13,0
msgNoStage db "stage2.bin not found",10,13,0
msgFoundStage db "Found Stage 2",10,13,0
diskError db "Disk Error",10,13,0
stageFileName db "STAGE2 BIN"
startingCluster dw 0x0000
bootDrive db 0x00
times 510 - ($-$$) db 0 ; Fill all remaing space with 0's except
dw 0xaa55 ; for the last two, which are 0xAA55