Add read-only cassette emulation to IBM 5150 PC. Patch from JohnElliott.

This commit is contained in:
SarahW 2019-04-17 20:37:23 +01:00
commit 84a098bc98
18 changed files with 1891 additions and 1136 deletions

View file

@ -3484,14 +3484,6 @@ void execx86(int cycs)
// if (instime) printf("%i %i %i %i\n",cycdiff,cycles,memcycs,fetchclocks);
FETCHADD(((cycdiff-cycles)-memcycs)-fetchclocks);
if ((cycdiff-cycles)<memcycs) cycles-=(memcycs-(cycdiff-cycles));
if (romset==ROM_IBMPC)
{
if ((cs+cpu_state.pc)==0xFE4A7) /*You didn't seriously think I was going to emulate the cassette, did you?*/
{
CX=1;
BX=0x500;
}
}
memcycs=0;
insc++;

View file

@ -23,13 +23,13 @@ wx-resources.cpp : pc.xrc
-wxrc -c pc.xrc -o wx-resources.cpp
# PCem
pcem_SOURCES = 386.c 386_dynarec.c 386_dynarec_ops.c 808x.c acc2036.c acc2168.c acc3221.c acer386sx.c ali1429.c amstrad.c cbm_io.c cdrom-image.cc cdrom-null.c \
pcem_SOURCES = 386.c 386_dynarec.c 386_dynarec_ops.c 808x.c acc2036.c acc2168.c acc3221.c acer386sx.c ali1429.c amstrad.c cassette.c cbm_io.c cdrom-image.cc cdrom-null.c \
cmd640.c codegen.c codegen_ops.c codegen_timing_486.c codegen_timing_686.c codegen_timing_common.c codegen_timing_pentium.c codegen_timing_winchip.c compaq.c config.c cpu.c \
dells200.c device.c disc.c disc_fdi.c disc_img.c disc_sector.c dma.c esdi_at.c fdc.c fdc37c665.c fdd.c fdi2raw.c gameport.c \
hdd.c hdd_esdi.c hdd_file.c headland.c i430hx.c i430lx.c i430fx.c i430vx.c ide.c ide_atapi.c intel.c intel_flash.c io.c jim.c joystick_ch_flightstick_pro.c joystick_standard.c joystick_sw_pad.c \
joystick_tm_fcs.c keyboard.c keyboard_amstrad.c keyboard_at.c keyboard_olim24.c keyboard_pcjr.c keyboard_xt.c \
laserxt.c lpt.c lpt_dac.c lpt_dss.c mca.c mcr.c mem.c mem_bios.c mfm_at.c mfm_xebec.c model.c mouse.c mouse_msystems.c mouse_ps2.c mouse_serial.c neat.c nmi.c nvr.c olivetti_m24.c \
opti495.c paths.c pc.c pc87306.c pci.c pic.c piix.c pit.c ppi.c ps1.c ps2.c ps2_mca.c ps2_nvr.c nvr_tc8521.c rom.c rtc.c rtc_tc8521.c scat.c scsi.c scsi_53c400.c scsi_aha1540.c scsi_cd.c scsi_hd.c scsi_zip.c \
opti495.c paths.c pc.c pc87306.c pci.c pic.c piix.c pit.c ppi.c ps1.c ps2.c ps2_mca.c ps2_nvr.c nvr_tc8521.c pzx.c rom.c rtc.c rtc_tc8521.c scat.c scsi.c scsi_53c400.c scsi_aha1540.c scsi_cd.c scsi_hd.c scsi_zip.c \
serial.c sio.c sis496.c sl82c460.c sound.c sound_ad1848.c sound_adlib.c sound_adlibgold.c sound_audiopci.c sound_cms.c sound_emu8k.c sound_gus.c \
sound_mpu401_uart.c sound_opl.c sound_pas16.c sound_ps1.c sound_pssj.c sound_sb.c sound_sb_dsp.c sound_sn76489.c \
sound_speaker.c sound_ssi2001.c sound_wss.c sound_ym7128.c soundopenal.c tandy_eeprom.c tandy_rom.c \

View file

