summaryrefslogtreecommitdiff
path: root/bootblocks/mbr.s
blob: fb3ac5045d8b607072a1e50a3457d8ae6c520c93 (plain)
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
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
!
! This is a 'Master Boot Record' following the MSDOS 'standards'.
! This BB successfully boots MSDOS, Windows or Linux in CHS or Linear.
!
! Copyright GPL2, Robert de Bath, 1996-2008.
!
! NB: This needs as86 0.16.0 or later
!
! Lowest available is $0500, MSDOS appears to use $0600 ... I wonder why?
ORGADDR=$0600
BOOTADDR=0x7c00

pbr=0	        ! Make this a partition boot record for ldboot.
direct=1        ! Direct boot from MBR to any sector.

copyright=0	! Add in the copyright message; if room.
mbrkey=0	! Option to choose the boot record based on keystroke (107)
message=1	! Display boot message (6+message space)

markptab=1	! Put an end marker just below the partition table.
use512=0	! Put the end marker at byte 510..512

linear=1	! Use the LBA BIOS addresses.
useCHS=1	! Disable CHS if you need space.
linCHS=1        ! Calculate CHS from linear mbr values.	 (41 bytes)

preboot=0	! Include the pre-boot loader. (40 bytes)

if linCHS
	! Allow immediate shifts etc.
	use16 186
endif
	mbr_extras=ORGADDR+0x1B6
	partition_start=ORGADDR+0x1BE
	partition_size=0x10
	partition_end=ORGADDR+0x1FE

if pbr|direct
	table_start=ORGADDR+0x1A2     ! Space for special table.
else
	table_start=mbr_extras
endif

export pbr
export direct
export linear
export useCHS
export linCHS
export mbrkey
export end_of_code

if preboot
export preboot
endif

org ORGADDR
    if pbr
	! Skip a potential MSDOS BPB.
boot_start:
	j	code
	nop        ! DOS appears to _require_ this to identify an MSDOS disk!!

	.blkb	boot_start+3-*
	.ascii	"LINUX"  ! System ID
	.byte	0,0,0
	.blkb	boot_start+0x5A-*
code:
    endif

	cli			! Assume _nothing_! (needed for NT 4)
	cld
	mov	bx,#BOOTADDR	! Pointer to start of BB.
	xor	ax,ax		! Segs all to zero
	mov	ss,ax
	mov	sp,bx		! SP Just below BB
    if pbr
	push	[si+10]		! Save the inbound disk offset and drive
	push	[si+8]		! As if called pbr(drive, offset)
	push	dx
    endif
	mov	ds,ax
	mov	es,ax
	mov	cx,#$100	! Move 256 words
	mov	si,bx		! From default BB
	mov	di,#ORGADDR	! To the correct address.
	rep
	 movsw
	jmpi	cont,#0		! Set CS:IP correct.
cont:
	sti			! Let the interrupts back in.

    if pbr
	pop	[boot_drive]
	pop	[boot_part]
	pop	[boot_part+2]
    endif

!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
! Next check for a pre-boot message, load or keypress
    if message
	call disp_message
    endif
    if preboot
	call preboot_code
    endif
    if mbrkey
	call 	key_wait
    endif

!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
!
  if (linear|useCHS)

    if pbr=0
! Now check the partition table, must use SI as pointer cause that's what the
! partition boot blocks expect.

! Normal active partition check, (Order: 1,2,3,4)
	mov	si,#partition_start
check_active:
	cmp	byte [si],#$80		! Flag for activated partition
	jz	found_active
    if direct=0
try_next_part:				! Only if no direct.
    endif
	add	si,#partition_size
	cmp	si,#partition_end
	jnz	check_active
    endif

    if pbr|direct
	mov	si,#table_start
	cmp	byte [si],#0
	jnz	found_active
try_next_part:
    endif

	jmp	no_partition

!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
! Active partition found, boot it.
found_active:
	mov	di,#6		! Max retries, int doc says 3 ... double it
	movb	[$7DFE],#0	! Clear magic for dosemu
retry:

!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
! If the BIOS has LBA extensions use them.
! If we have no CHS don't check for LBA just use it.
    if linear
	mov	dx,[si]		! dh = Drive head, dl = $80 ie HD drive 0

      if useCHS
	mov	ah,#$41
	mov	bx,#$55AA
       if mbrkey
	test	dl,#$80
	jz	do_CHS
       endif
	int	$13
	jc	do_CHS          ! Unknown call
	cmp	bx,#$AA55
	jnz	do_CHS          ! Wrong call
	test  cl,#1
	jz 	do_CHS		! EDD basic functions OK.
      endif

	push	si		! Save SI on read.

      if pbr|direct
	mov   ax,[si+8]	! 32bit disk address
	mov   cx,[si+10]
	cmp   si,#table_start
	jnz   normal_part
	add   ax,[boot_offset]
	adc   cx,[boot_offset+2]
