2.7.0 part1

This commit is contained in:
Mustafa Tufan 2013-12-11 00:06:12 +02:00
parent f4abf5efc2
commit a0d31440ab
16 changed files with 11217 additions and 0 deletions

1
compile Symbolic link
View File

@ -0,0 +1 @@
/usr/local/Cellar/automake/1.14/share/automake-1.14/compile

2154
src/gfxboard.c Normal file

File diff suppressed because it is too large Load Diff

23
src/include/gfxboard.h Normal file
View File

@ -0,0 +1,23 @@
extern addrbank gfxboard_bank_memory;
extern addrbank gfxboard_bank_registers;
extern void gfxboard_init_memory (void);
extern void gfxboard_init_memory_p4_z2 (void);
extern void gfxboard_init_registers (void);
extern void gfxboard_free (void);
extern void gfxboard_reset (void);
extern void gfxboard_vsync_handler (void);
extern bool gfxboard_is_z3 (int);
extern bool gfxboard_is_registers (int);
extern int gfxboard_get_vram_min (int);
extern int gfxboard_get_vram_max (int);
extern bool gfxboard_need_byteswap (int type);
extern double gfxboard_get_vsync (void);
extern void gfxboard_refresh (void);
extern bool gfxboard_toggle (int mode);
extern int gfxboard_num_boards (int type);
#define GFXBOARD_UAE_Z2 0
#define GFXBOARD_UAE_Z3 1
#define GFXBOARD_HARDWARE 2

3122
src/qemuvga/cirrus_vga.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,223 @@
/*
* QEMU Cirrus CLGD 54xx VGA Emulator.
*
* Copyright (c) 2004 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
STATIC_INLINE void glue(rop_8_,ROP_NAME)(uint8_t *dst, uint8_t src)
{
*dst = ROP_FN(*dst, src);
}
STATIC_INLINE void glue(rop_16_,ROP_NAME)(uint16_t *dst, uint16_t src)
{
*dst = ROP_FN(*dst, src);
}
STATIC_INLINE void glue(rop_32_,ROP_NAME)(uint32_t *dst, uint32_t src)
{
*dst = ROP_FN(*dst, src);
}
#define ROP_OP(d, s) glue(rop_8_,ROP_NAME)(d, s)
#define ROP_OP_16(d, s) glue(rop_16_,ROP_NAME)(d, s)
#define ROP_OP_32(d, s) glue(rop_32_,ROP_NAME)(d, s)
#undef ROP_FN
static void
glue(cirrus_bitblt_rop_fwd_, ROP_NAME)(CirrusVGAState *s,
uint8_t *dst,const uint8_t *src,
int dstpitch,int srcpitch,
int bltwidth,int bltheight)
{
int x,y;
dstpitch -= bltwidth;
srcpitch -= bltwidth;
#if 0
/* Invalid test! TW */
if (dstpitch < 0 || srcpitch < 0) {
/* is 0 valid? srcpitch == 0 could be useful */
return;
}
#endif
for (y = 0; y < bltheight; y++) {
for (x = 0; x < (bltwidth & ~3); x += 4) {
ROP_OP_32((uint32_t*)dst, *((uint32_t*)src));
dst += 4;
src += 4;
}
for (; x < bltwidth; x++) {
ROP_OP(dst, *src);
dst++;
src++;
}
dst += dstpitch;
src += srcpitch;
}
}
static void
glue(cirrus_bitblt_rop_bkwd_, ROP_NAME)(CirrusVGAState *s,
uint8_t *dst,const uint8_t *src,
int dstpitch,int srcpitch,
int bltwidth,int bltheight)
{
int x,y;
dstpitch += bltwidth;
srcpitch += bltwidth;
for (y = 0; y < bltheight; y++) {
for (x = 0; x < (bltwidth & ~3); x += 4) {
dst -= 3;
src -= 3;
ROP_OP_32((uint32_t*)dst, *((uint32_t*)src));
dst -= 1;
src -= 1;
}
for (; x < bltwidth; x++) {
ROP_OP(dst, *src);
dst--;
src--;
}
dst += dstpitch;
src += srcpitch;
}
}
static void
glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_8)(CirrusVGAState *s,
uint8_t *dst,const uint8_t *src,
int dstpitch,int srcpitch,
int bltwidth,int bltheight)
{
int x,y;
uint8_t p;
dstpitch -= bltwidth;
srcpitch -= bltwidth;
for (y = 0; y < bltheight; y++) {
for (x = 0; x < bltwidth; x++) {
p = *dst;
ROP_OP(&p, *src);
if (p != s->vga.gr[0x34]) *dst = p;
dst++;
src++;
}
dst += dstpitch;
src += srcpitch;
}
}
static void
glue(glue(cirrus_bitblt_rop_bkwd_transp_, ROP_NAME),_8)(CirrusVGAState *s,
uint8_t *dst,const uint8_t *src,
int dstpitch,int srcpitch,
int bltwidth,int bltheight)
{
int x,y;
uint8_t p;
dstpitch += bltwidth;
srcpitch += bltwidth;
for (y = 0; y < bltheight; y++) {
for (x = 0; x < bltwidth; x++) {
p = *dst;
ROP_OP(&p, *src);
if (p != s->vga.gr[0x34]) *dst = p;
dst--;
src--;
}
dst += dstpitch;
src += srcpitch;
}
}
static void
glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_16)(CirrusVGAState *s,
uint8_t *dst,const uint8_t *src,
int dstpitch,int srcpitch,
int bltwidth,int bltheight)
{
int x,y;
uint8_t p1, p2;
dstpitch -= bltwidth;
srcpitch -= bltwidth;
for (y = 0; y < bltheight; y++) {
for (x = 0; x < bltwidth; x+=2) {
p1 = *dst;
p2 = *(dst+1);
ROP_OP(&p1, *src);
ROP_OP(&p2, *(src + 1));
if ((p1 != s->vga.gr[0x34]) || (p2 != s->vga.gr[0x35])) {
*dst = p1;
*(dst+1) = p2;
}
dst+=2;
src+=2;
}
dst += dstpitch;
src += srcpitch;
}
}
static void
glue(glue(cirrus_bitblt_rop_bkwd_transp_, ROP_NAME),_16)(CirrusVGAState *s,
uint8_t *dst,const uint8_t *src,
int dstpitch,int srcpitch,
int bltwidth,int bltheight)
{
int x,y;
uint8_t p1, p2;
dstpitch += bltwidth;
srcpitch += bltwidth;
for (y = 0; y < bltheight; y++) {
for (x = 0; x < bltwidth; x+=2) {
p1 = *(dst-1);
p2 = *dst;
ROP_OP(&p1, *(src - 1));
ROP_OP(&p2, *src);
if ((p1 != s->vga.gr[0x34]) || (p2 != s->vga.gr[0x35])) {
*(dst-1) = p1;
*dst = p2;
}
dst-=2;
src-=2;
}
dst += dstpitch;
src += srcpitch;
}
}
#define DEPTH 8
#include "cirrus_vga_rop2.h"
#define DEPTH 16
#include "cirrus_vga_rop2.h"
#define DEPTH 24
#include "cirrus_vga_rop2.h"
#define DEPTH 32
#include "cirrus_vga_rop2.h"
#undef ROP_NAME
#undef ROP_OP
#undef ROP_OP_16
#undef ROP_OP_32

View File

@ -0,0 +1,283 @@
/*
* QEMU Cirrus CLGD 54xx VGA Emulator.
*
* Copyright (c) 2004 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#if DEPTH == 8
#define PUTPIXEL() ROP_OP(&d[0], col)
#elif DEPTH == 16
#define PUTPIXEL() ROP_OP_16((uint16_t *)&d[0], col)
#elif DEPTH == 24
#define PUTPIXEL() ROP_OP(&d[0], col); \
ROP_OP(&d[1], (col >> 8)); \
ROP_OP(&d[2], (col >> 16))
#elif DEPTH == 32
#define PUTPIXEL() ROP_OP_32(((uint32_t *)&d[0]), col)
#else
#error unsupported DEPTH
#endif
static void
glue(glue(glue(cirrus_patternfill_, ROP_NAME), _),DEPTH)
(CirrusVGAState * s, uint8_t * dst,
const uint8_t * src,
int dstpitch, int srcpitch,
int bltwidth, int bltheight)
{
uint8_t *d;
int x, y, pattern_y, pattern_pitch, pattern_x;
unsigned int col;
const uint8_t *src1;
#if DEPTH == 24
int skipleft = s->vga.gr[0x2f] & 0x1f;
#else
int skipleft = (s->vga.gr[0x2f] & 0x07) * (DEPTH / 8);
#endif
#if DEPTH == 8
pattern_pitch = 8;
#elif DEPTH == 16
pattern_pitch = 16;
#else
pattern_pitch = 32;
#endif
pattern_y = s->cirrus_blt_srcaddr & 7;
for(y = 0; y < bltheight; y++) {
pattern_x = skipleft;
d = dst + skipleft;
src1 = src + pattern_y * pattern_pitch;
for (x = skipleft; x < bltwidth; x += (DEPTH / 8)) {
#if DEPTH == 8
col = src1[pattern_x];
pattern_x = (pattern_x + 1) & 7;
#elif DEPTH == 16
col = ((uint16_t *)(src1 + pattern_x))[0];
pattern_x = (pattern_x + 2) & 15;
#elif DEPTH == 24
{
const uint8_t *src2 = src1 + pattern_x * 3;
col = src2[0] | (src2[1] << 8) | (src2[2] << 16);
pattern_x = (pattern_x + 1) & 7;
}
#else
col = ((uint32_t *)(src1 + pattern_x))[0];
pattern_x = (pattern_x + 4) & 31;
#endif
PUTPIXEL();
d += (DEPTH / 8);
}
pattern_y = (pattern_y + 1) & 7;
dst += dstpitch;
}
}
/* NOTE: srcpitch is ignored */
static void
glue(glue(glue(cirrus_colorexpand_transp_, ROP_NAME), _),DEPTH)
(CirrusVGAState * s, uint8_t * dst,
const uint8_t * src,
int dstpitch, int srcpitch,
int bltwidth, int bltheight)
{
uint8_t *d;
int x, y;
unsigned bits, bits_xor;
unsigned int col;
unsigned bitmask;
unsigned index;
#if DEPTH == 24
int dstskipleft = s->vga.gr[0x2f] & 0x1f;
int srcskipleft = dstskipleft / 3;
#else
int srcskipleft = s->vga.gr[0x2f] & 0x07;
int dstskipleft = srcskipleft * (DEPTH / 8);
#endif
if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) {
bits_xor = 0xff;
// Color expansion + transparency: fgcol, not bgcol. TW.
col = s->cirrus_blt_fgcol;
} else {
bits_xor = 0x00;
col = s->cirrus_blt_fgcol;
}
for(y = 0; y < bltheight; y++) {
bitmask = 0x80 >> srcskipleft;
bits = *src++ ^ bits_xor;
d = dst + dstskipleft;
for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
if ((bitmask & 0xff) == 0) {
bitmask = 0x80;
bits = *src++ ^ bits_xor;
}
index = (bits & bitmask);
if (index) {
PUTPIXEL();
}
d += (DEPTH / 8);
bitmask >>= 1;
}
dst += dstpitch;
}
}
static void
glue(glue(glue(cirrus_colorexpand_, ROP_NAME), _),DEPTH)
(CirrusVGAState * s, uint8_t * dst,
const uint8_t * src,
int dstpitch, int srcpitch,
int bltwidth, int bltheight)
{
uint32_t colors[2];
uint8_t *d;
int x, y;
unsigned bits;
unsigned int col;
unsigned bitmask;
int srcskipleft = s->vga.gr[0x2f] & 0x07;
int dstskipleft = srcskipleft * (DEPTH / 8);
colors[0] = s->cirrus_blt_bgcol;
colors[1] = s->cirrus_blt_fgcol;
for(y = 0; y < bltheight; y++) {
bitmask = 0x80 >> srcskipleft;
bits = *src++;
d = dst + dstskipleft;
for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
if ((bitmask & 0xff) == 0) {
bitmask = 0x80;
bits = *src++;
}
col = colors[!!(bits & bitmask)];
PUTPIXEL();
d += (DEPTH / 8);
bitmask >>= 1;
}
dst += dstpitch;
}
}
static void
glue(glue(glue(cirrus_colorexpand_pattern_transp_, ROP_NAME), _),DEPTH)
(CirrusVGAState * s, uint8_t * dst,
const uint8_t * src,
int dstpitch, int srcpitch,
int bltwidth, int bltheight)
{
uint8_t *d;
int x, y, bitpos, pattern_y;
unsigned int bits, bits_xor;
unsigned int col;
#if DEPTH == 24
int dstskipleft = s->vga.gr[0x2f] & 0x1f;
int srcskipleft = dstskipleft / 3;
#else
int srcskipleft = s->vga.gr[0x2f] & 0x07;
int dstskipleft = srcskipleft * (DEPTH / 8);
#endif
if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) {
bits_xor = 0xff;
// Color expansion + transparency: fgcol, not bgcol. TW.
col = s->cirrus_blt_fgcol;
} else {
bits_xor = 0x00;
col = s->cirrus_blt_fgcol;
}
pattern_y = s->cirrus_blt_srcaddr & 7;
for(y = 0; y < bltheight; y++) {
bits = src[pattern_y] ^ bits_xor;
bitpos = 7 - srcskipleft;
d = dst + dstskipleft;
for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
if ((bits >> bitpos) & 1) {
PUTPIXEL();
}
d += (DEPTH / 8);
bitpos = (bitpos - 1) & 7;
}
pattern_y = (pattern_y + 1) & 7;
dst += dstpitch;
}
}
static void
glue(glue(glue(cirrus_colorexpand_pattern_, ROP_NAME), _),DEPTH)
(CirrusVGAState * s, uint8_t * dst,
const uint8_t * src,
int dstpitch, int srcpitch,
int bltwidth, int bltheight)
{
uint32_t colors[2];
uint8_t *d;
int x, y, bitpos, pattern_y;
unsigned int bits;
unsigned int col;
int srcskipleft = s->vga.gr[0x2f] & 0x07;
int dstskipleft = srcskipleft * (DEPTH / 8);
colors[0] = s->cirrus_blt_bgcol;
colors[1] = s->cirrus_blt_fgcol;
pattern_y = s->cirrus_blt_srcaddr & 7;
for(y = 0; y < bltheight; y++) {
bits = src[pattern_y];
bitpos = 7 - srcskipleft;
d = dst + dstskipleft;
for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
col = colors[(bits >> bitpos) & 1];
PUTPIXEL();
d += (DEPTH / 8);
bitpos = (bitpos - 1) & 7;
}
pattern_y = (pattern_y + 1) & 7;
dst += dstpitch;
}
}
static void
glue(glue(glue(cirrus_fill_, ROP_NAME), _),DEPTH)
(CirrusVGAState *s,
uint8_t *dst, int dst_pitch,
int width, int height)
{
uint8_t *d, *d1;
uint32_t col;
int x, y;
col = s->cirrus_blt_fgcol;
d1 = dst;
for(y = 0; y < height; y++) {
d = d1;
for(x = 0; x < width; x += (DEPTH / 8)) {
PUTPIXEL();
d += (DEPTH / 8);
}
d1 += dst_pitch;
}
}
#undef DEPTH
#undef PUTPIXEL

