Merge branch 'master' of github.com:OBattler/86Box

This commit is contained in:
TC1995
2018-03-16 19:04:54 +01:00
67 changed files with 6522 additions and 4257 deletions

View File

@@ -45,7 +45,7 @@ guide:
7. In order to test your fresh build, replace the `86Box.exe` in your current
86Box enviroment with your freshly built one. If you do not have a
pre-existing 86Box environment, download the latest successful build from
http://ci.86box.net, and the ROM set from http://tinyurl.com/rs20180302.
http://ci.86box.net, and the ROM set from http://tinyurl.com/rs20180312.
8. Enjoy using and testing the emulator! :)
If you encounter issues at any step or have additional questions, please join

View File

@@ -8,7 +8,7 @@
*
* Main include file for the application.
*
* Version: @(#)86box.h 1.0.17 2018/01/18
* Version: @(#)86box.h 1.0.18 2018/03/13
*
* Authors: Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
@@ -105,6 +105,7 @@ extern int cpu_manufacturer, /* (C) cpu manufacturer */
cpu, /* (C) cpu type */
cpu_use_dynarec, /* (C) cpu uses/needs Dyna */
enable_external_fpu; /* (C) enable external FPU */
extern int enable_sync; /* (C) enable time sync */
#ifdef ENABLE_LOG_TOGGLES

View File

@@ -9,7 +9,7 @@
* Implementation of the CD-ROM drive with SCSI(-like)
* commands, for both ATAPI and SCSI usage.
*
* Version: @(#)cdrom.c 1.0.34 2018/03/06
* Version: @(#)cdrom.c 1.0.35 2018/03/15
*
* Author: Miran Grca, <mgrca8@gmail.com>
*
@@ -886,12 +886,6 @@ static void cdrom_unit_attention(uint8_t id)
cdrom_log("CD-ROM %i: UNIT ATTENTION\n", id);
}
static void cdrom_bus_master_error(uint8_t id)
{
cdrom_sense_key = cdrom_asc = cdrom_ascq = 0;
cdrom_cmd_error(id);
}
static void cdrom_not_ready(uint8_t id)
{
cdrom_sense_key = SENSE_NOT_READY;
@@ -2722,17 +2716,12 @@ int cdrom_read_from_ide_dma(uint8_t channel)
return 0;
if (ide_bus_master_write) {
if (ide_bus_master_write(channel >> 1, cdbufferb, cdrom[id].packet_len)) {
cdrom_bus_master_error(id);
cdrom_phase_callback(id);
if (ide_bus_master_write(channel >> 1, cdbufferb, cdrom[id].packet_len))
return 0;
} else
else
return 1;
} else {
cdrom_bus_master_error(id);
cdrom_phase_callback(id);
} else
return 0;
}
return 0;
}
@@ -2750,6 +2739,12 @@ int cdrom_read_from_scsi_dma(uint8_t scsi_id, uint8_t scsi_lun)
return 1;
}
void cdrom_irq_raise(uint8_t id)
{
if (cdrom_drives[id].bus_type < CDROM_BUS_SCSI)
ide_irq_raise(&(ide_drives[cdrom_drives[id].ide_channel]));
}
int cdrom_read_from_dma(uint8_t id)
{
int32_t *BufLen = &SCSIDevices[cdrom_drives[id].scsi_device_id][cdrom_drives[id].scsi_device_lun].BufferLength;
@@ -2770,43 +2765,33 @@ int cdrom_read_from_dma(uint8_t id)
cdrom_log("CD-ROM %i: ATAPI Input data length: %i\n", id, cdrom[id].packet_len);
ret = cdrom_phase_data_out(id);
if (!ret) {
cdrom_phase_callback(id);
return 0;
} else
return 1;
return 0;
if (ret) {
cdrom_buf_free(id);
cdrom[id].packet_status = CDROM_PHASE_COMPLETE;
cdrom[id].status = READY_STAT;
cdrom[id].phase = 3;
ui_sb_update_icon(SB_CDROM | id, 0);
cdrom_irq_raise(id);
return 1;
} else
return 0;
}
int cdrom_write_to_ide_dma(uint8_t channel)
{
uint8_t id = atapi_cdrom_drives[channel];
if (id > CDROM_NUM) {
cdrom_log("CD-ROM %i: Drive not found\n", id);
if (id > CDROM_NUM)
return 0;
}
if (ide_bus_master_read) {
if (ide_bus_master_read(channel >> 1, cdbufferb, cdrom[id].packet_len)) {
cdrom_log("CD-ROM %i: ATAPI DMA error\n", id);
cdrom_bus_master_error(id);
cdrom_phase_callback(id);
if (ide_bus_master_read(channel >> 1, cdbufferb, cdrom[id].packet_len))
return 0;
}
else {
cdrom_log("CD-ROM %i: ATAPI DMA success\n", id);
else
return 1;
}
} else {
cdrom_log("CD-ROM %i: No bus master\n", id);
cdrom_bus_master_error(id);
cdrom_phase_callback(id);
} else
return 0;
}
return 0;
}
int cdrom_write_to_scsi_dma(uint8_t scsi_id, uint8_t scsi_lun)
@@ -2834,16 +2819,16 @@ int cdrom_write_to_dma(uint8_t id)
} else
ret = cdrom_write_to_ide_dma(cdrom_drives[id].ide_channel);
if (!ret)
if (ret) {
cdrom_buf_free(id);
cdrom[id].packet_status = CDROM_PHASE_COMPLETE;
cdrom[id].status = READY_STAT;
cdrom[id].phase = 3;
ui_sb_update_icon(SB_CDROM | id, 0);
cdrom_irq_raise(id);
return 1;
} else
return 0;
return 1;
}
void cdrom_irq_raise(uint8_t id)
{
if (cdrom_drives[id].bus_type < CDROM_BUS_SCSI)
ide_irq_raise(&(ide_drives[cdrom_drives[id].ide_channel]));
}
/* If the result is 1, issue an IRQ, otherwise not. */
@@ -2879,12 +2864,6 @@ void cdrom_phase_callback(uint8_t id)
case CDROM_PHASE_DATA_OUT_DMA:
cdrom_log("CD-ROM %i: CDROM_PHASE_DATA_OUT_DMA\n", id);
cdrom_read_from_dma(id);
cdrom_buf_free(id);
cdrom[id].packet_status = CDROM_PHASE_COMPLETE;
cdrom[id].status = READY_STAT;
cdrom[id].phase = 3;
ui_sb_update_icon(SB_CDROM | id, 0);
cdrom_irq_raise(id);
return;
case CDROM_PHASE_DATA_IN:
cdrom_log("CD-ROM %i: CDROM_PHASE_DATA_IN\n", id);
@@ -2895,12 +2874,6 @@ void cdrom_phase_callback(uint8_t id)
case CDROM_PHASE_DATA_IN_DMA:
cdrom_log("CD-ROM %i: CDROM_PHASE_DATA_IN_DMA\n", id);
cdrom_write_to_dma(id);
cdrom_buf_free(id);
cdrom[id].packet_status = CDROM_PHASE_COMPLETE;
cdrom[id].status = READY_STAT;
cdrom[id].phase = 3;
ui_sb_update_icon(SB_CDROM | id, 0);
cdrom_irq_raise(id);
return;
case CDROM_PHASE_ERROR:
cdrom_log("CD-ROM %i: CDROM_PHASE_ERROR\n", id);

View File

@@ -9,7 +9,7 @@
* Implementation of the CD-ROM drive with SCSI(-like)
* commands, for both ATAPI and SCSI usage.
*
* Version: @(#)cdrom.h 1.0.5 2018/03/06
* Version: @(#)cdrom.h 1.0.6 2018/03/15
*
* Author: Miran Grca, <mgrca8@gmail.com>
*
@@ -148,6 +148,8 @@ typedef struct {
int block_descriptor_len;
int init_length;
int16_t cd_buffer[BUF_SIZE];
} cdrom_t;
typedef struct {
@@ -187,7 +189,6 @@ typedef struct {
int cd_state;
uint32_t cd_pos;
uint32_t cd_end;
int16_t cd_buffer[BUF_SIZE];
int cd_buflen;
} cdrom_image_t;
@@ -199,7 +200,6 @@ typedef struct {
int tocvalid;
int cd_state;
uint32_t cd_end;
int16_t cd_buffer[BUF_SIZE];
int cd_buflen;
int actual_requested_blocks;
int last_track_pos;

View File

@@ -80,9 +80,9 @@ void image_audio_callback(uint8_t id, int16_t *output, int len)
{
if (cdrom[id].seek_pos < cdrom_image[id].cd_end)
{
if (!cdimg[id]->ReadSector((unsigned char*)&cdrom_image[id].cd_buffer[cdrom_image[id].cd_buflen], true, cdrom[id].seek_pos))
if (!cdimg[id]->ReadSector((unsigned char*)&cdrom[id].cd_buffer[cdrom_image[id].cd_buflen], true, cdrom[id].seek_pos))
{
memset(&cdrom_image[id].cd_buffer[cdrom_image[id].cd_buflen], 0, (BUF_SIZE - cdrom_image[id].cd_buflen) * 2);
memset(&cdrom[id].cd_buffer[cdrom_image[id].cd_buflen], 0, (BUF_SIZE - cdrom_image[id].cd_buflen) * 2);
cdrom_image[id].cd_state = CD_STOPPED;
cdrom_image[id].cd_buflen = len;
}
@@ -94,13 +94,13 @@ void image_audio_callback(uint8_t id, int16_t *output, int len)
}
else
{
memset(&cdrom_image[id].cd_buffer[cdrom_image[id].cd_buflen], 0, (BUF_SIZE - cdrom_image[id].cd_buflen) * 2);
memset(&cdrom[id].cd_buffer[cdrom_image[id].cd_buflen], 0, (BUF_SIZE - cdrom_image[id].cd_buflen) * 2);
cdrom_image[id].cd_state = CD_STOPPED;
cdrom_image[id].cd_buflen = len;
}
}
memcpy(output, cdrom_image[id].cd_buffer, len * 2);
memmove(cdrom_image[id].cd_buffer, &cdrom_image[id].cd_buffer[len], (BUF_SIZE - len) * 2);
memcpy(output, cdrom[id].cd_buffer, len * 2);
memmove(cdrom[id].cd_buffer, &cdrom[id].cd_buffer[len], (BUF_SIZE - len) * 2);
cdrom_image[id].cd_buflen -= len;
}

View File

@@ -8,7 +8,7 @@
*
* CPU type handler.
*
* Version: @(#)cpu.c 1.0.13 2018/03/02
* Version: @(#)cpu.c 1.0.14 2018/03/11
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* leilei,
@@ -113,7 +113,7 @@ int cpuspeed;
uint64_t cpu_CR4_mask;
int cpu_cycles_read, cpu_cycles_read_l, cpu_cycles_write, cpu_cycles_write_l;
int cpu_prefetch_cycles, cpu_prefetch_width;
int cpu_prefetch_cycles, cpu_prefetch_width, cpu_mem_prefetch_cycles, cpu_rom_prefetch_cycles;
int cpu_waitstates;
int cpu_cache_int_enabled, cpu_cache_ext_enabled;
int cpu_pci_speed;
@@ -246,6 +246,11 @@ void cpu_set()
isa_cycles = cpu_s->atclk_div;
if (cpu_s->rspeed <= 8000000)
cpu_rom_prefetch_cycles = cpu_mem_prefetch_cycles;
else
cpu_rom_prefetch_cycles = cpu_s->rspeed / 1000000;
if (cpu_s->pci_speed)
{
pci_nonburst_time = 4*cpu_s->rspeed / cpu_s->pci_speed;
@@ -2217,7 +2222,10 @@ void cpu_update_waitstates()
{
cpu_s = &machines[machine].cpu[cpu_manufacturer].cpus[cpu_effective];
cpu_prefetch_width = cpu_16bitbus ? 2 : 4;
if (is486)
cpu_prefetch_width = 16;
else
cpu_prefetch_width = cpu_16bitbus ? 2 : 4;
if (cpu_cache_int_enabled)
{
@@ -2251,4 +2259,9 @@ void cpu_update_waitstates()
cpu_cycles_write = cpu_s->mem_write_cycles;
cpu_cycles_write_l = (cpu_16bitbus ? 2 : 1) * cpu_s->mem_write_cycles;
}
if (is486)
cpu_prefetch_cycles *= 4;
cpu_mem_prefetch_cycles = cpu_prefetch_cycles;
if (cpu_s->rspeed <= 8000000)
cpu_rom_prefetch_cycles = cpu_mem_prefetch_cycles;
}

View File

@@ -8,7 +8,7 @@
*
* CPU type handler.
*
* Version: @(#)cpu.h 1.0.9 2018/03/02
* Version: @(#)cpu.h 1.0.10 2018/03/11
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* leilei,
@@ -387,7 +387,7 @@ extern x86seg _oldds;
#define ISA_CYCLES(x) ((x * isa_cycles) >> ISA_CYCLES_SHIFT)
extern int cpu_cycles_read, cpu_cycles_read_l, cpu_cycles_write, cpu_cycles_write_l;
extern int cpu_prefetch_cycles, cpu_prefetch_width;
extern int cpu_prefetch_cycles, cpu_prefetch_width, cpu_mem_prefetch_cycles, cpu_rom_prefetch_cycles;
extern int cpu_waitstates;
extern int cpu_cache_int_enabled, cpu_cache_ext_enabled;
extern int cpu_pci_speed;

View File

@@ -9,7 +9,7 @@
* Implementation of the IDE emulation for hard disks and ATAPI
* CD-ROM devices.
*
* Version: @(#)hdc_ide.c 1.0.32 2018/03/06
* Version: @(#)hdc_ide.c 1.0.35 2018/03/16
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -20,10 +20,11 @@
#define __USE_LARGEFILE64
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <inttypes.h>
#include <wchar.h>
#define HAVE_STDARG_H
@@ -90,6 +91,14 @@
#define WIN_SET_FEATURES 0xEF
#define WIN_READ_NATIVE_MAX 0xF8
#define FEATURE_SET_TRANSFER_MODE 0x03
#define FEATURE_ENABLE_IRQ_OVERLAPPED 0x5d
#define FEATURE_ENABLE_IRQ_SERVICE 0x5e
#define FEATURE_DISABLE_REVERT 0x66
#define FEATURE_ENABLE_REVERT 0xcc
#define FEATURE_DISABLE_IRQ_OVERLAPPED 0xdd
#define FEATURE_DISABLE_IRQ_SERVICE 0xde
enum
{
IDE_NONE = 0,
@@ -397,11 +406,13 @@ static void ide_identify(IDE *ide)
if (ide->buffer[49] & (1 << 8))
{
ide->buffer[52] = 2 << 8; /*DMA timing mode*/
ide->buffer[51] = 120;
ide->buffer[52] = 120; /*DMA timing mode*/
ide->buffer[53] |= 6;
ide->buffer[62] = 7;
ide->buffer[63] = 7;
ide->buffer[64] = 3; /*PIO Modes 3 & 4*/
ide->buffer[88] = 7;
if (ide->mdma_mode != -1)
{
@@ -411,7 +422,10 @@ static void ide_identify(IDE *ide)
ide->buffer[88] |= d;
else if ((ide->mdma_mode & 0x300) == 0x100)
ide->buffer[63] |= d;
else
else if ((ide->mdma_mode & 0x300) == 0x400) {
if ((ide->mdma_mode & 0xff) >= 3)
ide->buffer[64] |= d;
} else
ide->buffer[62] |= d;
ide_log(" IDENTIFY DMA Mode: %04X, %04X\n", ide->buffer[62], ide->buffer[63]);
}
@@ -452,10 +466,12 @@ static void ide_atapi_identify(IDE *ide)
if (PCI && (ide->board < 2) && (cdrom_drives[cdrom_id].bus_type == CDROM_BUS_ATAPI_PIO_AND_DMA))
{
ide->buffer[49] |= 0x100; /* DMA supported */
ide->buffer[52] = 2 << 8; /*DMA timing mode*/
ide->buffer[51] = 120;
ide->buffer[52] = 120; /*DMA timing mode*/
ide->buffer[53] = 7;
ide->buffer[62] = 7;
ide->buffer[63] = 7;
ide->buffer[64] = 3; /*PIO Modes 3 & 4*/
ide->buffer[88] = 7;
if (ide->mdma_mode != -1)
{
@@ -465,12 +481,16 @@ static void ide_atapi_identify(IDE *ide)
ide->buffer[88] |= d;
else if ((ide->mdma_mode & 0x300) == 0x100)
ide->buffer[63] |= d;
else
else if ((ide->mdma_mode & 0x300) == 0x400) {
if ((ide->mdma_mode & 0xff) >= 3)
ide->buffer[64] |= d;
} else
ide->buffer[62] |= d;
ide_log("PIDENTIFY DMA Mode: %04X, %04X\n", ide->buffer[62], ide->buffer[63]);
}
ide->buffer[65] = 0xb4;
ide->buffer[66] = 0xb4;
ide->buffer[65] = 120;
ide->buffer[66] = 120;
ide->buffer[67] = 120;
ide->buffer[71] = 30;
ide->buffer[72] = 30;
ide->buffer[80] = 0x1e; /*ATA-1 to ATA-4 supported*/
@@ -506,26 +526,42 @@ static void ide_atapi_zip_identify(IDE *ide)
if (PCI && (ide->board < 2) && (zip_drives[zip_id].bus_type == ZIP_BUS_ATAPI_PIO_AND_DMA))
{
ide->buffer[49] |= 0x100; /* DMA supported */
ide->buffer[52] = 0 << 8; /*DMA timing mode*/
ide->buffer[53] = 6;
ide->buffer[63] = 3;
ide->buffer[88] = 7;
if (ide->mdma_mode != -1)
if (zip_drives[zip_id].is_250) {
ide->buffer[52] = 0 << 8; /*DMA timing mode*/
ide->buffer[53] = 6;
ide->buffer[63] = 3;
ide->buffer[88] = 7;
ide->buffer[64] = 0x0001; /*PIO Mode 3*/
ide->buffer[65] = 0x96;
ide->buffer[66] = 0x96;
ide->buffer[67] = 0xb4;
ide->buffer[68] = 0xb4;
ide->buffer[80] = 0x30; /*Supported ATA versions : ATA/ATAPI-4 ATA/ATAPI-5*/
ide->buffer[81] = 0x15; /*Maximum ATA revision supported : ATA/ATAPI-5 T13 1321D revision 1*/
} else {
ide->buffer[51] = 120;
ide->buffer[52] = 120;
ide->buffer[53] = 2; /*Words 64-70 are valid*/
ide->buffer[63] = 0x0003; /*Multi-word DMA 0 & 1*/
ide->buffer[64] = 0x0001; /*PIO Mode 3*/
ide->buffer[65] = 120;
ide->buffer[66] = 120;
ide->buffer[67] = 120;
}
if (ide->mdma_mode != -1)
{
d = (ide->mdma_mode & 0xff);
d <<= 8;
if ((ide->mdma_mode & 0x300) == 0x200)
ide->buffer[88] |= d;
else
else if ((ide->mdma_mode & 0x300) == 0x400) {
if ((ide->mdma_mode & 0xff) >= 3)
ide->buffer[64] |= d;
} else
ide->buffer[63] |= d;
ide_log("PIDENTIFY DMA Mode: %04X, %04X\n", ide->buffer[62], ide->buffer[63]);
}
ide->buffer[65] = 0x96;
ide->buffer[66] = 0x96;
ide->buffer[67] = 0xb4;
ide->buffer[68] = 0xb4;
ide->buffer[80] = 0x30; /*Supported ATA versions : ATA/ATAPI-4 ATA/ATAPI-5*/
ide->buffer[81] = 0x15; /*Maximum ATA revision supported : ATA/ATAPI-5 T13 1321D revision 1*/
}
}
@@ -631,14 +667,25 @@ static int ide_set_features(IDE *ide)
if (ide_drive_is_zip(ide)) {
bus = zip_drives[atapi_zip_drives[ide->channel]].bus_type;
dma = (bus == ZIP_BUS_ATAPI_PIO_AND_DMA);
max_pio = 0;
if (!PCI || !dma || (ide->board >= 2))
max_pio = 0;
else
max_pio = 3;
max_mdma = 1;
} else if (ide_drive_is_cdrom(ide)) {
bus = cdrom_drives[atapi_cdrom_drives[ide->channel]].bus_type;
dma = (bus == CDROM_BUS_ATAPI_PIO_AND_DMA);
if (!PCI || !dma || (ide->board >= 2))
max_pio = 0;
else
max_pio = 4;
} else {
bus = hdd[ide->hdd_num].bus;
dma = (bus == HDD_BUS_IDE_PIO_AND_DMA);
if (!PCI || !dma || (ide->board >= 2))
max_pio = 0;
else
max_pio = 2;
}
ide_log("Features code %02X\n", features);
@@ -647,7 +694,7 @@ static int ide_set_features(IDE *ide)
switch(features)
{
case 0x03: /* Set transfer mode. */
case FEATURE_SET_TRANSFER_MODE: /* Set transfer mode. */
ide_log("Transfer mode %02X\n", features_data >> 3);
mode = (features_data >> 3);
@@ -669,7 +716,7 @@ static int ide_set_features(IDE *ide)
{
return 0;
}
ide->mdma_mode = -1;
ide->mdma_mode = (1 << submode) | 0x400;
ide_log("IDE %02X: Setting PIO mode: %02X, %08X\n", ide->channel, submode, ide->mdma_mode);
break;
@@ -704,8 +751,17 @@ static int ide_set_features(IDE *ide)
return 0;
}
case 0x66: /* Disable reverting to power on defaults. */
case 0xCC: /* Enable reverting to power on defaults. */
case FEATURE_ENABLE_IRQ_OVERLAPPED:
case FEATURE_ENABLE_IRQ_SERVICE:
case FEATURE_DISABLE_IRQ_OVERLAPPED:
case FEATURE_DISABLE_IRQ_SERVICE:
if (!PCI || !dma || (ide->board >= 2))
return 0;
else
return 1;
case FEATURE_DISABLE_REVERT: /* Disable reverting to power on defaults. */
case FEATURE_ENABLE_REVERT: /* Enable reverting to power on defaults. */
return 1;
default:
@@ -738,6 +794,24 @@ void ide_ter_disable_cond();
void ide_qua_disable_cond();
void ide_destroy_buffers(void)
{
int d;
for (d = 0; d < (IDE_NUM+XTIDE_NUM); d++)
{
if (ide_drives[d].buffer) {
free(ide_drives[d].buffer);
ide_drives[d].buffer = NULL;
}
if (ide_drives[d].sector_buffer) {
free(ide_drives[d].sector_buffer);
ide_drives[d].sector_buffer = NULL;
}
}
}
void ide_reset(void)
{
int c, d;
@@ -763,8 +837,18 @@ void ide_reset(void)
ide_drives[d].atastat = READY_STAT | DSC_STAT;
ide_drives[d].service = 0;
ide_drives[d].board = d >> 1;
if (ide_drives[d].buffer) {
free(ide_drives[d].buffer);
ide_drives[d].buffer = NULL;
}
if (ide_drives[d].sector_buffer) {
free(ide_drives[d].sector_buffer);
ide_drives[d].sector_buffer = NULL;
}
}
idecallback[0]=idecallback[1]=0LL;
idecallback[2]=idecallback[3]=0LL;
idecallback[4]=0LL;
@@ -777,12 +861,16 @@ void ide_reset(void)
{
ide_log("Found IDE hard disk on channel %i\n", hdd[d].ide_channel);
loadhd(&ide_drives[hdd[d].ide_channel], d, hdd[d].fn);
ide_drives[hdd[d].ide_channel].sector_buffer = NULL; /* Important, makes sure malloc does not reuse an existing pointer from elsewhere. */
ide_drives[hdd[d].ide_channel].sector_buffer = (uint8_t *) malloc(256*512);
if (++c >= (IDE_NUM+XTIDE_NUM)) break;
}
if ((hdd[d].bus==HDD_BUS_XTIDE) && (hdd[d].xtide_channel < XTIDE_NUM))
{
ide_log("Found XT IDE hard disk on channel %i\n", hdd[d].xtide_channel);
loadhd(&ide_drives[hdd[d].xtide_channel | 8], d, hdd[d].fn);
ide_drives[hdd[d].xtide_channel | 8].sector_buffer = NULL; /* Important, makes sure malloc does not reuse an existing pointer from elsewhere. */
ide_drives[hdd[d].xtide_channel | 8].sector_buffer = (uint8_t *) malloc(256*512);
if (++c >= (IDE_NUM+XTIDE_NUM)) break;
}
}
@@ -790,13 +878,14 @@ void ide_reset(void)
for (d = 0; d < IDE_NUM; d++)
{
if (ide_drive_is_zip(&ide_drives[d]) && (ide_drives[d].type != IDE_HDD))
{
if (ide_drive_is_zip(&ide_drives[d]) && (ide_drives[d].type == IDE_NONE))
ide_drives[d].type = IDE_ZIP;
}
else if (ide_drive_is_cdrom(&ide_drives[d]) && (ide_drives[d].type != IDE_HDD))
{
else if (ide_drive_is_cdrom(&ide_drives[d]) && (ide_drives[d].type == IDE_NONE))
ide_drives[d].type = IDE_CDROM;
if (ide_drives[d].type != IDE_NONE) {
ide_drives[d].buffer = NULL; /* Important, makes sure malloc does not reuse an existing pointer from elsewhere. */
ide_drives[d].buffer = (uint16_t *) malloc(65536 * sizeof(uint16_t));
}
ide_set_signature(&ide_drives[d]);

View File

@@ -9,7 +9,7 @@
* Implementation of the IDE emulation for hard disks and ATAPI
* CD-ROM devices.
*
* Version: @(#)hdd_ide.h 1.0.6 2018/02/14
* Version: @(#)hdd_ide.h 1.0.7 2018/03/15
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -36,7 +36,7 @@ typedef struct {
int packetstatus;
uint8_t asc;
int reset;
uint16_t buffer[65536];
uint16_t *buffer;
int irqstat;
int service;
int lba;
@@ -49,7 +49,7 @@ typedef struct {
int hdd_num;
uint8_t specify_success;
int mdma_mode;
uint8_t sector_buffer[256*512];
uint8_t *sector_buffer;
int do_initial_read;
int sector_pos;
} IDE;
@@ -104,6 +104,7 @@ extern void ide_set_callback(uint8_t channel, int64_t callback);
extern void secondary_ide_check(void);
extern void ide_padstr8(uint8_t *buf, int buf_size, const char *src);
extern void ide_destroy_buffers(void);
extern int (*ide_bus_master_read)(int channel, uint8_t *data, int transfer_length);
extern int (*ide_bus_master_write)(int channel, uint8_t *data, int transfer_length);

View File

@@ -9,7 +9,7 @@
* Implementation of the Iomega ZIP drive with SCSI(-like)
* commands, for both ATAPI and SCSI usage.
*
* Version: @(#)zip.c 1.0.8 2018/03/07
* Version: @(#)zip.c 1.0.9 2018/03/15
*
* Author: Miran Grca, <mgrca8@gmail.com>
*
@@ -1084,12 +1084,6 @@ static void zip_unit_attention(uint8_t id)
zip_log("ZIP %i: UNIT ATTENTION\n", id);
}
static void zip_bus_master_error(uint8_t id)
{
zip_sense_key = zip_asc = zip_ascq = 0;
zip_cmd_error(id);
}
static void zip_not_ready(uint8_t id)
{
zip_sense_key = SENSE_NOT_READY;
@@ -2220,19 +2214,12 @@ int zip_read_from_ide_dma(uint8_t channel)
return 0;
if (ide_bus_master_write) {
if (ide_bus_master_write(channel >> 1, zipbufferb, zip[id].packet_len)) {
zip_bus_master_error(id);
zip_phase_callback(id);
if (ide_bus_master_write(channel >> 1, zipbufferb, zip[id].packet_len))
return 0;
} else
else
return 1;
} else {
zip_bus_master_error(id);
zip_phase_callback(id);
} else
return 0;
}
return 0;
}
int zip_read_from_scsi_dma(uint8_t scsi_id, uint8_t scsi_lun)
@@ -2248,6 +2235,12 @@ int zip_read_from_scsi_dma(uint8_t scsi_id, uint8_t scsi_lun)
return 1;
}
void zip_irq_raise(uint8_t id)
{
if (zip_drives[id].bus_type < ZIP_BUS_SCSI)
ide_irq_raise(&(ide_drives[zip_drives[id].ide_channel]));
}
int zip_read_from_dma(uint8_t id)
{
int32_t *BufLen = &SCSIDevices[zip_drives[id].scsi_device_id][zip_drives[id].scsi_device_lun].BufferLength;
@@ -2273,13 +2266,17 @@ int zip_read_from_dma(uint8_t id)
}
ret = zip_phase_data_out(id);
if (!ret) {
zip_phase_callback(id);
return 0;
} else
return 1;
return 0;
if (ret) {
zip_buf_free(id);
zip[id].packet_status = ZIP_PHASE_COMPLETE;
zip[id].status = READY_STAT;
zip[id].phase = 3;
ui_sb_update_icon(SB_ZIP | id, 0);
zip_irq_raise(id);
return 1;
} else
return 0;
}
int zip_write_to_ide_dma(uint8_t channel)
@@ -2292,24 +2289,12 @@ int zip_write_to_ide_dma(uint8_t channel)
}
if (ide_bus_master_read) {
if (ide_bus_master_read(channel >> 1, zipbufferb, zip[id].packet_len)) {
zip_log("ZIP %i: ATAPI DMA error\n", id);
zip_bus_master_error(id);
zip_phase_callback(id);
if (ide_bus_master_read(channel >> 1, zipbufferb, zip[id].packet_len))
return 0;
}
else {
zip_log("ZIP %i: ATAPI DMA success\n", id);
else
return 1;
}
} else {
zip_log("ZIP %i: No bus master\n", id);
zip_bus_master_error(id);
zip_phase_callback(id);
} else
return 0;
}
return 0;
}
int zip_write_to_scsi_dma(uint8_t scsi_id, uint8_t scsi_lun)
@@ -2337,16 +2322,16 @@ int zip_write_to_dma(uint8_t id)
} else
ret = zip_write_to_ide_dma(zip_drives[id].ide_channel);
if (!ret)
if (ret) {
zip_buf_free(id);
zip[id].packet_status = ZIP_PHASE_COMPLETE;
zip[id].status = READY_STAT;
zip[id].phase = 3;
ui_sb_update_icon(SB_ZIP | id, 0);
zip_irq_raise(id);
return 1;
} else
return 0;
return 1;
}
void zip_irq_raise(uint8_t id)
{
if (zip_drives[id].bus_type < ZIP_BUS_SCSI)
ide_irq_raise(&(ide_drives[zip_drives[id].ide_channel]));
}
/* If the result is 1, issue an IRQ, otherwise not. */
@@ -2382,12 +2367,6 @@ void zip_phase_callback(uint8_t id)
case ZIP_PHASE_DATA_OUT_DMA:
zip_log("ZIP %i: ZIP_PHASE_DATA_OUT_DMA\n", id);
zip_read_from_dma(id);
zip_buf_free(id);
zip[id].packet_status = ZIP_PHASE_COMPLETE;
zip[id].status = READY_STAT;
zip[id].phase = 3;
ui_sb_update_icon(SB_ZIP | id, 0);
zip_irq_raise(id);
return;
case ZIP_PHASE_DATA_IN:
zip_log("ZIP %i: ZIP_PHASE_DATA_IN\n", id);
@@ -2398,12 +2377,6 @@ void zip_phase_callback(uint8_t id)
case ZIP_PHASE_DATA_IN_DMA:
zip_log("ZIP %i: ZIP_PHASE_DATA_IN_DMA\n", id);
zip_write_to_dma(id);
zip_buf_free(id);
zip[id].packet_status = ZIP_PHASE_COMPLETE;
zip[id].status = READY_STAT;
zip[id].phase = 3;
ui_sb_update_icon(SB_ZIP | id, 0);
zip_irq_raise(id);
return;
case ZIP_PHASE_ERROR:
zip_log("ZIP %i: ZIP_PHASE_ERROR\n", id);

810
src/dma.c

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@
*
* Implementation of the Intel DMA controllers.
*
* Version: @(#)dma.h 1.0.4 2017/12/15
* Version: @(#)dma.h 1.0.5 2018/03/11
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -25,32 +25,25 @@
#define DMA_VERIFY 0x20000
typedef struct DMA {
uint32_t ab[4],
ac[4];
uint16_t cb[4];
int cc[4];
int wp;
uint8_t m,
mode[4];
uint8_t page[4];
uint8_t stat,
stat_rq;
uint8_t command;
uint8_t request;
int xfr_command,
xfr_channel;
int byte_ptr;
int is_ps2;
uint8_t arb_level[4];
uint8_t ps2_mode[4];
} DMA;
extern DMA dma, dma16;
/*DMA*/
typedef struct dma_t
{
uint32_t ab, ac;
uint16_t cb;
int cc;
int wp;
uint8_t m, mode;
uint8_t page;
uint8_t stat, stat_rq;
uint8_t command;
int size;
uint8_t ps2_mode;
uint8_t arb_level;
uint16_t io_addr;
} dma_t;
extern dma_t dma[8];
extern void dma_init(void);
extern void dma16_init(void);

View File

@@ -8,7 +8,7 @@
*
* Implementation of the floppy drive emulation.
*
* Version: @(#)fdd.c 1.0.8 2018/03/06
* Version: @(#)fdd.c 1.0.9 2018/03/14
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -447,6 +447,7 @@ void fdd_load(int drive, wchar_t *fn)
{
driveloaders[drive] = c;
memcpy(floppyfns[drive], fn, (wcslen(fn) << 1) + 2);
d86f_initialize_linked_lists(drive);
loaders[c].load(drive, floppyfns[drive]);
drive_empty[drive] = 0;
fdd_forced_seek(drive, 0);
@@ -468,6 +469,8 @@ void fdd_close(int drive)
drive_empty[drive] = 1;
fdd_set_head(drive, 0);
floppyfns[drive][0] = 0;
d86f_destroy_linked_lists(drive, 0);
d86f_destroy_linked_lists(drive, 1);
drives[drive].hole = NULL;
drives[drive].poll = NULL;
drives[drive].seek = NULL;

View File

@@ -263,7 +263,7 @@ typedef union
void d86f_set_version(int drive, uint16_t version);
void d86f_initialize_last_sector_id(int drive, int c, int h, int r, int n);
void d86f_zero_bit_field(int drive, int side);
void d86f_destroy_linked_lists(int drive, int side);
void d86f_set_fdc(void *fdc);
void fdi_set_fdc(void *fdc);

View File

@@ -10,7 +10,7 @@
* data in the form of FM/MFM-encoded transitions) which also
* forms the core of the emulator's floppy disk emulation.
*
* Version: @(#)fdd_86f.c 1.0.15 2018/03/05
* Version: @(#)fdd_86f.c 1.0.17 2018/03/14
*
* Author: Miran Grca, <mgrca8@gmail.com>
* Copyright 2016-2018 Miran Grca.
@@ -195,6 +195,14 @@ typedef union {
static fdc_t *d86f_fdc;
#pragma pack(push,1)
typedef struct
{
uint8_t c, h, r, n;
void *prev;
} sector_t;
#pragma pack(pop)
#pragma pack(push,1)
struct
{
@@ -239,7 +247,7 @@ struct
uint8_t *outbuf;
uint32_t dma_over;
int turbo_pos;
uint16_t sector_id_bit_field[2][256][256][256];
sector_t *last_side_sector[2];
} d86f[FDD_NUM + 1];
#pragma pack(pop)
@@ -265,25 +273,29 @@ d86f_log(const char *format, ...)
}
void d86f_zero_bit_field(int drive, int side)
void d86f_initialize_linked_lists(int drive)
{
int i = 0;
int j = 0;
int k = 0;
int l = 0;
d86f[drive].last_side_sector[0] = NULL;
d86f[drive].last_side_sector[1] = NULL;
}
for (i = 0; i < side; i++)
void d86f_destroy_linked_lists(int drive, int side)
{
sector_t *s;
sector_t *t;
if (d86f[drive].last_side_sector[side])
{
for (j = 0; j < 256; j++)
{
for (k = 0; k < 256; k++)
{
for (l = 0; l < 256; l++)
{
d86f[drive].sector_id_bit_field[i][j][k][l] = 0;
}
}
s = d86f[drive].last_side_sector[side];
while (s) {
t = s->prev;
free(s);
s = NULL;
if (!t)
break;
s = t;
}
d86f[drive].last_side_sector[side] = NULL;
}
}
@@ -2190,6 +2202,24 @@ void d86f_turbo_format(int drive, int side, int nop)
}
}
int d86f_sector_is_present(int drive, int side, uint8_t c, uint8_t h, uint8_t r, uint8_t n)
{
sector_t *s, *t;
if (d86f[drive].last_side_sector[side]) {
s = d86f[drive].last_side_sector[side];
while (s) {
if ((s->c == c) && (s->h == h) && (s->r == r) && (s->n == n))
return 1;
if (!s->prev)
break;
t = s->prev;
s = t;
}
}
return 0;
}
void d86f_turbo_poll(int drive, int side)
{
if ((d86f[drive].state != STATE_IDLE) && (d86f[drive].state != STATE_SECTOR_NOT_FOUND) && ((d86f[drive].state & 0xF8) != 0xE8))
@@ -2213,7 +2243,11 @@ void d86f_turbo_poll(int drive, int side)
d86f[drive].state++;
return;
case STATE_02_FIND_ID:
if (!(d86f[drive].sector_id_bit_field[side][fdc_get_read_track_sector(d86f_fdc).id.c][fdc_get_read_track_sector(d86f_fdc).id.h][fdc_get_read_track_sector(d86f_fdc).id.r] & (1 << fdc_get_read_track_sector(d86f_fdc).id.n)))
if (!d86f_sector_is_present(drive, side,
fdc_get_read_track_sector(d86f_fdc).id.c,
fdc_get_read_track_sector(d86f_fdc).id.h,
fdc_get_read_track_sector(d86f_fdc).id.r,
fdc_get_read_track_sector(d86f_fdc).id.n))
{
d86f[drive].id_find.sync_marks = d86f[drive].id_find.bits_obtained = d86f[drive].id_find.bytes_obtained = d86f[drive].error_condition = 0;
fdc_nosector(d86f_fdc);
@@ -2234,7 +2268,11 @@ void d86f_turbo_poll(int drive, int side)
case STATE_0C_FIND_ID:
case STATE_11_FIND_ID:
case STATE_16_FIND_ID:
if (!(d86f[drive].sector_id_bit_field[side][d86f[drive].req_sector.id.c][d86f[drive].req_sector.id.h][d86f[drive].req_sector.id.r] & (1 << d86f[drive].req_sector.id.n)))
if (!d86f_sector_is_present(drive, side,
d86f[drive].req_sector.id.c,
d86f[drive].req_sector.id.h,
d86f[drive].req_sector.id.r,
d86f[drive].req_sector.id.n))
{
d86f[drive].id_find.sync_marks = d86f[drive].id_find.bits_obtained = d86f[drive].id_find.bytes_obtained = d86f[drive].error_condition = 0;
fdc_nosector(d86f_fdc);
@@ -2577,6 +2615,8 @@ uint16_t d86f_prepare_pretrack(int drive, int side, int iso)
d86f[drive].index_hole_pos[side] = 0;
d86f_destroy_linked_lists(drive, side);
for (i = 0; i < raw_size; i++)
{
d86f_write_direct_common(drive, side, gap_fill, 0, i);
@@ -2621,6 +2661,7 @@ uint16_t d86f_prepare_sector(int drive, int side, int prev_pos, uint8_t *id_buf,
uint16_t pos;
int i;
sector_t *s;
int real_gap2_len = gap2;
int real_gap3_len = gap3;
@@ -2635,7 +2676,17 @@ uint16_t d86f_prepare_sector(int drive, int side, int prev_pos, uint8_t *id_buf,
uint16_t dataam_mfm = 0x4555;
uint16_t datadam_mfm = 0x4A55;
d86f[drive].sector_id_bit_field[side][id_buf[0]][id_buf[1]][id_buf[2]] |= (1 << id_buf[3]);
if (fdd_get_turbo(drive) && (d86f[drive].version == 0x0063)) {
s = (sector_t *) malloc(sizeof(sector_t));
memset(s, 0, sizeof(sector_t));
s->c = id_buf[0];
s->h = id_buf[1];
s->r = id_buf[2];
s->n = id_buf[3];
if (d86f[drive].last_side_sector[side])
s->prev = d86f[drive].last_side_sector[side];
d86f[drive].last_side_sector[side] = s;
}
mfm = d86f_is_mfm(drive);
@@ -3748,10 +3799,16 @@ void d86f_load(int drive, wchar_t *fn)
void d86f_init()
{
int i;
memset(d86f, 0, sizeof(d86f));
d86f_setupcrc(0x1021);
d86f[0].state = d86f[1].state = STATE_IDLE;
for (i = 0; i < (FDD_NUM + 1); i++)
{
d86f[i].state = STATE_IDLE;
d86f_initialize_linked_lists(i);
}
}
void d86f_set_fdc(void *fdc)

View File

@@ -10,7 +10,7 @@
* data in the form of FM/MFM-encoded transitions) which also
* forms the core of the emulator's floppy disk emulation.
*
* Version: @(#)floppy_86f.h 1.0.4 2018/01/18
* Version: @(#)floppy_86f.h 1.0.5 2018/03/14
*
* Author: Miran Grca, <mgrca8@gmail.com>
* Copyright 2016-2018 Miran Grca.
@@ -69,7 +69,9 @@ extern int gap4_size[2];
#define D86FVER 0x020B
extern void d86f_initialize_last_sector_id(int drive, int c, int h, int r, int n);
extern void d86f_zero_bit_field(int drive, int side);
extern void d86f_initialize_linked_lists(int drive);
extern void d86f_destroy_linked_lists(int drive, int side);
#endif /*EMU_FLOPPY_86F_H*/

View File

@@ -8,7 +8,7 @@
*
* Implementation of the IMD floppy image format.
*
* Version: @(#)fdd_imd.c 1.0.7 2018/03/06
* Version: @(#)fdd_imd.c 1.0.8 2018/03/14
*
* Author: Miran Grca, <mgrca8@gmail.com>
* Copyright 2016-2018 Miran Grca.
@@ -542,8 +542,8 @@ void imd_seek(int drive, int track)
d86f_reset_index_hole_pos(drive, 0);
d86f_reset_index_hole_pos(drive, 1);
d86f_zero_bit_field(drive, 0);
d86f_zero_bit_field(drive, 1);
d86f_destroy_linked_lists(drive, 0);
d86f_destroy_linked_lists(drive, 1);
if (track > imd[drive].track_count)
{

View File

@@ -9,7 +9,7 @@
* Implementation of the raw sector-based floppy image format,
* as well as the Japanese FDI, CopyQM, and FDF formats.
*
* Version: @(#)fdd_img.c 1.0.8 2018/01/16
* Version: @(#)fdd_img.c 1.0.9 2018/03/14
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -960,8 +960,8 @@ void img_seek(int drive, int track)
d86f_reset_index_hole_pos(drive, 0);
d86f_reset_index_hole_pos(drive, 1);
d86f_zero_bit_field(drive, 0);
d86f_zero_bit_field(drive, 1);
d86f_destroy_linked_lists(drive, 0);
d86f_destroy_linked_lists(drive, 1);
if (track > img[drive].tracks)
{

View File

@@ -8,7 +8,7 @@
*
* Implementation of the PCjs JSON floppy image format.
*
* Version: @(#)fdd_json.c 1.0.10 2018/01/16
* Version: @(#)fdd_json.c 1.0.11 2018/03/14
*
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
*
@@ -357,8 +357,8 @@ json_seek(int drive, int track)
/* Reset the 86F state machine. */
d86f_reset_index_hole_pos(drive, 0);
d86f_reset_index_hole_pos(drive, 1);
d86f_zero_bit_field(drive, 0);
d86f_zero_bit_field(drive, 1);
d86f_destroy_linked_lists(drive, 0);
d86f_destroy_linked_lists(drive, 1);
interleave_type = 0;

View File

@@ -8,7 +8,7 @@
*
* Implementation of the Teledisk floppy image format.
*
* Version: @(#)fdd_td0.c 1.0.7 2018/01/16
* Version: @(#)fdd_td0.c 1.0.8 2018/03/14
*
* Authors: Milodrag Milanovic,
* Haruhiko OKUMURA,
@@ -1111,8 +1111,8 @@ void td0_seek(int drive, int track)
d86f_reset_index_hole_pos(drive, 0);
d86f_reset_index_hole_pos(drive, 1);
d86f_zero_bit_field(drive, 0);
d86f_zero_bit_field(drive, 1);
d86f_destroy_linked_lists(drive, 0);
d86f_destroy_linked_lists(drive, 1);
if (track > td0[drive].tracks)
{

View File

@@ -8,7 +8,7 @@
*
* Intel 8042 (AT keyboard controller) emulation.
*
* Version: @(#)keyboard_at.c 1.0.27 2018/03/02
* Version: @(#)keyboard_at.c 1.0.30 2018/03/13
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -91,7 +91,7 @@ typedef struct {
uint8_t status;
uint8_t mem[0x100];
uint8_t out;
int out_new;
int out_new, out_delayed;
uint8_t secr_phase;
uint8_t mem_addr;
@@ -645,7 +645,7 @@ kbd_poll(void *priv)
kbdlog("ATkbd: want keyboard data\n");
if (kbd->mem[0] & 0x01)
picint(2);
kbd->out = kbd->out_new;
kbd->out = kbd->out_new & 0xff;
kbd->out_new = -1;
kbd->status |= STAT_OFULL;
kbd->status &= ~STAT_IFULL;
@@ -656,8 +656,16 @@ kbd_poll(void *priv)
if (kbd->out_new == -1 && !(kbd->status & STAT_OFULL) &&
key_ctrl_queue_start != key_ctrl_queue_end) {
kbd->out_new = key_ctrl_queue[key_ctrl_queue_start];
kbd->out_new = key_ctrl_queue[key_ctrl_queue_start] | 0x200;
key_ctrl_queue_start = (key_ctrl_queue_start + 1) & 0xf;
} else if (!(kbd->status & STAT_OFULL) && kbd->out_new == -1 &&
kbd->out_delayed != -1) {
kbd->out_new = kbd->out_delayed;
kbd->out_delayed = -1;
} else if (!(kbd->status & STAT_OFULL) && kbd->out_new == -1 &&
!(kbd->mem[0] & 0x10) && kbd->out_delayed != -1) {
kbd->out_new = kbd->out_delayed;
kbd->out_delayed = -1;
} else if (!(kbd->status & STAT_OFULL) && kbd->out_new == -1/* && !(kbd->mem[0] & 0x20)*/ &&
(mouse_queue_start != mouse_queue_end)) {
kbd->out_new = mouse_queue[mouse_queue_start] | 0x100;
@@ -675,6 +683,11 @@ kbd_adddata(uint8_t val)
{
key_ctrl_queue[key_ctrl_queue_end] = val;
key_ctrl_queue_end = (key_ctrl_queue_end + 1) & 0xf;
if (!(CurrentKbd->out_new & 0x300)) {
CurrentKbd->out_delayed = CurrentKbd->out_new;
CurrentKbd->out_new = -1;
}
}
@@ -960,7 +973,7 @@ kbd_cmd_write(atkbd_t *kbd, uint8_t val)
/* ISA AT keyboard controllers use bit 5 for keyboard mode (1 = PC/XT, 2 = AT);
PS/2 (and EISA/PCI) keyboard controllers use it as the PS/2 mouse enable switch. */
if ((kbd->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_1) {
if (((kbd->flags & KBC_VEN_MASK) == KBC_VEN_AMI) || ((kbd->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_1)) {
keyboard_mode &= ~CCB_PCMODE;
mouse_scan = !(val & 0x20);

View File

@@ -1,9 +1,155 @@
/*
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Implementation of the Toshiba T3100e.
*
* The Toshiba 3100e is a 286-based portable.
*
* To bring up the BIOS setup screen hold down the 'Fn' key
* on booting.
*
* Memory management
* ~~~~~~~~~~~~~~~~~
*
* Motherboard memory is divided into:
* - Conventional memory: Either 512k or 640k
* - Upper memory: Either 512k or 384k, depending on
* amount of conventional memory.
* Upper memory can be used as EMS or XMS.
* - High memory: 0-4Mb, depending on RAM installed.
* The BIOS setup screen allows some or
* all of this to be used as EMS; the
* remainder is XMS.
*
* Additional memory (either EMS or XMS) can also be provided
* by ISA expansion cards.
*
* Under test in PCem, the BIOS will boot with up to 65368Kb
* of memory in total (16Mb less 16k). However it will give
* an error with RAM sizes above 8Mb, if any of the high
* memory is allocated as EMS, because the builtin EMS page
* registers can only access up to 8Mb.
*
* Memory is controlled by writes to I/O port 8084h:
* Bit 7: Always 0 }
* Bit 6: Always 1 } These bits select which motherboard
* Bit 5: Always 0 } function to access.
* Bit 4: Set to treat upper RAM as XMS
* Bit 3: Enable external RAM boards?
* Bit 2: Set for 640k conventional memory, clear for 512k
* Bit 1: Enable RAM beyond 1Mb.
* Bit 0: Enable EMS.
*
* The last value written to this port is saved at 0040:0093h,
* and in CMOS memory at offset 0x37. If the top bit of the
* CMOS byte is set, then high memory is being provided by
* an add-on card rather than the mainboard; accordingly,
* the BIOS will not allow high memory to be used as EMS.
*
* EMS is controlled by 16 page registers:
*
* Page mapped at 0xD000 0xD400 0xD800 0xDC00
* ------------------------------------------------------
* Pages 0x00-0x7F 0x208 0x4208 0x8208 0xc208
* Pages 0x80-0xFF 0x218 0x4218 0x8218 0xc218
* Pages 0x100-0x17F 0x258 0x4258 0x8258 0xc258
* Pages 0x180-0x1FF 0x268 0x4268 0x8268 0xc268
*
* The value written has bit 7 set to enable EMS, reset to
* disable it.
*
* So:
* OUT 0x208, 0x80 will page in the first 16k page at 0xD0000.
* OUT 0x208, 0x00 will page out EMS, leaving nothing at 0xD0000.
* OUT 0x4208, 0x80 will page in the first 16k page at 0xD4000.
* OUT 0x218, 0x80 will page in the 129th 16k page at 0xD0000.
* etc.
*
* To use EMS from DOS, you will need the Toshiba EMS driver
* (TOSHEMM.ZIP). This supports the above system, plus further
* ranges of ports at 0x_2A8, 0x_2B8, 0x_2C8.
*
* Features not implemented:
* > Four video fonts.
* > BIOS-controlled mapping of serial ports to IRQs.
* > Custom keyboard controller. This has a number of extra
* commands in the 0xB0-0xBC range, for such things as turbo
* on/off, and switching the keyboard between AT and PS/2
* modes. Currently I have only implemented command 0xBB,
* so that self-test completes successfully. Commands include:
*
* 0xB0: Turbo on
* 0xB1: Turbo off
* 0xB2: Internal display on?
* 0xB3: Internal display off?
* 0xB5: Get settings byte (bottom bit is color/mono setting)
* 0xB6: Set settings byte
* 0xB7: Behave as 101-key PS/2 keyboard
* 0xB8: Behave as 84-key AT keyboard
* 0xBB: Return a byte, bit 2 is Fn key state, other bits unknown.
*
* The other main I/O port needed to POST is:
* 0x8084: System control.
* Top 3 bits give command, bottom 5 bits give parameters.
* 000 => set serial port IRQ / addresses
* bit 4: IRQ5 serial port base: 1 => 0x338, 0 => 0x3E8
* bits 3, 2, 0 specify serial IRQs for COM1, COM2, COM3:
* 00 0 => 4, 3, 5
* 00 1 => 4, 5, 3
* 01 0 => 3, 4, 5
* 01 1 => 3, 5, 4
* 10 0 => 4, -, 3
* 10 1 => 3, -, 4
* 010 => set memory mappings
* bit 4 set if upper RAM is XMS
* bit 3 enable add-on memory boards beyond 5Mb?
* bit 2 set for 640k sysram, clear for 512k sysram
* bit 1 enable mainboard XMS
* bit 0 enable mainboard EMS
* 100 => set parallel mode / LCD settings
* bit 4 set for bidirectional parallel port
* bit 3 set to disable internal CGA
* bit 2 set for single-pixel LCD font
* bits 0,1 for display font
*
* Version: @(#)m_at_t3100e.c 1.0.3 2018/03/05
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* Sarah Walker, <tommowalker@tommowalker.co.uk>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
* Copyright 2008-2018 Sarah Walker.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "../86box.h"
#include "../io.h"
#include "../mouse.h"
@@ -13,124 +159,18 @@
#include "../cpu/cpu.h"
#include "../floppy/fdd.h"
#include "../floppy/fdc.h"
#include "../video/vid_t3100e.h"
#include "machine.h"
#include "m_at_t3100e.h"
/* The Toshiba 3100e is a 286-based portable.
*
* To bring up the BIOS setup screen hold down the 'Fn' key on booting
*
* Memory management
* ~~~~~~~~~~~~~~~~~
*
* Motherboard memory is divided into:
* - Conventional memory: Either 512k or 640k
* - Upper memory: Either 512k or 384k, depending on amount of
* conventional memory. Upper memory can be
* used either as EMS or XMS.
* - High memory: 0-4Mb, depending on RAM installed. The BIOS
* setup screen allows some or all of this to be
* used as EMS; the remainder is XMS.
*
* Additional memory (either EMS or XMS) can also be provided by ISA
* expansion cards.
*
* Under test in PCEM, the BIOS will boot with up to 65368Kb of memory in
* total (16Mb less 16k). However it will give an error with RAM sizes
* above 8Mb, if any of the high memory is allocated as EMS, because the
* builtin EMS page registers can only access up to 8Mb.
*
* Memory is controlled by writes to I/O port 8084h:
* Bit 7: Always 0 }
* Bit 6: Always 1 } These bits select which motherboard function to
* Bit 5: Always 0 } access.
* Bit 4: Set to treat upper RAM as XMS
* Bit 3: Enable external RAM boards?
* Bit 2: Set for 640k conventional memory, clear for 512k
* Bit 1: Enable RAM beyond 1Mb.
* Bit 0: Enable EMS.
*
* The last value written to this port is saved at 0040:0093h, and in
* CMOS memory at offset 0x37. If the top bit of the CMOS byte is set,
* then high memory is being provided by an add-on card rather than the
* mainboard; accordingly, the BIOS will not allow high memory to be
* used as EMS.
*
* EMS is controlled by 16 page registers:
*
* Page mapped at 0xD000 0xD400 0xD800 0xDC00
* ------------------------------------------------------
* Pages 0x00-0x7F 0x208 0x4208 0x8208 0xc208
* Pages 0x80-0xFF 0x218 0x4218 0x8218 0xc218
* Pages 0x100-0x17F 0x258 0x4258 0x8258 0xc258
* Pages 0x180-0x1FF 0x268 0x4268 0x8268 0xc268
*
* The value written has bit 7 set to enable EMS, reset to disable it.
*
* So: OUT 0x208, 0x80 will page in the first 16k page at 0xD0000.
* OUT 0x208, 0x00 will page out EMS, leaving nothing at 0xD0000.
* OUT 0x4208, 0x80 will page in the first 16k page at 0xD4000.
* OUT 0x218, 0x80 will page in the 129th 16k page at 0xD0000.
*
* etc.
*
* To use EMS from DOS, you will need the Toshiba EMS driver (TOSHEMM.ZIP).
* This supports the above system, plus further ranges of ports at
* 0x_2A8, 0x_2B8, 0x_2C8.
*
*/
static const int t3100e_log = 0;
extern uint8_t *ram; /* Physical RAM */
/* Features not implemented:
* > Four video fonts.
* > BIOS-controlled mapping of serial ports to IRQs.
* > Custom keyboard controller. This has a number of extra commands in the
* 0xB0-0xBC range, for such things as turbo on/off, and switching the
* keyboard between AT and PS/2 modes. Currently I have only implemented
* command 0xBB, so that self-test completes successfully. Commands include:
*
* 0xB0: Turbo on
* 0xB1: Turbo off
* 0xB2: Internal display on?
* 0xB3: Internal display off?
* 0xB5: Get settings byte (bottom bit is colour / mono setting)
* 0xB6: Set settings byte
* 0xB7: Behave as 101-key PS/2 keyboard
* 0xB8: Behave as 84-key AT keyboard
* 0xBB: Return a byte, bit 2 is Fn key state, other bits unknown.
*
* The other main I/O port needed to POST is:
* 0x8084: System control.
* Top 3 bits give command, bottom 5 bits give parameters.
* 000 => set serial port IRQ / addresses
* bit 4: IRQ5 serial port base: 1 => 0x338, 0 => 0x3E8
* bits 3, 2, 0 specify serial IRQs for COM1, COM2, COM3:
* 00 0 => 4, 3, 5
* 00 1 => 4, 5, 3
* 01 0 => 3, 4, 5
* 01 1 => 3, 5, 4
* 10 0 => 4, -, 3
* 10 1 => 3, -, 4
* 010 => set memory mappings
* bit 4 set if upper RAM is XMS
* bit 3 enable add-on memory boards beyond 5Mb?
* bit 2 set for 640k sysram, clear for 512k sysram
* bit 1 enable mainboard XMS
* bit 0 enable mainboard EMS
* 100 => set parallel mode / LCD settings
* bit 4 set for bidirectional parallel port
* bit 3 set to disable internal CGA
* bit 2 set for single-pixel LCD font
* bits 0,1 for display font
*/
void at_init();
static const int t3100e_log = 0;
/* The T3100e motherboard can (and does) dynamically reassign RAM between
* conventional, XMS and EMS. This translates to monkeying with the mappings.
*/

View File

@@ -1,7 +1,58 @@
void t3100e_notify_set(uint8_t value);
void t3100e_display_set(uint8_t value);
uint8_t t3100e_display_get();
uint8_t t3100e_config_get();
void t3100e_turbo_set(uint8_t value);
uint8_t t3100e_mono_get();
void t3100e_mono_set(uint8_t value);
/*
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Definitions for the Toshiba T3100e system.
*
* Version: @(#)m_at_t3100e.h 1.0.2 2018/03/05
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* Sarah Walker, <tommowalker@tommowalker.co.uk>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
* Copyright 2008-2018 Sarah Walker.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#ifndef MACHINE_T3100E_H
# define MACHINE_T3100E_H
extern device_t t3100e_device;
extern void t3100e_notify_set(uint8_t value);
extern void t3100e_display_set(uint8_t value);
extern uint8_t t3100e_display_get(void);
extern uint8_t t3100e_config_get(void);
extern void t3100e_turbo_set(uint8_t value);
extern uint8_t t3100e_mono_get(void);
extern void t3100e_mono_set(uint8_t value);
extern void t3100e_video_options_set(uint8_t options);
extern void t3100e_display_set(uint8_t internal);
#endif /*MACHINE_T3100E_H*/

View File

@@ -0,0 +1,763 @@
/*
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Implementation of the Toshiba 3100e plasma display.
* This display has a fixed 640x400 resolution.
*
* T3100e CRTC regs (from the ROM):
*
* Selecting a character height of 3 seems to be sufficient to
* convert the 640x200 graphics mode to 640x400 (and, by
* analogy, 320x200 to 320x400).
*
* Horiz-----> Vert------> I ch
* 38 28 2D 0A 1F 06 19 1C 02 07 06 07 CO40
* 71 50 5A 0A 1F 06 19 1C 02 07 06 07 CO80
* 38 28 2D 0A 7F 06 64 70 02 01 06 07 Graphics
* 61 50 52 0F 19 06 19 19 02 0D 0B 0C MONO
* 2D 28 22 0A 67 00 64 67 02 03 06 07 640x400
*
* Version: @(#)m_at_t3100e_vid.c 1.0.3 2018/03/07
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* Sarah Walker, <tommowalker@tommowalker.co.uk>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
* Copyright 2008-2018 Sarah Walker.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "../86box.h"
#include "../device.h"
#include "../io.h"
#include "../mem.h"
#include "../timer.h"
#include "../cpu/cpu.h"
#include "../video/video.h"
#include "../video/vid_cga.h"
#include "m_at_t3100e.h"
#define T3100E_XSIZE 640
#define T3100E_YSIZE 400
/* Mapping of attributes to colours */
static uint32_t amber, black;
static uint8_t boldcols[256]; /* Which attributes use the bold font */
static uint32_t blinkcols[256][2];
static uint32_t normcols[256][2];
/* Video options set by the motherboard; they will be picked up by the card
* on the next poll.
*
* Bit 3: Disable built-in video (for add-on card)
* Bit 2: Thin font
* Bits 0,1: Font set (not currently implemented)
*/
static uint8_t st_video_options;
static int8_t st_display_internal = -1;
void t3100e_video_options_set(uint8_t options)
{
st_video_options = options;
}
void t3100e_display_set(uint8_t internal)
{
st_display_internal = internal;
}
uint8_t t3100e_display_get()
{
return st_display_internal;
}
typedef struct t3100e_t
{
mem_mapping_t mapping;
cga_t cga; /* The CGA is used for the external
* display; most of its registers are
* ignored by the plasma display. */
int font; /* Current font, 0-3 */
int enabled; /* Hardware enabled, 0 or 1 */
int internal; /* Using internal display? */
uint8_t attrmap; /* Attribute mapping register */
int dispontime, dispofftime;
int linepos, displine;
int vc;
int dispon;
int vsynctime;
uint8_t video_options;
uint8_t *vram;
} t3100e_t;
void t3100e_recalctimings(t3100e_t *t3100e);
void t3100e_write(uint32_t addr, uint8_t val, void *p);
uint8_t t3100e_read(uint32_t addr, void *p);
void t3100e_recalcattrs(t3100e_t *t3100e);
void t3100e_out(uint16_t addr, uint8_t val, void *p)
{
t3100e_t *t3100e = (t3100e_t *)p;
switch (addr)
{
/* Emulated CRTC, register select */
case 0x3d0: case 0x3d2: case 0x3d4: case 0x3d6:
cga_out(addr, val, &t3100e->cga);
break;
/* Emulated CRTC, value */
case 0x3d1: case 0x3d3: case 0x3d5: case 0x3d7:
/* Register 0x12 controls the attribute mappings for the
* plasma screen. */
if (t3100e->cga.crtcreg == 0x12)
{
t3100e->attrmap = val;
t3100e_recalcattrs(t3100e);
return;
}
cga_out(addr, val, &t3100e->cga);
t3100e_recalctimings(t3100e);
return;
/* CGA control register */
case 0x3D8:
cga_out(addr, val, &t3100e->cga);
return;
/* CGA colour register */
case 0x3D9:
cga_out(addr, val, &t3100e->cga);
return;
}
}
uint8_t t3100e_in(uint16_t addr, void *p)
{
t3100e_t *t3100e = (t3100e_t *)p;
uint8_t val;
switch (addr)
{
case 0x3d1: case 0x3d3: case 0x3d5: case 0x3d7:
if (t3100e->cga.crtcreg == 0x12)
{
val = t3100e->attrmap & 0x0F;
if (t3100e->internal) val |= 0x30; /* Plasma / CRT */
return val;
}
}
return cga_in(addr, &t3100e->cga);
}
void t3100e_write(uint32_t addr, uint8_t val, void *p)
{
t3100e_t *t3100e = (t3100e_t *)p;
egawrites++;
// pclog("CGA_WRITE %04X %02X\n", addr, val);
t3100e->vram[addr & 0x7fff] = val;
cycles -= 4;
}
uint8_t t3100e_read(uint32_t addr, void *p)
{
t3100e_t *t3100e = (t3100e_t *)p;
egareads++;
cycles -= 4;
// pclog("CGA_READ %04X\n", addr);
return t3100e->vram[addr & 0x7fff];
}
void t3100e_recalctimings(t3100e_t *t3100e)
{
double disptime;
double _dispontime, _dispofftime;
if (!t3100e->internal)
{
cga_recalctimings(&t3100e->cga);
return;
}
disptime = 651;
_dispontime = 640;
_dispofftime = disptime - _dispontime;
t3100e->dispontime = (int)(_dispontime * (1 << TIMER_SHIFT));
t3100e->dispofftime = (int)(_dispofftime * (1 << TIMER_SHIFT));
}
/* Draw a row of text in 80-column mode */
void t3100e_text_row80(t3100e_t *t3100e)
{
uint32_t cols[2];
int x, c;
uint8_t chr, attr;
int drawcursor;
int cursorline;
int bold;
int blink;
uint16_t addr;
uint8_t sc;
uint16_t ma = (t3100e->cga.crtc[13] | (t3100e->cga.crtc[12] << 8)) & 0x7fff;
uint16_t ca = (t3100e->cga.crtc[15] | (t3100e->cga.crtc[14] << 8)) & 0x7fff;
sc = (t3100e->displine) & 15;
addr = ((ma & ~1) + (t3100e->displine >> 4) * 80) * 2;
ma += (t3100e->displine >> 4) * 80;
if ((t3100e->cga.crtc[10] & 0x60) == 0x20)
{
cursorline = 0;
}
else
{
cursorline = ((t3100e->cga.crtc[10] & 0x0F)*2 <= sc) &&
((t3100e->cga.crtc[11] & 0x0F)*2 >= sc);
}
for (x = 0; x < 80; x++)
{
chr = t3100e->vram[(addr + 2 * x) & 0x7FFF];
attr = t3100e->vram[(addr + 2 * x + 1) & 0x7FFF];
drawcursor = ((ma == ca) && cursorline &&
(t3100e->cga.cgamode & 8) && (t3100e->cga.cgablink & 16));
blink = ((t3100e->cga.cgablink & 16) && (t3100e->cga.cgamode & 0x20) &&
(attr & 0x80) && !drawcursor);
if (t3100e->video_options & 4)
bold = boldcols[attr] ? chr + 256 : chr;
else
bold = boldcols[attr] ? chr : chr + 256;
bold += 512 * (t3100e->video_options & 3);
if (t3100e->cga.cgamode & 0x20) /* Blink */
{
cols[1] = blinkcols[attr][1];
cols[0] = blinkcols[attr][0];
if (blink) cols[1] = cols[0];
}
else
{
cols[1] = normcols[attr][1];
cols[0] = normcols[attr][0];
}
if (drawcursor)
{
for (c = 0; c < 8; c++)
{
((uint32_t *)buffer32->line[t3100e->displine])[(x << 3) + c] = cols[(fontdatm[bold][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ (amber ^ black);
}
}
else
{
for (c = 0; c < 8; c++)
((uint32_t *)buffer32->line[t3100e->displine])[(x << 3) + c] = cols[(fontdatm[bold][sc] & (1 << (c ^ 7))) ? 1 : 0];
}
++ma;
}
}
/* Draw a row of text in 40-column mode */
void t3100e_text_row40(t3100e_t *t3100e)
{
uint32_t cols[2];
int x, c;
uint8_t chr, attr;
int drawcursor;
int cursorline;
int bold;
int blink;
uint16_t addr;
uint8_t sc;
uint16_t ma = (t3100e->cga.crtc[13] | (t3100e->cga.crtc[12] << 8)) & 0x7fff;
uint16_t ca = (t3100e->cga.crtc[15] | (t3100e->cga.crtc[14] << 8)) & 0x7fff;
sc = (t3100e->displine) & 15;
addr = ((ma & ~1) + (t3100e->displine >> 4) * 40) * 2;
ma += (t3100e->displine >> 4) * 40;
if ((t3100e->cga.crtc[10] & 0x60) == 0x20)
{
cursorline = 0;
}
else
{
cursorline = ((t3100e->cga.crtc[10] & 0x0F)*2 <= sc) &&
((t3100e->cga.crtc[11] & 0x0F)*2 >= sc);
}
for (x = 0; x < 40; x++)
{
chr = t3100e->vram[(addr + 2 * x) & 0x7FFF];
attr = t3100e->vram[(addr + 2 * x + 1) & 0x7FFF];
drawcursor = ((ma == ca) && cursorline &&
(t3100e->cga.cgamode & 8) && (t3100e->cga.cgablink & 16));
blink = ((t3100e->cga.cgablink & 16) && (t3100e->cga.cgamode & 0x20) &&
(attr & 0x80) && !drawcursor);
if (t3100e->video_options & 4)
bold = boldcols[attr] ? chr + 256 : chr;
else bold = boldcols[attr] ? chr : chr + 256;
bold += 512 * (t3100e->video_options & 3);
if (t3100e->cga.cgamode & 0x20) /* Blink */
{
cols[1] = blinkcols[attr][1];
cols[0] = blinkcols[attr][0];
if (blink) cols[1] = cols[0];
}
else
{
cols[1] = normcols[attr][1];
cols[0] = normcols[attr][0];
}
if (drawcursor)
{
for (c = 0; c < 8; c++)
{
((uint32_t *)buffer32->line[t3100e->displine])[(x << 4) + c*2] =
((uint32_t *)buffer32->line[t3100e->displine])[(x << 4) + c*2 + 1] = cols[(fontdatm[bold][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ (amber ^ black);
}
}
else
{
for (c = 0; c < 8; c++)
{
((uint32_t *)buffer32->line[t3100e->displine])[(x << 4) + c*2] =
((uint32_t *)buffer32->line[t3100e->displine])[(x << 4) + c*2+1] = cols[(fontdatm[bold][sc] & (1 << (c ^ 7))) ? 1 : 0];
}
}
++ma;
}
}
/* Draw a line in CGA 640x200 or T3100e 640x400 mode */
void t3100e_cgaline6(t3100e_t *t3100e)
{
int x, c;
uint8_t dat;
uint32_t ink = 0;
uint16_t addr;
uint32_t fg = (t3100e->cga.cgacol & 0x0F) ? amber : black;
uint32_t bg = black;
uint16_t ma = (t3100e->cga.crtc[13] | (t3100e->cga.crtc[12] << 8)) & 0x7fff;
if (t3100e->cga.crtc[9] == 3) /* 640*400 */
{
addr = ((t3100e->displine) & 1) * 0x2000 +
((t3100e->displine >> 1) & 1) * 0x4000 +
(t3100e->displine >> 2) * 80 +
((ma & ~1) << 1);
}
else
{
addr = ((t3100e->displine >> 1) & 1) * 0x2000 +
(t3100e->displine >> 2) * 80 +
((ma & ~1) << 1);
}
for (x = 0; x < 80; x++)
{
dat = t3100e->vram[addr & 0x7FFF];
addr++;
for (c = 0; c < 8; c++)
{
ink = (dat & 0x80) ? fg : bg;
if (!(t3100e->cga.cgamode & 8)) ink = black;
((uint32_t *)buffer32->line[t3100e->displine])[x*8+c] = ink;
dat = dat << 1;
}
}
}
/* Draw a line in CGA 320x200 mode. Here the CGA colours are converted to
* dither patterns: colour 1 to 25% grey, colour 2 to 50% grey */
void t3100e_cgaline4(t3100e_t *t3100e)
{
int x, c;
uint8_t dat, pattern;
uint32_t ink0 = 0, ink1 = 0;
uint16_t addr;
uint16_t ma = (t3100e->cga.crtc[13] | (t3100e->cga.crtc[12] << 8)) & 0x7fff;
if (t3100e->cga.crtc[9] == 3) /* 320*400 undocumented */
{
addr = ((t3100e->displine) & 1) * 0x2000 +
((t3100e->displine >> 1) & 1) * 0x4000 +
(t3100e->displine >> 2) * 80 +
((ma & ~1) << 1);
}
else /* 320*200 */
{
addr = ((t3100e->displine >> 1) & 1) * 0x2000 +
(t3100e->displine >> 2) * 80 +
((ma & ~1) << 1);
}
for (x = 0; x < 80; x++)
{
dat = t3100e->vram[addr & 0x7FFF];
addr++;
for (c = 0; c < 4; c++)
{
pattern = (dat & 0xC0) >> 6;
if (!(t3100e->cga.cgamode & 8)) pattern = 0;
switch (pattern & 3)
{
case 0: ink0 = ink1 = black; break;
case 1: if (t3100e->displine & 1)
{
ink0 = black; ink1 = black;
}
else
{
ink0 = amber; ink1 = black;
}
break;
case 2: if (t3100e->displine & 1)
{
ink0 = black; ink1 = amber;
}
else
{
ink0 = amber; ink1 = black;
}
break;
case 3: ink0 = ink1 = amber; break;
}
((uint32_t *)buffer32->line[t3100e->displine])[x*8+2*c] = ink0;
((uint32_t *)buffer32->line[t3100e->displine])[x*8+2*c+1] = ink1;
dat = dat << 2;
}
}
}
void t3100e_poll(void *p)
{
t3100e_t *t3100e = (t3100e_t *)p;
if (t3100e->video_options != st_video_options)
{
t3100e->video_options = st_video_options;
if (t3100e->video_options & 8) /* Disable internal CGA */
mem_mapping_disable(&t3100e->mapping);
else mem_mapping_enable(&t3100e->mapping);
/* Set the font used for the external display */
t3100e->cga.fontbase = (512 * (t3100e->video_options & 3))
+ ((t3100e->video_options & 4) ? 256 : 0);
}
/* Switch between internal plasma and external CRT display. */
if (st_display_internal != -1 && st_display_internal != t3100e->internal)
{
t3100e->internal = st_display_internal;
t3100e_recalctimings(t3100e);
}
if (!t3100e->internal)
{
cga_poll(&t3100e->cga);
return;
}
if (!t3100e->linepos)
{
t3100e->cga.vidtime += t3100e->dispofftime;
t3100e->cga.cgastat |= 1;
t3100e->linepos = 1;
if (t3100e->dispon)
{
if (t3100e->displine == 0)
{
video_wait_for_buffer();
}
/* Graphics */
if (t3100e->cga.cgamode & 0x02)
{
if (t3100e->cga.cgamode & 0x10)
t3100e_cgaline6(t3100e);
else t3100e_cgaline4(t3100e);
}
else
if (t3100e->cga.cgamode & 0x01) /* High-res text */
{
t3100e_text_row80(t3100e);
}
else
{
t3100e_text_row40(t3100e);
}
}
t3100e->displine++;
/* Hardcode a fixed refresh rate and VSYNC timing */
if (t3100e->displine == 400) /* Start of VSYNC */
{
t3100e->cga.cgastat |= 8;
t3100e->dispon = 0;
}
if (t3100e->displine == 416) /* End of VSYNC */
{
t3100e->displine = 0;
t3100e->cga.cgastat &= ~8;
t3100e->dispon = 1;
}
}
else
{
if (t3100e->dispon)
{
t3100e->cga.cgastat &= ~1;
}
t3100e->cga.vidtime += t3100e->dispontime;
t3100e->linepos = 0;
if (t3100e->displine == 400)
{
/* Hardcode 640x400 window size */
if (T3100E_XSIZE != xsize || T3100E_YSIZE != ysize)
{
xsize = T3100E_XSIZE;
ysize = T3100E_YSIZE;
if (xsize < 64) xsize = 656;
if (ysize < 32) ysize = 200;
set_screen_size(xsize, ysize);
}
video_blit_memtoscreen(0, 0, 0, ysize, xsize, ysize);
frames++;
/* Fixed 640x400 resolution */
video_res_x = T3100E_XSIZE;
video_res_y = T3100E_YSIZE;
if (t3100e->cga.cgamode & 0x02)
{
if (t3100e->cga.cgamode & 0x10)
video_bpp = 1;
else video_bpp = 2;
}
else video_bpp = 0;
t3100e->cga.cgablink++;
}
}
}
void t3100e_recalcattrs(t3100e_t *t3100e)
{
int n;
/* val behaves as follows:
* Bit 0: Attributes 01-06, 08-0E are inverse video
* Bit 1: Attributes 01-06, 08-0E are bold
* Bit 2: Attributes 11-16, 18-1F, 21-26, 28-2F ... F1-F6, F8-FF
* are inverse video
* Bit 3: Attributes 11-16, 18-1F, 21-26, 28-2F ... F1-F6, F8-FF
* are bold */
/* Set up colours */
amber = makecol(0xf7, 0x7C, 0x34);
black = makecol(0x17, 0x0C, 0x00);
/* Initialise the attribute mapping. Start by defaulting everything
* to black on amber, and with bold set by bit 3 */
for (n = 0; n < 256; n++)
{
boldcols[n] = (n & 8) != 0;
blinkcols[n][0] = normcols[n][0] = amber;
blinkcols[n][1] = normcols[n][1] = black;
}
/* Colours 0x11-0xFF are controlled by bits 2 and 3 of the
* passed value. Exclude x0 and x8, which are always black on
* amber. */
for (n = 0x11; n <= 0xFF; n++)
{
if ((n & 7) == 0) continue;
if (t3100e->attrmap & 4) /* Inverse */
{
blinkcols[n][0] = normcols[n][0] = amber;
blinkcols[n][1] = normcols[n][1] = black;
}
else /* Normal */
{
blinkcols[n][0] = normcols[n][0] = black;
blinkcols[n][1] = normcols[n][1] = amber;
}
if (t3100e->attrmap & 8) boldcols[n] = 1; /* Bold */
}
/* Set up the 01-0E range, controlled by bits 0 and 1 of the
* passed value. When blinking is enabled this also affects 81-8E. */
for (n = 0x01; n <= 0x0E; n++)
{
if (n == 7) continue;
if (t3100e->attrmap & 1)
{
blinkcols[n][0] = normcols[n][0] = amber;
blinkcols[n][1] = normcols[n][1] = black;
blinkcols[n+128][0] = amber;
blinkcols[n+128][1] = black;
}
else
{
blinkcols[n][0] = normcols[n][0] = black;
blinkcols[n][1] = normcols[n][1] = amber;
blinkcols[n+128][0] = black;
blinkcols[n+128][1] = amber;
}
if (t3100e->attrmap & 2) boldcols[n] = 1;
}
/* Colours 07 and 0F are always amber on black. If blinking is
* enabled so are 87 and 8F. */
for (n = 0x07; n <= 0x0F; n += 8)
{
blinkcols[n][0] = normcols[n][0] = black;
blinkcols[n][1] = normcols[n][1] = amber;
blinkcols[n+128][0] = black;
blinkcols[n+128][1] = amber;
}
/* When not blinking, colours 81-8F are always amber on black. */
for (n = 0x81; n <= 0x8F; n ++)
{
normcols[n][0] = black;
normcols[n][1] = amber;
boldcols[n] = (n & 0x08) != 0;
}
/* Finally do the ones which are solid black. These differ between
* the normal and blinking mappings */
for (n = 0; n <= 0xFF; n += 0x11)
{
normcols[n][0] = normcols[n][1] = black;
}
/* In the blinking range, 00 11 22 .. 77 and 80 91 A2 .. F7 are black */
for (n = 0; n <= 0x77; n += 0x11)
{
blinkcols[n][0] = blinkcols[n][1] = black;
blinkcols[n+128][0] = blinkcols[n+128][1] = black;
}
}
void *t3100e_init(device_t *info)
{
t3100e_t *t3100e = malloc(sizeof(t3100e_t));
memset(t3100e, 0, sizeof(t3100e_t));
cga_init(&t3100e->cga);
t3100e->internal = 1;
/* 32k video RAM */
t3100e->vram = malloc(0x8000);
timer_add(t3100e_poll, &t3100e->cga.vidtime, TIMER_ALWAYS_ENABLED, t3100e);
/* Occupy memory between 0xB8000 and 0xBFFFF */
mem_mapping_add(&t3100e->mapping, 0xb8000, 0x8000, t3100e_read, NULL, NULL, t3100e_write, NULL, NULL, NULL, 0, t3100e);
/* Respond to CGA I/O ports */
io_sethandler(0x03d0, 0x000c, t3100e_in, NULL, NULL, t3100e_out, NULL, NULL, t3100e);
/* Default attribute mapping is 4 */
t3100e->attrmap = 4;
t3100e_recalcattrs(t3100e);
/* Start off in 80x25 text mode */
t3100e->cga.cgastat = 0xF4;
t3100e->cga.vram = t3100e->vram;
t3100e->enabled = 1;
t3100e->video_options = 0xFF;
return t3100e;
}
void t3100e_close(void *p)
{
t3100e_t *t3100e = (t3100e_t *)p;
free(t3100e->vram);
free(t3100e);
}
void t3100e_speed_changed(void *p)
{
t3100e_t *t3100e = (t3100e_t *)p;
t3100e_recalctimings(t3100e);
}
device_t t3100e_device =
{
"Toshiba T3100e",
0,
0,
t3100e_init,
t3100e_close,
NULL,
NULL,
t3100e_speed_changed,
NULL,
NULL
};

View File

@@ -1,10 +1,10 @@
/*
* 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.
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the 86Box distribution.
* This file is part of the VARCem Project.
*
* Implementation of the Schneider EuroPC system.
*
@@ -27,8 +27,7 @@
* f000:ecc5 801a video setup error
* f000:d6c9 copyright output
* f000:e1b7
* f000:e1be DI bits set mean output text!!!,
* (801a)
* f000:e1be DI bits set mean output text!!! (801a)
* f000: 0x8000 output
* 1 rtc error
* 2 rtc time or date error
@@ -53,21 +52,23 @@
* output cl at jim 0xa
* write ah hinibble as lownibble into jim 0xa
* write ah lownibble into jim 0xa
* f000:ef66 RTC read reg cl
* f000:ef66 RTC read reg cl
* polls until jim 0xa is zero,
* output cl at jim 0xa
* read low 4 nibble at jim 0xa
* read low 4 nibble at jim 0xa
* return first nibble<<4|second nibble in ah
* f000:f046 seldom compares ret
* f000:fe87 0 -> ds
* 0000:0469 bit 0: b0000 memory available
* f000:f046 seldom compares ret
* f000:fe87 0 -> ds
*
* Memory:
* 0000:0469 bit 0: b0000 memory available
* bit 1: b8000 memory available
* 0000:046a: 00 jim 250 01 jim 350
* 0000:046a: 00 jim 250 01 jim 350
*
* WARNING THIS IS A WORK-IN-PROGRESS MODULE. USE AT OWN RISK.
*
* Version: @(#)europc.c 1.0.6 2018/01/16
* Version: @(#)europc.c 1.0.2 2018/03/11
*
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
*
@@ -77,12 +78,43 @@
* input from people with real EuroPC hardware.
*
* Copyright 2017,2018 Fred N. van Kempen.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include <time.h>
#include "../86box.h"
#include "../io.h"
#include "../nmi.h"
@@ -90,17 +122,17 @@
#include "../rom.h"
#include "../nvr.h"
#include "../device.h"
#include "../floppy/fdd.h"
#include "../floppy/fdc.h"
#include "../disk/hdc.h"
#include "../keyboard.h"
#include "../mouse.h"
#include "../game/gameport.h"
#include "../floppy/fdd.h"
#include "../floppy/fdc.h"
#include "../disk/hdc.h"
#include "../video/video.h"
#include "machine.h"
#define EUROPC_DEBUG 1 /* current debugging level */
#define EUROPC_DEBUG 0 /* current debugging level */
/* M3002 RTC chip registers. */
@@ -109,7 +141,7 @@
#define MRTC_HOURS 0x02 /* BCD, 00-23 */
#define MRTC_DAYS 0x03 /* BCD, 01-31 */
#define MRTC_MONTHS 0x04 /* BCD, 01-12 */
#define MRTC_YEARS 0x05 /* BCD, 00-99 (2017 is 0x17) */
#define MRTC_YEARS 0x05 /* BCD, 00-99 (year only) */
#define MRTC_WEEKDAY 0x06 /* BCD, 01-07 */
#define MRTC_WEEKNO 0x07 /* BCD, 01-52 */
#define MRTC_CONF_A 0x08 /* EuroPC config, binary */
@@ -121,127 +153,232 @@
#define MRTC_CHECK_HI 0x0e /* Checksum, high byte */
#define MRTC_CTRLSTAT 0x0f /* RTC control/status, binary */
typedef struct {
uint16_t jim; /* JIM base address */
uint8_t regs[16]; /* JIM internal regs (8) */
struct {
uint8_t dat[16];
uint8_t stat;
uint8_t addr;
} rtc;
nvr_t nvr; /* 86Box NVR */
} vm_t;
nvr_t nvr; /* NVR */
uint8_t nvr_stat;
uint8_t nvr_addr;
} europc_t;
static vm_t *vm = NULL;
static europc_t europc;
/* Load the relevant portion of the NVR to disk. */
static int8_t
load_nvr(wchar_t *fn)
{
FILE *f;
if (vm == NULL) return(0);
f = nvr_fopen(fn, L"rb");
if (f != NULL) {
(void)fread(vm->rtc.dat, 1, 16, f);
(void)fclose(f);
pclog("EuroPC: CMOS data loaded from file %ls !\n", fn);
return(1);
}
pclog("EuroPC: unable to load NVR !\n");
return(0);
}
/* Save the relevant portion of the NVR to disk. */
static int8_t
save_nvr(wchar_t *fn)
{
FILE *f;
if (vm == NULL) return(0);
f = nvr_fopen(fn, L"wb");
if (f != NULL) {
(void)fwrite(vm->rtc.dat, 1, 16, f);
(void)fclose(f);
return(1);
}
pclog("EuroPC: unable to save NVR !\n");
return(0);
}
/* This is called every second through the NVR/RTC hook. */
/*
* This is called every second through the NVR/RTC hook.
*
* We fake a 'running' RTC by updating its registers on
* each passing second. Not exactly accurate, but good
* enough.
*
* Note that this code looks nasty because of all the
* BCD to decimal vv going on.
*
* FIXME: should we mark NVR as dirty?
*/
static void
rtc_hook(nvr_t *nvr)
rtc_tick(nvr_t *nvr)
{
#if 0
int month, year;
uint8_t *regs;
int mon, yr;
sys->rtc.dat[0] = bcd_adjust(sys->rtc.dat[0]+1);
if (sys->rtc.dat[0] >= 0x60) {
sys->rtc.dat[0] = 0;
sys->rtc.dat[1] = bcd_adjust(sys->rtc.dat[1]+1);
if (sys->rtc.dat[1] >= 0x60) {
sys->rtc.dat[1] = 0;
sys->rtc.dat[2] = bcd_adjust(sys->rtc.dat[2]+1);
if (sys->rtc.dat[2] >= 0x24) {
sys->rtc.dat[2] = 0;
sys->uropc_rtc.data[3]=bcd_adjust(sys->uropc_rtc.data[3]+1);
month = bcd_2_dec(sys->rtc.dat[4]);
/* Only if RTC is running.. */
regs = nvr->regs;
if (! (regs[MRTC_CTRLSTAT] & 0x01)) return;
/* Save for julian_days_in_month_calculation. */
year = bcd_2_dec(sys->rtc.dat[5])+2000;
if (sys->rtc.dat[3] > gregorian_days_in_month(month, year)) {
sys->rtc.dat[3] = 1;
sys->rtc.dat[4] = bcd_adjust(sys->rtc.dat[4]+1);
if (sys->rtc.dat[4]>0x12) {
sys->rtc.dat[4] = 1;
sys->rtc.dat[5] = bcd_adjust(sys->rtc.dat[5]+1)&0xff;
}
regs[MRTC_SECONDS] = RTC_BCDINC(nvr->regs[MRTC_SECONDS], 1);
if (regs[MRTC_SECONDS] >= RTC_BCD(60)) {
regs[MRTC_SECONDS] = RTC_BCD(0);
regs[MRTC_MINUTES] = RTC_BCDINC(regs[MRTC_MINUTES], 1);
if (regs[MRTC_MINUTES] >= RTC_BCD(60)) {
regs[MRTC_MINUTES] = RTC_BCD(0);
regs[MRTC_HOURS] = RTC_BCDINC(regs[MRTC_HOURS], 1);
if (regs[MRTC_HOURS] >= RTC_BCD(24)) {
regs[MRTC_HOURS] = RTC_BCD(0);
regs[MRTC_DAYS] = RTC_BCDINC(regs[MRTC_DAYS], 1);
mon = RTC_DCB(regs[MRTC_MONTHS]);
yr = RTC_DCB(regs[MRTC_YEARS]) + 1900;
if (RTC_DCB(regs[MRTC_DAYS]) > nvr_get_days(mon, yr)) {
regs[MRTC_DAYS] = RTC_BCD(1);
regs[MRTC_MONTHS] = RTC_BCDINC(regs[MRTC_MONTHS], 1);
if (regs[MRTC_MONTHS] > RTC_BCD(12)) {
regs[MRTC_MONTHS] = RTC_BCD(1);
regs[MRTC_YEARS] = RTC_BCDINC(regs[MRTC_YEARS], 1) & 0xff;
}
}
}
}
}
}
/* Get the current NVR time. */
static void
rtc_time_get(uint8_t *regs, struct tm *tm)
{
/* NVR is in BCD data mode. */
tm->tm_sec = RTC_DCB(regs[MRTC_SECONDS]);
tm->tm_min = RTC_DCB(regs[MRTC_MINUTES]);
tm->tm_hour = RTC_DCB(regs[MRTC_HOURS]);
tm->tm_wday = (RTC_DCB(regs[MRTC_WEEKDAY]) - 1);
tm->tm_mday = RTC_DCB(regs[MRTC_DAYS]);
tm->tm_mon = (RTC_DCB(regs[MRTC_MONTHS]) - 1);
tm->tm_year = RTC_DCB(regs[MRTC_YEARS]);
#if USE_Y2K
tm->tm_year += (RTC_DCB(regs[MRTC_CENTURY]) * 100) - 1900;
#endif
}
/* Set the current NVR time. */
static void
rtc_time_set(uint8_t *regs, struct tm *tm)
{
/* NVR is in BCD data mode. */
regs[MRTC_SECONDS] = RTC_BCD(tm->tm_sec);
regs[MRTC_MINUTES] = RTC_BCD(tm->tm_min);
regs[MRTC_HOURS] = RTC_BCD(tm->tm_hour);
regs[MRTC_WEEKDAY] = RTC_BCD(tm->tm_wday + 1);
regs[MRTC_DAYS] = RTC_BCD(tm->tm_mday);
regs[MRTC_MONTHS] = RTC_BCD(tm->tm_mon + 1);
regs[MRTC_YEARS] = RTC_BCD(tm->tm_year % 100);
#if USE_Y2K
regs[MRTC_CENTURY] = RTC_BCD((tm->tm_year+1900) / 100);
#endif
}
static void
rtc_start(nvr_t *nvr)
{
struct tm tm;
/* Initialize the internal and chip times. */
if (enable_sync) {
/* Use the internal clock's time. */
nvr_time_get(&tm);
rtc_time_set(nvr->regs, &tm);
} else {
/* Set the internal clock from the chip time. */
rtc_time_get(nvr->regs, &tm);
nvr_time_set(&tm);
}
#if 0
void europc_rtc_init(void)
{
europc_rtc.data[0xf]=1;
europc_rtc.timer = timer_alloc(europc_rtc_timer);
timer_adjust(europc_rtc.timer, 0, 0, 1.0);
}
/* Start the RTC - BIOS will do this. */
nvr->regs[MRTC_CTRLSTAT] = 0x01;
#endif
}
/* Create a valid checksum for the current NVR data. */
static uint8_t
rtc_checksum(uint8_t *ptr)
{
uint8_t sum;
int i;
/* Calculate all bytes with XOR. */
sum = 0x00;
for (i=MRTC_CONF_A; i<=MRTC_CONF_E; i++)
sum += ptr[i];
return(sum);
}
/* Reset the machine's NVR to a sane state. */
static void
rtc_reset(nvr_t *nvr)
{
/* Initialize the RTC to a known state. */
nvr->regs[MRTC_SECONDS] = RTC_BCD(0); /* seconds */
nvr->regs[MRTC_MINUTES] = RTC_BCD(0); /* minutes */
nvr->regs[MRTC_HOURS] = RTC_BCD(0); /* hours */
nvr->regs[MRTC_DAYS] = RTC_BCD(1); /* days */
nvr->regs[MRTC_MONTHS] = RTC_BCD(1); /* months */
nvr->regs[MRTC_YEARS] = RTC_BCD(80); /* years */
nvr->regs[MRTC_WEEKDAY] = RTC_BCD(1); /* weekday */
nvr->regs[MRTC_WEEKNO] = RTC_BCD(1); /* weekno */
/*
* EuroPC System Configuration:
*
* [A] unknown
*
* [B] 7 1 bootdrive extern
* 0 bootdribe intern
* 6:5 11 invalid hard disk type
* 10 hard disk installed, type 2
* 01 hard disk installed, type 1
* 00 hard disk not installed
* 4:3 11 invalid external drive type
* 10 external drive 720K
* 01 external drive 360K
* 00 external drive disabled
* 2 unknown
* 1:0 11 invalid internal drive type
* 10 internal drive 360K
* 01 internal drive 720K
* 00 internal drive disabled
*
* [C] 7:6 unknown
* 5 monitor detection OFF
* 4 unknown
* 3:2 11 illegal memory size
* 10 512K
* 01 256K
* 00 640K
* 1:0 11 illegal game port
* 10 gameport as mouse port
* 01 gameport as joysticks
* 00 gameport disabled
*
* [D] 7:6 10 9MHz CPU speed
* 01 7MHz CPU speed
* 00 4.77 MHz CPU
* 5 unknown
* 4 external: color, internal: mono
* 3 unknown
* 2 internal video ON
* 1:0 11 mono
* 10 color80
* 01 color40
* 00 special (EGA,VGA etc)
*
* [E] 7:4 unknown
* 3:0 country (00=Deutschland, 0A=ASCII)
*/
nvr->regs[MRTC_CONF_A] = 0x00; /* CONFIG A */
nvr->regs[MRTC_CONF_B] = 0x0A; /* CONFIG B */
nvr->regs[MRTC_CONF_C] = 0x28; /* CONFIG C */
nvr->regs[MRTC_CONF_D] = 0x12; /* CONFIG D */
nvr->regs[MRTC_CONF_E] = 0x0A; /* CONFIG E */
nvr->regs[MRTC_CHECK_LO] = 0x00; /* checksum (LO) */
nvr->regs[MRTC_CHECK_HI] = 0x00; /* checksum (HI) */
nvr->regs[MRTC_CTRLSTAT] = 0x01; /* status/control */
/* Generate a valid checksum. */
nvr->regs[MRTC_CHECK_LO] = rtc_checksum(nvr->regs);
}
/* Execute a JIM control command. */
static void
jim_action(vm_t *sys, uint8_t reg, uint8_t val)
jim_set(europc_t *sys, uint8_t reg, uint8_t val)
{
switch(reg) {
case 0: /* MISC control (WO) */
//pclog("EuroPC: write MISC = %02x\n", val);
// bit0: enable MOUSE
// bit1: enable joystick
break;
case 2: /* AGA control */
//pclog("EuroPC: write AGA = %02x\n", val);
if (! (val & 0x80)) {
/* Reset AGA. */
break;
@@ -276,7 +413,6 @@ jim_action(vm_t *sys, uint8_t reg, uint8_t val)
break;
case 4: /* CPU Speed control */
//pclog("EuroPC: write CPUCLK = %02x\n", val);
switch(val & 0xc0) {
case 0x00: /* 4.77 MHz */
// cpu_set_clockscale(0, 1.0/2);
@@ -304,7 +440,7 @@ jim_action(vm_t *sys, uint8_t reg, uint8_t val)
static void
jim_write(uint16_t addr, uint8_t val, void *priv)
{
vm_t *sys = (vm_t *)priv;
europc_t *sys = (europc_t *)priv;
uint8_t b;
#if EUROPC_DEBUG > 1
@@ -320,28 +456,30 @@ jim_write(uint16_t addr, uint8_t val, void *priv)
case 0x05:
case 0x06:
case 0x07:
jim_action(sys, (addr & 0x07), val);
jim_set(sys, (addr & 0x07), val);
break;
case 0x0A: /* M3002 RTC INDEX/DATA register */
switch(sys->rtc.stat) {
case 0x0a: /* M3002 RTC INDEX/DATA register */
switch(sys->nvr_stat) {
case 0: /* save index */
sys->rtc.addr = val & 0x0f;
sys->rtc.stat++;
sys->nvr_addr = val & 0x0f;
sys->nvr_stat++;
break;
case 1: /* save data HI nibble */
b = sys->rtc.dat[sys->rtc.addr] & 0x0f;
b = sys->nvr.regs[sys->nvr_addr] & 0x0f;
b |= (val << 4);
sys->rtc.dat[sys->rtc.addr] = b;
sys->rtc.stat++;
sys->nvr.regs[sys->nvr_addr] = b;
sys->nvr_stat++;
nvr_dosave++;
break;
case 2: /* save data LO nibble */
b = sys->rtc.dat[sys->rtc.addr] & 0xf0;
b = sys->nvr.regs[sys->nvr_addr] & 0xf0;
b |= (val & 0x0f);
sys->rtc.dat[sys->rtc.addr] = b;
sys->rtc.stat = 0;
sys->nvr.regs[sys->nvr_addr] = b;
sys->nvr_stat = 0;
nvr_dosave++;
break;
}
break;
@@ -357,7 +495,7 @@ jim_write(uint16_t addr, uint8_t val, void *priv)
static uint8_t
jim_read(uint16_t addr, void *priv)
{
vm_t *sys = (vm_t *)priv;
europc_t *sys = (europc_t *)priv;
uint8_t r = 0xff;
switch (addr & 0x000f) {
@@ -375,20 +513,20 @@ jim_read(uint16_t addr, void *priv)
r = sys->regs[addr & 0x07];
break;
case 0x0A: /* M3002 RTC INDEX/DATA register */
switch(sys->rtc.stat) {
case 0x0a: /* M3002 RTC INDEX/DATA register */
switch(sys->nvr_stat) {
case 0:
r = 0x00;
break;
case 1: /* read data HI nibble */
r = (sys->rtc.dat[sys->rtc.addr] >> 4);
sys->rtc.stat++;
r = (sys->nvr.regs[sys->nvr_addr] >> 4);
sys->nvr_stat++;
break;
case 2: /* read data LO nibble */
r = (sys->rtc.dat[sys->rtc.addr] & 0x0f);
sys->rtc.stat = 0;
r = (sys->nvr.regs[sys->nvr_addr] & 0x0f);
sys->nvr_stat = 0;
break;
}
break;
@@ -406,120 +544,29 @@ jim_read(uint16_t addr, void *priv)
}
static uint8_t
rtc_checksum(uint8_t *ptr)
{
uint8_t sum;
int i;
/* Calculate all bytes with XOR. */
sum = 0x00;
for (i=MRTC_CONF_A; i<=MRTC_CONF_E; i++)
sum += ptr[i];
return(sum);
}
/*
* Initialize the mainboard 'device' of the machine.
*/
/* Initialize the mainboard 'device' of the machine. */
static void *
europc_boot(device_t *info)
{
vm_t *sys = vm;
europc_t *sys = &europc;
uint8_t b;
#if EUROPC_DEBUG
pclog("EuroPC: booting mainboard..\n");
#endif
/* Try to load the NVR from file. */
if (! nvr_load()) {
/* Load failed, reset to defaults. */
sys->rtc.dat[0x00] = 0x00; /* RTC seconds */
sys->rtc.dat[0x01] = 0x00; /* RTC minutes */
sys->rtc.dat[0x02] = 0x00; /* RTC hours */
sys->rtc.dat[0x03] = 0x01; /* RTC days */
sys->rtc.dat[0x04] = 0x01; /* RTC months */
sys->rtc.dat[0x05] = 0x17; /* RTC years */
sys->rtc.dat[0x06] = 0x01; /* RTC weekday */
sys->rtc.dat[0x07] = 0x01; /* RTC weekno */
/*
* EuroPC System Configuration:
*
* [A] unknown
*
* [B] 7 1 bootdrive extern
* 0 bootdribe intern
* 6:5 11 invalid hard disk type
* 10 hard disk installed, type 2
* 01 hard disk installed, type 1
* 00 hard disk not installed
* 4:3 11 invalid external drive type
* 10 external drive 720K
* 01 external drive 360K
* 00 external drive disabled
* 2 unknown
* 1:0 11 invalid internal drive type
* 10 internal drive 360K
* 01 internal drive 720K
* 00 internal drive disabled
*
* [C] 7:6 unknown
* 5 monitor detection OFF
* 4 unknown
* 3:2 11 illegal memory size
* 10 512K
* 01 256K
* 00 640K
* 1:0 11 illegal game port
* 10 gameport as mouse port
* 01 gameport as joysticks
* 00 gameport disabled
*
* [D] 7:6 10 9MHz CPU speed
* 01 7MHz CPU speed
* 00 4.77 MHz CPU
* 5 unknown
* 4 external: color, internal: mono
* 3 unknown
* 2 internal video ON
* 1:0 11 mono
* 10 color80
* 01 color40
* 00 special (EGA,VGA etc)
*
* [E] 7:4 unknown
* 3:0 country (00=Deutschland, 0A=ASCII)
*/
sys->rtc.dat[MRTC_CONF_A] = 0x00; /* CONFIG A */
sys->rtc.dat[MRTC_CONF_B] = 0x0A; /* CONFIG B */
sys->rtc.dat[MRTC_CONF_C] = 0x28; /* CONFIG C */
sys->rtc.dat[MRTC_CONF_D] = 0x12; /* CONFIG D */
sys->rtc.dat[MRTC_CONF_E] = 0x0A; /* CONFIG E */
sys->rtc.dat[MRTC_CHECK_LO] = 0x44; /* checksum (LO) */
sys->rtc.dat[MRTC_CHECK_HI] = 0x00; /* checksum (HI) */
sys->rtc.dat[MRTC_CTRLSTAT] = 0x01; /* status/control */
/* Provide correct checksum. */
sys->rtc.dat[MRTC_CHECK_LO] = rtc_checksum(sys->rtc.dat);
}
pclog("EuroPC: NVR=[ %02x %02x %02x %02x %02x ] %sVALID\n",
sys->rtc.dat[MRTC_CONF_A], sys->rtc.dat[MRTC_CONF_B],
sys->rtc.dat[MRTC_CONF_C], sys->rtc.dat[MRTC_CONF_D],
sys->rtc.dat[MRTC_CONF_E],
(sys->rtc.dat[MRTC_CHECK_LO]!=rtc_checksum(sys->rtc.dat))?"IN":"");
sys->nvr.regs[MRTC_CONF_A], sys->nvr.regs[MRTC_CONF_B],
sys->nvr.regs[MRTC_CONF_C], sys->nvr.regs[MRTC_CONF_D],
sys->nvr.regs[MRTC_CONF_E],
(sys->nvr.regs[MRTC_CHECK_LO]!=rtc_checksum(sys->nvr.regs))?"IN":"");
/*
* Now that we have initialized the NVR (either from file,
* or by setting it to defaults) we can start overriding it
* with values set by the 86Box user.
* with values set by the user.
*/
b = (sys->rtc.dat[MRTC_CONF_D] & ~0x17);
b = (sys->nvr.regs[MRTC_CONF_D] & ~0x17);
switch(gfxcard) {
case GFX_CGA: /* Color, CGA */
case GFX_COLORPLUS: /* Color, Hercules ColorPlus */
@@ -536,10 +583,10 @@ europc_boot(device_t *info)
b |= 0x10; /* external video, special */
}
sys->rtc.dat[MRTC_CONF_D] = b;
sys->nvr.regs[MRTC_CONF_D] = b;
/* Update the memory size. */
b = (sys->rtc.dat[MRTC_CONF_C] & 0xf3);
b = (sys->nvr.regs[MRTC_CONF_C] & 0xf3);
switch(mem_size) {
case 256:
b |= 0x04;
@@ -553,10 +600,10 @@ europc_boot(device_t *info)
b |= 0x00;
break;
}
sys->rtc.dat[MRTC_CONF_C] = b;
sys->nvr.regs[MRTC_CONF_C] = b;
/* Update CPU speed. */
b = (sys->rtc.dat[MRTC_CONF_D] & 0x3f);
b = (sys->nvr.regs[MRTC_CONF_D] & 0x3f);
switch(cpu) {
case 0: /* 8088, 4.77 MHz */
b |= 0x00;
@@ -570,26 +617,26 @@ europc_boot(device_t *info)
b |= 0x80;
break;
}
sys->rtc.dat[MRTC_CONF_D] = b;
sys->nvr.regs[MRTC_CONF_D] = b;
/* Set up game port. */
b = (sys->rtc.dat[MRTC_CONF_C] & 0xfc);
b = (sys->nvr.regs[MRTC_CONF_C] & 0xfc);
if (mouse_type == MOUSE_TYPE_LOGIBUS) {
b |= 0x01; /* enable port as MOUSE */
} else if (joystick_type != 7) {
b |= 0x02; /* enable port as joysticks */
device_add(&gameport_device);
}
sys->rtc.dat[MRTC_CONF_C] = b;
sys->nvr.regs[MRTC_CONF_C] = b;
#if 0
/* Set up floppy types. */
sys->rtc.dat[MRTC_CONF_B] = 0x2A;
sys->nvr.regs[MRTC_CONF_B] = 0x2a;
#endif
/* Validate the NVR checksum. */
sys->rtc.dat[MRTC_CHECK_LO] = rtc_checksum(sys->rtc.dat);
nvr_save();
/* Validate the NVR checksum and save. */
sys->nvr.regs[MRTC_CHECK_LO] = rtc_checksum(sys->nvr.regs);
nvr_dosave++;
/*
* Allocate the system's I/O handlers.
@@ -600,11 +647,10 @@ europc_boot(device_t *info)
* the way of other cards that need this range.
*/
io_sethandler(sys->jim, 16,
jim_read, NULL, NULL,
jim_write, NULL, NULL, sys);
jim_read,NULL,NULL, jim_write,NULL,NULL, sys);
/* Only after JIM has been initialized. */
device_add(&keyboard_xt_device);
(void)device_add(&keyboard_xt_device);
/*
* Set up and enable the HD20 disk controller.
@@ -612,7 +658,7 @@ europc_boot(device_t *info)
* We only do this if we have not configured another one.
*/
if (hdc_current == 1)
device_add(&europc_hdc_device);
(void)device_add(&europc_hdc_device);
return(sys);
}
@@ -621,13 +667,10 @@ europc_boot(device_t *info)
static void
europc_close(void *priv)
{
nvr_t *nvr = &vm->nvr;
nvr_t *nvr = &europc.nvr;
if (nvr->fname != NULL)
free(nvr->fname);
free(vm);
vm = NULL;
if (nvr->fn != NULL)
free(nvr->fn);
}
@@ -655,9 +698,7 @@ static device_config_t europc_config[] = {
device_t europc_device = {
"EuroPC System Board",
0, 0,
europc_boot, /* init */
europc_close, /* close */
NULL,
europc_boot, europc_close, NULL,
NULL, NULL, NULL, NULL,
europc_config
};
@@ -674,36 +715,28 @@ device_t europc_device = {
void
machine_europc_init(machine_t *model)
{
vm_t *sys;
/* Allocate machine data. */
sys = (vm_t *)malloc(sizeof(vm_t));
if (sys == NULL) {
pclog("EuroPC: unable to allocate machine data!\n");
return;
}
memset(sys, 0x00, sizeof(vm_t));
sys->jim = 0x0250;
vm = sys;
/* Clear the machine state. */
memset(&europc, 0x00, sizeof(europc_t));
europc.jim = 0x0250;
machine_common_init(model);
nmi_init();
mem_add_bios();
/* This is machine specific. */
vm->nvr.mask = model->nvrmask;
vm->nvr.irq = -1;
europc.nvr.size = model->nvrmask + 1;
europc.nvr.irq = -1;
/* Set up any local handlers here. */
vm->nvr.load = load_nvr;
vm->nvr.save = save_nvr;
vm->nvr.hook = rtc_hook;
europc.nvr.reset = rtc_reset;
europc.nvr.start = rtc_start;
europc.nvr.tick = rtc_tick;
/* Initialize the actual NVR. */
nvr_init(&vm->nvr);
nvr_init(&europc.nvr);
/* Enable and set up the FDC. */
device_add(&fdc_xt_device);
(void)device_add(&fdc_xt_device);
/* Enable and set up the mainboard device. */
device_add(&europc_device);

View File

@@ -1,10 +1,10 @@
/*
* 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.
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the 86Box distribution.
* This file is part of the VARCem Project.
*
* Implementation of the EuroPC HD20 internal controller.
*
@@ -20,13 +20,45 @@
* This driver is based on the information found in the IBM-PC
* Technical Reference manual, pp 187 and on.
*
* Version: @(#)europc_hdc.c 1.0.2 2017/11/18
*
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
* Based on the original "xebec.c" from Sarah Walker.
*
* Version: @(#)m_europc_hdc.c 1.0.2 2018/03/11
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Sarah Walker, <tommowalker@tommowalker.co.uk>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2008-2017 Sarah Walker.
* Copyright 2017 Fred N. van Kempen.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define __USE_LARGEFILE64
#define _LARGEFILE_SOURCE
@@ -44,11 +76,12 @@
#include "../timer.h"
#include "../disk/hdc.h"
#include "../disk/hdd.h"
#include "../plat.h"
#include "../ui.h"
#include "machine.h"
#define HDC_DEBUG 1
#define HDC_DEBUG 0
#define HDC_NEWPARAMS 1 /* use NEW parameter block */
#define HDD_IOADDR 0x0320

View File

@@ -40,7 +40,9 @@ static struct
uint8_t io_id;
mem_mapping_t shadow_mapping;
mem_mapping_t split_mapping;
mem_mapping_t expansion_mapping;
mem_mapping_t cache_mapping;
uint8_t (*planar_read)(uint16_t port);
void (*planar_write)(uint16_t port, uint8_t val);
@@ -48,11 +50,99 @@ static struct
uint8_t mem_regs[3];
uint32_t split_addr, split_size;
uint32_t split_phys;
uint8_t mem_pos_regs[8];
uint8_t mem_2mb_pos_regs[8];
int pending_cache_miss;
} ps2;
/*The model 70 type 3/4 BIOS performs cache testing. Since 86Box doesn't have any
proper cache emulation, it's faked a bit here.
Port E2 is used for cache diagnostics. Bit 7 seems to be set on a cache miss,
toggling bit 2 seems to clear this. The BIOS performs at least the following
tests :
- Disable RAM, access low 64kb (386) / 8kb (486), execute code from cache to
access low memory and verify that there are no cache misses.
- Write to low memory using DMA, read low memory and verify that all accesses
cause cache misses.
- Read low memory, verify that first access is cache miss. Read again and
verify that second access is cache hit.
These tests are also performed on the 486 model 70, despite there being no
external cache on this system. Port E2 seems to control the internal cache on
these systems. Presumably this port is connected to KEN#/FLUSH# on the 486.
This behaviour is required to pass the timer interrupt test on the 486 version
- the BIOS uses a fixed length loop that will terminate too early on a 486/25
if it executes from internal cache.
To handle this, 86Box uses some basic heuristics :
- If cache is enabled but RAM is disabled, accesses to low memory go directly
to cache memory.
- Reads to cache addresses not 'valid' will set the cache miss flag, and mark
that line as valid.
- Cache flushes will clear the valid array.
- DMA via the undocumented PS/2 command 0xb will clear the valid array.
- Disabling the cache will clear the valid array.
- Disabling the cache will also mark shadowed ROM areas as using ROM timings.
This works around the timing loop mentioned above.
*/
static uint8_t ps2_cache[65536];
static int ps2_cache_valid[65536/8];
static uint8_t ps2_read_cache_ram(uint32_t addr, void *priv)
{
// pclog("ps2_read_cache_ram: addr=%08x %i %04x:%04x\n", addr, ps2_cache_valid[addr >> 3], CS,cpu_state.pc);
if (!ps2_cache_valid[addr >> 3])
{
ps2_cache_valid[addr >> 3] = 1;
ps2.mem_regs[2] |= 0x80;
}
else
ps2.pending_cache_miss = 0;
return ps2_cache[addr];
}
static uint16_t ps2_read_cache_ramw(uint32_t addr, void *priv)
{
// pclog("ps2_read_cache_ramw: addr=%08x %i %04x:%04x\n", addr, ps2_cache_valid[addr >> 3], CS,cpu_state.pc);
if (!ps2_cache_valid[addr >> 3])
{
ps2_cache_valid[addr >> 3] = 1;
ps2.mem_regs[2] |= 0x80;
}
else
ps2.pending_cache_miss = 0;
return *(uint16_t *)&ps2_cache[addr];
}
static uint32_t ps2_read_cache_raml(uint32_t addr, void *priv)
{
// pclog("ps2_read_cache_raml: addr=%08x %i %04x:%04x\n", addr, ps2_cache_valid[addr >> 3], CS,cpu_state.pc);
if (!ps2_cache_valid[addr >> 3])
{
ps2_cache_valid[addr >> 3] = 1;
ps2.mem_regs[2] |= 0x80;
}
else
ps2.pending_cache_miss = 0;
return *(uint32_t *)&ps2_cache[addr];
}
static void ps2_write_cache_ram(uint32_t addr, uint8_t val, void *priv)
{
// pclog("ps2_write_cache_ram: addr=%08x val=%02x %04x:%04x %i\n", addr, val, CS,cpu_state.pc, ins);
ps2_cache[addr] = val;
}
void ps2_cache_clean(void)
{
memset(ps2_cache_valid, 0, sizeof(ps2_cache_valid));
}
static uint8_t ps2_read_shadow_ram(uint32_t addr, void *priv)
{
@@ -85,6 +175,37 @@ static void ps2_write_shadow_raml(uint32_t addr, uint32_t val, void *priv)
mem_write_raml(addr, val, priv);
}
static uint8_t ps2_read_split_ram(uint32_t addr, void *priv)
{
addr = (addr % (ps2.split_size << 10)) + ps2.split_phys;
return mem_read_ram(addr, priv);
}
static uint16_t ps2_read_split_ramw(uint32_t addr, void *priv)
{
addr = (addr % (ps2.split_size << 10)) + ps2.split_phys;
return mem_read_ramw(addr, priv);
}
static uint32_t ps2_read_split_raml(uint32_t addr, void *priv)
{
addr = (addr % (ps2.split_size << 10)) + ps2.split_phys;
return mem_read_raml(addr, priv);
}
static void ps2_write_split_ram(uint32_t addr, uint8_t val, void *priv)
{
addr = (addr % (ps2.split_size << 10)) + ps2.split_phys;
mem_write_ram(addr, val, priv);
}
static void ps2_write_split_ramw(uint32_t addr, uint16_t val, void *priv)
{
addr = (addr % (ps2.split_size << 10)) + ps2.split_phys;
mem_write_ramw(addr, val, priv);
}
static void ps2_write_split_raml(uint32_t addr, uint32_t val, void *priv)
{
addr = (addr % (ps2.split_size << 10)) + ps2.split_phys;
mem_write_raml(addr, val, priv);
}
#define PS2_SETUP_IO 0x80
#define PS2_SETUP_VGA 0x20
@@ -139,6 +260,30 @@ static uint8_t model_55sx_read(uint16_t port)
return 0xff;
}
static uint8_t model_70_type3_read(uint16_t port)
{
switch (port)
{
case 0x100:
return 0xff;
case 0x101:
return 0xf9;
case 0x102:
return ps2.option[0];
case 0x103:
return ps2.option[1];
case 0x104:
return ps2.option[2];
case 0x105:
return ps2.option[3];
case 0x106:
return ps2.subaddr_lo;
case 0x107:
return ps2.subaddr_hi;
}
return 0xff;
}
static uint8_t model_80_read(uint16_t port)
{
switch (port)
@@ -296,6 +441,56 @@ static void model_55sx_write(uint16_t port, uint8_t val)
}
}
static void model_70_type3_write(uint16_t port, uint8_t val)
{
switch (port)
{
case 0x100:
break;
case 0x101:
break;
case 0x102:
lpt1_remove();
serial_remove(1);
if (val & 0x04)
{
if (val & 0x08)
serial_setup(1, SERIAL1_ADDR, SERIAL1_IRQ);
else
serial_setup(1, SERIAL2_ADDR, SERIAL2_IRQ);
}
else
serial_remove(1);
if (val & 0x10)
{
switch ((val >> 5) & 3)
{
case 0:
lpt1_init(0x3bc);
break;
case 1:
lpt1_init(0x378);
break;
case 2:
lpt1_init(0x278);
break;
}
}
ps2.option[0] = val;
break;
case 0x105:
ps2.option[3] = val;
break;
case 0x106:
ps2.subaddr_lo = val;
break;
case 0x107:
ps2.subaddr_hi = val;
break;
}
}
static void model_80_write(uint16_t port, uint8_t val)
{
switch (port)
@@ -677,7 +872,7 @@ static void ps2_mca_board_model_55sx_init()
static void mem_encoding_update()
{
mem_split_disable(ps2.split_size, ps2.split_addr);
mem_mapping_disable(&ps2.split_mapping);
ps2.split_addr = ((uint32_t) (ps2.mem_regs[0] & 0xf)) << 20;
@@ -699,12 +894,16 @@ static void mem_encoding_update()
if (!(ps2.mem_regs[1] & 8))
{
if (ps2.mem_regs[1] & 4)
if (ps2.mem_regs[1] & 4) {
ps2.split_size = 384;
else
ps2.split_phys = 0x80000;
} else {
ps2.split_size = 256;
ps2.split_phys = 0xa0000;
}
mem_split_enable(ps2.split_size, ps2.split_addr);
mem_mapping_set_exec(&ps2.split_mapping, &ram[ps2.split_phys]);
mem_mapping_set_addr(&ps2.split_mapping, ps2.split_addr, ps2.split_size << 10);
/* pclog("PS/2 Model 80-111: Split memory block enabled at %08X\n", ps2.split_addr); */
} /* else {
@@ -737,6 +936,169 @@ static void mem_encoding_write(uint16_t addr, uint8_t val, void *p)
mem_encoding_update();
}
static uint8_t mem_encoding_read_cached(uint16_t addr, void *p)
{
switch (addr)
{
case 0xe0:
return ps2.mem_regs[0];
case 0xe1:
return ps2.mem_regs[1];
case 0xe2:
return ps2.mem_regs[2];
}
return 0xff;
}
static void mem_encoding_write_cached(uint16_t addr, uint8_t val, void *p)
{
uint8_t old;
switch (addr)
{
case 0xe0:
ps2.mem_regs[0] = val;
break;
case 0xe1:
ps2.mem_regs[1] = val;
break;
case 0xe2:
old = ps2.mem_regs[2];
ps2.mem_regs[2] = (ps2.mem_regs[2] & 0x80) | (val & ~0x88);
if (val & 2)
{
// pclog("Clear latch - %i\n", ps2.pending_cache_miss);
if (ps2.pending_cache_miss)
ps2.mem_regs[2] |= 0x80;
else
ps2.mem_regs[2] &= ~0x80;
ps2.pending_cache_miss = 0;
}
if ((val & 0x21) == 0x20 && (old & 0x21) != 0x20)
ps2.pending_cache_miss = 1;
if ((val & 0x21) == 0x01 && (old & 0x21) != 0x01)
ps2_cache_clean();
if (val & 0x01)
ram_mid_mapping.flags |= MEM_MAPPING_ROM;
else
ram_mid_mapping.flags &= ~MEM_MAPPING_ROM;
break;
}
// pclog("mem_encoding_write: addr=%02x val=%02x %04x:%04x %02x %02x\n", addr, val, CS,cpu_state.pc, ps2.mem_regs[1],ps2.mem_regs[2]);
mem_encoding_update();
if ((ps2.mem_regs[1] & 0x10) && (ps2.mem_regs[2] & 0x21) == 0x20)
{
mem_mapping_disable(&ram_low_mapping);
mem_mapping_enable(&ps2.cache_mapping);
flushmmucache();
}
else
{
mem_mapping_disable(&ps2.cache_mapping);
mem_mapping_enable(&ram_low_mapping);
flushmmucache();
}
}
static void ps2_mca_board_model_70_type34_init(int is_type4)
{
ps2_mca_board_common_init();
mem_remap_top_256k();
ps2.split_addr = mem_size * 1024;
mca_init(4);
ps2.planar_read = model_70_type3_read;
ps2.planar_write = model_70_type3_write;
device_add(&ps2_nvr_device);
io_sethandler(0x00e0, 0x0003, mem_encoding_read_cached, NULL, NULL, mem_encoding_write_cached, NULL, NULL, NULL);
ps2.mem_regs[1] = 2;
switch (mem_size/1024)
{
case 2:
ps2.option[1] = 0xa6;
ps2.option[2] = 0x01;
break;
case 4:
ps2.option[1] = 0xaa;
ps2.option[2] = 0x01;
break;
case 6:
ps2.option[1] = 0xca;
ps2.option[2] = 0x01;
break;
case 8:
default:
ps2.option[1] = 0xca;
ps2.option[2] = 0x02;
break;
}
if (is_type4)
ps2.option[2] |= 0x04; /*486 CPU*/
mem_mapping_add(&ps2.cache_mapping,
0,
is_type4 ? (8 * 1024) : (64 * 1024),
ps2_read_cache_ram,
ps2_read_cache_ramw,
ps2_read_cache_raml,
ps2_write_cache_ram,
NULL,
NULL,
ps2_cache,
MEM_MAPPING_INTERNAL,
NULL);
mem_mapping_disable(&ps2.cache_mapping);
if (mem_size > 8192)
{
/* Only 8 MB supported on planar, create a memory expansion card for the rest */
mem_mapping_set_addr(&ram_high_mapping, 0x100000, 0x700000);
ps2.mem_pos_regs[0] = 0xff;
ps2.mem_pos_regs[1] = 0xfc;
switch (mem_size/1024)
{
case 10:
ps2.mem_pos_regs[4] = 0xfe;
break;
case 12:
ps2.mem_pos_regs[4] = 0xfa;
break;
case 14:
ps2.mem_pos_regs[4] = 0xea;
break;
case 16:
ps2.mem_pos_regs[4] = 0xaa;
break;
}
mca_add(ps2_mem_expansion_read, ps2_mem_expansion_write, NULL);
mem_mapping_add(&ps2.expansion_mapping,
0x800000,
(mem_size - 8192)*1024,
mem_read_ram,
mem_read_ramw,
mem_read_raml,
mem_write_ram,
mem_write_ramw,
mem_write_raml,
&ram[0x800000],
MEM_MAPPING_INTERNAL,
NULL);
mem_mapping_disable(&ps2.expansion_mapping);
}
device_add(&ps1vga_device);
}
static void ps2_mca_board_model_80_type2_init(int is486)
{
ps2_mca_board_common_init();
@@ -783,6 +1145,20 @@ static void ps2_mca_board_model_80_type2_init(int is486)
ps2.mem_regs[0] |= ((mem_size/1024) & 0x0f);
mem_mapping_add(&ps2.split_mapping,
(mem_size+256) * 1024,
256*1024,
ps2_read_split_ram,
ps2_read_split_ramw,
ps2_read_split_raml,
ps2_write_split_ram,
ps2_write_split_ramw,
ps2_write_split_raml,
&ram[0xa0000],
MEM_MAPPING_INTERNAL,
NULL);
mem_mapping_disable(&ps2.split_mapping);
if ((mem_size > 4096) && !is486)
{
/* Only 4 MB supported on planar, create a memory expansion card for the rest */
@@ -876,6 +1252,21 @@ machine_ps2_model_55sx_init(machine_t *model)
ps2_mca_board_model_55sx_init();
}
void
machine_ps2_model_70_type3_init(machine_t *model)
{
machine_ps2_common_init(model);
ps2_mca_board_model_70_type34_init(0);
}
void
machine_ps2_model_70_type4_init(machine_t *model)
{
machine_ps2_common_init(model);
ps2_mca_board_model_70_type34_init(1);
}
void
machine_ps2_model_80_init(machine_t *model)

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,59 @@
void t1000_syskey(uint8_t andmask, uint8_t ormask, uint8_t xormask);
/*
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Definitions for the Toshiba T1000/T1200 machines.
*
* Version: @(#)m_xt_t1000.h 1.0.2 2018/03/10
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* Sarah Walker, <tommowalker@tommowalker.co.uk>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
* Copyright 2008-2018 Sarah Walker.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#ifndef MACHINE_T1000_H
# define MACHINE_T1000_H
void t1000_configsys_loadnvr();
void t1000_emsboard_loadnvr();
void t1000_configsys_savenvr();
void t1000_emsboard_savenvr();
extern device_t t1000_video_device;
extern device_t t1200_video_device;
extern void t1000_video_options_set(uint8_t options);
extern void t1000_display_set(uint8_t internal);
extern void t1000_syskey(uint8_t amask, uint8_t omask, uint8_t xmask);
extern void t1000_configsys_load(void);
extern void t1000_configsys_save(void);
extern void t1000_emsboard_load(void);
extern void t1000_emsboard_save(void);
#endif /*MACHINE_T1000_H*/

View File

@@ -0,0 +1,753 @@
/*
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Implementation of the Toshiba T1000 plasma display, which
* has a fixed resolution of 640x200 pixels.
*
* Version: @(#)m_xt_t1000_vid.c 1.0.4 2018/03/10
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* Sarah Walker, <tommowalker@tommowalker.co.uk>
*
* Copyright 2018 Fred N. van Kempen.
* Copyright 2018 Miran Grca.
* Copyright 2018 Sarah Walker.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "../86box.h"
#include "../device.h"
#include "../io.h"
#include "../mem.h"
#include "../timer.h"
#include "../cpu/cpu.h"
#include "../video/video.h"
#include "../video/vid_cga.h"
#include "m_xt_t1000.h"
#define T1000_XSIZE 640
#define T1000_YSIZE 200
/* Mapping of attributes to colours */
static uint32_t blue, grey;
static uint8_t boldcols[256]; /* Which attributes use the bold font */
static uint32_t blinkcols[256][2];
static uint32_t normcols[256][2];
static uint8_t language;
/* Video options set by the motherboard; they will be picked up by the card
* on the next poll.
*
* Bit 1: Danish
* Bit 0: Thin font
*/
static uint8_t st_video_options;
static int8_t st_display_internal = -1;
void t1000_video_options_set(uint8_t options)
{
st_video_options = options & 1;
st_video_options |= language;
}
void t1000_display_set(uint8_t internal)
{
st_display_internal = (int8_t)internal;
}
uint8_t t1000_display_get()
{
return (uint8_t)st_display_internal;
}
typedef struct t1000_t
{
mem_mapping_t mapping;
cga_t cga; /* The CGA is used for the external
* display; most of its registers are
* ignored by the plasma display. */
int font; /* Current font, 0-3 */
int enabled; /* Hardware enabled, 0 or 1 */
int internal; /* Using internal display? */
uint8_t attrmap; /* Attribute mapping register */
int dispontime, dispofftime;
int linepos, displine;
int vc;
int dispon;
int vsynctime;
uint8_t video_options;
uint8_t *vram;
} t1000_t;
static void t1000_recalctimings(t1000_t *t1000);
static void t1000_write(uint32_t addr, uint8_t val, void *p);
static uint8_t t1000_read(uint32_t addr, void *p);
static void t1000_recalcattrs(t1000_t *t1000);
static void t1000_out(uint16_t addr, uint8_t val, void *p)
{
t1000_t *t1000 = (t1000_t *)p;
switch (addr)
{
/* Emulated CRTC, register select */
case 0x3d0: case 0x3d2: case 0x3d4: case 0x3d6:
cga_out(addr, val, &t1000->cga);
break;
/* Emulated CRTC, value */
case 0x3d1: case 0x3d3: case 0x3d5: case 0x3d7:
/* Register 0x12 controls the attribute mappings for the
* LCD screen. */
if (t1000->cga.crtcreg == 0x12)
{
t1000->attrmap = val;
t1000_recalcattrs(t1000);
return;
}
cga_out(addr, val, &t1000->cga);
t1000_recalctimings(t1000);
return;
/* CGA control register */
case 0x3D8:
cga_out(addr, val, &t1000->cga);
return;
/* CGA colour register */
case 0x3D9:
cga_out(addr, val, &t1000->cga);
return;
}
}
static uint8_t t1000_in(uint16_t addr, void *p)
{
t1000_t *t1000 = (t1000_t *)p;
uint8_t val;
switch (addr)
{
case 0x3d1: case 0x3d3: case 0x3d5: case 0x3d7:
if (t1000->cga.crtcreg == 0x12)
{
val = t1000->attrmap & 0x0F;
if (t1000->internal) val |= 0x20; /* LCD / CRT */
return val;
}
}
return cga_in(addr, &t1000->cga);
}
static void t1000_write(uint32_t addr, uint8_t val, void *p)
{
t1000_t *t1000 = (t1000_t *)p;
egawrites++;
// pclog("CGA_WRITE %04X %02X\n", addr, val);
t1000->vram[addr & 0x3fff] = val;
cycles -= 4;
}
static uint8_t t1000_read(uint32_t addr, void *p)
{
t1000_t *t1000 = (t1000_t *)p;
egareads++;
cycles -= 4;
// pclog("CGA_READ %04X\n", addr);
return t1000->vram[addr & 0x3fff];
}
static void t1000_recalctimings(t1000_t *t1000)
{
double disptime;
double _dispontime, _dispofftime;
if (!t1000->internal)
{
cga_recalctimings(&t1000->cga);
return;
}
disptime = 651;
_dispontime = 640;
_dispofftime = disptime - _dispontime;
t1000->dispontime = (int)(_dispontime * (1 << TIMER_SHIFT));
t1000->dispofftime = (int)(_dispofftime * (1 << TIMER_SHIFT));
}
/* Draw a row of text in 80-column mode */
static void t1000_text_row80(t1000_t *t1000)
{
uint32_t cols[2];
int x, c;
uint8_t chr, attr;
int drawcursor;
int cursorline;
int bold;
int blink;
uint16_t addr;
uint8_t sc;
uint16_t ma = (t1000->cga.crtc[13] | (t1000->cga.crtc[12] << 8)) & 0x3fff;
uint16_t ca = (t1000->cga.crtc[15] | (t1000->cga.crtc[14] << 8)) & 0x3fff;
sc = (t1000->displine) & 7;
addr = ((ma & ~1) + (t1000->displine >> 3) * 80) * 2;
ma += (t1000->displine >> 3) * 80;
if ((t1000->cga.crtc[10] & 0x60) == 0x20)
{
cursorline = 0;
}
else
{
cursorline = ((t1000->cga.crtc[10] & 0x0F) <= sc) &&
((t1000->cga.crtc[11] & 0x0F) >= sc);
}
for (x = 0; x < 80; x++)
{
chr = t1000->vram[(addr + 2 * x) & 0x3FFF];
attr = t1000->vram[(addr + 2 * x + 1) & 0x3FFF];
drawcursor = ((ma == ca) && cursorline &&
(t1000->cga.cgamode & 8) && (t1000->cga.cgablink & 16));
blink = ((t1000->cga.cgablink & 16) && (t1000->cga.cgamode & 0x20) &&
(attr & 0x80) && !drawcursor);
if (t1000->video_options & 1)
bold = boldcols[attr] ? chr : chr + 256;
else
bold = boldcols[attr] ? chr + 256 : chr;
if (t1000->video_options & 2)
bold += 512;
if (t1000->cga.cgamode & 0x20) /* Blink */
{
cols[1] = blinkcols[attr][1];
cols[0] = blinkcols[attr][0];
if (blink) cols[1] = cols[0];
}
else
{
cols[1] = normcols[attr][1];
cols[0] = normcols[attr][0];
}
if (drawcursor)
{
for (c = 0; c < 8; c++)
{
((uint32_t *)buffer32->line[t1000->displine])[(x << 3) + c] = cols[(fontdat[bold][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ (blue ^ grey);
}
}
else
{
for (c = 0; c < 8; c++)
((uint32_t *)buffer32->line[t1000->displine])[(x << 3) + c] = cols[(fontdat[bold][sc] & (1 << (c ^ 7))) ? 1 : 0];
}
++ma;
}
}
/* Draw a row of text in 40-column mode */
static void t1000_text_row40(t1000_t *t1000)
{
uint32_t cols[2];
int x, c;
uint8_t chr, attr;
int drawcursor;
int cursorline;
int bold;
int blink;
uint16_t addr;
uint8_t sc;
uint16_t ma = (t1000->cga.crtc[13] | (t1000->cga.crtc[12] << 8)) & 0x3fff;
uint16_t ca = (t1000->cga.crtc[15] | (t1000->cga.crtc[14] << 8)) & 0x3fff;
sc = (t1000->displine) & 7;
addr = ((ma & ~1) + (t1000->displine >> 3) * 40) * 2;
ma += (t1000->displine >> 3) * 40;
if ((t1000->cga.crtc[10] & 0x60) == 0x20)
{
cursorline = 0;
}
else
{
cursorline = ((t1000->cga.crtc[10] & 0x0F) <= sc) &&
((t1000->cga.crtc[11] & 0x0F) >= sc);
}
for (x = 0; x < 40; x++)
{
chr = t1000->vram[(addr + 2 * x) & 0x3FFF];
attr = t1000->vram[(addr + 2 * x + 1) & 0x3FFF];
drawcursor = ((ma == ca) && cursorline &&
(t1000->cga.cgamode & 8) && (t1000->cga.cgablink & 16));
blink = ((t1000->cga.cgablink & 16) && (t1000->cga.cgamode & 0x20) &&
(attr & 0x80) && !drawcursor);
if (t1000->video_options & 1)
bold = boldcols[attr] ? chr : chr + 256;
else
bold = boldcols[attr] ? chr + 256 : chr;
if (t1000->video_options & 2)
bold += 512;
if (t1000->cga.cgamode & 0x20) /* Blink */
{
cols[1] = blinkcols[attr][1];
cols[0] = blinkcols[attr][0];
if (blink) cols[1] = cols[0];
}
else
{
cols[1] = normcols[attr][1];
cols[0] = normcols[attr][0];
}
if (drawcursor)
{
for (c = 0; c < 8; c++)
{
((uint32_t *)buffer32->line[t1000->displine])[(x << 4) + c*2] =
((uint32_t *)buffer32->line[t1000->displine])[(x << 4) + c*2 + 1] = cols[(fontdat[bold][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ (blue ^ grey);
}
}
else
{
for (c = 0; c < 8; c++)
{
((uint32_t *)buffer32->line[t1000->displine])[(x << 4) + c*2] =
((uint32_t *)buffer32->line[t1000->displine])[(x << 4) + c*2+1] = cols[(fontdat[bold][sc] & (1 << (c ^ 7))) ? 1 : 0];
}
}
++ma;
}
}
/* Draw a line in CGA 640x200 mode */
static void t1000_cgaline6(t1000_t *t1000)
{
int x, c;
uint8_t dat;
uint32_t ink = 0;
uint16_t addr;
uint32_t fg = (t1000->cga.cgacol & 0x0F) ? blue : grey;
uint32_t bg = grey;
uint16_t ma = (t1000->cga.crtc[13] | (t1000->cga.crtc[12] << 8)) & 0x3fff;
addr = ((t1000->displine) & 1) * 0x2000 +
(t1000->displine >> 1) * 80 +
((ma & ~1) << 1);
for (x = 0; x < 80; x++)
{
dat = t1000->vram[addr & 0x3FFF];
addr++;
for (c = 0; c < 8; c++)
{
ink = (dat & 0x80) ? fg : bg;
if (!(t1000->cga.cgamode & 8))
ink = grey;
((uint32_t *)buffer32->line[t1000->displine])[x*8+c] = ink;
dat = dat << 1;
}
}
}
/* Draw a line in CGA 320x200 mode. Here the CGA colours are converted to
* dither patterns: colour 1 to 25% grey, colour 2 to 50% grey */
static void t1000_cgaline4(t1000_t *t1000)
{
int x, c;
uint8_t dat, pattern;
uint32_t ink0, ink1;
uint16_t addr;
uint16_t ma = (t1000->cga.crtc[13] | (t1000->cga.crtc[12] << 8)) & 0x3fff;
addr = ((t1000->displine) & 1) * 0x2000 +
(t1000->displine >> 1) * 80 +
((ma & ~1) << 1);
for (x = 0; x < 80; x++)
{
dat = t1000->vram[addr & 0x3FFF];
addr++;
for (c = 0; c < 4; c++)
{
pattern = (dat & 0xC0) >> 6;
if (!(t1000->cga.cgamode & 8)) pattern = 0;
switch (pattern & 3)
{
default:
case 0: ink0 = ink1 = grey; break;
case 1: if (t1000->displine & 1)
{
ink0 = grey; ink1 = grey;
}
else
{
ink0 = blue; ink1 = grey;
}
break;
case 2: if (t1000->displine & 1)
{
ink0 = grey; ink1 = blue;
}
else
{
ink0 = blue; ink1 = grey;
}
break;
case 3: ink0 = ink1 = blue; break;
}
((uint32_t *)buffer32->line[t1000->displine])[x*8+2*c] = ink0;
((uint32_t *)buffer32->line[t1000->displine])[x*8+2*c+1] = ink1;
dat = dat << 2;
}
}
}
static void t1000_poll(void *p)
{
t1000_t *t1000 = (t1000_t *)p;
if (t1000->video_options != st_video_options)
{
t1000->video_options = st_video_options;
/* Set the font used for the external display */
t1000->cga.fontbase = ((t1000->video_options & 3) * 256);
}
/* Switch between internal plasma and external CRT display. */
if (st_display_internal != -1 && st_display_internal != t1000->internal)
{
t1000->internal = st_display_internal;
t1000_recalctimings(t1000);
}
if (!t1000->internal)
{
cga_poll(&t1000->cga);
return;
}
if (!t1000->linepos)
{
t1000->cga.vidtime += t1000->dispofftime;
t1000->cga.cgastat |= 1;
t1000->linepos = 1;
if (t1000->dispon)
{
if (t1000->displine == 0)
{
video_wait_for_buffer();
}
/* Graphics */
if (t1000->cga.cgamode & 0x02)
{
if (t1000->cga.cgamode & 0x10)
t1000_cgaline6(t1000);
else t1000_cgaline4(t1000);
}
else
if (t1000->cga.cgamode & 0x01) /* High-res text */
{
t1000_text_row80(t1000);
}
else
{
t1000_text_row40(t1000);
}
}
t1000->displine++;
/* Hardcode a fixed refresh rate and VSYNC timing */
if (t1000->displine == 200) /* Start of VSYNC */
{
t1000->cga.cgastat |= 8;
t1000->dispon = 0;
}
if (t1000->displine == 216) /* End of VSYNC */
{
t1000->displine = 0;
t1000->cga.cgastat &= ~8;
t1000->dispon = 1;
}
}
else
{
if (t1000->dispon)
{
t1000->cga.cgastat &= ~1;
}
t1000->cga.vidtime += t1000->dispontime;
t1000->linepos = 0;
if (t1000->displine == 200)
{
/* Hardcode 640x200 window size */
if (T1000_XSIZE != xsize || T1000_YSIZE != ysize)
{
xsize = T1000_XSIZE;
ysize = T1000_YSIZE;
if (xsize < 64) xsize = 656;
if (ysize < 32) ysize = 200;
set_screen_size(xsize, ysize);
}
video_blit_memtoscreen(0, 0, 0, ysize, xsize, ysize);
frames++;
/* Fixed 640x200 resolution */
video_res_x = T1000_XSIZE;
video_res_y = T1000_YSIZE;
if (t1000->cga.cgamode & 0x02)
{
if (t1000->cga.cgamode & 0x10)
video_bpp = 1;
else video_bpp = 2;
}
else video_bpp = 0;
t1000->cga.cgablink++;
}
}
}
static void t1000_recalcattrs(t1000_t *t1000)
{
int n;
/* val behaves as follows:
* Bit 0: Attributes 01-06, 08-0E are inverse video
* Bit 1: Attributes 01-06, 08-0E are bold
* Bit 2: Attributes 11-16, 18-1F, 21-26, 28-2F ... F1-F6, F8-FF
* are inverse video
* Bit 3: Attributes 11-16, 18-1F, 21-26, 28-2F ... F1-F6, F8-FF
* are bold */
/* Set up colours */
blue = makecol(0x2D, 0x39, 0x5A);
grey = makecol(0x85, 0xa0, 0xD6);
/* Initialise the attribute mapping. Start by defaulting everything
* to grey on blue, and with bold set by bit 3 */
for (n = 0; n < 256; n++)
{
boldcols[n] = (n & 8) != 0;
blinkcols[n][0] = normcols[n][0] = blue;
blinkcols[n][1] = normcols[n][1] = grey;
}
/* Colours 0x11-0xFF are controlled by bits 2 and 3 of the
* passed value. Exclude x0 and x8, which are always grey on
* blue. */
for (n = 0x11; n <= 0xFF; n++)
{
if ((n & 7) == 0) continue;
if (t1000->attrmap & 4) /* Inverse */
{
blinkcols[n][0] = normcols[n][0] = blue;
blinkcols[n][1] = normcols[n][1] = grey;
}
else /* Normal */
{
blinkcols[n][0] = normcols[n][0] = grey;
blinkcols[n][1] = normcols[n][1] = blue;
}
if (t1000->attrmap & 8) boldcols[n] = 1; /* Bold */
}
/* Set up the 01-0E range, controlled by bits 0 and 1 of the
* passed value. When blinking is enabled this also affects 81-8E. */
for (n = 0x01; n <= 0x0E; n++)
{
if (n == 7) continue;
if (t1000->attrmap & 1)
{
blinkcols[n][0] = normcols[n][0] = blue;
blinkcols[n][1] = normcols[n][1] = grey;
blinkcols[n+128][0] = blue;
blinkcols[n+128][1] = grey;
}
else
{
blinkcols[n][0] = normcols[n][0] = grey;
blinkcols[n][1] = normcols[n][1] = blue;
blinkcols[n+128][0] = grey;
blinkcols[n+128][1] = blue;
}
if (t1000->attrmap & 2) boldcols[n] = 1;
}
/* Colours 07 and 0F are always blue on grey. If blinking is
* enabled so are 87 and 8F. */
for (n = 0x07; n <= 0x0F; n += 8)
{
blinkcols[n][0] = normcols[n][0] = grey;
blinkcols[n][1] = normcols[n][1] = blue;
blinkcols[n+128][0] = grey;
blinkcols[n+128][1] = blue;
}
/* When not blinking, colours 81-8F are always blue on grey. */
for (n = 0x81; n <= 0x8F; n ++)
{
normcols[n][0] = grey;
normcols[n][1] = blue;
boldcols[n] = (n & 0x08) != 0;
}
/* Finally do the ones which are solid grey. These differ between
* the normal and blinking mappings */
for (n = 0; n <= 0xFF; n += 0x11)
{
normcols[n][0] = normcols[n][1] = grey;
}
/* In the blinking range, 00 11 22 .. 77 and 80 91 A2 .. F7 are grey */
for (n = 0; n <= 0x77; n += 0x11)
{
blinkcols[n][0] = blinkcols[n][1] = grey;
blinkcols[n+128][0] = blinkcols[n+128][1] = grey;
}
}
static void *t1000_init(device_t *info)
{
t1000_t *t1000 = malloc(sizeof(t1000_t));
memset(t1000, 0, sizeof(t1000_t));
cga_init(&t1000->cga);
t1000->internal = 1;
/* 16k video RAM */
t1000->vram = malloc(0x4000);
timer_add(t1000_poll, &t1000->cga.vidtime, TIMER_ALWAYS_ENABLED, t1000);
/* Occupy memory between 0xB8000 and 0xBFFFF */
mem_mapping_add(&t1000->mapping, 0xb8000, 0x8000, t1000_read, NULL, NULL, t1000_write, NULL, NULL, NULL, 0, t1000);
/* Respond to CGA I/O ports */
io_sethandler(0x03d0, 0x000c, t1000_in, NULL, NULL, t1000_out, NULL, NULL, t1000);
/* Default attribute mapping is 4 */
t1000->attrmap = 4;
t1000_recalcattrs(t1000);
/* Start off in 80x25 text mode */
t1000->cga.cgastat = 0xF4;
t1000->cga.vram = t1000->vram;
t1000->enabled = 1;
t1000->video_options = 0x01;
language = device_get_config_int("display_language") ? 2 : 0;
return t1000;
}
static void t1000_close(void *p)
{
t1000_t *t1000 = (t1000_t *)p;
free(t1000->vram);
free(t1000);
}
static void t1000_speed_changed(void *p)
{
t1000_t *t1000 = (t1000_t *)p;
t1000_recalctimings(t1000);
}
static device_config_t t1000_config[] =
{
{
.name = "display_language",
.description = "Language",
.type = CONFIG_SELECTION,
.selection =
{
{
.description = "USA",
.value = 0
},
{
.description = "Danish",
.value = 1
}
},
.default_int = 0
},
{
.type = -1
}
};
device_t t1000_video_device = {
"Toshiba T1000 Video",
0, 0,
t1000_init, t1000_close, NULL,
NULL,
t1000_speed_changed,
NULL,
NULL,
t1000_config
};
device_t t1200_video_device = {
"Toshiba T1200 Video",
0, 0,
t1000_init, t1000_close, NULL,
NULL,
t1000_speed_changed,
NULL,
NULL,
t1000_config
};

View File

@@ -8,7 +8,7 @@
*
* Handling of the emulated machines.
*
* Version: @(#)machine.c 1.0.29 2018/01/16
* Version: @(#)machine.c 1.0.30 2018/03/15
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -52,6 +52,9 @@ machine_init(void)
AT = IS_ARCH(machine, MACHINE_AT);
PCI = IS_ARCH(machine, MACHINE_PCI);
/* Resize the memory. */
mem_resize();
/* Load the machine's ROM BIOS. */
rom_load_bios(romset);
mem_add_bios();

View File

@@ -162,6 +162,8 @@ extern void machine_ps1_m2133_init(machine_t *);
extern void machine_ps2_m30_286_init(machine_t *);
extern void machine_ps2_model_50_init(machine_t *);
extern void machine_ps2_model_55sx_init(machine_t *);
extern void machine_ps2_model_70_type3_init(machine_t *);
extern void machine_ps2_model_70_type4_init(machine_t *);
extern void machine_ps2_model_80_init(machine_t *);
#ifdef WALTJE
extern void machine_ps2_model_80_486_init(machine_t *);

View File

@@ -11,7 +11,7 @@
* NOTES: OpenAT wip for 286-class machine with open BIOS.
* PS2_M80-486 wip, pending receipt of TRM's for machine.
*
* Version: @(#)machine_table.c 1.0.24 2018/03/06
* Version: @(#)machine_table.c 1.0.25 2018/03/13
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -44,14 +44,14 @@ machine_t machines[] = {
{ "[8088] Generic XT clone", ROM_GENXT, "genxt", {{"", cpus_8088}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA, 64, 640, 64, 0, machine_xt_init, NULL, NULL },
{ "[8088] Juko XT clone", ROM_JUKOPC, "jukopc", {{"", cpus_8088}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA, 64, 640, 64, 0, machine_xt_init, NULL, NULL },
{ "[8088] Phoenix XT clone", ROM_PXXT, "pxxt", {{"", cpus_8088}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA, 64, 640, 64, 0, machine_xt_init, NULL, NULL },
{ "[8088] Schneider EuroPC", ROM_EUROPC, "europc", {{"Siemens",cpus_europc}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA | MACHINE_HDC | MACHINE_VIDEO | MACHINE_MOUSE, 512, 640, 128, 0, machine_europc_init, NULL, NULL },
{ "[8088] Schneider EuroPC", ROM_EUROPC, "europc", {{"Siemens",cpus_europc}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA | MACHINE_HDC | MACHINE_VIDEO | MACHINE_MOUSE, 512, 640, 128, 15, machine_europc_init, NULL, NULL },
{ "[8088] Tandy 1000", ROM_TANDY, "tandy", {{"", cpus_8088}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA, 128, 640, 128, 0, machine_tandy1k_init, tandy1k_get_device, NULL },
{ "[8088] Tandy 1000 HX", ROM_TANDY1000HX, "tandy1000hx", {{"", cpus_8088}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA, 256, 640, 128, 0, machine_tandy1k_init, tandy1k_hx_get_device, NULL },
{ "[8088] Toshiba 1000", ROM_T1000, "t1000", {{"", cpus_8088}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA | MACHINE_VIDEO, 512, 1280, 768, 0, machine_xt_t1000_init, t1000_get_device, NULL },
{ "[8088] Toshiba 1000", ROM_T1000, "t1000", {{"", cpus_8088}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA | MACHINE_VIDEO, 512, 1280, 768, 63, machine_xt_t1000_init, NULL, NULL },
#if defined(DEV_BRANCH) && defined(USE_LASERXT)
{ "[8088] VTech Laser Turbo XT", ROM_LTXT, "ltxt", {{"", cpus_8088}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA, 512, 512, 256, 0, machine_xt_laserxt_init, NULL, NULL },
#endif
{ "[8088] Xi8088", ROM_XI8088, "xi8088", {{"", cpus_8088}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA | MACHINE_PS2, 64, 1024, 128, 127, machine_xt_xi8088_init, xi8088_get_device, nvr_at_close },
{ "[8088] Xi8088", ROM_XI8088, "xi8088", {{"", cpus_8088}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA | MACHINE_AT | MACHINE_PS2, 64, 1024, 128, 127, machine_xt_xi8088_init, NULL, nvr_at_close },
{ "[8086] Amstrad PC1512", ROM_PC1512, "pc1512", {{"", cpus_pc1512}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA | MACHINE_VIDEO | MACHINE_MOUSE, 512, 640, 128, 63, machine_amstrad_init, NULL, nvr_at_close },
{ "[8086] Amstrad PC1640", ROM_PC1640, "pc1640", {{"", cpus_8086}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA | MACHINE_VIDEO | MACHINE_MOUSE, 640, 640, 0, 63, machine_amstrad_init, NULL, nvr_at_close },
@@ -60,7 +60,7 @@ machine_t machines[] = {
{ "[8086] Amstrad PC20(0)", ROM_PC200, "pc200", {{"", cpus_8086}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA | MACHINE_VIDEO | MACHINE_MOUSE, 512, 640, 128, 63, machine_amstrad_init, NULL, nvr_at_close },
{ "[8086] Olivetti M24", ROM_OLIM24, "olivetti_m24", {{"", cpus_8086}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA | MACHINE_VIDEO | MACHINE_MOUSE, 128, 640, 128, 0, machine_olim24_init, NULL, NULL },
{ "[8086] Tandy 1000 SL/2", ROM_TANDY1000SL2, "tandy1000sl2", {{"", cpus_8086}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA, 512, 768, 128, 0, machine_tandy1k_init, NULL, NULL },
{ "[8086] Toshiba 1200", ROM_T1200, "t1200", {{"", cpus_8086}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA | MACHINE_VIDEO, 1024, 2048,1024, 0, machine_xt_t1200_init, t1200_get_device, NULL },
{ "[8086] Toshiba 1200", ROM_T1200, "t1200", {{"", cpus_8086}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA | MACHINE_VIDEO, 1024, 2048,1024, 63, machine_xt_t1200_init, NULL, NULL },
#if defined(DEV_BRANCH) && defined(USE_LASERXT)
{ "[8086] VTech Laser XT3", ROM_LXT3, "lxt3", {{"", cpus_8086}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA, 256, 512, 256, 0, machine_xt_laserxt_init, NULL, NULL },
#endif
@@ -104,6 +104,7 @@ machine_t machines[] = {
#if defined(DEV_BRANCH) && defined(USE_PORTABLE3)
{ "[386DX ISA] Compaq Portable III (386)", ROM_PORTABLEIII386, "portableiii386", {{"Intel", cpus_i386DX}, {"AMD", cpus_Am386DX}, {"Cyrix", cpus_486DLC}, {"", NULL}, {"", NULL}}, 1, MACHINE_ISA | MACHINE_AT | MACHINE_HDC | MACHINE_VIDEO, 1, 14, 1, 127, machine_at_compaq_init, NULL, nvr_at_close },
#endif
{ "[386DX MCA] IBM PS/2 model 70 (type 3)", ROM_IBMPS2_M70_TYPE3, "ibmps2_m70_type3", {{"Intel", cpus_i386DX}, {"AMD", cpus_Am386DX}, {"Cyrix", cpus_486DLC}, {"", NULL}, {"", NULL}}, 1, MACHINE_MCA | MACHINE_AT | MACHINE_PS2 | MACHINE_HDC_PS2, 2, 16, 2, 63, machine_ps2_model_70_type3_init, NULL, nvr_at_close },
{ "[386DX MCA] IBM PS/2 model 80", ROM_IBMPS2_M80, "ibmps2_m80", {{"Intel", cpus_i386DX}, {"AMD", cpus_Am386DX}, {"Cyrix", cpus_486DLC}, {"", NULL}, {"", NULL}}, 1, MACHINE_MCA | MACHINE_AT | MACHINE_PS2 | MACHINE_HDC_PS2, 1, 12, 1, 63, machine_ps2_model_80_init, NULL, nvr_at_close },
@@ -113,6 +114,8 @@ machine_t machines[] = {
{ "[486 ISA] DTK PKM-0038S E-2", ROM_DTK486, "dtk486", {{"Intel", cpus_i486}, {"AMD", cpus_Am486}, {"Cyrix", cpus_Cx486}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA | MACHINE_VLB | MACHINE_AT | MACHINE_HDC, 1, 128, 1, 127, machine_at_dtk486_init, NULL, nvr_at_close },
{ "[486 ISA] IBM PS/1 model 2133", ROM_IBMPS1_2133, "ibmps1_2133", {{"Intel", cpus_i486}, {"AMD", cpus_Am486}, {"Cyrix", cpus_Cx486}, {"", NULL}, {"", NULL}}, 0, MACHINE_ISA | MACHINE_VLB | MACHINE_AT | MACHINE_PS2 | MACHINE_HDC, 1, 64, 1, 127, machine_ps1_m2133_init, NULL, nvr_at_close },
{ "[486 MCA] IBM PS/2 model 70 (type 4)", ROM_IBMPS2_M70_TYPE4, "ibmps2_m70_type4", {{"Intel", cpus_i486}, {"AMD", cpus_Am486}, {"Cyrix", cpus_Cx486}, {"", NULL}, {"", NULL}}, 1, MACHINE_MCA | MACHINE_AT | MACHINE_PS2 | MACHINE_HDC_PS2, 2, 64, 2, 63, machine_ps2_model_70_type4_init, NULL, nvr_at_close },
#ifdef WALTJE
{ "[486 MCA] IBM PS/2 model 80-486", ROM_IBMPS2_M80_486, "ibmps2_m80-486", {{"Intel", cpus_i486}, {"AMD", cpus_Am486}, {"Cyrix", cpus_Cx486}, {"", NULL}, {"", NULL}}, 1, MACHINE_MCA | MACHINE_AT | MACHINE_PS2 | MACHINE_HDC_PS2, 1, 32, 1, 63, machine_ps2_model_80_486_init, NULL, nvr_at_close },
#endif
@@ -135,9 +138,7 @@ machine_t machines[] = {
{ "[Socket 5 FX] President Award 430FX PCI",ROM_PRESIDENT, "president", {{ "Intel", cpus_PentiumS5}, {"IDT", cpus_WinChip}, {"AMD", cpus_K5}, {"", NULL}, {"", NULL}}, 0, MACHINE_PCI | MACHINE_ISA | MACHINE_VLB | MACHINE_AT | MACHINE_HDC, 8, 128, 8, 127, machine_at_president_init, NULL, nvr_at_close },
{ "[Socket 7 FX] Intel Advanced/ATX", ROM_THOR, "thor", {{"Intel", cpus_Pentium}, {"IDT", cpus_WinChip}, {"AMD", cpus_K56}, {"Cyrix", cpus_6x86},{"", NULL}}, 0, MACHINE_PCI | MACHINE_ISA | MACHINE_VLB | MACHINE_AT | MACHINE_PS2 | MACHINE_HDC, 8, 128, 8, 127, machine_at_thor_init, NULL, nvr_at_close },
#if defined(DEV_BRANCH) && defined(USE_MRTHOR)
{ "[Socket 7 FX] MR Intel Advanced/ATX", ROM_MRTHOR, "mrthor", {{"Intel", cpus_Pentium}, {"IDT", cpus_WinChip}, {"AMD", cpus_K56}, {"Cyrix", cpus_6x86},{"", NULL}}, 0, MACHINE_PCI | MACHINE_ISA | MACHINE_VLB | MACHINE_AT | MACHINE_PS2 | MACHINE_HDC, 8, 128, 8, 127, machine_at_thor_init, NULL, nvr_at_close },
#endif
{ "[Socket 7 HX] Acer M3a", ROM_ACERM3A, "acerm3a", {{"Intel", cpus_Pentium}, {"IDT", cpus_WinChip}, {"AMD", cpus_K56}, {"Cyrix", cpus_6x86},{"", NULL}}, 0, MACHINE_PCI | MACHINE_ISA | MACHINE_VLB | MACHINE_AT | MACHINE_PS2 | MACHINE_HDC, 8, 192, 8, 127, machine_at_acerm3a_init, NULL, nvr_at_close },
{ "[Socket 7 HX] Acer V35n", ROM_ACERV35N, "acerv35n", {{"Intel", cpus_Pentium}, {"IDT", cpus_WinChip}, {"AMD", cpus_K56}, {"Cyrix", cpus_6x86},{"", NULL}}, 0, MACHINE_PCI | MACHINE_ISA | MACHINE_VLB | MACHINE_AT | MACHINE_PS2 | MACHINE_HDC, 8, 192, 8, 127, machine_at_acerv35n_init, NULL, nvr_at_close },
@@ -158,9 +159,7 @@ machine_t machines[] = {
{ "[Socket 5 FX] President Award 430FX PCI",ROM_PRESIDENT, "president", {{ "Intel", cpus_PentiumS5}, {"IDT", cpus_WinChip}, {"", NULL}, {"", NULL}, {"", NULL}}, 0, MACHINE_PCI | MACHINE_ISA | MACHINE_VLB | MACHINE_AT | MACHINE_HDC, 8, 128, 8, 127, machine_at_president_init, NULL, nvr_at_close },
{ "[Socket 7 FX] Intel Advanced/ATX", ROM_THOR, "thor", {{"Intel", cpus_Pentium}, {"IDT", cpus_WinChip}, {"Cyrix", cpus_6x86}, {"", NULL}, {"", NULL}}, 0, MACHINE_PCI | MACHINE_ISA | MACHINE_VLB | MACHINE_AT | MACHINE_PS2 | MACHINE_HDC, 8, 128, 8, 127, machine_at_thor_init, NULL, nvr_at_close },
#if defined(DEV_BRANCH) && defined(USE_MRTHOR)
{ "[Socket 7 FX] MR Intel Advanced/ATX", ROM_MRTHOR, "mrthor", {{"Intel", cpus_Pentium}, {"IDT", cpus_WinChip}, {"Cyrix", cpus_6x86}, {"", NULL}, {"", NULL}}, 0, MACHINE_PCI | MACHINE_ISA | MACHINE_VLB | MACHINE_AT | MACHINE_PS2 | MACHINE_HDC, 8, 128, 8, 127, machine_at_thor_init, NULL, nvr_at_close },
#endif
{ "[Socket 7 HX] Acer M3a", ROM_ACERM3A, "acerm3a", {{"Intel", cpus_Pentium}, {"IDT", cpus_WinChip}, {"Cyrix", cpus_6x86}, {"", NULL}, {"", NULL}}, 0, MACHINE_PCI | MACHINE_ISA | MACHINE_VLB | MACHINE_AT | MACHINE_PS2 | MACHINE_HDC, 8, 192, 8, 127, machine_at_acerm3a_init, NULL, nvr_at_close },
{ "[Socket 7 HX] Acer V35n", ROM_ACERV35N, "acerv35n", {{"Intel", cpus_Pentium}, {"IDT", cpus_WinChip}, {"Cyrix", cpus_6x86}, {"", NULL}, {"", NULL}}, 0, MACHINE_PCI | MACHINE_ISA | MACHINE_VLB | MACHINE_AT | MACHINE_PS2 | MACHINE_HDC, 8, 192, 8, 127, machine_at_acerv35n_init, NULL, nvr_at_close },

View File

@@ -3,3 +3,5 @@ extern void mca_add(uint8_t (*read)(int addr, void *priv), void (*write)(int add
extern void mca_set_index(int index);
extern uint8_t mca_read(uint16_t port);
extern void mca_write(uint16_t port, uint8_t val);
extern void ps2_cache_clean(void);

181
src/mem.c
View File

@@ -389,15 +389,14 @@ uint8_t *getpccache(uint32_t a)
}
a&=rammask;
if (isram[a>>16])
{
if ((a >> 16) != 0xF || shadowbios)
addreadlookup(a2, a);
return &ram[(uintptr_t)(a & 0xFFFFF000) - (uintptr_t)(a2 & ~0xFFF)];
}
if (_mem_exec[a >> 14])
{
if (_mem_mapping_r[a >> 14]->flags & MEM_MAPPING_ROM)
cpu_prefetch_cycles = cpu_rom_prefetch_cycles;
else
cpu_prefetch_cycles = cpu_mem_prefetch_cycles;
return &_mem_exec[a >> 14][(uintptr_t)(a & 0x3000) - (uintptr_t)(a2 & ~0xFFF)];
}
@@ -1282,24 +1281,24 @@ void mem_add_bios()
{
if (AT || (romset == ROM_XI8088 && xi8088_bios_128kb()))
{
mem_mapping_add(&bios_mapping[0], 0xe0000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom, MEM_MAPPING_EXTERNAL, 0);
mem_mapping_add(&bios_mapping[1], 0xe4000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x4000 & biosmask), MEM_MAPPING_EXTERNAL, 0);
mem_mapping_add(&bios_mapping[2], 0xe8000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x8000 & biosmask), MEM_MAPPING_EXTERNAL, 0);
mem_mapping_add(&bios_mapping[3], 0xec000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0xc000 & biosmask), MEM_MAPPING_EXTERNAL, 0);
mem_mapping_add(&bios_mapping[0], 0xe0000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom, MEM_MAPPING_EXTERNAL|MEM_MAPPING_ROM, 0);
mem_mapping_add(&bios_mapping[1], 0xe4000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x4000 & biosmask), MEM_MAPPING_EXTERNAL|MEM_MAPPING_ROM, 0);
mem_mapping_add(&bios_mapping[2], 0xe8000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x8000 & biosmask), MEM_MAPPING_EXTERNAL|MEM_MAPPING_ROM, 0);
mem_mapping_add(&bios_mapping[3], 0xec000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0xc000 & biosmask), MEM_MAPPING_EXTERNAL|MEM_MAPPING_ROM, 0);
}
mem_mapping_add(&bios_mapping[4], 0xf0000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x10000 & biosmask), MEM_MAPPING_EXTERNAL, 0);
mem_mapping_add(&bios_mapping[5], 0xf4000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x14000 & biosmask), MEM_MAPPING_EXTERNAL, 0);
mem_mapping_add(&bios_mapping[6], 0xf8000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x18000 & biosmask), MEM_MAPPING_EXTERNAL, 0);
mem_mapping_add(&bios_mapping[7], 0xfc000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x1c000 & biosmask), MEM_MAPPING_EXTERNAL, 0);
mem_mapping_add(&bios_mapping[4], 0xf0000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x10000 & biosmask), MEM_MAPPING_EXTERNAL|MEM_MAPPING_ROM, 0);
mem_mapping_add(&bios_mapping[5], 0xf4000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x14000 & biosmask), MEM_MAPPING_EXTERNAL|MEM_MAPPING_ROM, 0);
mem_mapping_add(&bios_mapping[6], 0xf8000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x18000 & biosmask), MEM_MAPPING_EXTERNAL|MEM_MAPPING_ROM, 0);
mem_mapping_add(&bios_mapping[7], 0xfc000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x1c000 & biosmask), MEM_MAPPING_EXTERNAL|MEM_MAPPING_ROM, 0);
mem_mapping_add(&bios_high_mapping[0], (AT && cpu_16bitbus) ? 0xfe0000 : 0xfffe0000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom, 0, 0);
mem_mapping_add(&bios_high_mapping[1], (AT && cpu_16bitbus) ? 0xfe4000 : 0xfffe4000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x4000 & biosmask), 0, 0);
mem_mapping_add(&bios_high_mapping[2], (AT && cpu_16bitbus) ? 0xfe8000 : 0xfffe8000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x8000 & biosmask), 0, 0);
mem_mapping_add(&bios_high_mapping[3], (AT && cpu_16bitbus) ? 0xfec000 : 0xfffec000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0xc000 & biosmask), 0, 0);
mem_mapping_add(&bios_high_mapping[4], (AT && cpu_16bitbus) ? 0xff0000 : 0xffff0000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x10000 & biosmask), 0, 0);
mem_mapping_add(&bios_high_mapping[5], (AT && cpu_16bitbus) ? 0xff4000 : 0xffff4000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x14000 & biosmask), 0, 0);
mem_mapping_add(&bios_high_mapping[6], (AT && cpu_16bitbus) ? 0xff8000 : 0xffff8000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x18000 & biosmask), 0, 0);
mem_mapping_add(&bios_high_mapping[7], (AT && cpu_16bitbus) ? 0xffc000 : 0xffffc000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x1c000 & biosmask), 0, 0);
mem_mapping_add(&bios_high_mapping[0], (AT && cpu_16bitbus) ? 0xfe0000 : 0xfffe0000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom, MEM_MAPPING_ROM, 0);
mem_mapping_add(&bios_high_mapping[1], (AT && cpu_16bitbus) ? 0xfe4000 : 0xfffe4000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x4000 & biosmask), MEM_MAPPING_ROM, 0);
mem_mapping_add(&bios_high_mapping[2], (AT && cpu_16bitbus) ? 0xfe8000 : 0xfffe8000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x8000 & biosmask), MEM_MAPPING_ROM, 0);
mem_mapping_add(&bios_high_mapping[3], (AT && cpu_16bitbus) ? 0xfec000 : 0xfffec000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0xc000 & biosmask), MEM_MAPPING_ROM, 0);
mem_mapping_add(&bios_high_mapping[4], (AT && cpu_16bitbus) ? 0xff0000 : 0xffff0000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x10000 & biosmask), MEM_MAPPING_ROM, 0);
mem_mapping_add(&bios_high_mapping[5], (AT && cpu_16bitbus) ? 0xff4000 : 0xffff4000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x14000 & biosmask), MEM_MAPPING_ROM, 0);
mem_mapping_add(&bios_high_mapping[6], (AT && cpu_16bitbus) ? 0xff8000 : 0xffff8000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x18000 & biosmask), MEM_MAPPING_ROM, 0);
mem_mapping_add(&bios_high_mapping[7], (AT && cpu_16bitbus) ? 0xffc000 : 0xffffc000, 0x04000, mem_read_bios, mem_read_biosw, mem_read_biosl, mem_write_null, mem_write_nullw, mem_write_nulll, rom + (0x1c000 & biosmask), MEM_MAPPING_ROM, 0);
}
int mem_a20_key = 0, mem_a20_alt = 0;
@@ -1318,6 +1317,45 @@ void mem_a20_init(void)
}
}
void mem_destroy_pages(void)
{
if (pages) {
free(pages);
pages = NULL;
}
}
void mem_resize_pages(void)
{
int total_size, c;
mem_destroy_pages();
if (AT) {
if (cpu_16bitbus)
total_size = 4096;
else {
total_size = (mem_size + 384) >> 2;
if ((total_size << 2) < (mem_size + 384))
total_size++;
if (total_size < 4096)
total_size = 4096;
}
} else
total_size = 256;
pages = malloc(total_size * sizeof(page_t));
memset(pages, 0, total_size * sizeof(page_t));
for (c = 0; c < total_size; c++)
{
pages[c].mem = &ram[c << 12];
pages[c].write_b = mem_write_ramb_page;
pages[c].write_w = mem_write_ramw_page;
pages[c].write_l = mem_write_raml_page;
}
}
void mem_init(void)
{
@@ -1339,22 +1377,13 @@ void mem_init(void)
writelookup2 = malloc(1024 * 1024 * sizeof(uintptr_t));
rom = NULL;
biosmask = 0xffff;
pages = malloc((1 << 20) * sizeof(page_t));
page_lookup = malloc((1 << 20) * sizeof(page_t *));
memset(pages, 0, (1 << 20) * sizeof(page_t));
memset(page_lookup, 0, (1 << 20) * sizeof(page_t *));
memset(ram_mapped_addr, 0, 64 * sizeof(uint32_t));
for (c = 0; c < (1 << 20); c++)
{
pages[c].mem = &ram[c << 12];
pages[c].write_b = mem_write_ramb_page;
pages[c].write_w = mem_write_ramw_page;
pages[c].write_l = mem_write_raml_page;
}
page_lookup = malloc((1 << 20) * sizeof(page_t *));
memset(page_lookup, 0, (1 << 20) * sizeof(page_t *));
mem_resize_pages();
memset(isram, 0, sizeof(isram));
for (c = 0; c < (mem_size / 64); c++)
@@ -1443,70 +1472,6 @@ void mem_remap_top_384k()
mem_remap_top(384);
}
void mem_split_enable(int max_size, uint32_t addr)
{
int c;
uint8_t *mem_split_buffer = &ram[0x80000];
if (split_mapping_enabled)
return;
#if 0
pclog("Split mapping enable at %08X\n", addr);
#endif
mem_set_mem_state(addr, max_size * 1024, MEM_READ_INTERNAL | MEM_WRITE_INTERNAL);
mem_mapping_set_addr(&ram_split_mapping, addr, max_size * 1024);
mem_mapping_set_exec(&ram_split_mapping, &ram[addr]);
if (max_size == 384)
memcpy(&ram[addr], mem_split_buffer, max_size);
else
memcpy(&ram[addr], &mem_split_buffer[128 * 1024], max_size);
for (c = ((addr / 1024) / 64); c < (((addr / 1024) + max_size - 1) / 64); c++)
{
isram[c] = 1;
}
flushmmucache();
split_mapping_enabled = 1;
}
void mem_split_disable(int max_size, uint32_t addr)
{
int c;
uint8_t *mem_split_buffer = &ram[0x80000];
if (!split_mapping_enabled)
return;
#if 0
pclog("Split mapping disable at %08X\n", addr);
#endif
if (max_size == 384)
memcpy(mem_split_buffer, &ram[addr], max_size);
else
memcpy(&mem_split_buffer[128 * 1024], &ram[addr], max_size);
mem_mapping_disable(&ram_split_mapping);
mem_set_mem_state(addr, max_size * 1024, 0);
mem_mapping_set_exec(&ram_split_mapping, NULL);
for (c = ((addr / 1024) / 64); c < (((addr / 1024) + max_size - 1) / 64); c++)
{
isram[c] = 0;
}
flushmmucache();
split_mapping_enabled = 0;
}
void mem_resize()
{
int c;
@@ -1525,15 +1490,6 @@ void mem_resize()
memset(ram, 0, (mem_size + 384) * 1024);
}
memset(pages, 0, (1 << 20) * sizeof(page_t));
for (c = 0; c < (1 << 20); c++)
{
pages[c].mem = &ram[c << 12];
pages[c].write_b = mem_write_ramb_page;
pages[c].write_w = mem_write_ramw_page;
pages[c].write_l = mem_write_raml_page;
}
memset(isram, 0, sizeof(isram));
for (c = 0; c < (mem_size / 64); c++)
{
@@ -1542,6 +1498,8 @@ void mem_resize()
isram[c] = 0;
}
mem_resize_pages();
memset(_mem_read_b, 0, sizeof(_mem_read_b));
memset(_mem_read_w, 0, sizeof(_mem_read_w));
memset(_mem_read_l, 0, sizeof(_mem_read_l));
@@ -1588,6 +1546,9 @@ void mem_resize()
void mem_reset_page_blocks()
{
int c;
if (!pages)
return;
for (c = 0; c < ((mem_size * 1024) >> 12); c++)
{

View File

@@ -68,6 +68,8 @@ typedef struct mem_mapping_t
#define MEM_MAPPING_EXTERNAL 1
/*Only present on internal bus (RAM)*/
#define MEM_MAPPING_INTERNAL 2
/*Executing from ROM may involve additional wait states*/
#define MEM_MAPPING_ROM 4
extern uint8_t *ram,*rom;
extern uint8_t romext[32768];
@@ -229,15 +231,15 @@ extern void flushmmucache_cr3(void);
extern void flushmmucache_nopc(void);
extern void mmu_invalidate(uint32_t addr);
extern void mem_split_enable(int max_size, uint32_t addr);
extern void mem_split_disable(int max_size, uint32_t addr);
extern void mem_add_bios(void);
extern void mem_init(void);
extern void mem_resize(void);
extern void mem_destroy_pages(void);
extern void mem_resize_pages(void);
extern uint8_t port_92_read(uint16_t port, void *priv);
extern void port_92_write(uint16_t port, uint8_t val, void *priv);
extern void port_92_clear_reset(void);

721
src/nvr.c
View File

@@ -1,200 +1,50 @@
/*
* 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.
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the 86Box distribution.
* This file is part of the VARCem Project.
*
* Implement a more-or-less defacto-standard RTC/NVRAM.
* Implement a generic NVRAM/CMOS/RTC device.
*
* When IBM released the PC/AT machine, it came standard with a
* battery-backed RTC chip to keep the time of day, something
* that was optional on standard PC's with a myriad variants
* being put on the market, often on cheap multi-I/O cards.
* NOTE: I should re-do 'intclk' using a TM struct.
*
* The PC/AT had an on-board DS12885-series chip ("the black
* block") which was an RTC/clock chip with onboard oscillator
* and a backup battery (hence the big size.) The chip also had
* a smal amount of RAM bytes available to the user, which was
* used by IBM's ROM BIOS to store machine configuration data.
* Version: @(#)nvr.c 1.0.2 2018/03/11
*
* Since then, pretty much any PC has an implementation of that
* device, which became known as the "nvr" or "cmos".
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
*
* NOTES Info extracted from the data sheets:
*
* * The century register at location 32h is a BCD register
* designed to automatically load the BCD value 20 as the
* year register changes from 99 to 00. The MSB of this
* register is not affected when the load of 20 occurs,
* and remains at the value written by the user.
*
* * Rate Selector (RS3:RS0)
* These four rate-selection bits select one of the 13
* taps on the 15-stage divider or disable the divider
* output. The tap selected can be used to generate an
* output square wave (SQW pin) and/or a periodic interrupt.
*
* The user can do one of the following:
* - enable the interrupt with the PIE bit;
* - enable the SQW output pin with the SQWE bit;
* - enable both at the same time and the same rate; or
* - enable neither.
*
* Table 3 lists the periodic interrupt rates and the square
* wave frequencies that can be chosen with the RS bits.
* These four read/write bits are not affected by !RESET.
*
* * Oscillator (DV2:DV0)
* These three bits are used to turn the oscillator on or
* off and to reset the countdown chain. A pattern of 010
* is the only combination of bits that turn the oscillator
* on and allow the RTC to keep time. A pattern of 11x
* enables the oscillator but holds the countdown chain in
* reset. The next update occurs at 500ms after a pattern
* of 010 is written to DV0, DV1, and DV2.
*
* * Update-In-Progress (UIP)
* This bit is a status flag that can be monitored. When the
* UIP bit is a 1, the update transfer occurs soon. When
* UIP is a 0, the update transfer does not occur for at
* least 244us. The time, calendar, and alarm information
* in RAM is fully available for access when the UIP bit
* is 0. The UIP bit is read-only and is not affected by
* !RESET. Writing the SET bit in Register B to a 1
* inhibits any update transfer and clears the UIP status bit.
*
* * Daylight Saving Enable (DSE)
* This bit is a read/write bit that enables two daylight
* saving adjustments when DSE is set to 1. On the first
* Sunday in April (or the last Sunday in April in the
* MC146818A), the time increments from 1:59:59 AM to
* 3:00:00 AM. On the last Sunday in October when the time
* first reaches 1:59:59 AM, it changes to 1:00:00 AM.
*
* When DSE is enabled, the internal logic test for the
* first/last Sunday condition at midnight. If the DSE bit
* is not set when the test occurs, the daylight saving
* function does not operate correctly. These adjustments
* do not occur when the DSE bit is 0. This bit is not
* affected by internal functions or !RESET.
*
* * 24/12
* The 24/12 control bit establishes the format of the hours
* byte. A 1 indicates the 24-hour mode and a 0 indicates
* the 12-hour mode. This bit is read/write and is not
* affected by internal functions or !RESET.
*
* * Data Mode (DM)
* This bit indicates whether time and calendar information
* is in binary or BCD format. The DM bit is set by the
* program to the appropriate format and can be read as
* required. This bit is not modified by internal functions
* or !RESET. A 1 in DM signifies binary data, while a 0 in
* DM specifies BCD data.
*
* * Square-Wave Enable (SQWE)
* When this bit is set to 1, a square-wave signal at the
* frequency set by the rate-selection bits RS3-RS0 is driven
* out on the SQW pin. When the SQWE bit is set to 0, the
* SQW pin is held low. SQWE is a read/write bit and is
* cleared by !RESET. SQWE is low if disabled, and is high
* impedance when VCC is below VPF. SQWE is cleared to 0 on
* !RESET.
*
* * Update-Ended Interrupt Enable (UIE)
* This bit is a read/write bit that enables the update-end
* flag (UF) bit in Register C to assert !IRQ. The !RESET
* pin going low or the SET bit going high clears the UIE bit.
* The internal functions of the device do not affect the UIE
* bit, but is cleared to 0 on !RESET.
*
* * Alarm Interrupt Enable (AIE)
* This bit is a read/write bit that, when set to 1, permits
* the alarm flag (AF) bit in Register C to assert !IRQ. An
* alarm interrupt occurs for each second that the three time
* bytes equal the three alarm bytes, including a don't-care
* alarm code of binary 11XXXXXX. The AF bit does not
* initiate the !IRQ signal when the AIE bit is set to 0.
* The internal functions of the device do not affect the AIE
* bit, but is cleared to 0 on !RESET.
*
* * Periodic Interrupt Enable (PIE)
* The PIE bit is a read/write bit that allows the periodic
* interrupt flag (PF) bit in Register C to drive the !IRQ pin
* low. When the PIE bit is set to 1, periodic interrupts are
* generated by driving the !IRQ pin low at a rate specified
* by the RS3-RS0 bits of Register A. A 0 in the PIE bit
* blocks the !IRQ output from being driven by a periodic
* interrupt, but the PF bit is still set at the periodic
* rate. PIE is not modified b any internal device functions,
* but is cleared to 0 on !RESET.
*
* * SET
* When the SET bit is 0, the update transfer functions
* normally by advancing the counts once per second. When
* the SET bit is written to 1, any update transfer is
* inhibited, and the program can initialize the time and
* calendar bytes without an update occurring in the midst of
* initializing. Read cycles can be executed in a similar
* manner. SET is a read/write bit and is not affected by
* !RESET or internal functions of the device.
*
* * Update-Ended Interrupt Flag (UF)
* This bit is set after each update cycle. When the UIE
* bit is set to 1, the 1 in UF causes the IRQF bit to be
* a 1, which asserts the !IRQ pin. This bit can be
* cleared by reading Register C or with a !RESET.
*
* * Alarm Interrupt Flag (AF)
* A 1 in the AF bit indicates that the current time has
* matched the alarm time. If the AIE bit is also 1, the
* !IRQ pin goes low and a 1 appears in the IRQF bit. This
* bit can be cleared by reading Register C or with a
* !RESET.
*
* * Periodic Interrupt Flag (PF)
* This bit is read-only and is set to 1 when an edge is
* detected on the selected tap of the divider chain. The
* RS3 through RS0 bits establish the periodic rate. PF is
* set to 1 independent of the state of the PIE bit. When
* both PF and PIE are 1s, the !IRQ signal is active and
* sets the IRQF bit. This bit can be cleared by reading
* Register C or with a !RESET.
*
* * Interrupt Request Flag (IRQF)
* The interrupt request flag (IRQF) is set to a 1 when one
* or more of the following are true:
* - PF == PIE == 1
* - AF == AIE == 1
* - UF == UIE == 1
* Any time the IRQF bit is a 1, the !IRQ pin is driven low.
* All flag bits are cleared after Register C is read by the
* program or when the !RESET pin is low.
*
* * Valid RAM and Time (VRT)
* This bit indicates the condition of the battery connected
* to the VBAT pin. This bit is not writeable and should
* always be 1 when read. If a 0 is ever present, an
* exhausted internal lithium energy source is indicated and
* both the contents of the RTC data and RAM data are
* questionable. This bit is unaffected by !RESET.
*
* This file implements an internal RTC clock, plus a generic
* version of the RTC/NVRAM chip, including the later update
* (DS12887A) which implemented a "century" register to be
* compatible with Y2K.
*
* Version: @(#)nvr.c 1.0.15 2018/02/26
*
* Authors: Miran Grca, <mgrca8@gmail.com>
* Mahod,
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2008-2018 Sarah Walker.
* Copyright 2016-2018 Miran Grca.
* Copyright 2017,2018 Fred N. van Kempen.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdint.h>
@@ -203,33 +53,37 @@
#include <time.h>
#include <wchar.h>
#include "86box.h"
#include "machine/machine.h"
#include "pic.h"
#include "pit.h"
#include "timer.h"
#include "machine/machine.h"
#include "plat.h"
#include "nvr.h"
int enable_sync; /* configuration variable: enable time sync */
int nvr_dosave; /* NVR is dirty, needs saved */
static nvr_t *saved_nvr = NULL;
static int8_t days_in_month[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
static struct {
/* Define the internal clock. */
typedef struct {
int16_t year;
int8_t sec;
int8_t min;
int8_t hour;
int8_t mday;
int8_t mon;
} intclk; /* the internal clock */
} intclk_t;
int enable_sync; /* configuration variable: enable time sync */
int nvr_dosave; /* NVR is dirty, needs saved */
static int8_t days_in_month[12] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
static intclk_t intclk;
static nvr_t *saved_nvr = NULL;
/* Determine whether or not the year is leap. */
static int8_t
is_leap(int8_t year)
int
nvr_is_leap(int year)
{
if (year % 400 == 0) return(1);
if (year % 100 == 0) return(0);
@@ -240,13 +94,13 @@ is_leap(int8_t year)
/* Determine the days in the current month. */
static int8_t
get_days(int8_t month, int8_t year)
int
nvr_get_days(int month, int year)
{
if (month != 2)
return(days_in_month[month - 1]);
return(is_leap(year) ? 29 : 28);
return(nvr_is_leap(year) ? 29 : 28);
}
@@ -267,7 +121,7 @@ rtc_tick(void)
intclk.hour = 0;
intclk.mday++;
}
if (intclk.mday == (get_days(intclk.mon, intclk.year) + 1)) {
if (intclk.mday == (nvr_get_days(intclk.mon, intclk.year) + 1)) {
intclk.mday = 1;
intclk.mon++;
}
@@ -278,107 +132,6 @@ rtc_tick(void)
}
/* Store the broken-down local time into the NVR. */
static void
rtc_getnvr(uint8_t *nvr, struct tm *tm)
{
if (nvr[RTC_REGB] & REGB_DM) {
/* NVR is in Binary data mode. */
nvr[RTC_SECONDS] = tm->tm_sec;
nvr[RTC_MINUTES] = tm->tm_min;
nvr[RTC_DOW] = tm->tm_wday+1;
nvr[RTC_DOM] = tm->tm_mday;
nvr[RTC_MONTH] = tm->tm_mon+1;
nvr[RTC_YEAR] = tm->tm_year%100;
if (nvr[RTC_REGB] & REGB_2412) {
/* NVR is in 24h mode. */
nvr[RTC_HOURS] = tm->tm_hour;
} else {
/* NVR is in 12h mode. */
nvr[RTC_HOURS] = (tm->tm_hour % 12) ? (tm->tm_hour % 12) : 12;
if (tm->tm_hour > 11)
nvr[RTC_HOURS] |= RTC_AMPM;
}
} else {
/* NVR is in BCD data mode. */
nvr[RTC_SECONDS] = RTC_BCD(tm->tm_sec);
nvr[RTC_MINUTES] = RTC_BCD(tm->tm_min);
nvr[RTC_DOW] = RTC_BCD(tm->tm_wday+1);
nvr[RTC_DOM] = RTC_BCD(tm->tm_mday);
nvr[RTC_MONTH] = RTC_BCD(tm->tm_mon+1);
nvr[RTC_YEAR] = RTC_BCD(tm->tm_year%100);
if (nvr[RTC_REGB] & REGB_2412) {
/* NVR is in 24h mode. */
nvr[RTC_HOURS] = RTC_BCD(tm->tm_hour);
} else {
/* NVR is in 12h mode. */
nvr[RTC_HOURS] = (tm->tm_hour % 12)
? RTC_BCD(tm->tm_hour % 12)
: RTC_BCD(12);
if (tm->tm_hour > 11)
nvr[RTC_HOURS] |= RTC_AMPM;
}
}
}
/* Load local time from the NVR. */
static void
rtc_setnvr(uint8_t *nvr)
{
int8_t temp;
if (nvr[RTC_REGB] & REGB_DM) {
intclk.sec = nvr[RTC_SECONDS];
intclk.min = nvr[RTC_MINUTES];
temp = nvr[RTC_HOURS];
intclk.mday = nvr[RTC_DOM];
intclk.mon = nvr[RTC_MONTH];
intclk.year = nvr[RTC_YEAR];
intclk.year += 1900;
} else {
intclk.sec = RTC_DCB(nvr[RTC_SECONDS]);
intclk.min = RTC_DCB(nvr[RTC_MINUTES]);
temp = RTC_DCB(nvr[RTC_HOURS]);
intclk.mday = RTC_DCB(nvr[RTC_DOM]);
intclk.mon = RTC_DCB(nvr[RTC_MONTH]);
intclk.year = RTC_DCB(nvr[RTC_YEAR]);
intclk.year += (RTC_DCB(nvr[RTC_CENTURY]) * 100);
}
/* Adjust for 12/24 hour mode. */
if (nvr[RTC_REGB] & REGB_2412)
intclk.hour = temp;
else
intclk.hour = ((temp & ~RTC_AMPM) % 12) + ((temp & RTC_AMPM) ? 12 : 0);
}
static void
rtc_sync(uint8_t *nvr)
{
struct tm *tm;
time_t now;
/* Get the current time of day, and convert to local time. */
(void)time(&now);
tm = localtime(&now);
/* Set the internal clock. */
intclk.sec = tm->tm_sec;
intclk.min = tm->tm_min;
intclk.hour = tm->tm_hour;
intclk.mday = tm->tm_mday;
intclk.mon = tm->tm_mon+1;
intclk.year = tm->tm_year+1900;
/* Set the NVR registers. */
rtc_getnvr(nvr, tm);
}
/* This is the RTC one-second timer. */
static void
onesec_timer(void *priv)
@@ -386,20 +139,13 @@ onesec_timer(void *priv)
nvr_t *nvr = (nvr_t *)priv;
if (++nvr->onesec_cnt >= 100) {
if (! (nvr->regs[RTC_REGB] & REGB_SET)) {
nvr->upd_stat = REGA_UIP;
/* Update the internal clock. */
rtc_tick();
/* Update the system RTC. */
rtc_tick();
/* Update the RTC device if needed. */
if (nvr->tick != NULL)
(*nvr->tick)(nvr);
if (nvr->hook != NULL)
(*nvr->hook)(nvr);
/* Re-calculate the timer. */
nvr_recalc();
nvr->upd_ecount = (int64_t)((244.0 + 1984.0) * TIMER_USEC);
}
nvr->onesec_cnt = 0;
}
@@ -407,241 +153,47 @@ onesec_timer(void *priv)
}
/* Check if the current time matches a set alarm time. */
static int8_t
check_alarm(nvr_t *nvr, int8_t addr)
{
#define ALARM_DONTCARE 0xc0
return((nvr->regs[addr+1] == nvr->regs[addr]) ||
((nvr->regs[addr+1] & ALARM_DONTCARE) == ALARM_DONTCARE));
}
/* This is the general update timer. */
static void
update_timer(void *priv)
{
nvr_t *nvr = (nvr_t *)priv;
struct tm tm;
int8_t dom, mon, sum, wd;
int16_t cent, yr;
if (! (nvr->regs[RTC_REGB] & REGB_SET)) {
/* Get the current time from the internal clock. */
tm.tm_sec = intclk.sec;
tm.tm_min = intclk.min;
tm.tm_hour = intclk.hour;
dom = intclk.mday;
mon = intclk.mon;
yr = intclk.year % 100;
cent = ((intclk.year - yr) / 100) % 4;
sum = dom+mon+yr+cent;
wd = ((sum + 6) % 7);
tm.tm_wday = wd;
tm.tm_mday = intclk.mday;
tm.tm_mon = intclk.mon-1;
tm.tm_year = intclk.year-1900;
rtc_getnvr(nvr->regs, &tm);
/* Clear update status. */
nvr->upd_stat = 0;
if (check_alarm(nvr, RTC_SECONDS) &&
check_alarm(nvr, RTC_MINUTES) &&
check_alarm(nvr, RTC_HOURS)) {
nvr->regs[RTC_REGC] |= REGC_AF;
if (nvr->regs[RTC_REGB] & REGB_AIE) {
nvr->regs[RTC_REGC] |= REGC_IRQF;
/* Generate an interrupt. */
if (nvr->irq != -1)
picint(1<<nvr->irq);
}
}
/*
* The flag and interrupt should be issued
* on update ended, not started.
*/
nvr->regs[RTC_REGC] |= REGC_UF;
if (nvr->regs[RTC_REGB] & REGB_UIE) {
nvr->regs[RTC_REGC] |= REGC_IRQF;
/* Generate an interrupt. */
if (nvr->irq != -1)
picint(1<<nvr->irq);
}
}
nvr->upd_ecount = 0;
}
static void
ticker_timer(void *priv)
{
nvr_t *nvr = (nvr_t *)priv;
int64_t c;
if (! (nvr->regs[RTC_REGA] & REGA_RS)) {
nvr->rtctime = 0x7fffffff;
return;
}
/* Update our ticker interval. */
c = 1 << ((nvr->regs[RTC_REGA] & REGA_RS) - 1);
nvr->rtctime += (int64_t)(RTCCONST*c*(1<<TIMER_SHIFT));
nvr->regs[RTC_REGC] |= REGC_PF;
if (nvr->regs[RTC_REGB] & REGB_PIE) {
nvr->regs[RTC_REGC] |= REGC_IRQF;
/* Generate an interrupt. */
if (nvr->irq != -1)
picint(1<<nvr->irq);
}
}
/* Set one of the chip's registers. */
static void
nvr_write(nvr_t *nvr, uint16_t reg, uint8_t val)
{
uint8_t old;
int64_t c;
old = nvr->regs[reg];
switch(reg) {
case RTC_REGA:
nvr->regs[reg] = val;
if (val & REGA_RS) {
c = 1 << ((val & REGA_RS) - 1);
nvr->rtctime += (int64_t)(RTCCONST*c*(1<<TIMER_SHIFT));
} else {
nvr->rtctime = 0x7fffffff;
}
break;
case RTC_REGB:
nvr->regs[reg] = val;
if (((old^val) & REGB_SET) && (val&REGB_SET)) {
/* This has to be done according to the datasheet. */
nvr->regs[RTC_REGA] &= ~REGA_UIP;
/* This also has to happen per the specification. */
nvr->regs[RTC_REGB] &= ~REGB_UIE;
}
break;
case RTC_REGC: /* R/O */
case RTC_REGD: /* R/O */
break;
default: /* non-RTC registers are just NVRAM */
if (nvr->regs[reg] != val) {
nvr->regs[reg] = val;
nvr_dosave = 1;
}
break;
}
if ((reg < RTC_REGA) || (reg == RTC_CENTURY)) {
if ((reg != 1) && (reg != 3) && (reg != 5)) {
if ((old != val) && !enable_sync) {
/* Update internal clock. */
rtc_setnvr(nvr->regs);
nvr_dosave = 1;
}
}
}
}
/* Get one of the chip's registers. */
static uint8_t
nvr_read(nvr_t *nvr, uint16_t reg)
{
uint8_t ret = 0xff;
switch(reg) {
case RTC_REGA:
ret = (nvr->regs[RTC_REGA] & 0x7f) | nvr->upd_stat;
break;
case RTC_REGC:
picintc(1<<nvr->irq);
ret = nvr->regs[RTC_REGC];
nvr->regs[RTC_REGC] = 0x00;
break;
case RTC_REGD:
nvr->regs[RTC_REGD] |= REGD_VRT;
ret = nvr->regs[RTC_REGD];
break;
default:
ret = nvr->regs[reg];
break;
}
return(ret);
}
/* Initialize the virtual RTC/NVRAM chip. */
/* Initialize the generic NVRAM/RTC device. */
void
nvr_init(nvr_t *nvr)
{
char temp[32];
int64_t c;
char temp[64];
struct tm *tm;
time_t now;
int c;
/* Clear some of it. */
nvr->upd_stat = 0;
nvr->upd_ecount = 0;
nvr->onesec_time = 0;
nvr->onesec_cnt = 0;
memset(&intclk, 0x00, sizeof(intclk));
/* Pre-initialize the NVR file's name here. */
sprintf(temp, "%s.nvr", machine_get_internal_name_ex(machine));
/* Set up the NVR file's name. */
sprintf(temp, "%s.nvr", machine_get_internal_name());
c = strlen(temp)+1;
nvr->fname = (wchar_t *)malloc(c*sizeof(wchar_t));
mbstowcs(nvr->fname, temp, c);
nvr->fn = (wchar_t *)malloc(c*sizeof(wchar_t));
mbstowcs(nvr->fn, temp, c);
/* Set up our local handlers. */
nvr->get = nvr_read;
nvr->set = nvr_write;
/* Initialize the internal clock as needed. */
memset(&intclk, 0x00, sizeof(intclk));
if (enable_sync) {
/* Get the current time of day, and convert to local time. */
(void)time(&now);
tm = localtime(&now);
/* Set up our timers. */
timer_add(ticker_timer, &nvr->rtctime, TIMER_ALWAYS_ENABLED, nvr);
/* Set the internal clock. */
nvr_time_set(tm);
} else {
/* Reset the internal clock to 1980/01/01 00:00. */
intclk.mon = 1;
intclk.year = 1980;
}
/* Set up our timer. */
timer_add(onesec_timer, &nvr->onesec_time, TIMER_ALWAYS_ENABLED, nvr);
timer_add(update_timer, &nvr->upd_ecount, &nvr->upd_ecount, nvr);
/* It does not need saving yet. */
nvr_dosave = 0;
/* Save the NVR data pointer. */
saved_nvr = nvr;
}
/* Re-calculate the timer values. */
void
nvr_recalc(void)
{
int64_t c, nt;
/* Make sure we have been initialized. */
if (saved_nvr == NULL) return;
c = 1 << ((saved_nvr->regs[RTC_REGA] & REGA_RS) - 1);
nt = (int64_t)(RTCCONST * c * (1<<TIMER_SHIFT));
if (saved_nvr->rtctime > nt)
saved_nvr->rtctime = nt;
/* Try to load the saved data. */
(void)nvr_load();
}
@@ -659,7 +211,6 @@ nvr_recalc(void)
int
nvr_load(void)
{
int64_t c;
FILE *f;
/* Make sure we have been initialized. */
@@ -669,41 +220,23 @@ nvr_load(void)
memset(saved_nvr->regs, 0xff, sizeof(saved_nvr->regs));
/* Set the defaults. */
memset(saved_nvr->regs, 0x00, RTC_REGS);
saved_nvr->regs[RTC_DOM] = 1;
saved_nvr->regs[RTC_MONTH] = 1;
saved_nvr->regs[RTC_YEAR] = RTC_BCD(80);
saved_nvr->regs[RTC_CENTURY] = RTC_BCD(19);
if (saved_nvr->load == NULL) {
/* We are responsible for loading. */
f = NULL;
if (saved_nvr->mask != 0) {
pclog("Opening NVR file: %ls...\n", nvr_path(saved_nvr->fname));
f = plat_fopen(nvr_path(saved_nvr->fname), L"rb");
}
if (saved_nvr->reset != NULL)
saved_nvr->reset(saved_nvr);
/* Load the (relevant) part of the NVR contents. */
if (saved_nvr->size != 0) {
pclog("NVR: loading from '%ls'\n", nvr_path(saved_nvr->fn));
f = plat_fopen(nvr_path(saved_nvr->fn), L"rb");
if (f != NULL) {
/* Read NVR contents from file. */
fread(saved_nvr->regs, sizeof(saved_nvr->regs), 1, f);
(void)fread(saved_nvr->regs, saved_nvr->size, 1, f);
(void)fclose(f);
}
} else {
/* OK, use alternate function. */
(*saved_nvr->load)(saved_nvr->fname);
}
/* Update the internal clock state based on the NVR registers. */
if (enable_sync)
rtc_sync(saved_nvr->regs);
else
rtc_setnvr(saved_nvr->regs);
/* Get the local RTC running! */
saved_nvr->regs[RTC_REGA] = (REGA_RS2|REGA_RS1);
saved_nvr->regs[RTC_REGB] = REGB_2412;
c = 1 << ((saved_nvr->regs[RTC_REGA] & REGA_RS) - 1);
saved_nvr->rtctime += (int64_t)(RTCCONST * c * (1<<TIMER_SHIFT));
if (saved_nvr->start != NULL)
saved_nvr->start(saved_nvr);
return(1);
}
@@ -718,22 +251,14 @@ nvr_save(void)
/* Make sure we have been initialized. */
if (saved_nvr == NULL) return(0);
if (saved_nvr->save == NULL) {
/* We are responsible for saving. */
f = NULL;
if (saved_nvr->mask != 0) {
pclog("Saving NVR file: %ls...\n", nvr_path(saved_nvr->fname));
f = plat_fopen(nvr_path(saved_nvr->fname), L"wb");
}
if (saved_nvr->size != 0) {
pclog("NVR: saving to '%ls'\n", nvr_path(saved_nvr->fn));
f = plat_fopen(nvr_path(saved_nvr->fn), L"wb");
if (f != NULL) {
/* Save NVR contents to file. */
(void)fwrite(saved_nvr->regs, sizeof(saved_nvr->regs), 1, f);
(void)fclose(f);
(void)fwrite(saved_nvr->regs, saved_nvr->size, 1, f);
fclose(f);
}
} else {
/* OK, use alternate function. */
(*saved_nvr->save)(saved_nvr->fname);
}
/* Device is clean again. */
@@ -743,6 +268,42 @@ nvr_save(void)
}
/* Get current time from internal clock. */
void
nvr_time_get(struct tm *tm)
{
int8_t dom, mon, sum, wd;
int16_t cent, yr;
tm->tm_sec = intclk.sec;
tm->tm_min = intclk.min;
tm->tm_hour = intclk.hour;
dom = intclk.mday;
mon = intclk.mon;
yr = (intclk.year % 100);
cent = ((intclk.year - yr) / 100) % 4;
sum = dom+mon+yr+cent;
wd = ((sum + 6) % 7);
tm->tm_wday = wd;
tm->tm_mday = intclk.mday;
tm->tm_mon = (intclk.mon - 1);
tm->tm_year = (intclk.year - 1900);
}
/* Set internal clock time. */
void
nvr_time_set(struct tm *tm)
{
intclk.sec = tm->tm_sec;
intclk.min = tm->tm_min;
intclk.hour = tm->tm_hour;
intclk.mday = tm->tm_mday;
intclk.mon = (tm->tm_mon + 1);
intclk.year = (tm->tm_year + 1900);
}
/* Get an absolute path to the NVR folder. */
wchar_t *
nvr_path(wchar_t *str)
@@ -759,11 +320,7 @@ nvr_path(wchar_t *str)
plat_dir_create(temp);
/* Now append the actual filename. */
#ifdef _WIN32
wcscat(temp, L"\\");
#else
wcscat(temp, L"/");
#endif
plat_path_slash(temp);
wcscat(temp, str);
return(temp);

218
src/nvr.h
View File

@@ -1,119 +1,99 @@
/*
* 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.
*
* Definitions for a defacto-standard RTC/NVRAM device.
*
* Version: @(#)nvr.h 1.0.5 2017/11/22
*
* Authors: Miran Grca, <mgrca8@gmail.com>
* Mahod,
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2016-2017 Miran Grca.
* Copyright 2016-2017 Mahod.
* Copyright 2017 Fred N. van Kempen.
*/
#ifndef EMU_NVR_H
# define EMU_NVR_H
/* Conversion from BCD to Binary and vice versa. */
#define RTC_BCD(x) (((x) % 10) | (((x) / 10) << 4))
#define RTC_DCB(x) ((((x) & 0xf0) >> 4) * 10 + ((x) & 0x0f))
/* RTC registers and bit definitions. */
#define RTC_SECONDS 0
#define RTC_ALSECONDS 1
#define RTC_MINUTES 2
#define RTC_ALMINUTES 3
#define RTC_HOURS 4
# define RTC_AMPM 0x80 /* PM flag if 12h format in use */
#define RTC_ALHOURS 5
#define RTC_DOW 6
#define RTC_DOM 7
#define RTC_MONTH 8
#define RTC_YEAR 9
#define RTC_REGA 10
# define REGA_UIP 0x80
# define REGA_DV2 0x40
# define REGA_DV1 0x20
# define REGA_DV0 0x10
# define REGA_DV 0x70
# define REGA_RS3 0x08
# define REGA_RS2 0x04
# define REGA_RS1 0x02
# define REGA_RS0 0x01
# define REGA_RS 0x0f
#define RTC_REGB 11
# define REGB_SET 0x80
# define REGB_PIE 0x40
# define REGB_AIE 0x20
# define REGB_UIE 0x10
# define REGB_SQWE 0x08
# define REGB_DM 0x04
# define REGB_2412 0x02
# define REGB_DSE 0x01
#define RTC_REGC 12
# define REGC_IRQF 0x80
# define REGC_PF 0x40
# define REGC_AF 0x20
# define REGC_UF 0x10
#define RTC_REGD 13
# define REGD_VRT 0x80
#define RTC_CENTURY 0x32 /* century register */
#define RTC_REGS 14 /* number of registers */
/* Define a (defacto-standard) RTC/NVRAM chip. */
typedef struct _nvr_ {
uint8_t regs[RTC_REGS+114]; /* these are the registers */
int64_t mask,
irq,
addr;
wchar_t *fname;
int64_t upd_stat,
upd_ecount,
onesec_time,
onesec_cnt,
rtctime,
oldmachine;
/* Hooks to internal RTC I/O functions. */
void (*set)(struct _nvr_ *, uint16_t, uint8_t);
uint8_t (*get)(struct _nvr_ *, uint16_t);
/* Hooks to alternative load/save functions. */
int8_t (*load)(wchar_t *fname);
int8_t (*save)(wchar_t *fname);
/* Hook to RTC ticker handler. */
void (*hook)(struct _nvr_ *);
} nvr_t;
extern int enable_sync;
extern int nvr_dosave;
extern void nvr_init(nvr_t *);
extern int nvr_load(void);
extern int nvr_save(void);
extern void nvr_recalc(void);
extern wchar_t *nvr_path(wchar_t *str);
extern FILE *nvr_fopen(wchar_t *str, wchar_t *mode);
extern void nvr_at_init(int64_t irq);
extern void nvr_at_close(void);
#endif /*EMU_NVR_H*/
/*
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Definitions for the generic NVRAM/CMOS driver.
*
* Version: @(#)nvr.h 1.0.2 2018/03/11
*
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2017,2018 Fred N. van Kempen.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef EMU_NVR_H
# define EMU_NVR_H
#define NVR_MAXSIZE 128 /* max size of NVR data */
/* Conversion from BCD to Binary and vice versa. */
#define RTC_BCD(x) (((x) % 10) | (((x) / 10) << 4))
#define RTC_DCB(x) ((((x) & 0xf0) >> 4) * 10 + ((x) & 0x0f))
#define RTC_BCDINC(x,y) RTC_BCD(RTC_DCB(x) + y)
/* Define a generic RTC/NVRAM device. */
typedef struct _nvr_ {
uint8_t regs[NVR_MAXSIZE]; /* these are the registers */
wchar_t *fn;
/* pathname of image file */
uint16_t size;
/* device configuration */
int8_t irq;
int8_t upd_stat, /* FIXME: move to private struct */
addr;
int64_t upd_ecount, /* FIXME: move to private struct */
onesec_time,
onesec_cnt,
rtctime;
/* Hooks to device functions. */
void (*reset)(struct _nvr_ *);
void (*start)(struct _nvr_ *);
void (*tick)(struct _nvr_ *);
} nvr_t;
extern int nvr_dosave;
extern void nvr_init(nvr_t *);
extern int nvr_load(void);
extern int nvr_save(void);
extern int nvr_is_leap(int year);
extern int nvr_get_days(int month, int year);
extern void nvr_time_get(struct tm *);
extern void nvr_time_set(struct tm *);
extern wchar_t *nvr_path(wchar_t *str);
extern FILE *nvr_fopen(wchar_t *str, wchar_t *mode);
extern void nvr_at_init(int irq);
extern void nvr_at_close(void);

View File

@@ -1,124 +1,661 @@
/*
* 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.
*
* IBM PC/AT RTC/NVRAM ("CMOS") emulation.
*
* The original PC/AT series had DS12885 series modules; later
* versions and clones used the 12886 and/or 1288(C)7 series,
* or the MC146818 series, all with an external battery. Many
* of those batteries would create corrosion issues later on
* in mainboard life...
*
* Version: @(#)nvr_at.c 1.0.9 2018/02/27
*
* Authors: Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2016-2018 Miran Grca.
* Copyright 2017,2018 Fred N. van Kempen.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include "cpu/cpu.h"
#include "io.h"
#include "device.h"
#include "machine/machine.h"
#include "mem.h"
#include "nmi.h"
#include "nvr.h"
#include "rom.h"
static nvr_t *nvrp;
static void
nvr_write(uint16_t addr, uint8_t val, void *priv)
{
nvr_t *nvr = (nvr_t *)priv;
cycles -= ISA_CYCLES(8);
if (! (addr & 1)) {
nvr->addr = (val & nvr->mask);
if (!(machines[machine].flags & MACHINE_MCA) && (romset != ROM_IBMPS1_2133))
nmi_mask = (~val & 0x80);
return;
}
/* Write the chip's registers. */
(*nvr->set)(nvr, nvr->addr, val);
}
static uint8_t
nvr_read(uint16_t addr, void *priv)
{
nvr_t *nvr = (nvr_t *)priv;
uint8_t ret;
cycles -= ISA_CYCLES(8);
if (addr & 1) {
/* Read from the chip's registers. */
ret = (*nvr->get)(nvr, nvr->addr);
} else {
ret = nvr->addr;
}
return(ret);
}
void
nvr_at_close(void)
{
if (nvrp == NULL)
return;
if (nvrp->fname != NULL)
free(nvrp->fname);
free(nvrp);
nvrp = NULL;
}
void
nvr_at_init(int64_t irq)
{
nvr_t *nvr;
/* Allocate an NVR for this machine. */
nvr = (nvr_t *)malloc(sizeof(nvr_t));
if (nvr == NULL) return;
memset(nvr, 0x00, sizeof(nvr_t));
/* This is machine specific. */
nvr->mask = machines[machine].nvrmask;
nvr->irq = irq;
/* Set up any local handlers here. */
/* Initialize the actual NVR. */
nvr_init(nvr);
/* Set up the PC/AT handler for this device. */
io_sethandler(0x0070, 2,
nvr_read, NULL, NULL, nvr_write, NULL, NULL, nvr);
/* Load the NVR into memory! */
(void)nvr_load();
nvrp = nvr;
}
/*
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Implement a more-or-less defacto-standard RTC/NVRAM.
*
* When IBM released the PC/AT machine, it came standard with a
* battery-backed RTC chip to keep the time of day, something
* that was optional on standard PC's with a myriad variants
* being put on the market, often on cheap multi-I/O cards.
*
* The PC/AT had an on-board DS12885-series chip ("the black
* block") which was an RTC/clock chip with onboard oscillator
* and a backup battery (hence the big size.) The chip also had
* a small amount of RAM bytes available to the user, which was
* used by IBM's ROM BIOS to store machine configuration data.
* Later versions and clones used the 12886 and/or 1288(C)7
* series, or the MC146818 series, all with an external battery.
* Many of those batteries would create corrosion issues later
* on in mainboard life...
*
* Since then, pretty much any PC has an implementation of that
* device, which became known as the "nvr" or "cmos".
*
* NOTES Info extracted from the data sheets:
*
* * The century register at location 32h is a BCD register
* designed to automatically load the BCD value 20 as the
* year register changes from 99 to 00. The MSB of this
* register is not affected when the load of 20 occurs,
* and remains at the value written by the user.
*
* * Rate Selector (RS3:RS0)
* These four rate-selection bits select one of the 13
* taps on the 15-stage divider or disable the divider
* output. The tap selected can be used to generate an
* output square wave (SQW pin) and/or a periodic interrupt.
*
* The user can do one of the following:
* - enable the interrupt with the PIE bit;
* - enable the SQW output pin with the SQWE bit;
* - enable both at the same time and the same rate; or
* - enable neither.
*
* Table 3 lists the periodic interrupt rates and the square
* wave frequencies that can be chosen with the RS bits.
* These four read/write bits are not affected by !RESET.
*
* * Oscillator (DV2:DV0)
* These three bits are used to turn the oscillator on or
* off and to reset the countdown chain. A pattern of 010
* is the only combination of bits that turn the oscillator
* on and allow the RTC to keep time. A pattern of 11x
* enables the oscillator but holds the countdown chain in
* reset. The next update occurs at 500ms after a pattern
* of 010 is written to DV0, DV1, and DV2.
*
* * Update-In-Progress (UIP)
* This bit is a status flag that can be monitored. When the
* UIP bit is a 1, the update transfer occurs soon. When
* UIP is a 0, the update transfer does not occur for at
* least 244us. The time, calendar, and alarm information
* in RAM is fully available for access when the UIP bit
* is 0. The UIP bit is read-only and is not affected by
* !RESET. Writing the SET bit in Register B to a 1
* inhibits any update transfer and clears the UIP status bit.
*
* * Daylight Saving Enable (DSE)
* This bit is a read/write bit that enables two daylight
* saving adjustments when DSE is set to 1. On the first
* Sunday in April (or the last Sunday in April in the
* MC146818A), the time increments from 1:59:59 AM to
* 3:00:00 AM. On the last Sunday in October when the time
* first reaches 1:59:59 AM, it changes to 1:00:00 AM.
*
* When DSE is enabled, the internal logic test for the
* first/last Sunday condition at midnight. If the DSE bit
* is not set when the test occurs, the daylight saving
* function does not operate correctly. These adjustments
* do not occur when the DSE bit is 0. This bit is not
* affected by internal functions or !RESET.
*
* * 24/12
* The 24/12 control bit establishes the format of the hours
* byte. A 1 indicates the 24-hour mode and a 0 indicates
* the 12-hour mode. This bit is read/write and is not
* affected by internal functions or !RESET.
*
* * Data Mode (DM)
* This bit indicates whether time and calendar information
* is in binary or BCD format. The DM bit is set by the
* program to the appropriate format and can be read as
* required. This bit is not modified by internal functions
* or !RESET. A 1 in DM signifies binary data, while a 0 in
* DM specifies BCD data.
*
* * Square-Wave Enable (SQWE)
* When this bit is set to 1, a square-wave signal at the
* frequency set by the rate-selection bits RS3-RS0 is driven
* out on the SQW pin. When the SQWE bit is set to 0, the
* SQW pin is held low. SQWE is a read/write bit and is
* cleared by !RESET. SQWE is low if disabled, and is high
* impedance when VCC is below VPF. SQWE is cleared to 0 on
* !RESET.
*
* * Update-Ended Interrupt Enable (UIE)
* This bit is a read/write bit that enables the update-end
* flag (UF) bit in Register C to assert !IRQ. The !RESET
* pin going low or the SET bit going high clears the UIE bit.
* The internal functions of the device do not affect the UIE
* bit, but is cleared to 0 on !RESET.
*
* * Alarm Interrupt Enable (AIE)
* This bit is a read/write bit that, when set to 1, permits
* the alarm flag (AF) bit in Register C to assert !IRQ. An
* alarm interrupt occurs for each second that the three time
* bytes equal the three alarm bytes, including a don't-care
* alarm code of binary 11XXXXXX. The AF bit does not
* initiate the !IRQ signal when the AIE bit is set to 0.
* The internal functions of the device do not affect the AIE
* bit, but is cleared to 0 on !RESET.
*
* * Periodic Interrupt Enable (PIE)
* The PIE bit is a read/write bit that allows the periodic
* interrupt flag (PF) bit in Register C to drive the !IRQ pin
* low. When the PIE bit is set to 1, periodic interrupts are
* generated by driving the !IRQ pin low at a rate specified
* by the RS3-RS0 bits of Register A. A 0 in the PIE bit
* blocks the !IRQ output from being driven by a periodic
* interrupt, but the PF bit is still set at the periodic
* rate. PIE is not modified b any internal device functions,
* but is cleared to 0 on !RESET.
*
* * SET
* When the SET bit is 0, the update transfer functions
* normally by advancing the counts once per second. When
* the SET bit is written to 1, any update transfer is
* inhibited, and the program can initialize the time and
* calendar bytes without an update occurring in the midst of
* initializing. Read cycles can be executed in a similar
* manner. SET is a read/write bit and is not affected by
* !RESET or internal functions of the device.
*
* * Update-Ended Interrupt Flag (UF)
* This bit is set after each update cycle. When the UIE
* bit is set to 1, the 1 in UF causes the IRQF bit to be
* a 1, which asserts the !IRQ pin. This bit can be
* cleared by reading Register C or with a !RESET.
*
* * Alarm Interrupt Flag (AF)
* A 1 in the AF bit indicates that the current time has
* matched the alarm time. If the AIE bit is also 1, the
* !IRQ pin goes low and a 1 appears in the IRQF bit. This
* bit can be cleared by reading Register C or with a
* !RESET.
*
* * Periodic Interrupt Flag (PF)
* This bit is read-only and is set to 1 when an edge is
* detected on the selected tap of the divider chain. The
* RS3 through RS0 bits establish the periodic rate. PF is
* set to 1 independent of the state of the PIE bit. When
* both PF and PIE are 1s, the !IRQ signal is active and
* sets the IRQF bit. This bit can be cleared by reading
* Register C or with a !RESET.
*
* * Interrupt Request Flag (IRQF)
* The interrupt request flag (IRQF) is set to a 1 when one
* or more of the following are true:
* - PF == PIE == 1
* - AF == AIE == 1
* - UF == UIE == 1
* Any time the IRQF bit is a 1, the !IRQ pin is driven low.
* All flag bits are cleared after Register C is read by the
* program or when the !RESET pin is low.
*
* * Valid RAM and Time (VRT)
* This bit indicates the condition of the battery connected
* to the VBAT pin. This bit is not writeable and should
* always be 1 when read. If a 0 is ever present, an
* exhausted internal lithium energy source is indicated and
* both the contents of the RTC data and RAM data are
* questionable. This bit is unaffected by !RESET.
*
* This file implements a generic version of the RTC/NVRAM chip,
* including the later update (DS12887A) which implemented a
* "century" register to be compatible with Y2K.
*
* Version: @(#)nvr_at.c 1.0.3 2018/03/11
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* Mahod,
* Sarah Walker, <tommowalker@tommowalker.co.uk>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2016-2018 Miran Grca.
* Copyright 2008-2018 Sarah Walker.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include <time.h>
#include "86box.h"
#include "cpu/cpu.h"
#include "machine/machine.h"
#include "io.h"
#include "pic.h"
#include "pit.h"
#include "mem.h"
#include "nmi.h"
#include "timer.h"
#include "device.h"
#include "nvr.h"
#include "rom.h"
/* RTC registers and bit definitions. */
#define RTC_SECONDS 0
#define RTC_ALSECONDS 1
#define RTC_MINUTES 2
#define RTC_ALMINUTES 3
#define RTC_HOURS 4
# define RTC_AMPM 0x80 /* PM flag if 12h format in use */
#define RTC_ALHOURS 5
#define RTC_DOW 6
#define RTC_DOM 7
#define RTC_MONTH 8
#define RTC_YEAR 9
#define RTC_REGA 10
# define REGA_UIP 0x80
# define REGA_DV2 0x40
# define REGA_DV1 0x20
# define REGA_DV0 0x10
# define REGA_DV 0x70
# define REGA_RS3 0x08
# define REGA_RS2 0x04
# define REGA_RS1 0x02
# define REGA_RS0 0x01
# define REGA_RS 0x0f
#define RTC_REGB 11
# define REGB_SET 0x80
# define REGB_PIE 0x40
# define REGB_AIE 0x20
# define REGB_UIE 0x10
# define REGB_SQWE 0x08
# define REGB_DM 0x04
# define REGB_2412 0x02
# define REGB_DSE 0x01
#define RTC_REGC 12
# define REGC_IRQF 0x80
# define REGC_PF 0x40
# define REGC_AF 0x20
# define REGC_UF 0x10
#define RTC_REGD 13
# define REGD_VRT 0x80
#define RTC_CENTURY 0x32 /* century register */
#define RTC_REGS 14 /* number of registers */
static nvr_t *nvrp;
/* Get the current NVR time. */
static void
time_get(uint8_t *regs, struct tm *tm)
{
int8_t temp;
if (regs[RTC_REGB] & REGB_DM) {
/* NVR is in Binary data mode. */
tm->tm_sec = regs[RTC_SECONDS];
tm->tm_min = regs[RTC_MINUTES];
temp = regs[RTC_HOURS];
tm->tm_wday = (regs[RTC_DOW] - 1);
tm->tm_mday = regs[RTC_DOM];
tm->tm_mon = (regs[RTC_MONTH] - 1);
tm->tm_year = regs[RTC_YEAR];
tm->tm_year += (regs[RTC_CENTURY] * 100) - 1900;
} else {
/* NVR is in BCD data mode. */
tm->tm_sec = RTC_DCB(regs[RTC_SECONDS]);
tm->tm_min = RTC_DCB(regs[RTC_MINUTES]);
temp = RTC_DCB(regs[RTC_HOURS]);
tm->tm_wday = (RTC_DCB(regs[RTC_DOW]) - 1);
tm->tm_mday = RTC_DCB(regs[RTC_DOM]);
tm->tm_mon = (RTC_DCB(regs[RTC_MONTH]) - 1);
tm->tm_year = RTC_DCB(regs[RTC_YEAR]);
tm->tm_year += (RTC_DCB(regs[RTC_CENTURY]) * 100) - 1900;
}
/* Adjust for 12/24 hour mode. */
if (regs[RTC_REGB] & REGB_2412)
tm->tm_hour = temp;
else
tm->tm_hour = ((temp & ~RTC_AMPM)%12) + ((temp&RTC_AMPM) ? 12 : 0);
}
/* Set the current NVR time. */
static void
time_set(uint8_t *regs, struct tm *tm)
{
int year = (tm->tm_year + 1900);
if (regs[RTC_REGB] & REGB_DM) {
/* NVR is in Binary data mode. */
regs[RTC_SECONDS] = tm->tm_sec;
regs[RTC_MINUTES] = tm->tm_min;
regs[RTC_DOW] = (tm->tm_wday + 1);
regs[RTC_DOM] = tm->tm_mday;
regs[RTC_MONTH] = (tm->tm_mon + 1);
regs[RTC_YEAR] = (year % 100);
regs[RTC_CENTURY] = (year / 100);
if (regs[RTC_REGB] & REGB_2412) {
/* NVR is in 24h mode. */
regs[RTC_HOURS] = tm->tm_hour;
} else {
/* NVR is in 12h mode. */
regs[RTC_HOURS] = (tm->tm_hour % 12) ? (tm->tm_hour % 12) : 12;
if (tm->tm_hour > 11)
regs[RTC_HOURS] |= RTC_AMPM;
}
} else {
/* NVR is in BCD data mode. */
regs[RTC_SECONDS] = RTC_BCD(tm->tm_sec);
regs[RTC_MINUTES] = RTC_BCD(tm->tm_min);
regs[RTC_DOW] = (RTC_BCD(tm->tm_wday) + 1);
regs[RTC_DOM] = RTC_BCD(tm->tm_mday);
regs[RTC_MONTH] = (RTC_BCD(tm->tm_mon) + 1);
regs[RTC_YEAR] = RTC_BCD(year % 100);
regs[RTC_CENTURY] = RTC_BCD(year / 100);
if (regs[RTC_REGB] & REGB_2412) {
/* NVR is in 24h mode. */
regs[RTC_HOURS] = RTC_BCD(tm->tm_hour);
} else {
/* NVR is in 12h mode. */
regs[RTC_HOURS] = (tm->tm_hour % 12)
? RTC_BCD(tm->tm_hour % 12)
: RTC_BCD(12);
if (tm->tm_hour > 11)
regs[RTC_HOURS] |= RTC_AMPM;
}
}
}
/* Check if the current time matches a set alarm time. */
static int8_t
check_alarm(uint8_t *regs, int8_t addr)
{
#define ALARM_DONTCARE 0xc0
return((regs[addr+1] == regs[addr]) ||
((regs[addr+1] & ALARM_DONTCARE) == ALARM_DONTCARE));
}
/* Update the NVR registers from the internal clock. */
static void
update_timer(void *priv)
{
nvr_t *nvr = (nvr_t *)priv;
struct tm tm;
if (! (nvr->regs[RTC_REGB] & REGB_SET)) {
/* Get the current time from the internal clock. */
nvr_time_get(&tm);
/* Update registers with current time. */
time_set(nvr->regs, &tm);
/* Clear update status. */
nvr->upd_stat = 0x00;
/* Check for any alarms we need to handle. */
if (check_alarm(nvr->regs, RTC_SECONDS) &&
check_alarm(nvr->regs, RTC_MINUTES) &&
check_alarm(nvr->regs, RTC_HOURS)) {
nvr->regs[RTC_REGC] |= REGC_AF;
if (nvr->regs[RTC_REGB] & REGB_AIE) {
nvr->regs[RTC_REGC] |= REGC_IRQF;
/* Generate an interrupt. */
if (nvr->irq != -1)
picint(1<<nvr->irq);
}
}
/*
* The flag and interrupt should be issued
* on update ended, not started.
*/
nvr->regs[RTC_REGC] |= REGC_UF;
if (nvr->regs[RTC_REGB] & REGB_UIE) {
nvr->regs[RTC_REGC] |= REGC_IRQF;
/* Generate an interrupt. */
if (nvr->irq != -1)
picint(1<<nvr->irq);
}
}
nvr->upd_ecount = 0;
}
/* Re-calculate the timer values. */
static void
rtc_timer_recalc(nvr_t *nvr, int add)
{
int64_t c, nt;
c = 1 << ((nvr->regs[RTC_REGA] & REGA_RS) - 1);
nt = (int64_t)(RTCCONST * c * (1<<TIMER_SHIFT));
if (add)
nvr->rtctime += nt;
else if (nvr->rtctime > nt)
nvr->rtctime = nt;
}
static void
rtc_timer(void *priv)
{
nvr_t *nvr = (nvr_t *)priv;
if (! (nvr->regs[RTC_REGA] & REGA_RS)) {
nvr->rtctime = 0x7fffffff;
return;
}
/* Update our timer interval. */
rtc_timer_recalc(nvr, 1);
nvr->regs[RTC_REGC] |= REGC_PF;
if (nvr->regs[RTC_REGB] & REGB_PIE) {
nvr->regs[RTC_REGC] |= REGC_IRQF;
/* Generate an interrupt. */
if (nvr->irq != -1)
picint(1<<nvr->irq);
}
}
/* Callback from internal clock, another second passed. */
static void
tick_timer(nvr_t *nvr)
{
if (nvr->regs[RTC_REGB] & REGB_SET) return;
nvr->upd_stat = REGA_UIP;
nvr->upd_ecount = (int64_t)((244.0 + 1984.0) * TIMER_USEC);
}
/* Write to one of the NVR registers. */
static void
nvr_write(uint16_t addr, uint8_t val, void *priv)
{
nvr_t *nvr = (nvr_t *)priv;
struct tm tm;
uint8_t old;
cycles -= ISA_CYCLES(8);
if (addr & 1) {
old = nvr->regs[nvr->addr];
switch(nvr->addr) {
case RTC_REGA:
nvr->regs[RTC_REGA] = val;
if (val & REGA_RS)
rtc_timer_recalc(nvr, 1);
else
nvr->rtctime = 0x7fffffff;
break;
case RTC_REGB:
nvr->regs[RTC_REGB] = val;
if (((old^val) & REGB_SET) && (val&REGB_SET)) {
/* According to the datasheet... */
nvr->regs[RTC_REGA] &= ~REGA_UIP;
nvr->regs[RTC_REGB] &= ~REGB_UIE;
}
break;
case RTC_REGC: /* R/O */
case RTC_REGD: /* R/O */
break;
default: /* non-RTC registers are just NVRAM */
if (nvr->regs[nvr->addr] != val) {
nvr->regs[nvr->addr] = val;
nvr_dosave = 1;
}
break;
}
if ((nvr->addr < RTC_REGA) || (nvr->addr == RTC_CENTURY)) {
if ((nvr->addr != 1) && (nvr->addr != 3) && (nvr->addr != 5)) {
if ((old != val) && !enable_sync) {
/* Update internal clock. */
time_get(nvr->regs, &tm);
nvr_time_set(&tm);
nvr_dosave = 1;
}
}
}
} else {
nvr->addr = (val & (nvr->size - 1));
if (!(machines[machine].flags & MACHINE_MCA) &&
(romset != ROM_IBMPS1_2133))
nmi_mask = (~val & 0x80);
}
}
/* Read from one of the NVR registers. */
static uint8_t
nvr_read(uint16_t addr, void *priv)
{
nvr_t *nvr = (nvr_t *)priv;
uint8_t ret;
cycles -= ISA_CYCLES(8);
if (addr & 1) switch(nvr->addr) {
case RTC_REGA:
ret = (nvr->regs[RTC_REGA] & 0x7f) | nvr->upd_stat;
break;
case RTC_REGC:
picintc(1<<nvr->irq);
ret = nvr->regs[RTC_REGC];
nvr->regs[RTC_REGC] = 0x00;
break;
case RTC_REGD:
nvr->regs[RTC_REGD] |= REGD_VRT;
ret = nvr->regs[RTC_REGD];
break;
default:
ret = nvr->regs[nvr->addr];
break;
} else {
ret = nvr->addr;
}
return(ret);
}
/* Reset the RTC state to 1980/01/01 00:00. */
static void
nvr_at_reset(nvr_t *nvr)
{
memset(nvr->regs, 0x00, RTC_REGS);
nvr->regs[RTC_DOM] = 1;
nvr->regs[RTC_MONTH] = 1;
nvr->regs[RTC_YEAR] = RTC_BCD(80);
nvr->regs[RTC_CENTURY] = RTC_BCD(19);
}
/* Process after loading from file. */
static void
nvr_at_start(nvr_t *nvr)
{
struct tm tm;
/* Initialize the internal and chip times. */
if (enable_sync) {
/* Use the internal clock's time. */
nvr_time_get(&tm);
time_set(nvr->regs, &tm);
} else {
/* Set the internal clock from the chip time. */
time_get(nvr->regs, &tm);
nvr_time_set(&tm);
}
/* Start the RTC. */
nvr->regs[RTC_REGA] = (REGA_RS2|REGA_RS1);
nvr->regs[RTC_REGB] = REGB_2412;
rtc_timer_recalc(nvr, 0);
}
void
nvr_at_init(int irq)
{
nvr_t *nvr;
/* Allocate an NVR for this machine. */
nvr = (nvr_t *)malloc(sizeof(nvr_t));
if (nvr == NULL) return;
memset(nvr, 0x00, sizeof(nvr_t));
/* This is machine specific. */
nvr->size = machines[machine].nvrmask + 1;
nvr->irq = irq;
/* Set up any local handlers here. */
nvr->reset = nvr_at_reset;
nvr->start = nvr_at_start;
nvr->tick = tick_timer;
/* Initialize the generic NVR. */
nvr_init(nvr);
/* Start the timers. */
timer_add(update_timer, &nvr->upd_ecount, &nvr->upd_ecount, nvr);
timer_add(rtc_timer, &nvr->rtctime, TIMER_ALWAYS_ENABLED, nvr);
/* Set up the I/O handler for this device. */
io_sethandler(0x0070, 2,
nvr_read,NULL,NULL, nvr_write,NULL,NULL, nvr);
nvrp = nvr;
}
void
nvr_at_close(void)
{
if (nvrp == NULL) return;
if (nvrp->fn != NULL)
free(nvrp->fn);
free(nvrp);
nvrp = NULL;

View File

@@ -1,116 +1,168 @@
/*
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Handling of the PS/2 series CMOS devices.
*
* Version: @(#)nvr_ps2.c 1.0.4 2018/03/13
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Sarah Walker, <tommowalker@tommowalker.co.uk>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2008-2018 Sarah Walker.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include "86box.h"
#include "machine/machine.h"
#include "cpu/cpu.h"
#include "machine/machine.h"
#include "device.h"
#include "io.h"
#include "mem.h"
#include "rom.h"
#include "nvr.h"
#include "nvr_ps2.h"
#include "rom.h"
typedef struct ps2_nvr_t
{
int addr;
uint8_t ram[8192];
typedef struct {
int addr;
uint8_t ram[8192];
} ps2_nvr_t;
static uint8_t ps2_nvr_read(uint16_t port, void *p)
static uint8_t
ps2_nvr_read(uint16_t port, void *priv)
{
ps2_nvr_t *nvr = (ps2_nvr_t *)p;
switch (port)
{
case 0x74:
return nvr->addr & 0xff;
case 0x75:
return nvr->addr >> 8;
case 0x76:
return nvr->ram[nvr->addr];
}
return 0xff;
ps2_nvr_t *nvr = (ps2_nvr_t *)priv;
uint8_t ret = 0xff;
switch (port) {
case 0x74:
ret = nvr->addr & 0xff;
break;
case 0x75:
ret = nvr->addr >> 8;
break;
case 0x76:
ret = nvr->ram[nvr->addr];
break;
}
return(ret);
}
static void ps2_nvr_write(uint16_t port, uint8_t val, void *p)
static void
ps2_nvr_write(uint16_t port, uint8_t val, void *priv)
{
ps2_nvr_t *nvr = (ps2_nvr_t *)p;
switch (port)
{
case 0x74:
nvr->addr = (nvr->addr & 0x1f00) | val;
break;
case 0x75:
nvr->addr = (nvr->addr & 0xff) | ((val & 0x1f) << 8);
break;
case 0x76:
nvr->ram[nvr->addr] = val;
break;
}
ps2_nvr_t *nvr = (ps2_nvr_t *)priv;
switch (port) {
case 0x74:
nvr->addr = (nvr->addr & 0x1f00) | val;
break;
case 0x75:
nvr->addr = (nvr->addr & 0xff) | ((val & 0x1f) << 8);
break;
case 0x76:
nvr->ram[nvr->addr] = val;
break;
}
}
static void *ps2_nvr_init(device_t *info)
static void *
ps2_nvr_init(device_t *info)
{
ps2_nvr_t *nvr = (ps2_nvr_t *)malloc(sizeof(ps2_nvr_t));
FILE *f = NULL;
ps2_nvr_t *nvr;
FILE *f = NULL;
memset(nvr, 0, sizeof(ps2_nvr_t));
io_sethandler(0x0074, 0x0003, ps2_nvr_read, NULL, NULL, ps2_nvr_write, NULL, NULL, nvr);
nvr = (ps2_nvr_t *)malloc(sizeof(ps2_nvr_t));
memset(nvr, 0x00, sizeof(ps2_nvr_t));
io_sethandler(0x0074, 3,
ps2_nvr_read,NULL,NULL, ps2_nvr_write,NULL,NULL, nvr);
switch (romset)
{
case ROM_IBMPS2_M80: f = nvr_fopen(L"ibmps2_m80_sec.nvr", L"rb"); break;
#ifdef WALTJE
case ROM_IBMPS2_M80_486: f = nvr_fopen(L"ibmps2_m80-486_sec.nvr", L"rb"); break;
#endif
}
if (f)
{
fread(nvr->ram, 8192, 1, f);
fclose(f);
}
else
memset(nvr->ram, 0xFF, 8192);
return nvr;
switch (romset) {
case ROM_IBMPS2_M80:
f = nvr_fopen(L"ibmps2_m80_sec.nvr", L"rb");
break;
}
memset(nvr->ram, 0xff, 8192);
if (f != NULL) {
(void)fread(nvr->ram, 8192, 1, f);
fclose(f);
}
return(nvr);
}
void ps2_nvr_close(void *p)
{
ps2_nvr_t *nvr = (ps2_nvr_t *)p;
FILE *f = NULL;
switch (romset)
{
case ROM_IBMPS2_M80: f = nvr_fopen(L"ibmps2_m80_sec.nvr", L"wb"); break;
#ifdef WALTJE
case ROM_IBMPS2_M80_486: f = nvr_fopen(L"ibmps2_m80-486_sec.nvr", L"wb"); break;
#endif
}
if (f)
{
fwrite(nvr->ram, 8192, 1, f);
fclose(f);
}
free(nvr);
static void
ps2_nvr_close(void *priv)
{
ps2_nvr_t *nvr = (ps2_nvr_t *)priv;
FILE *f = NULL;
switch (romset) {
case ROM_IBMPS2_M70_TYPE3:
f = nvr_fopen(L"ibmps2_m70_type3_sec.nvr", L"rb");
break;
case ROM_IBMPS2_M70_TYPE4:
f = nvr_fopen(L"ibmps2_m70_type4_sec.nvr", L"rb");
break;
case ROM_IBMPS2_M80:
f = nvr_fopen(L"ibmps2_m80_sec.nvr", L"wb");
break;
}
if (f != NULL) {
(void)fwrite(nvr->ram, 8192, 1, f);
fclose(f);
}
free(nvr->ram);
free(nvr);
}
device_t ps2_nvr_device =
{
"PS/2 NVRRAM",
0,
0,
ps2_nvr_init,
ps2_nvr_close,
NULL,
NULL, NULL, NULL, NULL
device_t ps2_nvr_device = {
"PS/2 Secondary NVRAM",
0, 0,
ps2_nvr_init, ps2_nvr_close, NULL,
NULL, NULL, NULL,
NULL
};

View File

@@ -1 +1,44 @@
extern device_t ps2_nvr_device;
/*
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Definitions for the PS/2 cmos/nvr device.
*
* Version: @(#)nvr_ps2.h 1.0.1 2018/02/14
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Sarah Walker, <tommowalker@tommowalker.co.uk>
*
* Copyright 2017,2018 Fred N. van Kempen.
* Copyright 2008-2018 Sarah Walker.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#ifndef EMU_NVRPS2_H
# define EMU_NVRPS2_H
extern device_t ps2_nvr_device;
#endif /*EMU_NVRPS2_H*/

View File

@@ -1,115 +0,0 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <wchar.h>
#include "86box.h"
#include "mem.h"
#include "io.h"
#include "nvr.h"
#include "nvr_tc8521.h"
#include "rtc_tc8521.h"
#include "pic.h"
#include "pit.h"
#include "plat.h"
#include "rom.h"
#include "timer.h"
#include "config.h"
#include "nmi.h"
#include "machine/machine.h"
int oldromset;
int nvrmask=63;
uint8_t nvrram[128];
int nvraddr;
int nvr_dosave = 0;
static int64_t nvr_onesec_time = 0, nvr_onesec_cnt = 0;
static void tc8521_onesec(void *p)
{
nvr_onesec_cnt++;
if (nvr_onesec_cnt >= 100)
{
tc8521_tick();
nvr_onesec_cnt = 0;
}
nvr_onesec_time += (int)(10000 * TIMER_USEC);
}
void write_tc8521(uint16_t addr, uint8_t val, void *priv)
{
uint8_t page = nvrram[0x0D] & 3;
addr &= 0x0F;
if (addr < 0x0D) addr += 16 * page;
if (addr >= 0x10 && nvrram[addr] != val)
nvr_dosave = 1;
nvrram[addr] = val;
}
uint8_t read_tc8521(uint16_t addr, void *priv)
{
uint8_t page = nvrram[0x0D] & 3;
addr &= 0x0F;
if (addr < 0x0D) addr += 16 * page;
return nvrram[addr];
}
void tc8521_loadnvr()
{
FILE *f;
nvrmask=63;
oldromset=romset;
switch (romset)
{
case ROM_T1000: f = plat_fopen(nvr_path(L"t1000.nvr"), L"rb"); break;
case ROM_T1200: f = plat_fopen(nvr_path(L"t1200.nvr"), L"rb"); break;
default: return;
}
if (!f)
{
memset(nvrram,0xFF,64);
nvrram[0x0E] = 0; /* Test register */
if (!enable_sync)
{
memset(nvrram, 0, 16);
}
return;
}
fread(nvrram,64,1,f);
if (enable_sync)
tc8521_internal_sync(nvrram);
else
tc8521_internal_set_nvrram(nvrram); /* Update the internal clock state based on the NVR registers. */
fclose(f);
}
void tc8521_savenvr()
{
FILE *f;
switch (oldromset)
{
case ROM_T1000: f = plat_fopen(nvr_path(L"t1000.nvr"), L"wb"); break;
case ROM_T1200: f = plat_fopen(nvr_path(L"t1200.nvr"), L"wb"); break;
default: return;
}
fwrite(nvrram,64,1,f);
fclose(f);
}
void nvr_tc8521_init()
{
io_sethandler(0x2C0, 0x10, read_tc8521, NULL, NULL, write_tc8521, NULL, NULL, NULL);
timer_add(tc8521_onesec, &nvr_onesec_time, TIMER_ALWAYS_ENABLED, NULL);
}

View File

@@ -1,12 +0,0 @@
void nvr_tc8521_init();
extern int enable_sync;
extern int nvr_dosave;
void tc8521_loadnvr();
void tc8521_savenvr();
void tc8521_nvr_recalc();
FILE *nvrfopen(char *fn, char *mode);

View File

@@ -8,7 +8,7 @@
*
* Main emulator module where most things are controlled.
*
* Version: @(#)pc.c 1.0.62 2018/03/06
* Version: @(#)pc.c 1.0.64 2018/03/15
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -126,6 +126,7 @@ int cpu_manufacturer = 0, /* (C) cpu manufacturer */
cpu_use_dynarec = 0, /* (C) cpu uses/needs Dyna */
cpu = 3, /* (C) cpu type */
enable_external_fpu = 0; /* (C) enable external FPU */
int enable_sync = 0; /* (C) enable time sync */
/* Statistics. */
@@ -500,8 +501,6 @@ pc_full_speed(void)
setpitclock(14318184.0);
}
atfullspeed = 1;
nvr_recalc();
}
@@ -512,8 +511,6 @@ pc_speed_changed(void)
setpitclock(machines[machine].cpu[cpu_manufacturer].cpus[cpu_effective].rspeed);
else
setpitclock(14318184.0);
nvr_recalc();
}
@@ -782,7 +779,6 @@ pc_reset_hard_init(void)
/* Reset the general machine support modules. */
io_init();
// cpu_set();
mem_resize();
timer_reset();
device_init();
@@ -970,6 +966,10 @@ pc_close(thread_t *ptr)
network_close();
sound_cd_thread_end();
mem_destroy_pages();
ide_destroy_buffers();
}

View File

@@ -13,7 +13,7 @@
* - c386sx16 BIOS fails checksum
* - the loadfont() calls should be done elsewhere
*
* Version: @(#)rom.c 1.0.35 2018/03/06
* Version: @(#)rom.c 1.0.35 2018/03/16
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -256,7 +256,7 @@ rom_init(rom_t *rom, wchar_t *fn, uint32_t addr, int sz, int mask, int off, uint
addr, sz,
rom_read, rom_readw, rom_readl,
mem_write_null, mem_write_nullw, mem_write_nulll,
rom->rom, flags, rom);
rom->rom, flags | MEM_MAPPING_ROM, rom);
return(0);
}
@@ -283,7 +283,7 @@ rom_init_interleaved(rom_t *rom, wchar_t *fnl, wchar_t *fnh, uint32_t addr, int
addr, sz,
rom_read, rom_readw, rom_readl,
mem_write_null, mem_write_nullw, mem_write_nulll,
rom->rom, flags, rom);
rom->rom, flags | MEM_MAPPING_ROM, rom);
return(0);
}
@@ -674,14 +674,11 @@ rom_load_bios(int rom_id)
break;
case ROM_SPC4216P:
if (rom_load_linear(
L"roms/machines/spc4216p/phoenix.bin",
0x000000, 65536, 0, rom)) return(1);
if (rom_load_interleaved(
if (! rom_load_interleaved(
L"roms/machines/spc4216p/7101.u8",
L"roms/machines/spc4216p/ac64.u10",
0x000000, 65536, 0, rom)) return(1);
break;
0x000000, 65536, 0, rom)) break;
return(1);
case ROM_KMXC02:
if (rom_load_linear(
@@ -720,6 +717,22 @@ rom_load_bios(int rom_id)
biosmask = 0x1ffff;
return(1);
case ROM_IBMPS2_M70_TYPE3:
if (! rom_load_interleaved(
L"roms/machines/ibmps2_m70_type3/70-a_even.bin",
L"roms/machines/ibmps2_m70_type3/70-a_odd.bin",
0x000000, 131072, 0, rom)) break;
biosmask = 0x1ffff;
return(1);
case ROM_IBMPS2_M70_TYPE4:
if (! rom_load_interleaved(
L"roms/machines/ibmps2_m70_type4/70-b_even.bin",
L"roms/machines/ibmps2_m70_type4/70-b_odd.bin",
0x000000, 131072, 0, rom)) break;
biosmask = 0x1ffff;
return(1);
case ROM_DTK486:
if (rom_load_linear(
L"roms/machines/dtk486/4siw005.bin",
@@ -848,14 +861,12 @@ rom_load_bios(int rom_id)
biosmask = 0x1ffff;
return(1);
#if defined(DEV_BRANCH) && defined(USE_MRTHOR)
case ROM_MRTHOR:
if (! rom_load_linear(
L"roms/machines/mrthor/mr_atx.bio",
0x000000, 131072, 0, rom)) break;
biosmask = 0x1ffff;
return(1);
#endif
case ROM_ZAPPA:
if (! rom_load_linear(
@@ -911,7 +922,6 @@ rom_load_bios(int rom_id)
if (!rom_load_linear(
L"roms/machines/t1000/t1000.rom",
0x000000, 32768, 0, rom)) break;
memcpy(rom + 0x8000, rom, 0x8000);
biosmask = 0x7fff;
return(1);
@@ -920,7 +930,6 @@ rom_load_bios(int rom_id)
if (!rom_load_linear(
L"roms/machines/t1200/t1200_019e.ic15.bin",
0x000000, 32768, 0, rom)) break;
memcpy(rom + 0x8000, rom, 0x8000);
biosmask = 0x7fff;
return(1);

View File

@@ -8,7 +8,7 @@
*
* Definitions for the ROM image handler.
*
* Version: @(#)rom.h 1.0.17 2018/03/06
* Version: @(#)rom.h 1.0.16 2018/03/02
*
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
* Copyright 2018 Fred N. van Kempen.
@@ -127,6 +127,9 @@ enum {
#endif
ROM_IBMPS1_2133,
ROM_IBMPS2_M70_TYPE3,
ROM_IBMPS2_M70_TYPE4,
#ifdef WALTJE
ROM_IBMPS2_M80_486,
#endif
@@ -147,9 +150,7 @@ enum {
ROM_PRESIDENT, /* President Award 430FX PCI/430FX/Award/Unknown SIO */
ROM_THOR, /* Intel Advanced_ATX/430FX/AMI/NS PC87306 */
#if defined(DEV_BRANCH) && defined(USE_MRTHOR)
ROM_MRTHOR, /* Intel Advanced_ATX/430FX/MR.BIOS/NS PC87306 */
#endif
ROM_ACERM3A, /* Acer M3A/430HX/Acer/SMC FDC37C932FR */
ROM_ACERV35N, /* Acer V35N/430HX/Acer/SMC FDC37C932FR */

View File

@@ -1,221 +0,0 @@
/* Emulation of:
Toshiba TC8521 Real Time Clock */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "nvr.h"
#include "rtc_tc8521.h"
#define peek2(a) (nvrram[(a##1)] + 10 * nvrram[(a##10)])
struct
{
int sec;
int min;
int hour;
int mday;
int mon;
int year;
} internal_clock;
/* Table for days in each month */
static int rtc_days_in_month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/* Called to determine whether the year is leap or not */
static int rtc_is_leap(int org_year)
{
if (org_year % 400 == 0) return 1;
if (org_year % 100 == 0) return 0;
if (org_year % 4 == 0) return 1;
return 0;
}
/* Called to determine the days in the current month */
static int rtc_get_days(int org_month, int org_year)
{
if (org_month != 2)
return rtc_days_in_month[org_month];
else
return rtc_is_leap(org_year) ? 29 : 28;
}
/* Called when the internal clock gets updated */
static void tc8521_recalc()
{
if (internal_clock.sec == 60)
{
internal_clock.sec = 0;
internal_clock.min++;
}
if (internal_clock.min == 60)
{
internal_clock.min = 0;
internal_clock.hour++;
}
if (internal_clock.hour == 24)
{
internal_clock.hour = 0;
internal_clock.mday++;
}
if (internal_clock.mday == (rtc_get_days(internal_clock.mon, internal_clock.year) + 1))
{
internal_clock.mday = 1;
internal_clock.mon++;
}
if (internal_clock.mon == 13)
{
internal_clock.mon = 1;
internal_clock.year++;
}
}
/* Called when ticking the second */
void tc8521_tick()
{
internal_clock.sec++;
tc8521_recalc();
}
/* Called when modifying the NVR registers */
void tc8521_update(uint8_t *nvrram, int reg)
{
int temp;
switch(reg)
{
case TC8521_SECOND1:
case TC8521_SECOND10:
internal_clock.sec = peek2(TC8521_SECOND);
break;
case TC8521_MINUTE1:
case TC8521_MINUTE10:
internal_clock.min = peek2(TC8521_MINUTE);
break;
case TC8521_HOUR1:
case TC8521_HOUR10:
temp = peek2(TC8521_HOUR);
if (nvrram[TC8521_24HR] & 1)
internal_clock.hour = temp;
else
internal_clock.hour = ((temp & ~0x20) % 12) + ((temp & 0x20) ? 12 : 0);
break;
case TC8521_DAY1:
case TC8521_DAY10:
internal_clock.mday = peek2(TC8521_DAY);
break;
case TC8521_MONTH1:
case TC8521_MONTH10:
internal_clock.mon = peek2(TC8521_MONTH);
break;
case TC8521_YEAR1:
case TC8521_YEAR10:
internal_clock.year = 1980 + peek2(TC8521_YEAR);
break;
}
}
/* Called to obtain the current day of the week based on the internal clock */
static int time_week_day()
{
int day_of_month = internal_clock.mday;
int month2 = internal_clock.mon;
int year2 = internal_clock.year % 100;
int century = ((internal_clock.year - year2) / 100) % 4;
int sum = day_of_month + month2 + year2 + century;
/* (Sum mod 7) gives 0 for Saturday, we need it for Sunday, so +6 for Saturday to get 6 and Sunday 0 */
int raw_wd = ((sum + 6) % 7);
return raw_wd;
}
/* Called to get time into the internal clock */
static void tc8521_internal_get(struct tm *time_var)
{
time_var->tm_sec = internal_clock.sec;
time_var->tm_min = internal_clock.min;
time_var->tm_hour = internal_clock.hour;
time_var->tm_wday = time_week_day();
time_var->tm_mday = internal_clock.mday;
time_var->tm_mon = internal_clock.mon - 1;
time_var->tm_year = internal_clock.year - 1900;
}
static void tc8521_internal_set(struct tm *time_var)
{
internal_clock.sec = time_var->tm_sec;
internal_clock.min = time_var->tm_min;
internal_clock.hour = time_var->tm_hour;
internal_clock.mday = time_var->tm_mday;
internal_clock.mon = time_var->tm_mon + 1;
internal_clock.year = time_var->tm_year + 1900;
}
static void tc8521_set_nvrram(uint8_t *nvrram, struct tm *cur_time_tm)
{
nvrram[TC8521_SECOND1] = cur_time_tm->tm_sec % 10;
nvrram[TC8521_SECOND10] = cur_time_tm->tm_sec / 10;
nvrram[TC8521_MINUTE1] = cur_time_tm->tm_min % 10;
nvrram[TC8521_MINUTE10] = cur_time_tm->tm_min / 10;
if (nvrram[TC8521_24HR] & 1)
{
nvrram[TC8521_HOUR1] = cur_time_tm->tm_hour % 10;
nvrram[TC8521_HOUR10] = cur_time_tm->tm_hour / 10;
}
else
{
nvrram[TC8521_HOUR1] = (cur_time_tm->tm_hour % 12) % 10;
nvrram[TC8521_HOUR10] = ((cur_time_tm->tm_hour % 12) / 10)
| (cur_time_tm->tm_hour >= 12) ? 2 : 0;
}
nvrram[TC8521_WEEKDAY] = cur_time_tm->tm_wday;
nvrram[TC8521_DAY1] = cur_time_tm->tm_mday % 10;
nvrram[TC8521_DAY10] = cur_time_tm->tm_mday / 10;
nvrram[TC8521_MONTH1] = (cur_time_tm->tm_mon + 1) / 10;
nvrram[TC8521_MONTH10] = (cur_time_tm->tm_mon + 1) % 10;
nvrram[TC8521_YEAR1] = (cur_time_tm->tm_year - 80) % 10;
nvrram[TC8521_YEAR10] = ((cur_time_tm->tm_year - 80) % 100) / 10;
}
void tc8521_internal_set_nvrram(uint8_t *nvrram)
{
/* Load the entire internal clock state from the NVR. */
internal_clock.sec = peek2(TC8521_SECOND);
internal_clock.min = peek2(TC8521_MINUTE);
if (nvrram[TC8521_24HR] & 1)
{
internal_clock.hour = peek2(TC8521_HOUR);
}
else
{
internal_clock.hour = (peek2(TC8521_HOUR) % 12)
+ (nvrram[TC8521_HOUR10] & 2) ? 12 : 0;
}
internal_clock.mday = peek2(TC8521_DAY);
internal_clock.mon = peek2(TC8521_MONTH);
internal_clock.year = 1980 + peek2(TC8521_YEAR);
}
void tc8521_internal_sync(uint8_t *nvrram)
{
struct tm *cur_time_tm;
time_t cur_time;
time(&cur_time);
cur_time_tm = localtime(&cur_time);
tc8521_internal_set(cur_time_tm);
tc8521_set_nvrram(nvrram, cur_time_tm);
}
void tc8521_get(uint8_t *nvrram)
{
struct tm cur_time_tm;
tc8521_internal_get(&cur_time_tm);
tc8521_set_nvrram(nvrram, &cur_time_tm);
}

View File

@@ -1,33 +0,0 @@
/* The TC8521 is a 4-bit RTC, so each memory location can only hold a single
* BCD digit. Hence everything has 'ones' and 'tens' digits. */
enum TC8521_ADDR
{
/* Page 0 registers */
TC8521_SECOND1,
TC8521_SECOND10,
TC8521_MINUTE1,
TC8521_MINUTE10,
TC8521_HOUR1,
TC8521_HOUR10,
TC8521_WEEKDAY,
TC8521_DAY1,
TC8521_DAY10,
TC8521_MONTH1,
TC8521_MONTH10,
TC8521_YEAR1,
TC8521_YEAR10,
TC8521_PAGE, /* PAGE register */
TC8521_TEST, /* TEST register */
TC8521_RESET, /* RESET register */
/* Page 1 registers */
TC8521_24HR = 0x1A,
TC8521_LEAPYEAR = 0x1B
};
void tc8521_tick();
void tc8521_update(uint8_t *nvrram, int reg);
void tc8521_get(uint8_t *nvrram);
void tc8521_internal_set_nvrram(uint8_t *nvrram);
void tc8521_internal_sync(uint8_t *nvrram);

View File

@@ -9,7 +9,7 @@
* Implementation of the SMC FDC37C932FR and FDC37C935 Super
* I/O Chips.
*
* Version: @(#)sio_fdc37c93x.c 1.0.10 2018/01/16
* Version: @(#)sio_fdc37c93x.c 1.0.11 2018/03/14
*
* Author: Miran Grca, <mgrca8@gmail.com>
* Copyright 2016-2018 Miran Grca.
@@ -36,7 +36,7 @@ static int fdc37c93x_curreg = 0;
static int fdc37c93x_gpio_reg = 0;
static uint8_t fdc37c93x_regs[48];
static uint8_t fdc37c93x_ld_regs[10][256];
static uint8_t fdc37c93x_gpio_base = 0x00EA;
static uint16_t fdc37c93x_gpio_base = 0x00EA;
static fdc_t *fdc37c93x_fdc;
static uint8_t tries;
@@ -122,12 +122,12 @@ static void fdc37c93x_auxio_handler(void)
{
uint16_t ld_port = 0;
uint8_t local_enable = !!fdc37c93x_ld_regs[3][0x30];
uint8_t local_enable = !!fdc37c93x_ld_regs[8][0x30];
io_removehandler(fdc37c93x_gpio_base, 0x0001, fdc37c93x_gpio_read, NULL, NULL, fdc37c93x_gpio_write, NULL, NULL, NULL);
if (local_enable)
{
fdc37c93x_gpio_base = ld_port = make_port(3);
fdc37c93x_gpio_base = ld_port = make_port(8);
/* pclog("fdc37c93x: Setting Auxiliary I/O port to %04X\n", ld_port); */
if ((ld_port >= 0x0100) && (ld_port <= 0x0FFF))
io_sethandler(fdc37c93x_gpio_base, 0x0001, fdc37c93x_gpio_read, NULL, NULL, fdc37c93x_gpio_write, NULL, NULL, NULL);
@@ -377,7 +377,7 @@ process_value:
}
break;
case 8:
/* Serial port 2 */
/* Auxiliary I/O */
switch(fdc37c93x_curreg)
{
case 0x30:

View File

@@ -3,6 +3,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <math.h>
#define HAVE_STDARG_H
#include "../86box.h"
#include "../device.h"
@@ -15,6 +16,12 @@
#include "snd_audiopci.h"
#define N 16
#define ES1371_NCoef 91
static float low_fir_es1371_coef[ES1371_NCoef];
typedef struct {
uint8_t pci_command, pci_serr;
@@ -55,6 +62,9 @@ typedef struct {
int16_t buffer_l[64], buffer_r[64];
int buffer_pos, buffer_pos_end;
int filtered_l[32], filtered_r[32];
int f_pos;
int16_t out_l, out_r;
int32_t vol_l, vol_r;
@@ -490,6 +500,7 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p)
case 0x71:
es1371->dac[0].vf = (es1371->dac[0].vf & ~0x1f8000) | ((val & 0xfc00) << 5);
es1371->dac[0].ac = (es1371->dac[0].ac & ~0x7f8000) | ((val & 0x00ff) << 15);
es1371->dac[0].f_pos = 0;
break;
case 0x72:
es1371->dac[0].ac = (es1371->dac[0].ac & ~0x7fff) | (val & 0x7fff);
@@ -501,6 +512,7 @@ static void es1371_outl(uint16_t port, uint32_t val, void *p)
case 0x75:
es1371->dac[1].vf = (es1371->dac[1].vf & ~0x1f8000) | ((val & 0xfc00) << 5);
es1371->dac[1].ac = (es1371->dac[1].ac & ~0x7f8000) | ((val & 0x00ff) << 15);
es1371->dac[1].f_pos = 0;
break;
case 0x76:
es1371->dac[1].ac = (es1371->dac[1].ac & ~0x7fff) | (val & 0x7fff);
@@ -996,15 +1008,60 @@ static void es1371_fetch(es1371_t *es1371, int dac_nr)
}
}
static void es1371_next_sample(es1371_t *es1371, int dac_nr)
static inline float low_fir_es1371(int dac_nr, int i, float NewSample)
{
static float x[2][2][128]; //input samples
static int x_pos[2] = {0, 0};
float out = 0.0;
int read_pos;
int n_coef;
int pos = x_pos[dac_nr];
x[dac_nr][i][pos] = NewSample;
/*Since only 1/16th of input samples are non-zero, only filter those that
are valid.*/
read_pos = (pos + 15) & (127 & ~15);
n_coef = (16 - pos) & 15;
while (n_coef < ES1371_NCoef)
{
out += low_fir_es1371_coef[n_coef] * x[dac_nr][i][read_pos];
read_pos = (read_pos + 16) & (127 & ~15);
n_coef += 16;
}
if (i == 1)
{
x_pos[dac_nr] = (x_pos[dac_nr] + 1) & 127;
if (x_pos[dac_nr] > 127)
x_pos[dac_nr] = 0;
}
return out;
}
static void es1371_next_sample_filtered(es1371_t *es1371, int dac_nr, int out_idx)
{
int out_l, out_r;
int c;
if ((es1371->dac[dac_nr].buffer_pos - es1371->dac[dac_nr].buffer_pos_end) >= 0)
{
es1371_fetch(es1371, dac_nr);
}
es1371->dac[dac_nr].out_l = es1371->dac[dac_nr].buffer_l[es1371->dac[dac_nr].buffer_pos & 63];
es1371->dac[dac_nr].out_r = es1371->dac[dac_nr].buffer_r[es1371->dac[dac_nr].buffer_pos & 63];
out_l = es1371->dac[dac_nr].buffer_l[es1371->dac[dac_nr].buffer_pos & 63];
out_r = es1371->dac[dac_nr].buffer_r[es1371->dac[dac_nr].buffer_pos & 63];
es1371->dac[dac_nr].filtered_l[out_idx] = (int)low_fir_es1371(dac_nr, 0, (float)out_l);
es1371->dac[dac_nr].filtered_r[out_idx] = (int)low_fir_es1371(dac_nr, 1, (float)out_r);
for (c = 1; c < 16; c++)
{
es1371->dac[dac_nr].filtered_l[out_idx+c] = (int)low_fir_es1371(dac_nr, 0, 0);
es1371->dac[dac_nr].filtered_r[out_idx+c] = (int)low_fir_es1371(dac_nr, 1, 0);
}
// audiopci_log("Use %02x %04x %04x\n", es1371->dac[dac_nr].buffer_pos & 63, es1371->dac[dac_nr].out_l, es1371->dac[dac_nr].out_r);
es1371->dac[dac_nr].buffer_pos++;
@@ -1012,20 +1069,64 @@ static void es1371_next_sample(es1371_t *es1371, int dac_nr)
}
//static FILE *es1371_f;//,*es1371_f2;
static void es1371_update(es1371_t *es1371)
{
int32_t l, r;
l = (es1371->dac[0].out_l * es1371->dac[0].vol_l) >> 12;
l += ((es1371->dac[1].out_l * es1371->dac[1].vol_l) >> 12);
r = (es1371->dac[0].out_r * es1371->dac[0].vol_r) >> 12;
r += ((es1371->dac[1].out_r * es1371->dac[1].vol_r) >> 12);
l >>= 1;
r >>= 1;
l = (l * es1371->master_vol_l) >> 15;
r = (r * es1371->master_vol_r) >> 15;
if (l < -32768)
l = -32768;
else if (l > 32767)
l = 32767;
if (r < -32768)
r = -32768;
else if (r > 32767)
r = 32767;
for (; es1371->pos < sound_pos_global; es1371->pos++)
{
es1371->buffer[es1371->pos*2] = l;
es1371->buffer[es1371->pos*2 + 1] = r;
}
}
static void es1371_poll(void *p)
{
es1371_t *es1371 = (es1371_t *)p;
es1371->dac[1].time += es1371->dac[1].latch;
es1371_update(es1371);
if (es1371->int_ctrl & INT_DAC1_EN)
{
int frac = es1371->dac[0].ac & 0x7fff;
int idx = es1371->dac[0].ac >> 15;
int samp1_l = es1371->dac[0].filtered_l[idx];
int samp1_r = es1371->dac[0].filtered_r[idx];
int samp2_l = es1371->dac[0].filtered_l[(idx + 1) & 31];
int samp2_r = es1371->dac[0].filtered_r[(idx + 1) & 31];
es1371->dac[0].out_l = ((samp1_l * (0x8000 - frac)) + (samp2_l * frac)) >> 15;
es1371->dac[0].out_r = ((samp1_r * (0x8000 - frac)) + (samp2_r * frac)) >> 15;
// audiopci_log("1Samp %i %i %08x\n", es1371->dac[0].curr_samp_ct, es1371->dac[0].samp_ct, es1371->dac[0].ac);
es1371->dac[0].ac += es1371->dac[0].vf;
if (es1371->dac[0].ac & (~0 << (15+4)))
es1371->dac[0].ac += es1371->dac[0].vf;
es1371->dac[0].ac &= ((32 << 15) - 1);
if ((es1371->dac[0].ac >> (15+4)) != es1371->dac[0].f_pos)
{
es1371->dac[0].ac &= ~(~0 << (15+4));
es1371_next_sample(es1371, 0);
es1371_next_sample_filtered(es1371, 0, es1371->dac[0].f_pos ? 16 : 0);
es1371->dac[0].f_pos = (es1371->dac[0].f_pos + 1) & 1;
es1371->dac[0].curr_samp_ct++;
if (es1371->dac[0].curr_samp_ct == es1371->dac[0].samp_ct)
@@ -1043,51 +1144,35 @@ static void es1371_poll(void *p)
if (es1371->int_ctrl & INT_DAC2_EN)
{
int frac = es1371->dac[1].ac & 0x7fff;
int idx = es1371->dac[1].ac >> 15;
int samp1_l = es1371->dac[1].filtered_l[idx];
int samp1_r = es1371->dac[1].filtered_r[idx];
int samp2_l = es1371->dac[1].filtered_l[(idx + 1) & 31];
int samp2_r = es1371->dac[1].filtered_r[(idx + 1) & 31];
es1371->dac[1].out_l = ((samp1_l * (0x8000 - frac)) + (samp2_l * frac)) >> 15;
es1371->dac[1].out_r = ((samp1_r * (0x8000 - frac)) + (samp2_r * frac)) >> 15;
// audiopci_log("2Samp %i %i %08x\n", es1371->dac[1].curr_samp_ct, es1371->dac[1].samp_ct, es1371->dac[1].ac);
es1371->dac[1].ac += es1371->dac[1].vf;
if (es1371->dac[1].ac & (~0 << (15+4)))
es1371->dac[1].ac &= ((32 << 15) - 1);
if ((es1371->dac[1].ac >> (15+4)) != es1371->dac[1].f_pos)
{
es1371->dac[1].ac &= ~(~0 << (15+4));
es1371_next_sample(es1371, 1);
es1371_next_sample_filtered(es1371, 1, es1371->dac[1].f_pos ? 16 : 0);
es1371->dac[1].f_pos = (es1371->dac[1].f_pos + 1) & 1;
es1371->dac[1].curr_samp_ct++;
if (es1371->dac[1].curr_samp_ct > es1371->dac[1].samp_ct)
{
es1371->dac[1].curr_samp_ct = 0;
// es1371->dac[1].curr_samp_ct = 0;
// audiopci_log("DAC2 IRQ\n");
es1371->int_status |= INT_STATUS_DAC2;
es1371_update_irqs(es1371);
}
if (es1371->dac[1].curr_samp_ct > es1371->dac[1].samp_ct)
es1371->dac[1].curr_samp_ct = 0;
}
}
for (; es1371->pos < sound_pos_global; es1371->pos++)
{
int32_t l, r;
l = (es1371->dac[0].out_l * es1371->dac[0].vol_l) >> 12;
l += ((es1371->dac[1].out_l * es1371->dac[1].vol_l) >> 12);
r = (es1371->dac[0].out_r * es1371->dac[0].vol_r) >> 12;
r += ((es1371->dac[1].out_r * es1371->dac[1].vol_r) >> 12);
l >>= 1;
r >>= 1;
l = (l * es1371->master_vol_l) >> 15;
r = (r * es1371->master_vol_r) >> 15;
if (l < -32768)
l = -32768;
else if (l > 32767)
l = 32767;
if (r < -32768)
r = -32768;
else if (r > 32767)
r = 32767;
es1371->buffer[es1371->pos*2] = l;
es1371->buffer[es1371->pos*2 + 1] = r;
}
}
static void es1371_get_buffer(int32_t *buffer, int len, void *p)
@@ -1095,12 +1180,48 @@ static void es1371_get_buffer(int32_t *buffer, int len, void *p)
es1371_t *es1371 = (es1371_t *)p;
int c;
es1371_update(es1371);
for (c = 0; c < len * 2; c++)
buffer[c] += (es1371->buffer[c] / 2);
es1371->pos = 0;
}
static inline double sinc(double x)
{
return sin(M_PI * x) / (M_PI * x);
}
static void generate_es1371_filter()
{
/*Cutoff frequency = 1 / 32*/
float fC = 1.0 / 32.0;
float gain;
int n;
for (n = 0; n < ES1371_NCoef; n++)
{
/*Blackman window*/
double w = 0.42 - (0.5 * cos((2.0*n*M_PI)/(double)(ES1371_NCoef-1))) + (0.08 * cos((4.0*n*M_PI)/(double)(ES1371_NCoef-1)));
/*Sinc filter*/
double h = sinc(2.0 * fC * ((double)n - ((double)(ES1371_NCoef-1) / 2.0)));
/*Create windowed-sinc filter*/
low_fir_es1371_coef[n] = w * h;
}
low_fir_es1371_coef[(ES1371_NCoef - 1) / 2] = 1.0;
gain = 0.0;
for (n = 0; n < ES1371_NCoef; n++)
gain += low_fir_es1371_coef[n] / (float)N;
/*Normalise filter, to produce unity gain*/
for (n = 0; n < ES1371_NCoef; n++)
low_fir_es1371_coef[n] /= gain;
}
static void *es1371_init()
{
es1371_t *es1371 = malloc(sizeof(es1371_t));
@@ -1111,6 +1232,8 @@ static void *es1371_init()
es1371->card = pci_add_card(PCI_ADD_NORMAL, es1371_pci_read, es1371_pci_write, es1371);
timer_add(es1371_poll, &es1371->dac[1].time, TIMER_ALWAYS_ENABLED, es1371);
generate_es1371_filter();
return es1371;
}
@@ -1128,6 +1251,54 @@ static void es1371_speed_changed(void *p)
es1371->dac[1].latch = (int)((double)TIMER_USEC * (1000000.0 / 48000.0));
}
void es1371_add_status_info_dac(es1371_t *es1371, char *s, int max_len, int dac_nr)
{
int ena = dac_nr ? INT_DAC2_EN : INT_DAC1_EN;
char *dac_name = dac_nr ? "DAC2 (Wave)" : "DAC1 (MIDI)";
char temps[128];
if (es1371->int_ctrl & ena)
{
int format = dac_nr ? ((es1371->si_cr >> 2) & 3) : (es1371->si_cr & 3);
double freq = 48000.0 * ((double)es1371->dac[dac_nr].vf / (32768.0 * 16.0));
switch (format)
{
case FORMAT_MONO_8:
snprintf(temps, 128, "%s format : 8-bit mono\n", dac_name);
break;
case FORMAT_STEREO_8:
snprintf(temps, 128, "%s format : 8-bit stereo\n", dac_name);
break;
case FORMAT_MONO_16:
snprintf(temps, 128, "%s format : 16-bit mono\n", dac_name);
break;
case FORMAT_STEREO_16:
snprintf(temps, 128, "%s format : 16-bit stereo\n", dac_name);
break;
}
strncat(s, temps, max_len);
max_len -= strlen(temps);
snprintf(temps, 128, "Playback frequency : %i Hz\n", (int)freq);
strncat(s, temps, max_len);
}
else
{
snprintf(temps, max_len, "%s stopped\n", dac_name);
strncat(s, temps, max_len);
}
}
void es1371_add_status_info(char *s, int max_len, void *p)
{
es1371_t *es1371 = (es1371_t *)p;
es1371_add_status_info_dac(es1371, s, max_len, 0);
es1371_add_status_info_dac(es1371, s, max_len, 1);
}
device_t es1371_device =
{
@@ -1140,6 +1311,6 @@ device_t es1371_device =
NULL,
es1371_speed_changed,
NULL,
NULL,
es1371_add_status_info,
NULL
};

View File

@@ -8,7 +8,7 @@
*
* ATI 18800 emulation (VGA Edge-16)
*
* Version: @(#)vid_ati18800.c 1.0.5 2018/02/07
* Version: @(#)vid_ati18800.c 1.0.7 2018/03/16
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -31,6 +31,7 @@
#include "vid_ati18800.h"
#include "vid_ati_eeprom.h"
#include "vid_svga.h"
#include "vid_svga_render.h"
#define BIOS_ROM_PATH_WONDER L"roms/video/ati18800/VGA_Wonder_V3-1.02.bin"
@@ -149,6 +150,39 @@ static uint8_t ati18800_in(uint16_t addr, void *p)
case 0x3D5:
temp = svga->crtc[svga->crtcreg];
break;
case 0x3DA:
svga->attrff = 0;
svga->attrff = 0;
svga->cgastat &= ~0x30;
/* copy color diagnostic info from the overscan color register */
switch (svga->attrregs[0x12] & 0x30)
{
case 0x00: /* P0 and P2 */
if (svga->attrregs[0x11] & 0x01)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x04)
svga->cgastat |= 0x20;
break;
case 0x10: /* P4 and P5 */
if (svga->attrregs[0x11] & 0x10)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x20)
svga->cgastat |= 0x20;
break;
case 0x20: /* P1 and P3 */
if (svga->attrregs[0x11] & 0x02)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x08)
svga->cgastat |= 0x20;
break;
case 0x30: /* P6 and P7 */
if (svga->attrregs[0x11] & 0x40)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x80)
svga->cgastat |= 0x20;
break;
}
return svga->cgastat;
default:
temp = svga_in(addr, svga);
break;
@@ -159,6 +193,28 @@ static uint8_t ati18800_in(uint16_t addr, void *p)
return temp;
}
static void ati18800_recalctimings(svga_t *svga)
{
ati18800_t *ati18800 = (ati18800_t *)svga->p;
if(svga->crtc[0x17] & 4)
{
svga->vtotal <<= 1;
svga->dispend <<= 1;
svga->vsyncstart <<= 1;
svga->split <<= 1;
svga->vblankstart <<= 1;
}
if (!svga->scrblank && (ati18800->regs[0xb0] & 0x20)) /*Extended 256 colour modes*/
{
svga->render = svga_render_8bpp_highres;
svga->bpp = 8;
svga->rowoffset <<= 1;
svga->ma <<= 1;
}
}
static void *ati18800_init(device_t *info)
{
ati18800_t *ati18800 = malloc(sizeof(ati18800_t));
@@ -178,7 +234,7 @@ static void *ati18800_init(device_t *info)
};
svga_init(&ati18800->svga, ati18800, 1 << 19, /*512kb*/
NULL,
ati18800_recalctimings,
ati18800_in, ati18800_out,
NULL,
NULL);

View File

@@ -8,7 +8,7 @@
*
* ATI 28800 emulation (VGA Charger and Korean VGA)
*
* Version: @(#)vid_ati28800.c 1.0.9 2018/03/05
* Version: @(#)vid_ati28800.c 1.0.12 2018/03/16
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -59,7 +59,7 @@ typedef struct ati28800_t
uint8_t regs[256];
int index;
uint32_t memory;
uint32_t memory;
} ati28800_t;
@@ -69,7 +69,7 @@ int in_get_korean_font_kind_set;
int get_korean_font_enabled;
int get_korean_font_index;
uint16_t get_korean_font_base;
extern int dbcs_mode_enabled;
int ksc5601_mode_enabled;
static void ati28800_out(uint16_t addr, uint8_t val, void *p)
@@ -106,9 +106,14 @@ static void ati28800_out(uint16_t addr, uint8_t val, void *p)
case 0xb3:
ati_eeprom_write(&ati28800->eeprom, val & 8, val & 2, val & 1);
break;
case 0xb6:
if((old ^ val) & 0x10) svga_recalctimings(svga);
break;
case 0xb6:
if((old ^ val) & 0x10) svga_recalctimings(svga);
break;
case 0xb8:
if((old ^ val) & 0x40) svga_recalctimings(svga);
break;
case 0xb9:
if((old ^ val) & 2) svga_recalctimings(svga);
}
break;
@@ -149,7 +154,7 @@ void ati28800k_out(uint16_t addr, uint8_t val, void *p)
case 0x1CF:
if(ati28800->index == 0xBF && ((ati28800->regs[0xBF] ^ val) & 0x20))
{
dbcs_mode_enabled = val & 0x20;
ksc5601_mode_enabled = val & 0x20;
svga_recalctimings(svga);
}
@@ -241,6 +246,39 @@ static uint8_t ati28800_in(uint16_t addr, void *p)
case 0x3D5:
temp = svga->crtc[svga->crtcreg];
break;
case 0x3DA:
svga->attrff = 0;
svga->attrff = 0;
svga->cgastat &= ~0x30;
/* copy color diagnostic info from the overscan color register */
switch (svga->attrregs[0x12] & 0x30)
{
case 0x00: /* P0 and P2 */
if (svga->attrregs[0x11] & 0x01)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x04)
svga->cgastat |= 0x20;
break;
case 0x10: /* P4 and P5 */
if (svga->attrregs[0x11] & 0x10)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x20)
svga->cgastat |= 0x20;
break;
case 0x20: /* P1 and P3 */
if (svga->attrregs[0x11] & 0x02)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x08)
svga->cgastat |= 0x20;
break;
case 0x30: /* P6 and P7 */
if (svga->attrregs[0x11] & 0x40)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x80)
svga->cgastat |= 0x20;
break;
}
return svga->cgastat;
default:
temp = svga_in(addr, svga);
break;
@@ -268,7 +306,7 @@ uint8_t ati28800k_in(uint16_t addr, void *p)
switch(get_korean_font_kind >> 8)
{
case 4: /* ROM font */
temp = fontdatksc5601[get_korean_font_base][get_korean_font_index++];
temp = fontdatksc5601[get_korean_font_base].chr[get_korean_font_index++];
break;
case 2: /* User defined font - TODO : Should be implemented later */
temp = 0;
@@ -291,13 +329,44 @@ static void ati28800_recalctimings(svga_t *svga)
{
ati28800_t *ati28800 = (ati28800_t *)svga->p;
if (ati28800->regs[0xb6] & 0x10)
{
switch(((ati28800->regs[0xbe] & 0x10) >> 1) | ((ati28800->regs[0xb9] & 2) << 1) | ((svga->miscout & 0x0C) >> 2))
{
case 0x00: svga->clock = cpuclock / 42954000.0; break;
case 0x01: svga->clock = cpuclock / 48771000.0; break;
case 0x03: svga->clock = cpuclock / 36000000.0; break;
case 0x04: svga->clock = cpuclock / 50350000.0; break;
case 0x05: svga->clock = cpuclock / 56640000.0; break;
case 0x07: svga->clock = cpuclock / 44900000.0; break;
case 0x08: svga->clock = cpuclock / 30240000.0; break;
case 0x09: svga->clock = cpuclock / 32000000.0; break;
case 0x0A: svga->clock = cpuclock / 37500000.0; break;
case 0x0B: svga->clock = cpuclock / 39000000.0; break;
case 0x0C: svga->clock = cpuclock / 40000000.0; break;
case 0x0D: svga->clock = cpuclock / 56644000.0; break;
case 0x0E: svga->clock = cpuclock / 75000000.0; break;
case 0x0F: svga->clock = cpuclock / 65000000.0; break;
default: break;
}
if(ati28800->regs[0xb8] & 0x40) svga->clock *= 2;
if (ati28800->regs[0xb6] & 0x10)
{
svga->hdisp <<= 1;
svga->htotal <<= 1;
svga->rowoffset <<= 1;
}
}
if(svga->crtc[0x17] & 4)
{
svga->vtotal <<= 1;
svga->dispend <<= 1;
svga->vsyncstart <<= 1;
svga->split <<= 1;
svga->vblankstart <<= 1;
}
if (!svga->scrblank && (ati28800->regs[0xb0] & 0x20)) /*Extended 256 colour modes*/
{
svga->render = svga_render_8bpp_highres;
@@ -307,6 +376,16 @@ static void ati28800_recalctimings(svga_t *svga)
}
}
void ati28800k_recalctimings(svga_t *svga)
{
ati28800_recalctimings(svga);
if (svga->render == svga_render_text_80 && ksc5601_mode_enabled)
{
svga->render = svga_render_text_80_ksc5601;
}
}
void *
ati28800k_init(device_t *info)
{
@@ -321,13 +400,13 @@ ati28800k_init(device_t *info)
get_korean_font_enabled = 0;
get_korean_font_kind = 0;
in_get_korean_font_kind_set = 0;
dbcs_mode_enabled = 0;
ksc5601_mode_enabled = 0;
rom_init(&ati28800->bios_rom, BIOS_ATIKOR_PATH, 0xc0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL);
loadfont(FONT_ATIKOR_PATH, 6);
svga_init(&ati28800->svga, ati28800, ati28800->memory << 10, /*Memory size, default 512KB*/
ati28800_recalctimings,
ati28800k_recalctimings,
ati28800k_in, ati28800k_out,
NULL,
NULL);
@@ -457,6 +536,17 @@ ati28800_force_redraw(void *priv)
ati->svga.fullchange = changeframecount;
}
void ati28800k_add_status_info(char *s, int max_len, void *p)
{
ati28800_t *ati28800 = (ati28800_t *)p;
char temps[128];
svga_add_status_info(s, max_len, &ati28800->svga);
sprintf(temps, "Korean SVGA mode enabled : %s\n\n", ksc5601_mode_enabled ? "Yes" : "No");
strncat(s, temps, max_len);
}
static void ati28800_add_status_info(char *s, int max_len, void *priv)
{
ati28800_t *ati = (ati28800_t *)priv;
@@ -537,7 +627,7 @@ device_t ati28800k_device =
ati28800k_available,
ati28800_speed_changed,
ati28800_force_redraw,
ati28800_add_status_info,
ati28800k_add_status_info,
ati28800_config
};

View File

@@ -8,7 +8,7 @@
*
* ATi Mach64 graphics card emulation.
*
* Version: @(#)vid_ati_mach64.c 1.0.14 2018/03/10
* Version: @(#)vid_ati_mach64.c 1.0.16 2018/03/16
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -429,6 +429,39 @@ uint8_t mach64_in(uint16_t addr, void *p)
if (svga->crtcreg > 0x18)
return 0xff;
return svga->crtc[svga->crtcreg];
case 0x3DA:
svga->attrff = 0;
svga->attrff = 0;
svga->cgastat &= ~0x30;
/* copy color diagnostic info from the overscan color register */
switch (svga->attrregs[0x12] & 0x30)
{
case 0x00: /* P0 and P2 */
if (svga->attrregs[0x11] & 0x01)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x04)
svga->cgastat |= 0x20;
break;
case 0x10: /* P4 and P5 */
if (svga->attrregs[0x11] & 0x10)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x20)
svga->cgastat |= 0x20;
break;
case 0x20: /* P1 and P3 */
if (svga->attrregs[0x11] & 0x02)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x08)
svga->cgastat |= 0x20;
break;
case 0x30: /* P6 and P7 */
if (svga->attrregs[0x11] & 0x40)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x80)
svga->cgastat |= 0x20;
break;
}
return svga->cgastat;
}
return svga_in(addr, svga);
}
@@ -1150,8 +1183,6 @@ void mach64_start_line(mach64_t *mach64)
else if (mach64->dp_pix_width & DP_BYTE_PIX_ORDER) dat = (svga->vram[((addr) >> 3) & mach64->vram_mask] >> ((addr) & 7)) & 1; \
else dat = (svga->vram[((addr) >> 3) & mach64->vram_mask] >> (7 - ((addr) & 7))) & 1;
#define READ1BPP(addr, dat, width)
#define MIX switch (mix ? mach64->accel.mix_fg : mach64->accel.mix_bg) \
{ \
case 0x0: dest_dat = ~dest_dat; break; \

View File

@@ -7,9 +7,9 @@
* This file is part of the 86Box distribution.
*
* Emulation of select Cirrus Logic cards (CL-GD 5428,
* CL-GD 5429, 5430, 5434 and 5436 are supported).
* CL-GD 5429, CL-GD 5430, CL-GD 5434 and CL-GD 5436 are supported).
*
* Version: @(#)vid_cl_54xx.c 1.0.11 2018/03/08
* Version: @(#)vid_cl_54xx.c 1.0.9 2018/03/02
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Barry Rodewald,
@@ -40,8 +40,8 @@
#include "vid_cl54xx.h"
#define BIOS_GD5424_PATH L"roms/video/cirruslogic/cl5424.bin"
#define BIOS_GD5428_ISA_PATH L"roms/video/cirruslogic/5428.bin"
#define BIOS_GD5428_VLB_PATH L"roms/video/cirruslogic/Diamond SpeedStar PRO VLB (Cirrus Logic 5428)_v3.04.bin"
#define BIOS_GD5426_PATH L"roms/video/cirruslogic/Diamond SpeedStar PRO VLB v3.04.bin"
#define BIOS_GD5428_PATH L"roms/video/cirruslogic/vlbusjapan.BIN"
#define BIOS_GD5429_PATH L"roms/video/cirruslogic/5429.vbi"
#define BIOS_GD5430_VLB_PATH L"roms/video/cirruslogic/diamondvlbus.bin"
#define BIOS_GD5430_PCI_PATH L"roms/video/cirruslogic/pci.bin"
@@ -49,6 +49,7 @@
#define BIOS_GD5436_PATH L"roms/video/cirruslogic/5436.vbi"
#define CIRRUS_ID_CLGD5424 0x94
#define CIRRUS_ID_CLGD5426 0x90
#define CIRRUS_ID_CLGD5428 0x98
#define CIRRUS_ID_CLGD5429 0x9c
#define CIRRUS_ID_CLGD5430 0xa0
@@ -77,9 +78,13 @@
#define CIRRUS_BUSTYPE_VLBSLOW 0x30
#define CIRRUS_BUSTYPE_ISA 0x38
#define CIRRUS_MMIO_ENABLE 0x04
#define CIRRUS_MMIO_USE_PCIADDR 0x40 // 0xb8000 if cleared.
#define CIRRUS_MMIO_USE_PCIADDR 0x40 /* 0xb8000 if cleared. */
#define CIRRUS_MEMSIZEEXT_DOUBLE 0x80
// control 0x0b
#define CIRRUS_BANKING_DUAL 0x01
#define CIRRUS_BANKING_GRANULARITY_16K 0x20 /* set:16k, clear:4k */
/* control 0x30 */
#define CIRRUS_BLTMODE_BACKWARDS 0x01
#define CIRRUS_BLTMODE_MEMSYSDEST 0x02
@@ -93,6 +98,13 @@
#define CIRRUS_BLTMODE_PIXELWIDTH24 0x20
#define CIRRUS_BLTMODE_PIXELWIDTH32 0x30
// control 0x31
#define CIRRUS_BLT_BUSY 0x01
#define CIRRUS_BLT_START 0x02
#define CIRRUS_BLT_RESET 0x04
#define CIRRUS_BLT_FIFOUSED 0x10
#define CIRRUS_BLT_AUTOSTART 0x80
// control 0x33
#define CIRRUS_BLTMODEEXT_SOLIDFILL 0x04
#define CIRRUS_BLTMODEEXT_COLOREXPINV 0x02
@@ -134,7 +146,7 @@ typedef struct gd54xx_t
uint32_t dst_addr, src_addr;
uint8_t mask, mode, rop;
uint8_t modeext;
uint8_t bltstart;
uint8_t status;
uint16_t trans_col, trans_mask;
uint32_t dst_addr_backup, src_addr_backup;
@@ -199,7 +211,7 @@ gd54xx_out(uint16_t addr, uint8_t val, void *p)
if (svga->seqaddr > 5) {
svga->seqregs[svga->seqaddr & 0x1f] = val;
switch (svga->seqaddr & 0x1f) {
case 6: /* cirrus unlock extensions */
case 6:
val &= 0x17;
if (val == 0x12)
svga->seqregs[6] = 0x12;
@@ -214,22 +226,29 @@ gd54xx_out(uint16_t addr, uint8_t val, void *p)
break;
case 0x10: case 0x30: case 0x50: case 0x70:
case 0x90: case 0xb0: case 0xd0: case 0xf0:
svga->hwcursor.x = (val << 3) | ((svga->seqaddr >> 5) & 7);
svga->hwcursor.x = (val << 3) | (svga->seqaddr >> 5);
break;
case 0x11: case 0x31: case 0x51: case 0x71:
case 0x91: case 0xb1: case 0xd1: case 0xf1:
svga->hwcursor.y = (val << 3) | ((svga->seqaddr >> 5) & 7);
svga->hwcursor.y = (val << 3) | (svga->seqaddr >> 5);
break;
case 0x12:
svga->hwcursor.ena = val & 1;
svga->hwcursor.ena = val & CIRRUS_CURSOR_SHOW;
svga->hwcursor.xsize = svga->hwcursor.ysize = (val & CIRRUS_CURSOR_LARGE) ? 64 : 32;
if (val & CIRRUS_CURSOR_LARGE)
svga->hwcursor.addr = (((gd54xx->vram_size<<20)-0x4000) + ((svga->seqregs[0x13] & 0x3c) * 256));
else
svga->hwcursor.addr = (((gd54xx->vram_size<<20)-0x4000) + ((svga->seqregs[0x13] & 0x3f) * 256));
break;
case 0x13:
svga->hwcursor.addr = (((gd54xx->vram_size<<20)-0x4000) + ((val & 0x3f) * 256));
if (svga->seqregs[0x12] & CIRRUS_CURSOR_LARGE)
svga->hwcursor.addr = (((gd54xx->vram_size<<20)-0x4000) + ((val & 0x3c) * 256));
else
svga->hwcursor.addr = (((gd54xx->vram_size<<20)-0x4000) + ((val & 0x3f) * 256));
break;
case 0x07:
svga->set_reset_disabled = svga->seqregs[7] & 1;
case 0x17:
/* svga->seqregs[0x17] = (svga->seqregs[0x17] & 0x38) | (val & 0xc7); */
gd543x_recalc_mapping(gd54xx);
break;
}
@@ -394,8 +413,7 @@ gd54xx_out(uint16_t addr, uint8_t val, void *p)
if ((svga->crtcreg == 7) && (svga->crtc[0x11] & 0x80))
val = (svga->crtc[7] & ~0x10) | (val & 0x10);
old = svga->crtc[svga->crtcreg];
if ((svga->crtcreg != 0x27) && (svga->crtcreg != 0x28))
svga->crtc[svga->crtcreg] = val;
svga->crtc[svga->crtcreg] = val;
if (old != val) {
if (svga->crtcreg < 0xe || svga->crtcreg > 0x10) {
@@ -421,18 +439,31 @@ gd54xx_in(uint16_t addr, void *p)
addr ^= 0x60;
switch (addr) {
case 0x3c4:
if ((svga->seqregs[6] & 0x17) == 0x12)
{
temp = svga->seqaddr;
if ((temp & 0x1e) == 0x10)
{
if (temp & 1)
temp = ((svga->hwcursor.y & 7) << 5) | 0x11;
else
temp = ((svga->hwcursor.x & 7) << 5) | 0x10;
}
return temp;
}
return svga->seqaddr;
case 0x3c5:
if (svga->seqaddr > 5) {
switch (svga->seqaddr) {
case 6:
return ((svga->seqregs[6] & 0x17) == 0x12) ? 0x12 : 0x0f;
return ((svga->seqregs[6] & 0x17) == 0x12) ? 0x12 : 0x0f;
case 0x0b: case 0x0c: case 0x0d: case 0x0e:
return gd54xx->vclk_n[svga->seqaddr-0x0b];
case 0x0f:
return svga->seqregs[0x0f];
return gd54xx->vclk_n[svga->seqaddr-0x0b];
case 0x17:
temp = svga->gdcreg[0x17] & ~(7 << 3);
if (svga->crtc[0x27] < CIRRUS_ID_CLGD5430) {
if (svga->crtc[0x27] <= CIRRUS_ID_CLGD5429) {
if (gd54xx->vlb)
temp |= (CL_GD5429_SYSTEM_BUS_VESA << 3);
else
@@ -448,8 +479,6 @@ gd54xx_in(uint16_t addr, void *p)
return temp;
case 0x1b: case 0x1c: case 0x1d: case 0x1e:
return gd54xx->vclk_d[svga->seqaddr-0x1b];
case 0x1f:
return svga->seqregs[0x1f];
}
return svga->seqregs[svga->seqaddr & 0x3f];
}
@@ -473,7 +502,7 @@ gd54xx_in(uint16_t addr, void *p)
case 0x24: /*Attribute controller toggle readback (R)*/
return svga->attrff << 7;
case 0x26: /*Attribute controller index readback (R)*/
return svga->attraddr & 0x3f;
return svga->attraddr & 0x3f;
case 0x27: /*ID*/
return svga->crtc[0x27]; /*GD542x/GD543x*/
case 0x28: /*Class ID*/
@@ -492,13 +521,13 @@ gd54xx_recalc_banking(gd54xx_t *gd54xx)
{
svga_t *svga = &gd54xx->svga;
if (svga->gdcreg[0xb] & 0x20)
if (svga->gdcreg[0x0b] & CIRRUS_BANKING_GRANULARITY_16K)
gd54xx->bank[0] = svga->gdcreg[0x09] << 14;
else
gd54xx->bank[0] = svga->gdcreg[0x09] << 12;
if (svga->gdcreg[0xb] & 0x01) {
if (svga->gdcreg[0xb] & 0x20)
if (svga->gdcreg[0x0b] & CIRRUS_BANKING_DUAL) {
if (svga->gdcreg[0x0b] & CIRRUS_BANKING_GRANULARITY_16K)
gd54xx->bank[1] = svga->gdcreg[0x0a] << 14;
else
gd54xx->bank[1] = svga->gdcreg[0x0a] << 12;
@@ -523,7 +552,7 @@ gd543x_recalc_mapping(gd54xx_t *gd54xx)
if (!(svga->seqregs[7] & 0xf0)) {
mem_mapping_disable(&gd54xx->linear_mapping);
switch (svga->gdcreg[6] & 0x0C) {
switch (svga->gdcreg[6] & 0x0c) {
case 0x0: /*128k at A0000*/
mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x20000);
svga->banked_mask = 0xffff;
@@ -534,12 +563,10 @@ gd543x_recalc_mapping(gd54xx_t *gd54xx)
break;
case 0x8: /*32k at B0000*/
mem_mapping_set_addr(&svga->mapping, 0xb0000, 0x08000);
mem_mapping_disable(&gd54xx->mmio_mapping);
svga->banked_mask = 0x7fff;
break;
case 0xC: /*32k at B8000*/
mem_mapping_set_addr(&svga->mapping, 0xb8000, 0x08000);
mem_mapping_disable(&gd54xx->mmio_mapping);
svga->banked_mask = 0x7fff;
gd54xx->mmio_vram_overlap = 1;
break;
@@ -552,7 +579,7 @@ gd543x_recalc_mapping(gd54xx_t *gd54xx)
uint32_t base, size;
if (svga->crtc[0x27] <= CIRRUS_ID_CLGD5429 || (!gd54xx->pci && !gd54xx->vlb)) {
if (svga->gdcreg[0xb] & 0x20) {
if (svga->gdcreg[0x0b] & CIRRUS_BANKING_GRANULARITY_16K) {
base = (svga->seqregs[7] & 0xf0) << 16;
size = 1 * 1024 * 1024;
} else {
@@ -577,7 +604,7 @@ gd543x_recalc_mapping(gd54xx_t *gd54xx)
mem_mapping_set_addr(&gd54xx->linear_mapping, base, size);
svga->linear_base = base;
if (svga->seqregs[0x17] & CIRRUS_MMIO_ENABLE) {
if (svga->seqregs[0x17] & 0x40) {
if (svga->seqregs[0x17] & CIRRUS_MMIO_USE_PCIADDR) {
if (size >= (4 * 1024 * 1024))
mem_mapping_disable(&gd54xx->mmio_mapping); /* MMIO is handled in the linear read/write functions */
else {
@@ -602,45 +629,85 @@ gd54xx_recalctimings(svga_t *svga)
svga->interlace = (svga->crtc[0x1a] & 0x01);
if (svga->seqregs[7] & 0x01) {
svga->render = svga_render_8bpp_highres;
svga->bpp = 8;
} else if (svga->gdcreg[5] & 0x40) {
svga->render = svga_render_8bpp_lowres;
svga->bpp = 8;
}
svga->ma_latch |= ((svga->crtc[0x1b] & 0x01) << 16) | ((svga->crtc[0x1b] & 0xc) << 15);
if (gd54xx->ramdac.ctrl & 0x80) {
if (gd54xx->ramdac.ctrl & 0x40) {
switch (gd54xx->ramdac.ctrl & 0xf) {
case 0x0:
svga->render = svga_render_15bpp_highres;
svga->bpp = 15;
break;
case 0x1:
svga->render = svga_render_16bpp_highres;
svga->bpp = 16;
break;
case 0x5:
if ((svga->crtc[0x27] >= CIRRUS_ID_CLGD5434) && (svga->seqregs[7] & 8)) {
svga->render = svga_render_32bpp_highres;
svga->bpp = 32;
if (svga->crtc[0x27] < CIRRUS_ID_CLGD5436)
svga->rowoffset *= 2;
} else {
svga->render = svga_render_24bpp_highres;
svga->bpp = 24;
}
break;
}
} else {
svga->render = svga_render_15bpp_highres;
svga->bpp = 15;
if (svga->seqregs[7] & CIRRUS_SR7_BPP_SVGA)
{
svga->bpp = 8;
svga->render = svga_render_8bpp_highres;
}
else if (svga->gdcreg[5] & 0x40)
{
svga->bpp = 8;
svga->render = svga_render_8bpp_lowres;
}
if (gd54xx->ramdac.ctrl & 0x80)
{
if (gd54xx->ramdac.ctrl & 0x40)
{
switch (gd54xx->ramdac.ctrl & 0xf)
{
case 0:
svga->bpp = 15;
svga->render = svga_render_15bpp_highres;
break;
case 1:
svga->bpp = 16;
svga->render = svga_render_16bpp_highres;
break;
case 5:
if ((svga->crtc[0x27] >= CIRRUS_ID_CLGD5434) && (svga->seqregs[7] & CIRRUS_SR7_BPP_32))
{
svga->bpp = 32;
svga->render = svga_render_32bpp_highres;
if (svga->crtc[0x27] < CIRRUS_ID_CLGD5436)
svga->rowoffset *= 2;
}
else
{
svga->bpp = 24;
svga->render = svga_render_24bpp_highres;
}
break;
case 0xf:
switch (svga->seqregs[7] & CIRRUS_SR7_BPP_MASK)
{
case CIRRUS_SR7_BPP_32:
svga->bpp = 32;
svga->render = svga_render_32bpp_highres;
svga->rowoffset *= 2;
break;
case CIRRUS_SR7_BPP_24:
svga->bpp = 24;
svga->render = svga_render_24bpp_highres;
break;
case CIRRUS_SR7_BPP_16:
case CIRRUS_SR7_BPP_16_DOUBLEVCLK:
svga->bpp = 16;
svga->render = svga_render_16bpp_highres;
break;
case CIRRUS_SR7_BPP_8:
svga->bpp = 8;
svga->render = svga_render_8bpp_highres;
break;
}
break;
}
}
else
{
svga->bpp = 15;
svga->render = svga_render_15bpp_highres;
}
}
clocksel = (svga->miscout >> 2) & 3;
if (!gd54xx->vclk_n[clocksel] || !gd54xx->vclk_d[clocksel])
@@ -656,7 +723,7 @@ gd54xx_recalctimings(svga_t *svga)
freq /= 2.0;
break;
case 4:
if (svga->crtc[0x27] < CIRRUS_ID_CLGD5436)
if (svga->crtc[0x27] <= CIRRUS_ID_CLGD5434)
freq /= 3.0;
break;
}
@@ -666,47 +733,50 @@ gd54xx_recalctimings(svga_t *svga)
svga->vram_display_mask = (svga->crtc[0x1b] & 2) ? gd54xx->vram_mask : 0x3ffff;
}
static void
gd54xx_hwcursor_draw(svga_t *svga, int displine)
static
void gd54xx_hwcursor_draw(svga_t *svga, int displine)
{
int x;
uint8_t dat[2];
int xx;
int offset = svga->hwcursor_latch.x - svga->hwcursor_latch.xoff;
int largecur = (svga->seqregs[0x12] & CIRRUS_CURSOR_LARGE);
int cursize = (largecur) ? 64 : 32;
int y_add = (enable_overscan && !suppress_overscan) ? 16 : 0;
int x_add = (enable_overscan && !suppress_overscan) ? 8 : 0;
if (svga->interlace && svga->hwcursor_oddeven)
svga->hwcursor_latch.addr += 4;
for (x = 0; x < cursize; x += 8) {
int x;
uint8_t dat[2];
int xx;
int offset = svga->hwcursor_latch.x - svga->hwcursor_latch.xoff;
int y_add = (enable_overscan && !suppress_overscan) ? 16 : 0;
int x_add = (enable_overscan && !suppress_overscan) ? 8 : 0;
int pitch = (svga->hwcursor.xsize == 64) ? 16 : 4;
if (svga->interlace && svga->hwcursor_oddeven)
svga->hwcursor_latch.addr += pitch;
for (x = 0; x < svga->hwcursor.xsize; x += 8) {
dat[0] = svga->vram[svga->hwcursor_latch.addr];
dat[1] = svga->vram[svga->hwcursor_latch.addr + 0x80];
if (svga->hwcursor.xsize == 64)
dat[1] = svga->vram[svga->hwcursor_latch.addr + 0x08];
else
dat[1] = svga->vram[svga->hwcursor_latch.addr + 0x80];
for (xx = 0; xx < 8; xx++) {
if (offset >= svga->hwcursor_latch.x) {
if (dat[1] & 0x80)
((uint32_t *)buffer32->line[displine + y_add])[offset + cursize + x_add] = 0;
((uint32_t *)buffer32->line[displine + y_add])[offset + 32 + x_add] = 0;
if (dat[0] & 0x80)
((uint32_t *)buffer32->line[displine + y_add])[offset + cursize + x_add] ^= 0xffffff;
((uint32_t *)buffer32->line[displine + y_add])[offset + 32 + x_add] ^= 0xffffff;
}
offset++;
dat[0] <<= 1;
dat[1] <<= 1;
}
svga->hwcursor_latch.addr++;
}
if (svga->interlace && !svga->hwcursor_oddeven)
svga->hwcursor_latch.addr += 4;
}
if (svga->hwcursor.xsize == 64)
svga->hwcursor_latch.addr += 8;
if (svga->interlace && !svga->hwcursor_oddeven)
svga->hwcursor_latch.addr += pitch;
}
static void
gd5428_copy_pixel(gd54xx_t *gd54xx, svga_t *svga, uint8_t src, uint8_t dst)
gd54xx_memsrc_rop(gd54xx_t *gd54xx, svga_t *svga, uint8_t src, uint8_t dst)
{
uint8_t res = src;
svga->changedvram[(gd54xx->blt.dst_addr_backup & svga->vram_mask) >> 12] = changeframecount;
@@ -751,7 +821,7 @@ gd54xx_blit_dword(gd54xx_t *gd54xx, svga_t *svga)
for (x=0;x<32;x+=8) {
pixel = ((gd54xx->blt.sys_buf & (0xff << x)) >> x);
if(gd54xx->blt.pixel_cnt <= gd54xx->blt.width)
gd5428_copy_pixel(gd54xx, svga, pixel, svga->vram[gd54xx->blt.dst_addr_backup & svga->vram_mask]);
gd54xx_memsrc_rop(gd54xx, svga, pixel, svga->vram[gd54xx->blt.dst_addr_backup & svga->vram_mask]);
gd54xx->blt.dst_addr_backup++;
gd54xx->blt.pixel_cnt++;
}
@@ -1123,7 +1193,7 @@ gd54xx_readb_linear(uint32_t addr, void *p)
}
if ((addr & 0x003fff00) == 0x003fff00) {
if ((svga->seqregs[0x17] & CIRRUS_MMIO_ENABLE) && (svga->seqregs[0x17] & 0x40))
if ((svga->seqregs[0x17] & CIRRUS_MMIO_ENABLE) && (svga->seqregs[0x17] & CIRRUS_MMIO_USE_PCIADDR))
return gd543x_mmio_read(addr & 0x000000ff, gd54xx);
}
@@ -1143,7 +1213,7 @@ gd54xx_readw_linear(uint32_t addr, void *p)
addr &= 0x003fffff; /* 4 MB mask */
if ((addr & 0x003fff00) == 0x003fff00) {
if ((svga->seqregs[0x17] & CIRRUS_MMIO_ENABLE) && (svga->seqregs[0x17] & 0x40)) {
if ((svga->seqregs[0x17] & CIRRUS_MMIO_ENABLE) && (svga->seqregs[0x17] & CIRRUS_MMIO_USE_PCIADDR)) {
if (ap == 2)
addr ^= 0x00000002;
@@ -1199,7 +1269,7 @@ gd54xx_readl_linear(uint32_t addr, void *p)
addr &= 0x003fffff; /* 4 MB mask */
if ((addr & 0x003fff00) == 0x003fff00) {
if ((svga->seqregs[0x17] & CIRRUS_MMIO_ENABLE) && (svga->seqregs[0x17] & 0x40)) {
if ((svga->seqregs[0x17] & CIRRUS_MMIO_ENABLE) && (svga->seqregs[0x17] & CIRRUS_MMIO_USE_PCIADDR)) {
temp = gd543x_mmio_readl(addr & 0x000000ff, gd54xx);
switch(ap) {
@@ -1286,7 +1356,7 @@ gd54xx_writeb_linear(uint32_t addr, uint8_t val, void *p)
}
if ((addr & 0x003fff00) == 0x003fff00) {
if ((svga->seqregs[0x17] & CIRRUS_MMIO_ENABLE) && (svga->seqregs[0x17] & 0x40))
if ((svga->seqregs[0x17] & CIRRUS_MMIO_ENABLE) && (svga->seqregs[0x17] & CIRRUS_MMIO_USE_PCIADDR))
gd543x_mmio_write(addr & 0x000000ff, val, gd54xx);
}
@@ -1320,7 +1390,7 @@ gd54xx_writew_linear(uint32_t addr, uint16_t val, void *p)
uint16_t temp;
if ((addr & 0x003fff00) == 0x003fff00) {
if ((svga->seqregs[0x17] & CIRRUS_MMIO_ENABLE) && (svga->seqregs[0x17] & 0x40)) {
if ((svga->seqregs[0x17] & CIRRUS_MMIO_ENABLE) && (svga->seqregs[0x17] & CIRRUS_MMIO_USE_PCIADDR)) {
switch(ap) {
case 0:
default:
@@ -1394,7 +1464,7 @@ gd54xx_writel_linear(uint32_t addr, uint32_t val, void *p)
uint32_t temp;
if ((addr & 0x003fff00) == 0x003fff00) {
if ((svga->seqregs[0x17] & CIRRUS_MMIO_ENABLE) && (svga->seqregs[0x17] & 0x40)) {
if ((svga->seqregs[0x17] & CIRRUS_MMIO_ENABLE) && (svga->seqregs[0x17] & CIRRUS_MMIO_USE_PCIADDR)) {
switch(ap) {
case 0:
default:
@@ -1520,7 +1590,7 @@ gd54xx_readl(uint32_t addr, void *p)
static int
gd543x_do_mmio(svga_t *svga, uint32_t addr)
{
if (svga->seqregs[0x17] & 0x40)
if (svga->seqregs[0x17] & CIRRUS_MMIO_USE_PCIADDR)
return 1;
else
return ((addr & ~0xff) == 0xb8000);
@@ -1620,7 +1690,7 @@ gd543x_mmio_write(uint32_t addr, uint8_t val, void *p)
else
gd54xx->blt.dst_addr &= 0x1fffff;
if ((svga->crtc[0x27] >= CIRRUS_ID_CLGD5436) && (gd54xx->blt.bltstart & 0x80)) {
if ((svga->crtc[0x27] == CIRRUS_ID_CLGD5436) && (gd54xx->blt.status & CIRRUS_BLT_AUTOSTART)) {
if (gd54xx->blt.mode == CIRRUS_BLTMODE_MEMSYSSRC) {
gd54xx->blt.sys_tx = 1;
gd54xx->blt.sys_cnt = 0;
@@ -1659,7 +1729,7 @@ gd543x_mmio_write(uint32_t addr, uint8_t val, void *p)
break;
case 0x1b:
if (svga->crtc[0x27] >= CIRRUS_ID_CLGD5436)
if (svga->crtc[0x27] == CIRRUS_ID_CLGD5436)
gd54xx->blt.modeext = val;
break;
@@ -1680,8 +1750,8 @@ gd543x_mmio_write(uint32_t addr, uint8_t val, void *p)
break;
case 0x40:
gd54xx->blt.bltstart = val;
if (gd54xx->blt.bltstart & 0x02) {
gd54xx->blt.status = val;
if (gd54xx->blt.status & CIRRUS_BLT_START) {
if (gd54xx->blt.mode == CIRRUS_BLTMODE_MEMSYSSRC) {
gd54xx->blt.sys_tx = 1;
gd54xx->blt.sys_cnt = 0;
@@ -1799,20 +1869,8 @@ gd54xx_start_blit(uint32_t cpu_dat, int count, gd54xx_t *gd54xx, svga_t *svga)
blt_mask *= 2;
break;
case CIRRUS_BLTMODE_PIXELWIDTH24:
if (svga->crtc[0x27] >= CIRRUS_ID_CLGD5436)
{
x_max = 24;
if (gd54xx->blt.mode & CIRRUS_BLTMODE_PATTERNCOPY)
{
blt_mask = (gd54xx->blt.mask & 0x1f);
blt_mask /= 3;
}
else
{
blt_mask = gd54xx->blt.mask & 7;
blt_mask *= 3;
}
}
blt_mask = (gd54xx->blt.mask & 0x1f);
x_max = 24;
break;
case CIRRUS_BLTMODE_PIXELWIDTH32:
blt_mask = gd54xx->blt.mask & 7;
@@ -1826,7 +1884,7 @@ gd54xx_start_blit(uint32_t cpu_dat, int count, gd54xx_t *gd54xx, svga_t *svga)
gd54xx->blt.src_addr_backup = gd54xx->blt.src_addr;
gd54xx->blt.width_backup = gd54xx->blt.width;
gd54xx->blt.height_internal = gd54xx->blt.height;
gd54xx->blt.x_count = 0;
gd54xx->blt.x_count = 0;
if ((gd54xx->blt.mode & (CIRRUS_BLTMODE_PATTERNCOPY|CIRRUS_BLTMODE_COLOREXPAND)) == (CIRRUS_BLTMODE_PATTERNCOPY|CIRRUS_BLTMODE_COLOREXPAND))
gd54xx->blt.y_count = gd54xx->blt.src_addr & 7;
else
@@ -1853,7 +1911,7 @@ gd54xx_start_blit(uint32_t cpu_dat, int count, gd54xx_t *gd54xx, svga_t *svga)
}
else
{
mem_mapping_set_handler(&gd54xx->linear_mapping, svga_read_linear, svga_readw_linear, svga_readl_linear, gd54xx_writeb_linear, gd54xx_writew_linear, gd54xx_writel_linear);
mem_mapping_set_handler(&gd54xx->linear_mapping, svga_readb_linear, svga_readw_linear, svga_readl_linear, gd54xx_writeb_linear, gd54xx_writew_linear, gd54xx_writel_linear);
mem_mapping_set_p(&gd54xx->linear_mapping, svga);
}
gd543x_recalc_mapping(gd54xx);
@@ -1892,19 +1950,16 @@ gd54xx_start_blit(uint32_t cpu_dat, int count, gd54xx_t *gd54xx, svga_t *svga)
}
break;
case CIRRUS_BLTMODE_PIXELWIDTH24:
if (svga->crtc[0x27] >= CIRRUS_ID_CLGD5436)
if ((gd54xx->blt.x_count % 3) == 2)
src = mask ? (gd54xx->blt.fg_col >> 16) : (gd54xx->blt.bg_col >> 16);
else if ((gd54xx->blt.x_count % 3) == 1)
src = mask ? (gd54xx->blt.fg_col >> 8) : (gd54xx->blt.bg_col >> 8);
else
src = mask ? gd54xx->blt.fg_col : gd54xx->blt.bg_col;
if ((gd54xx->blt.x_count % 3) == 2)
{
if ((gd54xx->blt.x_count % 3) == 2)
src = mask ? (gd54xx->blt.fg_col >> 16) : (gd54xx->blt.bg_col >> 16);
else if ((gd54xx->blt.x_count % 3) == 1)
src = mask ? (gd54xx->blt.fg_col >> 8) : (gd54xx->blt.bg_col >> 8);
else
src = mask ? gd54xx->blt.fg_col : gd54xx->blt.bg_col;
if ((gd54xx->blt.x_count % 3) == 2)
{
cpu_dat <<= 1;
count--;
}
cpu_dat <<= 1;
count--;
}
break;
case CIRRUS_BLTMODE_PIXELWIDTH32:
@@ -1941,9 +1996,7 @@ gd54xx_start_blit(uint32_t cpu_dat, int count, gd54xx_t *gd54xx, svga_t *svga)
src = svga->vram[(gd54xx->blt.src_addr & (svga->vram_mask & ~3)) + (gd54xx->blt.y_count << 4) + (gd54xx->blt.x_count & 15)];
break;
case CIRRUS_BLTMODE_PIXELWIDTH24:
pclog("Pattern copy\n");
if (svga->crtc[0x27] >= CIRRUS_ID_CLGD5436)
src = svga->vram[(gd54xx->blt.src_addr & (svga->vram_mask & ~3)) + (gd54xx->blt.y_count << 5) + (gd54xx->blt.x_count % 24)];
src = svga->vram[(gd54xx->blt.src_addr & (svga->vram_mask & ~3)) + (gd54xx->blt.y_count << 5) + (gd54xx->blt.x_count % 24)];
break;
case CIRRUS_BLTMODE_PIXELWIDTH32:
src = svga->vram[(gd54xx->blt.src_addr & (svga->vram_mask & ~3)) + (gd54xx->blt.y_count << 5) + (gd54xx->blt.x_count & 31)];
@@ -1966,20 +2019,19 @@ gd54xx_start_blit(uint32_t cpu_dat, int count, gd54xx_t *gd54xx, svga_t *svga)
src = mask ? gd54xx->blt.fg_col : gd54xx->blt.bg_col;
break;
case CIRRUS_BLTMODE_PIXELWIDTH24:
if (svga->crtc[0x27] >= CIRRUS_ID_CLGD5436)
{
mask = svga->vram[gd54xx->blt.src_addr & svga->vram_mask] & (0x80 >> (gd54xx->blt.x_count / 3));
if ((gd54xx->blt.dst_addr % 3) == 2)
src = mask ? (gd54xx->blt.fg_col >> 16) : (gd54xx->blt.bg_col >> 16);
else if ((gd54xx->blt.dst_addr % 3) == 1)
src = mask ? (gd54xx->blt.fg_col >> 8) : (gd54xx->blt.bg_col >> 8);
else
src = mask ? gd54xx->blt.fg_col : gd54xx->blt.bg_col;
}
mask = svga->vram[gd54xx->blt.src_addr & svga->vram_mask] & (0x80 >> (gd54xx->blt.x_count / 3));
if ((gd54xx->blt.dst_addr % 3) == 2)
src = mask ? (gd54xx->blt.fg_col >> 16) : (gd54xx->blt.bg_col >> 16);
else if ((gd54xx->blt.dst_addr % 3) == 1)
src = mask ? (gd54xx->blt.fg_col >> 8) : (gd54xx->blt.bg_col >> 8);
else
src = mask ? gd54xx->blt.fg_col : gd54xx->blt.bg_col;
break;
case CIRRUS_BLTMODE_PIXELWIDTH32:
mask = svga->vram[gd54xx->blt.src_addr & svga->vram_mask] & (0x80 >> (gd54xx->blt.x_count >> 2));
if ((gd54xx->blt.dst_addr & 3) == 2)
if ((gd54xx->blt.dst_addr & 3) == 3)
src = mask ? (gd54xx->blt.fg_col >> 24) : (gd54xx->blt.bg_col >> 24);
else if ((gd54xx->blt.dst_addr & 3) == 2)
src = mask ? (gd54xx->blt.fg_col >> 16) : (gd54xx->blt.bg_col >> 16);
else if ((gd54xx->blt.dst_addr & 3) == 1)
src = mask ? (gd54xx->blt.fg_col >> 8) : (gd54xx->blt.bg_col >> 8);
@@ -2039,16 +2091,13 @@ gd54xx_start_blit(uint32_t cpu_dat, int count, gd54xx_t *gd54xx, svga_t *svga)
src = mask ? gd54xx->blt.fg_col : gd54xx->blt.bg_col;
break;
case CIRRUS_BLTMODE_PIXELWIDTH24:
if (svga->crtc[0x27] >= CIRRUS_ID_CLGD5436)
{
mask = svga->vram[(gd54xx->blt.src_addr & svga->vram_mask & ~7) | gd54xx->blt.y_count] & (0x80 >> (gd54xx->blt.x_count / 3));
if ((gd54xx->blt.dst_addr % 3) == 2)
src = mask ? (gd54xx->blt.fg_col >> 16) : (gd54xx->blt.bg_col >> 16);
else if ((gd54xx->blt.dst_addr % 3) == 1)
src = mask ? (gd54xx->blt.fg_col >> 8) : (gd54xx->blt.bg_col >> 8);
else
src = mask ? gd54xx->blt.fg_col : gd54xx->blt.bg_col;
}
mask = svga->vram[(gd54xx->blt.src_addr & svga->vram_mask & ~7) | gd54xx->blt.y_count] & (0x80 >> (gd54xx->blt.x_count / 3));
if ((gd54xx->blt.dst_addr % 3) == 2)
src = mask ? (gd54xx->blt.fg_col >> 16) : (gd54xx->blt.bg_col >> 16);
else if ((gd54xx->blt.dst_addr % 3) == 1)
src = mask ? (gd54xx->blt.fg_col >> 8) : (gd54xx->blt.bg_col >> 8);
else
src = mask ? gd54xx->blt.fg_col : gd54xx->blt.bg_col;
break;
case CIRRUS_BLTMODE_PIXELWIDTH32:
@@ -2074,7 +2123,7 @@ gd54xx_start_blit(uint32_t cpu_dat, int count, gd54xx_t *gd54xx, svga_t *svga)
switch (gd54xx->blt.rop) {
case 0x00: dst = 0; break;
case 0x05: dst = src & dst; break;
case 0x06: /* dst = dst; */ break;
case 0x06: dst = dst; break;
case 0x09: dst = src & ~dst; break;
case 0x0b: dst = ~ dst; break;
case 0x0d: dst = src; break;
@@ -2106,7 +2155,9 @@ gd54xx_start_blit(uint32_t cpu_dat, int count, gd54xx_t *gd54xx, svga_t *svga)
gd54xx->blt.dst_addr += ((gd54xx->blt.mode & CIRRUS_BLTMODE_BACKWARDS) ? -1 : 1);
gd54xx->blt.x_count++;
if (gd54xx->blt.x_count == x_max) {
if (gd54xx->blt.x_count == x_max)
{
gd54xx->blt.x_count = 0;
if ((gd54xx->blt.mode & (CIRRUS_BLTMODE_PATTERNCOPY|CIRRUS_BLTMODE_COLOREXPAND)) == CIRRUS_BLTMODE_COLOREXPAND)
gd54xx->blt.src_addr++;
@@ -2146,7 +2197,7 @@ gd54xx_start_blit(uint32_t cpu_dat, int count, gd54xx_t *gd54xx, svga_t *svga)
}
else
{
mem_mapping_set_handler(&gd54xx->linear_mapping, svga_read_linear, svga_readw_linear, svga_readl_linear, gd54xx_writeb_linear, gd54xx_writew_linear, gd54xx_writel_linear);
mem_mapping_set_handler(&gd54xx->linear_mapping, svga_readb_linear, svga_readw_linear, svga_readl_linear, gd54xx_writeb_linear, gd54xx_writew_linear, gd54xx_writel_linear);
mem_mapping_set_p(&gd54xx->linear_mapping, svga);
}
gd543x_recalc_mapping(gd54xx);
@@ -2172,16 +2223,9 @@ cl_pci_read(int func, int addr, void *p)
case 0x01: return 0x10;
case 0x02:
switch (svga->crtc[0x27]) {
case CIRRUS_ID_CLGD5430:
return 0xa0;
case CIRRUS_ID_CLGD5434:
return 0xa8;
case CIRRUS_ID_CLGD5436:
return 0xac;
}
return 0xff;
return svga->crtc[0x27];
case 0x03: return 0x00;
case PCI_REG_COMMAND:
return gd54xx->pci_regs[PCI_REG_COMMAND]; /*Respond to IO and memory accesses*/
@@ -2198,10 +2242,10 @@ cl_pci_read(int func, int addr, void *p)
case 0x12: return 0x00;
case 0x13: return gd54xx->lfb_base >> 24;
case 0x30: return (gd54xx->has_bios) ? (gd54xx->pci_regs[0x30] & 0x01) : 0x00; /*BIOS ROM address*/
case 0x30: return (gd54xx->pci_regs[0x30] & 0x01); /*BIOS ROM address*/
case 0x31: return 0x00;
case 0x32: return (gd54xx->has_bios) ? gd54xx->pci_regs[0x32] : 0x00;
case 0x33: return (gd54xx->has_bios) ? gd54xx->pci_regs[0x33] : 0x00;
case 0x32: return gd54xx->pci_regs[0x32];
case 0x33: return gd54xx->pci_regs[0x33];
case 0x3c: return gd54xx->int_line;
case 0x3d: return PCI_INTA;
@@ -2230,8 +2274,6 @@ cl_pci_write(int func, int addr, uint8_t val, void *p)
break;
case 0x30: case 0x32: case 0x33:
if (!(gd54xx->has_bios))
return;
gd54xx->pci_regs[addr] = val;
if (gd54xx->pci_regs[0x30] & 0x01) {
uint32_t addr = (gd54xx->pci_regs[0x32] << 16) | (gd54xx->pci_regs[0x33] << 24);
@@ -2252,21 +2294,20 @@ static void
{
gd54xx_t *gd54xx = malloc(sizeof(gd54xx_t));
svga_t *svga = &gd54xx->svga;
int id = info->local & 0x7FFF;
int id = info->local;
wchar_t *romfn = NULL;
memset(gd54xx, 0, sizeof(gd54xx_t));
gd54xx->has_bios = !(info->local & 0x8000);
gd54xx->pci = !!(info->flags & DEVICE_PCI);
gd54xx->vlb = !!(info->flags & DEVICE_VLB);
switch (id) {
case CIRRUS_ID_CLGD5426:
romfn = BIOS_GD5426_PATH;
break;
case CIRRUS_ID_CLGD5428:
if (gd54xx->vlb)
romfn = BIOS_GD5428_VLB_PATH;
else
romfn = BIOS_GD5428_ISA_PATH;
romfn = BIOS_GD5428_PATH;
break;
case CIRRUS_ID_CLGD5429:
@@ -2292,7 +2333,6 @@ static void
gd54xx->vram_size = device_get_config_int("memory");
gd54xx->vram_mask = (gd54xx->vram_size << 20) - 1;
if (gd54xx->has_bios)
rom_init(&gd54xx->bios_rom, romfn, 0xc0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL);
svga_init(&gd54xx->svga, gd54xx, gd54xx->vram_size << 20,
@@ -2307,23 +2347,6 @@ static void
io_sethandler(0x03c0, 0x0020, gd54xx_in, NULL, NULL, gd54xx_out, NULL, NULL, gd54xx);
svga->decode_mask = (4 << 20) - 1;
switch (gd54xx->vram_size) {
case 1: /*1MB*/
svga->vram_mask = (1 << 20) - 1;
svga->vram_max = 1 << 20;
break;
case 2: /*2MB*/
svga->vram_mask = (2 << 20) - 1;
svga->vram_max = 2 << 20;
break;
case 4: /*4MB*/
svga->vram_mask = (4 << 20) - 1;
svga->vram_max = 4 << 20;
break;
}
svga->hwcursor.yoff = 32;
svga->hwcursor.xoff = 0;
@@ -2334,10 +2357,10 @@ static void
gd54xx->bank[1] = 0x8000;
if ((info->flags & DEVICE_PCI) && id >= CIRRUS_ID_CLGD5430)
if (gd54xx->pci && id >= CIRRUS_ID_CLGD5430)
pci_add_card(PCI_ADD_VIDEO, cl_pci_read, cl_pci_write, gd54xx);
gd54xx->pci_regs[0x04] = 7;
gd54xx->pci_regs[PCI_REG_COMMAND] = 7;
gd54xx->pci_regs[0x30] = 0x00;
gd54xx->pci_regs[0x32] = 0x0c;
@@ -2349,19 +2372,17 @@ static void
}
static int
gd5428_isa_available(void)
gd5426_available(void)
{
return rom_present(BIOS_GD5428_ISA_PATH);
return rom_present(BIOS_GD5426_PATH);
}
static int
gd5428_vlb_available(void)
gd5428_available(void)
{
return rom_present(BIOS_GD5428_VLB_PATH);
return rom_present(BIOS_GD5428_PATH);
}
static int
gd5429_available(void)
{
@@ -2396,7 +2417,7 @@ void
gd54xx_close(void *p)
{
gd54xx_t *gd54xx = (gd54xx_t *)p;
svga_close(&gd54xx->svga);
free(gd54xx);
@@ -2484,6 +2505,20 @@ static device_config_t gd5434_config[] =
}
};
device_t gd5426_vlb_device =
{
"Cirrus Logic CL-GD 5426 (VLB)",
DEVICE_VLB,
CIRRUS_ID_CLGD5426,
gd54xx_init,
gd54xx_close,
NULL,
gd5426_available,
gd54xx_speed_changed,
gd54xx_force_redraw,
gd54xx_add_status_info,
gd5428_config
};
device_t gd5428_isa_device =
{
@@ -2493,7 +2528,7 @@ device_t gd5428_isa_device =
gd54xx_init,
gd54xx_close,
NULL,
gd5428_isa_available,
gd5428_available,
gd54xx_speed_changed,
gd54xx_force_redraw,
gd54xx_add_status_info,
@@ -2508,7 +2543,7 @@ device_t gd5428_vlb_device =
gd54xx_init,
gd54xx_close,
NULL,
gd5428_vlb_available,
gd5428_available,
gd54xx_speed_changed,
gd54xx_force_redraw,
gd54xx_add_status_info,

View File

@@ -1,6 +1,7 @@
/* Copyright holders: Sarah Walker
see COPYING for more details
*/
extern device_t gd5426_vlb_device;
extern device_t gd5428_isa_device;
extern device_t gd5428_vlb_device;
extern device_t gd5429_isa_device;

View File

@@ -11,7 +11,7 @@
* This is intended to be used by another SVGA driver,
* and not as a card in it's own right.
*
* Version: @(#)vid_svga.c 1.0.24 2018/03/05
* Version: @(#)vid_svga.c 1.0.26 2018/03/16
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -69,7 +69,7 @@ void svga_out(uint16_t addr, uint8_t val, void *p)
switch (addr)
{
case 0x3C0:
case 0x3C1:
case 0x3C1:
if (!svga->attrff)
{
svga->attraddr = val & 31;
@@ -109,14 +109,9 @@ void svga_out(uint16_t addr, uint8_t val, void *p)
case 0x3C2:
svga->miscout = val;
svga->vidclock = val & 4;
if (val & 1)
{
io_removehandler(0x03a0, 0x0020, svga->video_in, NULL, NULL, svga->video_out, NULL, NULL, svga->p);
}
else
{
io_removehandler(0x03a0, 0x0020, svga->video_in, NULL, NULL, svga->video_out, NULL, NULL, svga->p);
if (!(val & 1))
io_sethandler(0x03a0, 0x0020, svga->video_in, NULL, NULL, svga->video_out, NULL, NULL, svga->p);
}
svga_recalctimings(svga);
break;
case 0x3C4:
@@ -300,43 +295,11 @@ uint8_t svga_in(uint16_t addr, void *p)
svga->attrff = 0;
svga->attrff = 0;
/* old diagnostic code
if (svga->cgastat & 0x01)
svga->cgastat &= ~0x30;
else
svga->cgastat ^= 0x30;
return svga->cgastat;
*/
svga->cgastat &= ~0x30;
/* copy color diagnostic info from the overscan color register */
switch (svga->attrregs[0x12] & 0x30)
{
case 0x00: /* P0 and P2 */
if (svga->attrregs[0x11] & 0x01)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x04)
svga->cgastat |= 0x20;
break;
case 0x10: /* P4 and P5 */
if (svga->attrregs[0x11] & 0x10)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x20)
svga->cgastat |= 0x20;
break;
case 0x20: /* P1 and P3 */
if (svga->attrregs[0x11] & 0x02)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x08)
svga->cgastat |= 0x20;
break;
case 0x30: /* P6 and P7 */
if (svga->attrregs[0x11] & 0x40)
svga->cgastat |= 0x10;
if (svga->attrregs[0x11] & 0x80)
svga->cgastat |= 0x20;
break;
}
return svga->cgastat;
}
return 0xFF;
}
@@ -390,15 +353,6 @@ void svga_recalctimings(svga_t *svga)
if (svga->crtc[9] & 0x20) svga->vblankstart |= 0x200;
svga->vblankstart++;
if(svga->crtc[0x17] & 4)
{
svga->vtotal <<= 1;
svga->dispend <<= 1;
svga->vsyncstart <<= 1;
svga->split <<= 1;
svga->vblankstart <<= 1;
}
svga->hdisp = svga->crtc[1];
svga->hdisp++;

View File

@@ -8,7 +8,7 @@
*
* SVGA renderers.
*
* Version: @(#)vid_svga_render.c 1.0.7 2018/03/05
* Version: @(#)vid_svga_render.c 1.0.9 2018/03/15
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -92,9 +92,6 @@ uint32_t shade[5][256] =
}
};
int dbcs_mode_enabled = 0;
void svga_render_blank(svga_t *svga)
{
int x, xx;
@@ -208,7 +205,7 @@ void svga_render_text_80(svga_t *svga)
uint32_t *p = &((uint32_t *)buffer32->line[svga->displine + y_add])[32 + x_add];
int x, xx;
int drawcursor;
uint8_t chr, attr, dat, nextchr;
uint8_t chr, attr, dat;
uint32_t charaddr;
int fg, bg;
int xinc = (svga->seqregs[1] & 1) ? 8 : 9;
@@ -220,83 +217,6 @@ void svga_render_text_80(svga_t *svga)
attr = svga->vram[((svga->ma << 1) + 1) & svga->vram_display_mask];
if(dbcs_mode_enabled && x + xinc < svga->hdisp && chr & 0x80)
{
nextchr = svga->vram[((svga->ma + 4) << 1) & svga->vram_display_mask];
if(nextchr & 0x80)
{
if (drawcursor)
{
bg = svga->pallook[svga->egapal[attr & 15]];
fg = svga->pallook[svga->egapal[attr >> 4]];
}
else
{
fg = svga->pallook[svga->egapal[attr & 15]];
bg = svga->pallook[svga->egapal[attr >> 4]];
if (attr & 0x80 && svga->attrregs[0x10] & 8)
{
bg = svga->pallook[svga->egapal[(attr >> 4) & 7]];
if (svga->blink & 16)
fg = bg;
}
}
dat = fontdatksc5601[((chr & 0x7F) << 7) | (nextchr & 0x7F)][svga->sc];
if (svga->seqregs[1] & 1)
{
for (xx = 0; xx < 8; xx++)
p[xx] = (dat & (0x80 >> xx)) ? fg : bg;
}
else
{
for (xx = 0; xx < 8; xx++)
p[xx] = (dat & (0x80 >> xx)) ? fg : bg;
if ((chr & ~0x1F) != 0xC0 || !(svga->attrregs[0x10] & 4))
p[8] = bg;
else
p[8] = (dat & 1) ? fg : bg;
}
attr = svga->vram[(((svga->ma + 4) << 1) + 1) & svga->vram_display_mask];
if (drawcursor)
{
bg = svga->pallook[svga->egapal[attr & 15]];
fg = svga->pallook[svga->egapal[attr >> 4]];
}
else
{
fg = svga->pallook[svga->egapal[attr & 15]];
bg = svga->pallook[svga->egapal[attr >> 4]];
if (attr & 0x80 && svga->attrregs[0x10] & 8)
{
bg = svga->pallook[svga->egapal[(attr >> 4) & 7]];
if (svga->blink & 16)
fg = bg;
}
}
dat = fontdatksc5601[((chr & 0x7F) << 7) | (nextchr & 0x7F)][svga->sc + 16];
if (svga->seqregs[1] & 1)
{
for (xx = 0; xx < 8; xx++)
p[xx+8] = (dat & (0x80 >> xx)) ? fg : bg;
}
else
{
for (xx = 0; xx < 8; xx++)
p[xx+9] = (dat & (0x80 >> xx)) ? fg : bg;
if ((chr & ~0x1F) != 0xC0 || !(svga->attrregs[0x10] & 4))
p[17] = bg;
else
p[17] = (dat & 1) ? fg : bg;
}
svga->ma += 8;
p += xinc * 2;
continue;
}
}
if (attr & 8) charaddr = svga->charsetb + (chr * 128);
else charaddr = svga->charseta + (chr * 128);
@@ -342,6 +262,127 @@ void svga_render_text_80(svga_t *svga)
}
}
void svga_render_text_80_ksc5601(svga_t *svga)
{
int y_add = enable_overscan ? (overscan_y >> 1) : 0;
int x_add = enable_overscan ? 8 : 0;
if (svga->firstline_draw == 2000)
svga->firstline_draw = svga->displine;
svga->lastline_draw = svga->displine;
if (svga->fullchange)
{
uint32_t *p = &((uint32_t *)buffer32->line[svga->displine + y_add])[32 + x_add];
int x, xx;
int drawcursor;
uint8_t chr, attr, dat, nextchr;
uint32_t charaddr;
int fg, bg;
int xinc = (svga->seqregs[1] & 1) ? 8 : 9;
for (x = 0; x < svga->hdisp; x += xinc)
{
drawcursor = ((svga->ma == svga->ca) && svga->con && svga->cursoron);
chr = svga->vram[(svga->ma << 1) & svga->vram_display_mask];
nextchr = svga->vram[((svga->ma + 4) << 1) & svga->vram_display_mask];
attr = svga->vram[((svga->ma << 1) + 1) & svga->vram_display_mask];
if (drawcursor)
{
bg = svga->pallook[svga->egapal[attr & 15]];
fg = svga->pallook[svga->egapal[attr >> 4]];
}
else
{
fg = svga->pallook[svga->egapal[attr & 15]];
bg = svga->pallook[svga->egapal[attr >> 4]];
if (attr & 0x80 && svga->attrregs[0x10] & 8)
{
bg = svga->pallook[svga->egapal[(attr >> 4) & 7]];
if (svga->blink & 16)
fg = bg;
}
}
fg = svga_color_transform(fg);
bg = svga_color_transform(bg);
if(x + xinc < svga->hdisp && (chr & nextchr & 0x80))
{
dat = fontdatksc5601[((chr & 0x7F) << 7) | (nextchr & 0x7F)].chr[svga->sc];
}
else
{
if (attr & 8) charaddr = svga->charsetb + (chr * 128);
else charaddr = svga->charseta + (chr * 128);
dat = svga->vram[charaddr + (svga->sc << 2)];
}
if (svga->seqregs[1] & 1)
{
for (xx = 0; xx < 8; xx++)
p[xx] = (dat & (0x80 >> xx)) ? fg : bg;
}
else
{
for (xx = 0; xx < 8; xx++)
p[xx] = (dat & (0x80 >> xx)) ? fg : bg;
if ((chr & ~0x1F) != 0xC0 || !(svga->attrregs[0x10] & 4))
p[8] = bg;
else
p[8] = (dat & 1) ? fg : bg;
}
svga->ma += 4;
p += xinc;
if(x + xinc < svga->hdisp && (chr & nextchr & 0x80))
{
attr = svga->vram[((svga->ma << 1) + 1) & svga->vram_display_mask];
if (drawcursor)
{
bg = svga->pallook[svga->egapal[attr & 15]];
fg = svga->pallook[svga->egapal[attr >> 4]];
}
else
{
fg = svga->pallook[svga->egapal[attr & 15]];
bg = svga->pallook[svga->egapal[attr >> 4]];
if (attr & 0x80 && svga->attrregs[0x10] & 8)
{
bg = svga->pallook[svga->egapal[(attr >> 4) & 7]];
if (svga->blink & 16)
fg = bg;
}
}
dat = fontdatksc5601[((chr & 0x7F) << 7) | (nextchr & 0x7F)].chr[svga->sc + 16];
if (svga->seqregs[1] & 1)
{
for (xx = 0; xx < 8; xx++)
p[xx] = (dat & (0x80 >> xx)) ? fg : bg;
}
else
{
for (xx = 0; xx < 8; xx++)
p[xx] = (dat & (0x80 >> xx)) ? fg : bg;
if ((chr & ~0x1F) != 0xC0 || !(svga->attrregs[0x10] & 4))
p[8] = bg;
else
p[8] = (dat & 1) ? fg : bg;
}
svga->ma += 4;
p += xinc;
x += xinc;
}
}
svga->ma &= svga->vram_display_mask;
}
}
void svga_render_2bpp_lowres(svga_t *svga)
{
int changed_offset;

View File

@@ -8,12 +8,12 @@
*
* SVGA renderers.
*
* Version: @(#)vid_svga_render.h 1.0.0 2017/05/30
* Version: @(#)vid_svga_render.h 1.0.1 2018/03/12
*
* Author: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Copyright 2008-2017 Sarah Walker.
* Copyright 2016-2017 Miran Grca.
* Copyright 2008-2018 Sarah Walker.
* Copyright 2016-2018 Miran Grca.
*/
extern int firstline_draw, lastline_draw;
@@ -29,9 +29,8 @@ extern uint8_t edatlookup[4][4];
void svga_render_blank(svga_t *svga);
void svga_render_text_40(svga_t *svga);
void svga_render_text_40_12(svga_t *svga);
void svga_render_text_80(svga_t *svga);
void svga_render_text_80_12(svga_t *svga);
void svga_render_text_80_ksc5601(svga_t *svga);
void svga_render_2bpp_lowres(svga_t *svga);
void svga_render_2bpp_highres(svga_t *svga);

View File

@@ -8,7 +8,7 @@
*
* Define all known video cards.
*
* Version: @(#)vid_table.c 1.0.24 2018/03/03
* Version: @(#)vid_table.c 1.0.24 2018/03/15
*
* Authors: Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
@@ -85,93 +85,94 @@ typedef struct {
static VIDEO_CARD
video_cards[] = {
{"None", "none", NULL, GFX_NONE },
{"Internal", "internal", NULL, GFX_INTERNAL, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] ATI Graphics Pro Turbo (Mach64 GX)", "mach64gx_isa", &mach64gx_isa_device, GFX_MACH64GX_ISA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 5, 5, 10}},
{"[ISA] ATI Korean VGA (ATI-28800-5)", "ati28800k", &ati28800k_device, GFX_ATIKOREANVGA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 5, 5, 10}},
{"[ISA] ATI VGA-88 (ATI-18800-1)", "ati18800v", &ati18800_vga88_device, GFX_VGA88, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] ATI VGA Charger (ATI-28800-5)", "ati28800", &ati28800_device, GFX_VGACHARGER, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 5, 5, 10}},
{"[ISA] ATI VGA Edge-16 (ATI-18800-5)", "ati18800", &ati18800_device, GFX_VGAEDGE16, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] ATI VGA Wonder (ATI-18800)", "ati18800w", &ati18800_wonder_device, GFX_VGAWONDER, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{ "None", "none", NULL, GFX_NONE },
{ "Internal", "internal", NULL, GFX_INTERNAL, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{ "[ISA] ATI Graphics Pro Turbo (Mach64 GX)", "mach64gx_isa", &mach64gx_isa_device, GFX_MACH64GX_ISA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 5, 5, 10}},
{"[ISA] ATI Korean VGA (ATI-28800-5)", "ati28800k", &ati28800k_device, GFX_ATIKOREANVGA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 5, 5, 10}},
{ "[ISA] ATI VGA-88 (ATI-18800-1)", "ati18800v", &ati18800_vga88_device, GFX_VGA88, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{ "[ISA] ATI VGA Charger (ATI-28800-5)", "ati28800", &ati28800_device, GFX_VGACHARGER, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 5, 5, 10}},
{ "[ISA] ATI VGA Edge-16 (ATI-18800-5)", "ati18800", &ati18800_device, GFX_VGAEDGE16, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{ "[ISA] ATI VGA Wonder (ATI-18800)", "ati18800w", &ati18800_wonder_device, GFX_VGAWONDER, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
#if defined(DEV_BRANCH) && defined(USE_XL24)
{"[ISA] ATI VGA Wonder XL24 (ATI-28800-6)", "ati28800w", &ati28800_wonderxl24_device, GFX_VGAWONDERXL24, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 5, 5, 10}},
{ "[ISA] ATI VGA Wonder XL24 (ATI-28800-6)", "ati28800w", &ati28800_wonderxl24_device,GFX_VGAWONDERXL24, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 5, 5, 10}},
#endif
{"[ISA] CGA", "cga", &cga_device, GFX_CGA, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] Chips & Technologies SuperEGA", "superega", &sega_device, GFX_SUPER_EGA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] Cirrus Logic CL-GD 5428", "cl_gd5428_isa", &gd5428_isa_device, GFX_CL_GD5428_ISA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 8, 8, 12}},
{"[ISA] Cirrus Logic CL-GD 5429", "cl_gd5429_isa", &gd5429_isa_device, GFX_CL_GD5429_ISA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 8, 8, 12}},
{"[ISA] Cirrus Logic CL-GD 5434", "cl_gd5434_isa", &gd5434_isa_device, GFX_CL_GD5434_ISA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 8, 8, 12}},
{"[ISA] Compaq ATI VGA Wonder XL (ATI-28800-5)", "compaq_ati28800", &compaq_ati28800_device, GFX_VGAWONDERXL, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 5, 5, 10}},
{"[ISA] Compaq CGA", "compaq_cga", &compaq_cga_device, GFX_COMPAQ_CGA, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] Compaq CGA 2", "compaq_cga_2", &compaq_cga_2_device, GFX_COMPAQ_CGA_2, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] Compaq EGA", "compaq_ega", &cpqega_device, GFX_COMPAQ_EGA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] EGA", "ega", &ega_device, GFX_EGA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] Hercules", "hercules", &hercules_device, GFX_HERCULES, VIDEO_FLAG_TYPE_MDA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] Hercules Plus", "hercules_plus", &herculesplus_device, GFX_HERCULESPLUS, VIDEO_FLAG_TYPE_MDA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] Hercules InColor", "incolor", &incolor_device, GFX_INCOLOR, VIDEO_FLAG_TYPE_MDA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] MDA", "mda", &mda_device, GFX_MDA, VIDEO_FLAG_TYPE_MDA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] MDSI Genius", "genius", &genius_device, GFX_GENIUS, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] OAK OTI-037C", "oti037c", &oti037c_device, GFX_OTI037C, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 6, 8, 16, 6, 8, 16}},
{"[ISA] OAK OTI-067", "oti067", &oti067_device, GFX_OTI067, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 6, 8, 16, 6, 8, 16}},
{"[ISA] OAK OTI-077", "oti077", &oti077_device, GFX_OTI077, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 6, 8, 16, 6, 8, 16}},
{"[ISA] Paradise PVGA1A", "pvga1a", &paradise_pvga1a_device, GFX_PVGA1A, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] Paradise WD90C11-LR", "wd90c11", &paradise_wd90c11_device, GFX_WD90C11, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] Paradise WD90C30-LR", "wd90c30", &paradise_wd90c30_device, GFX_WD90C30, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 6, 8, 16, 6, 8, 16}},
{"[ISA] Plantronics ColorPlus", "plantronics", &colorplus_device, GFX_COLORPLUS, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{ "[ISA] CGA", "cga", &cga_device, GFX_CGA, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{ "[ISA] Chips & Technologies SuperEGA", "superega", &sega_device, GFX_SUPER_EGA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{ "[ISA] Cirrus Logic CL-GD 5428", "cl_gd5428_isa", &gd5428_isa_device, GFX_CL_GD5428_ISA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 8, 8, 12}},
{ "[ISA] Cirrus Logic CL-GD 5429", "cl_gd5429_isa", &gd5429_isa_device, GFX_CL_GD5429_ISA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 8, 8, 12}},
{ "[ISA] Cirrus Logic CL-GD 5434", "cl_gd5434_isa", &gd5434_isa_device, GFX_CL_GD5434_ISA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 8, 8, 12}},
{ "[ISA] Compaq ATI VGA Wonder XL (ATI-28800-5)","compaq_ati28800", &compaq_ati28800_device, GFX_VGAWONDERXL, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 5, 5, 10}},
{ "[ISA] Compaq CGA", "compaq_cga", &compaq_cga_device, GFX_COMPAQ_CGA, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{ "[ISA] Compaq CGA 2", "compaq_cga_2", &compaq_cga_2_device, GFX_COMPAQ_CGA_2, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{ "[ISA] Compaq EGA", "compaq_ega", &cpqega_device, GFX_COMPAQ_EGA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{ "[ISA] EGA", "ega", &ega_device, GFX_EGA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{ "[ISA] Hercules", "hercules", &hercules_device, GFX_HERCULES, VIDEO_FLAG_TYPE_MDA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{ "[ISA] Hercules Plus", "hercules_plus", &herculesplus_device, GFX_HERCULESPLUS, VIDEO_FLAG_TYPE_MDA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{ "[ISA] Hercules InColor", "incolor", &incolor_device, GFX_INCOLOR, VIDEO_FLAG_TYPE_MDA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{ "[ISA] MDA", "mda", &mda_device, GFX_MDA, VIDEO_FLAG_TYPE_MDA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{ "[ISA] MDSI Genius", "genius", &genius_device, GFX_GENIUS, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] OAK OTI-037C", "oti037c", &oti037c_device, GFX_OTI037C, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 6, 8, 16, 6, 8, 16}},
{"[ISA] OAK OTI-067", "oti067", &oti067_device, GFX_OTI067, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 6, 8, 16, 6, 8, 16}},
{"[ISA] OAK OTI-077", "oti077", &oti077_device, GFX_OTI077, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 6, 8, 16, 6, 8, 16}},
{"[ISA] Paradise PVGA1A", "pvga1a", &paradise_pvga1a_device, GFX_PVGA1A, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] Paradise WD90C11-LR", "wd90c11", &paradise_wd90c11_device, GFX_WD90C11, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] Paradise WD90C30-LR", "wd90c30", &paradise_wd90c30_device, GFX_WD90C30, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 6, 8, 16, 6, 8, 16}},
{"[ISA] Plantronics ColorPlus", "plantronics", &colorplus_device, GFX_COLORPLUS, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
#if defined(DEV_BRANCH) && defined(USE_TI)
{"[ISA] TI CF62011 SVGA", "ti_cf62011", &ti_cf62011_device, GFX_TICF62011, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] TI CF62011 SVGA", "ti_cf62011", &ti_cf62011_device, GFX_TICF62011, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
#endif
{"[ISA] Trident TVGA8900D", "tvga8900d", &tvga8900d_device, GFX_TVGA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 8, 8, 12}},
{"[ISA] Tseng ET4000AX", "et4000ax", &et4000_device, GFX_ET4000, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 5, 5, 10}},
{"[ISA] VGA", "vga", &vga_device, GFX_VGA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] Wyse 700", "wy700", &wy700_device, GFX_WY700, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[PCI] ATI Graphics Pro Turbo (Mach64 GX)", "mach64gx_pci", &mach64gx_pci_device, GFX_MACH64GX_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 1, 20, 20, 21}},
{"[PCI] ATI Video Xpression (Mach64 VT2)", "mach64vt2", &mach64vt2_device, GFX_MACH64VT2, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 1, 20, 20, 21}},
{"[PCI] Cardex Tseng ET4000/w32p", "et4000w32p_pci", &et4000w32p_cardex_pci_device, GFX_ET4000W32_CARDEX_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 4, 10, 10, 10}},
{"[PCI] Cirrus Logic CL-GD 5430", "cl_gd5430_pci", &gd5430_pci_device, GFX_CL_GD5430_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 8, 10, 10, 20}},
{"[PCI] Cirrus Logic CL-GD 5434", "cl_gd5434_pci", &gd5434_pci_device, GFX_CL_GD5434_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 8, 10, 10, 20}},
{"[PCI] Cirrus Logic CL-GD 5436", "cl_gd5436_pci", &gd5436_pci_device, GFX_CL_GD5436_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 8, 10, 10, 20}},
{"[ISA] Trident TVGA8900D", "tvga8900d", &tvga8900d_device, GFX_TVGA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 8, 8, 12}},
{"[ISA] Tseng ET4000AX", "et4000ax", &et4000_device, GFX_ET4000, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 3, 3, 6, 5, 5, 10}},
{"[ISA] VGA", "vga", &vga_device, GFX_VGA, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[ISA] Wyse 700", "wy700", &wy700_device, GFX_WY700, VIDEO_FLAG_TYPE_CGA, {VIDEO_ISA, 8, 16, 32, 8, 16, 32}},
{"[PCI] ATI Graphics Pro Turbo (Mach64 GX)", "mach64gx_pci", &mach64gx_pci_device, GFX_MACH64GX_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 1, 20, 20, 21}},
{"[PCI] ATI Video Xpression (Mach64 VT2)", "mach64vt2", &mach64vt2_device, GFX_MACH64VT2, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 1, 20, 20, 21}},
{"[PCI] Cardex Tseng ET4000/w32p", "et4000w32p_pci", &et4000w32p_cardex_pci_device,GFX_ET4000W32_CARDEX_PCI,VIDEO_FLAG_TYPE_SPECIAL,{VIDEO_BUS, 4, 4, 4, 10, 10, 10}},
{"[PCI] Cirrus Logic CL-GD 5430", "cl_gd5430_pci", &gd5430_pci_device, GFX_CL_GD5430_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 8, 10, 10, 20}},
{"[PCI] Cirrus Logic CL-GD 5434", "cl_gd5434_pci", &gd5434_pci_device, GFX_CL_GD5434_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 8, 10, 10, 20}},
{"[PCI] Cirrus Logic CL-GD 5436", "cl_gd5436_pci", &gd5436_pci_device, GFX_CL_GD5436_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 8, 10, 10, 20}},
#if defined(DEV_BRANCH) && defined(USE_STEALTH32)
{"[PCI] Diamond Stealth 32 (Tseng ET4000/w32p)","stealth32_pci", &et4000w32p_pci_device, GFX_ET4000W32_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 4, 10, 10, 10}},
{"[PCI] Diamond Stealth 32 (Tseng ET4000/w32p)","stealth32_pci", &et4000w32p_pci_device, GFX_ET4000W32_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 4, 10, 10, 10}},
#endif
{"[PCI] Diamond Stealth 3D 2000 (S3 ViRGE)", "stealth3d_2000_pci",&s3_virge_pci_device, GFX_VIRGE_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 28, 28, 45}},
{"[PCI] Diamond Stealth 3D 3000 (S3 ViRGE/VX)", "stealth3d_3000_pci",&s3_virge_988_pci_device, GFX_VIRGEVX_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 4, 26, 26, 42}},
{"[PCI] Diamond Stealth 64 DRAM (S3 Trio64)", "stealth64d_pci", &s3_diamond_stealth64_pci_device, GFX_STEALTH64_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 4, 26, 26, 42}},
{"[PCI] Diamond Stealth 3D 2000 (S3 ViRGE)", "stealth3d_2000_pci",&s3_virge_pci_device, GFX_VIRGE_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 28, 28, 45}},
{"[PCI] Diamond Stealth 3D 3000 (S3 ViRGE/VX)", "stealth3d_3000_pci",&s3_virge_988_pci_device, GFX_VIRGEVX_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 4, 26, 26, 42}},
{"[PCI] Diamond Stealth 64 DRAM (S3 Trio64)", "stealth64d_pci", &s3_diamond_stealth64_pci_device,GFX_STEALTH64_PCI,VIDEO_FLAG_TYPE_SPECIAL,{VIDEO_BUS, 2, 2, 4, 26, 26, 42}},
#if defined(DEV_BRANCH) && defined(USE_RIVA)
{"[PCI] nVidia RIVA 128", "riva128", &riva128_device, GFX_RIVA128, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 24, 24, 36}},
{"[PCI] nVidia RIVA TNT", "rivatnt", &rivatnt_device, GFX_RIVATNT, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 24, 24, 36}},
{"[PCI] nVidia RIVA TNT2", "rivatnt2", &rivatnt2_device, GFX_RIVATNT2, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 24, 24, 36}},
{"[PCI] nVidia RIVA 128", "riva128", &riva128_device, GFX_RIVA128, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 24, 24, 36}},
{"[PCI] nVidia RIVA TNT", "rivatnt", &rivatnt_device, GFX_RIVATNT, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 24, 24, 36}},
{"[PCI] nVidia RIVA TNT2", "rivatnt2", &rivatnt2_device, GFX_RIVATNT2, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 24, 24, 36}},
#endif
{"[PCI] Number Nine 9FX (S3 Trio64)", "n9_9fx_pci", &s3_9fx_pci_device, GFX_N9_9FX_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 3, 2, 4, 25, 25, 40}},
{"[PCI] Paradise Bahamas 64 (S3 Vision864)", "bahamas64_pci", &s3_bahamas64_pci_device, GFX_BAHAMAS64_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 5, 20, 20, 35}},
{"[PCI] Phoenix S3 Vision864", "px_vision864_pci", &s3_phoenix_vision864_pci_device, GFX_PHOENIX_VISION864_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 5, 20, 20, 35}},
{"[PCI] Phoenix S3 Trio32", "px_trio32_pci", &s3_phoenix_trio32_pci_device, GFX_PHOENIX_TRIO32_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 3, 2, 4, 25, 25, 40}},
{"[PCI] Phoenix S3 Trio64", "px_trio64_pci", &s3_phoenix_trio64_pci_device, GFX_PHOENIX_TRIO64_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 3, 2, 4, 25, 25, 40}},
{"[PCI] S3 ViRGE/DX", "virge375_pci", &s3_virge_375_pci_device, GFX_VIRGEDX_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 28, 28, 45}},
{"[PCI] S3 ViRGE/DX (VBE 2.0)", "virge375_vbe20_pci",&s3_virge_375_4_pci_device, GFX_VIRGEDX4_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 28, 28, 45}},
{"[PCI] Trident TGUI9440", "tgui9440_pci", &tgui9440_pci_device, GFX_TGUI9440_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 8, 16, 4, 8, 16}},
{"[VLB] ATI Graphics Pro Turbo (Mach64 GX)", "mach64gx_vlb", &mach64gx_vlb_device, GFX_MACH64GX_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 1, 20, 20, 21}},
{"[VLB] Cardex Tseng ET4000/w32p", "et4000w32p_vlb", &et4000w32p_cardex_vlb_device, GFX_ET4000W32_CARDEX_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 4, 10, 10, 10}},
{"[VLB] Cirrus Logic CL-GD 5429", "cl_gd5429_vlb", &gd5429_vlb_device, GFX_CL_GD5429_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 8, 10, 10, 20}},
{"[VLB] Cirrus Logic CL-GD 5434", "cl_gd5434_vlb", &gd5434_vlb_device, GFX_CL_GD5434_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 8, 10, 10, 20}},
{"[PCI] Number Nine 9FX (S3 Trio64)", "n9_9fx_pci", &s3_9fx_pci_device, GFX_N9_9FX_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 3, 2, 4, 25, 25, 40}},
{"[PCI] Paradise Bahamas 64 (S3 Vision864)", "bahamas64_pci", &s3_bahamas64_pci_device, GFX_BAHAMAS64_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 5, 20, 20, 35}},
{"[PCI] Phoenix S3 Vision864", "px_vision864_pci", &s3_phoenix_vision864_pci_device,GFX_PHOENIX_VISION864_PCI,VIDEO_FLAG_TYPE_SPECIAL,{VIDEO_BUS, 4, 4, 5, 20, 20, 35}},
{"[PCI] Phoenix S3 Trio32", "px_trio32_pci", &s3_phoenix_trio32_pci_device,GFX_PHOENIX_TRIO32_PCI,VIDEO_FLAG_TYPE_SPECIAL,{VIDEO_BUS, 3, 2, 4, 25, 25, 40}},
{"[PCI] Phoenix S3 Trio64", "px_trio64_pci", &s3_phoenix_trio64_pci_device,GFX_PHOENIX_TRIO64_PCI,VIDEO_FLAG_TYPE_SPECIAL,{VIDEO_BUS, 3, 2, 4, 25, 25, 40}},
{"[PCI] S3 ViRGE/DX", "virge375_pci", &s3_virge_375_pci_device, GFX_VIRGEDX_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 28, 28, 45}},
{"[PCI] S3 ViRGE/DX (VBE 2.0)", "virge375_vbe20_pci",&s3_virge_375_4_pci_device,GFX_VIRGEDX4_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 28, 28, 45}},
{"[PCI] Trident TGUI9440", "tgui9440_pci", &tgui9440_pci_device, GFX_TGUI9440_PCI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 8, 16, 4, 8, 16}},
{"[VLB] ATI Graphics Pro Turbo (Mach64 GX)", "mach64gx_vlb", &mach64gx_vlb_device, GFX_MACH64GX_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 1, 20, 20, 21}},
{"[VLB] Cardex Tseng ET4000/w32p", "et4000w32p_vlb", &et4000w32p_cardex_vlb_device,GFX_ET4000W32_CARDEX_VLB,VIDEO_FLAG_TYPE_SPECIAL,{VIDEO_BUS, 4, 4, 4, 10, 10, 10}},
{"[VLB] Cirrus Logic CL-GD 5428", "cl_gd5428_vlb", &gd5428_vlb_device, GFX_CL_GD5428_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 8, 10, 10, 20}},
{"[VLB] Cirrus Logic CL-GD 5429", "cl_gd5429_vlb", &gd5429_vlb_device, GFX_CL_GD5429_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 8, 10, 10, 20}},
{"[VLB] Cirrus Logic CL-GD 5434", "cl_gd5434_vlb", &gd5434_vlb_device, GFX_CL_GD5434_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 8, 10, 10, 20}},
#if defined(DEV_BRANCH) && defined(USE_STEALTH32)
{"[VLB] Diamond Stealth 32 (Tseng ET4000/w32p)","stealth32_vlb", &et4000w32p_vlb_device, GFX_ET4000W32_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 4, 10, 10, 10}},
{"[VLB] Diamond Stealth 32 (Tseng ET4000/w32p)","stealth32_vlb", &et4000w32p_vlb_device, GFX_ET4000W32_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 4, 10, 10, 10}},
#endif
{"[VLB] Diamond SpeedStar PRO (CL-GD5428)", "cl_gd5428_vlb", &gd5428_vlb_device, GFX_CL_GD5428_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 8, 10, 10, 20}},
{"[VLB] Diamond SpeedStar PRO SE (CL-GD5430)", "cl_gd5430_vlb", &gd5430_vlb_device, GFX_CL_GD5430_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 8, 10, 10, 20}},
{"[VLB] Diamond Stealth 3D 2000 (S3 ViRGE)", "stealth3d_2000_vlb",&s3_virge_vlb_device, GFX_VIRGE_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 28, 28, 45}},
{"[VLB] Diamond Stealth 3D 3000 (S3 ViRGE/VX)", "stealth3d_3000_vlb",&s3_virge_988_vlb_device, GFX_VIRGEVX_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 4, 26, 26, 42}},
{"[VLB] Diamond Stealth 64 DRAM (S3 Trio64)", "stealth64d_vlb", &s3_diamond_stealth64_vlb_device, GFX_STEALTH64_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 4, 26, 26, 42}},
{"[VLB] Number Nine 9FX (S3 Trio64)", "n9_9fx_vlb", &s3_9fx_vlb_device, GFX_N9_9FX_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 3, 2, 4, 25, 25, 40}},
{"[VLB] Paradise Bahamas 64 (S3 Vision864)", "bahamas64_vlb", &s3_bahamas64_vlb_device, GFX_BAHAMAS64_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 5, 20, 20, 35}},
{"[VLB] Phoenix S3 Vision864", "px_vision864_vlb", &s3_phoenix_vision864_vlb_device, GFX_PHOENIX_VISION864_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 5, 20, 20, 35}},
{"[VLB] Phoenix S3 Trio32", "px_trio32_vlb", &s3_phoenix_trio32_vlb_device, GFX_PHOENIX_TRIO32_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 3, 2, 4, 25, 25, 40}},
{"[VLB] Phoenix S3 Trio64", "px_trio64_vlb", &s3_phoenix_trio64_vlb_device, GFX_PHOENIX_TRIO64_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 3, 2, 4, 25, 25, 40}},
{"[VLB] S3 ViRGE/DX", "virge375_vlb", &s3_virge_375_vlb_device, GFX_VIRGEDX_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 28, 28, 45}},
{"[VLB] S3 ViRGE/DX (VBE 2.0)", "virge375_vbe20_vlb",&s3_virge_375_4_vlb_device, GFX_VIRGEDX4_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 28, 28, 45}},
{"[VLB] Trident TGUI9400CXi", "tgui9400cxi_vlb", &tgui9400cxi_device, GFX_TGUI9400CXI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 8, 16, 4, 8, 16}},
{"[VLB] Trident TGUI9440", "tgui9440_vlb", &tgui9440_vlb_device, GFX_TGUI9440_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 8, 16, 4, 8, 16}},
{"", "", NULL, -1 }
{"[VLB] Diamond SpeedStar PRO (CL-GD5426)", "cl_gd5426_vlb", &gd5426_vlb_device, GFX_CL_GD5426_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 8, 10, 10, 20}},
{"[VLB] Diamond SpeedStar PRO SE (CL-GD5430)", "cl_gd5430_vlb", &gd5430_vlb_device, GFX_CL_GD5430_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 8, 10, 10, 20}},
{"[VLB] Diamond Stealth 3D 2000 (S3 ViRGE)", "stealth3d_2000_vlb",&s3_virge_vlb_device, GFX_VIRGE_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 28, 28, 45}},
{"[VLB] Diamond Stealth 3D 3000 (S3 ViRGE/VX)", "stealth3d_3000_vlb",&s3_virge_988_vlb_device, GFX_VIRGEVX_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 4, 26, 26, 42}},
{"[VLB] Diamond Stealth 64 DRAM (S3 Trio64)", "stealth64d_vlb", &s3_diamond_stealth64_vlb_device,GFX_STEALTH64_VLB,VIDEO_FLAG_TYPE_SPECIAL,{VIDEO_BUS, 2, 2, 4, 26, 26, 42}},
{"[VLB] Number Nine 9FX (S3 Trio64)", "n9_9fx_vlb", &s3_9fx_vlb_device, GFX_N9_9FX_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 3, 2, 4, 25, 25, 40}},
{"[VLB] Paradise Bahamas 64 (S3 Vision864)", "bahamas64_vlb", &s3_bahamas64_vlb_device, GFX_BAHAMAS64_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 4, 5, 20, 20, 35}},
{"[VLB] Phoenix S3 Vision864", "px_vision864_vlb", &s3_phoenix_vision864_vlb_device,GFX_PHOENIX_VISION864_VLB,VIDEO_FLAG_TYPE_SPECIAL,{VIDEO_BUS, 4, 4, 5, 20, 20, 35}},
{"[VLB] Phoenix S3 Trio32", "px_trio32_vlb", &s3_phoenix_trio32_vlb_device,GFX_PHOENIX_TRIO32_VLB,VIDEO_FLAG_TYPE_SPECIAL,{VIDEO_BUS, 3, 2, 4, 25, 25, 40}},
{"[VLB] Phoenix S3 Trio64", "px_trio64_vlb", &s3_phoenix_trio64_vlb_device,GFX_PHOENIX_TRIO64_VLB,VIDEO_FLAG_TYPE_SPECIAL,{VIDEO_BUS, 3, 2, 4, 25, 25, 40}},
{"[VLB] S3 ViRGE/DX", "virge375_vlb", &s3_virge_375_vlb_device, GFX_VIRGEDX_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 28, 28, 45}},
{"[VLB] S3 ViRGE/DX (VBE 2.0)", "virge375_vbe20_vlb",&s3_virge_375_4_vlb_device,GFX_VIRGEDX4_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 2, 2, 3, 28, 28, 45}},
{"[VLB] Trident TGUI9400CXi", "tgui9400cxi_vlb", &tgui9400cxi_device, GFX_TGUI9400CXI, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 8, 16, 4, 8, 16}},
{"[VLB] Trident TGUI9440", "tgui9440_vlb", &tgui9440_vlb_device, GFX_TGUI9440_VLB, VIDEO_FLAG_TYPE_SPECIAL, {VIDEO_BUS, 4, 8, 16, 4, 8, 16}},
{"", "", NULL, -1 }
};
@@ -189,6 +190,11 @@ video_reset(int card)
if ((card == GFX_NONE) || \
(card == GFX_INTERNAL) || machines[machine].fixed_gfxcard) return;
if (fontdatksc5601) {
free(fontdatksc5601);
fontdatksc5601 = NULL;
}
pclog("VIDEO: initializing '%s'\n", video_cards[video_old_to_new(card)].name);
/* Initialize the video card. */
device_add(video_cards[video_old_to_new(card)].device);
@@ -317,6 +323,8 @@ int video_is_mda(void)
case ROM_IBMPS2_M30_286:
case ROM_IBMPS2_M50:
case ROM_IBMPS2_M55SX:
case ROM_IBMPS2_M70_TYPE3:
case ROM_IBMPS2_M70_TYPE4:
case ROM_IBMPS2_M80:
case ROM_IBMPS1_2121:
case ROM_T3100E:
@@ -348,6 +356,8 @@ int video_is_cga(void)
case ROM_IBMPS2_M30_286:
case ROM_IBMPS2_M50:
case ROM_IBMPS2_M55SX:
case ROM_IBMPS2_M70_TYPE3:
case ROM_IBMPS2_M70_TYPE4:
case ROM_IBMPS2_M80:
case ROM_IBMPS1_2121:
return 0;
@@ -378,6 +388,8 @@ int video_is_ega_vga(void)
case ROM_IBMPS2_M30_286:
case ROM_IBMPS2_M50:
case ROM_IBMPS2_M55SX:
case ROM_IBMPS2_M70_TYPE3:
case ROM_IBMPS2_M70_TYPE4:
case ROM_IBMPS2_M80:
case ROM_IBMPS1_2121:
return 1;

View File

@@ -40,7 +40,7 @@
* W = 3 bus clocks
* L = 4 bus clocks
*
* Version: @(#)video.c 1.0.18 2018/03/02
* Version: @(#)video.c 1.0.19 2018/03/15
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -79,7 +79,7 @@ uint8_t fontdat[2048][8]; /* IBM CGA font */
uint8_t fontdatm[2048][16]; /* IBM MDA font */
uint8_t fontdatw[512][32]; /* Wyse700 font */
uint8_t fontdat8x12[256][16]; /* MDSI Genius font */
uint8_t fontdatksc5601[16384][32]; /* Korean KSC-5601 font */
dbcs_font_t *fontdatksc5601;; /* Korean KSC-5601 font */
uint32_t pal_lookup[256];
int xsize = 1,
ysize = 1;
@@ -656,6 +656,11 @@ video_close(void)
destroy_bitmap(buffer);
destroy_bitmap(buffer32);
if (fontdatksc5601) {
free(fontdatksc5601);
fontdatksc5601 = NULL;
}
}
@@ -765,11 +770,14 @@ loadfont(wchar_t *s, int format)
break;
case 6: /* Korean KSC-5601 */
if (!fontdatksc5601)
fontdatksc5601 = malloc(16384 * sizeof(dbcs_font_t));
for (c=0;c<16384;c++)
{
for (d=0;d<32;d++)
{
fontdatksc5601[c][d]=getc(f);
fontdatksc5601[c].chr[d]=getc(f);
}
}
break;

View File

@@ -8,7 +8,7 @@
*
* Definitions for the video controller module.
*
* Version: @(#)video.h 1.0.22 2018/03/02
* Version: @(#)video.h 1.0.23 2018/03/15
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -76,11 +76,12 @@ enum {
GFX_MACH64VT2, /* ATI Mach64 VT2 */
GFX_CL_GD5424_ISA, /* Cirrus Logic CL-GD 5424 ISA */
GFX_CL_GD5424_VLB, /* Cirrus Logic CL-GD 5424 VLB */
GFX_CL_GD5426_VLB, /* Diamond SpeedStar PRO (Cirrus Logic CL-GD 5426) VLB */
GFX_CL_GD5428_ISA, /* Cirrus Logic CL-GD 5428 ISA */
GFX_CL_GD5428_VLB, /* Diamond SpeedStar PRO (Cirrus Logic CL-GD 5428) VLB */
GFX_CL_GD5428_VLB, /* Cirrus Logic CL-GD 5428 VLB */
GFX_CL_GD5429_ISA, /* Cirrus Logic CL-GD 5429 ISA */
GFX_CL_GD5429_VLB, /* Cirrus Logic CL-GD 5429 VLB */
GFX_CL_GD5430_VLB, /* Diamond SpeedStar PRO SE (Cirrus Logic CL-GD 5430) PCI */
GFX_CL_GD5430_VLB, /* Diamond SpeedStar PRO SE (Cirrus Logic CL-GD 5430) VLB */
GFX_CL_GD5430_PCI, /* Cirrus Logic CL-GD 5430 PCI */
GFX_CL_GD5434_ISA, /* Cirrus Logic CL-GD 5434 ISA */
GFX_CL_GD5434_VLB, /* Cirrus Logic CL-GD 5434 VLB */
@@ -150,6 +151,10 @@ typedef struct {
uint8_t r, g, b;
} rgb_t;
typedef struct {
uint8_t chr[32];
} dbcs_font_t;
typedef rgb_t PALETTE[256];
@@ -170,7 +175,7 @@ extern int video_fullscreen,
extern int fullchange;
extern uint8_t fontdat[2048][8];
extern uint8_t fontdatm[2048][16];
extern uint8_t fontdatksc5601[16384][32];
extern dbcs_font_t *fontdatksc5601;
extern uint32_t *video_6to8,
*video_15to32,
*video_16to32;

View File

@@ -8,7 +8,7 @@
#
# Makefile for Win32 (MinGW32) environment.
#
# Version: @(#)Makefile.mingw 1.0.110 2018/03/07
# Version: @(#)Makefile.mingw 1.0.112 2018/03/16
#
# Authors: Miran Grca, <mgrca8@gmail.com>
# Fred N. van Kempen, <decwiz@yahoo.com>
@@ -292,7 +292,8 @@ ifneq ($(WX), n)
LIBS += $(WX_LIBS)
UIOBJ := wx_main.o wx_ui.o wx_stbar.o wx_render.o
else
UIOBJ := win_ui.o win_ddraw.o win_d3d.o win_png.o \
UIOBJ := win_ui.o \
win_ddraw.o win_d3d.o \
win_dialog.o win_about.o win_status.o win_stbar.o \
win_settings.o win_devconf.o win_snd_gain.o \
win_new_floppy.o win_jsconf.o
@@ -415,8 +416,7 @@ CFLAGS := $(CFLAGS)
#########################################################################
MAINOBJ := pc.o config.o random.o timer.o io.o dma.o nmi.o pic.o \
pit.o ppi.o pci.o mca.o mcr.o mem.o memregs.o rom.o \
device.o nvr.o nvr_tc8521.o rtc_tc8521.o nvr_at.o \
nvr_ps2.o $(VNCOBJ) $(RDPOBJ)
device.o nvr.o nvr_at.o nvr_ps2.o $(VNCOBJ) $(RDPOBJ)
INTELOBJ := intel.o \
intel_flash.o \
@@ -429,7 +429,8 @@ CPUOBJ := cpu.o cpu_table.o \
MCHOBJ := machine.o machine_table.o \
m_xt.o m_xt_compaq.o \
m_xt_t1000.o m_xt_xi8088.o \
m_xt_t1000.o m_xt_t1000_vid.o \
m_xt_xi8088.o \
m_pcjr.o \
m_amstrad.o \
m_europc.o m_europc_hdc.o \
@@ -437,7 +438,7 @@ MCHOBJ := machine.o machine_table.o \
m_at.o \
m_at_ali1429.o m_at_commodore.o \
m_at_neat.o m_at_headland.o \
m_at_t3100e.o \
m_at_t3100e.o m_at_t3100e_vid.o \
m_ps1.o \
m_ps2_isa.o m_ps2_mca.o \
m_at_opti495.o m_at_scat.o \
@@ -526,7 +527,6 @@ VIDOBJ := video.o \
vid_hercules.o vid_herculesplus.o vid_incolor.o \
vid_colorplus.o \
vid_genius.o \
vid_t1000.o vid_t3100e.o \
vid_wy700.o \
vid_ega.o vid_ega_render.o \
vid_svga.o vid_svga_render.o \
@@ -568,15 +568,15 @@ LIBS := -mwindows \
-lddraw -ldinput8 -ldxguid -ld3d9 -ld3dx9 \
-lcomctl32 -lwinmm
ifeq ($(VNC), y)
LIBS += $(VNCLIB) -lws2_32 -lz
LIBS += $(VNCLIB) -lws2_32
endif
ifeq ($(RDP), y)
LIBS += $(RDPLIB)
endif
ifneq ($(WX), n)
LIBS += $(WX_LIBS) -lz -lm
LIBS += $(WX_LIBS) -lm
endif
LIBS += -lkernel32 -lwsock32 -liphlpapi -lpsapi
LIBS += -lpng -lz -lkernel32 -lwsock32 -liphlpapi -lpsapi
LIBS += -lpthread -static -lstdc++ -lgcc
ifneq ($(X64), y)
LIBS += -Wl,--large-address-aware

View File

@@ -8,7 +8,7 @@
*
* Platform main support module for Windows.
*
* Version: @(#)win.c 1.0.45 2018/01/23
* Version: @(#)win.c 1.0.46 2018/03/16
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -770,32 +770,27 @@ take_screenshot(void)
wcscat(path, L"\\");
wcsftime(fn, 128, L"%Y%m%d_%H%M%S.png", info);
wcscat(path, fn);
switch(vid_api) {
#ifdef USE_WX
case 0:
case 1:
wcsftime(fn, 128, L"%Y%m%d_%H%M%S.png", info);
wcscat(path, fn);
wx_screenshot(path);
break;
#else
case 0: /* ddraw */
wcsftime(fn, 128, L"%Y%m%d_%H%M%S.bmp", info);
wcscat(path, fn);
ddraw_take_screenshot(path);
break;
case 1: /* d3d9 */
wcsftime(fn, 128, L"%Y%m%d_%H%M%S.png", info);
wcscat(path, fn);
d3d_take_screenshot(path);
break;
#endif
#ifdef USE_VNC
case 2: /* vnc */
wcsftime(fn, 128, L"%Y%m%d_%H%M%S.png", info);
wcscat(path, fn);
vnc_take_screenshot(path);
break;
#endif

View File

@@ -9,7 +9,7 @@
* Implementation of the CD-ROM host drive IOCTL interface for
* Windows using SCSI Passthrough Direct.
*
* Version: @(#)cdrom_ioctl.c 1.0.12 2018/02/25
* Version: @(#)cdrom_ioctl.c 1.0.13 2018/03/15
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -98,9 +98,9 @@ void ioctl_audio_callback(uint8_t id, int16_t *output, int len)
in.DiskOffset.HighPart = 0;
in.SectorCount = 1;
in.TrackMode = CDDA;
if (!DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL, IOCTL_CDROM_RAW_READ, &in, sizeof(in), &(cdrom_ioctl[id].cd_buffer[cdrom_ioctl[id].cd_buflen]), 2352, &count, NULL))
if (!DeviceIoControl(cdrom_ioctl_windows[id].hIOCTL, IOCTL_CDROM_RAW_READ, &in, sizeof(in), &(cdrom[id].cd_buffer[cdrom_ioctl[id].cd_buflen]), 2352, &count, NULL))
{
memset(&(cdrom_ioctl[id].cd_buffer[cdrom_ioctl[id].cd_buflen]), 0, (BUF_SIZE - cdrom_ioctl[id].cd_buflen) * 2);
memset(&(cdrom[id].cd_buffer[cdrom_ioctl[id].cd_buflen]), 0, (BUF_SIZE - cdrom_ioctl[id].cd_buflen) * 2);
cdrom_ioctl_windows[id].is_playing = 0;
ioctl_close(id);
cdrom_ioctl[id].cd_state = CD_STOPPED;
@@ -114,15 +114,15 @@ void ioctl_audio_callback(uint8_t id, int16_t *output, int len)
}
else
{
memset(&(cdrom_ioctl[id].cd_buffer[cdrom_ioctl[id].cd_buflen]), 0, (BUF_SIZE - cdrom_ioctl[id].cd_buflen) * 2);
memset(&(cdrom[id].cd_buffer[cdrom_ioctl[id].cd_buflen]), 0, (BUF_SIZE - cdrom_ioctl[id].cd_buflen) * 2);
cdrom_ioctl_windows[id].is_playing = 0;
ioctl_close(id);
cdrom_ioctl[id].cd_state = CD_STOPPED;
cdrom_ioctl[id].cd_buflen = len;
}
}
memcpy(output, cdrom_ioctl[id].cd_buffer, len * 2);
memcpy(&cdrom_ioctl[id].cd_buffer[0], &(cdrom_ioctl[id].cd_buffer[len]), (BUF_SIZE - len) * 2);
memcpy(output, cdrom[id].cd_buffer, len * 2);
memcpy(&cdrom[id].cd_buffer[0], &(cdrom[id].cd_buffer[len]), (BUF_SIZE - len) * 2);
cdrom_ioctl[id].cd_buflen -= len;
}

View File

@@ -11,7 +11,7 @@
* NOTES: This code should be re-merged into a single init() with a
* 'fullscreen' argument, indicating FS mode is requested.
*
* Version: @(#)win_ddraw.cpp 1.0.5 2018/01/15
* Version: @(#)win_ddraw.cpp 1.0.6 2018/03/16
*
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
@@ -27,13 +27,16 @@
#define BITMAP WINDOWS_BITMAP
#include <windows.h>
#undef BITMAP
#define PNG_DEBUG 0
#include <png.h>
#include "../86box.h"
#include "../device.h"
#include "../video/video.h"
#include "../plat.h"
#include "../ui.h"
#include "win_ddraw.h"
#include "win_png.h"
#include "win.h"
@@ -49,6 +52,9 @@ static HBITMAP hbitmap;
static int ddraw_w, ddraw_h,
xs, ys, ys2;
static png_structp png_ptr;
static png_infop info_ptr;
static void
CopySurface(IDirectDrawSurface4 *pDDSurface)
@@ -71,6 +77,24 @@ CopySurface(IDirectDrawSurface4 *pDDSurface)
}
static void
bgra_to_rgb(png_bytep *b_rgb, uint8_t *bgra, int width, int height)
{
int i, j;
uint8_t *r, *b;
for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
r = &b_rgb[(height - 1) - i][j * 3];
b = &bgra[((i * width) + j) * 4];
r[0] = b[2];
r[1] = b[1];
r[2] = b[0];
}
}
}
static void
DoubleLines(uint8_t *dst, uint8_t *src)
{
@@ -84,69 +108,112 @@ DoubleLines(uint8_t *dst, uint8_t *src)
static void
SaveBitmap(wchar_t *szFilename, HBITMAP hBitmap)
SavePNG(wchar_t *szFilename, HBITMAP hBitmap)
{
static WCHAR szMessage[512];
BITMAPFILEHEADER bmpFileHeader;
BITMAPINFO bmpInfo;
HDC hdc;
FILE *fp = NULL;
LPVOID pBuf = NULL;
LPVOID pBuf2 = NULL;
png_bytep *b_rgb = NULL;
int i;
do {
hdc = GetDC(NULL);
/* create file */
FILE *fp = plat_fopen(szFilename, (wchar_t *) L"wb");
if (!fp) {
pclog("[SavePNG] File %ls could not be opened for writing", szFilename);
return;
}
ZeroMemory(&bmpInfo, sizeof(BITMAPINFO));
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
/* initialize stuff */
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
GetDIBits(hdc, hBitmap, 0, 0, NULL, &bmpInfo, DIB_RGB_COLORS);
if (!png_ptr) {
pclog("[SavePNG] png_create_write_struct failed");
fclose(fp);
return;
}
if (bmpInfo.bmiHeader.biSizeImage <= 0)
bmpInfo.bmiHeader.biSizeImage =
bmpInfo.bmiHeader.biWidth*abs(bmpInfo.bmiHeader.biHeight)*(bmpInfo.bmiHeader.biBitCount+7)/8;
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
pclog("[SavePNG] png_create_info_struct failed");
fclose(fp);
return;
}
if ((pBuf = malloc(bmpInfo.bmiHeader.biSizeImage)) == NULL) {
// pclog("ERROR: Unable to Allocate Bitmap Memory");
break;
png_init_io(png_ptr, fp);
hdc = GetDC(NULL);
ZeroMemory(&bmpInfo, sizeof(BITMAPINFO));
bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
GetDIBits(hdc, hBitmap, 0, 0, NULL, &bmpInfo, DIB_RGB_COLORS);
if (bmpInfo.bmiHeader.biSizeImage <= 0)
bmpInfo.bmiHeader.biSizeImage =
bmpInfo.bmiHeader.biWidth*abs(bmpInfo.bmiHeader.biHeight)*(bmpInfo.bmiHeader.biBitCount+7)/8;
if ((pBuf = malloc(bmpInfo.bmiHeader.biSizeImage)) == NULL) {
pclog("[SavePNG] Unable to Allocate Bitmap Memory");
fclose(fp);
return;
}
if (ys2 <= 250) {
bmpInfo.bmiHeader.biSizeImage <<= 1;
if ((pBuf2 = malloc(bmpInfo.bmiHeader.biSizeImage)) == NULL) {
pclog("[SavePNG] Unable to Allocate Secondary Bitmap Memory");
free(pBuf);
fclose(fp);
return;
}
if (ys2 <= 250)
pBuf2 = malloc(bmpInfo.bmiHeader.biSizeImage * 2);
bmpInfo.bmiHeader.biHeight <<= 1;
}
bmpInfo.bmiHeader.biCompression = BI_RGB;
pclog("save png w=%i h=%i\n", bmpInfo.bmiHeader.biWidth, bmpInfo.bmiHeader.biHeight);
GetDIBits(hdc, hBitmap, 0, bmpInfo.bmiHeader.biHeight, pBuf, &bmpInfo, DIB_RGB_COLORS);
bmpInfo.bmiHeader.biCompression = BI_RGB;
if ((fp = _wfopen(szFilename, L"wb")) == NULL) {
_swprintf(szMessage,
plat_get_string(IDS_2088), szFilename);
ui_msgbox(MBX_ERROR, szMessage);
break;
}
GetDIBits(hdc, hBitmap, 0, bmpInfo.bmiHeader.biHeight, pBuf, &bmpInfo, DIB_RGB_COLORS);
bmpFileHeader.bfReserved1 = 0;
bmpFileHeader.bfReserved2 = 0;
if (pBuf2) {
bmpInfo.bmiHeader.biSizeImage <<= 1;
bmpInfo.bmiHeader.biHeight <<= 1;
}
bmpFileHeader.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+bmpInfo.bmiHeader.biSizeImage;
bmpFileHeader.bfType=0x4D42;
bmpFileHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
png_set_IHDR(png_ptr, info_ptr, bmpInfo.bmiHeader.biWidth, bmpInfo.bmiHeader.biHeight,
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
(void)fwrite(&bmpFileHeader,sizeof(BITMAPFILEHEADER),1,fp);
(void)fwrite(&bmpInfo.bmiHeader,sizeof(BITMAPINFOHEADER),1,fp);
if (pBuf2) {
DoubleLines((uint8_t *) pBuf2, (uint8_t *) pBuf);
(void)fwrite(pBuf2,bmpInfo.bmiHeader.biSizeImage,1,fp);
} else {
(void)fwrite(pBuf,bmpInfo.bmiHeader.biSizeImage,1,fp);
}
} while(false);
if ((b_rgb = (png_bytep *) malloc(sizeof(png_bytep) * bmpInfo.bmiHeader.biHeight)) == NULL) {
pclog("[SavePNG] Unable to Allocate RGB Bitmap Memory");
free(pBuf2);
free(pBuf);
fclose(fp);
return;
}
for (i = 0; i < bmpInfo.bmiHeader.biHeight; i++)
b_rgb[i] = (png_byte *) malloc(png_get_rowbytes(png_ptr, info_ptr));
if (pBuf2) {
DoubleLines((uint8_t *) pBuf2, (uint8_t *) pBuf);
bgra_to_rgb(b_rgb, (uint8_t *) pBuf2, bmpInfo.bmiHeader.biWidth, bmpInfo.bmiHeader.biHeight);
} else
bgra_to_rgb(b_rgb, (uint8_t *) pBuf, bmpInfo.bmiHeader.biWidth, bmpInfo.bmiHeader.biHeight);
png_write_info(png_ptr, info_ptr);
png_write_image(png_ptr, b_rgb);
png_write_end(png_ptr, NULL);
/* cleanup heap allocation */
if (hdc) ReleaseDC(NULL,hdc);
for (i = 0; i < bmpInfo.bmiHeader.biHeight; i++)
if (b_rgb[i]) free(b_rgb[i]);
if (b_rgb) free(b_rgb);
if (pBuf2) free(pBuf2);
if (pBuf) free(pBuf);
@@ -425,7 +492,7 @@ ddraw_take_screenshot(wchar_t *fn)
CopySurface(lpdds_back2);
SaveBitmap(fn, hbitmap);
SavePNG(fn, hbitmap);
}

View File

@@ -1,508 +0,0 @@
/*
* 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.
*
* Simple PNG image file format handler.
*
* Adapted for use with 86Box. Writing of (very basic) PNG
* image files, mostly intended to be used for creating
* screenshots. We only support sRGB format (3-byte pixel
* data) with a color depth of 8 bits per sample- so, 24bpp.
*
* NOTES: This is a stripped-down version of my full library for PNG
* image file format support. All the 'reading' code has been
* removed, and some other stuff we don't need in 86Box.
*
* TODO: Compression is currently not supported, until I figure out
* how ZLIB works so I can interface with it here.
*
* Version: @(#)win_png.c 1.0.1 2017/11/11
*
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2017 Fred N. van Kempen.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "../86Box.h"
#include "../plat.h"
#include "win_png.h"
#ifdef CRC_DYNAMIC
static uint32_t crc_table[256];
static int crc_tblok = 0;
/* Pre-calculate the CRC table. */
static void
crc_create(void)
{
uint32_t crc;
int i, k;
for (i=0; i<256; i++) {
crc = (uint32_t)i;
for (k=0; k<8; k++) {
if (crc & 1)
crc = 0xedb88320UL ^ (crc >> 1);
else
crc >>= 1;
}
crc_table[i] = crc;
}
crc_tblok = 1;
}
#else
static uint32_t crc_table[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
#endif
/* Standard Network CRC32. */
static uint32_t
crc_upd(uint32_t crc, uint8_t *bufp, int buflen)
{
int i;
#ifdef CRC_DYNAMIC
if (! crc_tblok)
crc_create();
#endif
for (i=0; i<buflen; i++)
crc = crc_table[(crc ^ *bufp++) & 0xff] ^ (crc >> 8);
return(crc);
}
static uint32_t
crc_init(void)
{
return(0xffffffffUL);
}
static uint32_t
crc_finish(uint32_t crc)
{
return(crc ^ 0xffffffffUL);
}
/* Perform CRC over the compressed data. */
static uint32_t
png_dcrc(uint32_t state, const uint8_t *bufp, uint32_t buflen)
{
uint16_t s1, s2;
uint32_t i;
s1 = (uint16_t)(state & 0xffff);
s2 = (uint16_t)(state >> 16);
for (i=0; i<buflen; i++) {
s1 = ((uint32_t)s1 + bufp[i]) % 65521;
s2 = ((uint32_t)s2 + s1) % 65521;
}
return((uint32_t)s2 << 16 | s1);
}
/* Finish the PNG file by writing the IEND record. */
static int
png_wriend(png_t *png)
{
uint8_t buff[12];
int i = 0;
/* Create the IEND chunk. */
png_putlong(&buff[i], 0, &i);
memcpy(&buff[i], "IEND", 4); i += 4;
png_putlong(&buff[i], 0xae426082, &i);
if (fwrite(buff, 1, sizeof(buff), png->fp) != sizeof(buff)) {
pclog("PNG(%ls): could not write trailer chunk!\n", png->name);
return(-1);
}
/* All is good! */
return(0);
}
/* Write an IDAT chunk header to the file. */
static uint32_t
png_wridat(png_t *png, uint32_t pixels)
{
uint8_t buff[10];
uint32_t cnt;
int i, fluff;
/*
* We must know the size of the chunk we are about to write,
* and that is not straightforward. If compression is enabled,
* we won't know until we're done with the chunk, so we would
* have to just write a dummy, and then go back afterwards and
* fix up the chunk size.
*
* Without compression, we could do the same, or we can do some
* calculations on how much data we expect to generate, and use
* that. For now, we'll go with the latter option.
*/
cnt = png->lwidth; /* bytes per scanline */
cnt += 1; /* filter type per line */
cnt *= (pixels/png->width); /* number of full lines */
cnt += (pixels%png->width); /* number of pixels */
/*
* We now have the total number of pixel bytes to write.
*
* All that is left is adding the expected overhead (since we
* are dealing with fixed-size blocks and their headers) and
* that should give us the grand total..
*/
i = (cnt / DEFL_MAX_BLKSZ); /* number of full blocks */
if ((cnt % DEFL_MAX_BLKSZ) != 0) i++; /* incomplete block */
cnt += (i * 5); /* block header is 5 bytes */
/*
* We are not using compression, so we must account for
* the extra data we insert to fake that. Below you will
* see the two DEFLATE bytes (this makes it look like a
* real deflate stream.) Upon closing, we also add the
* four compressed-data CRC bytes, so, a total of 6.
*/
fluff = 6;
i = 0;
png_putlong(&buff[i], cnt+fluff, &i); /* IDAT chunk */
memcpy(&buff[i], "IDAT", 4); i += 4;
buff[i++] = 0x08; /* deflate data: zlib */
buff[i++] = 0x1d; /* zlib method number */
if (fwrite(buff, 1, i, png->fp) != i) {
pclog("PNG(%ls): unable to write IDAT header!\n", png->name);
png_close(png);
return(-1);
}
/* Initialize IDAT CRC. */
png->crc = crc_upd(png->crc, &buff[4], i-4);
/* Initialize for writing data. */
png->bufcnt = 0; /* no data in buffer */
return(cnt);
}
/*
* API: Write (more) data to the PNG file.
*
* To keep things semi-simple, we generate a single IDAT
* chunk for each write. So, if the application calls us
* with the entire image, we have one IDAT. If it calls
* us multiple times (for example, once per scanline), we
* get one IDAT per scanline.
*/
int
png_write(png_t *png, uint8_t *bitmap, uint32_t pixels)
{
uint8_t buff[10];
uint32_t cnt, n;
uint16_t s;
int i;
/* Do they want to close up? */
if (bitmap == NULL && pixels == 0) {
i = png_wriend(png);
return(i);
}
if (png->line >= png->height) {
pclog("PNG(%ls): cannot write %u pixels!\n", png->name, pixels);
png_close(png);
return(-1);
}
/* Start a new IDAT chunk. */
cnt = png_wridat(png, pixels);
/* Loop, writing all pixels to the file. */
while (cnt > 0) {
if (png->bufcnt == 0) {
/*
* Initialize a new block header.
*
* Bit0: LastBlock
* Bits[2:1]: compression type (00=stored)
* Len: block size (MSB)
* Nlen: block size, negated (MSB)
*/
s = (cnt < DEFL_MAX_BLKSZ) ? cnt : DEFL_MAX_BLKSZ;
i = 0;
buff[i++] = (cnt <= DEFL_MAX_BLKSZ) ? 1:0;
buff[i++] = ((s >> 8) & 0xff);
buff[i++] = (s & 0xff);
buff[i++] = ((s >> 8) ^ 0xff);
buff[i++] = (s ^ 0xff);
if (fwrite(buff, 1, i, png->fp) != i) {
pclog("PNG(%ls): block header write failed!\n",
png->name);
png_close(png);
return(-1);
}
png->crc = crc_upd(png->crc, buff, i);
cnt -= i;
}
if (png->col == 0) {
/* Beginning of line, write filter method. */
buff[0] = 0x00;
if (fwrite(buff, 1, 1, png->fp) != 1) {
pclog("PNG(%ls): cannot write filter?!\n", png->name);
png_close(png);
return(-1);
}
png->crc = crc_upd(png->crc, buff, 1);
png->dcrc = png_dcrc(png->dcrc, buff, 1);
png->bufcnt++;
cnt--;
}
/* See how many pixels we can write for this scanline. */
n = cnt;
if ((png->lwidth - png->col) < n)
n = (png->lwidth - png->col);
if ((DEFL_MAX_BLKSZ - png->bufcnt) < n)
n = (DEFL_MAX_BLKSZ - png->bufcnt);
/* Write the pixel data for this block. */
if (fwrite(bitmap, 1, n, png->fp) != n) {
pclog("PNG(%ls): cannot write pixeldata?!\n", png->name);
png_close(png);
return(-1);
}
/* Update the CRCs for these pixels. */
png->crc = crc_upd(png->crc, bitmap, n);
png->dcrc = png_dcrc(png->dcrc, bitmap, n);
/* Update stats. */
bitmap += n;
png->bufcnt += n;
if (png->bufcnt == DEFL_MAX_BLKSZ)
png->bufcnt = 0;
cnt -= n;
png->col += n;
if (png->col == png->lwidth) {
png->col = 0;
if (++png->line == png->height) {
if (cnt > 0) {
pclog("PNG(%ls): done, more data?!\n",
png->name);
png_close(png);
return(-1);
}
}
}
}
/* Write the CRCs. */
i = 0;
png_putlong(buff, png->dcrc, &i);
png->crc = crc_finish(crc_upd(png->crc, buff, i));
png_putlong(&buff[i], png->crc, &i);
if (fwrite(buff, 1, i, png->fp) != i) {
pclog("PNG(%ls): cannot write IDAT trailer?!\n", png->name);
png_close(png);
return(-1);
}
return(0);
}
/* Write an unsigned long 32bit value in MSB. */
void
png_putlong(uint8_t *ptr, uint32_t val, int *off)
{
*ptr++ = (val >> 24) & 0xff;
*ptr++ = (val >> 16) & 0xff;
*ptr++ = (val >> 8) & 0xff;
*ptr = val & 0xff;
if (off != NULL)
*off += sizeof(uint32_t);
}
/* API: Close the current PNG file. */
void
png_close(png_t *png)
{
if (png->fp != NULL) {
fflush(png->fp);
(void)fclose(png->fp);
free(png);
}
}
/* API: Create a new PNG file. */
png_t *
png_create(wchar_t *fn, int width, int height, int bpp)
{
uint8_t buff[33];
uint32_t crc;
png_t *png;
int i;
/* Make sure we can do this. */
if ((bpp != 24) ||
(width<=0 || width >= 65536) ||
(height<=0 || height >= 65536)) {
pclog("PNG(%ls): invalid image parameters!\n", fn);
return(NULL);
}
/* Allocate the control block. */
png = (png_t *)malloc(sizeof(png_t));
if (png == NULL) {
pclog("PNG(%ls): out of memory!\n", fn);
return(NULL);
}
memset(png, 0x00, sizeof(png_t));
png->name = fn;
png->width = width; /* width, in pixels */
png->height = height; /* height, in pixels */
png->bpp = bpp; /* total bits per pixel */
png->ctype = PNG_COLOR_TYPE; /* 02 - sRBG */
png->cdepth = (bpp/3); /* bits per color sample */
png->pwidth = (bpp/8); /* bytes per pixel */
png->lwidth = (png->pwidth*width); /* line width in bytes */
png->crc = crc_init(); /* initialize data CRC */
png->dcrc = 1; /* compressed-data CRC */
/* Create the data file. */
if ((png->fp = plat_fopen(fn, L"wb")) == NULL) {
pclog("PNG(%ls): unable to create file!\n", fn);
png_close(png);
return(NULL);
}
/* Write out the basic header chunks. */
i = 0;
buff[i++] = 0x89; /* standard PNG header */
memcpy(&buff[i], "PNG", 3); i += 3;
buff[i++] = 0x0d; buff[i++] = 0x0a;
buff[i++] = 0x1a; buff[i++] = 0x0a;
png_putlong(&buff[i], 13, &i); /* IHDR chunk */
memcpy(&buff[i], "IHDR", 4); i += 4;
png_putlong(&buff[i], png->width, &i); /* width */
png_putlong(&buff[i], png->height, &i); /* height */
buff[i++] = png->cdepth; /* color depth (per color) */
buff[i++] = png->ctype; /* color type (2=RGB) */
buff[i++] = PNG_COMPRESSION_TYPE; /* compression (0=deflate) */
buff[i++] = PNG_FILTER_TYPE; /* filter (0=adaptive) */
buff[i++] = PNG_INTERLACE_MODE; /* interlace (0=none) */
crc = crc_finish(crc_upd(crc_init(), &buff[12], 17));
png_putlong(&buff[i], crc, &i);
if (fwrite(buff, 1, i, png->fp) != i) {
pclog("PNG(%ls): unable to write PNG header!\n", fn);
png_close(png);
return(NULL);
}
i = 4;
memcpy(&buff[i], "tEXt", 4); i += 4;
memcpy(&buff[i], "Software", 8); i+= 8;
buff[i++] = 0x00;
memcpy(&buff[i], "86Box PNGlib", 12); i+= 12;
png_putlong(buff, i-8, NULL);
crc = crc_finish(crc_upd(crc_init(), &buff[4], i-4));
png_putlong(&buff[i], crc, &i);
if (fwrite(buff, 1, i, png->fp) != i) {
pclog("PNG(%ls): unable to write chunk header!\n", fn);
png_close(png);
return(NULL);
}
return(png);
}

View File

@@ -1,66 +0,0 @@
/*
* 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.
*
* Definitions for the Simple PNG image file format handler.
*
* Version: @(#)win_png.h 1.0.1 2017/11/11
*
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2017 Fred N. van Kempen.
*/
#ifndef WIN_PNG_H
# define WIN_PNG_H
/* PNG defintions, as per the specification. */
#define PNG_COLOR_TYPE 0x02 /* 3-sample sRGB */
#define PNG_COMPRESSION_TYPE 0x00 /* deflate compression */
#define PNG_FILTER_TYPE 0x00 /* no filtering */
#define PNG_INTERLACE_MODE 0x00 /* no interlacing */
/* DEFLATE definition, as per RFC1950/1 specification. */
#define DEFL_MAX_BLKSZ 65535 /* DEFLATE max block size */
typedef struct {
wchar_t *name; /* name of datafile */
FILE *fp;
uint16_t width, /* configured with in pixels */
height; /* configured with in pixels */
uint8_t bpp, /* configured bits per pixel */
ctype; /* configured color type */
uint16_t col, /* current column */
line, /* current scanline */
lwidth; /* line width in bytes */
uint8_t cdepth, /* color depth in bits */
pwidth; /* bytes per pixel */
uint32_t crc; /* idat chunk crc */
uint32_t dcrc; /* deflate data crc */
uint32_t bufcnt; /* #bytes in block */
} png_t;
#ifdef __cplusplus
extern "C" {
#endif
extern void png_putlong(uint8_t *ptr, uint32_t val, int *off);
extern void png_close(png_t *png);
extern png_t *png_create(wchar_t *fn, int width, int height, int bpp);
extern int png_write(png_t *png, uint8_t *bitmap, uint32_t pixels);
#ifdef __cplusplus
}
#endif
#endif /*WIN_PNG_H*/