@ -4,13 +4,13 @@ CC = gcc
WXRC = wxrc
CFLAGS = -O3 -fomit-frame-pointer -msse2 -m32 $(shell wx-config --cxxflags) $(shell sdl2-config --cflags)
CXXFLAGS = $(CFLAGS)
OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acc2036.o acc2168.o acc3221.o acer386sx.o ali1429.o amstrad.o cbm_io.o cdrom-ioctl-linux.o cdrom-image.o cdrom-null.o \
OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acc2036.o acc2168.o acc3221.o acer386sx.o ali1429.o amstrad.o cassette.o cbm_io.o cdrom-ioctl-linux.o cdrom-image.o cdrom-null.o \
cmd640.o codegen.o codegen_ops.o codegen_timing_486.o codegen_timing_686.o codegen_timing_common.o codegen_timing_pentium.o codegen_timing_winchip.o codegen_x86.c compaq.o config.o cpu.o \
dells200.o device.o disc.o disc_fdi.o disc_img.o disc_sector.o dma.o esdi_at.o fdc.o fdc37c665.o fdd.o fdi2raw.o gameport.o \
hdd.o hdd_esdi.o hdd_file.o headland.o i430hx.o i430lx.o i430fx.o i430vx.o ide.o ide_atapi.o intel.o intel_flash.o io.o jim.o joystick_ch_flightstick_pro.o joystick_standard.o joystick_sw_pad.o \
joystick_tm_fcs.o keyboard.o keyboard_amstrad.o keyboard_at.o keyboard_olim24.o keyboard_pcjr.o keyboard_xt.o \
laserxt.o lpt.o lpt_dac.o lpt_dss.o mca.o mcr.o mem.o mem_bios.o mfm_at.o mfm_xebec.o model.o mouse.o mouse_msystems.o mouse_ps2.o mouse_serial.o neat.o nmi.o nvr.o nvr_tc8521.o olivetti_m24.o \
opti495.o paths.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o ps2.o ps2_mca.o ps2_nvr.o rom.o rtc.o rtc_tc8521.o scat.o scsi.o scsi_53c400.o scsi_aha1540.o scsi_cd.o scsi_hd.o scsi_zip.o \
opti495.o paths.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o ps2.o ps2_mca.o ps2_nvr.o pzx.o rom.o rtc.o rtc_tc8521.o scat.o scsi.o scsi_53c400.o scsi_aha1540.o scsi_cd.o scsi_hd.o scsi_zip.o \
serial.o sio.o sis496.o sl82c460.o sound.o sound_ad1848.o sound_adlib.o sound_adlibgold.o sound_audiopci.o sound_cms.o sound_emu8k.o sound_gus.o \
sound_mpu401_uart.o sound_opl.o sound_pas16.o sound_ps1.o sound_pssj.o sound_sb.o sound_sb_dsp.o sound_sn76489.o \
sound_speaker.o sound_ssi2001.o sound_wss.o sound_ym7128.o soundopenal.o t1000.o t3100e.o tandy_eeprom.o tandy_rom.o \

View file

@ -4,13 +4,13 @@ CC = gcc
WXRC = wxrc
CFLAGS = -O3 -fomit-frame-pointer -msse2 -m64 $(shell wx-config --cxxflags) $(shell sdl2-config --cflags)
CXXFLAGS = $(CFLAGS)
OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acc2036.o acc2168.o acc3221.o acer386sx.o ali1429.o amstrad.o cbm_io.o cdrom-ioctl-linux.o cdrom-image.o cdrom-null.o \
OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acc2036.o acc2168.o acc3221.o acer386sx.o ali1429.o amstrad.o cassette.o cbm_io.o cdrom-ioctl-linux.o cdrom-image.o cdrom-null.o \
cmd640.o codegen.o codegen_ops.o codegen_timing_486.o codegen_timing_686.o codegen_timing_common.o codegen_timing_pentium.o codegen_timing_winchip.o codegen_x86-64.c compaq.o config.o cpu.o \
dells200.o device.o disc.o disc_fdi.o disc_img.o disc_sector.o dma.o esdi_at.o fdc.o fdc37c665.o fdd.o fdi2raw.o gameport.o \
hdd.o hdd_esdi.o hdd_file.o headland.o i430hx.o i430lx.o i430fx.o i430vx.o ide.o ide_atapi.o intel.o intel_flash.o io.o jim.o joystick_ch_flightstick_pro.o joystick_standard.o joystick_sw_pad.o \
joystick_tm_fcs.o keyboard.o keyboard_amstrad.o keyboard_at.o keyboard_olim24.o keyboard_pcjr.o keyboard_xt.o \
laserxt.o lpt.o lpt_dac.o lpt_dss.o mca.o mcr.o mem.o mem_bios.o mfm_at.o mfm_xebec.o model.o mouse.o mouse_msystems.o mouse_ps2.o mouse_serial.o neat.o nmi.o nvr.o nvr_tc8521.o olivetti_m24.o \
opti495.o paths.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o ps2.o ps2_mca.o ps2_nvr.o rom.o rtc.o rtc_tc8521.o scat.o scsi.o scsi_53c400.o scsi_aha1540.o scsi_cd.o scsi_hd.o scsi_zip.o \
opti495.o paths.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o ps2.o ps2_mca.o ps2_nvr.o pzx.o rom.o rtc.o rtc_tc8521.o scat.o scsi.o scsi_53c400.o scsi_aha1540.o scsi_cd.o scsi_hd.o scsi_zip.o \
serial.o sio.o sis496.o sl82c460.o sound.o sound_ad1848.o sound_adlib.o sound_adlibgold.o sound_audiopci.o sound_cms.o sound_emu8k.o sound_gus.o \
sound_mpu401_uart.o sound_opl.o sound_pas16.o sound_ps1.o sound_pssj.o sound_sb.o sound_sb_dsp.o sound_sn76489.o \
sound_speaker.o sound_ssi2001.o sound_wss.o sound_ym7128.o soundopenal.o t1000.o t3100e.o tandy_eeprom.o tandy_rom.o \