View File

@ -0,0 +1,102 @@
/*
* QEMU Cirrus VGA Emulator templates
*
* Copyright (c) 2003 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#if DEPTH == 8
#define BPP 1
#elif DEPTH == 15 || DEPTH == 16
#define BPP 2
#elif DEPTH == 32
#define BPP 4
#else
#error unsupported depth
#endif
static void glue(vga_draw_cursor_line_, DEPTH)(uint8_t *d1,
const uint8_t *src1,
int poffset, int w,
unsigned int color0,
unsigned int color1,
unsigned int color_xor)
{
const uint8_t *plane0, *plane1;
int x, b0, b1;
uint8_t *d;
d = d1;
plane0 = src1;
plane1 = src1 + poffset;
for (x = 0; x < w; x++) {
b0 = (plane0[x >> 3] >> (7 - (x & 7))) & 1;
b1 = (plane1[x >> 3] >> (7 - (x & 7))) & 1;
#if DEPTH == 8
switch (b0 | (b1 << 1)) {
case 0:
break;
case 1:
d[0] ^= color_xor;
break;
case 2:
d[0] = color0;
break;
case 3:
d[0] = color1;
break;
}
#elif DEPTH == 16
switch (b0 | (b1 << 1)) {
case 0:
break;
case 1:
((uint16_t *)d)[0] ^= color_xor;
break;
case 2:
((uint16_t *)d)[0] = color0;
break;
case 3:
((uint16_t *)d)[0] = color1;
break;
}
#elif DEPTH == 32
switch (b0 | (b1 << 1)) {
case 0:
break;
case 1:
((uint32_t *)d)[0] ^= color_xor;
break;
case 2:
((uint32_t *)d)[0] = color0;
break;
case 3:
((uint32_t *)d)[0] = color1;
break;
}
#else
#error unsupported depth
#endif
d += BPP;
}
}
#undef DEPTH
#undef BPP

53
src/qemuvga/pixel_ops.h Normal file
View File

@ -0,0 +1,53 @@
static inline unsigned int rgb_to_pixel8(unsigned int r, unsigned int g,
unsigned int b)
{
return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
}
static inline unsigned int rgb_to_pixel15(unsigned int r, unsigned int g,
unsigned int b)
{
return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
}
static inline unsigned int rgb_to_pixel15bgr(unsigned int r, unsigned int g,
unsigned int b)
{
return ((b >> 3) << 10) | ((g >> 3) << 5) | (r >> 3);
}
static inline unsigned int rgb_to_pixel16(unsigned int r, unsigned int g,
unsigned int b)
{
return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
}
static inline unsigned int rgb_to_pixel16bgr(unsigned int r, unsigned int g,
unsigned int b)
{
return ((b >> 3) << 11) | ((g >> 2) << 5) | (r >> 3);
}
static inline unsigned int rgb_to_pixel24(unsigned int r, unsigned int g,
unsigned int b)
{
return (r << 16) | (g << 8) | b;
}
static inline unsigned int rgb_to_pixel24bgr(unsigned int r, unsigned int g,
unsigned int b)
{
return (b << 16) | (g << 8) | r;
}
static inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g,
unsigned int b)
{
return (r << 16) | (g << 8) | b;
}
static inline unsigned int rgb_to_pixel32bgr(unsigned int r, unsigned int g,
unsigned int b)
{
return (b << 16) | (g << 8) | r;
}

902
src/qemuvga/qemumemory.h Normal file
View File

@ -0,0 +1,902 @@
/*
* Physical memory management API
*
* Copyright 2011 Red Hat, Inc. and/or its affiliates
*
* Authors:
* Avi Kivity <avi@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
*/
#ifndef MEMORY_H
#define MEMORY_H
#ifndef CONFIG_USER_ONLY
#include <stdint.h>
typedef struct MemoryRegionOps MemoryRegionOps;
typedef struct MemoryRegionPortio MemoryRegionPortio;
typedef struct MemoryRegionMmio MemoryRegionMmio;
/* Must match *_DIRTY_FLAGS in cpu-all.h. To be replaced with dynamic
* registration.
*/
#define DIRTY_MEMORY_VGA 0
#define DIRTY_MEMORY_CODE 1
#define DIRTY_MEMORY_MIGRATION 3
struct MemoryRegionMmio {
CPUReadMemoryFunc *read[3];
CPUWriteMemoryFunc *write[3];
};
/* Internal use; thunks between old-style IORange and MemoryRegions. */
typedef struct MemoryRegionIORange MemoryRegionIORange;
struct MemoryRegionIORange {
IORange iorange;
//MemoryRegion *mr;
hwaddr offset;
};
/*
* Memory region callbacks
*/
struct MemoryRegionOps {
/* Read from the memory region. @addr is relative to @mr; @size is
* in bytes. */
uint64_t (*read)(void *opaque,
hwaddr addr,
unsigned size);
/* Write to the memory region. @addr is relative to @mr; @size is
* in bytes. */
void (*write)(void *opaque,
hwaddr addr,
uint64_t data,
unsigned size);
enum device_endian endianness;
/* Guest-visible constraints: */
struct {
/* If nonzero, specify bounds on access sizes beyond which a machine
* check is thrown.
*/
unsigned min_access_size;
unsigned max_access_size;
/* If true, unaligned accesses are supported. Otherwise unaligned
* accesses throw machine checks.
*/
bool unaligned;
/*
* If present, and returns #false, the transaction is not accepted
* by the device (and results in machine dependent behaviour such
* as a machine check exception).
*/
bool (*accepts)(void *opaque, hwaddr addr,
unsigned size, bool is_write);
} valid;
/* Internal implementation constraints: */
struct {
/* If nonzero, specifies the minimum size implemented. Smaller sizes
* will be rounded upwards and a partial result will be returned.
*/
unsigned min_access_size;
/* If nonzero, specifies the maximum size implemented. Larger sizes
* will be done as a series of accesses with smaller sizes.
*/
unsigned max_access_size;
/* If true, unaligned accesses are supported. Otherwise all accesses
* are converted to (possibly multiple) naturally aligned accesses.
*/
bool unaligned;
} impl;
#if 0
/* If .read and .write are not present, old_portio may be used for
* backwards compatibility with old portio registration
*/
const MemoryRegionPortio *old_portio;
/* If .read and .write are not present, old_mmio may be used for
* backwards compatibility with old mmio registration
*/
const MemoryRegionMmio old_mmio;
#endif
};
typedef struct CoalescedMemoryRange CoalescedMemoryRange;
typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd;
typedef struct MemoryRegion {
/* All fields are private - violators will be prosecuted */
void *opaque;
#if 0
const MemoryRegionOps *ops;
MemoryRegion *parent;
Int128 size;
hwaddr addr;
void (*destructor)(MemoryRegion *mr);
ram_addr_t ram_addr;
bool subpage;
bool terminates;
bool readable;
bool ram;
bool readonly; /* For RAM regions */
bool enabled;
bool rom_device;
bool warning_printed; /* For reservations */
bool flush_coalesced_mmio;
MemoryRegion *alias;
hwaddr alias_offset;
unsigned priority;
bool may_overlap;
QTAILQ_HEAD(subregions, MemoryRegion) subregions;
QTAILQ_ENTRY(MemoryRegion) subregions_link;
QTAILQ_HEAD(coalesced_ranges, CoalescedMemoryRange) coalesced;
const char *name;
uint8_t dirty_log_mask;
unsigned ioeventfd_nb;
MemoryRegionIoeventfd *ioeventfds;
#endif
} MemoryRegion;
struct MemoryRegionPortio {
uint32_t offset;
uint32_t len;
unsigned size;
IOPortReadFunc *read;
IOPortWriteFunc *write;
};
#define PORTIO_END_OF_LIST() { }
/**
* AddressSpace: describes a mapping of addresses to #MemoryRegion objects
*/
typedef struct AddressSpace AddressSpace;
struct AddressSpace {
/* All fields are private. */
const char *name;
MemoryRegion *root;
struct FlatView *current_map;
int ioeventfd_nb;
struct MemoryRegionIoeventfd *ioeventfds;
struct AddressSpaceDispatch *dispatch;
#if 0
QTAILQ_ENTRY(AddressSpace) address_spaces_link;
#endif
};
/**
* MemoryRegionSection: describes a fragment of a #MemoryRegion
*
* @mr: the region, or %NULL if empty
* @address_space: the address space the region is mapped in
* @offset_within_region: the beginning of the section, relative to @mr's start
* @size: the size of the section; will not exceed @mr's boundaries
* @offset_within_address_space: the address of the first byte of the section
* relative to the region's address space
* @readonly: writes to this section are ignored
*/
typedef struct MemoryRegionSection MemoryRegionSection;
struct MemoryRegionSection {
MemoryRegion *mr;
AddressSpace *address_space;
hwaddr offset_within_region;
uint64_t size;
hwaddr offset_within_address_space;
bool readonly;
};
typedef struct MemoryListener MemoryListener;
/**
* MemoryListener: callbacks structure for updates to the physical memory map
*
* Allows a component to adjust to changes in the guest-visible memory map.
* Use with memory_listener_register() and memory_listener_unregister().
*/
struct MemoryListener {
void (*begin)(MemoryListener *listener);
void (*commit)(MemoryListener *listener);
void (*region_add)(MemoryListener *listener, MemoryRegionSection *section);
void (*region_del)(MemoryListener *listener, MemoryRegionSection *section);
void (*region_nop)(MemoryListener *listener, MemoryRegionSection *section);
void (*log_start)(MemoryListener *listener, MemoryRegionSection *section);
void (*log_stop)(MemoryListener *listener, MemoryRegionSection *section);
void (*log_sync)(MemoryListener *listener, MemoryRegionSection *section);
void (*log_global_start)(MemoryListener *listener);
void (*log_global_stop)(MemoryListener *listener);
#if 0
void (*eventfd_add)(MemoryListener *listener, MemoryRegionSection *section,
bool match_data, uint64_t data, EventNotifier *e);
void (*eventfd_del)(MemoryListener *listener, MemoryRegionSection *section,
bool match_data, uint64_t data, EventNotifier *e);
void (*coalesced_mmio_add)(MemoryListener *listener, MemoryRegionSection *section,
hwaddr addr, hwaddr len);
void (*coalesced_mmio_del)(MemoryListener *listener, MemoryRegionSection *section,
hwaddr addr, hwaddr len);
#endif
/* Lower = earlier (during add), later (during del) */
unsigned priority;
AddressSpace *address_space_filter;
#if 0
QTAILQ_ENTRY(MemoryListener) link;
#endif
};
/**
* memory_region_init: Initialize a memory region
*
* The region typically acts as a container for other memory regions. Use
* memory_region_add_subregion() to add subregions.
*
* @mr: the #MemoryRegion to be initialized
* @name: used for debugging; not visible to the user or ABI
* @size: size of the region; any subregions beyond this size will be clipped
*/
void memory_region_init(MemoryRegion *mr,
const char *name,
uint64_t size);
/**
* memory_region_init_io: Initialize an I/O memory region.
*
* Accesses into the region will cause the callbacks in @ops to be called.
* if @size is nonzero, subregions will be clipped to @size.
*
* @mr: the #MemoryRegion to be initialized.
* @ops: a structure containing read and write callbacks to be used when
* I/O is performed on the region.
* @opaque: passed to to the read and write callbacks of the @ops structure.
* @name: used for debugging; not visible to the user or ABI
* @size: size of the region.
*/
void memory_region_init_io(MemoryRegion *mr,
const MemoryRegionOps *ops,
void *opaque,
const char *name,
uint64_t size);
/**
* memory_region_init_ram: Initialize RAM memory region. Accesses into the
* region will modify memory directly.
*
* @mr: the #MemoryRegion to be initialized.
* @name: the name of the region.
* @size: size of the region.
*/
void memory_region_init_ram(MemoryRegion *mr,
const char *name,
uint64_t size);
/**
* memory_region_init_ram_ptr: Initialize RAM memory region from a
* user-provided pointer. Accesses into the
* region will modify memory directly.
*
* @mr: the #MemoryRegion to be initialized.
* @name: the name of the region.
* @size: size of the region.
* @ptr: memory to be mapped; must contain at least @size bytes.
*/
void memory_region_init_ram_ptr(MemoryRegion *mr,
const char *name,
uint64_t size,
void *ptr);
/**
* memory_region_init_alias: Initialize a memory region that aliases all or a
* part of another memory region.
*
* @mr: the #MemoryRegion to be initialized.
* @name: used for debugging; not visible to the user or ABI
* @orig: the region to be referenced; @mr will be equivalent to
* @orig between @offset and @offset + @size - 1.
* @offset: start of the section in @orig to be referenced.
* @size: size of the region.
*/
void memory_region_init_alias(MemoryRegion *mr,
const char *name,
MemoryRegion *orig,
hwaddr offset,
uint64_t size);
/**
* memory_region_init_rom_device: Initialize a ROM memory region. Writes are
* handled via callbacks.
*
* @mr: the #MemoryRegion to be initialized.
* @ops: callbacks for write access handling.
* @name: the name of the region.
* @size: size of the region.
*/
void memory_region_init_rom_device(MemoryRegion *mr,
const MemoryRegionOps *ops,
void *opaque,
const char *name,
uint64_t size);
/**
* memory_region_init_reservation: Initialize a memory region that reserves
* I/O space.
*
* A reservation region primariy serves debugging purposes. It claims I/O
* space that is not supposed to be handled by QEMU itself. Any access via
* the memory API will cause an abort().
*
* @mr: the #MemoryRegion to be initialized
* @name: used for debugging; not visible to the user or ABI
* @size: size of the region.
*/
void memory_region_init_reservation(MemoryRegion *mr,
const char *name,
uint64_t size);
/**
* memory_region_destroy: Destroy a memory region and reclaim all resources.
*
* @mr: the region to be destroyed. May not currently be a subregion
* (see memory_region_add_subregion()) or referenced in an alias
* (see memory_region_init_alias()).
*/
void memory_region_destroy(MemoryRegion *mr);
/**
* memory_region_size: get a memory region's size.
*
* @mr: the memory region being queried.
*/
uint64_t memory_region_size(MemoryRegion *mr);
/**
* memory_region_is_ram: check whether a memory region is random access
*
* Returns %true is a memory region is random access.
*
* @mr: the memory region being queried
*/
bool memory_region_is_ram(MemoryRegion *mr);
/**
* memory_region_is_romd: check whether a memory region is ROMD
*
* Returns %true is a memory region is ROMD and currently set to allow
* direct reads.
*
* @mr: the memory region being queried
*/
#if 0
static inline bool memory_region_is_romd(MemoryRegion *mr)
{
return mr->rom_device && mr->readable;
}
#endif
/**
* memory_region_name: get a memory region's name
*
* Returns the string that was used to initialize the memory region.
*
* @mr: the memory region being queried
*/
const char *memory_region_name(MemoryRegion *mr);
/**
* memory_region_is_logging: return whether a memory region is logging writes
*
* Returns %true if the memory region is logging writes
*
* @mr: the memory region being queried
*/
bool memory_region_is_logging(MemoryRegion *mr);
/**
* memory_region_is_rom: check whether a memory region is ROM
*
* Returns %true is a memory region is read-only memory.
*
* @mr: the memory region being queried
*/
bool memory_region_is_rom(MemoryRegion *mr);
/**
* memory_region_get_ram_ptr: Get a pointer into a RAM memory region.
*
* Returns a host pointer to a RAM memory region (created with
* memory_region_init_ram() or memory_region_init_ram_ptr()). Use with
* care.
*
* @mr: the memory region being queried.
*/
void *memory_region_get_ram_ptr(MemoryRegion *mr);
/**
* memory_region_set_log: Turn dirty logging on or off for a region.
*
* Turns dirty logging on or off for a specified client (display, migration).
* Only meaningful for RAM regions.
*
* @mr: the memory region being updated.
* @log: whether dirty logging is to be enabled or disabled.
* @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or
* %DIRTY_MEMORY_VGA.
*/
void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client);
/**
* memory_region_get_dirty: Check whether a range of bytes is dirty
* for a specified client.
*
* Checks whether a range of bytes has been written to since the last
* call to memory_region_reset_dirty() with the same @client. Dirty logging
* must be enabled.
*
* @mr: the memory region being queried.
* @addr: the address (relative to the start of the region) being queried.
* @size: the size of the range being queried.
* @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or
* %DIRTY_MEMORY_VGA.
*/
bool memory_region_get_dirty(MemoryRegion *mr, hwaddr addr,
hwaddr size, unsigned client);
/**
* memory_region_set_dirty: Mark a range of bytes as dirty in a memory region.
*
* Marks a range of bytes as dirty, after it has been dirtied outside
* guest code.
*
* @mr: the memory region being dirtied.
* @addr: the address (relative to the start of the region) being dirtied.
* @size: size of the range being dirtied.
*/
void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr,
hwaddr size);
/**
* memory_region_test_and_clear_dirty: Check whether a range of bytes is dirty
* for a specified client. It clears them.
*
* Checks whether a range of bytes has been written to since the last
* call to memory_region_reset_dirty() with the same @client. Dirty logging
* must be enabled.
*
* @mr: the memory region being queried.
* @addr: the address (relative to the start of the region) being queried.
* @size: the size of the range being queried.
* @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or
* %DIRTY_MEMORY_VGA.
*/
bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr,
hwaddr size, unsigned client);
/**
* memory_region_sync_dirty_bitmap: Synchronize a region's dirty bitmap with
* any external TLBs (e.g. kvm)
*
* Flushes dirty information from accelerators such as kvm and vhost-net
* and makes it available to users of the memory API.
*
* @mr: the region being flushed.
*/
void memory_region_sync_dirty_bitmap(MemoryRegion *mr);
/**
* memory_region_reset_dirty: Mark a range of pages as clean, for a specified
* client.
*
* Marks a range of pages as no longer dirty.
*
* @mr: the region being updated.
* @addr: the start of the subrange being cleaned.
* @size: the size of the subrange being cleaned.
* @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or
* %DIRTY_MEMORY_VGA.
*/
void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr,
hwaddr size, unsigned client);
/**
* memory_region_set_readonly: Turn a memory region read-only (or read-write)
*
* Allows a memory region to be marked as read-only (turning it into a ROM).
* only useful on RAM regions.
*
* @mr: the region being updated.
* @readonly: whether rhe region is to be ROM or RAM.
*/
void memory_region_set_readonly(MemoryRegion *mr, bool readonly);
/**
* memory_region_rom_device_set_readable: enable/disable ROM readability
*
* Allows a ROM device (initialized with memory_region_init_rom_device() to
* to be marked as readable (default) or not readable. When it is readable,
* the device is mapped to guest memory. When not readable, reads are
* forwarded to the #MemoryRegion.read function.
*
* @mr: the memory region to be updated
* @readable: whether reads are satisified directly (%true) or via callbacks
* (%false)
*/
void memory_region_rom_device_set_readable(MemoryRegion *mr, bool readable);
/**
* memory_region_set_coalescing: Enable memory coalescing for the region.
*
* Enabled writes to a region to be queued for later processing. MMIO ->write
* callbacks may be delayed until a non-coalesced MMIO is issued.
* Only useful for IO regions. Roughly similar to write-combining hardware.
*
* @mr: the memory region to be write coalesced
*/
void memory_region_set_coalescing(MemoryRegion *mr);
/**
* memory_region_add_coalescing: Enable memory coalescing for a sub-range of
* a region.
*
* Like memory_region_set_coalescing(), but works on a sub-range of a region.
* Multiple calls can be issued coalesced disjoint ranges.
*
* @mr: the memory region to be updated.
* @offset: the start of the range within the region to be coalesced.
* @size: the size of the subrange to be coalesced.
*/
void memory_region_add_coalescing(MemoryRegion *mr,
hwaddr offset,
uint64_t size);
/**
* memory_region_clear_coalescing: Disable MMIO coalescing for the region.
*
* Disables any coalescing caused by memory_region_set_coalescing() or
* memory_region_add_coalescing(). Roughly equivalent to uncacheble memory
* hardware.
*
* @mr: the memory region to be updated.
*/
void memory_region_clear_coalescing(MemoryRegion *mr);
/**
* memory_region_set_flush_coalesced: Enforce memory coalescing flush before
* accesses.
*
* Ensure that pending coalesced MMIO request are flushed before the memory
* region is accessed. This property is automatically enabled for all regions
* passed to memory_region_set_coalescing() and memory_region_add_coalescing().
*
* @mr: the memory region to be updated.
*/
void memory_region_set_flush_coalesced(MemoryRegion *mr);
/**
* memory_region_clear_flush_coalesced: Disable memory coalescing flush before
* accesses.
*
* Clear the automatic coalesced MMIO flushing enabled via
* memory_region_set_flush_coalesced. Note that this service has no effect on
* memory regions that have MMIO coalescing enabled for themselves. For them,
* automatic flushing will stop once coalescing is disabled.
*
* @mr: the memory region to be updated.
*/
void memory_region_clear_flush_coalesced(MemoryRegion *mr);
#if 0
/**
* memory_region_add_eventfd: Request an eventfd to be triggered when a word
* is written to a location.
*
* Marks a word in an IO region (initialized with memory_region_init_io())
* as a trigger for an eventfd event. The I/O callback will not be called.
* The caller must be prepared to handle failure (that is, take the required
* action if the callback _is_ called).
*
* @mr: the memory region being updated.
* @addr: the address within @mr that is to be monitored
* @size: the size of the access to trigger the eventfd
* @match_data: whether to match against @data, instead of just @addr
* @data: the data to match against the guest write
* @fd: the eventfd to be triggered when @addr, @size, and @data all match.
**/
void memory_region_add_eventfd(MemoryRegion *mr,
hwaddr addr,
unsigned size,
bool match_data,
uint64_t data,
EventNotifier *e);
/**
* memory_region_del_eventfd: Cancel an eventfd.
*
* Cancels an eventfd trigger requested by a previous
* memory_region_add_eventfd() call.
*
* @mr: the memory region being updated.
* @addr: the address within @mr that is to be monitored
* @size: the size of the access to trigger the eventfd
* @match_data: whether to match against @data, instead of just @addr
* @data: the data to match against the guest write
* @fd: the eventfd to be triggered when @addr, @size, and @data all match.
*/
void memory_region_del_eventfd(MemoryRegion *mr,
hwaddr addr,
unsigned size,
bool match_data,
uint64_t data,
EventNotifier *e);
#endif
/**
* memory_region_add_subregion: Add a subregion to a container.
*
* Adds a subregion at @offset. The subregion may not overlap with other
* subregions (except for those explicitly marked as overlapping). A region
* may only be added once as a subregion (unless removed with
* memory_region_del_subregion()); use memory_region_init_alias() if you
* want a region to be a subregion in multiple locations.
*
* @mr: the region to contain the new subregion; must be a container
* initialized with memory_region_init().
* @offset: the offset relative to @mr where @subregion is added.
* @subregion: the subregion to be added.
*/
void memory_region_add_subregion(MemoryRegion *mr,
hwaddr offset,
MemoryRegion *subregion);
/**
* memory_region_add_subregion_overlap: Add a subregion to a container
* with overlap.
*
* Adds a subregion at @offset. The subregion may overlap with other
* subregions. Conflicts are resolved by having a higher @priority hide a
* lower @priority. Subregions without priority are taken as @priority 0.
* A region may only be added once as a subregion (unless removed with
* memory_region_del_subregion()); use memory_region_init_alias() if you
* want a region to be a subregion in multiple locations.
*
* @mr: the region to contain the new subregion; must be a container
* initialized with memory_region_init().
* @offset: the offset relative to @mr where @subregion is added.
* @subregion: the subregion to be added.
* @priority: used for resolving overlaps; highest priority wins.
*/
void memory_region_add_subregion_overlap(MemoryRegion *mr,
hwaddr offset,
MemoryRegion *subregion,
unsigned priority);
#if 0
/**
* memory_region_get_ram_addr: Get the ram address associated with a memory
* region
*
* DO NOT USE THIS FUNCTION. This is a temporary workaround while the Xen
* code is being reworked.
*/
ram_addr_t memory_region_get_ram_addr(MemoryRegion *mr);
#endif
/**
* memory_region_del_subregion: Remove a subregion.
*
* Removes a subregion from its container.
*
* @mr: the container to be updated.
* @subregion: the region being removed; must be a current subregion of @mr.
*/
void memory_region_del_subregion(MemoryRegion *mr,
MemoryRegion *subregion);
/*
* memory_region_set_enabled: dynamically enable or disable a region
*
* Enables or disables a memory region. A disabled memory region
* ignores all accesses to itself and its subregions. It does not
* obscure sibling subregions with lower priority - it simply behaves as
* if it was removed from the hierarchy.
*
* Regions default to being enabled.
*
* @mr: the region to be updated
* @enabled: whether to enable or disable the region
*/
void memory_region_set_enabled(MemoryRegion *mr, bool enabled);
/*
* memory_region_set_address: dynamically update the address of a region
*
* Dynamically updates the address of a region, relative to its parent.
* May be used on regions are currently part of a memory hierarchy.
*
* @mr: the region to be updated
* @addr: new address, relative to parent region
*/
void memory_region_set_address(MemoryRegion *mr, hwaddr addr);
/*
* memory_region_set_alias_offset: dynamically update a memory alias's offset
*
* Dynamically updates the offset into the target region that an alias points
* to, as if the fourth argument to memory_region_init_alias() has changed.
*
* @mr: the #MemoryRegion to be updated; should be an alias.
* @offset: the new offset into the target memory region
*/
void memory_region_set_alias_offset(MemoryRegion *mr,
hwaddr offset);
/**
* memory_region_find: locate a MemoryRegion in an address space
*
* Locates the first #MemoryRegion within an address space given by
* @address_space that overlaps the range given by @addr and @size.
*
* Returns a #MemoryRegionSection that describes a contiguous overlap.
* It will have the following characteristics:
* .@offset_within_address_space >= @addr
* .@offset_within_address_space + .@size <= @addr + @size
* .@size = 0 iff no overlap was found
* .@mr is non-%NULL iff an overlap was found
*
* @address_space: a top-level (i.e. parentless) region that contains
* the region to be found
* @addr: start of the area within @address_space to be searched
* @size: size of the area to be searched
*/
MemoryRegionSection memory_region_find(MemoryRegion *address_space,
hwaddr addr, uint64_t size);
/**
* memory_region_section_addr: get offset within MemoryRegionSection
*
* Returns offset within MemoryRegionSection
*
* @section: the memory region section being queried
* @addr: address in address space
*/
STATIC_INLINE hwaddr
memory_region_section_addr(MemoryRegionSection *section,
hwaddr addr)
{
addr -= section->offset_within_address_space;
addr += section->offset_within_region;
return addr;
}
/**
* memory_global_sync_dirty_bitmap: synchronize the dirty log for all memory
*
* Synchronizes the dirty page log for an entire address space.
* @address_space: a top-level (i.e. parentless) region that contains the
* memory being synchronized
*/
void memory_global_sync_dirty_bitmap(MemoryRegion *address_space);
/**
* memory_region_transaction_begin: Start a transaction.
*
* During a transaction, changes will be accumulated and made visible
* only when the transaction ends (is committed).
*/
void memory_region_transaction_begin(void);
/**
* memory_region_transaction_commit: Commit a transaction and make changes
* visible to the guest.
*/
void memory_region_transaction_commit(void);
/**
* memory_listener_register: register callbacks to be called when memory
* sections are mapped or unmapped into an address
* space
*
* @listener: an object containing the callbacks to be called
* @filter: if non-%NULL, only regions in this address space will be observed
*/
void memory_listener_register(MemoryListener *listener, AddressSpace *filter);
/**
* memory_listener_unregister: undo the effect of memory_listener_register()
*
* @listener: an object containing the callbacks to be removed
*/
void memory_listener_unregister(MemoryListener *listener);
/**
* memory_global_dirty_log_start: begin dirty logging for all regions
*/
void memory_global_dirty_log_start(void);
/**
* memory_global_dirty_log_stop: end dirty logging for all regions
*/
void memory_global_dirty_log_stop(void);
#if 0
void mtree_info(fprintf_function mon_printf, void *f);
#endif
/**
* address_space_init: initializes an address space
*
* @as: an uninitialized #AddressSpace
* @root: a #MemoryRegion that routes addesses for the address space
*/
void address_space_init(AddressSpace *as, MemoryRegion *root);
/**
* address_space_destroy: destroy an address space
*
* Releases all resources associated with an address space. After an address space
* is destroyed, its root memory region (given by address_space_init()) may be destroyed
* as well.
*
* @as: address space to be destroyed
*/
void address_space_destroy(AddressSpace *as);
/**
* address_space_rw: read from or write to an address space.
*
* @as: #AddressSpace to be accessed
* @addr: address within that address space
* @buf: buffer with the data transferred
* @is_write: indicates the transfer direction
*/
void address_space_rw(AddressSpace *as, hwaddr addr, uint8_t *buf,
int len, bool is_write);
/**
* address_space_write: write to address space.
*
* @as: #AddressSpace to be accessed
* @addr: address within that address space
* @buf: buffer with the data transferred
*/
void address_space_write(AddressSpace *as, hwaddr addr,
const uint8_t *buf, int len);
/**
* address_space_read: read from an address space.
*
* @as: #AddressSpace to be accessed
* @addr: address within that address space
* @buf: buffer with the data transferred
*/
void address_space_read(AddressSpace *as, hwaddr addr, uint8_t *buf, int len);
/* address_space_map: map a physical memory region into a host virtual address
*
* May map a subset of the requested range, given by and returned in @plen.
* May return %NULL if resources needed to perform the mapping are exhausted.
* Use only for reads OR writes - not for read-modify-write operations.
* Use cpu_register_map_client() to know when retrying the map operation is
* likely to succeed.
*
* @as: #AddressSpace to be accessed
* @addr: address within that address space
* @plen: pointer to length of buffer; updated on return
* @is_write: indicates the transfer direction
*/
void *address_space_map(AddressSpace *as, hwaddr addr,
hwaddr *plen, bool is_write);
/* address_space_unmap: Unmaps a memory region previously mapped by address_space_map()
*
* Will also mark the memory as dirty if @is_write == %true. @access_len gives
* the amount of memory that was actually read or written by the caller.
*
* @as: #AddressSpace used
* @addr: address within that address space
* @len: buffer length as returned by address_space_map()
* @access_len: amount of data actually transferred
* @is_write: indicates the transfer direction
*/
void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
int is_write, hwaddr access_len);
#endif
#endif

