Port RunCPM to the Metro RP2350 with HSTX

This uses the built in HSTX connector for video out, and the built in SD card for file storage.
It's a 1-board emulator for the 8-bit CP/M operating system!

At this time, unreleased modifications to Adafruit_dvhstx and Pico-PIO-USB are needed. Otherwise,
the code will not work properly.
This commit is contained in:
Jeff Epler 2025-02-24 10:10:40 -06:00
parent a6851c595e
commit 4151751dd7
6 changed files with 287 additions and 322 deletions

View file

@ -6,6 +6,9 @@
#include "pio_usb.h"
#include "Adafruit_TinyUSB.h"
#include "pico/stdlib.h"
#include "Adafruit_dvhstx.h"
DVHSTXText3 display(DVHSTX_PINOUT_DEFAULT);
// Pin D+ for host, D- = D+ + 1
#ifndef PIN_USB_HOST_DP
@ -28,16 +31,22 @@ Adafruit_USBH_Host USBHost;
SerialPIO pio_serial(1 /* RX of the sibling board */, SerialPIO::NOPIN);
void setup() {
// ensure text generation interrupt takes place on core0
display.begin();
display.println("Hello hstx\n");
}
void loop() {
}
void setup1() {
while(!display) ;
delay(10);
display.println("Hello hstx in setup1\n");
// override tools menu CPU frequency setting
set_sys_clock_khz(120'000, true);
//set_sys_clock_khz(264'000, true);
Serial.begin(115200);
#if 0
while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
@ -50,6 +59,9 @@ void setup1() {
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
pio_cfg.pin_dp = PIN_USB_HOST_DP;
pio_cfg.tx_ch = dma_claim_unused_channel(true);
dma_channel_unclaim(pio_cfg.tx_ch);
USBHost.configure_pio_usb(1, &pio_cfg);
// run host stack on controller (rhport) 1
@ -59,6 +71,8 @@ void setup1() {
// this `begin` is a void function, no way to check for failure!
pio_serial.begin(115200);
display.println("end of setup1\n");
display.show_cursor();
}
int old_ascii = -1;
@ -70,16 +84,21 @@ const uint32_t initial_repeat_time = 500;
void send_ascii(uint8_t code, uint32_t repeat_time=default_repeat_time) {
old_ascii = code;
repeat_timeout = millis() + repeat_time;
if (code > 32 && code < 127) {
Serial.printf("'%c'\r\n", code);
if (code >= 32 && code < 127) {
display.printf("%c", code);
} else {
Serial.printf("'\\x%02x'\r\n", code);
display.printf("\\x%02x", code);
}
pio_serial.write(code);
}
void loop1()
{
static bool last_serial;
if (!last_serial && Serial) {
last_serial = true;
Serial.println("Hello host serial");
}
uint32_t now = millis();
uint32_t deadline = repeat_timeout - now;
if (old_ascii >= 0 && deadline > INT32_MAX) {
@ -166,12 +185,12 @@ bool report_contains(const hid_keyboard_report_t &report, uint8_t key) {
hid_keyboard_report_t old_report;
static bool caps, num;
static uint8_t old_leds;
void process_event(uint8_t dev_addr, uint8_t instance, const hid_keyboard_report_t &report) {
bool alt = report.modifier & 0x44;
bool shift = report.modifier & 0x22;
bool ctrl = report.modifier & 0x11;
bool caps = old_report.reserved & 1;
bool num = old_report.reserved & 2;
uint8_t code = 0;
if (report.keycode[0] == 1 && report.keycode[1] == 1) {
@ -188,8 +207,10 @@ void process_event(uint8_t dev_addr, uint8_t instance, const hid_keyboard_report
/* key is newly pressed */
if (keycode == HID_KEY_NUM_LOCK) {
Serial.println("toggle numlock");
num = !num;
} else if (keycode == HID_KEY_CAPS_LOCK) {
Serial.println("toggle capslock");
caps = !caps;
} else {
for (const auto &mapper : keycode_to_ascii) {
@ -219,15 +240,15 @@ void process_event(uint8_t dev_addr, uint8_t instance, const hid_keyboard_report
}
}
uint8_t leds = (caps | (num << 1));
if (leds != old_report.reserved) {
uint8_t leds = (caps << 1) | num;
if (leds != old_leds) {
old_leds = leds;
Serial.printf("Send LEDs report %d (dev:instance = %d:%d)\r\n", leds, dev_addr, instance);
// no worky
auto r = tuh_hid_set_report(dev_addr, instance/*idx*/, 0/*report_id*/, HID_REPORT_TYPE_OUTPUT/*report_type*/, &leds, sizeof(leds));
auto r = tuh_hid_set_report(dev_addr, instance/*idx*/, 0/*report_id*/, HID_REPORT_TYPE_OUTPUT/*report_type*/, &old_leds, sizeof(old_leds));
Serial.printf("set_report() -> %d\n", (int)r);
}
old_report = report;
old_report.reserved = leds;
}
// Invoked when received report from device via interrupt endpoint

View file

@ -13,6 +13,10 @@
#define HostOS 0x01
// #endif
#if !defined(FILE_TYPE)
#define FILE_TYPE FsFile
#endif
#if defined CORE_TEENSY
#define HostOS 0x04
#endif
@ -26,7 +30,7 @@
/* Memory abstraction functions */
/*===============================================================================*/
bool _RamLoad(char* filename, uint16 address) {
File32 f;
FILE_TYPE f;
bool result = false;
if (f = SD.open(filename, FILE_READ)) {
@ -40,7 +44,7 @@ bool _RamLoad(char* filename, uint16 address) {
/* Filesystem (disk) abstraction fuctions */
/*===============================================================================*/
File32 rootdir, userdir;
FILE_TYPE rootdir, userdir;
#define FOLDERCHAR '/'
typedef struct {
@ -60,31 +64,29 @@ typedef struct {
uint8 al[16];
} CPM_DIRENTRY;
static DirFat_t fileDirEntry;
bool _sys_exists(uint8* filename) {
return(SD.exists((const char *)filename));
}
File32 _sys_fopen_w(uint8* filename) {
FILE_TYPE _sys_fopen_w(uint8* filename) {
return(SD.open((char*)filename, O_CREAT | O_WRITE));
}
int _sys_fputc(uint8 ch, File32& f) {
int _sys_fputc(uint8 ch, FILE_TYPE& f) {
return(f.write(ch));
}
void _sys_fflush(File32& f) {
void _sys_fflush(FILE_TYPE& f) {
f.flush();
}
void _sys_fclose(File32& f) {
void _sys_fclose(FILE_TYPE& f) {
f.close();
}
int _sys_select(uint8* disk) {
uint8 result = FALSE;
File32 f;
FILE_TYPE f;
digitalWrite(LED, HIGH ^ LEDinv);
if (f = SD.open((char*)disk, O_READ)) {
@ -98,7 +100,7 @@ int _sys_select(uint8* disk) {
long _sys_filesize(uint8* filename) {
long l = -1;
File32 f;
FILE_TYPE f;
digitalWrite(LED, HIGH ^ LEDinv);
if (f = SD.open((char*)filename, O_RDONLY)) {
@ -110,13 +112,12 @@ long _sys_filesize(uint8* filename) {
}
int _sys_openfile(uint8* filename) {
File32 f;
FILE_TYPE f;
int result = 0;
digitalWrite(LED, HIGH ^ LEDinv);
f = SD.open((char*)filename, O_READ);
if (f) {
f.dirEntry(&fileDirEntry);
f.close();
result = 1;
}
@ -125,7 +126,7 @@ int _sys_openfile(uint8* filename) {
}
int _sys_makefile(uint8* filename) {
File32 f;
FILE_TYPE f;
int result = 0;
digitalWrite(LED, HIGH ^ LEDinv);
@ -145,7 +146,7 @@ int _sys_deletefile(uint8* filename) {
}
int _sys_renamefile(uint8* filename, uint8* newname) {
File32 f;
FILE_TYPE f;
int result = 0;
digitalWrite(LED, HIGH ^ LEDinv);
@ -165,7 +166,7 @@ void _sys_logbuffer(uint8* buffer) {
#ifdef CONSOLELOG
puts((char*)buffer);
#else
File32 f;
FILE_TYPE f;
uint8 s = 0;
while (*(buffer + s)) // Computes buffer size
++s;
@ -181,7 +182,7 @@ void _sys_logbuffer(uint8* buffer) {
bool _sys_extendfile(char* fn, unsigned long fpos)
{
uint8 result = true;
File32 f;
FILE_TYPE f;
unsigned long i;
digitalWrite(LED, HIGH ^ LEDinv);
@ -204,7 +205,7 @@ bool _sys_extendfile(char* fn, unsigned long fpos)
uint8 _sys_readseq(uint8* filename, long fpos) {
uint8 result = 0xff;
File32 f;
FILE_TYPE f;
uint8 bytesread;
uint8 dmabuf[BlkSZ];
uint8 i;
@ -234,7 +235,7 @@ uint8 _sys_readseq(uint8* filename, long fpos) {
uint8 _sys_writeseq(uint8* filename, long fpos) {
uint8 result = 0xff;
File32 f;
FILE_TYPE f;
digitalWrite(LED, HIGH ^ LEDinv);
if (_sys_extendfile((char*)filename, fpos))
@ -256,7 +257,7 @@ uint8 _sys_writeseq(uint8* filename, long fpos) {
uint8 _sys_readrand(uint8* filename, long fpos) {
uint8 result = 0xff;
File32 f;
FILE_TYPE f;
uint8 bytesread;
uint8 dmabuf[BlkSZ];
uint8 i;
@ -297,7 +298,7 @@ uint8 _sys_readrand(uint8* filename, long fpos) {
uint8 _sys_writerand(uint8* filename, long fpos) {
uint8 result = 0xff;
File32 f;
FILE_TYPE f;
digitalWrite(LED, HIGH ^ LEDinv);
if (_sys_extendfile((char*)filename, fpos)) {
@ -325,7 +326,7 @@ static uint16 fileExtentsUsed = 0;
static uint16 firstFreeAllocBlock;
uint8 _findnext(uint8 isdir) {
File32 f;
FILE_TYPE f;
uint8 result = 0xff;
bool isfile;
uint32 bytes;
@ -339,7 +340,6 @@ uint8 _findnext(uint8 isdir) {
f.getName((char*)&findNextDirName[0], 13);
isfile = !f.isDirectory();
bytes = f.size();
f.dirEntry(&fileDirEntry);
f.close();
if (!isfile)
continue;
@ -440,7 +440,7 @@ uint8 _findfirstallusers(uint8 isdir) {
}
uint8 _Truncate(char* filename, uint8 rc) {
File32 f;
FILE_TYPE f;
int result = 0;
digitalWrite(LED, HIGH ^ LEDinv);

View file

@ -1,130 +0,0 @@
// SPDX-FileCopyrightText: 2023 Mockba the Borg
// SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
//
// SPDX-License-Identifier: MIT
#include <SdFat.h> // SDFat - Adafruit Fork
#include <PicoDVI.h>
#include "../../console.h"
#include "../../arduino_hooks.h"
#ifndef USE_DISPLAY
#define USE_DISPLAY (1)
#endif
#ifndef USE_MSC
#define USE_MSC (0)
#endif
#if USE_DISPLAY
DVItext1 display(DVI_RES_800x240p30, adafruit_feather_dvi_cfg);
#endif
#define SPI_CLOCK (20'000'000)
#define SD_CS_PIN (10)
#define SD_CONFIG SdSpiConfig(SD_CS_PIN, DEDICATED_SPI, SPI_CLOCK)
DedicatedSpiCard blockdevice;
FatFileSystem SD; // Filesystem object from SdFat
// =========================================================================================
// Define Board-Data
// GP25 green onboard LED
// =========================================================================================
#define LED (13)
#define LEDinv (false)
#define board_pico
#define board_analog_io
#define board_digital_io
// FUNCTIONS REQUIRED FOR USB MASS STORAGE ---------------------------------
#if USE_MSC
Adafruit_USBD_MSC usb_msc; // USB mass storage object
static bool msc_changed = true; // Is set true on filesystem changes
// Callback on READ10 command.
int32_t msc_read_cb(uint32_t lba, void *buffer, uint32_t bufsize) {
return blockdevice.readBlocks(lba, (uint8_t *)buffer, bufsize / 512) ? bufsize : -1;
}
// Callback on WRITE10 command.
int32_t msc_write_cb(uint32_t lba, uint8_t *buffer, uint32_t bufsize) {
digitalWrite(LED_BUILTIN, HIGH);
return blockdevice.writeBlocks(lba, buffer, bufsize / 512) ? bufsize : -1;
}
// Callback on WRITE10 completion.
void msc_flush_cb(void) {
blockdevice.syncBlocks(); // Sync with blockdevice
SD.cacheClear(); // Clear filesystem cache to force refresh
digitalWrite(LED_BUILTIN, LOW);
msc_changed = true;
}
#endif
#if USE_DISPLAY
uint16_t underCursor = ' ';
void putch_display(uint8_t ch) {
auto x = display.getCursorX();
auto y = display.getCursorY();
display.drawPixel(x, y, underCursor);
if(ch == 8) {
if(x > 0) {
display.setCursor(--x, y);
display.drawPixel(x, y, ' ');
}
} else {
display.write(ch);
}
x = display.getCursorX();
y = display.getCursorY();
underCursor = display.getPixel(x, y);
display.drawPixel(x, y, 0xDB);
}
#endif
uint8_t getch_serial1(void) {
while(true) {
int r = Serial1.read();
if(r != -1) {
return r;
}
}
}
bool kbhit_serial1(void) {
return Serial1.available();
}
bool port_init_early() {
#if USE_DISPLAY
vreg_set_voltage(VREG_VOLTAGE_1_20);
delay(10);
if (!display.begin()) { return false; }
_putch_hook = putch_display;
#endif
_getch_hook = getch_serial1;
_kbhit_hook = kbhit_serial1;
// USB mass storage / filesystem setup (do BEFORE Serial init)
if (!blockdevice.begin(SD_CONFIG)) { _puts("Failed to initialize SD card"); return false; }
#if USE_MSC
// Set disk vendor id, product id and revision
usb_msc.setID("Adafruit", "Internal Flash", "1.0");
// Set disk size, block size is 512 regardless of blockdevice page size
usb_msc.setCapacity(blockdevice.sectorCount(), 512);
usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb);
usb_msc.setUnitReady(true); // MSC is ready for read/write
if (!usb_msc.begin()) {
_puts("Failed to initialize USB MSC"); return false;
}
#endif
return true;
}
bool port_flash_begin() {
if (!SD.begin(&blockdevice, true, 1)) { // Start filesystem on the blockdevice
_puts("!SD.begin()"); return false;
}
return true;
}

View file

@ -1,56 +1,91 @@
// SPDX-FileCopyrightText: 2023 Mockba the Borg
// SPDX-FileCopyrightText: 2023 Jeff Epler for Adafruit Industries
//
// SPDX-License-Identifier: MIT
// pio-usb is required for rp2040 host
#include "pio_usb.h"
#include "../../arduino_hooks.h"
#include "../../console.h"
#include "Adafruit_TinyUSB.h"
#include "pico/stdlib.h"
#include "Adafruit_dvhstx.h"
#include "pio_usb.h"
#include <Adafruit_dvhstx.h>
#include <SdCard/SdSpiCard.h>
#include <SdFat.h>
DVHSTXText3 display(DVHSTX_PINOUT_DEFAULT);
// Pin D+ for host, D- = D+ + 1
#ifndef PIN_USB_HOST_DP
#define PIN_USB_HOST_DP 16
#endif
// Pin for enabling Host VBUS. comment out if not used
#ifndef PIN_5V_EN
#define PIN_5V_EN 18
#endif
#ifndef PIN_5V_EN_STATE
#define PIN_5V_EN_STATE 1
#endif
// USB Host object
Adafruit_USBH_Host USBHost;
// Serial output for RunCPM
SerialPIO pio_serial(1 /* RX of the sibling board */, SerialPIO::NOPIN);
#define SD_CS_PIN 39
void setup() {
// ensure text generation interrupt takes place on core0
display.begin();
display.println("Hello hstx\n");
SdFat SD;
SdSpiConfig config(SD_CS_PIN, DEDICATED_SPI, SD_SCK_MHZ(16), &SPI1);
static bool start1;
// =========================================================================================
// Define Board-Data
// GP25 green onboard LED
// =========================================================================================
#define LED (13)
#define LEDinv (false)
#define board_pico
#define board_analog_io
#define board_digital_io
queue_t kb_queue;
void putch_display(uint8_t ch) {
if (ch == 8) {
auto x = display.getCursorX();
if (x > 0) {
auto y = display.getCursorY();
display.setCursor(--x, y);
display.drawPixel(x, y, ' ');
}
} else {
display.write(ch);
}
}
void loop() {
uint8_t getch_usbhost(void) {
uint8_t result;
queue_remove_blocking(&kb_queue, &result);
return result;
}
bool kbhit_usbhost(void) { return queue_get_level(&kb_queue) > 0; }
bool port_init_early() {
if (!display.begin()) {
return false;
}
display.showCursor();
_putch_hook = putch_display;
delay(10);
queue_init_with_spinlock(&kb_queue, 1, 64, spin_lock_claim_unused(true));
start1 = true;
_getch_hook = getch_usbhost;
_kbhit_hook = kbhit_usbhost;
return true;
}
bool port_flash_begin() {
if (!SD.begin(config)) {
return false;
}
return true;
}
/*************************************************************************
* USB Host keyboard
*************************************************************************/
void setup1() {
while(!display) ;
delay(10);
display.println("Hello hstx in setup1\n");
// override tools menu CPU frequency setting
//set_sys_clock_khz(264'000, true);
Serial.begin(115200);
#if 0
while ( !Serial ) delay(10); // wait for native usb
Serial.println("Core1 setup to run TinyUSB host with pio-usb");
#endif
while (!start1)
;
#ifdef PIN_5V_EN
pinMode(PIN_5V_EN, OUTPUT);
@ -59,20 +94,17 @@ Serial.begin(115200);
pio_usb_configuration_t pio_cfg = PIO_USB_DEFAULT_CONFIG;
pio_cfg.pin_dp = PIN_USB_HOST_DP;
pio_cfg.tx_ch = dma_claim_unused_channel(true);
pio_cfg.tx_ch = dma_claim_unused_channel(true);
dma_channel_unclaim(pio_cfg.tx_ch);
USBHost.configure_pio_usb(1, &pio_cfg);
// run host stack on controller (rhport) 1
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have most of the
// host bit-banging processing works done in core1 to free up core0 for other works
// Note: For rp2040 pico-pio-usb, calling USBHost.begin() on core1 will have
// most of the host bit-banging processing works done in core1 to free up
// core0 for other works
USBHost.begin(1);
// this `begin` is a void function, no way to check for failure!
pio_serial.begin(115200);
display.println("end of setup1\n");
display.show_cursor();
Serial.println("end of setup1");
}
int old_ascii = -1;
@ -81,24 +113,13 @@ uint32_t repeat_timeout;
const uint32_t default_repeat_time = 50;
const uint32_t initial_repeat_time = 500;
void send_ascii(uint8_t code, uint32_t repeat_time=default_repeat_time) {
void send_ascii(uint8_t code, uint32_t repeat_time = default_repeat_time) {
old_ascii = code;
repeat_timeout = millis() + repeat_time;
if (code >= 32 && code < 127) {
display.printf("%c", code);
} else {
display.printf("\\x%02x", code);
}
pio_serial.write(code);
queue_try_add(&kb_queue, &code); // failure is ignored
}
void loop1()
{
static bool last_serial;
if (!last_serial && Serial) {
last_serial = true;
Serial.println("Hello host serial");
}
void loop1() {
uint32_t now = millis();
uint32_t deadline = repeat_timeout - now;
if (old_ascii >= 0 && deadline > INT32_MAX) {
@ -115,13 +136,15 @@ Serial.println("Hello host serial");
// tuh_hid_parse_report_descriptor() can be used to parse common/simple enough
// descriptor. Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE,
// it will be skipped therefore report_desc = NULL, desc_len = 0
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_report, uint16_t desc_len) {
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance,
uint8_t const *desc_report, uint16_t desc_len) {
(void)desc_report;
(void)desc_len;
uint16_t vid, pid;
tuh_vid_pid_get(dev_addr, &vid, &pid);
Serial.printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
Serial.printf("HID device address = %d, instance = %d is mounted\r\n",
dev_addr, instance);
Serial.printf("VID = %04x, PID = %04x\r\n", vid, pid);
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
@ -135,7 +158,8 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *desc_re
// Invoked when device with hid interface is un-mounted
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
Serial.printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
Serial.printf("HID device address = %d, instance = %d is unmounted\r\n",
dev_addr, instance);
}
#define FLAG_ALPHABETIC (1)
@ -144,41 +168,81 @@ void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
#define FLAG_CTRL (8)
#define FLAG_LUT (16)
const char * const lut[] = {
"!@#$%^&*()", /* 0 - shifted numeric keys */
"\r\x1b\10\t -=[]\\#;'`,./", /* 1 - symbol keys */
"\n\x1b\177\t _+{}|~:\"~<>?", /* 2 - shifted */
"\12\13\10\22", /* 3 - arrow keys RLDU */
"/*-+\n1234567890.", /* 4 - keypad w/numlock */
"/*-+\n\xff\2\xff\4\xff\3\xff\1\xff\xff.", /* 5 - keypad w/o numlock */
const char *const lut[] = {
"!@#$%^&*()", /* 0 - shifted numeric keys */
"\r\x1b\10\t -=[]\\#;'`,./", /* 1 - symbol keys */
"\n\x1b\177\t _+{}|~:\"~<>?", /* 2 - shifted */
"\12\13\10\22", /* 3 - arrow keys RLDU */
"/*-+\n1234567890.", /* 4 - keypad w/numlock */
"/*-+\n\xff\2\xff\4\xff\3\xff\1\xff\xff.", /* 5 - keypad w/o numlock */
};
struct keycode_mapper {
uint8_t first, last, code, flags;
} keycode_to_ascii[] = {
{ HID_KEY_A, HID_KEY_Z, 'a', FLAG_ALPHABETIC, },
{
HID_KEY_A,
HID_KEY_Z,
'a',
FLAG_ALPHABETIC,
},
{ HID_KEY_1, HID_KEY_9, 0, FLAG_SHIFT | FLAG_LUT, },
{ HID_KEY_1, HID_KEY_9, '1', 0, },
{ HID_KEY_0, HID_KEY_0, ')', FLAG_SHIFT, },
{ HID_KEY_0, HID_KEY_0, '0', 0, },
{
HID_KEY_1,
HID_KEY_9,
0,
FLAG_SHIFT | FLAG_LUT,
},
{
HID_KEY_1,
HID_KEY_9,
'1',
0,
},
{
HID_KEY_0,
HID_KEY_0,
')',
FLAG_SHIFT,
},
{
HID_KEY_0,
HID_KEY_0,
'0',
0,
},
{ HID_KEY_ENTER, HID_KEY_ENTER, '\n', FLAG_CTRL },
{ HID_KEY_ENTER, HID_KEY_SLASH, 2, FLAG_SHIFT | FLAG_LUT, },
{ HID_KEY_ENTER, HID_KEY_SLASH, 1, FLAG_LUT, },
{HID_KEY_ENTER, HID_KEY_ENTER, '\n', FLAG_CTRL},
{
HID_KEY_ENTER,
HID_KEY_SLASH,
2,
FLAG_SHIFT | FLAG_LUT,
},
{
HID_KEY_ENTER,
HID_KEY_SLASH,
1,
FLAG_LUT,
},
{ HID_KEY_F1, HID_KEY_F1, 0x1e, 0, }, // help key on xerox 820 kbd
{
HID_KEY_F1,
HID_KEY_F1,
0x1e,
0,
}, // help key on xerox 820 kbd
{ HID_KEY_ARROW_RIGHT, HID_KEY_ARROW_UP, 3, FLAG_LUT },
{HID_KEY_ARROW_RIGHT, HID_KEY_ARROW_UP, 3, FLAG_LUT},
{ HID_KEY_KEYPAD_DIVIDE, HID_KEY_KEYPAD_DECIMAL, 4, FLAG_NUMLOCK | FLAG_LUT },
{ HID_KEY_KEYPAD_DIVIDE, HID_KEY_KEYPAD_DECIMAL, 5, FLAG_LUT },
{HID_KEY_KEYPAD_DIVIDE, HID_KEY_KEYPAD_DECIMAL, 4, FLAG_NUMLOCK | FLAG_LUT},
{HID_KEY_KEYPAD_DIVIDE, HID_KEY_KEYPAD_DECIMAL, 5, FLAG_LUT},
};
bool report_contains(const hid_keyboard_report_t &report, uint8_t key) {
for (int i = 0; i < 6; i++) {
if (report.keycode[i] == key) return true;
if (report.keycode[i] == key)
return true;
}
return false;
}
@ -187,7 +251,8 @@ hid_keyboard_report_t old_report;
static bool caps, num;
static uint8_t old_leds;
void process_event(uint8_t dev_addr, uint8_t instance, const hid_keyboard_report_t &report) {
void process_event(uint8_t dev_addr, uint8_t instance,
const hid_keyboard_report_t &report) {
bool alt = report.modifier & 0x44;
bool shift = report.modifier & 0x22;
bool ctrl = report.modifier & 0x11;
@ -202,15 +267,17 @@ void process_event(uint8_t dev_addr, uint8_t instance, const hid_keyboard_report
old_ascii = -1;
for (auto keycode : report.keycode) {
if (keycode == 0) continue;
if (report_contains(old_report, keycode)) continue;
if (keycode == 0)
continue;
if (report_contains(old_report, keycode))
continue;
/* key is newly pressed */
if (keycode == HID_KEY_NUM_LOCK) {
Serial.println("toggle numlock");
Serial.println("toggle numlock");
num = !num;
} else if (keycode == HID_KEY_CAPS_LOCK) {
Serial.println("toggle capslock");
Serial.println("toggle capslock");
caps = !caps;
} else {
for (const auto &mapper : keycode_to_ascii) {
@ -232,8 +299,10 @@ void process_event(uint8_t dev_addr, uint8_t instance, const hid_keyboard_report
code ^= ('a' ^ 'A');
}
}
if (ctrl) code &= 0x1f;
if (alt) code ^= 0x80;
if (ctrl)
code &= 0x1f;
if (alt)
code ^= 0x80;
send_ascii(code, initial_repeat_time);
break;
}
@ -243,20 +312,25 @@ void process_event(uint8_t dev_addr, uint8_t instance, const hid_keyboard_report
uint8_t leds = (caps << 1) | num;
if (leds != old_leds) {
old_leds = leds;
Serial.printf("Send LEDs report %d (dev:instance = %d:%d)\r\n", leds, dev_addr, instance);
Serial.printf("Send LEDs report %d (dev:instance = %d:%d)\r\n", leds,
dev_addr, instance);
// no worky
auto r = tuh_hid_set_report(dev_addr, instance/*idx*/, 0/*report_id*/, HID_REPORT_TYPE_OUTPUT/*report_type*/, &old_leds, sizeof(old_leds));
auto r = tuh_hid_set_report(dev_addr, instance /*idx*/, 0 /*report_id*/,
HID_REPORT_TYPE_OUTPUT /*report_type*/,
&old_leds, sizeof(old_leds));
Serial.printf("set_report() -> %d\n", (int)r);
}
old_report = report;
}
// Invoked when received report from device via interrupt endpoint
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
if ( len != sizeof(hid_keyboard_report_t) ) {
Serial.printf("report len = %u NOT 8, probably something wrong !!\r\n", len);
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance,
uint8_t const *report, uint16_t len) {
if (len != sizeof(hid_keyboard_report_t)) {
Serial.printf("report len = %u NOT 8, probably something wrong !!\r\n",
len);
} else {
process_event(dev_addr, instance, *(hid_keyboard_report_t*)report);
process_event(dev_addr, instance, *(hid_keyboard_report_t *)report);
}
// continue to request to receive report
if (!tuh_hid_receive_report(dev_addr, instance)) {

View file

@ -12,8 +12,7 @@
#include <SPI.h>
#include <SdFat.h> // One SD library to rule them all - Greinman SdFat from Library Manager
#include <Adafruit_SPIFlash.h>
#include <SdFat.h> // One SD library to rule them all - Greinman SdFat from Library Manager
#ifndef USE_VT100
#define USE_VT100 (0)
@ -29,14 +28,14 @@
#include "globals.h"
// =========================================================================================
// Board definitions go into the "hardware" folder, if you use a board different than the
// Arduino DUE, choose/change a file from there and reference that file here
// Board definitions go into the "hardware" folder, if you use a board different
// than the Arduino DUE, choose/change a file from there and reference that file
// here
// =========================================================================================
// Raspberry Pi Pico - normal (LED = GPIO25)
#include "hardware/pico/feather_dvi.h"
// Metro RP2350
#include "hardware/pico/metro_rp2350.h"
#ifndef BOARD_TEXT
#define BOARD_TEXT USB_MANUFACTURER " " USB_PRODUCT
@ -52,7 +51,6 @@
#define sDELAY 100
#define DELAY 1200
// =========================================================================================
// Serial port speed
// =========================================================================================
@ -62,7 +60,7 @@
// PUN: device configuration
// =========================================================================================
#ifdef USE_PUN
File32 pun_dev;
FILE_TYPE pun_dev;
int pun_open = FALSE;
#endif
@ -70,16 +68,16 @@ int pun_open = FALSE;
// LST: device configuration
// =========================================================================================
#ifdef USE_LST
File32 lst_dev;
FILE_TYPE lst_dev;
int lst_open = FALSE;
#endif
#include "ram.h"
#include "console.h"
#include "cpm.h"
#include "cpu.h"
#include "disk.h"
#include "host.h"
#include "cpm.h"
#include "ram.h"
#ifdef CCP_INTERNAL
#include "ccp.h"
#endif
@ -88,7 +86,6 @@ void setup(void) {
pinMode(LED, OUTPUT);
digitalWrite(LED, LOW ^ LEDinv);
// =========================================================================================
// Serial Port Definition
// =========================================================================================
@ -119,7 +116,7 @@ void setup(void) {
// _clrscr();
// _puts("Opening serial-port...\r\n");
Serial.begin(SERIALSPD);
while (!Serial) { // Wait until serial is connected
while (!Serial) { // Wait until serial is connected
digitalWrite(LED, HIGH ^ LEDinv);
delay(sDELAY);
digitalWrite(LED, LOW ^ LEDinv);
@ -131,7 +128,6 @@ void setup(void) {
_sys_deletefile((uint8 *)LogName);
#endif
// =========================================================================================
// Printing the Startup-Messages
// =========================================================================================
@ -139,10 +135,12 @@ void setup(void) {
_clrscr();
// if (bootup_press == 1)
// { _puts("Recognized " TEXT_BOLD "#" TEXT_NORMAL " key as pressed! :)\r\n\r\n");
// { _puts("Recognized " TEXT_BOLD "#" TEXT_NORMAL " key as pressed!
// :)\r\n\r\n");
// }
_puts("CP/M Emulator " TEXT_BOLD "v" VERSION "" TEXT_NORMAL " by " TEXT_BOLD "Marcelo Dantas" TEXT_NORMAL "\r\n");
_puts("CP/M Emulator " TEXT_BOLD "v" VERSION "" TEXT_NORMAL
" by " TEXT_BOLD "Marcelo Dantas" TEXT_NORMAL "\r\n");
_puts("----------------------------------------------\r\n");
_puts(" running on [" TEXT_BOLD BOARD_TEXT TEXT_NORMAL "]\r\n");
_puts("----------------------------------------------\r\n");
@ -166,61 +164,63 @@ void setup(void) {
_puts("" TEXT_NORMAL "]banks\r\n");
#endif
// Serial.printf("Free Memory [" TEXT_BOLD "%d bytes" TEXT_NORMAL "]\r\n", freeMemory());
// Serial.printf("Free Memory [" TEXT_BOLD "%d bytes" TEXT_NORMAL
// "]\r\n", freeMemory());
_puts("CPU-Clock [" TEXT_BOLD);
_putdec((clock_get_hz( clk_sys ) + 500'000) / 1'000'000);
_puts(TEXT_NORMAL "] MHz\r\n");
_putdec((clock_get_hz(clk_sys) + 500'000) / 1'000'000);
_puts(TEXT_NORMAL "] MHz\r\n");
_puts("Init Storage [ " TEXT_BOLD "");
if (port_flash_begin()) {
_puts("OK " TEXT_NORMAL "]\r\n");
_puts("----------------------------------------------");
_puts("Init Storage [ " TEXT_BOLD "");
if (port_flash_begin()) {
_puts("OK " TEXT_NORMAL "]\r\n");
_puts("----------------------------------------------");
if (VersionCCP >= 0x10 || SD.exists(CCPname)) {
while (true) {
_puts(CCPHEAD);
_PatchCPM();
Status = 0;
if (VersionCCP >= 0x10 || SD.exists(CCPname)) {
while (true) {
_puts(CCPHEAD);
_PatchCPM();
Status = 0;
#ifndef CCP_INTERNAL
if (!_RamLoad((char *)CCPname, CCPaddr)) {
_puts("Unable to load the CCP.\r\nCPU halted.\r\n");
break;
}
Z80reset();
SET_LOW_REGISTER(BC, _RamRead(DSKByte));
PC = CCPaddr;
Z80run();
if (!_RamLoad((char *)CCPname, CCPaddr)) {
_puts("Unable to load the CCP.\r\nCPU halted.\r\n");
break;
}
Z80reset();
SET_LOW_REGISTER(BC, _RamRead(DSKByte));
PC = CCPaddr;
Z80run();
#else
_ccp();
_ccp();
#endif
if (Status == 1)
break;
if (Status == 1)
break;
#ifdef USE_PUN
if (pun_dev)
_sys_fflush(pun_dev);
if (pun_dev)
_sys_fflush(pun_dev);
#endif
#ifdef USE_LST
if (lst_dev)
_sys_fflush(lst_dev);
if (lst_dev)
_sys_fflush(lst_dev);
#endif
}
} else {
_puts("Unable to load CP/M CCP.\r\nCPU halted.\r\n");
}
} else {
_puts("ERR " TEXT_NORMAL "]\r\nUnable to initialize SD card.\r\nCPU halted.\r\n");
}
}
}
} else {
_puts("Unable to load CP/M CCP.\r\nCPU halted.\r\n");
}
} else {
_puts("ERR " TEXT_NORMAL
"]\r\nUnable to initialize SD card.\r\nCPU halted.\r\n");
}
}
// if loop is reached, blink LED forever to signal error
void loop(void) {
digitalWrite(LED, HIGH^LEDinv);
delay(DELAY);
digitalWrite(LED, LOW^LEDinv);
delay(DELAY);
digitalWrite(LED, HIGH^LEDinv);
delay(DELAY);
digitalWrite(LED, LOW^LEDinv);
delay(DELAY * 4);
}
// if loop is reached, blink LED forever to signal error
void loop(void) {
digitalWrite(LED, HIGH ^ LEDinv);
delay(DELAY);
digitalWrite(LED, LOW ^ LEDinv);
delay(DELAY);
digitalWrite(LED, HIGH ^ LEDinv);
delay(DELAY);
digitalWrite(LED, LOW ^ LEDinv);
delay(DELAY * 4);
}