Merge branch 'master' into psram

This commit is contained in:
folkert van heusden 2024-04-29 22:28:53 +02:00
commit b1c1a75208
Signed by untrusted user who does not match committer: folkert
GPG key ID: 6B6455EDFEED3BD1
36 changed files with 316 additions and 205 deletions

View file

@ -30,7 +30,7 @@ jobs:
if: ${{ matrix.language == 'cpp' }}
run: |
sudo apt-get update
sudo apt-get install --yes libncurses-dev
sudo apt-get install --yes libncurses-dev libjansson-dev
- name: Configure (cpp)
if: ${{ matrix.language == 'cpp' }}

View file

@ -6,8 +6,10 @@ cmake_minimum_required(VERSION 3.9)
add_compile_options(-Wall -pedantic -Wextra)
#add_compile_options(-fsanitize=address)
#add_link_options(-fsanitize=address)
#add_compile_options(-fsanitize=undefined)
#add_link_options(-fsanitize=undefined)
#add_compile_options(-fanalyzer)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

View file

@ -72,7 +72,6 @@ void console_esp32::panel_update_thread()
pixels.begin();
pixels.clear();
pixels.show();
constexpr uint8_t brightness = 16;
@ -103,7 +102,7 @@ void console_esp32::panel_update_thread()
pixels.clear();
pixels.show();
for(;;) {
while(!stop_panel) {
vTaskDelay(20 / portTICK_PERIOD_MS);
try {
@ -141,5 +140,10 @@ void console_esp32::panel_update_thread()
put_string_lf("Unknown exception in panel thread");
}
}
pixels.clear();
pixels.show();
Serial.println(F("panel task terminating"));
#endif
}

View file