126
src/qemuvga/qemuuaeglue.c Normal file
View File

@ -0,0 +1,126 @@
#include "sysdeps.h"
#include "qemuuaeglue.h"
#include "vga_int.h"
void memory_region_transaction_begin(void)
{
}
void memory_region_transaction_commit(void)
{
}
void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size)
{
}
void memory_region_add_subregion(MemoryRegion *mr,
hwaddr offset,
MemoryRegion *subregion)
{
}
void memory_region_add_subregion_overlap(MemoryRegion *mr,
hwaddr offset,
MemoryRegion *subregion,
unsigned priority)
{
}
void memory_region_del_subregion(MemoryRegion *mr,
MemoryRegion *subregion)
{
}
void memory_region_destroy(MemoryRegion *mr)
{
}
void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client)
{
}
void memory_region_init(MemoryRegion *mr,
const char *name,
uint64_t size)
{
}
void memory_region_set_flush_coalesced(MemoryRegion *mr)
{
}
uint64_t memory_region_size(MemoryRegion *mr)
{
return 0;
}
void memory_region_sync_dirty_bitmap(MemoryRegion *mr)
{
}
void memory_region_set_coalescing(MemoryRegion *mr)
{
}
uint16_t le16_to_cpu(uint16_t v)
{
return v;
}
uint32_t le32_to_cpu(uint32_t v)
{
return v;
}
void graphic_hw_update(QemuConsole *con)
{
}
void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
int dst_x, int dst_y, int w, int h)
{
}
void qemu_flush_coalesced_mmio_buffer(void)
{
}
QEMUClock *vm_clock;
enum vga_retrace_method vga_retrace_method_value = VGA_RETRACE_DUMB;
int64_t qemu_get_clock_ns(QEMUClock *clock)
{
return 0;
}
int64_t qemu_get_clock_ms(QEMUClock *clock)
{
struct timeval tv;
gettimeofday (&tv, NULL);
return (uae_u64)tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
int64_t get_ticks_per_sec(void)
{
return 1000000;
}
void portio_list_init(PortioList *piolist,
const struct MemoryRegionPortio *callbacks,
void *opaque, const char *name)
{
}
void portio_list_destroy(PortioList *piolist)
{
}
void portio_list_add(PortioList *piolist,
struct MemoryRegion *address_space,
uint32_t addr)
{
}
void portio_list_del(PortioList *piolist)
{
}
void dpy_text_cursor(QemuConsole *con, int x, int y)
{
}
void dpy_text_update(QemuConsole *con, int x, int y, int w, int h)
{
}
void dpy_text_resize(QemuConsole *con, int w, int h)
{
}
void dpy_gfx_replace_surface(QemuConsole *con,
DisplaySurface *surface)
{
}

285
src/qemuvga/qemuuaeglue.h Normal file
View File

@ -0,0 +1,285 @@
#include <stdint.h>
#include <stdio.h>
//#define DEBUG_VGA_REG
//#define DEBUG_VGA
extern void write_log (const char *, ...);
#ifndef glue
#define xglue(x, y) x ## y
#define glue(x, y) xglue(x, y)
#define stringify(s) tostring(s)
#define tostring(s) #s
#endif
#ifndef likely
#if __GNUC__ < 3
#define __builtin_expect(x, n) (x)
#endif
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif
#ifdef _MSC_VER
#include <windows.h>
#define container_of(address, type, field) ((type *)( \
(PCHAR)(address) - \
(ULONG_PTR)(&((type *)0)->field)))
#define STATIC_INLINE static __forceinline
#define snprintf c99_snprintf
inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap)
{
int count = -1;
if (size != 0)
count = _vsnprintf_s(str, size, _TRUNCATE, format, ap);
if (count == -1)
count = _vscprintf(format, ap);
return count;
}
inline int c99_snprintf(char* str, size_t size, const char* format, ...)
{
int count;
va_list ap;
va_start(ap, format);
count = c99_vsnprintf(str, size, format, ap);
va_end(ap);
return count;
}
#else
#ifndef container_of
/*#define container_of(ptr, type, member) ({ \
const typeof(((type *) 0)->member) *__mptr = (ptr); \
(type *) ((char *) __mptr - offsetof(type, member));}) */
#define container_of(ptr, type, member) \
((type *) ((char *)(ptr) - offsetof(type, member)))
#endif
#endif
#ifndef ABS
#define ABS(x) abs(x)
#endif
#define g_free free
#define g_malloc malloc
#define g_new(type, num) ((type*)calloc(sizeof(type),num))
enum device_endian {
DEVICE_NATIVE_ENDIAN,
DEVICE_BIG_ENDIAN,
DEVICE_LITTLE_ENDIAN,
};
enum vga_retrace_method {
VGA_RETRACE_DUMB,
VGA_RETRACE_PRECISE
};
extern enum vga_retrace_method vga_retrace_method_value;
typedef uint32_t QEMUClock;
extern QEMUClock *vm_clock;
int64_t qemu_get_clock_ns(QEMUClock *clock);
int64_t qemu_get_clock_ms(QEMUClock *clock);
int64_t get_ticks_per_sec(void);
#define isa_mem_base 0
#define QemuConsole uint32_t
#define console_ch_t uint8_t
typedef struct GraphicHwOps {
void (*invalidate)(void *opaque);
void (*gfx_update)(void *opaque);
void (*text_update)(void *opaque, console_ch_t *text);
void (*update_interval)(void *opaque, uint64_t interval);
} GraphicHwOps;
#define VMStateDescription uint32_t
#define hwaddr uint32_t
#define ram_addr_t uint32_t
typedef struct DisplaySurface {
void *bah;
} DisplaySurface;
uint16_t le16_to_cpu(uint16_t v);
uint32_t le32_to_cpu(uint32_t v);
static inline void cpu_to_32wu(uint32_t *p, uint32_t v)
{
}
void graphic_hw_update(QemuConsole *con);
void qemu_console_copy(QemuConsole *con, int src_x, int src_y,
int dst_x, int dst_y, int w, int h);
void qemu_console_resize(QemuConsole *con, int width, int height);
DisplaySurface *qemu_console_surface(QemuConsole *con);
DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
int linesize, uint8_t *data,
bool byteswap);
int surface_stride(DisplaySurface *s);
uint8_t *surface_data(DisplaySurface *s);
int is_surface_bgr(DisplaySurface *surface);
static inline int is_buffer_shared(DisplaySurface *surface)
{
return 0;
}
void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h);
void dpy_text_cursor(QemuConsole *con, int x, int y);
void dpy_text_update(QemuConsole *con, int x, int y, int w, int h);
void dpy_text_resize(QemuConsole *con, int w, int h);
void dpy_gfx_replace_surface(QemuConsole *con,
DisplaySurface *surface);
static inline void console_write_ch(console_ch_t *dest, uint32_t ch)
{
if (!(ch & 0xff))
ch |= ' ';
*dest = ch;
}
void qemu_flush_coalesced_mmio_buffer(void);
int surface_bits_per_pixel(DisplaySurface *s);
int surface_bytes_per_pixel(DisplaySurface *s);
typedef struct PortioList {
const struct MemoryRegionPortio *ports;
struct MemoryRegion *address_space;
unsigned nr;
struct MemoryRegion **regions;
struct MemoryRegion **aliases;
void *opaque;
const char *name;
} PortioList;
void portio_list_init(PortioList *piolist,
const struct MemoryRegionPortio *callbacks,
void *opaque, const char *name);
void portio_list_destroy(PortioList *piolist);
void portio_list_add(PortioList *piolist,
struct MemoryRegion *address_space,
uint32_t addr);
void portio_list_del(PortioList *piolist);
typedef struct IORange IORange;
typedef struct IORangeOps IORangeOps;
struct IORangeOps {
void (*read)(IORange *iorange, uint64_t offset, unsigned width,
uint64_t *data);
void (*write)(IORange *iorange, uint64_t offset, unsigned width,
uint64_t data);
void (*destructor)(IORange *iorange);
};
typedef struct IORange {
const IORangeOps *ops;
uint64_t base;
uint64_t len;
} IORange;
typedef void (IOPortWriteFunc)(void *opaque, uint32_t address, uint32_t data);
typedef uint32_t (IOPortReadFunc)(void *opaque, uint32_t address);
typedef void CPUWriteMemoryFunc(void *opaque, hwaddr addr, uint32_t value);
typedef uint32_t CPUReadMemoryFunc(void *opaque, hwaddr addr);
#include "qemumemory.h"
#include "pixel_ops.h"
static inline uint32_t lduw_raw(void *p)
{
return ((uint32_t*)p)[0];
}
typedef void QEMUResetHandler(void *opaque);
void qemu_register_reset(QEMUResetHandler *func, void *opaque);
#include "vga_int.h"
// ID
#define CIRRUS_ID_CLGD5422 (0x23<<2)
#define CIRRUS_ID_CLGD5426 (0x24<<2)
#define CIRRUS_ID_CLGD5424 (0x25<<2)
#define CIRRUS_ID_CLGD5428 (0x26<<2)
#define CIRRUS_ID_CLGD5430 (0x28<<2)
#define CIRRUS_ID_CLGD5434 (0x2A<<2)
#define CIRRUS_ID_CLGD5436 (0x2B<<2)
#define CIRRUS_ID_CLGD5446 (0x2E<<2)
typedef void (*cirrus_bitblt_rop_t) (struct CirrusVGAState *s,
uint8_t * dst, const uint8_t * src,
int dstpitch, int srcpitch,
int bltwidth, int bltheight);
typedef void (*cirrus_fill_t)(struct CirrusVGAState *s,
uint8_t *dst, int dst_pitch, int width, int height);
typedef struct CirrusVGAState {
VGACommonState vga;
MemoryRegion cirrus_vga_io;
MemoryRegion cirrus_linear_io;
MemoryRegion cirrus_linear_bitblt_io;
MemoryRegion cirrus_mmio_io;
MemoryRegion pci_bar;
bool linear_vram; /* vga.vram mapped over cirrus_linear_io */
MemoryRegion low_mem_container; /* container for 0xa0000-0xc0000 */
MemoryRegion low_mem; /* always mapped, overridden by: */
MemoryRegion cirrus_bank[2]; /* aliases at 0xa0000-0xb0000 */
uint32_t cirrus_addr_mask;
uint32_t linear_mmio_mask;
uint8_t cirrus_shadow_gr0;
uint8_t cirrus_shadow_gr1;
uint8_t cirrus_hidden_dac_lockindex;
uint8_t cirrus_hidden_dac_data;
uint32_t cirrus_bank_base[2];
uint32_t cirrus_bank_limit[2];
uint8_t cirrus_hidden_palette[48];
uint32_t hw_cursor_x;
uint32_t hw_cursor_y;
int cirrus_blt_pixelwidth;
int cirrus_blt_width;
int cirrus_blt_height;
int cirrus_blt_dstpitch;
int cirrus_blt_srcpitch;
uint32_t cirrus_blt_fgcol;
uint32_t cirrus_blt_bgcol;
uint32_t cirrus_blt_dstaddr;
uint32_t cirrus_blt_srcaddr;
uint8_t cirrus_blt_mode;
uint8_t cirrus_blt_modeext;
cirrus_bitblt_rop_t cirrus_rop;
#define CIRRUS_BLTBUFSIZE (2048 * 4) /* one line width */
uint8_t cirrus_bltbuf[CIRRUS_BLTBUFSIZE];
uint8_t *cirrus_srcptr;
uint8_t *cirrus_srcptr_end;
uint32_t cirrus_srccounter;
/* hwcursor display state */
int last_hw_cursor_size;
int last_hw_cursor_x;
int last_hw_cursor_y;
int last_hw_cursor_y_start;
int last_hw_cursor_y_end;
int real_vram_size; /* XXX: suppress that */
int total_vram_size;
int device_id;
int bustype;
int valid_memory_config;
} CirrusVGAState;
void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci,
MemoryRegion *system_memory,
MemoryRegion *system_io);

