simh-testsetgenerator/BESM6/besm6_sys.c

734 lines
23 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* besm6_sys.c: BESM-6 simulator interface
*
* Copyright (c) 2009, Serge Vakulenko
* Copyright (c) 2009, Leonid Broukhis
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* SERGE VAKULENKO OR LEONID BROUKHIS BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
* OR OTHER DEALINGS IN THE SOFTWARE.
* Except as contained in this notice, the name of Leonid Broukhis or
* Serge Vakulenko shall not be used in advertising or otherwise to promote
* the sale, use or other dealings in this Software without prior written
* authorization from Leonid Broukhis and Serge Vakulenko.
*
* This file implements three essential functions:
*
* sim_load() - loading and dumping memory and CPU state
* in a way, specific for BESM-6 architecture
* fprint_sym() - print a machune instruction using
* opcode mnemonic or in a digital format
* parse_sym() - scan a string and build an instruction
* word from it
*/
#include "besm6_defs.h"
#include <math.h>
const char *opname_short_bemsh [64] = {
"зп", "зпм", "рег", "счм", "сл", "вч", "вчоб","вчаб",
"сч", "и", "нтж", "слц", "знак","или", "дел", "умн",
"сбр", "рзб", "чед", "нед", "слп", "вчп", "сд", "рж",
"счрж","счмр","э32", "увв", "слпа","вчпа","сда", "ржа",
"уи", "уим", "счи", "счим","уии", "сли", "э46", "э47",
"э50", "э51", "э52", "э53", "э54", "э55", "э56", "э57",
"э60", "э61", "э62", "э63", "э64", "э65", "э66", "э67",
"э70", "э71", "э72", "э73", "э74", "э75", "э76", "э77",
};
static const char *opname_long_bemsh [16] = {
"э20", "э21", "мода","мод", "уиа", "слиа","по", "пе",
"пб", "пв", "выпр","стоп","пио", "пино","э36", "цикл",
};
const char *opname_short_madlen [64] = {
"atx", "stx", "mod", "xts", "a+x", "a-x", "x-a", "amx",
"xta", "aax", "aex", "arx", "avx", "aox", "a/x", "a*x",
"apx", "aux", "acx", "anx", "e+x", "e-x", "asx", "xtr",
"rte", "yta", "*32", "ext", "e+n", "e-n", "asn", "ntr",
"ati", "sti", "ita", "its", "mtj", "j+m", "*46", "*47",
"*50", "*51", "*52", "*53", "*54", "*55", "*56", "*57",
"*60", "*61", "*62", "*63", "*64", "*65", "*66", "*67",
"*70", "*71", "*72", "*73", "*74", "*75", "*76", "*77",
};
static const char *opname_long_madlen [16] = {
"*20", "*21", "utc", "wtc", "vtm", "utm", "uza", "u1a",
"uj", "vjm", "ij", "stop", "vzm", "v1m", "*36", "vlm",
};
/*
* Выдача мнемоники по коду инструкции.
* Код должен быть в диапазоне 000..077 или 0200..0370.
*/
const char *besm6_opname (int opcode)
{
if (sim_switches & SWMASK ('L')) {
/* Latin mnemonics. */
if (opcode & 0200)
return opname_long_madlen [(opcode >> 3) & 017];
return opname_short_madlen [opcode];
}
if (opcode & 0200)
return opname_long_bemsh [(opcode >> 3) & 017];
return opname_short_bemsh [opcode];
}
/*
* Выдача кода инструкции по мнемонике (UTF-8).
*/
int besm6_opcode (char *instr)
{
int i;
for (i=0; i<64; ++i)
if (strcmp (opname_short_bemsh[i], instr) == 0 ||
strcmp (opname_short_madlen[i], instr) == 0)
return i;
for (i=0; i<16; ++i)
if (strcmp (opname_long_bemsh[i], instr) == 0 ||
strcmp (opname_long_madlen[i], instr) == 0)
return (i << 3) | 0200;
return -1;
}
/*
* Выдача на консоль и в файл протокола.
* Если первый символ формата - подчерк, на консоль не печатаем.
* Добавляет перевод строки.
*/
void besm6_log (const char *fmt, ...)
{
va_list args;
if (*fmt == '_')
++fmt;
else {
va_start (args, fmt);
vprintf (fmt, args);
printf ("\r\n");
va_end (args);
}
if (sim_log) {
va_start (args, fmt);
vfprintf (sim_log, fmt, args);
if (sim_log == stdout)
fprintf (sim_log, "\r");
fprintf (sim_log, "\n");
fflush (sim_log);
va_end (args);
}
}
/*
* Не добавляет перевод строки.
*/
void besm6_log_cont (const char *fmt, ...)
{
va_list args;
if (*fmt == '_')
++fmt;
else {
va_start (args, fmt);
vprintf (fmt, args);
va_end (args);
}
if (sim_log) {
va_start (args, fmt);
vfprintf (sim_log, fmt, args);
fflush (sim_log);
va_end (args);
}
}
/*
* Выдача на консоль и в файл отладки: если включён режим "cpu debug".
* Добавляет перевод строки.
*/
void besm6_debug (const char *fmt, ...)
{
va_list args;
va_start (args, fmt);
vprintf (fmt, args);
printf ("\r\n");
va_end (args);
if (sim_deb && sim_deb != stdout) {
va_start (args, fmt);
vfprintf (sim_deb, fmt, args);
fprintf (sim_deb, "\n");
fflush (sim_deb);
va_end (args);
}
}
/*
* Преобразование вещественного числа в формат БЭСМ-6.
*
* Представление чисел в IEEE 754 (double):
* 64 63———53 52————1
* знак порядок мантисса
* Старший (53-й) бит мантиссы не хранится и всегда равен 1.
*
* Представление чисел в БЭСМ-6:
* 48——42 41 40————————————————1
* порядок знак мантисса в доп. коде
*/
t_value ieee_to_besm6 (double d)
{
t_value word;
int exponent;
int sign;
sign = d < 0;
if (sign)
d = -d;
d = frexp (d, &exponent);
/* 0.5 <= d < 1.0 */
d = ldexp (d, 40);
word = (t_value)d;
if (d - word >= 0.5)
word += 1; /* Округление. */
if (exponent < -64)
return 0LL; /* Близкое к нулю число */
if (exponent > 63) {
return sign ?
0xFEFFFFFFFFFFLL : /* Максимальное число */
0xFF0000000000LL; /* Минимальное число */
}
if (sign)
word = 0x20000000000LL-word; /* Знак. */
word |= ((t_value) (exponent + 64)) << 41;
return word;
}
double besm6_to_ieee (t_value word)
{
double mantissa;
int exponent;
/* Убираем свертку */
word &= BITS48;
/* Сдвигаем так, чтобы знак мантиссы пришелся на знак целого;
* таким образом, mantissa равно исходной мантиссе, умноженной на 2**63.
*/
mantissa = (double)(((t_int64) word) << (64 - 48 + 7));
exponent = word >> 41;
/* Порядок смещен вверх на 64, и мантиссу нужно скорректировать */
return ldexp (mantissa, exponent - 64 - 63);
}
/*
* Пропуск пробелов.
*/
CONST char *skip_spaces (CONST char *p)
{
for (;;) {
if (*p == (char) 0xEF && p[1] == (char) 0xBB && p[2] == (char) 0xBF) {
/* Skip zero width no-break space. */
p += 3;
continue;
}
if (*p == ' ' || *p == '\t' || *p == '\r') {
++p;
continue;
}
return p;
}
}
/*
* Fetch Unicode symbol from UTF-8 string.
* Advance string pointer.
*/
int utf8_to_unicode (CONST char **p)
{
int c1, c2, c3;
c1 = (unsigned char) *(*p)++;
if (! (c1 & 0x80))
return c1;
c2 = (unsigned char) *(*p)++;
if (! (c1 & 0x20))
return (c1 & 0x1f) << 6 | (c2 & 0x3f);
c3 = (unsigned char) *(*p)++;
return (c1 & 0x0f) << 12 | (c2 & 0x3f) << 6 | (c3 & 0x3f);
}
char *besm6_parse_octal (const char *cptr, int *offset)
{
char *eptr;
*offset = strtol (cptr, &eptr, 8);
if (eptr == cptr)
return 0;
return eptr;
}
static CONST char *get_alnum (CONST char *iptr, char *optr)
{
while ((*iptr >= 'a' && *iptr<='z') ||
(*iptr >= 'A' && *iptr<='Z') ||
(*iptr >= '0' && *iptr<='9') || (*iptr & 0x80)) {
*optr++ = *iptr++;
}
*optr = 0;
return iptr;
}
/*
* Parse single instruction (half word).
* Allow mnemonics or octal code.
*/
CONST char *parse_instruction (CONST char *cptr, uint32 *val)
{
int opcode, reg, addr, negate;
char gbuf[CBUFSIZE];
cptr = skip_spaces (cptr); /* absorb spaces */
if (*cptr >= '0' && *cptr <= '7') {
/* Восьмеричное представление. */
cptr = besm6_parse_octal (cptr, &reg); /* get register */
if (! cptr || reg > 15) {
/*printf ("Bad register\n");*/
return 0;
}
cptr = skip_spaces (cptr); /* absorb spaces */
if (*cptr == '2' || *cptr == '3') {
/* Длинная команда. */
cptr = besm6_parse_octal (cptr, &opcode);
if (! cptr || opcode < 020 || opcode > 037) {
/*printf ("Bad long opcode\n");*/
return 0;
}
opcode <<= 3;
} else {
/* Короткая команда. */
cptr = besm6_parse_octal (cptr, &opcode);
if (! cptr || opcode > 0177) {
/*printf ("Bad short opcode\n");*/
return 0;
}
}
cptr = besm6_parse_octal (cptr, &addr); /* get address */
if (! cptr || addr > BITS(15) ||
(opcode <= 0177 && addr > BITS(12))) {
/*printf ("Bad address\n");*/
return 0;
}
} else {
/* Мнемоническое представление команды. */
cptr = get_alnum (cptr, gbuf); /* get opcode */
opcode = besm6_opcode (gbuf);
if (opcode < 0) {
/*printf ("Bad opname: %s\n", gbuf);*/
return 0;
}
negate = 0;
cptr = skip_spaces (cptr); /* absorb spaces */
if (*cptr == '-') { /* negative offset */
negate = 1;
cptr = skip_spaces (cptr + 1); /* absorb spaces */
}
addr = 0;
if (*cptr >= '0' && *cptr <= '7') {
/* Восьмеричный адрес. */
cptr = besm6_parse_octal (cptr, &addr);
if (! cptr || addr > BITS(15)) {
/*printf ("Bad address: %o\n", addr);*/
return 0;
}
if (negate)
addr = (- addr) & BITS(15);
if (opcode <= 077 && addr > BITS(12)) {
if (addr < 070000) {
/*printf ("Bad short address: %o\n", addr);*/
return 0;
}
opcode |= 0100;
addr &= BITS(12);
}
}
reg = 0;
cptr = skip_spaces (cptr); /* absorb spaces */
if (*cptr == '(') {
/* Индекс-регистр в скобках. */
cptr = besm6_parse_octal (cptr+1, &reg);
if (! cptr || reg > 15) {
/*printf ("Bad register: %o\n", reg);*/
return 0;
}
cptr = skip_spaces (cptr); /* absorb spaces */
if (*cptr != ')') {
/*printf ("No closing brace\n");*/
return 0;
}
++cptr;
}
}
*val = reg << 20 | opcode << 12 | addr;
return cptr;
}
/*
* Instruction parse: two commands per word.
*/
t_stat parse_instruction_word (CONST char *cptr, t_value *val)
{
uint32 left, right;
*val = 0;
cptr = parse_instruction (cptr, &left);
if (! cptr)
return SCPE_ARG;
right = 0;
cptr = skip_spaces (cptr);
if (*cptr == ',') {
cptr = parse_instruction (cptr + 1, &right);
if (! cptr)
return SCPE_ARG;
}
cptr = skip_spaces (cptr); /* absorb spaces */
if (*cptr != 0 && *cptr != ';' && *cptr != '\n' && *cptr != '\r') {
/*printf ("Extra symbols at eoln: %s\n", cptr);*/
return SCPE_2MARG;
}
*val = (t_value) left << 24 | right;
return SCPE_OK;
}
/*
* Печать машинной инструкции с мнемоникой.
*/
void besm6_fprint_cmd (FILE *of, uint32 cmd)
{
int reg, opcode, addr;
reg = (cmd >> 20) & 017;
if (cmd & BBIT(20)) {
opcode = (cmd >> 12) & 0370;
addr = cmd & BITS(15);
} else {
opcode = (cmd >> 12) & 077;
addr = cmd & 07777;
if (cmd & BBIT(19))
addr |= 070000;
}
fprintf (of, "%s", besm6_opname (opcode));
if (addr) {
fprintf (of, " ");
if (addr >= 077700)
fprintf (of, "-%o", (addr ^ 077777) + 1);
else
fprintf (of, "%o", addr);
}
if (reg) {
if (! addr)
fprintf (of, " ");
fprintf (of, "(%o)", reg);
}
}
/*
* Печать машинной инструкции в восьмеричном виде.
*/
void besm6_fprint_insn (FILE *of, uint32 insn)
{
if (insn & BBIT(20))
fprintf (of, "%02o %02o %05o ",
insn >> 20, (insn >> 15) & 037, insn & BITS(15));
else
fprintf (of, "%02o %03o %04o ",
insn >> 20, (insn >> 12) & 0177, insn & 07777);
}
/*
* Symbolic decode
*
* Inputs:
* *of = output stream
* addr = current PC
* *val = pointer to data
* *uptr = pointer to unit
* sw = switches
* Outputs:
* return = status code
*/
t_stat fprint_sym (FILE *of, t_addr addr, t_value *val,
UNIT *uptr, int32 sw)
{
t_value cmd;
if (uptr && (uptr != &cpu_unit)) /* must be CPU */
return SCPE_ARG;
cmd = val[0];
if (sw & SWMASK ('M')) { /* symbolic decode? */
if (sw & SIM_SW_STOP && addr == PC && !(RUU & RUU_RIGHT_INSTR))
fprintf (of, "-> ");
besm6_fprint_cmd (of, (uint32)(cmd >> 24));
if (sw & SIM_SW_STOP) /* stop point */
fprintf (of, ", ");
else
fprintf (of, ",\n\t");
if (sw & SIM_SW_STOP && addr == PC && (RUU & RUU_RIGHT_INSTR))
fprintf (of, "-> ");
besm6_fprint_cmd (of, cmd & BITS(24));
} else if (sw & SWMASK ('I')) {
besm6_fprint_insn (of, (cmd >> 24) & BITS(24));
besm6_fprint_insn (of, cmd & BITS(24));
} else if (sw & SWMASK ('F')) {
fprintf (of, "%#.2g", besm6_to_ieee(cmd));
} else if (sw & SWMASK ('B')) {
fprintf (of, "%03o %03o %03o %03o %03o %03o",
(int) (cmd >> 40) & 0377,
(int) (cmd >> 32) & 0377,
(int) (cmd >> 24) & 0377,
(int) (cmd >> 16) & 0377,
(int) (cmd >> 8) & 0377,
(int) cmd & 0377);
} else if (sw & SWMASK ('X')) {
fprintf (of, "%013llx", cmd);
} else
fprintf (of, "%04o %04o %04o %04o",
(int) (cmd >> 36) & 07777,
(int) (cmd >> 24) & 07777,
(int) (cmd >> 12) & 07777,
(int) cmd & 07777);
return SCPE_OK;
}
/*
* Symbolic input
*
* Inputs:
* *cptr = pointer to input string
* addr = current PC
* *uptr = pointer to unit
* *val = pointer to output values
* sw = switches
* Outputs:
* status = error status
*/
t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)
{
int32 i;
if (uptr && (uptr != &cpu_unit)) /* must be CPU */
return SCPE_ARG;
if (! parse_instruction_word (cptr, val)) /* symbolic parse? */
return SCPE_OK;
val[0] = 0;
for (i=0; i<16; i++) {
if (*cptr < '0' || *cptr > '7')
break;
val[0] = (val[0] << 3) | (*cptr - '0');
cptr = skip_spaces (cptr+1); /* next char */
}
if (*cptr != 0 && *cptr != ';' && *cptr != '\n' && *cptr != '\r') {
/*printf ("Extra symbols at eoln: %s\n", cptr);*/
return SCPE_2MARG;
}
return SCPE_OK;
}
/*
* Чтение строки входного файла.
* Форматы строк:
* п 76543 - адрес пуска
* в 12345 - адрес ввода
* ч -123.45e+6 - вещественное число
* с 0123 4567 0123 4567 - восьмеричное слово
* к 00 22 00000 00 010 0000 - команды
*/
t_stat besm6_read_line (FILE *input, int *type, t_value *val)
{
char buf [512];
CONST char *p;
int i, c;
again:
if (! fgets (buf, sizeof (buf), input)) {
*type = 0;
return SCPE_OK;
}
p = skip_spaces (buf);
if (*p == '\n' || *p == ';')
goto again;
c = utf8_to_unicode (&p);
if (c == CYRILLIC_SMALL_LETTER_VE ||
c == CYRILLIC_CAPITAL_LETTER_VE ||
c == 'b' || c == 'B') {
/* Адрес размещения данных. */
*type = ':';
*val = strtol (p, 0, 8);
return SCPE_OK;
}
if (c == CYRILLIC_SMALL_LETTER_PE ||
c == CYRILLIC_CAPITAL_LETTER_PE ||
c == 'p' || c == 'P') {
/* Стартовый адрес. */
*type = '@';
*val = strtol (p, 0, 8);
return SCPE_OK;
}
if (c == CYRILLIC_SMALL_LETTER_CHE ||
c == CYRILLIC_CAPITAL_LETTER_CHE ||
c == 'f' || c == 'F') {
/* Вещественное число. */
*type = '=';
*val = ieee_to_besm6 (strtod (p, 0));
return SCPE_OK;
}
if (c == CYRILLIC_SMALL_LETTER_ES ||
c == CYRILLIC_CAPITAL_LETTER_ES ||
c == 'c' || c == 'C') {
/* Восьмеричное слово. */
*type = '=';
*val = 0;
for (i=0; i<16; ++i) {
p = skip_spaces (p);
if (*p < '0' || *p > '7') {
if (i == 0) {
/* слишком короткое слово */
goto bad;
}
break;
}
*val = *val << 3 | (*p++ - '0');
}
return SCPE_OK;
}
if (c == CYRILLIC_SMALL_LETTER_KA ||
c == CYRILLIC_CAPITAL_LETTER_KA ||
c == 'k' || c == 'K') {
/* Команда. */
*type = '*';
if (parse_instruction_word (p, val) != SCPE_OK)
goto bad;
return SCPE_OK;
}
/* Неверная строка входного файла */
bad: besm6_log ("Invalid input line: %s", buf);
return SCPE_FMT;
}
/*
* Load memory from file.
*/
t_stat besm6_load (FILE *input)
{
int addr, type;
t_value word;
t_stat err;
addr = 1;
PC = 1;
for (;;) {
err = besm6_read_line (input, &type, &word);
if (err)
return err;
switch (type) {
case 0: /* EOF */
return SCPE_OK;
case ':': /* address */
addr = (int)word;
break;
case '=': /* word */
if (addr < 010)
pult [0][addr] = SET_PARITY (word, PARITY_NUMBER);
else
memory [addr] = SET_PARITY (word, PARITY_NUMBER);
++addr;
break;
case '*': /* instruction */
if (addr < 010)
pult [0][addr] = SET_PARITY (word, PARITY_INSN);
else
memory [addr] = SET_PARITY (word, PARITY_INSN);
++addr;
break;
case '@': /* start address */
PC = (uint32)word;
break;
}
if (addr > MEMSIZE)
return SCPE_FMT;
}
return SCPE_OK;
}
/*
* Dump memory to file.
*/
t_stat besm6_dump (FILE *of, const char *fnam)
{
int addr, last_addr = -1;
t_value word;
fprintf (of, "; %s\n", fnam);
for (addr=1; addr<MEMSIZE; ++addr) {
if (addr < 010)
word = pult [0][addr];
else
word = memory [addr];
if (word == 0)
continue;
if (addr != last_addr+1) {
fprintf (of, "\nв %05o\n", addr);
}
last_addr = addr;
if (IS_INSN (word)) {
fprintf (of, "к ");
besm6_fprint_cmd (of, (uint32)(word >> 24));
fprintf (of, ", ");
besm6_fprint_cmd (of, word & BITS(24));
fprintf (of, "\t\t; %05o - ", addr);
fprintf (of, "%04o %04o %04o %04o\n",
(int) (word >> 36) & 07777,
(int) (word >> 24) & 07777,
(int) (word >> 12) & 07777,
(int) word & 07777);
} else {
fprintf (of, "с %04o %04o %04o %04o",
(int) (word >> 36) & 07777,
(int) (word >> 24) & 07777,
(int) (word >> 12) & 07777,
(int) word & 07777);
fprintf (of, "\t\t; %05o\n", addr);
}
}
return SCPE_OK;
}
/*
* Loader/dumper
*/
t_stat sim_load (FILE *fi, CONST char *cptr, CONST char *fnam, int dump_flag)
{
if (dump_flag)
return besm6_dump (fi, fnam);
return besm6_load (fi);
}