mirror of
https://github.com/PDP-10/klh10.git
synced 2026-01-11 23:52:54 +00:00
Split ksim into a main application, and a library file.
This commit is contained in:
parent
e80f454a98
commit
62e4a25818
@ -4,6 +4,6 @@ LDFLAGS = -g
|
||||
all: ksim
|
||||
|
||||
clean:
|
||||
rm ksim.o ksim
|
||||
rm ksim.o main.o ksim
|
||||
|
||||
ksim: ksim.o
|
||||
ksim: ksim.o main.o
|
||||
|
||||
121
src/ksim/ksim.c
121
src/ksim/ksim.c
@ -36,15 +36,14 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include "ksim.h"
|
||||
|
||||
// The following definition is used to suppress warnings about unused
|
||||
// function parameters, but might not work with compilers other than
|
||||
@ -52,9 +51,9 @@
|
||||
// declared with an empty defnition.
|
||||
#define UNUSED __attribute__((unused))
|
||||
|
||||
char *progname;
|
||||
static char *progname;
|
||||
|
||||
void usage (FILE *f)
|
||||
static void usage (FILE *f)
|
||||
{
|
||||
fprintf (f, "usage: %s [options...] <bootstrap>\n", progname);
|
||||
fprintf (f, "options:\n");
|
||||
@ -65,9 +64,7 @@ void usage (FILE *f)
|
||||
}
|
||||
|
||||
// generate fatal error message to stderr, doesn't return
|
||||
void fatal (int ret, char *format, ...) __attribute__ ((noreturn));
|
||||
|
||||
void fatal (int ret, char *format, ...)
|
||||
void ksim_fatal (int ret, char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
@ -89,7 +86,7 @@ FILE *trace_f = NULL;
|
||||
struct termios tty_cooked;
|
||||
struct termios tty_raw;
|
||||
|
||||
void console_init (void)
|
||||
static void console_init (void)
|
||||
{
|
||||
tcgetattr (STDIN_FILENO, & tty_cooked);
|
||||
tcgetattr (STDIN_FILENO, & tty_raw);
|
||||
@ -101,7 +98,7 @@ void console_init (void)
|
||||
tcsetattr (STDIN_FILENO, TCSAFLUSH, & tty_raw);
|
||||
}
|
||||
|
||||
void console_cleanup (void)
|
||||
static void console_cleanup (void)
|
||||
{
|
||||
tcsetattr (STDIN_FILENO, TCSAFLUSH, & tty_cooked);
|
||||
}
|
||||
@ -143,7 +140,7 @@ bool flag_cy;
|
||||
// R = sign of result
|
||||
// For subtraction and comparisons, replace O2 with not O2.
|
||||
|
||||
void set_f (uint8_t f_value)
|
||||
static void set_f (uint8_t f_value)
|
||||
{
|
||||
flag_sp_data = (f_value & 0x80) | (((f_value >> 5) ^ ~f_value) & 0x04);
|
||||
|
||||
@ -159,7 +156,7 @@ void set_f (uint8_t f_value)
|
||||
flag_cy = f_value & 1;
|
||||
}
|
||||
|
||||
uint8_t get_f (void)
|
||||
static uint8_t get_f (void)
|
||||
{
|
||||
return ((FLAG_S << 7) | (FLAG_Z << 6) | (FLAG_AC << 4) |
|
||||
(FLAG_P << 2) | (1 << 1) | (FLAG_CY << 0));
|
||||
@ -204,7 +201,7 @@ uint8_t get_f (void)
|
||||
|
||||
|
||||
|
||||
uint8_t mem [0x10000];
|
||||
static uint8_t mem [0x10000];
|
||||
|
||||
static inline uint8_t mem_read (uint16_t addr)
|
||||
{
|
||||
@ -1003,7 +1000,7 @@ static void op_IN (uint8_t opcode UNUSED)
|
||||
timeout.tv_usec = 0;
|
||||
status = select (1, & read_fds, & write_fds, & except_fds, & timeout);
|
||||
if (status < 0)
|
||||
fatal (EX_IOERR, "select() error %d", status);
|
||||
ksim_fatal (EX_IOERR, "select() error %d", status);
|
||||
REG_A = FD_ISSET (STDIN_FILENO, & read_fds);
|
||||
break;
|
||||
case 0x01:
|
||||
@ -1079,7 +1076,7 @@ typedef struct
|
||||
addr_mode_t mode;
|
||||
} op_info_t;
|
||||
|
||||
op_info_t op_tbl [256] =
|
||||
static op_info_t op_tbl [256] =
|
||||
{
|
||||
[0000] = { 4, op_NOP, "NOP", MODE_IMP },
|
||||
[0001] = { 10, op_LXI, "LXI BC,#%04xh", MODE_IMM2 },
|
||||
@ -1403,7 +1400,7 @@ op_info_t op_tbl [256] =
|
||||
};
|
||||
|
||||
|
||||
void disassemble_inst (FILE *f, uint16_t addr)
|
||||
static void disassemble_inst (FILE *f, uint16_t addr)
|
||||
{
|
||||
uint8_t opcode = mem_read (addr++);
|
||||
addr_mode_t mode = op_tbl [opcode].mode;
|
||||
@ -1423,11 +1420,11 @@ void disassemble_inst (FILE *f, uint16_t addr)
|
||||
fprintf (f, op_tbl [opcode].mnem, data);
|
||||
break;
|
||||
default:
|
||||
fprintf (f, op_tbl [opcode].mnem);
|
||||
fprintf (f, "%s", op_tbl [opcode].mnem);
|
||||
}
|
||||
}
|
||||
|
||||
void execute_instruction (void)
|
||||
bool ksim_execute_instruction (void)
|
||||
{
|
||||
uint8_t opcode;
|
||||
uint16_t old_pc;
|
||||
@ -1446,13 +1443,14 @@ void execute_instruction (void)
|
||||
fprintf (trace_f, " BC=%04x DE=%04x HL=%04x AF=%02x%02x\n",
|
||||
REG_PAIR_BC, REG_PAIR_DE, REG_PAIR_HL, REG_A, get_f ());
|
||||
}
|
||||
return halted;
|
||||
}
|
||||
|
||||
void reset_processor (void)
|
||||
void ksim_reset_processor (uint16_t a)
|
||||
{
|
||||
halted = false;
|
||||
interrupt_enabled = false;
|
||||
pc = 0x0000;
|
||||
pc = a;
|
||||
}
|
||||
|
||||
|
||||
@ -1475,7 +1473,7 @@ static void init_parity_table (void)
|
||||
}
|
||||
|
||||
|
||||
uint32_t hex_extract (char *p, int count)
|
||||
static uint32_t hex_extract (char *p, int count)
|
||||
{
|
||||
uint32_t data = 0;
|
||||
while (count--)
|
||||
@ -1492,10 +1490,9 @@ uint32_t hex_extract (char *p, int count)
|
||||
return data;
|
||||
}
|
||||
|
||||
uint32_t start_addr = 0;
|
||||
|
||||
void load_hex (char *fn)
|
||||
uint32_t ksim_load_hex (char *fn)
|
||||
{
|
||||
uint32_t start_addr = 0;
|
||||
FILE *f;
|
||||
char buf [257];
|
||||
|
||||
@ -1535,9 +1532,10 @@ void load_hex (char *fn)
|
||||
}
|
||||
|
||||
fclose (f);
|
||||
return start_addr;
|
||||
}
|
||||
|
||||
void load_binary (char *fn)
|
||||
uint32_t ksim_load_binary (char *fn)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
@ -1552,80 +1550,15 @@ void load_binary (char *fn)
|
||||
fread (mem, sizeof (mem), 1, f);
|
||||
|
||||
fclose (f);
|
||||
|
||||
start_addr = 0x0000;
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
int main (int argc, char **argv)
|
||||
void ksim_init (char *x, bool b, FILE *f)
|
||||
{
|
||||
bool hex_file = false;
|
||||
bool bin_file = false;
|
||||
char *fn = NULL;
|
||||
char *trace_fn = NULL;
|
||||
|
||||
progname = argv [0];
|
||||
|
||||
progname = x;
|
||||
bdos_sim = b;
|
||||
trace_f = f;
|
||||
init_parity_table ();
|
||||
|
||||
while (--argc)
|
||||
{
|
||||
argv++;
|
||||
if (argv [0][0] == '-')
|
||||
{
|
||||
if (strcmp (argv [0], "-b") == 0)
|
||||
bin_file = true;
|
||||
else if (strcmp (argv [0], "-h") == 0)
|
||||
hex_file = true;
|
||||
else if (strcmp (argv [0], "-t") == 0)
|
||||
{
|
||||
if (! --argc)
|
||||
fatal (EX_USAGE, "Missing argument for '-t' option.\n");
|
||||
if (trace_fn)
|
||||
fatal (EX_USAGE, "Only one '-t' option may be specified.\n");
|
||||
trace_fn = argv [1];
|
||||
argv++;
|
||||
}
|
||||
else if (strcmp (argv [0], "--bdos") == 0)
|
||||
bdos_sim = true;
|
||||
else
|
||||
fatal (EX_USAGE, "Unknown option '%s'.\n", argv [0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! fn)
|
||||
fn = argv [0];
|
||||
else
|
||||
fatal (EX_USAGE, "Only one bootstrap file may be specified.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (! fn)
|
||||
fatal (EX_USAGE, "A bootstrap file must be specified.\n");
|
||||
|
||||
if (hex_file && bin_file)
|
||||
fatal (EX_USAGE, "The -b and -h options are mutually exclusive.\n");
|
||||
|
||||
if (trace_fn)
|
||||
{
|
||||
trace_f = fopen (trace_fn, "w");
|
||||
if (! trace_f)
|
||||
fatal (EX_CANTCREAT, "Can't create trace file '%f'.\n", trace_fn);
|
||||
}
|
||||
|
||||
if (hex_file)
|
||||
load_hex (fn);
|
||||
else if (bin_file)
|
||||
load_binary (fn);
|
||||
|
||||
console_init ();
|
||||
atexit (console_cleanup);
|
||||
|
||||
reset_processor ();
|
||||
|
||||
pc = start_addr;
|
||||
|
||||
while (! halted)
|
||||
execute_instruction ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
47
src/ksim/ksim.h
Normal file
47
src/ksim/ksim.h
Normal file
@ -0,0 +1,47 @@
|
||||
// ksim: an 8080 simulator
|
||||
// Copyright 2011, 2014 Eric Smith <spacewar@gmail.com>
|
||||
|
||||
// ksim was written with the intent of having an accurate 8080 simulator
|
||||
// for reference. Maximum simulation performance is not a goal of ksim,
|
||||
// and there is little performance optimization. There are many available
|
||||
// Z80 simulators, but few for the 8080, and even fewer that simulate the
|
||||
// processor flags correctly. While it is certainly possible that there
|
||||
// may be some remaining bugs in ksim, it does pass several published
|
||||
// 8080 exerciser programs, including one which tests the processor flags
|
||||
// fairly extensively:
|
||||
// http://www.idb.me.uk/sunhillow/8080.html
|
||||
|
||||
// This version of ksim does not include any interrupts, and has only
|
||||
// rudimentary I/O support, which is only intended as an example. Console
|
||||
// I/O is supported via stdin and stdout, using input port 0 for input
|
||||
// status, input port 1 for character input, and output port 0 for character
|
||||
// output. It is assumed that stdin and stdout are a tty device, which is
|
||||
// put into raw mode. Because of this use of raw mode, the interrupt
|
||||
// character (usually Control-C) will not stop the program, so use of
|
||||
// another shell to send a TERM signal (typically by use of the "kill"
|
||||
// command) is required if the simulated program does not halt.
|
||||
|
||||
// LICENSE:
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of version 3 of the GNU General Public License as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
// 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
extern void ksim_init (char *, bool, FILE *);
|
||||
extern void ksim_fatal (int ret, char *format, ...) __attribute__ ((noreturn));
|
||||
extern uint32_t ksim_load_binary (char *);
|
||||
extern uint32_t ksim_load_hex (char *);
|
||||
extern void ksim_reset_processor (uint16_t);
|
||||
extern bool ksim_execute_instruction (void);
|
||||
116
src/ksim/main.c
Normal file
116
src/ksim/main.c
Normal file
@ -0,0 +1,116 @@
|
||||
// ksim: an 8080 simulator
|
||||
// Copyright 2011, 2014 Eric Smith <spacewar@gmail.com>
|
||||
|
||||
// ksim was written with the intent of having an accurate 8080 simulator
|
||||
// for reference. Maximum simulation performance is not a goal of ksim,
|
||||
// and there is little performance optimization. There are many available
|
||||
// Z80 simulators, but few for the 8080, and even fewer that simulate the
|
||||
// processor flags correctly. While it is certainly possible that there
|
||||
// may be some remaining bugs in ksim, it does pass several published
|
||||
// 8080 exerciser programs, including one which tests the processor flags
|
||||
// fairly extensively:
|
||||
// http://www.idb.me.uk/sunhillow/8080.html
|
||||
|
||||
// This version of ksim does not include any interrupts, and has only
|
||||
// rudimentary I/O support, which is only intended as an example. Console
|
||||
// I/O is supported via stdin and stdout, using input port 0 for input
|
||||
// status, input port 1 for character input, and output port 0 for character
|
||||
// output. It is assumed that stdin and stdout are a tty device, which is
|
||||
// put into raw mode. Because of this use of raw mode, the interrupt
|
||||
// character (usually Control-C) will not stop the program, so use of
|
||||
// another shell to send a TERM signal (typically by use of the "kill"
|
||||
// command) is required if the simulated program does not halt.
|
||||
|
||||
// LICENSE:
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of version 3 of the GNU General Public License as
|
||||
// published by the Free Software Foundation.
|
||||
|
||||
// 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include "ksim.h"
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
bool hex_file = false;
|
||||
bool bin_file = false;
|
||||
bool bdos_sim = false;
|
||||
char *fn = NULL;
|
||||
char *trace_fn = NULL;
|
||||
FILE *trace_f = NULL;
|
||||
int halted = 0;
|
||||
char *progname;
|
||||
uint16_t start_addr;
|
||||
|
||||
progname = argv [0];
|
||||
|
||||
while (--argc)
|
||||
{
|
||||
argv++;
|
||||
if (argv [0][0] == '-')
|
||||
{
|
||||
if (strcmp (argv [0], "-b") == 0)
|
||||
bin_file = true;
|
||||
else if (strcmp (argv [0], "-h") == 0)
|
||||
hex_file = true;
|
||||
else if (strcmp (argv [0], "-t") == 0)
|
||||
{
|
||||
if (! --argc)
|
||||
ksim_fatal (EX_USAGE, "Missing argument for '-t' option.\n");
|
||||
if (trace_fn)
|
||||
ksim_fatal (EX_USAGE, "Only one '-t' option may be specified.\n");
|
||||
trace_fn = argv [1];
|
||||
argv++;
|
||||
}
|
||||
else if (strcmp (argv [0], "--bdos") == 0)
|
||||
bdos_sim = true;
|
||||
else
|
||||
ksim_fatal (EX_USAGE, "Unknown option '%s'.\n", argv [0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! fn)
|
||||
fn = argv [0];
|
||||
else
|
||||
ksim_fatal (EX_USAGE, "Only one bootstrap file may be specified.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (! fn)
|
||||
ksim_fatal (EX_USAGE, "A bootstrap file must be specified.\n");
|
||||
|
||||
if (hex_file && bin_file)
|
||||
ksim_fatal (EX_USAGE, "The -b and -h options are mutually exclusive.\n");
|
||||
|
||||
if (trace_fn)
|
||||
{
|
||||
trace_f = fopen (trace_fn, "w");
|
||||
if (! trace_f)
|
||||
ksim_fatal (EX_CANTCREAT, "Can't create trace file '%f'.\n", trace_fn);
|
||||
}
|
||||
|
||||
ksim_init (progname, bdos_sim, trace_f);
|
||||
|
||||
if (hex_file)
|
||||
start_addr = ksim_load_hex (fn);
|
||||
else if (bin_file)
|
||||
start_addr = ksim_load_binary (fn);
|
||||
|
||||
ksim_reset_processor (start_addr);
|
||||
|
||||
while (! halted)
|
||||
halted = ksim_execute_instruction ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user