saturnng/romram49.c
2022-03-21 11:05:59 +01:00

887 lines
20 KiB
C
Raw Blame History

/* -------------------------------------------------------------------------
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: romram49.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $
.context : SATURN, Saturn CPU / HPxx emulator
.title : $RCSfile: romram49.c,v $
.kind : C source
.author : Ivan Cibrario B.
.site : CSTV-CNR
.creation : 21-Sep-2000
.keywords : *
.description :
This module emulates the Internal Flash Rom/Ram peripheral modules of
the HP49.
Known deficiencies of the current Flash ROM emulation:
- for efficiency reasons, the Flash ROM state machine is bypassed
when the Flash is read through the ROM controller.
- see flash49.h for additional, more specific information about
Flash ROM emulation issues.
References:
Guide to the Saturn Processor Rev. 1.0b by Matthew Mastracci
HP49 Memory Explained, USENET post, by Steve Sousa.
Emu48 Service Pack 20, by Christoph Gie<69><65>elink.
28F160S5/28F320S5 Data Sheet, by Intel Corp.
.include : config.h, machdep.h, cpu.h, modules.h
.notes :
$Log: romram49.c,v $
Revision 4.1 2000/12/11 09:54:19 cibrario
Public release.
Revision 3.10 2000/10/24 16:14:55 cibrario
Added/Replaced GPL header
Revision 3.4 2000/09/27 10:05:55 cibrario
Bug fix: MP bit of HST no longer set in Ce2Init49() and NCe3Init49(),
to avoid spurious warmstarts of the HP39/40 firmware.
* Revision 3.3 2000/09/26 14:56:43 cibrario
* Revised to implement Flash ROM write access:
* - mod_status_49 is no longer static; flash49.c needs access to the
* Flash ROM array.
* - implemented RomSave49(), RomWrite49().
* - NCe3 controller now accesses the Flash ROM when the LCR_LED bit is set;
* address translation is done here, the actual access is revectored to
* FlashRead49() and FlashWrite49(). Notice that the NCe3 controller
* must be registered with the MOD_MAP_FLAGS_ABS bit set in its map_flags.
*
* Revision 3.2 2000/09/22 14:48:13 cibrario
* *** empty log message ***
*
.- */
#ifndef lint
static char rcs_id[] = "$Id: romram49.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 "flash49.h"
#include "disk_io.h"
#include "debug.h"
#include "args.h"
#define CHF_MODULE_ID MOD_CHF_MODULE_ID
#include <Chf.h>
#define FLASH_VIEW_SELECTOR 0x40000
#define FLASH_BANK_MASK 0x3FFFF
#define CE2_RAM_OFFSET 0x80000
#define NCE3_RAM_OFFSET 0xC0000
#define NCE3_RAM_MASK 0x3FFFF
#define HDW_LCR_OFFSET 0x1C
#define LCR_LED 0x8
/* 3.3: This is no longer static, because flash49.c needs access to
the Flash ROM array... yes, I know this is not particularly nice,
*/
struct ModStatus_49 *mod_status_49;
/*---------------------------------------------------------------------------
Rom module
---------------------------------------------------------------------------*/
/* .+
.title : RomInit49
.kind : C function
.creation : 21-Sep-2000
.description :
This function allocates the dynamically-allocated portion of the
module status structure, and initializes the Flash Rom module.
.call :
RomInit49();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
MOD_F_ROM_INIT
MOD_F_MOD_STATUS_ALLOC
.notes :
3.2, 21-Sep-2000, creation
.- */
void RomInit49(void)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RomInit49");
if((mod_status_49 =
(struct ModStatus_49 *)malloc(sizeof(struct ModStatus_49)))
== (struct ModStatus_49 *)NULL)
{
ChfErrnoCondition;
ChfCondition MOD_F_MOD_STATUS_ALLOC, CHF_FATAL,
sizeof(struct ModStatus_49) ChfEnd;
ChfSignal();
}
if(ReadNibblesFromFile(args.rom_file_name, N_FLASH_SIZE_49,
mod_status_49->flash))
{
ChfCondition MOD_F_ROM_INIT, CHF_FATAL ChfEnd;
ChfSignal();
}
}
/* .+
.title : RomSave49
.kind : C function
.creation : 21-Sep-2000
.description :
This function saves the status of the Flash Rom.
.call :
RomSave49();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
MOD_E_ROM_SAVE
.notes :
3.2, 21-Sep-2000, creation
3.3, 25-Sep-2000, implemented
.- */
void RomSave49(void)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RomSave49");
if(WriteNibblesToFile(mod_status_49->flash, N_FLASH_SIZE_49,
args.rom_file_name))
{
ChfErrnoCondition;
ChfCondition MOD_E_ROM_SAVE, CHF_ERROR ChfEnd;
ChfSignal();
}
}
/* .+
.title : RomRead49
.kind : C function
.creation : 21-Sep-2000
.description :
This function reads a nibble from the internal Flash Rom address
'rel_address' and returns it.
.call :
d = RomRead49(rel_address);
.input :
Address rel_address, memory address
.output :
Nibble *d, datum read from memory
.status_codes :
MOD_I_CALLED
.notes :
3.2, 21-Sep-2000, creation
.- */
Nibble RomRead49(Address rel_address)
{
register XAddress view;
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RomRead49");
view = mod_status.hdw.accel.a49.view[
(rel_address & FLASH_VIEW_SELECTOR) != 0];
return mod_status_49->flash[view | (rel_address & FLASH_BANK_MASK)];
}
/* .+
.title : RomWrite49
.kind : C function
.creation : 21-Sep-2000
.description :
This function writes the nibble 'datum' into the address 'rel_address'
of the internal RAM.
Flash ROM cannot be written using the ROM controller; however,
the current (1.19-4) HP49 firmware apparently executes ROM
write cycles through this controller when the ON key is pressed.
Those cycles are silently ignored.
.call :
RomWrite49(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 :
3.2, 21-Sep-2000, creation
3.3, 26-Sep-2000, implemented
.- */
void RomWrite49(Address rel_address, Nibble datum)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RomWrite49");
/* Ignore write cycles through ROM controller; HP49 ROM 1.19-4
can do this when to ON key is pressed.
*/
}
/*---------------------------------------------------------------------------
Main Ram module
---------------------------------------------------------------------------*/
/* .+
.title : RamInit49
.kind : C function
.creation : 21-Sep-2000
.description :
This function initializes the Ram module.
.call :
RamInit49();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
MOD_W_RAM_INIT
.notes :
3.2, 21-Sep-2000, creation
.- */
void RamInit49(void)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RamInit49");
if(ReadNibblesFromFile(args.ram_file_name, N_RAM_SIZE_49,
mod_status_49->ram))
{
ChfCondition MOD_W_RAM_INIT, CHF_WARNING ChfEnd;
ChfSignal();
(void)memset(mod_status_49->ram, 0, sizeof(mod_status_49->ram));
}
}
/* .+
.title : RamSave49
.kind : C function
.creation : 21-Sep-2000
.description :
This function saves the status of the Ram module to disk.
.call :
RamSave49();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
MOD_E_RAM_SAVE
.notes :
3.2, 21-Sep-2000, creation
.- */
void RamSave49(void)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RamSave49");
if(WriteNibblesToFile(mod_status_49->ram, N_RAM_SIZE_49,
args.ram_file_name))
{
ChfErrnoCondition;
ChfCondition MOD_E_RAM_SAVE, CHF_ERROR ChfEnd;
ChfSignal();
}
}
/* .+
.title : RamRead49
.kind : C function
.creation : 21-Sep-2000
.description :
This function reads a nibble from the internal RAM address 'rel_address'
and returns it.
.call :
d = RamRead49(rel_address);
.input :
Address rel_address, memory address
.output :
Nibble *d, datum read from memory
.status_codes :
MOD_I_CALLED
.notes :
3.2, 21-Sep-2000, creation
.- */
Nibble RamRead49(Address rel_address)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RamRead49");
return mod_status_49->ram[rel_address];
}
/* .+
.title : RamWrite49
.kind : C function
.creation : 21-Sep-2000
.description :
This function writes the nibble 'datum' into the address 'rel_address'
of the internal RAM.
.call :
RamWrite49(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 :
3.2, 21-Sep-2000, creation
.- */
void RamWrite49(Address rel_address, Nibble datum)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "RamWrite49");
mod_status_49->ram[rel_address] = datum;
}
/*---------------------------------------------------------------------------
Ce1 module
---------------------------------------------------------------------------*/
/* .+
.title : Ce1Init49
.kind : C function
.creation : 21-Sep-2000
.description :
This function initializes the Ce1 module, corresponding to the
Back Switcher.
.call :
Ce1Init49();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
.notes :
3.2, 21-Sep-2000, creation
.- */
void Ce1Init49(void)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Init49");
/* Check if bank-switcher accelerators are valid; if not, initialize
them to a reasonable value (that is, select Flash Rom bank 0 for
both views).
*/
if(!mod_status.hdw.accel_valid)
{
mod_status.hdw.accel_valid = 1;
mod_status.hdw.accel.a49.view[0] =
mod_status.hdw.accel.a49.view[1] = (XAddress)0;
}
}
/* .+
.title : Ce1Save49
.kind : C function
.creation : 21-Sep-2000
.description :
This function saves the status of the Ce1 module.
.call :
Ce1Save49();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
.notes :
3.2, 21-Sep-2000, creation
.- */
void Ce1Save49(void)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Save49");
/* Nothing to be done here; the bank-switcher accelerators are saved
by the hdw modules
*/
}
/* This fragment of code is used by both Ce1Read49() and Ce1Write49();
the macro definition is here to allow us to write the code once and
use it many times without incurring a function call overhead.
*/
#define Ce1SetViews \
{ \
mod_status.hdw.accel.a49.view[0] = \
((XAddress)((rel_address >> 5) & 0x03) << 18); \
\
mod_status.hdw.accel.a49.view[1] = \
((XAddress)((rel_address >> 1) & 0x0F) << 18); \
}
/* .+
.title : Ce1Read49
.kind : C function
.creation : 21-Sep-2000
.description :
This function reads a nibble from the Ce1 module; the address of
the access cycle is converted into a pair of XAddress
(base addresses of Flash Rom views) and saved into
mod_status_hdw.accel.a49.view[]. They will be used to supply the
most significant bits of addresses when accessing Flash Rom.
.call :
d = Ce1Read49(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 :
3.2, 21-Sep-2000, creation
.- */
Nibble Ce1Read49(Address rel_address)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Read49");
debug1(DEBUG_C_MODULES, MOD_I_BS_ADDRESS, rel_address);
/* Save the ROM view base addresses address into the hdw accelerators.
view[] can be directly or-ed with a relative port address to
obtain a valid index in Flash Rom.
*/
Ce1SetViews;
return (Nibble)0x0;
}
/* .+
.title : Ce1Write49
.kind : C function
.creation : 21-Sep-2000
.description :
This function reads a nibble from the Ce1 module; the address of
the access cycle is converted into a pair of XAddress
(base addresses of Flash Rom views) and saved into
mod_status_hdw.accel.a49.view[]. They will be used to supply the
most significant bits of addresses when accessing Flash Rom.
.call :
Ce1Write49(rel_address, datum);
.input :
Address rel_address, memory address
Nibble datum, datum to be written into memory; ignored
.output :
void
.status_codes :
MOD_I_CALLED
MOD_I_BS_ADDRESS
.notes :
3.2, 21-Sep-2000, creation
.- */
void Ce1Write49(Address rel_address, Nibble datum)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce1Write49");
debug1(DEBUG_C_MODULES, MOD_I_BS_ADDRESS, rel_address);
/* Save the ROM view base addresses address into the hdw accelerators.
view[] can be directly or-ed with a relative port address to
obtain a valid index in Flash Rom.
*/
Ce1SetViews;
}
/*---------------------------------------------------------------------------
Ce2 module
---------------------------------------------------------------------------*/
/* .+
.title : Ce2Init49
.kind : C function
.creation : 21-Sep-2000
.description :
This function initializes the Ce2 module, corresponding to
the first bank of ERAM.
.call :
Ce2Init49();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
.notes :
3.2, 21-Sep-2000, creation
3.4, 27-Sep-2000, update:
- MP bit in HST no longer set, to avoid spurious warmstarts of the
HP39/40 firmware.
.- */
void Ce2Init49(void)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Init49");
/* Set base of ce2 area */
mod_status_49->ce2 = mod_status_49->ram + CE2_RAM_OFFSET;
/* CE2 always present and write enabled */
mod_status.hdw.card_status |= (CE2_CARD_PRESENT|CE2_CARD_WE);
#if 0
/* card_status changed; update, set MP bit in HST and post
interrupt request.
*/
cpu_status.HST |= HST_MP_MASK;
CpuIntRequest(INT_REQUEST_IRQ);
#endif
}
/* .+
.title : Ce2Save49
.kind : C function
.creation : 21-Sep-2000
.description :
This function saves the status of the Ce2 module.
.call :
Ce2Save49();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
.notes :
3.2, 21-Sep-2000, creation
.- */
void Ce2Save49(void)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Save49");
/* Do nothing; the whole RAM is saved by RamSave49() */
}
/* .+
.title : Ce2Read49
.kind : C function
.creation : 21-Sep-2000
.description :
This function reads a nibble from the Ce2 module.
.call :
d = Ce2Read49(rel_address)
.input :
Address rel_address, memory address
.output :
Nibble *d, datum read from memory
.status_codes :
MOD_I_CALLED
.notes :
3.2, 21-Sep-2000, creation
.- */
Nibble Ce2Read49(Address rel_address)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Read49");
return mod_status_49->ce2[rel_address];
}
/* .+
.title : Ce2Write49
.kind : C function
.creation : 21-Sep-2000
.description :
This function writes a nibble to the Ce2 module.
.call :
Ce2Write49(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 :
3.2, 21-Sep-2000, creation
.- */
void Ce2Write49(Address rel_address, Nibble datum)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "Ce2Write49");
mod_status_49->ce2[rel_address] = datum;
}
/*---------------------------------------------------------------------------
NCe3 module
---------------------------------------------------------------------------*/
/* .+
.title : NCe3Init49
.kind : C function
.creation : 21-Sep-2000
.description :
This function initializes the Ce2 module, corresponding to
the first bank of ERAM.
.call :
NCe3Init49();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
.notes :
3.2, 21-Sep-2000, creation
3.4, 27-Sep-2000, update:
- MP bit in HST no longer set, to avoid spurious warmstarts of the
HP39/40 firmware.
.- */
void NCe3Init49(void)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Init49");
/* Set base of nce3 area */
mod_status_49->nce3 = mod_status_49->ram + NCE3_RAM_OFFSET;
/* NCE3 always present and write enabled */
mod_status.hdw.card_status |= (NCE3_CARD_PRESENT|NCE3_CARD_WE);
#if 0
/* card_status changed; update, set MP bit in HST and post
interrupt request.
*/
cpu_status.HST |= HST_MP_MASK;
CpuIntRequest(INT_REQUEST_IRQ);
#endif
}
/* .+
.title : NCe3Save49
.kind : C function
.creation : 21-Sep-2000
.description :
This function saves the status of the NCe3 module.
.call :
NCe3Save49();
.input :
void
.output :
void
.status_codes :
MOD_I_CALLED
.notes :
3.2, 21-Sep-2000, creation
.- */
void NCe3Save49(void)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Save49");
/* Do nothing; the whole RAM is saved by RamSave49() */
}
/* .+
.title : NCe3Read49
.kind : C function
.creation : 21-Sep-2000
.description :
This function reads a nibble from the NCe3 module.
If LCR_LED bit is not set, data comes from ERAM Bank 1, if LCR_LED is set,
data comes from the Flash ROM.
In both cases, address translation is done here.
This module must have the MOD_MAP_FLAGS_ABS set in
its mod_description[].map_flags; this way, rel_address will be
the ABSOLUTE address of the memory access.
.call :
d = NCe3Read49(rel_address)
.input :
Address rel_address, memory address (ABSOLUTE)
.output :
Nibble *d, datum read from memory
.status_codes :
MOD_I_CALLED
.notes :
3.2, 21-Sep-2000, creation
3.3, 25-Sep-2000, update
- added vectors into flash49 for FlashROM read ops
.- */
Nibble NCe3Read49(Address rel_address)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Read49");
return
(mod_status.hdw.hdw[HDW_LCR_OFFSET] & LCR_LED)
? FlashRead49(
mod_status.hdw.accel.a49.view[
(rel_address & FLASH_VIEW_SELECTOR) != 0]
| (rel_address & FLASH_BANK_MASK))
: mod_status_49->nce3[rel_address & NCE3_RAM_MASK];
}
/* .+
.title : NCe3Write49
.kind : C function
.creation : 21-Sep-2000
.description :
This function writes a nibble to the NCe3 module.
If LCR_LED bit is not set, data goes to ERAM Bank 1, if LCR_LED is set,
data goes to the Flash ROM.
In both cases, address translation is done here.
This module must have the MOD_MAP_FLAGS_ABS set in
its mod_description[].map_flags; this way, rel_address will be
the ABSOLUTE address of the memory access.
.call :
NCe3Write49(rel_address, datum);
.input :
Address rel_address, memory address (ABSOLUTE)
Nibble datum, datum to be written into memory
.output :
void
.status_codes :
MOD_I_CALLED
.notes :
3.2, 21-Sep-2000, creation
3.3, 25-Sep-2000, update
- added vectors into flash49 for FlashROM write ops
.- */
void NCe3Write49(Address rel_address, Nibble datum)
{
debug1(DEBUG_C_TRACE, MOD_I_CALLED, "NCe3Write49");
if(mod_status.hdw.hdw[HDW_LCR_OFFSET] & LCR_LED)
FlashWrite49(mod_status.hdw.accel.a49.view[
(rel_address & FLASH_VIEW_SELECTOR) != 0]
| (rel_address & FLASH_BANK_MASK),
datum);
else
mod_status_49->nce3[rel_address & NCE3_RAM_MASK] = datum;
}