Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Console redirection and adding extra ports on the ESP32 #85

Closed
moxhamj opened this issue Aug 11, 2019 · 2 comments
Closed

Console redirection and adding extra ports on the ESP32 #85

moxhamj opened this issue Aug 11, 2019 · 2 comments

Comments

@moxhamj
Copy link

moxhamj commented Aug 11, 2019

This isn't really an 'issue' so can be closed by Mr Mockba, but I thought I might write some notes about the code that was changed to enable Kermit file transfers.

  1. The board I am using is called ESPDUINO-32 and if you google this and select images, you can see it is an arduino form factor. I specifically went for the one with the large USB socket as this is plugged and unplugged many times and is mechanically stronger than the small ones. This particular board is not listed in the arduino IDE but it seems to compile ok with many of the ones that are listed. I used the first board in the list which is called ESP332 Dev Module.

  2. Near the beginning of the RunCPM module are some ifdef's, I changed the pins for the SD card to 19,23 and 18, the CS pin is 5, and the led is on pin 2.

#elif defined ESP32 // ESP32 boards
  SdFatSoftSpiEX<19, 23, 18> SD; // MISO, MOSI, SCK Some boards use 2,15,14,13, other 12,14,27,26
  #define SDINIT 5 // CS
  #define LED 2 // TTGO_T1=22 LOLIN32_Pro=5(inverted) DOIT_Esp32=2 ESP32-PICO-KIT=no led, Espduino pin 2
  #define LEDinv 0
  #define BOARD "ESPDUINO-32"
  1. in void setup() I added the two new serial ports (the ESP32 has three uarts. Just a thought here, the DUE has 4 ports so this idea could also work on that board too).
#ifdef ESP32  
  Serial1.begin(9600,SERIAL_8N1, 26, 25);    //Baud rate, parity mode, RX, TX  ESP32
  Serial2.begin(9600,SERIAL_8N1, 17, 16);    //Baud rate, parity mode, RX, TX  ESP32
#endif  
  1. For hardware I used Max3232 modules available on ebay. Run from the 3V3 pin. In keeping with the old-skool spirit, computers have male D9 plugs and devices (modems, terminals) are female D9. The female Max3232 modules are much more common - I had to search a bit to get the male ones. I made a crossover cable with two female sockets and pin 5 to 5, 2 to 3 and 3 to 2. I also had some modules with faults - some wouldn't work, and one had crosstalk between Tx and Rx so characters printed twice on the screen. I've also had some dodgy max3232 chips before and found that in this case it may be worth getting these from a big supplier like Radio Spares or Element 14 or similar.

  2. I got a copy of generic Kermit from Columbia University. It is in the CP/M archives. This needs no modification at all. If anyone has problems getting this I'll post a copy here.

  3. Below is a wall of code. This goes in the abstraction_arduino.h file. It needs to go above the console abstraction functions (I think this is because functions need to be declared before you use them, and this applies in .h files but not .ino files. ). Once this code is copied in, test everything still compiles ok. None of this should affect compilation as it isn't linked yet to any of the existing code.

// ***************************** Physical Device List **************************************
//CON: = TTY: CRT: BAT: UC1:
//RDR: = TTY: PTR: UR1: UR2:
//PUN: = TTY: PTP: UP1: UP2:
//LST: = TTY: CRT: LPT: UL1:

// TTY

void deviceTTYout(uint8_t ch)  // same as _putch 
{
  Serial1.write(ch); // same as _putch but with buffer overflow protection
  while (Serial1.availableForWrite() < 20) {
      delay(100); // at 9600 baud, ESP32 output buffer is 128 bytes, if stops when only 20 chars left in the output buffer, and pause 100ms, then have about 115 left as sending about 1 per ms.
  }
}

uint8_t deviceTTYavailable(void) {  // same as _kbhit
  return(Serial1.available());
}

