/[gxemul]/trunk/src/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

Annotation of /trunk/src/pc_bios.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10 - (hide annotations)
Mon Oct 8 16:18:27 2007 UTC (16 years, 5 months ago) by dpavlin
File MIME type: text/plain
File size: 54068 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.815 2005/06/27 23:04:35 debug Exp $
20050617	Experimenting some more with netbooting OpenBSD/sgi. Adding
		a hack which allows emulated ethernet networks to be
		distributed across multiple emulator processes.
20050618	Minor updates (documentation, dummy YAMON emulation, etc).
20050620	strcpy/strcat -> strlcpy/strlcat updates.
		Some more progress on evbmips (Malta).
20050621	Adding a section to doc/configfiles.html about ethernet
		emulation across multiple hosts.
		Beginning the work on the ARM translation engine (using the
		dynamic-but-not-binary translation method).
		Fixing a bintrans bug: 0x9fc00000 should always be treated as
		PROM area, just as 0xbfc00000 is.
		Minor progress on Malta emulation (the PCI-ISA bus).
20050622	NetBSD/evbmips can now be installed (using another emulated
		machine) and run (including userland and so on). :-)
		Spliting up the bintrans haddr_entry field into two (one for
		read, one for write). Probably not much of a speed increase,
		though.
		Updating some NetBSD 2.0 -> 2.0.2 in the documentation.
20050623	Minor updates (documentation, the TODO file, etc).
		gzipped kernels are now always automagically gunzipped when
		loaded.
20050624	Adding a dummy Playstation Portable (PSP) mode, just barely
		enough to run Hello World (in weird colors :-).
		Removing the -b command line option; old bintrans is enabled
		by default instead. It makes more sense.
		Trying to finally fix the non-working performance measurement
		thing (instr/second etc).
20050625	Continuing on the essential basics for ARM emulation. Two
		instructions seem to work, a branch and a simple "mov". (The
		mov arguments are not correct yet.) Performance is definitely
		reasonable.
		Various other minor updates.
		Adding the ARM "bl" instruction.
		Adding support for combining multiple ARM instructions into one
		function call. ("mov" + "mov" is the only one implemented so
		far, but it seems to work.)
		Cleaning up some IP32 interrupt things (crime/mace); disabling
		the PS/2 keyboard controller on IP32, so that NetBSD/sgimips
		boots into userland again.
20050626	Finally! NetBSD/sgimips netboots. Adding instructions to
		doc/guestoses.html on how to set up an nfs server etc.
		Various other minor fixes.
		Playstation Portable ".pbp" files can now be used directly.
		(The ELF part of the .pbp is extracted transparently.)
		Converting some sprintf -> snprintf.
		Adding some more instructions to the ARM disassembler.
20050627	More ARM updates. Adding some simple ldr(b), str(b),
		cmps, and conditional branch instructions, enough to run
		a simple Hello World program.
		All ARM instructions are now inlined/generated for all possible
		condition codes.
		Adding add and sub, and more load/store instructions.
		Removing dummy files: cpu_alpha.c, cpu_hppa.c, and cpu_sparc.c.
		Some minor documentation updates; preparing for a 0.3.4
		release. Updating some URLs.

==============  RELEASE 0.3.4  ==============


