Ported over the VARCem NVR commit.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
* Intel 8042 (AT keyboard controller) emulation.
|
||||
*
|
||||
* Version: @(#)keyboard_at.c 1.0.29 2018/03/13
|
||||
* Version: @(#)keyboard_at.c 1.0.30 2018/03/13
|
||||
*
|
||||
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
|
||||
* Miran Grca, <mgrca8@gmail.com>
|
||||
@@ -645,7 +645,7 @@ kbd_poll(void *priv)
|
||||
kbdlog("ATkbd: want keyboard data\n");
|
||||
if (kbd->mem[0] & 0x01)
|
||||
picint(2);
|
||||
keyboard_at.out = keyboard_at.out_new & 0xff;
|
||||
kbd->out = kbd->out_new & 0xff;
|
||||
kbd->out_new = -1;
|
||||
kbd->status |= STAT_OFULL;
|
||||
kbd->status &= ~STAT_IFULL;
|
||||
@@ -656,16 +656,16 @@ kbd_poll(void *priv)
|
||||
|
||||
if (kbd->out_new == -1 && !(kbd->status & STAT_OFULL) &&
|
||||
key_ctrl_queue_start != key_ctrl_queue_end) {
|
||||
keyboard_at.out_new = key_ctrl_queue[key_ctrl_queue_start] | 0x200;
|
||||
kbd->out_new = key_ctrl_queue[key_ctrl_queue_start] | 0x200;
|
||||
key_ctrl_queue_start = (key_ctrl_queue_start + 1) & 0xf;
|
||||
else if (!(keyboard_at.status & STAT_OFULL) && keyboard_at.out_new == -1 &&
|
||||
keyboard_at.out_delayed != -1) {
|
||||
keyboard_at.out_new = keyboard_at.out_delayed;
|
||||
keyboard_at.out_delayed = -1;
|
||||
} else if (!(keyboard_at.status & STAT_OFULL) && keyboard_at.out_new == -1 &&
|
||||
!(keyboard_at.mem[0] & 0x10) && keyboard_at.out_delayed != -1) {
|
||||
keyboard_at.out_new = keyboard_at.out_delayed;
|
||||
keyboard_at.out_delayed = -1;
|
||||
} 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;
|
||||
@@ -684,9 +684,9 @@ kbd_adddata(uint8_t val)
|
||||
key_ctrl_queue[key_ctrl_queue_end] = val;
|
||||
key_ctrl_queue_end = (key_ctrl_queue_end + 1) & 0xf;
|
||||
|
||||
if (!(keyboard_at.out_new & 0x300)) {
|
||||
keyboard_at.out_delayed = keyboard_at.out_new;
|
||||
keyboard_at.out_new = -1;
|
||||
if (!(CurrentKbd->out_new & 0x300)) {
|
||||
CurrentKbd->out_delayed = CurrentKbd->out_new;
|
||||
CurrentKbd->out_new = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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*/
|
||||
|
||||
763
src/machine/m_at_t3100e_vid.c
Normal file
763
src/machine/m_at_t3100e_vid.c
Normal 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
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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*/
|
||||
|
||||
753
src/machine/m_xt_t1000_vid.c
Normal file
753
src/machine/m_xt_t1000_vid.c
Normal 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
|
||||
};
|
||||
@@ -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/11
|
||||
* Version: @(#)machine_table.c 1.0.25 2018/03/13
|
||||
*
|
||||
* Authors: Sarah Walker, <http://pcem-emulator.co.uk/>
|
||||
* Miran Grca, <mgrca8@gmail.com>
|
||||
@@ -44,10 +44,10 @@ 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, NULL, 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
|
||||
@@ -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, NULL, 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
|
||||
|
||||
721
src/nvr.c
721
src/nvr.c
@@ -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®B_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);
|
||||
|
||||
144
src/nvr.h
144
src/nvr.h
@@ -1,118 +1,98 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Definitions for a defacto-standard RTC/NVRAM device.
|
||||
* Definitions for the generic NVRAM/CMOS driver.
|
||||
*
|
||||
* Version: @(#)nvr.h 1.0.5 2017/11/22
|
||||
* Version: @(#)nvr.h 1.0.2 2018/03/11
|
||||
*
|
||||
* Authors: Miran Grca, <mgrca8@gmail.com>
|
||||
* Mahod,
|
||||
* Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
*
|
||||
* Copyright 2016-2017 Miran Grca.
|
||||
* Copyright 2016-2017 Mahod.
|
||||
* Copyright 2017 Fred N. van Kempen.
|
||||
* 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))
|
||||
|
||||
/* 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 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 (defacto-standard) RTC/NVRAM chip. */
|
||||
/* Define a generic RTC/NVRAM device. */
|
||||
typedef struct _nvr_ {
|
||||
uint8_t regs[RTC_REGS+114]; /* these are the registers */
|
||||
uint8_t regs[NVR_MAXSIZE]; /* these are the registers */
|
||||
wchar_t *fn;
|
||||
/* pathname of image file */
|
||||
uint16_t size;
|
||||
/* device configuration */
|
||||
int64_t mask,
|
||||
irq,
|
||||
int8_t irq;
|
||||
|
||||
|
||||
wchar_t *fname;
|
||||
|
||||
int64_t upd_stat,
|
||||
upd_ecount,
|
||||
int8_t upd_stat, /* FIXME: move to private struct */
|
||||
addr;
|
||||
int64_t upd_ecount, /* FIXME: move to private struct */
|
||||
rtctime,
|
||||
oldmachine;
|
||||
onesec_time,
|
||||
onesec_cnt,
|
||||
/* 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_ *);
|
||||
rtctime;
|
||||
|
||||
/* Hooks to device functions. */
|
||||
void (*reset)(struct _nvr_ *);
|
||||
void (*start)(struct _nvr_ *);
|
||||
void (*tick)(struct _nvr_ *);
|
||||
} nvr_t;
|
||||
extern int enable_sync;
|
||||
|
||||
|
||||
extern int nvr_dosave;
|
||||
|
||||
|
||||
extern void nvr_init(nvr_t *);
|
||||
extern void nvr_recalc(void);
|
||||
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 void nvr_at_init(int64_t irq);
|
||||
extern FILE *nvr_fopen(wchar_t *str, wchar_t *mode);
|
||||
|
||||
extern void nvr_at_init(int irq);
|
||||
extern void nvr_at_close(void);
|
||||
|
||||
625
src/nvr_at.c
625
src/nvr_at.c
@@ -1,65 +1,547 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* IBM PC/AT RTC/NVRAM ("CMOS") emulation.
|
||||
* Implement a more-or-less defacto-standard RTC/NVRAM.
|
||||
*
|
||||
* 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...
|
||||
* 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.
|
||||
*
|
||||
* Version: @(#)nvr_at.c 1.0.9 2018/02/27
|
||||
* 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...
|
||||
*
|
||||
* Authors: Miran Grca, <mgrca8@gmail.com>
|
||||
* Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
* 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 2016-2018 Miran Grca.
|
||||
* 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 "io.h"
|
||||
#include "device.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)) {
|
||||
nvr->addr = (val & nvr->mask);
|
||||
if (!(machines[machine].flags & MACHINE_MCA) && (romset != ROM_IBMPS1_2133))
|
||||
|
||||
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®B_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))
|
||||
|
||||
return;
|
||||
nmi_mask = (~val & 0x80);
|
||||
|
||||
/* Write the chip's registers. */
|
||||
(*nvr->set)(nvr, nvr->addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Read from one of the NVR registers. */
|
||||
static uint8_t
|
||||
nvr_read(uint16_t addr, void *priv)
|
||||
@@ -68,9 +550,25 @@ nvr_read(uint16_t addr, void *priv)
|
||||
uint8_t ret;
|
||||
|
||||
cycles -= ISA_CYCLES(8);
|
||||
if (addr & 1) {
|
||||
/* Read from the chip's registers. */
|
||||
ret = (*nvr->get)(nvr, nvr->addr);
|
||||
|
||||
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;
|
||||
@@ -79,23 +577,44 @@ nvr_read(uint16_t addr, void *priv)
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void
|
||||
nvr_at_close(void)
|
||||
|
||||
/* Reset the RTC state to 1980/01/01 00:00. */
|
||||
static void
|
||||
nvr_at_reset(nvr_t *nvr)
|
||||
if (nvrp == NULL)
|
||||
return;
|
||||
{
|
||||
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);
|
||||
}
|
||||
if (nvrp->fname != NULL)
|
||||
free(nvrp->fname);
|
||||
|
||||
free(nvrp);
|
||||
|
||||
/* Process after loading from file. */
|
||||
static void
|
||||
nvr_at_start(nvr_t *nvr)
|
||||
{
|
||||
struct tm tm;
|
||||
nvrp = NULL;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
||||
nvr_at_init(int64_t irq)
|
||||
void
|
||||
nvr_at_init(int irq)
|
||||
{
|
||||
nvr_t *nvr;
|
||||
@@ -105,20 +624,38 @@ nvr_at_init(int64_t irq)
|
||||
if (nvr == NULL) return;
|
||||
memset(nvr, 0x00, sizeof(nvr_t));
|
||||
|
||||
nvr->mask = machines[machine].nvrmask;
|
||||
/* 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 actual NVR. */
|
||||
|
||||
/* Initialize the generic 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);
|
||||
|
||||
/* Start the timers. */
|
||||
timer_add(update_timer, &nvr->upd_ecount, &nvr->upd_ecount, nvr);
|
||||
timer_add(rtc_timer, &nvr->rtctime, TIMER_ALWAYS_ENABLED, nvr);
|
||||
/* Load the NVR into memory! */
|
||||
(void)nvr_load();
|
||||
|
||||
/* 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;
|
||||
|
||||
212
src/nvr_ps2.c
212
src/nvr_ps2.c
@@ -1,120 +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;
|
||||
ps2_nvr_t *nvr = (ps2_nvr_t *)priv;
|
||||
uint8_t ret = 0xff;
|
||||
|
||||
switch (port)
|
||||
{
|
||||
case 0x74:
|
||||
return nvr->addr & 0xff;
|
||||
case 0x75:
|
||||
return nvr->addr >> 8;
|
||||
case 0x76:
|
||||
return nvr->ram[nvr->addr];
|
||||
}
|
||||
switch (port) {
|
||||
case 0x74:
|
||||
ret = nvr->addr & 0xff;
|
||||
break;
|
||||
|
||||
return 0xff;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
static void
|
||||
ps2_nvr_write(uint16_t port, uint8_t val, void *priv)
|
||||
{
|
||||
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));
|
||||
nvr = (ps2_nvr_t *)malloc(sizeof(ps2_nvr_t));
|
||||
memset(nvr, 0x00, sizeof(ps2_nvr_t));
|
||||
|
||||
io_sethandler(0x0074, 0x0003, ps2_nvr_read, NULL, NULL, ps2_nvr_write, NULL, NULL, nvr);
|
||||
io_sethandler(0x0074, 3,
|
||||
ps2_nvr_read,NULL,NULL, ps2_nvr_write,NULL,NULL, nvr);
|
||||
|
||||
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"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);
|
||||
switch (romset) {
|
||||
case ROM_IBMPS2_M80:
|
||||
f = nvr_fopen(L"ibmps2_m80_sec.nvr", L"rb");
|
||||
break;
|
||||
}
|
||||
|
||||
return nvr;
|
||||
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)
|
||||
|
||||
static void
|
||||
ps2_nvr_close(void *priv)
|
||||
{
|
||||
ps2_nvr_t *nvr = (ps2_nvr_t *)p;
|
||||
FILE *f = NULL;
|
||||
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"wb"); break;
|
||||
case ROM_IBMPS2_M70_TYPE4: f = nvr_fopen(L"ibmps2_m70_type4_sec.nvr", L"wb"); break;
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
free(nvr);
|
||||
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
|
||||
};
|
||||
|
||||
@@ -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*/
|
||||
|
||||
115
src/nvr_tc8521.c
115
src/nvr_tc8521.c
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
7
src/pc.c
7
src/pc.c
@@ -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.63 2018/03/13
|
||||
*
|
||||
* 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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
221
src/rtc_tc8521.c
221
src/rtc_tc8521.c
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -8,7 +8,7 @@
|
||||
#
|
||||
# Makefile for Win32 (MinGW32) environment.
|
||||
#
|
||||
# Version: @(#)Makefile.mingw 1.0.110 2018/03/07
|
||||
# Version: @(#)Makefile.mingw 1.0.111 2018/03/13
|
||||
#
|
||||
# Authors: Miran Grca, <mgrca8@gmail.com>
|
||||
# Fred N. van Kempen, <decwiz@yahoo.com>
|
||||
@@ -415,8 +415,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 +428,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 +437,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 +526,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 \
|
||||
|
||||
Reference in New Issue
Block a user