/[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 6 - (hide annotations)
Mon Oct 8 16:18:11 2007 UTC (16 years, 5 months ago) by dpavlin
File MIME type: text/plain
File size: 53876 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.772 2005/06/04 12:02:16 debug Exp $
20050428	Disabling the "-fmove-all-movables" option in the configure
		script, because it causes the compile to fail on OpenBSD/sgi.
20050502	Minor updates.
20050503	Removing the WRT54G mode (it was bogus anyway), and adding a
		comment about Windows NT for MIPS in doc/experiments.html.
		Minor updates to the x86 instruction decoding.
20050504	Adding some more x86 instructions.
		Adding support for reading files from ISO9660 CDROMs (including
		gzipped files). It's an ugly hack, but it seems to work.
		Various other minor updates (dev_vga.c, pc_bios.c etc).
20050505	Some more x86-related updates.
		Beginning (what I hope will be) a major code cleanup phase.
		"bootris" (an x86 bootsector) runs :-)
20050506	Adding some more x86 instructions.
20050507	tmpnam => mkstemp.
		Working on a hack to allow VGA charcells to be shown even when
		not running with X11.
		Adding more x86 instructions.
20050508	x86 32-bit SIB addressing fix, and more instructions.
20050509	Adding more x86 instructions.
20050510	Minor documentation updates, and other updates (x86 stuff etc.)
20050511	More x86-related updates.
20050513	Various updates, mostly x86-related. (Trying to fix flag 
		calculation, factoring out the ugly shift/rotate code, and
		some other things.)
20050514	Adding support for loading some old i386 a.out executables.
		Finally beginning the cleanup of machine/PROM/bios dependant
		info.
		Some minor documentation updates.
		Trying to clean up ARCBIOS stuff a little.
20050515	Trying to make it possible to actually use more than one disk
		type per machine (floppy, ide, scsi).
		Trying to clean up the kbd vs PROM console stuff. (For PC and
		ARC emulation modes, mostly.)
		Beginning to add an 8259 interrupt controller, and connecting
		it to the x86 emulation.
20050516	The first x86 interrupts seem to work (keyboard stuff).
		Adding a 8253/8254 programmable interval timer skeleton.
		FreeDOS now reaches a command prompt and can be interacted
		with.
20050517	After some bugfixes, MS-DOS also (sometimes) reaches a
		command prompt now.
		Trying to fix the pckbc to work with MS-DOS' keyb.com, but no
		success yet.
20050518	Adding a simple 32-bit x86 MMU skeleton.
20050519	Some more work on the x86 stuff. (Beginning the work on paging,
		and various other fixes).
20050520	More updates. Working on dev_vga (4-bit graphics modes), adding
		40 columns support to the PC bios emulation.
		Trying to add support for resizing windows when switching
		between graphics modes.
20050521	Many more x86-related updates.
20050522	Correcting the initial stack pointer's sign-extension for
		ARCBIOS emulation (thanks to Alec Voropay for noticing the
		error).
		Continuing on the cleanup (ARCBIOS etc).
		dev_vga updates.
20050523	More x86 updates: trying to add some support for protected mode
		interrupts (via gate descriptors) and many other fixes.
		More ARCBIOS cleanup.
		Adding a device flag which indicates that reads cause no
		side-effects. (Useful for the "dump" command in the debugger,
		and other things.)
		Adding support for directly starting up x86 ELFs, skipping the
		bootloader stage. (Most ELFs, however, are not suitable for
		this.)
20050524	Adding simple 32-bit x86 TSS task switching, but no privilege
		level support yet.
		More work on dev_vga. A small "Copper bars" demo works. :-)
		Adding support for Trap Flag (single-step exceptions), at least
		in real mode, and various other x86-related fixes.
20050525	Adding a new disk image prefix (gH;S;) which can be used to
		override the default nr of heads and sectors per track.
20050527	Various bug fixes, more work on the x86 mode (stack change on
		interrupts between different priv.levels), and some minor
		documentation updates.
20050528	Various fixes (x86 stuff).
20050529	More x86 fixes. An OpenBSD/i386 bootfloppy reaches userland
		and can be interacted with (although there are problems with
		key repetition). NetBSD/i386 triggers a serious CISC-related
		problem: instruction fetches across page boundaries, where
		the later part isn't actually part of the instruction.
20050530	Various minor updates. (Documentation updates, etc.)
20050531	Adding some experimental code (experiments/new_test_*) which
		could be useful for dynamic (but not binary) translation in
		the future.
20050602	Adding a dummy ARM skeleton.
		Fixing the pckbc key repetition problem (by adding release
		scancodes for all keypresses).
20050603	Minor updates for the next release.
20050604	Release testing. Minor updates.

==============  RELEASE 0.3.3  ==============

