kopia lustrzana https://github.com/Jean-MarcHarvengt/MCUME
623 wiersze
14 KiB
C++
623 wiersze
14 KiB
C++
/*
|
|
* Castaway
|
|
* (C) 1994 - 2002 Martin Doering, Joachim Hoenig
|
|
*
|
|
* IO.c - ST hardware emulation
|
|
*
|
|
* This file is distributed under the GPL, version 2 or at your
|
|
* option any later version. See doc/license.txt for details.
|
|
*
|
|
* revision history
|
|
* 23.05.2002 JH FAST1.0.1 code import: KR -> ANSI, restructuring
|
|
* 09.06.2002 JH Renamed io.c to st.c again (io.h conflicts with system headers)
|
|
* 12.06.2002 JH Correct bus error/address error exception stack frame
|
|
* 14.06.2002 JH Implemented STOP, shutdown CPU after multiple bus errors.
|
|
* Removed inst parameter from CPU opcode functions.
|
|
* 20.08.2002 JH Fixed sign bug in DoIORW() and DoIORL()
|
|
* 10.09.2002 JH Bugfix: MOVE.L 0xfffa00,d0 and the like should not raise bus error
|
|
* 16.09.2002 JH Bugfix: Word access on unmapped I/O address stacked
|
|
* two bus error stack frames. Fault address corrected.
|
|
* Merged some code from JH_TOS206_patches branch.
|
|
* 02.10.2002 JH Eliminated a lot of silly bugs introduced recently. Shame on me.
|
|
No more CPU bus errors from blitter.c::bitblt().
|
|
* 10.10.2002 JH Compatibility improvements.
|
|
*/
|
|
static char sccsid[] = "$Id: st.c,v 1.14 2002/10/10 19:41:27 jhoenig Exp $";
|
|
#include <stdio.h>
|
|
#include "dcastaway.h"
|
|
#include "st.h"
|
|
#include "mem.h"
|
|
#include "m68k_intrf.h"
|
|
#ifndef NO_SOUND
|
|
#include "sound.h"
|
|
#endif
|
|
#include "emuapi.h"
|
|
|
|
|
|
#ifdef DEBUG
|
|
#include <assert.h>
|
|
#endif
|
|
|
|
#include <Arduino.h>
|
|
|
|
#define VALUE_OPEN 0xff
|
|
/*
|
|
* startup display mode
|
|
*/
|
|
int display_mode = COL4;
|
|
|
|
/*
|
|
* I/O Registers
|
|
*/
|
|
uint8 memconf;
|
|
|
|
//Video shifter
|
|
uint32 vid_adr;
|
|
uint8 vid_baseh, vid_basem;
|
|
uint32 vid_mem=0x10000;
|
|
uint8 vid_syncmode=2, vid_shiftmode;
|
|
int16 vid_col[16];
|
|
int vid_flag;
|
|
|
|
uint16 dma_car, dma_scr, dma_sr, dma_mode;
|
|
uint8 dma_adrh, dma_adrm, dma_adrl;
|
|
uint8 mfp_gpip, mfp_aer, mfp_ddr, mfp_iera, mfp_ierb, mfp_ipra,
|
|
mfp_iprb, mfp_isra, mfp_isrb, mfp_imra, mfp_imrb, mfp_ivr,
|
|
mfp_tacr, mfp_tbcr, mfp_tcdcr, mfp_scr, mfp_ucr, mfp_rsr, mfp_tsr, mfp_udr;
|
|
|
|
|
|
//Mfp delay timer variables
|
|
int32 mfp_reg[12];
|
|
#define mfp_tadr mfp_reg[0]
|
|
#define mfp_tbdr mfp_reg[1]
|
|
#define mfp_tcdr mfp_reg[2]
|
|
#define mfp_tddr mfp_reg[3]
|
|
#define mfp_acount mfp_reg[4]
|
|
#define mfp_bcount mfp_reg[5]
|
|
#define mfp_ccount mfp_reg[6]
|
|
#define mfp_dcount mfp_reg[7]
|
|
#define mfp_ascale mfp_reg[8]
|
|
#define mfp_bscale mfp_reg[9]
|
|
#define mfp_cscale mfp_reg[10]
|
|
#define mfp_dscale mfp_reg[11]
|
|
|
|
uint8 acia1_cr, acia1_sr, acia1_dr, acia2_cr, acia2_sr, acia2_dr;
|
|
|
|
uint16 blt_halftone[16];
|
|
int16 blt_src_x_inc, blt_src_y_inc;
|
|
uint32 blt_src_addr;
|
|
int16 blt_end_1, blt_end_2, blt_end_3;
|
|
int16 blt_dst_x_inc, blt_dst_y_inc;
|
|
uint32 blt_dst_addr;
|
|
uint16 blt_x_cnt, blt_y_cnt;
|
|
int8 blt_hop, blt_op, blt_status, blt_skew;
|
|
int8 blt_ready;
|
|
|
|
uint32 psg[26];
|
|
//unsigned char sample[10000];
|
|
#define phase0 psg[16]
|
|
#define phase1 psg[17]
|
|
#define phase2 psg[18]
|
|
#define phase3 psg[19]
|
|
#define psg_epos psg[20]
|
|
#define psgcontrol psg[21]
|
|
#define phase4 psg[22]
|
|
#define nrand psg[23]
|
|
#define sampos psg[24]
|
|
#define lastpsg psg[25]
|
|
|
|
PROGMEM static const int samvol[16]={0,0,0,1,1,1,2,3,5,7,11,17,25,38,57,85};
|
|
static int samvol2[4096];
|
|
|
|
const int32 mfpcycletab[16] = {0,80402,32161,20100,6432,5025,3216,1608,1,0,0,0,0,0,0,0};
|
|
|
|
PROGMEM void IOInit(void)
|
|
{
|
|
int n,a,b,c;
|
|
//Create sample lookup table (4096 entries)
|
|
for (a=0; a<16; a++) {
|
|
for (b=0; b<16; b++) {
|
|
for (c=0; c<16; c++) {
|
|
samvol2[(a<<8)+(b<<4)+c]=samvol[a]+samvol[b]+samvol[c];
|
|
}
|
|
}
|
|
}
|
|
//Reset mfp variables
|
|
mfp_tadr=256<<20; mfp_tbdr=256<<20; mfp_tcdr=256<<20; mfp_tddr=256<<20;
|
|
mfp_tacr=0; mfp_tbcr=0; mfp_tcdcr=0;
|
|
mfp_acount=256<<20; mfp_bcount=256<<20; mfp_ccount=256<<20; mfp_dcount=256<<20;
|
|
mfp_ascale=0; mfp_bscale=0; mfp_cscale=0; mfp_dscale=0;
|
|
for (n=0; n<24; n++) psg[n]=0;
|
|
nrand=1;
|
|
//Other stuff
|
|
vid_baseh = 0;
|
|
vid_basem = 0;
|
|
vid_shiftmode = display_mode;
|
|
dma_sr = 1; /* DMA status ready */
|
|
if (display_mode == MONO) {
|
|
mfp_gpip = 0x39; /* no lpr, no blt, no interrupt, monochrome */
|
|
} else {
|
|
mfp_gpip = 0xb9; /* no lpr, no blt, no interrupt, color */
|
|
}
|
|
#ifdef sun
|
|
act.sa_handler = Sigbus;
|
|
(void) sigaction (SIGBUS, &act, &oldsigbus);
|
|
#endif
|
|
#ifdef USE_MMAP
|
|
act.sa_handler = Sigsegv;
|
|
(void) sigaction (SIGSEGV, &act, &oldsigsegv);
|
|
#endif
|
|
}
|
|
|
|
PROGMEM static void update_psg(uint8 value) {
|
|
#ifndef NO_SOUND
|
|
Sound_Update();
|
|
#endif
|
|
//Update psg register
|
|
psg[psgcontrol]=value;
|
|
|
|
switch(psgcontrol)
|
|
{
|
|
case 13:
|
|
#ifndef NO_SOUND
|
|
bEnvelopeFreqFlag = 1;
|
|
bWriteEnvelopeFreq = 1;
|
|
#endif
|
|
break;
|
|
case 12:
|
|
psg_epos=0;
|
|
break;
|
|
case 8:
|
|
#ifndef NO_SOUND
|
|
bWriteChannelAAmp= 1;
|
|
#endif
|
|
break;
|
|
case 9:
|
|
#ifndef NO_SOUND
|
|
bWriteChannelBAmp= 1;
|
|
#endif
|
|
break;
|
|
case 10:
|
|
#ifndef NO_SOUND
|
|
bWriteChannelCAmp= 1;
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
PROGMEM void DoIOWB(uint32 address, uint8 value)
|
|
{
|
|
address&=0x7fff;
|
|
|
|
//Video and dma emu
|
|
if (address<0x800) {
|
|
switch (address) {
|
|
case MEM_CONF&0x7fff:
|
|
memconf = value;
|
|
break;
|
|
case VID_BASEH&0x7fff:
|
|
vid_baseh = value;
|
|
vid_mem = (vid_baseh<<16)+(vid_basem<<8);
|
|
break;
|
|
case VID_BASEM&0x7fff:
|
|
vid_basem = value;
|
|
vid_mem = (vid_baseh<<16)+(vid_basem<<8);
|
|
break;
|
|
case VID_SYNCMODE&0x7fff:
|
|
vid_syncmode = value;
|
|
maybe_border++;
|
|
break;
|
|
case VID_SHIFTMODE&0x7fff:
|
|
vid_shiftmode = value;
|
|
vid_flag=1;
|
|
break;
|
|
case DMA_ADRH&0x7fff:
|
|
dma_adrh = value;
|
|
break;
|
|
case DMA_ADRM&0x7fff:
|
|
dma_adrm = value;
|
|
break;
|
|
case DMA_ADRL&0x7fff:
|
|
dma_adrl = value;
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
//Sound emu
|
|
if (address<0x900) {
|
|
#if defined(USE_FAME_CORE) && defined(USE_MOVEM_FAME_PATCH)
|
|
static unsigned back_cycles=0;
|
|
static unsigned back_value=0;
|
|
static unsigned back_ctrl=0;
|
|
static unsigned new_value=0;
|
|
#endif
|
|
if (address<0x804)
|
|
waitstate+=1;
|
|
address&=3;
|
|
if (address==0) {
|
|
psgcontrol=value; //&15;
|
|
#if defined(USE_FAME_CORE) && defined(USE_MOVEM_FAME_PATCH)
|
|
if ((M68KCONTEXT.cycles_counter+IO_CYCLE)==back_cycles) {
|
|
psg[back_ctrl]=back_value;
|
|
update_psg(new_value);
|
|
}
|
|
#endif
|
|
}else if (address==2 && psgcontrol<16) {
|
|
#if defined(USE_FAME_CORE) && defined(USE_MOVEM_FAME_PATCH)
|
|
back_ctrl=psgcontrol;
|
|
back_value=psg[psgcontrol];
|
|
new_value=value;
|
|
#endif
|
|
update_psg(value);
|
|
}
|
|
#if defined(USE_FAME_CORE) && defined(USE_MOVEM_FAME_PATCH)
|
|
back_cycles=IO_CYCLE+M68KCONTEXT.cycles_counter;
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
//Bus error?
|
|
if (address<0xb00) {
|
|
ExceptionGroup0(BUSERR, address|0xff8000, 0);
|
|
return;
|
|
}
|
|
|
|
//MFP emu
|
|
if (address<0x7c00) {
|
|
waitstate+=4;
|
|
switch(address) {
|
|
case MFP_AER&0x7fff:
|
|
mfp_aer = value;
|
|
break;
|
|
case MFP_DDR&0x7fff:
|
|
mfp_ddr = value;
|
|
break;
|
|
case MFP_IERA&0x7fff:
|
|
mfp_iera = value;
|
|
mfp_ipra &= mfp_iera;
|
|
break;
|
|
case MFP_IERB&0x7fff:
|
|
mfp_ierb = value;
|
|
mfp_iprb &= mfp_ierb;
|
|
break;
|
|
case MFP_IPRA&0x7fff:
|
|
mfp_ipra &= value;
|
|
break;
|
|
case MFP_IPRB&0x7fff:
|
|
mfp_iprb &= value;
|
|
break;
|
|
case MFP_ISRA&0x7fff:
|
|
mfp_isra &= value;
|
|
#ifndef USE_FAME_CORE
|
|
recalc_int = 1;
|
|
#endif
|
|
break;
|
|
case MFP_ISRB&0x7fff:
|
|
mfp_isrb &= value;
|
|
#ifndef USE_FAME_CORE
|
|
recalc_int = 1;
|
|
#endif
|
|
break;
|
|
case MFP_IMRA&0x7fff:
|
|
mfp_imra = value;
|
|
#ifndef USE_FAME_CORE
|
|
recalc_int = 1;
|
|
#endif
|
|
break;
|
|
case MFP_IMRB&0x7fff:
|
|
mfp_imrb = value;
|
|
#ifndef USE_FAME_CORE
|
|
recalc_int = 1;
|
|
#endif
|
|
break;
|
|
case MFP_IVR&0x7fff:
|
|
mfp_ivr = value;
|
|
break;
|
|
case MFP_TACR&0x7fff:
|
|
mfp_tacr = value&15;
|
|
#if defined(USE_FAME_CORE) && defined(USE_SHORT_SLICE)
|
|
if (mfp_ascale)
|
|
m68k_stop_emulating();
|
|
#endif
|
|
mfp_ascale = mfpcycletab[mfp_tacr];
|
|
break;
|
|
case MFP_TBCR&0x7fff:
|
|
mfp_tbcr = value&15;
|
|
#if defined(USE_FAME_CORE) && defined(USE_SHORT_SLICE)
|
|
if (mfp_bscale)
|
|
m68k_stop_emulating();
|
|
#endif
|
|
mfp_bscale = mfpcycletab[mfp_tbcr];
|
|
break;
|
|
case MFP_TCDCR&0x7fff:
|
|
mfp_tcdcr = value&0x77;
|
|
#if defined(USE_FAME_CORE) && defined(USE_SHORT_SLICE)
|
|
if (mfp_cscale || mfp_dscale)
|
|
m68k_stop_emulating();
|
|
#endif
|
|
mfp_cscale = mfpcycletab[mfp_tcdcr>>4];
|
|
mfp_dscale = mfpcycletab[mfp_tcdcr&7];
|
|
break;
|
|
case MFP_TADR&0x7fff:
|
|
if (value==0) mfp_tadr=256<<20; else mfp_tadr=value<<20;
|
|
if (mfp_ascale==0) mfp_acount=mfp_tadr;
|
|
break;
|
|
case MFP_TBDR&0x7fff:
|
|
if (value==0) mfp_tbdr=256<<20; else mfp_tbdr=value<<20;
|
|
if (mfp_bscale==0) mfp_bcount=mfp_tbdr;
|
|
break;
|
|
case MFP_TCDR&0x7fff:
|
|
if (value==0) mfp_tcdr=256<<20; else mfp_tcdr=value<<20;
|
|
if (mfp_cscale==0) mfp_ccount=mfp_tcdr;
|
|
break;
|
|
case MFP_TDDR&0x7fff:
|
|
if (value==0) mfp_tddr=256<<20; else mfp_tddr=value<<20;
|
|
if (mfp_dscale==0) mfp_dcount=mfp_tddr;
|
|
break;
|
|
case MFP_SCR&0x7fff:
|
|
mfp_scr = value;
|
|
break;
|
|
case MFP_UCR&0x7fff:
|
|
mfp_ucr = value;
|
|
break;
|
|
case MFP_RSR&0x7fff:
|
|
mfp_rsr = value;
|
|
break;
|
|
case MFP_TSR&0x7fff:
|
|
mfp_tsr = value;
|
|
break;
|
|
case MFP_UDR&0x7fff:
|
|
mfp_udr = value;
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch(address) {
|
|
case ACIA1_SR&0x7fff:
|
|
//Serial.println(value);
|
|
waitstate+=8;
|
|
acia1_cr = value;
|
|
break;
|
|
case ACIA1_DR&0x7fff:
|
|
//Serial.println(value);
|
|
waitstate+=8;
|
|
IkbdRecv (value);
|
|
break;
|
|
case ACIA2_SR&0x7fff:
|
|
waitstate+=8;
|
|
acia2_cr = value;
|
|
break;
|
|
case ACIA2_DR&0x7fff:
|
|
emu_MidiOnDataReceived(value);
|
|
waitstate+=8;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
PROGMEM void DoIOWW(uint32 address, uint16 value)
|
|
{
|
|
if (address >= VID_COL0 && address <= VID_COL15) {
|
|
vid_col[(address & 0x1f) >> 1] = value&0x777;
|
|
vid_flag = 1;
|
|
return;
|
|
}
|
|
else
|
|
switch (address) {
|
|
case DMA_MODE:
|
|
dma_mode = value;
|
|
break;
|
|
case DMA_CAR:
|
|
waitstate+=4;
|
|
if (dma_mode & 0x10) dma_scr = value;
|
|
else if (dma_mode & 0x8) dma_car = value;
|
|
else {
|
|
switch (dma_mode & 0x6) {
|
|
case 0:
|
|
fdc_command = value;
|
|
FDCCommand ();
|
|
break;
|
|
case 2:
|
|
fdc_track = value;
|
|
break;
|
|
case 4:
|
|
fdc_sector = value;
|
|
break;
|
|
case 6:
|
|
fdc_data = value;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
DoIOWB(address, value>>8);
|
|
DoIOWB(address+1, value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
PROGMEM void DoIOWL(uint32 address, uint32 value)
|
|
{
|
|
DoIOWW(address, value>>16);
|
|
DoIOWW(address+2, value);
|
|
}
|
|
|
|
static __inline__ void calculate_vid_adr(void)
|
|
{
|
|
unsigned yet=(vid_cycle[cyclenext-IO_CYCLE]-vid_adr_cycleyet)&(~3);
|
|
vid_adr+=yet;
|
|
vid_adr_cycleyet+=yet;
|
|
}
|
|
|
|
PROGMEM uint8 DoIORB(uint32 address)
|
|
{
|
|
address&=0x7fff;
|
|
|
|
//Video and dma emu
|
|
if (address<0x800) {
|
|
switch (address) {
|
|
case MEM_CONF&0x7fff:
|
|
return memconf;
|
|
case VID_BASEH&0x7fff:
|
|
return vid_baseh;
|
|
case VID_BASEM&0x7fff:
|
|
return vid_basem;
|
|
case VID_ADRH&0x7fff:
|
|
calculate_vid_adr();
|
|
return (unsigned char)(vid_adr>>16);
|
|
case VID_ADRM&0x7fff:
|
|
calculate_vid_adr();
|
|
return (unsigned char)(vid_adr>>8);
|
|
case VID_ADRL&0x7fff:
|
|
calculate_vid_adr();
|
|
return (unsigned char)(vid_adr);
|
|
case VID_SYNCMODE&0x7fff:
|
|
return vid_syncmode;
|
|
case VID_LINEWIDTH&0x7fff:
|
|
return 0xff;
|
|
case VID_SHIFTMODE&0x7fff:
|
|
return vid_shiftmode;
|
|
case DMA_ADRH&0x7fff:
|
|
return dma_adrh;
|
|
case DMA_ADRM&0x7fff:
|
|
return dma_adrm;
|
|
case DMA_ADRL&0x7fff:
|
|
return dma_adrl;
|
|
}
|
|
return VALUE_OPEN;
|
|
}
|
|
|
|
//Sound emu
|
|
if (address<0x900) {
|
|
address&=3;
|
|
if (!address)
|
|
{
|
|
waitstate+=4;
|
|
if (psgcontrol>=16) return 0xff;
|
|
return psg[psgcontrol];
|
|
}
|
|
else if (address<4)
|
|
waitstate++;
|
|
return VALUE_OPEN;
|
|
}
|
|
|
|
//Bus error?
|
|
if (address<0xb00) {
|
|
ExceptionGroup0(BUSERR, address|0xff8000, 0);
|
|
return VALUE_OPEN;
|
|
}
|
|
|
|
//MFP emu
|
|
if (address<0x7c00) {
|
|
waitstate+=4;
|
|
switch(address) {
|
|
case MFP_GPIP&0x7fff:
|
|
return mfp_gpip;
|
|
case MFP_AER&0x7fff:
|
|
return mfp_aer;
|
|
case MFP_DDR&0x7fff:
|
|
return mfp_ddr;
|
|
case MFP_IERA&0x7fff:
|
|
return mfp_iera;
|
|
case MFP_IERB&0x7fff:
|
|
return mfp_ierb;
|
|
case MFP_IPRA&0x7fff:
|
|
return mfp_ipra;
|
|
case MFP_IPRB&0x7fff:
|
|
return mfp_iprb;
|
|
case MFP_ISRA&0x7fff:
|
|
return mfp_isra;
|
|
case MFP_ISRB&0x7fff:
|
|
return mfp_isrb;
|
|
case MFP_IMRA&0x7fff:
|
|
return mfp_imra;
|
|
case MFP_IMRB&0x7fff:
|
|
return mfp_imrb;
|
|
case MFP_IVR&0x7fff:
|
|
return mfp_ivr;
|
|
case MFP_TACR&0x7fff:
|
|
return mfp_tacr;
|
|
case MFP_TBCR&0x7fff:
|
|
return mfp_tbcr;
|
|
case MFP_TCDCR&0x7fff:
|
|
return mfp_tcdcr;
|
|
case MFP_TADR&0x7fff:
|
|
return (mfp_acount+0xfffff)>>20;
|
|
case MFP_TBDR&0x7fff:
|
|
return (mfp_bcount+0xfffff)>>20;
|
|
case MFP_TCDR&0x7fff:
|
|
return (mfp_ccount+0xfffff)>>20;
|
|
case MFP_TDDR&0x7fff:
|
|
return (mfp_dcount+0xfffff)>>20;
|
|
case MFP_SCR&0x7fff:
|
|
return mfp_scr;
|
|
case MFP_UCR&0x7fff:
|
|
return mfp_ucr;
|
|
case MFP_RSR&0x7fff:
|
|
return mfp_rsr;
|
|
case MFP_TSR&0x7fff:
|
|
return mfp_tsr;
|
|
case MFP_UDR&0x7fff:
|
|
return mfp_udr;
|
|
}
|
|
return VALUE_OPEN;
|
|
}
|
|
|
|
//Acia emu
|
|
switch(address) {
|
|
case ACIA1_SR&0x7fff:
|
|
waitstate+=8;
|
|
return 2 | acia1_sr;
|
|
case ACIA1_DR&0x7fff:
|
|
waitstate+=8;
|
|
if (!(acia1_cr & 0x20)) {acia1_sr&=0x7e; mfp_gpip|=0x10;}
|
|
return acia1_dr;
|
|
case ACIA2_SR&0x7fff:
|
|
waitstate+=8;
|
|
return 2;
|
|
case ACIA2_DR&0x7fff:
|
|
waitstate+=8;
|
|
return 1;
|
|
}
|
|
return VALUE_OPEN;
|
|
|
|
}
|
|
|
|
PROGMEM uint16 DoIORW(uint32 address)
|
|
{
|
|
if (address >= VID_COL0 && address <= VID_COL15) {
|
|
return vid_col[(address & 0x1f) >> 1];
|
|
}
|
|
switch (address) {
|
|
case DMA_SR:
|
|
return dma_sr;
|
|
case DMA_CAR:
|
|
waitstate+=4;
|
|
if (dma_mode & 0x10) return dma_scr;
|
|
else if (dma_mode & 0x8) return dma_car;
|
|
else {
|
|
switch (dma_mode & 0x6) {
|
|
case 0:
|
|
if (!fdc_int) mfp_gpip |= 0x20;
|
|
return fdc_status;
|
|
case 2:
|
|
return fdc_track;
|
|
case 4:
|
|
return fdc_sector;
|
|
case 6:
|
|
return fdc_data;
|
|
}
|
|
return 0;
|
|
}
|
|
default:
|
|
return (((uint32)DoIORB(address))<<8)+DoIORB(address+1);
|
|
}
|
|
}
|
|
|
|
PROGMEM uint32 DoIORL(uint32 address)
|
|
{
|
|
return (((uint32)DoIORW(address))<<16)+DoIORW(address+2);
|
|
}
|