View file

@ -6,12 +6,12 @@ WXVERSION = 31
WXINCLUDE = e:/MinGWget/include/wx/
CFLAGS = -O3 -march=i686 -fomit-frame-pointer -msse2 -mstackrealign -Werror -fno-strict-aliasing
CXXFLAGS = $(CFLAGS)
OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acc2036.o acc2168.o acc3221.o acer386sx.o ali1429.o amstrad.o cbm_io.o cdrom-ioctl.o cdrom-image.o \
OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acc2036.o acc2168.o acc3221.o acer386sx.o ali1429.o amstrad.o cassette.o cbm_io.o cdrom-ioctl.o cdrom-image.o \
cmd640.o codegen.o codegen_ops.o codegen_timing_486.o codegen_timing_686.o codegen_timing_common.o codegen_timing_pentium.o codegen_timing_winchip.o codegen_x86.o compaq.o config.o cpu.o \
device.o dells200.o disc.o disc_fdi.o disc_img.o disc_sector.o dma.o esdi_at.o fdc.o fdc37c665.o fdd.o fdi2raw.o gameport.o hdd.o hdd_esdi.o hdd_file.o headland.o i430hx.o i430lx.o i430fx.o \
i430vx.o ide.o ide_atapi.o intel.o intel_flash.o io.o jim.o joystick_ch_flightstick_pro.o joystick_standard.o joystick_sw_pad.o joystick_tm_fcs.o keyboard.o keyboard_amstrad.o keyboard_at.o \
keyboard_olim24.o keyboard_pcjr.o keyboard_xt.o laserxt.o lpt.o lpt_dac.o lpt_dss.o mca.o mcr.o mem.o mem_bios.o mfm_at.o mfm_xebec.o model.o mouse.o mouse_msystems.o mouse_ps2.o \
mouse_serial.o neat.o nmi.o nvr.o nvr_tc8521.o olivetti_m24.o opti495.o paths.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o ps2.o ps2_mca.o ps2_nvr.o rom.o rtc.o rtc_tc8521.o \
mouse_serial.o neat.o nmi.o nvr.o nvr_tc8521.o olivetti_m24.o opti495.o paths.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o ps2.o ps2_mca.o ps2_nvr.o pzx.o rom.o rtc.o rtc_tc8521.o \
scat.o scsi.o scsi_53c400.o scsi_aha1540.o scsi_cd.o scsi_hd.o scsi_zip.o serial.o sio.o sis496.o sl82c460.o sound.o sound_ad1848.o sound_adlib.o sound_adlibgold.o sound_audiopci.o sound_cms.o sound_dbopl.o \
sound_emu8k.o sound_gus.o sound_mpu401_uart.o sound_opl.o sound_pas16.o sound_ps1.o sound_pssj.o sound_resid.o \
sound_sb.o sound_sb_dsp.o sound_sn76489.o sound_speaker.o sound_ssi2001.o sound_wss.o \

View file

@ -6,12 +6,12 @@ WXVERSION = 31
WXINCLUDE = e:/mingwget/include/wx/
CFLAGS = -O3 -march=i686 -fomit-frame-pointer -msse2 -mstackrealign -Werror -fno-strict-aliasing -DUSE_NETWORKING
CXXFLAGS = $(CFLAGS)
OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acc2036.o acc2168.o acc3221.o acer386sx.o ali1429.o amstrad.o cbm_io.o cdrom-ioctl.o cdrom-image.o \
OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acc2036.o acc2168.o acc3221.o acer386sx.o ali1429.o amstrad.o cassette.o cbm_io.o cdrom-ioctl.o cdrom-image.o \
cmd640.o codegen.o codegen_ops.o codegen_timing_486.o codegen_timing_686.o codegen_timing_common.o codegen_timing_pentium.o codegen_timing_winchip.o codegen_x86.o compaq.o config.o cpu.o \
device.o dells200.o disc.o disc_fdi.o disc_img.o disc_sector.o dma.o esdi_at.o fdc.o fdc37c665.o fdd.o fdi2raw.o gameport.o hdd.o hdd_esdi.o hdd_file.o headland.o i430hx.o i430lx.o i430fx.o \
i430vx.o ide.o ide_atapi.o intel.o intel_flash.o io.o jim.o joystick_ch_flightstick_pro.o joystick_standard.o joystick_sw_pad.o joystick_tm_fcs.o keyboard.o keyboard_amstrad.o keyboard_at.o \
keyboard_olim24.o keyboard_pcjr.o keyboard_xt.o laserxt.o lpt.o lpt_dac.o lpt_dss.o mca.o mcr.o mem.o mem_bios.o mfm_at.o mfm_xebec.o model.o mouse.o mouse_msystems.o mouse_ps2.o \
mouse_serial.o neat.o nmi.o nvr.o nvr_tc8521.o olivetti_m24.o opti495.o paths.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o ps2.o ps2_mca.o ps2_nvr.o rom.o rtc.o rtc_tc8521.o \
mouse_serial.o neat.o nmi.o nvr.o nvr_tc8521.o olivetti_m24.o opti495.o paths.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o ps2.o ps2_mca.o ps2_nvr.o pzx.o rom.o rtc.o rtc_tc8521.o \
scat.o scsi.o scsi_53c400.o scsi_aha1540.o scsi_cd.o scsi_hd.o scsi_zip.o serial.o sio.o sis496.o sl82c460.o sound.o sound_ad1848.o sound_adlib.o sound_adlibgold.o sound_audiopci.o sound_cms.o sound_dbopl.o \
sound_emu8k.o sound_gus.o sound_mpu401_uart.o sound_opl.o sound_pas16.o sound_ps1.o sound_pssj.o sound_resid.o \
sound_sb.o sound_sb_dsp.o sound_sn76489.o sound_speaker.o sound_ssi2001.o sound_wss.o \

