saturnng/src/x_func.c
2024-03-26 13:36:50 +01:00

456 lines
15 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: x_func.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $
.context : SATURN, Saturn CPU / HPxx emulator
.title : $RCSfile: x_func.c,v $
.kind : C source
.author : Ivan Cibrario B.
.site : CSTV-CNR
.creation : 3-Nov-2000
.keywords : *
.description :
This module implements the emulator's extended functions, that is,
functions that the real machine has not.
References:
Private communications with Prof. B. Parisse
.include : config.h, machdep.h, cpu.h, x_func.h
.notes :
$Log: x_func.c,v $
Revision 4.1 2000/12/11 09:54:19 cibrario
Public release.
Revision 3.15 2000/11/15 14:16:45 cibrario
GUI enhancements and assorted bug fixes:
- Implemented command-line option -batchXfer
Revision 3.14 2000/11/13 11:11:01 cibrario
Implemented fast load/save; improved keyboard interface emulation at
high emulated CPU speed:
- Added credits in file doc
- Implemented CPU status access functions ByteFromAddress(),
NameFromD1()
- Implemented new static function BinaryHeader(), to select an header
of binary files appropriate for the current hw configuration
- Implemented new static functions Kget()/KgetContinuation(), to load
a file from disk into the calculator, and Send/SendContinuation(),
to save a file from the calculator's memory into a disk file.
- Removed test functions TestFSB() and TestFSBContinuation()
- Implemented static helper function SetupXfer(), to setup a disk
transfer.
- Updated static function[] table
Revision 3.13 2000/11/09 11:42:22 cibrario
*** empty log message ***
.- */
#ifndef lint
static char rcs_id[] = "$Id: x_func.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $";
#endif
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <X11/Xlib.h> /* Main X header */
#include <X11/Intrinsic.h> /* Main Xt header */
#include "config.h"
#include "machdep.h"
#include "cpu.h"
#include "modules.h"
#include "disk_io.h"
#include "x11.h" /* ActivateFSB() */
#include "x_func.h"
#include "args.h"
#include "debug.h"
#define CHF_MODULE_ID X_FUNC_CHF_MODULE_ID
#include <Chf.h>
/*---------------------------------------------------------------------------
Private functions: CPU access
---------------------------------------------------------------------------*/
/* Return the A field of a DataRegister as an integer. */
static int R2int( const Nibble* r )
{
return ( ( ( int )r[ 0 ] ) | ( ( int )r[ 1 ] << 4 ) | ( ( int )r[ 2 ] << 8 ) | ( ( int )r[ 3 ] << 12 ) | ( ( int )r[ 4 ] << 16 ) );
}
/* Return the contents of the byte pointed by addr.
Memory is accessed through ReadNibble()
*/
static int ByteFromAddress( Address addr ) { return ( int )ReadNibble( addr ) + ( int )ReadNibble( addr + 1 ) * 16; }
/* Return a dynamically-allocated copy of the contents of the IDNT
object pointed by D1. D1 points to the *body* of the
RPL object, that is, to the IDNT length byte directly and *not*
to the prologue.
*/
static char* NameFromD1( void )
{
Address addr = cpu_status.D1; /* Points to the IDNT body */
int len = ByteFromAddress( addr ); /* IDNT length */
char* name = XtMalloc( len + 1 ); /* IDNT name buffer */
int c;
/* Read the name; toascii() is there to avoid 'strange' characters */
for ( c = 0; c < len; c++ ) {
addr += 2;
name[ c ] = ( char )toascii( ByteFromAddress( addr ) );
}
name[ c ] = '\0'; /* Terminate and return the name */
return name;
}
/*---------------------------------------------------------------------------
Private functions: action routines
---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/* Set the emulator speed to the given value (in MHz); the desired speed
value is held in the A field of the C CPU register. No handshake.
*/
static void SetSpeed( Nibble function_code )
{
debug1( DEBUG_C_TRACE, X_FUNC_I_CALLED, "SetSpeed" );
#ifndef REAL_CPU_SPEED
ChfCondition X_FUNC_E_NO_SPEED, CHF_ERROR ChfEnd;
ChfSignal();
#else
{
int new_speed;
/* Get new_speed from A field of C register */
new_speed = R2int( cpu_status.C );
/* Compute inner loop limit; 4 is the real CPU speed in MHz when
the limit is set to INNER_LOOP_MAX. No overflow checks,
because new_speed is >=0, has an architectural upper limit of 2^20,
and int are at least 2^31.
*/
cpu_status.inner_loop_max = ( new_speed * INNER_LOOP_MAX ) / 4;
/* Notify the user about the speed change */
if ( cpu_status.inner_loop_max )
ChfCondition X_FUNC_I_SET_SPEED, CHF_INFO, new_speed ChfEnd;
else
ChfCondition X_FUNC_I_MAX_SPEED, CHF_INFO ChfEnd;
ChfSignal();
}
#endif
}
/*---------------------------------------------------------------------------*/
/* This array holds the binary headers for all known hw configurations;
here, '?' is a wildcard character when reading from file
(see ReadObjectFromFile()) and is replaced by 'S' when writing
to file (see WriteObjectToFile()).
*/
struct BinHdrMapping {
char* hw;
char* hdr;
};
static const struct BinHdrMapping bin_hdr_mapping[] = {
{"hp48", "HPHP48-?"},
{"hp49", "HPHP49-?"}
};
#define N_BIN_HDR_MAPPING ( sizeof( bin_hdr_mapping ) / sizeof( bin_hdr_mapping[ 0 ] ) )
/* Return the header of binary files for current hw configuration;
return NULL if the header cannot be determined. In the latter case,
generate an appropriate condition.
*/
static const char* BinaryHeader( void )
{
int i;
for ( i = 0; i < N_BIN_HDR_MAPPING; i++ )
if ( strcmp( args.hw, bin_hdr_mapping[ i ].hw ) == 0 )
return bin_hdr_mapping[ i ].hdr;
ChfCondition X_FUNC_E_NO_BIN_HDR, CHF_ERROR, args.hw ChfEnd;
return ( char* )NULL;
}
/* This function is the continuation of Kget(); it is invoked when the
user interaction with the FSB ends.
*/
static void KgetContinuation( int proceed, char* file_name )
{
/* Check whether continuation should proceed */
if ( !proceed ) {
ChfCondition X_FUNC_W_ABORTED, CHF_WARNING ChfEnd;
ChfSignal();
}
else {
/* Ok to proceed; read:
- target start address from A[A]
- target end address from C[A]
- binary header with BinaryHeader()
*/
int start_addr = R2int( cpu_status.A );
int end_addr = R2int( cpu_status.C );
const char* bin_hdr = BinaryHeader();
debug1( DEBUG_C_X_FUNC, X_FUNC_I_FILE_NAME, file_name );
debug3( DEBUG_C_X_FUNC, X_FUNC_I_KGET, start_addr, end_addr, bin_hdr );
if ( bin_hdr == ( const char* )NULL || ReadObjectFromFile( file_name, bin_hdr, ( Address )start_addr, ( Address )end_addr ) ) {
ChfCondition X_FUNC_W_FAILED, CHF_WARNING ChfEnd;
ChfSignal();
}
}
CpuRunRequest();
}
/* This function is the continuation of Send(); it is invoked when the
user interaction with the FSB ends.
*/
static void SendContinuation( int proceed, char* file_name )
{
if ( !proceed ) {
ChfCondition X_FUNC_W_ABORTED, CHF_WARNING ChfEnd;
ChfSignal();
}
else {
/* Ok to proceed; read:
- source start address from A[A]
- source end address from C[A]
- binary header with BinaryHeader()
*/
int start_addr = R2int( cpu_status.A );
int end_addr = R2int( cpu_status.C );
const char* bin_hdr = BinaryHeader();
debug1( DEBUG_C_X_FUNC, X_FUNC_I_FILE_NAME, file_name );
debug3( DEBUG_C_X_FUNC, X_FUNC_I_SEND, start_addr, end_addr, bin_hdr );
if ( bin_hdr == ( const char* )NULL || WriteObjectToFile( ( Address )start_addr, ( Address )end_addr, bin_hdr, file_name ) ) {
ChfCondition X_FUNC_W_FAILED, CHF_WARNING ChfEnd;
ChfSignal();
}
}
CpuRunRequest();
}
/* This function does the setup of a transfer, performing the following
actions:
- If CPU halt requests are not allowed, it signals X_FUNC_E_NO_HALT
- Gets the FSB title from 'msg' and 'def_msg', using ChfGetMessage()
- Gets the default file name using NameFromD1()
- Invokes ActivateFSB() to pop the FSB up; continuation 'cont' will
be invoked when the user interaction ends
- Halts the CPU
*/
static void SetupXfer( int msg, const char* def_msg, FsbContinuation cont )
{
debug1( DEBUG_C_TRACE, X_FUNC_I_CALLED, "SetupXfer" );
if ( CpuHaltAllowed() ) {
char* fsb_title = XtNewString( ChfGetMessage( CHF_MODULE_ID, msg, def_msg ) );
char* fsb_file = NameFromD1();
ActivateFSB( fsb_title, fsb_file, cont );
/* Free *before* CpuHaltRequest() because it does not return, and
ActivateFSB() copied its argument when necessary.
*/
XtFree( fsb_title );
XtFree( fsb_file );
( void )CpuHaltRequest();
}
else {
ChfCondition X_FUNC_E_NO_HALT, CHF_ERROR ChfEnd;
ChfSignal();
}
}
/* This is the emulator's extended function for 'kget': this function
transfers a file from disk into the calculator's memory.
*/
static void Kget( Nibble function_code )
{
debug1( DEBUG_C_TRACE, X_FUNC_I_CALLED, "Kget" );
/* Setup File Selection Box if transfers are *not* in batch mode */
if ( !args.batchXfer )
SetupXfer( X_FUNC_M_KGET, "Kget", KgetContinuation );
else {
/* Ok to proceed; read:
- file name from @D1
- target start address from A[A]
- target end address from C[A]
- binary header with BinaryHeader()
*/
char* file_name = NameFromD1();
int start_addr = R2int( cpu_status.A );
int end_addr = R2int( cpu_status.C );
const char* bin_hdr = BinaryHeader();
debug1( DEBUG_C_X_FUNC, X_FUNC_I_FILE_NAME, file_name );
debug3( DEBUG_C_X_FUNC, X_FUNC_I_KGET, start_addr, end_addr, bin_hdr );
if ( bin_hdr == ( const char* )NULL || ReadObjectFromFile( file_name, bin_hdr, ( Address )start_addr, ( Address )end_addr ) ) {
ChfCondition X_FUNC_W_FAILED, CHF_WARNING ChfEnd;
ChfSignal();
}
}
}
/* This is the emulator's extended function for 'send': this function
transfers an object from the calculator's memory into a disk file.
*/
static void Send( Nibble function_code )
{
debug1( DEBUG_C_TRACE, X_FUNC_I_CALLED, "Send" );
/* Setup File Selection Box if transfers are *not* in batch mode */
if ( !args.batchXfer )
SetupXfer( X_FUNC_M_SEND, "Send", SendContinuation );
else {
/* Ok to proceed; read:
- file name from @D1
- source start address from A[A]
- source end address from C[A]
- binary header with BinaryHeader()
*/
char* file_name = NameFromD1();
int start_addr = R2int( cpu_status.A );
int end_addr = R2int( cpu_status.C );
const char* bin_hdr = BinaryHeader();
debug1( DEBUG_C_X_FUNC, X_FUNC_I_FILE_NAME, file_name );
debug3( DEBUG_C_X_FUNC, X_FUNC_I_SEND, start_addr, end_addr, bin_hdr );
if ( bin_hdr == ( const char* )NULL || WriteObjectToFile( ( Address )start_addr, ( Address )end_addr, bin_hdr, file_name ) ) {
ChfCondition X_FUNC_W_FAILED, CHF_WARNING ChfEnd;
ChfSignal();
}
}
}
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------
Dispatch table of emulator's extended functions, indexed by function code;
the function code is propagated to functions in the table.
---------------------------------------------------------------------------*/
typedef void ( *XFunc )( Nibble );
static const XFunc function[] = {
SetSpeed, /* Function code 0 */
Kget, /* 1 */
Send /* 2 */
};
#define N_X_FUNC ( sizeof( function ) / sizeof( function[ 0 ] ) )
/*---------------------------------------------------------------------------
Public functions
---------------------------------------------------------------------------*/
/* .+
.title : ExtendedFunction
.kind : C function
.creation : 3-Nov-2000
.description :
This function executes the emulator's extended function identified
by 'function_code'; communications with the triggering code on
the calculator side are made through CPU registers.
.call :
ExtendedFunction(function_code);
.input :
Nibble function_code, function code
.output :
void
.status_codes :
X_FUNC_I_CALLED
X_FUNC_I_CODE
X_FUNC_W_BAD_CODE
* Any other condition code generated by action functions
.notes :
3.13, 3-Nov-2000, creation
.- */
void ExtendedFunction( Nibble function_code )
{
debug1( DEBUG_C_TRACE, X_FUNC_I_CALLED, "ExtendedFunction" );
debug1( DEBUG_C_X_FUNC, X_FUNC_I_CODE, function_code );
/* Some sanity checks, first */
if ( function_code < 0 || function_code >= N_X_FUNC || function[ ( int )function_code ] == ( XFunc )NULL ) {
ChfCondition X_FUNC_W_BAD_CODE, CHF_WARNING, function_code ChfEnd;
ChfSignal();
}
/* Dispatch */
else
function[ ( int )function_code ]( function_code );
}