20050604	There'll probably be a 0.3.3.1 release soon, with some very
		very tiny updates.


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 6 * $Id: pc_bios.c,v 1.97 2005/06/02 17:11:34 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     sprintf(t, "GXemul");
1633     #ifdef VERSION
1634     sprintf(t + strlen(t), " "VERSION);
1635     #endif
1636     set_cursor_pos(cpu, 2, 1);
1637     pc_bios_printstr(cpu, t, 0x1f);
1638    
1639     sprintf(t, "%i cpu%s (%s), %i MB memory",
1640     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     sprintf(t, "%c%c", i<2? ('A'+i):' ', i<2? ':':' ');
1660     pc_bios_printstr(cpu, t, 0xf);
1661     if (i < 2)
1662     nfloppies ++;
1663     sprintf(t, " (bios disk %02x) FLOPPY", i);
1664     pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1665     sprintf(t, ", %i KB", (int)(p->size / 1024));
1666     pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1667     if (cpu->machine->md.pc.columns <= 40)
1668     pc_bios_printstr(cpu, "\n ", 0x07);
1669     sprintf(t, " (CHS=%i,%i,%i)", p->cylinders, p->heads,
1670     p->sectorspertrack);
1671     pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1672     if (boot_id == i && boot_type == DISKIMAGE_FLOPPY) {
1673     bios_boot_id = i;
1674     pc_bios_printstr(cpu, " [boot device]", 0xf);
1675     }
1676     pc_bios_printstr(cpu, "\n",
1677     cpu->machine->md.pc.curcolor);
1678     any_disk = 1;
1679     }
1680     }
1681     disknr = 0x80;
1682     for (i=0; i<8; i++) {
1683     if (diskimage_exist(cpu->machine, i, DISKIMAGE_IDE)) {
1684     struct pc_bios_disk *p;
1685     p = add_disk(cpu->machine, disknr, i, DISKIMAGE_IDE);
1686     sprintf(t, "%s", disknr==0x80? "C:" : " ");
1687     pc_bios_printstr(cpu, t, 0xf);
1688     nhds ++;
1689     sprintf(t, " (bios disk %02x) IDE %s, id %i",
1690     disknr, diskimage_is_a_cdrom(cpu->machine, i,
1691     DISKIMAGE_IDE)? "cdrom" : (
1692     diskimage_is_a_tape(cpu->machine, i,
1693     DISKIMAGE_IDE)? "tape" : "disk"),
1694     i);
1695     pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1696     if (cpu->machine->md.pc.columns <= 40)
1697     pc_bios_printstr(cpu, "\n ", 0x07);
1698     else
1699     pc_bios_printstr(cpu, ", ",
1700     cpu->machine->md.pc.curcolor);
1701     sprintf(t, "%lli MB", (long long) (p->size >> 20));
1702     pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1703     if (boot_id == i && boot_type == DISKIMAGE_IDE) {
1704     bios_boot_id = disknr;
1705     pc_bios_printstr(cpu, " [boot device]", 0xf);
1706     }
1707     pc_bios_printstr(cpu, "\n",
1708     cpu->machine->md.pc.curcolor);
1709     disknr++;
1710     any_disk = 1;
1711     }
1712     }
1713     for (i=0; i<8; i++) {
1714     if (diskimage_exist(cpu->machine, i, DISKIMAGE_SCSI)) {
1715     struct pc_bios_disk *p;
1716     p = add_disk(cpu->machine, disknr, i, DISKIMAGE_SCSI);
1717     sprintf(t, "%s", disknr==0x80? "C:" : " ");
1718     pc_bios_printstr(cpu, t, 0xf);
1719     nhds ++;
1720     sprintf(t, " (bios disk %02x) SCSI disk, id %i",
1721     disknr, i);
1722     pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1723     if (cpu->machine->md.pc.columns <= 40)
1724     pc_bios_printstr(cpu, "\n ", 0x07);
1725     else
1726     pc_bios_printstr(cpu, ", ",
1727     cpu->machine->md.pc.curcolor);
1728     sprintf(t, "%lli MB", (long long) (p->size >> 20));
1729     pc_bios_printstr(cpu, t, cpu->machine->md.pc.curcolor);
1730     if (boot_id == i && boot_type == DISKIMAGE_SCSI) {
1731     bios_boot_id = disknr;
1732     pc_bios_printstr(cpu, " [boot device]", 0xf);
1733     }
1734     pc_bios_printstr(cpu, "\n",
1735     cpu->machine->md.pc.curcolor);
1736     disknr++;
1737     any_disk = 1;
1738     }
1739     }
1740    
1741     if (any_disk)
1742     pc_bios_printstr(cpu, "\n", cpu->machine->md.pc.curcolor);
1743     else
1744     pc_bios_printstr(cpu, "No disks attached!\n\n", 0x0f);
1745    
1746     /* See http://members.tripod.com/~oldboard/assembly/bios_data_area.html
1747     for more info. */
1748     if (nfloppies > 0)
1749     nfloppies --;
1750    
1751     reload_segment_descriptor(cpu, X86_S_FS, 0x0000, NULL);
1752     store_16bit_word(cpu, 0x400, 0x03F8); /* COM1 */
1753     store_16bit_word(cpu, 0x402, 0x0378); /* COM2 */
1754     store_byte(cpu, 0x410, (nfloppies << 6) | 0x0f); /* nfloppies etc */
1755     store_byte(cpu, 0x411, 2 << 1); /* nserials etc */
1756     store_16bit_word(cpu, 0x413, 640); /* KB of low RAM */
1757     store_byte(cpu, 0x449, cpu->machine->md.pc.videomode); /* video mode */
1758     store_16bit_word(cpu, 0x44a, cpu->machine->md.pc.columns);/* columns */
1759     store_16bit_word(cpu, 0x463, 0x3D4); /* CRT base port */
1760     store_byte(cpu, 0x475, nhds); /* nr of harddisks */
1761     store_byte(cpu, 0x484, cpu->machine->md.pc.rows-1);/* nr of lines-1 */
1762     store_byte(cpu, 0x485, 16); /* font height */
1763    
1764     /* Registers passed to the bootsector code: */
1765     reload_segment_descriptor(cpu, X86_S_CS, 0x0000, NULL);
1766     reload_segment_descriptor(cpu, X86_S_DS, 0x0000, NULL);
1767     reload_segment_descriptor(cpu, X86_S_ES, 0x0000, NULL);
1768     reload_segment_descriptor(cpu, X86_S_SS, 0x0000, NULL);
1769    
1770     cpu->cd.x86.r[X86_R_AX] = 0xaa55;
1771     cpu->cd.x86.r[X86_R_CX] = 0x0001;
1772     cpu->cd.x86.r[X86_R_DI] = 0xffe4;
1773     cpu->cd.x86.r[X86_R_SP] = 0xfffe;
1774     cpu->cd.x86.r[X86_R_DX] = bios_boot_id;
1775    
1776     cpu->cd.x86.rflags |= X86_FLAGS_IF;
1777     cpu->pc = 0x7c00;
1778    
1779     cpu->machine->md.pc.initialized = 1;
1780     }
1781    
1782    
1783     /*
1784 dpavlin 4 * 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 dpavlin 6 int int_nr, flags;
1790     int enable_ints_after_return = 0;
1791     unsigned char w[2];
1792 dpavlin 4
1793 dpavlin 6 if (addr == 0xffff0) {
1794     fatal("[ bios reboot ]\n");
1795     cpu->running = 0;
1796     return 0;
1797     }
1798 dpavlin 4
1799 dpavlin 6 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 dpavlin 4 switch (int_nr) {
1809 dpavlin 6 case 0x02: /* NMI? */
1810     debug("[ pc_bios: NMI? TODO ]\n");
1811 dpavlin 4 break;
1812 dpavlin 6 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 dpavlin 4 case 0x13:
1829     pc_bios_int13(cpu);
1830 dpavlin 6 enable_ints_after_return = 1;
1831 dpavlin 4 break;
1832 dpavlin 6 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 dpavlin 4 break;
1841 dpavlin 6 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 dpavlin 4 default:
1854     fatal("FATAL: Unimplemented PC BIOS interrupt 0x%02x.\n",
1855     int_nr);
1856     cpu->running = 0;
1857     cpu->dead = 1;
1858 dpavlin 6 return 0;
1859 dpavlin 4 }
1860    
1861     /*
1862     * Return from the interrupt: Pop ip (pc), cs, and flags.
1863     */
1864 dpavlin 6 cpu->cd.x86.cursegment = X86_S_SS;
1865 dpavlin 4 cpu->pc = load_16bit_word(cpu, cpu->cd.x86.r[X86_R_SP]);
1866 dpavlin 6 reload_segment_descriptor(cpu, X86_S_CS,
1867     load_16bit_word(cpu, cpu->cd.x86.r[X86_R_SP] + 2), NULL);
1868 dpavlin 4
1869 dpavlin 6 /* Actually, don't pop flags, because they contain result bits
1870     from interrupt calls. Only pop the Interrupt Flag. */
1871     flags = load_16bit_word(cpu, cpu->cd.x86.r[X86_R_SP] + 4);
1872     cpu->cd.x86.rflags &= ~X86_FLAGS_IF;
1873     cpu->cd.x86.rflags |= (flags & X86_FLAGS_IF);
1874    
1875     if (enable_ints_after_return)
1876     cpu->cd.x86.rflags |= X86_FLAGS_IF;
1877    
1878 dpavlin 4 cpu->cd.x86.r[X86_R_SP] = (cpu->cd.x86.r[X86_R_SP] & ~0xffff)
1879     | ((cpu->cd.x86.r[X86_R_SP] + 6) & 0xffff);
1880    
1881     return 1;
1882     }
1883    
1884 dpavlin 6
1885     #endif /* ENABLE_X86 */

  ViewVC Help
Powered by ViewVC 1.1.26