2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
/* -------------------------------------------------------------------------
|
|
|
|
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: cpu.c,v 4.1 2000/12/11 09:54:19 cibrario Rel $
|
|
|
|
.context : SATURN, Saturn CPU / HP48 emulator
|
|
|
|
.title : $RCSfile: cpu.c,v $
|
|
|
|
.kind : C source
|
|
|
|
.author : Ivan Cibrario B.
|
|
|
|
.site : CSTV-CNR
|
|
|
|
.creation : 2-Feb-1998
|
|
|
|
.keywords : *
|
|
|
|
.description :
|
|
|
|
This file executes the Saturn CPU opcodes. 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
|
|
|
|
|
|
|
|
.notes :
|
|
|
|
$Log: cpu.c,v $
|
|
|
|
Revision 4.1 2000/12/11 09:54:19 cibrario
|
|
|
|
Public release.
|
|
|
|
|
|
|
|
Revision 3.14 2000/11/13 10:30:04 cibrario
|
|
|
|
Implemented fast load/save; improved keyboard interface emulation at
|
|
|
|
high emulated CPU speed:
|
|
|
|
|
|
|
|
- Added a delay loop in ExecIN(), when CPU_SLOW_IN is defined. the loop
|
|
|
|
is implemented executing the same instruction multiple times and is
|
|
|
|
needed because the HP firmware uses an active loop instead of a
|
|
|
|
timer to determine the keyboard automatic repeat rate.
|
|
|
|
|
|
|
|
- Changed initial value of cpu_status.inner_loop_max after a CPU reset,
|
|
|
|
to be as documented (that is, maximum speed).
|
|
|
|
|
|
|
|
- During CPU initialization, both shutdn and halt flags are now
|
|
|
|
resetted.
|
|
|
|
|
|
|
|
Revision 3.13 2000/11/09 11:23:12 cibrario
|
|
|
|
Revised to add file selection box GUI element, CPU halt/run
|
|
|
|
requests and emulator's extended functions:
|
|
|
|
|
|
|
|
- Implemented CpuHaltRequest(), CpuRunRequest(), CpuHaltAllowed()
|
|
|
|
|
|
|
|
Revision 3.10 2000/10/24 16:14:28 cibrario
|
|
|
|
Added/Replaced GPL header
|
|
|
|
|
|
|
|
Revision 3.5 2000/10/02 09:42:09 cibrario
|
|
|
|
Linux support:
|
|
|
|
- gcc does not like array subscripts with type 'char', and it is right.
|
|
|
|
|
|
|
|
Revision 3.1 2000/09/20 13:39:18 cibrario
|
|
|
|
Minor updates and fixes to avoid gcc compiler warnings on Solaris
|
|
|
|
when -ansi -pedantic -Wall options are selected.
|
|
|
|
|
|
|
|
* Revision 1.2 2000/09/07 14:31:34 cibrario
|
|
|
|
* Bug fix: cpu_status.return_sp and .reset_req were not reset; this gave
|
|
|
|
* troubles when attempting to override a corrupt status with CpuReset().
|
|
|
|
*
|
|
|
|
* Revision 1.1 1998/02/17 15:25:16 cibrario
|
|
|
|
* Initial revision
|
|
|
|
*
|
|
|
|
|
|
|
|
.- */
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
static char rcs_id[] = "$Id: cpu.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 "config.h"
|
|
|
|
#include "machdep.h"
|
|
|
|
#include "cpu.h"
|
|
|
|
#include "modules.h"
|
|
|
|
#include "keyb.h"
|
2024-03-26 13:36:50 +01:00
|
|
|
#include "disk_io.h" /* 3.1: ReadStructFromFile/WriteStructToFile */
|
2022-03-21 11:05:59 +01:00
|
|
|
#include "args.h"
|
|
|
|
#include "debug.h"
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
#define CHF_MODULE_ID CPU_CHF_MODULE_ID
|
2022-03-21 11:05:59 +01:00
|
|
|
#include <Chf.h>
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
#define GetNibble FetchNibble
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Global variables
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
struct CpuStatus cpu_status;
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Private variables
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* Field selector indexes, lo/hi nibble.
|
|
|
|
NOTE: The P and WP elements of the array must be dynamically adjusted
|
2024-03-26 13:36:50 +01:00
|
|
|
since they depend on the current value of the P CPU register
|
2022-03-21 11:05:59 +01:00
|
|
|
*/
|
2024-03-26 13:36:50 +01:00
|
|
|
static const int fs_idx_lo[ N_FS ] =
|
|
|
|
/* P, WP, XS, X, S, M, B, W
|
|
|
|
??, ??, ??, ??, ??, ??, ??, A
|
|
|
|
*/
|
|
|
|
{ 0, 0, 2, 0, 15, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
static const int fs_idx_hi[ N_FS ] =
|
|
|
|
/* P, WP, XS, X, S, M, B, W
|
|
|
|
??, ??, ??, ??, ??, ??, ??, A
|
|
|
|
*/
|
|
|
|
{ 0, 0, 2, 2, 15, 14, 1, 15, 0, 0, 0, 0, 0, 0, 0, 4 };
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
/* Register Pair pointers */
|
2024-03-26 13:36:50 +01:00
|
|
|
static Nibble* const reg_pair_0[] =
|
|
|
|
/* AB, BC, CA, DC */
|
|
|
|
{ cpu_status.A, cpu_status.B, cpu_status.C, cpu_status.D };
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
static Nibble* const reg_pair_1[] =
|
|
|
|
/* AB, BC, CA, DC */
|
|
|
|
{ cpu_status.B, cpu_status.C, cpu_status.A, cpu_status.C };
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
/* Nibble bit masks */
|
2024-03-26 13:36:50 +01:00
|
|
|
static const Nibble nibble_bit_mask[] = { 0x1, 0x2, 0x4, 0x8 };
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
/* ProgramStatusRegister bit masks */
|
2024-03-26 13:36:50 +01:00
|
|
|
static const ProgramStatusRegister st_bit_mask[] = { 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
|
|
|
|
0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000 };
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
/* Decimal sum/carry tables, range 0..31 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static const int dec_sum[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1 };
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
static const int dec_carry[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
/* Decimal sub/borrow tables, range -10..15 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static const int dec_sub_t[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5 };
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
static const int dec_borrow_t[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
static const int* const dec_sub = dec_sub_t + 10;
|
|
|
|
static const int* const dec_borrow = dec_borrow_t + 10;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
/* Decimal one's complement table */
|
2024-03-26 13:36:50 +01:00
|
|
|
static const int dec_one_c[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0 };
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Private functions: return stack handling
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* PushRSTK */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void PushRSTK( const Address r )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "PushRSTK" );
|
|
|
|
cpu_status.return_stack[ cpu_status.return_sp ] = r;
|
|
|
|
cpu_status.return_sp = ( cpu_status.return_sp + 1 ) & RETURN_SP_MASK;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* PopRSTK */
|
2024-03-26 13:36:50 +01:00
|
|
|
static Address PopRSTK( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
Address r;
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "PopRSTK" );
|
|
|
|
cpu_status.return_sp = ( cpu_status.return_sp - 1 ) & RETURN_SP_MASK;
|
|
|
|
r = cpu_status.return_stack[ cpu_status.return_sp ];
|
|
|
|
cpu_status.return_stack[ cpu_status.return_sp ] = ( Address )0;
|
|
|
|
return r;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Private functions: interrupt handling
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* RTI */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecRTI( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE | DEBUG_C_INT, CPU_I_CALLED, "ExecRTI" );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
if ( cpu_status.int_pending != INT_REQUEST_NONE ) {
|
|
|
|
debug1( DEBUG_C_INT, CPU_I_RTI_LOOP, ( cpu_status.int_pending == INT_REQUEST_NMI ? "NMI" : "IRQ" ) );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Service immediately any pending interrupt request */
|
|
|
|
cpu_status.int_service = 1;
|
|
|
|
cpu_status.int_pending = INT_REQUEST_NONE;
|
|
|
|
cpu_status.PC = INT_HANDLER_PC;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
else {
|
|
|
|
/* Reenable interrupts and return */
|
|
|
|
debug0( DEBUG_C_INT, CPU_I_RTI_END );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.int_service = 0;
|
|
|
|
cpu_status.PC = PopRSTK();
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* RSI */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecRSI( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE | DEBUG_C_INT, CPU_I_CALLED, "ExecRSI" );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Discard last nibble of RSI opcode */
|
|
|
|
cpu_status.PC++;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
KeybRSI();
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* INTON */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecINTON( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE | DEBUG_C_INT, CPU_I_CALLED, "ExecINTON" );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Enable maskable interrupts */
|
|
|
|
cpu_status.int_enable = 1;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* INTOFF */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecINTOFF( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE | DEBUG_C_INT, CPU_I_CALLED, "ExecINTOFF" );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.int_enable = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Private functions: bus input/output
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* BUSCB */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecBUSCB( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecBUSCB" );
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_WARNING, "BUSCB" ChfEnd;
|
|
|
|
ChfSignal();
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* BUSCC */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecBUSCC( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecBUSCC" );
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_WARNING, "BUSCC" ChfEnd;
|
|
|
|
ChfSignal();
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* BUSCD */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecBUSCD( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecBUSCD" );
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_WARNING, "BUSCD" ChfEnd;
|
|
|
|
ChfSignal();
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* SREQ */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecSREQ( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecSREQ" );
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_WARNING, "SREQ" ChfEnd;
|
|
|
|
ChfSignal();
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* OUTC */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecOUTC( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecOUTC" );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.OUT = ( ( OutputRegister )cpu_status.C[ 0 ] ) | ( ( OutputRegister )cpu_status.C[ 1 ] << 4 ) |
|
|
|
|
( ( OutputRegister )cpu_status.C[ 2 ] << 8 );
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* OUTCS */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecOUTCS( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecOUTCS" );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.OUT = ( ( OutputRegister )cpu_status.C[ 0 ] ) | ( cpu_status.OUT & 0xFF0 );
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* IN */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecIN( Nibble* r )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
/* In */
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecIN" );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
#ifdef CPU_SLOW_IN
|
2024-03-26 13:36:50 +01:00
|
|
|
/* We must slow the A=IN and C=IN instruction down a bit, depending
|
|
|
|
on the emulated CPU speed. This is necessary because the HP firmware
|
|
|
|
uses an active loop instead of a timer to determine the keyboard
|
|
|
|
automatic repeat rate.
|
|
|
|
|
|
|
|
Since implementing a precise, tiny (~ 1 microsecond), passive delay
|
|
|
|
in unix is almost impossible, we chose to execute the same
|
|
|
|
instruction (A=IN or C=IN) multiple times by artificially resetting
|
|
|
|
the PC as appropriate.
|
|
|
|
|
|
|
|
The number of repetions depends linearly, with gain CPU_SLOW_IN,
|
|
|
|
from the current value of cpu_status.inner_loop:
|
|
|
|
cpu_status.inner_loop==INNER_LOOP_MAX corresponds to the nominal
|
|
|
|
CPU speed of 4MHz and to a repetition rate of 1 (instructions are
|
|
|
|
executed once as usual).
|
2022-03-21 11:05:59 +01:00
|
|
|
*/
|
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
static int count_down = 0;
|
|
|
|
|
|
|
|
/* Decrement counter; set PC back and return immediately if counter
|
|
|
|
was not zero (counter not expired yet).
|
|
|
|
*/
|
|
|
|
if ( count_down-- != 0 ) {
|
|
|
|
cpu_status.PC -= 3;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Counter expired; reset counter and execute the instruction */
|
|
|
|
count_down = ( ( cpu_status.inner_loop + ( INNER_LOOP_MAX / 2 ) ) / INNER_LOOP_MAX ) * CPU_SLOW_IN;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
#endif
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.IN = KeybIN( cpu_status.OUT );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
r[ 0 ] = ( Nibble )( cpu_status.IN & NIBBLE_MASK );
|
|
|
|
r[ 1 ] = ( Nibble )( ( cpu_status.IN ) >> 4 & NIBBLE_MASK );
|
|
|
|
r[ 2 ] = ( Nibble )( ( cpu_status.IN ) >> 8 & NIBBLE_MASK );
|
|
|
|
r[ 3 ] = ( Nibble )( ( cpu_status.IN ) >> 12 & NIBBLE_MASK );
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Private functions: CPU control
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecSHUTDN( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "SHUTDN" );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
#ifdef CPU_SPIN_SHUTDN
|
2024-03-26 13:36:50 +01:00
|
|
|
/* If the CPU_SPIN_SHUTDN symbol is defined, the CPU module implements
|
|
|
|
SHUTDN as a spin loop; the program counter is reset to the starting
|
|
|
|
nibble of the SHUTDN opcode.
|
|
|
|
*/
|
|
|
|
cpu_status.PC -= 3;
|
2022-03-21 11:05:59 +01:00
|
|
|
#endif
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Set shutdown flag */
|
|
|
|
cpu_status.shutdn = 1;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
#ifndef CPU_SPIN_SHUTDN
|
2024-03-26 13:36:50 +01:00
|
|
|
/* If the CPU_SPIN_SHUTDN symbol is not defined, the CPU module implements
|
|
|
|
SHUTDN signalling the condition CPU_I_SHUTDN
|
|
|
|
*/
|
|
|
|
ChfCondition CPU_I_SHUTDN, CHF_INFO ChfEnd;
|
|
|
|
ChfSignal();
|
2022-03-21 11:05:59 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Private functions: data type conversions
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* Copies the A field of a DataRegister into an Address; this is not a
|
|
|
|
loop to achieve greater execution speed.
|
|
|
|
*/
|
2024-03-26 13:36:50 +01:00
|
|
|
static Address R2Addr( const Nibble* r )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "R2Addr" );
|
|
|
|
return ( ( ( Address )r[ 0 ] ) | ( ( Address )r[ 1 ] << 4 ) | ( ( Address )r[ 2 ] << 8 ) | ( ( Address )r[ 3 ] << 12 ) |
|
|
|
|
( ( Address )r[ 4 ] << 16 ) );
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns the nibs 0-3 of a DataRegister into an Address; this is not a
|
|
|
|
loop to achieve greater execution speed.
|
|
|
|
*/
|
2024-03-26 13:36:50 +01:00
|
|
|
static Address R2AddrS( const Nibble* r )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "R2AddrS" );
|
|
|
|
return ( ( ( Address )r[ 0 ] ) | ( ( Address )r[ 1 ] << 4 ) | ( ( Address )r[ 2 ] << 8 ) | ( ( Address )r[ 3 ] << 12 ) );
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Copies an Address into the A field of a register; this is not a loop
|
|
|
|
to achieve grater execution speed
|
|
|
|
*/
|
2024-03-26 13:36:50 +01:00
|
|
|
static void Addr2R( Nibble* d, Address a )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "Addr2R" );
|
|
|
|
d[ 0 ] = ( Nibble )( a & NIBBLE_MASK );
|
|
|
|
a >>= 4;
|
|
|
|
d[ 1 ] = ( Nibble )( a & NIBBLE_MASK );
|
|
|
|
a >>= 4;
|
|
|
|
d[ 2 ] = ( Nibble )( a & NIBBLE_MASK );
|
|
|
|
a >>= 4;
|
|
|
|
d[ 3 ] = ( Nibble )( a & NIBBLE_MASK );
|
|
|
|
a >>= 4;
|
|
|
|
d[ 4 ] = ( Nibble )( a & NIBBLE_MASK );
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Copies an Address into nibs 0-3 of a register; this is not a loop
|
|
|
|
to achieve grater execution speed
|
|
|
|
*/
|
2024-03-26 13:36:50 +01:00
|
|
|
static void Addr2RS( Nibble* d, Address a )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "Addr2RS" );
|
|
|
|
d[ 0 ] = ( Nibble )( a & NIBBLE_MASK );
|
|
|
|
a >>= 4;
|
|
|
|
d[ 1 ] = ( Nibble )( a & NIBBLE_MASK );
|
|
|
|
a >>= 4;
|
|
|
|
d[ 2 ] = ( Nibble )( a & NIBBLE_MASK );
|
|
|
|
a >>= 4;
|
|
|
|
d[ 3 ] = ( Nibble )( a & NIBBLE_MASK );
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy the 12 low-order bits of ST into C */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void St2C( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "St2C" );
|
|
|
|
cpu_status.C[ 0 ] = ( Nibble )( cpu_status.ST & NIBBLE_MASK );
|
|
|
|
cpu_status.C[ 1 ] = ( Nibble )( ( cpu_status.ST >> 4 ) & NIBBLE_MASK );
|
|
|
|
cpu_status.C[ 2 ] = ( Nibble )( ( cpu_status.ST >> 8 ) & NIBBLE_MASK );
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy the 12 low-order bits of C into ST */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void C2St( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "C2St" );
|
|
|
|
cpu_status.ST = ( ProgramStatusRegister )cpu_status.C[ 0 ] | ( ( ProgramStatusRegister )cpu_status.C[ 1 ] << 4 ) |
|
|
|
|
( ( ProgramStatusRegister )cpu_status.C[ 2 ] << 8 ) | ( cpu_status.ST & CLRST_MASK );
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Exchange the 12 low-order bits of C with the 12 low-order bits of ST */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void CStExch( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
ProgramStatusRegister tst = cpu_status.ST;
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "CStExch" );
|
|
|
|
cpu_status.ST = ( ProgramStatusRegister )cpu_status.C[ 0 ] | ( ( ProgramStatusRegister )cpu_status.C[ 1 ] << 4 ) |
|
|
|
|
( ( ProgramStatusRegister )cpu_status.C[ 2 ] << 8 ) | ( cpu_status.ST & CLRST_MASK );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.C[ 0 ] = ( Nibble )( tst & NIBBLE_MASK );
|
|
|
|
cpu_status.C[ 1 ] = ( Nibble )( ( tst >> 4 ) & NIBBLE_MASK );
|
|
|
|
cpu_status.C[ 2 ] = ( Nibble )( ( tst >> 8 ) & NIBBLE_MASK );
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Private functions: data memory read/write
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* Read a field of a DataRegister from memory */
|
2024-03-26 13:36:50 +01:00
|
|
|
void ReadDAT( Nibble* d, Address s, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
for ( n = lo; n <= hi; n++ )
|
|
|
|
d[ n ] = ReadNibble( s++ );
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Read a field of a DataRegister from memory, with immediate fs */
|
2024-03-26 13:36:50 +01:00
|
|
|
void ReadDATImm( Nibble* d, Address s, int imm_fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int n;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
for ( n = 0; n <= imm_fs; n++ )
|
|
|
|
d[ n ] = ReadNibble( s++ );
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Write a field of a DataRegister into memory */
|
2024-03-26 13:36:50 +01:00
|
|
|
void WriteDAT( Address d, const Nibble* r, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
for ( n = lo; n <= hi; n++ )
|
|
|
|
WriteNibble( d++, r[ n ] );
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Write a field of a DataRegister into memory, with immediate fs */
|
2024-03-26 13:36:50 +01:00
|
|
|
void WriteDATImm( Address d, const Nibble* r, int imm_fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int n;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
for ( n = 0; n <= imm_fs; n++ )
|
|
|
|
WriteNibble( d++, r[ n ] );
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Private functions: instruction fetch/immediate register load
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* Read two nibbles in two-complement form, starting from pc */
|
2024-03-26 13:36:50 +01:00
|
|
|
static Address Get2Nibbles2C( Address pc )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
Address v = ( Address )GetNibble( pc ) | ( ( Address )GetNibble( pc + 1 ) << 4 );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
return ( v & 0x80 ) ? v - 0x100 : v;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Read three nibbles in two-complement form, starting from pc */
|
2024-03-26 13:36:50 +01:00
|
|
|
static Address Get3Nibbles2C( Address pc )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
Address v = ( Address )GetNibble( pc ) | ( ( Address )GetNibble( pc + 1 ) << 4 ) | ( ( Address )GetNibble( pc + 2 ) << 8 );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
return ( v & 0x800 ) ? v - 0x1000 : v;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Read four nibbles in two-complement form, starting from pc */
|
2024-03-26 13:36:50 +01:00
|
|
|
static Address Get4Nibbles2C( Address pc )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
Address v = ( Address )GetNibble( pc ) | ( ( Address )GetNibble( pc + 1 ) << 4 ) | ( ( Address )GetNibble( pc + 2 ) << 8 ) |
|
|
|
|
( ( Address )GetNibble( pc + 3 ) << 12 );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
return ( v & 0x8000 ) ? v - 0x10000 : v;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Read four nibbles in absolute form, starting from pc */
|
2024-03-26 13:36:50 +01:00
|
|
|
static Address Get5NibblesAbs( Address pc )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
Address v = ( Address )GetNibble( pc ) | ( ( Address )GetNibble( pc + 1 ) << 4 ) | ( ( Address )GetNibble( pc + 2 ) << 8 ) |
|
|
|
|
( ( Address )GetNibble( pc + 3 ) << 12 ) | ( ( Address )GetNibble( pc + 4 ) << 16 );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
return v;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Fetch the lower 'n' nibbles of the D register pointed by 'd' from the
|
|
|
|
current instruction body
|
|
|
|
*/
|
2024-03-26 13:36:50 +01:00
|
|
|
void FetchD( Address* d, register int n )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register Address mask = ADDRESS_MASK;
|
|
|
|
register Address v = 0x00000;
|
|
|
|
register int shift = 0;
|
|
|
|
register int i;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
for ( i = 0; i < n; i++ ) {
|
|
|
|
v |= ( ( Address )GetNibble( cpu_status.PC++ ) << shift );
|
|
|
|
mask <<= 4;
|
|
|
|
shift += 4;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
*d = ( *d & mask ) | v;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
2024-03-26 13:36:50 +01:00
|
|
|
|
2022-03-21 11:05:59 +01:00
|
|
|
/* Fetch 'n'+1 nibbles of the DataRegister r from the current instruction body,
|
|
|
|
starting from the nibble pointed by the P register.
|
|
|
|
*/
|
2024-03-26 13:36:50 +01:00
|
|
|
void FetchR( Nibble* r, register int n )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int p = ( int )cpu_status.P;
|
|
|
|
register int i;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
for ( i = 0; i <= n; i++ ) {
|
|
|
|
r[ p++ ] = GetNibble( cpu_status.PC++ );
|
|
|
|
if ( p >= NIBBLE_PER_REGISTER )
|
|
|
|
p = 0;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Private functions: P register setting
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
2024-03-26 13:36:50 +01:00
|
|
|
void SetP( Nibble n )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.P = n;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.fs_idx_lo[ FS_P ] = n;
|
|
|
|
cpu_status.fs_idx_hi[ FS_P ] = n;
|
|
|
|
cpu_status.fs_idx_hi[ FS_WP ] = n;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Private functions: DataRegister tests
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* ?r=s */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void TestRREq( int rp, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register const Nibble* const r = reg_pair_0[ rp ];
|
|
|
|
register const Nibble* const s = reg_pair_1[ rp ];
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "TestRREq" );
|
|
|
|
for ( n = lo; n <= hi; n++ )
|
|
|
|
if ( r[ n ] != s[ n ] ) {
|
|
|
|
cpu_status.carry = 0;
|
|
|
|
return;
|
|
|
|
};
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.carry = 1;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ?r=0 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void TestRZ( int rp, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register const Nibble* const r = reg_pair_0[ rp ];
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "TestRZ" );
|
|
|
|
for ( n = lo; n <= hi; n++ )
|
|
|
|
if ( r[ n ] != ( Nibble )0 ) {
|
|
|
|
cpu_status.carry = 0;
|
|
|
|
return;
|
|
|
|
};
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.carry = 1;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ?r#s */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void TestRRNe( int rp, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register const Nibble* const r = reg_pair_0[ rp ];
|
|
|
|
register const Nibble* const s = reg_pair_1[ rp ];
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "TestRRNe" );
|
|
|
|
for ( n = lo; n <= hi; n++ )
|
|
|
|
if ( r[ n ] != s[ n ] ) {
|
|
|
|
cpu_status.carry = 1;
|
|
|
|
return;
|
|
|
|
};
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.carry = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ?r#0 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void TestRNZ( int rp, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register const Nibble* const r = reg_pair_0[ rp ];
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "TestRNZ" );
|
|
|
|
for ( n = lo; n <= hi; n++ )
|
|
|
|
if ( r[ n ] != ( Nibble )0 ) {
|
|
|
|
cpu_status.carry = 1;
|
|
|
|
return;
|
|
|
|
};
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.carry = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ?r>s */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void TestRRGt( int rp, int fs )
|
|
|
|
{
|
|
|
|
register const Nibble* const r = reg_pair_0[ rp ];
|
|
|
|
register const Nibble* const s = reg_pair_1[ rp ];
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "TestRRGt" );
|
|
|
|
for ( n = hi; n >= lo; n-- ) {
|
|
|
|
if ( r[ n ] > s[ n ] ) {
|
|
|
|
cpu_status.carry = 1;
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
if ( r[ n ] < s[ n ] ) {
|
|
|
|
cpu_status.carry = 0;
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.carry = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ?r>=s */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void TestRRGe( int rp, int fs )
|
|
|
|
{
|
|
|
|
register const Nibble* const r = reg_pair_0[ rp ];
|
|
|
|
register const Nibble* const s = reg_pair_1[ rp ];
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "TestRRGe" );
|
|
|
|
for ( n = hi; n >= lo; n-- ) {
|
|
|
|
if ( r[ n ] > s[ n ] ) {
|
|
|
|
cpu_status.carry = 1;
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
if ( r[ n ] < s[ n ] ) {
|
|
|
|
cpu_status.carry = 0;
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.carry = 1;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ?r<s */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void TestRRLt( int rp, int fs )
|
|
|
|
{
|
|
|
|
register const Nibble* const r = reg_pair_0[ rp ];
|
|
|
|
register const Nibble* const s = reg_pair_1[ rp ];
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "TestRRLt" );
|
|
|
|
for ( n = hi; n >= lo; n-- ) {
|
|
|
|
if ( r[ n ] < s[ n ] ) {
|
|
|
|
cpu_status.carry = 1;
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
if ( r[ n ] > s[ n ] ) {
|
|
|
|
cpu_status.carry = 0;
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.carry = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ?r<=s */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void TestRRLe( int rp, int fs )
|
|
|
|
{
|
|
|
|
register const Nibble* const r = reg_pair_0[ rp ];
|
|
|
|
register const Nibble* const s = reg_pair_1[ rp ];
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "TestRRLe" );
|
|
|
|
for ( n = hi; n >= lo; n-- ) {
|
|
|
|
if ( r[ n ] < s[ n ] ) {
|
|
|
|
cpu_status.carry = 1;
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
if ( r[ n ] > s[ n ] ) {
|
|
|
|
cpu_status.carry = 0;
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.carry = 1;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Private functions: DataRegister operations
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* r=r+r */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void AddRR( register Nibble* d, register const Nibble* a, register const Nibble* b, int fs )
|
|
|
|
{
|
|
|
|
register int carry;
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
register int s;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "AddRR" );
|
|
|
|
carry = 0;
|
|
|
|
|
|
|
|
if ( cpu_status.hexmode ) {
|
|
|
|
for ( n = lo; n <= hi; n++ ) {
|
|
|
|
s = a[ n ] + b[ n ] + carry;
|
|
|
|
|
|
|
|
d[ n ] = ( Nibble )( s & NIBBLE_MASK );
|
|
|
|
carry = ( ( s & ~NIBBLE_MASK ) != 0 );
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
else {
|
|
|
|
for ( n = lo; n <= hi; n++ ) {
|
|
|
|
s = a[ n ] + b[ n ] + carry;
|
|
|
|
d[ n ] = dec_sum[ s ];
|
|
|
|
carry = dec_carry[ s ];
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.carry = carry;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* r=r+1 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void IncrR( register Nibble* d, int fs )
|
|
|
|
{
|
|
|
|
register int carry;
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
register int s;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "IncrR" );
|
|
|
|
carry = 1;
|
|
|
|
|
|
|
|
if ( cpu_status.hexmode ) {
|
|
|
|
for ( n = lo; n <= hi; n++ ) {
|
|
|
|
s = d[ n ] + carry;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
d[ n ] = ( Nibble )( s & NIBBLE_MASK );
|
|
|
|
carry = ( ( s & ~NIBBLE_MASK ) != 0 );
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
else {
|
|
|
|
for ( n = lo; n <= hi; n++ ) {
|
|
|
|
s = d[ n ] + carry;
|
|
|
|
d[ n ] = dec_sum[ s ];
|
|
|
|
carry = dec_carry[ s ];
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.carry = carry;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* r=r-r */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void SubRR( register Nibble* d, register Nibble* a, register Nibble* b, int fs )
|
|
|
|
{
|
|
|
|
register int carry;
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
register int s;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "SubRR" );
|
|
|
|
carry = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
if ( cpu_status.hexmode ) {
|
|
|
|
for ( n = lo; n <= hi; n++ ) {
|
|
|
|
s = a[ n ] - b[ n ] - carry;
|
|
|
|
|
|
|
|
d[ n ] = ( Nibble )( s & NIBBLE_MASK );
|
|
|
|
carry = ( ( s & ~NIBBLE_MASK ) != 0 );
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
else {
|
|
|
|
for ( n = lo; n <= hi; n++ ) {
|
|
|
|
s = a[ n ] - b[ n ] - carry;
|
|
|
|
d[ n ] = dec_sub[ s ];
|
|
|
|
carry = dec_borrow[ s ];
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.carry = carry;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* r=r-1 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void DecrR( register Nibble* d, int fs )
|
|
|
|
{
|
|
|
|
register int carry;
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
register int s;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "DecrR" );
|
|
|
|
carry = 1;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
if ( cpu_status.hexmode ) {
|
|
|
|
for ( n = lo; n <= hi; n++ ) {
|
|
|
|
s = d[ n ] - carry;
|
|
|
|
|
|
|
|
d[ n ] = ( Nibble )( s & NIBBLE_MASK );
|
|
|
|
carry = ( ( s & ~NIBBLE_MASK ) != 0 );
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
else {
|
|
|
|
for ( n = lo; n <= hi; n++ ) {
|
|
|
|
s = d[ n ] - carry;
|
|
|
|
d[ n ] = dec_sub[ s ];
|
|
|
|
carry = dec_borrow[ s ];
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.carry = carry;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* r=0 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ClearR( register Nibble* d, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ClearR" );
|
|
|
|
for ( n = lo; n <= hi; n++ ) {
|
|
|
|
d[ n ] = ( Nibble )0;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* r=r */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void CopyRR( register Nibble* d, register Nibble* s, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "CopyRR" );
|
|
|
|
for ( n = lo; n <= hi; n++ ) {
|
|
|
|
d[ n ] = s[ n ];
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* rrEX */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExchRR( register Nibble* d, register Nibble* s, int fs )
|
|
|
|
{
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register Nibble t;
|
|
|
|
register int n;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExchRR" );
|
|
|
|
for ( n = lo; n <= hi; n++ ) {
|
|
|
|
t = d[ n ];
|
|
|
|
d[ n ] = s[ n ];
|
|
|
|
s[ n ] = t;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* rSL */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ShiftLeftR( register Nibble* d, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ShiftLeftR" );
|
|
|
|
for ( n = hi; n > lo; n-- )
|
|
|
|
d[ n ] = d[ n - 1 ];
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
d[ lo ] = ( Nibble )0;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* rSR */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ShiftRightR( register Nibble* d, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ShiftRightR" );
|
|
|
|
if ( d[ lo ] != ( Nibble )0 )
|
|
|
|
cpu_status.HST |= HST_SB_MASK;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
for ( n = lo; n < hi; n++ )
|
|
|
|
d[ n ] = d[ n + 1 ];
|
|
|
|
|
|
|
|
d[ hi ] = ( Nibble )0;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* rSRB */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ShiftRightBitR( register Nibble* d, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ShiftRightBitR" );
|
|
|
|
if ( ( d[ lo ] & nibble_bit_mask[ 0 ] ) != ( Nibble )0 )
|
|
|
|
cpu_status.HST |= HST_SB_MASK;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
for ( n = lo; n < hi; n++ ) {
|
|
|
|
d[ n ] >>= 1;
|
|
|
|
d[ n ] |= ( ( d[ n + 1 ] & nibble_bit_mask[ 0 ] ) ? nibble_bit_mask[ 3 ] : 0 );
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
d[ hi ] >>= 1;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* rSLC */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ShiftLeftCircR( register Nibble* d, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register Nibble s;
|
|
|
|
register int n;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ShiftLeftCircR" );
|
|
|
|
s = d[ hi ];
|
|
|
|
for ( n = hi; n > lo; n-- )
|
|
|
|
d[ n ] = d[ n - 1 ];
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
d[ lo ] = s;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* rSRC */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ShiftRightCircR( register Nibble* d, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register Nibble s;
|
|
|
|
register int n;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ShiftRightCircR" );
|
|
|
|
if ( ( s = d[ lo ] ) != ( Nibble )0 )
|
|
|
|
cpu_status.HST |= HST_SB_MASK;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
for ( n = lo; n < hi; n++ )
|
|
|
|
d[ n ] = d[ n + 1 ];
|
|
|
|
|
|
|
|
d[ hi ] = s;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* r=-r */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void TwoComplR( register Nibble* d, int fs )
|
|
|
|
{
|
|
|
|
register int carry;
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
register int s;
|
|
|
|
register int nz;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "TwoComplR" );
|
|
|
|
carry = 0;
|
|
|
|
nz = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
if ( cpu_status.hexmode ) {
|
|
|
|
for ( n = lo; n <= hi; n++ ) {
|
|
|
|
s = -d[ n ] - carry;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
d[ n ] = ( Nibble )( s & NIBBLE_MASK );
|
|
|
|
carry = ( ( s & ~NIBBLE_MASK ) != 0 );
|
|
|
|
|
|
|
|
nz = nz || ( d[ n ] != ( Nibble )0 );
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
else {
|
|
|
|
for ( n = lo; n <= hi; n++ ) {
|
|
|
|
s = -d[ n ] - carry;
|
|
|
|
d[ n ] = dec_sub[ s ];
|
|
|
|
carry = dec_borrow[ s ];
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
nz = nz || ( d[ n ] != ( Nibble )0 );
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.carry = nz;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* r=-r-1 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void OneComplR( register Nibble* d, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "OneComplR" );
|
|
|
|
if ( cpu_status.hexmode ) {
|
|
|
|
for ( n = lo; n <= hi; n++ )
|
|
|
|
d[ n ] = ( 0xF - d[ n ] ) & NIBBLE_MASK;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
else {
|
|
|
|
for ( n = lo; n <= hi; n++ )
|
|
|
|
d[ n ] = dec_one_c[ ( int )d[ n ] ];
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.carry = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* r=r&r */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void AndRR( register Nibble* d, register const Nibble* a, register const Nibble* b, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "AndRR" );
|
|
|
|
for ( n = lo; n <= hi; n++ )
|
|
|
|
d[ n ] = a[ n ] & b[ n ];
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* r=r!r */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void OrRR( register Nibble* d, register const Nibble* a, register const Nibble* b, int fs )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "OrRR" );
|
|
|
|
for ( n = lo; n <= hi; n++ )
|
|
|
|
d[ n ] = a[ n ] | b[ n ];
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add immediate value 'v'+1 to the DataRegister 'r', Field Selector 'fs',
|
|
|
|
always HEX mode
|
|
|
|
*/
|
2024-03-26 13:36:50 +01:00
|
|
|
static void AddRImm( Nibble* r, int fs, Nibble v )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int carry;
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
register int s;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "AddRImm" );
|
|
|
|
carry = ( int )v + 1;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
for ( n = lo; n <= hi; n++ ) {
|
|
|
|
s = r[ n ] + carry;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
r[ n ] = ( Nibble )( s & NIBBLE_MASK );
|
|
|
|
carry = ( ( s & ~NIBBLE_MASK ) != 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
cpu_status.carry = carry;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Subtract immediate value 'v'+1 from the DataRegister 'r',
|
|
|
|
Field Selector 'fs', always HEX mode
|
|
|
|
*/
|
2024-03-26 13:36:50 +01:00
|
|
|
static void SubRImm( register Nibble* r, int fs, Nibble v )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
register int carry;
|
|
|
|
register int lo = cpu_status.fs_idx_lo[ fs ];
|
|
|
|
register int hi = cpu_status.fs_idx_hi[ fs ];
|
|
|
|
register int n;
|
|
|
|
register int s;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "DecrR" );
|
|
|
|
carry = ( int )v + 1;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
for ( n = lo; n <= hi; n++ ) {
|
|
|
|
s = r[ n ] - carry;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
r[ n ] = ( Nibble )( s & NIBBLE_MASK );
|
|
|
|
carry = ( ( s & ~NIBBLE_MASK ) != 0 );
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.carry = carry;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Private functions: DataRegister bit operations
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
2024-03-26 13:36:50 +01:00
|
|
|
void ExecBIT0( Nibble* r, Nibble n ) { r[ n / 4 ] &= ~nibble_bit_mask[ n % 4 ]; }
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
void ExecBIT1( Nibble* r, Nibble n ) { r[ n / 4 ] |= nibble_bit_mask[ n % 4 ]; }
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
void TestBIT0( Nibble* r, Nibble n ) { cpu_status.carry = ( ( r[ n / 4 ] & nibble_bit_mask[ n % 4 ] ) == 0 ); }
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
void TestBIT1( Nibble* r, Nibble n ) { cpu_status.carry = ( ( r[ n / 4 ] & nibble_bit_mask[ n % 4 ] ) != 0 ); }
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Private functions: jumps/subroutine calls
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* GOYES/RTNYES */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecGOYES_RTNYES( void )
|
|
|
|
{
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecGOYES_RTNYES" );
|
|
|
|
if ( cpu_status.carry ) {
|
|
|
|
/* Taken */
|
|
|
|
Address offset = Get2Nibbles2C( cpu_status.PC );
|
|
|
|
|
|
|
|
if ( offset == 0 )
|
|
|
|
/* RTNYES */
|
|
|
|
cpu_status.PC = PopRSTK();
|
|
|
|
else
|
|
|
|
cpu_status.PC += offset;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
else
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Not taken */
|
|
|
|
cpu_status.PC += 2;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Private functions: instruction stream decoding
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* ?..., GOYES/RTNYES, Test with Field Selector, opcode 9ftyy, length 5 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecTest_9( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
Nibble f = GetNibble( cpu_status.PC++ );
|
|
|
|
Nibble t = GetNibble( cpu_status.PC++ );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
int fs = GetFS( f );
|
|
|
|
int tc = GetOC_2( f, t );
|
|
|
|
int rp = GetRP( t );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecTest_9" );
|
|
|
|
/* Decode test code */
|
|
|
|
switch ( tc ) {
|
|
|
|
case 0:
|
|
|
|
TestRREq( rp, fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 1:
|
|
|
|
TestRRNe( rp, fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 2:
|
|
|
|
TestRZ( rp, fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 3:
|
|
|
|
TestRNZ( rp, fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 4:
|
|
|
|
TestRRGt( rp, fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 5:
|
|
|
|
TestRRLt( rp, fs );
|
|
|
|
break;
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 6:
|
|
|
|
TestRRGe( rp, fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 7:
|
|
|
|
TestRRLe( rp, fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
default:
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Test_Code" ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Execute GOYES/RTNYES */
|
|
|
|
ExecGOYES_RTNYES();
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ?..., GOYES/RTNYES, Test on A Fields, opcode 8Atyy, length 5 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecTest_8A( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
Nibble t = GetNibble( cpu_status.PC++ );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
int tc = GetOC_1( t );
|
|
|
|
int rp = GetRP( t );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecTest_8A" );
|
|
|
|
/* Decode test code */
|
|
|
|
switch ( tc ) {
|
|
|
|
case 0:
|
|
|
|
TestRREq( rp, FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 1:
|
|
|
|
TestRRNe( rp, FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 2:
|
|
|
|
TestRZ( rp, FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 3:
|
|
|
|
TestRNZ( rp, FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
default:
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Test_Code" ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Execute GOYES/RTNYES */
|
|
|
|
ExecGOYES_RTNYES();
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ?..., GOYES/RTNYES, Test on A Fields, opcode 8Btyy, length 5 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecTest_8B( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
Nibble t = GetNibble( cpu_status.PC++ );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
int tc = GetOC_1( t );
|
|
|
|
int rp = GetRP( t );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecTest_8B" );
|
|
|
|
/* Decode test code */
|
|
|
|
switch ( tc ) {
|
|
|
|
case 0:
|
|
|
|
TestRRGt( rp, FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 1:
|
|
|
|
TestRRLt( rp, FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 2:
|
|
|
|
TestRRGe( rp, FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 3:
|
|
|
|
TestRRLe( rp, FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
default:
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Test_Code" ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Execute GOYES/RTNYES */
|
|
|
|
ExecGOYES_RTNYES();
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ..., Register Operation with Field Selector, opcode Afo, length 3 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecRegOp_A( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
Nibble f = GetNibble( cpu_status.PC++ );
|
|
|
|
Nibble o = GetNibble( cpu_status.PC++ );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
int fs = GetFS( f );
|
|
|
|
int oc = GetOC_2( f, o );
|
|
|
|
int rp = GetRP( o );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecRegOp_A" );
|
|
|
|
/* Decode operation code */
|
|
|
|
switch ( oc ) {
|
|
|
|
case 0:
|
|
|
|
AddRR( reg_pair_0[ rp ], reg_pair_0[ rp ], reg_pair_1[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 1:
|
|
|
|
AddRR( reg_pair_0[ rp ], reg_pair_0[ rp ], reg_pair_0[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 2:
|
|
|
|
AddRR( reg_pair_1[ rp ], reg_pair_1[ rp ], reg_pair_0[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 3:
|
|
|
|
DecrR( reg_pair_0[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 4:
|
|
|
|
ClearR( reg_pair_0[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 5:
|
|
|
|
CopyRR( reg_pair_0[ rp ], reg_pair_1[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 6:
|
|
|
|
CopyRR( reg_pair_1[ rp ], reg_pair_0[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 7:
|
|
|
|
ExchRR( reg_pair_0[ rp ], reg_pair_1[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
default:
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ..., Register Operation with Field Selector, opcode Bfo, length 3 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecRegOp_B( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
Nibble f = GetNibble( cpu_status.PC++ );
|
|
|
|
Nibble o = GetNibble( cpu_status.PC++ );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
int fs = GetFS( f );
|
|
|
|
int oc = GetOC_2( f, o );
|
|
|
|
int rp = GetRP( o );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecRegOp_B" );
|
|
|
|
/* Decode operation code */
|
|
|
|
switch ( oc ) {
|
|
|
|
case 0:
|
|
|
|
SubRR( reg_pair_0[ rp ], reg_pair_0[ rp ], reg_pair_1[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 1:
|
|
|
|
IncrR( reg_pair_0[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 2:
|
|
|
|
SubRR( reg_pair_1[ rp ], reg_pair_1[ rp ], reg_pair_0[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 3:
|
|
|
|
SubRR( reg_pair_0[ rp ], reg_pair_1[ rp ], reg_pair_0[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 4:
|
|
|
|
ShiftLeftR( reg_pair_0[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 5:
|
|
|
|
ShiftRightR( reg_pair_0[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 6:
|
|
|
|
TwoComplR( reg_pair_0[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 7:
|
|
|
|
OneComplR( reg_pair_0[ rp ], fs );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
default:
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ..., Register Operation on A Fields, opcode Co, length 2 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecRegOp_C( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
Nibble o = GetNibble( cpu_status.PC++ );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
int oc = GetOC_1( o );
|
|
|
|
int rp = GetRP( o );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecRegOp_C" );
|
|
|
|
/* Decode operation code */
|
|
|
|
switch ( oc ) {
|
|
|
|
case 0:
|
|
|
|
AddRR( reg_pair_0[ rp ], reg_pair_0[ rp ], reg_pair_1[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 1:
|
|
|
|
AddRR( reg_pair_0[ rp ], reg_pair_0[ rp ], reg_pair_0[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 2:
|
|
|
|
AddRR( reg_pair_1[ rp ], reg_pair_1[ rp ], reg_pair_0[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 3:
|
|
|
|
DecrR( reg_pair_0[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
default:
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ..., Register Operation on A Fields, opcode Do, length 2 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecRegOp_D( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
Nibble o = GetNibble( cpu_status.PC++ );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
int oc = GetOC_1( o );
|
|
|
|
int rp = GetRP( o );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecRegOp_D" );
|
|
|
|
/* Decode operation code */
|
|
|
|
switch ( oc ) {
|
|
|
|
case 0:
|
|
|
|
ClearR( reg_pair_0[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 1:
|
|
|
|
CopyRR( reg_pair_0[ rp ], reg_pair_1[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 2:
|
|
|
|
CopyRR( reg_pair_1[ rp ], reg_pair_0[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 3:
|
|
|
|
ExchRR( reg_pair_0[ rp ], reg_pair_1[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
default:
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ..., Register Operation on A Fields, opcode Eo, length 2 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecRegOp_E( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
Nibble o = GetNibble( cpu_status.PC++ );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
int oc = GetOC_1( o );
|
|
|
|
int rp = GetRP( o );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecRegOp_E" );
|
|
|
|
/* Decode operation code */
|
|
|
|
switch ( oc ) {
|
|
|
|
case 0:
|
|
|
|
SubRR( reg_pair_0[ rp ], reg_pair_0[ rp ], reg_pair_1[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 1:
|
|
|
|
IncrR( reg_pair_0[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 2:
|
|
|
|
SubRR( reg_pair_1[ rp ], reg_pair_1[ rp ], reg_pair_0[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 3:
|
|
|
|
SubRR( reg_pair_0[ rp ], reg_pair_1[ rp ], reg_pair_0[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
default:
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ..., Register Operation on A Fields, opcode Fo, length 2 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecRegOp_F( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
Nibble o = GetNibble( cpu_status.PC++ );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
int oc = GetOC_1( o );
|
|
|
|
int rp = GetRP( o );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecRegOp_F" );
|
|
|
|
/* Decode operation code */
|
|
|
|
switch ( oc ) {
|
|
|
|
case 0:
|
|
|
|
ShiftLeftR( reg_pair_0[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 1:
|
|
|
|
ShiftRightR( reg_pair_0[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 2:
|
|
|
|
TwoComplR( reg_pair_0[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 3:
|
|
|
|
OneComplR( reg_pair_0[ rp ], FS_A );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
default:
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* .&., .!., AND/OR Operations, opcode 0Efo, length 4 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecAND_OR( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
Nibble f = GetNibble( cpu_status.PC++ );
|
|
|
|
Nibble o = GetNibble( cpu_status.PC++ );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
int oc = GetOC_1( o );
|
|
|
|
int rp = GetRP( o );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecAND_OR" );
|
|
|
|
/* Decode operation code */
|
|
|
|
switch ( oc ) {
|
|
|
|
case 0:
|
|
|
|
AndRR( reg_pair_0[ rp ], reg_pair_0[ rp ], reg_pair_1[ rp ], f );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 1:
|
|
|
|
AndRR( reg_pair_1[ rp ], reg_pair_1[ rp ], reg_pair_0[ rp ], f );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 2:
|
|
|
|
OrRR( reg_pair_0[ rp ], reg_pair_0[ rp ], reg_pair_1[ rp ], f );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
case 3:
|
|
|
|
OrRR( reg_pair_1[ rp ], reg_pair_1[ rp ], reg_pair_0[ rp ], f );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
default:
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Instruction Group_0 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecGroup_0( void )
|
|
|
|
{
|
|
|
|
Nibble n = GetNibble( cpu_status.PC++ );
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecGroup_0" );
|
|
|
|
switch ( n ) {
|
|
|
|
case 0: /* RTNSXM */
|
|
|
|
cpu_status.HST |= HST_XM_MASK;
|
|
|
|
cpu_status.PC = PopRSTK();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: /* RTN */
|
|
|
|
cpu_status.PC = PopRSTK();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: /* RTNSC */
|
|
|
|
cpu_status.carry = 1;
|
|
|
|
cpu_status.PC = PopRSTK();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: /* RTNCC */
|
|
|
|
cpu_status.carry = 0;
|
|
|
|
cpu_status.PC = PopRSTK();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4: /* SETHEX */
|
|
|
|
cpu_status.hexmode = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5: /* SETDEC */
|
|
|
|
cpu_status.hexmode = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6: /* RSTK=C */
|
|
|
|
PushRSTK( R2Addr( cpu_status.C ) );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7: /* C=RSTK */
|
|
|
|
Addr2R( cpu_status.C, PopRSTK() );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 8: /* CLRST */
|
|
|
|
cpu_status.ST &= CLRST_MASK;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 9: /* C=ST */
|
|
|
|
St2C();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xA: /* ST=C */
|
|
|
|
C2St();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xB: /* CSTEX */
|
|
|
|
CStExch();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xC: /* P=P+1 */
|
|
|
|
{
|
|
|
|
if ( cpu_status.P == NIBBLE_MASK ) {
|
|
|
|
SetP( 0 );
|
|
|
|
cpu_status.carry = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
SetP( cpu_status.P + 1 );
|
|
|
|
cpu_status.carry = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0xD: /* P=P-1 */
|
|
|
|
{
|
|
|
|
if ( cpu_status.P == ( Nibble )0 ) {
|
|
|
|
SetP( NIBBLE_MASK );
|
|
|
|
cpu_status.carry = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
|
|
|
SetP( cpu_status.P - 1 );
|
|
|
|
cpu_status.carry = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0xE: /* AND_OR */
|
|
|
|
ExecAND_OR();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xF: /* RTI */
|
|
|
|
ExecRTI();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: /* Unknown opcode */
|
|
|
|
ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, cpu_status.PC, n ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Instruction Group_1 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecGroup_1( void )
|
|
|
|
{
|
|
|
|
Nibble n = GetNibble( cpu_status.PC++ );
|
|
|
|
Nibble f;
|
|
|
|
int rn, ac;
|
|
|
|
int oc, is;
|
|
|
|
Address ta;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecGroup_1" );
|
|
|
|
switch ( n ) {
|
|
|
|
case 0: /* Rn=A/C */
|
|
|
|
n = GetNibble( cpu_status.PC++ );
|
|
|
|
rn = GetRn( n );
|
|
|
|
ac = GetAC( n );
|
|
|
|
|
|
|
|
CopyRR( cpu_status.R[ rn ], ( ac ? cpu_status.C : cpu_status.A ), FS_W );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: /* A/C=Rn */
|
|
|
|
n = GetNibble( cpu_status.PC++ );
|
|
|
|
rn = GetRn( n );
|
|
|
|
ac = GetAC( n );
|
|
|
|
|
|
|
|
CopyRR( ( ac ? cpu_status.C : cpu_status.A ), cpu_status.R[ rn ], FS_W );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
/* ARnEX, CRnEX */
|
|
|
|
n = GetNibble( cpu_status.PC++ );
|
|
|
|
rn = GetRn( n );
|
|
|
|
ac = GetAC( n );
|
|
|
|
|
|
|
|
ExchRR( ( ac ? cpu_status.C : cpu_status.A ), cpu_status.R[ rn ], FS_W );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
/* Copy/Exchange A/C and D0/D1 */
|
|
|
|
switch ( GetNibble( cpu_status.PC++ ) ) {
|
|
|
|
case 0: /* D0=A */
|
|
|
|
cpu_status.D0 = R2Addr( cpu_status.A );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: /* D1=A */
|
|
|
|
cpu_status.D1 = R2Addr( cpu_status.A );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: /* AD0EX */
|
|
|
|
ta = cpu_status.D0;
|
|
|
|
cpu_status.D0 = R2Addr( cpu_status.A );
|
|
|
|
Addr2R( cpu_status.A, ta );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: /* AD1EX */
|
|
|
|
ta = cpu_status.D1;
|
|
|
|
cpu_status.D1 = R2Addr( cpu_status.A );
|
|
|
|
Addr2R( cpu_status.A, ta );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4: /* D0=C */
|
|
|
|
cpu_status.D0 = R2Addr( cpu_status.C );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5: /* D1=C */
|
|
|
|
cpu_status.D1 = R2Addr( cpu_status.C );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6: /* CD0EX */
|
|
|
|
ta = cpu_status.D0;
|
|
|
|
cpu_status.D0 = R2Addr( cpu_status.C );
|
|
|
|
Addr2R( cpu_status.C, ta );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7: /* CD1EX */
|
|
|
|
ta = cpu_status.D1;
|
|
|
|
cpu_status.D1 = R2Addr( cpu_status.C );
|
|
|
|
Addr2R( cpu_status.C, ta );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 8: /* D0=AS */
|
|
|
|
cpu_status.D0 = R2AddrS( cpu_status.A ) | ( cpu_status.D0 & D_S_MASK );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 9: /* D1=AS */
|
|
|
|
cpu_status.D1 = R2AddrS( cpu_status.A ) | ( cpu_status.D1 & D_S_MASK );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xA: /* AD0XS */
|
|
|
|
ta = cpu_status.D0;
|
|
|
|
cpu_status.D0 = R2AddrS( cpu_status.A ) | ( cpu_status.D0 & D_S_MASK );
|
|
|
|
Addr2RS( cpu_status.A, ta );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xB: /* AD1XS */
|
|
|
|
ta = cpu_status.D1;
|
|
|
|
cpu_status.D1 = R2AddrS( cpu_status.A ) | ( cpu_status.D1 & D_S_MASK );
|
|
|
|
Addr2RS( cpu_status.A, ta );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xC: /* D0=CS */
|
|
|
|
cpu_status.D0 = R2AddrS( cpu_status.C ) | ( cpu_status.D0 & D_S_MASK );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xD: /* D1=CS */
|
|
|
|
cpu_status.D1 = R2AddrS( cpu_status.C ) | ( cpu_status.D1 & D_S_MASK );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xE: /* CD0XS */
|
|
|
|
ta = cpu_status.D0;
|
|
|
|
cpu_status.D0 = R2AddrS( cpu_status.C ) | ( cpu_status.D0 & D_S_MASK );
|
|
|
|
Addr2RS( cpu_status.C, ta );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xF: /* CD1XS */
|
|
|
|
ta = cpu_status.D1;
|
|
|
|
cpu_status.D1 = R2AddrS( cpu_status.C ) | ( cpu_status.D1 & D_S_MASK );
|
|
|
|
Addr2RS( cpu_status.C, ta );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
/* Load/Store A/C to @D0/@D1, Field selector A or B */
|
|
|
|
switch ( GetNibble( cpu_status.PC++ ) ) {
|
|
|
|
case 0: /* DAT0=A A */
|
|
|
|
WriteDAT( cpu_status.D0, cpu_status.A, FS_A );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: /* DAT1=A A */
|
|
|
|
WriteDAT( cpu_status.D1, cpu_status.A, FS_A );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: /* A=DAT0 A */
|
|
|
|
ReadDAT( cpu_status.A, cpu_status.D0, FS_A );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: /* A=DAT1 A */
|
|
|
|
ReadDAT( cpu_status.A, cpu_status.D1, FS_A );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4: /* DAT0=C A */
|
|
|
|
WriteDAT( cpu_status.D0, cpu_status.C, FS_A );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5: /* DAT1=C A */
|
|
|
|
WriteDAT( cpu_status.D1, cpu_status.C, FS_A );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6: /* C=DAT0 A */
|
|
|
|
ReadDAT( cpu_status.C, cpu_status.D0, FS_A );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7: /* C=DAT1 A */
|
|
|
|
ReadDAT( cpu_status.C, cpu_status.D1, FS_A );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 8: /* DAT0=A B */
|
|
|
|
WriteDAT( cpu_status.D0, cpu_status.A, FS_B );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 9: /* DAT1=A B */
|
|
|
|
WriteDAT( cpu_status.D1, cpu_status.A, FS_B );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xA: /* A=DAT0 B */
|
|
|
|
ReadDAT( cpu_status.A, cpu_status.D0, FS_B );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xB: /* A=DAT1 B */
|
|
|
|
ReadDAT( cpu_status.A, cpu_status.D1, FS_B );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xC: /* DAT0=C B */
|
|
|
|
WriteDAT( cpu_status.D0, cpu_status.C, FS_B );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xD: /* DAT1=C B */
|
|
|
|
WriteDAT( cpu_status.D1, cpu_status.C, FS_B );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xE: /* C=DAT0 B */
|
|
|
|
ReadDAT( cpu_status.C, cpu_status.D0, FS_B );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xF: /* C=DAT1 B */
|
|
|
|
ReadDAT( cpu_status.C, cpu_status.D1, FS_B );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5:
|
|
|
|
/* Load/Store A/C to @D0/@D1, Other Field Selectors */
|
|
|
|
n = GetNibble( cpu_status.PC++ );
|
|
|
|
f = GetNibble( cpu_status.PC++ );
|
|
|
|
oc = GetOC_3b( n );
|
|
|
|
is = GetImmFS( n );
|
|
|
|
|
|
|
|
switch ( oc ) {
|
|
|
|
case 0: /* DAT0=A */
|
|
|
|
if ( is )
|
|
|
|
WriteDATImm( cpu_status.D0, cpu_status.A, f );
|
|
|
|
else
|
|
|
|
WriteDAT( cpu_status.D0, cpu_status.A, f );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: /* DAT1=A */
|
|
|
|
if ( is )
|
|
|
|
WriteDATImm( cpu_status.D1, cpu_status.A, f );
|
|
|
|
else
|
|
|
|
WriteDAT( cpu_status.D1, cpu_status.A, f );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: /* A=DAT0 */
|
|
|
|
if ( is )
|
|
|
|
ReadDATImm( cpu_status.A, cpu_status.D0, f );
|
|
|
|
else
|
|
|
|
ReadDAT( cpu_status.A, cpu_status.D0, f );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: /* A=DAT1 */
|
|
|
|
if ( is )
|
|
|
|
ReadDATImm( cpu_status.A, cpu_status.D1, f );
|
|
|
|
else
|
|
|
|
ReadDAT( cpu_status.A, cpu_status.D1, f );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4: /* DAT0=C */
|
|
|
|
if ( is )
|
|
|
|
WriteDATImm( cpu_status.D0, cpu_status.C, f );
|
|
|
|
else
|
|
|
|
WriteDAT( cpu_status.D0, cpu_status.C, f );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5: /* DAT1=C */
|
|
|
|
if ( is )
|
|
|
|
WriteDATImm( cpu_status.D1, cpu_status.C, f );
|
|
|
|
else
|
|
|
|
WriteDAT( cpu_status.D1, cpu_status.C, f );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6: /* C=DAT0 */
|
|
|
|
if ( is )
|
|
|
|
ReadDATImm( cpu_status.C, cpu_status.D0, f );
|
|
|
|
else
|
|
|
|
ReadDAT( cpu_status.C, cpu_status.D0, f );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7: /* C=DAT1 */
|
|
|
|
if ( is )
|
|
|
|
ReadDATImm( cpu_status.C, cpu_status.D1, f );
|
|
|
|
else
|
|
|
|
ReadDAT( cpu_status.C, cpu_status.D1, f );
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
/* D0=D0+n+1 */
|
|
|
|
n = GetNibble( cpu_status.PC++ );
|
|
|
|
ta = ( cpu_status.D0 + n + 1 ) & ADDRESS_MASK;
|
|
|
|
cpu_status.carry = ( ta < cpu_status.D0 );
|
|
|
|
cpu_status.D0 = ta;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7:
|
|
|
|
/* D1=D1+n+1 */
|
|
|
|
n = GetNibble( cpu_status.PC++ );
|
|
|
|
ta = ( cpu_status.D1 + n + 1 ) & ADDRESS_MASK;
|
|
|
|
cpu_status.carry = ( ta < cpu_status.D1 );
|
|
|
|
cpu_status.D1 = ta;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 8:
|
|
|
|
/* D0=D0-(n+1) */
|
|
|
|
n = GetNibble( cpu_status.PC++ );
|
|
|
|
ta = ( cpu_status.D0 - n - 1 ) & ADDRESS_MASK;
|
|
|
|
cpu_status.carry = ( ta > cpu_status.D0 );
|
|
|
|
cpu_status.D0 = ta;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 9:
|
|
|
|
/* D0=(2) nn */
|
|
|
|
FetchD( &cpu_status.D0, 2 );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xA:
|
|
|
|
/* D0=(4) nn */
|
|
|
|
FetchD( &cpu_status.D0, 4 );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xB:
|
|
|
|
/* D0=(5) nn */
|
|
|
|
FetchD( &cpu_status.D0, 5 );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xC:
|
|
|
|
/* D1=D1-(n+1) */
|
|
|
|
n = GetNibble( cpu_status.PC++ );
|
|
|
|
ta = ( cpu_status.D1 - n - 1 ) & ADDRESS_MASK;
|
|
|
|
cpu_status.carry = ( ta > cpu_status.D1 );
|
|
|
|
cpu_status.D1 = ta;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xD:
|
|
|
|
/* D1=(2) nn */
|
|
|
|
FetchD( &cpu_status.D1, 2 );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xE:
|
|
|
|
/* D1=(4) nn */
|
|
|
|
FetchD( &cpu_status.D1, 4 );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xF:
|
|
|
|
/* D1=(5) nn */
|
|
|
|
FetchD( &cpu_status.D1, 5 );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
default:
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Unknown opcode */
|
|
|
|
ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, cpu_status.PC, n ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Instruction Group_808 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecGroup_808( void )
|
|
|
|
{
|
|
|
|
Nibble n = GetNibble( cpu_status.PC++ );
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecGroup_808" );
|
|
|
|
switch ( n ) {
|
|
|
|
case 0: /* INTON */
|
|
|
|
ExecINTON();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: /* RSI */
|
|
|
|
ExecRSI();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: /* LA(m) n..n */
|
|
|
|
FetchR( cpu_status.A, GetNibble( cpu_status.PC++ ) );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: /* BUSCB */
|
|
|
|
ExecBUSCB();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4: /* ABIT=0 d */
|
|
|
|
ExecBIT0( cpu_status.A, GetNibble( cpu_status.PC++ ) );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5: /* ABIT=1 d */
|
|
|
|
ExecBIT1( cpu_status.A, GetNibble( cpu_status.PC++ ) );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6: /* ?ABIT=0 d */
|
|
|
|
TestBIT0( cpu_status.A, GetNibble( cpu_status.PC++ ) );
|
|
|
|
ExecGOYES_RTNYES();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7: /* ?ABIT=1 d */
|
|
|
|
TestBIT1( cpu_status.A, GetNibble( cpu_status.PC++ ) );
|
|
|
|
ExecGOYES_RTNYES();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 8: /* CBIT=0 d */
|
|
|
|
ExecBIT0( cpu_status.C, GetNibble( cpu_status.PC++ ) );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 9: /* CBIT=1 d */
|
|
|
|
ExecBIT1( cpu_status.C, GetNibble( cpu_status.PC++ ) );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xA: /* ?CBIT=0 d */
|
|
|
|
TestBIT0( cpu_status.C, GetNibble( cpu_status.PC++ ) );
|
|
|
|
ExecGOYES_RTNYES();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xB: /* ?CBIT=1 d */
|
|
|
|
TestBIT1( cpu_status.C, GetNibble( cpu_status.PC++ ) );
|
|
|
|
ExecGOYES_RTNYES();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xC: /* PC=(A) */
|
|
|
|
cpu_status.PC = Get5NibblesAbs( R2Addr( cpu_status.A ) );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xD:
|
|
|
|
/* BUSCD */
|
|
|
|
ExecBUSCD();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xE:
|
|
|
|
/* PC=(C) */
|
|
|
|
cpu_status.PC = Get5NibblesAbs( R2Addr( cpu_status.C ) );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xF:
|
|
|
|
/* INTOFF */
|
|
|
|
ExecINTOFF();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* Unknown opcode */
|
|
|
|
ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, cpu_status.PC, n ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Instruction Group_80 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecGroup_80( void )
|
|
|
|
{
|
|
|
|
Nibble n = GetNibble( cpu_status.PC++ );
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecGroup_80" );
|
|
|
|
switch ( n ) {
|
|
|
|
case 0: /* OUT=CS */
|
|
|
|
ExecOUTCS();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: /* OUT=C */
|
|
|
|
ExecOUTC();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: /* A=IN */
|
|
|
|
ExecIN( cpu_status.A );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: /* C=IN */
|
|
|
|
ExecIN( cpu_status.C );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4: /* UNCNFG */
|
|
|
|
ModUnconfig( R2Addr( cpu_status.C ) );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5: /* CONFIG */
|
|
|
|
ModConfig( R2Addr( cpu_status.C ) );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6: /* C=ID */
|
|
|
|
Addr2R( cpu_status.C, ModGetID() );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7: /* SHUTDN */
|
|
|
|
ExecSHUTDN();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 8: /* Group 808 */
|
|
|
|
ExecGroup_808();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 9: /* C+P+1 */
|
|
|
|
AddRImm( cpu_status.C, FS_A, cpu_status.P );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xA: /* RESET */
|
|
|
|
ModReset();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xB: /* BUSCC */
|
|
|
|
ExecBUSCC();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xE: /* SREQ? */
|
|
|
|
ExecSREQ();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xC: /* C=P n */
|
|
|
|
cpu_status.C[ ( int )GetNibble( cpu_status.PC++ ) ] = cpu_status.P;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xD: /* P=C n */
|
|
|
|
SetP( cpu_status.C[ ( int )GetNibble( cpu_status.PC++ ) ] );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xF: /* CPEX */
|
|
|
|
{
|
|
|
|
Nibble t;
|
|
|
|
n = GetNibble( cpu_status.PC++ );
|
|
|
|
t = cpu_status.P;
|
|
|
|
SetP( cpu_status.C[ ( int )n ] );
|
|
|
|
cpu_status.C[ ( int )n ] = t;
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
default:
|
|
|
|
ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, cpu_status.PC, n ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Special functions Group_81 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecSpecialGroup_81( int rp )
|
|
|
|
{
|
|
|
|
Nibble n, f, m;
|
|
|
|
int rn, ac;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecSpecialGroup_81" );
|
|
|
|
switch ( rp ) {
|
|
|
|
case 0: /* r=r+-CON fs, d */
|
|
|
|
f = GetNibble( cpu_status.PC++ );
|
|
|
|
n = GetNibble( cpu_status.PC++ );
|
|
|
|
m = GetNibble( cpu_status.PC++ );
|
|
|
|
rp = GetRP( n );
|
|
|
|
|
|
|
|
if ( GetAS( n ) ) /* Subtract */
|
|
|
|
SubRImm( reg_pair_0[ rp ], f, m );
|
|
|
|
else /* Add */
|
|
|
|
AddRImm( reg_pair_0[ rp ], f, m );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: /* rSRB.f fs */
|
|
|
|
f = GetNibble( cpu_status.PC++ );
|
|
|
|
n = GetNibble( cpu_status.PC++ );
|
|
|
|
rp = GetRP( n );
|
|
|
|
ShiftRightBitR( reg_pair_0[ rp ], f );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: /* Rn=r.F fs, r=R0.F fs, rRnEX.F fs */
|
|
|
|
f = GetNibble( cpu_status.PC++ );
|
|
|
|
n = GetNibble( cpu_status.PC++ );
|
|
|
|
m = GetNibble( cpu_status.PC++ );
|
|
|
|
rn = GetRn( m );
|
|
|
|
ac = GetAC( m );
|
|
|
|
|
|
|
|
switch ( n ) {
|
|
|
|
case 0: /* Rn=r.F fs */
|
|
|
|
CopyRR( cpu_status.R[ rn ], ( ac ? cpu_status.C : cpu_status.A ), f );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: /* r=R0.F fs */
|
|
|
|
CopyRR( ( ac ? cpu_status.C : cpu_status.A ), cpu_status.R[ rn ], f );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: /* rRnEX.F fs */
|
|
|
|
ExchRR( ( ac ? cpu_status.C : cpu_status.A ), cpu_status.R[ rn ], f );
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, cpu_status.PC, n ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: /* Group 81B */
|
|
|
|
switch ( n = GetNibble( cpu_status.PC++ ) ) {
|
|
|
|
case 2: /* PC=A */
|
|
|
|
cpu_status.PC = R2Addr( cpu_status.A );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: /* PC=C */
|
|
|
|
cpu_status.PC = R2Addr( cpu_status.C );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4: /* A=PC */
|
|
|
|
Addr2R( cpu_status.A, cpu_status.PC );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5: /* C=PC */
|
|
|
|
Addr2R( cpu_status.C, cpu_status.PC );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6: /* APCEX */
|
|
|
|
{
|
|
|
|
Address t;
|
|
|
|
t = R2Addr( cpu_status.A );
|
|
|
|
Addr2R( cpu_status.A, cpu_status.PC );
|
|
|
|
cpu_status.PC = t;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 7: /* CPCEX */
|
|
|
|
{
|
|
|
|
Address t;
|
|
|
|
t = R2Addr( cpu_status.C );
|
|
|
|
Addr2R( cpu_status.C, cpu_status.PC );
|
|
|
|
cpu_status.PC = t;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, cpu_status.PC, n ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
default:
|
2024-03-26 13:36:50 +01:00
|
|
|
ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Register_Pair" ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Instruction Group_8 */
|
2024-03-26 13:36:50 +01:00
|
|
|
static void ExecGroup_8( void )
|
|
|
|
{
|
|
|
|
Nibble n = GetNibble( cpu_status.PC++ );
|
|
|
|
Address addr;
|
|
|
|
int oc, rp;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "ExecGroup_8" );
|
|
|
|
switch ( n ) {
|
|
|
|
case 0:
|
|
|
|
ExecGroup_80();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: /* rSLC, rSRC, rSRB, Special Group_81 */
|
|
|
|
n = GetNibble( cpu_status.PC++ );
|
|
|
|
oc = GetOC_1( n );
|
|
|
|
rp = GetRP( n );
|
|
|
|
|
|
|
|
switch ( oc ) {
|
|
|
|
case 0: /* rSLC */
|
|
|
|
ShiftLeftCircR( reg_pair_0[ rp ], FS_W );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: /* rSRC */
|
|
|
|
ShiftRightCircR( reg_pair_0[ rp ], FS_W );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: /* Special Group_81 */
|
|
|
|
ExecSpecialGroup_81( rp );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: /* rSRB */
|
|
|
|
ShiftRightBitR( reg_pair_0[ rp ], FS_W );
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ChfCondition CPU_F_INTERR, CHF_FATAL, "Bad_Operation_Code" ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: /* CLRHSn */
|
|
|
|
cpu_status.HST &= ~GetNibble( cpu_status.PC++ );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: /* ?HS=0 */
|
|
|
|
n = GetNibble( cpu_status.PC++ );
|
|
|
|
cpu_status.carry = ( ( cpu_status.HST & n ) == 0 );
|
|
|
|
ExecGOYES_RTNYES();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4: /* ST=0 n */
|
|
|
|
cpu_status.ST &= ~st_bit_mask[ ( int )GetNibble( cpu_status.PC++ ) ];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5: /* ST=1 n */
|
|
|
|
cpu_status.ST |= st_bit_mask[ ( int )GetNibble( cpu_status.PC++ ) ];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6: /* ?ST=0 n */
|
|
|
|
cpu_status.carry = ( ( cpu_status.ST & st_bit_mask[ ( int )GetNibble( cpu_status.PC++ ) ] ) == 0 );
|
|
|
|
ExecGOYES_RTNYES();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7: /* ?ST=1 n */
|
|
|
|
cpu_status.carry = ( ( cpu_status.ST & st_bit_mask[ ( int )GetNibble( cpu_status.PC++ ) ] ) != 0 );
|
|
|
|
ExecGOYES_RTNYES();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 8: /* ?P#n */
|
|
|
|
cpu_status.carry = ( cpu_status.P != GetNibble( cpu_status.PC++ ) );
|
|
|
|
ExecGOYES_RTNYES();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 9: /* ?P=n */
|
|
|
|
cpu_status.carry = ( cpu_status.P == GetNibble( cpu_status.PC++ ) );
|
|
|
|
ExecGOYES_RTNYES();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xA: /* Test */
|
|
|
|
ExecTest_8A();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xB: /* Test */
|
|
|
|
ExecTest_8B();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xC: /* GOLONG */
|
|
|
|
addr = Get4Nibbles2C( cpu_status.PC );
|
|
|
|
cpu_status.PC += addr;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xD:
|
|
|
|
/* GOVLNG */
|
|
|
|
cpu_status.PC = Get5NibblesAbs( cpu_status.PC );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xE:
|
|
|
|
/* GOSUBL */
|
|
|
|
addr = Get4Nibbles2C( cpu_status.PC );
|
|
|
|
cpu_status.PC += 4;
|
|
|
|
PushRSTK( cpu_status.PC );
|
|
|
|
cpu_status.PC += addr;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xF:
|
|
|
|
/* GOSBVL */
|
|
|
|
PushRSTK( cpu_status.PC + 5 );
|
|
|
|
cpu_status.PC = Get5NibblesAbs( cpu_status.PC );
|
|
|
|
break;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
default:
|
2024-03-26 13:36:50 +01:00
|
|
|
ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, cpu_status.PC, n ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Private functions: dump
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
const char* DumpR( Nibble* r )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
static char b[ NIBBLE_PER_REGISTER + 1 ];
|
|
|
|
static const char hex_char[ NIBBLE_PER_REGISTER ] = "0123456789ABCDEF";
|
|
|
|
int n;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
for ( n = 0; n < NIBBLE_PER_REGISTER; n++ )
|
|
|
|
b[ n ] = hex_char[ ( int )r[ NIBBLE_PER_REGISTER - 1 - n ] ];
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
b[ NIBBLE_PER_REGISTER ] = '\0';
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
return b;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
2024-03-26 13:36:50 +01:00
|
|
|
Public functions
|
2022-03-21 11:05:59 +01:00
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/* .+
|
|
|
|
|
|
|
|
.title : CpuReset
|
|
|
|
.kind : C function
|
|
|
|
.creation : 3-Feb-1998
|
|
|
|
.description :
|
|
|
|
This function resets the CPU, performing the following operations:
|
|
|
|
|
|
|
|
- Copies the field selector index arrays to the cpu_status structure
|
|
|
|
- Set P=0
|
|
|
|
- Clears registers A, B, C, D, Rn
|
|
|
|
- Clears registers D0, D1
|
|
|
|
- Sets PC to zero
|
|
|
|
- Clears registers IN, OUT, ST, HST
|
|
|
|
- Sets hex mode for arithmetic operations
|
|
|
|
- Clears carry, int_enable, int_service, int_pending, and shutdn
|
|
|
|
- The inner_loop limit is set to INNER_LOOP_MED
|
|
|
|
|
|
|
|
.call :
|
2024-03-26 13:36:50 +01:00
|
|
|
CpuReset();
|
2022-03-21 11:05:59 +01:00
|
|
|
.input :
|
2024-03-26 13:36:50 +01:00
|
|
|
void
|
2022-03-21 11:05:59 +01:00
|
|
|
.output :
|
2024-03-26 13:36:50 +01:00
|
|
|
void
|
2022-03-21 11:05:59 +01:00
|
|
|
.status_codes :
|
2024-03-26 13:36:50 +01:00
|
|
|
CPU_I_CALLED
|
|
|
|
CPU_E_BAD_OPCODE
|
|
|
|
CPU_F_INTERR
|
2022-03-21 11:05:59 +01:00
|
|
|
.notes :
|
|
|
|
1.1, 3-Feb-1998, creation
|
|
|
|
1.2, 7-Sep-2000, bug fix
|
|
|
|
- cpu_status.return_sp and .reset_req were not reset; this gave troubles
|
|
|
|
when attempting to override a corrupt status with CpuReset().
|
|
|
|
3.13, 2-Nov-2000, update
|
|
|
|
- cpu_status.halt and cpu_status.inner_loop_max need reset
|
|
|
|
3.14, 10-Nov-2000, bug fix
|
|
|
|
- cpu_status.inner_loop_max must be reset to 0, because the default
|
|
|
|
emulator speed is maximum speed.
|
|
|
|
.- */
|
2024-03-26 13:36:50 +01:00
|
|
|
void CpuReset( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
int n;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "CpuReset" );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Copy field selector index arrays to the cpu_status structure */
|
|
|
|
( void )memcpy( cpu_status.fs_idx_lo, fs_idx_lo, sizeof( fs_idx_lo ) );
|
|
|
|
( void )memcpy( cpu_status.fs_idx_hi, fs_idx_hi, sizeof( fs_idx_hi ) );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Set P=0 and adjust fs index arrays */
|
|
|
|
SetP( ( Nibble )0 );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Clear A, B, C, D */
|
|
|
|
for ( n = 0; n < N_WORKING_REGISTER; n++ )
|
|
|
|
ClearR( cpu_status.work[ n ], FS_W );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Clear Rn */
|
|
|
|
for ( n = 0; n < N_SCRATCH_REGISTER; n++ )
|
|
|
|
ClearR( cpu_status.R[ n ], FS_W );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Clear D0, D1 */
|
|
|
|
cpu_status.D0 = cpu_status.D1 = ( Address )0;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Clear PC */
|
|
|
|
cpu_status.PC = ( Address )0;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Clear IN, OUT, ST, HST */
|
|
|
|
cpu_status.IN = ( InputRegister )0;
|
|
|
|
cpu_status.OUT = ( OutputRegister )0;
|
|
|
|
cpu_status.ST = ( ProgramStatusRegister )0;
|
|
|
|
cpu_status.HST = ( Nibble )0;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Fill the return stack with (Address)0 */
|
|
|
|
cpu_status.return_sp = 0;
|
|
|
|
for ( n = 0; n < RETURN_STACK_SIZE; n++ )
|
|
|
|
cpu_status.return_stack[ n ] = ( Address )0;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Set hexmode */
|
|
|
|
cpu_status.hexmode = 1;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Clear carry */
|
|
|
|
cpu_status.carry = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Disable maskable interrupts */
|
|
|
|
cpu_status.int_enable = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* No interrupts are pending (for now) */
|
|
|
|
cpu_status.int_service = 0;
|
|
|
|
cpu_status.int_pending = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* The CPU is running */
|
|
|
|
cpu_status.shutdn = cpu_status.halt = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Set inner_loop and inner_loop_max to default values */
|
|
|
|
cpu_status.inner_loop = INNER_LOOP_MED;
|
|
|
|
cpu_status.inner_loop_max = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Reset reset_req if necessary */
|
2022-03-21 11:05:59 +01:00
|
|
|
#ifdef CPU_SPIN_LOOP
|
2024-03-26 13:36:50 +01:00
|
|
|
cpu_status.reset_req = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* .+
|
|
|
|
|
|
|
|
.title : CpuInit
|
|
|
|
.kind : C function
|
|
|
|
.creation : 11-Feb-1998
|
|
|
|
.description :
|
|
|
|
This function initializes the Saturn CPU, reading its status from disk.
|
|
|
|
If something goes wrong with the disk I/O, the function resets the CPU.
|
|
|
|
|
|
|
|
.call :
|
2024-03-26 13:36:50 +01:00
|
|
|
CpuInit();
|
2022-03-21 11:05:59 +01:00
|
|
|
.input :
|
2024-03-26 13:36:50 +01:00
|
|
|
void
|
2022-03-21 11:05:59 +01:00
|
|
|
.output :
|
2024-03-26 13:36:50 +01:00
|
|
|
void
|
2022-03-21 11:05:59 +01:00
|
|
|
.status_codes :
|
2024-03-26 13:36:50 +01:00
|
|
|
CPU_I_CALLED
|
|
|
|
CPU_I_REVISION
|
|
|
|
CPU_W_RESETTING
|
2022-03-21 11:05:59 +01:00
|
|
|
.notes :
|
|
|
|
1.1, 11-Feb-1998, creation
|
|
|
|
3.14, 10-Nov-2000, update
|
|
|
|
- clear both shutdn and halt cpu flags here; this helps when the CPU
|
|
|
|
state was saved and reloaded when the CPU was halted.
|
|
|
|
.- */
|
2024-03-26 13:36:50 +01:00
|
|
|
void CpuInit( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "CpuInit" );
|
|
|
|
debug1( DEBUG_C_REVISION, CPU_I_REVISION, CPU_RCS_INFO );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
if ( ReadStructFromFile( args.cpu_file_name, sizeof( cpu_status ), &cpu_status ) ) {
|
|
|
|
ChfCondition CPU_W_RESETTING, CHF_WARNING ChfEnd;
|
|
|
|
ChfSignal();
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
CpuReset();
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* The CPU is running */
|
|
|
|
cpu_status.shutdn = cpu_status.halt = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* .+
|
|
|
|
|
|
|
|
.title : CpuSave
|
|
|
|
.kind : C function
|
|
|
|
.creation : 11-Feb-1998
|
|
|
|
.description :
|
|
|
|
This function saves the current Saturn CPU status to disk.
|
|
|
|
|
|
|
|
.call :
|
2024-03-26 13:36:50 +01:00
|
|
|
CpuSave();
|
2022-03-21 11:05:59 +01:00
|
|
|
.input :
|
2024-03-26 13:36:50 +01:00
|
|
|
void
|
2022-03-21 11:05:59 +01:00
|
|
|
.output :
|
2024-03-26 13:36:50 +01:00
|
|
|
void
|
2022-03-21 11:05:59 +01:00
|
|
|
.status_codes :
|
2024-03-26 13:36:50 +01:00
|
|
|
CPU_I_CALLED
|
|
|
|
CPU_E_SAVE
|
2022-03-21 11:05:59 +01:00
|
|
|
.notes :
|
|
|
|
1.1, 11-Feb-1998, creation
|
|
|
|
|
|
|
|
.- */
|
2024-03-26 13:36:50 +01:00
|
|
|
void CpuSave( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_CALLED, "CpuSave" );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
if ( WriteStructToFile( &cpu_status, sizeof( cpu_status ), args.cpu_file_name ) ) {
|
|
|
|
ChfCondition CPU_E_SAVE, CHF_ERROR ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* .+
|
|
|
|
|
|
|
|
.title : CpuIntRequest
|
|
|
|
.kind : C function
|
|
|
|
.creation : 11-Feb-1998
|
|
|
|
.description :
|
|
|
|
This function posts an interrupt request for the Saturn CPU.
|
2024-03-26 13:36:50 +01:00
|
|
|
|
2022-03-21 11:05:59 +01:00
|
|
|
The NMI interrupt requests are always honored; the IRQ requests are
|
|
|
|
honored immediately only if the CPU interrupts are enabled, otherwise
|
|
|
|
they will be honored as soon as the CPU reenables interrupts.
|
|
|
|
|
|
|
|
NOTE: The interrupt request can be INT_REQUEST_NONE; in this case, this
|
2024-03-26 13:36:50 +01:00
|
|
|
function does not post any interrupt request.
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
.call :
|
2024-03-26 13:36:50 +01:00
|
|
|
CpuIntRequest(ireq);
|
2022-03-21 11:05:59 +01:00
|
|
|
.input :
|
2024-03-26 13:36:50 +01:00
|
|
|
enum IntRequest ireq, interrupt request type, or
|
|
|
|
INT_REQUEST_NONE
|
2022-03-21 11:05:59 +01:00
|
|
|
.output :
|
2024-03-26 13:36:50 +01:00
|
|
|
void
|
2022-03-21 11:05:59 +01:00
|
|
|
.status_codes :
|
2024-03-26 13:36:50 +01:00
|
|
|
CPU_I_CALLED
|
|
|
|
CPU_I_INT
|
|
|
|
CPU_I_INT_PENDING
|
2022-03-21 11:05:59 +01:00
|
|
|
.notes :
|
|
|
|
1.1, 11-Feb-1998, creation
|
|
|
|
|
|
|
|
.- */
|
2024-03-26 13:36:50 +01:00
|
|
|
void CpuIntRequest( enum IntRequest ireq )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE | DEBUG_C_INT, CPU_I_CALLED, "CpuIntRequest" );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
if ( ( ireq == INT_REQUEST_IRQ && cpu_status.int_enable ) || ireq == INT_REQUEST_NMI ) {
|
|
|
|
/* Wake the CPU if it's sleeping */
|
|
|
|
CpuWake();
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Check if immediate vectoring is ok */
|
|
|
|
if ( cpu_status.int_service == 0 ) {
|
|
|
|
/* Vector immediately */
|
|
|
|
cpu_status.int_service = 1;
|
|
|
|
cpu_status.int_pending = INT_REQUEST_NONE;
|
|
|
|
PushRSTK( cpu_status.PC );
|
|
|
|
cpu_status.PC = INT_HANDLER_PC;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_INT, CPU_I_INT, ( ireq == INT_REQUEST_NMI ? "NMI" : "IRQ" ) );
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
else {
|
|
|
|
/* int_service is set; save the request for later processing */
|
|
|
|
cpu_status.int_pending = ireq;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_INT, CPU_I_INT_PENDING, ( ireq == INT_REQUEST_NMI ? "NMI" : "IRQ" ) );
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* .+
|
|
|
|
|
|
|
|
.title : CpuWake
|
|
|
|
.kind : C function
|
|
|
|
.creation : 11-Feb-1998
|
|
|
|
.description :
|
|
|
|
This function awakes the CPU if it has executed a SHUTDN instruction
|
|
|
|
and no halt requests are pending (see CpuHaltRequest() for more
|
|
|
|
information).
|
|
|
|
|
|
|
|
If the CPU is running, this function has no effect.
|
|
|
|
|
|
|
|
.call :
|
2024-03-26 13:36:50 +01:00
|
|
|
CpuWake();
|
2022-03-21 11:05:59 +01:00
|
|
|
.input :
|
2024-03-26 13:36:50 +01:00
|
|
|
void
|
2022-03-21 11:05:59 +01:00
|
|
|
.output :
|
2024-03-26 13:36:50 +01:00
|
|
|
void
|
2022-03-21 11:05:59 +01:00
|
|
|
.status_codes :
|
2024-03-26 13:36:50 +01:00
|
|
|
CPU_I_CALLED
|
|
|
|
CPU_I_WAKE
|
2022-03-21 11:05:59 +01:00
|
|
|
.notes :
|
|
|
|
1.1, 11-Feb-1998, creation
|
|
|
|
3.13, 2-Nov-2000, update:
|
|
|
|
- the CPU must be awoken only if no halt request is pending
|
|
|
|
|
|
|
|
.- */
|
2024-03-26 13:36:50 +01:00
|
|
|
void CpuWake( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE | DEBUG_C_INT, CPU_I_CALLED, "CpuWake" );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
if ( cpu_status.shutdn ) {
|
|
|
|
if ( cpu_status.halt == 0 ) {
|
|
|
|
debug0( DEBUG_C_INT, CPU_I_WAKE );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Clear SHUTDN flag */
|
|
|
|
cpu_status.shutdn = 0;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
#ifdef CPU_SPIN_SHUTDN
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Adjust PC if SHUTDN is implemented using a spin loop */
|
|
|
|
cpu_status.PC += 3;
|
2022-03-21 11:05:59 +01:00
|
|
|
#endif
|
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Clear PC if necessary */
|
|
|
|
/* if(cpu_status.OUT == (OutputRegister)0)
|
|
|
|
cpu_status.PC = (Address)0;
|
|
|
|
*/
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* .+
|
|
|
|
|
|
|
|
.title : CpuHaltRequest
|
|
|
|
.kind : C function
|
|
|
|
.creation : 2-Nov-2000
|
|
|
|
.description :
|
|
|
|
This function makes an halt request to the CPU emulator.
|
|
|
|
|
|
|
|
The halt condition is similar to the shutdn condition, and
|
|
|
|
actually forces a shutdn, but it cannot be broken by CpuWake();
|
|
|
|
only CpuRunRequest() can do this. When the CPU is halted:
|
|
|
|
|
|
|
|
- instruction execution is suspended
|
|
|
|
- IRQ and NMI service is delayed until the halt condition is broken
|
|
|
|
- timers are not updated (they will be resynchronized later)
|
|
|
|
- GUI events are handled
|
|
|
|
|
|
|
|
Multiple calls to CpuHaltRequest()/CpuRunRequest() can be nested;
|
|
|
|
the CPU remains halted as long as there are more than zero pending
|
|
|
|
halt requests.
|
|
|
|
|
|
|
|
CpuHaltRequest() relies on the presence of a suitable handler
|
|
|
|
of the CPU_I_SHUTDN condition; this is currently true only
|
|
|
|
if CpuHaltRequest() is invoked when the emulator loop is active.
|
|
|
|
|
|
|
|
Notice that setting the CPU_SPIN_SHUTDN build-time option
|
|
|
|
in config.h disables all halt requests; both CpuHaltRequest()
|
|
|
|
and CpuRunRequest() return -1 in this case.
|
|
|
|
|
|
|
|
The function returns the updated number of pending halt requests,
|
|
|
|
or -1 if halt/run requests are disabled; in the latter case,
|
|
|
|
the CPU_E_NO_HALT condition is generated and signalled, too.
|
|
|
|
|
|
|
|
The function may never return to the caller if the CPU_SPIN_SHUTDN
|
|
|
|
is handled locally by the handler, or if an unwind occurs.
|
|
|
|
|
|
|
|
.call :
|
2024-03-26 13:36:50 +01:00
|
|
|
ph = CpuHaltRequest();
|
2022-03-21 11:05:59 +01:00
|
|
|
.input :
|
2024-03-26 13:36:50 +01:00
|
|
|
void
|
2022-03-21 11:05:59 +01:00
|
|
|
.output :
|
2024-03-26 13:36:50 +01:00
|
|
|
int ph, updated number of pending halt requests, or
|
|
|
|
-1 if halt/run requests are disabled
|
2022-03-21 11:05:59 +01:00
|
|
|
.status_codes :
|
2024-03-26 13:36:50 +01:00
|
|
|
CPU_I_CALLED
|
|
|
|
CPU_I_HALT
|
|
|
|
CPU_E_NO_HALT
|
2022-03-21 11:05:59 +01:00
|
|
|
.notes :
|
|
|
|
3.13, 2-Nov-2000, creation
|
|
|
|
|
|
|
|
*/
|
2024-03-26 13:36:50 +01:00
|
|
|
int CpuHaltRequest( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE | DEBUG_C_INT, CPU_I_CALLED, "CpuHaltRequest" );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
#ifdef CPU_SPIN_SHUTDN
|
|
|
|
ChfCondition CPU_E_NO_HALT, CHF_ERROR ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
#else
|
2024-03-26 13:36:50 +01:00
|
|
|
if ( cpu_status.halt++ == 0 ) {
|
|
|
|
debug0( DEBUG_C_INT, CPU_I_HALT );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* CPU must actually be halted: call ExecSHUTDN() to simulate
|
|
|
|
the execution of a regular SHUTDN instruction.
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
CpuWake() will check .halt before clearing this condition.
|
|
|
|
*/
|
|
|
|
ExecSHUTDN();
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return cpu_status.halt;
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* .+
|
|
|
|
|
|
|
|
.title : CpuRunRequest
|
|
|
|
.kind : C function
|
|
|
|
.creation : 2-Nov-2000
|
|
|
|
.description :
|
|
|
|
This function undoes exactly one CpuHaltRequest(); it has no effect
|
|
|
|
if the CPU is not halted. See CpuHaltRequest() for more information.
|
|
|
|
|
|
|
|
The function returns the updated number of pending halt requests,
|
|
|
|
or -1 if halt/run requests are disabled; in the latter case,
|
|
|
|
the CPU_W_NO_HALT condition is generated, but not signalled, too.
|
|
|
|
|
|
|
|
.call :
|
2024-03-26 13:36:50 +01:00
|
|
|
ph = CpuRunRequest();
|
2022-03-21 11:05:59 +01:00
|
|
|
.input :
|
2024-03-26 13:36:50 +01:00
|
|
|
void
|
2022-03-21 11:05:59 +01:00
|
|
|
.output :
|
2024-03-26 13:36:50 +01:00
|
|
|
int ph, updated number of pending halt requests, or
|
|
|
|
-1 if halt requests are disabled
|
2022-03-21 11:05:59 +01:00
|
|
|
.status_codes :
|
2024-03-26 13:36:50 +01:00
|
|
|
CPU_I_CALLED
|
|
|
|
CPU_I_RUN
|
|
|
|
CPU_E_NO_HALT
|
2022-03-21 11:05:59 +01:00
|
|
|
.notes :
|
|
|
|
3.13, 2-Nov-2000, creation
|
|
|
|
|
|
|
|
*/
|
2024-03-26 13:36:50 +01:00
|
|
|
int CpuRunRequest( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE | DEBUG_C_INT, CPU_I_CALLED, "CpuRunRequest" );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
#ifdef CPU_SPIN_SHUTDN
|
|
|
|
ChfCondition CPU_E_NO_HALT, CHF_ERROR ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
#else
|
2024-03-26 13:36:50 +01:00
|
|
|
if ( cpu_status.halt > 0 )
|
|
|
|
if ( --cpu_status.halt == 0 ) {
|
|
|
|
debug0( DEBUG_C_INT, CPU_I_RUN );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* CPU must actually be awoken: call CpuWake() */
|
|
|
|
CpuWake();
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
return cpu_status.halt;
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* .+
|
|
|
|
|
|
|
|
.title : CpuHaltAllowed
|
|
|
|
.kind : C function
|
|
|
|
.creation : 7-Nov-2000
|
|
|
|
.description :
|
|
|
|
This function return a non-zero value if CpuHaltRequest()
|
|
|
|
is allowed, zero otherwise.
|
|
|
|
|
|
|
|
.call :
|
2024-03-26 13:36:50 +01:00
|
|
|
s = CpuHaltRequest();
|
2022-03-21 11:05:59 +01:00
|
|
|
.input :
|
2024-03-26 13:36:50 +01:00
|
|
|
void
|
2022-03-21 11:05:59 +01:00
|
|
|
.output :
|
2024-03-26 13:36:50 +01:00
|
|
|
int s, non-zero if CpuHaltRequest() is allowed, 0 otherwise
|
2022-03-21 11:05:59 +01:00
|
|
|
.status_codes :
|
2024-03-26 13:36:50 +01:00
|
|
|
CPU_I_CALLED
|
2022-03-21 11:05:59 +01:00
|
|
|
.notes :
|
|
|
|
3.13, 7-Nov-2000, creation
|
|
|
|
|
|
|
|
*/
|
2024-03-26 13:36:50 +01:00
|
|
|
int CpuHaltAllowed( void )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
debug1( DEBUG_C_TRACE | DEBUG_C_INT, CPU_I_CALLED, "CpuHaltAllowed" );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
#ifdef CPU_SPIN_SHUTDN
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#else
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* .+
|
|
|
|
|
|
|
|
.title : DumpCpuStatus
|
|
|
|
.kind : C function
|
|
|
|
.creation : 3-Feb-1998
|
|
|
|
.description :
|
|
|
|
This function dumps the current CPU status into the string buffer 'ob'.
|
|
|
|
|
|
|
|
.call :
|
2024-03-26 13:36:50 +01:00
|
|
|
DumpCpuStatus(ob);
|
2022-03-21 11:05:59 +01:00
|
|
|
.input :
|
2024-03-26 13:36:50 +01:00
|
|
|
void
|
2022-03-21 11:05:59 +01:00
|
|
|
.output :
|
2024-03-26 13:36:50 +01:00
|
|
|
char ob[DUMP_CPU_STATUS_OB_SIZE];
|
2022-03-21 11:05:59 +01:00
|
|
|
.status_codes :
|
2024-03-26 13:36:50 +01:00
|
|
|
*
|
2022-03-21 11:05:59 +01:00
|
|
|
.notes :
|
|
|
|
1.1, 3-Feb-1998, creation
|
|
|
|
|
|
|
|
.- */
|
2024-03-26 13:36:50 +01:00
|
|
|
void DumpCpuStatus( char ob[ DUMP_CPU_STATUS_OB_SIZE ] )
|
2022-03-21 11:05:59 +01:00
|
|
|
{
|
2024-03-26 13:36:50 +01:00
|
|
|
static const char* work_n[ N_WORKING_REGISTER ] = { "A", "B", "C", "D" };
|
|
|
|
char dob[ DISASSEMBLE_OB_SIZE ];
|
|
|
|
int n;
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Dump PC and current instruction */
|
|
|
|
( void )Disassemble( cpu_status.PC, dob );
|
|
|
|
sprintf( ob, "%s\n\n", dob );
|
|
|
|
ob += strlen( ob );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Dump A, B, C, D */
|
|
|
|
for ( n = 0; n < N_WORKING_REGISTER; n++ ) {
|
|
|
|
sprintf( ob, "%s:\t%s\n", work_n[ n ], DumpR( cpu_status.work[ n ] ) );
|
|
|
|
ob += strlen( ob );
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
sprintf( ob, "\n" );
|
|
|
|
ob += strlen( ob );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
/* Dump Rn */
|
|
|
|
for ( n = 0; n < N_SCRATCH_REGISTER; n++ ) {
|
|
|
|
sprintf( ob, "R%d:\t%s\n", n, DumpR( cpu_status.R[ n ] ) );
|
|
|
|
ob += strlen( ob );
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
sprintf( ob, "\n" );
|
|
|
|
ob += strlen( ob );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
sprintf( ob, "D0:\t%05X\t\tD1:\t%05X\n", cpu_status.D0, cpu_status.D1 );
|
|
|
|
ob += strlen( ob );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
sprintf( ob, "P:\t%01X\t\tIN:\t%04X\t\tOUT:\t%03X\n", cpu_status.P, cpu_status.IN, cpu_status.OUT );
|
|
|
|
ob += strlen( ob );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
sprintf( ob, "HST:\t%01X\t\tST:\t%04X\n", cpu_status.HST, cpu_status.ST );
|
|
|
|
ob += strlen( ob );
|
2022-03-21 11:05:59 +01:00
|
|
|
|
2024-03-26 13:36:50 +01:00
|
|
|
sprintf( ob, "hexmode: %d, carry: %d, int_enable/pending/service: %d/%d/%d, shutdn:%d\n", cpu_status.hexmode, cpu_status.carry,
|
|
|
|
cpu_status.int_enable, cpu_status.int_pending, cpu_status.int_service, cpu_status.shutdn );
|
|
|
|
ob += strlen( ob );
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
|
|
|
|
/* .+
|
|
|
|
|
|
|
|
.title : OneStep
|
|
|
|
.kind : C function
|
|
|
|
.creation : 3-Feb-1998
|
|
|
|
.description :
|
|
|
|
This function executes a Saturn instruction starting from the current
|
|
|
|
program counter, updating accordingly the global cpu_status data structure.
|
|
|
|
|
|
|
|
The function signals all exceptional situations through Chf conditions.
|
|
|
|
|
|
|
|
.call :
|
2024-03-26 13:36:50 +01:00
|
|
|
OneStep()
|
2022-03-21 11:05:59 +01:00
|
|
|
.input :
|
2024-03-26 13:36:50 +01:00
|
|
|
void
|
2022-03-21 11:05:59 +01:00
|
|
|
.output :
|
2024-03-26 13:36:50 +01:00
|
|
|
void
|
2022-03-21 11:05:59 +01:00
|
|
|
.status_codes :
|
2024-03-26 13:36:50 +01:00
|
|
|
CPU_I_EXECUTING
|
|
|
|
CPU_E_BAD_OPCODE
|
|
|
|
CPU_F_INTERR
|
2022-03-21 11:05:59 +01:00
|
|
|
.notes :
|
|
|
|
1.1, 3-Feb-1998, creation
|
|
|
|
|
|
|
|
.- */
|
2024-03-26 13:36:50 +01:00
|
|
|
void OneStep( void )
|
|
|
|
{
|
|
|
|
Nibble n;
|
|
|
|
Address offset;
|
|
|
|
|
|
|
|
debug1( DEBUG_C_TRACE, CPU_I_EXECUTING, cpu_status.PC );
|
|
|
|
|
|
|
|
/* Get first instruction nibble */
|
|
|
|
n = GetNibble( cpu_status.PC++ );
|
|
|
|
|
|
|
|
switch ( n ) {
|
|
|
|
case 0: /* Group_0 */
|
|
|
|
ExecGroup_0();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: /* Group_1 */
|
|
|
|
ExecGroup_1();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: /* P=n */
|
|
|
|
SetP( GetNibble( cpu_status.PC++ ) );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: /* LC(m) n...n */
|
|
|
|
FetchR( cpu_status.C, GetNibble( cpu_status.PC++ ) );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4: /* RTNC/GOC */
|
|
|
|
if ( cpu_status.carry ) {
|
|
|
|
offset = Get2Nibbles2C( cpu_status.PC );
|
|
|
|
if ( offset == 0 )
|
|
|
|
cpu_status.PC = PopRSTK();
|
|
|
|
else
|
|
|
|
cpu_status.PC += offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
cpu_status.PC += 2;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5: /* RTNNC/GONC */
|
|
|
|
if ( !cpu_status.carry ) {
|
|
|
|
offset = Get2Nibbles2C( cpu_status.PC );
|
|
|
|
if ( offset == 0 )
|
|
|
|
cpu_status.PC = PopRSTK();
|
|
|
|
else
|
|
|
|
cpu_status.PC += offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
cpu_status.PC += 2;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6: /* GOTO */
|
|
|
|
cpu_status.PC += Get3Nibbles2C( cpu_status.PC );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7: /* GOSUB */
|
|
|
|
offset = Get3Nibbles2C( cpu_status.PC );
|
|
|
|
cpu_status.PC += 3;
|
|
|
|
PushRSTK( cpu_status.PC );
|
|
|
|
cpu_status.PC += offset;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 8: /* Group_8 */
|
|
|
|
ExecGroup_8();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 9: /* Test */
|
|
|
|
ExecTest_9();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xA: /* Register Operation, group A */
|
|
|
|
ExecRegOp_A();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xB: /* Register Operation, group B */
|
|
|
|
ExecRegOp_B();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xC: /* Register Operation, group C */
|
|
|
|
ExecRegOp_C();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xD: /* Register Operation, group D */
|
|
|
|
ExecRegOp_D();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xE: /* Register Operation, group E */
|
|
|
|
ExecRegOp_E();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xF: /* Register Operation, group F */
|
|
|
|
ExecRegOp_F();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ChfCondition CPU_E_BAD_OPCODE, CHF_ERROR, cpu_status.PC, n ChfEnd;
|
|
|
|
ChfSignal();
|
|
|
|
break;
|
|
|
|
}
|
2022-03-21 11:05:59 +01:00
|
|
|
}
|