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

Contents of /trunk/src/devices/dev_vga.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 24 - (show annotations)
Mon Oct 8 16:19:56 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 34015 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1256 2006/06/23 20:43:44 debug Exp $
20060219	Various minor updates. Removing the old MIPS16 skeleton code,
		because it will need to be rewritten for dyntrans anyway.
20060220-22	Removing the non-working dyntrans backend support.
		Continuing on the 64-bit dyntrans virtual memory generalization.
20060223	More work on the 64-bit vm generalization.
20060225	Beginning on MIPS dyntrans load/store instructions.
		Minor PPC updates (64-bit load/store, etc).
		Fixes for the variable-instruction-length framework, some
		minor AVR updates (a simple Hello World program works!).
		Beginning on a skeleton for automatically generating documen-
		tation (for devices etc.).
20060226	PPC updates (adding some more 64-bit instructions, etc).
		AVR updates (more instructions).
		FINALLY found and fixed the zs bug, making NetBSD/macppc
		accept the serial console.
20060301	Adding more AVR instructions.
20060304	Continuing on AVR-related stuff. Beginning on a framework for
		cycle-accurate device emulation. Adding an experimental "PAL
		TV" device (just a dummy so far).
20060305	Adding more AVR instructions.
		Adding a dummy epcom serial controller (for TS7200 emulation).
20060310	Removing the emul() command from configuration files, so only
		net() and machine() are supported.
		Minor progress on the MIPS dyntrans rewrite.
20060311	Continuing on the MIPS dyntrans rewrite (adding more
		instructions, etc).
20060315	Adding more instructions (sllv, srav, srlv, bgtz[l], blez[l],
		beql, bnel, slti[u], various loads and stores).
20060316	Removing the ALWAYS_SIGNEXTEND_32 option, since it was rarely
		used.
		Adding more MIPS dyntrans instructions, and fixing bugs.
20060318	Implementing fast loads/stores for MIPS dyntrans (big/little
		endian, 32-bit and 64-bit modes).
20060320	Making MIPS dyntrans the default configure option; use
		"--enable-oldmips" to use the old bintrans system.
		Adding MIPS dyntrans dmult[u]; minor updates.
20060322	Continuing... adding some more instructions.
		Adding a simple skeleton for demangling C++ "_ZN" symbols.
20060323	Moving src/debugger.c into a new directory (src/debugger/).
20060324	Fixing the hack used to load PPC ELFs (useful for relocated
		Linux/ppc kernels), and adding a dummy G3 machine mode.
20060325-26	Beginning to experiment with GDB remote serial protocol
		connections; adding a -G command line option for selecting
		which TCP port to listen to.
20060330	Beginning a major cleanup to replace things like "0x%016llx"
		with more correct "0x%016"PRIx64, etc.
		Continuing on the GDB remote serial protocol support.
20060331	More cleanup, and some minor GDB remote progress.
20060402	Adding a hack to the configure script, to allow compilation
		on systems that lack PRIx64 etc.
20060406	Removing the temporary FreeBSD/arm hack in dev_ns16550.c and
		replacing it with a better fix from Olivier Houchard.
20060407	A remote debugger (gdb or ddd) can now start and stop the
		emulator using the GDB remote serial protocol, and registers
		and memory can be read. MIPS only for now.
20060408	More GDB progress: single-stepping also works, and also adding
		support for ARM, PowerPC, and Alpha targets.
		Continuing on the delay-slot-across-page-boundary issue.
20060412	Minor update: beginning to add support for the SPARC target
		to the remote GDB functionality.
20060414	Various MIPS updates: adding more instructions for dyntrans
		(eret, add), and making some exceptions work. Fixing a bug
		in dmult[u].
		Implementing the first SPARC instructions (sethi, or).
20060415	Adding "magic trap" instructions so that PROM calls can be
		software emulated in MIPS dyntrans.
		Adding more MIPS dyntrans instructions (ddiv, dadd) and
		fixing another bug in dmult.
20060416	More MIPS dyntrans progress: adding [d]addi, movn, movz, dsllv,
		rfi, an ugly hack for supporting R2000/R3000 style faked caches,
		preliminary interrupt support, and various other updates and
		bugfixes.
20060417	Adding more SPARC instructions (add, sub, sll[x], sra[x],
		srl[x]), and useful SPARC header definitions.
		Adding the first (trivial) x86/AMD64 dyntrans instructions (nop,
		cli/sti, stc/clc, std/cld, simple mov, inc ax). Various other
		x86 updates related to variable instruction length stuff.
		Adding unaligned loads/stores to the MIPS dyntrans mode (but
		still using the pre-dyntrans (slow) imlementation).
20060419	Fixing a MIPS dyntrans exception-in-delay-slot bug.
		Removing the old "show opcode statistics" functionality, since
		it wasn't really useful and isn't implemented for dyntrans.
		Single-stepping (or running with instruction trace) now looks
		ok with dyntrans with delay-slot architectures.
20060420	Minor hacks (removing the -B command line option when compiled
		for non-bintrans, and some other very minor updates).
		Adding (slow) MIPS dyntrans load-linked/store-conditional.
20060422	Applying fixes for bugs discovered by Nils Weller's nwcc
		(static DEC memmap => now per machine, and adding an extern
		keyword in cpu_arm_instr.c).
		Finally found one of the MIPS dyntrans bugs that I've been
		looking for (copy/paste spelling error BIG vs LITTLE endian in
		cpu_mips_instr_loadstore.c for 16-bit fast stores).
		FINALLY found the major MIPS dyntrans bug: slti vs sltiu
		signed/unsigned code in cpu_mips_instr.c. :-)
		Adding more MIPS dyntrans instructions (lwc1, swc1, bgezal[l],
		ctc1, tlt[u], tge[u], tne, beginning on rdhwr).
		NetBSD/hpcmips can now reach userland when using dyntrans :-)
		Adding some more x86 dyntrans instructions.
		Finally removed the old Alpha-specific virtual memory code,
		and replaced it with the generic 64-bit version.
		Beginning to add disassembly support for SPECIAL3 MIPS opcodes.
20060423	Continuing on the delay-slot-across-page-boundary issue;
		adding an end_of_page2 ic slot (like I had planned before, but
		had removed for some reason).
		Adding a quick-and-dirty fallback to legacy coprocessor 1
		code (i.e. skipping dyntrans implementation for now).
		NetBSD/hpcmips and NetBSD/pmax (when running on an emulated
		R4400) can now be installed and run. :-)  (Many bugs left
		to fix, though.)
		Adding more MIPS dyntrans instructions: madd[u], msub[u].
		Cleaning up the SPECIAL2 vs R5900/TX79/C790 "MMI" opcode
		maps somewhat (disassembly and dyntrans instruction decoding).
20060424	Adding an isa_revision field to mips_cpu_types.h, and making
		sure that SPECIAL3 opcodes cause Reserved Instruction
		exceptions on MIPS32/64 revisions lower than 2.
		Adding the SPARC 'ba', 'call', 'jmpl/retl', 'and', and 'xor'
		instructions.
