/[gxemul]/upstream/0.4.3/src/promemul/pc_bios.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /upstream/0.4.3/src/promemul/pc_bios.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 33 - (show annotations)
Mon Oct 8 16:21:06 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 54080 byte(s)
0.4.3
1 /*
2 * Copyright (C) 2005-2006 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: pc_bios.c,v 1.7 2006/07/26 23:21:48 debug Exp $
29 *
30 * Generic PC BIOS emulation.
31 *
32 * See http://hdebruijn.soo.dto.tudelft.nl/newpage/interupt/INT.HTM for
33 * details on what different BIOS interrupts do.
34 *
35 *
36 * The BIOS address space is used as follows:
37 *
38 * 0xf1000 GDT + PD + PTs used for booting in pmode
39 * 0xf8yy0 real mode interrupt handler (int 0xyy)
40 * 0xf9000 SMP tables
41 * 0xfefc7 disk table
42 * 0xff66e 8x8 font (chars 128..255)
43 * 0xffa6e 8x8 font (chars 0..127)
44 * 0xfffd0 System Configuration Parameters (8 bytes)
45 * 0xffff0 Reboot "code".
46 *
47 * TODO: Keep the "BIOS data area" in synch. (Such as keyboard shift state,
48 * disk access, video mode, error codes, x and y charcell resolution...)
49 */
50
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <time.h>
55
56 #include "cpu.h"
57 #include "misc.h"
58
59 #ifndef ENABLE_X86
60
61 /* Don't include PC bios support if we don't have x86 cpu support. */
62 /* These are just do-nothing functions. */
63
64 void pc_bios_simple_pmode_setup(struct cpu *cpu) { }
65 void pc_bios_init(struct cpu *cpu) { }
66 int pc_bios_emul(struct cpu *cpu) { return 0; }
67
68
69 #else
70
71
72 #include "console.h"
73 #include "cpu_x86.h"
74 #include "devices.h"
75 #include "diskimage.h"
76 #include "machine.h"
77 #include "memory.h"
78
79
80 extern int quiet_mode;
81
82 extern unsigned char font8x8[];
83
84 #define dec_to_bcd(x) ( (((x) / 10) << 4) + ((x) % 10) )
85
86
87 /*
88 * add_disk():
89 */
90 static struct pc_bios_disk *add_disk(struct machine *machine, int biosnr,
91 int id, int type)
92 {
93 struct pc_bios_disk *p = malloc(sizeof(struct pc_bios_disk));
94
95 if (p == NULL) {
96 fprintf(stderr, "add_disk(): out of memory\n");
97 exit(1);
98 }
99
100 p->next = machine->md.pc.first_disk;
101 machine->md.pc.first_disk = p;
102
103 p->nr = biosnr; p->id = id; p->type = type;
104
105 p->size = diskimage_getsize(machine, id, type);
106 diskimage_getchs(machine, id, type, &p->cylinders, &p->heads,
107 &p->sectorspertrack);
108
109 return p;
110 }
111
112
113 static struct pc_bios_disk *get_disk(struct machine *machine, int biosnr)
114 {
115 struct pc_bios_disk *p = machine->md.pc.first_disk;
116 while (p != NULL) {
117 if (p->nr == biosnr)
118 break;
119 p = p->next;
120 }
121 return p;
122 }
123
124
125 /*
126 * output_char():
127 */
128 static void output_char(struct cpu *cpu, int x, int y, int ch, int color)
129 {
130 uint64_t addr = (y * cpu->machine->md.pc.columns + x) * 2 + 0xb8000;
131 unsigned char w[2];
132 int len = 2;
133
134 w[0] = ch; w[1] = color;
135 if (color < 0)
136 len = 1;
137
138 cpu->memory_rw(cpu, cpu->mem, addr, &w[0], len, MEM_WRITE,
139 CACHE_NONE | PHYSICAL);
140 }
141
142
143 /*
144 * cmos_write():
145 */
146 static void cmos_write(struct cpu *cpu, int addr, int value)
147 {
148 unsigned char c;
149 c = addr;
150 cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE + 0x70, &c, 1, MEM_WRITE,
151 CACHE_NONE | PHYSICAL);
152 c = value;
153 cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE + 0x71, &c, 1, MEM_WRITE,
154 CACHE_NONE | PHYSICAL);
155 }
156
157
158 /*
159 * set_cursor_pos():
160 */
161 static void set_cursor_pos(struct cpu *cpu, int x, int y)
162 {
163 int addr = y * cpu->machine->md.pc.columns + x;
164 unsigned char byte;
165 uint64_t ctrlregs = X86_IO_BASE + 0x3c0;
166
167 byte = 0x0e;
168 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x14,
169 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
170 byte = (addr >> 8) & 255;
171 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x15,
172 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
173 byte = 0x0f;
174 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x14,
175 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
176 byte = addr & 255;
177 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x15,
178 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
179 }
180
181
182 /*
183 * set_cursor_scanlines():
184 */
185 static void set_cursor_scanlines(struct cpu *cpu, int start, int end)
186 {
187 unsigned char byte;
188 uint64_t ctrlregs = X86_IO_BASE + 0x3c0;
189
190 byte = 0x0a;
191 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x14,
192 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
193 byte = start;
194 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x15,
195 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
196 byte = 0x0b;
197 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x14,
198 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
199 byte = end;
200 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x15,
201 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
202 }
203
204
205 /*
206 * get_cursor_pos():
207 */
208 static void get_cursor_pos(struct cpu *cpu, int *x, int *y)
209 {
210 int addr;
211 unsigned char byte;
212 uint64_t ctrlregs = X86_IO_BASE + 0x3c0;
213
214 byte = 0x0e;
215 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x14,
216 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
217 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x15,
218 &byte, sizeof(byte), MEM_READ, CACHE_NONE | PHYSICAL);
219 addr = byte;
220
221 byte = 0x0f;
222 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x14,
223 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
224 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x15,
225 &byte, sizeof(byte), MEM_READ, CACHE_NONE | PHYSICAL);
226 addr = addr*256 + byte;
227
228 *x = addr % cpu->machine->md.pc.columns;
229 *y = addr / cpu->machine->md.pc.columns;
230 }
231
232
233 /*
234 * set_palette():
235 */
236 static void set_palette(struct cpu *cpu, int n, int r, int g, int b)
237 {
238 unsigned char byte = n;
239 cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE + 0x3c8,
240 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
241 byte = r;
242 cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE + 0x3c9,
243 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
244 byte = g;
245 cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE + 0x3c9,
246 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
247 byte = b;
248 cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE + 0x3c9,
249 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
250 }
251
252
253 /*
254 * get_palette():
255 */
256 static void get_palette(struct cpu *cpu, int n, unsigned char *rgb)
257 {
258 unsigned char byte = n;
259 cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE + 0x3c8,
260 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
261 cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE + 0x3c9,
262 &rgb[0], 1, MEM_READ, CACHE_NONE | PHYSICAL);
263 cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE + 0x3c9,
264 &rgb[1], 1, MEM_READ, CACHE_NONE | PHYSICAL);
265 cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE + 0x3c9,
266 &rgb[2], 1, MEM_READ, CACHE_NONE | PHYSICAL);
267 }
268
269
270 /*
271 * scroll_up():
272 */
273 static void scroll_up(struct cpu *cpu, int x1, int y1, int x2, int y2, int attr)
274 {
275 int x, y;
276
277 if (x1 < 0) x1 = 0;
278 if (y1 < 0) y1 = 0;
279 if (x2 >= cpu->machine->md.pc.columns)
280 x2 = cpu->machine->md.pc.columns - 1;
281 if (y2 >= cpu->machine->md.pc.rows)
282 y2 = cpu->machine->md.pc.rows - 1;
283
284 /* Scroll up by copying lines: */
285 for (y=y1; y<=y2-1; y++) {
286 int addr = (cpu->machine->md.pc.columns*y + x1) * 2 + 0xb8000;
287 int len = (x2-x1+1) * 2;
288 unsigned char w[160];
289 addr += (cpu->machine->md.pc.columns * 2);
290 cpu->memory_rw(cpu, cpu->mem, addr, &w[0], len,
291 MEM_READ, CACHE_NONE | PHYSICAL);
292 addr -= (cpu->machine->md.pc.columns * 2);
293 cpu->memory_rw(cpu, cpu->mem, addr, &w[0], len,
294 MEM_WRITE, CACHE_NONE | PHYSICAL);
295 }
296
297 /* Clear lowest line: */
298 for (x=x1; x<=x2; x++)
299 output_char(cpu, x, y2, ' ', attr);
300 }
301
302
303 /*
304 * scroll_down():
305 */
306 static void scroll_down(struct cpu *cpu, int x1, int y1, int x2, int y2,
307 int attr)
308 {
309 int x, y;
310
311 if (x1 < 0) x1 = 0;
312 if (y1 < 0) y1 = 0;
313 if (x2 >= cpu->machine->md.pc.columns)
314 x2 = cpu->machine->md.pc.columns - 1;
315 if (y2 >= cpu->machine->md.pc.rows)
316 y2 = cpu->machine->md.pc.rows - 1;
317
318 /* Scroll down by copying lines: */
319 for (y=y2; y>=y1+1; y--) {
320 int addr = (cpu->machine->md.pc.columns*y + x1) * 2 + 0xb8000;
321 int len = (x2-x1+1) * 2;
322 unsigned char w[160];
323 addr -= cpu->machine->md.pc.columns * 2;
324 cpu->memory_rw(cpu, cpu->mem, addr, &w[0], len,
325 MEM_READ, CACHE_NONE | PHYSICAL);
326 addr += cpu->machine->md.pc.columns * 2;
327 cpu->memory_rw(cpu, cpu->mem, addr, &w[0], len,
328 MEM_WRITE, CACHE_NONE | PHYSICAL);
329 }
330
331 /* Clear the uppermost line: */
332 for (x=x1; x<=x2; x++)
333 output_char(cpu, x, y1, ' ', attr);
334 }
335
336
337 /*
338 * pc_bios_putchar():
339 */
340 static void pc_bios_putchar(struct cpu *cpu, char ch, int attr,
341 int linewrap_and_scroll)
342 {
343 int x, y;
344
345 get_cursor_pos(cpu, &x, &y);
346
347 if (!linewrap_and_scroll) {
348 if (x < cpu->machine->md.pc.columns &&
349 y < cpu->machine->md.pc.rows) {
350 output_char(cpu, x, y, ch, attr);
351 x++;
352 set_cursor_pos(cpu, x, y);
353 }
354 return;
355 }
356
357 /* Put the character on the screen, move cursor, and so on: */
358 switch (ch) {
359 case '\r': x = -1; break;
360 case '\n': x = cpu->machine->md.pc.columns; break;
361 case '\b': x -= 2; break;
362 default: output_char(cpu, x, y, ch, attr);
363 }
364 x++;
365 if (x < 0)
366 x = 0;
367 if (x >= cpu->machine->md.pc.columns) {
368 x=0; y++;
369 }
370
371 if (attr < 0)
372 attr = cpu->machine->md.pc.curcolor;
373
374 if (y >= cpu->machine->md.pc.rows) {
375 scroll_up(cpu, 0,0, cpu->machine->md.pc.columns-1,
376 cpu->machine->md.pc.rows-1, attr);
377 x = 0; y = cpu->machine->md.pc.rows - 1;
378 }
379 set_cursor_pos(cpu, x, y);
380 }
381
382
383 /*
384 * pc_bios_printstr():
385 */
386 static void pc_bios_printstr(struct cpu *cpu, char *s, int attr)
387 {
388 while (*s)
389 pc_bios_putchar(cpu, *s++, attr, 1);
390 }
391
392
393 /*
394 * set_video_mode():
395 */
396 static void set_video_mode(struct cpu *cpu, int al)
397 {
398 uint64_t ctrlregs = X86_IO_BASE + 0x3c0;
399 int x, y, text;
400 unsigned char byte = 0xff;
401
402 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x14, &byte, sizeof(byte),
403 MEM_WRITE, CACHE_NONE | PHYSICAL);
404 byte = al;
405 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x15, &byte, sizeof(byte),
406 MEM_WRITE, CACHE_NONE | PHYSICAL);
407
408 text = 0;
409
410 switch (al) {
411 case 0x00: /* 40x25 non-color textmode */
412 case 0x01: /* 40x25 color textmode */
413 cpu->machine->md.pc.columns = 40;
414 cpu->machine->md.pc.rows = 25;
415 text = 1;
416 break;
417 case 0x02: /* 80x25 non-color textmode */
418 case 0x03: /* 80x25 color textmode */
419 cpu->machine->md.pc.columns = 80;
420 cpu->machine->md.pc.rows = 25;
421 text = 1;
422 break;
423 case 0x19: /* ? */
424 break;
425 case 0x0d: /* 320x200 x 16 colors graphics */
426 set_cursor_scanlines(cpu, 0x40, 0);
427 break;
428 case 0x12: /* 640x480 x 16 colors graphics */
429 set_cursor_scanlines(cpu, 0x40, 0);
430 break;
431 case 0x13: /* 320x200 x 256 colors graphics */
432 set_cursor_scanlines(cpu, 0x40, 0);
433 break;
434 default:
435 fatal("[ set_video_mode(): unimplemented video mode "
436 "0x%02x ]\n", al);
437 cpu->running = 0;
438 }
439
440 cpu->machine->md.pc.curcolor = 0x07;
441 cpu->machine->md.pc.videomode = al;
442
443 if (text) {
444 /* Simply clear the screen and home the cursor
445 for now. TODO: More advanced stuff. */
446 set_cursor_pos(cpu, 0, 0);
447 for (y=0; y<cpu->machine->md.pc.rows; y++)
448 for (x=0; x<cpu->machine->md.pc.columns; x++)
449 output_char(cpu, x,y, ' ',
450 cpu->machine->md.pc.curcolor);
451 }
452 }
453
454
455 /*
456 * pc_bios_int8():
457 *
458 * Interrupt handler for the timer.
459 */
460 static int pc_bios_int8(struct cpu *cpu)
461 {
462 unsigned char ticks[4];
463 unsigned char tmpbyte;
464
465 /* TODO: ack the timer interrupt some other way? */
466 cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE + 0x43,
467 &tmpbyte, 1, MEM_READ, CACHE_NONE | PHYSICAL);
468
469 /* EOI the interrupt. */
470 cpu->machine->isa_pic_data.pic1->isr &= ~0x01;
471
472 /* "Call" INT 0x1C: */
473 /* TODO: how about non-real-mode? */
474 cpu->memory_rw(cpu, cpu->mem, 0x1C * 4,
475 ticks, 4, MEM_READ, CACHE_NONE | PHYSICAL);
476 cpu->pc = ticks[0] + (ticks[1] << 8);
477 reload_segment_descriptor(cpu, X86_S_CS, ticks[2] + (ticks[3] << 8),
478 NULL);
479 return 0;
480 }
481
482
483 /*
484 * pc_bios_int9():
485 *
486 * Interrupt handler for the keyboard.
487 */
488 static void pc_bios_int9(struct cpu *cpu)
489 {
490 uint8_t byte;
491
492 /* Read a key from the keyboard: */
493 cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE + 0x60,
494 &byte, sizeof(byte), MEM_READ, CACHE_NONE | PHYSICAL);
495
496 /* (The read should have acknowdledged the interrupt.) */
497
498 /* Add the key to the keyboard buffer: */
499 cpu->machine->md.pc.kbd_buf_scancode[
500 cpu->machine->md.pc.kbd_buf_tail] = byte;
501
502 /* TODO: The shift state should be located in the BIOS
503 data area. */
504
505 if (byte == 0x2a) {
506 cpu->machine->md.pc.shift_state |= PC_KBD_SHIFT;
507 byte = 0;
508 }
509 if (byte == 0x1d) {
510 cpu->machine->md.pc.shift_state |= PC_KBD_CTRL;
511 byte = 0;
512 }
513 if (byte == 0x2a + 0x80) {
514 cpu->machine->md.pc.shift_state &= ~PC_KBD_SHIFT;
515 byte = 0;
516 }
517 if (byte == 0x1d + 0x80) {
518 cpu->machine->md.pc.shift_state &= ~PC_KBD_CTRL;
519 byte = 0;
520 }
521
522 /* Convert scancode into ASCII: */
523 /* (TODO: Maybe this should be somewhere else?) */
524 switch (cpu->machine->md.pc.shift_state) {
525 case 0: if (byte >= 1 && byte <= 0xf)
526 byte = "\0331234567890-=\b\t"[byte-1];
527 else if (byte >= 0x10 && byte <= 0x1b)
528 byte = "qwertyuiop[]"[byte-0x10];
529 else if (byte >= 0x1c && byte <= 0x2b)
530 byte = "\r\000asdfghjkl;'`\000\\"[byte-0x1c];
531 else if (byte >= 0x2c && byte <= 0x35)
532 byte = "zxcvbnm,./"[byte-0x2c];
533 else if (byte >= 0x37 && byte <= 0x39)
534 byte = "*\000 "[byte-0x37];
535 else
536 byte = 0;
537 break;
538 case PC_KBD_SHIFT:
539 if (byte >= 1 && byte <= 0xf)
540 byte = "\033!@#$%^&*()_+\b\t"[byte-1];
541 else if (byte >= 0x10 && byte <= 0x1b)
542 byte = "QWERTYUIOP{}"[byte-0x10];
543 else if (byte >= 0x1c && byte <= 0x2b)
544 byte = "\r\000ASDFGHJKL:\"~\000|"[byte-0x1c];
545 else if (byte >= 0x2c && byte <= 0x35)
546 byte = "ZXCVBNM<>?"[byte-0x2c];
547 else if (byte >= 0x37 && byte <= 0x39)
548 byte = "*\000 "[byte-0x37];
549 else
550 byte = 0;
551 break;
552 default:
553 byte = 0;
554 }
555
556 cpu->machine->md.pc.kbd_buf[cpu->machine->md.pc.kbd_buf_tail] = byte;
557
558 cpu->machine->md.pc.kbd_buf_tail ++;
559 cpu->machine->md.pc.kbd_buf_tail %= PC_BIOS_KBD_BUF_SIZE;
560
561 /* EOI the interrupt. */
562 cpu->machine->isa_pic_data.pic1->isr &= ~0x02;
563 }
564
565
566 /*
567 * pc_bios_int10():
568 *
569 * Video functions.
570 */
571 static void pc_bios_int10(struct cpu *cpu)
572 {
573 uint64_t ctrlregs = X86_IO_BASE + 0x3c0;
574 unsigned char byte;
575 unsigned char rgb[3];
576 int x,y, oldx,oldy;
577 int ah = (cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff;
578 int al = cpu->cd.x86.r[X86_R_AX] & 0xff;
579 int dh = (cpu->cd.x86.r[X86_R_DX] >> 8) & 0xff;
580 int dl = cpu->cd.x86.r[X86_R_DX] & 0xff;
581 int ch = (cpu->cd.x86.r[X86_R_CX] >> 8) & 0xff;
582 int cl = cpu->cd.x86.r[X86_R_CX] & 0xff;
583 int bh = (cpu->cd.x86.r[X86_R_BX] >> 8) & 0xff;
584 int bl = cpu->cd.x86.r[X86_R_BX] & 0xff;
585 int cx = cpu->cd.x86.r[X86_R_CX] & 0xffff;
586 int dx = cpu->cd.x86.r[X86_R_DX] & 0xffff;
587 int bp = cpu->cd.x86.r[X86_R_BP] & 0xffff;
588
589 switch (ah) {
590 case 0x00: /* Switch video mode. */
591 set_video_mode(cpu, al);
592 break;
593 case 0x01:
594 /* ch = starting line, cl = ending line */
595 /* TODO: it seems that FreeDOS uses start=6 end=7. hm */
596 if (ch == 6 && cl == 7)
597 ch = 12, cl = 14;
598 set_cursor_scanlines(cpu, ch, cl);
599 break;
600 case 0x02: /* set cursor position */
601 set_cursor_pos(cpu, dl, dh);
602 break;
603 case 0x03: /* read cursor position */
604 get_cursor_pos(cpu, &x, &y);
605 cpu->cd.x86.r[X86_R_DX] = (y << 8) + x;
606 /* ch/cl = cursor start end... TODO */
607 cpu->cd.x86.r[X86_R_CX] = 0x000f;
608 break;
609 case 0x05: /* set active display page */
610 if (al != 0)
611 fatal("WARNING: int 0x10, func 0x05, al = 0x%02\n", al);
612 break;
613 case 0x06:
614 if (al < 1)
615 al = 25;
616 while (al-- > 0)
617 scroll_up(cpu, cl, ch, dl, dh, bh);
618 break;
619 case 0x07:
620 if (al < 1)
621 al = 25;
622 while (al-- > 0)
623 scroll_down(cpu, cl, ch, dl, dh, bh);
624 break;
625 case 0x08: /* read char and attr at cur position */
626 /* TODO: return AH=attr, AL=char */
627 break;
628 case 0x09: /* write character and attribute(todo) */
629 case 0x0a: /* write character only */
630 get_cursor_pos(cpu, &oldx, &oldy);
631 while (cx-- > 0)
632 pc_bios_putchar(cpu, al, ah==9? bl : -1, 0);
633 if (ah == 9)
634 cpu->machine->md.pc.curcolor = bl;
635 set_cursor_pos(cpu, oldx, oldy);
636 break;
637 case 0x0b: /* set background palette */
638 fatal("WARNING: int 0x10, func 0x0b: TODO\n");
639 /* cpu->running = 0; */
640 break;
641 case 0x0e: /* tty output */
642 pc_bios_putchar(cpu, al, -1, 1);
643 break;
644 case 0x0f: /* get video mode */
645 cpu->cd.x86.r[X86_R_AX] = cpu->machine->md.pc.columns << 8;
646
647 byte = 0xff;
648 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x14,
649 &byte, sizeof(byte), MEM_WRITE, CACHE_NONE | PHYSICAL);
650 cpu->memory_rw(cpu, cpu->mem, ctrlregs + 0x15,
651 &byte, sizeof(byte), MEM_READ, CACHE_NONE | PHYSICAL);
652 cpu->cd.x86.r[X86_R_AX] |= byte;
653
654 cpu->cd.x86.r[X86_R_BX] &= ~0xff00; /* BH = pagenr */
655 break;
656 case 0x10: /* Palette stuff */
657 switch (al) {
658 case 0x00:
659 /* Hm. Is this correct? How about the upper 4
660 bits of bh? TODO */
661 set_palette(cpu, bl,
662 ((bh >> 2) & 1) * 0xaa + (bh&8? 0x55 : 0),
663 ((bh >> 1) & 1) * 0xaa + (bh&8? 0x55 : 0),
664 ((bh >> 0) & 1) * 0xaa + (bh&8? 0x55 : 0));
665 break;
666 case 0x01: /* TODO: Set border color. */
667 fatal("TODO int 10,ah=10,al=01\n");
668 break;
669 case 0x02: /* Set all palette registers. */
670 /* Load from ES:DX */
671 fatal("TODO: int10,10,02\n");
672 break;
673 case 0x03: /* TODO: intensity/blinking bit */
674 debug("TODO int 10,ah=10,al=03\n");
675 break;
676 case 0x10:
677 set_palette(cpu, bl, dh, cl, ch);
678 break;
679 case 0x12: /* Set block of palette registers. */
680 /* Load from ES:DX, BX=start color, CX =
681 nr of registers to load */
682 while (cx-- > 0) {
683 cpu->cd.x86.cursegment = X86_S_ES;
684 cpu->memory_rw(cpu, cpu->mem, dx, rgb, 3,
685 MEM_READ, CACHE_DATA | NO_EXCEPTIONS);
686 set_palette(cpu, bl, rgb[0],rgb[1],rgb[2]);
687 dx += 3;
688 bl ++;
689 }
690 break;
691 case 0x17: /* Read block of palette registers. */
692 /* Load into ES:DX, BX=start color, CX =
693 nr of registers to load */
694 while (cx-- > 0) {
695 get_palette(cpu, bl, rgb);
696 cpu->cd.x86.cursegment = X86_S_ES;
697 cpu->memory_rw(cpu, cpu->mem, dx, rgb, 3,
698 MEM_WRITE, CACHE_DATA | NO_EXCEPTIONS);
699 dx += 3;
700 bl ++;
701 }
702 break;
703 case 0x1a: /* Get DAC State: TODO */
704 cpu->cd.x86.r[X86_R_BX] &= ~0xff;
705 break;
706 default:fatal("Unimplemented INT 0x10,AH=0x10,AL=0x%02x\n", al);
707 cpu->running = 0;
708 }
709 break;
710 case 0x11: /* Character generator */
711 /* TODO */
712 switch (al) {
713 case 0x12:
714 break;
715 case 0x14:
716 break;
717 case 0x30:
718 switch (bh) {
719 case 0x03: /* 8x8 font */
720 cpu->cd.x86.r[X86_R_BP] &= ~0xffff;
721 cpu->cd.x86.r[X86_R_BP] |= 0xfa6e;
722 reload_segment_descriptor(cpu, X86_S_ES,
723 0xf000, NULL);
724 /* TODO: cx and dl, better values? */
725 cpu->cd.x86.r[X86_R_CX] &= ~0xffff;
726 cpu->cd.x86.r[X86_R_CX] |= 16;
727 cpu->cd.x86.r[X86_R_DX] &= ~0xff;
728 cpu->cd.x86.r[X86_R_DX] |= 24;
729 break;
730 default:
731 fatal("[ pc_bios: Get Font: TODO ]\n");
732 }
733 break;
734 default:fatal("Unimplemented INT 0x10,AH=0x11,AL=0x%02x\n", al);
735 cpu->running = 0;
736 }
737 break;
738 case 0x12: /* Video Subsystem Configuration */
739 /* TODO */
740 switch (bl) {
741 case 0x10:
742 cpu->cd.x86.r[X86_R_BX] &= ~0xffff;
743 cpu->cd.x86.r[X86_R_BX] |= 0x0003;
744 break;
745 case 0x30: /* select nr of scanlines (200 + 50*al) */
746 debug("[ pc_bios: %i scanlines ]\n", 200+50*al);
747 cpu->cd.x86.r[X86_R_AX] &= ~0xff;
748 cpu->cd.x86.r[X86_R_AX] |= 0x12;
749 break;
750 case 0x34: /* TODO */
751 break;
752 default:fatal("Unimplemented INT 0x10,AH=0x12,BL=0x%02x\n", bl);
753 cpu->running = 0;
754 }
755 break;
756 case 0x13: /* write string */
757 /* TODO: other flags in al */
758 get_cursor_pos(cpu, &oldx, &oldy);
759 set_cursor_pos(cpu, dl, dh);
760 while (cx-- > 0) {
761 int len = 1;
762 unsigned char byte[2];
763 byte[1] = 0x07;
764 if (al & 2)
765 len = 2;
766 cpu->cd.x86.cursegment = X86_S_ES;
767 cpu->memory_rw(cpu, cpu->mem, bp, &byte[0], len,
768 MEM_READ, CACHE_DATA | NO_EXCEPTIONS);
769 bp += len;
770 pc_bios_putchar(cpu, byte[0], byte[1], 1);
771 cpu->machine->md.pc.curcolor = byte[1];
772 }
773 if (!(al & 1))
774 set_cursor_pos(cpu, oldx, oldy);
775 break;
776 case 0x1a: /* get/set video display combination */
777 if (al != 0) {
778 fatal("FATAL: Unimplemented BIOS int 0x10 function"
779 " 0x%02x, al=0x%02\n", ah, al);
780 cpu->running = 0;
781 }
782 cpu->cd.x86.r[X86_R_AX] &= ~0xff;
783 cpu->cd.x86.r[X86_R_AX] |= 0x1a;
784 cpu->cd.x86.r[X86_R_BX] &= ~0xffff;
785 cpu->cd.x86.r[X86_R_BX] |= 0x0008;
786 break;
787 case 0x1b: /* State Information: TODO */
788 fatal("TODO: int10,1b\n");
789 break;
790 case 0x4f: /* VESA */
791 /* TODO: See http://www.uv.tietgen.dk/staff/mlha/PC/
792 Prog/asm/int/INT10.htm#4F for more info. */
793 switch (al) {
794 case 0x00: /* Detect VESA */
795 #if 0
796 cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
797 cpu->cd.x86.r[X86_R_AX] |= 0x004f;
798 /* TODO: the VESA struct at ES:DI */
799 #endif
800 break;
801 case 0x01: /* Return mode info */
802 fatal("TODO: VESA mode 0x%04x\n", cx);
803 break;
804 default:
805 fatal("TODO: int 0x10, function 0x4f, al=0x%02x\n", al);
806 }
807 break;
808 case 0xef: /* Hercules Detection */
809 case 0xfa: /* EGA Register Interface Library */
810 /* TODO: How to accurately return failure? */
811 debug("TODO: int10,ah=0x%02x\n", ah);
812 break;
813 default:
814 fatal("FATAL: Unimplemented PC BIOS interrupt 0x10 function"
815 " 0x%02x.\n", ah);
816 cpu->running = 0;
817 }
818 }
819
820
821 /*
822 * pc_bios_int13():
823 *
824 * Disk-related functions. These usually return CF on error.
825 */
826 static void pc_bios_int13(struct cpu *cpu)
827 {
828 struct pc_bios_disk *disk;
829 int res, nread, err;
830 int ah = (cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff;
831 int al = (cpu->cd.x86.r[X86_R_AX] >> 0) & 0xff;
832 int dh = (cpu->cd.x86.r[X86_R_DX] >> 8) & 0xff;
833 int dl = (cpu->cd.x86.r[X86_R_DX] >> 0) & 0xff;
834 int ch = (cpu->cd.x86.r[X86_R_CX] >> 8) & 0xff;
835 int cl = (cpu->cd.x86.r[X86_R_CX] >> 0) & 0xff;
836 int bx = cpu->cd.x86.r[X86_R_BX] & 0xffff;
837 uint64_t offset;
838
839 switch (ah) {
840 case 0x00: /* Reset disk, dl = drive */
841 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
842 cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
843 /* Do nothing. :-) */
844 break;
845 case 0x02: /* Read sector */
846 case 0x03: /* Write sector */
847 /*
848 * Read/Write sector(s). al = nr of sectors
849 * dh = head, dl = disk id (0-based),
850 * ch = cyl, cl = 1-based starting sector nr
851 * es:bx = destination buffer; return carryflag = error
852 */
853 cpu->cd.x86.rflags |= X86_FLAGS_CF;
854 disk = get_disk(cpu->machine, cpu->cd.x86.r[X86_R_DX] & 0xff);
855 if (disk != NULL) {
856 unsigned char *buf;
857
858 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
859 ch = ch + ((cl >> 6) << 8);
860 cl = (cl & 0x3f) - 1;
861 offset = (cl + disk->sectorspertrack * dh +
862 disk->sectorspertrack * disk->heads * ch) * 512;
863 nread = 0; err = 0;
864 debug("[ pc_bios_int13(): reading from disk 0x%x, "
865 "CHS=%i,%i,%i ]\n", dl, ch, dh, cl);
866
867 buf = malloc(512 * al);
868
869 if (cl+al > disk->sectorspertrack ||
870 dh >= disk->heads || ch > disk->cylinders) {
871 al = 0; err = 4; /* sector not found */
872 fatal("[ pc_bios: attempt to %s outside the d"
873 "isk? bios id=0x%02x, chs=%i,%i,%i, acces"
874 "s at %i,%i,%i ]\n", ah==2? "read" :
875 "write", dl, disk->cylinders, disk->heads,
876 disk->sectorspertrack, ch, dh, cl);
877 }
878
879 debug("[ pc_bios_int13(): %s biosdisk 0x%02x (offset="
880 "0x%"PRIx64") mem=0x%04x:0x%04x ]\n", ah==2?
881 "read from" : "write to", dl, (uint64_t) offset,
882 cpu->cd.x86.s[X86_S_ES], bx);
883
884 if (ah == 3) {
885 fatal("TODO: bios disk write\n");
886 /* cpu->running = 0; */
887 /* TODO */
888 al = 0;
889 }
890 if (al > 0)
891 res = diskimage_access(cpu->machine, disk->id,
892 disk->type, 0, offset, buf, al * 512);
893 else
894 res = 0;
895 nread = al;
896 if (!res) {
897 err = 4;
898 fatal("[ pc_bios_int13(): FAILED to %s"
899 " biosdisk 0x%02x (offset=0x%"PRIx64")"
900 " ]\n", ah==2? "read from" :
901 "write to", dl, (uint64_t) offset);
902 } else if (ah == 2) {
903 cpu->cd.x86.cursegment = X86_S_ES;
904 if (bx + 512*al > 0x10000) {
905 /* DMA overrun */
906 fatal("[ pc_bios: DMA overrun ]\n");
907 err = 9;
908 nread = al = (0x10000 - bx) / 512;
909 }
910 store_buf(cpu, bx, (char *)buf, 512 * al);
911 }
912 free(buf);
913 cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
914 cpu->cd.x86.r[X86_R_AX] |= nread;
915 } else
916 err = 0x80;
917 if (err) {
918 cpu->cd.x86.rflags |= X86_FLAGS_CF;
919 cpu->cd.x86.r[X86_R_AX] |= (err << 8);
920 }
921 break;
922 case 4: /* verify disk sectors */
923 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
924 cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
925 /* Do nothing. :-) */
926 break;
927 case 8: /* get drive status: TODO */
928 cpu->cd.x86.rflags |= X86_FLAGS_CF;
929 cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
930 cpu->cd.x86.r[X86_R_AX] |= 0x8080;
931 disk = get_disk(cpu->machine, cpu->cd.x86.r[X86_R_DX] & 0xff);
932 if (disk != NULL) {
933 int cyl_hi, cyl_lo;
934
935 cyl_lo = disk->cylinders & 255;
936 cyl_hi = ((disk->cylinders >> 8) & 3) << 6;
937 cyl_hi |= disk->sectorspertrack;
938
939 cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
940 cpu->cd.x86.r[X86_R_BX] &= ~0xffff;
941 if (disk->type == DISKIMAGE_FLOPPY)
942 cpu->cd.x86.r[X86_R_BX] |= 4;
943 cpu->cd.x86.r[X86_R_CX] &= ~0xffff;
944 cpu->cd.x86.r[X86_R_CX] |= (cyl_lo << 8) | cyl_hi;
945 cpu->cd.x86.r[X86_R_DX] &= ~0xffff;
946 cpu->cd.x86.r[X86_R_DX] |= 0x01 |
947 ((disk->heads - 1) << 8);
948 /* TODO: dl = nr of drives */
949 /* TODO: es:di? */
950 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
951 }
952 break;
953 case 0x15: /* Read DASD Type */
954 /* TODO: generalize */
955 cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
956 cpu->cd.x86.r[X86_R_AX] |= 0x0100;
957 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
958 break;
959 case 0x41: /* Check for Extended Functions */
960 /* There is no such support. :) */
961 cpu->cd.x86.rflags |= X86_FLAGS_CF;
962 break;
963 case 0x42: /* Extended Read: */
964 /* TODO */
965 cpu->cd.x86.rflags |= X86_FLAGS_CF;
966 cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
967 cpu->cd.x86.r[X86_R_AX] |= 0x0100;
968 break;
969 case 0x48: /* ? */
970 /* TODO */
971 cpu->cd.x86.rflags |= X86_FLAGS_CF;
972 break;
973 case 0x4b: /* CDROM emulation (TODO) */
974 cpu->cd.x86.rflags |= X86_FLAGS_CF;
975 break;
976 case 0xfa: /* ? */
977 cpu->cd.x86.rflags |= X86_FLAGS_CF;
978 break;
979 default:
980 fatal("FATAL: Unimplemented PC BIOS interrupt 0x13 function"
981 " 0x%02x.\n", ah);
982 cpu->running = 0;
983 }
984 }
985
986
987 /*
988 * pc_bios_int14():
989 *
990 * Serial port stuff.
991 */
992 static void pc_bios_int14(struct cpu *cpu)
993 {
994 int ah = (cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff;
995
996 switch (ah) {
997 case 0: debug("[ pc_bios_14(): TODO ]\n");
998 break;
999 default:
1000 fatal("FATAL: Unimplemented PC BIOS interrupt 0x14 function"
1001 " 0x%02x.\n", ah);
1002 cpu->running = 0;
1003 }
1004 }
1005
1006
1007 /*
1008 * pc_bios_int15():
1009 */
1010 static void pc_bios_int15(struct cpu *cpu)
1011 {
1012 int ah = (cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff;
1013 int al = cpu->cd.x86.r[X86_R_AX] & 0xff;
1014 int cx = cpu->cd.x86.r[X86_R_CX] & 0xffff;
1015 int si = cpu->cd.x86.r[X86_R_SI] & 0xffff;
1016 int m;
1017 unsigned char src_entry[8];
1018 unsigned char dst_entry[8];
1019 uint32_t src_addr, dst_addr;
1020
1021 switch (ah) {
1022 case 0x00: /* TODO? */
1023 fatal("[ PC BIOS int 0x15,0x00: TODO ]\n");
1024 cpu->cd.x86.rflags |= X86_FLAGS_CF;
1025 cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1026 cpu->cd.x86.r[X86_R_AX] |= 0x8600; /* TODO */
1027 break;
1028 case 0x06: /* TODO */
1029 fatal("[ PC BIOS int 0x15,0x06: TODO ]\n");
1030 cpu->cd.x86.rflags |= X86_FLAGS_CF;
1031 cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1032 cpu->cd.x86.r[X86_R_AX] |= 0x8600; /* TODO */
1033 break;
1034 case 0x24: /* TODO */
1035 fatal("[ PC BIOS int 0x15,0x24: TODO ]\n");
1036 cpu->cd.x86.rflags |= X86_FLAGS_CF;
1037 cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1038 cpu->cd.x86.r[X86_R_AX] |= 0x8600; /* TODO */
1039 break;
1040 case 0x41: /* TODO */
1041 fatal("[ PC BIOS int 0x15,0x41: TODO ]\n");
1042 cpu->cd.x86.rflags |= X86_FLAGS_CF;
1043 cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1044 cpu->cd.x86.r[X86_R_AX] |= 0x8600; /* TODO */
1045 break;
1046 case 0x4f: /* Keyboard Scancode Intercept (TODO) */
1047 cpu->cd.x86.rflags |= X86_FLAGS_CF;
1048 break;
1049 case 0x53: /* TODO */
1050 fatal("[ PC BIOS int 0x15,0x53: TODO ]\n");
1051 cpu->cd.x86.rflags |= X86_FLAGS_CF;
1052 break;
1053 case 0x86: /* Wait */
1054 /* No. :-) */
1055 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1056 break;
1057 case 0x87: /* Move to/from extended memory, via a GDT */
1058 cpu->cd.x86.cursegment = X86_S_ES;
1059 cpu->memory_rw(cpu, cpu->mem, si + 0x10, src_entry, 8,
1060 MEM_READ, CACHE_DATA);
1061 cpu->memory_rw(cpu, cpu->mem, si + 0x18, dst_entry, 8,
1062 MEM_READ, CACHE_DATA);
1063 src_addr = src_entry[2]+(src_entry[3]<<8)+(src_entry[4]<<16);
1064 dst_addr = dst_entry[2]+(dst_entry[3]<<8)+(dst_entry[4]<<16);
1065 if (src_entry[5] != 0x92 && src_entry[5] != 0x93)
1066 fatal("WARNING: int15,87: bad src access right?"
1067 " (0x%02x, should be 0x93)\n", src_entry[5]);
1068 if (dst_entry[5] != 0x92 && dst_entry[5] != 0x93)
1069 fatal("WARNING: int15,87: bad dst access right?"
1070 " (0x%02x, should be 0x93)\n", dst_entry[5]);
1071 debug("[ pc_bios: INT15: copying %i bytes from 0x%x to 0x%x"
1072 " ]\n", cx*2, src_addr, dst_addr);
1073 if (cx > 0x8000)
1074 fatal("WARNING! INT15 func 0x87 cx=0x%04x, max allowed"
1075 " is supposed to be 0x8000!\n", cx);
1076 while (cx*2 > 0) {
1077 unsigned char buf[2];
1078 cpu->memory_rw(cpu, cpu->mem, src_addr, buf, 2,
1079 MEM_READ, NO_SEGMENTATION | CACHE_DATA);
1080 cpu->memory_rw(cpu, cpu->mem, dst_addr, buf, 2,
1081 MEM_WRITE, NO_SEGMENTATION | CACHE_DATA);
1082 src_addr += 2; dst_addr += 2; cx --;
1083 }
1084 cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1085 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1086 cpu->cd.x86.rflags |= X86_FLAGS_ZF;
1087 break;
1088 case 0x88: /* Extended Memory Size Determination */
1089 /* TODO: Max 16 or 64 MB? */
1090 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1091 cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
1092 if (cpu->machine->physical_ram_in_mb <= 64)
1093 cpu->cd.x86.r[X86_R_AX] |= (cpu->machine->
1094 physical_ram_in_mb - 1) * 1024;
1095 else
1096 cpu->cd.x86.r[X86_R_AX] |= 63*1024;
1097 break;
1098 case 0x8A: /* Get "Big" memory size */
1099 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1100 m = (cpu->machine->physical_ram_in_mb - 1) * 1024;
1101 cpu->cd.x86.r[X86_R_DX] &= ~0xffff;
1102 cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
1103 cpu->cd.x86.r[X86_R_DX] |= ((m >> 16) & 0xffff);
1104 cpu->cd.x86.r[X86_R_AX] |= (m & 0xffff);
1105 break;
1106 case 0x91: /* Interrupt Complete (bogus) */
1107 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1108 break;
1109 case 0xc0: /* System Config: (at 0xfffd:0) */
1110 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1111 cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1112 cpu->cd.x86.r[X86_R_BX] &= ~0xffff;
1113 cpu->cd.x86.s[X86_S_ES] = 0xfffd;
1114 reload_segment_descriptor(cpu, X86_S_ES, 0xfffd, NULL);
1115 break;
1116 case 0xc1: /* Extended Bios Data-seg (TODO) */
1117 cpu->cd.x86.rflags |= X86_FLAGS_CF;
1118 break;
1119 case 0xe8: /* TODO */
1120 switch (al) {
1121 case 0x01:
1122 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1123 m = cpu->machine->physical_ram_in_mb;
1124 if (m > 16)
1125 m = 16;
1126 m = (m - 1) * 1024;
1127 /* between 1MB and 16MB: (1KB blocks) */
1128 cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
1129 cpu->cd.x86.r[X86_R_AX] |= (m & 0xffff);
1130 /* mem above 16MB, 64K blocks: */
1131 m = cpu->machine->physical_ram_in_mb;
1132 if (m < 16)
1133 m = 0;
1134 else
1135 m = (m-16) / 16;
1136 cpu->cd.x86.r[X86_R_BX] &= ~0xffff;
1137 cpu->cd.x86.r[X86_R_BX] |= (m & 0xffff);
1138 /* CX and DX are "configured" memory */
1139 cpu->cd.x86.r[X86_R_CX] &= ~0xffff;
1140 cpu->cd.x86.r[X86_R_DX] &= ~0xffff;
1141 cpu->cd.x86.r[X86_R_CX] |= (
1142 cpu->cd.x86.r[X86_R_AX] & 0xffff);
1143 cpu->cd.x86.r[X86_R_DX] |= (
1144 cpu->cd.x86.r[X86_R_BX] & 0xffff);
1145 break;
1146 case 0x20: /* Get memory map: TODO */
1147 cpu->cd.x86.rflags |= X86_FLAGS_CF;
1148 cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1149 cpu->cd.x86.r[X86_R_AX] |= 0x8600;
1150 break;
1151 default:fatal("[ PC BIOS int 0x15,0xe8: al=0x%02x "
1152 " TODO ]\n", al);
1153 cpu->running = 0;
1154 }
1155 break;
1156 default:
1157 fatal("FATAL: Unimplemented PC BIOS interrupt 0x15 function"
1158 " 0x%02x.\n", ah);
1159 cpu->running = 0;
1160 }
1161 }
1162
1163
1164 /*
1165 * pc_bios_int16():
1166 *
1167 * Keyboard-related functions.
1168 */
1169 static int pc_bios_int16(struct cpu *cpu, int *enable_ints_after_returnp)
1170 {
1171 int ah = (cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff;
1172 /* int al = cpu->cd.x86.r[X86_R_AX] & 0xff; */
1173 int scancode, asciicode;
1174 unsigned char tmpchar;
1175
1176 switch (ah) {
1177 case 0x00: /* getchar */
1178 scancode = asciicode = 0;
1179 if (cpu->machine->md.pc.kbd_buf_head !=
1180 cpu->machine->md.pc.kbd_buf_tail) {
1181 asciicode = cpu->machine->md.pc.kbd_buf[
1182 cpu->machine->md.pc.kbd_buf_head];
1183 scancode = cpu->machine->md.pc.kbd_buf_scancode[
1184 cpu->machine->md.pc.kbd_buf_head];
1185 if (asciicode != 0) {
1186 cpu->cd.x86.r[X86_R_AX] =
1187 (scancode << 8) | asciicode;
1188 }
1189 cpu->machine->md.pc.kbd_buf_head ++;
1190 cpu->machine->md.pc.kbd_buf_head %=
1191 PC_BIOS_KBD_BUF_SIZE;
1192 }
1193 *enable_ints_after_returnp = 1;
1194 if (asciicode == 0)
1195 return 0;
1196 break;
1197 case 0x01: /* non-destructive "isavail" */
1198 cpu->cd.x86.rflags |= X86_FLAGS_ZF;
1199 cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
1200 scancode = asciicode = 0;
1201 if (cpu->machine->md.pc.kbd_buf_head !=
1202 cpu->machine->md.pc.kbd_buf_tail) {
1203 asciicode = cpu->machine->md.pc.kbd_buf[
1204 cpu->machine->md.pc.kbd_buf_head];
1205 scancode = cpu->machine->md.pc.kbd_buf_scancode[
1206 cpu->machine->md.pc.kbd_buf_head];
1207 cpu->cd.x86.rflags &= ~X86_FLAGS_ZF;
1208 cpu->cd.x86.r[X86_R_AX] |= (scancode << 8) | asciicode;
1209 }
1210 *enable_ints_after_returnp = 1;
1211 break;
1212 case 0x02: /* read keyboard flags */
1213 /* TODO: keep this byte updated */
1214 cpu->memory_rw(cpu, cpu->mem, 0x417, &tmpchar, 1,
1215 MEM_READ, PHYSICAL);
1216 cpu->cd.x86.r[X86_R_AX] = (cpu->cd.x86.r[X86_R_AX] & ~0xff)
1217 | tmpchar;
1218 break;
1219 case 0x03: /* Set Keyboard Typematic Rate: TODO */
1220 break;
1221 case 0x55: /* Microsoft stuff: Ignore :-) */
1222 break;
1223 case 0x92: /* Keyboard "Capabilities Check": TODO */
1224 break;
1225 default:
1226 fatal("FATAL: Unimplemented PC BIOS interrupt 0x16 function"
1227 " 0x%02x.\n", ah);
1228 cpu->running = 0;
1229 }
1230
1231 return 1;
1232 }
1233
1234
1235 /*
1236 * pc_bios_int17():
1237 *
1238 * Printer port stuff.
1239 */
1240 static void pc_bios_int17(struct cpu *cpu)
1241 {
1242 int ah = (cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff;
1243
1244 switch (ah) {
1245 case 0x01:
1246 debug("[ PC BIOS int 0x17,0x01: TODO ]\n");
1247 cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1248 break;
1249 default:
1250 fatal("FATAL: Unimplemented PC BIOS interrupt 0x17 function"
1251 " 0x%02x.\n", ah);
1252 cpu->running = 0;
1253 }
1254 }
1255
1256
1257 /*
1258 * pc_bios_int1a():
1259 *
1260 * Time of Day stuff.
1261 */
1262 static void pc_bios_int1a(struct cpu *cpu)
1263 {
1264 unsigned char ticks[4];
1265 int ah = (cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff;
1266 time_t tim;
1267 struct tm *tm;
1268
1269 switch (ah) {
1270 case 0x00: /* Read tick count. */
1271 cpu->memory_rw(cpu, cpu->mem, 0x46C,
1272 ticks, sizeof(ticks), MEM_READ, CACHE_NONE | PHYSICAL);
1273 cpu->cd.x86.r[X86_R_CX] = (ticks[3] << 8) | ticks[2];
1274 cpu->cd.x86.r[X86_R_DX] = (ticks[1] << 8) | ticks[0];
1275 break;
1276 case 0x01: /* Set tick count. */
1277 ticks[0] = cpu->cd.x86.r[X86_R_DX];
1278 ticks[1] = cpu->cd.x86.r[X86_R_DX] >> 8;
1279 ticks[2] = cpu->cd.x86.r[X86_R_CX];
1280 ticks[3] = cpu->cd.x86.r[X86_R_CX] >> 8;
1281 cpu->memory_rw(cpu, cpu->mem, 0x46C,
1282 ticks, sizeof(ticks), MEM_WRITE, CACHE_NONE | PHYSICAL);
1283 break;
1284 case 0x02: /* Read real time clock time (AT,PS/2) */
1285 tim = time(NULL);
1286 tm = gmtime(&tim);
1287 cpu->cd.x86.r[X86_R_CX] &= ~0xffff;
1288 cpu->cd.x86.r[X86_R_DX] &= ~0xffff;
1289 cpu->cd.x86.r[X86_R_CX] |= (dec_to_bcd(tm->tm_hour) << 8) |
1290 dec_to_bcd(tm->tm_min);
1291 cpu->cd.x86.r[X86_R_DX] |= dec_to_bcd(tm->tm_sec) << 8;
1292 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1293 break;
1294 case 0x04: /* Read real time clock date (AT,PS/2) */
1295 tim = time(NULL);
1296 tm = gmtime(&tim);
1297 cpu->cd.x86.r[X86_R_CX] &= ~0xffff;
1298 cpu->cd.x86.r[X86_R_DX] &= ~0xffff;
1299 cpu->cd.x86.r[X86_R_CX] |=
1300 (dec_to_bcd((tm->tm_year+1900)/100) << 8) |
1301 dec_to_bcd(tm->tm_year % 100);
1302 cpu->cd.x86.r[X86_R_DX] |= (dec_to_bcd(tm->tm_mon+1) << 8) |
1303 dec_to_bcd(tm->tm_mday);
1304 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1305 break;
1306 case 0xb1: /* Intel PCI Bios */
1307 /* ... not installed :) */
1308 cpu->cd.x86.rflags |= X86_FLAGS_CF;
1309 break;
1310 default:
1311 fatal("FATAL: Unimplemented PC BIOS interrupt 0x1a function"
1312 " 0x%02x.\n", ah);
1313 cpu->running = 0;
1314 }
1315 }
1316
1317
1318 /*
1319 * pc_bios_int1c():
1320 *
1321 * Increase the timer-tick word at 0x40:0x6C.
1322 */
1323 static void pc_bios_int1c(struct cpu *cpu)
1324 {
1325 unsigned char ticks[4];
1326 size_t i;
1327
1328 /* Increase word at 0x0040:0x006C */
1329 cpu->memory_rw(cpu, cpu->mem, 0x46C,
1330 ticks, sizeof(ticks), MEM_READ, CACHE_NONE | PHYSICAL);
1331 for (i=0; i<sizeof(ticks); i++) {
1332 ticks[i] ++;
1333 if (ticks[i] != 0)
1334 break;
1335 }
1336 cpu->memory_rw(cpu, cpu->mem, 0x46C,
1337 ticks, sizeof(ticks), MEM_WRITE, CACHE_NONE | PHYSICAL);
1338 }
1339
1340
1341 /*
1342 * pc_bios_smp_init():
1343 *
1344 * Initialize the "_MP_" struct in BIOS memory.
1345 *
1346 * TODO: Don't use hardcoded values like this.
1347 */
1348 void pc_bios_smp_init(struct cpu *cpu)
1349 {
1350 int i, chksum;
1351
1352 reload_segment_descriptor(cpu, X86_S_FS, 0xf000, NULL);
1353 store_buf(cpu, 0x9000, "_MP_", 4);
1354 store_byte(cpu, 0x9004, 0x10); /* ptr to table */
1355 store_byte(cpu, 0x9005, 0x90);
1356 store_byte(cpu, 0x9006, 0x0f);
1357 store_byte(cpu, 0x9007, 0x00);
1358 store_byte(cpu, 0x9008, 0x01); /* length. should be 1 */
1359 store_byte(cpu, 0x9009, 0x04); /* version. 4 means "1.4" */
1360 /* Byte at 0x0a is checksum. TODO: make this automagic */
1361 chksum = '_' + 'M' + 'P' + '_' + 0x10 + 0x90 + 0xf + 1 + 4;
1362 store_byte(cpu, 0x900a, 0 - chksum);
1363
1364 /* PCMP struct, at addr 0x9010. */
1365 store_buf(cpu, 0x9010, "PCMP", 4);
1366 store_16bit_word(cpu, 0x9014, 43);
1367 store_byte(cpu, 0x9016, 4); /* rev 1.4 */
1368 /* 9017 is checksum */
1369 store_buf(cpu, 0x9018, "GXemul ", 8);
1370 store_buf(cpu, 0x9020, "SMP ", 12);
1371
1372 /* Nr of entries (one per cpu): */
1373 store_16bit_word(cpu, 0x9010 + 34, cpu->machine->ncpus);
1374
1375 if (cpu->machine->ncpus > 16)
1376 fatal("WARNING: More than 16 CPUs?\n");
1377
1378 for (i=0; i<cpu->machine->ncpus; i++) {
1379 int ofs = 44 + 20*i;
1380 /* 20 bytes per CPU: */
1381 store_byte(cpu, 0x9010 + ofs + 0, 0x00); /* cpu */
1382 store_byte(cpu, 0x9010 + ofs + 1, i); /* id */
1383 store_byte(cpu, 0x9010 + ofs + 3, 1 | /* enable */
1384 ((i == cpu->machine->bootstrap_cpu)? 2 : 0));
1385 }
1386 }
1387
1388
1389 /*
1390 * pc_bios_simple_pmode_setup():
1391 *
1392 * This function is called from emul.c before loading a 32-bit or 64-bit ELF.
1393 * Loading ELFs when the emulation is set to 16-bit real mode is not a good
1394 * thing, so this function sets up a simple GDT which maps every 0xZyyyyyyy
1395 * to 0x0yyyyyyy.
1396 *
1397 * 0xf4000 GDT:
1398 * 00 = NULL
1399 * 08 = code
1400 * 10 = data
1401 */
1402 void pc_bios_simple_pmode_setup(struct cpu *cpu)
1403 {
1404 int i, j, addr = 0, npts;
1405 uint32_t pt_base;
1406 cpu->cd.x86.cursegment = X86_S_FS;
1407 reload_segment_descriptor(cpu, X86_S_FS, 0xf100, NULL);
1408
1409 /* 0x00 = NULL descriptor. */
1410 addr += 8;
1411
1412 /* 0x08 = Code descriptor. */
1413 store_byte(cpu, addr + 0, 0xff);
1414 store_byte(cpu, addr + 1, 0xff);
1415 store_byte(cpu, addr + 2, 0x00);
1416 store_byte(cpu, addr + 3, 0x00);
1417 store_byte(cpu, addr + 4, 0x00);
1418 store_byte(cpu, addr + 5, 0x9f);
1419 store_byte(cpu, addr + 6, 0xcf);
1420 store_byte(cpu, addr + 7, 0x00);
1421 addr += 8;
1422
1423 /* 0x10 = Data descriptor. */
1424 store_byte(cpu, addr + 0, 0xff);
1425 store_byte(cpu, addr + 1, 0xff);
1426 store_byte(cpu, addr + 2, 0x00);
1427 store_byte(cpu, addr + 3, 0x00);
1428 store_byte(cpu, addr + 4, 0x00);
1429 store_byte(cpu, addr + 5, 0x93);
1430 store_byte(cpu, addr + 6, 0xcf);
1431 store_byte(cpu, addr + 7, 0x00);
1432 addr += 8;
1433
1434 cpu->cd.x86.gdtr = 0xf1000;
1435 cpu->cd.x86.gdtr_limit = 0xfff;
1436
1437 addr = 0x1000;
1438 cpu->cd.x86.cr[3] = 0xf2000;
1439
1440 npts = 4;
1441 pt_base = 0xf3000; /* 0xf3000, f4000, f5000, f6000 */
1442
1443 /* Set up the page directory: */
1444 for (i=0; i<1024; i++) {
1445 uint32_t pde = pt_base + 0x03 + ((i & (npts-1)) << 12);
1446 store_32bit_word(cpu, addr + i*4, pde);
1447 }
1448 addr += 4096;
1449
1450 /* Set up the page tables: */
1451 for (i=0; i<npts; i++) {
1452 for (j=0; j<1024; j++) {
1453 uint32_t pte = (i << 22) + (j << 12) + 0x03;
1454 store_32bit_word(cpu, addr + j*4, pte);
1455 }
1456 addr += 4096;
1457 }
1458
1459 cpu->cd.x86.cr[0] |= X86_CR0_PE | X86_CR0_PG;
1460
1461 /* Interrupts are dangerous when we start in pmode! */
1462 cpu->cd.x86.rflags &= ~X86_FLAGS_IF;
1463
1464 reload_segment_descriptor(cpu, X86_S_CS, 0x08, NULL);
1465 reload_segment_descriptor(cpu, X86_S_DS, 0x10, NULL);
1466 reload_segment_descriptor(cpu, X86_S_ES, 0x10, NULL);
1467 reload_segment_descriptor(cpu, X86_S_SS, 0x10, NULL);
1468 cpu->cd.x86.r[X86_R_SP] = 0x7000;
1469 cpu->cd.x86.cursegment = X86_S_DS;
1470 }
1471
1472
1473 /*
1474 * pc_bios_init():
1475 */
1476 void pc_bios_init(struct cpu *cpu)
1477 {
1478 char t[81];
1479 int x, y, nboxlines, i, any_disk = 0, disknr, tmp;
1480 int old_cursegment = cpu->cd.x86.cursegment;
1481 int boot_id, boot_type, bios_boot_id = 0, nfloppies = 0, nhds = 0;
1482
1483 /* Go to real mode: */
1484 cpu->cd.x86.cr[0] &= ~X86_CR0_PE;
1485
1486 boot_id = diskimage_bootdev(cpu->machine, &boot_type);
1487
1488 if (cpu->machine->md.pc.initialized) {
1489 fatal("ERROR: pc_bios_init(): Already initialized.\n");
1490 return;
1491 }
1492
1493 if (cpu->machine->isa_pic_data.pic1 == NULL) {
1494 fatal("ERROR: No interrupt controller?\n");
1495 exit(1);
1496 } else
1497 cpu->machine->isa_pic_data.pic1->irq_base = 0x08;
1498
1499 /* pic2 can be NULL when emulating an original XT: */
1500 if (cpu->machine->isa_pic_data.pic2 != NULL)
1501 cpu->machine->isa_pic_data.pic2->irq_base = 0x70;
1502
1503 /* Disk Base Table (11 or 12 bytes?) at F000h:EFC7: */
1504 cpu->cd.x86.cursegment = X86_S_FS;
1505 reload_segment_descriptor(cpu, X86_S_FS, 0xf000, NULL);
1506 store_byte(cpu, 0xefc7 + 0, 0xcf);
1507 store_byte(cpu, 0xefc7 + 1, 0xb8);
1508 store_byte(cpu, 0xefc7 + 2, 1); /* timer ticks till shutoff */
1509 store_byte(cpu, 0xefc7 + 3, 2); /* 512 bytes per sector */
1510 store_byte(cpu, 0xefc7 + 4, 17);
1511 store_byte(cpu, 0xefc7 + 5, 0xd8);
1512 store_byte(cpu, 0xefc7 + 6, 0xff);
1513 store_byte(cpu, 0xefc7 + 7, 0);
1514 store_byte(cpu, 0xefc7 + 8, 0xf6);
1515 store_byte(cpu, 0xefc7 + 9, 1); /* head bounce delay in msec */
1516 store_byte(cpu, 0xefc7 + 10, 1);/* motor start time in 1/8 secs */
1517 store_byte(cpu, 0xefc7 + 11, 1);/* motor stop time in 1/4 secs */
1518
1519 /* BIOS System Configuration Parameters (8 bytes) at 0xfffd:0: */
1520 reload_segment_descriptor(cpu, X86_S_FS, 0xfffd, NULL);
1521 store_byte(cpu, 0, 8); store_byte(cpu, 1, 0); /* len */
1522 store_byte(cpu, 2, 0xfc); /* model */
1523 store_byte(cpu, 3, 0); /* sub-model */
1524 store_byte(cpu, 4, 0); /* bios revision */
1525 store_byte(cpu, 5, 0x60); /* features */
1526 /* see http://members.tripod.com/~oldboard/assembly/
1527 int_15-c0.html for details */
1528
1529 /* Some info in the last paragraph of the BIOS: */
1530 reload_segment_descriptor(cpu, X86_S_FS, 0xffff, NULL);
1531 /* TODO: current date :-) */
1532 store_byte(cpu, 0x05, '0'); store_byte(cpu, 0x06, '1');
1533 store_byte(cpu, 0x07, '/');
1534 store_byte(cpu, 0x08, '0'); store_byte(cpu, 0x09, '1');
1535 store_byte(cpu, 0x0a, '/');
1536 store_byte(cpu, 0x0b, '0'); store_byte(cpu, 0x0c, '5');
1537 store_byte(cpu, 0x0e, 0xfc);
1538
1539 /* Copy the first 128 chars of the 8x8 VGA font into 0xf000:0xfa6e */
1540 reload_segment_descriptor(cpu, X86_S_FS, 0xf000, NULL);
1541 store_buf(cpu, 0xfa6e, (char *)font8x8, 8*128);
1542 store_buf(cpu, 0xfa6e - 1024, (char *)font8x8 + 1024, 8*128);
1543
1544 /*
1545 * Initialize all real-mode interrupt vectors to point to somewhere
1546 * within the PC BIOS area (0xf000:0x8yy0), and place an IRET
1547 * instruction (too fool someone who really reads the BIOS memory).
1548 */
1549 for (i=0; i<256; i++) {
1550 if (i == 0x20)
1551 i = 0x70;
1552 if (i == 0x78)
1553 break;
1554 reload_segment_descriptor(cpu, X86_S_FS, 0x0000, NULL);
1555 store_16bit_word(cpu, i*4, 0x8000 + i*16);
1556 store_16bit_word(cpu, i*4 + 2, 0xf000);
1557
1558 /* Exceptions: int 0x1e = ptr to disk table, 1f=fonthigh */
1559 if (i == 0x1e)
1560 store_16bit_word(cpu, i*4, 0xefc7);
1561 if (i == 0x1f)
1562 store_16bit_word(cpu, i*4, 0xfa6e - 1024);
1563
1564 reload_segment_descriptor(cpu, X86_S_FS, 0xf000, NULL);
1565 store_byte(cpu, 0x8000 + i*16, 0xCF); /* IRET */
1566 }
1567
1568 /* For SMP emulation, create an "MP" struct in BIOS memory: */
1569 if (cpu->machine->ncpus > 1)
1570 pc_bios_smp_init(cpu);
1571
1572 /* Prepare for text mode: (0x03 = 80x25, 0x01 = 40x25) */
1573 set_video_mode(cpu, 0x03);
1574
1575 cmos_write(cpu, 0x15, 640 & 255);
1576 cmos_write(cpu, 0x16, 640 >> 8);
1577 tmp = cpu->machine->physical_ram_in_mb / 1024;
1578 if (tmp > 63*1024)
1579 tmp = 63*1024;
1580 cmos_write(cpu, 0x17, tmp & 255);
1581 cmos_write(cpu, 0x18, tmp >> 8);
1582
1583 /* Clear the screen first: */
1584 set_cursor_pos(cpu, 0, 0);
1585 for (y=0; y<cpu->machine->md.pc.rows; y++)
1586 for (x=0; x<cpu->machine->md.pc.columns; x++)
1587 output_char(cpu, x,y, ' ', 0x07);
1588
1589 nboxlines = cpu->machine->md.pc.columns <= 40? 4 : 3;
1590
1591 /* Draw a nice box at the top: */
1592 for (y=0; y<nboxlines; y++)
1593 for (x=0; x<cpu->machine->md.pc.columns; x++) {
1594 unsigned char ch = ' ';
1595 if (cpu->machine->use_x11) {
1596 if (y == 0) {
1597 ch = 196;
1598 if (x == 0)
1599 ch = 218;
1600 if (x == cpu->machine->md.pc.columns-1)
1601 ch = 191;
1602 } else if (y == nboxlines-1) {
1603 ch = 196;
1604 if (x == 0)
1605 ch = 192;
1606 if (x == cpu->machine->md.pc.columns-1)
1607 ch = 217;
1608 } else if (x == 0 || x ==
1609 cpu->machine->md.pc.columns-1)
1610 ch = 179;
1611 } else {
1612 if (y == 0 || y == nboxlines-1) {
1613 ch = '-';
1614 if (x == 0 || x ==
1615 cpu->machine->md.pc.columns-1)
1616 ch = '+';
1617 } else {
1618 if (x == 0 || x ==
1619 cpu->machine->md.pc.columns-1)
1620 ch = '|';
1621 }
1622 }
1623 output_char(cpu, x,y, ch, 0x19);
1624 }
1625
1626 snprintf(t, sizeof(t), "GXemul");
1627 #ifdef VERSION
1628 snprintf(t + strlen(t), sizeof(t)-strlen(t), " "VERSION);
1629 #endif
1630 set_cursor_pos(cpu, 2, 1);
1631 pc_bios_printstr(cpu, t, 0x1f);
1632
1633 snprintf(t, sizeof(t), "%i cpu%s (%s), %i MB memory",
1634 cpu->machine->ncpus, cpu->machine->ncpus > 1? "s" : "",
1635 cpu->cd.x86.model.name, cpu->machine->physical_ram_in_mb);
1636 if (cpu->machine->md.pc.columns <= 40)
1637 set_cursor_pos(cpu, 2, 2);
1638 else
1639 set_cursor_pos(cpu, 78 - strlen(t), 1);
1640 pc_bios_printstr(cpu, t, 0x17);
1641 if (cpu->machine->md.pc.columns <= 40)
1642 set_cursor_pos(cpu, 0, 5);
1643 else
1644 set_cursor_pos(cpu, 0, 4);
1645
1646 cpu->machine->md.pc.curcolor = 0x07;
1647
1648 /* "Detect" Floppies, IDE disks, and SCSI disks: */
1649 for (i=0; i<4; i++) {
1650 if (diskimage_exist(cpu->machine, i, DISKIMAGE_FLOPPY)) {
1651 struct pc_bios_disk *p;
1652 p = add_disk(cpu->machine, i, i, DISKIMAGE_FLOPPY);
1653 snprintf(t, sizeof(t), "%c%c", i<2? ('A'+i):' ',
1654 i<2? ':':' ');
1655 pc_bios_printstr(cpu, t, 0xf);
1656 if (i < 2)
1657 nfloppies ++;
1658 snprintf(t, sizeof(t), " (bios disk %02x) FLOPPY", i);
1659 pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1660 snprintf(t, sizeof(t), ", %i KB", (int)(p->size/1024));
1661 pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1662 if (cpu->machine->md.pc.columns <= 40)
1663 pc_bios_printstr(cpu, "\n ", 0x07);
1664 snprintf(t, sizeof(t), " (CHS=%i,%i,%i)", p->cylinders,
1665 p->heads, p->sectorspertrack);
1666 pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1667 if (boot_id == i && boot_type == DISKIMAGE_FLOPPY) {
1668 bios_boot_id = i;
1669 pc_bios_printstr(cpu, " [boot device]", 0xf);
1670 }
1671 pc_bios_printstr(cpu, "\n",
1672 cpu->machine->md.pc.curcolor);
1673 any_disk = 1;
1674 }
1675 }
1676 disknr = 0x80;
1677 for (i=0; i<8; i++) {
1678 if (diskimage_exist(cpu->machine, i, DISKIMAGE_IDE)) {
1679 struct pc_bios_disk *p;
1680 p = add_disk(cpu->machine, disknr, i, DISKIMAGE_IDE);
1681 snprintf(t, sizeof(t), "%s", disknr==0x80? "C:" : " ");
1682 pc_bios_printstr(cpu, t, 0xf);
1683 nhds ++;
1684 snprintf(t, sizeof(t),
1685 " (bios disk %02x) IDE %s, id %i",
1686 disknr, diskimage_is_a_cdrom(cpu->machine, i,
1687 DISKIMAGE_IDE)? "cdrom" : (
1688 diskimage_is_a_tape(cpu->machine, i,
1689 DISKIMAGE_IDE)? "tape" : "disk"),
1690 i);
1691 pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1692 if (cpu->machine->md.pc.columns <= 40)
1693 pc_bios_printstr(cpu, "\n ", 0x07);
1694 else
1695 pc_bios_printstr(cpu, ", ",
1696 cpu->machine->md.pc.curcolor);
1697 snprintf(t, sizeof(t), "%lli MB", (long long)
1698 (p->size >> 20));
1699 pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1700 if (boot_id == i && boot_type == DISKIMAGE_IDE) {
1701 bios_boot_id = disknr;
1702 pc_bios_printstr(cpu, " [boot device]", 0xf);
1703 }
1704 pc_bios_printstr(cpu, "\n",
1705 cpu->machine->md.pc.curcolor);
1706 disknr++;
1707 any_disk = 1;
1708 }
1709 }
1710 for (i=0; i<8; i++) {
1711 if (diskimage_exist(cpu->machine, i, DISKIMAGE_SCSI)) {
1712 struct pc_bios_disk *p;
1713 p = add_disk(cpu->machine, disknr, i, DISKIMAGE_SCSI);
1714 snprintf(t, sizeof(t), "%s", disknr==0x80? "C:" : " ");
1715 pc_bios_printstr(cpu, t, 0xf);
1716 nhds ++;
1717 snprintf(t, sizeof(t),
1718 " (bios disk %02x) SCSI disk, id %i", disknr, i);
1719 pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1720 if (cpu->machine->md.pc.columns <= 40)
1721 pc_bios_printstr(cpu, "\n ", 0x07);
1722 else
1723 pc_bios_printstr(cpu, ", ",
1724 cpu->machine->md.pc.curcolor);
1725 snprintf(t, sizeof(t), "%lli MB", (long long)
1726 (p->size >> 20));
1727 pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1728 if (boot_id == i && boot_type == DISKIMAGE_SCSI) {
1729 bios_boot_id = disknr;
1730 pc_bios_printstr(cpu, " [boot device]", 0xf);
1731 }
1732 pc_bios_printstr(cpu, "\n",
1733 cpu->machine->md.pc.curcolor);
1734 disknr++;
1735 any_disk = 1;
1736 }
1737 }
1738
1739 if (any_disk)
1740 pc_bios_printstr(cpu, "\n", cpu->machine->md.pc.curcolor);
1741 else
1742 pc_bios_printstr(cpu, "No disks attached!\n\n", 0x0f);
1743
1744 /* See http://members.tripod.com/~oldboard/assembly/bios_data_area.html
1745 for more info. */
1746 if (nfloppies > 0)
1747 nfloppies --;
1748
1749 reload_segment_descriptor(cpu, X86_S_FS, 0x0000, NULL);
1750 store_16bit_word(cpu, 0x400, 0x03F8); /* COM1 */
1751 store_16bit_word(cpu, 0x402, 0x0378); /* COM2 */
1752 store_byte(cpu, 0x410, (nfloppies << 6) | 0x0f); /* nfloppies etc */
1753 store_byte(cpu, 0x411, 2 << 1); /* nserials etc */
1754 store_16bit_word(cpu, 0x413, 640); /* KB of low RAM */
1755 store_byte(cpu, 0x449, cpu->machine->md.pc.videomode); /* video mode */
1756 store_16bit_word(cpu, 0x44a, cpu->machine->md.pc.columns);/* columns */
1757 store_16bit_word(cpu, 0x463, 0x3D4); /* CRT base port */
1758 store_byte(cpu, 0x475, nhds); /* nr of harddisks */
1759 store_byte(cpu, 0x484, cpu->machine->md.pc.rows-1);/* nr of lines-1 */
1760 store_byte(cpu, 0x485, 16); /* font height */
1761
1762 /* Registers passed to the bootsector code: */
1763 reload_segment_descriptor(cpu, X86_S_CS, 0x0000, NULL);
1764 reload_segment_descriptor(cpu, X86_S_DS, 0x0000, NULL);
1765 reload_segment_descriptor(cpu, X86_S_ES, 0x0000, NULL);
1766 reload_segment_descriptor(cpu, X86_S_SS, 0x0000, NULL);
1767
1768 cpu->cd.x86.r[X86_R_AX] = 0xaa55;
1769 cpu->cd.x86.r[X86_R_CX] = 0x0001;
1770 cpu->cd.x86.r[X86_R_DI] = 0xffe4;
1771 cpu->cd.x86.r[X86_R_SP] = 0xfffe;
1772 cpu->cd.x86.r[X86_R_DX] = bios_boot_id;
1773
1774 cpu->cd.x86.rflags |= X86_FLAGS_IF;
1775 cpu->pc = 0x7c00;
1776
1777 cpu->machine->md.pc.initialized = 1;
1778
1779 cpu->cd.x86.cursegment = old_cursegment;
1780 }
1781
1782
1783 /*
1784 * pc_bios_emul():
1785 */
1786 int pc_bios_emul(struct cpu *cpu)
1787 {
1788 uint32_t addr = (cpu->cd.x86.s[X86_S_CS] << 4) + cpu->pc;
1789 int int_nr, flags;
1790 int enable_ints_after_return = 0;
1791 unsigned char w[2];
1792
1793 if (addr == 0xffff0) {
1794 fatal("[ bios reboot ]\n");
1795 cpu->running = 0;
1796 return 0;
1797 }
1798
1799 int_nr = (addr >> 4) & 0xff;
1800
1801 if (cpu->cd.x86.cr[0] & X86_CR0_PE) {
1802 fatal("TODO: BIOS interrupt 0x%02x, but we're not in real-"
1803 "mode?\n", int_nr);
1804 cpu->running = 0;
1805 return 0;
1806 }
1807
1808 switch (int_nr) {
1809 case 0x02: /* NMI? */
1810 debug("[ pc_bios: NMI? TODO ]\n");
1811 break;
1812 case 0x08:
1813 if (pc_bios_int8(cpu) == 0)
1814 return 0;
1815 break;
1816 case 0x09: pc_bios_int9(cpu); break;
1817 case 0x10: pc_bios_int10(cpu); break;
1818 case 0x11:
1819 /* return bios equipment data in ax */
1820 cpu->memory_rw(cpu, cpu->mem, 0x410, &w[0], sizeof(w),
1821 MEM_READ, CACHE_NONE | PHYSICAL);
1822 cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
1823 cpu->cd.x86.r[X86_R_AX] |= (w[1] << 8) | w[0];
1824 break;
1825 case 0x12: /* return memory size in KBs */
1826 cpu->cd.x86.r[X86_R_AX] = 640;
1827 break;
1828 case 0x13:
1829 pc_bios_int13(cpu);
1830 enable_ints_after_return = 1;
1831 break;
1832 case 0x14: pc_bios_int14(cpu); break;
1833 case 0x15: pc_bios_int15(cpu); break;
1834 case 0x16:
1835 if (pc_bios_int16(cpu, &enable_ints_after_return) == 0) {
1836 if (enable_ints_after_return)
1837 cpu->cd.x86.rflags |= X86_FLAGS_IF;
1838 return 0;
1839 }
1840 break;
1841 case 0x17: pc_bios_int17(cpu); break;
1842 case 0x18:
1843 pc_bios_printstr(cpu, "Disk boot failed. (INT 0x18 called.)\n",
1844 0x07);
1845 cpu->running = 0;
1846 break;
1847 case 0x19:
1848 pc_bios_printstr(cpu, "Rebooting. (INT 0x19 called.)\n", 0x07);
1849 cpu->running = 0;
1850 break;
1851 case 0x1a: pc_bios_int1a(cpu); break;
1852 case 0x1c: pc_bios_int1c(cpu); break;
1853 default:
1854 fatal("FATAL: Unimplemented PC BIOS interrupt 0x%02x.\n",
1855 int_nr);
1856 cpu->running = 0;
1857 return 0;
1858 }
1859
1860 /*
1861 * Return from the interrupt: Pop ip (pc), cs, and flags.
1862 */
1863 cpu->cd.x86.cursegment = X86_S_SS;
1864 cpu->pc = load_16bit_word(cpu, cpu->cd.x86.r[X86_R_SP]);
1865 reload_segment_descriptor(cpu, X86_S_CS,
1866 load_16bit_word(cpu, cpu->cd.x86.r[X86_R_SP] + 2), NULL);
1867
1868 /* Actually, don't pop flags, because they contain result bits
1869 from interrupt calls. Only pop the Interrupt Flag. */
1870 flags = load_16bit_word(cpu, cpu->cd.x86.r[X86_R_SP] + 4);
1871 cpu->cd.x86.rflags &= ~X86_FLAGS_IF;
1872 cpu->cd.x86.rflags |= (flags & X86_FLAGS_IF);
1873
1874 if (enable_ints_after_return)
1875 cpu->cd.x86.rflags |= X86_FLAGS_IF;
1876
1877 cpu->cd.x86.r[X86_R_SP] = (cpu->cd.x86.r[X86_R_SP] & ~0xffff)
1878 | ((cpu->cd.x86.r[X86_R_SP] + 6) & 0xffff);
1879
1880 return 1;
1881 }
1882
1883
1884 #endif /* ENABLE_X86 */

  ViewVC Help
Powered by ViewVC 1.1.26