View file

@ -3,13 +3,13 @@ CPP = g++
CC = gcc
WXRC = wxrc
CFLAGS = -D__unix=1 -DPCEM_RENDER_WITH_TIMER -DPCEM_RENDER_TIMER_LOOP -Dfopen64=fopen -Dfseeko64=fseeko -Dftello64=ftello -Doff64_t=off_t -O3 -fomit-frame-pointer -msse2 -m64 $(shell wx-config --cxxflags) $(shell sdl2-config --cflags) -I/usr/local/opt/openal-soft/include -I/usr/local/include
OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acc2036.o acc2168.o acc3221.o acer386sx.o ali1429.o amstrad.o cbm_io.o cdrom-ioctl-osx.o cdrom-image.o cdrom-null.o \
OBJ = 386.o 386_dynarec.o 386_dynarec_ops.o 808x.o acc2036.o acc2168.o acc3221.o acer386sx.o ali1429.o amstrad.o cassette.o cbm_io.o cdrom-ioctl-osx.o cdrom-image.o cdrom-null.o \
cmd640.o codegen.o codegen_ops.o codegen_timing_486.o codegen_timing_686.o codegen_timing_common.o codegen_timing_pentium.o codegen_timing_winchip.o codegen_x86-64.c compaq.o config.o cpu.o \
device.o dells200.o disc.o disc_fdi.o disc_img.o disc_sector.o dma.o esdi_at.o fdc.o fdc37c665.o fdd.o fdi2raw.o gameport.o \
hdd.o hdd_esdi.o hdd_file.o headland.o i430hx.o i430lx.o i430fx.o i430vx.o ide.o ide_atapi.o intel.o intel_flash.o io.o jim.o joystick_ch_flightstick_pro.o joystick_standard.o joystick_sw_pad.o \
joystick_tm_fcs.o keyboard.o keyboard_amstrad.o keyboard_at.o keyboard_olim24.o keyboard_pcjr.o keyboard_xt.o \
laserxt.o lpt.o lpt_dac.o lpt_dss.o mca.o mcr.o mem.o mem_bios.o mfm_at.o mfm_xebec.o model.o mouse.o mouse_msystems.o mouse_ps2.o mouse_serial.o neat.o nmi.o nvr.o nvr_tc8521.o olivetti_m24.o \
opti495.o paths.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o ps2.o ps2_mca.o ps2_nvr.o rom.o rtc.o rtc_tc8521.o scat.o scsi.o scsi_53c400.o scsi_aha1540.o scsi_cd.o scsi_hd.o scsi_zip.o \
opti495.o paths.o pc.o pc87306.o pci.o pic.o piix.o pit.o ppi.o ps1.o ps2.o ps2_mca.o ps2_nvr.o pzx.o rom.o rtc.o rtc_tc8521.o scat.o scsi.o scsi_53c400.o scsi_aha1540.o scsi_cd.o scsi_hd.o scsi_zip.o \
serial.o sio.o sis496.o sl82c460.o sound.o sound_ad1848.o sound_adlib.o sound_adlibgold.o sound_audiopci.o sound_cms.o sound_emu8k.o sound_gus.o \
sound_mpu401_uart.o sound_opl.o sound_pas16.o sound_ps1.o sound_pssj.o sound_sb.o sound_sb_dsp.o sound_sn76489.o \
sound_speaker.o sound_ssi2001.o sound_wss.o sound_ym7128.o soundopenal.o t1000.o t3100e.o tandy_eeprom.o tandy_rom.o \

186
src/cassette.c Normal file
View file

