Specifically, I wrote an 8086 PC emulator for this, and I have a VNC/RFB server in the code to allow a client to connect and use the emulator. This worked great on an Arduino Due with an Ethernet shield. I'm using the same RFB server code, just slightly changed to use the WiFiServer/WiFiClient instead. Are there differences between these APIs that I need to look out for? They look almost identical.
It actually has no problem with the initial VNC connection and first full screen update, but then it starts going haywire during incremental updates after that. Again, everything worked great on the Ethernet shield. It was very stable. Could it be a memory issue? Something about how the ESP32 is handling the transmit packet buffering? Although I seem to have 100+ KB free.
This is the VNC/RFB code. rfbInit() gets called once at startup, and rfbRun() is called frequently from the main loop. Thanks to anybody that can help. I'm going to be working at this some more tonight, but maybe somebody here will see some obvious mistake that I'm missing.
Code: Select all
#include <WiFi.h>
#include "emu.h"
#include "font.h"
#define RFB_STATE_WAIT_VERSION 0
#define RFB_STATE_WAIT_SECURITY 1
#define RFB_STATE_WAIT_SECURITY_2 2
#define RFB_STATE_WAIT_CLIENT_INIT 3
#define RFB_STATE_OPERATIONAL 4
#define RFB_STATE_IDLE 5
#define WIDTH 640
#define HEIGHT 400
#define RFB_NAME "Fake86 for ESP32"
#define RFB_OUT_BUFFER_SIZE 128
WiFiServer wfserver;
WiFiClient wfclient;
uint8_t clientMsgLen[7] = { 20, 1, 0, 10, 8, 6, 0 };
uint8_t rfbState = RFB_STATE_IDLE, rfbLastPin = HIGH;
uint8_t rfbBuf[256], palettecga[16];
uint8_t rfbOutBuf[RFB_OUT_BUFFER_SIZE];
uint32_t rfbLastMS = 0, rfbCurMS = 0, rfbPinMS = 0, rfbOutPos = 0;
uint16_t rfbKnownRects = 0;
uint32_t rfbWrites[64];
uint8_t rfbWriteCount = 0;
uint8_t xcurs, ycurs;
void rfbParseIncoming();
void serverRun() {
WiFiClient newClient = wfserver.accept();
if (newClient) {
if (wfclient) wfclient.stop();
wfclient = newClient;
//wfclient.setNoDelay(true);
wfclient.print("RFB 003.008\n");
rfbState = RFB_STATE_WAIT_VERSION;
rfbWriteCount = 0;
}
}
void rfbDiscardRx() {
while(wfclient.available()) {
wfclient.read();
}
}
void rfbFlush() {
if (rfbOutPos == 0) return;
wfclient.write(rfbOutBuf, rfbOutPos);
rfbOutPos = 0;
}
void rfbOutByte(uint8_t value) {
if (rfbOutPos == RFB_OUT_BUFFER_SIZE) rfbFlush();
rfbOutBuf[rfbOutPos++] = value;
}
void rfbOutString(char *str) {
uint16_t i = 0;
uint8_t curchar;
while (curchar = str[i++]) { //asignment, not comparison. abort loop upon finding null terminator.
rfbOutByte(curchar);
}
}
void rfbSend32(uint32_t value) {
rfbOutByte((uint8_t)(value >> 24));
rfbOutByte((uint8_t)(value >> 16));
rfbOutByte((uint8_t)(value >> 8));
rfbOutByte((uint8_t)(value & 255));
}
void rfbSend16(uint16_t value) {
rfbOutByte((uint8_t)(value >> 8));
rfbOutByte((uint8_t)(value & 255));
}
void rfbSend8(uint8_t value) {
rfbOutByte(value);
}
uint32_t rfbRead32() {
uint32_t ret;
ret = (uint32_t)wfclient.read() << 24;
ret |= (uint32_t)wfclient.read() << 16;
ret |= (uint32_t)wfclient.read() << 8;
ret |= (uint32_t)wfclient.read();
return ret;
}
uint16_t rfbRead16() {
uint16_t ret;
ret |= (uint32_t)wfclient.read() << 8;
ret |= (uint32_t)wfclient.read();
return ret;
}
uint8_t rfbRead8() {
return (uint8_t)wfclient.read();
}
uint8_t normalize(uint8_t value) {
switch (value) {
case 0: case 9: case 10: case 13: case 26: case 255: //all of these are blank on screen
value = 32;
break;
}
return value;
}
void rfbDrawChar80(uint16_t xpos, uint16_t ypos, uint16_t w, uint16_t h) {
uint16_t x, y, xchar, ychar, cursstart, cursend;
uint8_t curpixel, curchar, attr;
uint32_t addr32;
cursstart = (crt_controller[0x0A] & 31) * 2;
cursend = (crt_controller[0x0B] & 31) * 2;
rfbSend16(xpos);
rfbSend16(ypos);
rfbSend16(w);
rfbSend16(h);
rfbSend32(0); //raw
for (y=ypos; y<(ypos+h); y++) {
for (x=xpos; x<(xpos+w); x++) {
ychar = y / 16;
xchar = x / 8;
addr32 = ychar*160 + xchar*2;
curchar = VRAM_read(addr32);
attr = VRAM_read(addr32+1);
curpixel = (ROM_READ(font, curchar*8+((y&15)>>1))>>(x&7))&1;
curpixel = curpixel ? attr & 15 : attr >> 4;
if (((x>>3) == xcurs) && ((y>>4) == ycurs)) {
if (((y & 15) >= cursstart) && ((y & 15) <= cursend)) {
curpixel = attr & 15;
}
}
rfbSend8(palettecga[curpixel]);
}
}
}
void rfbChar80(uint32_t addr32) {
uint16_t xchar, ychar, curchar, attr, xpos, ypos, xcopy, ycopy, i;
uint8_t doCopyRect = 0;
if (rfbState != RFB_STATE_OPERATIONAL) return;
if (addr32 >= 4000) return;
addr32 &= 0xFFFFE;
ychar = addr32 / 160;
xchar = (addr32 % 160) >> 1;
ypos = ychar * 16;
xpos = xchar * 8;
curchar = normalize(VRAM_read(addr32));
attr = VRAM_read(addr32+1);
rfbSend8(0);
rfbSend8(0);
rfbSend16(1);
if (doCopyRect == 0) {
rfbDrawChar80(xpos, ypos, 8, 16);
} else {
rfbSend16(xpos);
rfbSend16(ypos);
rfbSend16(8);
rfbSend16(16);
rfbSend32(1); //copy rect
rfbSend16(xcopy);
rfbSend16(ycopy);
}
}
void rfbDrawChar40(uint16_t xpos, uint16_t ypos, uint16_t w, uint16_t h) {
uint16_t x, y, xchar, ychar, cursstart, cursend;
uint8_t curpixel, curchar, attr;
uint32_t addr32;
cursstart = (crt_controller[0x0A] & 31) * 2;
cursend = (crt_controller[0x0B] & 31) * 2;
rfbSend16(xpos);
rfbSend16(ypos);
rfbSend16(w);
rfbSend16(h);
rfbSend32(0); //raw
for (y=ypos; y<(ypos+h); y++) {
for (x=xpos; x<(xpos+w); x++) {
ychar = y / 16;
xchar = x / 16;
addr32 = ychar*80 + xchar*2;
curchar = VRAM_read(addr32);
attr = VRAM_read(addr32+1);
curpixel = (ROM_READ(font, curchar*8+((y&15)>>1))>>((x>>1)&7))&1;
curpixel = curpixel ? attr & 15 : attr >> 4;
if (((x>>3) == xcurs) && ((y>>4) == ycurs)) {
if (((y & 15) >= cursstart) && ((y & 15) <= cursend)) {
curpixel = attr & 15;
}
}
rfbSend8(palettecga[curpixel]);
}
}
}
void rfbChar40(uint32_t addr32) {
uint16_t xchar, ychar, curchar, attr, xpos, ypos, xcopy, ycopy, i;
uint8_t doCopyRect = 0;
if (rfbState != RFB_STATE_OPERATIONAL) return;
if (addr32 >= 4000) return;
addr32 &= 0xFFFFE;
ychar = addr32 / 80;
xchar = (addr32 % 80) >> 1;
ypos = ychar * 16;
xpos = xchar * 16;
curchar = normalize(VRAM_read(addr32));
attr = VRAM_read(addr32+1);
rfbSend8(0);
rfbSend8(0);
rfbSend16(1);
if (doCopyRect == 0) {
rfbDrawChar40(xpos, ypos, 16, 16);
} else {
rfbSend16(xpos);
rfbSend16(ypos);
rfbSend16(16);
rfbSend16(16);
rfbSend32(1); //copy rect
rfbSend16(xcopy);
rfbSend16(ycopy);
}
//rfbFlush();
}
void rfbDrawGfxLo(uint16_t xpos, uint16_t ypos, uint16_t w, uint16_t h) {
uint16_t x, y, xchar, ychar;
uint8_t curpixel, curchar, attr, usepal, intensity;
uint32_t addr32;
usepal = (portram[0x3D9]>>5) & 1;
intensity = ((portram[0x3D9]>>4) & 1) << 3;
rfbSend16(xpos);
rfbSend16(ypos);
rfbSend16(w);
rfbSend16(h);
rfbSend32(0); //raw
for (y=ypos; y<(ypos+h); y++) {
for (x=xpos; x<(xpos+w); x++) {
ychar = y >> 1;
xchar = x >> 1;
addr32 = (ychar & 1) * 8192 + (ychar >> 1) * 80 + (xchar >> 2);
curchar = VRAM_read(addr32);
switch (xchar & 3) {
case 3: curpixel = curchar & 3; break;
case 2: curpixel = (curchar >> 2) & 3; break;
case 1: curpixel = (curchar >> 4) & 3; break;
case 0: curpixel = (curchar >> 6) & 3; break;
}
curpixel = (curpixel << 1) + usepal + intensity;
if (curpixel == (usepal + intensity)) curpixel = 0;
rfbSend8(palettecga[curpixel]);
}
}
}
void rfbGfxLo(uint32_t addr32) {
uint16_t xchar, ychar, curchar, attr, xpos, ypos, xcopy, ycopy, i;
uint8_t doCopyRect = 0;
if (rfbState != RFB_STATE_OPERATIONAL) return;
if ((addr32 >= 8000) && (addr32 < 8192)) return;
if (addr32 >= 16192) return;
if (addr32 >= 8192) {
addr32 -= 8192;
ypos = ((addr32 / 80) << 2) + 2;
xpos = (addr32 % 80) << 3;
} else {
ypos = (addr32 / 80) << 2;
xpos = (addr32 % 80) << 3;
}
rfbSend8(0);
rfbSend8(0);
rfbSend16(1);
rfbDrawGfxLo(xpos, ypos, 8, 2);
}
void rfbDrawGfxHi(uint16_t xpos, uint16_t ypos, uint16_t w, uint16_t h) {
uint16_t x, y, xchar, ychar;
uint8_t curpixel, curchar;
uint32_t addr32;
rfbSend16(xpos);
rfbSend16(ypos);
rfbSend16(w);
rfbSend16(h);
rfbSend32(0); //raw
for (y=ypos; y<(ypos+h); y++) {
for (x=xpos; x<(xpos+w); x++) {
ychar = y >> 1;
xchar = x;
addr32 = (ychar & 1) * 8192 + (ychar >> 1) * 80 + (xchar >> 3);
curchar = VRAM_read(addr32);
curpixel = ((curchar >> (7-(x&7))) & 1) ? 15 : 0;
rfbSend8(palettecga[curpixel]);
}
}
}
void rfbGfxHi(uint32_t addr32) {
uint16_t xchar, ychar, curchar, attr, xpos, ypos, xcopy, ycopy, i;
uint8_t doCopyRect = 0;
if (rfbState != RFB_STATE_OPERATIONAL) return;
if ((addr32 >= 8000) && (addr32 < 8192)) return;
if (addr32 >= 16192) return;
if (addr32 >= 8192) {
addr32 -= 8192;
ypos = ((addr32 / 80) << 2) + 2;
xpos = (addr32 % 80) << 3;
} else {
ypos = (addr32 / 80) << 2;
xpos = (addr32 % 80) << 3;
}
rfbSend8(0);
rfbSend8(0);
rfbSend16(1);
rfbDrawGfxHi(xpos, ypos, 8, 2);
}
void rfbFlushWrites() {
uint8_t i;
if (rfbWriteCount == 0) {
rfbFlush();
return;
}
switch (vidmode) {
case 0:
case 1:
for (i=0; i<rfbWriteCount; i++) {
rfbChar40(rfbWrites[i]);
}
break;
case 2:
case 3:
for (i=0; i<rfbWriteCount; i++) {
rfbChar80(rfbWrites[i]);
}
break;
case 4:
case 5:
for (i=0; i<rfbWriteCount; i++) {
rfbGfxLo(rfbWrites[i]);
}
break;
case 6:
for (i=0; i<rfbWriteCount; i++) {
rfbGfxHi(rfbWrites[i]);
}
break;
}
rfbFlush();
rfbWriteCount = 0;
}
void rfbWrite(uint32_t addr32) {
uint8_t i;
if (rfbWriteCount == 64) rfbFlushWrites();
if (vidmode < 4) addr32 &= 0xFFFFE;
for (i=0; i<rfbWriteCount; i++) {
if (rfbWrites[i] == addr32) return;
}
rfbWrites[rfbWriteCount++] = addr32;
}
void rfbParseIncoming() {
uint8_t cmd;
uint32_t len;
if (rfbState != RFB_STATE_OPERATIONAL) return;
while ((wfclient.available() > 0) && wfclient.connected()) {
cmd = wfclient.peek();
switch (cmd) {
case 2: //setencodings
{
uint16_t i;
rfbRead16();
len = rfbRead16();
for (i=0; i<len; i++) {
rfbRead32();
}
break;
}
case 4: //keyevent
{
uint8_t isdown, scancode;
uint32_t keysym;
//SerDev.println("Key Event");
rfbRead8();
isdown = rfbRead8();
rfbRead16();
keysym = rfbRead32() & 0xFFFF;
scancode = translatescancode(keysym);
if (scancode != 0xFF) {
portram[0x60] = scancode;
if (!isdown) portram[0x60] |= 0x80;
portram[0x64] |= 2;
doirq(1);
}
break;
}
case 6: //clientcuttext
{
uint32_t i;
rfbRead32();
len = rfbRead32();
for (i=0; i<len; i++) {
rfbRead8();
}
break;
}
default:
{
uint8_t i;
for (i=0; i<clientMsgLen[cmd]; i++) {
rfbRead8();
}
break;
}
}
}
}
void rfbWholeScreen() {
rfbSend8(0);
rfbSend8(0);
rfbSend16(1);
switch (vidmode) {
case 0:
case 1:
rfbDrawChar40(0, 0, 640, 400);
break;
case 2:
case 3:
rfbDrawChar80(0, 0, 640, 400);
break;
case 4:
case 5:
rfbDrawGfxLo(0, 0, 640, 400);
break;
case 6:
rfbDrawGfxHi(0, 0, 640, 400);
break;
}
rfbFlush();
}
void rfbRun() {
uint8_t curPin, cmd;
uint32_t len;
serverRun();
if (!wfclient.connected()) {
rfbState = RFB_STATE_IDLE;
wfclient.stop();
}
switch (rfbState) {
case RFB_STATE_WAIT_VERSION:
if (wfclient.available() >= 12) {
wfclient.readBytes(rfbBuf, 12);
if ((rfbBuf[0] == 'R') && (rfbBuf[1] == 'F') && (rfbBuf[2] == 'B')) {
rfbSend8(1);
rfbSend8(1); //no authentication
rfbState = RFB_STATE_WAIT_SECURITY_2;
} else {
rfbDiscardRx();
}
}
break;
case RFB_STATE_WAIT_SECURITY:
if (wfclient.available() >= 1) {
uint8_t i;
wfclient.readBytes(rfbBuf, 1);
for (i=0; i<16; i++) {
rfbSend8(random(256));
}
rfbState = RFB_STATE_WAIT_SECURITY_2;
}
break;
case RFB_STATE_WAIT_SECURITY_2:
rfbSend32(0); //security handshake successful
rfbState = RFB_STATE_WAIT_CLIENT_INIT;
break;
case RFB_STATE_WAIT_CLIENT_INIT:
if (wfclient.available() >= 1) {
uint16_t i;
wfclient.readBytes(rfbBuf, 1);
rfbOutByte((uint8_t)(WIDTH >> 8));
rfbOutByte((uint8_t)(WIDTH & 255));
rfbOutByte((uint8_t)(HEIGHT >> 8));
rfbOutByte((uint8_t)(HEIGHT & 255));
rfbOutByte((uint8_t)8); //bpp
rfbOutByte((uint8_t)8); //depth
rfbOutByte((uint8_t)0); //big endian flag
rfbOutByte((uint8_t)1); //true color flag
rfbSend16(3); //red max
rfbSend16(7); //green max
rfbSend16(3); //blue max
rfbSend8(0); //red shift
rfbSend8(2); //green shift
rfbSend8(6); //blue shift
rfbOutByte((uint8_t)0); //padding
rfbOutByte((uint8_t)0); //padding
rfbOutByte((uint8_t)0); //padding
rfbOutByte((uint8_t)0);
rfbOutByte((uint8_t)0);
rfbOutByte((uint8_t)0);
rfbOutByte(strlen(RFB_NAME));
rfbOutString(RFB_NAME);
rfbState = RFB_STATE_OPERATIONAL;
rfbKnownRects = 0;
rfbWholeScreen();
rfbKnownRects = 4000;
rfbWriteCount = 0;
}
break;
case RFB_STATE_OPERATIONAL:
rfbParseIncoming();
break;
case RFB_STATE_IDLE:
break;
}
rfbFlushWrites();
}
void setcursor(uint16_t addr) {
uint16_t oldaddr;
oldaddr = ycurs * 160 + xcurs * 2;
ycurs = addr / 80;
xcurs = addr % 80;
if (vidmode < 4) {
rfbWrite(oldaddr);
rfbWrite(ycurs * 160 + xcurs * 2);
}
}
uint32_t rgb(uint32_t r, uint32_t g, uint32_t b) {
return (r>>6) | ((g>>5)<<2) | ((b>>6)<<6);
}
void rfbInit() {
palettecga[0] = 0;
palettecga[1] = rgb (0, 0, 0xAA);
palettecga[2] = rgb (0, 0xAA, 0);
palettecga[3] = rgb (0, 0xAA, 0xAA);
palettecga[4] = rgb (0xAA, 0, 0);
palettecga[5] = rgb (0xAA, 0, 0xAA);
palettecga[6] = rgb (0xAA, 0x55, 0);
palettecga[7] = rgb (0xAA, 0xAA, 0xAA);
palettecga[8] = rgb (0x55, 0x55, 0x55);
palettecga[9] = rgb (0x55, 0x55, 0xFF);
palettecga[10] = rgb (0x55, 0xFF, 0x55);
palettecga[11] = rgb (0x55, 0xFF, 0xFF);
palettecga[12] = rgb (0xFF, 0x55, 0x55);
palettecga[13] = rgb (0xFF, 0x55, 0xFF);
palettecga[14] = rgb (0xFF, 0xFF, 0x55);
palettecga[15] = rgb (0xFF, 0xFF, 0xFF);
WiFi.begin("REDACTED", "REDACTED");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
SerDev.print(".");
}
SerDev.println(WiFi.localIP());
wfserver.begin(5900);
}