414 lines
14 KiB
C
414 lines
14 KiB
C
#include <string.h>
|
|
|
|
#include "types.h"
|
|
#include "rom.h"
|
|
#include "ram.h"
|
|
#include "ports.h"
|
|
#include "hdw.h"
|
|
#include "bus.h"
|
|
|
|
#define SEG_OF( adr ) ( ( adr ) >> 12 )
|
|
#define OFFSET_OF( adr ) ( ( adr ) & 0xFFF )
|
|
#define CAN_READ( adr ) ( read_map[ SEG_OF( adr ) ] != NULL )
|
|
#define CAN_WRITE( adr ) ( write_map[ SEG_OF( adr ) ] != NULL )
|
|
#define MAP_READ( adr ) ( read_map[ SEG_OF( adr ) ] + OFFSET_OF( adr ) )
|
|
#define MAP_WRITE( adr ) ( write_map[ SEG_OF( adr ) ] + OFFSET_OF( adr ) )
|
|
|
|
BusInfo bus_info = {
|
|
// hdw ram sz ram ce1 sz ce1 ce2 sz ce2 nce3 sz nce3
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // base or size
|
|
false, false, false, false, false, false, false, false, false, // configured
|
|
false, false, false, // read only
|
|
// ce1_bs da19 ben
|
|
false, false, false,
|
|
// rom ram ce1 ce2 nce3
|
|
NULL, NULL, NULL, NULL, NULL, // data
|
|
0x0, 0x0, 0x0, 0x0, 0x0, 0 // mask
|
|
};
|
|
|
|
static byte* read_map[ 256 ];
|
|
static byte* write_map[ 256 ];
|
|
static address hdw_seg;
|
|
|
|
word crc;
|
|
|
|
void bus_init( void )
|
|
{
|
|
rom_init();
|
|
ram_init();
|
|
ports_init();
|
|
bus_reset();
|
|
}
|
|
|
|
void bus_exit( void )
|
|
{
|
|
rom_exit();
|
|
ram_exit();
|
|
}
|
|
|
|
static inline void update_crc( byte nibble ) { crc = ( crc >> 4 ) ^ ( ( ( crc ^ nibble ) & 0xF ) * 0x1081 ); }
|
|
|
|
void bus_read( byte* buf, address adr, address len )
|
|
{
|
|
int n, i;
|
|
|
|
while ( true ) {
|
|
if ( hdw_seg == SEG_OF( adr ) && ( ( bus_info.hdw_base ^ adr ) & 0xFFFC0 ) == 0 ) {
|
|
n = MIN( len, 0x40 - ( adr & 0x3F ) );
|
|
for ( i = 0; i < n; i++ ) {
|
|
buf[ i ] = hdw_read_nibble( ( adr & 0x3F ) + i );
|
|
}
|
|
if ( ( ( adr & 0x3F ) + n ) == 0x40 ) {
|
|
update_crc( buf[ n - 1 ] );
|
|
}
|
|
} else {
|
|
if ( hdw_seg == SEG_OF( adr ) && ( bus_info.hdw_base & 0xFFFC0 ) - adr > 0 ) {
|
|
n = MIN( len, ( bus_info.hdw_base & 0xFFFC0 ) - adr );
|
|
} else {
|
|
n = MIN( len, 0x1000 - OFFSET_OF( adr ) );
|
|
}
|
|
if ( CAN_READ( adr ) ) {
|
|
memcpy( buf, MAP_READ( adr ), n );
|
|
} else {
|
|
for ( i = 0; i < n; i++ ) {
|
|
buf[ i ] = ( ( i + adr ) & 1 ) ? 0xE : 0xD;
|
|
}
|
|
if ( bus_info.ce1_bs && bus_info.ce1_cfg && ( ( bus_info.ce1_base ^ adr ) & bus_info.ce1_size ) ) {
|
|
ports_switch_bank( OFFSET_OF( adr + n ) );
|
|
}
|
|
}
|
|
for ( i = 0; i < n; i++ ) {
|
|
update_crc( buf[ i ] );
|
|
}
|
|
}
|
|
len -= n;
|
|
if ( !len ) {
|
|
break;
|
|
}
|
|
buf += n;
|
|
adr += n;
|
|
adr &= 0xFFFFF;
|
|
}
|
|
}
|
|
|
|
void bus_write( byte* buf, address adr, address len )
|
|
{
|
|
int n, i;
|
|
|
|
while ( true ) {
|
|
if ( hdw_seg == SEG_OF( adr ) && ( ( bus_info.hdw_base ^ adr ) & 0xFFFC0 ) == 0 ) {
|
|
n = MIN( len, 0x40 - ( adr & 0x3F ) );
|
|
for ( i = 0; i < n; i++ ) {
|
|
hdw_write_nibble( buf[ i ], ( adr & 0x3F ) + i );
|
|
}
|
|
} else {
|
|
if ( hdw_seg == SEG_OF( adr ) && ( bus_info.hdw_base & 0xFFFC0 ) - adr > 0 ) {
|
|
n = MIN( len, ( bus_info.hdw_base & 0xFFFC0 ) - adr );
|
|
} else {
|
|
n = MIN( len, 0x1000 - OFFSET_OF( adr ) );
|
|
}
|
|
if ( CAN_WRITE( adr ) ) {
|
|
memcpy( MAP_WRITE( adr ), buf, n );
|
|
} else if ( bus_info.ce1_bs ) {
|
|
if ( bus_info.ce1_cfg && ( ( bus_info.ce1_base ^ adr ) & bus_info.ce1_size ) ) {
|
|
if ( !bus_info.nce3_r_o ) {
|
|
ports_switch_bank( OFFSET_OF( adr + n - 1 ) );
|
|
} else if ( ( adr + n ) & 1 ) {
|
|
ports_switch_bank( OFFSET_OF( adr + n - 1 ) );
|
|
} else if ( adr & 0x1 ) {
|
|
ports_switch_bank( OFFSET_OF( adr ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
len -= n;
|
|
if ( !len ) {
|
|
break;
|
|
}
|
|
buf += n;
|
|
adr += n;
|
|
adr &= 0xFFFFF;
|
|
}
|
|
}
|
|
|
|
static void bus_peek( byte* buf, address adr, address len )
|
|
{
|
|
int n, i;
|
|
|
|
while ( true ) {
|
|
if ( hdw_seg == SEG_OF( adr ) && ( ( bus_info.hdw_base ^ adr ) & 0xFFFC0 ) == 0 ) {
|
|
n = MIN( len, 0x40 - ( adr & 0x3F ) );
|
|
for ( i = 0; i < n; i++ ) {
|
|
buf[ i ] = hdw_read_nibble( ( adr & 0x3F ) + i );
|
|
}
|
|
} else {
|
|
if ( hdw_seg == SEG_OF( adr ) && ( bus_info.hdw_base & 0xFFFC0 ) - adr > 0 ) {
|
|
n = MIN( len, ( bus_info.hdw_base & 0xFFFC0 ) - adr );
|
|
} else {
|
|
n = MIN( len, 0x1000 - OFFSET_OF( adr ) );
|
|
}
|
|
if ( CAN_READ( adr ) ) {
|
|
memcpy( buf, MAP_READ( adr ), n );
|
|
} else {
|
|
for ( i = 0; i < n; i++ ) {
|
|
buf[ i ] = ( ( i + adr ) & 1 ) ? 0xE : 0xD;
|
|
}
|
|
}
|
|
}
|
|
len -= n;
|
|
if ( !len ) {
|
|
break;
|
|
}
|
|
buf += n;
|
|
adr += n;
|
|
adr &= 0xFFFFF;
|
|
}
|
|
}
|
|
|
|
/* Call only when you know that hdw is not in the range of nibbles read */
|
|
static void bus_peek_no_hdw( byte* buf, address adr, address len )
|
|
{
|
|
int n, i;
|
|
|
|
while ( true ) {
|
|
n = MIN( len, 0x1000 - OFFSET_OF( adr ) );
|
|
if ( CAN_READ( adr ) ) {
|
|
memcpy( buf, MAP_READ( adr ), n );
|
|
} else {
|
|
for ( i = 0; i < n; i++ ) {
|
|
buf[ i ] = ( ( i + adr ) & 1 ) ? 0xE : 0xD;
|
|
}
|
|
}
|
|
len -= n;
|
|
if ( !len ) {
|
|
break;
|
|
}
|
|
buf += n;
|
|
adr += n;
|
|
adr &= 0xFFFFF;
|
|
}
|
|
}
|
|
|
|
/* Returns a pointer for fast read access to memory (don't write).
|
|
* If a direct pointer can't be returnd it will use the caller supplied buffer
|
|
* instead. If the buffer is not used the pointer returned is valid as long as
|
|
* bus_info.map_cnt has the same value (until a call to bus_remap). If the
|
|
* buffer is used the pointer returned (== buf) is valid only until the memory
|
|
* is written to.
|
|
* Set len to the number of nibbles you want to access. It is changed to the
|
|
* actual number of nibbles that can safetly be read through the pointer (can be
|
|
* more or less then the original).
|
|
*/
|
|
byte* bus_fast_peek( byte* buf, address adr, int* len )
|
|
{
|
|
static byte tmp_buf[ FAST_PEEK_MAX ];
|
|
static int tmp_len;
|
|
address adr2;
|
|
|
|
if ( !buf ) {
|
|
buf = tmp_buf;
|
|
}
|
|
if ( !len ) {
|
|
tmp_len = FAST_PEEK_MAX;
|
|
len = &tmp_len;
|
|
}
|
|
if ( *len > FAST_PEEK_MAX ) {
|
|
*len = FAST_PEEK_MAX;
|
|
}
|
|
|
|
adr2 = adr + *len - 1;
|
|
|
|
if ( ( SEG_OF( adr ) == hdw_seg || SEG_OF( adr2 ) == hdw_seg ) &&
|
|
( ( ( bus_info.hdw_base ^ adr ) & 0xFFFC0 ) == 0 || ( ( bus_info.hdw_base ^ adr2 ) & 0xFFFC0 ) == 0 ) ) {
|
|
bus_peek( buf, adr, *len );
|
|
return buf;
|
|
} else if ( !CAN_READ( adr ) ) {
|
|
bus_peek_no_hdw( buf, adr, *len );
|
|
return buf;
|
|
} else if ( SEG_OF( adr ) == SEG_OF( adr2 ) ) {
|
|
if ( hdw_seg == SEG_OF( adr ) && ( bus_info.hdw_base & 0xFFFC0 ) - adr > 0 ) {
|
|
*len = ( bus_info.hdw_base & 0xFFFC0 ) - adr;
|
|
} else {
|
|
*len = 0x1000 - OFFSET_OF( adr );
|
|
}
|
|
return MAP_READ( adr );
|
|
} else if ( CAN_READ( adr2 ) && MAP_READ( adr ) + *len - 1 == MAP_READ( adr2 ) ) {
|
|
if ( hdw_seg == SEG_OF( adr2 ) ) {
|
|
*len = ( bus_info.hdw_base & 0xFFFC0 ) - adr;
|
|
} else {
|
|
*len = 0x2000 - OFFSET_OF( adr );
|
|
}
|
|
return MAP_READ( adr );
|
|
} else {
|
|
bus_peek_no_hdw( buf, adr, *len );
|
|
return buf;
|
|
}
|
|
}
|
|
|
|
void bus_remap( void )
|
|
{
|
|
int adr;
|
|
|
|
for ( adr = 0; adr < 0x100000; adr += 0x01000 ) {
|
|
if ( bus_info.ram_cfg && ( ( bus_info.ram_base ^ adr ) & bus_info.ram_size ) == 0 ) {
|
|
read_map[ SEG_OF( adr ) ] = bus_info.ram_data + ( adr & bus_info.ram_mask );
|
|
write_map[ SEG_OF( adr ) ] = bus_info.ram_data + ( adr & bus_info.ram_mask );
|
|
} else if ( bus_info.ce2_cfg && ( ( bus_info.ce2_base ^ adr ) & bus_info.ce2_size ) == 0 ) {
|
|
if ( bus_info.ce2_data ) {
|
|
read_map[ SEG_OF( adr ) ] = bus_info.ce2_data + ( adr & bus_info.ce2_mask );
|
|
if ( !bus_info.ce2_r_o ) {
|
|
write_map[ SEG_OF( adr ) ] = bus_info.ce2_data + ( adr & bus_info.ce2_mask );
|
|
} else {
|
|
write_map[ SEG_OF( adr ) ] = NULL;
|
|
}
|
|
} else {
|
|
read_map[ SEG_OF( adr ) ] = NULL;
|
|
write_map[ SEG_OF( adr ) ] = NULL;
|
|
}
|
|
} else if ( bus_info.ce1_cfg && ( ( bus_info.ce1_base ^ adr ) & bus_info.ce1_size ) == 0 ) {
|
|
if ( bus_info.ce1_data ) {
|
|
read_map[ SEG_OF( adr ) ] = bus_info.ce1_data + ( adr & bus_info.ce1_mask );
|
|
if ( !bus_info.ce1_r_o ) {
|
|
write_map[ SEG_OF( adr ) ] = bus_info.ce1_data + ( adr & bus_info.ce1_mask );
|
|
} else {
|
|
write_map[ SEG_OF( adr ) ] = NULL;
|
|
}
|
|
} else {
|
|
read_map[ SEG_OF( adr ) ] = NULL;
|
|
write_map[ SEG_OF( adr ) ] = NULL;
|
|
}
|
|
} else if ( bus_info.nce3_cfg && ( ( bus_info.nce3_base ^ adr ) & bus_info.nce3_size ) == 0 ) {
|
|
if ( bus_info.nce3_data && bus_info.ben && !bus_info.da19 ) {
|
|
read_map[ SEG_OF( adr ) ] = bus_info.nce3_data + ( adr & bus_info.nce3_mask );
|
|
if ( !bus_info.nce3_r_o ) {
|
|
write_map[ SEG_OF( adr ) ] = bus_info.nce3_data + ( adr & bus_info.nce3_mask );
|
|
} else {
|
|
write_map[ SEG_OF( adr ) ] = NULL;
|
|
}
|
|
} else {
|
|
read_map[ SEG_OF( adr ) ] = NULL;
|
|
write_map[ SEG_OF( adr ) ] = NULL;
|
|
}
|
|
} else {
|
|
read_map[ SEG_OF( adr ) ] = bus_info.rom_data + ( adr & bus_info.rom_mask & ( bus_info.da19 ? 0xFFFFF : 0x7FFFF ) );
|
|
write_map[ SEG_OF( adr ) ] = NULL;
|
|
}
|
|
}
|
|
bus_info.map_cnt++; // Flag that pointers returned by bus_fast_peek may be
|
|
// invalid
|
|
}
|
|
|
|
void bus_configure( address adr )
|
|
{
|
|
if ( !bus_info.hdw_cfg ) {
|
|
bus_info.hdw_base = adr & 0xFFFC0;
|
|
bus_info.hdw_cfg = true;
|
|
hdw_seg = SEG_OF( adr );
|
|
} else if ( !bus_info.ram_sz_cfg ) {
|
|
bus_info.ram_size = adr & 0xFF000;
|
|
bus_info.ram_sz_cfg = true;
|
|
} else if ( !bus_info.ram_cfg ) {
|
|
bus_info.ram_base = adr & 0xFF000;
|
|
bus_info.ram_cfg = true;
|
|
bus_remap();
|
|
} else if ( !bus_info.ce1_sz_cfg ) {
|
|
bus_info.ce1_size = adr & 0xFF000;
|
|
bus_info.ce1_sz_cfg = true;
|
|
} else if ( !bus_info.ce1_cfg ) {
|
|
bus_info.ce1_base = adr & 0xFF000;
|
|
bus_info.ce1_cfg = true;
|
|
bus_remap();
|
|
} else if ( !bus_info.ce2_sz_cfg ) {
|
|
bus_info.ce2_size = adr & 0xFF000;
|
|
bus_info.ce2_sz_cfg = true;
|
|
} else if ( !bus_info.ce2_cfg ) {
|
|
bus_info.ce2_base = adr & 0xFF000;
|
|
bus_info.ce2_cfg = true;
|
|
bus_remap();
|
|
} else if ( !bus_info.nce3_sz_cfg ) {
|
|
bus_info.nce3_size = adr & 0xFF000;
|
|
bus_info.nce3_sz_cfg = true;
|
|
} else if ( !bus_info.nce3_cfg ) {
|
|
bus_info.nce3_base = adr & 0xFF000;
|
|
bus_info.nce3_cfg = true;
|
|
bus_remap();
|
|
}
|
|
}
|
|
|
|
void bus_unconfigure( address adr )
|
|
{
|
|
if ( bus_info.hdw_cfg && ( ( adr ^ bus_info.hdw_base ) & 0xFFFC0 ) == 0 ) {
|
|
bus_info.hdw_cfg = false;
|
|
hdw_seg = -1;
|
|
} else if ( bus_info.ram_cfg && ( ( adr ^ bus_info.ram_base ) & bus_info.ram_size ) == 0 ) {
|
|
bus_info.ram_cfg = false;
|
|
bus_info.ram_sz_cfg = false;
|
|
bus_remap();
|
|
} else if ( bus_info.ce2_cfg && ( ( adr ^ bus_info.ce2_base ) & bus_info.ce2_size ) == 0 ) {
|
|
bus_info.ce2_cfg = false;
|
|
bus_info.ce2_sz_cfg = false;
|
|
bus_remap();
|
|
} else if ( bus_info.ce1_cfg && ( ( adr ^ bus_info.ce1_base ) & bus_info.ce1_size ) == 0 ) {
|
|
bus_info.ce1_cfg = false;
|
|
bus_info.ce1_sz_cfg = false;
|
|
bus_remap();
|
|
} else if ( bus_info.nce3_cfg && ( ( adr ^ bus_info.nce3_base ) & bus_info.nce3_size ) == 0 ) {
|
|
bus_info.nce3_cfg = false;
|
|
bus_info.nce3_sz_cfg = false;
|
|
bus_remap();
|
|
}
|
|
}
|
|
|
|
void bus_reset( void )
|
|
{
|
|
bus_info.hdw_base = 0x00000;
|
|
bus_info.hdw_cfg = false;
|
|
|
|
bus_info.ram_base = 0x00000;
|
|
bus_info.ram_size = 0x00000;
|
|
bus_info.ram_cfg = false;
|
|
bus_info.ram_sz_cfg = false;
|
|
|
|
bus_info.ce1_base = 0x00000;
|
|
bus_info.ce1_size = 0x00000;
|
|
bus_info.ce1_cfg = false;
|
|
bus_info.ce1_sz_cfg = false;
|
|
|
|
bus_info.ce2_base = 0x00000;
|
|
bus_info.ce2_size = 0x00000;
|
|
bus_info.ce2_cfg = false;
|
|
bus_info.ce2_sz_cfg = false;
|
|
|
|
bus_info.nce3_base = 0x00000;
|
|
bus_info.nce3_size = 0x00000;
|
|
bus_info.nce3_cfg = false;
|
|
bus_info.nce3_sz_cfg = false;
|
|
|
|
hdw_seg = -1;
|
|
bus_remap();
|
|
}
|
|
|
|
address bus_get_id( void )
|
|
{
|
|
if ( !bus_info.hdw_cfg ) {
|
|
return bus_info.hdw_base | 0x00019;
|
|
} else if ( !bus_info.ram_sz_cfg ) {
|
|
return bus_info.ram_size | 0x00003;
|
|
} else if ( !bus_info.ram_cfg ) {
|
|
return bus_info.ram_base | 0x000F4;
|
|
} else if ( !bus_info.ce1_sz_cfg ) {
|
|
return bus_info.ce1_size | 0x00005;
|
|
} else if ( !bus_info.ce1_cfg ) {
|
|
return bus_info.ce1_base | 0x000F6;
|
|
} else if ( !bus_info.ce2_sz_cfg ) {
|
|
return bus_info.ce2_size | 0x00007;
|
|
} else if ( !bus_info.ce2_cfg ) {
|
|
return bus_info.ce2_base | 0x000F8;
|
|
} else if ( !bus_info.nce3_sz_cfg ) {
|
|
return bus_info.nce3_size | 0x00001;
|
|
} else if ( !bus_info.nce3_cfg ) {
|
|
return bus_info.nce3_base | 0x000F2;
|
|
} else {
|
|
return 0x00000;
|
|
}
|
|
}
|