@ -0,0 +1,186 @@
/************************************************************************
PCem: IBM 5150 Cassette support
Copyright (C) 2019 John Elliott <seasip.webmaster@gmail.com>
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*************************************************************************/
#include <stdlib.h>
#include "ibm.h"
#include "cpu.h"
#include "device.h"
#include "pzx.h"
char cassettefn[256];
typedef struct cassette_t
{
uint8_t motor; /* Motor status */
pzxfile_t pzx;
int cycles_last; /* Cycle count at last cassette poll */
} cassette_t;
extern device_t cassette_device;
static cassette_t *st_cas;
#define CAS_LOG(x) pclog x
// #define CAS_LOG(x)
/* The PCem CPU uses IBM cycles (4.77MHz). PZX uses Spectrum cycles (3.5MHz)
* so scale accordingly. */
static int32_t pzx_cycles(int32_t pc)
{
double d = pc;
return (int32_t)(((d * 3.5) / 4.772728) + 0.5);
}
void cassette_eject(void)
{
if (st_cas->pzx.input)
{
pzx_close(&st_cas->pzx);
}
cassettefn[0] = 0;
}
void cassette_load(const char *filename)
{
FILE *fp;
unsigned char magic[8];
if (!filename) return;
fp = fopen(filename, "rb");
if (!fp)
{
/* Warn user? */
CAS_LOG(("Failed to open cassette input %s\n", filename));
return;
}
memset(magic, 0, sizeof(magic));
fread(magic, 1, sizeof(magic), fp);
/* Check for PZX signature. In due course support could be added for
* other formats like TZX */
if (!memcmp(magic, "PZXT", 4))
{
const char *result;
result = pzx_open(&st_cas->pzx, fp);
if (result)
{
CAS_LOG(("Failed to open %s as PZX: %s\n",
filename, result));
fclose(fp);
return;
}
strcpy(cassettefn, filename);
}
}
uint8_t cassette_input(void)
{
int ticks;
/* While motor is off, result is loopback */
if (!st_cas->motor)
{
return ppispeakon;
}
/* If there is no tapefile open don't try to extract data */
if (st_cas->pzx.input == NULL) return 0;
/* Otherwise see how many ticks there have been since the last input */
if (st_cas->cycles_last == -1)
{
st_cas->cycles_last = cycles;
}
if (cycles <= st_cas->cycles_last)
{
ticks = (st_cas->cycles_last - cycles);
}
else
{
ticks = (st_cas->cycles_last + (cpu_get_speed() / 100) - cycles);
}
st_cas->cycles_last = cycles;
return pzx_advance(&st_cas->pzx, pzx_cycles(ticks));
}
void cassette_set_motor(uint8_t on)
{
if (on && !st_cas->motor)
{
CAS_LOG(("Start cassette motor\n"));
st_cas->cycles_last = -1;
}
if (st_cas->motor && !on)
{
CAS_LOG(("Stop cassette motor\n"));
st_cas->cycles_last = -1;
}
st_cas->motor = on;
}
static void *cassette_init(void)
{
cassette_t *cas = malloc(sizeof(cassette_t));
memset(cas, 0, sizeof(cassette_t));
pzx_init(&cas->pzx);
st_cas = cas;
return cas;
}
static void cassette_close(void *p)
{
cassette_t *cas = (cassette_t *)p;
pzx_close(&cas->pzx);
free(cas);
}
device_t cassette_device =
{
"Cassette",
0,
cassette_init,
cassette_close,
NULL,
NULL,
NULL,
NULL,
NULL
};

30
src/cassette.h Normal file
View file

@ -0,0 +1,30 @@
/************************************************************************
PCem: IBM 5150 cassette support
Copyright (C) 2019 John Elliott <seasip.webmaster@gmail.com>
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*************************************************************************/
extern device_t cassette_device;
uint8_t cassette_input(void);
void cassette_set_motor(uint8_t on);
void cassette_eject(void);
void cassette_load(const char *filename);
extern char cassettefn[256];

View file

@ -1,5 +1,6 @@
#include "ibm.h"
#include "device.h"
#include "cassette.h"
#include "fdd.h"
#include "io.h"
#include "mem.h"
@ -126,7 +127,9 @@ void keyboard_xt_write(uint16_t port, uint8_t val, void *priv)
timer_process();
timer_update_outstanding();
if (keyboard_xt.pb2_turbo)
if (romset == ROM_IBMPC)
cassette_set_motor((val & 8) ? 0 : 1);
else if (keyboard_xt.pb2_turbo)
cpu_set_turbo((val & 4) ? 0 : 1);
speaker_update();
@ -183,6 +186,8 @@ uint8_t keyboard_xt_read(uint16_t port, void *priv)
temp = ((mem_size-64) / 32) & 0xf;
else
temp = ((mem_size-64) / 32) >> 4;
temp |= (cassette_input()) ? 0x10 : 0;
}
else if (romset == ROM_ATARIPC3)
{

View file

@ -15,6 +15,7 @@
#include "compaq.h"
#include "dells200.h"
#include "device.h"
#include "cassette.h"
#include "dma.h"
#include "fdc.h"
#include "fdc37c665.h"
@ -301,6 +302,8 @@ void xt_init()
keyboard_xt_init();
nmi_init();
device_add(&gameport_device);
if (romset == ROM_IBMPC)
device_add(&cassette_device);
}
void compaq_pip_init()

View file

