bootsectorpos equ 0x7c00
%include "fat.inc"
%define BOCHS
[BITS 16]
ORG 0
jmp start
oldirq6ip dw 0
oldirq6cs dw 0
irqcalled db 0
lpmotoron db "Turning motor on",13,10,0
lpcalibrating db "Calibrating drive",13,10,0
lpseeking db "Seeking head",13,10,0
lpsector db "Checking sector ID",13,10,0
lpdma db "Opening dma channel",13,10,0
lpreading db "Reading sector",13,10,0
lpphasetwo db "Receiving sector info",13,10,0
lpmotoroff db "Turning motor off",13,10,0
lpdone db "Done",13,10,0
lpnoint db "Interrupt not called!",13,10,0
lpkeypressed db "Key pressed",13,10,0
lptimer db "Timer interrupt",13,10,0
sector db 16
track db 0
head db 1
keypressed db 0
sto db 0
stl db 0
stz db 0
readtrack db 0
readhead db 0
readsector db 0
readsecsize db 0
oldmpic db 0
oldspic db 0
rootdirpos dd 0
rootdirsize dd 0
rootdirloadpos dd 0
sectorspace times 514 db 0
; check for protected or V86 mode. Code from Freedows '98 ldr_asm.asm
; Copyright (C) 1997 Joachim Breitsprecher <j.breitsprecher@schwaben.de>
start: mov ax,cs
mov ds,ax
mov es,ax
smsw ax
test al,1 ; look at PE bit of MSW (CR0)
je start2
; exit to DOS with error code 1
db 0xea
dw 0x0000
dw 0xffff
; set base of code/data descriptors to CS<<4/DS<<4 (CS=DS)
start2: xor ebx,ebx
mov bx,cs ; BX=segment
shl ebx,4 ; EBX=linear address of segment base
mov eax,ebx
mov [gdt1 + 2],ax
mov [gdt2 + 2],ax
mov [gdt3 + 2],ax
shr eax,16
mov [gdt1 + 4],al
mov [gdt2 + 4],al
mov [gdt3 + 4],al
mov [gdt1 + 7],ah
mov [gdt2 + 7],ah
mov [gdt3 + 7],ah
; point gdtr to the gdt, point idtr to the idt
add ebx,gdt ; EBX=linear address of gdt
mov [gdtr + 2],ebx
add ebx,idt - gdt ; EBX=linear address of idt
mov [idtr + 2],ebx
; disable interrupts
;cli
; load GDT and IDT for full protected mode
lgdt [gdtr]
lidt [idtr]
; save real-mode CS in BP
mov bp,cs
;set up irq masks
; in al,0x20
; mov [oldmpic],al
; in al,0xa0
; mov [oldspic],al
; mov al,0xfc
; out 0x20,al
; mov al,0xff
; out 0xa0,al
; set PE [protected mode enable] bit and go
mov eax,cr0
or al,1
mov cr0,eax
jmp SYS_CODE_SEL:do_pm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 32-bit protected mode
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[BITS 32]
do_pm: mov ax,SYS_DATA_SEL
mov ds,ax ; not segments anymore: SELECTORS
mov ss,ax
nop
mov esp,0xf000
mov es,ax
mov fs,ax
mov gs,ax
; say hello!
lea esi,[hi_msg]
call wrstr
; try an interrupt
int 0x20
; copy info from first sector
mov ax,LINEAR_SEL
mov ds,ax
mov es,ax
mov esi,0x7c00
mov edi,eof
mov ecx,BootSectorValues.end
cld
rep movsb
; reinstate segment values
mov ax,SYS_DATA_SEL
mov ds,ax
mov es,ax
; use this info to find the root dir
xor eax,eax
mov al,[eof+BootSectorValues.FatCopies]
mul word [eof+BootSectorValues.SectorsPerFAT]
inc ax
inc ax
mov [rootdirpos],eax
; calculate rootdir size
xor eax,eax
mov ax,[eof+BootSectorValues.RootDirEntries]
shr ax,4 ;16 entries per sector so div by 16=2^4
mov [rootdirsize],eax
; load rootdir straight after values
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;TODO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Get the routine from bootloader that calcs
; track and head and stuff, make into routine
;call floppystuff
;mov byte [sector],2
;xor cx,cx
;mov [CsrX],cx
;inc byte [CsrY]
;call floppystuff
; switch to 16-bit protected mode on your way to real mode
jmp REAL_CODE_SEL:do_16
;here we set keyboard interrupts to work
IBF_loop:
in al,0x64 ;check keyboard buffer isn't full
and al,2 ;test IBF flag
jz IBF_loop ;if not then loop
ret
;here we set keyboard interrupts to work
OBF_loop:
in al,0x64 ;check keyboard buffer isn't full
and al,1 ;test OBF flag
jnz OBF_loop ;if not then loop
ret
waitforkeyboardirq:
mov al,[keypressed]
or al,al
jz waitforkeyboardirq
ret
printhex8:
push ecx
push eax
push edx
mov ecx,2
mov dl,al
nexthex8:
rol dl,4
mov al,0x0f
and al,dl
add al,0x90
daa
adc al,0x40
daa
call wrch
loop nexthex8
pop edx
pop eax
pop ecx
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; character-output video routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
wrch: push gs
push ecx
push ebx
push eax
mov ax,LINEAR_SEL
mov gs,ax
; (Y * 80 + X) * 2 --> EAX
movzx eax,byte [CsrY]
mov cl,80
mul cl
add al,[CsrX]
adc ah,0
shl eax,1
; EAX + 0xB8000 --> EBX; store char
lea ebx,[eax + 0xB8000]
pop eax
push eax
mov [gs:ebx],al
; advance cursor
mov cx,[CsrX]
inc cl
cmp cl,80 ; cursor off right side of screen?
jb wrch2
xor cl,cl ; yes, wrap to left side...
inc ch ; ...and down one line
cmp ch,25 ; cursor off bottom of screen?
jb wrch2
xor ch,ch ; yes, wrap to top left corner (no scroll)
wrch2: mov [CsrX],cx
pop eax
pop ebx
pop ecx
pop gs
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; string-output video routine
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
wrstr: push esi
push eax
push ecx
cld
jmp wrstr2
wrstr1: call wrch
wrstr2: lodsb
cmp al,13
je setcr
cmp al,10
je setlf
or al,al
jne wrstr1
pop ecx
pop eax
pop esi
ret
setcr:
xor cx,cx
mov [CsrX],cl
jmp wrstr2
setlf:
inc byte [CsrY]
jmp wrstr2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; default handler for interrupts/exceptions
; prints " Unhandled interrupt!"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
unhand: cli
lea esi,[unhand_msg]
call wrstr
jmp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; handler for INT 0x20
; prints " Hey, INT 0x20 occured!"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
isr20: push esi
lea esi,[isr20_msg]
call wrstr
pop esi
iret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 16-bit protected mode
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[BITS 16]
; switch to 16-bit stack and data
do_16: mov ax,REAL_DATA_SEL
mov ds,ax
mov ss,ax
nop
; push real-mode CS:IP
lea bx,[do_rm]
push bp
push bx
; clear PE [protected mode enable] bit and return to real mode
mov eax,cr0
and al,0xFE
mov cr0,eax
retf ; jumps to do_rm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 16-bit real mode again
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; restore real-mode segment register values
do_rm: mov ax,cs
mov ds,ax
mov ss,ax
nop
mov es,ax
mov fs,ax
mov gs,ax
; point to real-mode IDTR
lidt [ridtr]
; re-enable interrupts
sti
; exit to DOS with errorlevel 0
xor ax,ax
int 0x16
mov ax,0x0e50
xor bx,bx
int 0x10
xor ax,ax
int 0x16
db 0xea
dw 0x0000
dw 0xffff
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
CsrX: db 0
CsrY: db 0
hi_msg: db "Hello, how's it going?", 0
unhand_msg:
db " Unhandled interrupt!", 0
isr20_msg:
db " Hey, INT 0x20 occured!", 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 16-bit limit/32-bit linear base address of GDT and IDT
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
gdtr: dw gdt_end - gdt - 1 ; GDT limit
dd gdt ; linear, physical address of GDT
idtr: dw idt_end - idt - 1 ; IDT limit
dd idt ; linear, physical address of IDT
; an IDTR 'appropriate' for real mode
ridtr: dw 0xFFFF ; limit=0xFFFF
dd 0 ; base=0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; global descriptor table (GDT)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; null descriptor
gdt: dw 0 ; limit 15:0
dw 0 ; base 15:0
db 0 ; base 23:16
db 0 ; type
db 0 ; limit 19:16, flags
db 0 ; base 31:24
; linear data segment descriptor
LINEAR_SEL equ $-gdt
dw 0xFFFF ; limit 0xFFFFF
dw 0 ; base 0
db 0
db 0x92 ; present, ring 0, data, expand-up, writable
db 0xCF ; page-granular, 32-bit
db 0
; code segment descriptor
SYS_CODE_SEL equ $-gdt
gdt1: dw 0xFFFF
dw 0 ; (base gets set above)
db 0
db 0x9A ; present, ring 0, code, non-conforming, readable
db 0xCF
db 0
; data segment descriptor
SYS_DATA_SEL equ $-gdt
gdt2: dw 0xFFFF
dw 0 ; (base gets set above)
db 0
db 0x92 ; present, ring 0, data, expand-up, writable
db 0xCF
db 0
; code segment descriptor that is 'appropriate' for real mode
; (16-bit, limit=0xFFFF)
REAL_CODE_SEL equ $-gdt
gdt3: dw 0xFFFF
dw 0 ; (base gets set above)
db 0
db 0x9A ; present, ring 0, code, non-conforming, readable
db 0 ; byte-granular, 16-bit
db 0
; data segment descriptor that is 'appropriate' for real mode
; (16-bit, limit=0xFFFF)
REAL_DATA_SEL equ $-gdt
gdt4: dw 0xFFFF
dw 0 ; (base gets set above)
db 0
db 0x92 ; present, ring 0, data, expand-up, writable
db 0 ; byte-granular, 16-bit
db 0
gdt_end:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; interrupt descriptor table (IDT)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 32 reserved interrupts:
idt: dw unhand ; entry point 15:0
dw SYS_CODE_SEL ; selector
db 0 ; word count
db 0x8E ; type (32-bit Ring 0 interrupt gate)
dw 0 ; entry point 31:16 (XXX - unhand >> 16)
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw systimer
dw SYS_CODE_SEL ;int 08h - system timer
db 0
db 0x8E
dw 0
dw keyboard_handler
dw SYS_CODE_SEL ;int 09h - irq 1 - keyboard
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw myirq
dw SYS_CODE_SEL
db 0 ;int 0eh
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
dw unhand
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
; user interrupt handler
dw isr20
dw SYS_CODE_SEL
db 0
db 0x8E
dw 0
idt_end:
[bits 32]
;keyboard_handler - handles the keyboard
keyboard_handler:
cli
push eax
in al,0x60
mov byte[keypressed],al
call printhex8
pop eax
push esi
mov esi,lpkeypressed
call wrstr
pop esi
sti
iret
;systimer - handles the system clock interrupt
systimer:
cli
push esi
mov esi,lptimer
call wrstr
pop esi
sti
nop
iret
cli ;disable interrupts
xor ax,ax ;get original int 0Eh
mov es,ax
mov dx,[es:0x0e*4]
mov [oldirq6ip],dx
mov dx,[es:0x0e*4+2]
mov [oldirq6cs],dx ;save pointer
lea dx,[myirq]
mov [es:0x0e*4],dx ;and hook our irq handler in there
mov dx,cs
mov [es:0x0e*4+2],dx
mov ax,cs
mov ds,ax ;set up segments
mov es,ax
sti ;and re-enable interrupts
[BITS 32]
floppystuff:
call motor_on
call recal
call seek
call dma_open
call disk_read
call motor_off
mov esi,lpdone
call printchar
;here we print what we found:
mov esi,sectorspace
call printchar
;should be fun :P
ret
xor ax,ax
int 0x16
db 0xea
dw 0x0000
dw 0xffff
;mov ah,0x4c
;int 0x21
;-------------------------------------------------
;our irq handler
myirq:
mov al,0x20
out 0x20,al
mov byte[irqcalled],1
iret
;-------------------------------------------------
;waits for an interrupt
irq_wait:
mov ecx,0xfffff
irq_loop:
cmp byte[irqcalled],1
je irq_done
loop irq_loop ;loop when using bochs, jmp when using real comp
mov esi,lpnoint
call printchar
irq_done:
ret
;-------------------------------------------------
;floppy drive motor on
motor_on:
mov al,0x1c
mov dx,0x3F2
OUT dx, al ;drive A selected, motor on, controller on, use DMA and IRQ
mov esi,lpmotoron
call printchar
ret
;-------------------------------------------------
;floppy drive motor off
motor_off:
xor al,al
mov dx,0x3F2
OUT dx, al ;no drive selected, motor off, controller off, no DMA no IRQ
mov esi,lpmotoroff
call printchar
ret
;-------------------------------------------------
;recalibrate head to 0
recal:
mov esi,lpcalibrating ;print message
call printchar
mov byte [irqcalled],0 ;reset irq-called flag
mov al,7
call send_byte ;send recalibrate command
xor al,al
call send_byte
call irq_wait ;wait for irq 6
mov al,8
call send_byte ;check if actually recalibrated
call get_byte
push ax
call get_byte
pop ax
and al,0x10
or al,al
jne recal
ret
;-------------------------------------------------
;seeks to the sector we want
seek:
mov esi,lpseeking
call printchar
mov al,0x0f ;seek head/track command
call send_byte
mov al,[head]
shl al,2 ;head #=bit 2, bits 1&0=0 (drive 0)
call send_byte
mov al,[track] ;send track number
call send_byte
call irq_wait ;wait for interrupt
mov al,0x08
call send_byte ;check if we're on the right track lol track
call get_byte
call get_byte
cmp al,[track]
jne seek ;if not, then re-seek
ret
;-------------------------------------------------
;receives a byte from the controller
get_byte:
call controller_wait
mov dx,0x3f5
in al,dx
ret
;-------------------------------------------------
;sends a byte to the controller
send_byte:
push ax
call controller_wait
mov dx,0x3f5
pop ax
out dx,al
ret
;-------------------------------------------------
;waits for the controller to be ready
controller_wait:
mov dx,0x3f4
in al,dx
and al,0x80
cmp al,0x80
jne controller_wait
ret
;-------------------------------------------------
;checks that we're all good to go
sector_id:
ret
;-------------------------------------------------
;read the sector
disk_read:
mov esi,lpreading
call printchar
mov al,0x46 ;read sector command
call send_byte
mov al,[head]
shl al,2
call send_byte ;bit 2=head, bits 1&0=drive (drive 0)
mov al,[track]
call send_byte ;send track #
mov al,[head]
call send_byte ;send head again
mov al,[sector]
call send_byte ;send sector number
mov al,2
call send_byte ;send sector size, 128*2^x (128*2^2=512)
mov al,[sector]
call send_byte ;last sector to read
mov al,27
call send_byte ;magic number for 3 1/2 floppy
mov al,0xff
call send_byte ;given sector size, so this is -1
;now receive results
mov byte [irqcalled],0
call irq_wait
mov esi,lpphasetwo
call printchar
%ifdef BOCHS
ret
%endif
call get_byte
mov [sto],al ;status registers 0-2
call get_byte
mov [stl],al
call get_byte
mov [stz],al
call get_byte ;track
mov [readtrack],al
call get_byte ;head
mov [readhead],al
call get_byte ;sector
mov [readsector],al
call get_byte ;secsize
mov [readsecsize],al
ret
;-------------------------------------------------
;open the dma channel
dma_open
mov esi,lpdma
call printchar
cli ;disable interrupts
mov al,4 ;mask channel
add al,2 ;channel 2
out 0x0a,al
xor al,al ;clear flip-flop
out 0x0c,al
mov al,0x56 ;dma transfer - single mode, cld, write to mem, channel 2
out 0x0b,al
xor eax,eax
mov ax,bp
shl eax,4 ;set up page and offset for writing data
add eax,sectorspace
mov dx,4
out dx,al ;set offset for memory write
mov al,ah
out dx,al
inc dx
mov ax,0x200
out dx,al ;set memory length
mov al,ah
out dx,al
shr eax,16
mov dx,0x81
out dx,al ;set page
mov al,2
out 0x0a,al ;unmask channel
sti ;enable interrupts
ret
pointer dd 0
;-------------------------------------------------
;printchar - DS:SI=ptr to null-terminated string
printchar:
jmp wrstr
ret
eof:
ALSO FAT.inc
struc BootSectorValues
.Jump resb 3
.OEMName resb 8
.BytesPerSector resw 1
.SectorsPerCluster resb 1
.ReservedSectors resw 1
.FatCopies resb 1
.RootDirEntries resw 1
.TotalSectors resw 1
.MediaDescriptor resb 1
.SectorsPerFAT resw 1
.SectorsPerTrack resw 1
.NumberOfHeads resw 1
.HiddenSectors resw 1
.end
endstruc