use16
org 0

start:
  jmp short code_start
  
; compile-time constants, disk image is max 64512 bytes (7 tracks, 2 heads, 9 spt, 512 bytes per sector)
OLD_INT13H                 equ 0FCh
IMG_DISK_TRACKS            equ 7
IMG_DISK_HEADS             equ 2
IMG_DISK_SECTORS           equ 9

IMG_DISK_LOAD_SEG	         dw 0
PCJR                       db 0
RAMERROR                   db 13,10,'>=256K',0
PCJRAMERROR                db 13,10,'640K',0
RAMERRORCONTINUE           db ' RAM needed',0

code_start:
  cli
  cld
  xor ax,ax
  mov ss,ax
  mov sp,0FFFFh
  mov ax,cs
  mov ds,ax
  mov es,ax
   
; at least 256K RAM required on a PC
  push ds
  mov ax,0f000h
  mov ds,ax
  mov si,0FFFEh
  lodsb
  pop ds
  cmp al,0FDh
  je short pcjr_ram_check
  push ds
  xor ax,ax
  mov ds,ax
  mov si,413h
  lodsw
  pop ds
  cmp ax,256d
  jae short @f
  mov si,RAMERROR
  call print
  mov si,RAMERRORCONTINUE
  call print
  sti
  jmp short $  
@@:
  jmp short ram_size_ok
  
; 640K on a PCjr, check by direct write due to memory hole at 128K
pcjr_ram_check:
  mov [PCJR],1
  push ds
  mov ax,09FFFh
  mov ds,ax
  mov es,ax
  mov di,0Ch
  mov ax,0DEADh
  stosw
  mov ax,0BEEFh
  stosw
  mov si,0Ch
  lodsw
  cmp ax,0DEADh
  jne short ram_fail_640k
  lodsw
  cmp ax,0BEEFh
  jne short ram_fail_640k
  mov ax,640d
  clc
  jmp short @f
ram_fail_640k:
  stc
@@:
  pop ds
  jnc short ram_size_ok
  mov si,PCJRAMERROR
  call print
  mov si,RAMERRORCONTINUE
  call print
  sti
  jmp short $
  
WELCOME			               db 13,10,'BASIC 1-stage bootloader, (c) J. Bogin',13,10,'Loading...',13,10,0

; are we relocated ?
ram_size_ok:
  cmp [cs:IMG_DISK_LOAD_SEG],0
  jne short begin_tape
  
; compute IMG_DISK_LOAD_SEG: conventional ram top in KB minus 64K
; patch subtracted RAM size into BIOS data area (PCjr always returns 128K minus videobuffer)
  sub ax,64d
  push ax
  mov cx,6
  shl ax,cl
  mov [cs:IMG_DISK_LOAD_SEG],ax
  mov di,413h
  xor ax,ax
  mov es,ax
  pop ax  
  cmp [PCJR],1
  je short @f
  stosw
  
; relocate this code after the end of the boot disk image (1K left for 64512B image used)
@@:
  mov si,start
  mov ax,[cs:IMG_DISK_LOAD_SEG]
  add ax,0FC0h
  mov es,ax
  push es
  xor di,di
  push di
  mov cx,1024d
  rep movsb
  retf
  
DATAERROR		               db 'Data error, any key to retry',13,10,0

; begin tape loading
begin_tape:
  mov ah,1
  mov cx,0607h
  int 10h
	mov si,WELCOME
	call print
  
	mov ax,[cs:IMG_DISK_LOAD_SEG]
	mov es,ax
  xor bx,bx
	mov ax,0200h
	mov cx,IMG_DISK_TRACKS*IMG_DISK_HEADS*IMG_DISK_SECTORS*512d
	int 15h

; check result
	cmp ah,0
	je short boot_tape
	mov si,DATAERROR
	call print
	xor ax,ax
	int 16h
	jmp short begin_tape
  
BOOTING                    db 'Booting...',13,10,0

; prepare to boot, override ROM BASIC handlers and install new ones
boot_tape:
  mov si,BOOTING
	call print

xor ax,ax
	mov es,ax
  mov bx,0F000h
  cmp [PCJR],0
  je short @f
; PCJR specifics: restore INT 0, 4, 9 (keyb IRQ), 0Bh (COM)
  mov ax,0F815h
  mov [es:0],ax
	mov [es:0+2],bx
  mov [es:4*4],ax
	mov [es:4*4+2],bx
  mov [es:0Bh*4],ax
	mov [es:0Bh*4+2],bx
  mov ax,1561h
  mov [es:9*4],ax
	mov [es:9*4+2],bx