@ -24,6 +24,11 @@ BEGIN
MENUITEM SEPARATOR
MENUITEM "&Configure hard discs...",IDM_HDCONF
END
POPUP "C&assette"
BEGIN
MENUITEM "&Load tapefile...", IDM_CASSETTE_LOAD
MENUITEM "&Eject tape", IDM_CASSETTE_EJECT
END
POPUP "&Settings"
BEGIN
MENUITEM "&Configure...", IDM_CONFIG

View file

@ -137,6 +137,15 @@
<radio>1</radio>
</object>
</object>
<object class="wxMenu" name="IDM_CASSETTE">
<label>C_assette</label>
<object class="wxMenuItem" name="IDM_CASSETTE_LOAD">
<label>_Load tapefile...</label>
</object>
<object class="wxMenuItem" name="IDM_CASSETTE_EJECT">
<label>_Eject tape</label>
</object>
</object>
<object class="wxMenu">
<label>Video</label>
<object class="wxMenu">

419
src/pzx.c Normal file
View file

@ -0,0 +1,419 @@
/************************************************************************
PCem: IBM 5150 Cassette support
Copyright (C) 2019 John Elliott <seasip.webmaster@gmail.com>
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*************************************************************************/
#include <stdlib.h>
#include "ibm.h"
#include "pzx.h"
/* This module is intended to abstract all the details of a PZX file and
* emit its contents as a bitstream in a form suitable for PCem. Similar
* modules could be written to add support for other tape formats such as TZX,
* TAP or CSW. */
#define CAS_LOG(x) pclog x
/* #define CAS_LOG(x) */
static uint32_t peek2(uint8_t *data)
{
return (((uint32_t)data[1]) << 8) | data[0];
}
static uint32_t peek4(uint8_t *data)
{
return (((uint32_t)data[3]) << 24) |
(((uint32_t)data[2]) << 16) |
(((uint32_t)data[1]) << 8) | data[0];
}
/* Cue up the next pulse definition from the current PULS block. */
static void pzx_parse_pulse(pzxfile_t *pzx)
{
pzx->puls_duration = peek2(pzx->curblock + pzx->puls_ptr);
pzx->puls_ptr += 2;
if (pzx->puls_duration > 0x8000)
{
pzx->puls_count = pzx->puls_duration & 0x7FFF;
pzx->puls_duration = peek2(pzx->curblock + pzx->puls_ptr);
pzx->puls_ptr += 2;
}
if (pzx->puls_duration >= 0x8000)
{
pzx->puls_duration &= 0x7FFF;
pzx->puls_duration <<= 16;
pzx->puls_duration |= peek2(pzx->curblock + pzx->puls_ptr);
pzx->puls_ptr += 2;
}
if (!pzx->puls_count) pzx->puls_count = 1;
}
void pzx_init(pzxfile_t *pzx)
{
memset(pzx, 0, sizeof(pzxfile_t));
pzx->state = PZX_CLOSED;
}
/* Load the next block from a PZX-format file.
*
* Returns block if successful, NULL if end of file or error
* Caller must free the block with free(). */
uint8_t *pzx_load_block(FILE *fp)
{
uint8_t block_header[8];
uint8_t *block_data;
uint32_t block_len;
/* The first 8 bytes of a PZX block are fixed: the first 4 give
* the ID, the second 4 the length (excluding the header itself) */
if (fread(block_header, 1, 8, fp) < 8) return NULL; /* EoF */
block_len = peek4(block_header + 4);
block_data = malloc(8 + block_len);
if (!block_data) return NULL;
memcpy(block_data, block_header, 8);
if (!block_len) /* Block is only the header */
{
/* CAS_LOG(("Loaded PZX block: %-4.4s\n", block_data)); */
return block_data;
}
if (fread(block_data + 8, 1, block_len, fp) < block_len)
{
free(block_data); /* Unexpected EoF */
return NULL;
}
/* CAS_LOG(("Loaded PZX block: %-4.4s\n", block_data)); */
return block_data;
}
/* Search the current file for PZX version headers and check they're all 1.x */
static const char * pzx_check_version(FILE *fp)
{
uint8_t *block;
static char message[80];
rewind(fp);
while ( (block = pzx_load_block(fp)) )
{
if (!memcmp(block, "PZXT", 4))
{
CAS_LOG(("PZX version %d.%d\n", block[8], block[9]));
if (block[8] != 1)
{
sprintf(message, "Unsupported PZX version %d.%d\n", block[8], block[9]);
free(block);
return message;
}
}
free(block);
}
rewind(fp);
return NULL;
}
const char *pzx_open(pzxfile_t *pzx, FILE *fp)
{
const char *result;
rewind(fp);
/* Check that this file is compatible */
result = pzx_check_version(fp);
if (result) return result;
pzx->level = 0;
pzx->state = PZX_IDLE;
pzx->input = fp;
return NULL;
}
void pzx_close(pzxfile_t *pzx)
{
if (pzx->input)
{
fclose(pzx->input);
pzx->input = NULL;
}
if (pzx->curblock)
{
free(pzx->curblock);
pzx->curblock = NULL;
}
pzx->state = PZX_CLOSED;
}
/* Read the next block of type DATA, PAUS or PULS */
int pzx_next_block(pzxfile_t *pzx)
{
long pos;
pos = ftell(pzx->input);
while (pzx->state == PZX_IDLE)
{
uint8_t *blk;
/* In idle state there should be no current block. But
* make sure of that */
if (pzx->curblock)
{
free(pzx->curblock);
pzx->curblock = NULL;
}
/* Load the next block */
blk = pzx_load_block(pzx->input);
/* If that didn't load we've reached the end of file; wrap to
* beginning. */
if (!blk)
{
rewind(pzx->input);
blk = pzx_load_block(pzx->input);
if (!blk) /* Couldn't even load first block */
{
pzx_close(pzx);
return 0;
}
/* Have we read the whole file and come back to where
* we were? */
if (ftell(pzx->input) == pos)
{
free(blk);
pzx_close(pzx);
return 0;
}
}
/* We have loaded the next block. What is it? */
if (!memcmp(blk, "PULS", 4))
{
pzx->state = PZX_IN_PULS;
pzx->curblock = blk;
pzx->puls_len = 8 + peek4(blk + 4);
pzx->puls_ptr = 8;
pzx->puls_count = 0;
pzx->puls_remain = 0;
pzx->puls_duration = 0;
pzx->level = 0;
CAS_LOG(("Beginning PULS block\n"));
}
else if (!memcmp(blk, "PAUS", 4))
{
pzx->state = PZX_IN_PAUS;
pzx->curblock = blk;
pzx->paus_remain = peek4(blk + 8);
pzx->level = (pzx->paus_remain >> 31);
pzx->paus_remain &= 0x7FFFFFFF;
CAS_LOG(("Beginning PAUS block, duration=%d\n",
pzx->paus_remain));
}
else if (!memcmp(blk, "DATA", 4))
{
pzx->state = PZX_IN_DATA;
pzx->curblock = blk;
pzx->data_bits = peek4(blk + 8);
pzx->level = (pzx->data_bits >> 31);
pzx->data_bits &= 0x7FFFFFFF;
pzx->data_tail = peek2(blk + 12);
pzx->data_p0 = blk[14];
pzx->data_p1 = blk[15];
pzx->data_p = 0;
pzx->data_w = 16;
pzx->data_remain = 0;
pzx->data_ptr = 16 + 2 * (pzx->data_p0 + pzx->data_p1);
pzx->data_mask = 0x80;
CAS_LOG(("Beginning DATA block, length=%d p0=%d p1=%d"
" data_ptr=%d\n",
pzx->data_bits,
pzx->data_p0, pzx->data_p1,
pzx->data_ptr));
}
}
return 1;
}
static void pzx_endblock(pzxfile_t *pzx)
{
if (pzx->curblock) free(pzx->curblock);
pzx->curblock = NULL;
pzx->state = PZX_IDLE;
}
/* PAUS is easy - just run the timer down */
static int pzx_advance_paus(pzxfile_t *pzx, int time)
{
if (pzx->paus_remain > time)
{
pzx->paus_remain -= time;
return 0;
}
time -= pzx->paus_remain;
pzx_endblock(pzx);
return time;
}
static int pzx_advance_puls(pzxfile_t *pzx, int time)
{
/* At the start of a pulse sequence? */
if (pzx->puls_count == 0)
{
pzx_parse_pulse(pzx);
pzx->puls_remain = pzx->puls_duration;
}
/* Does sample trigger a pulse change? If not, that's easy. */
if (time < pzx->puls_remain)
{
pzx->puls_remain -= time;
return 0;
}
/* Sample does trigger a pulse change */
time -= pzx->puls_remain;
/* If there's another pulse in the current sequence, that's
* straightforward; just flip the level and continue */
--pzx->puls_count;
pzx->level = !pzx->level;
if (pzx->puls_count)
{
pzx->puls_remain = pzx->puls_duration;
return time;
}
/* If we've reached the end of the pulse sequence, there may be
* another one */
if (pzx->puls_ptr < pzx->puls_len)
{
return time;
}
/* If there isn't another one, it's the end of the block */
pzx_endblock(pzx);
return time;
}
/* Decode a DATA block */
static int pzx_advance_data(pzxfile_t *pzx, int time)
{
uint8_t bit;
/* Reached end of data? */
if (pzx->data_bits == 0)
{
/* Time interval is covered by the tail bit */
if (pzx->data_tail > time)
{
pzx->data_tail -= time;
return 0;
}
/* Have run out of block */
time -= pzx->data_tail;
pzx_endblock(pzx);
return time;
}
/* No more time remaining on the current bit? */
if (pzx->data_p < 1 && !pzx->data_remain)
{
bit = pzx->curblock[pzx->data_ptr] & pzx->data_mask;
pzx->data_mask >>= 1;
if (!pzx->data_mask)
{
pzx->data_mask = 0x80;
++pzx->data_ptr;
}
--pzx->data_bits;
if (bit)
{
pzx->data_p = pzx->data_p1;
pzx->data_w = 16 + 2 * pzx->data_p0;
pzx->data_remain = 0;
}
else
{
pzx->data_p = pzx->data_p0;
pzx->data_w = 16;
pzx->data_remain = 0;
}
}
/* See if we've started processing the current waveform. If not,
* load its first element (assuming that there is one) */
if (!pzx->data_remain)
{
if (pzx->data_p)
{
pzx->data_remain = peek2(pzx->curblock + pzx->data_w);
pzx->data_w += 2;
pzx->data_p--;
}
}
if (pzx->data_remain > time)
{
/* Time advance is contained within current wave */
pzx->data_remain -= time;
return 0;
}
else /* Move on to next element of wave / next bit / next block */
{
time -= pzx->data_remain;
pzx->data_remain = 0;
pzx->level = !pzx->level;
}
return time;
}
int pzx_advance(pzxfile_t *pzx, int time)
{
if (pzx->state == PZX_CLOSED) return 0; /* No tape loaded */
while (time)
{
switch (pzx->state)
{
case PZX_IDLE:
if (!pzx_next_block(pzx)) return 0;
break;
case PZX_IN_PULS:
time = pzx_advance_puls(pzx, time);
break;
case PZX_IN_PAUS:
time = pzx_advance_paus(pzx, time);
break;
case PZX_IN_DATA:
time = pzx_advance_data(pzx, time);
break;
case PZX_CLOSED: /*Should never get here*/
return 0;
}
}
return pzx->level;
}