uint8_t deviceTTYin(void) {  // same as _getch
  while (!Serial1.available());
  return(Serial1.read());
}

uint8_t deviceTTYinEcho(void) { // same as _getche
  uint8_t ch = deviceTTYin();
  deviceTTYout(ch);
  return(ch);
}

// CRT

void deviceCRTout(uint8_t ch)  // same as _putch 
{
  Serial.write(ch); // same as _putch but with buffer overflow protection
  while (Serial.availableForWrite() < 20) {
      delay(100); // at 9600 baud, ESP32 output buffer is 128 bytes, if stop when only 20 chars left in the output buffer, and pause 100ms, then have about 115 left as sending about 1 per ms.
  }
}

uint8_t deviceCRTavailable(void) {  // same as _kbhit
  return(Serial.available());
}

uint8_t deviceCRTin(void) {  // same as _getch
  while (!Serial.available());
  return(Serial.read());
}

uint8_t deviceCRTinEcho(void) { // same as _getche
  uint8_t ch = deviceCRTin();
  deviceCRTout(ch);
  return(ch);
}

// UR1 

uint8_t deviceUR1in(void) {
    uint8_t ch = 26;
    return ch;
}

// UP1 

void deviceUP1out(uint8_t ch) {
  // add code here
}

// BAT, console device, input and output and can see if characters waiting, input used by Kermit as the default input device

void deviceBATout(uint8_t ch) 
{
  Serial2.write(ch); // same as _putch but with buffer overflow protection
  while (Serial2.availableForWrite() < 20) {
      delay(100); // at 9600 baud, ESP32 output buffer is 128 bytes, if stop when only 20 chars left in the output buffer, and pause 100ms, then have about 115 left as sending about 1 per ms.
  }
}

uint8_t deviceBATavailable(void) {  
   return(Serial2.available());
}

uint8_t deviceBATin(void) {  
  while (!Serial2.available());
  return(Serial2.read());
}

uint8_t deviceBATinEcho(void) { 
  uint8_t ch = deviceBATin();
  deviceBATout(ch);
  return(ch);
}

// UC1, console device

void deviceUC1out(uint8_t ch) 
{
  // add code here
}

uint8_t deviceUC1available(void) {  
  return(1); // add code here
}

uint8_t deviceUC1in(void) {  
    return(26); // add code here
}

uint8_t deviceUC1inEcho(void) { 
  uint8_t ch = deviceUC1in();
  deviceUC1out(ch);
  return(ch);
}

// PTR, reader device

uint8_t devicePTRin(void) {
    return 26; // add code here
}

// UR2, reader device

uint8_t deviceUR2in(void) {
    return 26; // add code here
}

// PTP,  punch device

void devicePTPout(uint8_t ch) {
    Serial2.write(ch); // used by Kermit as the default output device
    while (Serial2.availableForWrite() < 20) {
      delay(100); // at 9600 baud, if stop when only 20 chars left, and pause 100ms, then have about 115 left, as sending about 1 per ms. Should not need to do this if using small packets with Kermit
    }
}

// UP2, punch device

void deviceUP2out(uint8_t ch) {
  // add code here - eg write to file
}

// UL1,  LST device

void deviceUL1out(uint8_t ch) {
  // add code here - eg write to file
}


// LPT,  LST device

void deviceLPTout(uint8_t ch) {
  // add code here
}

// *************************************

// redirection for CON RDR PUN and LST eg CONin, CONout, CONavailable directed to one of four places depending on IOBYTE. 

uint8_t iobyteCON()
{
  uint8_t iobyte;
  iobyte = _RamRead(0x0003);
  iobyte = iobyte & 0x03; // mask off 00000011
  return iobyte;
}

uint8_t iobyteRDR()
{
  uint8_t iobyte;
  iobyte = _RamRead(0x0003);
  iobyte = iobyte & 0x0C; // mask off 00001100
  iobyte = iobyte >> 2; // bitshift so low 2 bits
  return iobyte;
}

