From 3a429d84c0e38b4a33834400aa0213082b634d55 Mon Sep 17 00:00:00 2001 From: Daniel Jones Date: Wed, 8 Jul 2020 23:15:20 +0930 Subject: Made gate updating work on separate thread, added 3 way NAND gate Becuase of a recursion and stack overflow problem when looping objects i've moved the object updating to a separate thread, and it no longer does recursion. I also added a 3 input NAND gate and paved the work to add more 3 input gates. --- Gate.cpp | 33 ++++---- Gate.h | 4 + MainWindow.cpp | 197 +++++++++++++++++++++++++++++++++++++------- MainWindow.h | 35 ++++++++ examples/test_all_gates.xml | 33 +++++--- icons.h | 20 +++++ icons/3NAND.xcf | Bin 0 -> 3918 bytes icons/NAND3_icon_data.gif | Bin 0 -> 245 bytes 8 files changed, 266 insertions(+), 56 deletions(-) create mode 100644 icons/3NAND.xcf create mode 100644 icons/NAND3_icon_data.gif diff --git a/Gate.cpp b/Gate.cpp index 0e206d0..33313f3 100644 --- a/Gate.cpp +++ b/Gate.cpp @@ -23,6 +23,7 @@ Gate::Gate(GATE_TYPE type, int x, int y, int width, int height, int loaded_id) this->gate_type = type; this->input_gate1 = nullptr; this->input_gate2 = nullptr; + this->input_gate3 = nullptr; //this->output_gate = nullptr; /* special handing of id - if the gate is loaded from file the loaded_id will be set and we use that */ @@ -53,6 +54,13 @@ Gate::remove_input_object(int id) if (input_gate2->get_id() == id) input_gate2 = nullptr; } + + if (input_gate3) + { + if (input_gate3->get_id() == id) + input_gate3 = nullptr; + } + } void Gate::update_state() @@ -110,6 +118,12 @@ void Gate::update_state() break; } + case NAND3: + { + if (!input_gate1 || !input_gate2 || !input_gate3) { output_state = false; return; } + output_state = !(input_gate1->get_output_state() && input_gate2->get_output_state() && input_gate3->get_output_state()); + break; + } case NOR: { @@ -149,53 +163,42 @@ Gate::get_output_type_text() { return "INPUT"; } - case OUTPUT: { return "OUTPUT"; } - case AND: { return "AND"; } - - case OR: { return "OR"; } - - case NOT: { return "NOT"; } - - case NAND: { return "NAND"; } - - + case NAND3: + { + return "3NAND"; + } case NOR: { return "NOR"; } - - case XOR: { return "XOR"; } - - case XNOR: { return "XNOR"; } - default: return "?"; } diff --git a/Gate.h b/Gate.h index b960978..6519791 100644 --- a/Gate.h +++ b/Gate.h @@ -35,6 +35,7 @@ class Gate : public Object NOR, XOR, XNOR, + NAND3, }; Gate(GATE_TYPE type = INPUT, int x = 0, int y = 0, int width = 70, int height = 50, int id = -1); @@ -44,8 +45,10 @@ class Gate : public Object GATE_TYPE get_gate_type() { return this->gate_type; }; Object *get_input_gate1() { return this->input_gate1; }; Object *get_input_gate2() { return this->input_gate2; }; + Object *get_input_gate3() { return this->input_gate3; }; void set_input_gate1(Object *gate) { this->input_gate1 = gate; }; void set_input_gate2(Object *gate) { this->input_gate2 = gate; }; + void set_input_gate3(Object *gate) { this->input_gate3 = gate; }; void remove_input_object(int id) override; void update_state() override; @@ -57,6 +60,7 @@ class Gate : public Object /* inputs/outputs */ Object *input_gate1; Object *input_gate2; + Object *input_gate3; std::string get_output_type_text(); diff --git a/MainWindow.cpp b/MainWindow.cpp index 9de495a..72a53d7 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -33,6 +33,7 @@ FXDEFMAP(MainWindow) MainWindow_Map[]= FXMAPFUNC(SEL_COMMAND, MainWindow::ID_BUTTON_OUTPUT, MainWindow::output_button_press), FXMAPFUNC(SEL_COMMAND, MainWindow::ID_BUTTON_AND, MainWindow::and_button_press), FXMAPFUNC(SEL_COMMAND, MainWindow::ID_BUTTON_NAND, MainWindow::nand_button_press), + FXMAPFUNC(SEL_COMMAND, MainWindow::ID_BUTTON_3NAND, MainWindow::nand3_button_press), FXMAPFUNC(SEL_COMMAND, MainWindow::ID_BUTTON_OR, MainWindow::or_button_press), FXMAPFUNC(SEL_COMMAND, MainWindow::ID_BUTTON_NOR, MainWindow::nor_button_press), FXMAPFUNC(SEL_COMMAND, MainWindow::ID_BUTTON_XOR, MainWindow::xor_button_press), @@ -43,6 +44,9 @@ FXDEFMAP(MainWindow) MainWindow_Map[]= /* options */ FXMAPFUNC(SEL_COMMAND, MainWindow::ID_BUTTON_SAVE, MainWindow::save_button_press), FXMAPFUNC(SEL_COMMAND, MainWindow::ID_BUTTON_LOAD, MainWindow::load_button_press), + + FXMAPFUNC(SEL_IO_READ, MainWindow::ID_UPDATE_OBJECTS, MainWindow::update_objects), + }; FXIMPLEMENT(MainWindow, FXMainWindow, MainWindow_Map, ARRAYNUMBER(MainWindow_Map)) @@ -51,10 +55,15 @@ MainWindow::MainWindow(FXApp *a) { app = a; create_ui(); + sig = new FXGUISignal(app, this, ID_UPDATE_OBJECTS); + update_thread = new Thread(this, sig); + update_thread->start(); } MainWindow::~MainWindow() { + delete update_thread; + delete sig; } void @@ -65,6 +74,7 @@ MainWindow::create() OUTPUT_icon->create(); AND_icon->create(); NAND_icon->create(); + NAND3_icon->create(); OR_icon->create(); NOR_icon->create(); XOR_icon->create(); @@ -73,6 +83,7 @@ MainWindow::create() BinaryDisplay_icon->create(); canvas_image->create(); show(PLACEMENT_SCREEN); + ready_to_draw = true; } void @@ -86,6 +97,7 @@ MainWindow::create_ui() OUTPUT_icon = new FXGIFIcon(app, OUTPUT_icon_data, IMAGE_KEEP); AND_icon = new FXGIFIcon(app, AND_icon_data, IMAGE_KEEP); NAND_icon = new FXGIFIcon(app, NAND_icon_data, IMAGE_KEEP); + NAND3_icon = new FXGIFIcon(app, NAND3_icon_data, IMAGE_KEEP); OR_icon = new FXGIFIcon(app, OR_icon_data, IMAGE_KEEP); NOR_icon = new FXGIFIcon(app, NOR_icon_data, IMAGE_KEEP); XOR_icon = new FXGIFIcon(app, XOR_icon_data, IMAGE_KEEP); @@ -103,6 +115,7 @@ MainWindow::create_ui() new FXButton(toolsFrame, "", OUTPUT_icon, this, MainWindow::ID_BUTTON_OUTPUT, BUTTON_NORMAL|LAYOUT_FILL_X); new FXButton(toolsFrame, "AND", AND_icon, this, MainWindow::ID_BUTTON_AND, BUTTON_NORMAL|LAYOUT_FILL_X); new FXButton(toolsFrame, "NAND", NAND_icon, this, MainWindow::ID_BUTTON_NAND, BUTTON_NORMAL|LAYOUT_FILL_X); + new FXButton(toolsFrame, "3 NAND", NAND3_icon, this, MainWindow::ID_BUTTON_3NAND, BUTTON_NORMAL|LAYOUT_FILL_X); new FXButton(toolsFrame, "OR", OR_icon, this, MainWindow::ID_BUTTON_OR, BUTTON_NORMAL|LAYOUT_FILL_X); new FXButton(toolsFrame, "NOR", NOR_icon, this, MainWindow::ID_BUTTON_NOR, BUTTON_NORMAL|LAYOUT_FILL_X); new FXButton(toolsFrame, "XOR", XOR_icon, this, MainWindow::ID_BUTTON_XOR, BUTTON_NORMAL|LAYOUT_FILL_X); @@ -157,6 +170,7 @@ MainWindow::create_ui() void MainWindow::draw() { + lock.lock(); FXDCWindow dc_image(canvas_image); dc_image.setFont(getApp()->getNormalFont()); dc_image.setForeground(FXRGB(255, 255, 255)); @@ -217,6 +231,13 @@ MainWindow::draw() break; } + case Gate::NAND3: + { + dc_image.drawIcon(NAND3_icon, gate1->get_x(), gate1->get_y()); + dc_image.drawText(gate1->get_x(), gate1->get_y()+gate1->get_height()+20, "3 Input NAND"); + break; + } + case Gate::OR: { dc_image.drawIcon(OR_icon, gate1->get_x(), gate1->get_y()); @@ -310,6 +331,7 @@ MainWindow::draw() continue; Object *in_gate1 = gate1->get_input_gate1(); Object *in_gate2 = gate1->get_input_gate2(); + Object *in_gate3 = gate1->get_input_gate3(); if (in_gate1 != nullptr) { if (in_gate1 == selected_input.object) @@ -325,6 +347,7 @@ MainWindow::draw() dc_image.setForeground(FXRGB(0, 0, 0)); } + /* nand3 gate does not need a special case for the first input */ else { dc_image.drawLine(in_gate1->get_x()+in_gate1->get_width()-5, in_gate1->get_y()+(in_gate1->get_height()/2), @@ -338,13 +361,19 @@ MainWindow::draw() { dc_image.setForeground(FXRGB(255, 0, 0)); } - if (gate1->get_gate_type() == Gate::NOT || gate1->get_gate_type() == Gate::OUTPUT) { /* NOT,OUTPUT need a special case */ continue; } + else if (gate1->get_gate_type() == Gate::NAND3) + { + /* special case for 3 input gates */ + dc_image.drawLine(in_gate2->get_x()+in_gate2->get_width()-5, in_gate2->get_y()+(in_gate2->get_height()/2), + gate1->get_x()+10, gate1->get_y()+25); + dc_image.setForeground(FXRGB(0, 0, 0)); + } else { @@ -353,6 +382,13 @@ MainWindow::draw() dc_image.setForeground(FXRGB(0, 0, 0)); } } + if (in_gate3 != nullptr) + { + /* special case for drawing third input gate */ + dc_image.drawLine(in_gate3->get_x()+in_gate3->get_width()-5, in_gate3->get_y()+(in_gate3->get_height()/2), + gate1->get_x()+10, gate1->get_y()+43); + dc_image.setForeground(FXRGB(0, 0, 0)); + } dc_image.setForeground(FXRGB(0, 0, 0)); break; } @@ -461,6 +497,7 @@ MainWindow::draw() FXDCWindow dc_canvas(canvas); dc_canvas.drawImage(canvas_image, 0, 0); + lock.unlock(); } Object @@ -503,9 +540,30 @@ Object return object; } +long +MainWindow::update_objects(FXObject*,FXSelector,void* ptr) +{ + lock.lock(); + Object *object; + for(auto o = objects.begin(); o != objects.end(); ++o) + { + object = (*o).get(); + object->update_state(); + } + if (ready_to_draw) + { + lock.unlock(); + draw(); + lock.lock(); + } + lock.unlock(); + return 1; +} + void MainWindow::update_object_state(Object *object) { + return; object->update_state(); /* update all objects that are using this object as an input */ Object *object2; @@ -520,7 +578,6 @@ MainWindow::update_object_state(Object *object) } } - void MainWindow::find_selected_input(int x, int y) { @@ -542,45 +599,64 @@ MainWindow::find_selected_input(int x, int y) case Object::GATE: { Gate *gate = (Gate*)object; - if (y-selected_object->get_y() <= selected_object->get_height()/2) - { input = 1; } else { input = 2; } - switch (input) + switch (gate->get_gate_type()) { - case 1: + case Gate::NAND3: { - if (gate->get_input_gate1() != nullptr) - { - input_object = gate->get_input_gate1(); - } + puts("nand"); + /* 3 input gates */ + int relypos = y-gate->get_y(); + if (relypos <= 16) { input = 1; input_object = gate->get_input_gate1(); } + if (relypos >16 && relypos <= 34) { input = 2; input_object = gate->get_input_gate2(); } + if (relypos >34) { input = 3; input_object = gate->get_input_gate3(); } break; } - - case 2: + default: { - if (gate->get_input_gate2() != nullptr) - { - input_object = gate->get_input_gate2(); - } - else + /* 2 or 1 input gates */ + if (y-selected_object->get_y() <= selected_object->get_height()/2) + { input = 1; } else { input = 2; } + switch (input) { - /* special check for gates with only one input */ - if (gate->get_gate_type() == Gate::NOT || gate->get_gate_type() == Gate::OUTPUT) + case 1: + { + if (gate->get_input_gate1() != nullptr) + { + input_object = gate->get_input_gate1(); + } + break; + } + + case 2: { - input_object = gate->get_input_gate1(); - input = 1; + if (gate->get_input_gate2() != nullptr) + { + input_object = gate->get_input_gate2(); + } + else + { + /* special check for gates with only one input */ + if (gate->get_gate_type() == Gate::NOT || gate->get_gate_type() == Gate::OUTPUT) + { + input_object = gate->get_input_gate1(); + input = 1; + } + } + break; } + + default: + input = -1; + break; } - break; + break; } - - default: - input = -1; - break; } break; } case Object::BINARYDISPLAY: { + puts("bdsp"); BinaryDisplay *bdsp = (BinaryDisplay*)object; int relypos = y-bdsp->get_y(); if (relypos <= 15) { input = 7; input_object = bdsp->get_input7(); } @@ -659,6 +735,8 @@ MainWindow::save_file() object_xml.append_attribute("input1_id") = gate->get_input_gate1()->get_id(); if (gate->get_input_gate2()) object_xml.append_attribute("input2_id") = gate->get_input_gate2()->get_id(); + if (gate->get_input_gate3()) + object_xml.append_attribute("input3_id") = gate->get_input_gate3()->get_id(); object_xml.append_attribute("output_state") = gate->get_output_state(); /* iterate through all output gates and write them */ @@ -817,11 +895,12 @@ MainWindow::load_file() } - /* iterate again through all gates in the xml file and set output gates if they exist */ + /* iterate again through all gates in the xml file and set input gates if they exist */ for (auto node: node_objects.children("Gate")) { int input1 = -1; int input2 = -1; + int input3 = -1; Gate *gate; if (strcmp(node.attribute("input1_id").as_string(), "") != 0) @@ -834,6 +913,12 @@ MainWindow::load_file() input2 = node.attribute("input2_id").as_int(); printf("input 2 exists: %d\n", input2); } + if (strcmp(node.attribute("input3_id").as_string(), "") != 0) + { + input3 = node.attribute("input3_id").as_int(); + printf("input 3 exists: %d\n", input2); + } + gate = (Gate*)find_object_by_id(node.attribute("id").as_int()); @@ -847,6 +932,12 @@ MainWindow::load_file() { gate->set_input_gate2(find_object_by_id(input2)); } + + if (input3 != -1) + { + gate->set_input_gate3(find_object_by_id(input3)); + } + } /* iterate again through all bdsp's in the xml file and set output objects if they exist */ @@ -964,6 +1055,12 @@ MainWindow::remove_object(Object &object) gate.get_input_gate2()->remove_output_object_id(gate.get_id()); update_object_state(gate.get_input_gate2()); } + if (gate.get_input_gate3()) + { + gate.get_input_gate3()->remove_output_object_id(gate.get_id()); + update_object_state(gate.get_input_gate3()); + } + break; } case Object::BINARYDISPLAY: @@ -1145,12 +1242,31 @@ MainWindow::on_left_mouse_up(FXObject*, FXSelector, void *ptr) if (gate && gate->get_gate_type() != Gate::INPUT) { int input = -1; - if (ev->last_y-gate->get_y() <= gate->get_height()/2) - input = 1; - else - input = 2; + switch (gate->get_gate_type()) + { + case Gate::NAND3: + { + /* case for gates with 3 inputs */ + int relypos = ev->last_y-gate->get_y(); + if (relypos <= 16) { input = 1; } + if (relypos >16 && relypos <= 34) { input = 2; } + if (relypos >34) { input = 3; } + + break; + } + default: + { + if (ev->last_y-gate->get_y() <= gate->get_height()/2) + input = 1; + else + input = 2; + break; + } + } + if (gate->get_gate_type() != Gate::NOT && gate->get_gate_type() != Gate::OUTPUT) { + // FIXME: these should be objects, cannot explicitly cast to gates printf("connecting gate %d with gate %d at input #%d\n", selected_object->get_id(), gate->get_id(), input); if (input == 1) { @@ -1160,6 +1276,10 @@ MainWindow::on_left_mouse_up(FXObject*, FXSelector, void *ptr) { gate->set_input_gate2((Gate*)selected_object); } + else if (input == 3) + { + gate->set_input_gate3((Gate*)selected_object); + } } else { @@ -1348,6 +1468,11 @@ MainWindow::on_key_release(FXObject *sender, FXSelector sel, void *ptr) gate->get_input_gate2()->remove_output_object_id(selected_object->get_id()); gate->set_input_gate2(nullptr); break; + case 3: + gate->get_input_gate3()->remove_output_object_id(selected_object->get_id()); + gate->set_input_gate3(nullptr); + break; + default: break; } break; @@ -1477,6 +1602,16 @@ MainWindow::nand_button_press(FXObject *sender, FXSelector sel, void *ptr) return 1; } +long +MainWindow::nand3_button_press(FXObject *sender, FXSelector sel, void *ptr) +{ + selected_object = nullptr; + selected_object_type = Object::GATE; + selected_gate_type = Gate::NAND3; + return 1; +} + + long MainWindow::or_button_press(FXObject *sender, FXSelector sel, void *ptr) { diff --git a/MainWindow.h b/MainWindow.h index 0902169..a443e71 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -32,6 +32,8 @@ #include "BinaryDisplay.h" #include "pugixml.hpp" // saving/loading +class Thread; + class MainWindow : public FXMainWindow { FXDECLARE(MainWindow) @@ -54,6 +56,7 @@ class MainWindow : public FXMainWindow ID_BUTTON_OUTPUT, ID_BUTTON_AND, ID_BUTTON_NAND, + ID_BUTTON_3NAND, ID_BUTTON_OR, ID_BUTTON_NOR, ID_BUTTON_XOR, @@ -63,6 +66,8 @@ class MainWindow : public FXMainWindow ID_BUTTON_SAVE, ID_BUTTON_LOAD, + + ID_UPDATE_OBJECTS, }; /* Event handlers */ @@ -78,6 +83,7 @@ class MainWindow : public FXMainWindow long output_button_press(FXObject*,FXSelector,void* ptr); long and_button_press(FXObject*,FXSelector,void* ptr); long nand_button_press(FXObject*,FXSelector,void* ptr); + long nand3_button_press(FXObject*,FXSelector,void* ptr); long or_button_press(FXObject*,FXSelector,void* ptr); long nor_button_press(FXObject*,FXSelector,void* ptr); long xor_button_press(FXObject*,FXSelector,void* ptr); @@ -94,6 +100,8 @@ class MainWindow : public FXMainWindow FXApp *get_app(){ return app; }; + long update_objects(FXObject*,FXSelector,void* ptr); + protected: MainWindow(){} @@ -147,6 +155,7 @@ class MainWindow : public FXMainWindow FXGIFIcon *OUTPUT_icon; FXGIFIcon *AND_icon; FXGIFIcon *NAND_icon; + FXGIFIcon *NAND3_icon; FXGIFIcon *OR_icon; FXGIFIcon *NOR_icon; FXGIFIcon *XOR_icon; @@ -159,6 +168,7 @@ class MainWindow : public FXMainWindow FXButton *OUTPUT_button; FXButton *AND_button; FXButton *NAND_button; + FXButton *NAND3_button; FXButton *OR_button; FXButton *NOR_button; FXButton *XOR_button; @@ -191,6 +201,31 @@ class MainWindow : public FXMainWindow /* saving/loading */ std::string file_name = ""; + /* threads */ + Thread *update_thread; + FXMutex lock; + FXGUISignal *sig; + bool ready_to_draw = false; +}; + +class Thread : public FXThread +{ + + private: + MainWindow *mw; + FXGUISignal *sig; + + public: + Thread(MainWindow *mw_, FXGUISignal *sig_) { mw = mw_; sig = sig_; }; + int run() + { + while (1) + { + FXThread::sleep(70000000); + sig->signal(); + }; + return 0; + }; }; #endif // MAINWINDOW_H diff --git a/examples/test_all_gates.xml b/examples/test_all_gates.xml index dc14c81..bbe1100 100644 --- a/examples/test_all_gates.xml +++ b/examples/test_all_gates.xml @@ -1,6 +1,6 @@ - + @@ -71,29 +71,28 @@ - - + - + - + - + - + - + - + @@ -101,7 +100,7 @@ - + @@ -110,4 +109,18 @@ + + + + + + + + + + + + + + diff --git a/icons.h b/icons.h index 43d667c..8fad1db 100644 --- a/icons.h +++ b/icons.h @@ -76,6 +76,26 @@ const unsigned char INPUT_icon_data[]={ 0x86,0x1c,0x3b,0x7a,0x9c,0x50,0x00,0x00,0x3b }; +/* created by reswrap from file icons/NAND3_icon_data.gif */ +const unsigned char NAND3_icon_data[]={ + 0x47,0x49,0x46,0x38,0x39,0x61,0x46,0x00,0x32,0x00,0x80,0x01,0x00,0x00,0x00,0x00, + 0xff,0xff,0xff,0x21,0xfe,0x26,0x4d,0x61,0x64,0x65,0x20,0x66,0x6f,0x72,0x20,0x66, + 0x6f,0x78,0x6c,0x6f,0x67,0x69,0x63,0x67,0x61,0x74,0x65,0x73,0x20,0x62,0x79,0x20, + 0x44,0x61,0x6e,0x69,0x65,0x6c,0x20,0x4a,0x6f,0x6e,0x65,0x73,0x00,0x21,0xf9,0x04, + 0x01,0x0a,0x00,0x01,0x00,0x2c,0x00,0x00,0x00,0x00,0x46,0x00,0x32,0x00,0x00,0x02, + 0xa2,0x8c,0x8f,0xa9,0xcb,0xed,0x0f,0xa3,0x9c,0xb4,0xda,0x8b,0xb3,0xde,0xbc,0xfb, + 0x0f,0x76,0xc0,0x18,0x96,0xcb,0x88,0x9a,0xaa,0x81,0x92,0x48,0x0b,0xa7,0xab,0x04, + 0x27,0xf1,0xdd,0xce,0x8e,0xfc,0xe2,0x3e,0xaf,0x7b,0xfc,0x86,0xae,0x60,0x83,0x88, + 0x34,0x32,0x90,0x4c,0xa5,0x8d,0xd9,0x74,0x06,0xa0,0x50,0x29,0xb5,0xaa,0xbc,0x62, + 0x83,0xda,0xed,0xac,0x1b,0xd5,0x81,0xc3,0xab,0xf1,0xf5,0x54,0xbc,0x98,0xcf,0x3d, + 0xe0,0x32,0xcd,0x5a,0x7b,0xa7,0xb9,0x5d,0xfd,0x20,0x9f,0xd7,0xec,0xee,0x7c,0x52, + 0xe1,0x86,0xd6,0xe7,0x87,0xe3,0x11,0x48,0x58,0xf8,0x85,0xb8,0xa7,0xb8,0x18,0x58, + 0xe2,0xf8,0x08,0x19,0x09,0x60,0x44,0x99,0xe5,0xe8,0x94,0xa9,0x89,0x28,0x45,0x97, + 0xe7,0x19,0xb7,0x16,0x8a,0x67,0x46,0xda,0xa6,0x65,0x05,0xf7,0x49,0xc6,0x39,0xf8, + 0x73,0x8a,0x5a,0x19,0xab,0x76,0x47,0x5b,0x21,0x79,0xab,0xbb,0xcb,0xdb,0xeb,0xfb, + 0xab,0x5b,0x00,0x00,0x3b + }; + /* created by reswrap from file icons/NAND_icon_data.gif */ const unsigned char NAND_icon_data[]={ 0x47,0x49,0x46,0x38,0x39,0x61,0x46,0x00,0x32,0x00,0x80,0x01,0x00,0x00,0x00,0x00, diff --git a/icons/3NAND.xcf b/icons/3NAND.xcf new file mode 100644 index 0000000..889f16e Binary files /dev/null and b/icons/3NAND.xcf differ diff --git a/icons/NAND3_icon_data.gif b/icons/NAND3_icon_data.gif new file mode 100644 index 0000000..de7eea9 Binary files /dev/null and b/icons/NAND3_icon_data.gif differ -- cgit v1.2.3