456 lines
15 KiB
C
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 );
|
|
}
|