uint8_t iobytePUN()
{
  uint8_t iobyte;
  iobyte = _RamRead(0x0003);
  iobyte = iobyte & 0x30; // mask off 00110000
  iobyte = iobyte >> 4; // bitshift so low 2 bits
  return iobyte;
}

uint8_t iobyteLST()
{
  uint8_t iobyte;
  iobyte = _RamRead(0x0003);
  iobyte = iobyte & 0xC0; // mask off 11000000
  iobyte = iobyte >> 6; // bitshift so low 2 bits
  return iobyte;
}

//CON: = TTY: CRT: BAT: UC1:
void outCON(uint8_t ch)
{
  uint8_t iobyte;
  iobyte = iobyteCON(); // 00, 01 10, 11
  switch (iobyte) {
    case 0: deviceTTYout(ch);
            break;
    case 1: deviceCRTout(ch);
            break;
    case 2: deviceBATout(ch);
            break;
    case 3: deviceUC1out(ch);
            break;                                 
  }
}

uint8_t availableCON()
{
  uint8_t iobyte;
  uint8_t dataAvailable;
  iobyte = iobyteCON(); // 00, 01 10, 11
  switch (iobyte) {
    case 0: dataAvailable = deviceTTYavailable();
            break;
    case 1: dataAvailable = deviceCRTavailable();
            break;
    case 2: dataAvailable = deviceBATavailable();
            break;
    case 3: dataAvailable = deviceUC1available();
            break;                                 
  }
  return dataAvailable;
}

uint8_t inCON(void)
{
  uint8_t iobyte;
  uint8_t dataIn;
  iobyte = iobyteCON(); // 00, 01 10, 11
  switch (iobyte) {
    case 0: dataIn = deviceTTYin();
            break;
    case 1: dataIn = deviceCRTin();
            break;
    case 2: dataIn = deviceBATin();
            break;
    case 3: dataIn = deviceUC1in();
            break;                                 
  }
  return dataIn;
}

uint8_t echoCON()
{
  uint8_t ch;
  ch = inCON();
  outCON(ch);
  return(ch);
}

//RDR: = TTY: PTR: UR1: UR2:
uint8_t inRDR(void)
{
  uint8_t iobyte;
  uint8_t dataIn;
  iobyte = iobyteRDR(); // 00, 01 10, 11
  switch (iobyte) {
    case 0: dataIn = deviceTTYin();
            break;
    case 1: dataIn = devicePTRin();
            break;
    case 2: dataIn = deviceUR1in();
            break;
    case 3: dataIn = deviceUR2in();
            break;                                 
  }
  return dataIn;
}

//PUN: = TTY: PTP: UP1: UP2:
void outPUN(uint8_t ch)
{
  uint8_t iobyte;
  iobyte = iobytePUN(); // 00, 01 10, 11
  switch (iobyte) {
    case 0: deviceTTYout(ch);
            break;
    case 1: devicePTPout(ch);
            break;
    case 2: deviceUP1out(ch);
            break;
    case 3: deviceUP2out(ch);
            break;                                 
  }
}

//LST: = TTY: CRT: LPT: UL1:
void outLST(uint8_t ch)
{
  uint8_t iobyte;
  iobyte = iobyteLST(); // 00, 01 10, 11
  switch (iobyte) {
    case 0: deviceTTYout(ch);
            break;
    case 1: deviceCRTout(ch);
            break;
    case 2: deviceLPTout(ch);
            break;
    case 3: deviceUL1out(ch);
            break;                                 
  }
}
  1. Next is to change the console code so that instead of, say, sending a character to the Serial port, instead, redirect it to the code above. I found it safer when debugging to comment out code rather than delete it, so you can see what has been changed.
/* Console abstraction functions */
/*===============================================================================*/

uint8_t _kbhit(void) {
  //return(Serial.available());
  return availableCON();  // redirect based on iobyte
}