71
src/pzx.h Normal file
View file

@ -0,0 +1,71 @@
/************************************************************************
PCem: IBM 5150 cassette support
Copyright (C) 2019 John Elliott <seasip.webmaster@gmail.com>
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*************************************************************************/
typedef enum
{
PZX_CLOSED, /* File is not open */
PZX_IDLE, /* File is open, no block loaded */
PZX_IN_PULS, /* File is open, current block is a PULS block */
PZX_IN_DATA, /* File is open, current block is a DATA block */
PZX_IN_PAUS, /* File is open, current block is a PAUS block */
} PZX_STATE;
typedef struct pzxfile_t
{
FILE *input; /* Input PZX file */
uint8_t *curblock; /* Currently-loaded block, if any */
int level; /* Current signal level */
PZX_STATE state; /* State machine current status */
/* State variables for PULS */
uint32_t puls_ptr; /* Pointer within PULS block */
uint32_t puls_len; /* Length of PULS block */
uint32_t puls_count; /* Count of pulses */
uint32_t puls_duration; /* Duration of each pulse */
uint32_t puls_remain; /* Time remaining in this pulse */
/* State variables for PAUS */
uint32_t paus_remain; /* Time remaining in this pause */
/* State variables for DATA */
uint32_t data_ptr; /* Pointer within DATA block */
uint32_t data_bits; /* Count of bits */
uint16_t data_tail; /* Length of pulse after last bit */
uint8_t data_mask; /* Mask for current bit */
uint8_t data_p0; /* Length of 0 encoding */
uint8_t data_p1; /* Length of 1 encoding */
int data_p; /* Current sequence being emitted */
uint32_t data_w; /* Current waveform */
uint32_t data_remain; /* Current data pulse time remaining */
} pzxfile_t;
uint8_t *pzx_load_block(FILE *fp);
/* Initialise structure */
void pzx_init(pzxfile_t *pzx);
/* Open file for input */
const char *pzx_open(pzxfile_t *pzx, FILE *fp);
/* Close file */
void pzx_close(pzxfile_t *pzx);
/* Advance by 'time' samples (3.5MHz sample rate) and return current state */
int pzx_advance(pzxfile_t *pzx, int time);

