mirror of
https://github.com/idolpx/libMeatloaf.git
synced 2025-12-06 04:38:49 -05:00
981 lines
28 KiB
C++
981 lines
28 KiB
C++
//#ifdef BUILD_IEC
|
|
|
|
#include "iec.h"
|
|
|
|
#include <cstring>
|
|
#include <memory>
|
|
|
|
#include "../../include/debug.h"
|
|
//#include "../../include/pinmap.h"
|
|
#include "../../include/cbm_defines.h"
|
|
//#include "led.h"
|
|
//#include "led_strip.h"
|
|
//#include "protocol/cpbstandardserial.h"
|
|
#include "string_utils.h"
|
|
//#include "utils.h"
|
|
|
|
//using namespace Protocol;
|
|
|
|
systemBus IEC;
|
|
|
|
static void cbm_on_attention_isr_handler(void *arg)
|
|
{
|
|
// systemBus *b = (systemBus *)arg;
|
|
|
|
// //b->pull(PIN_IEC_SRQ);
|
|
|
|
// // Go to listener mode and get command
|
|
// b->release(PIN_IEC_CLK_OUT);
|
|
// b->pull(PIN_IEC_DATA_OUT);
|
|
|
|
// b->flags = CLEAR;
|
|
// b->flags |= ATN_PULLED;
|
|
// b->bus_state = BUS_ACTIVE;
|
|
|
|
// //b->release(PIN_IEC_SRQ);
|
|
}
|
|
|
|
/**
|
|
* Static callback function for the interrupt rate limiting timer. It sets the interruptProceed
|
|
* flag to true. This is set to false when the interrupt is serviced.
|
|
*/
|
|
static void onTimer(void *info)
|
|
{
|
|
// systemBus *parent = (systemBus *)info;
|
|
// //portENTER_CRITICAL_ISR(&parent->timerMux);
|
|
// parent->interruptSRQ = !parent->interruptSRQ;
|
|
// //portEXIT_CRITICAL_ISR(&parent->timerMux);
|
|
}
|
|
|
|
static void ml_iec_intr_task(void* arg)
|
|
{
|
|
// while ( true )
|
|
// {
|
|
// // if ( IEC.enabled )
|
|
// // {
|
|
// IEC.service();
|
|
// if ( IEC.bus_state < BUS_ACTIVE )
|
|
// taskYIELD();
|
|
// // }
|
|
// }
|
|
}
|
|
|
|
void init_pin(int pin)
|
|
{
|
|
// gpio_set_direction(pin, GPIO_MODE_INPUT);
|
|
// gpio_set_pull_mode(pin, GPIO_PULLUP_ONLY);
|
|
// gpio_set_level(pin, LOW);
|
|
// return;
|
|
}
|
|
|
|
|
|
// true => PULL => LOW
|
|
void systemBus::pull ( int _pin )
|
|
{
|
|
// int _reg = GPIO_ENABLE_REG;
|
|
// if (_pin > 31)
|
|
// {
|
|
// _reg = GPIO_ENABLE1_REG, _pin -= 32;
|
|
// }
|
|
// REG_SET_BIT(_reg, 1 << _pin); // GPIO_MODE_OUTPUT
|
|
}
|
|
|
|
// false => RELEASE => HIGH
|
|
void systemBus::release ( int _pin )
|
|
{
|
|
// int _reg = GPIO_ENABLE_REG;
|
|
// if (_pin > 31)
|
|
// {
|
|
// _reg = GPIO_ENABLE1_REG, _pin -= 32;
|
|
// }
|
|
// REG_CLR_BIT(_reg, 1 << _pin); // GPIO_MODE_INPUT
|
|
}
|
|
|
|
bool systemBus::status ( int _pin )
|
|
{
|
|
// #ifndef IEC_SPLIT_LINES
|
|
// release ( _pin );
|
|
// #endif
|
|
|
|
// return gpio_get_level ( ( gpio_num_t ) _pin ) ? RELEASED : PULLED;
|
|
}
|
|
|
|
// int IRAM_ATTR systemBus::status()
|
|
// {
|
|
// int data = 0;
|
|
|
|
// gpio_config_t io_config =
|
|
// {
|
|
// .pin_bit_mask = BIT(PIN_IEC_CLK_IN) | BIT(PIN_IEC_DATA_IN) | BIT(PIN_IEC_SRQ) | BIT(PIN_IEC_RESET),
|
|
// .mode = GPIO_MODE_INPUT,
|
|
// .pull_up_en = GPIO_PULLUP_ENABLE,
|
|
// .intr_type = GPIO_INTR_DISABLE,
|
|
// };
|
|
|
|
// ESP_ERROR_CHECK(gpio_config(&io_config));
|
|
// uint64_t io_data = REG_READ(GPIO_IN_REG);
|
|
|
|
// //data |= (0 & (io_data & (1 << PIN_IEC_ATN)));
|
|
// data |= (1 & (io_data & (1 << PIN_IEC_CLK_IN)));
|
|
// data |= (2 & (io_data & (1 << PIN_IEC_DATA_IN)));
|
|
// data |= (3 & (io_data & (1 << PIN_IEC_SRQ)));
|
|
// data |= (4 & (io_data & (1 << PIN_IEC_RESET)));
|
|
|
|
// return data;
|
|
// }
|
|
|
|
void systemBus::setup()
|
|
{
|
|
// Debug_printf("IEC systemBus::setup()\r\n");
|
|
|
|
// flags = CLEAR;
|
|
// protocol = selectProtocol();
|
|
// release(PIN_IEC_CLK_OUT);
|
|
// release(PIN_IEC_DATA_OUT);
|
|
// release(PIN_IEC_SRQ);
|
|
|
|
// // initial pin modes in GPIO
|
|
// init_pin(PIN_IEC_ATN);
|
|
// init_pin(PIN_IEC_CLK_OUT);
|
|
// init_pin(PIN_IEC_DATA_OUT);
|
|
// init_pin(PIN_IEC_SRQ);
|
|
// #ifdef IEC_HAS_RESET
|
|
// init_pin(PIN_IEC_RESET);
|
|
// #endif
|
|
|
|
// // Start task
|
|
// //xTaskCreate(ml_iec_intr_task, "ml_iec_intr_task", 2048, NULL, 10, NULL);
|
|
// xTaskCreatePinnedToCore(ml_iec_intr_task, "ml_iec_intr_task", 4096, NULL, 20, NULL, 1);
|
|
|
|
// // Setup interrupt for ATN
|
|
// gpio_config_t io_conf = {
|
|
// .pin_bit_mask = ( 1ULL << PIN_IEC_ATN ), // bit mask of the pins that you want to set
|
|
// .mode = GPIO_MODE_INPUT, // set as input mode
|
|
// .pull_up_en = GPIO_PULLUP_DISABLE, // disable pull-up mode
|
|
// .pull_down_en = GPIO_PULLDOWN_DISABLE, // disable pull-down mode
|
|
// .intr_type = GPIO_INTR_NEGEDGE // interrupt of falling edge
|
|
// };
|
|
// //configure GPIO with the given settings
|
|
// gpio_config(&io_conf);
|
|
// gpio_isr_handler_add((gpio_num_t)PIN_IEC_ATN, cbm_on_attention_isr_handler, this);
|
|
|
|
// // Start SRQ timer service
|
|
// timer_start();
|
|
}
|
|
|
|
|
|
void systemBus::service()
|
|
{
|
|
// // pull( PIN_IEC_SRQ );
|
|
|
|
// // Disable Interrupt
|
|
// // gpio_intr_disable((gpio_num_t)PIN_IEC_ATN);
|
|
|
|
// // TODO IMPLEMENT
|
|
|
|
// if (bus_state < BUS_ACTIVE)
|
|
// {
|
|
// // Handle SRQ for devices
|
|
// for (auto devicep : _daisyChain)
|
|
// {
|
|
// for (unsigned char i=0;i<16;i++)
|
|
// devicep->poll_interrupt(i);
|
|
// }
|
|
|
|
// return;
|
|
// }
|
|
|
|
// #ifdef IEC_HAS_RESET
|
|
|
|
// // Check if CBM is sending a reset (setting the RESET line high). This is typically
|
|
// // when the CBM is reset itself. In this case, we are supposed to reset all states to initial.
|
|
// bool pin_reset = status(PIN_IEC_RESET);
|
|
// if (pin_reset == PULLED)
|
|
// {
|
|
// if (status(PIN_IEC_ATN) == PULLED)
|
|
// {
|
|
// // If RESET & ATN are both PULLED then CBM is off
|
|
// bus_state = BUS_OFFLINE;
|
|
// // gpio_intr_enable((gpio_num_t)PIN_IEC_ATN);
|
|
// return;
|
|
// }
|
|
|
|
// Debug_printf("IEC Reset! reset[%d]\r\n", pin_reset);
|
|
// data.init(); // Clear bus data
|
|
// releaseLines();
|
|
// bus_state = BUS_IDLE;
|
|
// Debug_printv("bus init");
|
|
|
|
// // Reset virtual devices
|
|
// reset_all_our_devices();
|
|
// // gpio_intr_enable((gpio_num_t)PIN_IEC_ATN);
|
|
// return;
|
|
// }
|
|
|
|
// #endif
|
|
|
|
// // Command or Data Mode
|
|
// do
|
|
// {
|
|
// // Exit if bus is offline
|
|
// if (bus_state == BUS_OFFLINE)
|
|
// break;
|
|
|
|
// if (bus_state == BUS_ACTIVE)
|
|
// {
|
|
// pull ( PIN_IEC_SRQ );
|
|
|
|
// //release ( PIN_IEC_CLK_OUT );
|
|
// //pull ( PIN_IEC_DATA_OUT );
|
|
|
|
// //flags = CLEAR;
|
|
|
|
// // Read bus command bytes
|
|
// //Debug_printv("command");
|
|
// read_command();
|
|
|
|
// release ( PIN_IEC_SRQ );
|
|
// }
|
|
|
|
// if (bus_state == BUS_PROCESS)
|
|
// {
|
|
// // Sometimes ATN isn't released immediately. Wait for ATN to be
|
|
// // released before trying to read payload. Long ATN delay (>1.5ms)
|
|
// // seems to occur more frequently with VIC-20.
|
|
// // protocol->timeoutWait(PIN_IEC_ATN, RELEASED, FOREVER, false);
|
|
|
|
// //Debug_printv("data");
|
|
// pull ( PIN_IEC_SRQ );
|
|
// if (data.secondary == IEC_OPEN || data.secondary == IEC_REOPEN)
|
|
// {
|
|
// // Switch to detected protocol
|
|
// //pull ( PIN_IEC_SRQ );
|
|
// protocol = selectProtocol();
|
|
// //release ( PIN_IEC_SRQ );
|
|
// }
|
|
|
|
// // Data Mode - Get Command or Data
|
|
// if (data.primary == IEC_LISTEN)
|
|
// {
|
|
// //Debug_printv("calling deviceListen()\r\n");
|
|
// deviceListen();
|
|
// }
|
|
// else if (data.primary == IEC_TALK)
|
|
// {
|
|
// //Debug_printv("calling deviceTalk()\r\n");
|
|
// deviceTalk();
|
|
// }
|
|
|
|
// // Queue control codes and command in specified device
|
|
// device_state_t device_state = deviceById(data.device)->queue_command(data);
|
|
|
|
// fnLedManager.set(eLed::LED_BUS, true);
|
|
|
|
// //Debug_printv("bus[%d] device[%d]", bus_state, device_state);
|
|
// device_state = deviceById(data.device)->process();
|
|
// if ( device_state < DEVICE_ACTIVE )
|
|
// {
|
|
// // for (auto devicep : _daisyChain)
|
|
// // {
|
|
// // if ( devicep->device_state > DEVICE_IDLE )
|
|
// // devicep->process();
|
|
// // }
|
|
// data.init();
|
|
// //Debug_printv("bus init");
|
|
// }
|
|
|
|
// //Debug_printv("bus[%d] device[%d] flags[%d]", bus_state, device_state, flags);
|
|
// bus_state = BUS_IDLE;
|
|
|
|
// // Switch back to standard serial
|
|
// detected_protocol = PROTOCOL_SERIAL;
|
|
// protocol = selectProtocol();
|
|
// release ( PIN_IEC_SRQ );
|
|
// }
|
|
|
|
// if ( status ( PIN_IEC_ATN ) )
|
|
// bus_state = BUS_ACTIVE;
|
|
|
|
// } while( bus_state > BUS_IDLE );
|
|
|
|
// // Cleanup and Re-enable Interrupt
|
|
// releaseLines();
|
|
// //gpio_intr_enable((gpio_num_t)PIN_IEC_ATN);
|
|
|
|
// //Debug_printv ( "primary[%.2X] secondary[%.2X] bus[%d] flags[%d]", data.primary, data.secondary, bus_state, flags );
|
|
// //Debug_printv ( "device[%d] channel[%d]", data.device, data.channel);
|
|
|
|
// Debug_printv("exit");
|
|
// Debug_printv("bus[%d] flags[%d]", bus_state, flags);
|
|
// //release( PIN_IEC_SRQ );
|
|
|
|
// //fnLedStrip.stopRainbow();
|
|
// //fnLedManager.set(eLed::LED_BUS, false);
|
|
}
|
|
|
|
void systemBus::read_command()
|
|
{
|
|
// // ATN was pulled, read bus command bytes
|
|
// // Sometimes the C64 pulls ATN but doesn't pull CLOCK right away
|
|
// //pull( PIN_IEC_SRQ );
|
|
// if ( protocol->timeoutWait ( PIN_IEC_CLK_IN, PULLED, TIMING_DELAY, false ) == TIMED_OUT )
|
|
// {
|
|
// Debug_printv ( "ATN/Clock delay" );
|
|
// return; // return error because timeout
|
|
// }
|
|
// //release( PIN_IEC_SRQ );
|
|
|
|
// //pull( PIN_IEC_SRQ );
|
|
// uint8_t c = receiveByte();
|
|
// //release( PIN_IEC_SRQ );
|
|
|
|
// // Check for error
|
|
// if ( flags & ERROR )
|
|
// {
|
|
// Debug_printv("Error reading command. flags[%d]", flags);
|
|
// if (c == 0xFFFFFFFF)
|
|
// bus_state = BUS_OFFLINE;
|
|
// else
|
|
// bus_state = BUS_ERROR;
|
|
// }
|
|
// else if ( flags & EMPTY_STREAM)
|
|
// {
|
|
// bus_state = BUS_IDLE;
|
|
// }
|
|
// else
|
|
// {
|
|
// if ( flags & JIFFYDOS_ACTIVE )
|
|
// {
|
|
// Debug_printf(" IEC: [JD][%.2X]", c);
|
|
// detected_protocol = PROTOCOL_JIFFYDOS;
|
|
// }
|
|
// else
|
|
// {
|
|
// Debug_printf(" IEC: [%.2X]", c);
|
|
// }
|
|
|
|
// // Decode command byte
|
|
// uint8_t command = c & 0x60;
|
|
// if (c == IEC_UNLISTEN)
|
|
// command = IEC_UNLISTEN;
|
|
// if (c == IEC_UNTALK)
|
|
// command = IEC_UNTALK;
|
|
|
|
// //Debug_printv ( "device[%d] channel[%d]", data.device, data.channel);
|
|
// //Debug_printv ("command[%.2X]", command);
|
|
|
|
// switch (command)
|
|
// {
|
|
// // case IEC_GLOBAL:
|
|
// // data.primary = IEC_GLOBAL;
|
|
// // data.device = c ^ IEC_GLOBAL;
|
|
// // bus_state = BUS_IDLE;
|
|
// // Debug_printf(" (00 GLOBAL %.2d COMMAND)\r\n", data.device);
|
|
// // break;
|
|
|
|
// case IEC_LISTEN:
|
|
// data.primary = IEC_LISTEN;
|
|
// data.device = c ^ IEC_LISTEN;
|
|
// data.secondary = IEC_REOPEN; // Default secondary command
|
|
// data.channel = CHANNEL_COMMAND; // Default channel
|
|
// data.payload = "";
|
|
// bus_state = BUS_ACTIVE;
|
|
// Debug_printf(" (20 LISTEN %.2d DEVICE)\r\n", data.device);
|
|
// break;
|
|
|
|
// case IEC_UNLISTEN:
|
|
// data.primary = IEC_UNLISTEN;
|
|
// bus_state = BUS_PROCESS;
|
|
// Debug_printf(" (3F UNLISTEN)\r\n");
|
|
// break;
|
|
|
|
// case IEC_TALK:
|
|
// data.primary = IEC_TALK;
|
|
// data.device = c ^ IEC_TALK;
|
|
// data.secondary = IEC_REOPEN; // Default secondary command
|
|
// data.channel = CHANNEL_COMMAND; // Default channel
|
|
// bus_state = BUS_ACTIVE;
|
|
// Debug_printf(" (40 TALK %.2d DEVICE)\r\n", data.device);
|
|
// break;
|
|
|
|
// case IEC_UNTALK:
|
|
// data.primary = IEC_UNTALK;
|
|
// data.secondary = 0x00;
|
|
// bus_state = BUS_PROCESS;
|
|
// Debug_printf(" (5F UNTALK)\r\n");
|
|
// break;
|
|
|
|
// default:
|
|
|
|
// //pull ( PIN_IEC_SRQ );
|
|
// std::string secondary;
|
|
// bus_state = BUS_PROCESS;
|
|
|
|
// command = c & 0xF0;
|
|
// switch ( command )
|
|
// {
|
|
// case IEC_OPEN:
|
|
// data.secondary = IEC_OPEN;
|
|
// data.channel = c ^ IEC_OPEN;
|
|
// secondary = "OPEN";
|
|
// break;
|
|
|
|
// case IEC_REOPEN:
|
|
// data.secondary = IEC_REOPEN;
|
|
// data.channel = c ^ IEC_REOPEN;
|
|
// secondary = "DATA";
|
|
// break;
|
|
|
|
// case IEC_CLOSE:
|
|
// data.secondary = IEC_CLOSE;
|
|
// data.channel = c ^ IEC_CLOSE;
|
|
// secondary = "CLOSE";
|
|
// break;
|
|
|
|
// default:
|
|
// bus_state = BUS_IDLE;
|
|
// }
|
|
|
|
// // *** IMPORTANT! This helps keep us in sync!
|
|
// protocol->wait( TIMING_SYNC, false);
|
|
// //release ( PIN_IEC_SRQ );
|
|
|
|
// Debug_printf(" (%.2X %s %.2d CHANNEL)\r\n", data.secondary, secondary.c_str(), data.channel);
|
|
// }
|
|
// }
|
|
|
|
// // Is this command for us?
|
|
// if ( !isDeviceEnabled( data.device ) )
|
|
// // if (!deviceById(data.device) || !deviceById(data.device)->device_active)
|
|
// {
|
|
// bus_state = BUS_IDLE;
|
|
// }
|
|
|
|
// // If the bus is idle then release the lines
|
|
// if ( bus_state < BUS_ACTIVE )
|
|
// {
|
|
// data.init();
|
|
// releaseLines();
|
|
// return;
|
|
// }
|
|
|
|
// #ifdef PARALLEL_BUS
|
|
// // Switch to Parallel if detected
|
|
// if ( PARALLEL.bus_state == PBUS_PROCESS )
|
|
// {
|
|
// if ( data.primary == IEC_LISTEN || data.primary == IEC_TALK )
|
|
// detected_protocol = PROTOCOL_SPEEDDOS;
|
|
// else if ( data.primary == IEC_OPEN || data.primary == IEC_REOPEN )
|
|
// detected_protocol = PROTOCOL_DOLPHINDOS;
|
|
|
|
// // Switch to parallel protocol
|
|
// protocol = selectProtocol();
|
|
|
|
// if ( data.primary == IEC_LISTEN )
|
|
// PARALLEL.setMode( MODE_RECEIVE );
|
|
// else
|
|
// PARALLEL.setMode( MODE_SEND );
|
|
|
|
// // Acknowledge parallel mode
|
|
// PARALLEL.handShake();
|
|
// }
|
|
// #endif
|
|
|
|
// //Debug_printv ( "code[%.2X] primary[%.2X] secondary[%.2X] bus[%d] flags[%d]", c, data.primary, data.secondary, bus_state, flags );
|
|
// //Debug_printv ( "device[%d] channel[%d]", data.device, data.channel);
|
|
|
|
// // Delay to stabalize bus
|
|
// // protocol->wait( TIMING_STABLE, false );
|
|
|
|
// //release( PIN_IEC_SRQ );
|
|
}
|
|
|
|
void systemBus::read_payload()
|
|
{
|
|
// // Record the command string until ATN is PULLED
|
|
// std::string listen_command = "";
|
|
|
|
// // ATN might get pulled right away if there is no command string to send
|
|
// //pull ( PIN_IEC_SRQ );
|
|
|
|
// // Sometimes ATN isn't released immediately. Wait for ATN to be
|
|
// // released before trying to read payload. Long ATN delay (>1.5ms)
|
|
// // seems to occur more frequently with VIC-20.
|
|
// protocol->timeoutWait(PIN_IEC_ATN, RELEASED, FOREVER, false);
|
|
|
|
// while (IEC.status(PIN_IEC_ATN) != PULLED)
|
|
// {
|
|
// //pull ( PIN_IEC_SRQ );
|
|
// int8_t c = protocol->receiveByte();
|
|
// //Debug_printv("c[%2X]", c);
|
|
// //release ( PIN_IEC_SRQ );
|
|
|
|
// if (flags & EMPTY_STREAM || flags & ERROR)
|
|
// {
|
|
// Debug_printv("flags[%2X]", flags);
|
|
// bus_state = BUS_ERROR;
|
|
// return;
|
|
// }
|
|
|
|
// if (c != 0xFFFFFFFF && c != 0x0D)
|
|
// {
|
|
// listen_command += (uint8_t)c;
|
|
// }
|
|
|
|
// if (flags & EOI_RECVD)
|
|
// {
|
|
// data.payload = listen_command;
|
|
// break;
|
|
// }
|
|
// }
|
|
|
|
// bus_state = BUS_IDLE;
|
|
}
|
|
|
|
/**
|
|
* Start the Interrupt rate limiting timer
|
|
*/
|
|
void systemBus::timer_start()
|
|
{
|
|
// esp_timer_create_args_t tcfg;
|
|
// tcfg.arg = this;
|
|
// tcfg.callback = onTimer;
|
|
// tcfg.dispatch_method = esp_timer_dispatch_t::ESP_TIMER_TASK;
|
|
// tcfg.name = nullptr;
|
|
// esp_timer_create(&tcfg, &rateTimerHandle);
|
|
// esp_timer_start_periodic(rateTimerHandle, timerRate * 1000);
|
|
}
|
|
|
|
/**
|
|
* Stop the Interrupt rate limiting timer
|
|
*/
|
|
void systemBus::timer_stop()
|
|
{
|
|
// // Delete existing timer
|
|
// if (rateTimerHandle != nullptr)
|
|
// {
|
|
// Debug_println("Deleting existing rateTimer\r\n");
|
|
// esp_timer_stop(rateTimerHandle);
|
|
// esp_timer_delete(rateTimerHandle);
|
|
// rateTimerHandle = nullptr;
|
|
// }
|
|
}
|
|
|
|
std::shared_ptr<std::string> systemBus::selectProtocol()
|
|
{
|
|
// //Debug_printv("protocol[%d]", detected_protocol);
|
|
|
|
// switch(detected_protocol)
|
|
// {
|
|
// #ifdef MEATLOAF_MAX
|
|
// case PROTOCOL_SAUCEDOS:
|
|
// {
|
|
// auto p = std::make_shared<SauceDOS>();
|
|
// return std::static_pointer_cast<IECProtocol>(p);
|
|
// }
|
|
// #endif
|
|
// case PROTOCOL_JIFFYDOS:
|
|
// {
|
|
// auto p = std::make_shared<JiffyDOS>();
|
|
// return std::static_pointer_cast<IECProtocol>(p);
|
|
// }
|
|
// #ifdef PARALLEL_BUS
|
|
// case PROTOCOL_DOLPHINDOS:
|
|
// {
|
|
// auto p = std::make_shared<DolphinDOS>();
|
|
// return std::static_pointer_cast<IECProtocol>(p);
|
|
// }
|
|
// #endif
|
|
// default:
|
|
// {
|
|
// #ifdef PARALLEL_BUS
|
|
// PARALLEL.bus_state = PBUS_IDLE;
|
|
// #endif
|
|
// auto p = std::make_shared<CPBStandardSerial>();
|
|
// return std::static_pointer_cast<IECProtocol>(p);
|
|
// }
|
|
// }
|
|
}
|
|
|
|
systemBus virtualDevice::get_bus()
|
|
{
|
|
return IEC;
|
|
}
|
|
|
|
device_state_t virtualDevice::process()
|
|
{
|
|
// switch ((bus_command_t)commanddata.primary)
|
|
// {
|
|
// case bus_command_t::IEC_LISTEN:
|
|
// device_state = DEVICE_LISTEN;
|
|
// break;
|
|
// case bus_command_t::IEC_UNLISTEN:
|
|
// device_state = DEVICE_PROCESS;
|
|
// break;
|
|
// case bus_command_t::IEC_TALK:
|
|
// device_state = DEVICE_TALK;
|
|
// break;
|
|
// default:
|
|
// break;
|
|
// }
|
|
|
|
// switch ((bus_command_t)commanddata.secondary)
|
|
// {
|
|
// case bus_command_t::IEC_OPEN:
|
|
// payload = commanddata.payload;
|
|
// break;
|
|
// case bus_command_t::IEC_CLOSE:
|
|
// payload.clear();
|
|
// std::queue<std::string>().swap(response_queue);
|
|
// pt.clear();
|
|
// pt.shrink_to_fit();
|
|
// break;
|
|
// case bus_command_t::IEC_REOPEN:
|
|
// if (device_state == DEVICE_TALK)
|
|
// {
|
|
// }
|
|
// else if (device_state == DEVICE_LISTEN)
|
|
// {
|
|
// payload = commanddata.payload;
|
|
// }
|
|
// break;
|
|
// default:
|
|
// break;
|
|
// }
|
|
|
|
// return device_state;
|
|
}
|
|
|
|
void virtualDevice::iec_talk_command_buffer_status()
|
|
{
|
|
// char reply[80];
|
|
// std::string s;
|
|
|
|
// //fnSystem.delay_microseconds(100);
|
|
|
|
// if (!status_override.empty())
|
|
// {
|
|
// Debug_printv("sending explicit response.");
|
|
// IEC.sendBytes(status_override, true);
|
|
// status_override.clear();
|
|
// status_override.shrink_to_fit();
|
|
// }
|
|
// else
|
|
// {
|
|
// snprintf(reply, 80, "%u,%s,%u,%u", iecStatus.error, iecStatus.msg.c_str(), iecStatus.connected, iecStatus.channel);
|
|
// s = std::string(reply);
|
|
// // s = mstr::toPETSCII2(s);
|
|
// Debug_printv("sending status: %s\r\n", reply);
|
|
// IEC.sendBytes(s, true);
|
|
// }
|
|
}
|
|
|
|
void virtualDevice::dumpData()
|
|
{
|
|
Debug_printf("%9s: %02X\r\n", "Primary", commanddata.primary);
|
|
Debug_printf("%9s: %02u\r\n", "Device", commanddata.device);
|
|
Debug_printf("%9s: %02X\r\n", "Secondary", commanddata.secondary);
|
|
Debug_printf("%9s: %02u\r\n", "Channel", commanddata.channel);
|
|
Debug_printf("%9s: %s\r\n", "Payload", commanddata.payload.c_str());
|
|
}
|
|
|
|
void systemBus::assert_interrupt()
|
|
{
|
|
// if (interruptSRQ)
|
|
// IEC.pull(PIN_IEC_SRQ);
|
|
// else
|
|
// IEC.release(PIN_IEC_SRQ);
|
|
}
|
|
|
|
int8_t systemBus::receiveByte()
|
|
{
|
|
int8_t b = 0; //protocol->receiveByte();
|
|
#ifdef DATA_STREAM
|
|
Debug_printf("%.2X ", (uint8_t)b);
|
|
#endif
|
|
if (b == -1)
|
|
{
|
|
if (!(IEC.flags & ATN_PULLED))
|
|
{
|
|
IEC.flags |= ERROR;
|
|
Debug_printv("error");
|
|
}
|
|
}
|
|
return b;
|
|
}
|
|
|
|
std::string systemBus::receiveBytes()
|
|
{
|
|
std::string s;
|
|
|
|
// do
|
|
// {
|
|
// int8_t b = receiveByte();
|
|
// if(b > -1)
|
|
// s += b;
|
|
// }while(!(flags & EOI_RECVD));
|
|
return s;
|
|
}
|
|
|
|
bool systemBus::sendByte(const char c, bool eoi)
|
|
{
|
|
// if (!protocol->sendByte(c, eoi))
|
|
// {
|
|
// if (!(IEC.flags & ATN_PULLED))
|
|
// {
|
|
// IEC.flags |= ERROR;
|
|
// Debug_printv("error");
|
|
// return false;
|
|
// }
|
|
// }
|
|
|
|
//#ifdef DATA_STREAM
|
|
if (eoi)
|
|
Debug_printf("%.2X[eoi] ", c);
|
|
else
|
|
Debug_printf("%.2X ", c);
|
|
//#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
bool systemBus::sendBytes(const char *buf, size_t len, bool eoi)
|
|
{
|
|
bool success = false;
|
|
|
|
for (size_t i = 0; i < len; i++)
|
|
{
|
|
if (i == (len - 1) && eoi)
|
|
success = sendByte(buf[i], true);
|
|
else
|
|
success = sendByte(buf[i], false);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
bool systemBus::sendBytes(std::string s, bool eoi)
|
|
{
|
|
return sendBytes(s.c_str(), s.size(), eoi);
|
|
}
|
|
|
|
void systemBus::process_cmd()
|
|
{
|
|
// fnLedManager.set(eLed::LED_BUS, true);
|
|
|
|
// TODO implement
|
|
|
|
// fnLedManager.set(eLed::LED_BUS, false);
|
|
}
|
|
|
|
void systemBus::process_queue()
|
|
{
|
|
// TODO IMPLEMENT
|
|
}
|
|
|
|
void systemBus::deviceListen()
|
|
{
|
|
// // If the command is SECONDARY and it is not to expect just a small command on the command channel, then
|
|
// // we're into something more heavy. Otherwise read it all out right here until UNLISTEN is received.
|
|
// if (data.secondary == IEC_REOPEN && data.channel != CHANNEL_COMMAND)
|
|
// {
|
|
// // A heapload of data might come now, too big for this context to handle so the caller handles this, we're done here.
|
|
// // Debug_printf(" (%.2X SECONDARY) (%.2X CHANNEL)\r\n", data.primary, data.channel);
|
|
// Debug_printf("REOPEN on non-command channel.\r\n");
|
|
// bus_state = BUS_ACTIVE;
|
|
// }
|
|
|
|
// // OPEN or DATA
|
|
// else if (data.secondary == IEC_OPEN || data.secondary == IEC_REOPEN)
|
|
// {
|
|
// read_payload();
|
|
// Debug_printf("{%s}\r\n", data.payload.c_str());
|
|
// }
|
|
|
|
// // CLOSE Named Channel
|
|
// else if (data.secondary == IEC_CLOSE)
|
|
// {
|
|
// // Debug_printf(" (E0 CLOSE) (%d CHANNEL)\r\n", data.channel);
|
|
// bus_state = BUS_PROCESS;
|
|
// }
|
|
|
|
// // Unknown
|
|
// else
|
|
// {
|
|
// Debug_printf(" OTHER (%.2X COMMAND) (%.2X CHANNEL) ", data.secondary, data.channel);
|
|
// bus_state = BUS_ERROR;
|
|
// }
|
|
}
|
|
|
|
void systemBus::deviceTalk(void)
|
|
{
|
|
// // Now do bus turnaround
|
|
// //pull(PIN_IEC_SRQ);
|
|
// if (!turnAround())
|
|
// {
|
|
// Debug_printv("error flags[%d]", flags);
|
|
// bus_state = BUS_ERROR;
|
|
// return;
|
|
// }
|
|
// //release(PIN_IEC_SRQ);
|
|
|
|
// We have recieved a CMD and we should talk now:
|
|
bus_state = BUS_PROCESS;
|
|
}
|
|
|
|
bool systemBus::turnAround()
|
|
{
|
|
/*
|
|
TURNAROUND
|
|
An unusual sequence takes place following ATN if the computer wishes the remote device to
|
|
become a talker. This will usually take place only after a Talk command has been sent.
|
|
Immediately after ATN is RELEASED, the selected device will be behaving like a listener. After all, it's
|
|
been listening during the ATN cycle, and the computer has been a talker. At this instant, we
|
|
have "wrong way" logic; the device is holding down the Data line, and the computer is holding the
|
|
Clock line. We must turn this around. Here's the sequence:
|
|
the computer quickly realizes what's going on, and pulls the Data line to true (it's already there), as
|
|
well as releasing the Clock line to false. The device waits for this: when it sees the Clock line go
|
|
true [sic], it releases the Data line (which stays true anyway since the computer is now holding it down)
|
|
and then pulls down the Clock line. We're now in our starting position, with the talker (that's the
|
|
device) holding the Clock true, and the listener (the computer) holding the Data line true. The
|
|
computer watches for this state; only when it has gone through the cycle correctly will it be ready
|
|
to receive data. And data will be signalled, of course, with the usual sequence: the talker releases
|
|
the Clock line to signal that it's ready to send.
|
|
*/
|
|
// Debug_printf("IEC turnAround: ");
|
|
|
|
// // Wait until the computer releases the ATN line
|
|
// if (protocol->timeoutWait(PIN_IEC_ATN, RELEASED, FOREVER) == TIMED_OUT)
|
|
// {
|
|
// Debug_printf("Wait until the computer releases the ATN line");
|
|
// flags |= ERROR;
|
|
// return false; // return error because timeout
|
|
// }
|
|
// release ( PIN_IEC_DATA_OUT );
|
|
|
|
// // Delay after ATN is RELEASED
|
|
// protocol->wait( ( TIMING_Ttk + TIMING_Tda ), 0, false );
|
|
// pull ( PIN_IEC_CLK_OUT );
|
|
|
|
// // Debug_println("turnaround complete");
|
|
return true;
|
|
} // turnAround
|
|
|
|
void systemBus::reset_all_our_devices()
|
|
{
|
|
// TODO iterate through our bus and send reset to each device.
|
|
}
|
|
|
|
void systemBus::setBitTiming(std::string set, int p1, int p2, int p3, int p4)
|
|
{
|
|
// uint8_t i = 0; // Send
|
|
// if (mstr::equals(set, "r")) i = 1;
|
|
// if (p1) protocol->bit_pair_timing[i][0] = p1;
|
|
// if (p2) protocol->bit_pair_timing[i][1] = p2;
|
|
// if (p3) protocol->bit_pair_timing[i][2] = p3;
|
|
// if (p4) protocol->bit_pair_timing[i][3] = p4;
|
|
|
|
// Debug_printv("i[%d] timing[%d][%d][%d][%d]", i,
|
|
// protocol->bit_pair_timing[i][0],
|
|
// protocol->bit_pair_timing[i][1],
|
|
// protocol->bit_pair_timing[i][2],
|
|
// protocol->bit_pair_timing[i][3]);
|
|
}
|
|
|
|
void systemBus::releaseLines(bool wait)
|
|
{
|
|
// // Release lines
|
|
// release(PIN_IEC_CLK_OUT);
|
|
// release(PIN_IEC_DATA_OUT);
|
|
|
|
// // Wait for ATN to release and quit
|
|
// if (wait)
|
|
// {
|
|
// Debug_printv("Waiting for ATN to release");
|
|
// protocol->timeoutWait(PIN_IEC_ATN, RELEASED, FOREVER);
|
|
// }
|
|
}
|
|
|
|
void systemBus::senderTimeout()
|
|
{
|
|
// releaseLines();
|
|
// this->bus_state = BUS_ERROR;
|
|
|
|
// protocol->wait( TIMING_EMPTY );
|
|
} // senderTimeout
|
|
|
|
void systemBus::addDevice(virtualDevice *pDevice, int device_id)
|
|
{
|
|
if (!pDevice)
|
|
{
|
|
Debug_printf("systemBus::addDevice() pDevice == nullptr! returning.\r\n");
|
|
return;
|
|
}
|
|
|
|
// TODO, add device shortcut pointer logic like others
|
|
Debug_printf("Device #%02d Ready!\r\n", device_id);
|
|
|
|
pDevice->_devnum = device_id;
|
|
_daisyChain.push_front(pDevice);
|
|
enabledDevices |= 1UL << device_id;
|
|
}
|
|
|
|
void systemBus::remDevice(virtualDevice *pDevice)
|
|
{
|
|
if (!pDevice)
|
|
{
|
|
Debug_printf("system Bus::remDevice() pDevice == nullptr! returning\r\n");
|
|
return;
|
|
}
|
|
|
|
_daisyChain.remove(pDevice);
|
|
enabledDevices &= ~ ( 1UL << pDevice->_devnum );
|
|
}
|
|
|
|
bool systemBus::isDeviceEnabled ( const uint8_t device_id )
|
|
{
|
|
return ( enabledDevices & ( 1 << device_id ) );
|
|
} // isDeviceEnabled
|
|
|
|
|
|
|
|
void systemBus::changeDeviceId(virtualDevice *pDevice, int device_id)
|
|
{
|
|
if (!pDevice)
|
|
{
|
|
Debug_printf("systemBus::changeDeviceId() pDevice == nullptr! returning.\r\n");
|
|
return;
|
|
}
|
|
|
|
for (auto devicep : _daisyChain)
|
|
{
|
|
if (devicep == pDevice)
|
|
devicep->_devnum = device_id;
|
|
}
|
|
}
|
|
|
|
virtualDevice *systemBus::deviceById(int device_id)
|
|
{
|
|
for (auto devicep : _daisyChain)
|
|
{
|
|
if (devicep->_devnum == device_id)
|
|
return devicep;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void systemBus::shutdown()
|
|
{
|
|
shuttingDown = true;
|
|
|
|
for (auto devicep : _daisyChain)
|
|
{
|
|
Debug_printf("Shutting down device #%02d\r\n", devicep->id());
|
|
devicep->shutdown();
|
|
}
|
|
Debug_printf("All devices shut down.\r\n");
|
|
}
|
|
|
|
|
|
//#endif /* BUILD_IEC */
|