normal_part:
      endif

	xor	bx,bx
	push  bx		! 64bit disk address
	push  bx
      if pbr|direct
	push  cx		! 32bit disk address
	push  ax
      else
	push  [si+10]		! 32bit disk address
	push  [si+8]
      endif
	push  bx		! Load segment
	push  #BOOTADDR	! Load address
	push  #1		! Number of sectors
	push  #16		! Packet size
	mov   si,sp
	mov	ah,#$42
	int	$13
	lea   sp,[si+16]

	pop	si
	jc	retry_error
	j	sector_loaded

    endif !linear

    if useCHS
do_CHS:

      if linCHS
	call  calc_chs
      else
	mov	dx,[si]		! dh = Drive head, dl = $80 ie HD drive 0
	mov	cx,[si+2]	! cx = Sector & head encoded for int $13
      endif

	mov   bx,#BOOTADDR    ! Pointer to start of BB.
	mov	ax,#$0201	! Read 1 sector
	int   $13		! Disk read.
	jnc	sector_loaded
    endif

! Error, reset and retry
retry_error:
	xor	ax,ax
	int	$13		! Disk reset

	dec	di
	jnz	retry		! Try again

	mov	si,#disk_read_error
	jmp	no_boot		! Sorry it ain't gonna work.

sector_loaded:
	mov	di,#$7DFE	! End of sector loaded
	cmp	[di],#$AA55	! Check for magic
	jnz	try_next_part	! No? Try next partition.

	mov	bp,si		! LILO says some BBs use bp rather than si
	jmpi	#BOOTADDR,#0	! Go!

no_partition:
	mov	si,#no_bootpart
no_boot:		! SI now has pointer to error message
	call	puts

  endif !(linear|useCHS)

! Fatal errors ...........
  if mbrkey
	mov	si,#crlf
	call	puts
	j	key_pause

  else

	mov	si,#press_key
	call	puts
keyboot:		! Wait for a key then reboot
	xor	ax,ax
	int	$16
	jmpi	$0,$FFFF	! Reboot.
  endif

  if useCHS
    if linCHS

calc_chs:               ! From lilo. 
	mov   ah,#8
	int   $13	        ! Drive Geom
	shr   dx,#8
	xchg  ax,dx
	inc   ax              ! AX = No. Heads
	dec   cx		! Davide BIOS bug: CX=0 => Sectors=64
	and   cx,#$3f         ! CX = Sectors 
	inc   cx

	mul   cx
	xchg  ax,bx           ! BX = Cylinder size

	mov   ax,[si+8]       ! Linear partition address.
	mov   dx,[si+10]

      if pbr|direct
	cmp   si,#table_start
	jnz   std_part
	add   ax,[boot_offset]
	adc   dx,[boot_offset+2]
std_part:
      endif

	div   bx              ! AX = Cyl, DX = head & sect

	shl   ah,#6
	xchg  ah,al
	xchg  dx,ax
	div   cl              ! AH = sect-1, AL = Head
	or    dl,ah           ! merge for CX arg.
	mov   cx,dx   
	inc   cx              ! Adjust sector No.

	mov   dx,[si]         ! dh = Orig Drive head, dl = $80 ie HD drive 0

	mov   dh,al           ! Head No.

	; CX & DX ready for int $13
	ret

    endif
  endif
!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
! 
  if message
disp_message:
	mov	si,#Banner
  endif

! Display message uses SI,AX,BX
puts:			! This is smaller than using $13
	lodsb
	cmp	al,#0
	jz	.EOS
	mov	bx,#7
	mov	ah,#$E
	int	$10
	jmp	puts
.EOS:
	ret

!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
!

  if mbrkey=0
press_key:
	.asciz	"\r\nPress return:"
  endif

!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
!

  if (linear|useCHS)
    if mbrkey
disk_read_error:
no_bootpart:
	.asciz	"Boot error"
    else
disk_read_error:
	.asciz	"Disk read error"
no_bootpart:
	.asciz	"No bootable partition"
    endif
  endif !(linear|useCHS)

!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
! Choose the partition based on a pressed key ...

  if mbrkey
key_wait:
	mov	si,#Prompt
	call	puts
	call	wait_key
	jnz	key_pause

	mov	si,#Unprompt		! Nothing has happened, return.
	call	puts
	ret

key_pause:
	mov	si,#Pause
	call	puts

key_tick:
	call	wait_key
	jz	key_tick
	j	Got_key

wait_key:
	mov	di,#19			! Wait for 18-19 ticks

