Files
pico/projects/tzpuPico/esp32/main/include/CommandProcessor.h
Philip Smart 4196e58420 MZ-1500 persona, expansion boards, Celestite LAN, QDF format, virtual mode
New drivers:
- MZ-1500 persona driver with MZ-700/MZ-1500 mode switch, PCG bank switching,
  PSG stubs, physical I/O forwarding for virtual mode
- MZ-2200 persona driver (based on MZ-2000)
- MZ-1R23/MZ-1R24 Kanji ROM / Dictionary ROM board (B8h/B9h IDM)
- MZ-1R37 640KB EMM (ACh/ADh, no auto-increment, 20-bit addressing)
- PIO-3034 320KB EMM (configurable base, 19-bit, auto-increment)
- Celestite LAN/Memory composite board:
  - W5100 TCP/IP via ESP32 WiFi (connect, send, recv, ping)
  - Integrated MZ-1R12 32/64KB CMOS RAM with SD persistence
  - Integrated MZ-1R37 640KB EMM with SD persistence
  - UFM flash, unlock state machine, interrupt controller

Celestite networking (Phase 2):
- New IPC commands: NET_CFG, NET_SOCK, NET_SEND, NET_RECV, NET_PING
- ESP32 BSD socket handlers with non-blocking connect and recv
- Shared volatile struct for cross-core results (bypasses responseQueue)
- Inline IDM read check for socket status and recv data
- Z80 test programs: celestite_test.asm (17 tests), celestite_stress.asm (loop)

MZ-1500 virtual mode fixes:
- I/O writes forwarded to physical hardware (PSG, bank switching, PCG)
- E000-E7FF always stays physical during bank switching
- PCG bank (F000-FFFF) properly remapped to PHYSICAL when open

QDF format support:
- Japanese standard QD format auto-detected on load (81936 bytes)
- Hunt pattern changed to 00+16 (mark+sync) for inter-block gap handling
- Sync stripping handles long preambles (9+ bytes)
- MZQDTool updated with -j flag and format conversion

Other:
- Debug shell load command: len parameter now optional
- FSPI filename field: memcpy instead of strncpy for binary data
- Interface availability expanded across MZ-700/1500/80A/2000/2200
- Web GUI: param hints for Celestite, updated driver interface lists

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-17 11:34:55 +01:00

271 lines
9.4 KiB
C++
Vendored

