Commit 313aa567 authored by bellard's avatar bellard
Browse files

added VGA emulation - added PS/2 mouse and keyboard emulation - use SDL for VGA display


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@356 c046a42c-6fe2-441c-8c8c-71466251a162
parent 4cbf74b6
......@@ -74,6 +74,9 @@
/* debug PC keyboard */
//#define DEBUG_KBD
/* debug PC keyboard : only mouse */
//#define DEBUG_MOUSE
#define PHYS_RAM_BASE 0xac000000
#define PHYS_RAM_MAX_SIZE (256 * 1024 * 1024)
......@@ -81,6 +84,8 @@
#define INITRD_LOAD_ADDR 0x00400000
#define KERNEL_PARAMS_ADDR 0x00090000
#define GUI_REFRESH_INTERVAL 30
#define MAX_DISKS 2
/* from plex86 (BSD license) */
......@@ -198,9 +203,6 @@ struct __attribute__ ((packed)) linux_params {
#define KERNEL_CS 0x10
#define KERNEL_DS 0x18
typedef void (IOPortWriteFunc)(CPUX86State *env, uint32_t address, uint32_t data);
typedef uint32_t (IOPortReadFunc)(CPUX86State *env, uint32_t address);
#define MAX_IOPORTS 4096
static const char *interp_prefix = CONFIG_QEMU_PREFIX;
......@@ -212,6 +214,11 @@ int loglevel;
IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS];
IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS];
BlockDriverState *bs_table[MAX_DISKS];
int vga_ram_size;
static DisplayState display_state;
int nodisp;
int term_inited;
int64_t ticks_per_sec;
/***********************************************************/
/* x86 io ports */
......@@ -430,49 +437,6 @@ void hw_error(const char *fmt, ...)
abort();
}
/***********************************************************/
/* vga emulation */
static uint8_t vga_index;
static uint8_t vga_regs[256];
static int last_cursor_pos;
void update_console_messages(void)
{
int c, i, cursor_pos, eol;
cursor_pos = vga_regs[0x0f] | (vga_regs[0x0e] << 8);
eol = 0;
for(i = last_cursor_pos; i < cursor_pos; i++) {
c = phys_ram_base[0xb8000 + (i) * 2];
if (c >= ' ') {
putchar(c);
eol = 0;
} else {
if (!eol)
putchar('\n');
eol = 1;
}
}
fflush(stdout);
last_cursor_pos = cursor_pos;
}
/* just to see first Linux console messages, we intercept cursor position */
void vga_ioport_write(CPUX86State *env, uint32_t addr, uint32_t data)
{
switch(addr) {
case 0x3d4:
vga_index = data;
break;
case 0x3d5:
vga_regs[vga_index] = data;
if (vga_index == 0x0f)
update_console_messages();
break;
}
}
/***********************************************************/
/* cmos emulation */
......@@ -555,6 +519,7 @@ void cmos_init(void)
/* various important CMOS locations needed by PC/Bochs bios */
cmos_data[REG_EQUIPMENT_BYTE] = 0x02; /* FPU is there */
cmos_data[REG_EQUIPMENT_BYTE] |= 0x04; /* PS/2 mouse installed */
/* memory size */
val = (phys_ram_size / 1024) - 1024;
......@@ -674,13 +639,13 @@ static void pic_update_irq(void)
int64_t irq_time[16];
int64_t cpu_get_ticks(void);
#endif
#ifdef DEBUG_PIC
#if defined(DEBUG_PIC)
int irq_level[16];
#endif
void pic_set_irq(int irq, int level)
{
#ifdef DEBUG_PIC
#if defined(DEBUG_PIC)
if (level != irq_level[irq]) {
printf("pic_set_irq: irq=%d level=%d\n", irq, level);
irq_level[irq] = level;
......@@ -702,7 +667,9 @@ int cpu_x86_get_pic_interrupt(CPUX86State *env)
/* signal the pic that the irq was acked by the CPU */
irq = pic_irq_requested;
#ifdef DEBUG_IRQ_LATENCY
printf("IRQ%d latency=%Ld\n", irq, cpu_get_ticks() - irq_time[irq]);
printf("IRQ%d latency=%0.3fus\n",
irq,
(double)(cpu_get_ticks() - irq_time[irq]) * 1000000.0 / ticks_per_sec);
#endif
#ifdef DEBUG_PIC
printf("pic_interrupt: irq=%d\n", irq);
......@@ -761,18 +728,22 @@ void pic_ioport_write(CPUX86State *env, uint32_t addr, uint32_t val)
}
if (val == 0xa0)
s->priority_add = (s->priority_add + 1) & 7;
pic_update_irq();
break;
case 0x60 ... 0x67:
priority = val & 7;
s->isr &= ~(1 << priority);
pic_update_irq();
break;
case 0xc0 ... 0xc7:
s->priority_add = (val + 1) & 7;
pic_update_irq();
break;
case 0xe0 ... 0xe7:
priority = val & 7;
s->isr &= ~(1 << priority);
s->priority_add = (priority + 1) & 7;
pic_update_irq();
break;
}
}
......@@ -861,8 +832,6 @@ int speaker_data_on;
int dummy_refresh_clock;
int pit_min_timer_count = 0;
int64_t ticks_per_sec;
int64_t get_clock(void)
{
struct timeval tv;
......@@ -1354,37 +1323,6 @@ void serial_received_byte(SerialState *s, int ch)
}
}
/* init terminal so that we can grab keys */
static struct termios oldtty;
static void term_exit(void)
{
tcsetattr (0, TCSANOW, &oldtty);
}
static void term_init(void)
{
struct termios tty;
tcgetattr (0, &tty);
oldtty = tty;
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
|INLCR|IGNCR|ICRNL|IXON);
tty.c_oflag |= OPOST;
tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
tty.c_cflag &= ~(CSIZE|PARENB);
tty.c_cflag |= CS8;
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 0;
tcsetattr (0, TCSANOW, &tty);
atexit(term_exit);
fcntl(0, F_SETFL, O_NONBLOCK);
}
void serial_init(void)
{
SerialState *s = &serial_ports[0];
......@@ -1393,8 +1331,6 @@ void serial_init(void)
register_ioport_write(0x3f8, 8, serial_ioport_write, 1);
register_ioport_read(0x3f8, 8, serial_ioport_read, 1);
term_init();
}
/***********************************************************/
......@@ -2597,69 +2533,111 @@ void ide_init(void)
#define KBD_MODE_RFU 0x80
/* Mouse Commands */
#define AUX_SET_RES 0xE8 /* Set resolution */
#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */
#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */
#define AUX_SET_RES 0xE8 /* Set resolution */
#define AUX_GET_SCALE 0xE9 /* Get scaling factor */
#define AUX_SET_STREAM 0xEA /* Set stream mode */
#define AUX_POLL 0xEB /* Poll */
#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */
#define AUX_SET_WRAP 0xEE /* Set wrap mode */
#define AUX_SET_REMOTE 0xF0 /* Set remote mode */
#define AUX_GET_TYPE 0xF2 /* Get type */
#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */
#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */
#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */
#define AUX_SET_DEFAULT 0xF6
#define AUX_RESET 0xFF /* Reset aux device */
#define AUX_ACK 0xFA /* Command byte ACK. */
#define KBD_QUEUE_SIZE 64
#define MOUSE_STATUS_REMOTE 0x40
#define MOUSE_STATUS_ENABLED 0x20
#define MOUSE_STATUS_SCALE21 0x10
#define KBD_QUEUE_SIZE 256
typedef struct {
uint8_t data[KBD_QUEUE_SIZE];
int rptr, wptr, count;
} KBDQueue;
enum KBDWriteState {
KBD_STATE_CMD = 0,
KBD_STATE_LED,
};
typedef struct KBDState {
KBDQueue queues[2];
uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
uint8_t status;
uint8_t mode;
/* keyboard state */
int kbd_write_cmd;
int scan_enabled;
/* mouse state */
int mouse_write_cmd;
uint8_t mouse_status;
uint8_t mouse_resolution;
uint8_t mouse_sample_rate;
uint8_t mouse_wrap;
uint8_t mouse_type; /* 0 = PS2, 3 = IMPS/2, 4 = IMEX */
uint8_t mouse_detect_state;
int mouse_dx; /* current values, needed for 'poll' mode */
int mouse_dy;
int mouse_dz;
uint8_t mouse_buttons;
} KBDState;
KBDState kbd_state;
int reset_requested;
int a20_enabled;
/* update irq and KBD_STAT_[MOUSE_]OBF */
static void kbd_update_irq(KBDState *s)
{
int level;
level = ((s->status & KBD_STAT_OBF) && (s->mode & KBD_MODE_KBD_INT));
pic_set_irq(1, level);
level = ((s->status & KBD_STAT_MOUSE_OBF) && (s->mode & KBD_MODE_MOUSE_INT));
pic_set_irq(12, level);
int irq12_level, irq1_level;
irq1_level = 0;
irq12_level = 0;
s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
if (s->queues[0].count != 0 ||
s->queues[1].count != 0) {
s->status |= KBD_STAT_OBF;
if (s->queues[1].count != 0) {
s->status |= KBD_STAT_MOUSE_OBF;
if (s->mode & KBD_MODE_MOUSE_INT)
irq12_level = 1;
} else {
if (s->mode & KBD_MODE_KBD_INT)
irq1_level = 1;
}
}
pic_set_irq(1, irq1_level);
pic_set_irq(12, irq12_level);
}
static void kbd_queue(KBDState *s, int b, int aux)
{
KBDQueue *q = &kbd_state.queues[aux];
#if defined(DEBUG_MOUSE) || defined(DEBUG_KBD)
if (aux)
printf("mouse event: 0x%02x\n", b);
#ifdef DEBUG_KBD
else
printf("kbd event: 0x%02x\n", b);
#endif
#endif
if (q->count >= KBD_QUEUE_SIZE)
return;
q->data[q->wptr] = b;
if (++q->wptr == KBD_QUEUE_SIZE)
q->wptr = 0;
q->count++;
s->status |= KBD_STAT_OBF;
if (aux)
s->status |= KBD_STAT_MOUSE_OBF;
kbd_update_irq(s);
}
void kbd_put_keycode(int keycode)
{
KBDState *s = &kbd_state;
kbd_queue(s, keycode, 0);
}
uint32_t kbd_read_status(CPUX86State *env, uint32_t addr)
{
KBDState *s = &kbd_state;
......@@ -2745,9 +2723,9 @@ uint32_t kbd_read_data(CPUX86State *env, uint32_t addr)
KBDQueue *q;
int val;
q = &s->queues[1]; /* first check AUX data */
q = &s->queues[0]; /* first check KBD data */
if (q->count == 0)
q = &s->queues[0]; /* then check KBD data */
q = &s->queues[1]; /* then check AUX data */
if (q->count == 0) {
/* XXX: return something else ? */
val = 0;
......@@ -2756,14 +2734,14 @@ uint32_t kbd_read_data(CPUX86State *env, uint32_t addr)
if (++q->rptr == KBD_QUEUE_SIZE)
q->rptr = 0;
q->count--;
/* reading deasserts IRQ */
if (q == &s->queues[0])
pic_set_irq(1, 0);
else
pic_set_irq(12, 0);
}
if (s->queues[1].count == 0) {
s->status &= ~KBD_STAT_MOUSE_OBF;
if (s->queues[0].count == 0)
s->status &= ~KBD_STAT_OBF;
kbd_update_irq(s);
}
/* reassert IRQs if data left */
kbd_update_irq(s);
#ifdef DEBUG_KBD
printf("kbd: read data=0x%02x\n", val);
#endif
......@@ -2820,12 +2798,212 @@ static void kbd_write_keyboard(KBDState *s, int val)
break;
case KBD_CMD_SET_LEDS:
kbd_queue(s, KBD_REPLY_ACK, 0);
s->kbd_write_cmd = -1;
break;
case KBD_CMD_SET_RATE:
kbd_queue(s, KBD_REPLY_ACK, 0);
s->kbd_write_cmd = -1;
break;
}
}
static void kbd_mouse_send_packet(KBDState *s)
{
unsigned int b;
int dx1, dy1, dz1;
dx1 = s->mouse_dx;
dy1 = s->mouse_dy;
dz1 = s->mouse_dz;
/* XXX: increase range to 8 bits ? */
if (dx1 > 127)
dx1 = 127;
else if (dx1 < -127)
dx1 = -127;
if (dy1 > 127)
dy1 = 127;
else if (dy1 < -127)
dy1 = -127;
b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
kbd_queue(s, b, 1);
kbd_queue(s, dx1 & 0xff, 1);
kbd_queue(s, dy1 & 0xff, 1);
/* extra byte for IMPS/2 or IMEX */
switch(s->mouse_type) {
default:
break;
case 3:
if (dz1 > 127)
dz1 = 127;
else if (dz1 < -127)
dz1 = -127;
kbd_queue(s, dz1 & 0xff, 1);
break;
case 4:
if (dz1 > 7)
dz1 = 7;
else if (dz1 < -7)
dz1 = -7;
b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
kbd_queue(s, b, 1);
break;
}
/* update deltas */
s->mouse_dx -= dx1;
s->mouse_dy -= dy1;
s->mouse_dz -= dz1;
}
void kbd_mouse_event(int dx, int dy, int dz, int buttons_state)
{
KBDState *s = &kbd_state;
/* check if deltas are recorded when disabled */
if (!(s->mouse_status & MOUSE_STATUS_ENABLED))
return;
s->mouse_dx += dx;
s->mouse_dy -= dy;
s->mouse_dz += dz;
s->mouse_buttons = buttons_state;
if (!(s->mouse_status & MOUSE_STATUS_REMOTE) &&
(s->queues[1].count < (KBD_QUEUE_SIZE - 16))) {
for(;;) {
/* if not remote, send event. Multiple events are sent if
too big deltas */
kbd_mouse_send_packet(s);
if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
break;
}
}
}
static void kbd_write_mouse(KBDState *s, int val)
{
#ifdef DEBUG_MOUSE
printf("kbd: write mouse 0x%02x\n", val);
#endif
switch(s->mouse_write_cmd) {
default:
case -1:
/* mouse command */
if (s->mouse_wrap) {
if (val == AUX_RESET_WRAP) {
s->mouse_wrap = 0;
kbd_queue(s, AUX_ACK, 1);
return;
} else if (val != AUX_RESET) {
kbd_queue(s, val, 1);
return;
}
}
switch(val) {
case AUX_SET_SCALE11:
s->mouse_status &= ~MOUSE_STATUS_SCALE21;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_SET_SCALE21:
s->mouse_status |= MOUSE_STATUS_SCALE21;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_SET_STREAM:
s->mouse_status &= ~MOUSE_STATUS_REMOTE;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_SET_WRAP:
s->mouse_wrap = 1;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_SET_REMOTE:
s->mouse_status |= MOUSE_STATUS_REMOTE;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_GET_TYPE:
kbd_queue(s, AUX_ACK, 1);
kbd_queue(s, s->mouse_type, 1);
break;
case AUX_SET_RES:
case AUX_SET_SAMPLE:
s->mouse_write_cmd = val;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_GET_SCALE:
kbd_queue(s, AUX_ACK, 1);
kbd_queue(s, s->mouse_status, 1);
kbd_queue(s, s->mouse_resolution, 1);
kbd_queue(s, s->mouse_sample_rate, 1);
break;
case AUX_POLL:
kbd_queue(s, AUX_ACK, 1);
kbd_mouse_send_packet(s);
break;
case AUX_ENABLE_DEV:
s->mouse_status |= MOUSE_STATUS_ENABLED;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_DISABLE_DEV:
s->mouse_status &= ~MOUSE_STATUS_ENABLED;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_SET_DEFAULT:
s->mouse_sample_rate = 100;
s->mouse_resolution = 2;
s->mouse_status = 0;
kbd_queue(s, AUX_ACK, 1);
break;
case AUX_RESET:
s->mouse_sample_rate = 100;
s->mouse_resolution = 2;
s->mouse_status = 0;
kbd_queue(s, AUX_ACK, 1);
kbd_queue(s, 0xaa, 1);
kbd_queue(s, s->mouse_type, 1);
break;
default:
break;
}
break;
case AUX_SET_SAMPLE:
s->mouse_sample_rate = val;
#if 0
/* detect IMPS/2 or IMEX */
switch(s->mouse_detect_state) {
default:
case 0:
if (val == 200)
s->mouse_detect_state = 1;
break;
case 1:
if (val == 100)
s->mouse_detect_state = 2;
else if (val == 200)
s->mouse_detect_state = 3;
else
s->mouse_detect_state = 0;
break;
case 2:
if (val == 80)
s->mouse_type = 3; /* IMPS/2 */
s->mouse_detect_state = 0;
break;
case 3:
if (val == 80)
s->mouse_type = 4; /* IMEX */
s->mouse_detect_state = 0;
break;
}
#endif
kbd_queue(s, AUX_ACK, 1);
s->mouse_write_cmd = -1;
break;
case AUX_SET_RES:
s->mouse_resolution = val;
kbd_queue(s, AUX_ACK, 1);
s->mouse_write_cmd = -1;
break;
}
s->kbd_write_cmd = -1;
}
void kbd_write_data(CPUX86State *env, uint32_t addr, uint32_t val)
......@@ -2857,6 +3035,9 @@ void kbd_write_data(CPUX86State *env, uint32_t addr, uint32_t val)
cpu_x86_interrupt(global_env, CPU_INTERRUPT_EXIT);
}
break;
case KBD_CCMD_WRITE_MOUSE:
kbd_write_mouse(s, val);
break;
default:
break;
}
......@@ -2869,8 +3050,9 @@ void kbd_reset(KBDState *s)
int i;
s->kbd_write_cmd = -1;
s->mouse_write_cmd = -1;
s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
s->status = KBD_MODE_SYS | KBD_MODE_NO_KEYLOCK;
s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
for(i = 0; i < 2; i++) {
q = &s->queues[i];
q->rptr = 0;
......@@ -2933,6 +3115,63 @@ void bochs_bios_init(void)
register_ioport_write(0x503, 1, bochs_bios_write, 1);
}
/***********************************************************/
/* dumb display */
/* init terminal so that we can grab keys */
static struct termios oldtty;
static void term_exit(void)
{
tcsetattr (0, TCSANOW, &oldtty);
}
static void term_init(void)
{
struct termios tty;
tcgetattr (0, &tty);
oldtty = tty;
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
|INLCR|IGNCR|ICRNL|IXON);
tty.c_oflag |= OPOST;
tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
tty.c_cflag &= ~(CSIZE|PARENB);
tty.c_cflag |= CS8;
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 0;
tcsetattr (0, TCSANOW, &tty);
atexit(term_exit);
fcntl(0, F_SETFL, O_NONBLOCK);
}
static void dumb_update(DisplayState *ds, int x, int y, int w, int h)
{
}
static void dumb_resize(DisplayState *ds, int w, int h)
{
}
static void dumb_refresh(DisplayState *ds)