From b9fc5fed91512208d861e287b1369f3122861144 Mon Sep 17 00:00:00 2001 From: David Phillips Date: Wed, 20 Jan 2016 14:03:21 +1300 Subject: Initial import --- .gitignore | 3 ++ Makefile | 20 ++++++++ casio-get.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++ casio-get.h | 8 +++ error.c | 15 ++++++ error.h | 6 +++ packet.c | 50 +++++++++++++++++++ packet.h | 32 ++++++++++++ port.c | 92 ++++++++++++++++++++++++++++++++++ port.h | 14 ++++++ screen-to-xpm.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 521 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 casio-get.c create mode 100644 casio-get.h create mode 100644 error.c create mode 100644 error.h create mode 100644 packet.c create mode 100644 packet.h create mode 100644 port.c create mode 100644 port.h create mode 100644 screen-to-xpm.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ecf1be --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +screen-to-xpm +casio-get diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..058c0b7 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ + +CFLAGS = -Wall -Werror -Wextra + +.PHONY: all +all: casio-get screen-to-xpm + +screen-to-xpm: screen-to-xpm.o packet.o + $(CC) -o $@ $^ $(LDFLAGS) + +casio-get: casio-get.o error.o port.o packet.o + $(CC) -o $@ $^ $(LDFLAGS) -lserialport + + +%.o: %.c + $(CC) -c -o $@ $< $(CFLAGS) + + +.PHONY: clean +clean: + rm -fv casio-get screen-to-xpm *.o diff --git a/casio-get.c b/casio-get.c new file mode 100644 index 0000000..9beb87f --- /dev/null +++ b/casio-get.c @@ -0,0 +1,129 @@ +#include "casio-get.h" + +#define SMALL_PACKET_WAIT 100 + +#include +#include +#include + + +void print_checksum_msg(unsigned char expected, unsigned short actual) +{ + fprintf(stderr, "Checksum\t: %s (needed 0x%x, found 0x%x)\n", expected==actual? "OK" : "BAD", expected, actual); +} + +int main() +{ + unsigned int read; + unsigned char temp_buffer[1024]; + unsigned char rx_buffer[65536]; + + setbuf(stdout, NULL); + memset(rx_buffer, 0, sizeof(rx_buffer)); + + long expected; + unsigned int x,y; + + unsigned char colours = 3; + unsigned char colour; + unsigned int i; + struct sp_port *port; + port = port_init("/dev/ttyUSB0"); + + // Wait for calculator and ACK + wait_calc(port); + send_calc_byte(port, ATTENTION_ACK); + fprintf(stderr,"Sent 0x%x (ACK)\n",ATTENTION_ACK); + + + + /******************************************* + * Receive header packet + *******************************************/ + read = calc_read(port, rx_buffer, PKT_HEADER_MAX_SIZE); + fprintf(stderr, "\n== HEADER ========================================\n"); + fprintf(stderr, "Size (bytes)\t: %d\n", read); + + unsigned char sum_expected = rx_buffer[read-1]; + unsigned char sum = calculate_checksum((const char*)rx_buffer+1, (const char*)rx_buffer+read-1); + + print_checksum_msg(sum_expected, sum); + + for(i = 0; i < read; i++) + printf("%c", (unsigned char)rx_buffer[i]); + + colours = 3; + + switch (get_packet_type(rx_buffer)) + { + /* Screenshot (F<->D button) */ + case PKT_SCREEN_HEADER_BW: + colours = 1; + case PKT_SCREEN_HEADER_COL: + send_calc_byte(port, MISC_ACK); + x = rx_buffer[4]; + y = rx_buffer[3]; + expected = (x*y)/8 + 2; + + /* multi-colour images have an extra byte per colour frame for colour identification */ + if (colours > 1) + expected++; + fprintf(stderr, + "Data type\t: Screen dump\n" + "Dimensions\t: %d x %d\n", x,y); + + fprintf(stderr, "\n== DATA ==========================================\n"); + + for (colour = 0; colour < colours; colour++) + { + read = calc_read(port, rx_buffer, expected); + fprintf(stderr,"Colour %d/%d read\t: %d bytes\n", colour+1, colours, read); + for(i = 0; i < read; i++) + printf("%c", rx_buffer[i]); + + sum_expected = rx_buffer[read-1]; + sum = calculate_checksum((const char*)rx_buffer+1+(colours > 1), (const char*)rx_buffer+read-1); + print_checksum_msg(sum_expected, sum); + + send_calc_byte(port, MISC_ACK); + + } + break; + + /* Full memory backup */ + case PKT_BACKUP_HEADER: + /* Get firmware name (starts at byte 27 and is hival-terminated */ + i = 27; + while((rx_buffer[i] != 0xff) && i < sizeof(temp_buffer)) + { + temp_buffer[i-27] = rx_buffer[i]; + i++; + } + temp_buffer[i-27] = '\0'; + + fprintf(stderr, "Memory backup coming (firmware %s)...\n", temp_buffer); + break; + case PKT_FUNCTION_HEADER: + fprintf(stderr, "Function(s) coming...\nNOT IMPLEMENTED\n"); + break; + case PKT_VALUE_HEADER: + fprintf(stderr, "Value coming...\nNOT IMPLEMENTED\n"); + //break; + case PKT_PROGRAM_HEADER: + fprintf(stderr, "Program coming...\nNOT IMPLEMENTED\n"); + //break; + default: + fprintf(stderr, "List(s) coming...\n"); + fprintf(stderr, "Matrix/Matrices coming...\n"); + fprintf(stderr, "NOT IMPLEMENTEEEED, NOT ANNYYYY OF THIIIIIIS!!!\n"); + break; + } + + + + + sp_close(port); + + + return 0; +} diff --git a/casio-get.h b/casio-get.h new file mode 100644 index 0000000..9324e2c --- /dev/null +++ b/casio-get.h @@ -0,0 +1,8 @@ +#ifndef GETSCREEN_H +#define GETSCREEN_H + +#include "error.h" +#include "packet.h" +#include "port.h" + +#endif diff --git a/error.c b/error.c new file mode 100644 index 0000000..bf30cd8 --- /dev/null +++ b/error.c @@ -0,0 +1,15 @@ +#include "casio-get.h" + +#include +#include + +void serial_error(const char* fname); + +/****************************************************************************** + * Report error related to serial port read/write etc + ******************************************************************************/ +void serial_error(const char *fname) +{ + fprintf(stderr, "Error in %s: %s\n", fname, sp_last_error_message()); + exit(EXIT_FAILURE); +} diff --git a/error.h b/error.h new file mode 100644 index 0000000..9869e7d --- /dev/null +++ b/error.h @@ -0,0 +1,6 @@ +#ifndef ERROR_H +#define ERROR_H + +void serial_error(const char* fname); + +#endif diff --git a/packet.c b/packet.c new file mode 100644 index 0000000..663778e --- /dev/null +++ b/packet.c @@ -0,0 +1,50 @@ +#include "casio-get.h" +#include + + +/***************************************************************************** + * Calculate the checksum of the data area of a packet + *****************************************************************************/ +unsigned char calculate_checksum(const char *start, const char *stop) +{ + unsigned char sum = 0; + while (start != stop) + sum += *(start++); + + return 1 + ~(sum); +} + + +unsigned short calculate_checksum_word(const char *start, const char *stop) +{ + unsigned short sum = 0; + while (start != stop) + sum += (unsigned char)(*(start++)); + + return 1 + ~(sum); +} + +/***************************************************************************** + * Returns the type of packet as determined from the packet header + *****************************************************************************/ +enum packet_types get_packet_type(const unsigned char* data) +{ + if (strncmp((char*)data, ":END", 4) == 0) + return PKT_END; + if (strncmp((char*)data, ":VAL", 4) == 0) + return PKT_VALUE_HEADER; + if (strncmp((char*)data, ":DD", 3) == 0) + return PKT_SCREEN_HEADER_BW; + if (strncmp((char*)data, ":DC", 3) == 0) + return PKT_SCREEN_HEADER_COL; + if (strncmp((char*)data, ":MEM", 4) == 0) + return PKT_BACKUP_HEADER; + if (strncmp((char*)data, ":FNC", 4) == 0) + return PKT_FUNCTION_HEADER; + if (strncmp((char*)data, ":TXT", 4) == 0) + return PKT_PROGRAM_HEADER; + if (strncmp((char*)data, ":\x00\x01\x00\x01", 4) == 0) + return PKT_VAL; + + return PKT_UNKNOWN; +} diff --git a/packet.h b/packet.h new file mode 100644 index 0000000..4850cb6 --- /dev/null +++ b/packet.h @@ -0,0 +1,32 @@ +#ifndef PACKET_H +#define PACKET_H + +#define MISC_ACK 0x06 + +/*#define REQUEST_TYPE_VAR 0x4D56 // VM +#define REQUEST_TYPE_PICTURE 0x4350 // PC +#define REQUEST_TYPE_LIST 0x544C // LT +#define REQUEST_TYPE_MATRIX 0x544D // MT +*/ + +#define PKT_HEADER_MAX_SIZE 50 + + +enum packet_types +{ + PKT_END, + PKT_VALUE_HEADER, + PKT_SCREEN_HEADER_BW, + PKT_SCREEN_HEADER_COL, + PKT_BACKUP_HEADER, + PKT_PROGRAM_HEADER, + PKT_FUNCTION_HEADER, + PKT_VAL, + PKT_UNKNOWN +}; + +unsigned char calculate_checksum(const char *start, const char *stop); +unsigned short calculate_checksum_word(const char *start, const char *stop); +enum packet_types get_packet_type(const unsigned char* data); + +#endif diff --git a/port.c b/port.c new file mode 100644 index 0000000..91e3a8c --- /dev/null +++ b/port.c @@ -0,0 +1,92 @@ +#include "casio-get.h" + +#define PACKET_WAIT_PER_BYTE 2 + +#include +#include +#include + +/****************************************************************************** + * Wait for the calculator to request a data transfer + ******************************************************************************/ +void wait_calc(struct sp_port *port) +{ + char buf[1]; + int read; + fprintf(stderr, "Waiting for calculator... "); + while((read = sp_blocking_read(port, buf, 1, 0)) > 0) + { + if (buf[0] == ATTENTION_REQ) + break; + } + + if (read <= 0) + serial_error("sp_blocking_read"); + + fprintf(stderr, "transfer request received!\n"); +} + + +/****************************************************************************** + * Send single byte to calculator + ******************************************************************************/ +void send_calc_byte(struct sp_port *port, const char val) +{ + char data[1]; + data[0] = val; + if (sp_blocking_write(port, data, 1, 0) != 1) + serial_error("sp_blocking_write"); +} + +enum sp_return calc_read(struct sp_port *port, unsigned char* buffer, size_t size) +{ + enum sp_return ret = sp_blocking_read(port, buffer, size, PACKET_WAIT_PER_BYTE*size); + if (ret < 0) + serial_error("sp_blocking_read"); + + return ret; +} + + +/****************************************************************************** + * Open and initialise serial port + ******************************************************************************/ +struct sp_port* port_init(const char* device) +{ + struct sp_port* port; + if (sp_get_port_by_name(device, &port) != SP_OK) + serial_error("sp_get_port_by_name"); + + if (sp_open(port, SP_MODE_READ|SP_MODE_WRITE) != SP_OK) + serial_error("sp_open"); + + + /* Port config is more or less hard-coded + The Casio calcs operate at 9600,8N with 2 stop bits RX + and 1 stop bit TX but 2 stop bits both ways can't hurt :) */ + if (sp_set_baudrate(port, 9600) != SP_OK) + serial_error("sp_set_baudrate"); + + if (sp_set_parity(port, SP_PARITY_NONE) != SP_OK) + serial_error("sp_set_parity"); + + if (sp_set_bits(port, 8) != SP_OK) + serial_error("sp_set_bits"); + + if (sp_set_stopbits(port, 1) != SP_OK) + serial_error("sp_set_stopbits"); + + if (sp_set_flowcontrol(port, SP_FLOWCONTROL_NONE) != SP_OK) + serial_error("sp_set_flowcontrol"); + + if (sp_set_xon_xoff(port, SP_XONXOFF_DISABLED) != SP_OK) + serial_error("sp_set_xon_xoff"); + + if (sp_set_dtr(port, SP_DTR_ON) != SP_OK) + serial_error("sp_set_dtr"); + + if (sp_set_rts(port, SP_RTS_OFF) != SP_OK) + serial_error("sp_set_rts"); + + return port; +} diff --git a/port.h b/port.h new file mode 100644 index 0000000..9b86a48 --- /dev/null +++ b/port.h @@ -0,0 +1,14 @@ +#ifndef PORT_H +#define PORT_H + +#include + +#define ATTENTION_REQ 0x16 +#define ATTENTION_ACK 0x13 + +void wait_calc(struct sp_port *port); +void send_calc_byte(struct sp_port *port, const char val); +enum sp_return calc_read(struct sp_port *port, unsigned char* buffer, size_t size); +struct sp_port* port_init(const char* device); + +#endif diff --git a/screen-to-xpm.c b/screen-to-xpm.c new file mode 100644 index 0000000..35a293a --- /dev/null +++ b/screen-to-xpm.c @@ -0,0 +1,152 @@ +#include +#include + +#include "packet.h" + + +#define IMAGE_HEADER_BYTES_BW 40 +#define IMAGE_HEADER_BYTES_COL 41 +#define HEADER_SIZE 3 +#define IMAGE_WIDTH 128 +#define IMAGE_HEIGHT 64 +#define NUM_COLOURS 3 + +#define COLOUR_BLANK '0' + +const char colours[] = {'B', 'G', 'R'}; + + +void load_mono(char (*image)[IMAGE_WIDTH][IMAGE_HEIGHT]) +{ + unsigned short x, y; + char c; + char i; + for (x = 0; x < IMAGE_WIDTH && !feof(stdin); x+=8) + { + for (y = 0; y < IMAGE_HEIGHT && !feof(stdin); y++) + { + c = fgetc(stdin); + for (i = 0; i < 8; i++) + (*image)[x+i][y] = c & (1<<(i))? colours[0] : COLOUR_BLANK; + } + } +} + +void load_colour(char (*image)[IMAGE_WIDTH][IMAGE_HEIGHT]) +{ + short x, y, colour; + char buffer[IMAGE_WIDTH/8][IMAGE_HEIGHT]; + char c; + char i; + for (colour = 0; colour < NUM_COLOURS; colour++) + { + fread(buffer, sizeof(buffer), 1, stdin); + for (x = 0; x < IMAGE_WIDTH && !feof(stdin); x+=8) + { + for (y = 0; y < IMAGE_HEIGHT && !feof(stdin); y++) + { + c = buffer[x/8][IMAGE_HEIGHT-1-y]; + for (i = 0; i < 8; i++) + { + if ((*image)[x+i][y] == COLOUR_BLANK) + (*image)[x+i][y] = c & (1<<(i))? colours[colour] : COLOUR_BLANK; + } + } + } + /* eat the checksum */ + fgetc(stdin); + + /* eat the : and the colour identification bytes if another channel remains */ + if (colour != NUM_COLOURS-1) + { + fgetc(stdin); + fgetc(stdin); + } + } +} + + + +int main(int argc, char **argv) +{ + unsigned char header[PKT_HEADER_MAX_SIZE]; + char image[IMAGE_WIDTH][IMAGE_HEIGHT]; + int x, y, read, expected; + enum packet_types type; + + if (argc != 1) + { + fprintf(stderr, + "Converts monochrom CASIO FX-9850 screen dumps to XPM images.\n" + "Reads screen dump from stdin and outputs XPM to stdout\n" + "Also prints information and warning messages to stderr when necessary\n" + "\n" + "Syntax: %s < screen.dump > screen.xpm\n", argv[0]); + return 1; + } + + /* Lazy here, we just catch EOF later */ + /* magic number: 3 == strlen(":DD") == strlen(":DC") */ + read = fread(header, 3, 1, stdin); + + switch (get_packet_type(header)) + { + case PKT_SCREEN_HEADER_BW: + type = PKT_SCREEN_HEADER_BW; + expected = IMAGE_HEADER_BYTES_BW; + break; + case PKT_SCREEN_HEADER_COL: + type = PKT_SCREEN_HEADER_COL; + expected = IMAGE_HEADER_BYTES_COL; + break; + default: + fprintf(stderr, "Invalid header '%s', not a Casio FX 9850 screenshot\n", header); + return 1; + break; + } + + fprintf(stderr, "Detected %s screenshot\n", + type == PKT_SCREEN_HEADER_BW? "monochrome" : "colour"); + + /* skip over rest of header */ + expected -= read; + while (--expected) + fgetc(stdin); + + memset(image, COLOUR_BLANK, sizeof(image)); + + if (type == PKT_SCREEN_HEADER_BW) + load_mono(&image); + else + load_colour(&image); + + /* Tasty! A hard-coded XPM header! */ + printf( "/* XPM */\n" + "static char *Pic_colors[] = {\n" + " \"%d %d 4 1\"\n" + " \"B c #000033\"\n" + " \"G c #005555\"\n" + " \"R c #FF6633\"\n" + " \"0 c #FFFFFF\"\n" + "};\n" + "static char *Pic_pixels[] = {\n",IMAGE_WIDTH,IMAGE_HEIGHT + ); + + + for (y = 0; y < IMAGE_HEIGHT; y++) + { + printf("\""); /* XPM stuff */ + + for (x = IMAGE_WIDTH-1; x >= 0; x--) + printf("%c",image[x][y]); + + printf("\",\n"); /* Again, XPM stuff */ + } + + + /* Hard-coded XPM footer */ + printf("\"};"); + + return 0; +} + -- cgit v1.1