/*
MSP430 Emulator
Copyright (C) 2020 Rudolf Geosits (rgeosits@live.esu.edu)
"MSP430 Emulator" 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.
"MSP430 Emulator" 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 .
*/
#include
#include "emu_server.h"
#define TXIFG 0x02
#define RXIFG 0x01
#define HIGH 1
#define LOW 0
#define OUT 1
#define IN 0
//
#define MAX_UPLOAD_FILENAME_SIZE 0xFFFF+1
char UploadFileName[MAX_UPLOAD_FILENAME_SIZE] = {0};
Emulator *emu = NULL;
uint8_t *data;
int lent;
void *thrd (void *ctxt)
{
Usci *usci = (Usci *)ctxt;
int i, j = 0;
int len = lent;
uint8_t *bytes = data;
//printf("len is %d\nstr is %s\n", len, bytes);
while (true) {
usleep(333);
while (*usci->IFG2 & RXIFG);
uint8_t thing = *(bytes);
if (thing == '\n') {
thing = '\r';
}
//printf("Got 0x%02X '%c'\n", thing, thing);
if (*bytes == '\\') {
++bytes;
if (*bytes == 'h' || *bytes == 'H') {
++bytes;
char buf[3] = {*((char*)bytes), *((char*)bytes+1), 0};
//printf("%s - %s\n", buf, bytes);
thing = (uint8_t) strtoul(buf, NULL, 16);
++bytes;
}
}
*usci->UCA0RXBUF = thing;
*usci->IFG2 |= RXIFG;
//printf("\n0x%04X in UCA0RXBUF\n", (uint8_t)*usci->UCA0RXBUF);
//puts("waiting..");
while (*usci->IFG2 & RXIFG);
//puts("done");
//*usci->IFG2 |= RXIFG;
if (*usci->UCA0RXBUF == '\r') break;
++bytes;
}
return NULL;
}
int callback_emu (struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
{
Cpu *cpu = emu->cpu;
Port_1 *p1 = emu->cpu->p1;
Debugger *deb = emu->debugger;
switch (reason)
{
case LWS_CALLBACK_ESTABLISHED:
{
puts("connection established");
// Flip ready flag for the emulator to begin
deb->web_server_ready = true;
// get the ball rolling
//libwebsocket_callback_on_writable(this, wsi);
lws_callback_on_writable(wsi);
break;
}
case LWS_CALLBACK_SERVER_WRITEABLE:
{
if ( !packet_queue_empty(emu) )
{
Packet p = packet_dequeue(emu);
void *msg = p.message;
size_t msg_len = p.length;
uint8_t op = p.opcode;
//printf("[%s] of len %u, opcode: %u\n\n",
//(char *)msg, (unsigned int)msg_len, op);
// Leave room for websock header/trailer & opcode
size_t pack_len = msg_len + sizeof(op) + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING;
void *packet = malloc(pack_len);
// Zero out our packet
memset( ((uint8_t*)packet + LWS_SEND_BUFFER_PRE_PADDING), 0, msg_len + sizeof(op) );
// Place opcode into packet
*((uint8_t *)((uint8_t*)packet + LWS_SEND_BUFFER_PRE_PADDING)) = op;
// Place message into packet
memcpy( ((uint8_t*)packet + LWS_SEND_BUFFER_PRE_PADDING + sizeof(op)), (const void *)p.message, msg_len);
/*
int i;
for (i = 0;i < pack_len;i++){
printf( "%02X (%c) | ", *((uint8_t *)(packet+i)),
*((char *)(packet+i)) );
}
puts("\n");
*/
lws_write(wsi, ((unsigned char*)(packet))+LWS_SEND_BUFFER_PRE_PADDING, msg_len + sizeof(op), LWS_WRITE_BINARY);
free(p.message);
free(packet);
}
static bool p1_0_on = false;
static bool p1_1_on = false;
static bool p1_2_on = false;
static bool p1_3_on = false;
static bool p1_4_on = false;
static bool p1_5_on = false;
static bool p1_6_on = false;
static bool p1_7_on = false;
// P1.0 ON/OFF
if (p1->DIR_0 == OUT)
{
if (p1->OUT_0 == HIGH)
{
if (p1_0_on == false)
{
send_control(emu, P1_0_ON_PACKET, NULL, 0);
p1_0_on = true;
}
}
else if (p1->OUT_0 == LOW)
{
if (p1_0_on == true)
{
send_control(emu, P1_0_OFF_PACKET, NULL, 0);
p1_0_on = false;
}
}
}
// P1.1 ON/OFF
if (p1->DIR_1 == OUT) {
if (p1->OUT_1 == HIGH) {
if (p1_1_on == false) {
send_control(emu, P1_1_ON_PACKET, NULL, 0);
p1_1_on = true;
}
}
else if (p1->OUT_1 == LOW) {
if (p1_1_on == true) {
send_control(emu, P1_1_OFF_PACKET, NULL, 0);
p1_1_on = false;
}
}
}
// P1.2 ON/OFF
if (p1->DIR_2 == OUT) {
if (p1->OUT_2 == HIGH) {
if (p1_2_on == false) {
send_control(emu, P1_2_ON_PACKET, NULL, 0);
puts("p2 on");
p1_2_on = true;
}
}
else if (p1->OUT_2 == LOW) {
if (p1_2_on == true) {
send_control(emu, P1_2_OFF_PACKET, NULL, 0);
p1_2_on = false;
}
}
}
// P1.3 ON/OFF
if (p1->DIR_3 == OUT) {
if (p1->OUT_3 == HIGH) {
if (p1_3_on == false) {
send_control(emu, P1_3_ON_PACKET, NULL, 0);
p1_3_on = true;
}
}
else if (p1->OUT_3 == LOW) {
if (p1_3_on == true) {
send_control(emu, P1_3_OFF_PACKET, NULL, 0);
p1_3_on = false;
}
}
}
// P1.4 ON/OFF
if (p1->DIR_4 == OUT) {
if (p1->OUT_4 == HIGH) {
if (p1_4_on == false) {
send_control(emu, P1_4_ON_PACKET, NULL, 0);
p1_4_on = true;
}
}
else if (p1->OUT_4 == LOW) {
if (p1_4_on == true) {
send_control(emu, P1_4_OFF_PACKET, NULL, 0);
p1_4_on = false;
}
}
}
// P1.5 ON/OFF
if (p1->DIR_5 == OUT) {
if (p1->OUT_5 == HIGH) {
if (p1_5_on == false) {
send_control(emu, P1_5_ON_PACKET, NULL, 0);
p1_5_on = true;
}
}
else if (p1->OUT_5 == LOW) {
if (p1_5_on == true) {
send_control(emu, P1_5_OFF_PACKET, NULL, 0);
p1_5_on = false;
}
}
}
// P1.6 ON/OFF
if (p1->DIR_6 == OUT) {
if (p1->OUT_6 == HIGH) {
if (p1_6_on == false) {
send_control(emu, P1_6_ON_PACKET, NULL, 0);
p1_6_on = true;
}
}
else if (p1->OUT_6 == LOW) {
if (p1_6_on == true) {
send_control(emu, P1_6_OFF_PACKET, NULL, 0);
p1_6_on = false;
}
}
}
// P1.7 ON/OFF
if (p1->DIR_7 == OUT) {
if (p1->OUT_7 == HIGH) {
if (p1_7_on == false) {
send_control(emu, P1_7_ON_PACKET, NULL, 0);
p1_7_on = true;
}
}
else if (p1->OUT_7 == LOW) {
if (p1_7_on == true) {
send_control(emu, P1_7_OFF_PACKET, NULL, 0);
p1_7_on = false;
}
}
}
lws_callback_on_writable( wsi);
break;
}
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: {
puts("Connection Error");
break;
}
case LWS_CALLBACK_CLOSED: {
puts("Connection Closed");
exit(0);
break;
}
case LWS_CALLBACK_CLIENT_WRITEABLE: {
puts("cli writable");
break;
}
case LWS_CALLBACK_RECEIVE:
{
static FILE *fp = NULL;
static bool upload_in_progress = false;
static uint32_t uploaded_bytes;
static uint16_t FileSize = 0;
char *buf = (char *)in;
if (upload_in_progress)
{ // Continue transaction of upload
int i;
for (i = 0;i < len;i++)
{
fwrite(&buf[i], 1, 1, fp);
uploaded_bytes++;
if (uploaded_bytes >= FileSize)
{
puts("met bytes");
fclose(fp);
// system("msp430-objcopy -O binary tmp.elf tmp.bin");
char cmdline[255] = "objcopy -I elf32-little -O binary \"";
strcat(cmdline, UploadFileName);
strcat(cmdline, "\" tmp.bin");
system(cmdline);
// std::string ObjCopyLine = "objcopy -I elf32-little -O binary \"" + std::string(UploadFileName) + "\" tmp.bin";
// system(ObjCopyLine.c_str());
deb->web_firmware_uploaded = true;
upload_in_progress = false;
return 0;
}
}
return 0;
}
unsigned char opcode = buf[0];
printf("opcode: %02X\n", *(uint8_t*)in);
switch (opcode)
{
case 0x00: // Upload File
{
// Get the 32-bit length of the file
FileSize = ntohs( *((uint16_t*)((uint8_t*)in + 1)) );
// Get the 8-bit length of the ASCII filename
uint16_t FileNameSize = ntohs(*((uint16_t*)((uint8_t*)in + 3)));
printf(" Got file size %u, got data len %u for first callback.\n", (unsigned int)FileSize, (unsigned int)len);
// Ensure that the file isn't too big, otherwise just quit on the user for now
if (FileSize >= 0xFFFF)
{
print_console(emu, "File Size Not Supported. Quitting.\n");
deb->quit = true;
exit(1);
}
// for now assume the entire filename is uploaded, and has no 0 at the end so add one
memset(UploadFileName, 0, MAX_UPLOAD_FILENAME_SIZE);
memmove(&UploadFileName[0], &buf[5], FileNameSize);
printf(" Got name size %u, name %s.\n", FileNameSize, UploadFileName);
upload_in_progress = true;
uploaded_bytes = 0;
fp = fopen(UploadFileName, "wb");
// Get Any Bytes that are in with this packet
for (uint32_t i = 5+FileNameSize;i < len;i++)
{
fwrite(&buf[i], 1, 1, fp);
uploaded_bytes++;
if (uploaded_bytes >= FileSize)
{
puts("met bytes");
fclose(fp);
//system("msp430-objcopy -O binary tmp.elf tmp.bin");
// std::string ObjCopyLine = "objcopy -I elf32-little -O binary \"" + std::string(UploadFileName) + "\" tmp.bin";
// system(ObjCopyLine.c_str());
char cmdline[255] = "objcopy -I elf32-little -O binary \"";
strcat(cmdline, UploadFileName);
strcat(cmdline, "\" tmp.bin");
system(cmdline);
deb->web_firmware_uploaded = true;
upload_in_progress = false;
return 0;
}
}
break;
}
case 0x01: { // PLAY
printf("Got play\n");
cpu->running = true;
deb->debug_mode = false;
update_register_display(emu);
return 0;
}
case 0x02: { // PAUSE
printf("Got pause\n");
if (cpu->running) {
cpu->running = false;
deb->debug_mode = true;
// display first round of registers
display_registers(emu);
disassemble(emu, cpu->pc, 1);
update_register_display(emu);
}
return 0;
}
case 0x03: { // SERIAL DATA
if (len > 1000) exit(1);
lent = len - 1;
data = ((uint8_t*)in + 1);
pthread_t t;
if( pthread_create(&t, NULL, thrd, (void *)cpu->usci ) ) {
fprintf(stderr, "Error creating thread\n");
}
//printf("Got serial data %s ... %d bytes long\n",
//(char *)(in + 1), (unsigned int)len - 1);
return 0;
}
case 0x04: { // Console Input Data
if (len > 1000) exit(1);
buf = buf + 1;
printf("%s\n", buf);
if (!cpu->running && deb->debug_mode) {
exec_cmd(emu, buf, len);
update_register_display(emu);
}
return 0;
}
default: break;
}
}
default: {
break;
}
}
return 0;
}
void *web_server (void *ctxt)
{
emu = (Emulator *) ctxt;
Debugger *deb = emu->debugger;
int port = 9001;
//struct libwebsocket_context *context;
struct lws_context *context;
struct lws_context_creation_info context_info = {0};
context_info.port = deb->ws_port;
context_info.iface = NULL;
context_info.protocols = protocols;
context_info.extensions = NULL;
context_info.ssl_cert_filepath = NULL;
context_info.ssl_private_key_filepath = NULL;
context_info.ssl_ca_filepath = NULL;
context_info.gid = -1;
context_info.uid = -1;
context_info.options = 0;
context_info.ka_time = false;
context_info.ka_probes = false;
context_info.ka_interval = false;
// Initialize packet queuing system
init_packet_queue(emu);
// create libwebsocket context representing this server
context = lws_create_context(&context_info);
if (context == NULL) {
fprintf(stderr, "libwebsocket init failed\n");
deb->quit = true;
exit(1);
}
printf("starting server...\n");
while (true) {
lws_service(context, 10); // ms
usleep(1000);
}
lws_context_destroy(context);
return NULL;
}
void send_control(Emulator *emu, uint8_t opcode, void *data, size_t data_size)
{
const static uint8_t NUM_OPCODE_BYTES = 1;
const static uint8_t NUM_DATA_LEN_BYTES = 1;
if (data == NULL) { // Simple case
packet_enqueue(emu, (void *)&opcode, 1, CONTROL_PACKET_OPCODE);
}
else { // data case ( OP DL DATADATADATA )
uint8_t full_packet[NUM_OPCODE_BYTES + NUM_DATA_LEN_BYTES + data_size];
// First part of packet is the opcode for specific control type
memcpy(full_packet, (void *)&opcode, NUM_OPCODE_BYTES);
// Second part is the data length field for following data
memcpy(full_packet + NUM_OPCODE_BYTES, &data_size,
NUM_DATA_LEN_BYTES);
// Third part is the data itself.
memcpy(full_packet + NUM_OPCODE_BYTES + NUM_DATA_LEN_BYTES,
data, data_size);
/*
int i;
for (i = 0;i < 1+size;i++) {
printf("%02X ", *(uint8_t *)(full_packet + i));
}
puts(" .");
*/
packet_enqueue(emu, (void *)&full_packet,
NUM_OPCODE_BYTES + NUM_DATA_LEN_BYTES + data_size,
CONTROL_PACKET_OPCODE);
}
}
void print_serial (Emulator *emu, char *buf)
{
packet_enqueue(emu, buf, strlen(buf) + 1, SERIAL_PACKET_OPCODE);
}
void print_console (Emulator *emu, const char *buf)
{
packet_enqueue(emu, (void*)buf, strlen(buf) + 1, CONSOLE_PACKET_OPCODE);
}
int callback_http (
struct lws *wsi,
//enum libwebsocket_callback_reasons reason,
enum lws_callback_reasons reason,
void *user, void *in, size_t len)
{
return 0;
}