next_loop:
	mov	ah,#1
	int	$16
	jnz   done_wait
	mov	ah,#0
	int	$1A			! Get current tick
	cmp	dx,si			! If changed DEC our counter.
	jz	next_loop
	mov	si,dx
	dec	di
	jnz	next_loop

done_wait:
	ret

Got_key:
	mov	ah,#0			! Clean the kbd buffer.
	int	$16
	cmp	al,#0x20
	jz	key_tick

	push	ax
	mov	Showkey,al
	mov	si,#Showkey
	call	puts
	pop	ax

	! ... Now we use our key ...
	! 0 		=> Floppy
	! 1 .. 4	=> Hard disk partition.

    if useCHS
	cmp	al,#'F
	jz	is_floppy
	cmp	al,#'f
	jz	is_floppy
    endif

	cmp	al,#'1
	jb	key_pause
	cmp	al,#'4
	ja	key_pause

	and   ax,#0x7
	dec	ax
	mov	cl,#4
	shl	ax,cl
	add	ax,#partition_start
	mov	si,ax

! Set active flag for disk interrupt.
	or	byte [si],#$80
	br	found_active

    if useCHS
is_floppy:
	mov	si,#floppy_part
	br	found_active
    endif

Prompt:
	.asciz	"\rMBR: "
Unprompt:
	.asciz	"\r    \r"
Pause:
    if useCHS
	.asciz	"\rMBR F1234> "
    else
	.asciz	"\rMBR 1234> "
    endif
Showkey:
	.ascii	" "
crlf:
	.asciz	"\r\n"
floppy_part:
	.word	0,1

  endif

!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
! This is the pre-boot loader it uses CHS but that's ok for track 0
!
  if preboot
public preboot_code
preboot_code:
	mov	si,#pre_boot_table
	lodsw
	mov	di,ax		! First word is execute address
more_boot:
	lodsw
	test	ah,ah
	jz	load_done
	mov	bx,ax		! word 1 address
	lodsw
	mov	cx,ax		! word 2 CX, cylinder/sector
	lodsw
	mov	dx,ax		! word 3 DX, drive, head
	lodsw			! word 4 AX, $02, sector count
	int	$13
	jnc	more_boot	! This doesn't retry, with a HD it shouldn't be bad.

	mov	si,#disk_read_error
	br	no_boot		! Sorry it ain't gonna work.
load_done:
	call	di
exec_done:
return:
	ret

export pre_boot_table
pre_boot_table:
	! Example: Do nothing.
	.word return,0

	! If the message is in use, preallocate some extents
    if message
	.blkb 64
    endif

	! Labels
	! .word <execute address>
	! Then repeat ..
	! .word <BX>, <CX>, <DX>, <AX>
	! Or.
	! .word <Load Address>
	! .byte <sector> + (<cyl> & $300)>>2), <cyl> & $FF
	! .byte <Drive>, <Head>, <cnt>, 2
	! Finally
	! .word 0

	! Example: Load rest of H0,C0 into mem at BOOTADDR
	! .word BOOTADDR
	! .word BOOTADDR,$0002,$8000,$0210
	! .word $0000
  endif

!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
!
end_of_code:

!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
!

  if message
export Banner
Banner:
    if (linear|useCHS)
      if *<ORGADDR+0x100
	org ORGADDR+0x100
      endif
	.blkb	80	! At least 80 bytes for the message.
    else
	.asciz "This disk is not bootable."
    endif
  endif

!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
! Now make sure this isn't too big! Don't overlap disk serial numbers.
	if *>table_start
	 fail! Partition table overlaps
	endif

!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
! And a copyright message if there's room.
  if copyright
    if *<ORGADDR+0x170
	org ORGADDR+0x170
	.asciz "ELKS MBR       "
	.asciz "Robert de Bath,"
	.asciz "Copyright GPL2 "
	.asciz "1996-2008."
    endif
  endif

!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
!
  if pbr|direct
      if message
	org table_start-1
	.byte 0xFF
      else
	org table_start
      endif
boot_drive:
	.word 0
	.blkb 6
boot_part:
	.long 0
	.long 0
boot_offset:
	.long 0x7FFFFFFF

   if pbr=0
export boot_offset
export boot_drive
export boot_part
   endif
  endif

	org mbr_extras     ! Dirty bit, Serial number
	.word 0
serial_no:
	.blkb 4

!-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
! Clear the sector to the bottom of the partition table.
  if markptab
    if *<partition_start
	org partition_start-1
	.byte 0
    endif
  endif

  if use512
	org ORGADDR+0x1FE
	.word 0xAA55
  endif

! Sanity check.

  if pbr|direct
    if useCHS
      if linCHS=0
	fail !Raw CHS doesn't work with pbr or direct.
      endif
    endif
  endif

!THE END