From f05e2f34b615d879e7a60216ab8d24961087a023 Mon Sep 17 00:00:00 2001 From: Akamaki <97360908+akmed772@users.noreply.github.com> Date: Wed, 19 Mar 2025 08:51:52 +0900 Subject: [PATCH 1/3] added JEGA video emulation --- src/include/86box/vid_ega.h | 21 ++ src/video/CMakeLists.txt | 1 + src/video/vid_ega.c | 26 +- src/video/vid_jega.c | 724 ++++++++++++++++++++++++++++++++++++ src/video/vid_table.c | 1 + 5 files changed, 757 insertions(+), 16 deletions(-) create mode 100644 src/video/vid_jega.c diff --git a/src/include/86box/vid_ega.h b/src/include/86box/vid_ega.h index 0bccd607e..082cb120a 100644 --- a/src/include/86box/vid_ega.h +++ b/src/include/86box/vid_ega.h @@ -140,6 +140,11 @@ typedef struct ega_t { uint32_t (*remap_func)(struct ega_t *ega, uint32_t in_addr); void (*render)(struct ega_t *svga); + + /*If set then another device is driving the monitor output and the EGA + card should not attempt to display anything */ + void (*render_override)(void *priv); + void *priv_parent; } ega_t; #endif @@ -150,6 +155,7 @@ extern const device_t sega_device; extern const device_t atiega800p_device; extern const device_t iskra_ega_device; extern const device_t et2000_device; +extern const device_t jega_device; #endif extern int update_overscan; @@ -199,4 +205,19 @@ void ega_render_text(ega_t *ega); void ega_render_graphics(ega_t *ega); #endif +enum { + EGA_IBM = 0, + EGA_COMPAQ, + EGA_SUPEREGA, + EGA_ATI800P, + EGA_ISKRA, + EGA_TSENG +}; + +enum { + EGA_TYPE_IBM = 0, + EGA_TYPE_OTHER = 1, + EGA_TYPE_COMPAQ = 2 +}; + #endif /*VIDEO_EGA_H*/ diff --git a/src/video/CMakeLists.txt b/src/video/CMakeLists.txt index fdd94f6c6..062fc272a 100644 --- a/src/video/CMakeLists.txt +++ b/src/video/CMakeLists.txt @@ -78,6 +78,7 @@ add_library(vid OBJECT vid_xga.c vid_bochs_vbe.c vid_ps55da2.c + vid_jega.c nv/nv_rivatimer.c ) diff --git a/src/video/vid_ega.c b/src/video/vid_ega.c index 10df85c79..781e9a9dd 100644 --- a/src/video/vid_ega.c +++ b/src/video/vid_ega.c @@ -45,21 +45,6 @@ void ega_doblit(int wx, int wy, ega_t *ega); #define BIOS_ISKRA_PATH "roms/video/ega/143-02.bin", "roms/video/ega/143-03.bin" #define BIOS_TSENG_PATH "roms/video/ega/EGA ET2000.BIN" -enum { - EGA_IBM = 0, - EGA_COMPAQ, - EGA_SUPEREGA, - EGA_ATI800P, - EGA_ISKRA, - EGA_TSENG -}; - -enum { - EGA_TYPE_IBM = 0, - EGA_TYPE_OTHER = 1, - EGA_TYPE_COMPAQ = 2 -}; - static video_timings_t timing_ega = { .type = VIDEO_ISA, .write_b = 8, .write_w = 16, .write_l = 32, .read_b = 8, .read_w = 16, .read_l = 32 }; static uint8_t ega_rotate[8][256]; static int active = 0; @@ -107,6 +92,8 @@ ega_out(uint16_t addr, uint8_t val, void *priv) case 0x3c0: case 0x3c1: + if (ega->actual_type == EGA_SUPEREGA) + val &= 0x7f; /* Bit 7 indicates the flipflop status (read only) */ if (!ega->attrff) { ega->attraddr = val & 31; if ((val & 0x20) != ega->attr_palette_enable) { @@ -325,6 +312,8 @@ ega_in(uint16_t addr, void *priv) case 0x3c0: if (ega_type == EGA_TYPE_OTHER) ret = ega->attraddr | ega->attr_palette_enable; + if (ega->actual_type == EGA_SUPEREGA && ega->attrff) + ret |= 0x80; /* Bit 7 indicates the flipflop status (read only) */ break; case 0x3c1: if (ega_type == EGA_TYPE_OTHER) @@ -754,7 +743,10 @@ ega_poll(void *priv) ega->y_add *= ega->vres + 1; for (y = 0; y <= ega->vres; y++) { /* Render scanline */ - ega->render(ega); + if(ega->render_override) + ega->render_override(ega->priv_parent); + else + ega->render(ega); /* Render overscan */ ega->x_add = (overscan_x >> 1); @@ -1429,6 +1421,8 @@ ega_init(ega_t *ega, int monitor_type, int is_mono) ega->crtc[0] = 63; ega->crtc[6] = 255; + ega->render_override = NULL; + timer_add(&ega->timer, ega_poll, ega, 1); if (ega_type == EGA_TYPE_COMPAQ) timer_add(&ega->dot_timer, ega_dot_poll, ega, 1); diff --git a/src/video/vid_jega.c b/src/video/vid_jega.c new file mode 100644 index 000000000..ad312faff --- /dev/null +++ b/src/video/vid_jega.c @@ -0,0 +1,724 @@ +/* + * 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 JEGA (Japanese EGA), a part of the AX architecture. + * + * It's an extension of the SuperEGA. Superimposing text (AX-2) is not available. + * + * Authors: Akamaki + * + * Copyright 2025 Akamaki + */ +#include +#include +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include <86box/86box.h> +#include "cpu.h" +#include <86box/io.h> +#include <86box/timer.h> +#include <86box/pic.h> +#include <86box/pit.h> +#include <86box/mem.h> +#include <86box/rom.h> +#include <86box/device.h> +#include <86box/video.h> +#include <86box/vid_ega.h> + +/* JEGA internal registers */ +#define RPESL 0x09 /* End Scan Line */ +#define RCCSL 0x0A /* Cursor Start Line */ +#define RCCEL 0x0B /* Cursor End Line */ +#define RCCLH 0x0E /* Cursor Location High */ +#define RCCLL 0x0F /* Cursor Location Low */ +#define RPULP 0x14 /* Under Line Position */ +/* + 0xB8 - 0x20 */ +#define RMOD1 0x21 /* Display out, EGA through, Superimpose, Sync with EGA, Master EGA, Slave EGA, n/a, Test */ +#define RMOD2 0x22 /* 1st Attr, 2nd Attr, Blink/Int, n/a, Font access mode (b3-2), Font map sel (b1-0)*/ +#define RDAGS 0x23 /* ANK Group Select */ +#define RDFFB 0x24 /* Font Access First Byte */ +#define RDFSB 0x25 /* Font Access Second Byte */ +#define RDFAP 0x26 /* Font Access Pattern */ +#define RSTAT 0x27 /* Font Status Register */ +/* + 0xD0 - 0x20 */ +#define RPSSU 0x29 /* Start Scan Upper */ +#define RPSSL 0x2A /* Start Scan Lower */ +#define RPSSC 0x2B /* Start Scan Count */ +#define RPPAJ 0x2C /* Phase Adjust Count */ +#define RCMOD 0x2D /* Cursor Mode */ +#define RCSKW 0x2E /* Cursor Skew Control */ +#define ROMSL 0x2F /* ? */ +#define RINVALID_INDEX 0x30 + +#define JEGA_PATH_BIOS "roms/video/jega/OKI_IF386AX_VBIOS.bin" +#define JEGA_PATH_FONTDBCS "roms/video/jega/JPNZN16X.FNT" +#define SBCS19_FILESIZE (256 * 19 * 2) /* 8 x 19 x 256 chr x 2 pages */ +#define DBCS16_CHARS 0x2c10 +#define DBCS16_FILESIZE (DBCS16_CHARS * 16 * 2) + +#define INVALIDACCESS8 0xffu +#define INVALIDACCESS16 0xffffu +#define INVALIDACCESS32 0xffffffffu + +#ifndef RELEASE_BUILD +// # define ENABLE_JEGA_LOG 1 +#endif + +#ifdef ENABLE_JEGA_LOG +int jega_do_log = ENABLE_JEGA_LOG; + +static void +jega_log(const char *fmt, ...) +{ + va_list ap; + + if (jega_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define jega_log(fmt, ...) +#endif + +static video_timings_t timing_ega = { .type = VIDEO_ISA, .write_b = 8, .write_w = 16, .write_l = 32, .read_b = 8, .read_w = 16, .read_l = 32 }; + +typedef struct jega_t { + rom_t bios_rom; + ega_t ega; + uint8_t regs_index; /* 3D4/3D5 index B9-BF, D9-DF */ + uint8_t regs[0x31]; + uint8_t egapal[16]; + uint8_t attrregs[32]; + uint8_t attraddr; + uint8_t attrff; + uint8_t attr_palette_enable; + uint32_t *pallook; + int con; + int cursoron; + int cursorblink_disable; + int ca; + int font_index; + int sbcsbank_inv; + int attr3_sbcsbank; + int start_scan_lower; + int start_scan_upper; + int start_scan_count; + uint8_t *vram; + uint8_t jfont_sbcs_19[SBCS19_FILESIZE]; /* 8 x 19 font */ + uint8_t jfont_dbcs_16[DBCS16_FILESIZE]; /* 16 x 16 font. Use dbcs_read/write to access it. */ +} jega_t; + +static void jega_recalctimings(void *priv); + +#define FONTX_LEN_ID 6 +#define FONTX_LEN_FN 8 + +typedef struct { + char id[FONTX_LEN_ID]; + char name[FONTX_LEN_FN]; + unsigned char width; + unsigned char height; + unsigned char type; +} fontx_h; + +typedef struct { + uint16_t start; + uint16_t end; +} fontx_tbl; + +static uint32_t pallook64[256]; +static bool is_SJIS_1(uint8_t chr) { return (chr >= 0x81 && chr <= 0x9f) || (chr >= 0xe0 && chr <= 0xfc); } +static bool is_SJIS_2(uint8_t chr) { return (chr >= 0x40 && chr <= 0x7e) || (chr >= 0x80 && chr <= 0xfc); } + +static uint16_t +SJIS_to_SEQ(uint16_t sjis) +{ + uint32_t chr1 = (sjis >> 8) & 0xff; + uint32_t chr2 = sjis & 0xff; + if (!is_SJIS_1(chr1) || !is_SJIS_2(chr2)) return INVALIDACCESS16; + chr1 -= 0x81; + if (chr1 > 0x5E) chr1 -= 0x40; + chr2 -= 0x40; + if (chr2 > 0x3F) chr2--; + chr1 *= 0xBC; + return (chr1 + chr2); +} + +static uint8_t +dbcs_read(uint16_t sjis, int index, void *priv) { + jega_t *jega = (jega_t *) priv; + int seq = SJIS_to_SEQ(sjis); + if (seq >= DBCS16_CHARS || index >= 32) + return INVALIDACCESS8; + return jega->jfont_dbcs_16[seq * 32 + index]; +} + +static void +dbcs_write(uint16_t sjis, int index, uint8_t val, void *priv) { + jega_t *jega = (jega_t *) priv; + int seq = SJIS_to_SEQ(sjis); + if (seq >= DBCS16_CHARS || index >= 32) + return; + jega->jfont_dbcs_16[seq * 32 + index] = val; +} + +/* Display Adapter Mode 3 Drawing */ +void +jega_render_text(void *priv) +{ + jega_t *jega = (jega_t *) priv; + if (jega->ega.firstline_draw == 2000) + jega->ega.firstline_draw = jega->ega.displine; + jega->ega.lastline_draw = jega->ega.displine; + + if (jega->ega.fullchange) { + // const bool doublewidth = ((jega->ega.seqregs[1] & 8) != 0); + const bool attrblink = ((jega->regs[RMOD2] & 0x20) == 0); /* JEGA specific */ + // const bool attrlinechars = (jega->ega.attrregs[0x10] & 4); + const bool crtcreset = ((jega->ega.crtc[0x17] & 0x80) == 0) || (jega->regs[RMOD1] & 0x80 == 0); + const int charwidth = 8; + const bool blinked = jega->ega.blink & 0x10; + uint32_t *p = &buffer32->line[jega->ega.displine + jega->ega.y_add][jega->ega.x_add]; + bool chr_wide = false; + int sc_wide = jega->ega.sc - jega->start_scan_count; + const bool cursoron = (blinked || jega->cursorblink_disable) + && (jega->ega.sc >= jega->regs[RCCSL]) && (jega->ega.sc <= jega->regs[RCCEL]); + uint32_t chr_first; + uint32_t attr_basic; + int fg; + int bg; + + for (int x = 0; x < (jega->ega.hdisp + jega->ega.scrollcache); x += charwidth) { + uint32_t addr = jega->ega.remap_func(&jega->ega, jega->ega.ma) & jega->ega.vrammask; + + int drawcursor = ((jega->ega.ma == jega->ca) && cursoron); + + uint32_t chr; + uint32_t attr; + if (!crtcreset) { + chr = jega->ega.vram[addr]; + attr = jega->ega.vram[addr + 1]; + } else + chr = attr = 0; + if (chr_wide) { + uint8_t attr_ext = 0; + /* the code may be in DBCS */ + if (jega->regs[RMOD2] & 0x40) { + /* Parse JEGA extended attribute */ + /* Bold | 2x width | 2x height | U/L select | R/L select | - | - | - */ + attr_ext = attr; + if ((attr_ext & 0x30) == 0x30) + sc_wide = jega->ega.sc - jega->start_scan_lower; /* Set top padding of lower 2x character */ + else if ((attr_ext & 0x30) == 0x20) + sc_wide = jega->ega.sc - jega->start_scan_upper; /* Set top padding of upper 2x character */ + else + sc_wide = jega->ega.sc - jega->start_scan_count; + } + if (is_SJIS_2(chr) && sc_wide >= 0 && sc_wide < 16 && jega->ega.sc <= jega->regs[RPESL]) { + chr_first <<= 8; + chr |= chr_first; + /* Vertical wide font (Extended Attribute) */ + if (attr_ext & 0x20) { + if (attr_ext & 0x10) + sc_wide = (sc_wide >> 1) + 8; + else + sc_wide = sc_wide >> 1; + } + /* Horizontal wide font (Extended Attribute) */ + if (attr_ext & 0x40) { + uint32_t dat = dbcs_read(chr, sc_wide, jega); + if (!(attr_ext & 0x08)) { /* right half of character */ + dat = dbcs_read(chr, sc_wide + 16, jega); + } + for (int xx = 0; xx < charwidth; xx++) { + p[xx * 2] = (dat & (0x80 >> xx)) ? fg : bg; + p[xx * 2 + 1] = (dat & (0x80 >> xx)) ? fg : bg; + } + } else { + uint32_t dat = dbcs_read(chr, sc_wide, jega); + dat <<= 8; + dat |= dbcs_read(chr, sc_wide + 16, jega); + /* Bold (Extended Attribute) */ + if (attr_ext &= 0x80) { + uint32_t dat2 = dat; + dat2 >>= 1; + dat |= dat2; + } + for (int xx = 0; xx < charwidth * 2; xx++) + p[xx] = (dat & (0x8000 >> xx)) ? fg : bg; + } + } else { + /* invalid DBCS code or line space then put blank */ + for (int xx = 0; xx < charwidth * 2; xx++) + p[xx] = bg; + } + if (attr_basic & 0x20) { /* vertical line */ + p[0] = fg; + } + if ((jega->ega.sc == jega->regs[RPULP]) && (attr_basic & 0x10)) { /* underline */ + for (int xx = 0; xx < charwidth * 2; xx++) + p[xx] = fg; + } + chr_wide = false; + p += (charwidth * 2); + } else { + /* SBCS or invalid second byte of DBCS */ + if (jega->regs[RMOD2] & 0x80) { + /* Parse attribute as JEGA */ + /* Blink | Reverse | V line | U line | (Bit 3-0 is the same as EGA) */ + /* The background color is always black (transparent in AX-2) */ + if (drawcursor || attr & 0x40) { + bg = jega->pallook[jega->egapal[attr & 0x0f]]; + fg = 0; + } else { + fg = jega->pallook[jega->egapal[attr & 0x0f]]; + bg = 0; + if (attr & 0x80) { + bg = 0; + if (blinked) + fg = bg; + } + } + attr_basic = attr; + } else { + /* Parse attribute as EGA */ + /* BInt/Blink | BR | BG | BB | Int/Group | R | G | B */ + if (drawcursor) { + bg = jega->pallook[jega->egapal[attr & 0x0f]]; + fg = jega->pallook[jega->egapal[attr >> 4]]; + } else { + fg = jega->pallook[jega->egapal[attr & 0x0f]]; + bg = jega->pallook[jega->egapal[attr >> 4]]; + if ((attr & 0x80) && attrblink) { + bg = jega->pallook[jega->egapal[(attr >> 4) & 7]]; + if (blinked) + fg = bg; + } + } + attr_basic = 0; + } + + if (is_SJIS_1(chr)) { + /* the char code maybe in DBCS */ + chr_first = chr; + chr_wide = true; + } else { + /* the char code is in SBCS */ + uint32_t charaddr = chr; + // if (jega->attr3_sbcsbank && (attr & 8)) + // charaddr |= 0x100; + // if (jega->sbcsbank_inv) + // charaddr ^= 0x100; + charaddr *= 19; + + uint32_t dat = jega->jfont_sbcs_19[charaddr + jega->ega.sc]; + for (int xx = 0; xx < charwidth; xx++) + p[xx] = (dat & (0x80 >> xx)) ? fg : bg; + + if (attr_basic & 0x20) { /* vertical line */ + p[0] = fg; + } + if ((jega->ega.sc == jega->regs[RPULP]) && (attr_basic & 0x10)) { /* underline */ + for (int xx = 0; xx < charwidth; xx++) + p[xx] = fg; + } + p += charwidth; + } + } + jega->ega.ma += 4; + } + jega->ega.ma &= 0x3ffff; + } +} + +static void +jega_out(uint16_t addr, uint8_t val, void *priv) +{ + uint8_t o; + jega_t *jega = (jega_t *) priv; + // jega_log("JEGA Out %04X %02X(%d) %04X:%04X\n", addr, val, val, cs >> 4, cpu_state.pc); + switch (addr) { + case 0x3c0: + case 0x3c1: + jega_log("Palette %02X %02X(%d) %04X:%04X\n", jega->attraddr, val, val, cs >> 4, cpu_state.pc); + /* Palette (write only) */ + if (!jega->attrff) { + jega->attraddr = val & 31; + if ((val & 0x20) != jega->attr_palette_enable) { + jega->ega.fullchange = 3; + jega->attr_palette_enable = val & 0x20; + jega_recalctimings(jega); + } + } else { + o = jega->attrregs[jega->attraddr & 31]; + jega->attrregs[jega->attraddr & 31] = val; + if (jega->attraddr < 0x10) { + for (uint8_t c = 0; c < 16; c++) { + jega->egapal[c] = jega->attrregs[c] & 0x3f; + } + jega->ega.fullchange = changeframecount; + } + } + jega->attrff ^= 1; + break; + case 0x3b4: + case 0x3d4: + /* Index 0x00-0x1F (write only), 0xB8-0xDF (write and read) */ + if (val >= 0xB8 && val <= 0xBF) + jega->regs_index = val - 0xB8 + 0x20; + else if (val >= 0xD8 && val <= 0xDF) + jega->regs_index = val - 0xD0 + 0x20; + else if (val <= 0x1F) + jega->regs_index = val; + else + jega->regs_index = RINVALID_INDEX; + break; + case 0x3b5: + case 0x3d5: + /* Data */ + if (jega->regs_index != RINVALID_INDEX) { + jega->regs[jega->regs_index] = val; + jega_log("JEGA Out %04X(%02X) %02Xh(%d) %04X:%04X\n", addr, jega->regs_index, val, val, cs >> 4, cpu_state.pc); + switch (jega->regs_index) { + case RMOD1: + /* if the value is changed */ + // if (jega->regs[jega->regs_index] != val) { + if (val & 0x40) + jega->ega.render_override = NULL; + else + jega->ega.render_override = jega_render_text; + // } + break; + case RDAGS: + switch (val & 0x03) { + case 0x00: + jega->attr3_sbcsbank = false; + jega->sbcsbank_inv = false; + break; + case 0x01: + jega->attr3_sbcsbank = true; + jega->sbcsbank_inv = false; + break; + case 0x02: + jega->attr3_sbcsbank = true; + jega->sbcsbank_inv = true; + break; + case 0x03: + jega->attr3_sbcsbank = false; + jega->sbcsbank_inv = true; + break; + } + break; + case RCCLH: + case RCCLL: + jega->ca = jega->regs[RCCLH] << 10 | jega->regs[RCCLL] << 2; + break; + case RCMOD: + jega->cursoron = (val & 0x80); + jega->cursorblink_disable = (~val & 0x20); + break; + case RDFFB: + case RDFSB: + /* reset the line number */ + jega->font_index = 0; + break; + case RPSSC: + if (val <= 17) + jega->start_scan_count = val + 1; + else + jega->start_scan_count = (val - 32) + 1; + break; + case RPSSL: + jega->start_scan_lower = val - 15; + break; + case RPSSU: + if (val <= 33) + jega->start_scan_upper = val + 4; + else + jega->start_scan_upper = (val - 64) + 4; + break; + case RDFAP: + uint16_t chr = jega->regs[RDFFB]; + if (is_SJIS_1(chr) && chr >= 0xf0 && chr <= 0xf3) { + chr <<= 8; + chr |= jega->regs[RDFSB]; + if (jega->font_index < 32) + dbcs_write(chr, jega->font_index, val, jega); + } else { + if (jega->font_index <19) + jega->jfont_sbcs_19[chr * 19 + jega->font_index] = val; + } + jega_log("JEGA Font W %X %d %02Xh(%d) %04X:%04X\n", chr, jega->font_index, val, val, cs >> 4, cpu_state.pc); + jega->font_index++; + break; + } + } + break; + default: + break; + } + if (jega->regs[RMOD1] & 0x0C) /* Accessing to Slave EGA is redirected to Master in AX-1. */ + ega_out(addr, val, &jega->ega); +} + +static uint8_t +jega_in(uint16_t addr, void *priv) +{ + jega_t *jega = (jega_t *) priv; + uint8_t ret = INVALIDACCESS8; + switch (addr) { + case 0x3b5: + case 0x3d5: + if (jega->regs_index >= 0x20 && jega->regs_index <= 0x2F) { + switch (jega->regs_index) { + case RDFAP: + uint16_t chr = jega->regs[RDFFB]; + /* DBCS or SBCS */ + if (is_SJIS_1(chr)) { + chr <<= 8; + chr |= jega->regs[RDFSB]; + if (jega->font_index < 32) + ret = dbcs_read(chr, jega->font_index, jega); + } else { + if (jega->font_index < 19) + ret = jega->jfont_sbcs_19[chr * 19 + jega->font_index]; + } + jega_log("JEGA Font R %X %d %02Xh(%d) %04X:%04X\n", chr, jega->font_index, ret, ret, cs >> 4, cpu_state.pc); + jega->font_index++; + break; + case RSTAT: + ret = 0x03; + break; + default: + ret = jega->regs[jega->regs_index]; + break; + } + jega_log("JEGA In %04X(%02X) %02X %04X:%04X\n", addr, jega->regs_index, ret, cs >> 4, cpu_state.pc); + } else if (jega->regs[RMOD1] & 0x0C) /* Accessing to Slave EGA is redirected to Master in AX-1. */ + ret = ega_in(addr, &jega->ega); + break; + case 0x3ba: + case 0x3da: + jega->attrff = 0; + default: + if (jega->regs[RMOD1] & 0x0C) /* Accessing to Slave is redirected to Master in AX-1. */ + ret = ega_in(addr, &jega->ega); + break; + } + // jega_log("JEGA In %04X(%02X) %02X %04X:%04X\n", addr, jega->regs_index, ret, cs >> 4, cpu_state.pc); + return ret; +} + +static int +getfontx2header(FILE *fp, fontx_h *header) +{ + fread(header->id, FONTX_LEN_ID, 1, fp); + if (strncmp(header->id, "FONTX2", FONTX_LEN_ID) != 0) { + return 1; + } + fread(header->name, FONTX_LEN_FN, 1, fp); + header->width = (uint8_t) getc(fp); + header->height = (uint8_t) getc(fp); + header->type = (uint8_t) getc(fp); + return 0; +} + +static uint16_t +chrtosht(FILE *fp) +{ + uint16_t i, j; + i = (uint16_t) getc(fp); + j = (uint16_t) getc(fp) << 8; + return (i | j); +} + +static void +readfontxtbl(fontx_tbl *table, int size, FILE *fp) +{ + while (size > 0) { + table->start = chrtosht(fp); + table->end = chrtosht(fp); + ++table; + --size; + } +} + +static int +LoadFontxFile(const char *fn, void *priv) +{ + fontx_h fhead; + fontx_tbl *ftbl; + uint16_t code; + uint16_t scode; + uint8_t size; + uint8_t buf; + int line; + jega_t *jega = (jega_t *) priv; + FILE *fp = rom_fopen(fn, "rb"); + jega_log("JEGA: Loading font\n"); + if (fp == NULL) { + jega_log("JEGA: font file '%s' not found.\n", fn); + return 0; + } + if (getfontx2header(fp, &fhead) != 0) { + fclose(fp); + jega_log("JEGA: FONTX2 header is incorrect.\n"); + return 1; + } + /* DBCS or SBCS */ + if (fhead.type == 1) { + if (fhead.width == 16 && fhead.height == 16) { + size = getc(fp); + ftbl = (fontx_tbl *) calloc(size, sizeof(fontx_tbl)); + readfontxtbl(ftbl, size, fp); + for (int i = 0; i < size; i++) { + for (code = ftbl[i].start; code <= ftbl[i].end; code++) { + scode = SJIS_to_SEQ(code); + if (scode != INVALIDACCESS16) { + for (line = 0; line < 16; line++) { + fread(&buf, sizeof(uint8_t), 1, fp); + jega->jfont_dbcs_16[(int) (scode * 32) + line] = buf; + fread(&buf, sizeof(uint8_t), 1, fp); + jega->jfont_dbcs_16[(int) (scode * 32) + line + 16] = buf; + } + } else { + fseek(fp, 32, SEEK_CUR); + } + } + } + } else { + fclose(fp); + jega_log("JEGA: Width or height of DBCS font doesn't match.\n"); + return 1; + } + } else { + if (fhead.width == 8 && fhead.height == 19) { + fread(jega->jfont_sbcs_19, sizeof(uint8_t), SBCS19_FILESIZE, fp); + } else { + fclose(fp); + jega_log("JEGA: Width or height of SBCS font doesn't match.\n"); + return 1; + } + } + fclose(fp); + return 0; +} + +static void * +jega_init(const device_t *info) +{ + jega_t *jega = calloc(1, sizeof(jega_t)); + + rom_init(&jega->bios_rom, JEGA_PATH_BIOS, 0xc0000, 0x8000, 0x7fff, 0, 0); + memset(&jega->jfont_dbcs_16, 0, DBCS16_FILESIZE); + LoadFontxFile(JEGA_PATH_FONTDBCS, jega); + + for (int c = 0; c < 256; c++) { + pallook64[c] = makecol32(((c >> 2) & 1) * 0xaa, ((c >> 1) & 1) * 0xaa, (c & 1) * 0xaa); + pallook64[c] += makecol32(((c >> 5) & 1) * 0x55, ((c >> 4) & 1) * 0x55, ((c >> 3) & 1) * 0x55); + } + + video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_ega); + jega->pallook = pallook64; + ega_init(&jega->ega, 9, 0); + jega->ega.actual_type = EGA_SUPEREGA; + jega->ega.priv_parent = jega; + mem_mapping_add(&jega->ega.mapping, 0xa0000, 0x20000, ega_read, NULL, NULL, ega_write, NULL, NULL, NULL, MEM_MAPPING_EXTERNAL, &jega->ega); + + io_sethandler(0x03b0, 0x0030, jega_in, NULL, NULL, jega_out, NULL, NULL, jega); + + jega->regs[RMOD1] = 0x48; +} + +static void +jega_close(void *priv) +{ + jega_t *jega = (jega_t *) priv; +#ifdef ENABLE_JEGA_LOG + FILE *f; + // f = fopen("jega_font16.dmp", "wb"); + // if (f != NULL) { + // fwrite(jega->jfont_dbcs_16, DBCS16_FILESIZE, 1, f); + // fclose(f); + // } + // f = fopen("jega_font19.dmp", "wb"); + // if (f != NULL) { + // fwrite(jega->jfont_sbcs_19, SBCS19_FILESIZE, 1, f); + // fclose(f); + // } + f = fopen("jega_regs.txt", "wb"); + if (f != NULL) { + for (int i = 0; i < 49; i++) + fprintf(f, "Regs %02X: %4X\n", i, jega->regs[i]); + for (int i = 0; i < 32; i++) + fprintf(f, "Attr %02X: %4X\n", i, jega->attrregs[i]); + for (int i = 0; i < 16; i++) + fprintf(f, "JEGAPal %02X: %4X\n", i, jega->egapal[i]); + for (int i = 0; i < 16; i++) + fprintf(f, "EGAPal %02X: %4X\n", i, jega->ega.egapal[i]); + for (int i = 0; i < 64; i++) + fprintf(f, "RealPal %02X: %4X\n", i, jega->pallook[i]); + fclose(f); + } + // f = fopen("ega_vram.dmp", "wb"); + // if (f != NULL) { + // fwrite(jega->ega.vram, 256 * 1024, 1, f); + // fclose(f); + // } + f = fopen("ram_bda.dmp", "wb"); + if (f != NULL) { + fwrite(&ram[0x0], 0x500, 1, f); + fclose(f); + } + // jega_log("jeclosed %04X:%04X DS %04X\n", cs >> 4, cpu_state.pc, DS); +#endif + if (jega->ega.eeprom) + free(jega->ega.eeprom); + free(jega->ega.vram); + free(jega); +} + +static void +jega_recalctimings(void *priv) +{ + jega_t *jega = (jega_t *) priv; + ega_recalctimings(&jega->ega); +} +static void +jega_speed_changed(void *priv) +{ + jega_t *jega = (jega_t *) priv; + + jega_recalctimings(jega); +} + +static int +jega_available(void) +{ + return (rom_present(JEGA_PATH_BIOS) && rom_present(JEGA_PATH_FONTDBCS)); +} + +const device_t jega_device = { + .name = "JEGA", + .internal_name = "jega", + .flags = DEVICE_ISA, + .local = 0, + .init = jega_init, + .close = jega_close, + .reset = NULL, + .available = jega_available, + .speed_changed = jega_speed_changed, + .force_redraw = NULL, + .config = NULL +}; diff --git a/src/video/vid_table.c b/src/video/vid_table.c index d1df2ba20..be26d9e18 100644 --- a/src/video/vid_table.c +++ b/src/video/vid_table.c @@ -68,6 +68,7 @@ video_cards[] = { { .device = &ati18800_wonder_device, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &cga_device, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &sega_device, .flags = VIDEO_FLAG_TYPE_NONE }, + { .device = &jega_device, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &gd5401_isa_device, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &gd5402_isa_device, .flags = VIDEO_FLAG_TYPE_NONE }, { .device = &gd5420_isa_device, .flags = VIDEO_FLAG_TYPE_NONE }, From 4d3e2a3be35606bce7d70275053ef18730c10e42 Mon Sep 17 00:00:00 2001 From: Akamaki <97360908+akmed772@users.noreply.github.com> Date: Wed, 19 Mar 2025 10:38:17 +0900 Subject: [PATCH 2/3] changed rom filename --- src/video/vid_jega.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/vid_jega.c b/src/video/vid_jega.c index ad312faff..b6695893c 100644 --- a/src/video/vid_jega.c +++ b/src/video/vid_jega.c @@ -59,7 +59,7 @@ #define ROMSL 0x2F /* ? */ #define RINVALID_INDEX 0x30 -#define JEGA_PATH_BIOS "roms/video/jega/OKI_IF386AX_VBIOS.bin" +#define JEGA_PATH_BIOS "roms/video/jega/JEGABIOS.BIN" #define JEGA_PATH_FONTDBCS "roms/video/jega/JPNZN16X.FNT" #define SBCS19_FILESIZE (256 * 19 * 2) /* 8 x 19 x 256 chr x 2 pages */ #define DBCS16_CHARS 0x2c10 From 21bafa37a5aae28aeb63f0e2f2ad4c5a0720d2b1 Mon Sep 17 00:00:00 2001 From: Akamaki <97360908+akmed772@users.noreply.github.com> Date: Thu, 20 Mar 2025 00:44:03 +0900 Subject: [PATCH 3/3] correct a compile error --- src/video/vid_jega.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/video/vid_jega.c b/src/video/vid_jega.c index b6695893c..3ff13e85d 100644 --- a/src/video/vid_jega.c +++ b/src/video/vid_jega.c @@ -347,6 +347,7 @@ jega_out(uint16_t addr, uint8_t val, void *priv) { uint8_t o; jega_t *jega = (jega_t *) priv; + uint16_t chr; // jega_log("JEGA Out %04X %02X(%d) %04X:%04X\n", addr, val, val, cs >> 4, cpu_state.pc); switch (addr) { case 0x3c0: @@ -449,7 +450,7 @@ jega_out(uint16_t addr, uint8_t val, void *priv) jega->start_scan_upper = (val - 64) + 4; break; case RDFAP: - uint16_t chr = jega->regs[RDFFB]; + chr = jega->regs[RDFFB]; if (is_SJIS_1(chr) && chr >= 0xf0 && chr <= 0xf3) { chr <<= 8; chr |= jega->regs[RDFSB]; @@ -477,13 +478,14 @@ jega_in(uint16_t addr, void *priv) { jega_t *jega = (jega_t *) priv; uint8_t ret = INVALIDACCESS8; + uint16_t chr; switch (addr) { case 0x3b5: case 0x3d5: if (jega->regs_index >= 0x20 && jega->regs_index <= 0x2F) { switch (jega->regs_index) { case RDFAP: - uint16_t chr = jega->regs[RDFFB]; + chr = jega->regs[RDFFB]; /* DBCS or SBCS */ if (is_SJIS_1(chr)) { chr <<= 8;