2433
src/qemuvga/vga.c Normal file

File diff suppressed because it is too large Load Diff

159
src/qemuvga/vga.h Normal file
View File

@ -0,0 +1,159 @@
/*
* linux/include/video/vga.h -- standard VGA chipset interaction
*
* Copyright 1999 Jeff Garzik <jgarzik@pobox.com>
*
* Copyright history from vga16fb.c:
* Copyright 1999 Ben Pfaff and Petr Vandrovec
* Based on VGA info at http://www.osdever.net/FreeVGA/home.htm
* Based on VESA framebuffer (c) 1998 Gerd Knorr
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file COPYING in the main directory of this
* archive for more details.
*
*/
#ifndef __linux_video_vga_h__
#define __linux_video_vga_h__
/* Some of the code below is taken from SVGAlib. The original,
unmodified copyright notice for that code is below. */
/* VGAlib version 1.2 - (c) 1993 Tommy Frandsen */
/* */
/* This library is free software; you can redistribute it and/or */
/* modify it without any restrictions. This library is distributed */
/* in the hope that it will be useful, but without any warranty. */
/* Multi-chipset support Copyright 1993 Harm Hanemaayer */
/* partially copyrighted (C) 1993 by Hartmut Schirmer */
/* VGA data register ports */
#define VGA_CRT_DC 0x3D5 /* CRT Controller Data Register - color emulation */
#define VGA_CRT_DM 0x3B5 /* CRT Controller Data Register - mono emulation */
#define VGA_ATT_R 0x3C1 /* Attribute Controller Data Read Register */
#define VGA_ATT_W 0x3C0 /* Attribute Controller Data Write Register */
#define VGA_GFX_D 0x3CF /* Graphics Controller Data Register */
#define VGA_SEQ_D 0x3C5 /* Sequencer Data Register */
#define VGA_MIS_R 0x3CC /* Misc Output Read Register */
#define VGA_MIS_W 0x3C2 /* Misc Output Write Register */
#define VGA_FTC_R 0x3CA /* Feature Control Read Register */
#define VGA_IS1_RC 0x3DA /* Input Status Register 1 - color emulation */
#define VGA_IS1_RM 0x3BA /* Input Status Register 1 - mono emulation */
#define VGA_PEL_D 0x3C9 /* PEL Data Register */
#define VGA_PEL_MSK 0x3C6 /* PEL mask register */
/* EGA-specific registers */
#define EGA_GFX_E0 0x3CC /* Graphics enable processor 0 */
#define EGA_GFX_E1 0x3CA /* Graphics enable processor 1 */
/* VGA index register ports */
#define VGA_CRT_IC 0x3D4 /* CRT Controller Index - color emulation */
#define VGA_CRT_IM 0x3B4 /* CRT Controller Index - mono emulation */
#define VGA_ATT_IW 0x3C0 /* Attribute Controller Index & Data Write Register */
#define VGA_GFX_I 0x3CE /* Graphics Controller Index */
#define VGA_SEQ_I 0x3C4 /* Sequencer Index */
#define VGA_PEL_IW 0x3C8 /* PEL Write Index */
#define VGA_PEL_IR 0x3C7 /* PEL Read Index */
/* standard VGA indexes max counts */
#define VGA_CRT_C 0x19 /* Number of CRT Controller Registers */
#define VGA_ATT_C 0x15 /* Number of Attribute Controller Registers */
#define VGA_GFX_C 0x09 /* Number of Graphics Controller Registers */
#define VGA_SEQ_C 0x05 /* Number of Sequencer Registers */
#define VGA_MIS_C 0x01 /* Number of Misc Output Register */
/* VGA misc register bit masks */
#define VGA_MIS_COLOR 0x01
#define VGA_MIS_ENB_MEM_ACCESS 0x02
#define VGA_MIS_DCLK_28322_720 0x04
#define VGA_MIS_ENB_PLL_LOAD (0x04 | 0x08)
#define VGA_MIS_SEL_HIGH_PAGE 0x20
/* VGA CRT controller register indices */
#define VGA_CRTC_H_TOTAL 0
#define VGA_CRTC_H_DISP 1
#define VGA_CRTC_H_BLANK_START 2
#define VGA_CRTC_H_BLANK_END 3
#define VGA_CRTC_H_SYNC_START 4
#define VGA_CRTC_H_SYNC_END 5
#define VGA_CRTC_V_TOTAL 6
#define VGA_CRTC_OVERFLOW 7
#define VGA_CRTC_PRESET_ROW 8
#define VGA_CRTC_MAX_SCAN 9
#define VGA_CRTC_CURSOR_START 0x0A
#define VGA_CRTC_CURSOR_END 0x0B
#define VGA_CRTC_START_HI 0x0C
#define VGA_CRTC_START_LO 0x0D
#define VGA_CRTC_CURSOR_HI 0x0E
#define VGA_CRTC_CURSOR_LO 0x0F
#define VGA_CRTC_V_SYNC_START 0x10
#define VGA_CRTC_V_SYNC_END 0x11
#define VGA_CRTC_V_DISP_END 0x12
#define VGA_CRTC_OFFSET 0x13
#define VGA_CRTC_UNDERLINE 0x14
#define VGA_CRTC_V_BLANK_START 0x15
#define VGA_CRTC_V_BLANK_END 0x16
#define VGA_CRTC_MODE 0x17
#define VGA_CRTC_LINE_COMPARE 0x18
#define VGA_CRTC_REGS VGA_CRT_C
/* VGA CRT controller bit masks */
#define VGA_CR11_LOCK_CR0_CR7 0x80 /* lock writes to CR0 - CR7 */
#define VGA_CR17_H_V_SIGNALS_ENABLED 0x80
/* VGA attribute controller register indices */
#define VGA_ATC_PALETTE0 0x00
#define VGA_ATC_PALETTE1 0x01
#define VGA_ATC_PALETTE2 0x02
#define VGA_ATC_PALETTE3 0x03
#define VGA_ATC_PALETTE4 0x04
#define VGA_ATC_PALETTE5 0x05
#define VGA_ATC_PALETTE6 0x06
#define VGA_ATC_PALETTE7 0x07
#define VGA_ATC_PALETTE8 0x08
#define VGA_ATC_PALETTE9 0x09
#define VGA_ATC_PALETTEA 0x0A
#define VGA_ATC_PALETTEB 0x0B
#define VGA_ATC_PALETTEC 0x0C
#define VGA_ATC_PALETTED 0x0D
#define VGA_ATC_PALETTEE 0x0E
#define VGA_ATC_PALETTEF 0x0F
#define VGA_ATC_MODE 0x10
#define VGA_ATC_OVERSCAN 0x11
#define VGA_ATC_PLANE_ENABLE 0x12
#define VGA_ATC_PEL 0x13
#define VGA_ATC_COLOR_PAGE 0x14
#define VGA_AR_ENABLE_DISPLAY 0x20
/* VGA sequencer register indices */
#define VGA_SEQ_RESET 0x00
#define VGA_SEQ_CLOCK_MODE 0x01
#define VGA_SEQ_PLANE_WRITE 0x02
#define VGA_SEQ_CHARACTER_MAP 0x03
#define VGA_SEQ_MEMORY_MODE 0x04
/* VGA sequencer register bit masks */
#define VGA_SR01_CHAR_CLK_8DOTS 0x01 /* bit 0: character clocks 8 dots wide are generated */
#define VGA_SR01_SCREEN_OFF 0x20 /* bit 5: Screen is off */
#define VGA_SR02_ALL_PLANES 0x0F /* bits 3-0: enable access to all planes */
#define VGA_SR04_EXT_MEM 0x02 /* bit 1: allows complete mem access to 256K */
#define VGA_SR04_SEQ_MODE 0x04 /* bit 2: directs system to use a sequential addressing mode */
#define VGA_SR04_CHN_4M 0x08 /* bit 3: selects modulo 4 addressing for CPU access to display memory */
/* VGA graphics controller register indices */
#define VGA_GFX_SR_VALUE 0x00
#define VGA_GFX_SR_ENABLE 0x01
#define VGA_GFX_COMPARE_VALUE 0x02
#define VGA_GFX_DATA_ROTATE 0x03
#define VGA_GFX_PLANE_READ 0x04
#define VGA_GFX_MODE 0x05
#define VGA_GFX_MISC 0x06
#define VGA_GFX_COMPARE_MASK 0x07
#define VGA_GFX_BIT_MASK 0x08
/* VGA graphics controller bit masks */
#define VGA_GR06_GRAPHICS_MODE 0x01
#endif /* __linux_video_vga_h__ */

