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 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: