|
|
1.1 ! 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. ! 32: ! 33: | 1.44Mb disks: ! 34: sectors = 18 ! 35: | 1.2Mb disks: ! 36: | sectors = 15 ! 37: | 720kB disks: ! 38: | sectors = 9 ! 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 ! 51: SYSSEG = 0x1000 | system loaded at 0x10000 (65536). ! 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 ! 70: mov sp,#0x400 | arbitrary value >>512 ! 71: ! 72: mov ah,#0x03 | read cursor pos ! 73: xor bh,bh ! 74: int 0x10 ! 75: ! 76: mov cx,#24 ! 77: mov bx,#0x0007 | page 0, attribute 7 (normal) ! 78: mov bp,#msg1 ! 79: mov ax,#0x1301 | write string, move cursor ! 80: int 0x10 ! 81: ! 82: | ok, we've written the message, now ! 83: | we want to load the system (at 0x10000) ! 84: ! 85: mov ax,#SYSSEG ! 86: mov es,ax | segment of 0x010000 ! 87: call read_it ! 88: call kill_motor ! 89: ! 90: | if the read went well we get current cursor position ans save it for ! 91: | posterity. ! 92: ! 93: mov ah,#0x03 | read cursor pos ! 94: xor bh,bh ! 95: int 0x10 | save it in known place, con_init fetches ! 96: mov [510],dx | it from 0x90510. ! 97: ! 98: | now we want to move to protected mode ... ! 99: ! 100: cli | no interrupts allowed ! ! 101: ! 102: | first we move the system to it's rightful place ! 103: ! 104: mov ax,#0x0000 ! 105: cld | 'direction'=0, movs moves forward ! 106: do_move: ! 107: mov es,ax | destination segment ! 108: add ax,#0x1000 ! 109: cmp ax,#0x9000 ! 110: jz end_move ! 111: mov ds,ax | source segment ! 112: sub di,di ! 113: sub si,si ! 114: mov cx,#0x8000 ! 115: rep ! 116: movsw ! 117: j do_move ! 118: ! 119: | then we load the segment descriptors ! 120: ! 121: end_move: ! 122: ! 123: mov ax,cs | right, forgot this at first. didn't work :-) ! 124: mov ds,ax ! 125: lidt idt_48 | load idt with 0,0 ! 126: lgdt gdt_48 | load gdt with whatever appropriate ! 127: ! 128: | that was painless, now we enable A20 ! 129: ! 130: call empty_8042 ! 131: mov al,#0xD1 | command write ! 132: out #0x64,al ! 133: call empty_8042 ! 134: mov al,#0xDF | A20 on ! 135: out #0x60,al ! 136: call empty_8042 ! 137: ! 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 ! 150: .word 0x00eb,0x00eb ! 151: mov al,#0x20 | start of hardware int's (0x20) ! 152: out #0x21,al ! 153: .word 0x00eb,0x00eb ! 154: mov al,#0x28 | start of hardware int's 2 (0x28) ! 155: out #0xA1,al ! 156: .word 0x00eb,0x00eb ! 157: mov al,#0x04 | 8259-1 is master ! 158: out #0x21,al ! 159: .word 0x00eb,0x00eb ! 160: mov al,#0x02 | 8259-2 is slave ! 161: out #0xA1,al ! 162: .word 0x00eb,0x00eb ! 163: mov al,#0x01 | 8086 mode for both ! 164: out #0x21,al ! 165: .word 0x00eb,0x00eb ! 166: out #0xA1,al ! 167: .word 0x00eb,0x00eb ! 168: mov al,#0xFF | mask off all interrupts for now ! 169: out #0x21,al ! 170: .word 0x00eb,0x00eb ! 171: out #0xA1,al ! 172: ! 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. ! 190: empty_8042: ! 191: .word 0x00eb,0x00eb ! 192: in al,#0x64 | 8042 status port ! 193: test al,#2 | is input buffer full? ! 194: jnz empty_8042 | yes - loop ! 195: ret ! 196: ! 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 ! 210: read_it: ! 211: mov ax,es ! 212: test ax,#0x0fff ! 213: die: jne die | es must be at 64kB boundary ! 214: xor bx,bx | bx is starting address within segment ! 215: rp_read: ! 216: mov ax,es ! 217: cmp ax,#ENDSEG | have we loaded all yet? ! 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: ! 299: .word 0,0,0,0 | dummy ! 300: ! 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 ! 310: ! 311: idt_48: ! 312: .word 0 | idt limit=0 ! 313: .word 0,0 | idt base=0L ! 314: ! 315: gdt_48: ! 316: .word 0x800 | gdt limit=2048, 256 GDT entries ! 317: .word gdt,0x9 | gdt base = 0X9xxxx ! 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.