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