mirror of
https://github.com/86Box/86Box.git
synced 2026-02-21 17:15:32 -07:00
2436 lines
82 KiB
C
2436 lines
82 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 IBM 5550 machine.
|
|
*
|
|
* The IBM 5550 was launched with three models:
|
|
* 5551-Axx: 12" monochrome CRT with 16x16 font
|
|
* (replaced by model D)
|
|
* 5551-Bxx: 15" monochrome CRT with 24x24 font
|
|
* (replaced by model G, J, M)
|
|
* 5551-Cxx: 14" color CRT with 16x16 font
|
|
* (replaced by model E, H, K, P)
|
|
* These first-gen models have 1-3 DSQD diskette drives.
|
|
* You need select "Type: 5.25" 720k" in the Settings dialog - Floppy & CD-ROM drives.
|
|
*
|
|
* Currently, this module supports model A and B configurations without hard disk.
|
|
*
|
|
* Authors: Akamaki.
|
|
*
|
|
* Copyright 2026 Akamaki.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <stdarg.h>
|
|
#include <wchar.h>
|
|
#define HAVE_STDARG_H
|
|
#include <86box/86box.h>
|
|
#include "cpu.h"
|
|
#include <86box/timer.h>
|
|
#include <86box/io.h>
|
|
#include <86box/dma.h>
|
|
#include <86box/pic.h>
|
|
#include <86box/ppi.h>
|
|
#include <86box/nmi.h>
|
|
#include <86box/mem.h>
|
|
#include <86box/device.h>
|
|
#include <86box/lpt.h>
|
|
#include <86box/nvr.h>
|
|
#include <86box/keyboard.h>
|
|
#include <86box/rom.h>
|
|
#include <86box/fdd.h>
|
|
#include <86box/fdc.h>
|
|
#include <86box/fdc_ext.h>
|
|
#include <86box/serial.h>
|
|
#include <86box/snd_speaker.h>
|
|
#include <86box/video.h>
|
|
#include <86box/machine.h>
|
|
#include <86box/plat_unused.h>
|
|
#define EMU_DEVICE_H
|
|
#include <86box/pit.h>
|
|
#include <86box/mouse.h>
|
|
|
|
// #define EPOCH_FONTROM_SIZE (1024 * 1024)
|
|
// #define EPOCH_FONTROM_MASK 0xffff
|
|
// #define EPOCH_FONTROM_BASESBCS 0x98000
|
|
#define EPOCH_VRAM_SBCS 0x38000
|
|
#define EPOCH_VRAM_SBEX 0x30000
|
|
#define EPOCH_INVALIDACCESS8 0xffu
|
|
#define EPOCH_INVALIDACCESS16 0xffffu
|
|
#define EPOCH_INVALIDACCESS32 0xffffffffu
|
|
#define EPOCH_SIZE_VRAM (256 * 1024) /* 0x40000 */
|
|
#define EPOCH_SIZE_CRAM (4 * 1024) /* 0x1000 */
|
|
#define EPOCH_MASK_A000 0x1ffff /* 0x1FFFF */
|
|
#define EPOCH_MASK_CRAM 0xfff /* 0xFFF */
|
|
#define EPOCH_MASK_VRAM 0x3ffff /* 0xFFFFF */
|
|
// #define EPOCH_MASK_VRAMPLANE 0x1ffff /* 0x1FFFF */
|
|
#define EPOCH_PIXELCLOCK24 40000000.0 /* 40 MHz interlaced */
|
|
#define EPOCH_PIXELCLOCK16 20000000.0 /* 20 MHz interlaced (not confirmed) */
|
|
#define EPOCH_CONFIG_MONO16 0 /* Model 5551-Axx (Font 16, monochrome) */
|
|
#define EPOCH_CONFIG_MONO24 1 /* Model 5551-Bxx (Font 24, monochrome) */
|
|
|
|
#define LC_INDEX 0x3D0
|
|
#define LC_DATA 0x3D1
|
|
#define LC_HORIZONTAL_TOTAL 0x00 /* -1 */
|
|
#define LC_HORIZONTAL_DISPLAYED 0x01
|
|
#define LC_H_SYNC_POSITION 0x02
|
|
#define LC_SYNC_WIDTH 0x03
|
|
#define LC_VERTICAL_TOTAL 0x04 /* -1 */
|
|
#define LC_V_TOTAL_ADJUST 0x05
|
|
#define LC_VERTICAL_DISPLAYED 0x06
|
|
#define LC_V_SYNC_POSITION 0x07
|
|
#define LC_INTERLACE_AND_SKEW 0x08
|
|
#define LC_MAXIMUM_SCAN_LINE 0x09 /* -1 */
|
|
#define LC_CURSOR_ROW_START 0x0A
|
|
#define LC_CURSOR_ROW_END 0x0B
|
|
#define LC_START_ADDRESS_HIGH 0x0C
|
|
#define LC_START_ADDRESS_LOW 0x0D
|
|
#define LC_CURSOR_LOC_HIGH 0x0E
|
|
#define LC_CURSOR_LOC_LOWJ 0x0F
|
|
#define LC_LIGHT_PEN_HIGH 0x10
|
|
#define LC_LIGHT_PEN_LOW 0x11
|
|
#define LS_ENABLE 0x3D2
|
|
#define LS_DISABLE 0x3D3
|
|
#define LS_MODE 0x3D8
|
|
#define LS_MONSENSE 0x3DA
|
|
// #define LV_PORT 0x3E8
|
|
// #define LV_PALETTE_0 0x00
|
|
// #define LV_MODE_CONTROL 0x10
|
|
// #define LV_OVERSCAN_COLOR 0x11
|
|
// #define LV_COLOR_PLANE_ENAB 0x12
|
|
// #define LV_PANNING 0x13
|
|
// #define LV_VIEWPORT1_BG 0x14
|
|
// #define LV_VIEWPORT2_BG 0x15
|
|
// #define LV_VIEWPORT3_BG 0x16
|
|
// #define LV_BLINK_COLOR 0x17
|
|
// #define LV_BLINK_CODE 0x18
|
|
// #define LV_GR_CURSOR_ROTATION 0x19
|
|
// #define LV_GR_CURSOR_COLOR 0x1A
|
|
// #define LV_GR_CURSOR_CONTROL 0x1B
|
|
// #define LV_COMMAND 0x1C
|
|
// #define LV_VP_BORDER_LINE 0x1D
|
|
// #define LV_SYNC_POLARITY 0x1F
|
|
// #define LV_CURSOR_CODE_0 0x20
|
|
// #define LV_GRID_COLOR_0 0x34
|
|
// #define LV_GRID_COLOR_1 0x35
|
|
// #define LV_GRID_COLOR_2 0x36
|
|
// #define LV_GRID_COLOR_3 0x37
|
|
// #define LV_ATTRIBUTE_CNTL 0x38
|
|
// #define LV_CURSOR_COLOR 0x3A
|
|
// #define LV_CURSOR_CONTROL 0x3B
|
|
// #define LV_RAS_STATUS_VIDEO 0x3C
|
|
// #define LV_PAS_STATUS_CNTRL 0x3D
|
|
// #define LV_IDENTIFICATION 0x3E
|
|
// #define LV_OUTPUT 0x3E
|
|
// #define LV_COMPATIBILITY 0x3F
|
|
|
|
#define TIMER_CTR_0 0 /* DMA */
|
|
#define TIMER_CTR_1 1 /* PIT */
|
|
#define TIMER_CTR_2 2 /* Speaker */
|
|
|
|
#define EPOCH_IRQ3_BIT (1 << 3) /* Keyboard */
|
|
#define EPOCH_IRQ6_BIT (1 << 6) /* PIT */
|
|
|
|
enum epoch_nvr_ADDR {
|
|
epoch_nvr_SECOND1,
|
|
epoch_nvr_SECOND10,
|
|
epoch_nvr_MINUTE1,
|
|
epoch_nvr_MINUTE10,
|
|
epoch_nvr_HOUR1,
|
|
epoch_nvr_HOUR10,
|
|
epoch_nvr_WEEKDAY,
|
|
epoch_nvr_DAY1,
|
|
epoch_nvr_DAY10,
|
|
epoch_nvr_MONTH1,
|
|
epoch_nvr_MONTH10,
|
|
epoch_nvr_YEAR1,
|
|
epoch_nvr_YEAR10,
|
|
epoch_nvr_UNKOWN_D,
|
|
epoch_nvr_UNKOWN_E,
|
|
epoch_nvr_UNKOWN_F,
|
|
epoch_nvr_CONTROL /* Internal data for configuration port (I/O 360h) */
|
|
};
|
|
|
|
#ifndef RELEASE_BUILD
|
|
//#define ENABLE_EPOCH_LOG 1
|
|
#endif
|
|
|
|
#ifdef ENABLE_EPOCH_LOG
|
|
#define ENABLE_EPOCH_DEBUGIO 1
|
|
// #define ENABLE_EPOCH_DEBUGKBD 1
|
|
int epoch_do_log = ENABLE_EPOCH_LOG;
|
|
|
|
static void
|
|
epoch_log(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
if (epoch_do_log) {
|
|
va_start(ap, fmt);
|
|
pclog_ex(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
#else
|
|
# define epoch_log(fmt, ...)
|
|
#endif
|
|
#ifdef ENABLE_EPOCH_DEBUGIO
|
|
# define epoch_iolog epoch_log
|
|
#else
|
|
# define epoch_iolog(fmt, ...)
|
|
#endif
|
|
#ifdef ENABLE_EPOCH_DEBUGKBD
|
|
# define epoch_kbdlog epoch_log
|
|
#else
|
|
# define epoch_kbdlog(fmt, ...)
|
|
#endif
|
|
|
|
|
|
typedef struct epoch_t {
|
|
mem_mapping_t cmapping;
|
|
|
|
uint16_t crtc[32];
|
|
// uint16_t crtc_vpreg[128];
|
|
// uint8_t crtc_vpsel;
|
|
uint8_t crtmode;
|
|
// uint8_t attrc[0x40];
|
|
// int attraddr, attrff;
|
|
// int attr_palette_enable;
|
|
int outflipflop;
|
|
int inflipflop;
|
|
int iolatch;
|
|
|
|
int crtcaddr;
|
|
|
|
uint8_t cgastat;
|
|
|
|
// int writemode, readplane;
|
|
// uint8_t planemask;
|
|
|
|
uint8_t egapal[16];
|
|
uint32_t pallook[64];
|
|
PALETTE vgapal;
|
|
|
|
int vtotal, dispend, vsyncstart, vblankstart;
|
|
int hdisp, htotal, hdisp_time, rowoffset;
|
|
int lowres;
|
|
int rowcount;
|
|
double clock;
|
|
uint32_t memaddr_latch, ca_adj;
|
|
|
|
uint64_t dispontime, dispofftime;
|
|
pc_timer_t timer;
|
|
uint64_t epochconst;
|
|
|
|
int dispon;
|
|
int hdisp_on;
|
|
|
|
uint32_t memaddr, memaddr_backup, cursoraddr;
|
|
int vc;
|
|
int scanline;
|
|
int linepos, vslines, linecountff;
|
|
int cursorvisible, cursoron, blink;
|
|
int scrollcache;
|
|
int char_width;
|
|
int font24;
|
|
double pixelclock;
|
|
|
|
int firstline, lastline;
|
|
int firstline_draw, lastline_draw;
|
|
int displine;
|
|
int oddeven;
|
|
|
|
/* Attribute Buffer E0000-E0FFFh (4 KB) */
|
|
uint8_t *cram;
|
|
/* SBCS Font Buffer D8000-DBFFFh (16 KB) */
|
|
/* APA Buffer A0000-DFFFFh (256 KB) */
|
|
uint8_t *vram;
|
|
mem_mapping_t cmap, vmap, paritymap;
|
|
/* Font ROM card option (?KB) */
|
|
// struct {
|
|
// int bank;
|
|
// mem_mapping_t map;
|
|
// uint8_t *rom;
|
|
// int charset;
|
|
// int portdata;
|
|
// } fontcard;
|
|
// uint8_t *changedvram;
|
|
uint32_t vram_display_mask;
|
|
|
|
int parityerror;
|
|
int parityenabled;
|
|
uint8_t parityerroraddr;
|
|
int lowmemorydisabled;
|
|
int crtioenabled;
|
|
|
|
int fullchange;
|
|
|
|
void (*render)(struct epoch_t *epoch);
|
|
|
|
nvr_t nvr;
|
|
int nvrctrl;
|
|
int nvrdata;
|
|
|
|
} epoch_t;
|
|
|
|
static void epoch_recalctimings(epoch_t *epoch);
|
|
static void epoch_reset(void *priv);
|
|
|
|
/*
|
|
[IRQ]
|
|
The IBM 5550 has different IRQ assignments like the 6580 Displaywriter System.
|
|
|
|
| IRQ | 5550 | Displaywriter | PC/XT |
|
|
| --- | ---------- | ---------------------------------- | ---------------- |
|
|
| 0 | ? | Incoming data for printer sharing | Timer |
|
|
| 1 | Async Comm | Transfer data to commo data link | Keyboard |
|
|
| 2 | Fixed Disk | Printer and Mag Card data transfer | Reserved |
|
|
| 3 | Keyboard | Keyboard incoming data | Async Comm (Sec) |
|
|
| 4 | Diskette | Diskette interrupt | Async Comm (Pri) |
|
|
| 5 | ? | Not used | Fixed Disk |
|
|
| 6 | Timer | Software timer | Diskette |
|
|
| 7 | ? | Error on commo data link | Printer |
|
|
|
|
[Memory map]
|
|
| Start Address | Function |
|
|
| ------------- | ----------------------------------------------------- |
|
|
| 0 | 256 KB RAM on System Board |
|
|
| 40000h | 128 KB Expansion RAM Card |
|
|
| 60000h | 128 KB Expansion RAM Card |
|
|
| 80000h | 128 KB Expansion RAM Card |
|
|
| A0000h | Video RAM (Font 16: 144 KB, Font 24: 256 KB) |
|
|
| E0000h | 4 KB Code/Attribute Buffer |
|
|
| E8000h | ? KB Hard Disk Control Local Memory (not implemented) |
|
|
| F0000h | Kanji Font Card (not implemented) |
|
|
| FC000h | ROM |
|
|
*/
|
|
|
|
#ifdef ENABLE_EPOCH_LOG
|
|
// #include <ctype.h>
|
|
// static int dumpno = 0x61;
|
|
// static void
|
|
// epoch_dumpvram(void *priv)
|
|
// {
|
|
// FILE *fp;
|
|
// epoch_t *epoch = (epoch_t *) priv;
|
|
// char str1[64] = "epoch_vramvm_";
|
|
// char str2[3] = {0x30, 0x30, 0};
|
|
// if (!isalnum(dumpno))
|
|
// return;
|
|
// str2[0] = dumpno;
|
|
// dumpno++;
|
|
// str2[1] = (epoch->crtmode & 0xf) + 0x30;
|
|
// strcat(str1,str2);
|
|
// fp = fopen(str1, "wb");
|
|
// if (fp != NULL) {
|
|
// fwrite(epoch->vram, EPOCH_SIZE_VRAM, 1, fp);
|
|
// fclose(fp);
|
|
// }
|
|
// }
|
|
#endif
|
|
|
|
static void
|
|
epoch_out(uint16_t addr, uint16_t val, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
epoch_iolog("%04X:%04X epoch Out addr %03X val %02X\n", cs >> 4, cpu_state.pc, addr, val);
|
|
switch (addr) {
|
|
case LC_INDEX:
|
|
epoch->crtcaddr = val;
|
|
break;
|
|
case LC_DATA:
|
|
if (epoch->crtcaddr > 0x1f)
|
|
return;
|
|
if (epoch->crtioenabled == 0)
|
|
return;
|
|
if (!(epoch->crtcaddr == LC_CURSOR_LOC_HIGH || epoch->crtcaddr == LC_CURSOR_LOC_LOWJ))
|
|
epoch_iolog("%04X:%04X epoch Out addr %03X idx %02X val %02X (%d)\n", cs >> 4, cpu_state.pc, addr, epoch->crtcaddr, val, val);
|
|
if (!(epoch->crtc[epoch->crtcaddr] ^ val))
|
|
return;
|
|
// switch (epoch->crtcaddr) {
|
|
// // case LC_CRTC_OVERFLOW:
|
|
// // // return;
|
|
// // break;
|
|
// case LC_MAXIMUM_SCAN_LINE:
|
|
// // if (!(epoch->ioctl[LS_MODE] & 0x01)) /* 16 or 256 color graphics mode */
|
|
// // val = 0;
|
|
// break;
|
|
// // case LC_VERTICAL_TOTALJ: /* Vertical Total */
|
|
// // break;
|
|
// }
|
|
switch (epoch->crtcaddr) {
|
|
case LC_START_ADDRESS_HIGH:
|
|
case LC_CURSOR_LOC_HIGH:
|
|
case LC_LIGHT_PEN_HIGH:
|
|
val &= 0x3F; /* this is required for the IPL BAT test */
|
|
break;
|
|
}
|
|
epoch->crtc[epoch->crtcaddr] = val;
|
|
switch (epoch->crtcaddr) {
|
|
case LC_HORIZONTAL_DISPLAYED:
|
|
case LC_VERTICAL_DISPLAYED:
|
|
case LC_MAXIMUM_SCAN_LINE:
|
|
case LC_START_ADDRESS_HIGH:
|
|
case LC_START_ADDRESS_LOW:
|
|
epoch->fullchange = 3;
|
|
epoch_recalctimings(epoch);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case LS_ENABLE:
|
|
// mem_mapping_disable(&epoch->paritymap);
|
|
epoch->crtioenabled = 1;
|
|
mem_mapping_enable(&epoch->cmap);
|
|
mem_mapping_enable(&epoch->vmap);
|
|
break;
|
|
case LS_DISABLE:
|
|
epoch->crtioenabled = 0;
|
|
mem_mapping_disable(&epoch->cmap);
|
|
mem_mapping_disable(&epoch->vmap);
|
|
// mem_mapping_enable(&epoch->paritymap);
|
|
break;
|
|
case LS_MODE:
|
|
/* Bit 3: Video output enable, Bit 1: Graphic mode (switch 16 / 9 bit word in Font 16 system) */
|
|
epoch->crtmode = val;
|
|
#ifdef ENABLE_EPOCH_LOG
|
|
// epoch_dumpvram(epoch);
|
|
#endif
|
|
epoch_recalctimings(epoch);
|
|
// epoch->attrff ^= 1;
|
|
break;
|
|
// case LV_PORT:
|
|
// // epoch_iolog("epoch Out addr %03X val %02X ff %d %04X:%04X\n", addr, val, epoch->attrff,cs >> 4, cpu_state.pc);
|
|
// if (!epoch->attrff) {
|
|
// epoch->attraddr = val & 0x3f;
|
|
// if ((val & 0x20) != (epoch->attr_palette_enable & 0x20)) {
|
|
// epoch->fullchange = 3;
|
|
// epoch->attr_palette_enable = val & 0x20;
|
|
// epoch_recalctimings(epoch);
|
|
// }
|
|
// // epoch_iolog("set attraddr: %X\n", epoch->attraddr);
|
|
// } else {
|
|
// if ((epoch->attraddr == LV_PANNING) && (epoch->attrc[LV_PANNING] != val))
|
|
// epoch->fullchange = changeframecount;
|
|
// if (epoch->attrc[epoch->attraddr & 0x3f] != val)
|
|
// epoch_iolog("attr changed %x: %x -> %x\n", epoch->attraddr & 0x3f, epoch->attrc[epoch->attraddr & 0x3f], val);
|
|
// epoch->attrc[epoch->attraddr & 0x3f] = val;
|
|
// // epoch_iolog("set attrc %x: %x\n", epoch->attraddr & 31, val);
|
|
// if (epoch->attraddr < 16)
|
|
// epoch->fullchange = changeframecount;
|
|
// if (epoch->attraddr == LV_MODE_CONTROL || epoch->attraddr < 0x10) {
|
|
// for (uint8_t c = 0; c < 16; c++) {
|
|
// // if (epoch->attrc[LV_MODE_CONTROL] & 0x80)
|
|
// // epoch->egapal[c] = epoch->attrc[c] & 0xf;
|
|
// // else
|
|
// // epoch->egapal[c] = epoch->attrc[c] & 0x3f;
|
|
// }
|
|
// }
|
|
// switch (epoch->attraddr) {
|
|
// case LV_COLOR_PLANE_ENAB:
|
|
// if ((val & 0xff) != epoch->plane_mask)
|
|
// epoch->fullchange = changeframecount;
|
|
// epoch->plane_mask = val & 0xff;
|
|
// break;
|
|
// case LV_CURSOR_CONTROL:
|
|
// switch (val & 0x18) {
|
|
// case 0x08: /* fast blink */
|
|
// epoch->blinkconf = 0x10;
|
|
// break;
|
|
// case 0x18: /* slow blink */
|
|
// epoch->blinkconf = 0x20;
|
|
// break;
|
|
// default: /* no blink */
|
|
// epoch->blinkconf = 0xff;
|
|
// break;
|
|
// }
|
|
// break;
|
|
// case LV_MODE_CONTROL:
|
|
// case LV_ATTRIBUTE_CNTL:
|
|
// case LV_COMPATIBILITY:
|
|
// epoch_recalctimings(epoch);
|
|
// break;
|
|
// default:
|
|
// break;
|
|
// }
|
|
// }
|
|
// epoch->attrff ^= 1;
|
|
// break;
|
|
// case LV_PORT+1:
|
|
// /* VZ Editor's CURSOR.COM writes data via this port */
|
|
// epoch->attrc[epoch->attraddr & 0x3f] = val;
|
|
// break;
|
|
default:
|
|
// epoch_iolog("epoch? Out addr %03X val %02X\n", addr, val);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static uint16_t
|
|
epoch_in(uint16_t addr, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
uint16_t temp = 0xff;
|
|
|
|
switch (addr) {
|
|
case LC_INDEX:
|
|
temp = epoch->crtcaddr;
|
|
break;
|
|
case LC_DATA:
|
|
if (epoch->crtcaddr > 0x1f)
|
|
return EPOCH_INVALIDACCESS16;
|
|
if (epoch->crtioenabled == 0)
|
|
return EPOCH_INVALIDACCESS16;
|
|
temp = epoch->crtc[epoch->crtcaddr];
|
|
break;
|
|
// case LV_PORT:
|
|
// temp = epoch->attraddr | epoch->attr_palette_enable;
|
|
// break;
|
|
// case LV_PORT + 1:
|
|
// switch (epoch->attraddr) {
|
|
// case LV_RAS_STATUS_VIDEO: /* this maybe equivalent to 3ba / 3da ISR1 */
|
|
// if (epoch->cgastat & 0x01)
|
|
// epoch->cgastat &= ~0x30;
|
|
// else
|
|
// epoch->cgastat ^= 0x30; /* toggle */
|
|
// if (epoch->cgastat & 0x08)
|
|
// epoch->cgastat &= ~0x08;
|
|
// else
|
|
// epoch->cgastat ^= 0x08; /* toggle */
|
|
// temp = epoch->cgastat;
|
|
// break;
|
|
// case LV_IDENTIFICATION:
|
|
// temp = 0x28;
|
|
// break;
|
|
// default:
|
|
// temp = epoch->attrc[epoch->attraddr];
|
|
// break;
|
|
// }
|
|
// // epoch_iolog("epoch In %04X(%02X) %04X %04X:%04X\n", addr, epoch->attraddr, temp, cs >> 4, cpu_state.pc);
|
|
// epoch->attrff = 0; /* reset flipflop (VGA does not reset flipflop) */
|
|
// break;
|
|
case LS_MONSENSE:
|
|
temp = 0xff;
|
|
if (!(epoch->crtmode & 0x08)) {/* The video out is active */
|
|
if(epoch->cgastat & 8)
|
|
temp &= 0x7f;
|
|
}
|
|
temp |= 0x01; /* monitor mono or !color */
|
|
// temp &= 0xfe; /* color */
|
|
break;
|
|
}
|
|
if (addr != LS_MONSENSE)
|
|
epoch_iolog("%04X:%04X epoch In %04X %04X\n", cs >> 4, cpu_state.pc, addr, temp);
|
|
return temp;
|
|
}
|
|
/*
|
|
* Write I/O
|
|
* out b(idx), out b(data), out b(data)
|
|
* out b(idx), out w(data)
|
|
* out b(idx), out w(data), out b(data)
|
|
* out w(idx)
|
|
* Read I/O
|
|
* out b(idx), in b(data)
|
|
* out b(idx), in b, in b(data)
|
|
* out b(idx), in w(data)
|
|
*/
|
|
static void
|
|
epoch_outb(uint16_t addr, uint8_t val, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
// epoch_iolog("%04X:%04X epoch Outb addr %03X val %02X es:di=%x:%x ds:si=%x:%x\n", cs >> 4, cpu_state.pc, addr, val, ES, DI, DS, SI);
|
|
epoch->inflipflop = 0;
|
|
switch (addr) {
|
|
// case LS_DATA:
|
|
// case LF_DATA:
|
|
case LC_DATA:
|
|
if (epoch->outflipflop) {
|
|
/* out b(idx), out b(data), out b(data) */
|
|
epoch->iolatch |= (uint16_t) val << 8;
|
|
epoch->outflipflop = 0;
|
|
} else { //
|
|
epoch->iolatch = val;
|
|
epoch->outflipflop = 1;
|
|
}
|
|
break;
|
|
// case LS_INDEX:
|
|
// case LF_INDEX:
|
|
case LC_INDEX:
|
|
default:
|
|
epoch->iolatch = val;
|
|
epoch->outflipflop = 0;
|
|
break;
|
|
}
|
|
epoch_out(addr, epoch->iolatch, epoch);
|
|
}
|
|
static void
|
|
epoch_outw(uint16_t addr, uint16_t val, void *priv)
|
|
{
|
|
epoch_iolog("epoch Outw addr %03X val %04X\n", addr, val);
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
epoch->inflipflop = 0;
|
|
switch (addr) {
|
|
// case LS_INDEX:
|
|
// case LF_INDEX:
|
|
case LC_INDEX:
|
|
// case LG_INDEX:
|
|
epoch_out(addr, val & 0xff, epoch);
|
|
epoch->iolatch = val >> 8;
|
|
epoch_out(addr + 1, epoch->iolatch, epoch);
|
|
epoch->outflipflop = 1;
|
|
break;
|
|
// case LV_PORT:
|
|
// // epoch->attrff = 0;
|
|
// epoch_out(addr, val & 0xff, epoch);
|
|
// epoch_out(addr, val >> 8, epoch);
|
|
// epoch->outflipflop = 0;
|
|
// break;
|
|
// case LS_DATA:
|
|
// case LF_DATA:
|
|
case LC_DATA:
|
|
// case LG_DATA:
|
|
default:
|
|
epoch_out(addr, val, epoch);
|
|
epoch->outflipflop = 0;
|
|
break;
|
|
}
|
|
}
|
|
static uint8_t
|
|
epoch_inb(uint16_t addr, void *priv)
|
|
{
|
|
uint8_t temp;
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
epoch->outflipflop = 0;
|
|
switch (addr) {
|
|
case LC_DATA:
|
|
if (epoch->inflipflop) {
|
|
/* out b(idx), in b(low data), in b(high data) */
|
|
temp = epoch->iolatch >> 8;
|
|
epoch->inflipflop = 0;
|
|
} else { //
|
|
epoch->iolatch = epoch_in(addr, epoch);
|
|
temp = epoch->iolatch & 0xff;
|
|
epoch->inflipflop = 1;
|
|
}
|
|
break;
|
|
// case LS_INDEX:
|
|
// case LF_INDEX:
|
|
case LC_INDEX:
|
|
// case LS_DATA:
|
|
// case LF_DATA:
|
|
default:
|
|
temp = epoch_in(addr, epoch) & 0xff;
|
|
epoch->inflipflop = 0;
|
|
break;
|
|
}
|
|
// epoch_iolog("epoch Inb %04X %02X %04X:%04X\n", addr, temp, cs >> 4, cpu_state.pc);
|
|
return temp;
|
|
}
|
|
static uint16_t
|
|
epoch_inw(uint16_t addr, void *priv)
|
|
{
|
|
uint16_t temp;
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
epoch->inflipflop = 0;
|
|
epoch->outflipflop = 0;
|
|
temp = epoch_in(addr, epoch);
|
|
epoch_iolog("epoch Inw addr %03X val %04X\n", addr, temp);
|
|
return temp;
|
|
}
|
|
|
|
/* Return a memory address for 9-bit word access */
|
|
static uint32_t
|
|
getaddr_9bitword(int32_t addr)
|
|
{
|
|
int32_t bit9addr = addr;
|
|
if (bit9addr & 2)
|
|
bit9addr--;
|
|
if (bit9addr & 0x20000)
|
|
bit9addr += 2;
|
|
bit9addr &= 0x1ffff;
|
|
return bit9addr;
|
|
}
|
|
|
|
/* Get font pattern in a line from video memory */
|
|
static uint32_t
|
|
getfont_ps55dbcs(int32_t code, int32_t line, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
uint32_t font = 0;
|
|
if (code < 1536) {
|
|
code *= 0x80;
|
|
code += line * 4;
|
|
if (epoch->font24) { /* Font 24 (2 x 13 x 29) */
|
|
font = epoch->vram[code];
|
|
font <<= 8;
|
|
code++;
|
|
font |= epoch->vram[code];
|
|
font <<= 8;
|
|
code++;
|
|
font |= epoch->vram[code];
|
|
font <<= 8;
|
|
code++;
|
|
font |= epoch->vram[code];
|
|
} else { /* Font 16 (2 x 9 x 21) */
|
|
int32_t bit9addr = getaddr_9bitword(code);
|
|
int bitnum = bit9addr & 7;
|
|
bit9addr >>= 3;
|
|
bit9addr += 0x20000; /* real: C0000h */
|
|
font = epoch->vram[code];
|
|
font <<= 8;
|
|
font |= (epoch->vram[bit9addr] << (7 - bitnum)) & 0x80; /* get 9th bit */
|
|
// font &= 0xff80;
|
|
// font |= epoch->vram[code + line * 4 + 1];
|
|
font <<= 8;
|
|
code++;
|
|
font |= epoch->vram[code];
|
|
font <<= 8;
|
|
bitnum = code & 0x7;
|
|
font |= (epoch->vram[bit9addr] << (7 - bitnum)) & 0x80; /* get 9th bit */
|
|
// font &= 0xff80ff80;
|
|
// font |= epoch->vram[code + line * 4 + 3];
|
|
}
|
|
} else
|
|
font = EPOCH_INVALIDACCESS32;
|
|
return font;
|
|
}
|
|
|
|
/* Get the foreground color from the attribute byte */
|
|
static uint8_t
|
|
getPS55ForeColor(uint8_t attr, epoch_t *epoch)
|
|
{
|
|
uint8_t foreground = ~attr & 0x08; /* 0000 1000 */
|
|
foreground <<= 2; /* 0010 0000 */
|
|
foreground |= ~attr & 0xc0; /* 1110 0000 */
|
|
foreground >>= 4; /* 0000 1110 */
|
|
// if (epoch->attrc[LV_PAS_STATUS_CNTRL] & 0x40)
|
|
// foreground |= 0x01; /* bright color palette */
|
|
return foreground;
|
|
}
|
|
|
|
static void
|
|
epoch_render_blank(epoch_t *epoch)
|
|
{
|
|
int x, xx;
|
|
|
|
if (epoch->firstline_draw == 2000)
|
|
epoch->firstline_draw = epoch->displine;
|
|
epoch->lastline_draw = epoch->displine;
|
|
|
|
for (x = 0; x < epoch->hdisp + epoch->scrollcache; x++) {
|
|
for (xx = 0; xx < epoch->char_width; xx++)
|
|
((uint32_t *) buffer32->line[epoch->displine])[(x * epoch->char_width) + xx] = 0;
|
|
}
|
|
}
|
|
/* Display Adapter Mode 8, E Drawing */
|
|
static void
|
|
epoch_render_text(epoch_t *epoch)
|
|
{
|
|
if (epoch->firstline_draw == 2000)
|
|
epoch->firstline_draw = epoch->displine;
|
|
epoch->lastline_draw = epoch->displine;
|
|
|
|
if (epoch->fullchange) {
|
|
int offset = (8 - epoch->scrollcache) + 24;
|
|
uint32_t *p = &((uint32_t *) buffer32->line[epoch->displine])[offset];
|
|
int x;
|
|
int drawcursor;
|
|
uint8_t chr, attr;
|
|
int fg, bg;
|
|
uint32_t chr_dbcs;
|
|
int underscore_y = (epoch->font24) ? 28 : 20;
|
|
int chr_wide = 0;
|
|
// int colormode = ((epoch->attrc[LV_PAS_STATUS_CNTRL] & 0x80) == 0x80);
|
|
int colormode = 0;
|
|
// epoch_log("displ: %x, first: %x, epochma: %x, epochsc: %x\n",
|
|
// epoch->displine , epoch->firstline_draw, epoch->memaddr, epoch->scanline);
|
|
for (x = 0; x < epoch->hdisp; x += epoch->char_width) {
|
|
chr = epoch->cram[(epoch->memaddr) & EPOCH_MASK_CRAM];
|
|
attr = epoch->cram[(epoch->memaddr + 1) & EPOCH_MASK_CRAM];
|
|
// if(chr!=0x20) epoch_log("chr: %x, attr: %x ", chr, attr);
|
|
if (colormode) /* IO 3E8h, Index 1Dh */
|
|
{ /* --Parse attribute byte in color mode-- */
|
|
bg = 0; /* bg color is always black (the only way to change background color is programming PAL) */
|
|
fg = getPS55ForeColor(attr, epoch);
|
|
if (attr & 0x04) { /* reverse 0000 0100 */
|
|
bg = fg;
|
|
fg = 0;
|
|
}
|
|
} else { /* --Parse attribute byte in monochrome mode-- */
|
|
if (attr & 0x08)
|
|
fg = 3; /* Highlight 0000 1000 */
|
|
else
|
|
fg = 2;
|
|
bg = 0; /* Background is always color #0 (default is black) */
|
|
if (!(~attr & 0xCC)) /* Invisible 11xx 11xx -> 00xx 00xx */
|
|
{
|
|
fg = bg;
|
|
attr &= 0x33; /* disable blinkking, underscore, highlight and reverse */
|
|
}
|
|
if (attr & 0x04) { /* reverse 0000 0100 */
|
|
bg = fg;
|
|
fg = 0;
|
|
}
|
|
/* Blinking 1000 0000 */
|
|
fg = ((epoch->blink & 0x20) || (!(attr & 0x80))) ? fg : bg;
|
|
// if(chr!=0x20) epoch_log("chr: %x, %x, %x, %x, %x ", chr, attr, fg, epoch->egapal[fg], epoch->pallook[epoch->egapal[fg]]);
|
|
}
|
|
/* Draw character */
|
|
for (uint32_t n = 0; n < epoch->char_width; n++)
|
|
p[n] = epoch->pallook[epoch->egapal[bg]]; /* draw blank */
|
|
/* SBCS or DBCS left half */
|
|
if (chr_wide == 0) {
|
|
if (attr & 0x01)
|
|
chr_wide = 1;
|
|
// chr_wide = 0;
|
|
/* Stay drawing If the char code is DBCS and not at last column. */
|
|
if (chr_wide) {
|
|
/* Get high DBCS code from the next video address */
|
|
chr_dbcs = epoch->cram[(epoch->memaddr + 2) & EPOCH_MASK_CRAM];
|
|
chr_dbcs <<= 8;
|
|
chr_dbcs |= chr;
|
|
/* Get the font pattern */
|
|
uint32_t font = getfont_ps55dbcs(chr_dbcs, epoch->scanline, epoch);
|
|
/* Draw 13 dots */
|
|
for (uint32_t n = 0; n < epoch->char_width; n++) {
|
|
p[n] = epoch->pallook[epoch->egapal[(font & 0x80000000) ? fg : bg]];
|
|
font <<= 1;
|
|
}
|
|
} else {
|
|
/* the char code is SBCS (ANK) */
|
|
uint32_t fontbase;
|
|
uint16_t font;
|
|
if (attr & 0x02) /* second map of SBCS font */
|
|
fontbase = EPOCH_VRAM_SBEX;
|
|
else
|
|
fontbase = EPOCH_VRAM_SBCS;
|
|
if (epoch->font24) {
|
|
font = epoch->vram[fontbase + chr * 0x80 + epoch->scanline * 4 + 2]; /* w13xh29 font */
|
|
font <<= 8;
|
|
font |= epoch->vram[fontbase + chr * 0x80 + epoch->scanline * 4 + 3];
|
|
} else {
|
|
uint32_t bitnum, bit9addr;
|
|
fontbase += chr * 0x80 + epoch->scanline * 4;
|
|
bit9addr = getaddr_9bitword(fontbase);
|
|
bitnum = bit9addr & 7;
|
|
bit9addr >>= 3;
|
|
bit9addr += 0x20000; /* real: C0000h */
|
|
|
|
fontbase &= 0x1ffff;
|
|
font = epoch->vram[fontbase + 2]; /* w9xh21 font */
|
|
font <<= 8;
|
|
font |= (epoch->vram[bit9addr] << (7 - bitnum)) & 0x80;
|
|
// if(chr!=0x20) epoch_log("faddr: %x, scline: %x, chr: %x, font: %x ", fontbase + chr * 0x80 + epoch->scanline * 4, epoch->scanline, chr, font);
|
|
}
|
|
// if(chr!=0x20) epoch_log("memaddr: %x, scanline: %x, chr: %x, font: %x ", epoch->memaddr, epoch->scanline, chr, font);
|
|
/* Draw 13 dots */
|
|
for (uint32_t n = 0; n < epoch->char_width; n++) {
|
|
p[n] = epoch->pallook[epoch->egapal[(font & 0x8000) ? fg : bg]];
|
|
font <<= 1;
|
|
}
|
|
}
|
|
}
|
|
/* right half of DBCS */
|
|
else {
|
|
uint32_t font = getfont_ps55dbcs(chr_dbcs, epoch->scanline, epoch);
|
|
/* Draw 13 dots */
|
|
for (uint32_t n = 0; n < epoch->char_width; n++) {
|
|
p[n] = epoch->pallook[epoch->egapal[(font & 0x8000) ? fg : bg]];
|
|
font <<= 1;
|
|
}
|
|
chr_wide = 0;
|
|
}
|
|
/* Line 28 (Underscore) Note: Draw this first to display blink + vertical + underline correctly. */
|
|
if (epoch->scanline == underscore_y && attr & 0x40 && !colormode) { /* Underscore only in monochrome mode */
|
|
for (uint32_t n = 0; n < epoch->char_width; n++)
|
|
p[n] = epoch->pallook[epoch->egapal[fg]]; /* under line (white) */
|
|
}
|
|
/* Column 1 (Vertical Line) */
|
|
if (attr & 0x10) {
|
|
p[0] = epoch->pallook[epoch->egapal[2]]; /* vertical line (white) */
|
|
}
|
|
if (epoch->scanline == 0 && attr & 0x20) { /* HGrid */
|
|
for (uint32_t n = 0; n < epoch->char_width; n++)
|
|
p[n] = epoch->pallook[epoch->egapal[2]]; /* horizontal line (white) */
|
|
}
|
|
/* Drawing text cursor */
|
|
drawcursor = ((epoch->memaddr == epoch->cursoraddr) && epoch->cursorvisible && epoch->cursoron);
|
|
if (drawcursor) {
|
|
// int cursorwidth = (epoch->crtc[LC_COMPATIBILITY] & 0x20 ? 26 : 13);
|
|
int cursorwidth = epoch->char_width;
|
|
int cursorcolor = 2; /* Choose color 2 if mode 8 */
|
|
fg = ((attr & 0x08) ? 3 : 2);
|
|
bg = 0;
|
|
if (attr & 0x04) { /* Color 0 if reverse */
|
|
bg = fg;
|
|
fg = 0;
|
|
}
|
|
for (uint32_t n = 0; n < cursorwidth; n++)
|
|
if (p[n] == epoch->pallook[epoch->egapal[cursorcolor]] || epoch->egapal[bg] == epoch->egapal[cursorcolor])
|
|
p[n] = (p[n] == epoch->pallook[epoch->egapal[bg]]) ? epoch->pallook[epoch->egapal[fg]] : epoch->pallook[epoch->egapal[bg]];
|
|
else
|
|
p[n] = (p[n] == epoch->pallook[epoch->egapal[bg]]) ? epoch->pallook[epoch->egapal[cursorcolor]] : p[n];
|
|
}
|
|
epoch->memaddr += 2;
|
|
p += epoch->char_width;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
epoch_render_color_4bpp(epoch_t *epoch)
|
|
{
|
|
// int changed_offset = epoch->memaddr >> 9;
|
|
// epoch_log("memaddr %x cf %x\n", epoch->memaddr, changed_offset);
|
|
// epoch->plane_mask &= 1; /*safety */
|
|
|
|
// if (epoch->changedvram[changed_offset] || epoch->changedvram[changed_offset + 1] || epoch->fullchange) {
|
|
int x;
|
|
int offset = (8 - epoch->scrollcache) + 24;
|
|
uint32_t *p = &((uint32_t *) buffer32->line[epoch->displine])[offset];
|
|
|
|
if (epoch->firstline_draw == 2000)
|
|
epoch->firstline_draw = epoch->displine;
|
|
epoch->lastline_draw = epoch->displine;
|
|
// epoch_log("d %X\n", epoch->memaddr);
|
|
int readvaddr = (epoch->memaddr * 8) + (epoch->scanline * 128);
|
|
for (x = 0; x <= epoch->hdisp; x += 8) /* hdisp = 1024 */
|
|
{
|
|
uint8_t edat[8];
|
|
uint8_t dat;
|
|
|
|
/* get 8 pixels from vram */
|
|
readvaddr &= epoch->vram_display_mask;
|
|
*(uint8_t *) (&edat[0]) = *(uint8_t *) (&epoch->vram[readvaddr]);
|
|
readvaddr += 1;
|
|
|
|
dat = ((edat[0] >> 7) & 1);
|
|
p[0] = epoch->pallook[epoch->egapal[dat]];
|
|
dat = ((edat[0] >> 6) & 1);
|
|
p[1] = epoch->pallook[epoch->egapal[dat]];
|
|
dat = ((edat[0] >> 5) & 1);
|
|
p[2] = epoch->pallook[epoch->egapal[dat]];
|
|
dat = ((edat[0] >> 4) & 1);
|
|
p[3] = epoch->pallook[epoch->egapal[dat]];
|
|
dat = ((edat[0] >> 3) & 1);
|
|
p[4] = epoch->pallook[epoch->egapal[dat]];
|
|
dat = ((edat[0] >> 2) & 1);
|
|
p[5] = epoch->pallook[epoch->egapal[dat]];
|
|
dat = ((edat[0] >> 1) & 1);
|
|
p[6] = epoch->pallook[epoch->egapal[dat]];
|
|
dat = ((edat[0] >> 0) & 1);
|
|
p[7] = epoch->pallook[epoch->egapal[dat]];
|
|
p += 8;
|
|
}
|
|
}
|
|
|
|
/*
|
|
INT 10h video modes supported in DOS K3.44.
|
|
Mode Type Colors Text Base Address PELs Render
|
|
2 A/N 3 80 x 25 E0000h 1040 x 725* text
|
|
8 A/N/K 3 80 x 25 E0000h 1040 x 725* text
|
|
9 APA 2 80 x 25 A0000h 720 x 512 color_4bpp
|
|
Ah APA 2 78 x 25 A0000h 1024 x 768 color_4bpp
|
|
Bh APA 16 40 x 25 A0000h 360 x 512 n/a
|
|
Ch APA 16 80 x 25 A0000h 720 x 512 n/a
|
|
Dh APA 16 78 x 25 A0000h 1024 x 768 n/a
|
|
Eh A/N/K 16 80 x 25 E0000h 1040 x 725* n/a
|
|
(* 720 x 525 in the Font 16 system.)
|
|
*/
|
|
static void
|
|
epoch_recalctimings(epoch_t *epoch)
|
|
{
|
|
double crtcconst;
|
|
double _dispontime, _dispofftime, disptime;
|
|
|
|
epoch->vblankstart = epoch->crtc[LC_VERTICAL_TOTAL] & 0x7f;
|
|
epoch->dispend = epoch->crtc[LC_VERTICAL_DISPLAYED] & 0x7f;
|
|
epoch->vsyncstart = epoch->crtc[LC_V_SYNC_POSITION] & 0x7f;
|
|
epoch->vblankstart += 1;
|
|
epoch->vtotal = epoch->vblankstart + (epoch->crtc[LC_V_TOTAL_ADJUST] & 0x1f);
|
|
epoch->hdisp = epoch->crtc[LC_HORIZONTAL_DISPLAYED] & 0xff;
|
|
|
|
// epoch->hdisp -= epoch->crtc[LC_START_H_DISPLAY_ENAB];
|
|
// epoch->dispend -= epoch->crtc[LC_START_V_DISPLAY_ENAB];
|
|
//epoch_log("Dispend %d\n", epoch->dispend);
|
|
// epoch->vsyncstart += 1;
|
|
//epoch->vtotal += 1;
|
|
|
|
epoch->htotal = epoch->crtc[LC_HORIZONTAL_TOTAL] & 0xff;
|
|
epoch->htotal += 1;
|
|
|
|
// epoch->rowoffset = epoch->crtc[LC_OFFSET]; /* number of bytes in a scanline */
|
|
epoch->rowoffset = epoch->crtc[LC_HORIZONTAL_DISPLAYED] & 0xff;
|
|
|
|
epoch->clock = epoch->epochconst;
|
|
|
|
if (epoch->vtotal == 0)
|
|
epoch->vtotal = epoch->vsyncstart = epoch->vblankstart = 32;
|
|
if (epoch->htotal == 0)
|
|
epoch->htotal = epoch->dispend = epoch->hdisp = 64;
|
|
if (epoch->rowoffset == 0)
|
|
epoch->rowoffset = 64 * 2; /* To avoid causing a DBZ error */
|
|
|
|
epoch->memaddr_latch = ((epoch->crtc[LC_START_ADDRESS_HIGH] & 0x3f) << 8) | epoch->crtc[LC_START_ADDRESS_LOW];
|
|
|
|
epoch->ca_adj = 0;
|
|
epoch->rowcount = epoch->crtc[LC_MAXIMUM_SCAN_LINE] & 0x1f;
|
|
epoch->rowcount += 1;
|
|
|
|
epoch->vtotal *= (epoch->rowcount + 1);
|
|
epoch->dispend *= (epoch->rowcount + 1);
|
|
epoch->vsyncstart *= (epoch->rowcount + 1);
|
|
epoch->vblankstart *= (epoch->rowcount + 1);
|
|
|
|
epoch->hdisp_time = epoch->hdisp;
|
|
|
|
/* determine display mode */
|
|
if (epoch->crtmode & 0x02) {
|
|
if (epoch->font24) {
|
|
epoch->hdisp *= 16;
|
|
epoch->char_width = 16;
|
|
} else {
|
|
epoch->hdisp *= 12;
|
|
epoch->char_width = 12;
|
|
}
|
|
/* PS/55 8-color */
|
|
epoch_log("Set videomode to PS/55 4 bpp graphics.\n");
|
|
epoch->render = epoch_render_color_4bpp;
|
|
epoch->vram_display_mask = EPOCH_MASK_A000;
|
|
} else {
|
|
/* PS/55 text(color/mono) */
|
|
epoch_log("Set videomode to PS/55 Mode 8/E text.\n");
|
|
epoch->render = epoch_render_text;
|
|
epoch->vram_display_mask = EPOCH_MASK_CRAM;
|
|
if (epoch->font24) {
|
|
epoch->hdisp *= 13;
|
|
epoch->char_width = 13;
|
|
} else {
|
|
epoch->hdisp *= 9;
|
|
epoch->char_width = 9;
|
|
}
|
|
}
|
|
if (epoch->crtmode & 0x08)
|
|
epoch->render = epoch_render_blank;
|
|
|
|
if (epoch->vblankstart < epoch->vsyncstart)
|
|
epoch->vsyncstart = epoch->vblankstart;
|
|
if (epoch->vsyncstart < epoch->dispend)
|
|
epoch->dispend = epoch->vsyncstart;
|
|
|
|
crtcconst = epoch->clock * epoch->char_width;
|
|
|
|
disptime = epoch->htotal;
|
|
_dispontime = epoch->hdisp_time;
|
|
|
|
epoch_log("Disptime %f dispontime %f hdisp %i\n", disptime, _dispontime, epoch->hdisp);
|
|
|
|
_dispofftime = disptime - _dispontime;
|
|
_dispontime *= crtcconst;
|
|
_dispofftime *= crtcconst;
|
|
|
|
epoch->dispontime = (uint64_t) _dispontime;
|
|
epoch->dispofftime = (uint64_t) _dispofftime;
|
|
if (epoch->dispontime < TIMER_USEC)
|
|
epoch->dispontime = TIMER_USEC;
|
|
if (epoch->dispofftime < TIMER_USEC)
|
|
epoch->dispofftime = TIMER_USEC;
|
|
epoch_log("epoch horiz display end %i vidclock %f htotal %i\n", epoch->hdisp, epoch->clock, epoch->htotal);
|
|
epoch_log("epoch vert display end %i max row %i vsyncstart %i vtotal %i\n",epoch->dispend,epoch->rowcount,epoch->vsyncstart,epoch->vtotal);
|
|
epoch_log("epoch dispon %lu dispoff %lu on(us) %f off(us) %f\n",epoch->dispontime, epoch->dispofftime,
|
|
(double)epoch->dispontime / (double)cpuclock / (double) (1ULL << 32) * 1000000.0,
|
|
(double)epoch->dispofftime / (double)cpuclock / (double) (1ULL << 32) * 1000000.0);
|
|
}
|
|
|
|
static void
|
|
epoch_doblit(int wx, int wy, epoch_t *epoch)
|
|
{
|
|
if (wx != xsize || wy != ysize) {
|
|
xsize = wx;
|
|
ysize = wy;
|
|
set_screen_size(xsize, ysize);
|
|
if (video_force_resize_get())
|
|
video_force_resize_set(0);
|
|
}
|
|
video_blit_memtoscreen(32, 0, xsize, ysize);
|
|
frames++;
|
|
|
|
video_res_x = wx;
|
|
video_res_y = wy;
|
|
video_bpp = 8;
|
|
}
|
|
|
|
static void
|
|
epoch_poll(void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
int x;
|
|
|
|
if (!epoch->linepos) {
|
|
timer_advance_u64(&epoch->timer, epoch->dispofftime);
|
|
|
|
epoch->cgastat |= 1;
|
|
epoch->linepos = 1;
|
|
|
|
if (epoch->dispon) {
|
|
epoch->hdisp_on = 1;
|
|
|
|
epoch->memaddr &= epoch->vram_display_mask;
|
|
if (epoch->firstline == 2000) {
|
|
epoch->firstline = epoch->displine;
|
|
video_wait_for_buffer();
|
|
}
|
|
|
|
if ((epoch->displine ^ !epoch->oddeven) & 1)
|
|
epoch->render(epoch);
|
|
|
|
if (epoch->lastline < epoch->displine)
|
|
epoch->lastline = epoch->displine;
|
|
}
|
|
|
|
// epoch_log("%03i %06X %06X\n", epoch->displine, epoch->memaddr,epoch->vram_display_mask);
|
|
epoch->displine++;
|
|
if ((epoch->cgastat & 8) && ((epoch->displine & 0xf) == (epoch->vblankstart & 0xf)) && epoch->vslines) {
|
|
// epoch_log("Vsync off at line %i\n",displine);
|
|
epoch->cgastat &= ~8;
|
|
}
|
|
epoch->vslines++;
|
|
if (epoch->displine > 2000)
|
|
epoch->displine = 0;
|
|
} else {
|
|
// epoch_log("VC %i memaddr %05X\n", epoch->vc, epoch->memaddr);
|
|
timer_advance_u64(&epoch->timer, epoch->dispontime);
|
|
|
|
if (epoch->dispon)
|
|
epoch->cgastat &= ~1;
|
|
epoch->hdisp_on = 0;
|
|
|
|
epoch->linepos = 0;
|
|
if (epoch->scanline == (epoch->crtc[LC_CURSOR_ROW_END] & 0x1f))
|
|
epoch->cursorvisible = 0;
|
|
if (epoch->dispon) {
|
|
if (epoch->scanline == epoch->rowcount) {
|
|
epoch->linecountff = 0;
|
|
epoch->scanline = 0;
|
|
|
|
epoch->memaddr_backup += (epoch->rowoffset << 1); /* interlace */
|
|
epoch->memaddr_backup &= epoch->vram_display_mask;
|
|
epoch->memaddr = epoch->memaddr_backup;
|
|
} else {
|
|
epoch->linecountff = 0;
|
|
epoch->scanline++;
|
|
epoch->scanline &= 0x1f;
|
|
epoch->memaddr = epoch->memaddr_backup;
|
|
}
|
|
}
|
|
|
|
epoch->vc++;
|
|
epoch->vc &= 0x7ff;
|
|
|
|
if (epoch->vc == epoch->dispend) {
|
|
epoch->dispon = 0;
|
|
if (!(epoch->crtmode & 0x02)) { /* in text mode */
|
|
switch (epoch->crtc[LC_CURSOR_ROW_START] & 0x60) {
|
|
case 0x20:
|
|
epoch->cursoron = 0;
|
|
break;
|
|
case 0x60:
|
|
epoch->cursoron = epoch->blink & 0x10;
|
|
break;
|
|
case 0x40:
|
|
epoch->cursoron = epoch->blink & 0x08;
|
|
break;
|
|
default:
|
|
epoch->cursoron = 1;
|
|
break;
|
|
}
|
|
if (!(epoch->blink & (0x08 - 2))) /* force redrawing for cursor and blink attribute */
|
|
epoch->fullchange = 3;
|
|
}
|
|
epoch->blink++;
|
|
|
|
// for (x = 0; x <= (EPOCH_MASK_VRAMPLANE >> 9); x++) {
|
|
// if (epoch->changedvram[x])
|
|
// epoch->changedvram[x]--;
|
|
// }
|
|
// memset(changedvram,0,2048); del
|
|
if (epoch->fullchange) {
|
|
epoch->fullchange--;
|
|
}
|
|
}
|
|
if (epoch->vc == epoch->vsyncstart) {
|
|
int wx, wy;
|
|
// epoch_log("VC vsync %i %i\n", epoch->firstline_draw, epoch->lastline_draw);
|
|
epoch->dispon = 0;
|
|
epoch->cgastat |= 8;
|
|
x = epoch->hdisp;
|
|
|
|
if (!epoch->oddeven)
|
|
epoch->lastline++;
|
|
if (epoch->oddeven)
|
|
epoch->firstline--;
|
|
|
|
wx = x;
|
|
wy = epoch->lastline - epoch->firstline;
|
|
|
|
epoch_doblit(wx, wy, epoch);
|
|
|
|
epoch->firstline = 2000;
|
|
epoch->lastline = 0;
|
|
|
|
epoch->firstline_draw = 2000;
|
|
epoch->lastline_draw = 0;
|
|
|
|
epoch->oddeven ^= 1;
|
|
|
|
epoch->vslines = 0;
|
|
|
|
epoch->memaddr
|
|
= epoch->memaddr_backup = epoch->memaddr_latch << 1;
|
|
epoch->cursoraddr = ((epoch->crtc[LC_CURSOR_LOC_HIGH] << 8) | epoch->crtc[LC_CURSOR_LOC_LOWJ]) + epoch->ca_adj;
|
|
epoch->cursoraddr <<= 1;
|
|
|
|
// epoch_log("Addr %08X vson %03X vsoff %01X\n",epoch->memaddr,epoch->vsyncstart,epoch->crtc[0x11]&0xF);
|
|
}
|
|
if (epoch->vc == epoch->vtotal) {
|
|
// epoch_log("VC vtotal\n");
|
|
// printf("Frame over at line %i %i %i %i\n",displine,vc,epoch_vsyncstart,epoch_dispend);
|
|
epoch->vc = 0;
|
|
epoch->scanline = 0;
|
|
epoch->dispon = 1;
|
|
epoch->displine = 0;
|
|
// epoch->scrollcache = epoch->attrc[LV_PANNING] & 0x07;
|
|
epoch->scrollcache = 0;
|
|
}
|
|
if (epoch->scanline == (epoch->crtc[LC_CURSOR_ROW_START] & 31))
|
|
epoch->cursorvisible = 1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
epoch_vram_write(uint32_t addr, uint8_t val, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
// epoch_log("epoch_vw: %x %x\n", addr, val);
|
|
addr &= EPOCH_MASK_VRAM;
|
|
epoch->vram[addr] = val;
|
|
epoch->fullchange = 3;
|
|
}
|
|
static uint8_t
|
|
epoch_vram_read(uint32_t addr, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
addr &= EPOCH_MASK_VRAM;
|
|
return epoch->vram[addr];
|
|
}
|
|
|
|
static void
|
|
epoch_vram_writew(uint32_t addr, uint16_t val, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
// epoch_log("%04X:%04X epoch_vww: %x, val %x DS %x SI %x ES %x DI %x %x\n", cs >> 4, cpu_state.pc, addr, val,DS,SI,ES,DI, epoch->crtc[LC_INTERLACE_AND_SKEW]);
|
|
// epoch_log("%04X:%04X epoch_vww: %x, val %x cm %x\n", cs >> 4, cpu_state.pc, addr, val, epoch->crtmode);
|
|
cycles -= video_timing_write_w;
|
|
addr -= 0xA0000;
|
|
if (!(epoch->crtmode & 0x02) && !(epoch->font24)) {
|
|
uint32_t toaddr, bitnum;
|
|
|
|
/* rw one word with 9 bits */
|
|
/* virtual: 20000h (0010b, 0011b) -> real: 00001h (0000b, 0001b) */
|
|
toaddr = getaddr_9bitword(addr);
|
|
bitnum = toaddr & 7;
|
|
epoch_vram_write(toaddr, val & 0xff, epoch);
|
|
|
|
/* get 9th bit */
|
|
toaddr >>= 3;
|
|
toaddr += 0x20000; /* real: C0000h */
|
|
val >>= 15;
|
|
val <<= bitnum;
|
|
|
|
/* mask to update one bit */
|
|
val |= (epoch_vram_read(toaddr, epoch) & (~(1 << bitnum)));
|
|
epoch_vram_write(toaddr, val, epoch);
|
|
// epoch_log("%x %x\n", toaddr, val);
|
|
// epoch_log("%x %x\n", addr, val);
|
|
} else {/* is graphic mode */
|
|
epoch_vram_write(addr, val & 0xff, epoch);
|
|
epoch_vram_write(addr + 1, val >> 8, epoch);
|
|
}
|
|
// epoch_log("%x %x\n", addr, val);
|
|
}
|
|
|
|
static uint16_t
|
|
epoch_vram_readw(uint32_t addr, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
cycles -= video_timing_read_w;
|
|
addr -= 0xA0000;
|
|
// epoch_log("%04X:%04X epoch_vrw: %x cm %x\n", cs >> 4, cpu_state.pc, addr, epoch->crtmode);
|
|
if (!(epoch->crtmode & 0x02) && !(epoch->font24)) {
|
|
uint16_t ret;
|
|
uint32_t bitnum;
|
|
uint32_t toaddr;
|
|
/* rw one word with 9 bits */
|
|
/* virtual: 20000h (0010b, 0011b) -> real: 00001h (0000b, 0001b) */
|
|
toaddr = getaddr_9bitword(addr);
|
|
bitnum = toaddr & 7;
|
|
ret = epoch_vram_read(toaddr, epoch);
|
|
/* get 9th bit */
|
|
toaddr >>= 3;
|
|
toaddr += 0x20000; /* real: C0000h */
|
|
ret |= (epoch_vram_read(toaddr, epoch) << (8 + 7 - bitnum)) & 0x8000;
|
|
return ret;
|
|
// return epoch_vram_read(addr, epoch) | (epoch_vram_read(addr + 1, epoch) << 8);
|
|
} else {/* is graphic mode */
|
|
return epoch_vram_read(addr, epoch) | (epoch_vram_read(addr + 1, epoch) << 8);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
epoch_cram_write(uint32_t addr, uint8_t val, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
addr &= EPOCH_MASK_CRAM;
|
|
epoch->cram[addr] = val;
|
|
epoch->fullchange = 3;
|
|
// epoch_log("cw %04X:%04X %04X %02X\n", cs >> 4, cpu_state.pc, addr, val);
|
|
}
|
|
static void
|
|
epoch_cram_writeb(uint32_t addr, uint8_t val, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
// epoch_log("epoch_cram_writeb: Write to %x, val %x\n", addr, val);
|
|
cycles -= video_timing_write_b;
|
|
epoch_cram_write(addr, val, epoch);
|
|
}
|
|
static void
|
|
epoch_cram_writew(uint32_t addr, uint16_t val, void *priv)
|
|
{
|
|
// epoch_log("epoch_cram_writ ew: Write to %x, val %x\n", addr, val);
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
cycles -= video_timing_write_w;
|
|
epoch_cram_write(addr, val & 0xff, epoch);
|
|
epoch_cram_write(addr + 1, val >> 8, epoch);
|
|
}
|
|
|
|
static uint8_t
|
|
epoch_cram_read(uint32_t addr, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
addr &= EPOCH_MASK_CRAM;
|
|
return epoch->cram[addr];
|
|
}
|
|
static uint8_t
|
|
epoch_cram_readb(uint32_t addr, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
cycles -= video_timing_read_b;
|
|
return epoch_cram_read(addr, epoch);
|
|
}
|
|
|
|
static uint16_t
|
|
epoch_cram_readw(uint32_t addr, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
cycles -= video_timing_read_w;
|
|
return epoch_cram_read(addr, epoch) | (epoch_cram_read(addr + 1, epoch) << 8);
|
|
}
|
|
|
|
static uint8_t
|
|
epoch_parity_readb(uint32_t addr, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
if ((epoch->parityerror != 0) && (epoch->parityenabled != 0)) {
|
|
epoch->parityerroraddr = (addr >> 16) & 0xFF; /* X000:0 */
|
|
epoch_log("%04X:%04X perror at %0X\n", cs >> 4, cpu_state.pc, addr);
|
|
if (nmi_mask)
|
|
nmi_raise();
|
|
}
|
|
if (epoch->lowmemorydisabled) {
|
|
if ((addr >= 0x40000) && (addr < 0xA0000)) {
|
|
epoch_log("%04X:%04X mrerror at %0X\n", cs >> 4, cpu_state.pc, addr);
|
|
return EPOCH_INVALIDACCESS8;
|
|
}
|
|
}
|
|
return mem_read_ram(addr, priv);
|
|
}
|
|
|
|
static uint16_t
|
|
epoch_parity_readw(uint32_t addr, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
if ((epoch->parityerror != 0) && (epoch->parityenabled != 0)) {
|
|
epoch->parityerroraddr = (addr >> 16) & 0xFF;
|
|
epoch_log("%04X:%04X perror at %X\n", cs >> 4, cpu_state.pc, addr);
|
|
if (nmi_mask)
|
|
nmi_raise();
|
|
}
|
|
if (epoch->lowmemorydisabled) {
|
|
if ((addr >= 0x40000) && (addr < 0xA0000)) {
|
|
epoch_log("%04X:%04X mrerror at %X\n", cs >> 4, cpu_state.pc, addr);
|
|
return EPOCH_INVALIDACCESS16;
|
|
}
|
|
}
|
|
return mem_read_ramw(addr, priv);
|
|
}
|
|
|
|
static void
|
|
epoch_parity_writeb(uint32_t addr, uint8_t val, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
// epoch_log("%04X:%04X mw %0X\n", cs >> 4, cpu_state.pc, addr);
|
|
if (epoch->parityenabled == 0)
|
|
epoch->parityerror = 1;
|
|
if (epoch->lowmemorydisabled) {
|
|
if ((addr >= 0x40000) && (addr < 0xA0000)) {
|
|
epoch_log("%04X:%04X mwerror at %X\n", cs >> 4, cpu_state.pc, addr);
|
|
if (epoch->parityenabled)
|
|
epoch->parityerror = 1;
|
|
return;
|
|
}
|
|
}
|
|
mem_write_ram(addr, val, priv);
|
|
}
|
|
static void
|
|
epoch_parity_writew(uint32_t addr, uint16_t val, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
if (epoch->parityenabled == 0)
|
|
epoch->parityerror = 1;
|
|
if (epoch->lowmemorydisabled) {
|
|
if ((addr >= 0x40000) && (addr < 0xA0000)) {
|
|
epoch_log("%04X:%04X mwerror at %X\n", cs >> 4, cpu_state.pc, addr);
|
|
if (epoch->parityenabled)
|
|
epoch->parityerror = 1;
|
|
return;
|
|
}
|
|
}
|
|
mem_write_ramw(addr, val, priv);
|
|
}
|
|
|
|
static uint8_t
|
|
epoch_misc_in(uint16_t port, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
uint8_t ret = 0xff;
|
|
|
|
switch (port) {
|
|
/*
|
|
I/O A0h R:
|
|
xxxx xxx1: Memory or parity error?
|
|
0xxx xxxx: Coprocessor installed?
|
|
*/
|
|
case 0xA0:
|
|
if(!epoch->parityenabled)
|
|
ret &= 0xfe;
|
|
else
|
|
ret = epoch->parityerror & 1;
|
|
if (fpu_type == FPU_NONE)
|
|
ret |= 0x80;
|
|
break;
|
|
case 0xA1: /* High address where memory error occured */
|
|
ret = epoch->parityerroraddr;
|
|
break;
|
|
/*
|
|
I/O A2h R:
|
|
xxxx 0x0x: Color 16 CRT
|
|
xxxx 1x0x: Mono 24 CRT ?
|
|
xxxx xx1x: Mono 16 CRT
|
|
xx1x xxxx: No hard drive
|
|
x1xx xxxx: No floppy drive
|
|
1xxx xxxx: No (bootable?) hard drive
|
|
*/
|
|
case 0xA2:
|
|
if (epoch->font24)
|
|
ret = 0xA8; /* Mono 24 */
|
|
else
|
|
ret = 0xA2; /* Mono 16 */
|
|
break;
|
|
/*
|
|
I/O A3h R:
|
|
xxxx x111: Main RAM 256 KB
|
|
xxxx x110: Main RAM 384 KB
|
|
xxxx x100: Main RAM 512 KB
|
|
xxxx x000: Main RAM 640 KB
|
|
xxxx 1xxx: Serial port 3f8h
|
|
xxx1 xxxx: Serial port 2f8h
|
|
*/
|
|
case 0xA3:
|
|
ret = 0x08;
|
|
if (mem_size < 384)
|
|
ret |= 0x07;
|
|
else if (mem_size < 512)
|
|
ret |= 0x06;
|
|
else if (mem_size < 640)
|
|
ret |= 0x04;
|
|
break;
|
|
case 0xA4:
|
|
ret = 0;
|
|
break;
|
|
case 0xA5:
|
|
ret = 0x08; /* Bit 3: Keyboard connected? */
|
|
break;
|
|
// case 0x164:
|
|
// switch (epoch->fontcard.portdata) {
|
|
// case 0x16A:
|
|
// ret = 0xFD;
|
|
// break;
|
|
// case 0x168:
|
|
// ret = 0xFE;
|
|
// break;
|
|
// }
|
|
// break;
|
|
default:
|
|
break;
|
|
}
|
|
epoch_log("%04X:%04X I/O In %02X: %02X\n", cs >> 4, cpu_state.pc, port, ret);
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
epoch_misc_out(uint16_t port, uint8_t val, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
pit_intf_t *pit_intf = &pit_devs[0];
|
|
|
|
// dev->regs[port & 0x0007] = val;
|
|
// epoch_log("%04X:%04X I/O Out %02X: %02X\n AX=%04X BX=%04X CX=%04X DX=%04X ES=%04X DI=%04X DS=%04X SI=%04X\n",
|
|
// cs >> 4, cpu_state.pc, port, val, AX, BX, CX, DX, ES, DI, DS, SI);
|
|
if(port != 0x44)
|
|
epoch_log("%04X:%04X I/O Out %02X: %02X\n", cs >> 4, cpu_state.pc, port, val);
|
|
|
|
switch (port) {
|
|
case 0x44:
|
|
for (uint8_t i = 0; i < 3; i++) {
|
|
pit_intf->set_gate(pit_intf->data, i, val & 1);
|
|
}
|
|
break;
|
|
/*
|
|
I/O A0h W:
|
|
x1xx xxxx: Enable parity update (Disable this -> Write data -> Enable this -> Read data, causes NMI)
|
|
1xxx xxxx: Enable NMI check
|
|
*/
|
|
case 0xA0:
|
|
nmi_mask = val & 0x80;
|
|
epoch->parityenabled = val & 0x40; /* 1 = Enable read/write with parity */
|
|
break;
|
|
case 0xA1:
|
|
/* Diagnostics LED (used by debug card module) */
|
|
break;
|
|
case 0xA2:
|
|
/* Reset memory error bit */
|
|
epoch->parityerror = 0;
|
|
break;
|
|
// case 0x160 ... 0x16A:
|
|
// mem_mapping_enable(&epoch->fontcard.map);
|
|
// epoch->fontcard.portdata = port;
|
|
// break;
|
|
case 0x310 ... 0x312:
|
|
epoch->lowmemorydisabled = 0;
|
|
epoch_log("Low memory enabled\n");
|
|
break;
|
|
case 0x314 ... 0x316:
|
|
epoch->lowmemorydisabled = 1;
|
|
epoch_log("Low memory disabled\n");
|
|
break;
|
|
}
|
|
}
|
|
typedef struct epochkbd_t {
|
|
int want_irq;
|
|
int blocked;
|
|
|
|
uint8_t pa;
|
|
uint8_t pb;
|
|
uint8_t clk_hold;
|
|
uint8_t key_waiting;
|
|
uint8_t kbd_reset_step;
|
|
uint8_t mouse_reset_step;
|
|
int mouse_enabled;
|
|
int mouse_queue_num;
|
|
uint8_t mouse_queue[4];
|
|
|
|
pc_timer_t send_delay_timer;
|
|
} epochkbd_t;
|
|
|
|
static uint8_t key_queue[16]; /* buffer in the keyboard */
|
|
static int key_queue_start = 0;
|
|
static int key_queue_end = 0;
|
|
|
|
static void
|
|
kbd_epoch_poll(void *priv)
|
|
{
|
|
epochkbd_t *kbd = (epochkbd_t *) priv;
|
|
|
|
timer_advance_u64(&kbd->send_delay_timer, 1000 * TIMER_USEC);
|
|
|
|
if (kbd->pb & 0x04) /* controller is sending something to keyboard */
|
|
return;
|
|
|
|
if (!(kbd->pb & 0x08)) /* keyboard interrupt is disabled */
|
|
return;
|
|
|
|
if (kbd->want_irq) {
|
|
kbd->want_irq = 0;
|
|
kbd->pa = kbd->key_waiting;
|
|
kbd->blocked = 1;
|
|
picint(EPOCH_IRQ3_BIT);
|
|
epoch_kbdlog("epochkbd: kbd_poll(): keyboard_xt : take IRQ\n");
|
|
}
|
|
|
|
if (!kbd->blocked) {
|
|
if (kbd->mouse_queue_num > 0) {
|
|
kbd->mouse_queue_num--;
|
|
kbd->key_waiting = kbd->mouse_queue[kbd->mouse_queue_num];
|
|
epoch_kbdlog("epochkbd: reading %02X from the mouse queue at %i\n", kbd->key_waiting, kbd->mouse_queue_num);
|
|
kbd->want_irq = 1;
|
|
} else if (key_queue_start != key_queue_end) {
|
|
kbd->key_waiting = key_queue[key_queue_start];
|
|
epoch_kbdlog("epochkbd: reading %02X from the key queue at %i\n",
|
|
kbd->key_waiting, key_queue_start);
|
|
key_queue_start = (key_queue_start + 1) & 0x0f;
|
|
kbd->want_irq = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
kbd_epoch_adddata_process(uint16_t val, void (*adddata)(uint16_t val))
|
|
{
|
|
// uint8_t num_lock = 0;
|
|
// uint8_t shift_states = 0;
|
|
|
|
if (!adddata)
|
|
return;
|
|
|
|
// keyboard_get_states(NULL, &num_lock, NULL, NULL);
|
|
// shift_states = keyboard_get_shift() & STATE_LSHIFT;
|
|
|
|
// /* If NumLock is on, invert the left shift state so we can always check for
|
|
// the the same way flag being set (and with NumLock on that then means it
|
|
// is actually *NOT* set). */
|
|
// if (num_lock)
|
|
// shift_states ^= STATE_LSHIFT;
|
|
|
|
adddata(val);
|
|
}
|
|
|
|
static void
|
|
kbd_epoch_adddata(uint16_t val)
|
|
{
|
|
key_queue[key_queue_end] = val;
|
|
epoch_log("epochkbd: %02X added to key queue at %i\n",
|
|
val, key_queue_end);
|
|
key_queue_end = (key_queue_end + 1) & 0x0f;
|
|
}
|
|
|
|
static void
|
|
kbd_adddata_ex(uint16_t val)
|
|
{
|
|
if (val < 0x100)
|
|
kbd_epoch_adddata_process(val, kbd_epoch_adddata);
|
|
}
|
|
|
|
/*
|
|
I/O 61h W:
|
|
xxxx xxx1: ? (used by kbd interrupt in DOS)
|
|
xxxx xx1x: Beep
|
|
xxxx x1xx: Hold clock line low (used to reset kbd)
|
|
xxxx 1xxx: Enable kbd?
|
|
xxx1 xxxx: Send data from system to kbd
|
|
1xxx xxxx: Clear buffer?
|
|
*/
|
|
static void
|
|
kbd_write(uint16_t port, uint8_t val, void *priv)
|
|
{
|
|
epochkbd_t *kbd = (epochkbd_t *) priv;
|
|
uint8_t new_clock;
|
|
epoch_kbdlog("%04X:%04X epochkbd: Port %02X out: %02X BX: %04x", cs >> 4, cpu_state.pc, port, val, BX);
|
|
epoch_kbdlog(" Clk: %x %x\n", kbd->blocked, kbd->clk_hold);
|
|
|
|
switch (port) {
|
|
case 0x60: /* Diagnostic Output */
|
|
if ((kbd->pb & 0x10) && ((val & 0xf0) == 0xd0)) {
|
|
kbd->mouse_enabled = 1;
|
|
epoch_kbdlog("epochkbd: Mouse is enabled.\n");
|
|
}
|
|
break;
|
|
case 0x61: /* Keyboard Control Register (aka Port B) */
|
|
kbd->pb = val;
|
|
|
|
if ((val & 0x18) == 0x08) {
|
|
new_clock = !(val & 0x04);
|
|
/* Trigger kbd reset after the clk line is reset to a high level */
|
|
if (kbd->clk_hold && new_clock) {
|
|
key_queue_start = key_queue_end = 0;
|
|
kbd->want_irq = 0;
|
|
kbd->blocked = 0;
|
|
kbd->kbd_reset_step = 0;
|
|
kbd->clk_hold = 0;
|
|
kbd->mouse_enabled = 0;
|
|
kbd->mouse_queue_num = 0;
|
|
epoch_kbdlog("epochkbd: Starting keyboard reset sequence.\n");
|
|
}
|
|
} else if ((val & 0x18) == 0x18) {
|
|
new_clock = !(val & 0x04);
|
|
if (kbd->clk_hold && new_clock) {
|
|
kbd->mouse_reset_step = 0;
|
|
epoch_kbdlog("epochkbd: Starting mouse reset sequence.\n");
|
|
}
|
|
}
|
|
|
|
if (kbd->pb & 0x08)
|
|
kbd->clk_hold = !!(kbd->pb & 0x04);
|
|
|
|
timer_process();
|
|
speaker_update();
|
|
// if(!speaker_enable && (val & 2)) epoch_log("Buz!\n");
|
|
speaker_gated = val & 2;
|
|
speaker_enable = val & 2;
|
|
|
|
if (speaker_enable)
|
|
was_speaker_enable = 1;
|
|
pit_devs[0].set_gate(pit_devs[0].data, TIMER_CTR_2, val & 2);
|
|
|
|
if (val & 0x80) {
|
|
/* clear buffer */
|
|
kbd->pa = 0;
|
|
kbd->blocked = 0;
|
|
/* IRQ will be cleared by the software */
|
|
// picintc(EPOCH_IRQ3_BIT);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
I/O 61h R
|
|
xxxx xx1x: Beep input?
|
|
xxxx x1xx: KB -CLK?
|
|
xxxx 1xxx: KB -DATA?
|
|
*/
|
|
static uint8_t
|
|
kbd_read(uint16_t port, void *priv)
|
|
{
|
|
epochkbd_t *kbd = (epochkbd_t *) priv;
|
|
uint8_t ret = 0;
|
|
|
|
switch (port) {
|
|
case 0x60: /* Keyboard Data Register (aka Port A) */
|
|
ret = kbd->pa;
|
|
break;
|
|
|
|
case 0x61: /* Keyboard Control Register (aka Port B) */
|
|
ret = kbd->pb & 0xf0; /* reset sense bit */
|
|
ret |= 0x0c; /* reset sense bit */
|
|
if (kbd->kbd_reset_step < 18) {
|
|
ret &= 0xf3;
|
|
if (!!(kbd->kbd_reset_step & 1))
|
|
ret |= 0x04;
|
|
switch (kbd->kbd_reset_step >> 1) { /* AAh (0 1010 1010) in serial data */
|
|
case 0:
|
|
case 2:
|
|
case 4:
|
|
case 6:
|
|
case 8:
|
|
ret |= 0x08;
|
|
break;
|
|
default:
|
|
ret |= 0x00;
|
|
break;
|
|
}
|
|
epoch_kbdlog(" reset step: %d %x %x", kbd->kbd_reset_step, ret & 0x08, ret & 0x04);
|
|
epoch_kbdlog(" Clk: %x %x\n", kbd->blocked, kbd->clk_hold);
|
|
kbd->kbd_reset_step++;
|
|
/* Specific 5556 keyboards send three bytes of identification code,
|
|
but this simply sends AAh that can pass the IPL and DOS K3.4 init. */
|
|
if (kbd->kbd_reset_step == 18)
|
|
kbd_epoch_adddata(0xaa);
|
|
} else if (kbd->mouse_reset_step < 20) {
|
|
ret &= 0xf3;
|
|
if (!!(kbd->mouse_reset_step & 1))
|
|
ret |= 0x04;
|
|
epoch_kbdlog(" reset step: %d %x %x\n", kbd->mouse_reset_step, ret & 0x08, ret & 0x04);
|
|
kbd->mouse_reset_step++;
|
|
}
|
|
|
|
/* Bit 1: Timer 2 (Speaker) out state */
|
|
if (pit_devs[0].get_outlevel(pit_devs[0].data, TIMER_CTR_2) && speaker_enable)
|
|
ret &= 0xfd; /* 1111 1101 */
|
|
else
|
|
ret |= 0x02;
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
epoch_kbdlog("%04X:%04X epochkbd: Port %02X in : %02X pb: %02x CX: %04x\n", cs >> 4, cpu_state.pc, port, ret, kbd->pb, CX);
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
kbd_reset(void *priv)
|
|
{
|
|
epochkbd_t *kbd = (epochkbd_t *) priv;
|
|
|
|
kbd->want_irq = 0;
|
|
kbd->blocked = 0;
|
|
kbd->pa = 0x00;
|
|
kbd->pb = 0x00;
|
|
kbd->kbd_reset_step = 0xff;
|
|
kbd->mouse_reset_step = 0xff;
|
|
|
|
keyboard_scan = 1;
|
|
|
|
key_queue_start = 0;
|
|
key_queue_end = 0;
|
|
|
|
kbd->mouse_enabled = 0;
|
|
kbd->mouse_queue_num = 0;
|
|
kbd_epoch_adddata(0xaa);
|
|
}
|
|
|
|
static void *
|
|
kbd_init(const device_t *info)
|
|
{
|
|
epochkbd_t *kbd;
|
|
|
|
kbd = (epochkbd_t *) calloc(1, sizeof(epochkbd_t));
|
|
|
|
io_sethandler(0x0060, 4,
|
|
kbd_read, NULL, NULL, kbd_write, NULL, NULL, kbd);
|
|
keyboard_send = kbd_adddata_ex;
|
|
kbd_reset(kbd);
|
|
|
|
timer_add(&kbd->send_delay_timer, kbd_epoch_poll, kbd, 1);
|
|
|
|
keyboard_set_table(scancode_set8a);
|
|
keyboard_mode = 0x8a;
|
|
|
|
return kbd;
|
|
}
|
|
|
|
static void
|
|
kbd_close(void *priv)
|
|
{
|
|
epochkbd_t *kbd = (epochkbd_t *) priv;
|
|
|
|
/* Stop the timer. */
|
|
timer_disable(&kbd->send_delay_timer);
|
|
mouse_close();
|
|
|
|
/* Disable scanning. */
|
|
keyboard_scan = 0;
|
|
keyboard_send = NULL;
|
|
|
|
io_removehandler(0x0060, 2,
|
|
kbd_read, NULL, NULL, kbd_write, NULL, NULL, kbd);
|
|
|
|
free(kbd);
|
|
}
|
|
|
|
static const device_t kbc_epoch_device = {
|
|
.name = "IBM 5550 Keyboard Controller",
|
|
.internal_name = "kbc_epoch",
|
|
.flags = 0,
|
|
.local = 0,
|
|
.init = kbd_init,
|
|
.close = kbd_close,
|
|
.reset = kbd_reset,
|
|
.available = NULL,
|
|
.speed_changed = NULL,
|
|
.force_redraw = NULL,
|
|
.config = NULL
|
|
};
|
|
|
|
/*
|
|
The IBM 5550 DOS K3.44 comes with a mouse driver
|
|
for the M1 and P1 keyboards that have a built-in mouse adapter.
|
|
The mouse adapter for the expansion slot was also available,
|
|
but it may require the bundled driver. (not confirmed)
|
|
|
|
IBM 5550 DOS K3.4 Mouse Driver (Int 0Bh and 33h)
|
|
61h <- 1st kbd indata
|
|
[si+4Ch] status <- 2nd kbd indata
|
|
1xxx xxxx negative dX
|
|
x1xx xxxx bit 9 of dX
|
|
xx1x xxxx negative dY
|
|
xxx1 xxxx bit 9 of dY
|
|
xxxx x1xx cursor moved
|
|
xxxx xx1x right button
|
|
xxxx xxx1 left button
|
|
[si+4Dh] previous button status <- [si+4Ch]
|
|
[si+4Eh] dX <- 3rd kbd indata
|
|
[si+50h] dY <- 4th kbd indata
|
|
*/
|
|
static int
|
|
epoch_mouse_poll(void *priv)
|
|
{
|
|
epochkbd_t *kbd = (epochkbd_t *) priv;
|
|
int dat = mouse_get_buttons_ex();
|
|
int dx, dy;
|
|
|
|
if (!kbd->mouse_enabled)
|
|
return 0;
|
|
|
|
if (kbd->mouse_queue_num > 0)
|
|
return 0;
|
|
|
|
if (kbd->pb & 0x10)
|
|
return 0;
|
|
|
|
dat &= 0x03;
|
|
if (!mouse_moved()) {
|
|
kbd->mouse_queue[1] = 0x61;
|
|
kbd->mouse_queue[0] = dat;
|
|
kbd->mouse_queue_num = 2;
|
|
} else {
|
|
dat |= 0x04;
|
|
mouse_subtract_x(&dx, NULL, -512, 511, 0);
|
|
mouse_clear_x();
|
|
if (dx < 0)
|
|
dat |= 0x20;
|
|
if (dx & 0x100)
|
|
dat |= 0x10;
|
|
mouse_subtract_y(&dy, NULL, -512, 511, 0, 0);
|
|
mouse_clear_y();
|
|
if (dy < 0)
|
|
dat |= 0x80;
|
|
if (dy & 0x100)
|
|
dat |= 0x40;
|
|
epoch_log("Mouse moved %x %x %x\n", dat, dx, dy);
|
|
kbd->mouse_queue[3] = 0x61;
|
|
kbd->mouse_queue[2] = dat;
|
|
kbd->mouse_queue[1] = dx & 0xff;
|
|
kbd->mouse_queue[0] = dy & 0xff;
|
|
kbd->mouse_queue_num = 4;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
epoch_nvr_time_set(uint8_t *regs, struct tm *tm)
|
|
{
|
|
regs[epoch_nvr_SECOND1] = tm->tm_sec % 10;
|
|
regs[epoch_nvr_SECOND10] = (tm->tm_sec / 10);
|
|
regs[epoch_nvr_MINUTE1] = (tm->tm_min % 10);
|
|
regs[epoch_nvr_MINUTE10] = (tm->tm_min / 10);
|
|
regs[epoch_nvr_HOUR1] = (tm->tm_hour % 10);
|
|
regs[epoch_nvr_HOUR10] = (tm->tm_hour / 10);
|
|
regs[epoch_nvr_WEEKDAY] = tm->tm_wday;
|
|
regs[epoch_nvr_DAY1] = (tm->tm_mday % 10);
|
|
regs[epoch_nvr_DAY10] = (tm->tm_mday / 10);
|
|
regs[epoch_nvr_MONTH1] = ((tm->tm_mon + 1) % 10);
|
|
regs[epoch_nvr_MONTH10] = ((tm->tm_mon + 1) / 10);
|
|
regs[epoch_nvr_YEAR1] = (tm->tm_year % 10);
|
|
regs[epoch_nvr_YEAR10] = ((tm->tm_year % 100) / 10);
|
|
}
|
|
|
|
/* Get the chip time. */
|
|
#define nibbles(a) (regs[(a##1)] + 10 * regs[(a##10)])
|
|
static void
|
|
epoch_nvr_time_get(uint8_t *regs, struct tm *tm)
|
|
{
|
|
tm->tm_sec = nibbles(epoch_nvr_SECOND);
|
|
tm->tm_min = nibbles(epoch_nvr_MINUTE);
|
|
tm->tm_hour = nibbles(epoch_nvr_HOUR);
|
|
tm->tm_wday = regs[epoch_nvr_WEEKDAY];
|
|
tm->tm_mday = nibbles(epoch_nvr_DAY);
|
|
tm->tm_mon = (nibbles(epoch_nvr_MONTH) - 1);
|
|
tm->tm_year = (nibbles(epoch_nvr_YEAR));
|
|
}
|
|
|
|
/* This is called every second through the NVR/RTC hook. */
|
|
static void
|
|
epoch_nvr_tick(nvr_t *nvr)
|
|
{
|
|
struct tm tm;
|
|
if (!(nvr->regs[epoch_nvr_CONTROL] & 0x40)) {
|
|
/* Get the current time from the internal clock. */
|
|
nvr_time_get(&tm);
|
|
/* Update registers with current time. */
|
|
epoch_nvr_time_set(nvr->regs, &tm);
|
|
}
|
|
}
|
|
|
|
static void
|
|
epoch_nvr_start(nvr_t *nvr)
|
|
{
|
|
struct tm tm;
|
|
/* Initialize the internal and chip times. */
|
|
if (time_sync & TIME_SYNC_ENABLED) {
|
|
/* Use the internal clock's time. */
|
|
nvr_time_get(&tm);
|
|
epoch_nvr_time_set(nvr->regs, &tm);
|
|
} else {
|
|
/* Set the internal clock from the chip time. */
|
|
epoch_nvr_time_get(nvr->regs, &tm);
|
|
nvr_time_set(&tm);
|
|
}
|
|
}
|
|
|
|
/* Write to one of the chip registers. */
|
|
static void
|
|
epoch_nvr_write(uint16_t port, uint8_t val, void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
int addr = 0;
|
|
|
|
switch (port) {
|
|
case 0x360:
|
|
epoch->nvrctrl = val;
|
|
addr = val & 0xf;
|
|
if (val & 0x20) { /* Write */
|
|
if (addr >= 0x8 && (epoch->nvr.regs[addr] != val))
|
|
nvr_dosave = 1;
|
|
epoch->nvr.regs[addr] = epoch->nvrdata;
|
|
}
|
|
break;
|
|
case 0x361:
|
|
if(epoch->nvrctrl & 0x40) /* Is the access enabled? */
|
|
epoch->nvrdata = val;
|
|
break;
|
|
}
|
|
// epoch_log("%04X:%04X I/O Out %02X: %02X\n", cs >> 4, cpu_state.pc, port, val);
|
|
}
|
|
|
|
/* Read from one of the chip registers. */
|
|
static uint8_t
|
|
epoch_nvr_read(uint16_t port, void *priv)
|
|
{
|
|
const epoch_t *epoch = (epoch_t *) priv;
|
|
|
|
switch (port) {
|
|
case 0x360:
|
|
return epoch->nvrctrl;
|
|
break;
|
|
case 0x361:
|
|
if(epoch->nvrctrl & 0x40) /* Is the access enabled? */
|
|
return (epoch->nvr.regs[(epoch->nvrctrl & 0xf)]);
|
|
break;
|
|
}
|
|
return EPOCH_INVALIDACCESS8;
|
|
}
|
|
|
|
/* Reset the RTC registers to a default state. */
|
|
static void
|
|
epoch_nvr_reset(nvr_t *nvr)
|
|
{
|
|
/* Clear the NVRAM. */
|
|
memset(nvr->regs, 0xff, nvr->size);
|
|
/* Reset the RTC registers. */
|
|
memset(nvr->regs, 0x00, 0xc);
|
|
nvr->regs[epoch_nvr_CONTROL] = 0;
|
|
}
|
|
|
|
static void
|
|
epoch_nvr_init(epoch_t *epoch)
|
|
{
|
|
nvr_t* nvr = &epoch->nvr;
|
|
/* This is machine specific. */
|
|
nvr->size = 17;
|
|
nvr->irq = -1;
|
|
/* Set up any local handlers here. */
|
|
nvr->reset = epoch_nvr_reset;
|
|
nvr->start = epoch_nvr_start;
|
|
nvr->tick = epoch_nvr_tick;
|
|
/* Initialize the actual NVR. */
|
|
nvr_init(nvr);
|
|
io_sethandler(0x0360, 2,
|
|
epoch_nvr_read, NULL, NULL, epoch_nvr_write, NULL, NULL, epoch);
|
|
}
|
|
|
|
static uint8_t ibm5550_attr_mono[16] =
|
|
{
|
|
0,6,6,62,0,0,0,0,0,0,0,0,0,0,0,0
|
|
};
|
|
|
|
// static uint8_t ps55_attr_color[16] = /* for video mode 0eh color character */
|
|
// {
|
|
// 0x00,0x38,0x24,0x3c,0x12,0x3a,0x36,0x3e,0x09,0x39,0x2d,0x3d,0x1b,0x3b,0x3f,0x3f
|
|
// };
|
|
|
|
/* 12-bit DAC color palette for IBMJ Display Adapter with color monitor */
|
|
static uint8_t ps55_palette_color[64][3] = {
|
|
{ 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x2A }, { 0x00, 0x2A, 0x00 }, { 0x00, 0x2A, 0x2A },
|
|
{ 0x2A, 0x00, 0x00 }, { 0x2A, 0x00, 0x2A }, { 0x2A, 0x2A, 0x00 }, { 0x2A, 0x2A, 0x2A },
|
|
{ 0x00, 0x00, 0x15 }, { 0x00, 0x00, 0x3F }, { 0x00, 0x2A, 0x15 }, { 0x00, 0x2A, 0x3F },
|
|
{ 0x2A, 0x00, 0x15 }, { 0x2A, 0x00, 0x3F }, { 0x2A, 0x2A, 0x15 }, { 0x2A, 0x2A, 0x3F },
|
|
{ 0x00, 0x15, 0x00 }, { 0x00, 0x15, 0x2A }, { 0x00, 0x3F, 0x00 }, { 0x00, 0x3F, 0x2A },
|
|
{ 0x2A, 0x15, 0x00 }, { 0x2A, 0x15, 0x2A }, { 0x2A, 0x3F, 0x00 }, { 0x2A, 0x3F, 0x2A },
|
|
{ 0x00, 0x15, 0x15 }, { 0x00, 0x15, 0x3F }, { 0x00, 0x3F, 0x15 }, { 0x00, 0x3F, 0x3F },
|
|
{ 0x2A, 0x15, 0x15 }, { 0x2A, 0x15, 0x3F }, { 0x2A, 0x3F, 0x15 }, { 0x2A, 0x3F, 0x3F },
|
|
{ 0x15, 0x00, 0x00 }, { 0x15, 0x00, 0x2A }, { 0x15, 0x2A, 0x00 }, { 0x15, 0x2A, 0x2A },
|
|
{ 0x3F, 0x00, 0x00 }, { 0x3F, 0x00, 0x2A }, { 0x3F, 0x2A, 0x00 }, { 0x3F, 0x2A, 0x2A },
|
|
{ 0x15, 0x00, 0x15 }, { 0x15, 0x00, 0x3F }, { 0x15, 0x2A, 0x15 }, { 0x15, 0x2A, 0x3F },
|
|
{ 0x3F, 0x00, 0x15 }, { 0x3F, 0x00, 0x3F }, { 0x3F, 0x2A, 0x15 }, { 0x3F, 0x2A, 0x3F },
|
|
{ 0x15, 0x15, 0x00 }, { 0x15, 0x15, 0x2A }, { 0x15, 0x3F, 0x00 }, { 0x15, 0x3F, 0x2A },
|
|
{ 0x3F, 0x15, 0x00 }, { 0x3F, 0x15, 0x2A }, { 0x3F, 0x3F, 0x00 }, { 0x3F, 0x3F, 0x2A },
|
|
{ 0x15, 0x15, 0x15 }, { 0x15, 0x15, 0x3F }, { 0x15, 0x3F, 0x15 }, { 0x15, 0x3F, 0x3F },
|
|
{ 0x3F, 0x15, 0x15 }, { 0x3F, 0x15, 0x3F }, { 0x3F, 0x3F, 0x15 }, { 0x3F, 0x3F, 0x3F }
|
|
};
|
|
|
|
static video_timings_t timing_epoch_vid =
|
|
{ .type = VIDEO_ISA, .write_b = 8, .write_w = 8, .write_l = 16, .read_b = 8, .read_w = 8, .read_l = 16 };
|
|
|
|
static void
|
|
epoch_reset(void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
|
|
epoch->parityerror = 0;
|
|
epoch->parityenabled = 1;
|
|
epoch->lowmemorydisabled = 1;
|
|
epoch->crtioenabled = 0;
|
|
mem_mapping_disable(&epoch->cmap);
|
|
mem_mapping_disable(&epoch->vmap);
|
|
// epoch->attrc[LV_CURSOR_COLOR] = 0x0f; /* cursor color */
|
|
epoch->crtc[LC_HORIZONTAL_TOTAL] = 103; /* Horizontal Total */
|
|
epoch->crtc[LC_VERTICAL_TOTAL] = 26; /* Vertical Total (These two must be set before the timer starts.) */
|
|
epoch->crtmode = 0;
|
|
epoch->vram_display_mask = EPOCH_MASK_CRAM;
|
|
// epoch->plane_mask = 1;
|
|
epoch->oddeven = 0;
|
|
// epoch->memaddr_latch = 0;
|
|
// epoch->attrc[LV_CURSOR_CONTROL] = 0x13; /* cursor options */
|
|
// epoch->attr_palette_enable = 0; /* disable attribute generator */
|
|
|
|
// epoch->attrc[LV_PAS_STATUS_CNTRL] = 0;
|
|
// epoch->attrc[LV_PANNING] = 0;
|
|
|
|
/* Set internal color palette registers */
|
|
for (uint16_t i = 0; i < 16; i++) {
|
|
epoch->egapal[i] = ibm5550_attr_mono[i];
|
|
}
|
|
/* Set color palette for video output */
|
|
for (uint16_t i = 0; i < 64; i++) {
|
|
epoch->vgapal[i].r = ps55_palette_color[i & 0x3F][0];
|
|
epoch->vgapal[i].g = ps55_palette_color[i & 0x3F][1];
|
|
epoch->vgapal[i].b = ps55_palette_color[i & 0x3F][2];
|
|
epoch->pallook[i] = makecol32((epoch->vgapal[i].r & 0x3f) * 4, (epoch->vgapal[i].g & 0x3f) * 4, (epoch->vgapal[i].b & 0x3f) * 4);
|
|
}
|
|
|
|
// mem_mapping_disable(&epoch->fontcard.map);
|
|
|
|
epoch_log("epoch_reset done.\n");
|
|
}
|
|
|
|
/*
|
|
//[Font ROM Map (DA1)]
|
|
//Bank 0
|
|
// 0000-581Fh Pointers (Low) for each character font?
|
|
// 5820-7FFFh Pointers (High) for each character font?
|
|
// 8000- * h Font Data
|
|
*/
|
|
// static void
|
|
// epoch_video_load_font(char *fname, epoch_t *epoch)
|
|
// {
|
|
// uint8_t buf;
|
|
// uint64_t fsize;
|
|
// if (!fname)
|
|
// return;
|
|
// if (*fname == '\0')
|
|
// return;
|
|
// FILE *mfile = rom_fopen(fname, "rb");
|
|
// if (!mfile) {
|
|
// // da2_log("MSG: Can't open binary ROM font file: %s\n", fname);
|
|
// return;
|
|
// }
|
|
// fseek(mfile, 0, SEEK_END);
|
|
// fsize = ftell(mfile); /* get filesize */
|
|
// fseek(mfile, 0, SEEK_SET);
|
|
// if (fsize > EPOCH_FONTROM_SIZE) {
|
|
// fsize = EPOCH_FONTROM_SIZE; /* truncate read data */
|
|
// // da2_log("MSG: The binary ROM font is truncated: %s\n", fname);
|
|
// // fclose(mfile);
|
|
// // return 1;
|
|
// }
|
|
// uint32_t j = 0;
|
|
// while (ftell(mfile) < fsize) {
|
|
// (void) !fread(&buf, sizeof(uint8_t), 1, mfile);
|
|
// epoch->fontcard.rom[j] = buf;
|
|
// j++;
|
|
// }
|
|
// fclose(mfile);
|
|
// return;
|
|
// }
|
|
|
|
// static void
|
|
// epoch_font_writeb(uint32_t addr, uint8_t val, void *priv)
|
|
// {
|
|
// epoch_t *epoch = (epoch_t *) priv;
|
|
// epoch->fontcard.bank = val;
|
|
// // if ((addr & ~0xfff) != 0xE0000) return;
|
|
// epoch_log("cw %04X %02X %04X %04X %04X %04X\n", addr, val, DS, SI, ES, DI);
|
|
// }
|
|
// static uint8_t
|
|
// epoch_font_readb(uint32_t addr, void *priv)
|
|
// {
|
|
// epoch_t *epoch = (epoch_t *) priv;
|
|
// uint32_t readaddr = epoch->fontcard.bank;
|
|
// addr &= EPOCH_FONTROM_MASK;
|
|
// readaddr *= 0xc000;/* xxx x000 0000 0000 0000 (8000h) */
|
|
// readaddr += addr;
|
|
// if (readaddr >= EPOCH_FONTROM_SIZE)
|
|
// return EPOCH_INVALIDACCESS8;
|
|
// // epoch_log("cr %X %x %04X %04X %04X %04X\n", readaddr, epoch->fontcard.rom[readaddr], DS, SI, ES, DI);
|
|
// // if(epoch->vram[addr] == 0xcb)
|
|
// // epoch_log("CB %04X:%04X %04X:%04X>%04X:%04X\n", cs >> 4, cpu_state.pc, DS, SI,ES,DI);
|
|
// return epoch->fontcard.rom[readaddr];
|
|
// }
|
|
static void *
|
|
epoch_init(UNUSED(const device_t *info))
|
|
{
|
|
epoch_t *epoch = calloc(1, sizeof(epoch_t));
|
|
epoch->font24 = device_get_config_int("model");
|
|
|
|
video_inform(VIDEO_FLAG_TYPE_NONE, &timing_epoch_vid);
|
|
video_update_timing();
|
|
|
|
epoch->dispontime = 1000ull << 32;
|
|
epoch->dispofftime = 1000ull << 32;
|
|
// epoch->changedvram = calloc(1, (EPOCH_MASK_VRAMPLANE + 1) >> 9); /* XX000h */
|
|
|
|
if (epoch->font24)
|
|
epoch->pixelclock = EPOCH_PIXELCLOCK24;
|
|
else
|
|
epoch->pixelclock = EPOCH_PIXELCLOCK16;
|
|
|
|
epoch->vram = calloc(1, 256* 1024);
|
|
// for(int i=0;i<256*1024;i++) /* for debug */
|
|
// epoch->vram[i] = 0xff;
|
|
epoch->cram = calloc(1, 4 * 1024);
|
|
// epoch->fontcard.rom = calloc(1, EPOCH_FONTROM_SIZE);
|
|
// epoch_video_load_font("roms/machines/ibm5550/GEN1FONT.BIN", epoch);
|
|
|
|
epoch->epochconst = (uint64_t) ((cpuclock / epoch->pixelclock) * (double) (1ull << 32));
|
|
|
|
mem_mapping_add(&epoch->cmap, 0xE0000, 0x1000, epoch_cram_readb, epoch_cram_readw, NULL,
|
|
epoch_cram_writeb, epoch_cram_writew, NULL, NULL, MEM_MAPPING_EXTERNAL, epoch);
|
|
mem_mapping_add(&epoch->vmap, 0xA0000, 0x40000, NULL, epoch_vram_readw, NULL,
|
|
NULL, epoch_vram_writew, NULL, NULL, MEM_MAPPING_EXTERNAL, epoch);
|
|
// mem_mapping_add(&epoch->fontcard.map, 0xF0000, 0xC000, epoch_font_readb, NULL, NULL,
|
|
// epoch_font_writeb, NULL, NULL, NULL, MEM_MAPPING_EXTERNAL, epoch);
|
|
|
|
// mem_mapping_disable(&epoch->fontcard.map);
|
|
mem_mapping_add(&epoch->paritymap, 0, 0xA0000, epoch_parity_readb, epoch_parity_readw, NULL,
|
|
epoch_parity_writeb, epoch_parity_writew, NULL, NULL, MEM_MAPPING_CACHE, epoch);
|
|
|
|
io_sethandler(0x03d0, 0x0020, epoch_inb, epoch_inw, NULL, epoch_outb, epoch_outw, NULL, epoch);
|
|
|
|
io_sethandler(0x44, 0x0001,
|
|
epoch_misc_in, NULL, NULL, epoch_misc_out, NULL, NULL, epoch);
|
|
io_sethandler(0xA0, 0x0006,
|
|
epoch_misc_in, NULL, NULL, epoch_misc_out, NULL, NULL, epoch);
|
|
io_sethandler(0x310, 0x0008,
|
|
epoch_misc_in, NULL, NULL, epoch_misc_out, NULL, NULL, epoch);
|
|
// io_sethandler(0x160, 0x0010,
|
|
// epoch_misc_in, NULL, NULL, epoch_misc_out, NULL, NULL, epoch);
|
|
|
|
epoch_reset(epoch);
|
|
|
|
timer_add(&epoch->timer, epoch_poll, epoch, 1);
|
|
|
|
epoch_nvr_init(epoch);
|
|
|
|
return epoch;
|
|
}
|
|
|
|
static void
|
|
epoch_close(void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
|
|
/* dump mem for debug */
|
|
#ifdef ENABLE_EPOCH_LOG
|
|
FILE *fp;
|
|
fp = fopen("epoch_cram.dmp", "wb");
|
|
if (fp != NULL) {
|
|
fwrite(epoch->cram, EPOCH_SIZE_CRAM, 1, fp);
|
|
fclose(fp);
|
|
}
|
|
fp = fopen("epoch_vram.dmp", "wb");
|
|
if (fp != NULL) {
|
|
fwrite(epoch->vram, EPOCH_SIZE_VRAM, 1, fp);
|
|
fclose(fp);
|
|
}
|
|
// fp = fopen("epoch_attrpal.dmp", "wb");
|
|
// if (fp != NULL) {
|
|
// fwrite(epoch->attrc, 32, 1, fp);
|
|
// fclose(fp);
|
|
// }
|
|
fp = fopen("epoch_daregs.txt", "w");
|
|
if (fp != NULL) {
|
|
fprintf(fp, "3d8(crtmode) %02X\n", epoch->crtmode);
|
|
// for (uint8_t i = 0; i < 0x10; i++)
|
|
// fprintf(fp, "3e1(ioctl) %02X: %4X %d\n", i, epoch->ioctl[i], epoch->ioctl[i]);
|
|
// for (uint8_t i = 0; i < 0x20; i++)
|
|
// fprintf(fp, "3e3(fctl) %02X: %4X %d\n", i, epoch->fctl[i], epoch->fctl[i]);
|
|
for (uint8_t i = 0; i < 0x20; i++)
|
|
fprintf(fp, "3e5(crtc) %02X: %4X %d\n", i, epoch->crtc[i], epoch->crtc[i]);
|
|
// for (uint8_t i = 0; i < 0x40; i++)
|
|
// fprintf(fp, "3e8(attr) %02X: %4X %d\n", i, epoch->attrc[i], epoch->attrc[i]);
|
|
// for (uint8_t i = 0; i < 0x10; i++)
|
|
// fprintf(fp, "3eb(gcr) %02X: %4X\n", i, epoch->gdcreg[i]);
|
|
// for (uint8_t i = 0; i < 0x20; i++) {
|
|
// fprintf(fp, "vp %02X: %4X %4X %4X %4X\n", i,
|
|
// epoch->crtc_vpreg[0 + i], epoch->crtc_vpreg[0x20 + i], epoch->crtc_vpreg[0x40 + i], epoch->crtc_vpreg[0x60 + i]);
|
|
// }
|
|
fclose(fp);
|
|
}
|
|
fp = fopen("ram_low.dmp", "wb");
|
|
if (fp != NULL) {
|
|
fwrite(ram, 0x40000, 1, fp);
|
|
fclose(fp);
|
|
}
|
|
epoch_log("closed %04X:%04X AX=%04X BX=%04X CX=%04X DX=%04X ES=%04X DI=%04X DS=%04X SI=%04X\n",
|
|
cs >> 4, cpu_state.pc, AX, BX, CX, DX, ES, DI, DS, SI);
|
|
epoch_log("PIC IRR=%02X ISR=%02X IMR=%02X ICW1=%02X ICW2=%02X ICW3=%02X ICW4=%02X OCW2=%02X OCW3=%02X\n",
|
|
pic.irr, pic.isr, pic.imr, pic.icw1, pic.icw2, pic.icw3, pic.icw4, pic.ocw2, pic.ocw3);
|
|
#endif
|
|
free(epoch->cram);
|
|
free(epoch->vram);
|
|
// free(epoch->fontcard.rom);
|
|
// free(epoch->changedvram);
|
|
free(epoch);
|
|
}
|
|
|
|
static void
|
|
epoch_speed_changed(void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
epoch->epochconst = (uint64_t) ((cpuclock / epoch->pixelclock) * (double) (1ull << 32));
|
|
epoch_recalctimings(epoch);
|
|
}
|
|
|
|
static void
|
|
epoch_force_redraw(void *priv)
|
|
{
|
|
epoch_t *epoch = (epoch_t *) priv;
|
|
epoch->fullchange = changeframecount;
|
|
}
|
|
|
|
static const device_config_t epoch_config[] = {
|
|
// clang-format off
|
|
{
|
|
.name = "model",
|
|
.description = "Model",
|
|
.type = CONFIG_SELECTION,
|
|
.default_int = EPOCH_CONFIG_MONO24,
|
|
.selection = {
|
|
{
|
|
.description = "A (Font 16)",
|
|
.value = EPOCH_CONFIG_MONO16
|
|
},
|
|
{
|
|
.description = "B (Font 24)",
|
|
.value = EPOCH_CONFIG_MONO24
|
|
},
|
|
{ .description = "" }
|
|
}
|
|
},
|
|
{ .name = "", .description = "", .type = CONFIG_END }
|
|
// clang-format on
|
|
};
|
|
|
|
const device_t ibm5550_vid_device = {
|
|
.name = "IBM 5550 Video Adapter",
|
|
.internal_name = "ibm5550vid",
|
|
.flags = DEVICE_ISA,
|
|
.local = 0,
|
|
.init = epoch_init,
|
|
.close = epoch_close,
|
|
.reset = epoch_reset,
|
|
.available = NULL,
|
|
.speed_changed = epoch_speed_changed,
|
|
.force_redraw = epoch_force_redraw,
|
|
.config = epoch_config
|
|
};
|
|
|
|
static void
|
|
pit_irq6_timer(int new_out, int old_out, UNUSED(void *priv))
|
|
{
|
|
// epoch_log("%04X:%04X IRQ6 Timer triggered.\n", cs >> 4, cpu_state.pc);
|
|
if (new_out && !old_out)
|
|
picint(EPOCH_IRQ6_BIT);
|
|
|
|
if (!new_out)
|
|
picintc(EPOCH_IRQ6_BIT);
|
|
}
|
|
|
|
static pit_t *
|
|
pit_ibm5550_init(void)
|
|
{
|
|
void *pit;
|
|
|
|
pit_intf_t *pit_intf = &pit_devs[0];
|
|
|
|
pit = device_add(&i8253_device);
|
|
*pit_intf = pit_classic_intf;
|
|
|
|
pit_intf->data = pit;
|
|
|
|
for (uint8_t i = 0; i < 3; i++) {
|
|
pit_intf->set_gate(pit_intf->data, i, 1);
|
|
pit_intf->set_using_timer(pit_intf->data, i, 1);
|
|
}
|
|
|
|
pit_intf->set_out_func(pit_intf->data, TIMER_CTR_1, pit_irq6_timer);
|
|
pit_intf->set_out_func(pit_intf->data, TIMER_CTR_0, pit_refresh_timer_xt);
|
|
pit_intf->set_out_func(pit_intf->data, TIMER_CTR_2, pit_speaker_timer);
|
|
pit_intf->set_load_func(pit_intf->data, TIMER_CTR_2, speaker_set_count);
|
|
|
|
pit_intf->set_gate(pit_intf->data, TIMER_CTR_2, 0);
|
|
|
|
return pit;
|
|
}
|
|
|
|
int
|
|
machine_xt_ibm5550_init(const machine_t *model)
|
|
{
|
|
int ret;
|
|
|
|
ret = bios_load_linear("roms/machines/ibm5550/ipl5550.rom",
|
|
0x000fc000, 16384, 0);
|
|
|
|
if (bios_only || !ret)
|
|
return ret;
|
|
|
|
device_add(&fdc_xt_5550_device);
|
|
|
|
epochkbd_t *kbc = device_add(&kbc_epoch_device);
|
|
|
|
pic_init();
|
|
dma_init();
|
|
pit_ibm5550_init();
|
|
nmi_mask = 0;
|
|
|
|
device_add(&ibm5550_vid_device);
|
|
|
|
device_add(&lpt_port_device);
|
|
serial_t *uart = device_add(&ns8250_device);
|
|
serial_setup(uart, 0x3f8, 1);/* Use IRQ 1 */
|
|
|
|
if (mouse_type == MOUSE_TYPE_INTERNAL) {
|
|
/* Tell mouse driver about our internal mouse. */
|
|
mouse_reset();
|
|
mouse_set_buttons(2);
|
|
mouse_set_poll(epoch_mouse_poll, kbc);
|
|
}
|
|
|
|
return ret;
|
|
}
|