|
|
1.1.1.2 ! root 1: ; ! 2: ; boot.s ! 3: ; ! 4: ; boot.s is loaded at 0x7c00 by the bios-startup routines, and moves itself ! 5: ; out of the way to address 0x90000, and jumps there. ! 6: ; ! 7: ; It then loads the system at 0x10000, using BIOS interrupts. Thereafter ! 8: ; it disables all interrupts, moves the system down to 0x0000, changes ! 9: ; to protected mode, and calls the start of system. System then must ! 10: ; RE-initialize the protected mode in it's own tables, and enable ! 11: ; interrupts as needed. ! 12: ; ! 13: ; NOTE! currently system is at most 8*65536 bytes long. This should be no ! 14: ; problem, even in the future. I want to keep it simple. This 512 kB ! 15: ; kernel size should be enough - in fact more would mean we'd have to move ! 16: ; not just these start-up routines, but also do something about the cache- ! 17: ; memory (block IO devices). The area left over in the lower 640 kB is meant ! 18: ; for these. No other memory is assumed to be "physical", ie all memory ! 19: ; over 1Mb is demand-paging. All addresses under 1Mb are guaranteed to match ! 20: ; their physical addresses. ! 21: ; ! 22: ; NOTE1 abouve is no longer valid in it's entirety. cache-memory is allocated ! 23: ; above the 1Mb mark as well as below. Otherwise it is mainly correct. ! 24: ; ! 25: ; NOTE 2! The boot disk type must be set at compile-time, by setting ! 26: ; the following equ. Having the boot-up procedure hunt for the right ! 27: ; disk type is severe brain-damage. ! 28: ; The loader has been made as simple as possible (had to, to get it ! 29: ; in 512 bytes with the code to move to protected mode), and continuos ! 30: ; read errors will result in a unbreakable loop. Reboot by hand. It ! 31: ; loads pretty fast by getting whole sectors at a time whenever possible. 1.1 root 32: 1.1.1.2 ! root 33: ; 1.44Mb disks: 1.1 root 34: sectors = 18 1.1.1.2 ! root 35: ; 1.2Mb disks: ! 36: ; sectors = 15 ! 37: ; 720kB disks: ! 38: ; sectors = 9 1.1 root 39: 40: .globl begtext, begdata, begbss, endtext, enddata, endbss 41: .text 42: begtext: 43: .data 44: begdata: 45: .bss 46: begbss: 47: .text 48: 49: BOOTSEG = 0x07c0 50: INITSEG = 0x9000 1.1.1.2 ! root 51: SYSSEG = 0x1000 ; system loaded at 0x10000 (65536). 1.1 root 52: ENDSEG = SYSSEG + SYSSIZE 53: 54: entry start 55: start: 56: mov ax,#BOOTSEG 57: mov ds,ax 58: mov ax,#INITSEG 59: mov es,ax 60: mov cx,#256 61: sub si,si 62: sub di,di 63: rep 64: movw 65: jmpi go,INITSEG 66: go: mov ax,cs 67: mov ds,ax 68: mov es,ax 69: mov ss,ax 1.1.1.2 ! root 70: mov sp,#0x400 ; arbitrary value >>512 1.1 root 71: 1.1.1.2 ! root 72: mov ah,#0x03 ; read cursor pos 1.1 root 73: xor bh,bh 74: int 0x10 75: 76: mov cx,#24 1.1.1.2 ! root 77: mov bx,#0x0007 ; page 0, attribute 7 (normal) 1.1 root 78: mov bp,#msg1 1.1.1.2 ! root 79: mov ax,#0x1301 ; write string, move cursor 1.1 root 80: int 0x10 81: 1.1.1.2 ! root 82: ; ok, we've written the message, now ! 83: ; we want to load the system (at 0x10000) 1.1 root 84: 85: mov ax,#SYSSEG 1.1.1.2 ! root 86: mov es,ax ; segment of 0x010000 1.1 root 87: call read_it 88: call kill_motor 89: 1.1.1.2 ! root 90: ; if the read went well we get current cursor position ans save it for ! 91: ; posterity. 1.1 root 92: 1.1.1.2 ! root 93: mov ah,#0x03 ; read cursor pos 1.1 root 94: xor bh,bh 1.1.1.2 ! root 95: int 0x10 ; save it in known place, con_init fetches ! 96: mov [510],dx ; it from 0x90510. 1.1 root 97: 1.1.1.2 ! root 98: ; now we want to move to protected mode ... 1.1 root 99: 1.1.1.2 ! root 100: cli ; no interrupts allowed ! 1.1 root 101: 1.1.1.2 ! root 102: ; first we move the system to it's rightful place 1.1 root 103: 104: mov ax,#0x0000 1.1.1.2 ! root 105: cld ; 'direction'=0, movs moves forward 1.1 root 106: do_move: 1.1.1.2 ! root 107: mov es,ax ; destination segment 1.1 root 108: add ax,#0x1000 109: cmp ax,#0x9000 110: jz end_move 1.1.1.2 ! root 111: mov ds,ax ; source segment 1.1 root 112: sub di,di 113: sub si,si 114: mov cx,#0x8000 115: rep 116: movsw 117: j do_move 118: 1.1.1.2 ! root 119: ; then we load the segment descriptors 1.1 root 120: 121: end_move: 122: 1.1.1.2 ! root 123: mov ax,cs ; right, forgot this at first. didn't work :-) 1.1 root 124: mov ds,ax 1.1.1.2 ! root 125: lidt idt_48 ; load idt with 0,0 ! 126: lgdt gdt_48 ; load gdt with whatever appropriate 1.1 root 127: 1.1.1.2 ! root 128: ; that was painless, now we enable A20 1.1 root 129: 130: call empty_8042 1.1.1.2 ! root 131: mov al,#0xD1 ; command write 1.1 root 132: out #0x64,al 133: call empty_8042 1.1.1.2 ! root 134: mov al,#0xDF ; A20 on 1.1 root 135: out #0x60,al 136: call empty_8042 137: 1.1.1.2 ! root 138: ; well, that went ok, I hope. Now we have to reprogram the interrupts :-( ! 139: ; we put them right after the intel-reserved hardware interrupts, at ! 140: ; int 0x20-0x2F. There they won't mess up anything. Sadly IBM really ! 141: ; messed this up with the original PC, and they haven't been able to ! 142: ; rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f, ! 143: ; which is used for the internal hardware interrupts as well. We just ! 144: ; have to reprogram the 8259's, and it isn't fun. ! 145: ! 146: mov al,#0x11 ; initialization sequence ! 147: out #0x20,al ; send it to 8259A-1 ! 148: .word 0x00eb,0x00eb ; jmp $+2, jmp $+2 ! 149: out #0xA0,al ; and to 8259A-2 1.1 root 150: .word 0x00eb,0x00eb 1.1.1.2 ! root 151: mov al,#0x20 ; start of hardware int's (0x20) 1.1 root 152: out #0x21,al 153: .word 0x00eb,0x00eb 1.1.1.2 ! root 154: mov al,#0x28 ; start of hardware int's 2 (0x28) 1.1 root 155: out #0xA1,al 156: .word 0x00eb,0x00eb 1.1.1.2 ! root 157: mov al,#0x04 ; 8259-1 is master 1.1 root 158: out #0x21,al 159: .word 0x00eb,0x00eb 1.1.1.2 ! root 160: mov al,#0x02 ; 8259-2 is slave 1.1 root 161: out #0xA1,al 162: .word 0x00eb,0x00eb 1.1.1.2 ! root 163: mov al,#0x01 ; 8086 mode for both 1.1 root 164: out #0x21,al 165: .word 0x00eb,0x00eb 166: out #0xA1,al 167: .word 0x00eb,0x00eb 1.1.1.2 ! root 168: mov al,#0xFF ; mask off all interrupts for now 1.1 root 169: out #0x21,al 170: .word 0x00eb,0x00eb 171: out #0xA1,al 172: 1.1.1.2 ! root 173: ; well, that certainly wasn't fun :-(. Hopefully it works, and we don't ! 174: ; need no steenking BIOS anyway (except for the initial loading :-). ! 175: ; The BIOS-routine wants lots of unnecessary data, and it's less ! 176: ; "interesting" anyway. This is how REAL programmers do it. ! 177: ; ! 178: ; Well, now's the time to actually move into protected mode. To make ! 179: ; things as simple as possible, we do no register set-up or anything, ! 180: ; we let the gnu-compiled 32-bit programs do that. We just jump to ! 181: ; absolute address 0x00000, in 32-bit protected mode. ! 182: ! 183: mov ax,#0x0001 ; protected mode (PE) bit ! 184: lmsw ax ; This is it! ! 185: jmpi 0,8 ; jmp offset 0 of segment 8 (cs) ! 186: ! 187: ; This routine checks that the keyboard command queue is empty ! 188: ; No timeout is used - if this hangs there is something wrong with ! 189: ; the machine, and we probably couldn't proceed anyway. 1.1 root 190: empty_8042: 191: .word 0x00eb,0x00eb 1.1.1.2 ! root 192: in al,#0x64 ; 8042 status port ! 193: test al,#2 ; is input buffer full? ! 194: jnz empty_8042 ; yes - loop 1.1 root 195: ret 196: 1.1.1.2 ! root 197: ; This routine loads the system at address 0x10000, making sure ! 198: ; no 64kB boundaries are crossed. We try to load it as fast as ! 199: ; possible, loading whole tracks whenever we can. ! 200: ; ! 201: ; in: es - starting address segment (normally 0x1000) ! 202: ; ! 203: ; This routine has to be recompiled to fit another drive type, ! 204: ; just change the "sectors" variable at the start of the file ! 205: ; (originally 18, for a 1.44Mb drive) ! 206: ; ! 207: sread: .word 1 ; sectors read of current track ! 208: head: .word 0 ; current head ! 209: track: .word 0 ; current track 1.1 root 210: read_it: 211: mov ax,es 212: test ax,#0x0fff 1.1.1.2 ! root 213: die: jne die ; es must be at 64kB boundary ! 214: xor bx,bx ; bx is starting address within segment 1.1 root 215: rp_read: 216: mov ax,es 1.1.1.2 ! root 217: cmp ax,#ENDSEG ; have we loaded all yet? 1.1 root 218: jb ok1_read 219: ret 220: ok1_read: 221: mov ax,#sectors 222: sub ax,sread 223: mov cx,ax 224: shl cx,#9 225: add cx,bx 226: jnc ok2_read 227: je ok2_read 228: xor ax,ax 229: sub ax,bx 230: shr ax,#9 231: ok2_read: 232: call read_track 233: mov cx,ax 234: add ax,sread 235: cmp ax,#sectors 236: jne ok3_read 237: mov ax,#1 238: sub ax,head 239: jne ok4_read 240: inc track 241: ok4_read: 242: mov head,ax 243: xor ax,ax 244: ok3_read: 245: mov sread,ax 246: shl cx,#9 247: add bx,cx 248: jnc rp_read 249: mov ax,es 250: add ax,#0x1000 251: mov es,ax 252: xor bx,bx 253: jmp rp_read 254: 255: read_track: 256: push ax 257: push bx 258: push cx 259: push dx 260: mov dx,track 261: mov cx,sread 262: inc cx 263: mov ch,dl 264: mov dx,head 265: mov dh,dl 266: mov dl,#0 267: and dx,#0x0100 268: mov ah,#2 269: int 0x13 270: jc bad_rt 271: pop dx 272: pop cx 273: pop bx 274: pop ax 275: ret 276: bad_rt: mov ax,#0 277: mov dx,#0 278: int 0x13 279: pop dx 280: pop cx 281: pop bx 282: pop ax 283: jmp read_track 284: 285: /* 286: * This procedure turns off the floppy drive motor, so 287: * that we enter the kernel in a known state, and 288: * don't have to worry about it later. 289: */ 290: kill_motor: 291: push dx 292: mov dx,#0x3f2 293: mov al,#0 294: outb 295: pop dx 296: ret 297: 298: gdt: 1.1.1.2 ! root 299: .word 0,0,0,0 ; dummy 1.1 root 300: 1.1.1.2 ! root 301: .word 0x07FF ; 8Mb - limit=2047 (2048*4096=8Mb) ! 302: .word 0x0000 ; base address=0 ! 303: .word 0x9A00 ; code read/exec ! 304: .word 0x00C0 ; granularity=4096, 386 ! 305: ! 306: .word 0x07FF ; 8Mb - limit=2047 (2048*4096=8Mb) ! 307: .word 0x0000 ; base address=0 ! 308: .word 0x9200 ; data read/write ! 309: .word 0x00C0 ; granularity=4096, 386 1.1 root 310: 311: idt_48: 1.1.1.2 ! root 312: .word 0 ; idt limit=0 ! 313: .word 0,0 ; idt base=0L 1.1 root 314: 315: gdt_48: 1.1.1.2 ! root 316: .word 0x800 ; gdt limit=2048, 256 GDT entries ! 317: .word gdt,0x9 ; gdt base = 0X9xxxx 1.1 root 318: 319: msg1: 320: .byte 13,10 321: .ascii "Loading system ..." 322: .byte 13,10,13,10 323: 324: .text 325: endtext: 326: .data 327: enddata: 328: .bss 329: endbss:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.