saturnng/src/romram.c
Gwenhael Le Moine e8746fec96
syntax "else"
2024-09-11 16:39:11 +02:00

854 lines
21 KiB
C

/* -------------------------------------------------------------------------
saturn - A poor-man's emulator of some HP calculators
Copyright (C) 1998-2000 Ivan Cibrario Bertolotti
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 the documentation of this program; if not, write to
the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
For more information, please contact the author, preferably by email,
at the following address:
Ivan Cibrario Bertolotti
IRITI - National Research Council
c/o IEN "Galileo Ferraris"
Strada delle Cacce, 91
10135 - Torino (ITALY)
email: cibrario@iriti.cnr.it
------------------------------------------------------------------------- */
/* +-+ */
/* .+
.identifier : $Id: romram.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $
.context : SATURN, Saturn CPU / HP48 emulator
.title : $RCSfile: romram.c,v $
.kind : C source
.author : Ivan Cibrario B.
.site : CSTV-CNR
.creation : 23-Jan-1998
.keywords : *
.description :
This module emulates the Internal Rom/Ram peripheral modules of the HP48.
References:
SASM.DOC by HP (HORN disk 4)
Guide to the Saturn Processor Rev. 0.00f by Matthew Mastracci
entries.srt by Mika Heiskanen (mheiskan@vipunen.hut.fi)
x48 source code by Eddie C. Dost (ecd@dressler.de)
.include : config.h, machdep.h, cpu.h, modules.h
.notes :
$Log: romram.c,v $
Revision 4.1 2000/12/11 09:54:19 cibrario
Public release.
Revision 3.10 2000/10/24 16:14:53 cibrario
Added/Replaced GPL header
Revision 3.2 2000/09/22 14:10:25 cibrario
Implemented preliminary support of HP49 hw architecture:
- Revised to handle the split of struct ModStatus in two
* Revision 3.1 2000/09/20 13:57:24 cibrario
* Minor updates and fixes to avoid gcc compiler warnings on Solaris
* when -ansi -pedantic -Wall options are selected.
*
* Revision 2.4 2000/09/12 15:25:16 cibrario
* Implemented emulation of Port 1 and 2; write protection and
* HST/MP interrupt generation has been implemented as well.
*
* Revision 1.1 1998/02/17 11:49:40 cibrario
* Initial revision
*
.- */
#ifndef lint
static char rcs_id[] = "$Id: romram.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $";
#endif
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <string.h>
#include <unistd.h> /* access() */
#include <errno.h>
#include "config.h"
#include "machdep.h"
#include "cpu.h"
#include "modules.h"
#include "disk_io.h"
#include "debug.h"
#include "args.h"
#define CHF_MODULE_ID MOD_CHF_MODULE_ID
#include <Chf.h>
/* 3.2: The rom/ram storage areas are now dynamically allocated in
a private struct ModStatus_48. The dynamic allocation is performed during
Rom initialization, and the following macro allows us to reuse the
existing code with minimal updates.
*/
static struct ModStatus_48* mod_status_48;
#define mod_status_hdw mod_status.hdw
#define mod_status_rom mod_status_48->rom
#define mod_status_ram mod_status_48->ram
#define mod_status_port_1 mod_status_48->port_1
#define mod_status_port_2 mod_status_48->port_2
/*---------------------------------------------------------------------------
Rom module
---------------------------------------------------------------------------*/
/* .+
.title : RomInit
.kind : C function
.creation : 23-Jan-1998
.description :
This function allocates the dynamically-allocated portion of the
module status structure, and initializes the Rom module.
.call :
RomInit();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
MOD_F_ROM_INIT
MOD_F_MOD_STATUS_ALLOC
.notes :
1.1, 23-Jan-1998, creation
.- */
void RomInit( void )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "RomInit" );
if ( ( mod_status_48 = ( struct ModStatus_48* )malloc( sizeof( struct ModStatus_48 ) ) ) == ( struct ModStatus_48* )NULL ) {
ChfErrnoCondition;
ChfCondition MOD_F_MOD_STATUS_ALLOC, CHF_FATAL, sizeof( struct ModStatus_48 ) ChfEnd;
ChfSignal();
}
if ( ReadNibblesFromFile( args.rom_file_name, N_ROM_SIZE, mod_status_rom ) ) {
ChfCondition MOD_F_ROM_INIT, CHF_FATAL ChfEnd;
ChfSignal();
}
}
/* .+
.title : RomSave
.kind : C function
.creation : 11-Feb-1998
.description :
This function saves the status of the Rom module; actually it does
nothing.
.call :
RomSave();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
.notes :
1.1, 11-Feb-1998, creation
.- */
void RomSave( void ) { debug1( DEBUG_C_TRACE, MOD_I_CALLED, "RomSave" ); }
/* .+
.title : RomRead
.kind : C function
.creation : 26-Jan-1998
.description :
This function reads a nibble from the internal ROM address 'rel_address'
and returns it.
.call :
d = RomRead(rel_address);
.input :
Address rel_address, memory address
.output :
Nibble *d, datum read from memory
.status_codes :
MOD_I_CALLED
.notes :
1.1, 26-Jan-1998, creation
.- */
Nibble RomRead( Address rel_address )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "RomRead" );
return mod_status_rom[ rel_address ];
}
/* .+
.title : RomWrite
.kind : C function
.creation : 26-Jan-1998
.description :
This function is called when the CPU attempt to write into an internal
ROM location. It signals an error condition and does nothing.
.call :
RomWrite(rel_address, datum);
.input :
Address rel_address, memory address
Nibble datum, datum to be written into memory
.output :
void
.status_codes :
MOD_I_CALLED
MOD_E_ROM_WRITE
.notes :
1.1, 26-Jan-1998, creation
.- */
void RomWrite( Address rel_address, Nibble datum )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "RomWrite" );
ChfCondition MOD_E_ROM_WRITE, CHF_ERROR, rel_address, datum ChfEnd;
ChfSignal();
}
/*---------------------------------------------------------------------------
Main Ram module
---------------------------------------------------------------------------*/
/* .+
.title : RamInit
.kind : C function
.creation : 23-Jan-1998
.description :
This function initializes the Ram module.
.call :
RamInit();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
MOD_W_RAM_INIT
.notes :
1.1, 23-Jan-1998, creation
.- */
void RamInit( void )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "RamInit" );
if ( ReadNibblesFromFile( args.ram_file_name, N_RAM_SIZE, mod_status_ram ) ) {
ChfCondition MOD_W_RAM_INIT, CHF_WARNING ChfEnd;
ChfSignal();
( void )memset( mod_status_ram, 0, sizeof( mod_status_ram ) );
}
}
/* .+
.title : RamSave
.kind : C function
.creation : 11-Feb-1998
.description :
This function saves the status of the Ram module to disk.
.call :
RamSave();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
MOD_E_RAM_SAVE
.notes :
1.1, 11-Feb-1998, creation
2.4, 12-Sep-2000, update
- upon failure, added push of ChfErrnoCondition to condition stack.
.- */
void RamSave( void )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "RamSave" );
if ( WriteNibblesToFile( mod_status_ram, N_RAM_SIZE, args.ram_file_name ) ) {
ChfErrnoCondition;
ChfCondition MOD_E_RAM_SAVE, CHF_ERROR ChfEnd;
ChfSignal();
}
}
/* .+
.title : RamRead
.kind : C function
.creation : 26-Jan-1998
.description :
This function reads a nibble from the internal RAM address 'rel_address'
and returns it.
.call :
d = RamRead(rel_address);
.input :
Address rel_address, memory address
.output :
Nibble *d, datum read from memory
.status_codes :
MOD_I_CALLED
.notes :
1.1, 26-Jan-1998, creation
.- */
Nibble RamRead( Address rel_address )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "RamRead" );
return mod_status_ram[ rel_address ];
}
/* .+
.title : RamWrite
.kind : C function
.creation : 26-Jan-1998
.description :
This function writes the nibble 'datum' into the address 'rel_address'
of the internal RAM.
.call :
RamWrite(rel_address, datum);
.input :
Address rel_address, memory address
Nibble datum, datum to be written into memory
.output :
void
.status_codes :
MOD_I_CALLED
.notes :
1.1, 26-Jan-1998, creation
.- */
void RamWrite( Address rel_address, Nibble datum )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "RamWrite" );
mod_status_ram[ rel_address ] = datum;
}
/*---------------------------------------------------------------------------
Ce1 module
---------------------------------------------------------------------------*/
/* .+
.title : Ce1Init
.kind : C function
.creation : 23-Jan-1998
.description :
This function initializes the Ce1 module, corresponding to the
Back Switcher.
.call :
Ce1Init();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
.notes :
1.1, 23-Jan-1998, creation
2.4, 11-Sep-2000, implemented
.- */
void Ce1Init( void )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Init" );
/* Check if bank-switcher accelerators are valid; if not, initialize
them to a reasonable value (that is, select Port_2 bank 0).
*/
if ( !mod_status_hdw.accel_valid ) {
mod_status_hdw.accel_valid = 1;
mod_status_hdw.accel.a48.bs_address = ( XAddress )0;
}
}
/* .+
.title : Ce1Save
.kind : C function
.creation : 11-Feb-1998
.description :
This function saves the status of the Ce1 module.
.call :
Ce1Save();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
.notes :
1.1, 11-Feb-1998, creation
2.4, 11-Sep-2000, implemented
.- */
void Ce1Save( void )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Save" );
/* Nothing to be done herel the bank-switcher accelerators are saved
by the hdw modules
*/
}
/* .+
.title : Ce1Read
.kind : C function
.creation : 23-Jan-1998
.description :
This function reads a nibble from the Ce1 module; the address of
the access cycle is converted into an XAddress and saved in
mod_status_hdw.accel.a48.bs_address. It will be used to supply the
most significant bits of Port_2 addresses when accessing that port.
.call :
d = Ce1Read(rel_address);
.input :
Address rel_address, memory address
.output :
Nibble *d, datum read from memory
.status_codes :
MOD_I_CALLED
MOD_I_BS_ADDRESS
.notes :
1.1, 23-Jan-1998, creation
2.4, 11-Sep-2000, implemented
.- */
Nibble Ce1Read( Address rel_address )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Read" );
debug1( DEBUG_C_MODULES, MOD_I_BS_ADDRESS, rel_address );
/* Save the read address into the hdw accelerators.
bs_address can be directly or-ed with a relative port address to
obtain a valid index in Port_2
*/
#ifdef N_PORT_2_BANK
mod_status_hdw.accel.a48.bs_address = ( ( XAddress )( ( rel_address >> 1 ) & 0x1F ) << 18 ) & ( N_PORT_2_SIZE - 1 );
#endif
return ( Nibble )0x0;
}
/* .+
.title : Ce1Write
.kind : C function
.creation : 23-Jan-1998
.description :
This function writes a nibble to the Ce1 module; the write attempt
is ignored and the status code MOD_E_CE1_WRITE is signaled. The
state of mod_status_hdw.accel.a48.bs_address is *not* changed.
.call :
Ce1Write(rel_address, datum);
.input :
Address rel_address, memory address
Nibble datum, datum to be written into memory
.output :
void
.status_codes :
MOD_I_CALLED
MOD_E_CE1_WRITE
.notes :
1.1, 23-Jan-1998, creation
2.4, 11-Sep-2000, implemented
.- */
void Ce1Write( Address rel_address, Nibble datum )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Write" );
ChfCondition MOD_E_CE1_WRITE, CHF_ERROR, rel_address, datum ChfEnd;
ChfSignal();
}
/*---------------------------------------------------------------------------
Ce2 module
---------------------------------------------------------------------------*/
/* .+
.title : Ce2Init
.kind : C function
.creation : 23-Jan-1998
.description :
This function initializes the Ce2 module, corresponding to Port 1.
.call :
Ce2Init();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
MOD_W_PORT_1_INIT
MOD_I_PORT_1_WP
.notes :
1.1, 23-Jan-1998, creation
2.4, 11-Sep-2000, implemented
.- */
void Ce2Init( void )
{
Nibble new_status;
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Init" );
if ( ReadNibblesFromFile( args.port_1_file_name, N_PORT_1_SIZE, mod_status_port_1 ) ) {
ChfCondition MOD_W_PORT_1_INIT, CHF_WARNING ChfEnd;
ChfSignal();
( void )memset( mod_status_port_1, 0, sizeof( mod_status_port_1 ) );
new_status = mod_status_hdw.card_status & ~( CE2_CARD_PRESENT | CE2_CARD_WE );
} else {
/* Card present; check write protection */
new_status = mod_status_hdw.card_status | CE2_CARD_PRESENT;
if ( access( args.port_1_file_name, W_OK ) == 0 )
new_status |= CE2_CARD_WE;
else {
new_status &= ~CE2_CARD_WE;
ChfErrnoCondition;
ChfCondition MOD_I_PORT_1_WP, CHF_INFO ChfEnd;
ChfSignal();
}
}
if ( new_status != mod_status_hdw.card_status ) {
/* card_status changed; update, set MP bit in HST and post
interrupt request.
*/
mod_status_hdw.card_status = new_status;
cpu_status.HST |= HST_MP_MASK;
CpuIntRequest( INT_REQUEST_IRQ );
}
}
/* .+
.title : Ce2Save
.kind : C function
.creation : 11-Feb-1998
.description :
This function saves the status of the Ce2 module, if it is
not write-protected.
.call :
Ce2Save();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
MOD_E_PORT_1_SAVE
.notes :
1.1, 11-Feb-1998, creation
2.4, 11-Sep-2000, implemented
.- */
void Ce2Save( void )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Save" );
/* Attempt to save only if port is write-enabled */
if ( ( mod_status_hdw.card_status & CE2_CARD_WE ) && WriteNibblesToFile( mod_status_port_1, N_PORT_1_SIZE, args.port_1_file_name ) ) {
ChfErrnoCondition;
ChfCondition MOD_E_PORT_1_SAVE, CHF_ERROR ChfEnd;
ChfSignal();
}
}
/* .+
.title : Ce2Read
.kind : C function
.creation : 23-Jan-1998
.description :
This function reads a nibble from the Ce2 module.
.call :
d = Ce2Read(rel_address)
.input :
Address rel_address, memory address
.output :
Nibble *d, datum read from memory
.status_codes :
MOD_I_CALLED
.notes :
1.1, 23-Jan-1998, creation
2.4, 11-Sep-2000, implemented
.- */
Nibble Ce2Read( Address rel_address )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Read" );
return mod_status_port_1[ rel_address ];
}
/* .+
.title : Ce2Write
.kind : C function
.creation : 23-Jan-1998
.description :
This function writes a nibble to the Ce2 module.
.call :
Ce2Write(rel_address, datum);
.input :
Address rel_address, memory address
Nibble datum, datum to be written into memory
.output :
void
.status_codes :
MOD_I_CALLED
.notes :
1.1, 23-Jan-1998, creation
2.4, 11-Sep-2000, implemented
.- */
void Ce2Write( Address rel_address, Nibble datum )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Write" );
mod_status_port_1[ rel_address ] = datum;
}
/*---------------------------------------------------------------------------
NCe3 module
---------------------------------------------------------------------------*/
/* .+
.title : NCe3Init
.kind : C function
.creation : 23-Jan-1998
.description :
This function initializes the NCe3 module, corresponding to the
(bank switched) port 2.
.call :
NCe3Init();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
MOD_W_PORT_2_INIT
MOD_I_PORT_2_WP
.notes :
1.1, 23-Jan-1998, creation
2.4, 11-Sep-2000, implemented
.- */
void NCe3Init( void )
{
Nibble new_status;
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Init" );
#ifdef N_PORT_2_BANK
if ( ReadNibblesFromFile( args.port_2_file_name, N_PORT_2_SIZE, mod_status_port_2 ) ) {
ChfCondition MOD_W_PORT_2_INIT, CHF_WARNING ChfEnd;
ChfSignal();
( void )memset( mod_status_port_2, 0, sizeof( mod_status_port_2 ) );
new_status = mod_status_hdw.card_status & ~( NCE3_CARD_PRESENT | NCE3_CARD_WE );
} else {
/* Card present; check write protection */
new_status = mod_status_hdw.card_status | NCE3_CARD_PRESENT;
if ( access( args.port_2_file_name, W_OK ) == 0 )
new_status |= NCE3_CARD_WE;
else {
new_status &= ~NCE3_CARD_WE;
ChfErrnoCondition;
ChfCondition MOD_I_PORT_2_WP, CHF_INFO ChfEnd;
ChfSignal();
}
}
#else
/* If N_PORT_2_BANK is undefined, Port 2 is not emulated */
new_status = mod_status_hdw.card_status & ~( NCE3_CARD_PRESENT | NCE3_CARD_WE );
#endif
if ( new_status != mod_status_hdw.card_status ) {
/* card_status changed; update, set MP bit in HST and post
interrupt request.
*/
mod_status_hdw.card_status = new_status;
cpu_status.HST |= HST_MP_MASK;
CpuIntRequest( INT_REQUEST_IRQ );
}
}
/* .+
.title : NCe3Save
.kind : C function
.creation : 11-Feb-1998
.description :
This function saves the status of the NCe3 module.
.call :
NCe3Save();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
MOD_E_PORT_2_SAVE
.notes :
1.1, 11-Feb-1998, creation
2.4, 11-Sep-2000, implemented
.- */
void NCe3Save( void )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Save" );
#ifdef N_PORT_2_BANK
/* Attempt to save only if port is write-enabled */
if ( ( mod_status_hdw.card_status & NCE3_CARD_WE ) && WriteNibblesToFile( mod_status_port_2, N_PORT_2_SIZE, args.port_2_file_name ) ) {
ChfErrnoCondition;
ChfCondition MOD_E_PORT_2_SAVE, CHF_ERROR ChfEnd;
ChfSignal();
}
#endif
}
/* .+
.title : NCe3Read
.kind : C function
.creation : 23-Jan-1998
.description :
This function reads a nibble from the NCe3 module.
.call :
d = NCe3Read(rel_address)
.input :
Address rel_address, memory address
.output :
Nibble *d, datum read from memory
.status_codes :
MOD_I_CALLED
MOD_E_NCE3_READ
.notes :
1.1, 23-Jan-1998, creation
2.4, 11-Sep-2000, implemented
.- */
Nibble NCe3Read( Address rel_address )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Read" );
#ifdef N_PORT_2_BANK
return mod_status_port_2[ rel_address | mod_status_hdw.accel.a48.bs_address ];
#else
ChfCondition MOD_E_NCE3_READ, CHF_ERROR, rel_address ChfEnd;
ChfSignal();
return ( Nibble )0;
#endif
}
/* .+
.title : NCe3Write
.kind : C function
.creation : 23-Jan-1998
.description :
This function writes a nibble to the NCe3 module;
it is not currently implemented.
.call :
NCe3Write(rel_address, datum);
.input :
Address rel_address, memory address
Nibble datum, datum to be written into memory
.output :
void
.status_codes :
MOD_I_CALLED
MOD_E_NCE3_WRITE
.notes :
1.1, 23-Jan-1998, creation
2.4, 11-Sep-2000, implemented
.- */
void NCe3Write( Address rel_address, Nibble datum )
{
debug1( DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Write" );
#ifdef N_PORT_2_BANK
mod_status_port_2[ rel_address | mod_status_hdw.accel.a48.bs_address ] = datum;
#else
ChfCondition MOD_E_NCE3_WRITE, CHF_ERROR, rel_address, datum ChfEnd;
ChfSignal();
#endif
}