Compare commits

..

No commits in common. "master" and "jsonsimh" have entirely different histories.

16 changed files with 1329 additions and 950 deletions

View file

@ -16,7 +16,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
include(FindPkgConfig)
set(APP_SOURCES
if (NOT WIN32)
add_executable(
kek
breakpoint.cpp
breakpoint_and.cpp
breakpoint_memory.cpp
@ -54,56 +57,58 @@ set(APP_SOURCES
utils.cpp
)
if (NOT WIN32)
add_executable(
kek
${APP_SOURCES}
)
add_executable(
kek-codecoverage
${APP_SOURCES}
)
add_executable(
kek-native
${APP_SOURCES}
)
target_compile_options(kek-native PRIVATE -march=native -mtune=native -O3)
target_compile_options(kek-codecoverage PRIVATE -fprofile-arcs -ftest-coverage)
target_link_options(kek-codecoverage PRIVATE -fprofile-arcs -ftest-coverage)
pkg_check_modules(NCURSES REQUIRED ncurses)
target_link_libraries(kek ${NCURSES_LIBRARIES})
target_include_directories(kek PUBLIC ${NCURSES_INCLUDE_DIRS})
target_compile_options(kek PUBLIC ${NCURSES_CFLAGS_OTHER})
target_link_libraries(kek-codecoverage ${NCURSES_LIBRARIES})
target_include_directories(kek-codecoverage PUBLIC ${NCURSES_INCLUDE_DIRS})
target_compile_options(kek-codecoverage PUBLIC ${NCURSES_CFLAGS_OTHER})
target_link_libraries(kek-native ${NCURSES_LIBRARIES})
target_include_directories(kek-native PUBLIC ${NCURSES_INCLUDE_DIRS})
target_compile_options(kek-native PUBLIC ${NCURSES_CFLAGS_OTHER})
pkg_check_modules(PANEL REQUIRED panel)
target_link_libraries(kek ${PANEL_LIBRARIES})
target_include_directories(kek PUBLIC ${PANEL_INCLUDE_DIRS})
target_compile_options(kek PUBLIC ${PANEL_CFLAGS_OTHER})
target_link_libraries(kek-codecoverage ${PANEL_LIBRARIES})
target_include_directories(kek-codecoverage PUBLIC ${PANEL_INCLUDE_DIRS})
target_compile_options(kek-codecoverage PUBLIC ${PANEL_CFLAGS_OTHER})
target_link_libraries(kek-native ${PANEL_LIBRARIES})
target_include_directories(kek-native PUBLIC ${PANEL_INCLUDE_DIRS})
target_compile_options(kek-native PUBLIC ${PANEL_CFLAGS_OTHER})
endif (NOT WIN32)
if (WIN32)
add_executable(
kek-win32
${APP_SOURCES}
breakpoint.cpp
breakpoint_and.cpp
breakpoint_memory.cpp
breakpoint_or.cpp
breakpoint_parser.cpp
breakpoint_register.cpp
bus.cpp
comm.cpp
comm_posix_tty.cpp
comm_tcp_socket_client.cpp
comm_tcp_socket_server.cpp
console.cpp
console_posix.cpp
cpu.cpp
dc11.cpp
debugger.cpp
device.cpp
disk_backend.cpp
disk_backend_file.cpp
disk_backend_nbd.cpp
error.cpp
kw11-l.cpp
loaders.cpp
log.cpp
main.cpp
memory.cpp
mmu.cpp
rk05.cpp
rl02.cpp
rp06.cpp
tm-11.cpp
tty.cpp
utils.cpp
win32.cpp
)
endif (WIN32)
include(CheckIPOSupported)
@ -119,7 +124,6 @@ set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads)
if (NOT WIN32)
target_link_libraries(kek Threads::Threads)
target_link_libraries(kek-codecoverage Threads::Threads)
else ()
target_link_libraries(kek-win32 Threads::Threads)
@ -128,21 +132,7 @@ endif ()
add_subdirectory(arduinojson)
target_link_libraries(kek ArduinoJson)
target_link_libraries(kek-codecoverage ArduinoJson)
target_link_libraries(kek-native ArduinoJson)
if (WIN32)
target_link_libraries(kek-win32 ArduinoJson)
endif ()
add_custom_target(coverage
COMMAND ${CMAKE_COMMAND} -E remove_directory coverage
COMMAND ${CMAKE_COMMAND} -E make_directory coverage
COMMAND lcov --capture --directory . --output-file coverage.info
COMMAND genhtml coverage.info --output-directory coverage
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_custom_target(coverage_reset
COMMAND lcov --directory . --zerocounters
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)

View file

@ -17,7 +17,7 @@ board_build.filesystem = littlefs
lib_deps = greiman/SdFat@^2.1.2
adafruit/Adafruit NeoPixel
bblanchon/ArduinoJson@^7.0.4
build_flags = -std=gnu++2a -DESP32=1 -ggdb3 -D_GLIBCXX_USE_C99 -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -DCONFIG_SPIRAM_USE_MALLOC -DRGBW_PIXELS -fexceptions -DNEOPIXELS_PIN=25 -DTTY_SERIAL_TX=17 -DTTY_SERIAL_RX=18
build_flags = -std=gnu++2a -DESP32=1 -ggdb3 -D_GLIBCXX_USE_C99 -DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -DCONFIG_SPIRAM_USE_MALLOC -DRGBW_PIXELS -fexceptions
build_unflags = -std=gnu++11 -std=gnu++17
extra_scripts = pre:prepare.py

View file

@ -1,5 +1,2 @@
41ef37ce6b7f13f1cc2df0e6ac570647fe07fe12:
MIPS: 0.74, relative speed: 33.23%, instructions executed: 3692600 in 5.00 seconds
38bff121c236e811335792d6e33ca33711daca9a:
MIPS: 0.34, relative speed: 15.32%, instructions executed: 1702381 in 5.00 seconds

View file

@ -139,7 +139,7 @@ void bus::set_memory_size(const int n_pages)
mmu_->begin(m, c);
TRACE("Memory is now %u kB (%d pages)", n_bytes / 1024, n_pages);
TRACE("Memory is now %u kB in size", n_bytes / 1024);
}
void bus::reset()

212
cpu.cpp
View file

@ -316,14 +316,10 @@ int cpu::getPSW_spl() const
void cpu::setPSW(const uint16_t v, const bool limited)
{
if (limited) {
int cur_mode = std::max( v >> 14, psw >> 14);
int prev_mode = std::max((v >> 12) & 3, (psw >> 12) & 3);
psw = (psw & 004340) | (v & 037) | (cur_mode << 14) | (prev_mode << 12);
}
else {
if (limited)
psw = (psw & 0174340) | (v & 037);
else
psw = v & 0174377; // mask off reserved bits
}
}
void cpu::setPSW_flags_nzv(const uint16_t value, const word_mode_t word_mode)
@ -457,9 +453,9 @@ void cpu::queue_interrupt(const uint8_t level, const uint8_t vector)
void cpu::addToMMR1(const gam_rc_t & g)
{
if (b->getMMU()->isMMR1Locked() == false && g.mmr1_update.has_value() == true) {
TRACE("MMR1: add %d to register R%d", g.mmr1_update.value().delta, g.mmr1_update.value().reg);
if (!b->getMMU()->isMMR1Locked() && g.mmr1_update.has_value()) {
assert(g.mmr1_update.value().delta);
b->getMMU()->addToMMR1(g.mmr1_update.value().delta, g.mmr1_update.value().reg);
}
}
@ -488,17 +484,17 @@ gam_rc_t cpu::getGAM(const uint8_t mode, const uint8_t reg, const word_mode_t wo
break;
case 2: // (Rn)+ / #n
g.addr = get_register(reg);
add_register(reg, word_mode == wm_word || reg == 7 || reg == 6 ? 2 : 1);
g.mmr1_update = { word_mode == wm_word || reg == 7 || reg == 6 ? 2 : 1, reg };
if (read_value)
g.value = b->read(g.addr.value(), word_mode, rm_cur, isR7_space);
add_register(reg, word_mode == wm_word || reg == 7 || reg == 6 ? 2 : 1);
g.mmr1_update = { word_mode == wm_word || reg == 7 || reg == 6 ? 2 : 1, reg };
break;
case 3: // @(Rn)+ / @#a
g.addr = b->read(get_register(reg), wm_word, rm_cur, isR7_space);
// might be wrong: the adds should happen when the read is really performed, because of traps
add_register(reg, 2);
g.mmr1_update = { 2, reg };
g.space = d_space;
// might be wrong: the adds should happen when the read is really performed(?), because of traps
add_register(reg, 2);
if (read_value)
g.value = b->read(g.addr.value(), word_mode, rm_cur, g.space);
break;
@ -584,21 +580,21 @@ bool cpu::double_operand_instructions(const uint16_t instr)
return single_operand_instructions(instr);
case 0b001: { // MOV/MOVB Move Word/Byte
gam_rc_t g_src = getGAM(src_mode, src_reg, word_mode);
bool set_flags = true;
gam_rc_t g_src = getGAM(src_mode, src_reg, word_mode);
if (word_mode == wm_byte && dst_mode == 0) {
bool set_flags = true;
if (word_mode == wm_byte && dst_mode == 0)
set_register(dst_reg, int8_t(g_src.value.value())); // int8_t: sign extension
addToMMR1(g_src);
}
else {
auto g_dst = getGAMAddress(dst_mode, dst_reg, word_mode);
addToMMR1(g_dst);
addToMMR1(g_src);
set_flags = putGAM(g_dst, g_src.value.value());
}
addToMMR1(g_src);
if (set_flags)
setPSW_flags_nzv(g_src.value.value(), word_mode);
@ -1658,6 +1654,7 @@ bool cpu::misc_operations(const uint16_t instr)
pop_from_stack_trace();
setPC(popStack());
setPSW(popStack(), !!getPSW_runmode());
psw &= ~020; // disable TRAP flag
return true;
case 0b0000000000000011: // BPT
@ -1799,8 +1796,13 @@ void cpu::trap(uint16_t vector, const int new_ipl, const bool is_interrupt)
set_register(6, 04);
}
else {
b->getMMU()->clearMMR1();
before_psw = getPSW();
before_pc = getPC();
// TODO set MMR2?
}
if (debug_mode)
@ -1852,11 +1854,8 @@ std::optional<cpu::operand_parameters> cpu::addressing_to_string(const uint8_t m
int run_mode = getPSW_runmode();
auto temp = b->peek_word(run_mode, pc & 65535);
if (temp.has_value() == false) {
operand_parameters out;
out.error = "cannot read from memory";
return out;
}
if (temp.has_value() == false)
return { };
uint16_t next_word = temp.value();
int reg = mode_register & 7;
uint16_t mask = word_mode == wm_byte ? 0xff : 0xffff;
@ -1872,110 +1871,108 @@ std::optional<cpu::operand_parameters> cpu::addressing_to_string(const uint8_t m
else
reg_name = format("R%d", reg);
std::optional<std::string> error;
int mode = mode_register >> 3;
switch(mode) {
switch(mode_register >> 3) {
case 0:
return { { reg_name, 2, -1, uint16_t(get_register(reg) & mask), true, { } } };
return { { reg_name, 2, -1, uint16_t(get_register(reg) & mask), true } };
case 1:
temp2 = b->peek_word(run_mode, get_register(reg));
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", get_register(reg));
temp2 = 0xffff, valid = false;
return { { format("(%s)", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid, error } };
return { { format("(%s)", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid } };
case 2:
if (reg == 7)
return { { format("#%06o", next_word), 4, int(next_word), uint16_t(next_word & mask), true, { } } };
return { { format("#%06o", next_word), 4, int(next_word), uint16_t(next_word & mask), true } };
temp2 = b->peek_word(run_mode, get_register(reg));
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", get_register(reg));
temp2 = 0xffff, valid = false;
return { { format("(%s)+", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid, error } };
return { { format("(%s)+", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid } };
case 3:
if (reg == 7) {
temp2 = b->peek_word(run_mode, next_word);
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", next_word);
temp2 = 0xffff, valid = false;
return { { format("@#%06o", next_word), 4, int(next_word), uint16_t(temp2.value() & mask), valid, error } };
return { { format("@#%06o", next_word), 4, int(next_word), uint16_t(temp2.value() & mask), valid } };
}
temp2 = b->peek_word(run_mode, get_register(reg));
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", get_register(reg));
else {
uint16_t keep = temp2.value();
temp2 = b->peek_word(run_mode, temp2.value());
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", keep);
}
return { { format("@(%s)+", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid, error } };
case 4: {
uint16_t calculated_address = get_register(reg) - (word_mode == wm_word || reg >= 6 ? 2 : 1);
temp2 = b->peek_word(run_mode, calculated_address);
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", calculated_address);
return { { format("-(%s)", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid, error } };
}
case 5: {
uint16_t calculated_address = get_register(reg) - 2;
temp2 = b->peek_word(run_mode, calculated_address);
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", calculated_address);
temp2 = 0xffff, valid = false;
else {
temp2 = b->peek_word(run_mode, temp2.value());
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", temp2.value());
temp2 = 0xffff, valid = false;
}
return { { format("@-(%s)", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid, error } };
return { { format("@(%s)+", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid } };
case 4:
temp2 = b->peek_word(run_mode, get_register(reg) - (word_mode == wm_word || reg >= 6 ? 2 : 1));
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false;
return { { format("-(%s)", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid } };
case 5:
temp2 = b->peek_word(run_mode, get_register(reg) - 2);
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false;
else {
temp2 = b->peek_word(run_mode, temp2.value());
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false;
}
return { { format("@-(%s)", reg_name.c_str()), 2, -1, uint16_t(temp2.value() & mask), valid } };
case 6:
{
uint16_t calculated_address = get_register(reg) + next_word;
temp2 = b->peek_word(run_mode, calculated_address);
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", calculated_address);
if (reg == 7) {
temp2 = b->peek_word(run_mode, get_register(reg) + next_word);
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false;
if (reg == 7)
return { { format("%06o", (pc + next_word + 2) & 65535), 4, int(next_word), uint16_t(temp2.value() & mask), valid, error } };
return { { format("%o(%s)", next_word, reg_name.c_str()), 4, int(next_word), uint16_t(temp2.value() & mask), valid, error } };
return { { format("%06o", (pc + next_word + 2) & 65535), 4, int(next_word), uint16_t(temp2.value() & mask), valid } };
}
case 7:
{
uint16_t calculated_address = get_register(reg) + next_word;
temp2 = b->peek_word(run_mode, calculated_address);
temp2 = b->peek_word(run_mode, get_register(reg) + next_word);
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", calculated_address);
temp2 = 0xffff, valid = false;
return { { format("%o(%s)", next_word, reg_name.c_str()), 4, int(next_word), uint16_t(temp2.value() & mask), valid } };
case 7:
if (reg == 7) {
temp2 = b->peek_word(run_mode, get_register(reg) + next_word);
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false;
else {
temp2 = b->peek_word(run_mode, temp2.value());
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false;
}
return { { format("@%06o", next_word), 4, int(next_word), uint16_t(temp2.value() & mask), valid } };
}
temp2 = b->peek_word(run_mode, get_register(reg) + next_word);
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false;
else {
temp2 = b->peek_word(run_mode, temp2.value());
if (temp2.has_value() == false)
temp2 = 0xffff, valid = false, error = format("cannot fetch memory from %o", temp2.value());
temp2 = 0xffff, valid = false;
}
if (reg == 7)
return { { format("@%06o", next_word), 4, int(next_word), uint16_t(temp2.value() & mask), valid, error } };
return { { format("@%o(%s)", next_word, reg_name.c_str()), 4, int(next_word), uint16_t(temp2.value() & mask), valid, error } };
}
return { { format("@%o(%s)", next_word, reg_name.c_str()), 4, int(next_word), uint16_t(temp2.value() & mask), valid } };
}
operand_parameters out;
out.error = format("unknown register mode %d", mode);
return out;
return { };
}
std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t addr) const
@ -2079,8 +2076,6 @@ std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t
case 0b000110100:
if (word_mode == wm_byte)
name = "MTPS";
else
name = "MARK";
break;
case 0b000110111:
@ -2094,8 +2089,8 @@ std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t
if (text.empty() && name.empty() == false)
text = name + word_mode_str + space + dst_text.operand;
if (text.empty() == false && dst_text.valid == false)
text += " (INV1)";
if (dst_text.valid == false)
text += " (INV)";
if (text.empty() == false && next_word != -1)
instruction_words.push_back(next_word);
@ -2142,7 +2137,7 @@ std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t
text = name + space + src_text + comma + dst_text.operand; // TODO: swap for ASH, ASHC
if (dst_text.valid == false)
text += " (INV2)";
text += " (INV)";
if (text.empty() == false && next_word != -1)
instruction_words.push_back(next_word);
@ -2199,10 +2194,8 @@ std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t
work_values.push_back(dst_text.work_value);
if (src_text.valid == false || dst_text.valid == false)
text += " (INV3)";
text += " (INV)";
if (do_opcode == 0b110)
word_mode_str.clear();
text = name + word_mode_str + space + src_text.operand + comma + dst_text.operand;
}
@ -2358,10 +2351,7 @@ std::map<std::string, std::vector<std::string> > cpu::disassemble(const uint16_t
text = std::string("JMP ") + dst_text.operand;
if (dst_text.valid == false)
text += " (INV4)";
if (addressing.value().error.has_value())
text += " " + addressing.value().error.value();
text += " (INV)";
}
if ((instruction & 0b1111111000000000) == 0b0000100000000000) {
@ -2444,8 +2434,13 @@ bool cpu::step()
{
it_is_a_trap = false;
if (any_queued_interrupts)
execute_any_pending_interrupt();
if (!b->getMMU()->isMMR1Locked())
b->getMMU()->clearMMR1();
if (any_queued_interrupts && execute_any_pending_interrupt()) {
if (!b->getMMU()->isMMR1Locked())
b->getMMU()->clearMMR1();
}
instruction_count++;
@ -2458,12 +2453,17 @@ bool cpu::step()
uint16_t instr = b->read_word(instruction_start);
add_register(7, 2);
if (double_operand_instructions(instr) || conditional_branch_instructions(instr) || condition_code_operations(instr) || misc_operations(instr)) {
if (!b->getMMU()->isMMR1Locked())
b->getMMU()->clearMMR1();
if (double_operand_instructions(instr))
return true;
if (conditional_branch_instructions(instr))
return true;
if (condition_code_operations(instr))
return true;
if (misc_operations(instr))
return true;
}
DOLOG(warning, false, "UNHANDLED instruction %06o @ %06o", instr, instruction_start);
@ -2472,7 +2472,7 @@ bool cpu::step()
return false;
}
catch(const int exception_nr) {
TRACE("trap during execution of command (%d)", exception_nr);
TRACE("bus-trap during execution of command (%d)", exception_nr);
}
return true;

1
cpu.h
View file

@ -109,7 +109,6 @@ private:
int instruction_part;
uint16_t work_value;
bool valid;
std::optional<std::string> error;
};
std::optional<operand_parameters> addressing_to_string(const uint8_t mode_register, const uint16_t pc, const word_mode_t word_mode) const;

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,6 @@
int disassemble(cpu *const c, console *const cnsl, const uint16_t pc, const bool instruction_only);
void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event, const std::optional<std::string> & init);
void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event);
void run_bic(console *const cnsl, bus *const b, std::atomic_uint32_t *const stop_event, const uint16_t bic_start);

185
json/produce-json.py Executable file
View file

@ -0,0 +1,185 @@
#! /usr/bin/python3
import copy
import json
from machine import PDP1170
from mmio import MMIO
from pdptraps import PDPTrap, PDPTraps
import random
import sys
ignore_traps = True
class MMIO_wrapper(MMIO):
def register(self, iofunc, offsetaddr, nwords, *, byte_writes=False, reset=False):
pass
def register_simpleattr(self, obj, attrname, addr, reset=False):
pass
def devicereset_register(self, func):
pass
class PDP1170_wrapper(PDP1170):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.logger.info('')
self.reset_mem_transactions_dict()
def reset_mem_transactions_dict(self):
self.mem_transactions = dict()
self.before = dict()
def get_mem_before(self):
return self.before
def get_mem_transactions_dict(self):
return self.mem_transactions
def put(self, physaddr, value):
self.before[physaddr] = value
super().physRW(physaddr, value)
def physRW(self, physaddr, value=None):
if value == None: # read
self.logger.info(f'Read from {physaddr:08o} (phys)')
if not physaddr in self.mem_transactions and not physaddr in self.before:
self.before[physaddr] = random.randint(0, 65536)
return super().physRW(physaddr, self.before[physaddr])
self.logger.info(f'Write to {physaddr:08o}: {value:06o} (phys)')
self.mem_transactions[physaddr] = value
return super().physRW(physaddr, value)
def physRW_N(self, physaddr, nwords, words=None):
temp_addr = physaddr
if words == None:
self.logger.info(f'Read {nwords} words from {physaddr:08o} (phys)')
for i in range(nwords):
self.physRW(temp_addr, random.randint(0, 65536))
temp_addr += 2
return super().physRW_N(physaddr, nwords)
self.logger.info(f'Write {nwords} ({len(words)}) words to {physaddr:08o} (phys)')
for w in words:
self.mem_transactions[temp_addr] = w
temp_addr += 2
return super().physRW_N(physaddr, nwords, words=words)
class test_generator:
def _invoke_bp(self, a, i):
return True
def put_registers(self, p, target, tag):
target[tag] = dict()
target[tag][0] = dict()
target[tag][1] = dict()
for set_ in range(0, 2):
for reg_ in range(0, 6):
target[tag][set_][reg_] = p.registerfiles[set_][reg_]
target[tag]['sp'] = copy.deepcopy(p.stackpointers)
target[tag]['pc'] = p.r[p.PC]
def create_test(self):
out = { }
p = PDP1170_wrapper(loglevel='DEBUG')
p.ub.mmio = MMIO_wrapper(p)
# TODO what is the maximum size of an instruction?
# non-mmu thus shall be below device range
addr = random.randint(0, 0o160000 - 8) & ~1
mem_kv = []
while True:
instr = random.randint(0, 65536 - 8)
if instr != 1: # TODO ignore 'WAIT' instruction
break
p.logger.info(f'emulating {instr:06o}')
mem_kv.append((addr + 0, instr))
mem_kv.append((addr + 2, random.randint(0, 65536 - 8)))
mem_kv.append((addr + 4, random.randint(0, 65536 - 8)))
mem_kv.append((addr + 6, random.randint(0, 65536 - 8)))
out['memory-before'] = dict()
for a, v in mem_kv:
p.put(a, v)
try:
# generate & set PSW
while True:
try:
p.psw = random.randint(0, 65536)
break
except PDPTraps.ReservedInstruction as ri:
pass
# generate other registers
reg_kv = []
for i in range(7):
reg_kv.append((i, random.randint(0, 65536)))
reg_kv.append((7, addr))
# set registers
set_ = (p.psw >> 11) & 1
for r, v in reg_kv:
p.registerfiles[set_][r] = v
p.registerfiles[1 - set_][r] = (~v) & 65535 # make sure it triggers errors
assert p.registerfiles[set_][r] == p.r[r]
p.r[6] = p.registerfiles[set_][6]
p.r[p.PC] = addr
p._syncregs()
self.put_registers(p, out, 'registers-before')
out['registers-before']['psw'] = p.psw
# run instruction
p.run_steps(pc=addr, steps=1)
if p.straps and ignore_traps:
return None
self.put_registers(p, out, 'registers-after')
out['registers-after']['psw'] = p.psw
mb = p.get_mem_before()
for a in mb:
out['memory-before'][a] = mb[a]
out['memory-after'] = dict()
mem_transactions = p.get_mem_transactions_dict()
for a in mem_transactions:
out['memory-after'][a] = mem_transactions[a]
# TODO originele geheugeninhouden checken
return out
except PDPTraps.ReservedInstruction as pri:
return None
except Exception as e:
# handle PDP11 traps; store them
print(f'test failed {e}, line number: {e.__traceback__.tb_lineno}')
return None
fh = open(sys.argv[1], 'w')
t = test_generator()
tests = []
try:
for i in range(32768):
if (i & 63) == 0:
print(f'{i}\r', end='')
test = t.create_test()
if test != None:
tests.append(test)
except KeyboardInterrupt as ki:
pass
fh.write(json.dumps(tests, indent=4))
fh.close()

218
json/produce-json2.py Executable file
View file

@ -0,0 +1,218 @@
#! /usr/bin/python3
import copy
import json
from machine import PDP1170
from mmio import MMIO
from op4 import op4_dispatch_table
import os
from pdptraps import PDPTrap, PDPTraps
import random
import sys
ignore_traps = True
class MMIO_wrapper(MMIO):
def register(self, iofunc, offsetaddr, nwords, *, byte_writes=False, reset=False):
pass
def register_simpleattr(self, obj, attrname, addr, reset=False):
pass
def devicereset_register(self, func):
pass
class PDP1170_wrapper(PDP1170):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.logger.info('')
self.reset_mem_transactions_dict()
def reset_mem_transactions_dict(self):
self.mem_transactions = dict()
self.before = dict()
def get_mem_before(self):
return self.before
def get_mem_transactions_dict(self):
return self.mem_transactions
def put(self, physaddr, value):
self.before[physaddr] = value
super().physRW(physaddr, value)
def physRW(self, physaddr, value=None):
if value == None: # read
self.logger.info(f'Read from {physaddr:08o} (phys)')
if not physaddr in self.mem_transactions and not physaddr in self.before:
self.before[physaddr] = random.randint(0, 65535)
return super().physRW(physaddr, self.before[physaddr])
self.logger.info(f'Write to {physaddr:08o}: {value:06o} (phys)')
self.mem_transactions[physaddr] = value
return super().physRW(physaddr, value)
def physRW_N(self, physaddr, nwords, words=None):
temp_addr = physaddr
if words == None:
self.logger.info(f'Read {nwords} words from {physaddr:08o} (phys)')
for i in range(nwords):
self.physRW(temp_addr, random.randint(0, 65535))
temp_addr += 2
return super().physRW_N(physaddr, nwords)
self.logger.info(f'Write {nwords} ({len(words)}) words to {physaddr:08o} (phys)')
for w in words:
self.mem_transactions[temp_addr] = w
temp_addr += 2
return super().physRW_N(physaddr, nwords, words=words)
def simple_run_1(self, pc):
thisPC = pc
self.r[self.PC] = (thisPC + 2) & 0o177777 # "could" wrap
try:
inst = self.mmu.wordRW(thisPC)
op4_dispatch_table[inst >> 12](self, inst)
return True
except PDPTrap as trap:
return False
class test_generator:
def _invoke_bp(self, a, i):
return True
def put_registers(self, p, target, tag):
target[tag] = dict()
target[tag][0] = dict()
target[tag][1] = dict()
for set_ in range(0, 2):
for reg_ in range(0, 6):
target[tag][set_][reg_] = p.registerfiles[set_][reg_]
target[tag]['sp'] = copy.deepcopy(p.stackpointers)
target[tag]['pc'] = p.r[p.PC]
def create_test(self, instr, psw):
try:
out = { }
p = PDP1170_wrapper(loglevel='ERROR')
p.ub.mmio = MMIO_wrapper(p)
# TODO what is the maximum size of an instruction?
# non-mmu thus shall be below device range
addr = random.randint(0, 0o160000 - 8) & ~1
mem_kv = []
if instr == 1: # TODO ignore 'WAIT' instruction
return None
p.logger.info(f'emulating {instr:06o}')
mem_kv.append((addr + 0, instr))
mem_kv.append((addr + 2, random.randint(0, 65535 - 8) & ~1))
mem_kv.append((addr + 4, random.randint(0, 65535 - 8) & ~1))
mem_kv.append((addr + 6, random.randint(0, 65535 - 8) & ~1))
out['memory-before'] = dict()
for a, v in mem_kv:
p.put(a, v)
p.psw = psw
# generate other registers
reg_kv = []
for i in range(7):
reg_kv.append((i, random.randint(0, 65535) & ~1))
reg_kv.append((7, addr))
# set registers
set_ = (p.psw >> 11) & 1
for r, v in reg_kv:
p.registerfiles[set_][r] = v
p.registerfiles[1 - set_][r] = (~v) & 65535 # make sure it triggers errors
assert p.registerfiles[set_][r] == p.r[r]
p.r[6] = p.registerfiles[set_][6]
p.r[p.PC] = addr
p._syncregs()
self.put_registers(p, out, 'registers-before')
out['registers-before']['psw'] = p.psw
# run instruction
p.run_steps(pc=addr, steps=1)
#if p.simple_run_1(addr) == False:
# return None
#if (p.straps or p.issues) and ignore_traps:
if p.straps and ignore_traps:
return None
p._syncregs()
self.put_registers(p, out, 'registers-after')
out['registers-after']['psw'] = p.psw
mb = p.get_mem_before()
for a in mb:
out['memory-before'][a] = mb[a]
out['memory-after'] = dict()
mem_transactions = p.get_mem_transactions_dict()
# verify original values
for a, v in mem_kv:
if not a in mem_transactions:
mem_transactions[a] = v
for a in mem_transactions:
out['memory-after'][a] = mem_transactions[a]
out['mmr0-after'] = p.mmu.MMR0
p.mmu._MMR1commit()
out['mmr1-after'] = p.mmu.MMR1
out['mmr2-after'] = p.mmu.MMR2
out['mmr3-after'] = p.mmu.MMR3
return out
except PDPTraps.ReservedInstruction as pri:
return None
except Exception as e:
# handle PDP11 traps; store them
print(f'test failed {e}, line number: {e.__traceback__.tb_lineno}')
return None
stop = False
while True:
psw = random.randint(0, 65535) & 0o174377
name = f'/data/data/temp2/0008-{psw:06o}.json'
if os.path.isfile(name):
print(f'skipping {name}')
continue
print(name)
fh = open(name, 'w')
t = test_generator()
tests = []
try:
for lb in range(65535):
if (lb & 63) == 0:
print(f'{lb} {len(tests)}\r', end='')
test = t.create_test(lb, psw)
if test != None:
tests.append(test)
except KeyboardInterrupt as ki:
stop = True
fh.write(json.dumps(tests, indent=4))
fh.close()
if stop:
break

View file

@ -53,6 +53,38 @@ void set_boot_loader(bus *const b, const bootloader_t which)
0005007
};
#if 0
// from https://github.com/amakukha/PyPDP11.git
offset = 02000;
start = 02002;
static uint16_t rk05_code[] = {
0042113, // "KD"
0012706, 02000, // MOV #boot_start, SP
0012700, 0000000, // MOV #unit, R0 ; unit number
0010003, // MOV R0, R3
0000303, // SWAB R3
0006303, // ASL R3
0006303, // ASL R3
0006303, // ASL R3
0006303, // ASL R3
0006303, // ASL R3
0012701, 0177412, // MOV #RKDA, R1 ; csr
0010311, // MOV R3, (R1) ; load da
0005041, // CLR -(R1) ; clear ba
0012741, 0177000, // MOV #-256.*2, -(R1) ; load wc
0012741, 0000005, // MOV #READ+GO, -(R1) ; read & go
0005002, // CLR R2
0005003, // CLR R3
0012704, 02020, // MOV #START+20, R4
0005005, // CLR R5
0105711, // TSTB (R1)
0100376, // BPL .-2
0105011, // CLRB (R1)
0005007 // CLR PC
};
#endif
bl = rk05_code;
size = sizeof(rk05_code)/sizeof(rk05_code[0]);
@ -112,7 +144,7 @@ void set_boot_loader(bus *const b, const bootloader_t which)
start = offset = 02000;
static const uint16_t rp06_code[] = {
012701, 0176700, 012700, 0176704, 012740, 0177000, 012740, 000071, 012700, 0, 000110, 000000
012700, 0176704, 012740, 0177000, 012740, 000071, 012700, 0, 000110, 000000
};

View file

@ -65,9 +65,8 @@ std::vector<std::pair<uint32_t, uint8_t> > get_memory_settings(const JsonArrayCo
for(const auto & kv: kv_dict.as<JsonObjectConst>()) {
uint32_t a = std::stoi(kv.key().c_str(), nullptr, 8);
uint16_t v = kv.value().as<int>();
out.push_back({ a, v });
if (a == 0 && v == 0)
printf("Suspect\n");
out.push_back({ a, v & 255 });
out.push_back({ a + 1, v >> 8 });
}
}
return out;
@ -113,7 +112,7 @@ int run_cpu_validation(console *const cnsl, const std::string & filename)
// create environment
event = 0;
bus *b = new bus();
b->set_memory_size(DEFAULT_N_PAGES);
b->set_memory_size(DEFAULT_N_PAGES * 8192l);
cpu *c = new cpu(b, &event);
b->add_cpu(c);
@ -131,9 +130,6 @@ int run_cpu_validation(console *const cnsl, const std::string & filename)
for(int i=0; i<4; i++)
c->set_stackpointer(i, get_register_value(before, format("stack-%d", i)));
b->getMMU()->setMMR1(get_register_value(before, "mmr1"));
b->getMMU()->setMMR2(get_register_value(before, "mmr2"));
// registers
for(int set=0; set<2; set++) {
for(int i=0; i<6; i++)
@ -144,27 +140,20 @@ int run_cpu_validation(console *const cnsl, const std::string & filename)
auto memory_before = before["memory"];
auto memory_before_settings = get_memory_settings(memory_before);
for(auto & element: memory_before_settings)
b->write_unibus_byte(element.first, element.second);
b->write_physical(element.first, element.second);
}
int run_n_instructions = test["before"]["run-n-instructions"].as<int>();
int cur_n_errors = 0;
// DO!
c->emulation_start();
for(int k=0; k<run_n_instructions; k++) {
disassemble(c, nullptr, c->getPC(), false);
if (c->step() == false) {
cnsl->put_string_lf("Treated as an invalid instruction");
cur_n_errors++;
break;
}
}
disassemble(c, nullptr, c->getPC(), false);
if (c->step() == false) {
cnsl->put_string_lf("Treated as an invalid instruction");
cur_n_errors++;
}
// VERIFY
if (cur_n_errors == 0) {
else {
auto after = test["after"];
cur_n_errors += !compare_values(cnsl, c->getPC(), get_register_value(after, "PC" ), "PC" );
@ -173,9 +162,6 @@ int run_cpu_validation(console *const cnsl, const std::string & filename)
for(int i=0; i<4; i++)
cur_n_errors += !compare_values(cnsl, c->get_stackpointer(i), get_register_value(after, format("stack-%d", i)), format("Stack pointer %d", i));
cur_n_errors += !compare_values(cnsl, b->getMMU()->getMMR1(), get_register_value(after, "mmr1"), "MMR1");
cur_n_errors += !compare_values(cnsl, b->getMMU()->getMMR2(), get_register_value(after, "mmr2"), "MMR2");
for(int set=0; set<2; set++) {
for(int i=0; i<6; i++)
cur_n_errors += !compare_values(cnsl, c->lowlevel_register_get(set, i), get_register_value(after, format("reg-%d.%d", i, set)), format("Register %d", i));
@ -253,12 +239,11 @@ void help()
printf("-B run tape file as a unit test (for .BIC files)\n");
printf("-r d.img load file as a disk device\n");
printf("-N host:port use NBD-server as disk device (like -r)\n");
printf("-R x select disk type (rk05, rl02, rp06 or rp07)\n");
printf("-R x select disk type (rk05, rl02 or rp06)\n");
printf("-p 123 set CPU start pointer to decimal(!) value\n");
printf("-b enable bootloader (builtin)\n");
printf("-n ncurses UI\n");
printf("-d enable debugger\n");
printf("-f x first process the commands from file x before entering the debugger\n");
printf("-S x set ram size (in number of 8 kB pages)\n");
printf("-s x,y set console switche state: set bit x (0...15) to y (0/1)\n");
printf("-t enable tracing (disassemble to stderr, requires -d as well)\n");
@ -267,7 +252,7 @@ void help()
printf("-X do not include timestamp in logging\n");
printf("-J x run validation suite x against the CPU emulation\n");
printf("-M log metrics\n");
printf("-1 x use x as device for DC-11 (instead of 4 tcp-sockets starting at port 1100)\n");
printf("-1 x use x as device for DC-11\n");
}
int main(int argc, char *argv[])
@ -277,8 +262,7 @@ int main(int argc, char *argv[])
std::vector<disk_backend *> disk_files;
std::string disk_type = "rk05";
bool run_debugger = false;
std::optional<std::string> debugger_init;
bool run_debugger = false;
bool enable_bootloader = false;
bootloader_t bootloader = BL_NONE;
@ -313,17 +297,13 @@ int main(int argc, char *argv[])
std::optional<std::string> dc11_device;
int opt = -1;
while((opt = getopt(argc, argv, "hqD:MT:Br:R:p:ndf:tL:bl:s:Q:N:J:XS:P1:")) != -1)
while((opt = getopt(argc, argv, "hqD:MT:Br:R:p:ndtL:bl:s:Q:N:J:XS:P1:")) != -1)
{
switch(opt) {
case 'h':
help();
return 1;
case 'f':
debugger_init = optarg;
break;
case '1':
dc11_device = optarg;
break;
@ -391,7 +371,7 @@ int main(int argc, char *argv[])
case 'R':
disk_type = optarg;
if (disk_type != "rk05" && disk_type != "rl02" && disk_type != "rp06" && disk_type != "rp07")
if (disk_type != "rk05" && disk_type != "rl02" && disk_type != "rp06")
error_exit(false, "Disk type not known");
break;
@ -480,7 +460,7 @@ int main(int argc, char *argv[])
if (set_ram_size.has_value())
b->set_memory_size(set_ram_size.value());
else
b->set_memory_size(DEFAULT_N_PAGES);
b->set_memory_size(DEFAULT_N_PAGES * 8192l);
b->set_console_switches(console_switches);
@ -495,7 +475,7 @@ int main(int argc, char *argv[])
rl02_dev->begin();
b->add_rl02(rl02_dev);
auto rp06_dev = new rp06(b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag(), disk_type == "rp07");
auto rp06_dev = new rp06(b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag());
rp06_dev->begin();
b->add_RP06(rp06_dev);
@ -511,7 +491,7 @@ int main(int argc, char *argv[])
for(auto & file: disk_files)
rl02_dev->access_disk_backends()->push_back(file);
}
else if (disk_type == "rp06" || disk_type == "rp07") {
else if (disk_type == "rp06") {
bootloader = BL_RP06;
for(auto & file: disk_files)
@ -618,7 +598,7 @@ int main(int argc, char *argv[])
if (is_bic)
run_bic(cnsl, b, &event, bic_start.value());
else if (run_debugger || (bootloader == BL_NONE && test.empty() && tape.empty()))
debugger(cnsl, b, &event, debugger_init);
debugger(cnsl, b, &event);
else if (benchmark) {
// FILL MEMORY
memory *m = b->getRAM();

25
mmu.cpp
View file

@ -126,11 +126,6 @@ void mmu::clearMMR0Bit(const int bit)
MMR0 &= ~(1 << bit);
}
void mmu::setMMR1(const uint16_t value)
{
MMR1 = value;
}
void mmu::setMMR2(const uint16_t value)
{
MMR2 = value;
@ -150,7 +145,6 @@ bool mmu::get_use_data_space(const int run_mode) const
void mmu::clearMMR1()
{
TRACE("clear MMR1");
MMR1 = 0;
}
@ -377,9 +371,20 @@ void mmu::verify_page_access(const uint16_t virt_addr, const int run_mode, const
TRACE("MMR0: %06o", temp);
}
TRACE("Page access %d (for virtual address %06o): trap 0250", access_control, virt_addr);
c->trap(0250); // abort
throw 5;
if (trap_action == T_TRAP_250) {
TRACE("Page access %d (for virtual address %06o): trap 0250", access_control, virt_addr);
c->trap(0250); // trap
throw 5;
}
else { // T_ABORT_4
TRACE("Page access %d (for virtual address %06o): trap 004", access_control, virt_addr);
c->trap(004); // abort
throw 5;
}
}
void mmu::verify_access_valid(const uint32_t m_offset, const int run_mode, const bool d, const int apf, const bool is_io, const bool is_write)
@ -405,7 +410,7 @@ void mmu::verify_access_valid(const uint32_t m_offset, const int run_mode, const
if (is_write)
set_page_trapped(run_mode, d, apf);
c->trap(0250);
c->trap(04);
throw 6;
}

View file

@ -14,22 +14,18 @@
#include "utils.h"
unsigned NSECT = 22; // sectors per track
unsigned NTRAC = 19; // tracks per cylinder
unsigned SECTOR_SIZE = 512;
constexpr const unsigned NSECT = 22; // sectors per track
constexpr const unsigned NTRAC = 19; // tracks per cylinder
constexpr const unsigned SECTOR_SIZE = 512;
constexpr const uint16_t default_DS = uint16_t(rp06::ds_bits::DPR) /* drive present */ | uint16_t(rp06::ds_bits::MOL) /* medium on-line */ | uint16_t(rp06::ds_bits::VV) /* volume valid */ | uint16_t(rp06::ds_bits::DRY) /* drive ready */;
constexpr const char *regnames[] { "Control", "Status", "Error register 1", "Maintenance", "Attention summary", "Desired sector/track address", "Error register 1", "Look ahead", "Drive type", "Serial no", "Offset", "Desired cylinder address", "Current cylinder address", "Error register 2", "Error register 3", "ECC position", "ECC pattern" };
rp06::rp06(bus *const b, std::atomic_bool *const disk_read_activity, std::atomic_bool *const disk_write_activity, const bool is_rp07) :
rp06::rp06(bus *const b, std::atomic_bool *const disk_read_activity, std::atomic_bool *const disk_write_activity) :
b(b),
disk_read_activity (disk_read_activity ),
disk_write_activity(disk_write_activity)
{
if (is_rp07) {
NSECT = 50;
NTRAC = 32;
}
}
rp06::~rp06()
@ -55,13 +51,13 @@ void rp06::show_state(console *const cnsl) const
JsonDocument rp06::serialize() const
{
JsonDocument j;
j["is-rp07"] = is_rp07;
return j;
}
rp06 *rp06::deserialize(const JsonVariantConst j, bus *const b)
{
rp06 *r = new rp06(b, nullptr, nullptr, j["is-rp07"].as<bool>());
rp06 *r = new rp06(b, nullptr, nullptr);
r->begin();
return r;

8
rp06.h
View file

@ -38,9 +38,9 @@ class bus;
class rp06: public disk_device
{
private:
bus *const b { nullptr };
bool is_rp07 { false };
uint16_t registers[32] { };
bus *const b;
uint16_t registers[32] { };
std::atomic_bool *const disk_read_activity { nullptr };
std::atomic_bool *const disk_write_activity { nullptr };
@ -50,7 +50,7 @@ private:
uint32_t compute_offset() const;
public:
rp06(bus *const b, std::atomic_bool *const disk_read_activity, std::atomic_bool *const disk_write_activity, const bool is_rp07);
rp06(bus *const b, std::atomic_bool *const disk_read_activity, std::atomic_bool *const disk_write_activity);
virtual ~rp06();
void begin() override;

View file

@ -174,7 +174,8 @@ void escape_print_xy(NEWWIN *win, int y, int x, char *str)
for(loop=0; loop<len; loop++)
{
if (str[loop] == '^') {
if (str[loop] == '^')
{
if (!inv)
mywattron(win -> win, A_REVERSE);
else
@ -182,7 +183,8 @@ void escape_print_xy(NEWWIN *win, int y, int x, char *str)
inv = 1 - inv;
}
else if (str[loop] == '_') {
else if (str[loop] == '_')
{
if (!underline)
mywattron(win -> win, A_UNDERLINE);
else
@ -190,11 +192,13 @@ void escape_print_xy(NEWWIN *win, int y, int x, char *str)
underline = 1 - underline;
}
else if (str[loop] == '\n') {
else if (str[loop] == '\n')
{
cursor_x = 0;
y++;
}
else {
else
{
mvwprintw(win -> win, y, x + cursor_x++, "%c", str[loop]);
}
}
@ -211,8 +215,10 @@ void escape_print(NEWWIN *win, const char *str, const char rev, const char un)
int loop, len = strlen(str);
bool inv = false, underline = false;
for(loop=0; loop<len; loop++) {
if (str[loop] == rev) {
for(loop=0; loop<len; loop++)
{
if (str[loop] == rev)
{
if (!inv)
mywattron(win -> win, A_REVERSE);
else
@ -220,7 +226,8 @@ void escape_print(NEWWIN *win, const char *str, const char rev, const char un)
inv = 1 - inv;
}
else if (str[loop] == un) {
else if (str[loop] == un)
{
if (!underline)
mywattron(win -> win, A_UNDERLINE);
else
@ -228,7 +235,8 @@ void escape_print(NEWWIN *win, const char *str, const char rev, const char un)
underline = 1 - underline;
}
else {
else
{
waddch(win -> win, str[loop]);
}
}
@ -252,7 +260,9 @@ void create_win_border(int x, int y, int width, int height, const char *title, N
*bwin = create_window_xy(y + 0, x + 0, height + 2 + wbb * 2, width + 2 + wbb * 2);
*win = create_window_xy(y + 1 + wbb, x + 1 + wbb, height + 0, width + 0);
mywattron((*bwin) -> win, A_REVERSE);
box((*bwin) -> win, 0, 0);
mywattroff((*bwin) -> win, A_REVERSE);
mywattron((*bwin) -> win, A_STANDOUT);