;----------------------------------------------------------------
; BootLoader - simple test boot loader for PCs
; The boot loader performs the following functions:
;   - sets the segments for use in the boot process.
;   - loads and verifies the boot image, then transfers control  to it.
; v 0.01  Joseph Osako 3 June 2002

%define entrypoint 0x7C00  ; can't us an equate constant with the ORG directive

stage2_entry   equ 0x1000   ; the segment:offset to load the second stage into
stage2_offset   equ 0x0000   
stack_seg   equ 0x9000
stack_top   equ 0xFFFC

VBIOS   equ 0x10   ; BIOS interrupt vector for video services
ttype   equ 0x0E   ; insert character in AL as if screen were teletype
EOL   equ 0x00   ;end of string marker
CR   equ 0x0D
LF   equ 0x0A

DBIOS   equ 0x13   ; BIOS interrupt vector for disk services
disk_reset   equ 0x00   ; disk reset service
disk_read   equ 0x02   ; disk read service

cyl   equ 0x00   ; cylinder to read from
head   equ 0x00   ; head to read from
startsector   equ 0x02   ; sector to start reading at
numsectors   equ 0x01   ; number of sectors to read

%define zero(x) xor x, x

%macro write 1
   mov si, %1
   call printstr
%endmacro


[bits 16]
[org entrypoint]

; entry - the entrypoint to the code. Make a short jump passed the BPB.
entry:
  jmp short entry2

; The Boot Parameter Block - keep this for compatibility with the FAT12 filesystem.
BPB times 56 db 0

; entry2 - do a far jump to ensure that you have the desired segment:offset location
entry2:
   jmp 0x0000:start

;***************************************************
; start
; This is the real begining of the code. The first order of
; business is clearing the interrupts, then setting the
; segment registers and the stack pointer.  

start:

   mov ax, stack_seg
   mov bx, stack_top
   cli
   mov ss, ax         ; and the stack at an arbitrarily high point past ES.
   mov sp,  bx        ; put the stack pointer to the top of SS
   sti                         ; reset ints so BIOS calls can be used
   mov ax, cs
   mov ds, ax         ; set DS == CS

   write testnumber   
   mov [bootdrv], dl   ; save boot drive info for later use
   write reset

; reset disk to read from
reset_disk:
   mov dl, [bootdrv]
   zero (ah)
   mov al, disk_reset
   int DBIOS
   jnc short read_disk
   write no_reset
   call shutdown

; read in the data from disk and load it to ES:BX (already initalized)
read_disk:
   write loading
  mov ax, stage2_entry
  mov es, ax
   mov dl, [bootdrv] 
   mov ch, cyl      ; cylinder
   mov dh, head      ; head
   mov cl, startsector   ; first sector 
   mov al, numsectors   ; number of sectors to load   
   mov ah, disk_read
  mov bx, stage2_offset
   int DBIOS
   jnc short done_reading
   write disk_fail
   call shutdown

done_reading:
   write done

; set up fake return frame for code returning from second stage
  mov ax, cs
  push ax
  mov ax, reenter
  push ax  

; fake a jump to the second stage entry point
  mov ax, stage2_entry
  mov es, ax
  mov bx, stage2_offset
   push es
  push bx
   write snd_stage
  retf

reenter:
  mov ax, cs
  mov ds, ax
  write returned

shutdown:
   write exit 
   jmp short $

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Auxilliary functions      

;; printstr - prints the string point to by SI

printstr:
   push ax
   mov ah, ttype     ; set function to 'teletype mode'
.loop:   
   lodsb   ; update byte to print
   cmp al, EOL   ; test that it isn't EOL
   jz short .endstr
   int  VBIOS   ; put character in AL at next cursor position
   jmp short .loop
.endstr:
   pop ax
   ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; data
testnumber   db 'Test #12A', CR, LF, EOL
reset   db 'Resetting disk drive.', CR, LF, EOL
loading   db 'Loading stage two... ', EOL
done   db 'done.', CR, LF, EOL
snd_stage   db  'Second stage loaded, proceeding to switch context.', CR, LF, EOL
returned            db 'Control returned to first stage, ', EOL
no_reset   db 'Could not reset drive,', EOL
disk_fail   db  'could not read second stage, ', EOL
exit   db 'system halted.', EOL

bootdrv  db 0  ; byte reserved for boot drive ID number

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; pad out to 510, and then add the last two bytes needed for a boot disk

space   times (0x0200 - 2) - ($-$$) db 0
bootsig   dw 0xAA55