216
src/qemuvga/vga_int.h Normal file
View File

@ -0,0 +1,216 @@
/*
* QEMU internal VGA defines.
*
* Copyright (c) 2003-2004 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef HW_VGA_INT_H
#define HW_VGA_INT_H 1
#if 0
#include <hw/hw.h>
#include "qapi/error.h"
#include "exec/memory.h"
#endif
#define ST01_V_RETRACE 0x08
#define ST01_DISP_ENABLE 0x01
#define VBE_DISPI_MAX_XRES 16000
#define VBE_DISPI_MAX_YRES 12000
#define VBE_DISPI_MAX_BPP 32
#define VBE_DISPI_INDEX_ID 0x0
#define VBE_DISPI_INDEX_XRES 0x1
#define VBE_DISPI_INDEX_YRES 0x2
#define VBE_DISPI_INDEX_BPP 0x3
#define VBE_DISPI_INDEX_ENABLE 0x4
#define VBE_DISPI_INDEX_BANK 0x5
#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
#define VBE_DISPI_INDEX_X_OFFSET 0x8
#define VBE_DISPI_INDEX_Y_OFFSET 0x9
#define VBE_DISPI_INDEX_NB 0xa /* size of vbe_regs[] */
#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa /* read-only, not in vbe_regs */
#define VBE_DISPI_ID0 0xB0C0
#define VBE_DISPI_ID1 0xB0C1
#define VBE_DISPI_ID2 0xB0C2
#define VBE_DISPI_ID3 0xB0C3
#define VBE_DISPI_ID4 0xB0C4
#define VBE_DISPI_ID5 0xB0C5
#define VBE_DISPI_DISABLED 0x00
#define VBE_DISPI_ENABLED 0x01
#define VBE_DISPI_GETCAPS 0x02
#define VBE_DISPI_8BIT_DAC 0x20
#define VBE_DISPI_LFB_ENABLED 0x40
#define VBE_DISPI_NOCLEARMEM 0x80
#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000
#define CH_ATTR_SIZE (160 * 100)
#define VGA_MAX_HEIGHT 2048
struct vga_precise_retrace {
int64_t ticks_per_char;
int64_t total_chars;
int htotal;
int hstart;
int hend;
int vstart;
int vend;
int freq;
};
union vga_retrace {
struct vga_precise_retrace precise;
};
struct VGACommonState;
typedef uint8_t (* vga_retrace_fn)(struct VGACommonState *s);
typedef void (* vga_update_retrace_info_fn)(struct VGACommonState *s);
typedef struct VGACommonState {
MemoryRegion *legacy_address_space;
uint8_t *vram_ptr;
MemoryRegion vram;
MemoryRegion vram_vbe;
uint32_t vram_size;
uint32_t vram_size_mb; /* property */
uint32_t latch;
MemoryRegion *chain4_alias;
uint8_t sr_index;
uint8_t sr[256];
uint8_t gr_index;
uint8_t gr[256];
uint8_t ar_index;
uint8_t ar[21];
int ar_flip_flop;
uint8_t cr_index;
uint8_t cr[256]; /* CRT registers */
uint8_t msr; /* Misc Output Register */
uint8_t fcr; /* Feature Control Register */
uint8_t st00; /* status 0 */
uint8_t st01; /* status 1 */
uint8_t dac_state;
uint8_t dac_sub_index;
uint8_t dac_read_index;
uint8_t dac_write_index;
uint8_t dac_cache[3]; /* used when writing */
int dac_8bit;
uint8_t palette[768];
int32_t bank_offset;
int (*get_bpp)(struct VGACommonState *s);
void (*get_offsets)(struct VGACommonState *s,
uint32_t *pline_offset,
uint32_t *pstart_addr,
uint32_t *pline_compare);
void (*get_resolution)(struct VGACommonState *s,
int *pwidth,
int *pheight);
/* bochs vbe state */
uint16_t vbe_index;
uint16_t vbe_regs[VBE_DISPI_INDEX_NB];
uint32_t vbe_start_addr;
uint32_t vbe_line_offset;
uint32_t vbe_bank_mask;
int vbe_mapped;
/* display refresh support */
QemuConsole *con;
uint32_t font_offsets[2];
int graphic_mode;
uint8_t shift_control;
uint8_t double_scan;
uint32_t line_offset;
uint32_t line_compare;
uint32_t start_addr;
uint32_t plane_updated;
uint32_t last_line_offset;
uint8_t last_cw, last_ch;
uint32_t last_width, last_height; /* in chars or pixels */
uint32_t last_scr_width, last_scr_height; /* in pixels */
uint32_t last_depth; /* in bits */
uint8_t cursor_start, cursor_end;
bool cursor_visible_phase;
int64_t cursor_blink_time;
uint32_t cursor_offset;
unsigned int (*rgb_to_pixel)(unsigned int r,
unsigned int g, unsigned b);
const GraphicHwOps *hw_ops;
bool full_update_text;
bool full_update_gfx;
/* hardware mouse cursor support */
uint32_t invalidated_y_table[VGA_MAX_HEIGHT / 32];
void (*cursor_invalidate)(struct VGACommonState *s);
void (*cursor_draw_line)(struct VGACommonState *s, uint8_t *d, int y);
/* tell for each page if it has been updated since the last time */
uint32_t last_palette[256];
uint32_t last_ch_attr[CH_ATTR_SIZE]; /* XXX: make it dynamic */
/* retrace */
vga_retrace_fn retrace;
vga_update_retrace_info_fn update_retrace_info;
union vga_retrace retrace_info;
uint8_t is_vbe_vmstate;
} VGACommonState;
STATIC_INLINE int c6_to_8(int v)
{
int b;
v &= 0x3f;
b = v & 1;
return (v << 2) | (b << 1) | b;
}
void vga_common_init(VGACommonState *s);
void vga_init(VGACommonState *s, MemoryRegion *address_space,
MemoryRegion *address_space_io, bool init_vga_ports);
MemoryRegion *vga_init_io(VGACommonState *s,
const MemoryRegionPortio **vga_ports,
const MemoryRegionPortio **vbe_ports);
void vga_common_reset(VGACommonState *s);
void vga_sync_dirty_bitmap(VGACommonState *s);
void vga_dirty_log_start(VGACommonState *s);
void vga_dirty_log_stop(VGACommonState *s);
extern const VMStateDescription vmstate_vga_common;
uint32_t vga_ioport_read(void *opaque, uint32_t addr);
void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val);
uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr);
void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val);
void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2);
int vga_ioport_invalid(VGACommonState *s, uint32_t addr);
void vga_init_vbe(VGACommonState *s, MemoryRegion *address_space);
uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr);
void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val);
void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val);
extern const uint8_t sr_mask[8];
extern const uint8_t gr_mask[16];
#define VGABIOS_FILENAME "vgabios.bin"
#define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin"
extern const MemoryRegionOps vga_mem_ops;
#endif

