/[gxemul]/trunk/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 /trunk/src/promemul/pc_bios.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 30 - (show annotations)
Mon Oct 8 16:20:40 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 54080 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1325 2006/08/15 15:38:37 debug Exp $
20060723	More Transputer instructions (pfix, nfix, opr, mint, ldl, ldlp,
		eqc, rev, ajw, stl, stlf, sthf, sub, ldnl, ldnlp, ldpi, move,
		wcnt, add, bcnt).
		Adding more SPARC instructions (andcc, addcc, bl, rdpr).
		Progress on the igsfb framebuffer used by NetBSD/netwinder.
		Enabling 8-bit fills in dev_fb.
		NetBSD/netwinder 3.0.1 can now run from a disk image :-)
20060724	Cleanup/performance fix for 64-bit virtual translation table
		updates (by removing the "timestamp" stuff). A full NetBSD/pmax
		3.0.1 install for R4400 has dropped from 667 seconds to 584 :)
		Fixing the igsfb "almost vga" color (it is 24-bit, not 18-bit).
		Adding some MIPS instruction combinations (3*lw, and 3*addu).
		The 8048 keyboard now turns off interrupt enable between the
		KBR_ACK and the KBR_RSTDONE, to work better with Linux 2.6.
		Not causing PPC DEC interrupts if PPC_NO_DEC is set for a
		specific CPU; NetBSD/bebox gets slightly further than before.
		Adding some more SPARC instructions: branches, udiv.
20060725	Refreshing dev_pckbc.c a little.
		Cleanups for the SH emulation mode, and adding the first
		"compact" (16-bit) instructions: various simple movs, nop,
		shll, stc, or, ldc.
20060726	Adding dummy "pcn" (AMD PCnet NIC) PCI glue.
20060727	Various cleanups; removing stuff from cpu.h, such as
		running_translated (not really meaningful anymore), and
		page flags (breaking into the debugger clears all translations
		anyway).
		Minor MIPS instruction combination updates.
20060807	Expanding the 3*sw and 3*lw MIPS instruction combinations to
		work with 2* and 4* too, resulting in a minor performance gain.
		Implementing a usleep hack for the RM52xx/MIPS32/MIPS64 "wait"
		instruction (when emulating 1 cpu).
20060808	Experimenting with some more MIPS instruction combinations.
		Implementing support for showing a (hardcoded 12x22) text
		cursor in igsfb.
20060809	Simplifying the NetBSD/evbmips (Malta) install instructions
		somewhat (by using a NetBSD/pmax ramdisk install kernel).
20060812	Experimenting more with the MIPS 'wait' instruction.
		PCI configuration register writes can now be handled, which
		allow PCI IDE controllers to work with NetBSD/Malta 3.0.1 and
		NetBSD/cobalt 3.0.1. (Previously only NetBSD 2.1 worked.)
20060813	Updating dev_gt.c based on numbers from Alec Voropay, to enable
		Linux 2.6 to use PCI on Malta.
		Continuing on Algor interrupt stuff.
20060814	Adding support for routing ISA interrupts to two different
		interrupts, making it possible to run NetBSD/algor :-)
20060814-15	Testing for the release.

==============  RELEASE 0.4.2  ==============


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