1 dpavlin 4 /*
2     * Copyright (C) 2005 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 dpavlin 10 * $Id: pc_bios.c,v 1.98 2005/06/26 11:36:28 debug Exp $
29 dpavlin 4 *
30     * Generic PC BIOS emulation.
31 dpavlin 6 *
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 dpavlin 4 */
50    
51     #include <stdio.h>
52     #include <stdlib.h>
53     #include <string.h>
54 dpavlin 6 #include <time.h>
55 dpavlin 4
56 dpavlin 6 #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 dpavlin 4 #include "console.h"
73     #include "cpu_x86.h"
74 dpavlin 6 #include "devices.h"
75     #include "diskimage.h"
76 dpavlin 4 #include "machine.h"
77     #include "memory.h"
78    
79    
80     extern int quiet_mode;
81    
82 dpavlin 6 extern unsigned char font8x8[];
83 dpavlin 4
84 dpavlin 6 #define dec_to_bcd(x) ( (((x) / 10) << 4) + ((x) % 10) )
85    
86    
87 dpavlin 4 /*
88 dpavlin 6 * 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 dpavlin 4 * output_char():
127     */
128     static void output_char(struct cpu *cpu, int x, int y, int ch, int color)
129     {
130 dpavlin 6 uint64_t addr = (y * cpu->machine->md.pc.columns + x) * 2 + 0xb8000;
131 dpavlin 4 unsigned char w[2];
132 dpavlin 6 int len = 2;
133 dpavlin 4
134     w[0] = ch; w[1] = color;
135 dpavlin 6 if (color < 0)
136     len = 1;
137    
138     cpu->memory_rw(cpu, cpu->mem, addr, &w[0], len, MEM_WRITE,
139 dpavlin 4 CACHE_NONE | PHYSICAL);
140     }
141    
142    
143     /*
144 dpavlin 6 * 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 dpavlin 4 * set_cursor_pos():
160     */
161     static void set_cursor_pos(struct cpu *cpu, int x, int y)
162     {
163 dpavlin 6 int addr = y * cpu->machine->md.pc.columns + x;
164 dpavlin 4 unsigned char byte;
165 dpavlin 6 uint64_t ctrlregs = X86_IO_BASE + 0x3c0;
166 dpavlin 4
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 dpavlin 6 * 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->md.pc.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->md.pc.pic1->isr &= ~0x02;
563     }
564    
565    
566     /*
567 dpavlin 4 * pc_bios_int10():
568     *
569     * Video functions.
570     */
571     static void pc_bios_int10(struct cpu *cpu)
572     {
573 dpavlin 6 uint64_t ctrlregs = X86_IO_BASE + 0x3c0;
574     unsigned char byte;
575     unsigned char rgb[3];
576     int x,y, oldx,oldy;
577 dpavlin 4 int ah = (cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff;
578     int al = cpu->cd.x86.r[X86_R_AX] & 0xff;
579 dpavlin 6 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 dpavlin 4
589     switch (ah) {
590     case 0x00: /* Switch video mode. */
591 dpavlin 6 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 dpavlin 4 switch (al) {
658 dpavlin 6 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 dpavlin 4 break;
666 dpavlin 6 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 dpavlin 4 cpu->running = 0;
708     }
709     break;
710 dpavlin 6 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 dpavlin 4 default:
814     fatal("FATAL: Unimplemented PC BIOS interrupt 0x10 function"
815     " 0x%02x.\n", ah);
816     cpu->running = 0;
817     cpu->dead = 1;
818     }
819     }
820    
821    
822     /*
823     * pc_bios_int13():
824     *
825 dpavlin 6 * Disk-related functions. These usually return CF on error.
826 dpavlin 4 */
827     static void pc_bios_int13(struct cpu *cpu)
828     {
829 dpavlin 6 struct pc_bios_disk *disk;
830     int res, nread, err;
831 dpavlin 4 int ah = (cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff;
832 dpavlin 6 int al = (cpu->cd.x86.r[X86_R_AX] >> 0) & 0xff;
833     int dh = (cpu->cd.x86.r[X86_R_DX] >> 8) & 0xff;
834     int dl = (cpu->cd.x86.r[X86_R_DX] >> 0) & 0xff;
835     int ch = (cpu->cd.x86.r[X86_R_CX] >> 8) & 0xff;
836     int cl = (cpu->cd.x86.r[X86_R_CX] >> 0) & 0xff;
837     int bx = cpu->cd.x86.r[X86_R_BX] & 0xffff;
838     uint64_t offset;
839 dpavlin 4
840     switch (ah) {
841     case 0x00: /* Reset disk, dl = drive */
842 dpavlin 6 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
843     cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
844 dpavlin 4 /* Do nothing. :-) */
845     break;
846 dpavlin 6 case 0x02: /* Read sector */
847     case 0x03: /* Write sector */
848     /*
849     * Read/Write sector(s). al = nr of sectors
850     * dh = head, dl = disk id (0-based),
851     * ch = cyl, cl = 1-based starting sector nr
852     * es:bx = destination buffer; return carryflag = error
853     */
854     cpu->cd.x86.rflags |= X86_FLAGS_CF;
855     disk = get_disk(cpu->machine, cpu->cd.x86.r[X86_R_DX] & 0xff);
856     if (disk != NULL) {
857     unsigned char *buf;
858    
859     cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
860     ch = ch + ((cl >> 6) << 8);
861     cl = (cl & 0x3f) - 1;
862     offset = (cl + disk->sectorspertrack * dh +
863     disk->sectorspertrack * disk->heads * ch) * 512;
864     nread = 0; err = 0;
865     debug("[ pc_bios_int13(): reading from disk 0x%x, "
866     "CHS=%i,%i,%i ]\n", dl, ch, dh, cl);
867    
868     buf = malloc(512 * al);
869    
870     if (cl+al > disk->sectorspertrack ||
871     dh >= disk->heads || ch > disk->cylinders) {
872     al = 0; err = 4; /* sector not found */
873     fatal("[ pc_bios: attempt to %s outside the d"
874     "isk? bios id=0x%02x, chs=%i,%i,%i, acces"
875     "s at %i,%i,%i ]\n", ah==2? "read" :
876     "write", dl, disk->cylinders, disk->heads,
877     disk->sectorspertrack, ch, dh, cl);
878     }
879    
880     debug("[ pc_bios_int13(): %s biosdisk 0x%02x (offset="
881     "0x%llx) mem=0x%04x:0x%04x ]\n", ah==2? "read from"
882     : "write to", dl, (long long)offset,
883     cpu->cd.x86.s[X86_S_ES], bx);
884    
885     if (ah == 3) {
886     fatal("TODO: bios disk write\n");
887     /* cpu->running = 0; */
888     /* TODO */
889     al = 0;
890     }
891     if (al > 0)
892     res = diskimage_access(cpu->machine, disk->id,
893     disk->type, 0, offset, buf, al * 512);
894     else
895     res = 0;
896     nread = al;
897     if (!res) {
898     err = 4;
899     fatal("[ pc_bios_int13(): FAILED to %s"
900     " biosdisk 0x%02x (offset=0x%llx)"
901     " ]\n", ah==2? "read from" :
902     "write to", dl, (long long)offset);
903     } else if (ah == 2) {
904     cpu->cd.x86.cursegment = X86_S_ES;
905     if (bx + 512*al > 0x10000) {
906     /* DMA overrun */
907     fatal("[ pc_bios: DMA overrun ]\n");
908     err = 9;
909     nread = al = (0x10000 - bx) / 512;
910     }
911     store_buf(cpu, bx, (char *)buf, 512 * al);
912     }
913     free(buf);
914     cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
915     cpu->cd.x86.r[X86_R_AX] |= nread;
916     } else
917     err = 0x80;
918     if (err) {
919     cpu->cd.x86.rflags |= X86_FLAGS_CF;
920     cpu->cd.x86.r[X86_R_AX] |= (err << 8);
921     }
922     break;
923     case 4: /* verify disk sectors */
924     cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
925     cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
926     /* Do nothing. :-) */
927     break;
928     case 8: /* get drive status: TODO */
929     cpu->cd.x86.rflags |= X86_FLAGS_CF;
930     cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
931     cpu->cd.x86.r[X86_R_AX] |= 0x8080;
932     disk = get_disk(cpu->machine, cpu->cd.x86.r[X86_R_DX] & 0xff);
933     if (disk != NULL) {
934     int cyl_hi, cyl_lo;
935    
936     cyl_lo = disk->cylinders & 255;
937     cyl_hi = ((disk->cylinders >> 8) & 3) << 6;
938     cyl_hi |= disk->sectorspertrack;
939    
940     cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
941     cpu->cd.x86.r[X86_R_BX] &= ~0xffff;
942     if (disk->type == DISKIMAGE_FLOPPY)
943     cpu->cd.x86.r[X86_R_BX] |= 4;
944     cpu->cd.x86.r[X86_R_CX] &= ~0xffff;
945     cpu->cd.x86.r[X86_R_CX] |= (cyl_lo << 8) | cyl_hi;
946     cpu->cd.x86.r[X86_R_DX] &= ~0xffff;
947     cpu->cd.x86.r[X86_R_DX] |= 0x01 |
948     ((disk->heads - 1) << 8);
949     /* TODO: dl = nr of drives */
950     /* TODO: es:di? */
951     cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
952     }
953     break;
954     case 0x15: /* Read DASD Type */
955     /* TODO: generalize */
956     cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
957     cpu->cd.x86.r[X86_R_AX] |= 0x0100;
958     cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
959     break;
960     case 0x41: /* Check for Extended Functions */
961     /* There is no such support. :) */
962     cpu->cd.x86.rflags |= X86_FLAGS_CF;
963     break;
964     case 0x42: /* Extended Read: */
965     /* TODO */
966     cpu->cd.x86.rflags |= X86_FLAGS_CF;
967     cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
968     cpu->cd.x86.r[X86_R_AX] |= 0x0100;
969     break;
970     case 0x48: /* ? */
971     /* TODO */
972     cpu->cd.x86.rflags |= X86_FLAGS_CF;
973     break;
974     case 0x4b: /* CDROM emulation (TODO) */
975     cpu->cd.x86.rflags |= X86_FLAGS_CF;
976     break;
977     case 0xfa: /* ? */
978     cpu->cd.x86.rflags |= X86_FLAGS_CF;
979     break;
980 dpavlin 4 default:
981 dpavlin 6 fatal("FATAL: Unimplemented PC BIOS interrupt 0x13 function"
982 dpavlin 4 " 0x%02x.\n", ah);
983     cpu->running = 0;
984     cpu->dead = 1;
985     }
986     }
987    
988    
989     /*
990 dpavlin 6 * pc_bios_int14():
991     *
992     * Serial port stuff.
993     */
994     static void pc_bios_int14(struct cpu *cpu)
995     {
996     int ah = (cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff;
997    
998     switch (ah) {
999     case 0: debug("[ pc_bios_14(): TODO ]\n");
1000     break;
1001     default:
1002     fatal("FATAL: Unimplemented PC BIOS interrupt 0x14 function"
1003     " 0x%02x.\n", ah);
1004     cpu->running = 0;
1005     cpu->dead = 1;
1006     }
1007     }
1008    
1009    
1010     /*
1011     * pc_bios_int15():
1012     */
1013     static void pc_bios_int15(struct cpu *cpu)
1014     {
1015     int ah = (cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff;
1016     int al = cpu->cd.x86.r[X86_R_AX] & 0xff;
1017     int cx = cpu->cd.x86.r[X86_R_CX] & 0xffff;
1018     int si = cpu->cd.x86.r[X86_R_SI] & 0xffff;
1019     int m;
1020     unsigned char src_entry[8];
1021     unsigned char dst_entry[8];
1022     uint32_t src_addr, dst_addr;
1023    
1024     switch (ah) {
1025     case 0x00: /* TODO? */
1026     fatal("[ PC BIOS int 0x15,0x00: TODO ]\n");
1027     cpu->cd.x86.rflags |= X86_FLAGS_CF;
1028     cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1029     cpu->cd.x86.r[X86_R_AX] |= 0x8600; /* TODO */
1030     break;
1031     case 0x06: /* TODO */
1032     fatal("[ PC BIOS int 0x15,0x06: TODO ]\n");
1033     cpu->cd.x86.rflags |= X86_FLAGS_CF;
1034     cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1035     cpu->cd.x86.r[X86_R_AX] |= 0x8600; /* TODO */
1036     break;
1037     case 0x24: /* TODO */
1038     fatal("[ PC BIOS int 0x15,0x24: TODO ]\n");
1039     cpu->cd.x86.rflags |= X86_FLAGS_CF;
1040     cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1041     cpu->cd.x86.r[X86_R_AX] |= 0x8600; /* TODO */
1042     break;
1043     case 0x41: /* TODO */
1044     fatal("[ PC BIOS int 0x15,0x41: TODO ]\n");
1045     cpu->cd.x86.rflags |= X86_FLAGS_CF;
1046     cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1047     cpu->cd.x86.r[X86_R_AX] |= 0x8600; /* TODO */
1048     break;
1049     case 0x4f: /* Keyboard Scancode Intercept (TODO) */
1050     cpu->cd.x86.rflags |= X86_FLAGS_CF;
1051     break;
1052     case 0x53: /* TODO */
1053     fatal("[ PC BIOS int 0x15,0x53: TODO ]\n");
1054     cpu->cd.x86.rflags |= X86_FLAGS_CF;
1055     break;
1056     case 0x86: /* Wait */
1057     /* No. :-) */
1058     cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1059     break;
1060     case 0x87: /* Move to/from extended memory, via a GDT */
1061     cpu->cd.x86.cursegment = X86_S_ES;
1062     cpu->memory_rw(cpu, cpu->mem, si + 0x10, src_entry, 8,
1063     MEM_READ, CACHE_DATA);
1064     cpu->memory_rw(cpu, cpu->mem, si + 0x18, dst_entry, 8,
1065     MEM_READ, CACHE_DATA);
1066     src_addr = src_entry[2]+(src_entry[3]<<8)+(src_entry[4]<<16);
1067     dst_addr = dst_entry[2]+(dst_entry[3]<<8)+(dst_entry[4]<<16);
1068     if (src_entry[5] != 0x92 && src_entry[5] != 0x93)
1069     fatal("WARNING: int15,87: bad src access right?"
1070     " (0x%02x, should be 0x93)\n", src_entry[5]);
1071     if (dst_entry[5] != 0x92 && dst_entry[5] != 0x93)
1072     fatal("WARNING: int15,87: bad dst access right?"
1073     " (0x%02x, should be 0x93)\n", dst_entry[5]);
1074     debug("[ pc_bios: INT15: copying %i bytes from 0x%x to 0x%x"
1075     " ]\n", cx*2, src_addr, dst_addr);
1076     if (cx > 0x8000)
1077     fatal("WARNING! INT15 func 0x87 cx=0x%04x, max allowed"
1078     " is supposed to be 0x8000!\n", cx);
1079     while (cx*2 > 0) {
1080     unsigned char buf[2];
1081     cpu->memory_rw(cpu, cpu->mem, src_addr, buf, 2,
1082     MEM_READ, NO_SEGMENTATION | CACHE_DATA);
1083     cpu->memory_rw(cpu, cpu->mem, dst_addr, buf, 2,
1084     MEM_WRITE, NO_SEGMENTATION | CACHE_DATA);
1085     src_addr += 2; dst_addr += 2; cx --;
1086     }
1087     cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1088     cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1089     cpu->cd.x86.rflags |= X86_FLAGS_ZF;
1090     break;
1091     case 0x88: /* Extended Memory Size Determination */
1092     /* TODO: Max 16 or 64 MB? */
1093     cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1094     cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
1095     if (cpu->machine->physical_ram_in_mb <= 64)
1096     cpu->cd.x86.r[X86_R_AX] |= (cpu->machine->
1097     physical_ram_in_mb - 1) * 1024;
1098     else
1099     cpu->cd.x86.r[X86_R_AX] |= 63*1024;
1100     break;
1101     case 0x8A: /* Get "Big" memory size */
1102     cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1103     m = (cpu->machine->physical_ram_in_mb - 1) * 1024;
1104     cpu->cd.x86.r[X86_R_DX] &= ~0xffff;
1105     cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
1106     cpu->cd.x86.r[X86_R_DX] |= ((m >> 16) & 0xffff);
1107     cpu->cd.x86.r[X86_R_AX] |= (m & 0xffff);
1108     break;
1109     case 0x91: /* Interrupt Complete (bogus) */
1110     cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1111     break;
1112     case 0xc0: /* System Config: (at 0xfffd:0) */
1113     cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1114     cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1115     cpu->cd.x86.r[X86_R_BX] &= ~0xffff;
1116     cpu->cd.x86.s[X86_S_ES] = 0xfffd;
1117     reload_segment_descriptor(cpu, X86_S_ES, 0xfffd, NULL);
1118     break;
1119     case 0xc1: /* Extended Bios Data-seg (TODO) */
1120     cpu->cd.x86.rflags |= X86_FLAGS_CF;
1121     break;
1122     case 0xe8: /* TODO */
1123     switch (al) {
1124     case 0x01:
1125     cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1126     m = cpu->machine->physical_ram_in_mb;
1127     if (m > 16)
1128     m = 16;
1129     m = (m - 1) * 1024;
1130     /* between 1MB and 16MB: (1KB blocks) */
1131     cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
1132     cpu->cd.x86.r[X86_R_AX] |= (m & 0xffff);
1133     /* mem above 16MB, 64K blocks: */
1134     m = cpu->machine->physical_ram_in_mb;
1135     if (m < 16)
1136     m = 0;
1137     else
1138     m = (m-16) / 16;
1139     cpu->cd.x86.r[X86_R_BX] &= ~0xffff;
1140     cpu->cd.x86.r[X86_R_BX] |= (m & 0xffff);
1141     /* CX and DX are "configured" memory */
1142     cpu->cd.x86.r[X86_R_CX] &= ~0xffff;
1143     cpu->cd.x86.r[X86_R_DX] &= ~0xffff;
1144     cpu->cd.x86.r[X86_R_CX] |= (
1145     cpu->cd.x86.r[X86_R_AX] & 0xffff);
1146     cpu->cd.x86.r[X86_R_DX] |= (
1147     cpu->cd.x86.r[X86_R_BX] & 0xffff);
1148     break;
1149     case 0x20: /* Get memory map: TODO */
1150     cpu->cd.x86.rflags |= X86_FLAGS_CF;
1151     cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1152     cpu->cd.x86.r[X86_R_AX] |= 0x8600;
1153     break;
1154     default:fatal("[ PC BIOS int 0x15,0xe8: al=0x%02x "
1155     " TODO ]\n", al);
1156     cpu->running = 0;
1157     }
1158     break;
1159     default:
1160     fatal("FATAL: Unimplemented PC BIOS interrupt 0x15 function"
1161     " 0x%02x.\n", ah);
1162     cpu->running = 0;
1163     cpu->dead = 1;
1164     }
1165     }
1166    
1167    
1168     /*
1169     * pc_bios_int16():
1170     *
1171     * Keyboard-related functions.
1172     */
1173     static int pc_bios_int16(struct cpu *cpu, int *enable_ints_after_returnp)
1174     {
1175     int ah = (cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff;
1176     /* int al = cpu->cd.x86.r[X86_R_AX] & 0xff; */
1177     int scancode, asciicode;
1178     unsigned char tmpchar;
1179    
1180     switch (ah) {
1181     case 0x00: /* getchar */
1182     scancode = asciicode = 0;
1183     if (cpu->machine->md.pc.kbd_buf_head !=
1184     cpu->machine->md.pc.kbd_buf_tail) {
1185     asciicode = cpu->machine->md.pc.kbd_buf[
1186     cpu->machine->md.pc.kbd_buf_head];
1187     scancode = cpu->machine->md.pc.kbd_buf_scancode[
1188     cpu->machine->md.pc.kbd_buf_head];
1189     if (asciicode != 0) {
1190     cpu->cd.x86.r[X86_R_AX] =
1191     (scancode << 8) | asciicode;
1192     }
1193     cpu->machine->md.pc.kbd_buf_head ++;
1194     cpu->machine->md.pc.kbd_buf_head %=
1195     PC_BIOS_KBD_BUF_SIZE;
1196     }
1197     *enable_ints_after_returnp = 1;
1198     if (asciicode == 0)
1199     return 0;
1200     break;
1201     case 0x01: /* non-destructive "isavail" */
1202     cpu->cd.x86.rflags |= X86_FLAGS_ZF;
1203     cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
1204     scancode = asciicode = 0;
1205     if (cpu->machine->md.pc.kbd_buf_head !=
1206     cpu->machine->md.pc.kbd_buf_tail) {
1207     asciicode = cpu->machine->md.pc.kbd_buf[
1208     cpu->machine->md.pc.kbd_buf_head];
1209     scancode = cpu->machine->md.pc.kbd_buf_scancode[
1210     cpu->machine->md.pc.kbd_buf_head];
1211     cpu->cd.x86.rflags &= ~X86_FLAGS_ZF;
1212     cpu->cd.x86.r[X86_R_AX] |= (scancode << 8) | asciicode;
1213     }
1214     *enable_ints_after_returnp = 1;
1215     break;
1216     case 0x02: /* read keyboard flags */
1217     /* TODO: keep this byte updated */
1218     cpu->memory_rw(cpu, cpu->mem, 0x417, &tmpchar, 1,
1219     MEM_READ, PHYSICAL);
1220     cpu->cd.x86.r[X86_R_AX] = (cpu->cd.x86.r[X86_R_AX] & ~0xff)
1221     | tmpchar;
1222     break;
1223     case 0x03: /* Set Keyboard Typematic Rate: TODO */
1224     break;
1225     case 0x55: /* Microsoft stuff: Ignore :-) */
1226     break;
1227     case 0x92: /* Keyboard "Capabilities Check": TODO */
1228     break;
1229     default:
1230     fatal("FATAL: Unimplemented PC BIOS interrupt 0x16 function"
1231     " 0x%02x.\n", ah);
1232     cpu->running = 0;
1233     cpu->dead = 1;
1234     }
1235    
1236     return 1;
1237     }
1238    
1239    
1240     /*
1241     * pc_bios_int17():
1242     *
1243     * Printer port stuff.
1244     */
1245     static void pc_bios_int17(struct cpu *cpu)
1246     {
1247     int ah = (cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff;
1248    
1249     switch (ah) {
1250     case 0x01:
1251     debug("[ PC BIOS int 0x17,0x01: TODO ]\n");
1252     cpu->cd.x86.r[X86_R_AX] &= ~0xff00;
1253     break;
1254     default:
1255     fatal("FATAL: Unimplemented PC BIOS interrupt 0x17 function"
1256     " 0x%02x.\n", ah);
1257     cpu->running = 0;
1258     cpu->dead = 1;
1259     }
1260     }
1261    
1262    
1263     /*
1264 dpavlin 4 * pc_bios_int1a():
1265     *
1266     * Time of Day stuff.
1267     */
1268     static void pc_bios_int1a(struct cpu *cpu)
1269     {
1270 dpavlin 6 unsigned char ticks[4];
1271 dpavlin 4 int ah = (cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff;
1272 dpavlin 6 time_t tim;
1273     struct tm *tm;
1274 dpavlin 4
1275     switch (ah) {
1276 dpavlin 6 case 0x00: /* Read tick count. */
1277     cpu->memory_rw(cpu, cpu->mem, 0x46C,
1278     ticks, sizeof(ticks), MEM_READ, CACHE_NONE | PHYSICAL);
1279     cpu->cd.x86.r[X86_R_CX] = (ticks[3] << 8) | ticks[2];
1280     cpu->cd.x86.r[X86_R_DX] = (ticks[1] << 8) | ticks[0];
1281 dpavlin 4 break;
1282 dpavlin 6 case 0x01: /* Set tick count. */
1283     ticks[0] = cpu->cd.x86.r[X86_R_DX];
1284     ticks[1] = cpu->cd.x86.r[X86_R_DX] >> 8;
1285     ticks[2] = cpu->cd.x86.r[X86_R_CX];
1286     ticks[3] = cpu->cd.x86.r[X86_R_CX] >> 8;
1287     cpu->memory_rw(cpu, cpu->mem, 0x46C,
1288     ticks, sizeof(ticks), MEM_WRITE, CACHE_NONE | PHYSICAL);
1289     break;
1290     case 0x02: /* Read real time clock time (AT,PS/2) */
1291     tim = time(NULL);
1292     tm = gmtime(&tim);
1293     cpu->cd.x86.r[X86_R_CX] &= ~0xffff;
1294     cpu->cd.x86.r[X86_R_DX] &= ~0xffff;
1295     cpu->cd.x86.r[X86_R_CX] |= (dec_to_bcd(tm->tm_hour) << 8) |
1296     dec_to_bcd(tm->tm_min);
1297     cpu->cd.x86.r[X86_R_DX] |= dec_to_bcd(tm->tm_sec) << 8;
1298     cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1299     break;
1300     case 0x04: /* Read real time clock date (AT,PS/2) */
1301     tim = time(NULL);
1302     tm = gmtime(&tim);
1303     cpu->cd.x86.r[X86_R_CX] &= ~0xffff;
1304     cpu->cd.x86.r[X86_R_DX] &= ~0xffff;
1305     cpu->cd.x86.r[X86_R_CX] |=
1306     (dec_to_bcd((tm->tm_year+1900)/100) << 8) |
1307     dec_to_bcd(tm->tm_year % 100);
1308     cpu->cd.x86.r[X86_R_DX] |= (dec_to_bcd(tm->tm_mon+1) << 8) |
1309     dec_to_bcd(tm->tm_mday);
1310     cpu->cd.x86.rflags &= ~X86_FLAGS_CF;
1311     break;
1312     case 0xb1: /* Intel PCI Bios */
1313     /* ... not installed :) */
1314     cpu->cd.x86.rflags |= X86_FLAGS_CF;
1315     break;
1316 dpavlin 4 default:
1317     fatal("FATAL: Unimplemented PC BIOS interrupt 0x1a function"
1318     " 0x%02x.\n", ah);
1319     cpu->running = 0;
1320     cpu->dead = 1;
1321     }
1322     }
1323    
1324    
1325     /*
1326 dpavlin 6 * pc_bios_int1c():
1327     *
1328     * Increase the timer-tick word at 0x40:0x6C.
1329     */
1330     static void pc_bios_int1c(struct cpu *cpu)
1331     {
1332     unsigned char ticks[4];
1333     int i;
1334    
1335     /* Increase word at 0x0040:0x006C */
1336     cpu->memory_rw(cpu, cpu->mem, 0x46C,
1337     ticks, sizeof(ticks), MEM_READ, CACHE_NONE | PHYSICAL);
1338     for (i=0; i<sizeof(ticks); i++) {
1339     ticks[i] ++;
1340     if (ticks[i] != 0)
1341     break;
1342     }
1343     cpu->memory_rw(cpu, cpu->mem, 0x46C,
1344     ticks, sizeof(ticks), MEM_WRITE, CACHE_NONE | PHYSICAL);
1345     }
1346    
1347    
1348     /*
1349     * pc_bios_smp_init():
1350     *
1351     * Initialize the "_MP_" struct in BIOS memory.
1352     *
1353     * TODO: Don't use hardcoded values like this.
1354     */
1355     void pc_bios_smp_init(struct cpu *cpu)
1356     {
1357     int i, chksum;
1358    
1359     reload_segment_descriptor(cpu, X86_S_FS, 0xf000, NULL);
1360     store_buf(cpu, 0x9000, "_MP_", 4);
1361     store_byte(cpu, 0x9004, 0x10); /* ptr to table */
1362     store_byte(cpu, 0x9005, 0x90);
1363     store_byte(cpu, 0x9006, 0x0f);
1364     store_byte(cpu, 0x9007, 0x00);
1365     store_byte(cpu, 0x9008, 0x01); /* length. should be 1 */
1366     store_byte(cpu, 0x9009, 0x04); /* version. 4 means "1.4" */
1367     /* Byte at 0x0a is checksum. TODO: make this automagic */
1368     chksum = '_' + 'M' + 'P' + '_' + 0x10 + 0x90 + 0xf + 1 + 4;
1369     store_byte(cpu, 0x900a, 0 - chksum);
1370    
1371     /* PCMP struct, at addr 0x9010. */
1372     store_buf(cpu, 0x9010, "PCMP", 4);
1373     store_16bit_word(cpu, 0x9014, 43);
1374     store_byte(cpu, 0x9016, 4); /* rev 1.4 */
1375     /* 9017 is checksum */
1376     store_buf(cpu, 0x9018, "GXemul ", 8);
1377     store_buf(cpu, 0x9020, "SMP ", 12);
1378    
1379     /* Nr of entries (one per cpu): */
1380     store_16bit_word(cpu, 0x9010 + 34, cpu->machine->ncpus);
1381    
1382     if (cpu->machine->ncpus > 16)
1383     fatal("WARNING: More than 16 CPUs?\n");
1384    
1385     for (i=0; i<cpu->machine->ncpus; i++) {
1386     int ofs = 44 + 20*i;
1387     /* 20 bytes per CPU: */
1388     store_byte(cpu, 0x9010 + ofs + 0, 0x00); /* cpu */
1389     store_byte(cpu, 0x9010 + ofs + 1, i); /* id */
1390     store_byte(cpu, 0x9010 + ofs + 3, 1 | /* enable */
1391     ((i == cpu->machine->bootstrap_cpu)? 2 : 0));
1392     }
1393     }
1394    
1395    
1396     /*
1397     * pc_bios_simple_pmode_setup():
1398     *
1399     * This function is called from emul.c before loading a 32-bit or 64-bit ELF.
1400     * Loading ELFs when the emulation is set to 16-bit real mode is not a good
1401     * thing, so this function sets up a simple GDT which maps every 0xZyyyyyyy
1402     * to 0x0yyyyyyy.
1403     *
1404     * 0xf4000 GDT:
1405     * 00 = NULL
1406     * 08 = code
1407     * 10 = data
1408     */
1409     void pc_bios_simple_pmode_setup(struct cpu *cpu)
1410     {
1411     int i, j, addr = 0, npts;
1412     uint32_t pt_base;
1413     cpu->cd.x86.cursegment = X86_S_FS;
1414     reload_segment_descriptor(cpu, X86_S_FS, 0xf100, NULL);
1415    
1416     /* 0x00 = NULL descriptor. */
1417     addr += 8;
1418    
1419     /* 0x08 = Code descriptor. */
1420     store_byte(cpu, addr + 0, 0xff);
1421     store_byte(cpu, addr + 1, 0xff);
1422     store_byte(cpu, addr + 2, 0x00);
1423     store_byte(cpu, addr + 3, 0x00);
1424     store_byte(cpu, addr + 4, 0x00);
1425     store_byte(cpu, addr + 5, 0x9f);
1426     store_byte(cpu, addr + 6, 0xcf);
1427     store_byte(cpu, addr + 7, 0x00);
1428     addr += 8;
1429    
1430     /* 0x10 = Data descriptor. */
1431     store_byte(cpu, addr + 0, 0xff);
1432     store_byte(cpu, addr + 1, 0xff);
1433     store_byte(cpu, addr + 2, 0x00);
1434     store_byte(cpu, addr + 3, 0x00);
1435     store_byte(cpu, addr + 4, 0x00);
1436     store_byte(cpu, addr + 5, 0x93);
1437     store_byte(cpu, addr + 6, 0xcf);
1438     store_byte(cpu, addr + 7, 0x00);
1439     addr += 8;
1440    
1441     cpu->cd.x86.gdtr = 0xf1000;
1442     cpu->cd.x86.gdtr_limit = 0xfff;
1443    
1444     addr = 0x1000;
1445     cpu->cd.x86.cr[3] = 0xf2000;
1446    
1447     npts = 4;
1448     pt_base = 0xf3000; /* 0xf3000, f4000, f5000, f6000 */
1449    
1450     /* Set up the page directory: */
1451     for (i=0; i<1024; i++) {
1452     uint32_t pde = pt_base + 0x03 + ((i & (npts-1)) << 12);
1453     store_32bit_word(cpu, addr + i*4, pde);
1454     }
1455     addr += 4096;
1456    
1457     /* Set up the page tables: */
1458     for (i=0; i<npts; i++) {
1459     for (j=0; j<1024; j++) {
1460     uint32_t pte = (i << 22) + (j << 12) + 0x03;
1461     store_32bit_word(cpu, addr + j*4, pte);
1462     }
1463     addr += 4096;
1464     }
1465    
1466     cpu->cd.x86.cr[0] |= X86_CR0_PE | X86_CR0_PG;
1467    
1468     /* Interrupts are dangerous when we start in pmode! */
1469     cpu->cd.x86.rflags &= ~X86_FLAGS_IF;
1470    
1471     reload_segment_descriptor(cpu, X86_S_CS, 0x08, NULL);
1472     reload_segment_descriptor(cpu, X86_S_DS, 0x10, NULL);
1473     reload_segment_descriptor(cpu, X86_S_ES, 0x10, NULL);
1474     reload_segment_descriptor(cpu, X86_S_SS, 0x10, NULL);
1475     cpu->cd.x86.r[X86_R_SP] = 0x7000;
1476     cpu->cd.x86.cursegment = X86_S_DS;
1477     }
1478    
1479    
1480     /*
1481     * pc_bios_init():
1482     */
1483     void pc_bios_init(struct cpu *cpu)
1484     {
1485     char t[81];
1486     int x, y, nboxlines, i, any_disk = 0, disknr, tmp;
1487     int boot_id, boot_type, bios_boot_id = 0, nfloppies = 0, nhds = 0;
1488    
1489     /* Go to real mode: */
1490     cpu->cd.x86.cr[0] &= ~X86_CR0_PE;
1491    
1492     boot_id = diskimage_bootdev(cpu->machine, &boot_type);
1493    
1494     if (cpu->machine->md.pc.initialized) {
1495     fatal("ERROR: pc_bios_init(): Already initialized.\n");
1496     return;
1497     }
1498    
1499     if (cpu->machine->md.pc.pic1 == NULL) {
1500     fatal("ERROR: No interrupt controller?\n");
1501     exit(1);
1502     } else
1503     cpu->machine->md.pc.pic1->irq_base = 0x08;
1504    
1505     /* pic2 can be NULL when emulating an original XT: */
1506     if (cpu->machine->md.pc.pic2 != NULL)
1507     cpu->machine->md.pc.pic2->irq_base = 0x70;
1508    
1509     /* Disk Base Table (11 or 12 bytes?) at F000h:EFC7: */
1510     cpu->cd.x86.cursegment = X86_S_FS;
1511     reload_segment_descriptor(cpu, X86_S_FS, 0xf000, NULL);
1512     store_byte(cpu, 0xefc7 + 0, 0xcf);
1513     store_byte(cpu, 0xefc7 + 1, 0xb8);
1514     store_byte(cpu, 0xefc7 + 2, 1); /* timer ticks till shutoff */
1515     store_byte(cpu, 0xefc7 + 3, 2); /* 512 bytes per sector */
1516     store_byte(cpu, 0xefc7 + 4, 17);
1517     store_byte(cpu, 0xefc7 + 5, 0xd8);
1518     store_byte(cpu, 0xefc7 + 6, 0xff);
1519     store_byte(cpu, 0xefc7 + 7, 0);
1520     store_byte(cpu, 0xefc7 + 8, 0xf6);
1521     store_byte(cpu, 0xefc7 + 9, 1); /* head bounce delay in msec */
1522     store_byte(cpu, 0xefc7 + 10, 1);/* motor start time in 1/8 secs */
1523     store_byte(cpu, 0xefc7 + 11, 1);/* motor stop time in 1/4 secs */
1524    
1525     /* BIOS System Configuration Parameters (8 bytes) at 0xfffd:0: */
1526     reload_segment_descriptor(cpu, X86_S_FS, 0xfffd, NULL);
1527     store_byte(cpu, 0, 8); store_byte(cpu, 1, 0); /* len */
1528     store_byte(cpu, 2, 0xfc); /* model */
1529     store_byte(cpu, 3, 0); /* sub-model */
1530     store_byte(cpu, 4, 0); /* bios revision */
1531     store_byte(cpu, 5, 0x60); /* features */
1532     /* see http://members.tripod.com/~oldboard/assembly/
1533     int_15-c0.html for details */
1534    
1535     /* Some info in the last paragraph of the BIOS: */
1536     reload_segment_descriptor(cpu, X86_S_FS, 0xffff, NULL);
1537     /* TODO: current date :-) */
1538     store_byte(cpu, 0x05, '0'); store_byte(cpu, 0x06, '1');
1539     store_byte(cpu, 0x07, '/');
1540     store_byte(cpu, 0x08, '0'); store_byte(cpu, 0x09, '1');
1541     store_byte(cpu, 0x0a, '/');
1542     store_byte(cpu, 0x0b, '0'); store_byte(cpu, 0x0c, '5');
1543     store_byte(cpu, 0x0e, 0xfc);
1544    
1545     /* Copy the first 128 chars of the 8x8 VGA font into 0xf000:0xfa6e */
1546     reload_segment_descriptor(cpu, X86_S_FS, 0xf000, NULL);
1547     store_buf(cpu, 0xfa6e, (char *)font8x8, 8*128);
1548     store_buf(cpu, 0xfa6e - 1024, (char *)font8x8 + 1024, 8*128);
1549    
1550     /*
1551     * Initialize all real-mode interrupt vectors to point to somewhere
1552     * within the PC BIOS area (0xf000:0x8yy0), and place an IRET
1553     * instruction (too fool someone who really reads the BIOS memory).
1554     */
1555     for (i=0; i<256; i++) {
1556     if (i == 0x20)
1557     i = 0x70;
1558     if (i == 0x78)
1559     break;
1560     reload_segment_descriptor(cpu, X86_S_FS, 0x0000, NULL);
1561     store_16bit_word(cpu, i*4, 0x8000 + i*16);
1562     store_16bit_word(cpu, i*4 + 2, 0xf000);
1563    
1564     /* Exceptions: int 0x1e = ptr to disk table, 1f=fonthigh */
1565     if (i == 0x1e)
1566     store_16bit_word(cpu, i*4, 0xefc7);
1567     if (i == 0x1f)
1568     store_16bit_word(cpu, i*4, 0xfa6e - 1024);
1569    
1570     reload_segment_descriptor(cpu, X86_S_FS, 0xf000, NULL);
1571     store_byte(cpu, 0x8000 + i*16, 0xCF); /* IRET */
1572     }
1573    
1574     /* For SMP emulation, create an "MP" struct in BIOS memory: */
1575     if (cpu->machine->ncpus > 1)
1576     pc_bios_smp_init(cpu);
1577    
1578     /* Prepare for text mode: (0x03 = 80x25, 0x01 = 40x25) */
1579     set_video_mode(cpu, 0x03);
1580    
1581     cmos_write(cpu, 0x15, 640 & 255);
1582     cmos_write(cpu, 0x16, 640 >> 8);
1583     tmp = cpu->machine->physical_ram_in_mb / 1024;
1584     if (tmp > 63*1024)
1585     tmp = 63*1024;
1586     cmos_write(cpu, 0x17, tmp & 255);
1587     cmos_write(cpu, 0x18, tmp >> 8);
1588    
1589     /* Clear the screen first: */
1590     set_cursor_pos(cpu, 0, 0);
1591     for (y=0; y<cpu->machine->md.pc.rows; y++)
1592     for (x=0; x<cpu->machine->md.pc.columns; x++)
1593     output_char(cpu, x,y, ' ', 0x07);
1594    
1595     nboxlines = cpu->machine->md.pc.columns <= 40? 4 : 3;
1596    
1597     /* Draw a nice box at the top: */
1598     for (y=0; y<nboxlines; y++)
1599     for (x=0; x<cpu->machine->md.pc.columns; x++) {
1600     unsigned char ch = ' ';
1601     if (cpu->machine->use_x11) {
1602     if (y == 0) {
1603     ch = 196;
1604     if (x == 0)
1605     ch = 218;
1606     if (x == cpu->machine->md.pc.columns-1)
1607     ch = 191;
1608     } else if (y == nboxlines-1) {
1609     ch = 196;
1610     if (x == 0)
1611     ch = 192;
1612     if (x == cpu->machine->md.pc.columns-1)
1613     ch = 217;
1614     } else if (x == 0 || x ==
1615     cpu->machine->md.pc.columns-1)
1616     ch = 179;
1617     } else {
1618     if (y == 0 || y == nboxlines-1) {
1619     ch = '-';
1620     if (x == 0 || x ==
1621     cpu->machine->md.pc.columns-1)
1622     ch = '+';
1623     } else {
1624     if (x == 0 || x ==
1625     cpu->machine->md.pc.columns-1)
1626     ch = '|';
1627     }
1628     }
1629     output_char(cpu, x,y, ch, 0x19);
1630     }
1631    
1632 dpavlin 10 snprintf(t, sizeof(t), "GXemul");
1633 dpavlin 6 #ifdef VERSION
1634 dpavlin 10 snprintf(t + strlen(t), sizeof(t)-strlen(t), " "VERSION);
1635 dpavlin 6 #endif
1636     set_cursor_pos(cpu, 2, 1);
1637     pc_bios_printstr(cpu, t, 0x1f);
1638    
1639 dpavlin 10 snprintf(t, sizeof(t), "%i cpu%s (%s), %i MB memory",
1640 dpavlin 6 cpu->machine->ncpus, cpu->machine->ncpus > 1? "s" : "",
1641     cpu->cd.x86.model.name, cpu->machine->physical_ram_in_mb);
1642     if (cpu->machine->md.pc.columns <= 40)
1643     set_cursor_pos(cpu, 2, 2);
1644     else
1645     set_cursor_pos(cpu, 78 - strlen(t), 1);
1646     pc_bios_printstr(cpu, t, 0x17);
1647     if (cpu->machine->md.pc.columns <= 40)
1648     set_cursor_pos(cpu, 0, 5);
1649     else
1650     set_cursor_pos(cpu, 0, 4);
1651    
1652     cpu->machine->md.pc.curcolor = 0x07;
1653    
1654     /* "Detect" Floppies, IDE disks, and SCSI disks: */
1655     for (i=0; i<4; i++) {
1656     if (diskimage_exist(cpu->machine, i, DISKIMAGE_FLOPPY)) {
1657     struct pc_bios_disk *p;
1658     p = add_disk(cpu->machine, i, i, DISKIMAGE_FLOPPY);
1659 dpavlin 10 snprintf(t, sizeof(t), "%c%c", i<2? ('A'+i):' ',
1660     i<2? ':':' ');
1661 dpavlin 6 pc_bios_printstr(cpu, t, 0xf);
1662     if (i < 2)
1663     nfloppies ++;
1664 dpavlin 10 snprintf(t, sizeof(t), " (bios disk %02x) FLOPPY", i);
1665 dpavlin 6 pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1666 dpavlin 10 snprintf(t, sizeof(t), ", %i KB", (int)(p->size/1024));
1667 dpavlin 6 pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1668     if (cpu->machine->md.pc.columns <= 40)
1669     pc_bios_printstr(cpu, "\n ", 0x07);
1670 dpavlin 10 snprintf(t, sizeof(t), " (CHS=%i,%i,%i)", p->cylinders,
1671     p->heads, p->sectorspertrack);
1672 dpavlin 6 pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1673     if (boot_id == i && boot_type == DISKIMAGE_FLOPPY) {
1674     bios_boot_id = i;
1675     pc_bios_printstr(cpu, " [boot device]", 0xf);
1676     }
1677     pc_bios_printstr(cpu, "\n",
1678     cpu->machine->md.pc.curcolor);
1679     any_disk = 1;
1680     }
1681     }
1682     disknr = 0x80;
1683     for (i=0; i<8; i++) {
1684     if (diskimage_exist(cpu->machine, i, DISKIMAGE_IDE)) {
1685     struct pc_bios_disk *p;
1686     p = add_disk(cpu->machine, disknr, i, DISKIMAGE_IDE);
1687 dpavlin 10 snprintf(t, sizeof(t), "%s", disknr==0x80? "C:" : " ");
1688 dpavlin 6 pc_bios_printstr(cpu, t, 0xf);
1689     nhds ++;
1690 dpavlin 10 snprintf(t, sizeof(t),
1691     " (bios disk %02x) IDE %s, id %i",
1692 dpavlin 6 disknr, diskimage_is_a_cdrom(cpu->machine, i,
1693     DISKIMAGE_IDE)? "cdrom" : (
1694     diskimage_is_a_tape(cpu->machine, i,
1695     DISKIMAGE_IDE)? "tape" : "disk"),
1696     i);
1697     pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1698     if (cpu->machine->md.pc.columns <= 40)
1699     pc_bios_printstr(cpu, "\n ", 0x07);
1700     else
1701     pc_bios_printstr(cpu, ", ",
1702     cpu->machine->md.pc.curcolor);
1703 dpavlin 10 snprintf(t, sizeof(t), "%lli MB", (long long)
1704     (p->size >> 20));
1705 dpavlin 6 pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1706     if (boot_id == i && boot_type == DISKIMAGE_IDE) {
1707     bios_boot_id = disknr;
1708     pc_bios_printstr(cpu, " [boot device]", 0xf);
1709     }
1710     pc_bios_printstr(cpu, "\n",
1711     cpu->machine->md.pc.curcolor);
1712     disknr++;
1713     any_disk = 1;
1714     }
1715     }
1716     for (i=0; i<8; i++) {
1717     if (diskimage_exist(cpu->machine, i, DISKIMAGE_SCSI)) {
1718     struct pc_bios_disk *p;
1719     p = add_disk(cpu->machine, disknr, i, DISKIMAGE_SCSI);
1720 dpavlin 10 snprintf(t, sizeof(t), "%s", disknr==0x80? "C:" : " ");
1721 dpavlin 6 pc_bios_printstr(cpu, t, 0xf);
1722     nhds ++;
1723 dpavlin 10 snprintf(t, sizeof(t),
1724     " (bios disk %02x) SCSI disk, id %i", disknr, i);
1725 dpavlin 6 pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1726     if (cpu->machine->md.pc.columns <= 40)
1727     pc_bios_printstr(cpu, "\n ", 0x07);
1728     else
1729     pc_bios_printstr(cpu, ", ",
1730     cpu->machine->md.pc.curcolor);
1731 dpavlin 10 snprintf(t, sizeof(t), "%lli MB", (long long)
1732     (p->size >> 20));
1733 dpavlin 6 pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1734     if (boot_id == i && boot_type == DISKIMAGE_SCSI) {
1735     bios_boot_id = disknr;
1736     pc_bios_printstr(cpu, " [boot device]", 0xf);
1737     }
1738     pc_bios_printstr(cpu, "\n",
1739     cpu->machine->md.pc.curcolor);
1740     disknr++;
1741     any_disk = 1;
1742     }
1743     }
1744    
1745     if (any_disk)
1746     pc_bios_printstr(cpu, "\n", cpu->machine->md.pc.curcolor);
1747     else
1748     pc_bios_printstr(cpu, "No disks attached!\n\n", 0x0f);
1749    
1750     /* See http://members.tripod.com/~oldboard/assembly/bios_data_area.html
1751     for more info. */
1752     if (nfloppies > 0)
1753     nfloppies --;
1754    
1755     reload_segment_descriptor(cpu, X86_S_FS, 0x0000, NULL);
1756     store_16bit_word(cpu, 0x400, 0x03F8); /* COM1 */
1757     store_16bit_word(cpu, 0x402, 0x0378); /* COM2 */
1758     store_byte(cpu, 0x410, (nfloppies << 6) | 0x0f); /* nfloppies etc */
1759     store_byte(cpu, 0x411, 2 << 1); /* nserials etc */
1760     store_16bit_word(cpu, 0x413, 640); /* KB of low RAM */
1761     store_byte(cpu, 0x449, cpu->machine->md.pc.videomode); /* video mode */
1762     store_16bit_word(cpu, 0x44a, cpu->machine->md.pc.columns);/* columns */
1763     store_16bit_word(cpu, 0x463, 0x3D4); /* CRT base port */
1764     store_byte(cpu, 0x475, nhds); /* nr of harddisks */
1765     store_byte(cpu, 0x484, cpu->machine->md.pc.rows-1);/* nr of lines-1 */
1766     store_byte(cpu, 0x485, 16); /* font height */
1767    
1768     /* Registers passed to the bootsector code: */
1769     reload_segment_descriptor(cpu, X86_S_CS, 0x0000, NULL);
1770     reload_segment_descriptor(cpu, X86_S_DS, 0x0000, NULL);
1771     reload_segment_descriptor(cpu, X86_S_ES, 0x0000, NULL);
1772     reload_segment_descriptor(cpu, X86_S_SS, 0x0000, NULL);
1773    
1774     cpu->cd.x86.r[X86_R_AX] = 0xaa55;
1775     cpu->cd.x86.r[X86_R_CX] = 0x0001;
1776     cpu->cd.x86.r[X86_R_DI] = 0xffe4;
1777     cpu->cd.x86.r[X86_R_SP] = 0xfffe;
1778     cpu->cd.x86.r[X86_R_DX] = bios_boot_id;
1779    
1780     cpu->cd.x86.rflags |= X86_FLAGS_IF;
1781     cpu->pc = 0x7c00;
1782    
1783     cpu->machine->md.pc.initialized = 1;
1784     }
1785    
1786    
1787     /*
1788 dpavlin 4 * pc_bios_emul():
1789     */
1790     int pc_bios_emul(struct cpu *cpu)
1791     {
1792     uint32_t addr = (cpu->cd.x86.s[X86_S_CS] << 4) + cpu->pc;
1793 dpavlin 6 int int_nr, flags;
1794     int enable_ints_after_return = 0;
1795     unsigned char w[2];
1796 dpavlin 4
1797 dpavlin 6 if (addr == 0xffff0) {
1798     fatal("[ bios reboot ]\n");
1799     cpu->running = 0;
1800     return 0;
1801     }
1802 dpavlin 4
1803 dpavlin 6 int_nr = (addr >> 4) & 0xff;
1804    
1805     if (cpu->cd.x86.cr[0] & X86_CR0_PE) {
1806     fatal("TODO: BIOS interrupt 0x%02x, but we're not in real-"
1807     "mode?\n", int_nr);
1808     cpu->running = 0;
1809     return 0;
1810     }
1811    
1812 dpavlin 4 switch (int_nr) {
1813 dpavlin 6 case 0x02: /* NMI? */
1814     debug("[ pc_bios: NMI? TODO ]\n");
1815 dpavlin 4 break;
1816 dpavlin 6 case 0x08:
1817     if (pc_bios_int8(cpu) == 0)
1818     return 0;
1819     break;
1820     case 0x09: pc_bios_int9(cpu); break;
1821     case 0x10: pc_bios_int10(cpu); break;
1822     case 0x11:
1823     /* return bios equipment data in ax */
1824     cpu->memory_rw(cpu, cpu->mem, 0x410, &w[0], sizeof(w),
1825     MEM_READ, CACHE_NONE | PHYSICAL);
1826     cpu->cd.x86.r[X86_R_AX] &= ~0xffff;
1827     cpu->cd.x86.r[X86_R_AX] |= (w[1] << 8) | w[0];
1828     break;
1829     case 0x12: /* return memory size in KBs */
1830     cpu->cd.x86.r[X86_R_AX] = 640;
1831     break;
1832 dpavlin 4 case 0x13:
1833     pc_bios_int13(cpu);
1834 dpavlin 6 enable_ints_after_return = 1;
1835 dpavlin 4 break;
1836 dpavlin 6 case 0x14: pc_bios_int14(cpu); break;
1837     case 0x15: pc_bios_int15(cpu); break;
1838     case 0x16:
1839     if (pc_bios_int16(cpu, &enable_ints_after_return) == 0) {
1840     if (enable_ints_after_return)
1841     cpu->cd.x86.rflags |= X86_FLAGS_IF;
1842     return 0;
1843     }
1844 dpavlin 4 break;
1845 dpavlin 6 case 0x17: pc_bios_int17(cpu); break;
1846     case 0x18:
1847     pc_bios_printstr(cpu, "Disk boot failed. (INT 0x18 called.)\n",
1848     0x07);
1849     cpu->running = 0;
1850     break;
1851     case 0x19:
1852     pc_bios_printstr(cpu, "Rebooting. (INT 0x19 called.)\n", 0x07);
1853     cpu->running = 0;
1854     break;
1855     case 0x1a: pc_bios_int1a(cpu); break;
1856     case 0x1c: pc_bios_int1c(cpu); break;
1857 dpavlin 4 default:
1858     fatal("FATAL: Unimplemented PC BIOS interrupt 0x%02x.\n",
1859     int_nr);
1860     cpu->running = 0;
1861     cpu->dead = 1;
1862 dpavlin 6 return 0;
1863 dpavlin 4 }
1864    
1865     /*
1866     * Return from the interrupt: Pop ip (pc), cs, and flags.
1867     */
1868 dpavlin 6 cpu->cd.x86.cursegment = X86_S_SS;
1869 dpavlin 4 cpu->pc = load_16bit_word(cpu, cpu->cd.x86.r[X86_R_SP]);
1870 dpavlin 6 reload_segment_descriptor(cpu, X86_S_CS,
1871     load_16bit_word(cpu, cpu->cd.x86.r[X86_R_SP] + 2), NULL);
1872 dpavlin 4
1873 dpavlin 6 /* Actually, don't pop flags, because they contain result bits
1874     from interrupt calls. Only pop the Interrupt Flag. */
1875     flags = load_16bit_word(cpu, cpu->cd.x86.r[X86_R_SP] + 4);
1876     cpu->cd.x86.rflags &= ~X86_FLAGS_IF;
1877     cpu->cd.x86.rflags |= (flags & X86_FLAGS_IF);
1878    
1879     if (enable_ints_after_return)
1880     cpu->cd.x86.rflags |= X86_FLAGS_IF;
1881    
1882 dpavlin 4 cpu->cd.x86.r[X86_R_SP] = (cpu->cd.x86.r[X86_R_SP] & ~0xffff)
1883     | ((cpu->cd.x86.r[X86_R_SP] + 6) & 0xffff);
1884    
1885     return 1;
1886     }
1887    
1888 dpavlin 6
1889     #endif /* ENABLE_X86 */

  ViewVC Help
Powered by ViewVC 1.1.26