@ -18,6 +18,9 @@
#include <sys/socket.h>
#include <sys/types.h>
#endif
#if defined(ESP32)
#include "esp_heap_caps.h"
#endif
#if defined(SHA2017)
#include "console_shabadge.h"
@ -70,14 +73,13 @@ std::atomic_bool *running { nullptr };
bool trace_output { false };
std::vector<disk_backend *> rk05_files;
std::vector<disk_backend *> rl02_files;
void console_thread_wrapper_panel(void *const c)
{
console *const cnsl = reinterpret_cast<console *>(c);
cnsl->panel_update_thread();
vTaskSuspend(nullptr);
}
uint32_t load_serial_speed_configuration()
@ -208,6 +210,14 @@ void set_tty_serial_speed(console *const c, const uint32_t bps)
c->put_string_lf("Failed to store configuration file with serial settings");
}
#if defined(ESP32)
void heap_caps_alloc_failed_hook(size_t requested_size, uint32_t caps, const char *function_name)
{
printf("%s was called but failed to allocate %d bytes with 0x%X capabilities\r\n", function_name, requested_size, caps);
}
#endif
void setup() {
Serial.begin(115200);
@ -222,6 +232,10 @@ void setup() {
Serial.print(F("Size of int: "));
Serial.println(sizeof(int));
#if defined(ESP32)
heap_caps_register_failed_alloc_callback(heap_caps_alloc_failed_hook);
#endif
#if !defined(BUILD_FOR_RP2040)
Serial.print(F("CPU clock frequency (MHz): "));
Serial.println(getCpuFrequencyMhz());
@ -296,13 +310,19 @@ void setup() {
cnsl = new console_esp32(&stop_event, serial_ports, 80, 25);
#endif
cnsl->set_bus(b);
cnsl->begin();
running = cnsl->get_running_flag();
Serial.println(F("Connect RK05 and RL02 to BUS"));
b->add_rk05(new rk05(rk05_files, b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag()));
Serial.println(F("Connect RK05 and RL02 devices to BUS"));
auto rk05_dev = new rk05(b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag());
rk05_dev->begin();
b->add_rk05(rk05_dev);
auto rl02_dev = new rl02(b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag());
rl02_dev->begin();
b->add_rl02(rl02_dev);
b->add_rl02(new rl02(rl02_files, b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag()));
Serial.println(F("Init TTY"));
tty_ = new tty(cnsl, b);
@ -311,7 +331,7 @@ void setup() {
#if !defined(BUILD_FOR_RP2040) // FIXME: led ring
Serial.println(F("Starting panel"));
xTaskCreate(&console_thread_wrapper_panel, "panel", 2048, cnsl, 1, nullptr);
xTaskCreate(&console_thread_wrapper_panel, "panel", 3072, cnsl, 1, nullptr);
#endif
#if !defined(BUILD_FOR_RP2040)

View file

@ -1,8 +1,6 @@
#! /usr/bin/python3
import json
import subprocess
import sys
def get_git_hash():
@ -12,6 +10,7 @@ def get_git_hash():
ret = subprocess.run(["git", "rev-parse", "--short", "HEAD"], stdout=subprocess.PIPE, text=True)
return ret.stdout.strip() + add
fh = open('version.cpp', 'w')
fh.write('const char *version_str = "' + get_git_hash() + '";\n')
fh.close()

View file

@ -1 +0,0 @@
../win32.cpp

View file

@ -1 +0,0 @@
../win32.h

View file

@ -11,6 +11,7 @@ To build for e.g. linux:
Required:
* libncursesw5-dev
* libjansson-dev
To build for e.g. windows:

View file

@ -31,7 +31,7 @@ std::optional<std::string> breakpoint_memory::is_triggered() const
if (it == values.end())
return { };
return format("MEM%c%c[%08a]=%06o", word_mode == wm_byte ? 'B' : 'W', is_virtual ? 'V' : 'P', addr, v);
return format("MEM%c%c[%08o]=%06o", word_mode == wm_byte ? 'B' : 'W', is_virtual ? 'V' : 'P', addr, v);
}
std::pair<breakpoint_memory *, std::optional<std::string> > breakpoint_memory::parse(bus *const b, const std::string & in)
@ -41,7 +41,7 @@ std::pair<breakpoint_memory *, std::optional<std::string> > breakpoint_memory::p
return { nullptr, "memory: key or value missing" };
auto values_in = parts.at(1);
auto v_parts = split(values_in, ",");
auto v_parts = split(std::move(values_in), ",");
std::set<uint16_t> values;
for(auto & v: v_parts)
values.insert(std::stoi(v, nullptr, 8));

View file

@ -101,12 +101,12 @@ std::pair<breakpoint_register *, std::optional<std::string> > breakpoint_registe
else if (key == "PC" || key == "pc") {
return { new breakpoint_register(b, 7, values), { } };
}
else if (key.substr(0, 3) == "MMR" or key.substr(0, 3) == "mmr") {
else if (key.substr(0, 3) == "MMR" || key.substr(0, 3) == "mmr") {
int which = key[3] - '0';
return { new breakpoint_register(b, hr_mmr0 + which, values), { } };
}
else if (key.substr(0, 3) == "PSW" or key.substr(0, 3) == "psw") {
else if (key.substr(0, 3) == "PSW" || key.substr(0, 3) == "psw") {
return { new breakpoint_register(b, hr_psw, values), { } };
}

30
bus.cpp
View file

@ -24,6 +24,7 @@
bus::bus()
{
mmu_ = new mmu();
mmu_->begin();
kw11_l_ = new kw11_l(this);
@ -65,7 +66,10 @@ json_t *bus::serialize() const
if (rl02_)
json_object_set(j_out, "rl02", rl02_->serialize());
// TODO: rk05, tm11
if (rk05_)
json_object_set(j_out, "rk05", rk05_->serialize());
// TODO: tm11
return j_out;
}
@ -112,7 +116,13 @@ bus *bus::deserialize(const json_t *const j, console *const cnsl, std::atomic_ui
b->add_rl02(rl02_);
}
// TODO: rk05, tm11
temp = json_object_get(j, "rk05");
if (temp) {
rk05 *rk05_ = rk05::deserialize(temp, b);
b->add_rk05(rk05_);
}
// TODO: tm11
return b;
}
@ -120,8 +130,6 @@ bus *bus::deserialize(const json_t *const j, console *const cnsl, std::atomic_ui
void bus::set_memory_size(const int n_pages)
{
this->n_pages = n_pages;
uint32_t n_bytes = n_pages * 8192l;
delete m;
@ -474,7 +482,7 @@ uint16_t bus::read(const uint16_t addr_in, const word_mode_t word_mode, const rm
}
// LO size register field must be all 1s, so subtract 1
uint32_t system_size = n_pages * 8192l / 64 - 1;
uint32_t system_size = m->get_memory_size() / 64 - 1;
if (a == ADDR_SYSSIZE + 2) { // system size HI
uint16_t temp = system_size >> 16;
@ -505,7 +513,7 @@ uint16_t bus::read(const uint16_t addr_in, const word_mode_t word_mode, const rm
return 0;
}
if (m_offset >= uint32_t(n_pages * 8192)) {
if (m_offset >= m->get_memory_size()) {
if (peek_only) {
DOLOG(debug, false, "READ from %06o - out of range!", addr_in);
return 0;
@ -696,8 +704,8 @@ uint32_t bus::calculate_physical_address(const int run_mode, const uint16_t a, c
}
}
if (m_offset >= n_pages * 8192l && !is_io) [[unlikely]] {
DOLOG(debug, !peek_only, "bus::calculate_physical_address %o >= %o", m_offset, n_pages * 8192l);
if (m_offset >= m->get_memory_size() && !is_io) [[unlikely]] {
DOLOG(debug, !peek_only, "bus::calculate_physical_address %o >= %o", m_offset, m->get_memory_size());
DOLOG(debug, false, "TRAP(04) (throw 6) on address %06o", a);
if (mmu_->is_locked() == false) {
@ -1025,7 +1033,7 @@ write_rc_t bus::write(const uint16_t addr_in, const word_mode_t word_mode, uint1
DOLOG(debug, false, "WRITE to %06o/%07o %c %c: %06o", addr_in, m_offset, space == d_space ? 'D' : 'I', word_mode == wm_byte ? 'B' : 'W', value);
if (m_offset >= uint32_t(n_pages * 8192)) {
if (m_offset >= m->get_memory_size()) {
c->trap(004); // no such RAM
throw 1;
}
@ -1042,7 +1050,7 @@ void bus::writePhysical(const uint32_t a, const uint16_t value)
{
DOLOG(debug, false, "physicalWRITE %06o to %o", value, a);
if (a >= n_pages * 8192l) {
if (a >= m->get_memory_size()) {
DOLOG(debug, false, "physicalWRITE to %o: trap 004", a);
c->trap(004);
throw 12;
@ -1054,7 +1062,7 @@ void bus::writePhysical(const uint32_t a, const uint16_t value)
uint16_t bus::readPhysical(const uint32_t a)
{
if (a >= n_pages * 8192l) {
if (a >= m->get_memory_size()) {
DOLOG(debug, false, "physicalREAD from %o: trap 004", a);
c->trap(004);
throw 13;

4
bus.h
View file

@ -75,10 +75,7 @@ private:
rl02 *rl02_ { nullptr };
tty *tty_ { nullptr };
kw11_l *kw11_l_ { nullptr };
mmu *mmu_ { nullptr };
int n_pages { DEFAULT_N_PAGES };
memory *m { nullptr };
uint16_t microprogram_break_register { 0 };
@ -104,7 +101,6 @@ public:
void set_debug_mode() { console_switches |= 128; }
uint16_t get_console_leds() { return console_leds; }
int get_memory_size() const { return n_pages; }
void set_memory_size(const int n_pages);
void mmudebug(const uint16_t a);

View file

@ -46,6 +46,10 @@ console::~console()
delete [] screen_buffer;
}
void console::begin()
{
}
void console::start_thread()
{
assert(b);
@ -266,33 +270,6 @@ std::string console::read_line(const std::string & prompt)
return edit_lines_hist.at(line_nr);
}
void console::debug(const std::string fmt, ...)
{
#if defined(BUILD_FOR_RP2040)
char buffer[128];
va_list ap;
va_start(ap, fmt);
vsnprintf(buffer, sizeof buffer, fmt.c_str(), ap);
va_end(ap);
put_string_lf(buffer);
#else
char *buffer = nullptr;
va_list ap;
va_start(ap, fmt);
vasprintf(&buffer, fmt.c_str(), ap);
va_end(ap);
put_string_lf(buffer);
free(buffer);
#endif
}
void console::put_char(const char c)
{
put_char_ll(c);

View file

@ -30,6 +30,7 @@ private:
protected:
std::atomic_uint32_t *const stop_event { nullptr };
std::atomic_bool stop_panel { false };
bus *b { nullptr };
#if !defined(BUILD_FOR_RP2040)
@ -60,6 +61,8 @@ public:
console(std::atomic_uint32_t *const stop_event, const int t_width = 80, const int t_height = 25);
virtual ~console();
virtual void begin();
void set_bus(bus *const b) { this->b = b; }
void start_thread();
@ -76,8 +79,6 @@ public:
void put_string(const std::string & what);
virtual void put_string_lf(const std::string & what) = 0;
void debug(const std::string fmt, ...);
virtual void resize_terminal() = 0;
virtual void refresh_virtual_terminal() = 0;
@ -88,5 +89,6 @@ public:
std::atomic_bool * get_disk_read_activity_flag() { return &disk_read_activity_flag; }
std::atomic_bool * get_disk_write_activity_flag() { return &disk_write_activity_flag; }
void stop_panel_thread() { stop_panel = true; }
virtual void panel_update_thread() = 0;
};

View file

@ -16,10 +16,6 @@
console_ncurses::console_ncurses(std::atomic_uint32_t *const stop_event): console(stop_event)
{
init_ncurses(true);
resize_terminal();
th_panel = new std::thread(&console_ncurses::panel_update_thread, this);
}
console_ncurses::~console_ncurses()
@ -51,6 +47,13 @@ console_ncurses::~console_ncurses()
endwin();
}
void console_ncurses::begin()
{
resize_terminal();
th_panel = new std::thread(&console_ncurses::panel_update_thread, this);
}
int console_ncurses::wait_for_char_ll(const short timeout)
{
struct pollfd fds[] = { { STDIN_FILENO, POLLIN, 0 } };
@ -133,7 +136,7 @@ void console_ncurses::panel_update_thread()
constexpr int refresh_rate = 50;
while(*stop_event != EVENT_TERMINATE) {
while(*stop_event != EVENT_TERMINATE && stop_panel == false) {
myusleep(1000000 / refresh_rate);
// note that these are approximately as there's no mutex on the emulation
@ -187,11 +190,11 @@ void console_ncurses::panel_update_thread()
auto psw = data["psw"][0];
std::string instruction_values;
for(auto iv : data["instruction-values"])
for(auto & iv : data["instruction-values"])
instruction_values += (instruction_values.empty() ? "" : ",") + iv;
std::string work_values;
for(auto wv : data["work-values"])
for(auto & wv : data["work-values"])
work_values += (work_values.empty() ? "" : ",") + wv;
std::string instruction = data["instruction-text"].at(0);

View file

@ -32,6 +32,8 @@ public:
console_ncurses(std::atomic_uint32_t *const stop_event);
virtual ~console_ncurses();
void begin() override;
void put_string_lf(const std::string & what) override;
void resize_terminal() override;

15
cpu.cpp
View file

@ -137,7 +137,7 @@ void cpu::reset()
uint16_t cpu::getRegister(const int nr, const rm_selection_t mode_selection) const
{
if (nr < 6) {
int set = getBitPSW(11);
int set = get_register_set();
return regs0_5[set][nr];
}
@ -157,7 +157,7 @@ uint16_t cpu::getRegister(const int nr, const rm_selection_t mode_selection) con
void cpu::setRegister(const int nr, const uint16_t value, const rm_selection_t mode_selection)
{
if (nr < 6) {
int set = getBitPSW(11);
int set = get_register_set();
regs0_5[set][nr] = value;
}
@ -204,7 +204,7 @@ bool cpu::put_result(const gam_rc_t & g, const uint16_t value)
uint16_t cpu::addRegister(const int nr, const rm_selection_t mode_selection, const uint16_t value)
{
if (nr < 6)
return regs0_5[getBitPSW(11)][nr] += value;
return regs0_5[get_register_set()][nr] += value;
if (nr == 6) {
if (mode_selection == rm_prev)
@ -584,12 +584,12 @@ bool cpu::double_operand_instructions(const uint16_t instr)
const uint8_t dst_mode = (dst >> 3) & 7;
const uint8_t dst_reg = dst & 7;
bool set_flags = true;
switch(operation) {
case 0b001: { // MOV/MOVB Move Word/Byte
gam_rc_t g_src = getGAM(src_mode, src_reg, word_mode, rm_cur);
bool set_flags = true;
if (word_mode == wm_byte && dst_mode == 0)
setRegister(dst_reg, int8_t(g_src.value.value())); // int8_t: sign extension
else {
@ -715,7 +715,7 @@ bool cpu::double_operand_instructions(const uint16_t instr)
setPSW_z(result == 0);
}
putGAM(g_dst, result);
(void)putGAM(g_dst, result);
return true;
}
@ -945,7 +945,6 @@ bool cpu::single_operand_instructions(const uint16_t instr)
const uint8_t dst_mode = (dst >> 3) & 7;
const uint8_t dst_reg = dst & 7;
const word_mode_t word_mode = instr & 0x8000 ? wm_byte : wm_word;
bool set_flags = true;
switch(opcode) {
case 0b00000011: { // SWAB
@ -959,7 +958,7 @@ bool cpu::single_operand_instructions(const uint16_t instr)
v = (v << 8) | (v >> 8);
set_flags = putGAM(g_dst, v);
bool set_flags = putGAM(g_dst, v);
if (set_flags) {
setPSW_flags_nzv(v, wm_byte);

1
cpu.h
View file

@ -160,6 +160,7 @@ public:
bool getBitPSW(const int bit) const;
int getPSW_runmode() const { return psw >> 14; };
int getPSW_prev_runmode() const { return (psw >> 12) & 3; };
bool get_register_set() const { return !!(psw & 04000); }
void setPSW_c(const bool v);
void setPSW_v(const bool v);

View file

@ -26,6 +26,7 @@
#include "disk_backend_nbd.h"
#include "loaders.h"
#include "log.h"
#include "memory.h"
#include "tty.h"
#include "utils.h"
@ -37,13 +38,9 @@
#include "rp2040.h"
#endif
void set_boot_loader(bus *const b);
void configure_disk(console *const c);
void configure_network(console *const c);
void check_network(console *const c);
void start_network(console *const c);
void configure_network(console *const cnsl);
void check_network(console *const cnsl);
void start_network(console *const cnsl);
void set_tty_serial_speed(console *const c, const uint32_t bps);
#endif
@ -85,10 +82,10 @@ std::optional<disk_backend *> select_disk_file(console *const c)
#if IS_POSIX
c->put_string_lf("Files in current directory: ");
#else
c->debug("MISO: %d", int(MISO));
c->debug("MOSI: %d", int(MOSI));
c->debug("SCK : %d", int(SCK ));
c->debug("SS : %d", int(SS ));
c->put_string_lf(format("MISO: %d", int(MISO)));
c->put_string_lf(format("MOSI: %d", int(MOSI)));
c->put_string_lf(format("SCK : %d", int(SCK )));
c->put_string_lf(format("SS : %d", int(SS )));
c->put_string_lf("Files on SD-card:");
@ -99,9 +96,9 @@ std::optional<disk_backend *> select_disk_file(console *const c)
if (!SD.begin(SS, SD_SCK_MHZ(15))) {
auto err = SD.sdErrorCode();
if (err)
c->debug("SDerror: 0x%x, data: 0x%x", err, SD.sdErrorData());
c->put_string_lf(format("SDerror: 0x%x, data: 0x%x", err, SD.sdErrorData()));
else
c->debug("Failed to initialize SD card");
c->put_string_lf("Failed to initialize SD card");
return { };
}
@ -239,20 +236,20 @@ std::optional<disk_backend *> select_disk_backend(console *const cnsl)
void configure_disk(bus *const b, console *const cnsl)
{
// TODO tape
int ch = wait_for_key("1. RK05, 2. RL02, 9. abort", cnsl, { '1', '2', '3', '9' });
int type_ch = wait_for_key("1. RK05, 2. RL02, 9. abort", cnsl, { '1', '2', '3', '9' });
bootloader_t bl = BL_NONE;
disk_device *dd = nullptr;
if (ch == '1') {
if (type_ch == '1') {
dd = b->getRK05();
bl = BL_RK05;
}
else if (ch == '2') {
else if (type_ch == '2') {
dd = b->getRL02();
bl = BL_RL02;
}
else if (ch == '9') {
else if (type_ch == '9') {
return;
}
@ -289,11 +286,11 @@ void configure_disk(bus *const b, console *const cnsl)
int slot = ch - 'A';
for(;;) {
int ch = wait_for_key("Select cartridge action: 1. load, 2. unload, 9. exit", cnsl, { '1', '2', '9' });
if (ch == '9')
int ch_action = wait_for_key("Select cartridge action: 1. load, 2. unload, 9. exit", cnsl, { '1', '2', '9' });
if (ch_action == '9')
break;
if (ch == '1') {
if (ch_action == '1') {
auto image_file = select_disk_backend(cnsl);
if (image_file.has_value()) {
@ -303,7 +300,7 @@ void configure_disk(bus *const b, console *const cnsl)
cnsl->put_string_lf("Cartridge loaded");
}
}
else if (ch == '2') {
else if (ch_action == '2') {
if (cartridge_slots->at(slot)) {
delete cartridge_slots->at(slot);
cartridge_slots->at(slot) = nullptr;
@ -325,11 +322,11 @@ int disassemble(cpu *const c, console *const cnsl, const uint16_t pc, const bool
auto psw = data["psw"][0];
std::string instruction_values;
for(auto iv : data["instruction-values"])
for(auto & iv : data["instruction-values"])
instruction_values += (instruction_values.empty() ? "" : ",") + iv;
std::string work_values;
for(auto wv : data["work-values"])
for(auto & wv : data["work-values"])
work_values += (work_values.empty() ? "" : ",") + wv;
std::string instruction = data["instruction-text"].at(0);
@ -358,7 +355,7 @@ int disassemble(cpu *const c, console *const cnsl, const uint16_t pc, const bool
);
if (cnsl)
cnsl->debug(result);
cnsl->put_string_lf(result);
else
DOLOG(debug, false, "%s", result.c_str());
@ -391,7 +388,7 @@ std::map<std::string, std::string> split(const std::vector<std::string> & kv_arr
{
std::map<std::string, std::string> out;
for(auto pair : kv_array) {
for(auto & pair : kv_array) {
auto kv = split(pair, splitter);
if (kv.size() == 1)
@ -663,7 +660,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
cnsl->put_string_lf("Breakpoints:");
for(auto a : bps)
for(auto & a : bps)
cnsl->put_string_lf(format("%d: %s", a.first, a.second->emit().c_str()));
if (bps.empty())
@ -825,6 +822,12 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
continue;
}
#if defined(ESP32)
else if (cmd == "debug") {
if (heap_caps_check_integrity_all(true) == false)
cnsl->put_string_lf("HEAP corruption!");
continue;
}
else if (cmd == "cfgnet") {
configure_network(cnsl);
@ -863,7 +866,7 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
if (parts.size() == 2)
b->set_memory_size(std::stoi(parts.at(1)));
else {
int n_pages = b->get_memory_size();
int n_pages = b->getRAM()->get_memory_size();
cnsl->put_string_lf(format("Memory size: %u pages or %u kB (decimal)", n_pages, n_pages * 8192 / 1024));
}
@ -942,7 +945,10 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
}
#endif
else if (parts[0] == "setsl" && parts.size() == 3) {
setloghost(parts.at(1).c_str(), parse_ll(parts[2]));
if (setloghost(parts.at(1).c_str(), parse_ll(parts[2])) == false)
cnsl->put_string_lf("Failed parsing IP address");
else
send_syslog(info, "Hello, world!");
continue;
}
@ -951,6 +957,11 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
continue;
}
else if (cmd == "dp") {
cnsl->stop_panel_thread();
continue;
}
else if (cmd == "bt") {
if (c->get_debug() == false)
cnsl->put_string_lf("Debug mode is disabled!");
@ -1006,11 +1017,13 @@ void debugger(console *const cnsl, bus *const b, std::atomic_uint32_t *const sto
"ser - serialize state to a file",
// "dser - deserialize state from a file",
#endif
"dp - disable panel",
#if defined(ESP32)
"cfgnet - configure network (e.g. WiFi)",
"startnet - start network",
"chknet - check network status",
"serspd - set serial speed in bps (8N1 are default)",
"debug - debugging info",
#endif
"cfgdisk - configure disk",
nullptr

View file

@ -4,6 +4,7 @@
#include <cassert>
#include "disk_backend.h"
#include "gen.h"
#if IS_POSIX
#include "disk_backend_file.h"
#include "disk_backend_nbd.h"
@ -68,7 +69,7 @@ json_t *disk_backend::serialize_overlay() const
for(size_t i=0; i<id.second.size(); i++)
json_array_append(j_data, json_integer(id.second.at(i)));
json_object_set(out, format("%u", id.first).c_str(), j_data);
json_object_set(out, format("%lu", id.first).c_str(), j_data);
}
return out;

View file

@ -137,7 +137,7 @@ bool disk_backend_nbd::connect(const bool retry)
uint64_t size;
uint32_t flags;
uint8_t padding[124];
} nbd_hello;
} nbd_hello { };
if (fd != -1) {
if (READ(fd, reinterpret_cast<char *>(&nbd_hello), sizeof nbd_hello) != sizeof nbd_hello) {
@ -194,7 +194,7 @@ bool disk_backend_nbd::read(const off_t offset_in, const size_t n, uint8_t *cons
uint64_t handle;
uint64_t offset;
uint32_t length;
} nbd_request { 0 };
} nbd_request { };
nbd_request.magic = ntohl(0x25609513);
nbd_request.type = 0; // READ

View file

@ -18,5 +18,7 @@ public:
virtual ~disk_device() {
}
virtual void begin() = 0;
std::vector<disk_backend *> * access_disk_backends() { return &fhs; }
};

View file

@ -40,7 +40,7 @@ void set_boot_loader(bus *const b, const bootloader_t which)
if (which == BL_RK05) {
start = offset = 01000;
static uint16_t rk05_code[] = {
static const uint16_t rk05_code[] = {
0012700,
0177406,
0012710,
@ -92,7 +92,7 @@ void set_boot_loader(bus *const b, const bootloader_t which)
start = offset = 01000;
/* from https://www.pdp-11.nl/peripherals/disk/rl-info.html
static uint16_t rl02_code[] = {
static const uint16_t rl02_code[] = {
0012701,
0174400,
0012761,
@ -120,7 +120,7 @@ void set_boot_loader(bus *const b, const bootloader_t which)
*/
// from http://gunkies.org/wiki/RL11_disk_controller
static uint16_t rl02_code[] = {
static const uint16_t rl02_code[] = {
0012700,
0174400,
0012760,
@ -207,7 +207,11 @@ std::optional<uint16_t> load_tape(bus *const b, const std::string & file)
break;
}
uint8_t c = fgetc(fh);
int c = fgetc(fh);
if (c == -1) {
DOLOG(warning, true, "read failure");
break;
}
#endif
csum += c;

71
log.cpp
View file

@ -15,7 +15,9 @@
#include "error.h"
#include "log.h"
#include "utils.h"
#if defined(_WIN32)
#include "win32.h"
#endif
static const char *logfile = strdup("/tmp/kek.log");
@ -23,10 +25,12 @@ static sockaddr_in syslog_ip_addr = { };
static bool is_file = true;
log_level_t log_level_file = warning;
log_level_t log_level_screen = warning;
FILE *lfh = nullptr;
static FILE *log_fh = nullptr;
static int lf_uid = -1;
static int lf_gid = -1;
static bool l_timestamp = true;
static thread_local int log_buffer_size = 128;
static thread_local char *log_buffer = reinterpret_cast<char *>(malloc(log_buffer_size));
#if defined(ESP32)
int gettid()
@ -37,8 +41,8 @@ int gettid()
void setlogfile(const char *const lf, const log_level_t ll_file, const log_level_t ll_screen, const bool timestamp)
{
if (lfh)
fclose(lfh);
if (log_fh)
fclose(log_fh);
free((void *)logfile);
@ -54,9 +58,10 @@ void setlogfile(const char *const lf, const log_level_t ll_file, const log_level
atexit(closelog);
}
void setloghost(const char *const host, const log_level_t ll)
bool setloghost(const char *const host, const log_level_t ll)
{
inet_aton(host, &syslog_ip_addr.sin_addr);
syslog_ip_addr.sin_family = AF_INET;
bool ok = inet_aton(host, &syslog_ip_addr.sin_addr) == 1;
syslog_ip_addr.sin_port = htons(514);
is_file = false;
@ -64,6 +69,8 @@ void setloghost(const char *const host, const log_level_t ll)
log_level_file = ll;
l_timestamp = false;
return ok;
}
void setll(const log_level_t ll_screen, const log_level_t ll_file)
@ -83,44 +90,52 @@ void send_syslog(const int ll, const std::string & what)
std::string msg = format("<%d>%s", 16 * 8 + ll, what.c_str());
int s = socket(AF_INET, SOCK_DGRAM, 0);
if (s != -1) {
(void)sendto(s, msg.c_str(), msg.size(), 0, reinterpret_cast<sockaddr *>(&syslog_ip_addr), sizeof syslog_ip_addr);
close(s);
}
}
void closelog()
{
if (lfh) {
fclose(lfh);
if (log_fh) {
fclose(log_fh);
lfh = nullptr;
log_fh = nullptr;
}
}
void dolog(const log_level_t ll, const char *fmt, ...)
{
#if !defined(BUILD_FOR_RP2040)
if (!lfh && logfile != nullptr) {
if (!log_fh && logfile != nullptr) {
#if !defined(ESP32)
lfh = fopen(logfile, "a+");
if (!lfh)
log_fh = fopen(logfile, "a+");
if (!log_fh)
error_exit(true, "Cannot access log-file %s", logfile);
#if !defined(_WIN32)
if (lf_uid != -1 && fchown(fileno(lfh), lf_uid, lf_gid) == -1)
if (lf_uid != -1 && fchown(fileno(log_fh), lf_uid, lf_gid) == -1)
error_exit(true, "Cannot change logfile (%s) ownership", logfile);
if (fcntl(fileno(lfh), F_SETFD, FD_CLOEXEC) == -1)
if (fcntl(fileno(log_fh), F_SETFD, FD_CLOEXEC) == -1)
error_exit(true, "fcntl(FD_CLOEXEC) failed");
#endif
#endif
}
char *str = nullptr;
for(;;) {
va_list ap;
va_start(ap, fmt);
(void)vasprintf(&str, fmt, ap);
int needed_length = vsnprintf(log_buffer, log_buffer_size, fmt, ap);
va_end(ap);
if (needed_length < log_buffer_size)
break;
log_buffer_size *= 2;
log_buffer = reinterpret_cast<char *>(realloc(log_buffer, log_buffer_size));
}
if (l_timestamp) {
uint64_t now = get_us();
time_t t_now = now / 1000000;
@ -132,39 +147,35 @@ void dolog(const log_level_t ll, const char *fmt, ...)
if (!localtime_r(&t_now, &tm))
error_exit(true, "localtime_r failed");
#endif
char *ts_str = nullptr;
char ts_str[64] { };
const char *const ll_names[] = { "emerg ", "alert ", "crit ", "error ", "warning", "notice ", "info ", "debug ", "none " };
asprintf(&ts_str, "%04d-%02d-%02d %02d:%02d:%02d.%06d %s|%s] ",
snprintf(ts_str, sizeof ts_str, "%04d-%02d-%02d %02d:%02d:%02d.%06d %s|%s] ",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, int(now % 1000000),
ll_names[ll], get_thread_name().c_str());
if (ll <= log_level_file && is_file == false)
send_syslog(ll, str);
send_syslog(ll, log_buffer);
#if !defined(ESP32)
if (ll <= log_level_file && lfh != nullptr)
fprintf(lfh, "%s%s\n", ts_str, str);
if (ll <= log_level_file && log_fh != nullptr)
fprintf(log_fh, "%s%s\n", ts_str, log_buffer);
#endif
if (ll <= log_level_screen)
printf("%s%s\r\n", ts_str, str);
free(ts_str);
printf("%s%s\r\n", ts_str, log_buffer);
}
else {
if (ll <= log_level_file && is_file == false)
send_syslog(ll, str);
send_syslog(ll, log_buffer);
#if !defined(ESP32)
if (ll <= log_level_file && lfh != nullptr)
fprintf(lfh, "%s\n", str);
if (ll <= log_level_file && log_fh != nullptr)
fprintf(log_fh, "%s\n", log_buffer);
#endif
if (ll <= log_level_screen)
printf("%s\r\n", str);
printf("%s\r\n", log_buffer);
}
free(str);
#endif
}

3
log.h
View file

@ -12,9 +12,10 @@ typedef enum { ll_emerg = 0, ll_alert, ll_critical, ll_error, warning, notice, i
log_level_t parse_ll(const std::string & str);
void setlogfile(const char *const lf, const log_level_t ll_file, const log_level_t ll_screen, const bool l_timestamp);
void setloghost(const char *const host, const log_level_t ll);
bool setloghost(const char *const host, const log_level_t ll);
void setll(const log_level_t ll_screen, const log_level_t ll_file);
void setloguid(const int uid, const int gid);
void send_syslog(const int ll, const std::string & what);
void closelog();
void dolog(const log_level_t ll, const char *fmt, ...);

View file

@ -111,9 +111,9 @@ int run_cpu_validation(const std::string & filename)
}
{
json_t *b_sp = json_object_get(registers_before, "sp");
size_t array_size = json_array_size(b_sp);
assert(array_size == 4);
for(size_t i=0; i<array_size; i++) {
size_t sp_array_size = json_array_size(b_sp);
assert(sp_array_size == 4);
for(size_t i=0; i<sp_array_size; i++) {
json_t *temp = json_array_get(b_sp, i);
c->lowlevel_register_sp_set(i, json_integer_value(temp));
}
@ -183,9 +183,9 @@ int run_cpu_validation(const std::string & filename)
{
json_t *a_sp = json_object_get(registers_after, "sp");
size_t array_size = json_array_size(a_sp);
assert(array_size == 4);
for(size_t i=0; i<array_size; i++) {
size_t sp_array_size = json_array_size(a_sp);
assert(sp_array_size == 4);
for(size_t i=0; i<sp_array_size; i++) {
json_t *temp = json_array_get(a_sp, i);
uint16_t sp = c->lowlevel_register_sp_get(i);
if (json_integer_value(temp) != sp) {
@ -525,18 +525,30 @@ int main(int argc, char *argv[])
cpu *c = new cpu(b, &event);
b->add_cpu(c);
if (rk05_files.empty() == false)
auto rk05_dev = new rk05(b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag());
rk05_dev->begin();
b->add_rk05(rk05_dev);
auto rl02_dev = new rl02(b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag());
rl02_dev->begin();
b->add_rl02(rl02_dev);
if (rk05_files.empty() == false) {
bootloader = BL_RK05;
if (rl02_files.empty() == false)
for(auto & file: rk05_files)
rk05_dev->access_disk_backends()->push_back(file);
}
if (rl02_files.empty() == false) {
bootloader = BL_RL02;
for(auto & file: rl02_files)
rl02_dev->access_disk_backends()->push_back(file);
}
if (enable_bootloader)
set_boot_loader(b, bootloader);
b->add_rk05(new rk05(rk05_files, b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag()));
b->add_rl02(new rl02(rl02_files, b, cnsl->get_disk_read_activity_flag(), cnsl->get_disk_write_activity_flag()));
}
else {
FILE *fh = fopen(deserialize.c_str(), "r");
@ -566,6 +578,7 @@ int main(int argc, char *argv[])
}
cnsl->set_bus(b);
cnsl->begin();
running = cnsl->get_running_flag();

View file

@ -21,6 +21,8 @@ public:
memory(const uint32_t size);
~memory();
uint32_t get_memory_size() const { return size; }
void reset();
#if IS_POSIX
json_t *serialize() const;

19
mmu.cpp
View file

@ -15,6 +15,11 @@ mmu::~mmu()
{
}
void mmu::begin()
{
reset();
}
void mmu::reset()
{
memset(pages, 0x00, sizeof pages);
@ -52,8 +57,6 @@ void mmu::setMMR0(uint16_t value)
value &= 254; // bits 7...1 are protected
}
// TODO if bit 15/14/13 are set (either of them), then do not modify bit 1...7
MMR0 = value;
}
@ -102,17 +105,6 @@ void mmu::addToMMR1(const int8_t delta, const uint8_t reg)
assert((getMMR0() & 0160000) == 0); // MMR1 should not be locked
#if defined(ESP32)
// if (MMR1 > 255)
// esp_backtrace_print(32);
#else
if (MMR1 > 255) {
extern FILE *lfh;
fflush(lfh);
}
assert(MMR1 < 256);
#endif
MMR1 <<= 8;
MMR1 |= (delta & 31) << 3;
@ -277,6 +269,7 @@ void mmu::set_par_pdr(const json_t *const j_in, const int run_mode, const bool i
mmu *mmu::deserialize(const json_t *const j)
{
mmu *m = new mmu();
m->begin();
for(int run_mode=0; run_mode<4; run_mode++) {
if (run_mode == 2)

2
mmu.h
View file

@ -50,6 +50,8 @@ public:
mmu();
virtual ~mmu();
void begin();
#if IS_POSIX
json_t *serialize() const;
static mmu *deserialize(const json_t *const j);

View file

@ -23,14 +23,11 @@ static const char * const regnames[] = {
"RK05_DATABUF "
};
rk05::rk05(const std::vector<disk_backend *> & files, bus *const b, std::atomic_bool *const disk_read_acitivity, std::atomic_bool *const disk_write_acitivity) :
rk05::rk05(bus *const b, std::atomic_bool *const disk_read_acitivity, std::atomic_bool *const disk_write_acitivity) :
b(b),
disk_read_acitivity(disk_read_acitivity),
disk_write_acitivity(disk_write_acitivity)
{
fhs = files;
reset();
}
rk05::~rk05()
@ -39,6 +36,11 @@ rk05::~rk05()
delete fh;
}
void rk05::begin()
{
reset();
}
void rk05::reset()
{
memset(registers, 0x00, sizeof registers);
@ -186,10 +188,10 @@ void rk05::writeWord(const uint16_t addr, const uint16_t v)
uint32_t temp_diskoffb = diskoffb;
uint32_t temp = reclen;
uint32_t temp_reclen = reclen;
uint32_t p = memoff;
while(temp > 0) {
uint32_t cur = std::min(uint32_t(sizeof xfer_buffer), temp);
while(temp_reclen > 0) {
uint32_t cur = std::min(uint32_t(sizeof xfer_buffer), temp_reclen);
if (!fhs.at(device)->read(temp_diskoffb, cur, xfer_buffer, 512)) {
DOLOG(ll_error, true, "RK05 read error %s from %u len %u", strerror(errno), temp_diskoffb, cur);
@ -205,7 +207,7 @@ void rk05::writeWord(const uint16_t addr, const uint16_t v)
update_bus_address(2);
}
temp -= cur;
temp_reclen -= cur;
if (++sector >= 12) {
sector = 0;
@ -248,3 +250,38 @@ void rk05::writeWord(const uint16_t addr, const uint16_t v)
}
}
}
#if IS_POSIX
json_t *rk05::serialize() const
{
json_t *j = json_object();
json_t *j_backends = json_array();
for(auto & dbe: fhs)
json_array_append(j_backends, dbe->serialize());
json_object_set(j, "backends", j_backends);
for(int regnr=0; regnr<7; regnr++)
json_object_set(j, format("register-%d", regnr).c_str(), json_integer(registers[regnr]));
return j;
}
rk05 *rk05::deserialize(const json_t *const j, bus *const b)
{
std::vector<disk_backend *> backends;
rk05 *r = new rk05(b, nullptr, nullptr);
r->begin();
json_t *j_backends = json_object_get(j, "backends");
for(size_t i=0; i<json_array_size(j_backends); i++)
r->access_disk_backends()->push_back(disk_backend::deserialize(json_array_get(j_backends, i)));
for(int regnr=0; regnr<7; regnr++)
r->registers[regnr] = json_integer_value(json_object_get(j, format("register-%d", regnr).c_str()));
return r;
}
#endif

9
rk05.h
View file

@ -11,6 +11,7 @@
#include "disk_device.h"
#include "disk_backend.h"
#include "gen.h"
#define RK05_DS 0177400 // drive status
@ -39,11 +40,17 @@ private:
void update_bus_address(const uint16_t v);
public:
rk05(const std::vector<disk_backend *> & files, bus *const b, std::atomic_bool *const disk_read_acitivity, std::atomic_bool *const disk_write_acitivity);
rk05(bus *const b, std::atomic_bool *const disk_read_acitivity, std::atomic_bool *const disk_write_acitivity);
virtual ~rk05();
void begin() override;
void reset() override;
#if IS_POSIX
json_t *serialize() const;
static rk05 *deserialize(const json_t *const j, bus *const b);
#endif
uint8_t readByte(const uint16_t addr) override;
uint16_t readWord(const uint16_t addr) override;

View file

@ -31,14 +31,11 @@ static const char * const commands[] = {
"read data w/o header check"
};
rl02::rl02(const std::vector<disk_backend *> & files, bus *const b, std::atomic_bool *const disk_read_activity, std::atomic_bool *const disk_write_activity) :
rl02::rl02(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)
{
fhs = files;
reset();
}
rl02::~rl02()
@ -47,6 +44,11 @@ rl02::~rl02()
delete fh;
}
void rl02::begin()
{
reset();
}
void rl02::reset()
{
memset(registers, 0x00, sizeof registers );
@ -86,11 +88,12 @@ rl02 *rl02::deserialize(const json_t *const j, bus *const b)
{
std::vector<disk_backend *> backends;
rl02 *r = new rl02(b, nullptr, nullptr);
r->begin();
json_t *j_backends = json_object_get(j, "backends");
for(size_t i=0; i<json_array_size(j_backends); i++)
backends.push_back(disk_backend::deserialize(json_array_get(j_backends, i)));
rl02 *r = new rl02(backends, b, nullptr, nullptr);
r->access_disk_backends()->push_back(disk_backend::deserialize(json_array_get(j_backends, i)));
for(int regnr=0; regnr<4; regnr++)
r->registers[regnr] = json_integer_value(json_object_get(j, format("register-%d", regnr).c_str()));
@ -201,7 +204,7 @@ void rl02::writeWord(const uint16_t addr, uint16_t v)
bool do_int = false;
if (size_t(device) >= fhs.size()) {
DOLOG(info, false, "RL02: PDP11/70 is accessing a not-attached virtual disk %d", device);
DOLOG(info, false, "RL02: PDP11/70 is accessing virtual disk %d which is not attached", device);
registers[(RL02_CSR - RL02_BASE) / 2] |= (1 << 10) | (1 << 15);
@ -365,7 +368,7 @@ void rl02::writeWord(const uint16_t addr, uint16_t v)
*disk_read_activity = false;
}
else {
DOLOG(warning, false, "RL02: command %d not implemented", command);
DOLOG(debug, false, "RL02: command %d not implemented", command);
}
if (do_int) {

3
rl02.h
View file

@ -47,9 +47,10 @@ private:
uint32_t calc_offset() const;
public:
rl02(const std::vector<disk_backend *> & files, bus *const b, std::atomic_bool *const disk_read_activity, std::atomic_bool *const disk_write_activity);
rl02(bus *const b, std::atomic_bool *const disk_read_activity, std::atomic_bool *const disk_write_activity);
virtual ~rl02();
void begin() override;
void reset() override;
#if IS_POSIX

View file

@ -10,11 +10,8 @@
#include "memory.h"
#include "utils.h"
tm_11::tm_11(const std::string & file, memory *const m) : m(m)
tm_11::tm_11(const std::string & file, memory *const m): file(file), m(m)
{
fh = fopen(file.c_str(), "rb");
reset();
}
tm_11::~tm_11()
@ -22,6 +19,13 @@ tm_11::~tm_11()
fclose(fh);
}
void tm_11::begin()
{
fh = fopen(file.c_str(), "rb");
reset();
}
void tm_11::reset()
{
memset(registers, 0x00, sizeof registers );

View file

@ -24,6 +24,7 @@ class memory;
class tm_11 : public device
{
private:
std::string file;
memory *const m { nullptr };
uint16_t registers[6] { 0 };
uint8_t xfer_buffer[65536];
@ -34,6 +35,8 @@ public:
tm_11(const std::string & file, memory *const m);
virtual ~tm_11();
void begin();
void reset() override;
uint8_t readByte(const uint16_t addr) override;

View file

@ -19,7 +19,9 @@
#include <vector>
#include <sys/time.h>
#if defined(_WIN32)
#include "win32.h"
#endif
void setBit(uint16_t & v, const int bit, const bool vb)
@ -34,8 +36,8 @@ void setBit(uint16_t & v, const int bit, const bool vb)
std::string format(const char *const fmt, ...)
{
#if defined(BUILD_FOR_RP2040)
char buffer[128];
#if defined(BUILD_FOR_RP2040) || defined(ESP32)
char buffer[256];
va_list ap;
va_start(ap, fmt);