Merge branch 'master' of https://github.com/86Box/86Box into feature/savquest

This commit is contained in:
RichardG867
2021-07-29 20:00:30 -03:00
34 changed files with 2047 additions and 144 deletions

View File

@@ -13,7 +13,7 @@
# Copyright 2020,2021 David Hrdlička.
#
add_library(dev OBJECT bugger.c hasp.c hwm.c hwm_lm75.c hwm_lm78.c hwm_gl518sm.c
add_library(dev OBJECT bugger.c cassette.c cartridge.c hasp.c hwm.c hwm_lm75.c hwm_lm78.c hwm_gl518sm.c
hwm_vt82c686.c ibm_5161.c isamem.c isartc.c ../lpt.c pci_bridge.c
postcard.c serial.c vpc2007.c clock_ics9xxx.c isapnp.c i2c.c i2c_gpio.c
smbus_piix4.c keyboard.c keyboard_xt.c keyboard_at.c mouse.c mouse_bus.c

208
src/device/cartridge.c Normal file
View File

@@ -0,0 +1,208 @@
/*
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
* This file is part of the 86Box distribution.
*
* Implementation of the PCjr cartridge emulation.
*
*
*
* Authors: Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2021 Miran Grca.
*/
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/timer.h>
#include <86box/plat.h>
#include <86box/ui.h>
#include <86box/mem.h>
#include <86box/machine.h>
#include <86box/cartridge.h>
typedef struct
{
uint8_t * buf;
uint32_t base;
} cart_t;
char cart_fns[2][512];
static cart_t carts[2];
static mem_mapping_t cart_mappings[2];
#ifdef ENABLE_CARTRIDGE_LOG
int cartridge_do_log = ENABLE_CARTRIDGE_LOG;
static void
cartridge_log(const char *fmt, ...)
{
va_list ap;
if (cartridge_do_log)
{
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define cartridge_log(fmt, ...)
#endif
static uint8_t
cart_read(uint32_t addr, void *priv)
{
cart_t *dev = (cart_t *) priv;
return dev->buf[addr - dev->base];
}
static void
cart_load_error(int drive, char *fn)
{
cartridge_log("Cartridge: could not load '%s'\n",fn);
memset(cart_fns[drive], 0, sizeof(cart_fns[drive]));
ui_sb_update_icon_state(SB_CARTRIDGE | drive, 1);
}
static void
cart_image_close(int drive)
{
if (carts[drive].buf != NULL) {
free(carts[drive].buf);
carts[drive].buf = NULL;
}
carts[drive].base = 0x00000000;
mem_mapping_disable(&cart_mappings[drive]);
}
static void
cart_image_load(int drive, char *fn)
{
FILE *f;
uint32_t size;
uint32_t base = 0x00000000;
cart_image_close(drive);
f = fopen(fn, "rb");
if (fseek(f, 0, SEEK_END) == -1)
fatal("cart_image_load(): Error seeking to the end of the file\n");
size = ftell(f);
if (size < 0x1200) {
cartridge_log("cart_image_load(): File size %i is too small\n", size);
cart_load_error(drive, fn);
return;
}
if (size & 0x00000fff) {
size -= 0x00000200;
fseek(f, 0x000001ce, SEEK_SET);
fread(&base, 1, 2, f);
base <<= 4;
fseek(f, 0x00000200, SEEK_SET);
carts[drive].buf = (uint8_t *) malloc(size);
memset(carts[drive].buf, 0x00, size);
fread(carts[drive].buf, 1, size, f);
fclose(f);
} else {
base = drive ? 0xe0000 : 0xd0000;
fseek(f, 0x00000000, SEEK_SET);
carts[drive].buf = (uint8_t *) malloc(size);
memset(carts[drive].buf, 0x00, size);
fread(carts[drive].buf, 1, size, f);
fclose(f);
}
cartridge_log("cart_image_load(): %s at %08X-%08X\n", fn, base, base + size - 1);
carts[drive].base = base;
mem_mapping_set_addr(&cart_mappings[drive], base, size);
mem_mapping_set_exec(&cart_mappings[drive], carts[drive].buf);
mem_mapping_set_p(&cart_mappings[drive], &(carts[drive]));
}
static void
cart_load_common(int drive, char *fn, uint8_t hard_reset)
{
FILE *f;
cartridge_log("Cartridge: loading drive %d with '%s'\n", drive, fn);
if (!fn)
return;
f = plat_fopen(fn, "rb");
if (f) {
fclose(f);
strcpy(cart_fns[drive], fn);
cart_image_load(drive, cart_fns[drive]);
/* On the real PCjr, inserting a cartridge causes a reset
in order to boot from the cartridge. */
if (!hard_reset)
resetx86();
} else
cart_load_error(drive, fn);
}
void
cart_load(int drive, char *fn)
{
cart_load_common(drive, fn, 0);
}
void
cart_close(int drive)
{
cartridge_log("Cartridge: closing drive %d\n", drive);
cart_image_close(drive);
cart_fns[drive][0] = 0;
ui_sb_update_icon_state(SB_CARTRIDGE | drive, 1);
}
void
cart_reset(void)
{
int i;
cart_image_close(1);
cart_image_close(0);
if (!(machines[machine].flags & MACHINE_CARTRIDGE))
return;
for (i = 0; i < 2; i++) {
mem_mapping_add(&cart_mappings[i], 0x000d0000, 0x00002000,
cart_read,NULL,NULL,
NULL,NULL,NULL,
NULL, MEM_MAPPING_EXTERNAL, NULL);
mem_mapping_disable(&cart_mappings[i]);
}
cart_load_common(0, cart_fns[0], 1);
cart_load_common(1, cart_fns[1], 1);
}

723
src/device/cassette.c Normal file
View File

@@ -0,0 +1,723 @@
/*****************************************************************************
* pce *
*****************************************************************************/
/*****************************************************************************
* File name: src/arch/ibmpc/cassette.c *
* Created: 2008-11-25 by Hampa Hug <hampa@hampa.ch> *
* Copyright: (C) 2008-2019 Hampa Hug <hampa@hampa.ch> *
*****************************************************************************/
/*****************************************************************************
* This program is free software. You can redistribute it and / or modify it *
* under the terms of the GNU General Public License version 2 as published *
* by the Free Software Foundation. *
* *
* 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. *
*****************************************************************************/
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/device.h>
#include "cpu.h"
#include <86box/machine.h>
#include <86box/plat.h>
#include <86box/ui.h>
#include <86box/timer.h>
#include <86box/pit.h>
#include <86box/cassette.h>
// #include <lib/console.h>
#define CAS_CLK 1193182
pc_cassette_t * cassette;
char cassette_fname[512];
char cassette_mode[512];
unsigned long cassette_pos, cassette_srate;
int cassette_enable;
int cassette_append, cassette_pcm;
int cassette_ui_writeprot;
static int cassette_cycles = -1;
static void pc_cas_reset (pc_cassette_t *cas);
#ifdef ENABLE_CASSETTE_LOG
int cassette_do_log = ENABLE_CASSETTE_LOG;
static void
cassette_log(const char *fmt, ...)
{
va_list ap;
if (cassette_do_log)
{
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
#define cassette_log(fmt, ...)
#endif
void pc_cas_init (pc_cassette_t *cas)
{
cas->save = 0;
cas->pcm = 0;
cas->motor = 0;
ui_sb_update_icon(SB_CASSETTE, 0);
cas->position = 0;
cas->position_save = 0;
cas->position_load = 0;
cas->data_out = 0;
cas->data_inp = 0;
cas->pcm_out_vol = 64;
cas->pcm_out_val = 0;
cas->cas_out_cnt = 0;
cas->cas_out_buf = 0;
cas->cas_inp_cnt = 0;
cas->cas_inp_buf = 0;
cas->cas_inp_bit = 0;
cas->clk = 0;
cas->clk_pcm = 0;
cas->clk_out = 0;
cas->clk_inp = 0;
cas->srate = 44100;
cas->close = 0;
cas->fname = NULL;
cas->fp = NULL;
pc_cas_reset (cas);
}
void pc_cas_free (pc_cassette_t *cas)
{
free (cas->fname);
if (cas->close) {
fclose (cas->fp);
}
}
pc_cassette_t *pc_cas_new (void)
{
pc_cassette_t *cas;
cas = malloc (sizeof (pc_cassette_t));
if (cas == NULL) {
return (NULL);
}
pc_cas_init (cas);
return (cas);
}
void pc_cas_del (pc_cassette_t *cas)
{
if (cas != NULL) {
pc_cas_free (cas);
free (cas);
}
}
int pc_cas_set_fname (pc_cassette_t *cas, const char *fname)
{
unsigned n;
const char * ext;
if (cas->close)
fclose (cas->fp);
cas->close = 0;
cas->fp = NULL;
free (cas->fname);
cas->fname = NULL;
cas->position = 0;
cas->position_save = 0;
cas->position_load = 0;
if (fname == NULL) {
ui_sb_update_icon_state(SB_CASSETTE, 1);
return (0);
}
cas->fp = plat_fopen (fname, "r+b");
if (cas->fp == NULL)
cas->fp = plat_fopen (fname, "w+b");
if (cas->fp == NULL) {
ui_sb_update_icon_state(SB_CASSETTE, 1);
return (1);
}
cas->close = 1;
pc_cas_append (cas);
cas->position_save = cas->position;
if (cas->save == 0)
pc_cas_set_position (cas, 0);
n = strlen (fname);
cas->fname = malloc ((n + 1) * sizeof(char));
if (cas->fname != NULL)
memcpy (cas->fname, fname, (n + 1) * sizeof(char));
if (n > 4) {
ext = fname + (n - 4);
/* Has to be 44.1 kHz, mono, 8-bit. */
if (stricmp (ext, ".pcm") == 0)
pc_cas_set_pcm (cas, 1);
else if (stricmp (ext, ".raw") == 0)
pc_cas_set_pcm (cas, 1);
else if (stricmp (ext, ".wav") == 0)
pc_cas_set_pcm (cas, 1);
else if (stricmp (ext, ".cas") == 0)
pc_cas_set_pcm (cas, 0);
}
return (0);
}
static
void pc_cas_reset (pc_cassette_t *cas)
{
unsigned i;
cas->clk_pcm = 0;
cas->clk_out = cas->clk;
cas->clk_inp = 0;
cas->pcm_out_val = 0;
cas->cas_out_cnt = 0;
cas->cas_out_buf = 0;
cas->cas_inp_cnt = 0;
cas->cas_inp_buf = 0;
cas->cas_inp_bit = 0;
for (i = 0; i < 3; i++) {
cas->pcm_inp_fir[i] = 0;
}
}
int pc_cas_get_mode (const pc_cassette_t *cas)
{
return (cas->save);
}
void pc_cas_set_mode (pc_cassette_t *cas, int save)
{
save = (save != 0);
if (cas->save == save) {
return;
}
if (cas->save) {
cas->position_save = cas->position;
cas->position = cas->position_load;
}
else {
cas->position_load = cas->position;
cas->position = cas->position_save;
}
cas->save = save;
memset(cassette_mode, 0x00, sizeof(cassette_mode));
if (save)
memcpy(cassette_mode, "save", strlen("save") + 1);
else
memcpy(cassette_mode, "load", strlen("load") + 1);
if (cas->fp != NULL) {
fflush (cas->fp);
pc_cas_set_position (cas, cas->position);
}
pc_cas_reset (cas);
}
int pc_cas_get_pcm (const pc_cassette_t *cas)
{
return (cas->pcm);
}
void pc_cas_set_pcm (pc_cassette_t *cas, int pcm)
{
cas->pcm = (pcm != 0);
cassette_pcm = (pcm != 0);
pc_cas_reset (cas);
}
unsigned long pc_cas_get_srate (const pc_cassette_t *cas)
{
return (cas->srate);
}
void pc_cas_set_srate (pc_cassette_t *cas, unsigned long srate)
{
cas->srate = srate;
pc_cas_reset (cas);
}
void pc_cas_rewind (pc_cassette_t *cas)
{
if (cas->fp != NULL) {
rewind (cas->fp);
cas->position = 0;
}
pc_cas_reset (cas);
}
void pc_cas_append (pc_cassette_t *cas)
{
if (cas->fp != NULL) {
fseek (cas->fp, 0, SEEK_END);
cas->position = ftell (cas->fp);
}
pc_cas_reset (cas);
}
unsigned long pc_cas_get_position (const pc_cassette_t *cas)
{
return (cas->position);
}
int pc_cas_set_position (pc_cassette_t *cas, unsigned long pos)
{
if (cas->fp == NULL) {
return (1);
}
if (fseek (cas->fp, pos, SEEK_SET) != 0) {
return (1);
}
cas->position = pos;
pc_cas_reset (cas);
return (0);
}
static
void pc_cas_read_bit (pc_cassette_t *cas)
{
int val;
if (cas->cas_inp_cnt == 0) {
if (cas->fp == NULL) {
return;
}
if (feof (cas->fp)) {
return;
}
val = fgetc (cas->fp);
if (val == EOF) {
cassette_log ("cassette EOF at %lu\n", cas->position);
return;
}
cas->position += 1;
cas->cas_inp_cnt = 8;
cas->cas_inp_buf = val;
}
cas->cas_inp_bit = ((cas->cas_inp_buf & 0x80) != 0);
cas->cas_inp_buf = (cas->cas_inp_buf << 1) & 0xff;
cas->cas_inp_cnt -= 1;
}
static
int pc_cas_read_smp (pc_cassette_t *cas)
{
int smp, *fir;
if (feof (cas->fp)) {
return (0);
}
smp = fgetc (cas->fp);
if (smp == EOF) {
cassette_log ("cassette EOF at %lu\n", cas->position);
return (0);
}
cas->position += 1;
fir = cas->pcm_inp_fir;
fir[0] = fir[1];
fir[1] = fir[2];
fir[2] = (smp & 0x80) ? (smp - 256) : smp;
smp = (fir[0] + 2 * fir[1] + fir[2]) / 4;
return (smp);
}
static
void pc_cas_write_bit (pc_cassette_t *cas, unsigned char val)
{
if (val && !cassette_ui_writeprot) {
cas->cas_out_buf |= (0x80 >> cas->cas_out_cnt);
}
cas->cas_out_cnt += 1;
if (cas->cas_out_cnt >= 8) {
if (cas->fp != NULL) {
if (!cassette_ui_writeprot)
fputc (cas->cas_out_buf, cas->fp);
cas->position += 1;
}
cas->cas_out_buf = 0;
cas->cas_out_cnt = 0;
}
}
static
void pc_cas_write_smp (pc_cassette_t *cas, int val)
{
unsigned char smp;
if (val < 0) {
smp = (val < -127) ? 0x80 : (val + 256);
}
else {
smp = (val > 127) ? 0x7f : val;
}
if (!cassette_ui_writeprot)
fputc (smp, cas->fp);
cas->position += 1;
}
void pc_cas_set_motor (pc_cassette_t *cas, unsigned char val)
{
unsigned i;
val = (val != 0);
if (val == cas->motor) {
return;
}
if ((val == 0) && cas->save && cas->pcm) {
for (i = 0; i < (cas->srate / 16); i++) {
pc_cas_write_smp (cas, 0);
}
}
cassette_log ("cassette %S at %lu motor %s\n", (cas->fname != NULL) ? cas->fname : "<none>", cas->position, val ? "on" : "off");
cas->motor = val;
if (cas->fp != NULL) {
fflush (cas->fp);
pc_cas_set_position (cas, cas->position);
}
pc_cas_reset (cas);
if (cas->motor)
timer_set_delay_u64(&cas->timer, 8ULL * PITCONST);
else
timer_disable(&cas->timer);
ui_sb_update_icon(SB_CASSETTE, !!val);
}
unsigned char pc_cas_get_inp (const pc_cassette_t *cas)
{
return (cas->data_inp);
}
void pc_cas_set_out (pc_cassette_t *cas, unsigned char val)
{
unsigned long clk;
val = (val != 0);
if (cas->motor == 0) {
cas->data_inp = val;
return;
}
if (cas->data_out == val) {
return;
}
cas->data_out = val;
if (cas->pcm) {
cas->pcm_out_val = val ? -cas->pcm_out_vol : cas->pcm_out_vol;
return;
}
if (cas->save == 0) {
return;
}
if (val == 0) {
return;
}
clk = cas->clk - cas->clk_out;
cas->clk_out = cas->clk;
if (clk < (CAS_CLK / 4000)) {
;
}
else if (clk < ((3 * CAS_CLK) / 4000)) {
pc_cas_write_bit (cas, 0);
}
else if (clk < ((5 * CAS_CLK) / 4000)) {
pc_cas_write_bit (cas, 1);
}
}
void pc_cas_print_state (const pc_cassette_t *cas)
{
cassette_log ("%s %s %lu %s %lu\n", (cas->fname != NULL) ? cas->fname : "<none>", cas->pcm ? "pcm" : "cas", cas->srate, cas->save ? "save" : "load", cas->position);
}
static
void pc_cas_clock_pcm (pc_cassette_t *cas, unsigned long cnt)
{
unsigned long i, n;
int v = 0;
n = cas->srate * cnt + cas->clk_pcm;
cas->clk_pcm = n % CAS_CLK;
n = n / CAS_CLK;
if (n == 0) {
return;
}
if (cas->save) {
for (i = 0; i < n; i++) {
pc_cas_write_smp (cas, cas->pcm_out_val);
}
}
else {
for (i = 0; i < n; i++) {
v = pc_cas_read_smp (cas);
}
cas->data_inp = (v < 0) ? 0 : 1;
}
}
void pc_cas_clock (pc_cassette_t *cas, unsigned long cnt)
{
cas->clk += cnt;
if (cas->motor == 0) {
return;
}
if (cas->pcm) {
pc_cas_clock_pcm (cas, cnt);
return;
}
if (cas->save) {
return;
}
if (cas->clk_inp > cnt) {
cas->clk_inp -= cnt;
return;
}
cnt -= cas->clk_inp;
cas->data_inp = !cas->data_inp;
if (cas->data_inp) {
pc_cas_read_bit (cas);
}
if (cas->cas_inp_bit) {
cas->clk_inp = CAS_CLK / 2000;
}
else {
cas->clk_inp = CAS_CLK / 4000;
}
if (cas->clk_inp > cnt) {
cas->clk_inp -= cnt;
}
}
void pc_cas_advance (pc_cassette_t *cas)
{
int ticks;
cpu_s = (CPU *) &cpu_f->cpus[cpu_effective];
if (cas->motor == 0)
return;
if (cassette_cycles == -1)
cassette_cycles = cycles;
if (cycles <= cassette_cycles)
ticks = (cassette_cycles - cycles);
else
ticks = (cassette_cycles + (cpu_s->rspeed / 100) - cycles);
cassette_cycles = cycles;
pc_cas_clock(cas, ticks);
}
static void
cassette_close(void *p)
{
if (cassette != NULL) {
free(cassette);
cassette = NULL;
}
}
static void
cassette_callback(void *p)
{
pc_cassette_t *cas = (pc_cassette_t *) p;
pc_cas_clock (cas, 8);
if (cas->motor)
ui_sb_update_icon(SB_CASSETTE, 1);
timer_advance_u64(&cas->timer, 8ULL * PITCONST);
}
static void *
cassette_init(const device_t *info)
{
cassette = NULL;
if (cassette_pcm == 1)
cassette_pcm = -1;
cassette_log("CASSETTE: file=%s mode=%s pcm=%d srate=%lu pos=%lu append=%d\n",
(cassette_fname != NULL) ? cassette_fname : "<none>", cassette_mode, cassette_pcm, cassette_srate, cassette_pos, cassette_append);
cassette = pc_cas_new();
if (cassette == NULL) {
cassette_log("ERROR: *** alloc failed\n");
return NULL;
}
if (strlen(cassette_fname) == 0) {
if (pc_cas_set_fname (cassette, NULL)) {
cassette_log("ERROR: *** opening file failed (%s)\n", cassette_fname);
}
} else {
if (pc_cas_set_fname (cassette, cassette_fname)) {
cassette_log("ERROR: *** opening file failed (%s)\n", cassette_fname);
}
}
if (strcmp (cassette_mode, "load") == 0)
pc_cas_set_mode (cassette, 0);
else if (strcmp (cassette_mode, "save") == 0)
pc_cas_set_mode (cassette, 1);
else {
cassette_log ("ERROR: *** unknown cassette mode (%s)\n", cassette_mode);
}
if (cassette_append)
pc_cas_append (cassette);
else
pc_cas_set_position (cassette, cassette_pos);
if (cassette_pcm >= 0)
pc_cas_set_pcm (cassette, cassette_pcm);
pc_cas_set_srate (cassette, cassette_srate);
timer_add(&cassette->timer, cassette_callback, cassette, 0);
return cassette;
}
const device_t cassette_device = {
"IBM PC/PCjr Cassette Device",
0,
0,
cassette_init, cassette_close, NULL,
{ NULL }, NULL, NULL,
NULL
};

View File

@@ -33,6 +33,7 @@
#include <86box/fdd.h>
#include <86box/machine.h>
#include <86box/m_xt_t1000.h>
#include <86box/cassette.h>
#include <86box/io.h>
#include <86box/pic.h>
#include <86box/pit.h>
@@ -515,13 +516,14 @@ kbd_write(uint16_t port, uint8_t val, void *priv)
timer_process();
if ((kbd->type <= 1) && (cassette != NULL))
pc_cas_set_motor(cassette, (kbd->pb & 0x08) == 0);
speaker_update();
if ((kbd->type <= 1) && !(kbd->pb & 0x08))
speaker_gated = speaker_enable = 1;
else {
speaker_gated = val & 1;
speaker_enable = val & 2;
}
speaker_gated = val & 1;
speaker_enable = val & 2;
if (speaker_enable)
was_speaker_enable = 1;
pit_ctr_set_gate(&pit->counters[2], val & 1);
@@ -622,8 +624,12 @@ kbd_read(uint16_t port, void *priv)
/* This is needed to avoid error 131 (cassette error).
This is serial read: bit 5 = clock, bit 4 = data, cassette header is 256 x 0xff. */
if (kbd->type <= 1)
ret |= (ppispeakon ? 0x10 : 0);
if (kbd->type <= 1) {
if (cassette == NULL)
ret |= (ppispeakon ? 0x10 : 0);
else
ret |= (pc_cas_get_inp(cassette) ? 0x10 : 0);
}
if (kbd->type == 5)
ret |= (tandy1k_eeprom_read() ? 0x10 : 0);