libx48ng/src/actions.c

703 lines
16 KiB
C
Raw Normal View History

/* #define DEBUG_INTERRUPT 1 */
/* #define DEBUG_KBD_INT 1 */
/* #define DEBUG_SHUTDOWN 1 */
/* #define DEBUG_CONFIG 1 */
/* #define DEBUG_ID 1 */
#include "global.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
2022-03-24 13:41:22 +01:00
#include "debugger.h"
#include "device.h"
#include "hp48.h"
#include "hp48_emu.h"
#include "romio.h"
2022-03-24 13:41:22 +01:00
#include "timer.h"
#include "x48_x11.h"
2022-03-24 13:41:22 +01:00
static int interrupt_called = 0;
2023-04-27 12:15:59 +02:00
extern long nibble_masks[ 16 ];
2022-03-24 13:41:22 +01:00
int got_alarm;
2023-04-27 12:15:59 +02:00
int first_press = 1; // PATCH
2022-03-24 13:41:22 +01:00
int conf_bank1 = 0x00000;
int conf_bank2 = 0x00000;
2023-04-27 12:15:59 +02:00
void do_in( void ) {
int i, in, out;
out = 0;
for ( i = 2; i >= 0; i-- ) {
out <<= 4;
out |= saturn.OUT[ i ];
}
in = 0;
for ( i = 0; i < 9; i++ )
if ( out & ( 1 << i ) )
in |= saturn.keybuf.rows[ i ];
#ifdef DEBUG_INOUT
2023-04-27 12:15:59 +02:00
fprintf( stderr, "saturn.OUT=%.3x, saturn.IN=%.4x\n", out, in );
#endif
2023-04-27 12:15:59 +02:00
// PATCH
// http://svn.berlios.de/wsvn/x48?op=comp&compare[]=/trunk@12&compare[]=/trunk@13
// PAS TERRIBLE VISIBLEMENT
if ( saturn.PC == 0x00E31 && !first_press &&
( ( out & 0x10 && in & 0x1 ) || // keys are Backspace
( out & 0x40 && in & 0x7 ) || // right, left & down
( out & 0x80 && in & 0x2 ) ) ) // up arrows
{
for ( i = 0; i < 9; i++ )
if ( out & ( 1 << i ) )
saturn.keybuf.rows[ i ] = 0;
first_press = 1;
} else
first_press = 0;
// FIN PATCH
for ( i = 0; i < 4; i++ ) {
saturn.IN[ i ] = in & 0xf;
in >>= 4;
}
}
2023-04-27 12:15:59 +02:00
void clear_program_stat( int n ) { saturn.PSTAT[ n ] = 0; }
2023-04-27 12:15:59 +02:00
void set_program_stat( int n ) { saturn.PSTAT[ n ] = 1; }
2023-04-27 12:15:59 +02:00
int get_program_stat( int n ) { return saturn.PSTAT[ n ]; }
2023-04-27 12:15:59 +02:00
void register_to_status( unsigned char* r ) {
int i;
2023-04-27 12:15:59 +02:00
for ( i = 0; i < 12; i++ ) {
saturn.PSTAT[ i ] = ( r[ i / 4 ] >> ( i % 4 ) ) & 1;
}
}
2023-04-27 12:15:59 +02:00
void status_to_register( unsigned char* r ) {
int i;
2023-04-27 12:15:59 +02:00
for ( i = 0; i < 12; i++ ) {
if ( saturn.PSTAT[ i ] ) {
r[ i / 4 ] |= 1 << ( i % 4 );
} else {
r[ i / 4 ] &= ~( 1 << ( i % 4 ) ) & 0xf;
}
}
}
2023-04-27 12:15:59 +02:00
void swap_register_status( unsigned char* r ) {
int i, tmp;
2023-04-27 12:15:59 +02:00
for ( i = 0; i < 12; i++ ) {
tmp = saturn.PSTAT[ i ];
saturn.PSTAT[ i ] = ( r[ i / 4 ] >> ( i % 4 ) ) & 1;
if ( tmp ) {
r[ i / 4 ] |= 1 << ( i % 4 );
} else {
r[ i / 4 ] &= ~( 1 << ( i % 4 ) ) & 0xf;
}
}
}
2023-04-27 12:15:59 +02:00
void clear_status( void ) {
int i;
2023-04-27 12:15:59 +02:00
for ( i = 0; i < 12; i++ ) {
saturn.PSTAT[ i ] = 0;
}
}
2023-04-27 12:15:59 +02:00
void set_register_nibble( unsigned char* reg, int n, unsigned char val ) {
reg[ n ] = val;
}
2023-04-27 12:15:59 +02:00
unsigned char get_register_nibble( unsigned char* reg, int n ) {
return reg[ n ];
}
2023-04-27 12:15:59 +02:00
void set_register_bit( unsigned char* reg, int n ) {
reg[ n / 4 ] |= ( 1 << ( n % 4 ) );
}
2023-04-27 12:15:59 +02:00
void clear_register_bit( unsigned char* reg, int n ) {
reg[ n / 4 ] &= ~( 1 << ( n % 4 ) );
}
2023-04-27 12:15:59 +02:00
int get_register_bit( unsigned char* reg, int n ) {
return ( ( int )( reg[ n / 4 ] & ( 1 << ( n % 4 ) ) ) > 0 ) ? 1 : 0;
}
2023-04-27 12:15:59 +02:00
short conf_tab_sx[] = { 1, 2, 2, 2, 2, 0 };
short conf_tab_gx[] = { 1, 2, 2, 2, 2, 0 };
2023-04-27 12:15:59 +02:00
void do_reset( void ) {
int i;
2023-04-27 12:15:59 +02:00
for ( i = 0; i < 6; i++ ) {
if ( opt_gx )
saturn.mem_cntl[ i ].unconfigured = conf_tab_gx[ i ];
else
saturn.mem_cntl[ i ].unconfigured = conf_tab_sx[ i ];
saturn.mem_cntl[ i ].config[ 0 ] = 0x0;
saturn.mem_cntl[ i ].config[ 1 ] = 0x0;
}
#ifdef DEBUG_CONFIG
2023-04-27 12:15:59 +02:00
fprintf( stderr, "%.5lx: RESET\n", saturn.PC );
for ( i = 0; i < 6; i++ ) {
if ( saturn.mem_cntl[ i ].unconfigured )
fprintf( stderr, "MEMORY CONTROLLER %d is unconfigured\n", i );
else
fprintf( stderr,
"MEMORY CONTROLLER %d is configured to %.5lx, %.5lx\n", i,
saturn.mem_cntl[ i ].config[ 0 ],
saturn.mem_cntl[ i ].config[ 1 ] );
}
#endif
}
2023-04-27 12:15:59 +02:00
void do_inton( void ) { saturn.kbd_ien = 1; }
2023-04-27 12:15:59 +02:00
void do_intoff( void ) { saturn.kbd_ien = 0; }
2023-04-27 12:15:59 +02:00
void do_return_interupt( void ) {
if ( saturn.int_pending ) {
#ifdef DEBUG_INTERRUPT
2023-04-27 12:15:59 +02:00
fprintf( stderr, "PC = %.5lx: RTI SERVICE PENDING INTERRUPT\n",
saturn.PC );
#endif
2023-04-27 12:15:59 +02:00
saturn.int_pending = 0;
saturn.intenable = 0;
saturn.PC = 0xf;
} else {
#ifdef DEBUG_INTERRUPT
2023-04-27 12:15:59 +02:00
fprintf( stderr, "PC = %.5lx: RETURN INTERRUPT to ", saturn.PC );
#endif
2023-04-27 12:15:59 +02:00
saturn.PC = pop_return_addr();
#ifdef DEBUG_INTERRUPT
2023-04-27 12:15:59 +02:00
fprintf( stderr, "%.5lx\n", saturn.PC );
#endif
2023-04-27 12:15:59 +02:00
saturn.intenable = 1;
2023-04-27 12:15:59 +02:00
if ( adj_time_pending ) {
schedule_event = 0;
sched_adjtime = 0;
}
}
}
2023-04-27 12:15:59 +02:00
void do_interupt( void ) {
interrupt_called = 1;
if ( saturn.intenable ) {
#ifdef DEBUG_INTERRUPT
2023-04-27 12:15:59 +02:00
fprintf( stderr, "PC = %.5lx: INTERRUPT\n", saturn.PC );
#endif
2023-04-27 12:15:59 +02:00
push_return_addr( saturn.PC );
saturn.PC = 0xf;
saturn.intenable = 0;
}
}
2023-04-27 12:15:59 +02:00
void do_kbd_int( void ) {
interrupt_called = 1;
if ( saturn.intenable ) {
#ifdef DEBUG_KBD_INT
2023-04-27 12:15:59 +02:00
fprintf( stderr, "PC = %.5lx: KBD INT\n", saturn.PC );
#endif
2023-04-27 12:15:59 +02:00
push_return_addr( saturn.PC );
saturn.PC = 0xf;
saturn.intenable = 0;
} else {
#ifdef DEBUG_KBD_INT
2023-04-27 12:15:59 +02:00
fprintf( stderr, "PC = %.5lx: KBD INT PENDING\n", saturn.PC );
#endif
2023-04-27 12:15:59 +02:00
saturn.int_pending = 1;
}
}
2023-04-27 12:15:59 +02:00
void do_reset_interrupt_system( void ) {
int i, gen_intr;
2023-04-27 12:15:59 +02:00
saturn.kbd_ien = 1;
gen_intr = 0;
for ( i = 0; i < 9; i++ ) {
if ( saturn.keybuf.rows[ i ] != 0 ) {
gen_intr = 1;
break;
}
}
if ( gen_intr ) {
do_kbd_int();
}
}
2023-04-27 12:15:59 +02:00
void do_unconfigure( void ) {
int i;
unsigned int conf;
2023-04-27 12:15:59 +02:00
conf = 0;
for ( i = 4; i >= 0; i-- ) {
conf <<= 4;
conf |= saturn.C[ i ];
}
2023-04-27 12:15:59 +02:00
for ( i = 0; i < 6; i++ ) {
if ( saturn.mem_cntl[ i ].config[ 0 ] == conf ) {
if ( opt_gx )
saturn.mem_cntl[ i ].unconfigured = conf_tab_gx[ i ];
else
saturn.mem_cntl[ i ].unconfigured = conf_tab_sx[ i ];
saturn.mem_cntl[ i ].config[ 0 ] = 0x0;
saturn.mem_cntl[ i ].config[ 1 ] = 0x0;
break;
}
}
#ifdef DEBUG_CONFIG
2023-04-27 12:15:59 +02:00
fprintf( stderr, "%.5lx: UNCNFG %.5x:\n", saturn.PC, conf );
for ( i = 0; i < 6; i++ ) {
if ( saturn.mem_cntl[ i ].unconfigured )
fprintf( stderr, "MEMORY CONTROLLER %d is unconfigured\n", i );
else
fprintf( stderr,
"MEMORY CONTROLLER %d is configured to %.5lx, %.5lx\n", i,
saturn.mem_cntl[ i ].config[ 0 ],
saturn.mem_cntl[ i ].config[ 1 ] );
}
#endif
}
2023-04-27 12:15:59 +02:00
void do_configure( void ) {
int i;
unsigned long conf;
2023-04-27 12:15:59 +02:00
conf = 0;
for ( i = 4; i >= 0; i-- ) {
conf <<= 4;
conf |= saturn.C[ i ];
}
2023-04-27 12:15:59 +02:00
for ( i = 0; i < 6; i++ ) {
if ( saturn.mem_cntl[ i ].unconfigured ) {
saturn.mem_cntl[ i ].unconfigured--;
saturn.mem_cntl[ i ].config[ saturn.mem_cntl[ i ].unconfigured ] =
conf;
break;
}
}
#ifdef DEBUG_CONFIG
2023-04-27 12:15:59 +02:00
fprintf( stderr, "%.5lx: CONFIG %.5lx:\n", saturn.PC, conf );
for ( i = 0; i < 6; i++ ) {
if ( saturn.mem_cntl[ i ].unconfigured )
fprintf( stderr, "MEMORY CONTROLLER %d is unconfigured\n", i );
else
fprintf( stderr, "MEMORY CONTROLLER %d at %.5lx, %.5lx\n", i,
saturn.mem_cntl[ i ].config[ 0 ],
saturn.mem_cntl[ i ].config[ 1 ] );
}
#endif
}
2023-04-27 12:15:59 +02:00
int get_identification( void ) {
int i;
static int chip_id[] = { 0, 0, 0, 0, 0x05, 0xf6,
0x07, 0xf8, 0x01, 0xf2, 0, 0 };
int id;
2023-04-27 12:15:59 +02:00
for ( i = 0; i < 6; i++ ) {
if ( saturn.mem_cntl[ i ].unconfigured )
break;
}
if ( i < 6 )
id = chip_id[ 2 * i + ( 2 - saturn.mem_cntl[ i ].unconfigured ) ];
else
id = 0;
#ifdef DEBUG_ID
2023-04-27 12:15:59 +02:00
fprintf( stderr, "%.5lx: C=ID, returning: %x\n", saturn.PC, id );
for ( i = 0; i < 6; i++ ) {
if ( saturn.mem_cntl[ i ].unconfigured == 2 )
fprintf( stderr, "MEMORY CONTROLLER %d is unconfigured\n", i );
else if ( saturn.mem_cntl[ i ].unconfigured == 1 ) {
if ( i == 0 )
fprintf( stderr, "MEMORY CONTROLLER %d unconfigured\n", i );
else
fprintf( stderr,
"MEMORY CONTROLLER %d configured to ????? %.5lx\n", i,
saturn.mem_cntl[ i ].config[ 1 ] );
} else
fprintf( stderr,
"MEMORY CONTROLLER %d configured to %.5lx, %.5lx\n", i,
saturn.mem_cntl[ i ].config[ 0 ],
saturn.mem_cntl[ i ].config[ 1 ] );
}
#endif
2023-04-27 12:15:59 +02:00
for ( i = 0; i < 3; i++ ) {
saturn.C[ i ] = id & 0x0f;
id >>= 4;
}
return 0;
}
2023-04-27 12:15:59 +02:00
void do_shutdown( void ) {
int wake, alarms;
t1_t2_ticks ticks;
2023-04-27 12:15:59 +02:00
if ( device.display_touched ) {
device.display_touched = 0;
update_display();
#ifdef HAVE_XSHM
2023-04-27 12:15:59 +02:00
if ( disp.display_update )
refresh_display();
#endif
2023-04-27 12:15:59 +02:00
}
2023-04-27 12:15:59 +02:00
stop_timer( RUN_TIMER );
start_timer( IDLE_TIMER );
2023-04-27 12:15:59 +02:00
if ( is_zero_register( saturn.OUT, OUT_FIELD ) ) {
#ifdef DEBUG_SHUTDOWN
2023-04-27 12:15:59 +02:00
fprintf( stderr, "%.5lx: SHUTDN: PC = 0\n", saturn.PC );
#endif
2023-04-27 12:15:59 +02:00
saturn.intenable = 1;
saturn.int_pending = 0;
}
#ifdef DEBUG_SHUTDOWN
2023-04-27 12:15:59 +02:00
fprintf( stderr, "%.5lx:\tSHUTDN: Timer 1 Control = %x, Timer 1 = %d\n",
saturn.PC, saturn.t1_ctrl, saturn.timer1 );
fprintf( stderr, "%.5lx:\tSHUTDN: Timer 2 Control = %x, Timer 2 = %ld\n",
saturn.PC, saturn.t2_ctrl, saturn.timer2 );
#endif
2023-04-27 12:15:59 +02:00
if ( in_debugger )
wake = 1;
else
wake = 0;
2023-04-27 12:15:59 +02:00
alarms = 0;
2023-04-27 12:15:59 +02:00
do {
2023-04-27 12:15:59 +02:00
pause();
2023-04-27 12:15:59 +02:00
if ( got_alarm ) {
got_alarm = 0;
#ifdef HAVE_XSHM
2023-04-27 12:15:59 +02:00
if ( disp.display_update )
refresh_display();
#endif
2023-04-27 12:15:59 +02:00
ticks = get_t1_t2();
if ( saturn.t2_ctrl & 0x01 ) {
saturn.timer2 = ticks.t2_ticks;
}
saturn.timer1 = set_t1 - ticks.t1_ticks;
set_t1 = ticks.t1_ticks;
interrupt_called = 0;
if ( GetEvent() ) {
if ( interrupt_called )
wake = 1;
}
if ( saturn.timer2 <= 0 ) {
if ( saturn.t2_ctrl & 0x04 ) {
wake = 1;
}
if ( saturn.t2_ctrl & 0x02 ) {
wake = 1;
saturn.t2_ctrl |= 0x08;
do_interupt();
}
}
if ( saturn.timer1 <= 0 ) {
saturn.timer1 &= 0x0f;
if ( saturn.t1_ctrl & 0x04 ) {
wake = 1;
}
if ( saturn.t1_ctrl & 0x03 ) {
wake = 1;
saturn.t1_ctrl |= 0x08;
do_interupt();
}
}
if ( wake == 0 ) {
interrupt_called = 0;
receive_char();
if ( interrupt_called )
wake = 1;
}
alarms++;
2022-03-24 13:41:22 +01:00
}
2023-04-27 12:15:59 +02:00
if ( enter_debugger ) {
wake = 1;
}
2023-04-27 12:15:59 +02:00
} while ( wake == 0 );
stop_timer( IDLE_TIMER );
start_timer( RUN_TIMER );
}
void set_hardware_stat( int op ) {
if ( op & 1 )
saturn.XM = 1;
if ( op & 2 )
saturn.SB = 1;
if ( op & 4 )
saturn.SR = 1;
if ( op & 8 )
saturn.MP = 1;
}
void clear_hardware_stat( int op ) {
if ( op & 1 )
saturn.XM = 0;
if ( op & 2 )
saturn.SB = 0;
if ( op & 4 )
saturn.SR = 0;
if ( op & 8 )
saturn.MP = 0;
}
int is_zero_hardware_stat( int op ) {
if ( op & 1 )
if ( saturn.XM != 0 )
return 0;
if ( op & 2 )
if ( saturn.SB != 0 )
return 0;
if ( op & 4 )
if ( saturn.SR != 0 )
return 0;
if ( op & 8 )
if ( saturn.MP != 0 )
return 0;
return 1;
}
void push_return_addr( long addr ) {
int i;
if ( ++saturn.rstkp >= NR_RSTK ) {
#if 0
fprintf(stderr, "%.5lx: RSTK overflow !!!\n", saturn.PC);
for (i = saturn.rstkp - 1; i >= 0; i--) {
fprintf(stderr, "\tRSTK[%d] %.5lx\n", i, saturn.rstk[i]);
}
#endif
2023-04-27 12:15:59 +02:00
for ( i = 1; i < NR_RSTK; i++ )
saturn.rstk[ i - 1 ] = saturn.rstk[ i ];
saturn.rstkp--;
}
saturn.rstk[ saturn.rstkp ] = addr;
#ifdef DEBUG_RSTK
2023-04-27 12:15:59 +02:00
fprintf( stderr, "PUSH %.5x:\n", addr );
for ( i = saturn.rstkp; i >= 0; i-- ) {
fprintf( stderr, "RSTK[%d] %.5x\n", i, saturn.rstk[ i ] );
}
#endif
}
2023-04-27 12:15:59 +02:00
long pop_return_addr( void ) {
#ifdef DEBUG_RSTK
2023-04-27 12:15:59 +02:00
int i;
2023-04-27 12:15:59 +02:00
for ( i = saturn.rstkp; i >= 0; i-- ) {
fprintf( stderr, "RSTK[%d] %.5x\n", i, saturn.rstk[ i ] );
}
fprintf( stderr, "POP %.5x:\n",
( saturn.rstkp >= 0 ) ? saturn.rstk[ saturn.rstkp ] : 0 );
#endif
2023-04-27 12:15:59 +02:00
if ( saturn.rstkp < 0 )
return 0;
return saturn.rstk[ saturn.rstkp-- ];
}
2023-04-27 12:15:59 +02:00
char* make_hexstr( long addr, int n ) {
static char str[ 44 ];
int i, t, trunc;
2023-04-27 12:15:59 +02:00
trunc = 0;
if ( n > 40 ) {
n = 40;
trunc = 1;
}
for ( i = 0; i < n; i++ ) {
t = read_nibble( addr + i );
if ( t <= 9 )
str[ i ] = '0' + t;
else
str[ i ] = 'a' + ( t - 10 );
}
str[ n ] = '\0';
if ( trunc ) {
str[ n ] = '.';
str[ n + 1 ] = '.';
str[ n + 2 ] = '.';
str[ n + 3 ] = '\0';
}
return str;
}
2023-04-27 12:15:59 +02:00
void load_constant( unsigned char* reg, int n, long addr ) {
int i, p;
2023-04-27 12:15:59 +02:00
p = saturn.P;
for ( i = 0; i < n; i++ ) {
reg[ p ] = read_nibble( addr + i );
p = ( p + 1 ) & 0xf;
}
}
2023-04-27 12:15:59 +02:00
void load_addr( word_20* dat, long addr, int n ) {
int i;
2023-04-27 12:15:59 +02:00
for ( i = 0; i < n; i++ ) {
*dat &= ~nibble_masks[ i ];
*dat |= read_nibble( addr + i ) << ( i * 4 );
}
}
2023-04-27 12:15:59 +02:00
void load_address( unsigned char* reg, long addr, int n ) {
int i;
2023-04-27 12:15:59 +02:00
for ( i = 0; i < n; i++ ) {
reg[ i ] = read_nibble( addr + i );
}
}
2023-04-27 12:15:59 +02:00
void register_to_address( unsigned char* reg, word_20* dat, int s ) {
int i, n;
2023-04-27 12:15:59 +02:00
if ( s )
n = 4;
else
n = 5;
for ( i = 0; i < n; i++ ) {
*dat &= ~nibble_masks[ i ];
*dat |= ( reg[ i ] & 0x0f ) << ( i * 4 );
}
}
2023-04-27 12:15:59 +02:00
void address_to_register( word_20 dat, unsigned char* reg, int s ) {
int i, n;
2023-04-27 12:15:59 +02:00
if ( s )
n = 4;
else
n = 5;
for ( i = 0; i < n; i++ ) {
reg[ i ] = dat & 0x0f;
dat >>= 4;
}
}
2023-04-27 12:15:59 +02:00
long dat_to_addr( unsigned char* dat ) {
int i;
long addr;
2023-04-27 12:15:59 +02:00
addr = 0;
for ( i = 4; i >= 0; i-- ) {
addr <<= 4;
addr |= ( dat[ i ] & 0xf );
}
return addr;
}
2023-04-27 12:15:59 +02:00
void addr_to_dat( long addr, unsigned char* dat ) {
int i;
for ( i = 0; i < 5; i++ ) {
dat[ i ] = ( addr & 0xf );
addr >>= 4;
}
}
2023-04-27 12:15:59 +02:00
void add_address( word_20* dat, int add ) {
*dat += add;
if ( *dat & ( word_20 )0xfff00000 ) {
saturn.CARRY = 1;
} else {
saturn.CARRY = 0;
}
*dat &= 0xfffff;
}
2023-04-27 12:15:59 +02:00
static int start_fields[] = { -1, 0, 2, 0, 15, 3, 0, 0, -1, 0,
2, 0, 15, 3, 0, 0, 0, 0, 0 };
2023-04-27 12:15:59 +02:00
static int end_fields[] = { -1, -1, 2, 2, 15, 14, 1, 15, -1, -1,
2, 2, 15, 14, 1, 4, 3, 2, 0 };
2023-04-27 12:15:59 +02:00
static inline int get_start( int code ) {
int s;
if ( ( s = start_fields[ code ] ) == -1 ) {
s = saturn.P;
}
return s;
}
2023-04-27 12:15:59 +02:00
static inline int get_end( int code ) {
int e;
2023-04-27 12:15:59 +02:00
if ( ( e = end_fields[ code ] ) == -1 ) {
e = saturn.P;
}
return e;
}
2023-04-27 12:15:59 +02:00
void store( word_20 dat, unsigned char* reg, int code ) {
int i, s, e;
2023-04-27 12:15:59 +02:00
s = get_start( code );
e = get_end( code );
for ( i = s; i <= e; i++ ) {
write_nibble( dat++, reg[ i ] );
}
}
2023-04-27 12:15:59 +02:00
void store_n( word_20 dat, unsigned char* reg, int n ) {
int i;
2023-04-27 12:15:59 +02:00
for ( i = 0; i < n; i++ ) {
write_nibble( dat++, reg[ i ] );
}
}
2023-04-27 12:15:59 +02:00
void recall( unsigned char* reg, word_20 dat, int code ) {
int i, s, e;
2023-04-27 12:15:59 +02:00
s = get_start( code );
e = get_end( code );
for ( i = s; i <= e; i++ ) {
reg[ i ] = read_nibble_crc( dat++ );
}
}
2023-04-27 12:15:59 +02:00
void recall_n( unsigned char* reg, word_20 dat, int n ) {
int i;
2023-04-27 12:15:59 +02:00
for ( i = 0; i < n; i++ ) {
reg[ i ] = read_nibble_crc( dat++ );
}
}