uint8_t _getch(void) {
  //while (!Serial.available());
  //return(Serial.read());
  return inCON(); // redirect based on iobyte
}

uint8_t _getche(void) {
  //uint8_t ch = _getch();
  //Serial.write(ch);
  //return(ch);
  return echoCON(); // redirect based on iobyte
}

void _putch(uint8_t ch) {
  //Serial.write(ch);
  outCON(ch); // redirect based on iobyte
}

void _clrscr(void) {
  Serial.println("\e[H\e[J"); // done once at startup so left as is
}
  1. Looking back through the code. I think that is pretty much it. I did end up adding in a tiny bit of code in the cpm.h bdos function to redirect to the reader device, however in the code above the reader device doesn't do anything. But this does give an idea how to add in other devices.
	/*
		C = 3 : Auxiliary (Reader) input
		Returns: A=Char
		*/
	case 3:
#ifdef ESP32
    HL = inRDR();
#else    
    HL = 0x1a; // ^Z end file
#enif d   
		break;
  1. Commented out the ioport ramwrite setting in cpm.h and moved it to the void setup() routine in RunCPM. I am not sure if this has been changed in a recent update. I did a clean install on 11th August and rebuilt it using these instructions and this did need to be changed.
    _RamWrite(0x0003, 0x3D);

  2. Thinking how to use the ports, Grant Searle has a system where the first character that comes in defines which port is being used. I am thinking of something even simpler. For debugging the iobyte is set at startup so CON goes to CRT on the USB serial port. Once a board is working and it is going in a box, recompile with the iobyte set at startup to TTY which is on Serial 1. The box I am making has a hole for the USB socket so you can plug that in and that is for programming and for using a PC terminal program. Then a male D9 for TTY and that goes to a physical terminal (eg a PropTerm, or a Raspberry Pi, or one using an Arduino Uno). And the second male D9 is BAT: and that is for file transfers. I have some vague ideas that the 4th port could be something to do with Wifi or Bluetooth as these are both on the ESP32.

  3. One small bug I am working on - kermit works rock solid between two boards, but when using teraterm with a kermit transfer, it seems to have many dropped packets. This is true at baud rates from 1200 to 115200. I have tried different Max3232 modules, different USB to serial devices and different boards. I haven't tried different terminal programs. This may not be such an issue, because the huge advantage of RunCPM over all other real and emulated CP/M systems is that it uses Fat32 files on the SD card. Thanks++ to Mr Borg for this :)

@MockbaTheBorg
Copy link
Owner

I will try to merge this onto the main code. Cool stuff.

@moxhamj
Copy link
Author

moxhamj commented Aug 12, 2019

Sorry, one more bit that needs adding from the bdos. The punch output needs to be redirected. This bit is a bit messy as I simply commented out the part that redirects to files. ?? redirect to files except on the esp32. Or put in an elif if the disk redirect is not selected. I'm not sure about how to add this. In any case the generic version of Kermit sends output to the punch device, and receives data via a redirected console device.

	case 4:
//#ifdef USE_PUN
//		if (!pun_open) {
//      pun_dev = _sys_fopen_w((uint8*)pun_file);
//			pun_open = TRUE;
//		}
//		if (pun_dev)
//			_sys_fputc(LOW_REGISTER(DE), pun_dev);
//#endif
#ifdef ESP32
    outPUN(LOW_REGISTER(DE));  
#endif    
		break;
		/*
		C = 5 : Printer output
		*/
	case 5:
//#ifdef USE_LST
//		if (!lst_open) {
//			lst_dev = _sys_fopen_w((uint8*)lst_file);
//			lst_open = TRUE;
//		}
//		if (lst_dev)
//			_sys_fputc(LOW_REGISTER(DE), lst_dev);
//#endif
#ifdef ESP32
    outLST(LOW_REGISTER(DE));  
#endif    
		break;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants