Files
86Box/src/machine/m_amstrad.c
OBattler 5318bc08d8 The FDC is now a device_t, and the FDC code has been cleaned up;
Merged floppy.c and fdd.c and renamed floppy_*.c (the floppy image format handlers) to fdd_*.c;
Reading the AT or PS/2 keyboard controller status no longer clears the transmit timeout bit, fixes error 8601 (mouse error) on the IBM PS/2 Model 80;
MMU translate and DMA physical reads and writes now go through _mem_exec instead of directly to ram[], should fix the last remaining problems with remapped mappings;
Implemented the Sound gain dialog;
Added the resource for the "New floppy image" dialog and the needed functions for the functionality of exporting the currently mounted floppy image as 86F, both of which should be finished in the next commit;
Applied the CD-ROM fixes from the PCem commit;
Added the "Keep ratio" option for full screen stretch.
2018-01-17 18:43:36 +01:00

1278 lines
29 KiB
C

/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Emulation of the Amstrad series of PC's: PC1512, PC1640 and
* PC200, including their keyboard, mouse and video devices, as
* well as the PC2086 and PC3086 systems.
*
* PC1512: The PC1512 extends CGA with a bit-planar 640x200x16 mode.
* Most CRTC registers are fixed.
*
* The Technical Reference Manual lists the video waitstate
* time as between 12 and 46 cycles. We currently always use
* the lower number.
*
* PC1640: Mostly standard EGA, but with CGA & Hercules emulation.
*
* PC200: CGA with some NMI stuff. But we don't need that as it's only
* used for TV and LCD displays, and we're emulating a CRT.
*
* TODO: This module is not complete yet:
*
* PC1512: The BIOS assumes 512K RAM, because I cannot figure out how to
* read the status of the LK4 jumper on the mainboard, which is
* somehow linked to the bus gate array on the NDMACS line...
*
* PC1612: EGA mode does not seem to work in the PC1640; it works fine
* in alpha mode, but in highres ("ECD350") mode, it displays
* some semi-random junk. Video-memory pointer maybe?
*
* Version: @(#)m_amstrad.c 1.0.5 2018/01/16
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2008-2018 Sarah Walker.
* Copyright 2016-2018 Miran Grca.
* Copyright 2017,2018 Fred N. van Kempen.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include "../86box.h"
#include "../cpu/cpu.h"
#include "../io.h"
#include "../nmi.h"
#include "../pic.h"
#include "../pit.h"
#include "../ppi.h"
#include "../mem.h"
#include "../rom.h"
#include "../timer.h"
#include "../device.h"
#include "../nvr.h"
#include "../keyboard.h"
#include "../mouse.h"
#include "../game/gameport.h"
#include "../lpt.h"
#include "../floppy/fdd.h"
#include "../floppy/fdc.h"
#include "../sound/sound.h"
#include "../sound/snd_speaker.h"
#include "../video/video.h"
#include "../video/vid_cga.h"
#include "../video/vid_ega.h"
#include "../video/vid_paradise.h"
#include "machine.h"
#define STAT_PARITY 0x80
#define STAT_RTIMEOUT 0x40
#define STAT_TTIMEOUT 0x20
#define STAT_LOCK 0x10
#define STAT_CD 0x08
#define STAT_SYSFLAG 0x04
#define STAT_IFULL 0x02
#define STAT_OFULL 0x01
typedef struct {
mem_mapping_t cga_map; /* 1512/1640/200 */
mem_mapping_t ega_map; /* 1640 */
rom_t bios_rom; /* 1640 */
cga_t cga; /* 1640/200 */
ega_t ega; /* 1640 */
uint8_t crtc[32];
int crtcreg;
int cga_enabled; /* 1640 */
uint8_t cgacol,
cgamode,
stat;
uint8_t plane_write, /* 1512/200 */
plane_read, /* 1512/200 */
border; /* 1512/200 */
int linepos,
displine;
int sc, vc;
int cgadispon;
int con, coff,
cursoron,
cgablink;
int64_t vsynctime;
int vadj;
uint16_t ma, maback;
int dispon;
int blink;
int64_t dispontime, /* 1512/1640 */
dispofftime; /* 1512/1640 */
int64_t vidtime; /* 1512/1640 */
int firstline,
lastline;
uint8_t *vram;
} amsvid_t;
typedef struct {
/* Machine stuff. */
uint8_t dead;
uint8_t stat1,
stat2;
/* Keyboard stuff. */
int8_t wantirq;
uint8_t key_waiting;
uint8_t pa;
uint8_t pb;
/* Mouse stuff. */
uint8_t mousex,
mousey;
int oldb;
/* Video stuff. */
amsvid_t *vid;
} amstrad_t;
static uint8_t key_queue[16];
static int key_queue_start = 0,
key_queue_end = 0;
static uint8_t crtc_mask[32] = {
0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x7f, 0x7f,
0xf3, 0x1f, 0x7f, 0x1f, 0x3f, 0xff, 0x3f, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static void
recalc_timings_1512(amsvid_t *vid)
{
double _dispontime, _dispofftime, disptime;
disptime = 128; /*Fixed on PC1512*/
_dispontime = 80;
_dispofftime = disptime - _dispontime;
_dispontime *= CGACONST;
_dispofftime *= CGACONST;
vid->dispontime = (int64_t)(_dispontime * (1 << TIMER_SHIFT));
vid->dispofftime = (int64_t)(_dispofftime * (1 << TIMER_SHIFT));
}
static void
vid_out_1512(uint16_t addr, uint8_t val, void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
uint8_t old;
switch (addr) {
case 0x03d4:
vid->crtcreg = val & 31;
return;
case 0x03d5:
old = vid->crtc[vid->crtcreg];
vid->crtc[vid->crtcreg] = val & crtc_mask[vid->crtcreg];
if (old != val) {
if (vid->crtcreg < 0xe || vid->crtcreg > 0x10) {
fullchange = changeframecount;
recalc_timings_1512(vid);
}
}
return;
case 0x03d8:
if ((val & 0x12) == 0x12 && (vid->cgamode & 0x12) != 0x12) {
vid->plane_write = 0xf;
vid->plane_read = 0;
}
vid->cgamode = val;
return;
case 0x03d9:
vid->cgacol = val;
return;
case 0x03dd:
vid->plane_write = val;
return;
case 0x03de:
vid->plane_read = val & 3;
return;
case 0x03df:
vid->border = val;
return;
}
}
static uint8_t
vid_in_1512(uint16_t addr, void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
uint8_t ret = 0xff;
switch (addr) {
case 0x03d4:
ret = vid->crtcreg;
case 0x03d5:
ret = vid->crtc[vid->crtcreg];
case 0x03da:
ret = vid->stat;
}
return(ret);
}
static void
vid_write_1512(uint32_t addr, uint8_t val, void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
egawrites++;
cycles -= 12;
addr &= 0x3fff;
if ((vid->cgamode & 0x12) == 0x12) {
if (vid->plane_write & 1) vid->vram[addr] = val;
if (vid->plane_write & 2) vid->vram[addr | 0x4000] = val;
if (vid->plane_write & 4) vid->vram[addr | 0x8000] = val;
if (vid->plane_write & 8) vid->vram[addr | 0xc000] = val;
} else
vid->vram[addr] = val;
}
static uint8_t
vid_read_1512(uint32_t addr, void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
egareads++;
cycles -= 12;
addr &= 0x3fff;
if ((vid->cgamode & 0x12) == 0x12)
return(vid->vram[addr | (vid->plane_read << 14)]);
return(vid->vram[addr]);
}
static void
vid_poll_1512(void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
uint16_t ca = (vid->crtc[15] | (vid->crtc[14] << 8)) & 0x3fff;
int drawcursor;
int x, c;
uint8_t chr, attr;
uint16_t dat, dat2, dat3, dat4;
int cols[4];
int col;
int oldsc;
if (! vid->linepos) {
vid->vidtime += vid->dispofftime;
vid->stat |= 1;
vid->linepos = 1;
oldsc = vid->sc;
if (vid->dispon) {
if (vid->displine < vid->firstline) {
vid->firstline = vid->displine;
video_wait_for_buffer();
}
vid->lastline = vid->displine;
for (c = 0; c < 8; c++) {
if ((vid->cgamode & 0x12) == 0x12) {
buffer->line[vid->displine][c] = (vid->border & 15) + 16;
if (vid->cgamode & 1)
buffer->line[vid->displine][c + (vid->crtc[1] << 3) + 8] = 0;
else
buffer->line[vid->displine][c + (vid->crtc[1] << 4) + 8] = 0;
} else {
buffer->line[vid->displine][c] = (vid->cgacol & 15) + 16;
if (vid->cgamode & 1)
buffer->line[vid->displine][c + (vid->crtc[1] << 3) + 8] = (vid->cgacol & 15) + 16;
else
buffer->line[vid->displine][c + (vid->crtc[1] << 4) + 8] = (vid->cgacol & 15) + 16;
}
}
if (vid->cgamode & 1) {
for (x = 0; x < 80; x++) {
chr = vid->vram[ ((vid->ma << 1) & 0x3fff)];
attr = vid->vram[(((vid->ma << 1) + 1) & 0x3fff)];
drawcursor = ((vid->ma == ca) && vid->con && vid->cursoron);
if (vid->cgamode & 0x20) {
cols[1] = (attr & 15) + 16;
cols[0] = ((attr >> 4) & 7) + 16;
if ((vid->blink & 16) && (attr & 0x80) && !drawcursor)
cols[1] = cols[0];
} else {
cols[1] = (attr & 15) + 16;
cols[0] = (attr >> 4) + 16;
}
if (drawcursor) {
for (c = 0; c < 8; c++)
buffer->line[vid->displine][(x << 3) + c + 8] = cols[(fontdat[chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0] ^ 15;
} else {
for (c = 0; c < 8; c++)
buffer->line[vid->displine][(x << 3) + c + 8] = cols[(fontdat[chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0];
}
vid->ma++;
}
} else if (! (vid->cgamode & 2)) {
for (x = 0; x < 40; x++) {
chr = vid->vram[((vid->ma << 1) & 0x3fff)];
attr = vid->vram[(((vid->ma << 1) + 1) & 0x3fff)];
drawcursor = ((vid->ma == ca) && vid->con && vid->cursoron);
if (vid->cgamode & 0x20) {
cols[1] = (attr & 15) + 16;
cols[0] = ((attr >> 4) & 7) + 16;
if ((vid->blink & 16) && (attr & 0x80))
cols[1] = cols[0];
} else {
cols[1] = (attr & 15) + 16;
cols[0] = (attr >> 4) + 16;
}
vid->ma++;
if (drawcursor) {
for (c = 0; c < 8; c++)
buffer->line[vid->displine][(x << 4) + (c << 1) + 8] =
buffer->line[vid->displine][(x << 4) + (c << 1) + 1 + 8] = cols[(fontdat[chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0] ^ 15;
} else {
for (c = 0; c < 8; c++)
buffer->line[vid->displine][(x << 4) + (c << 1) + 8] =
buffer->line[vid->displine][(x << 4) + (c << 1) + 1 + 8] = cols[(fontdat[chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0];
}
}
} else if (! (vid->cgamode & 16)) {
cols[0] = (vid->cgacol & 15) | 16;
col = (vid->cgacol & 16) ? 24 : 16;
if (vid->cgamode & 4) {
cols[1] = col | 3;
cols[2] = col | 4;
cols[3] = col | 7;
} else if (vid->cgacol & 32) {
cols[1] = col | 3;
cols[2] = col | 5;
cols[3] = col | 7;
} else {
cols[1] = col | 2;
cols[2] = col | 4;
cols[3] = col | 6;
}
for (x = 0; x < 40; x++) {
dat = (vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 1) * 0x2000)] << 8) | vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 1) * 0x2000) + 1];
vid->ma++;
for (c = 0; c < 8; c++) {
buffer->line[vid->displine][(x << 4) + (c << 1) + 8] =
buffer->line[vid->displine][(x << 4) + (c << 1) + 1 + 8] = cols[dat >> 14];
dat <<= 2;
}
}
} else {
for (x = 0; x < 40; x++) {
ca = ((vid->ma << 1) & 0x1fff) + ((vid->sc & 1) * 0x2000);
dat = (vid->vram[ca] << 8) | vid->vram[ca + 1];
dat2 = (vid->vram[ca + 0x4000] << 8) | vid->vram[ca + 0x4001];
dat3 = (vid->vram[ca + 0x8000] << 8) | vid->vram[ca + 0x8001];
dat4 = (vid->vram[ca + 0xc000] << 8) | vid->vram[ca + 0xc001];
vid->ma++;
for (c = 0; c < 16; c++) {
buffer->line[vid->displine][(x << 4) + c + 8] = (((dat >> 15) | ((dat2 >> 15) << 1) | ((dat3 >> 15) << 2) | ((dat4 >> 15) << 3)) & (vid->cgacol & 15)) + 16;
dat <<= 1;
dat2 <<= 1;
dat3 <<= 1;
dat4 <<= 1;
}
}
}
} else {
cols[0] = ((vid->cgamode & 0x12) == 0x12) ? 0 : (vid->cgacol & 15) + 16;
if (vid->cgamode & 1)
hline(buffer, 0, vid->displine, (vid->crtc[1] << 3) + 16, cols[0]);
else
hline(buffer, 0, vid->displine, (vid->crtc[1] << 4) + 16, cols[0]);
}
vid->sc = oldsc;
if (vid->vsynctime)
vid->stat |= 8;
vid->displine++;
if (vid->displine >= 360)
vid->displine = 0;
} else {
vid->vidtime += vid->dispontime;
if ((vid->lastline - vid->firstline) == 199)
vid->dispon = 0; /*Amstrad PC1512 always displays 200 lines, regardless of CRTC settings*/
if (vid->dispon)
vid->stat &= ~1;
vid->linepos = 0;
if (vid->vsynctime) {
vid->vsynctime--;
if (! vid->vsynctime)
vid->stat &= ~8;
}
if (vid->sc == (vid->crtc[11] & 31)) {
vid->con = 0;
vid->coff = 1;
}
if (vid->vadj) {
vid->sc++;
vid->sc &= 31;
vid->ma = vid->maback;
vid->vadj--;
if (! vid->vadj) {
vid->dispon = 1;
vid->ma = vid->maback = (vid->crtc[13] | (vid->crtc[12] << 8)) & 0x3fff;
vid->sc = 0;
}
} else if (vid->sc == vid->crtc[9]) {
vid->maback = vid->ma;
vid->sc = 0;
vid->vc++;
vid->vc &= 127;
if (vid->displine == 32) {
vid->vc = 0;
vid->vadj = 6;
if ((vid->crtc[10] & 0x60) == 0x20)
vid->cursoron = 0;
else
vid->cursoron = vid->blink & 16;
}
if (vid->displine >= 262) {
vid->dispon = 0;
vid->displine = 0;
vid->vsynctime = 46;
if (vid->cgamode&1)
x = (vid->crtc[1] << 3) + 16;
else
x = (vid->crtc[1] << 4) + 16;
vid->lastline++;
if ((x != xsize) || ((vid->lastline - vid->firstline) != ysize) || video_force_resize_get()) {
xsize = x;
ysize = vid->lastline - vid->firstline;
if (xsize < 64) xsize = 656;
if (ysize < 32) ysize = 200;
set_screen_size(xsize, (ysize << 1) + 16);
if (video_force_resize_get())
video_force_resize_set(0);
}
video_blit_memtoscreen_8(0, vid->firstline - 4, 0, (vid->lastline - vid->firstline) + 8, xsize, (vid->lastline - vid->firstline) + 8);
video_res_x = xsize - 16;
video_res_y = ysize;
if (vid->cgamode & 1) {
video_res_x /= 8;
video_res_y /= vid->crtc[9] + 1;
video_bpp = 0;
} else if (! (vid->cgamode & 2)) {
video_res_x /= 16;
video_res_y /= vid->crtc[9] + 1;
video_bpp = 0;
} else if (! (vid->cgamode & 16)) {
video_res_x /= 2;
video_bpp = 2;
} else {
video_bpp = 4;
}
vid->firstline = 1000;
vid->lastline = 0;
vid->blink++;
}
} else {
vid->sc++;
vid->sc &= 31;
vid->ma = vid->maback;
}
if (vid->sc == (vid->crtc[10] & 31))
vid->con = 1;
}
}
static void
vid_init_1512(amstrad_t *ams)
{
amsvid_t *vid;
/* Allocate a video controller block. */
vid = (amsvid_t *)malloc(sizeof(amsvid_t));
memset(vid, 0x00, sizeof(amsvid_t));
vid->vram = malloc(0x10000);
vid->cgacol = 7;
vid->cgamode = 0x12;
timer_add(vid_poll_1512, &vid->vidtime, TIMER_ALWAYS_ENABLED, vid);
mem_mapping_add(&vid->cga_map, 0xb8000, 0x08000,
vid_read_1512, NULL, NULL, vid_write_1512, NULL, NULL,
NULL, 0, vid);
io_sethandler(0x03d0, 16,
vid_in_1512, NULL, NULL, vid_out_1512, NULL, NULL, vid);
overscan_x = overscan_y = 16;
ams->vid = vid;
}
static void
vid_close_1512(void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
free(vid->vram);
free(vid);
}
static void
vid_speed_change_1512(void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
recalc_timings_1512(vid);
}
static device_t vid_1512_device = {
"Amstrad PC1512 (video)",
0, 0,
NULL, vid_close_1512, NULL,
NULL,
vid_speed_change_1512,
NULL,
NULL
};
static void
recalc_timings_1640(amsvid_t *vid)
{
cga_recalctimings(&vid->cga);
ega_recalctimings(&vid->ega);
if (vid->cga_enabled) {
overscan_x = overscan_y = 16;
vid->dispontime = vid->cga.dispontime;
vid->dispofftime = vid->cga.dispofftime;
} else {
overscan_x = 16; overscan_y = 28;
vid->dispontime = vid->ega.dispontime;
vid->dispofftime = vid->ega.dispofftime;
}
}
static void
vid_out_1640(uint16_t addr, uint8_t val, void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
switch (addr) {
case 0x03db:
vid->cga_enabled = val & 0x40;
if (vid->cga_enabled) {
mem_mapping_enable(&vid->cga_map);
mem_mapping_disable(&vid->ega_map);
} else {
mem_mapping_disable(&vid->cga_map);
switch (vid->ega.gdcreg[6] & 0xc) {
case 0x0: /*128k at A0000*/
mem_mapping_set_addr(&vid->ega_map,
0xa0000, 0x20000);
break;
case 0x4: /*64k at A0000*/
mem_mapping_set_addr(&vid->ega_map,
0xa0000, 0x10000);
break;
case 0x8: /*32k at B0000*/
mem_mapping_set_addr(&vid->ega_map,
0xb0000, 0x08000);
break;
case 0xC: /*32k at B8000*/
mem_mapping_set_addr(&vid->ega_map,
0xb8000, 0x08000);
break;
}
}
return;
}
if (vid->cga_enabled)
cga_out(addr, val, &vid->cga);
else
ega_out(addr, val, &vid->ega);
}
static uint8_t
vid_in_1640(uint16_t addr, void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
switch (addr) {
}
if (vid->cga_enabled)
return(cga_in(addr, &vid->cga));
else
return(ega_in(addr, &vid->ega));
}
static void
vid_poll_1640(void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
if (vid->cga_enabled) {
overscan_x = overscan_y = 16;
vid->cga.vidtime = vid->vidtime;
cga_poll(&vid->cga);
vid->vidtime = vid->cga.vidtime;
} else {
overscan_x = 16; overscan_y = 28;
vid->ega.vidtime = vid->vidtime;
ega_poll(&vid->ega);
vid->vidtime = vid->ega.vidtime;
}
}
static void
vid_init_1640(amstrad_t *ams)
{
amsvid_t *vid;
/* Allocate a video controller block. */
vid = (amsvid_t *)malloc(sizeof(amsvid_t));
memset(vid, 0x00, sizeof(amsvid_t));
rom_init(&vid->bios_rom, L"roms/machines/pc1640/40100",
0xc0000, 0x8000, 0x7fff, 0, 0);
ega_init(&vid->ega);
vid->cga.vram = vid->ega.vram;
vid->cga_enabled = 1;
cga_init(&vid->cga);
mem_mapping_add(&vid->cga_map, 0xb8000, 0x08000,
cga_read, NULL, NULL, cga_write, NULL, NULL, NULL, 0, &vid->cga);
mem_mapping_add(&vid->ega_map, 0, 0,
ega_read, NULL, NULL, ega_write, NULL, NULL, NULL, 0, &vid->ega);
io_sethandler(0x03a0, 64,
vid_in_1640, NULL, NULL, vid_out_1640, NULL, NULL, vid);
timer_add(vid_poll_1640, &vid->vidtime, TIMER_ALWAYS_ENABLED, vid);
overscan_x = overscan_y = 16;
ams->vid = vid;
}
static void
vid_close_1640(void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
free(vid->ega.vram);
free(vid);
}
static void
vid_speed_changed_1640(void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
recalc_timings_1640(vid);
}
static device_t vid_1640_device = {
"Amstrad PC1640 (video)",
0, 0,
NULL, vid_close_1640, NULL,
NULL,
vid_speed_changed_1640,
NULL,
NULL
};
static void
vid_out_200(uint16_t addr, uint8_t val, void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
cga_t *cga = &vid->cga;
uint8_t old;
switch (addr) {
case 0x03d5:
if (!(vid->plane_read & 0x40) && cga->crtcreg <= 11) {
if (vid->plane_read & 0x80)
nmi = 1;
vid->plane_write = 0x20 | (cga->crtcreg & 0x1f);
vid->border = val;
return;
}
old = cga->crtc[cga->crtcreg];
cga->crtc[cga->crtcreg] = val & crtc_mask[cga->crtcreg];
if (old != val) {
if (cga->crtcreg < 0xe || cga->crtcreg > 0x10) {
fullchange = changeframecount;
cga_recalctimings(cga);
}
}
return;
case 0x03d8:
old = cga->cgamode;
cga->cgamode = val;
if ((cga->cgamode ^ old) & 3)
cga_recalctimings(cga);
vid->plane_write |= 0x80;
if (vid->plane_read & 0x80)
nmi = 1;
return;
case 0x03de:
vid->plane_read = val;
vid->plane_write = 0x1f;
if (val & 0x80)
vid->plane_write |= 0x40;
return;
}
cga_out(addr, val, cga);
}
static uint8_t
vid_in_200(uint16_t addr, void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
cga_t *cga = &vid->cga;
uint8_t ret;
switch (addr) {
case 0x03d8:
return(cga->cgamode);
case 0x03dd:
ret = vid->plane_write;
vid->plane_write &= 0x1f;
nmi = 0;
return(ret);
case 0x03de:
return((vid->plane_read & 0xc7) | 0x10); /*External CGA*/
case 0x03df:
return(vid->border);
}
return(cga_in(addr, cga));
}
static void
vid_init_200(amstrad_t *ams)
{
amsvid_t *vid;
cga_t *cga;
/* Allocate a video controller block. */
vid = (amsvid_t *)malloc(sizeof(amsvid_t));
memset(vid, 0x00, sizeof(amsvid_t));
cga = &vid->cga;
cga->vram = malloc(0x4000);
cga_init(cga);
mem_mapping_add(&vid->cga_map, 0xb8000, 0x08000,
cga_read, NULL, NULL, cga_write, NULL, NULL, NULL, 0, cga);
io_sethandler(0x03d0, 16,
vid_in_200, NULL, NULL, vid_out_200, NULL, NULL, vid);
timer_add(cga_poll, &cga->vidtime, TIMER_ALWAYS_ENABLED, cga);
overscan_x = overscan_y = 16;
ams->vid = vid;
}
static void
vid_close_200(void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
free(vid->cga.vram);
free(vid);
}
static void
vid_speed_changed_200(void *priv)
{
amsvid_t *vid = (amsvid_t *)priv;
cga_recalctimings(&vid->cga);
}
static device_t vid_200_device = {
"Amstrad PC200 (video)",
0, 0,
NULL, vid_close_200, NULL,
NULL,
vid_speed_changed_200,
NULL,
NULL
};
static void
ms_write(uint16_t addr, uint8_t val, void *priv)
{
amstrad_t *ams = (amstrad_t *)priv;
if (addr == 0x78)
ams->mousex = 0;
else
ams->mousey = 0;
}
static uint8_t
ms_read(uint16_t addr, void *priv)
{
amstrad_t *ams = (amstrad_t *)priv;
if (addr == 0x78)
return(ams->mousex);
return(ams->mousey);
}
static int
ms_poll(int x, int y, int z, int b, void *priv)
{
amstrad_t *ams = (amstrad_t *)priv;
ams->mousex += x;
ams->mousey -= y;
if ((b & 1) && !(ams->oldb & 1))
keyboard_send(0x7e);
if ((b & 2) && !(ams->oldb & 2))
keyboard_send(0x7d);
if (!(b & 1) && (ams->oldb & 1))
keyboard_send(0xfe);
if (!(b & 2) && (ams->oldb & 2))
keyboard_send(0xfd);
ams->oldb = b;
return(0);
}
static void
kbd_adddata(uint16_t val)
{
key_queue[key_queue_end] = val;
#if ENABLE_KEYBOARD_LOG
pclog("keyboard_amstrad : %02X added to key queue at %i\n",
val, key_queue_end);
#endif
key_queue_end = (key_queue_end + 1) & 0xf;
}
static void
kbd_adddata_ex(uint16_t val)
{
kbd_adddata_process(val, kbd_adddata);
}
static void
kbd_write(uint16_t port, uint8_t val, void *priv)
{
amstrad_t *ams = (amstrad_t *)priv;
#ifdef WALTJE
int i = 0;
#endif
#if ENABLE_KEYBOARD_LOG
pclog("keyboard_amstrad : write %04X %02X %02X\n", port, val, ams->pb);
#endif
switch (port) {
case 0x61:
/*
* PortB - System Control.
*
* 7 Enable Status-1/Disable Keyboard Code on Port A.
* 6 Enable incoming Keyboard Clock.
* 5 Prevent external parity errors from causing NMI.
* 4 Disable parity checking of on-board system Ram.
* 3 Undefined (Not Connected).
* 2 Enable Port C LSB / Disable MSB. (See 1.8.3)
* 1 Speaker Drive.
* 0 8253 GATE 2 (Speaker Modulate).
*
* This register is controlled by BIOS and/or ROS.
*/
#if ENABLE_KEYBOARD_LOG
pclog("AMSkb: write PB %02x (%02x)\n", val, ams->pb);
#endif
if (!(ams->pb & 0x40) && (val & 0x40)) { /*Reset keyboard*/
#if ENABLE_KEYBOARD_LOG
pclog("AMSkb: reset keyboard\n");
#endif
kbd_adddata(0xaa);
}
ams->pb = val;
ppi.pb = val;
timer_process();
timer_update_outstanding();
speaker_update();
speaker_gated = val & 0x01;
speaker_enable = val & 0x02;
if (speaker_enable)
was_speaker_enable = 1;
pit_set_gate(&pit, 2, val & 0x01);
if (val & 0x80) {
/* Keyboard enabled, so enable PA reading. */
ams->pa = 0x00;
}
break;
case 0x63:
break;
case 0x64:
#ifdef WALTJE
pclog("AMSkb: STAT1 = %02x (%02x)\n", val, ams->stat1);
#endif
ams->stat1 = val;
break;
case 0x65:
#ifdef WALTJE
pclog("AMSkb: STAT2 = %02x (%02x)\n", val, ams->stat2);
i = 512 + (((val & 0x1f) - 0x0e) * 32);
pclog("AMSkb: %d KB RAM installed.\n", i);
#endif
ams->stat2 = val;
break;
case 0x66:
#ifdef WALTJE
pclog("AMSkb: RESET REQUESTED !\n");
#endif
pc_reset(1);
break;
default:
pclog("AMSkb: bad keyboard write %04X %02X\n", port, val);
}
}
static uint8_t
kbd_read(uint16_t port, void *priv)
{
amstrad_t *ams = (amstrad_t *)priv;
uint8_t ret = 0xff;
switch (port) {
case 0x60:
if (ams->pb & 0x80) {
/*
* PortA - System Status 1
*
* 7 Always 0 (KBD7)
* 6 Second Floppy disk drive installed (KBD6)
* 5 DDM1 - Default Display Mode bit 1 (KBD5)
* 4 DDM0 - Default Display Mode bit 0 (KBD4)
* 3 Always 1 (KBD3)
* 2 Always 1 (KBD2)
* 1 8087 NDP installed (KBD1)
* 0 Always 1 (KBD0)
*
* DDM00
* 00 unknown, external color?
* 01 Color,alpha,40x25, bright white on black.
* 10 Color,alpha,80x25, bright white on black.
* 11 External Monochrome,80x25.
*
* Following a reset, the hardware selects VDU mode
* 2. The ROS then sets the initial VDU state based
* on the DDM value.
*/
ret = (0x0d | ams->stat1) & 0x7f;
} else {
ret = ams->pa;
if (key_queue_start == key_queue_end) {
ams->wantirq = 0;
} else {
ams->key_waiting = key_queue[key_queue_start];
key_queue_start = (key_queue_start + 1) & 0xf;
ams->wantirq = 1;
}
}
break;
case 0x61:
ret = ams->pb;
break;
case 0x62:
/*
* PortC - System Status 2.
*
* 7 On-board system RAM parity error.
* 6 External parity error (I/OCHCK from expansion bus).
* 5 8253 PIT OUT2 output.
* 4 Undefined (Not Connected).
*-------------------------------------------
* LSB MSB (depends on PB2)
*-------------------------------------------
* 3 RAM3 Undefined
* 2 RAM2 Undefined
* 1 RAM1 Undefined
* 0 RAM0 RAM4
*
* PC7 is forced to 0 when on-board system RAM parity
* checking is disabled by PB4.
*
* RAM4:0
* 01110 512K bytes on-board.
* 01111 544K bytes (32K external).
* 10000 576K bytes (64K external).
* 10001 608K bytes (96K external).
* 10010 640K bytes (128K external or fitted on-board).
*/
if (ams->pb & 0x04)
ret = ams->stat2 & 0x0f;
else
ret = ams->stat2 >> 4;
ret |= (ppispeakon ? 0x20 : 0);
if (nmi)
ret |= 0x40;
break;
default:
pclog("AMDkb: bad keyboard read %04X\n", port);
}
return(ret);
}
static void
kbd_poll(void *priv)
{
amstrad_t *ams = (amstrad_t *)priv;
keyboard_delay += (1000 * TIMER_USEC);
if (ams->wantirq)
{
ams->wantirq = 0;
ams->pa = ams->key_waiting;
picint(2);
#if ENABLE_KEYBOARD_LOG
pclog("keyboard_amstrad : take IRQ\n");
#endif
}
if (key_queue_start != key_queue_end && !ams->pa) {
ams->key_waiting = key_queue[key_queue_start];
#if ENABLE_KEYBOARD_LOG
pclog("Reading %02X from the key queue at %i\n",
ams->key_waiting, key_queue_start);
#endif
key_queue_start = (key_queue_start + 1) & 0xf;
ams->wantirq = 1;
}
}
static void
ams_write(uint16_t port, uint8_t val, void *priv)
{
amstrad_t *ams = (amstrad_t *)priv;
switch (port) {
case 0xdead:
ams->dead = val;
break;
}
}
static uint8_t
ams_read(uint16_t port, void *priv)
{
amstrad_t *ams = (amstrad_t *)priv;
uint8_t ret = 0xff;
switch (port) {
case 0x0379: /* printer control, also set LK1-3.
* 0 English Language.
* 1 German Language.
* 2 French Language.
* 3 Spanish Language.
* 4 Danish Language.
* 5 Swedish Language.
* 6 Italian Language.
* 7 Diagnostic Mode.
*/
ret = 0x02; /* ENGLISH. no Diags mode */
break;
case 0x037a: /* printer status */
switch(romset) {
case ROM_PC1512:
ret = 0x20;
break;
case ROM_PC200:
ret = 0x80;
break;
default:
ret = 0x00;
}
break;
case 0xdead:
ret = ams->dead;
break;
}
return(ret);
}
void
machine_amstrad_init(machine_t *model)
{
amstrad_t *ams;
ams = (amstrad_t *)malloc(sizeof(amstrad_t));
memset(ams, 0x00, sizeof(amstrad_t));
nvr_at_init(1);
machine_common_init(model);
nmi_init();
lpt2_remove_ams();
io_sethandler(0x0379, 2,
ams_read, NULL, NULL, NULL, NULL, NULL, ams);
io_sethandler(0xdead, 1,
ams_read, NULL, NULL, ams_write, NULL, NULL, ams);
io_sethandler(0x0078, 1,
ms_read, NULL, NULL, ms_write, NULL, NULL, ams);
io_sethandler(0x007a, 1,
ms_read, NULL, NULL, ms_write, NULL, NULL, ams);
if (gfxcard == GFX_INTERNAL) switch(romset) {
case ROM_PC1512:
loadfont(L"roms/machines/pc1512/40078", 2);
vid_init_1512(ams);
device_add_ex(&vid_1512_device, ams->vid);
break;
case ROM_PC1640:
vid_init_1640(ams);
device_add_ex(&vid_1640_device, ams->vid);
break;
case ROM_PC200:
loadfont(L"roms/machines/pc200/40109.bin", 1);
vid_init_200(ams);
device_add_ex(&vid_200_device, ams->vid);
break;
case ROM_PC2086:
device_add(&paradise_pvga1a_pc2086_device);
break;
case ROM_PC3086:
device_add(&paradise_pvga1a_pc3086_device);
break;
case ROM_MEGAPC:
device_add(&paradise_wd90c11_megapc_device);
break;
}
/* Initialize the (custom) keyboard/mouse interface. */
ams->wantirq = 0;
io_sethandler(0x0060, 7,
kbd_read, NULL, NULL, kbd_write, NULL, NULL, ams);
timer_add(kbd_poll, &keyboard_delay, TIMER_ALWAYS_ENABLED, ams);
keyboard_set_table(scancode_xt);
keyboard_send = kbd_adddata_ex;
keyboard_scan = 1;
/* Tell mouse driver about our internal mouse. */
mouse_set_poll(ms_poll, ams);
if (joystick_type != 7)
device_add(&gameport_device);
device_add(&fdc_xt_amstrad_device);
}