Skip to content
Browse files

MAJOR structural overhaul - prepared for multimachine support

  • Loading branch information...
1 parent 1a28f80 commit 47d3340bab7f1be16068ffbefa755202f6803506 @stg committed May 23, 2012
View
2 .gitignore
@@ -1,4 +1,2 @@
-adafruit-knitting_machine-cf67b9f/
private/
-./*.exe
thumbs.db
View
36 README
@@ -1,21 +1,22 @@
-Disk image manager for Brother Electroknit KH-940 knitting machine.
+Disk image manager for knitting machines:
+* Brother Electroknit KH-940
This program is capable of reading and writing:
* 80k disk image files (our proprietary format)
* Steve Conklin's Tandy PDD1 floppy drive emulator data folders.
== FILES =============================================================
-src/ Source files for the disk image manager
-sample/ Samples of disk images
+bin-X/ Binaries/executables for operating system X
+lib/ Source files for the disk image manager/emulator
+cli/ Source files for command line interface
+cpp_class/ Source files for C++ wrapper
+disk_images/ Samples of disk images
1.img Three small designs (actual data from machine)
2.img Two wide/tall designs (actual data from machine)
3.img Seven very different designs (generated file)
-doc/ File format documentation
- mem_format.txt Description of the KH-940 memory layout
- img_format.txt Description of the disk image formats
- raw_format.txt Description of pattern file format
-patterns/ Sample patterns as both bmp and raw
+doc/ File/memory layout description
+ptn/ Sample patterns as both original bmp and raw
== HOW TO USE ========================================================
@@ -26,6 +27,7 @@ Typing ? or help will give
?/help show this
r/read read in data from file
w/write write out data to file
+ m/machine select knitting machine
f/format clear all tracks
t/track set working track
a/add add pattern to track
@@ -52,6 +54,16 @@ write - Write memory to image on disk
See "read" command for more info.
+machine - Select knitting machine
+ This will show the currently selected machine as well as list
+ the available/supported machines.
+
+ After listing, you are prompted to select a new machine and
+ this is selected by typing the short name of the machine
+ you wish to use, ie:
+ machine> kh940
+
+
format - Clear computer ram contents
This will delete everything currently contained in memory and
ready the program for input of a new file.
@@ -65,7 +77,7 @@ track - Set working track
info commands.
Tracks are entered as a number between 1 and 2, ie:
- track #> 1
+ track> 1
add - Add a pattern to memory
@@ -79,7 +91,7 @@ show - Show patterns contained in memory
This will list all available patterns and to into pattern
display mode. In this mode you get the following promt:
- pattern #>
+ pattern>
instead of the regular one.
@@ -146,15 +158,15 @@ halt - Enable/disable halt on errors
All commands can be executed from command line, for example:
-knit x a ptn/blocks.raw a ptn/inca.raw a ptn/text.raw w out/img/ q
+knit x a ptn/tile.raw a ptn/inca.raw a ptn/text.raw w test.img q
Will do the following:
* Enable halt on error
* Add ptn/blocks.raw as #901
* Add ptn/inca.raw as #902
* Add ptn/text.raw as #903
-* Write python floppy emulator folder out/img/
+* Write disk image to test.img
* Quit
In addition commands can also be piped in, or typed manually in
View
BIN bin-linux/knit
Binary file not shown.
View
BIN bin-win/knit.exe
Binary file not shown.
View
BIN bin-windows/knit.exe
Binary file not shown.
View
357 cli/src/knit.c
@@ -0,0 +1,357 @@
+// Source file for the command line disk image manager
+// See README for HOW TO USE
+//
+// senseitg@gmail.com 2012-May-17
+
+//== DECLARATIONS ====================================================
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include "machine.h"
+#include "image.h"
+
+// command memory
+static char cmds[8192]={0};
+static char cmd[256];
+
+// flags
+static bool halt=false;
+
+// machine
+static machine_t *p_mach;
+
+//== UTIL FUNCTIONS ==================================================
+
+// command input routine that handles several
+// commands in one input as well as strings
+static bool read_cmd(const char *fail) {
+ char *ret;
+ char *p_src,*p_dst;
+ uint16_t len;
+ bool out=false;
+ if(strlen(cmds)==0) {
+ if(fgets(cmds,sizeof(cmds),stdin)==NULL) {
+ strcpy(cmds,fail);
+ out=true;
+ } else {
+ len=strlen(cmds);
+ if(cmds[len-1]=='\n') cmds[len-1]=0;
+ }
+ //count leading spaces
+ p_src=cmds;
+ while(*p_src==' ') p_src++;
+ if(*p_src==0) {
+ //just spaces
+ cmds[0]=0;
+ } else {
+ //trim leading spaces
+ memmove(cmds,p_src,strlen(p_src)+1);
+ //trim trailing spaces
+ p_src=cmds+strlen(cmds)-1;
+ while(*p_src==' ')*p_src--=0;
+ }
+ } else {
+ out=true;
+ }
+ //have nothing
+ if(strlen(cmds)==0) return false;
+ p_dst=cmd;
+ p_src=cmds;
+ while(*p_src==' ') p_src++;
+ if(*p_src=='"') {
+ p_src++;
+ while(*p_src!='"'&&*p_src!=0) *p_dst++=*p_src++;
+ if(*p_src=='"')p_src++;
+ } else {
+ while(*p_src!=' '&&*p_src!=0) *p_dst++=*p_src++;
+ }
+ *p_dst=0;
+ if(out)puts(cmd);
+ memmove(cmds,p_src,strlen(p_src)+1);
+ return true;
+}
+
+
+// find and read pattern with specific pattern number/id
+// return true if found
+static bool find_pattern(ptndesc_t *p_desc,uint32_t ptn_id) {
+ uint8_t n;
+ for(n=0;n<=p_mach->pattern_max-p_mach->pattern_min;n++) {
+ if(p_mach->decode_header(p_desc,n)){
+ if(p_desc->id==ptn_id) return true;
+ }
+ }
+ return false;
+}
+
+// convert pattern id as string to int
+static uint16_t str_to_id(char *str) {
+ uint16_t out=0;
+ while(*str!=0){
+ if(*str<'0'||*str>'9') return 0;
+ out=out*10+*str++-'0';
+ if(out>p_mach->pattern_max) return 0;
+ }
+ if(out<p_mach->pattern_min) return 0;
+ return(out);
+}
+
+// convert track id as string to int
+static uint8_t trk_to_id(char *str) {
+ uint8_t out=0;
+ while(*str!=0){
+ if(*str<'0'||*str>'9') return 0;
+ out=out*10+*str++-'0';
+ if(out>p_mach->track_count) return 0;
+ }
+ if(out<1) return 0;
+ return(out);
+}
+
+
+// format with printout
+void cmd_format() {
+ p_mach->format();
+ p_mach->set_track(0);
+ printf("memory initialized, track is 1\n");
+}
+
+// prompt for set current track
+static void cmd_track() {
+ uint8_t trk_id;
+ printf("current track is %i\n",p_mach->get_track()+1);
+ printf("track> ");
+ if(read_cmd("")) {
+ trk_id=trk_to_id(cmd);
+ if(trk_id) {
+ p_mach->set_track(trk_id-1);
+ printf("track is now %s\n",cmd);
+ } else {
+ printf("need number between 1 and %i\n",p_mach->track_count);
+ if(halt) exit(1);
+ }
+ }
+}
+
+// print image to screen
+static void image_print(uint8_t *p_img,uint16_t w,uint16_t h) {
+ uint16_t x,y;
+ for(y=0;y<h;y++) {
+ for(x=0;x<w;x++) {
+ putchar(image_sample(p_img,w,x,y)?'X':'-');
+ }
+ printf("\n");
+ }
+}
+
+// display patterns contained in memory
+static void cmd_show() {
+ uint8_t n;
+ uint16_t ptn_id;
+ ptndesc_t desc;
+ uint8_t *p_img;
+ for(n=0;n<98;n++) {
+ if(p_mach->decode_header(&desc,n)) {
+ printf("pattern %i is %ix%i @%04X\n",desc.id,desc.width,desc.height,desc.pattern);
+ }
+ }
+ while(1) {
+ printf("pattern> ");
+ if(!read_cmd("")) break;
+ if(strcmp(cmd,"d")==0||strcmp(cmd,"done")==0) break;
+ ptn_id=str_to_id(cmd);
+ if(ptn_id) {
+ if(find_pattern(&desc,ptn_id)){
+ p_img=image_alloc(desc.width,desc.height);
+ p_mach->decode_pattern(&desc,p_img);
+ image_print(p_img,desc.width,desc.height);
+ free(p_img);
+ } else {
+ printf("pattern number %i does not exist\n",ptn_id);
+ if(halt) exit(1);
+ }
+ } else {
+ printf("need done or number between %i and %i\n",p_mach->pattern_min,p_mach->pattern_max);
+ if(halt) exit(1);
+ }
+ }
+}
+
+// add pattern from file
+static void cmd_add() {
+ uint8_t *p_img;
+ uint16_t w,h;
+ FILE *f;
+ uint16_t ptn_id;
+ ptndesc_t desc;
+ printf("filename> ");
+ if(read_cmd("")) {
+ f=fopen(cmd,"rb");
+ if(f) {
+ // read image file
+ p_img=(uint8_t*)image_read(f,&w,&h);
+ fclose(f);
+ // verify machine capability
+ if(p_mach->size_check(w,h)) {
+ if(p_img) {
+ // display
+ image_print(p_img,w,h);
+ // add pattern to memory
+ ptn_id=p_mach->add_pattern(p_img,w,h);
+ if(ptn_id==0) {
+ printf("not enough memory to store pattern\n");
+ if(halt) {
+ free(p_img);
+ exit(1);
+ }
+ }
+ if(find_pattern(&desc,ptn_id) ) {
+ printf("added pattern %i as %ix%i @%04X\n",desc.id,desc.width,desc.height,desc.pattern);
+ } else {
+ printf("pattern was added but can not be found\n");
+ printf("memory may be corrupted, format suggested\n");
+ }
+ // free loaded data
+ free(p_img);
+ } else {
+ printf("file does not have the correct format\n");
+ if(halt) exit(1);
+ }
+ } else {
+ printf("pattern is too big for this machine\n",cmd);
+ if(halt) exit(1);
+ }
+ } else {
+ printf("unable to open file %s\n",cmd);
+ if(halt) exit(1);
+ }
+ }
+}
+
+void cmd_read() {
+ printf("file/directory> ");
+ if(read_cmd("")) {
+ if(machine_load(cmd)) {
+ printf("disk image loaded\n");
+ } else {
+ printf("unable to read specified file/directory\n");
+ printf("memory may be corrupted, format suggested\n");
+ }
+ }
+}
+
+void cmd_write() {
+ printf("file/directory> ");
+ if(read_cmd("")) {
+ if(machine_save(cmd)) {
+ printf("disk image saved\n");
+ } else {
+ printf("unable to write specified file/directory\n");
+ }
+ }
+}
+
+void cmd_emulate() {
+ printf("device> ");
+ if(read_cmd("")) {
+ machine_emulate(cmd,stdout);
+ }
+}
+
+void cmd_machine() {
+ uint8_t n;
+ machine_t *p_machtemp;
+ printf("current machine is %s\n",p_mach->name);
+ for(n=0;n<255;n++) {
+ p_machtemp=machine_get(n);
+ if(p_machtemp==NULL)break;
+ printf("machine %s is %s\n",p_machtemp->code,p_machtemp->name);
+ }
+ printf("machine> ");
+ if(read_cmd("")) {
+ for(n=0;n<255;n++) {
+ p_machtemp=machine_get(n);
+ if(p_machtemp==NULL)break;
+ if(strcmp(p_machtemp->code,cmd)==0) {
+ p_mach=p_machtemp;
+ printf("machine is now %s\n",p_mach->name);
+ return;
+ }
+ }
+ }
+ printf("machine %s not found\n",cmd);
+ if(halt) exit(1);
+}
+
+// do the nasty
+int main(int argc,char**argv) {
+ uint8_t n;
+ uint8_t *hdr;
+ uint32_t ptn_id;
+ FILE *f;
+ // skip executable
+ if(argc) {
+ argc--;
+ argv++;
+ }
+ // fetch arguments
+ while(argc--) {
+ strcat(cmds,*argv++);
+ if(argc)strcat(cmds," ");
+ }
+ // initialize machine
+ machine_init();
+ p_mach=machine_get(0);
+ // show machine
+ printf("machine is %s\n",p_mach->name);
+ // init memory
+ cmd_format();
+ while(1) {
+ printf("> ");
+ if(read_cmd("q")) {
+ if(strcmp(cmd,"help")==0||strcmp(cmd,"?")==0) {
+ printf("?/help show this\n");
+ printf("r/read read in data from file\n");
+ printf("w/write write out data to file\n");
+ printf("m/machine select knitting machine\n");
+ printf("f/format clear all tracks\n");
+ printf("t/track set working track\n");
+ printf("a/add add pattern to track\n");
+ printf("s/show display content of track\n");
+ printf("i/info additional track info\n");
+ printf("e/emulate emulate floppy\n");
+ printf("q/quit end program\n");
+ printf("x/halt halt on errors\n");
+ } else if(strcmp(cmd,"x")==0||strcmp(cmd,"halt")==0) {
+ halt=!halt;
+ printf("halt on errors: %s\n",halt?"yes":"no");
+ } else if(strcmp(cmd,"quit")==0||strcmp(cmd,"q")==0) {
+ printf("See you!\n");
+ exit(0);
+ } else if(strcmp(cmd,"r")==0||strcmp(cmd,"read")==0) {
+ cmd_read();
+ } else if(strcmp(cmd,"w")==0||strcmp(cmd,"write")==0) {
+ cmd_write();
+ } else if(strcmp(cmd,"m")==0||strcmp(cmd,"machine")==0) {
+ cmd_machine();
+ } else if(strcmp(cmd,"t")==0||strcmp(cmd,"track")==0) {
+ cmd_track();
+ } else if(strcmp(cmd,"f")==0||strcmp(cmd,"format")==0) {
+ cmd_format();
+ } else if(strcmp(cmd,"a")==0||strcmp(cmd,"add")==0) {
+ cmd_add();
+ } else if(strcmp(cmd,"i")==0||strcmp(cmd,"info")==0) {
+ p_mach->info(stdout);
+ } else if(strcmp(cmd,"s")==0||strcmp(cmd,"show")==0) {
+ cmd_show();
+ } else if(strcmp(cmd,"e")==0||strcmp(cmd,"emulate")==0) {
+ cmd_emulate();
+ } else {
+ printf("Unrecognized command: %s\n",cmd);
+ if(halt) exit(1);
+ }
+ }
+ }
+}
View
19 cpp_class/include/knit.h
@@ -0,0 +1,19 @@
+// C++ wrapper class for library functions
+//
+// senseitg@gmail.com 2012-May-22
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include "machine.h"
+#include "emulate.h"
+
+class knit {
+public:
+ knit();
+ machine_t *driver_get(uint8_t index);
+ bool memory_load(char *path);
+ bool memory_save(char *path);
+ void emulate_start(char *device,bool verbose);
+ void emulate_stop();
+};
View
31 cpp_class/src/knit.cpp
@@ -0,0 +1,31 @@
+// C++ wrapper class for library functions
+//
+// Requires inclusion of lib/src/*.c in project
+//
+// senseitg@gmail.com 2012-May-22
+
+#include "main-class.h"
+
+knit::knit() {
+ machine_init();
+}
+
+machine_t *knit::driver_get(uint8_t index) {
+ return machine_get(index);
+}
+
+bool knit::memory_load(char *path) {
+ return machine_load(path);
+}
+
+bool knit::memory_save(char *path) {
+ return machine_save(path);
+}
+
+void knit::emulate_start(char *device,bool verbose) {
+ machine_emulate(device,verbose?stdout:NULL);
+}
+
+void knit::emulate_stop() {
+ emulate_stop();
+}
View
0 sample/1.img → disk_images/1.img
File renamed without changes.
View
0 sample/2.img → disk_images/2.img
File renamed without changes.
View
0 sample/3.img → disk_images/3.img
File renamed without changes.
View
10 doc/img_format.txt → doc/diskimage_format.txt
@@ -1,4 +1,4 @@
-== BROTHER ELECTROKNIT KH940 PROPRIETARY FLOPPY IMAGE FORMAT =========
+== PROPRIETARY TANDY PDD1 FLOPPY IMAGE FORMAT ========================
Offset Bytes Content
0x000000 1024 Sector 0 - data
@@ -15,10 +15,8 @@ Offset Bytes Content
...........................
0x0143B4 12 Sector 79 - data
-Actual data in sectors 0 to 31 is described in mem_format.txt
-
-== BROTHER ELECTROKNIT TANDY PDD1 FLOPPY EMULATOR DATA FOLDER ========
+== STEVE CONKLIN PY TANDY PDD1 FLOPPY EMULATOR DATA FOLDER ===========
File Bytes Content
00.dat 1024 Sector 0 - id
@@ -33,6 +31,4 @@ File Bytes Content
02.id 12 Sector 2 - id
...........................
...........................
-79.id 12 Sector 79 - id
-
-Actual data in sectors 0 to 31 is described in mem_format.txt
+79.id 12 Sector 79 - id
View
628 doc/mem_format.txt → doc/kh940_format.txt
@@ -1,314 +1,314 @@
-== BROTHER ELECTROKNIT KH940 FLOPPY FILE/MEMORY STRUCTURE ============
-
-The machine uses the first 32 sectors on a floppy for storage.
-For the entirety of this document, the contents of these sectors
-are treated as a single coherent 32kB chunk of binary data with
-big-endian format where applicable.
-
-Note that this is not likely an actual file format, rather a binary
-dump of all contents in the external RAM memory of the machine.
-
-* When writing about offsets, they are from LAST_BYTE (end of file),
- Example: The file address for offset of 0x0120 would be
- 0x7EDF = 0x7FFF - 0x0120
-
-** When writing about alternatives, it means that contents is not
- entirely classified, and could be any of several alternatives.
- Further investigation was not warranted because knowing the
- exact specification is not necessary to build a functional
- disk image.
-
-Offset Bytes Content
-
-0x0000 686 PATTERN_LIST
-
- Contains array of *up to* 98 pattern descriptors.
- Unused items are filled with 0x55 except for FINHDR which
- also needs XX=0 and PTN_NUM=next unused id.
-
- Patterns are not always stored in linear order, ie the list
- may be 901, 904, 902, 903. This happens when patterns are
- deleted in the machine.
-
- Content Address (Offset* from header)
- /---------------\ <- 0x0000
- | Header 901 |
- \---------------/ <- 0x0006
-
- /---------------\ <- 0x0007
- | Header 902 |
- \---------------/ <- 0x000D
-
- /---------------\ <- 0x000E
- | FINHDR |
- \---------------/ <- 0x0014
-
- /---------------\ <- 0x0015 <- CONTROL_DATA 0x10 points here
- . . .
- . . .
- | Unused memory |
- \---------------/ <- 0x02AD
-
- Each entry is 7 bytes:
-
- / DATA_OFFSET \ / HEIGHT \/ WIDTH \ /XX\/ PTN_NUM \
- OOOOOOOO-OOOOOOOO-HHHHHHHH-HHHHWWWW-WWWWWWWW-0000NNNN-NNNNNNNN
- \byte 0/ \byte 1/ \byte 2/ \byte 3/ \byte 4/ \byte 5/ \byte 6/
-
- DATA_OFFSET
- Format: Binary 16bit unsigned
- Data: Offset* to last byte of pattern data
-
- HEIGHT
- Format: BCD 3nibbles/digits
- Data: Heigh/number of rows
-
- WIDTH
- Format: BCD 3nibbles/digits
- Data: Width/number of stitches
-
- XX
- Always a 0x0 nibble
-
- PTN_NUM
- Format: BCD 3nibbles/digits
- Data: Pattern number 0x901 to 0x998
-
-
-0x02AE 31794 PATTERN_MEMORY
-
- Pattern memory is arranged much as a stack and is written backwards
- so that the first pattern has it's last byte in location 0x7EDF.
-
- Example:
-
- Content Address (Offset* from header)
- /---------------\ <- 0x02AE
- . . .
- . . .
- | Unused memory |
- \---------------/ <- 0x7ECF
-
- /---------------\ <- 0x7ED0
- | Pattern 902 |
- \---------------/ <- 0x7EDB (0x0124)
-
- /---------------\ <- 0x7EDC
- | Pattern 901 |
- \---------------/ <- 0x7EDF (0x0120)
-
- There MAY be gaps between patterns read from the machine so do not
- expect patterns to be arranged end-to-end: USE THE POINTERS!
-
- Unused memory is filled with either 0x55 or 0xAA.
-
- Each pattern is divided into two sections, arranged as:
-
- /---------------\
- | PatternDATA |
- \---------------/
-
- /---------------\
- | PatternMEMO |
- \---------------/ <- Offset* points here
-
- MEMO
-
- Each row of the pattern may have a number assigned to it but
- it is not significant for operation, consider it meta-data.
-
- Each such number requires one nibble for every row, ie HEIGHT/2.
- This is rounded to the nearest byte, hence a pattern
- containing 7 rows will have 4 bytes of MEMO data.
- A pattern with 10 rows will have 5 bytes of MEMO data.
-
- This can be calculated as:
- ceil(height/2) // where ceil() is round upwards
-
- Write as: 0x00
-
- DATA
-
- Each row of the pattern requires WIDTH bits for storage and
- this is rounded up to the nearest nibble by padding it with
- 0-bits until the number of bits is a multiple of 4.
-
- A pattern that has 7 stitches will therefore require 2 bytes
- per row. One that has 9 stiches will require 2.5 bytes per row.
-
- This means that rows may not be aligned on byte boundaries.
-
- Patterns are stored top-down, right-left.
- This means the pattern is flipped right to left compared to the
- actual knitted design once completed.
-
- The number of bytes used to store data can be calculated as:
- ceil(ceil(width/4)*height/2) // where ceil() is round upwards
-
- The layout is best described via examples:
- 0 = pad bits (binary 0)
- X = stitch bits (binary 1)
- - = no stitch bits (binary 0)
-
- Example 1 - 4x4
- ---- Row 0: 0x0
- -XX- Row 1: 0x6
- -XX- Row 2: 0x6
- ---- Row 3: 0x0
- Byte data (hex): 06 60
-
-
- Example 2 - 6x5
- 00X----X Row 0: 0x21
- 00------ Row 1: 0x00
- 00--XX-- Row 2: 0x0C
- 00X----X Row 3: 0x21
- 00-XXXX- Row 4: 0x1E
- Byte data (hex): 21 00 0C 21 1E
-
- Example 3 - 9x9
- 0000 Padding: 0x0
- 000XXXXXXXXX Row 0: 0x1FF
- 000X-------X Row 1: 0x101
- 000X-XXXXX-X Row 2: 0x17D
- 000X-X---X-X Row 3: 0x145
- 000X-X-X-X-X Row 4: 0x155
- 000X-X---X-X Row 5: 0x145
- 000X-XXXXX-X Row 6: 0x17D
- 000X-------X Row 7: 0x101
- 000XXXXXXXXX Row 8: 0x1FF
- Byte data (hex): 01 FF 10 11 7D 14 51 55 14 51 7D 10 11 FF
-
- Note for example 3 that there is a nibble of padding before the
- design because the design does not round up to an even byte.
-
-
-0x7EE0 7 AREA0
-
- Seems to be completely unused.
-
- Content with patterns: 0x55 or 0xAA
- Content after format: 0x55
- Write as: 0x55
-
-
-0x7EE7 25 AREA1
-
- Contains unidentified bit pattern.
- Best guess is working memory for pattern input.
-
- Content with patterns: several repetitions of an n-byte pattern
- Content after format: 0x55
- Write as: 0x00
-
-
-0x7F00 23 CONTROL_DATA
-
- 0x00 2 PATTERN_PTR1
- Format: Binary 16-bit unsigned
- Data: Offset* of next byte to be written by next pattern input
- Write as: Offset of first byte of last pattern + 1
-
- 0x02 2 UNK1
- Format: Binary 16-bit unsigned
- Data: Unknown, 0x0001 with patterns, 0x0000 after format
- Write as: 0x0001
-
- 0x04 2 PATTERN_PTR0
- Format: Binary 16-bit unsigned
- Data: Same as BYTE 0-1 with patterns, 0x0000 after format
- Write as: Offset of first byte of last pattern + 1
-
- 0x06 2 LAST_BOTTOM
- Format: Binary 16-bit unsigned
- Data: Alt1**: Offset to last byte of currently active pattern
- Alt2**: Offset to last byte of last created pattern
- Write as: Offset to last byte of last created pattern
-
- 0x08 2 UNK2
- Data: Always 0x0000
- Write as: 0x0000
-
- 0x0A 2 LAST_TOP
- Format: Binary 16-bit unsigned
- Data: Alt1**: Offset to first byte of currently active pattern
- Alt2**: Offset to first byte of last created pattern
- Write as: Offset to first byte of last created pattern
-
- 0x0C 4 UNK3
- Data: Unknown, 0x00008100 with patterns, 0x00000000 after format
- Write as: 0x00008100
-
- 0x10 2 HEADER_PTR
- Format: Binary 16-bit unsigned
- Data: Offset to end of pattern header list, 0x7FF9 after format
- See PATTERN_LIST memory layout for exact pointer
- Write as: 0x7FF9
-
- 0x12 2 UNK_PTR
- Format: Binary 16-bit unsigned
- Data: Unknown offset
- This was only present in one file, after a delete had
- recently been made. Best guess is mem. move pointer.
- Write as: 0x0000
-
- 0x14 3 UNK4
- Data: Always 0x000000
- Write as: 0x000000
-
-
-0x7F17 25 AREA2
-
- Contains unidentified bit pattern.
- Best guess is working memory for pattern input.
-
- Content with patterns: several repetitions of an n-byte pattern
- Content after format: 0x55
- Write as: 0x00
-
-
-0x7F30 186 AREA3
-
- Seems to be completely unused.
-
- Content with patterns: 0x00
- Content after format : 0x00
- Write as: 0x00
-
-
-0x7FEA 2 LOADED_PATTERN
-
- Alt1**: Contains currently active pattern number.
- Alt2**: Contains last created pattern number.
-
- Content after format : 0x1000
- Write as: Last created pattern number
-
- [XX]/ PTN_NUM \
- 0001NNNN-NNNNNNNN
- \byte 0/ \byte 1/
-
- XX
- Always a 0x1 nibble
-
- PTN_NUM
- Format: BCD 3nibbles/digits
- Data: Pattern number
-
-
-0x7FEC 19 AREA4
-
- Seems to be completely unused.
-
- Content with patterns: 0x00
- Content after format : 0x00
- Write as: 0x00
-
-
-0x7FFF 1 LAST_BYTE
-
- Contains unidentified byte.
-
- Content with patterns: Different every time
- Content after format: 0x00
- Write as: 0x02
+== BROTHER ELECTROKNIT KH940 FLOPPY FILE/MEMORY STRUCTURE ============
+
+The machine uses the first 32 sectors on a floppy for storage.
+For the entirety of this document, the contents of these sectors
+are treated as a single coherent 32kB chunk of binary data with
+big-endian format where applicable.
+
+Note that this is not likely an actual file format, rather a binary
+dump of all contents in the external RAM memory of the machine.
+
+* When writing about offsets, they are from LAST_BYTE (end of file),
+ Example: The file address for offset of 0x0120 would be
+ 0x7EDF = 0x7FFF - 0x0120
+
+** When writing about alternatives, it means that contents is not
+ entirely classified, and could be any of several alternatives.
+ Further investigation was not warranted because knowing the
+ exact specification is not necessary to build a functional
+ disk image.
+
+Offset Bytes Content
+
+0x0000 686 PATTERN_LIST
+
+ Contains array of *up to* 98 pattern descriptors.
+ Unused items are filled with 0x55 except for FINHDR which
+ also needs XX=0 and PTN_NUM=next unused id.
+
+ Patterns are not always stored in linear order, ie the list
+ may be 901, 904, 902, 903. This happens when patterns are
+ deleted in the machine.
+
+ Content Address (Offset* from header)
+ /---------------\ <- 0x0000
+ | Header 901 |
+ \---------------/ <- 0x0006
+
+ /---------------\ <- 0x0007
+ | Header 902 |
+ \---------------/ <- 0x000D
+
+ /---------------\ <- 0x000E
+ | FINHDR |
+ \---------------/ <- 0x0014
+
+ /---------------\ <- 0x0015 <- CONTROL_DATA 0x10 points here
+ . . .
+ . . .
+ | Unused memory |
+ \---------------/ <- 0x02AD
+
+ Each entry is 7 bytes:
+
+ / DATA_OFFSET \ / HEIGHT \/ WIDTH \ /XX\/ PTN_NUM \
+ OOOOOOOO-OOOOOOOO-HHHHHHHH-HHHHWWWW-WWWWWWWW-0000NNNN-NNNNNNNN
+ \byte 0/ \byte 1/ \byte 2/ \byte 3/ \byte 4/ \byte 5/ \byte 6/
+
+ DATA_OFFSET
+ Format: Binary 16bit unsigned
+ Data: Offset* to last byte of pattern data
+
+ HEIGHT
+ Format: BCD 3nibbles/digits
+ Data: Heigh/number of rows
+
+ WIDTH
+ Format: BCD 3nibbles/digits
+ Data: Width/number of stitches
+
+ XX
+ Always a 0x0 nibble
+
+ PTN_NUM
+ Format: BCD 3nibbles/digits
+ Data: Pattern number 0x901 to 0x998
+
+
+0x02AE 31794 PATTERN_MEMORY
+
+ Pattern memory is arranged much as a stack and is written backwards
+ so that the first pattern has it's last byte in location 0x7EDF.
+
+ Example:
+
+ Content Address (Offset* from header)
+ /---------------\ <- 0x02AE
+ . . .
+ . . .
+ | Unused memory |
+ \---------------/ <- 0x7ECF
+
+ /---------------\ <- 0x7ED0
+ | Pattern 902 |
+ \---------------/ <- 0x7EDB (0x0124)
+
+ /---------------\ <- 0x7EDC
+ | Pattern 901 |
+ \---------------/ <- 0x7EDF (0x0120)
+
+ There MAY be gaps between patterns read from the machine so do not
+ expect patterns to be arranged end-to-end: USE THE POINTERS!
+
+ Unused memory is filled with either 0x55 or 0xAA.
+
+ Each pattern is divided into two sections, arranged as:
+
+ /---------------\
+ | PatternDATA |
+ \---------------/
+
+ /---------------\
+ | PatternMEMO |
+ \---------------/ <- Offset* points here
+
+ MEMO
+
+ Each row of the pattern may have a number assigned to it but
+ it is not significant for operation, consider it meta-data.
+
+ Each such number requires one nibble for every row, ie HEIGHT/2.
+ This is rounded to the nearest byte, hence a pattern
+ containing 7 rows will have 4 bytes of MEMO data.
+ A pattern with 10 rows will have 5 bytes of MEMO data.
+
+ This can be calculated as:
+ ceil(height/2) // where ceil() is round upwards
+
+ Write as: 0x00
+
+ DATA
+
+ Each row of the pattern requires WIDTH bits for storage and
+ this is rounded up to the nearest nibble by padding it with
+ 0-bits until the number of bits is a multiple of 4.
+
+ A pattern that has 7 stitches will therefore require 2 bytes
+ per row. One that has 9 stiches will require 2.5 bytes per row.
+
+ This means that rows may not be aligned on byte boundaries.
+
+ Patterns are stored top-down, right-left.
+ This means the pattern is flipped right to left compared to the
+ actual knitted design once completed.
+
+ The number of bytes used to store data can be calculated as:
+ ceil(ceil(width/4)*height/2) // where ceil() is round upwards
+
+ The layout is best described via examples:
+ 0 = pad bits (binary 0)
+ X = stitch bits (binary 1)
+ - = no stitch bits (binary 0)
+
+ Example 1 - 4x4
+ ---- Row 0: 0x0
+ -XX- Row 1: 0x6
+ -XX- Row 2: 0x6
+ ---- Row 3: 0x0
+ Byte data (hex): 06 60
+
+
+ Example 2 - 6x5
+ 00X----X Row 0: 0x21
+ 00------ Row 1: 0x00
+ 00--XX-- Row 2: 0x0C
+ 00X----X Row 3: 0x21
+ 00-XXXX- Row 4: 0x1E
+ Byte data (hex): 21 00 0C 21 1E
+
+ Example 3 - 9x9
+ 0000 Padding: 0x0
+ 000XXXXXXXXX Row 0: 0x1FF
+ 000X-------X Row 1: 0x101
+ 000X-XXXXX-X Row 2: 0x17D
+ 000X-X---X-X Row 3: 0x145
+ 000X-X-X-X-X Row 4: 0x155
+ 000X-X---X-X Row 5: 0x145
+ 000X-XXXXX-X Row 6: 0x17D
+ 000X-------X Row 7: 0x101
+ 000XXXXXXXXX Row 8: 0x1FF
+ Byte data (hex): 01 FF 10 11 7D 14 51 55 14 51 7D 10 11 FF
+
+ Note for example 3 that there is a nibble of padding before the
+ design because the design does not round up to an even byte.
+
+
+0x7EE0 7 AREA0
+
+ Seems to be completely unused.
+
+ Content with patterns: 0x55 or 0xAA
+ Content after format: 0x55
+ Write as: 0x55
+
+
+0x7EE7 25 AREA1
+
+ Contains unidentified bit pattern.
+ Best guess is working memory for pattern input.
+
+ Content with patterns: several repetitions of an n-byte pattern
+ Content after format: 0x55
+ Write as: 0x00
+
+
+0x7F00 23 CONTROL_DATA
+
+ 0x00 2 PATTERN_PTR1
+ Format: Binary 16-bit unsigned
+ Data: Offset* of next byte to be written by next pattern input
+ Write as: Offset of first byte of last pattern + 1
+
+ 0x02 2 UNK1
+ Format: Binary 16-bit unsigned
+ Data: Unknown, 0x0001 with patterns, 0x0000 after format
+ Write as: 0x0001
+
+ 0x04 2 PATTERN_PTR0
+ Format: Binary 16-bit unsigned
+ Data: Same as BYTE 0-1 with patterns, 0x0000 after format
+ Write as: Offset of first byte of last pattern + 1
+
+ 0x06 2 LAST_BOTTOM
+ Format: Binary 16-bit unsigned
+ Data: Alt1**: Offset to last byte of currently active pattern
+ Alt2**: Offset to last byte of last created pattern
+ Write as: Offset to last byte of last created pattern
+
+ 0x08 2 UNK2
+ Data: Always 0x0000
+ Write as: 0x0000
+
+ 0x0A 2 LAST_TOP
+ Format: Binary 16-bit unsigned
+ Data: Alt1**: Offset to first byte of currently active pattern
+ Alt2**: Offset to first byte of last created pattern
+ Write as: Offset to first byte of last created pattern
+
+ 0x0C 4 UNK3
+ Data: Unknown, 0x00008100 with patterns, 0x00000000 after format
+ Write as: 0x00008100
+
+ 0x10 2 HEADER_PTR
+ Format: Binary 16-bit unsigned
+ Data: Offset to end of pattern header list, 0x7FF9 after format
+ See PATTERN_LIST memory layout for exact pointer
+ Write as: 0x7FF9
+
+ 0x12 2 UNK_PTR
+ Format: Binary 16-bit unsigned
+ Data: Unknown offset
+ This was only present in one file, after a delete had
+ recently been made. Best guess is mem. move pointer.
+ Write as: 0x0000
+
+ 0x14 3 UNK4
+ Data: Always 0x000000
+ Write as: 0x000000
+
+
+0x7F17 25 AREA2
+
+ Contains unidentified bit pattern.
+ Best guess is working memory for pattern input.
+
+ Content with patterns: several repetitions of an n-byte pattern
+ Content after format: 0x55
+ Write as: 0x00
+
+
+0x7F30 186 AREA3
+
+ Seems to be completely unused.
+
+ Content with patterns: 0x00
+ Content after format : 0x00
+ Write as: 0x00
+
+
+0x7FEA 2 LOADED_PATTERN
+
+ Alt1**: Contains currently active pattern number.
+ Alt2**: Contains last created pattern number.
+
+ Content after format : 0x1000
+ Write as: Last created pattern number
+
+ [XX]/ PTN_NUM \
+ 0001NNNN-NNNNNNNN
+ \byte 0/ \byte 1/
+
+ XX
+ Always a 0x1 nibble
+
+ PTN_NUM
+ Format: BCD 3nibbles/digits
+ Data: Pattern number
+
+
+0x7FEC 19 AREA4
+
+ Seems to be completely unused.
+
+ Content with patterns: 0x00
+ Content after format : 0x00
+ Write as: 0x00
+
+
+0x7FFF 1 LAST_BYTE
+
+ Contains unidentified byte.
+
+ Content with patterns: Different every time
+ Content after format: 0x00
+ Write as: 0x02
View
0 doc/raw_format.txt → doc/rawimage_format.txt
File renamed without changes.
View
24 lib/include/emulate.h
@@ -0,0 +1,24 @@
+// Header for Tandy PDD1 floppy drive emulator
+//
+// senseitg@gmail.com 2012-May-22
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdio.h>
+
+// start emulate floppy drive
+// device specifies device for sopen, see serial.c/h
+// verbose specifies FILE* for verbose output, such as stdout
+// emulate is blocking
+void emulate(char *device,uint8_t *p_sect_data,uint8_t *p_sids_data,FILE *verbose);
+
+// stop emulating floppy drive
+// since emulate is blocking, this must be called from a separate process
+void emulate_stop();
+
+#ifdef __cplusplus
+}
+#endif
View
28 lib/include/fileio.h
@@ -0,0 +1,28 @@
+// Header for disk image file I/O
+//
+// senseitg@gmail.com 2012-May-22
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _WIN32
+#define PATH_SEPARATOR '\\'
+#else
+#define PATH_SEPARATOR '/'
+#endif
+
+// write out a disk image or floppy emulator folder
+// pass path to read folder, file to read disk image
+bool disk_write(char *path,uint8_t *data, uint8_t *sids);
+
+// read in a disk image or floppy emulator folder
+// pass path to read folder, file to read disk image
+bool disk_read(char *path,uint8_t *data,uint8_t *sids);
+
+#ifdef __cplusplus
+}
+#endif
View
27 lib/include/image.h
@@ -0,0 +1,27 @@
+// Header file for image management functions
+//
+// senseitg@gmail.com 2012-May-22
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// allocate memory for image
+uint8_t *image_alloc(uint16_t width,uint16_t height);
+
+// get pixel from image
+bool image_sample(uint8_t *p_image,uint16_t width,uint16_t x,uint16_t y);
+
+// set pixel in image
+void image_pset(uint8_t *p_image,uint16_t width,uint16_t x,uint16_t y,bool pixel);
+
+// read image file
+uint8_t *image_read(FILE *f,uint16_t *width,uint16_t *height);
+
+#ifdef __cplusplus
+}
+#endif
View
62 lib/include/machine.h
@@ -0,0 +1,62 @@
+// Header file for machine definitions
+//
+// senseitg@gmail.com 2012-May-22
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// pattern info
+typedef struct {
+ uint16_t id;
+ uint32_t location;
+ uint16_t width;
+ uint16_t height;
+ uint32_t pattern;
+} ptndesc_t;
+
+// machine descriptor
+typedef struct {
+ char code[16];
+ char *name;
+ uint16_t (*memory_used)(ptndesc_t* p_desc);
+ bool (*decode_header)(ptndesc_t* p_desc,uint8_t index);
+ void (*decode_pattern)(ptndesc_t* p_desc,uint8_t *p_image);
+ void (*format)(void);
+ void (*set_track)(uint8_t track);
+ uint8_t (*get_track)(void);
+ bool (*size_check)(uint16_t width,uint16_t height);
+ uint16_t (*add_pattern)(uint8_t *p_image,uint16_t width,uint16_t height);
+ void (*info)(FILE *output);
+ uint16_t pattern_min;
+ uint16_t pattern_max;
+ uint16_t track_count;
+} machine_t;
+
+// Machine initialisers
+void kh940_init(machine_t *p_machine,uint8_t *p_disk_data,uint8_t *p_disk_sids);
+
+// Initialize all machines
+// Must be called before machine_Get
+void machine_init();
+
+// Get machine descriptor with specific index
+// Returns NULL if not found
+machine_t *machine_get(uint8_t index);
+
+// Load machine memory from disk
+bool machine_load(char *path);
+
+// Save machine memory to disk
+bool machine_save(char *path);
+
+// Start floppy drive emulator
+void machine_emulate(char *device,FILE *verbose);
+
+#ifdef __cplusplus
+}
+#endif
View
34 lib/include/serial.h
@@ -0,0 +1,34 @@
+// Header for platform independant serial communications
+//
+// senseitg@gmail.com 2012-May-22
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+
+// open serial port
+// device has system dependant form
+// returns true if successful
+bool sopen(char* device);
+
+// configure serial port
+// fmt has form "baud,parity,databits,stopbit", ie: "9600,N,8,1"
+// returns true if successful
+bool sconfig(char* fmt);
+
+// read from serial port
+// returns bytes actually read
+int32_t sread(void *p_read,uint16_t i_read);
+
+// write to serial port
+int32_t swrite(void* p_write,uint16_t i_write);
+
+// close serial port
+bool sclose();
+
+#ifdef __cplusplus
+}
+#endif
View
49 lib/include/util.h
@@ -0,0 +1,49 @@
+// Header file for utility functions
+//
+// senseitg@gmail.com 2012-May-22
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// nibble access convenience macros
+#define MSN(B) (((unsigned char)B)>>4)
+#define LSN(B) (((unsigned char)B)&0x0F)
+
+// prints hex data in traditional 16 column format
+void print_hex(FILE *output,uint8_t* mem,uint32_t length);
+
+// prints hex data in slim format
+void print_slim_hex(FILE *output,uint8_t* mem,uint32_t length);
+
+// set bit in *p_data @ bit pointer bp
+void bit_set(uint8_t *p_data,uint32_t bp);
+
+// clear bit in *p_data @ bit pointer bp
+void bit_clr(uint8_t *p_data,uint32_t bp);
+
+// get bit in *p_data @ bit pointer bp
+bool bit_get(uint8_t *p_mem,uint32_t bp);
+
+// get nibble in *p_data @ nibble pointer np
+uint8_t nib_get(uint8_t *p_data,uint32_t np);
+
+// set nibble in *p_data @ nibble pointer np
+void nib_set(uint8_t *p_data,uint32_t np,uint8_t val);
+
+// get big-endian int16 in *p_data @ byte pointer p
+uint16_t int_get(uint8_t *p_data,uint32_t p);
+
+// set big-endian int16 in *p_data @ byte pointer p
+void int_set(uint8_t *p_data,uint32_t p,uint32_t val);
+
+// returns bcd number of column with specified value (ie 1, 10, 100, etc)
+uint8_t bcd_get(uint16_t n,uint16_t value);
+
+#ifdef __cplusplus
+}
+#endif
View
242 lib/src/emulate.c
@@ -0,0 +1,242 @@
+// Source file for Tandy PDD1 floppy drive emulator
+//
+// Supports only a limited subset of actual drive
+// Sector size locked to 1024
+// In operation mode:
+// Switch to FDC emulation mode
+// In FDC emulation mode
+// Format (only to sector size 1024)
+// Read/write sector ID
+// Read/write sector DATA
+// Search for sector with ID
+//
+// senseitg@gmail.com 2012-May-22
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include "serial.h"
+#include "emulate.h"
+
+uint8_t *p_sect, *p_sids;
+
+bool stop=false;
+FILE* p_out=NULL;
+
+// serial write with error printing
+static int32_t eswrite(void* p_write,uint16_t i_write) {
+ if(swrite(p_write,i_write)!=i_write) if(p_out)fprintf(p_out,"unable to write to serial port\n");
+}
+
+// op mode command executer
+void exec_op(uint8_t cmd[]) {
+ if(cmd[0]==0x08) {
+ if(p_out)fprintf(p_out,"[op mode] recv 0x08 [fdc mode ]\n");
+ } else {
+ if(p_out)fprintf(p_out,"[op mode] recv 0x%02X bad/unsupported command\n",cmd[0]);
+ }
+}
+
+// returns pointer to name of fdc command
+static const char* fdc_name(uint8_t cmd) {
+ switch(cmd) {
+ case 'A': return "read id ";
+ case 'R': return "read sect ";
+ case 'S': return "find sect ";
+ case 'B': case 'C': return "write id ";
+ case 'W': case 'X': return "write sect";
+ default: return "?";
+ }
+}
+
+// make hex char
+static char hexchar(uint8_t c) {
+ if(c<10)return '0'+c;
+ return 'A'+c-10;
+}
+
+// sets successful response code
+static void fdc_ok(char *p_ret,uint8_t sect) {
+ strcpy(p_ret,"00000000");
+ p_ret[2]=hexchar(sect>>4);
+ p_ret[3]=hexchar(sect&0xF);
+}
+
+// fdc mode command executer
+static uint16_t exec_fdc(uint8_t cmd[0]) {
+ char ret[9]="80000000";
+ uint16_t count=0;
+ uint8_t n;
+ if(cmd[0]=='F'||cmd[0]=='G') {
+ // check length code (but format anyways)
+ if(cmd[1]!=5) if(p_out)fprintf(p_out,"recv %02X [format ] unsupported length code 0x%02X\n",cmd[0],cmd[1]);
+ // format memory
+ memset(p_sect,0x00,80*1024);
+ for(n=0;n<80;n++) memset(&p_sids[n*12],0x00,12);
+ // respond ok
+ strcpy(ret,"00000000");
+ if(p_out)fprintf(p_out,"[fdc emu] exec 0x%02X [format ] resp: %s\n",cmd[0],ret);
+ } else {
+ if(cmd[1]==0xFF)cmd[1]=0; // physical sector default
+ if(cmd[2]==0xFF)cmd[2]=1; // logical sector default
+ // check sector validity
+ if(cmd[1]<80&&cmd[2]==1) {
+ // respond ok
+ fdc_ok(ret,cmd[1]);
+ // return
+ switch(cmd[0]) {
+ case 'A': case 'R': count= 1; break;
+ case 'S': case 'B': case 'C': count= 12; break;
+ case 'W': case 'X': count=1024; break;
+ }
+ if(p_out)fprintf(p_out,"[fdc emu] recv 0x%02X [%s] resp: %s expect %i bytes\n",cmd[0],fdc_name(cmd[0]),ret,count);
+ } else if(p_out)fprintf(p_out,"[fdc emu] recv 0x%02X [%s] resp: %s bad sector %i\\%i\n",cmd[0],fdc_name(cmd[0]),ret,cmd[1],cmd[2]);
+ }
+ // send response
+ eswrite(ret,8);
+ return count;
+}
+
+// fdc mode command+data executer
+static void exec_fdc_data(uint8_t *cmd) {
+ char ret[9];
+ uint8_t *p_data=&cmd[4];
+ uint8_t n;
+ switch(cmd[0]) {
+ case 'A': // read sector id
+ if(*p_data=='\x0D') eswrite(&p_sids[cmd[1]*12],12);
+ if(p_out)fprintf(p_out,"[fdc emu] exec 0x%02X [%s]\n",cmd[0],fdc_name(cmd[0]));
+ return;
+ case 'R': // read sector data
+ if(*p_data=='\x0D') eswrite(&p_sect[(uint16_t)(cmd[1])<<10],1024);
+ if(p_out)fprintf(p_out,"[fdc emu] exec 0x%02X [%s]\n",cmd[0],fdc_name(cmd[0]));
+ return;
+ case 'S': // find sector
+ strcpy(ret,"40000000"); // fail
+ for(n=0;n<80;n++) {
+ if(memcmp(&p_sids[n*12],p_data,12)==0) {
+ fdc_ok(ret,n); // success
+ break;
+ }
+ }
+ break;
+ case 'B': case 'C': // write sector id
+ memcpy(&p_sids[cmd[1]*12],p_data,12);
+ fdc_ok(ret,cmd[1]);
+ break;
+ case 'W': case 'X': // write sector data
+ memcpy(&p_sect[(uint16_t)(cmd[1])<<10],p_data,1024);
+ fdc_ok(ret,cmd[1]);
+ break;
+ }
+ if(p_out)fprintf(p_out,"[fdc emu] exec 0x%02X [%s] resp: %s\n",cmd[0],fdc_name(cmd[0]),ret);
+ eswrite(ret,8);
+}
+
+// stop emulator
+void emulate_stop() {
+ stop=true;
+}
+
+// ctrl^c handler
+static void sigint(int z) {
+ emulate_stop();
+}
+
+// start emulator
+void emulate(char *device,uint8_t *p_sect_data,uint8_t *p_sids_data,FILE *verbose) {
+ uint8_t byte;
+ uint8_t state,csum;
+ uint16_t count;
+ uint8_t buf[1028],*p_buf;
+ uint8_t rx[1024],*p_rx;
+ char fmt[]="9600,N,8,1";
+ p_sect=p_sect_data;
+ p_sids=p_sids_data;
+ p_out=verbose;
+ if(sopen(device)) {
+ if(p_out)fprintf(p_out,"serial port open\n");
+ if(!sconfig(fmt)) {
+ if(p_out)fprintf(p_out,"unable to configure serial port - ignoring\n");
+ }
+ if(p_out)fprintf(p_out,"serial port listening... (ctrl)^C/SIGINT to stop\n");
+ // listen for ctrl^C
+ stop=false;
+ signal(SIGINT,sigint);
+ while(!stop) {
+ if(sread(&byte,1)==1) {
+ switch(state) {
+ case 0:
+ if(byte=='Z') state=1;
+ else if(byte!=0x00&&strchr("FGARSBCWX",byte)!=NULL) {
+ p_buf=buf;
+ *p_buf++=csum=byte;
+ buf[1]=buf[2]=0xFF;
+ state=6;
+ } else if(byte!=0x0D) { // ignore blank commands
+ if(p_out)fprintf(p_out,"[general] recv 0x%02X bad/unsupported command\n",byte);
+ }
+ break;
+ case 1: // opmode second preamble
+ if(byte=='Z') state=2;
+ else if(p_out)fprintf(p_out,"[op mode] recv 0x%02X expected preamble 0x5A\n",byte);
+ break;
+ case 2: // opmode block format
+ p_buf=buf;
+ *p_buf++=csum=byte;
+ state=3;
+ break;
+ case 3: // opmode length of data block
+ state=(count=byte)?4:5;
+ csum+=byte;
+ break;
+ case 4: // opmode data block (ignored)
+ csum+=byte;
+ if(--count==0) state=5;
+ break;
+ case 5: // opmode checksum
+ if((csum^0xFF)==byte) exec_op(buf);
+ else if(p_out)fprintf(p_out,"[op mode] recv 0x%02X expected checksum 0x%02X\n",byte,csum^0xFF);
+ state=0;
+ break;
+ case 6: // fdc params
+ if(byte=='\x0D'||byte=='\x0A') {
+ if(*p_buf==0xFF) p_buf--;
+ count=exec_fdc(buf);
+ state=count?7:0;
+ p_buf=&buf[4];
+ } else if(byte==',') {
+ if(++p_buf>&buf[3]) {
+ if(p_out)fprintf(p_out,"[fdc emu] recv too many parameters\n");
+ state=0;
+ }
+ } else if(byte>='0'&&byte<='9') {
+ if(*p_buf==0xFF)*p_buf=0;
+ *p_buf*=10;
+ *p_buf+=byte-'0';
+ } else if(byte!=' ') {
+ if(p_out)fprintf(p_out,"[fdc emu] recv 0x%02X expected parameter data\n",byte);
+ state=0;
+ }
+ break;
+ case 7: // fdc data
+ *p_buf++=byte;
+ if(--count==0) {
+ exec_fdc_data(buf);
+ state=0;
+ }
+ break;
+ }
+ }
+ }
+ if(sclose()) {
+ if(p_out)fprintf(p_out,"serial port closed\n");
+ } else {
+ if(p_out)fprintf(p_out,"unable to close serial port\n");
+ }
+ } else {
+ if(p_out)fprintf(p_out,"unable to open serial port\n");
+ }
+}
View
103 lib/src/fileio.c
@@ -0,0 +1,103 @@
+// Source file for disk image file I/O
+//
+// Supports proprietary disk image format and
+// Steve Conclin's floppy emulator data folders
+//
+// senseitg@gmail.com 2012-May-22
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdio.h>
+#include "fileio.h"
+
+// write out a disk image or floppy emulator folder
+bool disk_write(char *path,uint8_t *data, uint8_t *sids) {
+ uint8_t n;
+ char fn[256];
+ uint32_t temp;
+ FILE *f;
+ temp=strlen(path)-1;
+ if(path[temp]==PATH_SEPARATOR) {
+ //folder
+ path[temp+3]=0;
+ for(n=0;n<80;n++) {
+ path[temp+1]='0'+(n/10);
+ path[temp+2]='0'+(n%10);
+ //read id
+ strcpy(fn,path);
+ strcat(fn,".id");
+ f=fopen(fn,"wb");
+ if(f) {
+ fwrite(&sids[n*12],1,12,f);
+ fclose(f);
+ } else break;
+ //read sector
+ strcpy(fn,path);
+ strcat(fn,".dat");
+ f=fopen(fn,"wb");
+ if(f) {
+ fwrite(&data[n<<10],1,1024,f);
+ fclose(f);
+ } else break;
+ }
+ if(n==80) return true;
+ } else {
+ f=fopen(path,"wb");
+ if(f) {
+ fwrite(data,1,81920,f);
+ fwrite(sids,1,940,f);
+ return true;
+ }
+ }
+ return false;
+}
+
+// read in a disk image or floppy emulator folder
+bool disk_read(char *path,uint8_t *data,uint8_t *sids) {
+ uint8_t n;
+ uint32_t ptn_id;
+ char fn[256];
+ uint32_t temp;
+ FILE *f;
+ temp=strlen(path)-1;
+ if(path[temp]==PATH_SEPARATOR) {
+ //folder
+ path[temp+3]=0;
+ for(n=0;n<80;n++) {
+ path[temp+1]='0'+(n/10);
+ path[temp+2]='0'+(n%10);
+ //read id
+ strcpy(fn,path);
+ strcat(fn,".id");
+ f=fopen(fn,"rb");
+ if(f) {
+ fread(&sids[n*12],1,12,f);
+ fclose(f);
+ } else break;
+ //read sector
+ strcpy(fn,path);
+ strcat(fn,".dat");
+ f=fopen(fn,"rb");
+ if(f) {
+ fread(&data[n<<10],1,1024,f);
+ fclose(f);
+ } else break;
+ }
+ if(n==80) return true;
+ } else {
+ f=fopen(path,"rb");
+ if(f) {
+ fseek(f,0,SEEK_END);
+ if(ftell(f)==81920+960) {
+ fseek(f,0,0);
+ fread(data,1,81920,f);
+ fread(sids,1,940,f);
+ fclose(f);
+ return true;
+ }
+ fclose(f);
+ }
+ }
+ return false;
+}
View
52 lib/src/image.c
@@ -0,0 +1,52 @@
+// Source file for image management functions
+//
+// Currently supports only proprietary RAW format
+//
+// senseitg@gmail.com 2012-May-22
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "image.h"
+
+// allocate memory for image
+uint8_t *image_alloc(uint16_t w,uint16_t h) {
+ return (uint8_t*)malloc(w*h);
+}
+
+// get pixel from image
+bool image_sample(uint8_t *p_image,uint16_t w,uint16_t x,uint16_t y) {
+ return p_image[y*w+x]<0x80;
+}
+
+// set pixel in image
+void image_pset(uint8_t *p_image,uint16_t w,uint16_t x,uint16_t y,bool p) {
+ p_image[y*w+x]=p?0x00:0xFF;
+}
+
+// read image file
+uint8_t *image_read(FILE *f,uint16_t *w,uint16_t *h) {
+ uint8_t *p_img;
+ size_t bytes;
+ uint8_t temp[4];
+ // get fle size
+ fseek(f,0,SEEK_END);
+ bytes=ftell(f);
+ fseek(f,0,0);
+ // ensure header
+ if(bytes<4) return NULL;
+ // read header - 4 bytes
+ fread(temp,1,4,f);
+ // get size
+ *w=(temp[0]<<8)|temp[1];
+ *h=(temp[2]<<8)|temp[3];
+ // ensure valid size
+ if(*w==0||*h==0) return NULL;
+ // ensure correct size
+ if(bytes!=4+*w**h) return NULL;
+ // read picture - w*h bytes: top-down, left-right, 8bpp grayscale
+ p_img=image_alloc(*w,*h);
+ fread(p_img,1,*w**h,f);
+ return p_img;
+}
View
355 lib/src/m_kh940.c
@@ -0,0 +1,355 @@
+// Source file for the Brother Electroknit KH-940 machine specifics
+//
+// senseitg@gmail.com 2012-May-22
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include "machine.h"
+#include "util.h"
+#include "image.h"
+
+static uint8_t *p_data; //81920
+static uint8_t *p_sids; //960
+static uint8_t *p_track;
+
+static char name[]="Brother Electroknit KH-940";
+
+// return nof bytes used (excl header) by currently loaded pattern
+static uint16_t get_size(ptndesc_t *p_desc) {
+ return ((p_desc->height+1)>>1)+(((((p_desc->width+3)>>2)*p_desc->height)+1)>>1);
+}
+
+// decode header and store in global ptn
+// return pattern number if successful
+static bool decode_header(ptndesc_t *p_desc,uint8_t index) {
+ uint8_t *p_hdr=&p_track[index*7];
+ if(p_hdr[0]!=0x55) {
+ p_desc->location = 0x7FFF-(index*7);
+ p_desc->pattern =p_hdr[0]<<8;
+ p_desc->pattern|=p_hdr[1];
+ p_desc->height =MSN(p_hdr[2])*100;
+ p_desc->height+=LSN(p_hdr[2])*10;
+ p_desc->height+=MSN(p_hdr[3])*1;
+ p_desc->width =LSN(p_hdr[3])*100;
+ p_desc->width +=MSN(p_hdr[4])*10;
+ p_desc->width +=LSN(p_hdr[4])*1;
+ if(MSN(p_hdr[5])!=0) return false;
+ p_desc->id =LSN(p_hdr[5])*100;
+ p_desc->id+=MSN(p_hdr[6])*10;
+ p_desc->id+=LSN(p_hdr[6])*1;
+ if(p_desc->id==0) return false;
+ if(p_desc->id>=901&&p_desc->id<=998) return true;
+ }
+ return false;
+}
+
+// decode a pattern from memory
+// caller must make sure p_image can hold image by using image_alloc
+static void decode_pattern(ptndesc_t *p_desc,uint8_t *p_image) {
+ uint32_t stride,memo,nptr;
+ uint32_t x,y;
+
+ stride=(p_desc->width+3)>>2; // nof nibbles per row
+ memo=(p_desc->height+1)&~1; // nof nibbles for memo
+
+ // calculate nibble pinter
+ nptr = 0xFFFF-(p_desc->pattern<<1)-(memo+stride*(p_desc->height-1));
+
+ // decode pattern
+ for(y=0;y<p_desc->height;y++) {
+ for(x=0;x<p_desc->width;x++) {
+ image_pset(p_image,p_desc->width,x,y,(nib_get(p_track,nptr-(x>>2))&(1<<(x&3))));
+ }
+ nptr+=stride;
+ }
+}
+
+// format memory
+static void format() {
+ uint8_t n;
+ uint8_t *p_trk;
+ // set sector ids
+ for(n=0;n<80;n++){
+ memset(&p_sids[n*12],0,12);
+ p_sids[n*12]=n<64?n/32:0;
+ }
+ for(n=0;n<2;n++) {
+ p_trk=&p_data[n<<15];
+ // clear data
+ memset(p_trk,0x55,32768-256);
+ memset(&p_trk[0x7F00],0x00,256);
+ p_trk[0x0005]=0x09; // pattern 901 partial header data
+ p_trk[0x0006]=0x01;
+ p_trk[0x7F00]=0x01; // next offset
+ p_trk[0x7F01]=0x20;
+ p_trk[0x7F10]=0x7F; // unknown pointer
+ p_trk[0x7F11]=0xF9;
+ p_trk[0x7FEA]=0x10; // 1xxx BCD (000=no pattern)
+ }
+}
+
+// set current track
+static void set_track(uint8_t track) {
+ p_track=&p_data[(track)<<15];
+}
+
+// get current track
+static uint8_t get_track() {
+ return (p_track-p_data)>>15;
+}
+
+// return true if machine can handle this size pattern
+bool size_check(uint16_t w,uint16_t h) {
+ // TODO: return w<?&&h<?;
+ return true;
+}
+
+// add pattern to memory
+// p_image must have width*height bytes
+static uint16_t add_pattern(uint8_t *p_image,uint16_t w,uint16_t h) {
+ uint8_t n;
+ uint16_t ptn_id;
+ uint32_t temp;
+ uint16_t x,y;
+ uint8_t *p_memory;
+ uint16_t b_stride;
+ uint32_t bp_last,bp_line,bp_this;
+ uint16_t memo_bytes,data_bytes;
+ uint16_t o_bottom;
+ uint8_t hdr_index;
+ ptndesc_t desc;
+
+ // find index&id
+ ptn_id=901;
+ hdr_index=0;
+ for(n=0;n<98;n++) {
+ if(decode_header(&desc,n)) {
+ hdr_index=n+1;
+ if(desc.id>=ptn_id) ptn_id=desc.id+1;
+ }
+ }
+
+ // remember bottom offset
+ o_bottom=int_get(p_track,0x7F00);
+
+ // calculate bytes needed to store pattern
+ memo_bytes=(h+1)>>1;
+ data_bytes=((((w+3)>>2)*h)+1)>>1;
+
+ // Check memory availability (should be 0x2AE, but leave some to be sure)
+ if(0x7FFF-(o_bottom+memo_bytes+data_bytes)>=0x02B0&&ptn_id<999) {
+
+ // make memo data
+ p_memory=(uint8_t*)malloc(memo_bytes);
+ memset(p_memory,0,memo_bytes);
+ // set memo data
+ memset(p_memory,0,memo_bytes);
+ // insert into memory @ PATTERN_PTR1
+ memcpy(&p_track[0x8000-int_get(p_track,0x7F00)-memo_bytes],p_memory,memo_bytes);
+ // update PATTERN_PTR1
+ int_set(p_track,0x7F00,int_get(p_track,0x7F00)+memo_bytes);
+ // free memo data
+ free(p_memory);
+
+ // make pattern data
+ p_memory=(uint8_t*)malloc(data_bytes);
+ memset(p_memory,0,data_bytes);
+ // stride (bits per row)
+ b_stride=(w+3)&~3;
+ // calculate index of last bit in pattern
+ bp_last=(data_bytes<<3)-1;
+ // set pattern data
+ for(y=0;y<h;y++) {
+ // calculate index of last bit on line
+ bp_line=bp_last-b_stride*(h-y-1);
+ for(x=0;x<w;x++) {
+ // calculate index of the current bit
+ bp_this=bp_line-x;
+ // sample image
+ if(image_sample(p_image,w,x,y)) bit_set(p_memory,bp_this);
+ }
+ }
+ // insert into memory @ PATTERN_PTR1
+ memcpy(&p_track[0x8000-int_get(p_track,0x7F00)-data_bytes],p_memory,data_bytes);
+ // update PATTERN_PTR1
+ int_set(p_track,0x7F00,int_get(p_track,0x7F00)+data_bytes);
+ // free pattern data
+ free(p_memory);
+
+ // write header
+ p_track[hdr_index*7+0]=o_bottom>>8; // byte 0
+ p_track[hdr_index*7+1]=o_bottom&0x00FF; // byte 1
+ nib_set(p_track,hdr_index*14+ 4,bcd_get(h,100)); // byte 2
+ nib_set(p_track,hdr_index*14+ 5,bcd_get(h, 10));
+ nib_set(p_track,hdr_index*14+ 6,bcd_get(h, 1)); // byte 3
+ nib_set(p_track,hdr_index*14+ 7,bcd_get(w,100));
+ nib_set(p_track,hdr_index*14+ 8,bcd_get(w, 10)); // byte 4
+ nib_set(p_track,hdr_index*14+ 9,bcd_get(w, 1));
+ nib_set(p_track,hdr_index*14+10,0); // byte 5
+ nib_set(p_track,hdr_index*14+11,bcd_get(ptn_id,100));
+ nib_set(p_track,hdr_index*14+12,bcd_get(ptn_id, 10)); // byte 6
+ nib_set(p_track,hdr_index*14+13,bcd_get(ptn_id, 1));
+
+ // write FINHDR
+ hdr_index++;
+ nib_set(p_track,hdr_index*14+10,0); // byte 5
+ nib_set(p_track,hdr_index*14+11,bcd_get(ptn_id+1,100));
+ nib_set(p_track,hdr_index*14+12,bcd_get(ptn_id+1, 10)); // byte 6
+ nib_set(p_track,hdr_index*14+13,bcd_get(ptn_id+1, 1));
+
+ // write LOADED_PATTERN
+ nib_set(p_track,(0x7FEA<<1)+0,0x01);
+ nib_set(p_track,(0x7FEA<<1)+1,bcd_get(ptn_id,100));
+ nib_set(p_track,(0x7FEA<<1)+2,bcd_get(ptn_id, 10));
+ nib_set(p_track,(0x7FEA<<1)+3,bcd_get(ptn_id, 1));
+
+ // write UNK1
+ int_set(p_track,0x7F02,0x0001);
+
+ // copy PATTERN_PTR1 to PATTERN_PTR2
+ int_set(p_track,0x7F04,int_get(p_track,0x7F00));
+
+ // write LAST_BOTTOM
+ int_set(p_track,0x7F06,o_bottom);
+
+ // write LAST_TOP
+ int_set(p_track,0x7F0A,int_get(p_track,0x7F04)-1);
+
+ // write UNK3
+ int_set(p_track,0x7F0E,0x8100);
+
+ // write HEADER_PTR
+ int_set(p_track,0x7F10,0x7FFF-(hdr_index+1)*7+1);
+
+ // return id
+ return ptn_id;
+ }
+ return 0;
+}
+
+// print out and validate non-pattern information
+static void info(FILE *output) {
+ uint16_t last_pattern,sel_pattern;
+ uint8_t n;
+ uint8_t rep[5];
+ uint16_t addr;
+ ptndesc_t desc;
+
+ // read selected pattern
+ sel_pattern = LSN(p_track[0x7FEA]) * 100
+ + MSN(p_track[0x7FEB]) * 10
+ + LSN(p_track[0x7FEB]) * 1;
+
+ // find last pattern
+ desc.location = 0x8006; // if no header is found
+ for(n=0;n<98;n++) decode_header(&desc,n);
+ last_pattern=desc.id;
+
+ // Check CONTROL_DATA
+ fprintf(output,"ADDRESS FORMAT CONTENT VALUE\n");
+ fprintf(output,"0x7F00 0x0120 write pointer : 0x%04X ",int_get(p_track,0x7F00));
+ if(int_get(p_track,0x7F00)==desc.pattern+get_size(&desc)) fprintf(output,"OK\n"); else fprintf(output,"FAIL %04X\n",desc.pattern+get_size(&desc));
+ fprintf(output,"0x7F02 0x0000 0x0001 : 0x%04X ",int_get(p_track,0x7F02));
+ if(int_get(p_track,0x7F02)==1) fprintf(output,"OK\n"); else fprintf(output,"FAIL\n");
+ fprintf(output,"0x7F04 0x0000 write pointer : 0x%04X ",int_get(p_track,0x7F04));
+ if(int_get(p_track,0x7F04)==desc.pattern+get_size(&desc)) fprintf(output,"OK\n"); else fprintf(output,"FAIL %04X\n",desc.pattern+get_size(&desc));
+ fprintf(output,"0x7F06 0x0000 loaded tail : 0x%04X ",int_get(p_track,0x7F06));
+ if(int_get(p_track,0x7F06)==desc.pattern) fprintf(output,"OK\n"); else fprintf(output,"FAIL %04X\n",desc.pattern);
+ fprintf(output,"0x7F08 0x0000 0x0000 : 0x%04X ",int_get(p_track,0x7F08));
+ if(int_get(p_track,0x7F08)==0) fprintf(output,"OK\n"); else fprintf(output,"FAIL\n");
+ fprintf(output,"0x7F0A 0x0000 loaded head : 0x%04X ",int_get(p_track,0x7F0A));
+ if(int_get(p_track,0x7F0A)==desc.pattern+get_size(&desc)-1) fprintf(output,"OK\n"); else fprintf(output,"FAIL %04X\n",desc.pattern+get_size(&desc)-1);
+ fprintf(output,"0x7F0C 0x00000000 0x00008100 : 0x%04X%04X ",int_get(p_track,0x7F0C),int_get(p_track,0x7F0E));
+ if(int_get(p_track,0x7F0C)==0&&int_get(p_track,0x7F0E)==0x8100) fprintf(output,"OK\n"); else fprintf(output,"FAIL\n");
+ fprintf(output,"0x7F10 0x7FF9 pointer 1 : 0x%04X ",int_get(p_track,0x7F10));
+ if(int_get(p_track,0x7F10)==desc.location-13) fprintf(output,"OK\n"); else fprintf(output,"FAIL %04X\n",desc.location-13);
+ fprintf(output,"0x7F12 0x0000 pointer 2 : 0x%04X ",int_get(p_track,0x7F12));
+ fprintf(output,"?\n");
+ fprintf(output,"0x7F12 0x000000 0x000000 : 0x%04X%02X ",int_get(p_track,0x7F14),p_track[0x7F16]);
+ if(int_get(p_track,0x7F14)==0&&p_track[0x7F16]==0) fprintf(output,"OK\n"); else fprintf(output,"FAIL\n");
+ fprintf(output,"0x7FEA 0x1000 loaded pattern: 0x%04X ",int_get(p_track,0x7FEA));
+ if(last_pattern==sel_pattern&&MSN(p_track[0x7FEA])==1) fprintf(output,"OK\n"); else fprintf(output,"FAIL\n");
+ fprintf(output,"0x7FFF 0x00 unknown : 0x%02X ", p_track[0x7FFF]);
+ fprintf(output,"?\n");
+
+ fprintf(output,"\n");
+
+ // Check AREA0
+ rep[0]=p_track[0x7EE0];
+ for(addr=0x7EE0;addr<0x7EE0+7;addr++) if(p_track[addr]!=rep[0]) break;
+ if(addr==0x7EE0+7&&((rep[0]==0xAA)||(rep[0]==0x55))) {
+ fprintf(output,"AREA0 0x%02X * 7 OK\n",rep[0]);
+ } else {
+ fprintf(output,"AREA0 FAIL\n");
+ print_hex(output,&p_track[0x7FE0],7);
+ }
+
+ // Just print AREA1 - don't know how to check
+ fprintf(output,"AREA1 ");
+ print_slim_hex(output,&p_track[0x7EE7],25);
+ fprintf(output,"\n");
+
+ // Just print AREA2 - don't know how to check
+ fprintf(output,"AREA2 ");
+ print_slim_hex(output,&p_track[0x7F17],25);
+ fprintf(output,"\n");
+
+ // Check AREA3
+ for(addr=0x7F30;addr<0x7F30+186;addr++) if(p_track[addr]!=00) break;
+ if(addr==0x7F30+186) {
+ fprintf(output,"AREA3 0x00 * 186 OK\n",rep[0]);
+ } else {
+ fprintf(output,"AREA3 FAIL\n");
+ print_hex(output,&p_track[0x7FE0],7);
+ }
+
+ // Check AREA4
+ for(addr=0x7FEC;addr<0x7FEC+19;addr++) if(p_track[addr]!=00) break;
+ if(addr==0x7FEC+19&&((rep[0]==0xAA)||(rep[0]==0x55))) {
+ fprintf(output,"AREA4 0x00 * 19 OK\n",rep[0]);
+ } else {
+ fprintf(output,"AREA4 FAIL\n");
+ print_hex(output,&p_track[0x7FEC],19);
+ }
+
+ fprintf(output,"\n");
+
+ // Check FINHDR
+ fprintf(output,"FINHDR ");
+ print_slim_hex(output,&p_track[0x7FFF-desc.location+7],7);
+ for(n=0;n<5;n++) {
+ if(p_track[0x7FFF-desc.location+7+n]!=0x55) break;
+ }
+ if(n==5
+ && MSN(p_track[0x7FFF-desc.location+7+5])==0
+ && LSN(p_track[0x7FFF-desc.location+7+5])==(((desc.id+1)/100)%10)
+ && MSN(p_track[0x7FFF-desc.location+7+6])==(((desc.id+1)/ 10)%10)
+ && LSN(p_track[0x7FFF-desc.location+7+6])==(((desc.id+1)/ 1)%10)) {
+ fprintf(output," OK\n");
+ } else {
+ fprintf(output," FAIL\n");
+ }
+}
+
+// init and set up descriptor for kh940 machine
+void kh940_init(machine_t *p_machine,uint8_t *p_disk_data,uint8_t *p_disk_sids) {
+ p_data=p_disk_data;
+ p_sids=p_disk_sids;
+ p_track=p_data;
+ p_machine->name=name;
+ p_machine->memory_used=get_size;
+ p_machine->decode_header=decode_header;
+ p_machine->decode_pattern=decode_pattern;
+ p_machine->format=format;
+ p_machine->set_track=set_track;
+ p_machine->get_track=get_track;
+ p_machine->size_check=size_check;
+ p_machine->add_pattern=add_pattern;
+ p_machine->info=info;
+ p_machine->pattern_min=901;
+ p_machine->pattern_max=998;
+ p_machine->track_count=2;
+}
View
51 lib/src/machine.c
@@ -0,0 +1,51 @@
+// Source file for machine definitions
+//
+// Currently supports only Brother Electroknit KH-940
+//
+// senseitg@gmail.com 2012-May-22
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include "machine.h"
+#include "emulate.h"
+#include "fileio.h"
+
+#define MACHINE_COUNT 1
+
+static machine_t mach[MACHINE_COUNT];
+static machine_t *p_mach=mach;
+static uint8_t data[81920];
+static uint8_t sids[960];
+
+// Initialize machine and add it to list of machines
+static void machine_add(const char *p_code,void(*fp_init)(machine_t*,uint8_t*,uint8_t*)) {
+ strcpy(p_mach->code,p_code);
+ fp_init(p_mach++,data,sids);
+}
+
+// Initialize all machines
+void machine_init() {
+ machine_add("kh940",kh940_init);
+}
+
+// Retrieve machine descriptor
+machine_t *machine_get(uint8_t index) {
+ if(index<MACHINE_COUNT)return &mach[index];
+ return NULL;
+}
+
+// Load machine memory from disk
+bool machine_load(char *path) {
+ return disk_read(path,data,sids);
+}
+
+// Save machine memory to disk
+bool machine_save(char *path) {
+ return disk_write(path,data,sids);
+}
+
+// Start the emulator
+void machine_emulate(char *device,FILE *verbose) {
+ emulate(device,data,sids,verbose);
+}
View
177 lib/src/serial.c
@@ -0,0 +1,177 @@
+// Source file for platform independant serial communications
+//
+// Currently supports windows and posix(unix, mac)
+//
+// senseitg@gmail.com 2012-May-22
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include "serial.h"
+
+#ifdef _WIN32
+
+#include <stdio.h>
+#include <windows.h>
+
+static HANDLE h_serial;
+static COMMTIMEOUTS restore;
+
+// windows - open serial port
+// device has form "COMn"
+bool sopen(char* device) {
+ h_serial=CreateFile(device,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,0);
+ return h_serial!=INVALID_HANDLE_VALUE;
+}
+
+// windows - configure serial port
+bool sconfig(char* fmt) {
+ DCB dcb;
+ COMMTIMEOUTS cmt;
+ // clear dcb
+ memset(&dcb,0,sizeof(DCB));
+ dcb.DCBlength=sizeof(DCB);
+ // configure serial parameters
+ if(!BuildCommDCB(fmt,&dcb)) return false;
+ dcb.fOutxCtsFlow=0;
+ dcb.fOutxDsrFlow=0;
+ dcb.fDtrControl=0;
+ dcb.fOutX=0;
+ dcb.fInX=0;
+ dcb.fRtsControl=0;
+ if(!SetCommState(h_serial,&dcb)) return false;
+ // configure buffers
+ if(!SetupComm(h_serial,1024,1024)) return false;
+ // configure timeouts
+ GetCommTimeouts(h_serial,&cmt);
+ memcpy(&restore,&cmt,sizeof(cmt));
+ cmt.ReadIntervalTimeout=100;
+ cmt.ReadTotalTimeoutMultiplier=100;
+ cmt.ReadTotalTimeoutConstant=100;
+ cmt.WriteTotalTimeoutConstant=100;
+ cmt.WriteTotalTimeoutMultiplier=100;
+ if(!SetCommTimeouts(h_serial,&cmt)) return false;
+ return true;
+}
+
+// windows - read from serial port
+int32_t sread(void *p_read,uint16_t i_read) {
+ DWORD i_actual=0;
+ if(!ReadFile(h_serial,p_read,i_read,&i_actual,NULL)) return -1;
+ return (int32_t)i_actual;
+}
+
+// windows - write to serial port
+int32_t swrite(void* p_write,uint16_t i_write) {
+ DWORD i_actual=0;
+ if(!WriteFile(h_serial,p_write,i_write,&i_actual,NULL)) return -1;
+ return (int32_t)i_actual;
+}
+
+// windows - close serial port
+bool sclose() {
+ // politeness: restore (some) original configuration
+ SetCommTimeouts(h_serial,&restore);
+ return CloseHandle(h_serial)!=0;
+}
+
+#else
+
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+static int h_serial;
+static struct termios restore;
+
+// posix - open serial port
+// device has form "/dev/ttySn"
+bool sopen(char* device) {
+ h_serial=open(device,O_RDWR|O_NOCTTY|O_NDELAY|O_NONBLOCK);
+ return h_serial>=0;
+}
+
+// posix - configure serial port
+bool sconfig(char* fmt) {
+ struct termios options;
+ char* argv[4];
+ unsigned char argc;
+ char* p_parse;
+ // quick and dirty parser
+ p_parse=fmt;
+ argc=1;
+ argv[0]=fmt;
+ while(*p_parse!=0) {
+ if(*p_parse==',') {
+ *p_parse=0;
+ if(argc>3) return false;
+ argv[argc++]=++p_parse;
+ } else p_parse++;
+ }
+ // get current settings
+ tcgetattr(h_serial,&options);
+ memcpy(&restore,&options,sizeof(options));
+ // configure baudrate
+ switch(atoi(argv[0])) {
+ case 1200: cfsetispeed(&options, B1200); cfsetospeed(&options, B1200); break;
+ case 2400: cfsetispeed(&options, B2400); cfsetospeed(&options, B2400); break;
+ case 4800: cfsetispeed(&options, B4800); cfsetospeed(&options, B4800); break;
+ case 9600: cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); break;
+ case 19200: cfsetispeed(&options, B19200); cfsetospeed(&options, B19200); break;
+ case 38400: cfsetispeed(&options, B38400); cfsetospeed(&options, B38400); break;