View file

@ -10,6 +10,8 @@
#define IDM_BPB_DISABLE 40015
#define IDM_CONFIG 40020
#define IDM_STATUS 40030
#define IDM_CASSETTE_LOAD 40040
#define IDM_CASSETTE_EJECT 40041
#define IDM_VID_RESIZE 40050
#define IDM_VID_REMEMBER 40051
#define IDM_VID_ASPECT 40052

File diff suppressed because it is too large Load diff

View file

@ -18,6 +18,8 @@
#include <stdlib.h>
#include <math.h>
#include "ibm.h"
#include "device.h"
#include "cassette.h"
#include "cdrom-ioctl.h"
#include "cdrom-image.h"
#include "config.h"
@ -899,6 +901,22 @@ int wx_handle_command(void* hwnd, int wParam, int checked)
{
zip_eject();
}
else if (ID_IS("IDM_CASSETTE_LOAD"))
{
if (!getfile(hwnd,
"Tape image (*.pzxi;*.pzx)|*.pzxi;*.pzx|All files (*.*)|*.*",
cassettefn))
{
cassette_eject();
cassette_load(openfilestring);
saveconfig(NULL);
}
}
else if (ID_IS("IDM_CASSETTE_EJECT"))
{
cassette_eject();
saveconfig(NULL);
}
else if (ID_IS("IDM_MACHINE_TOGGLE"))
{
if (emulation_state != EMULATION_STOPPED)