/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Name: CommandProcessor.h
// Created: Jan 2025
// Version: v1.1
// Author(s): Philip Smart
// Description: Class definition to encapsulate a command processor, whose purpose is to act as slave
// taking commands from the RP2350 and execute them.
// v1.1: Command reception moved to binary SPI IPC protocol.
// Binary opcode dispatch replaces ASCII string map lookup.
// RBURST/WBURST commands added for multi-sector burst I/O.
// vTaskDelay(1) polling replaced by SPI blocking wait.
// UART path retained for INF and OOB (legacy compatibility).
// Credits:
// Copyright: (c) 2019-2026 Philip Smart <philip.smart@net2net.org>
//
// History: v1.00 Jan 2025 - Initial write.
// v1.10 Mar 2026 - Binary SPI dispatch, burst commands, SPI-driven wait loop.
//
// Notes: See Makefile to enable/disable conditional components
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// This source file is free software: you can redistribute it and#or modify
// it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This source file is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifndef CMDPROC_H
#define CMDPROC_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <iostream>
#include <vector>
#include <map>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/spi_slave.h"
#include "driver/gpio.h"
#include "esp_system.h"
#include "esp_log.h"
#include "sdmmc_cmd.h"
#include "driver/sdspi_host.h"
#include "driver/spi_common.h"
#include "esp_vfs_fat.h"
#include <string>
#include <functional>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include "lwip/sockets.h"
#include "esp_netif.h"
#include "FSPI.h"
#include "WiFi.h"
#include "cJSON.h"
#include "SDCard.h"
#include "ipc_protocol.h"
// Define Command Processor class.
class CommandProcessor : public WiFi, FSPI, SDCard
{
#define NUMELEM(a) (sizeof(a) / sizeof(a[0]))
#define COMMANDPROCESSOR_VERSION 1.10
#define CMD_BUF_SIZE (128)
#define CMD_SIZE (3)
#define CMD_SEPERATOR ':'
#define TASK_STACK_SIZE (8192)
// Command loop uses portMAX_DELAY — see CommandProcessor.cpp for rationale.
public:
virtual ~CommandProcessor(void) {};
void processCommand(const t_IpcFrameHdr &frame);
void waitForCommand(void);
void start(void);
void init();
// Constructor — sets references and command table.
CommandProcessor(WiFi &wifi, FSPI &fspi, SDCard &sdcard, cJSON *config) : wifi(wifi), fspi(fspi), sdcard(sdcard)
{
(void) config;
for (int i = 0; i < 4; i++) netSockFd[i] = -1;
}
const char *getClassName(const std::string &prettyFunction)
{
size_t colons = prettyFunction.find("::");
if (colons == std::string::npos)
return "::";
size_t begin = prettyFunction.substr(0, colons).rfind(" ") + 1;
size_t end = colons - begin;
return (prettyFunction.substr(begin, end).c_str());
}
void replaceExt(std::string &fileName, const std::string &newExt)
{
std::string::size_type extPos = fileName.rfind('.', fileName.length());
if (extPos != std::string::npos)
fileName.replace(extPos + 1, newExt.length(), newExt);
}
template <typename E> constexpr typename std::underlying_type<E>::type to_underlying(E e) noexcept
{
return static_cast<typename std::underlying_type<E>::type>(e);
}
virtual float version(void)
{
return (COMMANDPROCESSOR_VERSION);
}
protected:
private:
WiFi &wifi;
FSPI &fspi;
SDCard &sdcard;
std::vector<std::string> split(const std::string &s, const std::string &delimiter);
// ---------------------------------------------------------------------------
// Binary IPC command dispatch — indexed by IPCF_CMD_* opcode.
// Each method receives the full decoded command frame.
// ---------------------------------------------------------------------------
void cmdNop(const t_IpcFrameHdr &frame); // defined in CommandProcessor.cpp
void cmdReadSector(const t_IpcFrameHdr &frame)
{
sdcard.readSectorViaSPI(frame, fspi);
}
void cmdReadBurst(const t_IpcFrameHdr &frame)
{
sdcard.readBurstViaSPI(frame, fspi);
}
void cmdWriteSector(const t_IpcFrameHdr &frame)
{
sdcard.writeSectorViaSPI(frame, fspi);
}
void cmdWriteBurst(const t_IpcFrameHdr &frame)
{
sdcard.writeBurstViaSPI(frame, fspi);
}
void cmdReadFile(const t_IpcFrameHdr &frame)
{
// Handles RFILE, RFD, RQD, RRF — all read file variants.
// WiFi tracking for floppy/QD:
if (frame.command == IPCF_CMD_RFD)
wifi.setFloppyDiskFile(std::string(frame.filename), frame.diskNo);
else if (frame.command == IPCF_CMD_RQD)
wifi.setQuickDiskFile(std::string(frame.filename), frame.diskNo);
else if (frame.command == IPCF_CMD_RRF)
wifi.setRamFile(std::string(frame.filename), frame.diskNo);
sdcard.readFileViaSPI(frame, fspi);
}
void cmdWriteFile(const t_IpcFrameHdr &frame)
{
sdcard.writeFileViaSPI(frame, fspi);
}
void cmdReadInfo(const t_IpcFrameHdr &frame)
{
extern void CP_markInitialSpiDone(void);
if (sdcard.storeRP2350Info(frame, fspi))
{
CP_markInitialSpiDone();
}
}
void cmdReadDir(const t_IpcFrameHdr &frame)
{
// Build a directory listing as text and send back as payload.
// The path is in frame.filename (relative to SD card mount point).
char fullPath[256];
if (frame.filename[0] == '\0')
snprintf(fullPath, sizeof(fullPath), "%s", SD_CARD_MOUNT_POINT);
else
snprintf(fullPath, sizeof(fullPath), "%s/%s", SD_CARD_MOUNT_POINT, frame.filename);
// Remove trailing slash if present (except for mount root).
size_t plen = strlen(fullPath);
if (plen > strlen(SD_CARD_MOUNT_POINT) && fullPath[plen - 1] == '/')
fullPath[plen - 1] = '\0';
ESP_LOGI("CMDPROC", "DIR: '%s' → '%s'", frame.filename, fullPath);
DIR *dir = opendir(fullPath);
t_IpcFrameHdr hdr = {};
hdr.frameType = IPCF_TYPE_RESPONSE;
hdr.command = frame.command;
if (!dir)
{
hdr.status = IPCF_STATUS_ERR;
hdr.payloadLen = 0;
uint32_t respSize = IPCF_HEADER_SIZE + IPCF_CRC_SIZE;
fspi.sendBinaryResp(&hdr, NULL, 0, respSize, portMAX_DELAY);
return;
}
// Build listing into a buffer. Use the DMA TX buffer scratch area.
static char dirBuf[IPCF_MAX_PAYLOAD];
int pos = 0;
struct dirent *entry;
while ((entry = readdir(dir)) != NULL && pos < (int) sizeof(dirBuf) - 80)
{
// Stat the entry for size and type.
char entryPath[512];
struct stat st;
snprintf(entryPath, sizeof(entryPath), "%.255s/%.255s", fullPath, entry->d_name);
bool isDir = (entry->d_type == DT_DIR);
long sz = 0;
if (!isDir && stat(entryPath, &st) == 0)
sz = (long) st.st_size;
if (isDir)
pos += snprintf(dirBuf + pos, sizeof(dirBuf) - pos, " <DIR> %s\n", entry->d_name);
else
pos += snprintf(dirBuf + pos, sizeof(dirBuf) - pos, " %7ld %s\n", sz, entry->d_name);
}
closedir(dir);
hdr.status = IPCF_STATUS_OK;
hdr.payloadLen = (uint16_t) pos;
uint32_t respSize = IPCF_HEADER_SIZE + pos + IPCF_CRC_SIZE;
fspi.sendBinaryResp(&hdr, (uint8_t *) dirBuf, pos, respSize, portMAX_DELAY);
}
void cmdUnknown(const t_IpcFrameHdr &frame)
{
ESP_LOGE("CMDPROC", "Unknown binary cmd opcode: %02X", frame.command);
// Send error response.
t_IpcFrameHdr hdr = {};
hdr.frameType = IPCF_TYPE_RESPONSE;
hdr.command = frame.command;
hdr.status = IPCF_STATUS_ERR;
hdr.payloadLen = 0;
uint32_t respSize = IPCF_HEADER_SIZE + IPCF_CRC_SIZE;
fspi.sendBinaryResp(&hdr, NULL, 0, respSize, portMAX_DELAY);
}
// Network command handlers (Celestite W5100 emulation).
void cmdNetCfg(const t_IpcFrameHdr &frame);
void cmdNetSocket(const t_IpcFrameHdr &frame);
void cmdNetSend(const t_IpcFrameHdr &frame);
void cmdNetRecv(const t_IpcFrameHdr &frame);
void cmdNetPing(const t_IpcFrameHdr &frame);
int netSockFd[4]; // BSD socket file descriptors, one per W5100 socket.
};
#endif // CMDPROC_H