459
src/qemuvga/vga_template.h Normal file
View File

@ -0,0 +1,459 @@
/*
* QEMU VGA Emulator templates
*
* Copyright (c) 2003 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#if DEPTH == 8
#define BPP 1
#define PIXEL_TYPE uint8_t
#elif DEPTH == 15 || DEPTH == 16
#define BPP 2
#define PIXEL_TYPE uint16_t
#elif DEPTH == 32
#define BPP 4
#define PIXEL_TYPE uint32_t
#else
#error unsupport depth
#endif
#ifdef BGR_FORMAT
#define PIXEL_NAME glue(DEPTH, bgr)
#else
#define PIXEL_NAME DEPTH
#endif /* BGR_FORMAT */
#if DEPTH != 15 && !defined(BGR_FORMAT)
static inline void glue(vga_draw_glyph_line_, DEPTH)(uint8_t *d,
uint32_t font_data,
uint32_t xorcol,
uint32_t bgcol)
{
#if BPP == 1
((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
#elif BPP == 2
((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
#else
((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
#endif
}
static void glue(vga_draw_glyph8_, DEPTH)(uint8_t *d, int linesize,
const uint8_t *font_ptr, int h,
uint32_t fgcol, uint32_t bgcol)
{
uint32_t font_data, xorcol;
xorcol = bgcol ^ fgcol;
do {
font_data = font_ptr[0];
glue(vga_draw_glyph_line_, DEPTH)(d, font_data, xorcol, bgcol);
font_ptr += 4;
d += linesize;
} while (--h);
}
static void glue(vga_draw_glyph16_, DEPTH)(uint8_t *d, int linesize,
const uint8_t *font_ptr, int h,
uint32_t fgcol, uint32_t bgcol)
{
uint32_t font_data, xorcol;
xorcol = bgcol ^ fgcol;
do {
font_data = font_ptr[0];
glue(vga_draw_glyph_line_, DEPTH)(d,
expand4to8[font_data >> 4],
xorcol, bgcol);
glue(vga_draw_glyph_line_, DEPTH)(d + 8 * BPP,
expand4to8[font_data & 0x0f],
xorcol, bgcol);
font_ptr += 4;
d += linesize;
} while (--h);
}
static void glue(vga_draw_glyph9_, DEPTH)(uint8_t *d, int linesize,
const uint8_t *font_ptr, int h,
uint32_t fgcol, uint32_t bgcol, int dup9)
{
uint32_t font_data, xorcol, v;
xorcol = bgcol ^ fgcol;
do {
font_data = font_ptr[0];
#if BPP == 1
cpu_to_32wu((uint32_t *)d, (dmask16[(font_data >> 4)] & xorcol) ^ bgcol);
v = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
cpu_to_32wu(((uint32_t *)d)+1, v);
if (dup9)
((uint8_t *)d)[8] = v >> (24 * (1 - BIG));
else
((uint8_t *)d)[8] = bgcol;
#elif BPP == 2
cpu_to_32wu(((uint32_t *)d)+0, (dmask4[(font_data >> 6)] & xorcol) ^ bgcol);
cpu_to_32wu(((uint32_t *)d)+1, (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol);
cpu_to_32wu(((uint32_t *)d)+2, (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol);
v = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
cpu_to_32wu(((uint32_t *)d)+3, v);
if (dup9)
((uint16_t *)d)[8] = v >> (16 * (1 - BIG));
else
((uint16_t *)d)[8] = bgcol;
#else
((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
v = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
((uint32_t *)d)[7] = v;
if (dup9)
((uint32_t *)d)[8] = v;
else
((uint32_t *)d)[8] = bgcol;
#endif
font_ptr += 4;
d += linesize;
} while (--h);
}
/*
* 4 color mode
*/
static void glue(vga_draw_line2_, DEPTH)(VGACommonState *s1, uint8_t *d,
const uint8_t *s, int width)
{
uint32_t plane_mask, *palette, data, v;
int x;
palette = s1->last_palette;
plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
width >>= 3;
for(x = 0; x < width; x++) {
data = ((uint32_t *)s)[0];
data &= plane_mask;
v = expand2[GET_PLANE(data, 0)];
v |= expand2[GET_PLANE(data, 2)] << 2;
((PIXEL_TYPE *)d)[0] = palette[v >> 12];
((PIXEL_TYPE *)d)[1] = palette[(v >> 8) & 0xf];
((PIXEL_TYPE *)d)[2] = palette[(v >> 4) & 0xf];
((PIXEL_TYPE *)d)[3] = palette[(v >> 0) & 0xf];
v = expand2[GET_PLANE(data, 1)];
v |= expand2[GET_PLANE(data, 3)] << 2;
((PIXEL_TYPE *)d)[4] = palette[v >> 12];
((PIXEL_TYPE *)d)[5] = palette[(v >> 8) & 0xf];
((PIXEL_TYPE *)d)[6] = palette[(v >> 4) & 0xf];
((PIXEL_TYPE *)d)[7] = palette[(v >> 0) & 0xf];
d += BPP * 8;
s += 4;
}
}
#if BPP == 1
#define PUT_PIXEL2(d, n, v) ((uint16_t *)d)[(n)] = (v)
#elif BPP == 2
#define PUT_PIXEL2(d, n, v) ((uint32_t *)d)[(n)] = (v)
#else
#define PUT_PIXEL2(d, n, v) \
((uint32_t *)d)[2*(n)] = ((uint32_t *)d)[2*(n)+1] = (v)
#endif
/*
* 4 color mode, dup2 horizontal
*/
static void glue(vga_draw_line2d2_, DEPTH)(VGACommonState *s1, uint8_t *d,
const uint8_t *s, int width)
{
uint32_t plane_mask, *palette, data, v;
int x;
palette = s1->last_palette;
plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
width >>= 3;
for(x = 0; x < width; x++) {
data = ((uint32_t *)s)[0];
data &= plane_mask;
v = expand2[GET_PLANE(data, 0)];
v |= expand2[GET_PLANE(data, 2)] << 2;
PUT_PIXEL2(d, 0, palette[v >> 12]);
PUT_PIXEL2(d, 1, palette[(v >> 8) & 0xf]);
PUT_PIXEL2(d, 2, palette[(v >> 4) & 0xf]);
PUT_PIXEL2(d, 3, palette[(v >> 0) & 0xf]);
v = expand2[GET_PLANE(data, 1)];
v |= expand2[GET_PLANE(data, 3)] << 2;
PUT_PIXEL2(d, 4, palette[v >> 12]);
PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]);
PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]);
PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]);
d += BPP * 16;
s += 4;
}
}
/*
* 16 color mode
*/
static void glue(vga_draw_line4_, DEPTH)(VGACommonState *s1, uint8_t *d,
const uint8_t *s, int width)
{
uint32_t plane_mask, data, v, *palette;
int x;
palette = s1->last_palette;
plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
width >>= 3;
for(x = 0; x < width; x++) {
data = ((uint32_t *)s)[0];
data &= plane_mask;
v = expand4[GET_PLANE(data, 0)];
v |= expand4[GET_PLANE(data, 1)] << 1;
v |= expand4[GET_PLANE(data, 2)] << 2;
v |= expand4[GET_PLANE(data, 3)] << 3;
((PIXEL_TYPE *)d)[0] = palette[v >> 28];
((PIXEL_TYPE *)d)[1] = palette[(v >> 24) & 0xf];
((PIXEL_TYPE *)d)[2] = palette[(v >> 20) & 0xf];
((PIXEL_TYPE *)d)[3] = palette[(v >> 16) & 0xf];
((PIXEL_TYPE *)d)[4] = palette[(v >> 12) & 0xf];
((PIXEL_TYPE *)d)[5] = palette[(v >> 8) & 0xf];
((PIXEL_TYPE *)d)[6] = palette[(v >> 4) & 0xf];
((PIXEL_TYPE *)d)[7] = palette[(v >> 0) & 0xf];
d += BPP * 8;
s += 4;
}
}
/*
* 16 color mode, dup2 horizontal
*/
static void glue(vga_draw_line4d2_, DEPTH)(VGACommonState *s1, uint8_t *d,
const uint8_t *s, int width)
{
uint32_t plane_mask, data, v, *palette;
int x;
palette = s1->last_palette;
plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
width >>= 3;
for(x = 0; x < width; x++) {
data = ((uint32_t *)s)[0];
data &= plane_mask;
v = expand4[GET_PLANE(data, 0)];
v |= expand4[GET_PLANE(data, 1)] << 1;
v |= expand4[GET_PLANE(data, 2)] << 2;
v |= expand4[GET_PLANE(data, 3)] << 3;
PUT_PIXEL2(d, 0, palette[v >> 28]);
PUT_PIXEL2(d, 1, palette[(v >> 24) & 0xf]);
PUT_PIXEL2(d, 2, palette[(v >> 20) & 0xf]);
PUT_PIXEL2(d, 3, palette[(v >> 16) & 0xf]);
PUT_PIXEL2(d, 4, palette[(v >> 12) & 0xf]);
PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]);
PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]);
PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]);
d += BPP * 16;
s += 4;
}
}
/*
* 256 color mode, double pixels
*
* XXX: add plane_mask support (never used in standard VGA modes)
*/
static void glue(vga_draw_line8d2_, DEPTH)(VGACommonState *s1, uint8_t *d,
const uint8_t *s, int width)
{
uint32_t *palette;
int x;
palette = s1->last_palette;
width >>= 3;
for(x = 0; x < width; x++) {
PUT_PIXEL2(d, 0, palette[s[0]]);
PUT_PIXEL2(d, 1, palette[s[1]]);
PUT_PIXEL2(d, 2, palette[s[2]]);
PUT_PIXEL2(d, 3, palette[s[3]]);
d += BPP * 8;
s += 4;
}
}
/*
* standard 256 color mode
*
* XXX: add plane_mask support (never used in standard VGA modes)
*/
static void glue(vga_draw_line8_, DEPTH)(VGACommonState *s1, uint8_t *d,
const uint8_t *s, int width)
{
uint32_t *palette;
int x;
palette = s1->last_palette;
width >>= 3;
for(x = 0; x < width; x++) {
((PIXEL_TYPE *)d)[0] = palette[s[0]];
((PIXEL_TYPE *)d)[1] = palette[s[1]];
((PIXEL_TYPE *)d)[2] = palette[s[2]];
((PIXEL_TYPE *)d)[3] = palette[s[3]];
((PIXEL_TYPE *)d)[4] = palette[s[4]];
((PIXEL_TYPE *)d)[5] = palette[s[5]];
((PIXEL_TYPE *)d)[6] = palette[s[6]];
((PIXEL_TYPE *)d)[7] = palette[s[7]];
d += BPP * 8;
s += 8;
}
}
#endif /* DEPTH != 15 */
/* XXX: optimize */
/*
* 15 bit color
*/
static void glue(vga_draw_line15_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d,
const uint8_t *s, int width)
{
#if DEPTH == 15 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
memcpy(d, s, width * 2);
#else
int w;
uint32_t v, r, g, b;
w = width;
do {
v = lduw_raw((void *)s);
r = (v >> 7) & 0xf8;
g = (v >> 2) & 0xf8;
b = (v << 3) & 0xf8;
((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
s += 2;
d += BPP;
} while (--w != 0);
#endif
}
/*
* 16 bit color
*/
static void glue(vga_draw_line16_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d,
const uint8_t *s, int width)
{
#if DEPTH == 16 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN)
memcpy(d, s, width * 2);
#else
int w;
uint32_t v, r, g, b;
w = width;
do {
v = lduw_raw((void *)s);
r = (v >> 8) & 0xf8;
g = (v >> 3) & 0xfc;
b = (v << 3) & 0xf8;
((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
s += 2;
d += BPP;
} while (--w != 0);
#endif
}
/*
* 24 bit color
*/
static void glue(vga_draw_line24_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d,
const uint8_t *s, int width)
{
int w;
uint32_t r, g, b;
w = width;
do {
#if defined(TARGET_WORDS_BIGENDIAN)
r = s[0];
g = s[1];
b = s[2];
#else
b = s[0];
g = s[1];
r = s[2];
#endif
((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
s += 3;
d += BPP;
} while (--w != 0);
}
/*
* 32 bit color
*/
static void glue(vga_draw_line32_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d,
const uint8_t *s, int width)
{
#if DEPTH == 32 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) && !defined(BGR_FORMAT)
memcpy(d, s, width * 4);
#else
int w;
uint32_t r, g, b;
w = width;
do {
#if defined(TARGET_WORDS_BIGENDIAN)
r = s[1];
g = s[2];
b = s[3];
#else
b = s[0];
g = s[1];
r = s[2];
#endif
((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b);
s += 4;
d += BPP;
} while (--w != 0);
#endif
}
#undef PUT_PIXEL2
#undef DEPTH
#undef BPP
#undef PIXEL_TYPE
#undef PIXEL_NAME
#undef BGR_FORMAT

676
src/scsitape.c Normal file
View File

@ -0,0 +1,676 @@
/*
* UAE - The Un*x Amiga Emulator
*
* SCSI tape emulation
*
* Copyright 2013 Toni Wilen
*
*/
#include "sysconfig.h"
#include "sysdeps.h"
#include "options.h"
#include "filesys.h"
#include "scsi.h"
#include "blkdev.h"
#include "zfile.h"
#include "memory.h"
#include "a2091.h"
#include "fsdb.h"
int log_tapeemu = 1;
#define TAPE_INDEX _T("index.tape")
static struct scsi_data_tape *tapeunits[MAX_FILESYSTEM_UNITS];
static bool notape (struct scsi_data_tape *tape)
{
return tape->tape_dir[0] == 0 || tape->nomedia;
}
static int rl (uae_u8 *p)
{
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]);
}
static void wl (uae_u8 *p, int v)
{
p[0] = v >> 24;
p[1] = v >> 16;
p[2] = v >> 8;
p[3] = v;
}
void tape_free (struct scsi_data_tape *tape)
{
if (!tape)
return;
zfile_fclose (tape->zf);
zfile_fclose (tape->index);
zfile_closedir_archive (tape->zd);
xfree(tape);
for (int i = 0; i < MAX_FILESYSTEM_UNITS; i++) {
if (tapeunits[i] == tape)
tapeunits[i] = NULL;
}
}
static void tape_init (int unit, struct scsi_data_tape *tape, const TCHAR *tape_directory, bool readonly)
{
TCHAR path[MAX_DPATH];
memset (tape, 0, sizeof (struct scsi_data_tape));
_tcscpy (tape->tape_dir, tape_directory);
tape->blocksize = 512;
tape->wp = readonly;
tape->beom = -1;
tape->nomedia = false;
tape->unloaded = false;
if (my_existsdir (tape->tape_dir)) {
tape->realdir = true;
} else {
tape->zd = zfile_opendir_archive (tape_directory);
//tape->zd = zfile_opendir_archive (tape_directory, ZFD_ARCHIVE | ZFD_NORECURSE);
if (!tape->zd)
tape->nomedia = true;
}
_tcscpy (path, tape_directory);
_tcscat (path, FSDB_DIR_SEPARATOR_S);
_tcscat (path, TAPE_INDEX);
tape->index = zfile_fopen (path, _T("rb"), ZFD_NORMAL);
if (tape->index)
write_log (_T("TAPEEMU INDEX: '%s'\n"), path);
tapeunits[unit] = tape;
}
struct scsi_data_tape *tape_alloc (int unitnum, const TCHAR *tape_directory, bool readonly)
{
struct scsi_data_tape *tape = xcalloc (struct scsi_data_tape, 1);
tape_init (unitnum, tape, tape_directory, readonly);
return tape;
}
void tape_media_change (int unitnum, struct uaedev_config_info *ci)
{
struct scsi_data_tape *tape = tapeunits[unitnum];
if (!tape)
return;
tape_init (unitnum, tape, ci->rootdir, ci->readonly);
}
bool tape_get_info (int unitnum, struct device_info *di)
{
struct scsi_data_tape *tape = tapeunits[unitnum];
memset (di, 0, sizeof (struct device_info));
if (!tape)
return false;
di->media_inserted = notape (tape) == false;
_tcscpy (di->label, tape->tape_dir);
return true;
}
static void tape_rewind (struct scsi_data_tape *tape)
{
zfile_fclose (tape->zf);
tape->zf = NULL;
my_closedir (tape->od);
tape->file_number = 0;
tape->file_offset = 0;
tape->od = NULL;
tape->beom = -1;
if (tape->index)
zfile_fseek (tape->index, 0, SEEK_SET);
if (tape->zd)
zfile_resetdir_archive (tape->zd);
}
static void erase (struct scsi_data_tape *tape)
{
struct my_opendir_s *od;
if (!tape->realdir)
return;
tape_rewind (tape);
od = my_opendir (tape->tape_dir);
if (od) {
for (;;) {
TCHAR path[MAX_DPATH], filename[MAX_DPATH];
if (!my_readdir (od, filename))
break;
TCHAR *ext = _tcsrchr (filename, '.');
if (ext && !_tcsicmp (ext, _T(".tape"))) {
_stprintf (path, _T("%s%s%s"), tape->tape_dir, FSDB_DIR_SEPARATOR_S, filename);
if (my_existsfile (path))
my_unlink (path);
}
}
my_closedir (od);
}
}
static bool next_file (struct scsi_data_tape *tape)
{
zfile_fclose (tape->zf);
tape->zf = NULL;
tape->file_offset = 0;
if (tape->index) {
TCHAR path[MAX_DPATH];
TCHAR name[256];
name[0] = 0;
zfile_fgets (name, sizeof name / sizeof (TCHAR), tape->index);
my_trim (name);
if (name[0] == 0)
goto end;
_tcscpy (path, tape->tape_dir);
_tcscat (path, FSDB_DIR_SEPARATOR_S);
_tcscat (path, name);
tape->zf = zfile_fopen (path, _T("rb"), ZFD_NORMAL);
write_log (_T("TAPEEMU: File '%s'\n"), path);
} else if (tape->realdir) {
TCHAR path[MAX_DPATH], filename[MAX_DPATH];
if (tape->od == NULL)
tape->od = my_opendir (tape->tape_dir);
if (!tape->od) {
tape_free (tape);
goto end;
}
for (;;) {
if (!my_readdir (tape->od, filename))
goto end;
if (_tcsicmp (filename, TAPE_INDEX))
continue;
_stprintf (path, _T("%s%s%s"), tape->tape_dir, FSDB_DIR_SEPARATOR_S, filename);
if (!my_existsfile (path))
continue;
tape->zf = zfile_fopen (path, _T("rb"), 0);
if (!tape->zf)
continue;
break;
}
if (tape->zf)
write_log (_T("TAPEEMU DIR: File '%s'\n"), zfile_getname (tape->zf));
} else {
tape->zf = zfile_readdir_archive_open (tape->zd, _T("rb"));
if (tape->zf)
write_log (_T("TAPEEMU ARC: File '%s'\n"), zfile_getname (tape->zf));
}
if (tape->zf) {
tape->file_number++;
return true;
}
end:
write_log (_T("TAPEEMU: end of tape\n"));
tape->beom = 1;
return false;
}
static int tape_read (struct scsi_data_tape *tape, uae_u8 *scsi_data, int len, bool *eof)
{
int got;
*eof = false;
if (tape->beom > 0) {
*eof = true;
return -1;
}
if (!tape->zf) {
tape_rewind (tape);
if (!next_file (tape))
return -1;
}
zfile_fseek (tape->zf, tape->file_offset, SEEK_SET);
got = zfile_fread (scsi_data, 1, len, tape->zf);
tape->file_offset += got;
if (got < len) {
*eof = true;
next_file (tape);
}
return got;
}
static int tape_write (struct scsi_data_tape *tape, uae_u8 *scsi_data, int len)
{
TCHAR path[MAX_DPATH], numname[30];
int exists;
if (!tape->realdir)
return -1;
_stprintf (numname, _T("%05d.tape"), tape->file_number);
_stprintf (path, _T("%s%s%s"), tape->tape_dir, FSDB_DIR_SEPARATOR_S, numname);
exists = my_existsfile (path);
struct zfile *zf = zfile_fopen (path, _T("a+b"), 0);
if (!zf)
return -1;
zfile_fseek (zf, 0, SEEK_END);
len = zfile_fwrite (scsi_data, 1, len, zf);
zfile_fclose (zf);
if (!exists) {
_stprintf (path, _T("%s%s%s"), tape->tape_dir, FSDB_DIR_SEPARATOR_S, TAPE_INDEX);
zf = zfile_fopen (path, _T("a+b"), 0);
if (zf) {
zfile_fputs (zf, numname);
zfile_fputs (zf, _T("\n"));
}
zfile_fclose (zf);
}
return len;
}
int scsi_tape_emulate (struct scsi_data_tape *tape, uae_u8 *cmdbuf, int scsi_cmd_len,
uae_u8 *scsi_data, int *data_len, uae_u8 *r, int *reply_len, uae_u8 *s, int *sense_len)
{
uae_s64 len;
int lr = 0, ls = 0;
int scsi_len = -1;
int status = 0;
int lun;
bool eof;
if (log_tapeemu)
write_log (_T("TAPEEMU: %02X.%02X.%02X.%02X.%02X.%02X\n"),
cmdbuf[0], cmdbuf[1], cmdbuf[2], cmdbuf[3], cmdbuf[4], cmdbuf[5]);
if (cmdbuf[0] == 3) {
if (tape->beom == -1)
s[9] |= 0x8; // beginning of media
if (tape->beom == 1)
s[2] |= 0x40; // end of media
if (*sense_len < 0x12)
*sense_len = 0x12;
return 0;
}
*reply_len = *sense_len = 0;
memset (r, 0, 256);
memset (s, 0, 256);
s[0] = 0x70;
lun = cmdbuf[1] >> 5;
if (cmdbuf[0] != 0x03 && cmdbuf[0] != 0x12 && lun) {
status = 2; /* CHECK CONDITION */
s[0] = 0x70;
s[2] = 5; /* ILLEGAL REQUEST */
s[12] = 0x25; /* INVALID LUN */
ls = 0x12;
goto err;
}
switch (cmdbuf[0])
{
case 0x00: /* TEST UNIT READY */
if (notape (tape))
goto notape;
if (tape->unloaded)
goto unloaded;
scsi_len = 0;
break;
case 0x19: /* ERASE */
if (log_tapeemu)
write_log (_T("TAPEEMU ERASE\n"));
if (notape (tape))
goto notape;
if (tape->unloaded)
goto unloaded;
if (tape->wp)
goto writeprot;
erase (tape);
tape_rewind (tape);
tape->beom = 1;
scsi_len = 0;
break;
case 0x1b: /* LOAD/UNLOAD */
{
bool load = (cmdbuf[4] & 1) != 0;
bool ret = (cmdbuf[4] & 2) != 0;
bool eot = (cmdbuf[4] & 4) != 0;
if (log_tapeemu)
write_log (_T("TAPEEMU LOAD/UNLOAD %d:%d:%d\n"), eot, ret, load);
if (notape (tape))
goto notape;
if (load && eot)
goto errreq;
tape_rewind (tape);
tape->unloaded = !load;
if (eot) {
tape->beom = 1;
} else {
tape->beom = -1;
}
scsi_len = 0;
break;
}
case 0x01: /* REWIND */
if (log_tapeemu)
write_log (_T("TAPEEMU: REWIND. IMMED=%d\n"), cmdbuf[1] & 1);
if (notape (tape))
goto notape;
if (tape->unloaded)
goto unloaded;
tape_rewind (tape);
scsi_len = 0;
break;
case 0x11: /* SPACE */
{
int code = cmdbuf[1] & 15;
int count = rl (cmdbuf + 1) & 0xffffff;
if (log_tapeemu)
write_log (_T("TAPEEMU: SPACE code=%d count=%d\n"), code, count);
if (notape (tape))
goto notape;
if (tape->unloaded)
goto unloaded;
if (code >= 2)
goto errreq;
if (code == 1) {
if (!next_file (tape))
goto endoftape;
}
scsi_len = 0;
}
break;
case 0x10: /* WRITE FILEMARK */
len = rl (cmdbuf + 1) & 0xffffff;
if (log_tapeemu)
write_log (_T("TAPEEMU WRITE FILEMARK %d\n"), len);
if (notape (tape))
goto notape;
if (tape->unloaded)
goto unloaded;
if (tape->wp)
goto writeprot;
if (len > 0) {
tape->file_number++;
tape_write (tape, scsi_data, 0);
}
scsi_len = 0;
break;
case 0x0a: /* WRITE (6) */
len = rl (cmdbuf + 1) & 0xffffff;
if (cmdbuf[1] & 1)
len *= tape->blocksize;
if (log_tapeemu)
write_log (_T("TAPEEMU WRITE %d (%d, %d)\n"), len, rl (cmdbuf + 1) & 0xffffff, cmdbuf[1] & 1);
if (notape (tape))
goto notape;
if (tape->unloaded)
goto unloaded;
if (tape->wp)
goto writeprot;
if (tape->beom < 0)
tape->beom = 0;
scsi_len = tape_write (tape, scsi_data, len);
if (scsi_len < 0)
goto writeprot;
break;
case 0x08: /* READ (6) */
len = rl (cmdbuf + 1) & 0xffffff;
if (cmdbuf[1] & 1)
len *= tape->blocksize;
if (log_tapeemu)
write_log (_T("TAPEEMU READ %d (%d, %d)\n"), len, rl (cmdbuf + 1) & 0xffffff, cmdbuf[1] & 1);
if (notape (tape))
goto notape;
if (tape->unloaded)
goto unloaded;
if (tape->beom < 0)
tape->beom = 0;
scsi_len = tape_read (tape, scsi_data, len, &eof);
if (log_tapeemu)
write_log (_T("-> READ %d bytes\n"), scsi_len);
if ((cmdbuf[1] & 1) && scsi_len > 0 && scsi_len < len) {
int gap = tape->blocksize - (scsi_len & (tape->blocksize - 1));
if (gap > 0 && gap < tape->blocksize)
memset (scsi_data + scsi_len, 0, gap);
scsi_len += tape->blocksize - 1;
scsi_len &= ~(tape->blocksize - 1);
}
if (scsi_len < 0) {
tape->beom = 2;
status = SCSI_STATUS_CHECK_CONDITION;
s[0] = 0x80 | 0x70;
s[2] = 8; /* BLANK CHECK */
if (cmdbuf[1] & 1)
wl (&s[3], len / tape->blocksize);
else
wl (&s[3], len);
s[12] = 0;
s[13] = 2; /* End-of-partition/medium detected */
ls = 0x12;
} else if (eof) {
status = SCSI_STATUS_CHECK_CONDITION;
s[0] = 0x80 | 0x70; // Valid + code
s[2] = 0x80 | 0; /* File Mark Detected + NO SENSE */
if (cmdbuf[1] & 1)
wl (&s[3], (len - scsi_len) / tape->blocksize);
else
wl (&s[3], len);
s[12] = 0;
s[13] = 1; /* File Mark detected */
ls = 0x12;
if (log_tapeemu)
write_log (_T("TAPEEMU READ FILE END, %d remaining\n"), len - scsi_len);
}
break;
case 0x5a: // MODE SENSE(10)
case 0x1a: /* MODE SENSE(6) */
{
uae_u8 *p;
int maxlen;
bool pcodeloop = false;
bool sense10 = cmdbuf[0] == 0x5a;
int totalsize, bdsize;
int pc = cmdbuf[2] >> 6;
int pcode = cmdbuf[2] & 0x3f;
int dbd = cmdbuf[1] & 8;
if (cmdbuf[0] == 0x5a)
dbd = 1;
if (log_tapeemu)
write_log (_T("TAPEEMU MODE SENSE PC=%d CODE=%d DBD=%d\n"), pc, pcode, dbd);
p = r;
if (sense10) {
totalsize = 8 - 2;
maxlen = (cmdbuf[7] << 8) | cmdbuf[8];
p[2] = 0;
p[3] = 0;
p[4] = 0;
p[5] = 0;
p[6] = 0;
p[7] = 0;
p += 8;
} else {
totalsize = 4 - 1;
maxlen = cmdbuf[4];
p[1] = 0;
p[2] = tape->wp ? 0x80 : 0;
p[3] = 0;
p += 4;
}
bdsize = 0;
if (!dbd) {
wl(p + 0, 0);
wl(p + 4, tape->blocksize);
bdsize = 8;
p += bdsize;
}
if (pcode)
goto errreq;
if (sense10) {
totalsize += bdsize;
r[6] = bdsize >> 8;
r[7] = bdsize & 0xff;
r[0] = totalsize >> 8;
r[1] = totalsize & 0xff;
} else {
totalsize += bdsize;
r[3] = bdsize & 0xff;
r[0] = totalsize & 0xff;
}
lr = totalsize + 1;
if (lr > maxlen)
lr = maxlen;
}
break;
case 0x55: // MODE SELECT(10)
case 0x15: // MODE SELECT(6)
{
uae_u8 *p;
bool mode10 = cmdbuf[0] == 0x55;
int nb = 0, bl = 512;
p = scsi_data + 4;
if (mode10)
p += 4;
if (scsi_data[3] >= 8) {
nb = rl (p) & 0xffffff;
bl = rl (p + 4) & 0xffffff;
}
if (log_tapeemu)
write_log (_T("TAPEEMU MODE SELECT BUFM=%d BDL=%d Density=%d NB=%d BL=%d\n"), (scsi_data[2] & 0x10) != 0, scsi_data[3], p[0], nb, bl);
if (nb || bl != 512)
goto err;
scsi_len = 0;
break;
}
case 0x05: /* READ BLOCK LIMITS */
if (log_tapeemu)
write_log (_T("TAPEEMU READ BLOCK LIMITS\n"));
r[0] = 0;
r[1] = 0;
r[2] = 2; // 0x200 = 512
r[3] = 0;
r[4] = 2; // 0x200 = 512
r[5] = 0;
lr = 6;
break;
case 0x16: /* RESERVE UNIT */
if (log_tapeemu)
write_log (_T("TAPEEMU RESERVE UNIT\n"));
scsi_len = 0;
break;
case 0x17: /* RELEASE UNIT */
if (log_tapeemu)
write_log (_T("TAPEEMU RELEASE UNIT\n"));
scsi_len = 0;
break;
case 0x1e: /* PREVENT/ALLOW MEDIUM REMOVAL */
if (log_tapeemu)
write_log (_T("TAPEEMU PREVENT/ALLOW MEDIUM REMOVAL\n"));
scsi_len = 0;
break;
case 0x12: /* INQUIRY */
{
if (log_tapeemu)
write_log (_T("TAPEEMU INQUIRY\n"));
if ((cmdbuf[1] & 1) || cmdbuf[2] != 0)
goto err;
len = cmdbuf[4];
if (cmdbuf[1] >> 5) {
r[0] = 0x7f;
} else {
r[0] = INQ_SEQD;
}
r[1] |= 0x80; // removable
r[2] = 2; /* supports SCSI-2 */
r[3] = 2; /* response data format */
r[4] = 32; /* additional length */
r[7] = 0;
scsi_len = lr = len < 36 ? len : 36;
r[2] = 2;
r[3] = 2;
char *s = ua (_T("UAE"));
memcpy (r + 8, s, strlen (s));
xfree (s);
s = ua (_T("SCSI TAPE"));
memcpy (r + 16, s, strlen (s));
xfree (s);
s = ua (_T("0.1"));
memcpy (r + 32, s, strlen (s));
xfree (s);
for (int i = 8; i < 36; i++) {
if (r[i] == 0)
r[i] = 32;
}
}
break;
default:
write_log (_T("TAPEEMU: unsupported scsi command 0x%02X\n"), cmdbuf[0]);
err:
status = SCSI_STATUS_CHECK_CONDITION;
s[0] = 0x70;
s[2] = SCSI_SK_ILLEGAL_REQ;
s[12] = SCSI_INVALID_COMMAND;
ls = 0x12;
break;
writeprot:
status = 2; /* CHECK CONDITION */
s[0] = 0x70;
s[2] = 7; /* DATA PROTECT */
s[12] = 0x27; /* WRITE PROTECTED */
ls = 0x12;
break;
errreq:
lr = -1;
status = SCSI_STATUS_CHECK_CONDITION;
s[0] = 0x70;
s[2] = SCSI_SK_ILLEGAL_REQ;
s[12] = SCSI_INVALID_FIELD;
ls = 0x12;
break;
endoftape:
status = SCSI_STATUS_CHECK_CONDITION;
s[0] = 0x70;
s[2] = SCSI_SK_MED_ERR;
s[12] = 0;
s[13] = 2; /* End-of-partition/medium detected */
ls = 0x12;
break;
unloaded:
status = SCSI_STATUS_CHECK_CONDITION;
s[0] = 0x70;
s[2] = SCSI_SK_NOT_READY;
s[12] = SCSI_NOT_READY;
ls = 0x12;
break;
notape:
status = SCSI_STATUS_CHECK_CONDITION;
s[0] = 0x70;
s[2] = SCSI_SK_NOT_READY;
s[12] = SCSI_MEDIUM_NOT_PRESENT;
ls = 0x12;
break;
}
*data_len = scsi_len;
*reply_len = lr;
*sense_len = ls;
if (ls > 0) {
if (tape->beom == 1)
s[2] |= 0x40;
if (tape->beom == -1)
s[9] |= 0x8;
if (log_tapeemu) {
write_log (_T("TAPEEMU SENSE: "));
for (int i = 0; i < ls; i++)
write_log (_T("%02X."), s[i]);
write_log (_T("\n"));
}
}
return status;
}