; both PC and Jr: restore original INT 1Bh and 1Ch handlers
@@:
  mov ax,0FF53h
  cmp [PCJR],0
  je short @f
  mov ax,0F83Ch
@@:
  mov [es:1Bh*4],ax
	mov [es:1Bh*4+2],bx
  mov [es:1Ch*4],ax
	mov [es:1Ch*4+2],bx
; INT 60h to FFh will point to IRET
  mov bx,60h
  mov cx,2
@@:
  mov di,bx
  shl di,cl
  mov ax,dummy_handler
  stosw
  mov ax,cs
  stosw
  inc bx
  cmp bx,100h
  jne short @b
; clear memory above BDA (1K to 64K)
  xor ax,ax
  mov di,500h
  mov cx,0FB00h
  rep stosb
; INT 13h vector -> save to OLD_INT13H and install new handler
	mov ax,[es:13h*4]
	mov bx,[es:13h*4+2]
	mov [es:OLD_INT13H*4],ax
	mov [es:OLD_INT13H*4+2],bx
	mov ax,shiny_new_int13h
  mov bx,cs
	mov [es:13h*4],ax
	mov [es:13h*4+2],bx
; INT 19h vector -> install new handler
  mov ax,shiny_new_int19h
	mov [es:19h*4],ax
	mov [es:19h*4+2],bx
  
; increment the number of floppies in system, mark bit 0 as available for boot
  xor ax,ax
  mov ds,ax
	mov di,410h
  mov si,di
	lodsw
  mov cx,6
  shr al,cl
  cmp al,2
  ja short @f
  inc al
@@:
  shl al,cl
  mov si,di
  mov bx,ax
  lodsw
  and al,3Fh
  or al,bl
  or al,1
	stosw
	
; boot
  int 19h
  
; print strings
print:
	mov ah,0Eh
	lodsb
	cmp al,00
	je short @f
	int 10h
	jmp short print
@@:
	ret
  
; dummy ISR
dummy_handler:
  iret
  
; new INT 19h handler: trash CS, IP, flags, copy bootsector to 0:7C00h, DL=0 and boot
shiny_new_int19h:
  cli
  mov ax,[cs:IMG_DISK_LOAD_SEG]
  mov ds,ax
	xor si,si
  mov es,si
  push es
	mov di,7C00h
  push di
	mov cx,512d
  xor dl,dl
	rep movsb
  sti
  retf

; new INT 13h handler
shiny_new_int13h:
	pushf
	cmp dl,0
	je short @f
	popf
	dec dl
	int OLD_INT13H
	inc dl
	push bp
	mov bp,sp
	push ax
	lahf
	mov [bp+6],ah
	pop ax
	pop bp
	iret
@@:
	cli
	push ax
	push cx
	push dx
	push bx
	push bp
	push si
	push di
	push ds
	sti
	cmp ah,02h
	je short new_int13h_read
	cmp ah,03h
	je short new_int13h_write
	cmp ah,08h
	je short new_int13h_params
	cmp ah,15h
	je short new_int13h_disktype
	jmp short new_int13h_end
new_int13h_read:
  xor ah,ah
	push ax
	mov di,bx
	xor si,si
	and cl,03Fh
	dec cl
	mov ax,IMG_DISK_SECTORS
	mul dh
	add si,ax
	mov ax,IMG_DISK_HEADS
	mov bl,IMG_DISK_SECTORS
	mul bl
	mul ch
	add si,ax
	xor ch,ch
	add si,cx
	mov cx,9
	shl si,cl
	mov ax,[cs:IMG_DISK_LOAD_SEG]
	mov ds,ax
	pop dx
	mov cx,9
	shl dx,cl
	mov cx,dx
  cld
	rep movsb
	jmp short new_int13h_end
new_int13h_write:
	mov bp,sp
	mov ax,1
	or [bp+22],al
	mov ax,3
	mov [bp+15],al
	jmp short new_int13h_end
new_int13h_params:
	mov bp,sp
	mov ch,IMG_DISK_TRACKS
	mov cl,IMG_DISK_SECTORS
	mov al,IMG_DISK_HEADS
	mov [bp+11],al
	mov [bp+12],cx
	jmp short new_int13h_end
new_int13h_disktype:
	mov bp,sp
	mov al,1
	mov [bp+15],al
	mov ax,0
	mov [bp+12],ax
	mov ax,IMG_DISK_TRACKS*IMG_DISK_HEADS*IMG_DISK_SECTORS
	mov [bp+10],ax
new_int13h_end:
	cli
	pop ds
	pop di
	pop si
	pop bp
	pop bx
	pop dx
	pop cx
	pop ax
	popf
	sti
	iret

; maximum size of this bootloader is 1K
times 1024-($-$$) db 0