20060425	Removing the -m command line option ("run at most x 
		instructions") and -T ("single_step_on_bad_addr"), because
		they never worked correctly with dyntrans anyway.
		Freshening up the man page.
20060428	Adding more MIPS dyntrans instructions: bltzal[l], idle.
		Enabling MIPS dyntrans compare interrupts.
20060429	FINALLY found the weird dyntrans bug, causing NetBSD etc. to
		behave strangely: some floating point code (conditional
		coprocessor branches) could not be reused from the old
		non-dyntrans code. The "quick-and-dirty fallback" only appeared
		to work. Fixing by implementing bc1* for MIPS dyntrans.
		More MIPS instructions: [d]sub, sdc1, ldc1, dmtc1, dmfc1, cfc0.
		Freshening up MIPS floating point disassembly appearance.
20060430	Continuing on C790/R5900/TX79 disassembly; implementing 128-bit
		"por" and "pextlw".
20060504	Disabling -u (userland emulation) unless compiled as unstable
		development version.
		Beginning on freshening up the testmachine include files,
		to make it easier to reuse those files (placing them in
		src/include/testmachine/), and beginning on a set of "demos"
		or "tutorials" for the testmachine functionality.
		Minor updates to the MIPS GDB remote protocol stub.
		Refreshing doc/experiments.html and gdb_remote.html.
		Enabling Alpha emulation in the stable release configuration,
		even though no guest OSes for Alpha can run yet.
20060505	Adding a generic 'settings' object, which will contain
		references to settable variables (which will later be possible
		to access using the debugger).
20060506	Updating dev_disk and corresponding demo/documentation (and
		switching from SCSI to IDE disk types, so it actually works
		with current test machines :-).
20060510	Adding a -D_LARGEFILE_SOURCE hack for 64-bit Linux hosts,
		so that fseeko() doesn't give a warning.
		Updating the section about how dyntrans works (the "runnable
		IR") in doc/intro.html.
		Instruction updates (some x64=1 checks, some more R5900
		dyntrans stuff: better mul/mult separation from MIPS32/64,
		adding ei and di).
		Updating MIPS cpuregs.h to a newer one (from NetBSD).
		Adding more MIPS dyntrans instructions: deret, ehb.
20060514	Adding disassembly and beginning implementation of SPARC wr
		and wrpr instructions.
20060515	Adding a SUN SPARC machine mode, with dummy SS20 and Ultra1
		machines. Adding the 32-bit "rd psr" instruction.
20060517	Disassembly support for the general SPARC rd instruction.
		Partial implementation of the cmp (subcc) instruction.
		Some other minor updates (making sure that R5900 processors
		start up with the EIE bit enabled, otherwise Linux/playstation2
		receives no interrupts).
20060519	Minor MIPS updates/cleanups.
20060521	Moving the MeshCube machine into evbmips; this seems to work
		reasonably well with a snapshot of a NetBSD MeshCube kernel.
		Cleanup/fix of MIPS config0 register initialization.
20060529	Minor MIPS fixes, including a sign-extension fix to the
		unaligned load/store code, which makes NetBSD/pmax on R3000
		work better with dyntrans. (Ultrix and Linux/DECstation still
		don't work, though.)
20060530	Minor updates to the Alpha machine mode: adding an AlphaBook
		mode, an LCA bus (forwarding accesses to an ISA bus), etc.
20060531	Applying a bugfix for the MIPS dyntrans sc[d] instruction from
		Ondrej Palkovsky. (Many thanks.)
20060601	Minifix to allow ARM immediate msr instruction to not give
		an error for some valid values.
		More Alpha updates.
20060602	Some minor Alpha updates.
20060603	Adding the Alpha cmpbge instruction. NetBSD/alpha prints its
		first boot messages :-) on an emulated Alphabook 1.
20060612	Minor updates; adding a dev_ether.h include file for the
		testmachine ether device. Continuing the hunt for the dyntrans
		bug which makes Linux and Ultrix on DECstation behave
		strangely... FINALLY found it! It seems to be related to
		invalidation of the translation cache, on tlbw{r,i}. There
		also seems to be some remaining interrupt-related problems.
20060614	Correcting the implementation of ldc1/sdc1 for MIPS dyntrans
		(so that it uses 16 32-bit registers if the FR bit in the
		status register is not set).
20060616	REMOVING BINTRANS COMPLETELY!
		Removing the old MIPS interpretation mode.
		Removing the MFHILO_DELAY and instruction delay stuff, because
		they wouldn't work with dyntrans anyway.
20060617	Some documentation updates (adding "NetBSD-archive" to some
		URLs, and new Debian/DECstation installation screenshots).
		Removing the "tracenull" and "enable-caches" configure options.
		Improving MIPS dyntrans performance somewhat (only invalidate
		translations if necessary, on writes to the entryhi register,
		instead of doing it for all cop0 writes).
20060618	More cleanup after the removal of the old MIPS emulation.
		Trying to fix the MIPS dyntrans performance bugs/bottlenecks;
		only semi-successful so far (for R3000).
20060620	Minor update to allow clean compilation again on Tru64/Alpha.
20060622	MIPS cleanup and fixes (removing the pc_last stuff, which
		doesn't make sense with dyntrans anyway, and fixing a cross-
		page-delay-slot-with-exception case in end_of_page).
		Removing the old max_random_cycles_per_chunk stuff, and the
		concept of cycles vs instructions for MIPS emulation.
		FINALLY found and fixed the bug which caused NetBSD/pmax
		clocks to behave strangely (it was a load to the zero register,
		which was treated as a NOP; now it is treated as a load to a
		dummy scratch register).
20060623	Increasing the dyntrans chunk size back to
		N_SAFE_DYNTRANS_LIMIT, instead of N_SAFE_DYNTRANS_LIMIT/2.
		Preparing for a quick release, even though there are known
		bugs, and performance for non-R3000 MIPS emulation is very
		poor. :-/
		Reverting to half the dyntrans chunk size again, because
		NetBSD/cats seemed less stable with full size chunks. :(
		NetBSD/sgimips 3.0 can now run :-)  (With release 0.3.8, only
		NetBSD/sgimips 2.1 worked, not 3.0.)

==============  RELEASE 0.4.0  ==============


1 /*
2 * Copyright (C) 2004-2006 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: dev_vga.c,v 1.99 2006/06/16 18:31:26 debug Exp $
29 *
30 * VGA charcell and graphics device.
31 *
32 * It should work with 80x25 and 40x25 text modes, and with a few graphics
33 * modes as long as no fancy VGA features are used.
34 */
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include "console.h"
41 #include "cpu.h"
42 #include "devices.h"
43 #include "machine.h"
44 #include "memory.h"
45 #include "misc.h"
46
47 #include "vga.h"
48
49 /* These are generated from binary font files: */
50 #include "fonts/font8x8.c"
51 #include "fonts/font8x10.c"
52 #include "fonts/font8x16.c"
53
54
55 /* For videomem -> framebuffer updates: */
56 #define VGA_TICK_SHIFT 18
57
58 #define MAX_RETRACE_SCANLINES 420
59 #define N_IS1_READ_THRESHOLD 50
60
61 #define GFX_ADDR_WINDOW 0x18000
62
63 #define VGA_FB_ADDR 0x1c00000000ULL
64
65 #define MODE_CHARCELL 1
66 #define MODE_GRAPHICS 2
67
68 #define GRAPHICS_MODE_8BIT 1
69 #define GRAPHICS_MODE_4BIT 2
70
71 struct vga_data {
72 uint64_t videomem_base;
73 uint64_t control_base;
74
75 struct vfb_data *fb;
76 uint32_t fb_size;
77
78 int fb_max_x; /* pixels */
79 int fb_max_y; /* pixels */
80 int max_x; /* charcells or pixels */
81 int max_y; /* charcells or pixels */
82
83 /* Selects charcell mode or graphics mode: */
84 int cur_mode;
85
86 /* Common for text and graphics modes: */
87 int pixel_repx, pixel_repy;
88
89 /* Textmode: */
90 int font_width;
91 int font_height;
92 unsigned char *font;
93 size_t charcells_size;
94 unsigned char *charcells; /* 2 bytes per char */
95 unsigned char *charcells_outputed; /* text */
96 unsigned char *charcells_drawn; /* framebuffer */
97
98 /* Graphics: */
99 int graphics_mode;
100 int bits_per_pixel;
101 unsigned char *gfx_mem;
102 uint32_t gfx_mem_size;
103
104 /* Registers: */
105 int attribute_state; /* 0 or 1 */
106 unsigned char attribute_reg_select;
107 unsigned char attribute_reg[256];
108
109 unsigned char misc_output_reg;
110
111 unsigned char sequencer_reg_select;
112 unsigned char sequencer_reg[256];
113
114 unsigned char graphcontr_reg_select;
115 unsigned char graphcontr_reg[256];
116
117 unsigned char crtc_reg_select;
118 unsigned char crtc_reg[256];
119
120 unsigned char palette_read_index;
121 char palette_read_subindex;
122 unsigned char palette_write_index;
123 char palette_write_subindex;
124
125 int current_retrace_line;
126 int input_status_1;
127
128 /* Palette per scanline during retrace: */
129 unsigned char *retrace_palette;
130 int use_palette_per_line;
131 int64_t n_is1_reads;
132
133 /* Misc.: */
134 int console_handle;
135
136 int cursor_x;
137 int cursor_y;
138
139 int modified;
140 int palette_modified;
141 int update_x1;
142 int update_y1;
143 int update_x2;
144 int update_y2;
145 };
146
147
148 /*
149 * recalc_cursor_position():
150 *
151 * Should be called whenever the cursor location _or_ the display
152 * base has been changed.
153 */
154 static void recalc_cursor_position(struct vga_data *d)
155 {
156 int base = (d->crtc_reg[VGA_CRTC_START_ADDR_HIGH] << 8)
157 + d->crtc_reg[VGA_CRTC_START_ADDR_LOW];
158 int ofs = d->crtc_reg[VGA_CRTC_CURSOR_LOCATION_HIGH] * 256 +
159 d->crtc_reg[VGA_CRTC_CURSOR_LOCATION_LOW];
160 ofs -= base;
161 d->cursor_x = ofs % d->max_x;
162 d->cursor_y = ofs / d->max_x;
163 }
164
165
166 /*
167 * register_reset():
168 *
169 * Resets many registers to sane values.
170 */
171 static void register_reset(struct vga_data *d)
172 {
173 /* Home cursor and start at the top: */
174 d->crtc_reg[VGA_CRTC_CURSOR_LOCATION_HIGH] =
175 d->crtc_reg[VGA_CRTC_CURSOR_LOCATION_LOW] = 0;
176 d->crtc_reg[VGA_CRTC_START_ADDR_HIGH] =
177 d->crtc_reg[VGA_CRTC_START_ADDR_LOW] = 0;
178
179 recalc_cursor_position(d);
180
181 /* Reset cursor scanline stuff: */
182 d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_START] = d->font_height - 2;
183 d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_END] = d->font_height - 1;
184
185 d->sequencer_reg[VGA_SEQ_MAP_MASK] = 0x0f;
186 d->graphcontr_reg[VGA_GRAPHCONTR_MASK] = 0xff;
187
188 d->misc_output_reg = VGA_MISC_OUTPUT_IOAS;
189 d->n_is1_reads = 0;
190 }
191
192
193 static void c_putstr(struct vga_data *d, char *s)
194 {
195 while (*s)
196 console_putchar(d->console_handle, *s++);
197 }
198
199
200 /*
201 * reset_palette():
202 */
203 static void reset_palette(struct vga_data *d, int grayscale)
204 {
205 int i, r, g, b;
206
207 /* TODO: default values for entry 16..255? */
208 for (i=16; i<256; i++)
209 d->fb->rgb_palette[i*3 + 0] = d->fb->rgb_palette[i*3 + 1] =
210 d->fb->rgb_palette[i*3 + 2] = (i & 15) * 4;
211 d->palette_modified = 1;
212 i = 0;
213
214 if (grayscale) {
215 for (r=0; r<2; r++)
216 for (g=0; g<2; g++)
217 for (b=0; b<2; b++) {
218 d->fb->rgb_palette[i + 0] =
219 d->fb->rgb_palette[i + 1] =
220 d->fb->rgb_palette[i + 2] =
221 (r+g+b) * 0xaa / 3;
222 d->fb->rgb_palette[i + 8*3 + 0] =
223 d->fb->rgb_palette[i + 8*3 + 1] =
224 d->fb->rgb_palette[i + 8*3 + 2] =
225 (r+g+b) * 0xaa / 3 + 0x55;
226 i+=3;
227 }
228 return;
229 }
230
231 for (r=0; r<2; r++)
232 for (g=0; g<2; g++)
233 for (b=0; b<2; b++) {
234 d->fb->rgb_palette[i + 0] = r * 0xaa;
235 d->fb->rgb_palette[i + 1] = g * 0xaa;
236 d->fb->rgb_palette[i + 2] = b * 0xaa;
237 i+=3;
238 }
239 for (r=0; r<2; r++)
240 for (g=0; g<2; g++)
241 for (b=0; b<2; b++) {
242 d->fb->rgb_palette[i + 0] = r * 0xaa + 0x55;
243 d->fb->rgb_palette[i + 1] = g * 0xaa + 0x55;
244 d->fb->rgb_palette[i + 2] = b * 0xaa + 0x55;
245 i+=3;
246 }
247 }
248
249
250 /*
251 * vga_update_textmode():
252 *
253 * Called from vga_update() when use_x11 is false. This causes modified
254 * character cells to be "simulated" by outputing ANSI escape sequences
255 * that draw the characters in a terminal window instead.
256 */
257 static void vga_update_textmode(struct machine *machine,
258 struct vga_data *d, int base, int start, int end)
259 {
260 char s[50];
261 int i, oldcolor = -1, printed_last = 0;
262
263 for (i=start; i<=end; i+=2) {
264 unsigned char ch = d->charcells[base+i];
265 int fg = d->charcells[base+i+1] & 15;
266 int bg = (d->charcells[base+i+1] >> 4) & 15;
267 /* top bit of bg = blink */
268 int x = (i/2) % d->max_x;
269 int y = (i/2) / d->max_x;
270
271 if (d->charcells[base+i] == d->charcells_outputed[i] &&
272 d->charcells[base+i+1] == d->charcells_outputed[i+1]) {
273 printed_last = 0;
274 continue;
275 }
276
277 d->charcells_outputed[i] = d->charcells[base+i];
278 d->charcells_outputed[i+1] = d->charcells[base+i+1];
279
280 if (!printed_last || x == 0) {
281 snprintf(s, sizeof(s), "\033[%i;%iH", y + 1, x + 1);
282 c_putstr(d, s);
283 }
284 if (oldcolor < 0 || (bg<<4)+fg != oldcolor || !printed_last) {
285 snprintf(s, sizeof(s), "\033[0;"); c_putstr(d, s);
286
287 switch (fg & 7) {
288 case 0: c_putstr(d, "30"); break;
289 case 1: c_putstr(d, "34"); break;
290 case 2: c_putstr(d, "32"); break;
291 case 3: c_putstr(d, "36"); break;
292 case 4: c_putstr(d, "31"); break;
293 case 5: c_putstr(d, "35"); break;
294 case 6: c_putstr(d, "33"); break;
295 case 7: c_putstr(d, "37"); break;
296 }
297 if (fg & 8)
298 c_putstr(d, ";1");
299 c_putstr(d, ";");
300 switch (bg & 7) {
301 case 0: c_putstr(d, "40"); break;
302 case 1: c_putstr(d, "44"); break;
303 case 2: c_putstr(d, "42"); break;
304 case 3: c_putstr(d, "46"); break;
305 case 4: c_putstr(d, "41"); break;
306 case 5: c_putstr(d, "45"); break;
307 case 6: c_putstr(d, "43"); break;
308 case 7: c_putstr(d, "47"); break;
309 }
310 /* TODO: blink */
311 c_putstr(d, "m");
312 }
313
314 if (ch >= 0x20 && ch != 127)
315 console_putchar(d->console_handle, ch);
316
317 oldcolor = (bg << 4) + fg;
318 printed_last = 1;
319 }
320
321 /* Restore the terminal's cursor position: */
322 snprintf(s, sizeof(s), "\033[%i;%iH", d->cursor_y + 1, d->cursor_x + 1);
323 c_putstr(d, s);
324 }
325
326
327 /*
328 * vga_update_graphics():
329 *
330 * This function should be called whenever any part of d->gfx_mem[] has
331 * been written to. It will redraw all pixels within the range x1,y1
332 * .. x2,y2 using the right palette.
333 */
334 static void vga_update_graphics(struct machine *machine, struct vga_data *d,
335 int x1, int y1, int x2, int y2)
336 {
337 int x, y, ix, iy, c, rx = d->pixel_repx, ry = d->pixel_repy;
338 unsigned char pixel[3];
339
340 for (y=y1; y<=y2; y++)
341 for (x=x1; x<=x2; x++) {
342 /* addr is where to read from VGA memory, addr2 is
343 where to write on the 24-bit framebuffer device */
344 int addr = (y * d->max_x + x) * d->bits_per_pixel;
345 switch (d->bits_per_pixel) {
346 case 8: addr >>= 3;
347 c = d->gfx_mem[addr];
348 pixel[0] = d->fb->rgb_palette[c*3+0];
349 pixel[1] = d->fb->rgb_palette[c*3+1];
350 pixel[2] = d->fb->rgb_palette[c*3+2];
351 break;
352 case 4: addr >>= 2;
353 if (addr & 1)
354 c = d->gfx_mem[addr >> 1] >> 4;
355 else
356 c = d->gfx_mem[addr >> 1] & 0xf;
357 pixel[0] = d->fb->rgb_palette[c*3+0];
358 pixel[1] = d->fb->rgb_palette[c*3+1];
359 pixel[2] = d->fb->rgb_palette[c*3+2];
360 break;
361 }
362 for (iy=y*ry; iy<(y+1)*ry; iy++)
363 for (ix=x*rx; ix<(x+1)*rx; ix++) {
364 uint32_t addr2 = (d->fb_max_x * iy
365 + ix) * 3;
366 if (addr2 < d->fb_size)
367 dev_fb_access(machine->cpus[0],
368 machine->memory, addr2,
369 pixel, sizeof(pixel),
370 MEM_WRITE, d->fb);
371 }
372 }
373 }
374
375
376 /*
377 * vga_update_text():
378 *
379 * This function should be called whenever any part of d->charcells[] has
380 * been written to. It will redraw all characters within the range x1,y1
381 * .. x2,y2 using the right palette.
382 */
383 static void vga_update_text(struct machine *machine, struct vga_data *d,
384 int x1, int y1, int x2, int y2)
385 {
386 int fg, bg, x,y, subx, line;
387 size_t i, start, end, base;
388 int font_size = d->font_height;
389 int font_width = d->font_width;
390 unsigned char *pal = d->fb->rgb_palette;
391
392 if (d->pixel_repx * font_width > 8*8) {
393 fatal("[ too large font ]\n");
394 return;
395 }
396
397 /* Hm... I'm still using the old start..end code: */
398 start = (d->max_x * y1 + x1) * 2;
399 end = (d->max_x * y2 + x2) * 2;
400
401 start &= ~1;
402 end |= 1;
403
404 if (end >= d->charcells_size)
405 end = d->charcells_size - 1;
406
407 base = ((d->crtc_reg[VGA_CRTC_START_ADDR_HIGH] << 8)
408 + d->crtc_reg[VGA_CRTC_START_ADDR_LOW]) * 2;
409
410 if (!machine->use_x11)
411 vga_update_textmode(machine, d, base, start, end);
412
413 for (i=start; i<=end; i+=2) {
414 unsigned char ch = d->charcells[i + base];
415
416 if (!d->palette_modified && d->charcells_drawn[i] == ch &&
417 d->charcells_drawn[i+1] == d->charcells[i+base+1])
418 continue;
419
420 d->charcells_drawn[i] = ch;
421 d->charcells_drawn[i+1] = d->charcells[i + base + 1];
422
423 fg = d->charcells[i+base + 1] & 15;
424 bg = (d->charcells[i+base + 1] >> 4) & 7;
425
426 /* Blink is hard to do :-), but inversion might be ok too: */
427 if (d->charcells[i+base + 1] & 128) {
428 int tmp = fg; fg = bg; bg = tmp;
429 }
430
431 x = (i/2) % d->max_x; x *= font_width;
432 y = (i/2) / d->max_x; y *= font_size;
433
434 /* Draw the character: */
435 for (line = 0; line < font_size; line++) {
436 /* hardcoded for max 8 scaleup... :-) */
437 unsigned char rgb_line[3 * 8 * 8];
438 int iy;
439
440 for (subx = 0; subx < font_width; subx++) {
441 int ix, color_index;
442
443 if (d->use_palette_per_line) {
444 int sline = d->pixel_repy * (line+y);
445 if (sline < MAX_RETRACE_SCANLINES)
446 pal = d->retrace_palette
447 + sline * 256*3;
448 else
449 pal = d->fb->rgb_palette;
450 }
451
452 if (d->font[ch * font_size + line] &
453 (128 >> subx))
454 color_index = fg;
455 else
456 color_index = bg;
457
458 for (ix=0; ix<d->pixel_repx; ix++)
459 memcpy(rgb_line + (subx*d->pixel_repx +
460 ix) * 3, &pal[color_index * 3], 3);
461 }
462
463 for (iy=0; iy<d->pixel_repy; iy++) {
464 uint32_t addr = (d->fb_max_x * (d->pixel_repy *
465 (line+y) + iy) + x * d->pixel_repx) * 3;
466 if (addr >= d->fb_size)
467 continue;
468 dev_fb_access(machine->cpus[0],
469 machine->memory, addr, rgb_line,
470 3 * machine->x11_scaleup * font_width,
471 MEM_WRITE, d->fb);
472 }
473 }
474 }
475 }
476
477
478 /*
479 * vga_update_cursor():
480 */
481 static void vga_update_cursor(struct machine *machine, struct vga_data *d)
482 {
483 int onoff = 1, height = d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_END]
484 - d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_START] + 1;
485
486 if (d->cur_mode != MODE_CHARCELL)
487 onoff = 0;
488
489 if (d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_START] >
490 d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_END]) {
491 onoff = 0;
492 height = 1;
493 }
494
495 if (d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_START] >= d->font_height)
496 onoff = 0;
497
498 dev_fb_setcursor(d->fb,
499 d->cursor_x * d->font_width * d->pixel_repx, (d->cursor_y *
500 d->font_height + d->crtc_reg[VGA_CRTC_CURSOR_SCANLINE_START]) *
501 d->pixel_repy, onoff, d->font_width * d->pixel_repx, height *
502 d->pixel_repy);
503 }
504
505
506 /*
507 * dev_vga_tick():
508 */
509 void dev_vga_tick(struct cpu *cpu, void *extra)
510 {
511 struct vga_data *d = extra;
512 int64_t low = -1, high;
513
514 vga_update_cursor(cpu->machine, d);
515
516 /* TODO: text vs graphics tick? */
517 memory_device_dyntrans_access(cpu, cpu->mem, extra,
518 (uint64_t *) &low, (uint64_t *) &high);
519
520 if (low != -1) {
521 int base = ((d->crtc_reg[VGA_CRTC_START_ADDR_HIGH] << 8)
522 + d->crtc_reg[VGA_CRTC_START_ADDR_LOW]) * 2;
523 int new_u_y1, new_u_y2;
524 debug("[ dev_vga_tick: dyntrans access, %"PRIx64" .. %"
525 PRIx64" ]\n", (uint64_t) low, (uint64_t) high);
526 low -= base;
527 high -= base;
528 d->update_x1 = 0;
529 d->update_x2 = d->max_x - 1;
530 new_u_y1 = (low/2) / d->max_x;
531 new_u_y2 = ((high/2) / d->max_x) + 1;
532 if (new_u_y1 < d->update_y1)
533 d->update_y1 = new_u_y1;
534 if (new_u_y2 > d->update_y2)
535 d->update_y2 = new_u_y2;
536 if (d->update_y1 < 0)
537 d->update_y1 = 0;
538 if (d->update_y2 >= d->max_y)
539 d->update_y2 = d->max_y - 1;
540 d->modified = 1;
541 }
542
543 if (d->n_is1_reads > N_IS1_READ_THRESHOLD &&
544 d->retrace_palette != NULL) {
545 d->use_palette_per_line = 1;
546 d->update_x1 = 0;
547 d->update_x2 = d->max_x - 1;
548 d->update_y1 = 0;
549 d->update_y2 = d->max_y - 1;
550 d->modified = 1;
551 } else {
552 if (d->use_palette_per_line) {
553 d->use_palette_per_line = 0;
554 d->update_x1 = 0;
555 d->update_x2 = d->max_x - 1;
556 d->update_y1 = 0;
557 d->update_y2 = d->max_y - 1;
558 d->modified = 1;
559 }
560 }
561
562 if (!cpu->machine->use_x11) {
563 /* NOTE: 2 > 0, so this only updates the cursor, no
564 character cells. */
565 vga_update_textmode(cpu->machine, d, 0, 2, 0);
566 }
567
568 if (d->modified) {
569 if (d->cur_mode == MODE_CHARCELL)
570 vga_update_text(cpu->machine, d, d->update_x1,
571 d->update_y1, d->update_x2, d->update_y2);
572 else
573 vga_update_graphics(cpu->machine, d, d->update_x1,
574 d->update_y1, d->update_x2, d->update_y2);
575
576 d->palette_modified = 0;
577 d->modified = 0;
578 d->update_x1 = 999999;
579 d->update_x2 = -1;
580 d->update_y1 = 999999;
581 d->update_y2 = -1;
582 }
583
584 if (d->n_is1_reads > N_IS1_READ_THRESHOLD)
585 d->n_is1_reads = 0;
586 }
587
588
589 /*
590 * vga_graphics_access():
591 *
592 * Reads and writes to the VGA video memory (pixels).
593 */
594 DEVICE_ACCESS(vga_graphics)
595 {
596 struct vga_data *d = extra;
597 int j, x=0, y=0, x2=0, y2=0, modified = 0;
598 size_t i;
599
600 if (relative_addr + len >= GFX_ADDR_WINDOW)
601 return 0;
602
603 if (d->cur_mode != MODE_GRAPHICS)
604 return 1;
605
606 switch (d->graphics_mode) {
607 case GRAPHICS_MODE_8BIT:
608 y = relative_addr / d->max_x;
609 x = relative_addr % d->max_x;
610 y2 = (relative_addr+len-1) / d->max_x;
611 x2 = (relative_addr+len-1) % d->max_x;
612
613 if (writeflag == MEM_WRITE) {
614 memcpy(d->gfx_mem + relative_addr, data, len);
615 modified = 1;
616 } else
617 memcpy(data, d->gfx_mem + relative_addr, len);
618 break;
619 case GRAPHICS_MODE_4BIT:
620 y = relative_addr * 8 / d->max_x;
621 x = relative_addr * 8 % d->max_x;
622 y2 = ((relative_addr+len)*8-1) / d->max_x;
623 x2 = ((relative_addr+len)*8-1) % d->max_x;
624 /* TODO: color stuff */
625
626 /* Read/write d->gfx_mem in 4-bit color: */
627 if (writeflag == MEM_WRITE) {
628 /* i is byte index to write, j is bit index */
629 for (i=0; i<len; i++)
630 for (j=0; j<8; j++) {
631 int pixelmask = 1 << (7-j);
632 int b = data[i] & pixelmask;
633 int m = d->sequencer_reg[
634 VGA_SEQ_MAP_MASK] & 0x0f;
635 uint32_t addr = (y * d->max_x + x +
636 i*8 + j) * d->bits_per_pixel / 8;
637 unsigned char byte;
638 if (!(d->graphcontr_reg[
639 VGA_GRAPHCONTR_MASK] & pixelmask))
640 continue;
641 if (addr >= d->gfx_mem_size)
642 continue;
643 byte = d->gfx_mem[addr];
644 if (b && j&1)
645 byte |= m << 4;
646 if (b && !(j&1))
647 byte |= m;
648 if (!b && j&1)
649 byte &= ~(m << 4);
650 if (!b && !(j&1))
651 byte &= ~m;
652 d->gfx_mem[addr] = byte;
653 }
654 modified = 1;
655 } else {
656 fatal("TODO: 4 bit graphics read, mask=0x%02x\n",
657 d->sequencer_reg[VGA_SEQ_MAP_MASK]);
658 for (i=0; i<len; i++)
659 data[i] = random();
660 }
661 break;
662 default:fatal("dev_vga: Unimplemented graphics mode %i\n",
663 d->graphics_mode);
664 cpu->running = 0;
665 }
666
667 if (modified) {
668 d->modified = 1;
669 if (x < d->update_x1) d->update_x1 = x;
670 if (x > d->update_x2) d->update_x2 = x;
671 if (y < d->update_y1) d->update_y1 = y;
672 if (y > d->update_y2) d->update_y2 = y;
673 if (x2 < d->update_x1) d->update_x1 = x2;
674 if (x2 > d->update_x2) d->update_x2 = x2;
675 if (y2 < d->update_y1) d->update_y1 = y2;
676 if (y2 > d->update_y2) d->update_y2 = y2;
677 if (y != y2) {
678 d->update_x1 = 0;
679 d->update_x2 = d->max_x - 1;
680 }
681 }
682 return 1;
683 }
684
685
686 /*
687 * dev_vga_access():
688 *
689 * Reads and writes to the VGA video memory (charcells).
690 */
691 DEVICE_ACCESS(vga)
692 {
693 struct vga_data *d = extra;
694 uint64_t idata = 0, odata = 0;
695 int x, y, x2, y2, r, base;
696 size_t i;
697
698 if (writeflag == MEM_WRITE)
699 idata = memory_readmax64(cpu, data, len);
700
701 base = ((d->crtc_reg[VGA_CRTC_START_ADDR_HIGH] << 8)
702 + d->crtc_reg[VGA_CRTC_START_ADDR_LOW]) * 2;
703 r = relative_addr - base;
704 y = r / (d->max_x * 2);
705 x = (r/2) % d->max_x;
706 y2 = (r+len-1) / (d->max_x * 2);
707 x2 = ((r+len-1)/2) % d->max_x;
708
709 if (relative_addr + len - 1 < d->charcells_size) {
710 if (writeflag == MEM_WRITE) {
711 for (i=0; i<len; i++) {
712 int old = d->charcells[relative_addr + i];
713 if (old != data[i]) {
714 d->charcells[relative_addr + i] =
715 data[i];
716 d->modified = 1;
717 }
718 }
719
720 if (d->modified) {
721 if (x < d->update_x1) d->update_x1 = x;
722 if (x > d->update_x2) d->update_x2 = x;
723 if (y < d->update_y1) d->update_y1 = y;
724 if (y > d->update_y2) d->update_y2 = y;
725 if (x2 < d->update_x1) d->update_x1 = x2;
726 if (x2 > d->update_x2) d->update_x2 = x2;
727 if (y2 < d->update_y1) d->update_y1 = y2;
728 if (y2 > d->update_y2) d->update_y2 = y2;
729
730 if (y != y2) {
731 d->update_x1 = 0;
732 d->update_x2 = d->max_x - 1;
733 }
734 }
735 } else
736 memcpy(data, d->charcells + relative_addr, len);
737 return 1;
738 }
739
740 switch (relative_addr) {
741 default:
742 if (writeflag==MEM_READ) {
743 debug("[ vga: read from 0x%08lx ]\n",
744 (long)relative_addr);
745 } else {
746 debug("[ vga: write to 0x%08lx: 0x%08x ]\n",
747 (long)relative_addr, idata);
748 }
749 }
750
751 if (writeflag == MEM_READ)
752 memory_writemax64(cpu, data, len, odata);
753
754 return 1;
755 }
756
757
758 /*
759 * vga_crtc_reg_write():
760 *
761 * Writes to VGA CRTC registers.
762 */
763 static void vga_crtc_reg_write(struct machine *machine, struct vga_data *d,
764 int regnr, int idata)
765 {
766 int grayscale;
767
768 switch (regnr) {
769 case VGA_CRTC_CURSOR_SCANLINE_START: /* 0x0a */
770 case VGA_CRTC_CURSOR_SCANLINE_END: /* 0x0b */
771 break;
772 case VGA_CRTC_START_ADDR_HIGH: /* 0x0c */
773 case VGA_CRTC_START_ADDR_LOW: /* 0x0d */
774 d->update_x1 = 0;
775 d->update_x2 = d->max_x - 1;
776 d->update_y1 = 0;
777 d->update_y2 = d->max_y - 1;
778 d->modified = 1;
779 recalc_cursor_position(d);
780 break;
781 case VGA_CRTC_CURSOR_LOCATION_HIGH: /* 0x0e */
782 case VGA_CRTC_CURSOR_LOCATION_LOW: /* 0x0f */
783 recalc_cursor_position(d);
784 break;
785 case 0xff:
786 grayscale = 0;
787 switch (d->crtc_reg[0xff]) {
788 case 0x00:
789 grayscale = 1;
790 case 0x01:
791 d->cur_mode = MODE_CHARCELL;
792 d->max_x = 40; d->max_y = 25;
793 d->pixel_repx = machine->x11_scaleup * 2;
794 d->pixel_repy = machine->x11_scaleup;
795 d->font_width = 8;
796 d->font_height = 16;
797 d->font = font8x16;
798 break;
799 case 0x02:
800 grayscale = 1;
801 case 0x03:
802 d->cur_mode = MODE_CHARCELL;
803 d->max_x = 80; d->max_y = 25;
804 d->pixel_repx = d->pixel_repy = machine->x11_scaleup;
805 d->font_width = 8;
806 d->font_height = 16;
807 d->font = font8x16;
808 break;
809 case 0x08:
810 d->cur_mode = MODE_GRAPHICS;
811 d->max_x = 160; d->max_y = 200;
812 d->graphics_mode = GRAPHICS_MODE_4BIT;
813 d->bits_per_pixel = 4;
814 d->pixel_repx = 4 * machine->x11_scaleup;
815 d->pixel_repy = 2 * machine->x11_scaleup;
816 break;
817 case 0x09:
818 case 0x0d:
819 d->cur_mode = MODE_GRAPHICS;
820 d->max_x = 320; d->max_y = 200;
821 d->graphics_mode = GRAPHICS_MODE_4BIT;
822 d->bits_per_pixel = 4;
823 d->pixel_repx = d->pixel_repy =
824 2 * machine->x11_scaleup;
825 break;
826 case 0x0e:
827 d->cur_mode = MODE_GRAPHICS;
828 d->max_x = 640; d->max_y = 200;
829 d->graphics_mode = GRAPHICS_MODE_4BIT;
830 d->bits_per_pixel = 4;
831 d->pixel_repx = machine->x11_scaleup;
832 d->pixel_repy = machine->x11_scaleup * 2;
833 break;
834 case 0x10:
835 d->cur_mode = MODE_GRAPHICS;
836 d->max_x = 640; d->max_y = 350;
837 d->graphics_mode = GRAPHICS_MODE_4BIT;
838 d->bits_per_pixel = 4;
839 d->pixel_repx = d->pixel_repy = machine->x11_scaleup;
840 break;
841 case 0x12:
842 d->cur_mode = MODE_GRAPHICS;
843 d->max_x = 640; d->max_y = 480;
844 d->graphics_mode = GRAPHICS_MODE_4BIT;
845 d->bits_per_pixel = 4;
846 d->pixel_repx = d->pixel_repy = machine->x11_scaleup;
847 break;
848 case 0x13:
849 d->cur_mode = MODE_GRAPHICS;
850 d->max_x = 320; d->max_y = 200;
851 d->graphics_mode = GRAPHICS_MODE_8BIT;
852 d->bits_per_pixel = 8;
853 d->pixel_repx = d->pixel_repy =
854 2 * machine->x11_scaleup;
855 break;
856 default:
857 fatal("TODO! video mode change hack (mode 0x%02x)\n",
858 d->crtc_reg[0xff]);
859 exit(1);
860 }
861
862 if (d->cur_mode == MODE_CHARCELL) {
863 dev_fb_resize(d->fb, d->max_x * d->font_width *
864 d->pixel_repx, d->max_y * d->font_height *
865 d->pixel_repy);
866 d->fb_size = d->max_x * d->pixel_repx * d->font_width *
867 d->max_y * d->pixel_repy * d->font_height * 3;
868 } else {
869 dev_fb_resize(d->fb, d->max_x * d->pixel_repx,
870 d->max_y * d->pixel_repy);
871 d->fb_size = d->max_x * d->pixel_repx *
872 d->max_y * d->pixel_repy * 3;
873 }
874
875 if (d->gfx_mem != NULL)
876 free(d->gfx_mem);
877 d->gfx_mem_size = 1;
878 if (d->cur_mode == MODE_GRAPHICS)
879 d->gfx_mem_size = d->max_x * d->max_y /
880 (d->graphics_mode == GRAPHICS_MODE_8BIT? 1 : 2);
881 d->gfx_mem = malloc(d->gfx_mem_size);
882
883 /* Clear screen and reset the palette: */
884 memset(d->charcells_outputed, 0, d->charcells_size);
885 memset(d->charcells_drawn, 0, d->charcells_size);
886 memset(d->gfx_mem, 0, d->gfx_mem_size);
887 d->update_x1 = 0;
888 d->update_x2 = d->max_x - 1;
889 d->update_y1 = 0;
890 d->update_y2 = d->max_y - 1;
891 d->modified = 1;
892 reset_palette(d, grayscale);
893 register_reset(d);
894 break;
895 default:fatal("[ vga_crtc_reg_write: regnr=0x%02x idata=0x%02x ]\n",
896 regnr, idata);
897 }
898 }
899
900
901 /*
902 * vga_sequencer_reg_write():
903 *
904 * Writes to VGA Sequencer registers.
905 */
906 static void vga_sequencer_reg_write(struct machine *machine, struct vga_data *d,
907 int regnr, int idata)
908 {
909 switch (regnr) {
910 case VGA_SEQ_RESET:
911 case VGA_SEQ_MAP_MASK:
912 case VGA_SEQ_SEQUENCER_MEMORY_MODE:
913 debug("[ vga_sequencer_reg_write: select %i: TODO ]\n", regnr);
914 break;
915 default:fatal("[ vga_sequencer_reg_write: select %i ]\n", regnr);
916 /* cpu->running = 0; */
917 }
918 }
919
920
921 /*
922 * vga_graphcontr_reg_write():
923 *
924 * Writes to VGA Graphics Controller registers.
925 */
926 static void vga_graphcontr_reg_write(struct machine *machine,
927 struct vga_data *d, int regnr, int idata)
928 {
929 switch (regnr) {
930 case VGA_GRAPHCONTR_READMAPSELECT:
931 case VGA_GRAPHCONTR_GRAPHICSMODE:
932 case VGA_GRAPHCONTR_MISC:
933 case VGA_GRAPHCONTR_MASK:
934 debug("[ vga_graphcontr_reg_write: select %i: TODO ]\n", regnr);
935 break;
936 default:fatal("[ vga_graphcontr_reg_write: select %i ]\n", regnr);
937 /* cpu->running = 0; */
938 }
939 }
940
941
942 /*
943 * vga_attribute_reg_write():
944 *
945 * Writes to VGA Attribute registers.
946 */
947 static void vga_attribute_reg_write(struct machine *machine, struct vga_data *d,
948 int regnr, int idata)
949 {
950 /* 0-15 are palette registers: TODO */
951 if (regnr >= 0 && regnr <= 0xf)
952 return;
953
954 switch (regnr) {
955 default:fatal("[ vga_attribute_reg_write: select %i ]\n", regnr);
956 /* cpu->running = 0; */
957 }
958 }
959
960
961 /*
962 * dev_vga_ctrl_access():
963 *
964 * Reads and writes of the VGA control registers.
965 */
966 DEVICE_ACCESS(vga_ctrl)
967 {
968 struct vga_data *d = extra;
969 size_t i;
970 uint64_t idata = 0, odata = 0;
971
972 for (i=0; i<len; i++) {
973 idata = data[i];
974
975 /* 0x3C0 + relative_addr... */
976
977 switch (relative_addr) {
978
979 case VGA_ATTRIBUTE_ADDR: /* 0x00 */
980 switch (d->attribute_state) {
981 case 0: if (writeflag == MEM_READ)
982 odata = d->attribute_reg_select;
983 else {
984 d->attribute_reg_select = 1;
985 d->attribute_state = 1;
986 }
987 break;
988 case 1: d->attribute_state = 0;
989 d->attribute_reg[d->attribute_reg_select] =
990 idata;
991 vga_attribute_reg_write(cpu->machine, d,
992 d->attribute_reg_select, idata);
993 break;
994 }
995 break;
996 case VGA_ATTRIBUTE_DATA_READ: /* 0x01 */
997 if (writeflag == MEM_WRITE)
998 fatal("[ dev_vga: WARNING: Write to "
999 "VGA_ATTRIBUTE_DATA_READ? ]\n");
1000 else {
1001 if (d->attribute_state == 0)
1002 fatal("[ dev_vga: WARNING: Read from "
1003 "VGA_ATTRIBUTE_DATA_READ, but no"
1004 " register selected? ]\n");
1005 else
1006 odata = d->attribute_reg[
1007 d->attribute_reg_select];
1008 }
1009 break;
1010
1011 case VGA_MISC_OUTPUT_W: /* 0x02 */
1012 if (writeflag == MEM_WRITE)
1013 d->misc_output_reg = idata;
1014 else {
1015 /* Reads: Input Status 0 */
1016 odata = 0x00;
1017 }
1018 break;
1019
1020 case VGA_SEQUENCER_ADDR: /* 0x04 */
1021 if (writeflag == MEM_READ)
1022 odata = d->sequencer_reg_select;
1023 else
1024 d->sequencer_reg_select = idata;
1025 break;
1026 case VGA_SEQUENCER_DATA: /* 0x05 */
1027 if (writeflag == MEM_READ)
1028 odata = d->sequencer_reg[
1029 d->sequencer_reg_select];
1030 else {
1031 d->sequencer_reg[d->
1032 sequencer_reg_select] = idata;
1033 vga_sequencer_reg_write(cpu->machine, d,
1034 d->sequencer_reg_select, idata);
1035 }
1036 break;
1037
1038 case VGA_DAC_ADDR_READ: /* 0x07 */
1039 if (writeflag == MEM_WRITE) {
1040 d->palette_read_index = idata;
1041 d->palette_read_subindex = 0;
1042 } else {
1043 debug("[ dev_vga: WARNING: Read from "
1044 "VGA_DAC_ADDR_READ? TODO ]\n");
1045 /* TODO */
1046 }
1047 break;
1048 case VGA_DAC_ADDR_WRITE: /* 0x08 */
1049 if (writeflag == MEM_WRITE) {
1050 d->palette_write_index = idata;
1051 d->palette_write_subindex = 0;
1052
1053 /* TODO: Is this correct? */
1054 d->palette_read_index = idata;
1055 d->palette_read_subindex = 0;
1056 } else {
1057 fatal("[ dev_vga: WARNING: Read from "
1058 "VGA_DAC_ADDR_WRITE? ]\n");
1059 odata = d->palette_write_index;
1060 }
1061 break;
1062 case VGA_DAC_DATA: /* 0x09 */
1063 if (writeflag == MEM_WRITE) {
1064 int new = (idata & 63) << 2;
1065 int old = d->fb->rgb_palette[d->
1066 palette_write_index*3+d->
1067 palette_write_subindex];
1068 d->fb->rgb_palette[d->palette_write_index * 3 +
1069 d->palette_write_subindex] = new;
1070 /* Redraw whole screen, if the
1071 palette changed: */
1072 if (new != old) {
1073 d->modified = 1;
1074 d->palette_modified = 1;
1075 d->update_x1 = d->update_y1 = 0;
1076 d->update_x2 = d->max_x - 1;
1077 d->update_y2 = d->max_y - 1;
1078 }
1079 d->palette_write_subindex ++;
1080 if (d->palette_write_subindex == 3) {
1081 d->palette_write_index ++;
1082 d->palette_write_subindex = 0;
1083 }
1084 } else {
1085 odata = (d->fb->rgb_palette[d->
1086 palette_read_index * 3 +
1087 d->palette_read_subindex] >> 2) & 63;
1088 d->palette_read_subindex ++;
1089 if (d->palette_read_subindex == 3) {
1090 d->palette_read_index ++;
1091 d->palette_read_subindex = 0;
1092 }
1093 }
1094 break;
1095
1096 case VGA_MISC_OUTPUT_R:
1097 odata = d->misc_output_reg;
1098 break;
1099
1100 case VGA_GRAPHCONTR_ADDR: /* 0x0e */
1101 if (writeflag == MEM_READ)
1102 odata = d->graphcontr_reg_select;
1103 else
1104 d->graphcontr_reg_select = idata;
1105 break;
1106 case VGA_GRAPHCONTR_DATA: /* 0x0f */
1107 if (writeflag == MEM_READ)
1108 odata = d->graphcontr_reg[
1109 d->graphcontr_reg_select];
1110 else {
1111 d->graphcontr_reg[d->
1112 graphcontr_reg_select] = idata;
1113 vga_graphcontr_reg_write(cpu->machine, d,
1114 d->graphcontr_reg_select, idata);
1115 }
1116 break;
1117
1118 case VGA_CRTC_ADDR: /* 0x14 */
1119 if (writeflag == MEM_READ)
1120 odata = d->crtc_reg_select;
1121 else
1122 d->crtc_reg_select = idata;
1123 break;
1124 case VGA_CRTC_DATA: /* 0x15 */
1125 if (writeflag == MEM_READ)
1126 odata = d->crtc_reg[d->crtc_reg_select];
1127 else {
1128 d->crtc_reg[d->crtc_reg_select] = idata;
1129 vga_crtc_reg_write(cpu->machine, d,
1130 d->crtc_reg_select, idata);
1131 }
1132 break;
1133
1134 case VGA_INPUT_STATUS_1: /* 0x1A */
1135 odata = 0;
1136 d->n_is1_reads ++;
1137 d->current_retrace_line ++;
1138 d->current_retrace_line %= (MAX_RETRACE_SCANLINES * 8);
1139 /* Whenever we are "inside" a scan line, copy the
1140 current palette into retrace_palette[][]: */
1141 if ((d->current_retrace_line & 7) == 7) {
1142 if (d->retrace_palette == NULL &&
1143 d->n_is1_reads > N_IS1_READ_THRESHOLD) {
1144 d->retrace_palette = malloc(
1145 MAX_RETRACE_SCANLINES * 256*3);
1146 if (d->retrace_palette == NULL) {
1147 fatal("out of memory\n");
1148 exit(1);
1149 }
1150 }
1151 if (d->retrace_palette != NULL)
1152 memcpy(d->retrace_palette + (d->
1153 current_retrace_line >> 3) * 256*3,
1154 d->fb->rgb_palette, d->cur_mode ==
1155 MODE_CHARCELL? (16*3) : (256*3));
1156 }
1157 /* These need to go on and off, to fake the
1158 real vertical and horizontal retrace info. */
1159 if (d->current_retrace_line < 20*8)
1160 odata |= VGA_IS1_DISPLAY_VRETRACE;
1161 else {
1162 if ((d->current_retrace_line & 7) == 0)
1163 odata = VGA_IS1_DISPLAY_DISPLAY_DISABLE;
1164 }
1165 break;
1166
1167 default:
1168 if (writeflag==MEM_READ) {
1169 debug("[ vga_ctrl: read from 0x%08lx ]\n",
1170 (long)relative_addr);
1171 } else {
1172 debug("[ vga_ctrl: write to 0x%08lx: 0x%08x"
1173 " ]\n", (long)relative_addr, (int)idata);
1174 }
1175 }
1176
1177 if (writeflag == MEM_READ)
1178 data[i] = odata;
1179
1180 /* For multi-byte accesses: */
1181 relative_addr ++;
1182 }
1183
1184 return 1;
1185 }
1186
1187
1188 /*
1189 * dev_vga_init():
1190 *
1191 * Register a VGA text console device. max_x and max_y could be something
1192 * like 80 and 25, respectively.
1193 */
1194 void dev_vga_init(struct machine *machine, struct memory *mem,
1195 uint64_t videomem_base, uint64_t control_base, char *name)
1196 {
1197 struct vga_data *d;
1198 size_t i;
1199 size_t allocsize;
1200
1201 d = malloc(sizeof(struct vga_data));
1202 if (d == NULL) {
1203 fprintf(stderr, "out of memory\n");
1204 exit(1);
1205 }
1206 memset(d, 0, sizeof(struct vga_data));
1207
1208 d->console_handle = console_start_slave(machine, "vga",
1209 CONSOLE_OUTPUT_ONLY);
1210
1211 d->videomem_base = videomem_base;
1212 d->control_base = control_base;
1213 d->max_x = 80;
1214 d->max_y = 25;
1215 d->cur_mode = MODE_CHARCELL;
1216 d->crtc_reg[0xff] = 0x03;
1217 d->charcells_size = 0x8000;
1218 d->gfx_mem_size = 1; /* Nothing, as we start in text mode */
1219 d->pixel_repx = d->pixel_repy = machine->x11_scaleup;
1220
1221 /* Allocate in full pages, to make it possible to use dyntrans: */
1222 allocsize = ((d->charcells_size-1) | (machine->arch_pagesize-1)) + 1;
1223 d->charcells = malloc(d->charcells_size);
1224 d->charcells_outputed = malloc(d->charcells_size);
1225 d->charcells_drawn = malloc(d->charcells_size);
1226 d->gfx_mem = malloc(d->gfx_mem_size);
1227 if (d->charcells == NULL || d->charcells_outputed == NULL ||
1228 d->charcells_drawn == NULL || d->gfx_mem == NULL) {
1229 fprintf(stderr, "out of memory in dev_vga_init()\n");
1230 exit(1);
1231 }
1232
1233 memset(d->charcells_drawn, 0, d->charcells_size);
1234
1235 for (i=0; i<d->charcells_size; i+=2) {
1236 d->charcells[i] = ' ';
1237 d->charcells[i+1] = 0x07; /* Default color */
1238 d->charcells_drawn[i] = ' ';
1239 d->charcells_drawn[i+1] = 0x07;
1240 }
1241
1242 memset(d->charcells_outputed, 0, d->charcells_size);
1243 memset(d->gfx_mem, 0, d->gfx_mem_size);
1244
1245 d->font = font8x16;
1246 d->font_width = 8;
1247 d->font_height = 16;
1248
1249 d->fb_max_x = d->pixel_repx * d->max_x;
1250 d->fb_max_y = d->pixel_repy * d->max_y;
1251 if (d->cur_mode == MODE_CHARCELL) {
1252 d->fb_max_x *= d->font_width;
1253 d->fb_max_y *= d->font_height;
1254 }
1255
1256 memory_device_register(mem, "vga_charcells", videomem_base + 0x18000,
1257 allocsize, dev_vga_access, d, DM_DYNTRANS_OK |
1258 DM_DYNTRANS_WRITE_OK | DM_READS_HAVE_NO_SIDE_EFFECTS,
1259 d->charcells);
1260 memory_device_register(mem, "vga_gfx", videomem_base, GFX_ADDR_WINDOW,
1261 dev_vga_graphics_access, d, DM_DEFAULT |
1262 DM_READS_HAVE_NO_SIDE_EFFECTS, d->gfx_mem);
1263 memory_device_register(mem, "vga_ctrl", control_base,
1264 32, dev_vga_ctrl_access, d, DM_DEFAULT, NULL);
1265
1266 d->fb = dev_fb_init(machine, mem, VGA_FB_ADDR, VFB_GENERIC,
1267 d->fb_max_x, d->fb_max_y, d->fb_max_x, d->fb_max_y, 24, "VGA");
1268 d->fb_size = d->fb_max_x * d->fb_max_y * 3;
1269
1270 reset_palette(d, 0);
1271
1272 /* This will force an initial redraw/resynch: */
1273 d->update_x1 = 0;
1274 d->update_x2 = d->max_x - 1;
1275 d->update_y1 = 0;
1276 d->update_y2 = d->max_y - 1;
1277 d->modified = 1;
1278
1279 machine_add_tickfunction(machine, dev_vga_tick, d,
1280 VGA_TICK_SHIFT, 0.0);
1281
1282 register_reset(d);
1283
1284 vga_update_cursor(machine, d);
1285 }
1286

  ViewVC Help
Powered by ViewVC 1.1.26