diff --git a/lib/bus/drivewire/drivewire.cpp b/lib/bus/drivewire/drivewire.cpp
index 7454e3aa1..08ec470f8 100755
--- a/lib/bus/drivewire/drivewire.cpp
+++ b/lib/bus/drivewire/drivewire.cpp
@@ -49,6 +49,55 @@ static void IRAM_ATTR drivewire_isr_handler(void *arg)
 }
 #endif
 
+/**
+ * Static callback function for the DriveWire state machine.
+ */
+#ifdef ESP_PLATFORM
+void onDriveWireStateMachineTimer(void *info)
+{
+    systemBus *parent = (systemBus *)info;
+    parent->resetState();
+}
+#endif
+
+/**
+ * Start the DriveWire state machine recovery timer.
+ */
+void systemBus::timer_start()
+{
+#ifdef ESP_PLATFORM
+    esp_timer_create_args_t tcfg;
+    tcfg.arg = this;
+    tcfg.callback = onDriveWireStateMachineTimer;
+    tcfg.dispatch_method = esp_timer_dispatch_t::ESP_TIMER_TASK;
+    tcfg.name = nullptr;
+    esp_timer_create(&tcfg, &stateMachineRecoveryTimerHandle);
+    esp_timer_start_periodic(stateMachineRecoveryTimerHandle, timerRate * 1000);
+#else
+    timerActive = true;
+    lastInterruptMs = fnSystem.millis();
+#endif
+}
+
+/**
+ * Stop the DriveWire state machine recovery timer
+ */
+void systemBus::timer_stop()
+{
+#ifdef ESP_PLATFORM
+    // Delete existing timer
+    if (stateMachineRecoveryTimerHandle != nullptr)
+    {
+        Debug_println("Deleting existing DriveWire state machine timer\n");
+        esp_timer_stop(stateMachineRecoveryTimerHandle);
+        esp_timer_delete(stateMachineRecoveryTimerHandle);
+        stateMachineRecoveryTimerHandle = nullptr;
+    }
+#else
+    timerActive = false;
+#endif
+}
+
 // Calculate 8-bit checksum
 inline uint16_t drivewire_checksum(uint8_t *buf, unsigned short len)
 {
@@ -60,11 +109,10 @@ inline uint16_t drivewire_checksum(uint8_t *buf, unsigned short len)
     return chk;
 }
 
-#ifdef ESP_PLATFORM
+#if defined(ESP_PLATFORM) && 1 == 0
 static void drivewire_intr_task(void *arg)
 {
     uint32_t gpio_num;
-    int64_t d;
 
     systemBus *bus = (systemBus *)arg;
 
@@ -89,194 +137,249 @@ static void drivewire_intr_task(void *arg)
 }
 #endif
 
-// Helper functions outside the class defintions
+// Helper functions outside the class definintions
 
 systemBus virtualDevice::get_bus() { return DRIVEWIRE; }
 
-void systemBus::op_jeff()
+
+
+void systemBus::resetState(void)
 {
-    fnDwCom.print("FUJINET");
-    Debug_println("Jeff's op");
+    dwStateMethod = &systemBus::_drivewire_process_cmd;
+    timer_stop();
 }
 
-void systemBus::op_nop()
+int systemBus::op_jeff(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
+    resetState();
+    Debug_println("OP_JEFF\n");
+    
+    return result;
 }
 
-void systemBus::op_reset()
+int systemBus::op_nop(std::vector<uint8_t> *q)
 {
-    Debug_printv("op_reset()");
+    int result = 1;
+    
+    resetState();
 
-    // When a reset transaction occurs, set the mounted disk image to the CONFIG disk image.
-    theFuji.boot_config = true;
-    theFuji.insert_boot_device(Config.get_general_boot_mode());
+    return result;
 }
 
-void systemBus::op_readex()
+int systemBus::op_reset(std::vector<uint8_t> *q)
 {
-    drivewireDisk *d = nullptr;
-    uint16_t c1 = 0, c2 = 0;
-
-    uint8_t *blk_buffer = sector_data;
-    uint16_t blk_size = MEDIA_BLOCK_SIZE;
-
-    uint8_t rc = DISK_CTRL_STATUS_CLEAR;
-
-    drive_num = fnDwCom.read();
-
-    lsn = fnDwCom.read() << 16;
-    lsn |= fnDwCom.read() << 8;
-    lsn |= fnDwCom.read();
-
-    Debug_printf("OP_READ: DRIVE %3u - SECTOR %8lu\n", drive_num, lsn);
+    int result = 1;
+    
+    Debug_printv("op_reset()");
 
-    if (theFuji.boot_config && drive_num == 0)
-        d = theFuji.bootdisk();
-    else
-        d = &theFuji.get_disks(drive_num)->disk_dev;
+    // When a reset transaction occurs, set the mounted disk image to the CONFIG disk image.
+    theFuji.boot_config = true;
+    theFuji.insert_boot_device(Config.get_general_boot_mode());
 
-    if (!d)
+    for (int i = 0; i < 16; i++)
     {
-        Debug_printv("Invalid drive #%3u", drive_num);
-        rc = 0xF6;
+        // clear all channel queues
+        fnDwCom.outgoingChannel[i].clear();
+        fnDwCom.incomingChannel[i].clear();
+        fnDwCom.incomingScreen[i].clear();
     }
+    
+    resetState();
+    
+    return result;
+}
 
-    if (rc == DISK_CTRL_STATUS_CLEAR && !d->device_active)
-    {
-        Debug_printv("Device not active.");
-        rc = 0xF6;
-    }
+int readexChecksum = 0;
+int readexError = 0;
 
-    if (rc == DISK_CTRL_STATUS_CLEAR)
-    {
-        bool use_media_buffer = true;
-        d->get_media_buffer(&blk_buffer, &blk_size);
-        if (blk_buffer == nullptr || blk_size == 0)
+int systemBus::op_readex(std::vector<uint8_t> *q)
+{
+    int result = 0;
+    readexChecksum = 0;
+    
+    if (q->size() >= 5) {
+        uint8_t drive_num = q->at(1);
+        int lsn = q->at(2) << 16 | q->at(3) << 8 | q->at(4);
+        drivewireDisk *d = nullptr;
+        uint8_t sector_data[MEDIA_BLOCK_SIZE];
+        uint8_t *blk_buffer = sector_data;
+        uint16_t blk_size = MEDIA_BLOCK_SIZE;
+        
+        result = 5;
+        
+        Debug_printf("OP_READEX: DRIVE %3u - SECTOR %8lu\n", drive_num, lsn);
+    
+        if (theFuji.boot_config && drive_num == 0)
+            d = theFuji.bootdisk();
+        else
+            d = &theFuji.get_disks(drive_num)->disk_dev;
+    
+        if (!d)
         {
-            // no media buffer, use "bus buffer" with default block size
-            blk_buffer = sector_data;
-            blk_size = MEDIA_BLOCK_SIZE;
-            use_media_buffer = false;
+            Debug_printv("Invalid drive #%3u", drive_num);
+            readexError = 0xF6;
         }
-
-        if (d->read(lsn, use_media_buffer ? nullptr : sector_data))
+    
+        if (readexError == DISK_CTRL_STATUS_CLEAR && !d->device_active)
         {
-            if (d->get_media_status() == 2)
+            Debug_printv("Device not active.");
+            readexError = 0xF6;
+        }
+    
+        if (readexError == DISK_CTRL_STATUS_CLEAR)
+        {
+            bool use_media_buffer = true;
+            d->get_media_buffer(&blk_buffer, &blk_size);
+            if (blk_buffer == nullptr || blk_size == 0)
             {
-                Debug_printf("EOF\n");
-                rc = 211;
+                // no media buffer, use "bus buffer" with default block size
+                blk_buffer = sector_data;
+                blk_size = MEDIA_BLOCK_SIZE;
+                use_media_buffer = false;
             }
-            else
+    
+            if (d->read(lsn, use_media_buffer ? nullptr : sector_data))
             {
-                Debug_printf("Read error\n");
-                rc = 0xF4;
+                if (d->get_media_status() == 2)
+                {
+                    Debug_printf("EOF\n");
+                    readexError = 211;
+                }
+                else
+                {
+                    Debug_printf("Read error\n");
+                    readexError = 0xF4;
+                }
             }
         }
-    }
-
-    // send zeros on error
-    if (rc != DISK_CTRL_STATUS_CLEAR)
-        memset(blk_buffer, 0x00, blk_size);
+    
+        // send zeros on error
+        if (readexError != DISK_CTRL_STATUS_CLEAR)
+        {
+            memset(blk_buffer, 0x00, blk_size);
+        }
+    
+        readexChecksum = drivewire_checksum(blk_buffer, blk_size);
 
-    // send sector data
-    fnDwCom.write(blk_buffer, blk_size);
+        // send sector data
+        fnDwCom.write(blk_buffer, blk_size);
+    
+        fnDwCom.flush();
 
-    // receive checksum
-    c1 = (fnDwCom.read()) << 8;
-    c1 |= fnDwCom.read();
+        dwStateMethod = &systemBus::op_readex_p2;    
+    }
+            
+    return result;
+}
+    
+int systemBus::op_readex_p2(std::vector<uint8_t> *q)
+{
+    int result = 0;
+    
+    if (q->size() >= 2) {
+        // We read 2 bytes into this buffer (guest's checksum).
+        // Here we're expecting the checksum from the guest.
+        result = 2; 
+        
+        int guestChecksum = q->at(0) * 256 + q->at(1);
+        if (readexChecksum != guestChecksum) {
+            Debug_printf("Checksum error: expected %d, got %d\n", readexChecksum, guestChecksum);
+            readexError = 243;
+        }
+        
+        // send status
+        fnDwCom.write(readexError);
 
-    // test checksum
-    if (rc == DISK_CTRL_STATUS_CLEAR)
-    {
-        c2 = drivewire_checksum(blk_buffer, blk_size);
+        resetState();
+    }
+    
+    return result;
+}
 
-        if (c1 != c2)
+int systemBus::op_write(std::vector<uint8_t> *q)
+{
+    int result = 0;
+    int rc = 0;
+    int expectedResult = 263;
+    
+    if (q->size() >= expectedResult) {
+        resetState();
+        result = expectedResult;
+        
+        int drive_num = q->at(1);
+        int lsn = q->at(2) << 16 | q->at(3) << 8 | q->at(4);
+        std::vector<uint8_t> sector_data(256);
+        std::copy(q->begin() + 5, q->begin() + 260, sector_data.begin());
+        int checksum = q->at(261)*256 + q->at(262);
+
+        int computedChecksum = drivewire_checksum(sector_data.data(), MEDIA_BLOCK_SIZE);
+        
+        if (computedChecksum == checksum) {
+            Debug_printf("OP_WRITE DRIVE %3u - SECTOR %8lu\n", drive_num, lsn);
+            drivewireDisk *d = &theFuji.get_disks(drive_num)->disk_dev;
+        
+            if (!d)
+            {
+                Debug_printv("Invalid drive #%3u", drive_num);
+                rc = 0xF6;
+            }
+            else if (!d->device_active)
+            {
+                Debug_printv("Device not active.");
+                rc = 0xF6;
+            }
+            else if (d->write(lsn, sector_data.data()))
+            {
+                Debug_print("Write error\n");
+                rc = 0xF5;
+            }
+        }
+        else
         {
-            Debug_printf("Checksum error: expected %d, got %d\n", c2, c1);
             rc = 243;
         }
-    }
 
-    // finally, send the transaction status
-    fnDwCom.write(rc);
-    fnDwCom.flush();
+        resetState();
+    }        
+        
+    return result;
 }
 
-void systemBus::op_write()
+int systemBus::op_fuji(std::vector<uint8_t> *q)
 {
-    drivewireDisk *d = nullptr;
-    uint16_t c1 = 0, c2 = 0;
-
-    drive_num = fnDwCom.read();
-
-    lsn = fnDwCom.read() << 16;
-    lsn |= fnDwCom.read() << 8;
-    lsn |= fnDwCom.read();
-
-    size_t s = fnDwCom.readBytes(sector_data, MEDIA_BLOCK_SIZE);
-
-    if (s != MEDIA_BLOCK_SIZE)
-    {
-        Debug_printv("Insufficient # of bytes for write, total recvd: %u", s);
-        fnDwCom.flush_input();
-        return;
-    }
-
-    // Todo handle checksum.
-    c1 = fnDwCom.read();
-    c1 |= fnDwCom.read() << 8;
-
-    c2 = drivewire_checksum(sector_data, MEDIA_BLOCK_SIZE);
-
-    // if (c1 != c2)
-    // {
-    //     Debug_printf("Checksum error\n");
-    //     fnDwCom.write(243);
-    //     return;
-    // }
-
-    Debug_printf("OP_WRITE DRIVE %3u - SECTOR %8lu\n", drive_num, lsn);
-
-    d = &theFuji.get_disks(drive_num)->disk_dev;
-
-    if (!d)
-    {
-        Debug_printv("Invalid drive #%3u", drive_num);
-        fnDwCom.write(0xF6);
-        return;
-    }
-
-    if (!d->device_active)
-    {
-        Debug_printv("Device not active.");
-        fnDwCom.write(0xF6);
-        return;
-    }
-
-    if (d->write(lsn, sector_data))
+    int result = 0;
+    
+    result = theFuji.process(q);
+    
+    if (result > 0)
     {
-        Debug_print("Write error\n");
-        fnDwCom.write(0xF5);
-        return;
+        Debug_printv("OP_FUJI");
+        Debug_printf("result = %d\n", result);
     }
-
-    fnDwCom.write(0x00); // success
-}
-
-void systemBus::op_fuji()
-{
-    theFuji.process();
+    
+    return result;
 }
 
-void systemBus::op_cpm()
+int systemBus::op_cpm(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
 #ifdef ESP_PLATFORM
     theCPM.process();
 #endif /* ESP_PLATFORM */
+
+    resetState();
+    Debug_printv("OP_CPM");    
+
+    return result;
 }
 
-void systemBus::op_net()
+int systemBus::op_net(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     // Get device ID
     uint8_t device_id = (uint8_t)fnDwCom.read();
 
@@ -288,22 +391,27 @@ void systemBus::op_net()
     }
 
     // And pass control to it
-    Debug_printf("OP_NET: %u\n",device_id);
     _netDev[device_id]->process();
+    resetState();
+    Debug_printf("OP_NET: %u\n", device_id);
+    
+    return result;
 }
 
-void systemBus::op_unhandled(uint8_t c)
+int systemBus::op_unhandled(std::vector<uint8_t> *q)
 {
-    Debug_printv("Unhandled opcode: %02x", c);
-
-    while (fnDwCom.available())
-        Debug_printf("%02x ", fnDwCom.read());
-
-    fnDwCom.flush_input();
+    int result = 1;
+    
+    resetState();
+    Debug_printv("Unhandled opcode: %02x", q->at(0));
+    
+    return result;
 }
 
-void systemBus::op_time()
+int systemBus::op_time(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     time_t tt = time(nullptr);
     struct tm *now = localtime(&tt);
 
@@ -311,239 +419,478 @@ void systemBus::op_time()
 
     Debug_printf("Returning %02d/%02d/%02d %02d:%02d:%02d\n", now->tm_year, now->tm_mon, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec);
 
-    fnDwCom.write(now->tm_year - 1900);
+    fnDwCom.write(now->tm_year);
     fnDwCom.write(now->tm_mon);
     fnDwCom.write(now->tm_mday);
     fnDwCom.write(now->tm_hour);
     fnDwCom.write(now->tm_min);
     fnDwCom.write(now->tm_sec);
+    
+    resetState();
+    Debug_printv("OP_TIME");    
+    
+    return result;
 }
 
-void systemBus::op_init()
+int systemBus::op_init(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
+    resetState();
     Debug_printv("OP_INIT");
+    
+    return result;
 }
 
-void systemBus::op_serinit()
+int systemBus::op_term(std::vector<uint8_t> *q)
 {
-    Debug_printv("OP_SERINIT");
-    fnDwCom.read();
+    int result = 1;
+    
+    resetState();
+    Debug_printv("OP_TERM");
+    
+    return result;
 }
 
-void systemBus::op_serterm()
+int systemBus::op_serinit(std::vector<uint8_t> *q)
 {
-    Debug_printv("OP_SERTERM");
-    fnDwCom.read();
+    int result = 0;
+    int expectedResult = 2;
+    
+    if (q->size() >= expectedResult)
+    {
+        // Clear the outgoing channel.
+        result = expectedResult;
+        int vchan = q->at(1);
+        fnDwCom.outgoingChannel[vchan].clear();
+        resetState();
+        Debug_printv("OP_SERINIT");
+    }
+        
+    return result;
 }
 
-void systemBus::op_dwinit()
+int systemBus::op_serterm(std::vector<uint8_t> *q)
 {
-    Debug_printv("OP_DWINIT - Sending feature byte 0x%02x", DWINIT_FEATURES);
-#define OS9 1
-#ifdef OS9
-    fnDwCom.write(0x04);
-#else
-    fnDwCom.write(DWINIT_FEATURES);
-#endif    
+    int result = 0;
+    int expectedResult = 2;
+    
+    if (q->size() >= expectedResult)
+    {
+        // Clear the outgoing channel.
+        result = expectedResult;
+        int vchan = q->at(1);
+        fnDwCom.outgoingChannel[vchan].clear();
+        resetState();
+        Debug_printv("OP_SERTERM");
+    }
+        
+    return result;
 }
 
-void systemBus::op_getstat()
+int systemBus::op_dwinit(std::vector<uint8_t> *q)
 {
-    Debug_printv("OP_GETSTAT: 0x%02x 0x%02x", fnDwCom.read(),fnDwCom.read());
+    int result = 0;
+    int expectedResult = 2;
+    
+    if (q->size() >= expectedResult)
+    {
+        result = expectedResult;
+        
+        // save capabilities byte
+        guestCapabilityByte = q->at(1);
+        uint8_t hostResponse = 0x00;
+        
+        if (guestCapabilityByte == 1)
+        {
+            // OS-9 is the only environment that uses OP_DWINIT.
+            // dwio sends OP_DWINIT followed by $01.
+            // If the host responds with $04, dwio starts the
+            // virtual interrupt service routine to poll for input,
+            // so we'll respond with that value here.
+            hostResponse = 0x04;
+            fnDwCom.pollingMode = true;
+        }
+        fnDwCom.write(hostResponse);
+
+        resetState();
+        Debug_printv("OP_DWINIT: %02x", guestCapabilityByte);
+    }
+    
+    return result;
 }
 
-void systemBus::op_setstat()
+int systemBus::op_getstat(std::vector<uint8_t> *q)
 {
-    Debug_printv("OP_SETSTAT: 0x%02x 0x%02x", fnDwCom.read(),fnDwCom.read());
+    int result = 0;
+    int expectedResult = 3;
+    
+    if (q->size() >= expectedResult) {
+        result = expectedResult;
+        int lastDriveNumber = q->at(1);
+        int lastGetStat = q->at(2);
+        resetState();
+        Debug_printv("OP_GETSTAT: 0x%02x 0x%02x", lastDriveNumber, lastGetStat);
+    }
+    
+    return result;
 }
 
-void systemBus::op_sergetstat()
+int systemBus::op_setstat(std::vector<uint8_t> *q)
 {
-    unsigned char vchan = fnDwCom.read();
-    unsigned char code = fnDwCom.read();
-    Debug_printv("OP_SERGETSTAT: 0x%02x 0x%02x", vchan, code);
+    int result = 0;
+    int expectedResult = 3;
+    
+    if (q->size() >= expectedResult) {
+        result = expectedResult;
+        int lastDriveNumber = q->at(1);
+        int lastSetStat = q->at(2);
+        resetState();
+        Debug_printv("OP_SETSTAT: 0x%02x 0x%02x", lastDriveNumber, lastSetStat);
+    }
+    
+    return result;
 }
 
-void systemBus::op_sersetstat()
+int systemBus::op_sergetstat(std::vector<uint8_t> *q)
 {
-    unsigned char vchan = fnDwCom.read();
-    unsigned char code = fnDwCom.read();
-    Debug_printv("OP_SERSETSTAT: 0x%02x 0x%02x", vchan, code);
-    if (code == 0x28) {
-        for (int i = 0; i < 26; i++) {
-            fnDwCom.read();
+    int result = 0;
+    int expectedResult = 3;
+    
+    if (q->size() >= expectedResult) {
+        result = expectedResult;
+        int lastChannelNumber = q->at(1);
+        int lastGetStat = q->at(2);
+        resetState();
+        Debug_printv("OP_SERGETSTAT: 0x%02x 0x%02x", lastChannelNumber, lastGetStat);
+    }
+    
+    return result;
+}
+
+int systemBus::op_sersetstat(std::vector<uint8_t> *q)
+{
+    int result = 0;
+    int expectedResult = 3;
+    
+    if (q->size() >= expectedResult) {
+        result = expectedResult;
+        int lastChannelNumber = q->at(1);
+        int lastSetStat = q->at(2);
+        
+        if (lastSetStat == 0x28)
+        {
+            dwStateMethod = &systemBus::op_sersetstat_comstat;
+        }
+        else
+        {
+            resetState();
         }
+        Debug_printv("OP_SERSETSTAT: 0x%02x 0x%02x", lastChannelNumber, lastSetStat);
     }
+    
+    return result;
 }
 
-void systemBus::op_serread()
+int systemBus::op_sersetstat_comstat(std::vector<uint8_t> *q)
 {
-    unsigned char vchan = 0;
-    unsigned char response = 0x00;
+    int result = 0;
+    int expectedResult = 26;
+    
+    if (q->size() >= expectedResult) {
+        result = expectedResult;
+        resetState();
+    }
+    
+    return result;
+}
 
+int systemBus::op_serread(std::vector<uint8_t> *q)
+{
+    int result = 1;
+    uint8_t responseByte1 = 0x00;
+    uint8_t responseByte2 = 0x00;
+    bool hasData = false;
+    
     // scan client channels for first that has available data    
     for (int i = 0; i < 16; i++) {
-        if (outgoingChannel[i].empty() == false) {
-            response = outgoingChannel[i].front();
-            outgoingChannel[i].pop();
-            vchan = i;
+        if (fnDwCom.outgoingChannel[i].empty() == false) {
+            responseByte1 = (i + 1); // virtual channel indicator
+            int sizeInChannel = fnDwCom.outgoingChannel[i].size();
+            if (sizeInChannel > 1)
+            {
+                responseByte1 |= 0x10; // multibyte response
+                responseByte2 = (sizeInChannel > 16) ? 16 : sizeInChannel;
+            }
+            else
+            {
+                responseByte2 = fnDwCom.outgoingChannel[i].front();
+                fnDwCom.outgoingChannel[i].erase(fnDwCom.outgoingChannel[i].begin());
+            }
+            fnDwCom.write(responseByte1); 
+            fnDwCom.write(responseByte2);
+            Debug_printv("OP_SERREAD: response1 $%02x - response2 $%02x\n", responseByte1, responseByte2);
+            hasData = true;
             break;
         }
     }
     
-    fnDwCom.write(vchan);
-    fnDwCom.write(response);
-
-    Debug_printv("OP_SERREAD: vchan $%02x - response $%02x\n", vchan, response);
+    if (hasData == false)
+    {
+        responseByte1 = 0x00;
+        responseByte2 = 0x00;
+        fnDwCom.write(responseByte1); 
+        fnDwCom.write(responseByte2);
+        Debug_printv("OP_SERREAD: response1 $%02x - response2 $%02x\n", responseByte1, responseByte2);
+    }
+    
+    resetState();    
+    
+    return result;
 }
 
-void systemBus::op_serreadm()
+int systemBus::op_serreadm(std::vector<uint8_t> *q)
 {
-    unsigned char vchan = fnDwCom.read();
-    unsigned char count = fnDwCom.read();
+    int result = 0;
+    int expectedResult = 3;
     
-    // scan client channels for first that has available data    
-    for (vchan = 0; vchan < 16; vchan++) {
-        if (outgoingChannel[vchan].empty() == false) {
-            if (outgoingChannel[vchan].size() < count) count = outgoingChannel[vchan].size();
+    if (q->size() >= expectedResult) {
+        result = expectedResult;
+        uint8_t vchan = q->at(1);
+        uint8_t count = q->at(2);
+    
+        Debug_printv("OP_SERREADM: vchan $%02x - count $%02x\n", vchan, count);
+
+        // scan client channels for first that has available data    
+        if (fnDwCom.outgoingChannel[vchan].empty() == false) {
+            if (fnDwCom.outgoingChannel[vchan].size() < count) count = fnDwCom.outgoingChannel[vchan].size();
             for (int i = 0; i < count; i++) {
-                int response = outgoingChannel[vchan].front();
-                outgoingChannel[vchan].pop();
+                int response = fnDwCom.outgoingChannel[vchan].front();
+                fnDwCom.outgoingChannel[vchan].erase(fnDwCom.outgoingChannel[vchan].begin());
                 fnDwCom.write(response);
-                Debug_printv("OP_SERREADM: vchan $%02x - response $%02x\n", vchan, response);
+                Debug_printv("Data to client -> $%02x", response);
             }
-            break;
         }
+
+        resetState();    
+    }
+    
+    return result;
+}
+
+int systemBus::op_serwrite(std::vector<uint8_t> *q)
+{
+    int result = 0;
+    int expectedResult = 3;
+    
+    if (q->size() >= expectedResult) {
+        result = expectedResult;
+        uint8_t vchan = q->at(1);
+        uint8_t byte = q->at(2);
+    
+        fnDwCom.incomingChannel[vchan].push_back(byte);
+        resetState();
+        Debug_printv("OP_SERWRITE: vchan $%02x - byte $%02x\n", vchan, byte);
     }
+    
+    return result;
 }
 
-void systemBus::op_serwrite()
+int systemBus::op_serwritem(std::vector<uint8_t> *q)
 {
-    unsigned char vchan = fnDwCom.read();
-    unsigned char byte = fnDwCom.read();
-    incomingChannel[vchan].push(byte);
-    Debug_printv("OP_SERWRITE: vchan $%02x - byte $%02x\n", vchan, byte);
+    int result = 0;
+    int expectedResult = 3;
+    
+    if (q->size() >= expectedResult) {
+        result = expectedResult;
+        uint8_t vchan = q->at(1);
+        uint8_t count = q->at(2);
+    
+        for (int i = 0; i < count; i++) {
+            int byte = q->at(i+3);
+            fnDwCom.incomingChannel[vchan].push_back(byte);
+        }
+        
+        resetState();
+        Debug_printv("OP_SERWRITEM: vchan $%02x\n", vchan);
+    }
+    
+    return result;
 }
 
-void systemBus::op_serwritem()
+int systemBus::op_print(std::vector<uint8_t> *q)
 {
-    unsigned char vchan = fnDwCom.read();
-    unsigned char byte = fnDwCom.read();
-    unsigned char count = fnDwCom.read();
+    int result = 0;
+    int expectedResult = 2;
     
-    for (int i = 0; i < count; i++) {
-        int byte = fnDwCom.read();
-        incomingChannel[vchan].push(byte);
-        Debug_printv("OP_SERWRITE: vchan $%02x - byte $%02x\n", vchan, byte);
+    if (q->size() >= expectedResult) {
+        uint8_t byte = q->at(1);
+        _printerdev->write(byte);
+        
+        resetState();
+        Debug_printv("OP_PRINT: byte $%02x\n", byte);
     }
+    
+    return result;
 }
 
-void systemBus::op_print()
+int systemBus::op_printflush(std::vector<uint8_t> *q)
 {
-    _printerdev->write(fnDwCom.read());
+    int result = 1;
+        
+    resetState();
+    Debug_printv("OP_PRINTFLUSH\n");
+
+    return result;
 }
 
-// Read and process a command frame from DRIVEWIRE
-void systemBus::_drivewire_process_cmd()
+int systemBus::op_fastwrite_serial(std::vector<uint8_t> *q)
 {
-    int c = fnDwCom.read();
-    if (c < 0)
-    {
-        Debug_println("Failed to read cmd!");
-        return;
+    int result = 0;
+    int expectedResult = 2;
+    
+    if (q->size() >= expectedResult) {
+        result = expectedResult;
+        int vchan = q->at(0) & 0x7F;
+        uint8_t c = q->at(1);
+        fnDwCom.incomingChannel[vchan].push_back(c);
+    
+        resetState();
+        Debug_printv("OP_FASTWRITE (serial): vchan = $%02x, byte $%02x\n", vchan, c);
+    }
+    
+    return result;
+}
+
+int systemBus::op_fastwrite_screen(std::vector<uint8_t> *q)
+{
+    int result = 0;
+    int expectedResult = 2;
+    
+    if (q->size() >= expectedResult) {
+        result = expectedResult;
+        int vchan = q->at(0) & 0x7F;
+        uint8_t c = q->at(1);
+        fnDwCom.incomingChannel[vchan].push_back(c);
+        
+        resetState();
+        Debug_printv("OP_FASTWRITE (serial): vchan = $%02x, byte $%02x\n", vchan, c);
     }
+    
+    return result;
+}
 
+// Read and process a command frame from DRIVEWIRE
+int systemBus::_drivewire_process_cmd(std::vector<uint8_t> *q)
+{
+    int result = 0;
+    
+    timer_start();
+    
+    uint8_t c = q->at(0);
+    
     fnLedManager.set(eLed::LED_BUS, true);
 
-    if (c >= 0x80 && c <= 0x8F) {
-        // handle FASTWRITE here
-        int vchan = c & 0xF;
-        int byte = fnDwCom.read();
-        incomingChannel[vchan].push(byte);
-    } else {
+    if (c >= 0x80 && c <= 0x8E)
+    {
+        // handle FASTWRITE serial
+        dwStateMethod = &systemBus::op_fastwrite_serial;
+    }
+    else if (c >= 0x91 && c <= 0x9E)
+    {
+        // handle FASTWRITE virtual screen
+        dwStateMethod = &systemBus::op_fastwrite_screen;
+    }
+    else
+    {
         switch (c)
         {
         case OP_JEFF:
-            op_jeff();
+            dwStateMethod = &systemBus::op_jeff;
 		    break;
 	    case OP_NOP:
-            op_nop();
+            dwStateMethod = &systemBus::op_nop;
             break;
         case OP_RESET1:
         case OP_RESET2:
         case OP_RESET3:
-            op_reset();
+            dwStateMethod = &systemBus::op_reset;
             break;
         case OP_READEX:
-            op_readex();
+            dwStateMethod = &systemBus::op_readex;
             break;
         case OP_WRITE:
-            op_write();
+            dwStateMethod = &systemBus::op_write;
             break;
         case OP_TIME:
-            op_time();
+            dwStateMethod = &systemBus::op_time;
             break;
         case OP_INIT:
-            op_init();
+            dwStateMethod = &systemBus::op_init;
             break;
         case OP_SERINIT:
-            op_serinit();
+            dwStateMethod = &systemBus::op_serinit;
             break;
         case OP_DWINIT:
-            op_dwinit();
+            dwStateMethod = &systemBus::op_dwinit;
             break;
         case OP_SERREAD:
-            op_serread();
+            dwStateMethod = &systemBus::op_serread;
             break;
         case OP_SERREADM:
-            op_serreadm();
+            dwStateMethod = &systemBus::op_serreadm;
             break;
         case OP_SERWRITE:
-            op_serwrite();
+            dwStateMethod = &systemBus::op_serwrite;
             break;
         case OP_SERWRITEM:
-            op_serwritem();
+            dwStateMethod = &systemBus::op_serwritem;
             break;
         case OP_PRINT:
-            op_print();
+            dwStateMethod = &systemBus::op_print;
             break;
         case OP_PRINTFLUSH:
-            // Not needed.
+            dwStateMethod = &systemBus::op_printflush;
             break;
         case OP_GETSTAT:
-            op_getstat();
+            dwStateMethod = &systemBus::op_getstat;
             break;
         case OP_SETSTAT:
-            op_setstat();
+            dwStateMethod = &systemBus::op_setstat;
             break;
         case OP_SERGETSTAT:
-            op_sergetstat();
+            dwStateMethod = &systemBus::op_sergetstat;
             break;
         case OP_SERSETSTAT:
-            op_sersetstat();
+            dwStateMethod = &systemBus::op_sersetstat;
             break;
         case OP_TERM:
-            Debug_printf("OP_TERM!\n");
+            dwStateMethod = &systemBus::op_term;
             break;
         case OP_SERTERM:
-            op_serterm();
+            dwStateMethod = &systemBus::op_serterm;
             break;
         case OP_FUJI:
-            op_fuji();
+            q->erase(q->begin()); // lob off OP_FUJI so parsing in fuji.cpp can go smoothly
+            dwStateMethod = &systemBus::op_fuji;
             break;
         case OP_NET:
-            op_net();
+            dwStateMethod = &systemBus::op_net;
             break;
         case OP_CPM:
-            op_cpm();
+            dwStateMethod = &systemBus::op_cpm;
             break;
         default:
-            op_unhandled(c);
+            dwStateMethod = &systemBus::op_unhandled;
             break;
         }
     }
     
+    result = (this->*dwStateMethod)(q);
+
     fnLedManager.set(eLed::LED_BUS, false);
+    
+    return result;
 }
 
 // Look to see if we have any waiting messages and process them accordingly
@@ -583,11 +930,62 @@ void systemBus::service()
         }
     }
 
-    if (fnDwCom.available())
-        _drivewire_process_cmd();
+    // TODO: read from serial port...
+//    if (fnDwCom.available())
+//        _drivewire_process_cmd();
+//    fnDwCom.poll(1);
 
-    fnDwCom.poll(1);
+    int gotNewData = 0;
+    
+    while (fnDwCom.available()) {
+        int byte = fnDwCom.read();
+        serialBuffer.push_back(byte);
+        gotNewData = 1;
+    }
+    
+    int showDump = 1;
+    
+#ifdef ESP_PLATFORM
+#else
+    uint64_t ms = fnSystem.millis();
+#ifdef SUPER_DEBUGGING
+    if (ms % 100 == 0) {
+        Debug_printv("MS = %ld, lastInterruptMs = %ld, TIMER = %ld\n", ms, lastInterruptMs, (timerActive == true && ms - lastInterruptMs >= timerRate));
+    }
+#endif
+    if (timerActive == true && ms - lastInterruptMs >= timerRate)
+    {
+        Debug_printv("State Reset Timer INVOKED!\n");
+        resetState();
+    }
+#endif
 
+#ifdef SUPER_DEBUGING
+    if (gotNewData == 1 && showDump == 1 && serialBuffer.size() > 0)
+    {
+        for (int i = 0; i < serialBuffer.size(); i++)
+        {
+            Debug_printf("$%02x ", serialBuffer.at(i));
+        }
+        
+        Debug_printf("=====================================\n");    
+    }
+#endif
+
+    if (gotNewData == 1)
+    {
+        int bytesConsumed = 0;
+        
+        do {
+            bytesConsumed = (this->*dwStateMethod)(&serialBuffer);
+            
+            if (bytesConsumed > 0 && serialBuffer.size() >= bytesConsumed) {
+                // chop off consumed bytes
+                serialBuffer.erase(serialBuffer.begin(), serialBuffer.begin() + bytesConsumed);
+            }
+        } while (bytesConsumed > 0 && serialBuffer.size() > 0);
+    }
+        
     // dload.dload_process();
 }
 
@@ -664,6 +1062,8 @@ void systemBus::setup()
     fnDwCom.set_becker_host(Config.get_boip_host().c_str(), Config.get_boip_port()); // Becker
     fnDwCom.set_drivewire_mode(Config.get_boip_enabled() ? DwCom::dw_mode::BECKER : DwCom::dw_mode::SERIAL);
 
+    resetState();
+    
     fnDwCom.begin(_drivewireBaud);
     fnDwCom.flush_input();
     Debug_printv("DRIVEWIRE MODE");
diff --git a/lib/bus/drivewire/drivewire.h b/lib/bus/drivewire/drivewire.h
index 7f5c98544..e933573e2 100644
--- a/lib/bus/drivewire/drivewire.h
+++ b/lib/bus/drivewire/drivewire.h
@@ -24,6 +24,10 @@
 #include <freertos/queue.h>
 #endif
 
+#include <chrono>
+#include <future>
+
+#include <vector>
 #include <forward_list>
 #include <map>
 // fnUartBUS (Serial only) was replaced with fnDwCom (Serial|TCP/Becker)
@@ -188,6 +192,10 @@ struct drivewire_message_t
 
 class systemBus
 {
+public:
+    int (drivewireFuji::*fnStateMethod)(std::vector<uint8_t> *);
+    void resetState(void);
+
 private:
     virtualDevice *_activeDev = nullptr;
     drivewireModem *_modemDev = nullptr;
@@ -198,9 +206,11 @@ class systemBus
     drivewireCPM *_cpmDev = nullptr;
     drivewirePrinter *_printerdev = nullptr;
 
-    void _drivewire_process_cmd();
+    int _drivewire_process_cmd(std::vector<uint8_t> *q);
     void _drivewire_process_queue();
 
+    int guestCapabilityByte;
+    
     /**
      * @brief Current Baud Rate
      */
@@ -221,32 +231,69 @@ class systemBus
      */
     uint8_t sector_data[MEDIA_BLOCK_SIZE];
 
+    int (systemBus::*dwStateMethod)(std::vector<uint8_t> *);
+    std::vector<uint8_t> serialBuffer;    
+
+    /**
+     * Timer Rate for interrupt timer (ms)
+     */
+#ifdef ESP_PLATFORM
+    int timerRate = 2000;
+#else
+    int timerRate = 2000;
+    bool timerActive = false;
+#endif
+
+    /**
+     * Timer handle for the DriveWire state machine recovery timer
+     */
+#ifdef ESP_PLATFORM
+    esp_timer_handle_t stateMachineRecoveryTimerHandle = nullptr;
+#else
+    uint64_t lastInterruptMs;
+#endif
+    /**
+     * Start the Interrupt rate limiting timer
+     */
+    void timer_start();
+
+    /**
+     * Stop the Interrupt rate limiting timer
+     */
+    void timer_stop();
+
     /**
      * @brief NOP command (do nothing)
      */
-    void op_jeff();
-    void op_nop();
-    void op_reset();
-    void op_readex();
-    void op_fuji();
-    void op_net();
-    void op_cpm();
-    void op_write();
-    void op_time();
-    void op_init();
-    void op_serinit();
-    void op_serterm();
-    void op_dwinit();
-    void op_unhandled(uint8_t c);
-    void op_getstat();
-    void op_setstat();
-    void op_sergetstat();
-    void op_sersetstat();
-    void op_serread();
-    void op_serreadm();
-    void op_serwrite();
-    void op_serwritem();
-    void op_print();
+    int op_jeff(std::vector<uint8_t> *q);
+    int op_nop(std::vector<uint8_t> *q);
+    int op_reset(std::vector<uint8_t> *q);
+    int op_readex(std::vector<uint8_t> *q);
+    int op_readex_p2(std::vector<uint8_t> *q);
+    int op_fuji(std::vector<uint8_t> *q);
+    int op_net(std::vector<uint8_t> *q);
+    int op_cpm(std::vector<uint8_t> *q);
+    int op_write(std::vector<uint8_t> *q);
+    int op_time(std::vector<uint8_t> *q);
+    int op_init(std::vector<uint8_t> *q);
+    int op_term(std::vector<uint8_t> *q);
+    int op_fastwrite_serial(std::vector<uint8_t> *q);
+    int op_fastwrite_screen(std::vector<uint8_t> *q);
+    int op_serinit(std::vector<uint8_t> *q);
+    int op_serterm(std::vector<uint8_t> *q);
+    int op_dwinit(std::vector<uint8_t> *q);
+    int op_unhandled(std::vector<uint8_t> *q);
+    int op_getstat(std::vector<uint8_t> *q);
+    int op_setstat(std::vector<uint8_t> *q);
+    int op_sergetstat(std::vector<uint8_t> *q);
+    int op_sersetstat(std::vector<uint8_t> *q);
+    int op_sersetstat_comstat(std::vector<uint8_t> *q);
+    int op_serread(std::vector<uint8_t> *q);
+    int op_serreadm(std::vector<uint8_t> *q);
+    int op_serwrite(std::vector<uint8_t> *q);
+    int op_serwritem(std::vector<uint8_t> *q);
+    int op_print(std::vector<uint8_t> *q);
+    int op_printflush(std::vector<uint8_t> *q);
 
     // int readSector(struct dwTransferData *dp);
     // int writeSector(struct dwTransferData *dp);
diff --git a/lib/bus/drivewire/dwcom/fnDwCom.h b/lib/bus/drivewire/dwcom/fnDwCom.h
index 556a0458c..2cceddafc 100644
--- a/lib/bus/drivewire/dwcom/fnDwCom.h
+++ b/lib/bus/drivewire/dwcom/fnDwCom.h
@@ -25,6 +25,14 @@ class DwCom
         BECKER
     };
 
+    bool pollingMode;
+    
+    // Host & client channel queues
+    std::vector<char> outgoingChannel[16];
+    std::vector<char> incomingChannel[16];
+    std::vector<char> incomingScreen[16];
+    
+
 private:
     dw_mode _dw_mode;
     DwPort *_dwPort;
@@ -68,14 +76,76 @@ class DwCom
 
     // write buffer
     ssize_t write(const uint8_t *buffer, size_t size) { return _dwPort->write(buffer, size); }
+
+    // write buffer to FN channel
+    ssize_t writeToFNChannel(int channel, const uint8_t *buffer, size_t size)
+    {
+        int result = 0;
+        
+        if (pollingMode == true)
+        {
+            for (int i = 0; i < size; i++)
+            {
+                outgoingChannel[channel].push_back(buffer[i]);
+            }
+            result = size;
+        }
+        else
+        {
+            result = write(buffer, size);
+        }
+        
+        return result;
+    }
+    
     // write C-string
     ssize_t write(const char *str) { return _dwPort->write((const uint8_t *)str, strlen(str)); }
 
+    // write C-string to FN channel
+    ssize_t writeToFNChannel(int channel, const char *str)
+    {
+        int result = 0;
+        
+        if (pollingMode == true)
+        {
+            result = strlen(str);
+            for (int i = 0; i < result; i++)
+            {
+                outgoingChannel[channel].push_back(str[i]);
+            }
+        }
+        else
+        {
+            result = write(str);
+        }
+        
+        return result;
+    }
+    
     // read single byte, mimic UARTManager
     int read();
+
     // write single byte, mimic UARTManager
     ssize_t write(uint8_t b) { return _dwPort->write(&b, 1); }
 
+    // write C-string to FN channel
+    ssize_t writeToFNChannel(int channel, uint8_t b)
+    {
+        int result = 0;
+        
+        if (pollingMode == true)
+        {
+            result = 1;
+            outgoingChannel[channel].push_back(b);
+        }
+        else
+        {
+            result = write(b);
+        }
+        
+        return result;
+    }
+
     // mimic UARTManager overloaded write functions
     size_t write(unsigned long n) { return write((uint8_t)n); }
     size_t write(long n) { return write((uint8_t)n); }
diff --git a/lib/device/drivewire/fuji.cpp b/lib/device/drivewire/fuji.cpp
index 658039fd1..11268ab3c 100755
--- a/lib/device/drivewire/fuji.cpp
+++ b/lib/device/drivewire/fuji.cpp
@@ -35,6 +35,12 @@ drivewireNetwork drivewireNetDevs[MAX_NETWORK_DEVICES];
 bool _validate_host_slot(uint8_t slot, const char *dmsg = nullptr);
 bool _validate_device_slot(uint8_t slot, const char *dmsg = nullptr);
 
+void drivewireFuji::resetState(void)
+{
+    _drivewire_bus->fnStateMethod = &drivewireFuji::process;
+    _drivewire_bus->resetState();
+}
+
 bool _validate_host_slot(uint8_t slot, const char *dmsg)
 {
     if (slot < MAX_HOSTS)
@@ -128,16 +134,24 @@ drivewireFuji::drivewireFuji()
 }
 
 // Reset FujiNet
-void drivewireFuji::reset_fujinet()
+int drivewireFuji::reset_fujinet(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     Debug_println("Fuji cmd: REBOOT");
     // drivewire_complete();
     fnSystem.reboot();
+    
+    resetState();
+    
+    return result;
 }
 
 // Scan for networks
-void drivewireFuji::net_scan_networks()
+int drivewireFuji::net_scan_networks(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     Debug_println("Fuji cmd: SCAN NETWORKS");
 
     if (!wifiScanStarted)
@@ -150,43 +164,61 @@ void drivewireFuji::net_scan_networks()
     response.shrink_to_fit();
 
     response += _countScannedSSIDs;
+    
+    resetState();
+    
+    return result;
 }
 
 // Return scanned network entry
-void drivewireFuji::net_scan_result()
+int drivewireFuji::net_scan_result(std::vector<uint8_t> *q)
 {
-    Debug_println("Fuji cmd: GET SCAN RESULT");
-
-    uint8_t n = fnDwCom.read();
-
-    wifiScanStarted = false;
-
-    // Response to  FUJICMD_GET_SCAN_RESULT
-    struct
-    {
-        char ssid[MAX_SSID_LEN + 1];
-        uint8_t rssi;
-    } detail;
-
-    if (n < _countScannedSSIDs)
-        fnWiFi.get_scan_result(n, detail.ssid, &detail.rssi);
-    else
+    int result = 0;
+    int expectedResult = 2;
+    
+    if (q->size() >= expectedResult)
     {
-        memset(&detail, 0, sizeof(detail));
-        errorCode = 144;
+      result = expectedResult;
+      
+      Debug_println("Fuji cmd: GET SCAN RESULT");
+  
+      uint8_t n = q->at(1);
+  
+      wifiScanStarted = false;
+  
+      // Response to FUJICMD_GET_SCAN_RESULT
+      struct
+      {
+          char ssid[MAX_SSID_LEN + 1];
+          uint8_t rssi;
+      } detail;
+  
+      if (n < _countScannedSSIDs)
+          fnWiFi.get_scan_result(n, detail.ssid, &detail.rssi);
+      else
+      {
+          memset(&detail, 0, sizeof(detail));
+          errorCode = 144;
+      }
+  
+      response.clear();
+      response.shrink_to_fit();
+  
+      response = std::string((const char *)&detail, sizeof(detail));
+  
+      errorCode = 1;
+      
+      resetState();
     }
-
-    response.clear();
-    response.shrink_to_fit();
-
-    response = std::string((const char *)&detail, sizeof(detail));
-
-    errorCode = 1;
+    
+    return result;
 }
 
 //  Get SSID
-void drivewireFuji::net_get_ssid()
+int drivewireFuji::net_get_ssid(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     Debug_println("Fuji cmd: GET SSID");
 
     // Response to  FUJICMD_GET_SSID
@@ -217,45 +249,66 @@ void drivewireFuji::net_get_ssid()
     response = std::string((const char *)&cfg, sizeof(cfg));
 
     errorCode = 1;
+    
+    resetState();
+    
+    return result;
 }
 
 // Set SSID
-void drivewireFuji::net_set_ssid()
+int drivewireFuji::net_set_ssid(std::vector<uint8_t> *q)
 {
-    Debug_printf("\r\nFuji cmd: SET SSID");
     struct
     {
         char ssid[MAX_SSID_LEN + 1];
         char password[MAX_WIFI_PASS_LEN];
     } cfg;
 
-    fnDwCom.readBytes((uint8_t *)&cfg, sizeof(cfg));
-
-    bool save = false; // for now don't save - to do save if connection was succesful
-
-    // URL Decode SSID/PASSWORD to handle special chars (FIXME)
-    //mstr::urlDecode(cfg.ssid, sizeof(cfg.ssid));
-    //mstr::urlDecode(cfg.password, sizeof(cfg.password));
-
-    Debug_printf("\r\nConnecting to net: %s password: %s\n", cfg.ssid, cfg.password);
-
-    if (fnWiFi.connect(cfg.ssid, cfg.password) == ESP_OK)
-    {
-        Config.store_wifi_ssid(cfg.ssid, sizeof(cfg.ssid));
-        Config.store_wifi_passphrase(cfg.password, sizeof(cfg.password));
-    }
-
-    // Only save these if we're asked to, otherwise assume it was a test for connectivity
-    // should only save if connection was successful - i think
-    if (save)
+    int result = 0;
+    int expectedResult = 1 + sizeof(cfg);
+    
+    if (q->size() >= expectedResult)
     {
-        Config.save();
+      result = expectedResult;
+      
+      Debug_printf("\r\nFuji cmd: SET SSID");
+  
+      std::vector<uint8_t> buff(sizeof(cfg));
+      std::copy(q->begin() + 1, q->begin() + expectedResult, buff.begin());
+      memcpy((uint8_t *)&cfg, buff.data(), sizeof(cfg));
+  
+      bool save = false; // for now don't save - to do save if connection was succesful
+  
+      // URL Decode SSID/PASSWORD to handle special chars (FIXME)
+      //mstr::urlDecode(cfg.ssid, sizeof(cfg.ssid));
+      //mstr::urlDecode(cfg.password, sizeof(cfg.password));
+  
+      Debug_printf("\r\nConnecting to net: %s password: %s\n", cfg.ssid, cfg.password);
+  
+      if (fnWiFi.connect(cfg.ssid, cfg.password) == ESP_OK)
+      {
+          Config.store_wifi_ssid(cfg.ssid, sizeof(cfg.ssid));
+          Config.store_wifi_passphrase(cfg.password, sizeof(cfg.password));
+      }
+  
+      // Only save these if we're asked to, otherwise assume it was a test for connectivity
+      // should only save if connection was successful - i think
+      if (save)
+      {
+          Config.save();
+      }
+    
+      resetState();
     }
+        
+    return result;
 }
 
 // Get WiFi Status
-void drivewireFuji::net_get_wifi_status()
+int drivewireFuji::net_get_wifi_status(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     uint8_t wifiStatus = fnWiFi.connected() ? 3 : 6;
     Debug_printv("Fuji cmd: GET WIFI STATUS: %u", wifiStatus);
 
@@ -265,11 +318,17 @@ void drivewireFuji::net_get_wifi_status()
     response += wifiStatus;
 
     errorCode = 1;
+    
+    resetState();
+    
+    return result;
 }
 
 // Check if Wifi is enabled
-void drivewireFuji::net_get_wifi_enabled()
+int drivewireFuji::net_get_wifi_enabled(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     uint8_t e = Config.get_wifi_enabled() ? 1 : 0;
 
     Debug_printv("Fuji cmd: GET WIFI ENABLED: %u", e);
@@ -280,69 +339,103 @@ void drivewireFuji::net_get_wifi_enabled()
     response += e;
 
     errorCode = 1; // Set it anyway.
+    
+    resetState();
+    
+    return result;
 }
 
 // Mount Server
-void drivewireFuji::mount_host()
+int drivewireFuji::mount_host(std::vector<uint8_t> *q)
 {
-    Debug_println("Fuji cmd: MOUNT HOST");
+    int result = 0;
+    int expectedResult = 2;
+    
+    if (q->size() >= expectedResult)
+    {
+      result = expectedResult;
+      Debug_println("Fuji cmd: MOUNT HOST");
 
-    unsigned char hostSlot = fnDwCom.read();
+      unsigned char hostSlot = q->at(1);
 
-    _fnHosts[hostSlot].mount();
+      _fnHosts[hostSlot].mount();
+      
+      resetState();
+    }
+    
+    return result;
 }
 
 // Disk Image Mount
-void drivewireFuji::disk_image_mount()
+int drivewireFuji::disk_image_mount(std::vector<uint8_t> *q)
 {
-    // TAPE or CASSETTE handling: this function can also mount CAS and WAV files
-    // to the C: device. Everything stays the same here and the mounting
-    // where all the magic happens is done in the drivewireDisk::mount() function.
-    // This function opens the file, so cassette does not need to open the file.
-    // Cassette needs the file pointer and file size.
-
-    Debug_println("Fuji cmd: MOUNT IMAGE");
-
-    uint8_t deviceSlot = fnDwCom.read();
-    uint8_t options = fnDwCom.read(); // DISK_ACCESS_MODE
-
-    // TODO: Implement FETCH?
-    char flag[3] = {'r', 0, 0};
-    if (options == DISK_ACCESS_MODE_WRITE)
-        flag[1] = '+';
-
-    // A couple of reference variables to make things much easier to read...
-    fujiDisk &disk = _fnDisks[deviceSlot];
-    fujiHost &host = _fnHosts[disk.host_slot];
-
-    Debug_printf("Selecting '%s' from host #%u as %s on D%u:\n",
-                 disk.filename, disk.host_slot, flag, deviceSlot + 1);
-
-    // TODO: Refactor along with mount disk image.
-    disk.disk_dev.host = &host;
-
-    disk.fileh = host.fnfile_open(disk.filename, disk.filename, sizeof(disk.filename), flag);
-
-    // We've gotten this far, so make sure our bootable CONFIG disk is disabled
-    boot_config = false;
-
-    // We need the file size for loading XEX files and for CASSETTE, so get that too
-    disk.disk_size = host.file_size(disk.fileh);
-
-    // And now mount it
-    disk.disk_type = disk.disk_dev.mount(disk.fileh, disk.filename, disk.disk_size);
+    int result = 0;
+    int expectedResult = 3;
+    
+    if (q->size() >= expectedResult)
+    {
+      // TAPE or CASSETTE handling: this function can also mount CAS and WAV files
+      // to the C: device. Everything stays the same here and the mounting
+      // where all the magic happens is done in the drivewireDisk::mount() function.
+      // This function opens the file, so cassette does not need to open the file.
+      // Cassette needs the file pointer and file size.
+      result = expectedResult;
+  
+      Debug_println("Fuji cmd: MOUNT IMAGE");
+  
+      uint8_t deviceSlot = q->at(1);
+      uint8_t options = q->at(2); // DISK_ACCESS_MODE
+  
+      // TODO: Implement FETCH?
+      char flag[3] = {'r', 0, 0};
+      if (options == DISK_ACCESS_MODE_WRITE)
+          flag[1] = '+';
+  
+      // A couple of reference variables to make things much easier to read...
+      fujiDisk &disk = _fnDisks[deviceSlot];
+      fujiHost &host = _fnHosts[disk.host_slot];
+  
+      Debug_printf("Selecting '%s' from host #%u as %s on D%u:\n",
+                   disk.filename, disk.host_slot, flag, deviceSlot + 1);
+  
+      // TODO: Refactor along with mount disk image.
+      disk.disk_dev.host = &host;
+  
+      disk.fileh = host.fnfile_open(disk.filename, disk.filename, sizeof(disk.filename), flag);
+  
+      // We've gotten this far, so make sure our bootable CONFIG disk is disabled
+      boot_config = false;
+  
+      // We need the file size for loading XEX files and for CASSETTE, so get that too
+      disk.disk_size = host.file_size(disk.fileh);
+  
+      // And now mount it
+      disk.disk_type = disk.disk_dev.mount(disk.fileh, disk.filename, disk.disk_size);
+      
+      resetState();
+    }    
+    
+    return result;
 }
 
 // Toggle boot config on/off, aux1=0 is disabled, aux1=1 is enabled
-void drivewireFuji::set_boot_config()
+int drivewireFuji::set_boot_config(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     // boot_config = cmdFrame.aux1;
     // drivewire_complete();
+    
+    resetState();
+    
+    return result;
 }
 
 // Do DRIVEWIRE copy
-void drivewireFuji::copy_file()
+int drivewireFuji::copy_file(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     // uint8_t csBuf[256];
     // string copySpec;
     // string sourcePath;
@@ -474,11 +567,26 @@ void drivewireFuji::copy_file()
     // fclose(sourceFile);
     // fclose(destFile);
     // free(dataBuf);
+    
+    resetState();
+    
+    return result;
 }
 
 // Mount all
-void drivewireFuji::mount_all()
+int drivewireFuji::local_mount_all(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
+    mount_all();
+    
+    resetState();
+    
+    return result;
+}
+      
+void drivewireFuji::mount_all()
+{    
     bool nodisks = true; // Check at the end if no disks are in a slot and disable config
 
     Debug_printf("drivewireFuji::mount_all()\n");
@@ -534,14 +642,24 @@ void drivewireFuji::mount_all()
     }
 
     Debug_printf("drivewireFuji::mount_all() done.\n");
-
 }
 
 // Set boot mode
-void drivewireFuji::set_boot_mode()
+int drivewireFuji::set_boot_mode(std::vector<uint8_t> *q)
 {
-    insert_boot_device(fnDwCom.read());
-    boot_config = true;
+    int result = 0;
+    int expectedResult = 2;
+    
+    if (q->size() >= expectedResult)
+    {
+      result = expectedResult;
+      insert_boot_device(q->at(1));
+      boot_config = true;
+      
+      resetState();
+    }
+    
+    return result;
 }
 
 char *_generate_appkey_filename(appkey *info)
@@ -559,116 +677,148 @@ char *_generate_appkey_filename(appkey *info)
  Requiring a separate OPEN command makes both the read and write commands behave similarly
  and leaves the possibity for a more robust/general file read/write function later.
 */
-void drivewireFuji::open_app_key()
-{
-    Debug_print("Fuji cmd: OPEN APPKEY\n");
-
-    fnDwCom.readBytes((uint8_t *)&_current_appkey, sizeof(_current_appkey));
-
-    // Endian swap
-    uint16_t tmp = _current_appkey.creator;
-    _current_appkey.creator = tmp >> 8 | tmp << 8;
-
-    // Basic check for valid data
-    if (_current_appkey.creator == 0 || _current_appkey.mode == APPKEYMODE_INVALID)
-    {
-        Debug_println("Invalid app key data");
-        errorCode = 144;
-        return;
+int drivewireFuji::open_app_key(std::vector<uint8_t> *q)
+{
+    int result = 0;
+    int expectedResult = sizeof(_current_appkey) + 1;
+
+    if (q->size() >= expectedResult) {
+      result = expectedResult;
+      Debug_print("Fuji cmd: OPEN APPKEY\n");
+  
+      std::vector<uint8_t> app_key(256);
+      std::copy(q->begin() + 1, q->begin() + expectedResult, app_key.begin());
+      memcpy((uint8_t *)&_current_appkey, app_key.data(), sizeof(_current_appkey));
+
+      // Endian swap
+      uint16_t tmp = _current_appkey.creator;
+      _current_appkey.creator = tmp >> 8 | tmp << 8;
+      
+      // Basic check for valid data
+      if (_current_appkey.creator == 0 || _current_appkey.mode == APPKEYMODE_INVALID)
+      {
+          Debug_println("Invalid app key data");
+          errorCode = 144;
+      }
+      else if (fnSDFAT.running() == false)
+      {
+          Debug_println("No SD mounted - returning error");
+          errorCode = 144;
+      }
+      else
+      {
+        errorCode = 1;
+        
+        Debug_printf("App key creator = 0x%04hx, app = 0x%02hhx, key = 0x%02hhx, mode = %hhu, filename = \"%s\"\n",
+                    _current_appkey.creator, _current_appkey.app, _current_appkey.key, _current_appkey.mode,
+                    _generate_appkey_filename(&_current_appkey));
+      }
+      
+      resetState();
     }
-
-    if (fnSDFAT.running() == false)
-    {
-        Debug_println("No SD mounted - returning error");
-        errorCode = 144;
-        return;
-    }
-
-    errorCode = 1;
-
-    Debug_printf("App key creator = 0x%04hx, app = 0x%02hhx, key = 0x%02hhx, mode = %hhu, filename = \"%s\"\n",
-                _current_appkey.creator, _current_appkey.app, _current_appkey.key, _current_appkey.mode,
-                _generate_appkey_filename(&_current_appkey));
+    
+    return result;
 }
 
 /*
   The app key close operation is a placeholder in case we want to provide more robust file
   read/write operations. Currently, the file is closed immediately after the read or write operation.
 */
-void drivewireFuji::close_app_key()
+int drivewireFuji::close_app_key(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     Debug_print("Fuji cmd: CLOSE APPKEY\n");
     _current_appkey.creator = 0;
     _current_appkey.mode = APPKEYMODE_INVALID;
     errorCode = 1;
+    
+    resetState();
+    
+    return result;
 }
 
 /*
  Write an "app key" to SD (ONLY!) storage.
 */
-void drivewireFuji::write_app_key()
+int drivewireFuji::write_app_key(std::vector<uint8_t> *q)
 {
-    uint8_t lenh = fnDwCom.read();
-    uint8_t lenl = fnDwCom.read();
-    uint16_t len = lenh << 8 | lenl;
-    uint8_t value[MAX_APPKEY_LEN];
-
-    memset(value,0,sizeof(value));
-
-    fnDwCom.readBytes(value, len);
-
-    // Make sure we have valid app key information
-    if (_current_appkey.creator == 0 || _current_appkey.mode != APPKEYMODE_WRITE)
-    {
-        Debug_println("Invalid app key metadata - aborting");
-        errorCode = 144;
-        return;
-    }
-
-    // Make sure we have an SD card mounted
-    if (fnSDFAT.running() == false)
-    {
-        Debug_println("No SD mounted - can't write app key");
-        errorCode = 144;
-        return;
-    }
-
-    char *filename = _generate_appkey_filename(&_current_appkey);
-
-    // Reset the app key data so we require calling APPKEY OPEN before another attempt
-    _current_appkey.creator = 0;
-    _current_appkey.mode = APPKEYMODE_INVALID;
-
-    Debug_printf("Writing appkey to \"%s\"\n", filename);
-
-    // Make sure we have a "/FujiNet" directory, since that's where we're putting these files
-    fnSDFAT.create_path("/FujiNet");
-
-    FILE *fOut = fnSDFAT.file_open(filename, "w");
-    if (fOut == nullptr)
-    {
-        Debug_printf("Failed to open/create output file: errno=%d\n", errno);
-        errorCode = 144;
-        return;
-    }
-    size_t count = fwrite(value, 1, len, fOut);
-    int e = errno;
-
-    fclose(fOut);
-
-    if (count != len)
+    int result = 0;
+    int expectedResult = 3 + MAX_APPKEY_LEN;
+    
+    if (q->size() >= expectedResult)
     {
-        Debug_printf("Only wrote %u bytes of expected %hu, errno=%d\n", count, len, e);
-        errorCode = 144;
+      result = expectedResult;
+      uint8_t lenh = q->at(1);
+      uint8_t lenl = q->at(2);
+      uint16_t len = lenh << 8 | lenl;
+      uint8_t value[MAX_APPKEY_LEN];
+  
+      memset(value,0,sizeof(value));
+  
+      std::vector<uint8_t> buff(256);
+      std::copy(q->begin() + 3, q->begin() + 3 + MAX_APPKEY_LEN, buff.begin());
+      memcpy((uint8_t *)value, buff.data(), MAX_APPKEY_LEN);
+  
+      // Make sure we have valid app key information
+      if (_current_appkey.creator == 0 || _current_appkey.mode != APPKEYMODE_WRITE)
+      {
+          Debug_println("Invalid app key metadata - aborting");
+          errorCode = 144;
+          return result;
+      }
+  
+      // Make sure we have an SD card mounted
+      if (fnSDFAT.running() == false)
+      {
+          Debug_println("No SD mounted - can't write app key");
+          errorCode = 144;
+          return result;
+      }
+  
+      char *filename = _generate_appkey_filename(&_current_appkey);
+  
+      // Reset the app key data so we require calling APPKEY OPEN before another attempt
+      _current_appkey.creator = 0;
+      _current_appkey.mode = APPKEYMODE_INVALID;
+  
+      Debug_printf("Writing appkey to \"%s\"\n", filename);
+  
+      // Make sure we have a "/FujiNet" directory, since that's where we're putting these files
+      fnSDFAT.create_path("/FujiNet");
+  
+      FILE *fOut = fnSDFAT.file_open(filename, "w");
+      if (fOut == nullptr)
+      {
+          Debug_printf("Failed to open/create output file: errno=%d\n", errno);
+          errorCode = 144;
+          return result;
+      }
+      size_t count = fwrite(value, 1, len, fOut);
+      int e = errno;
+  
+      fclose(fOut);
+  
+      if (count != len)
+      {
+          Debug_printf("Only wrote %u bytes of expected %hu, errno=%d\n", count, len, e);
+          errorCode = 144;
+      }
+      errorCode = 1;
+      
+      resetState();
     }
-    errorCode = 1;
+        
+    return result;
 }
 
 /*
  Read an "app key" from SD (ONLY!) storage
 */
-void drivewireFuji::read_app_key()
+int drivewireFuji::read_app_key(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     Debug_println("Fuji cmd: READ APPKEY");
 
     // Make sure we have an SD card mounted
@@ -676,7 +826,7 @@ void drivewireFuji::read_app_key()
     {
         Debug_println("No SD mounted - can't read app key");
         errorCode = 144;
-        return;
+        return result;
     }
 
     // Make sure we have valid app key information
@@ -684,7 +834,7 @@ void drivewireFuji::read_app_key()
     {
         Debug_println("Invalid app key metadata - aborting");
         errorCode = 144;
-        return;
+        return result;
     }
 
     char *filename = _generate_appkey_filename(&_current_appkey);
@@ -696,7 +846,7 @@ void drivewireFuji::read_app_key()
     {
         Debug_printf("Failed to open input file: errno=%d\n", errno);
         errorCode = 144;
-        return;
+        return result;
     }
 
     std::vector<uint8_t> buffer(MAX_APPKEY_LEN);
@@ -711,22 +861,36 @@ void drivewireFuji::read_app_key()
     response.append(reinterpret_cast<char*>(buffer.data()), count);
 
     errorCode = 1;
+
+    resetState();
+    
+    return result;
 }
 
 // Disk Image Unmount
-void drivewireFuji::disk_image_umount()
+int drivewireFuji::disk_image_umount(std::vector<uint8_t> *q)
 {
-    uint8_t deviceSlot = fnDwCom.read();
-
-    Debug_printf("Fuji cmd: UNMOUNT IMAGE 0x%02X\n", deviceSlot);
-
-    // Handle disk slots
-    if (deviceSlot < MAX_DISK_DEVICES)
+    int result = 0;
+    int expectedResult = 2;
+    
+    if (q->size() >= expectedResult)
     {
-        _fnDisks[deviceSlot].disk_dev.unmount();
-        _fnDisks[deviceSlot].disk_dev.device_active = false;
-        _fnDisks[deviceSlot].reset();
-    }
+      result = expectedResult;
+      uint8_t deviceSlot = q->at(1);
+  
+      Debug_printf("Fuji cmd: UNMOUNT IMAGE 0x%02X\n", deviceSlot);
+  
+      // Handle disk slots
+      if (deviceSlot < MAX_DISK_DEVICES)
+      {
+          _fnDisks[deviceSlot].disk_dev.unmount();
+          _fnDisks[deviceSlot].disk_dev.device_active = false;
+          _fnDisks[deviceSlot].reset();
+      }
+      
+      resetState();
+    }    
+    return result;
 }
 
 // Disk Image Rotate
@@ -777,22 +941,30 @@ void drivewireFuji::image_rotate()
 }
 
 // This gets called when we're about to shutdown/reboot
-void drivewireFuji::shutdown()
+void drivewireFuji::shutdown(void)
 {
     for (int i = 0; i < MAX_DISK_DEVICES; i++)
         _fnDisks[i].disk_dev.unmount();
-}
+ }
 
-void drivewireFuji::open_directory()
+int drivewireFuji::open_directory(std::vector<uint8_t> *q)
 {
-    Debug_println("Fuji cmd: OPEN DIRECTORY");
-
-    uint8_t hostSlot = fnDwCom.read();
+    int result = 0;
+    int expectedResult = 2 + 256;
+    
+    if (q->size() >= expectedResult)
+    {
+      result = expectedResult;
+      Debug_println("Fuji cmd: OPEN DIRECTORY");
 
-    fnDwCom.readBytes((uint8_t *)&dirpath, 256);
+      uint8_t hostSlot = q->at(1);
 
-    if (_current_open_directory_slot == -1)
-    {
+      std::vector<uint8_t> buff(256);
+      std::copy(q->begin() + 2, q->begin() + 2 + 256, buff.begin());
+      memcpy((uint8_t *)dirpath, buff.data(), 256);
+  
+      if (_current_open_directory_slot == -1)
+      {
         // See if there's a search pattern after the directory path
         const char *pattern = nullptr;
         int pathlen = strnlen(dirpath, sizeof(dirpath));
@@ -803,18 +975,23 @@ void drivewireFuji::open_directory()
             if (patternlen < 1)
                 pattern = nullptr;
         }
-
+  
         // Remove trailing slash
         if (pathlen > 1 && dirpath[pathlen - 1] == '/')
             dirpath[pathlen - 1] = '\0';
-
+  
         Debug_printf("Opening directory: \"%s\", pattern: \"%s\"\n", dirpath, pattern ? pattern : "");
-
+  
         if (_fnHosts[hostSlot].dir_open(dirpath, pattern, 0))
         {
             _current_open_directory_slot = hostSlot;
         }
+      }
+      
+      resetState();
     }
+    
+    return result;
 }
 
 void _set_additional_direntry_details(fsdir_entry_t *f, uint8_t *dest, uint8_t maxlen)
@@ -861,95 +1038,127 @@ void _set_additional_direntry_details(fsdir_entry_t *f, uint8_t *dest, uint8_t m
 
 char current_entry[256];
 
-void drivewireFuji::read_directory_entry()
+int drivewireFuji::read_directory_entry(std::vector<uint8_t> *q)
 {
-    uint8_t maxlen = fnDwCom.read();
-    uint8_t addtl = fnDwCom.read();
-
-    Debug_printf("Fuji cmd: READ DIRECTORY ENTRY (max=%hu) (addtl=%02x)\n", maxlen, addtl);
-
-    memset(current_entry, 0, sizeof(current_entry));
-
-    fsdir_entry_t *f = _fnHosts[_current_open_directory_slot].dir_nextfile();
-
-    if (f == nullptr)
-    {
-        Debug_println("Reached end of of directory");
-        current_entry[0] = 0x7F;
-        current_entry[1] = 0x7F;
-    }
-    else
+    int result = 0;
+    int expectedResult = 3;
+    
+    if (q->size() >= expectedResult)
     {
-        Debug_printf("::read_direntry \"%s\"\n", f->filename);
-
-        int bufsize = sizeof(current_entry);
-        int fno=0;
-
-        // If 0x80 is set on AUX2, send back additional information
-        if (addtl & 0x80)
-        {
-            Debug_printf("Add additional info.\n");
-            _set_additional_direntry_details(f, (uint8_t *)current_entry, maxlen);
-            // Adjust remaining size of buffer and file path destination
-            bufsize = sizeof(dirpath) - ADDITIONAL_DETAILS_BYTES;
-            fno += ADDITIONAL_DETAILS_BYTES;
-        }
-        else
-        {
-            bufsize = maxlen;
-        }
-
-        // int filelen = strlcpy(filenamedest, f->filename, bufsize);
-        int filelen = util_ellipsize(f->filename, &current_entry[fno], bufsize);
-
-        // Add a slash at the end of directory entries
-        if (f->isDir && filelen < (bufsize - 2))
-        {
-            current_entry[filelen] = '/';
-            current_entry[filelen + 1] = '\0';
-        }
+      result = expectedResult;
+    
+      uint8_t maxlen = q->at(1);
+      uint8_t addtl = q->at(2);
+
+      Debug_printf("Fuji cmd: READ DIRECTORY ENTRY (max=%hu) (addtl=%02x)\n", maxlen, addtl);
+  
+      memset(current_entry, 0, sizeof(current_entry));
+  
+      fsdir_entry_t *f = _fnHosts[_current_open_directory_slot].dir_nextfile();
+  
+      if (f == nullptr)
+      {
+          Debug_println("Reached end of of directory");
+          current_entry[0] = 0x7F;
+          current_entry[1] = 0x7F;
+      }
+      else
+      {
+          Debug_printf("::read_direntry \"%s\"\n", f->filename);
+  
+          int bufsize = sizeof(current_entry);
+          int fno=0;
+  
+          // If 0x80 is set on AUX2, send back additional information
+          if (addtl & 0x80)
+          {
+              Debug_printf("Add additional info.\n");
+              _set_additional_direntry_details(f, (uint8_t *)current_entry, maxlen);
+              // Adjust remaining size of buffer and file path destination
+              bufsize = sizeof(dirpath) - ADDITIONAL_DETAILS_BYTES;
+              fno += ADDITIONAL_DETAILS_BYTES;
+          }
+          else
+          {
+              bufsize = maxlen;
+          }
+  
+          // int filelen = strlcpy(filenamedest, f->filename, bufsize);
+          int filelen = util_ellipsize(f->filename, &current_entry[fno], bufsize);
+  
+          // Add a slash at the end of directory entries
+          if (f->isDir && filelen < (bufsize - 2))
+          {
+              current_entry[filelen] = '/';
+              current_entry[filelen + 1] = '\0';
+          }
+      }
+      
+      response.clear();
+      response.shrink_to_fit();
+  
+      response = std::string((const char *)current_entry, maxlen);
+    
+      resetState();
     }
     
-    response.clear();
-    response.shrink_to_fit();
-
-    response = std::string((const char *)current_entry, maxlen);
+    return result;
 }
 
-void drivewireFuji::get_directory_position()
+int drivewireFuji::get_directory_position(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     Debug_println("Fuji cmd: GET DIRECTORY POSITION");
 
     uint16_t pos = _fnHosts[_current_open_directory_slot].dir_tell();
 
     // Return the value we read
-    fnDwCom.write(pos << 8);
-    fnDwCom.write(pos & 0xFF);
+    fnDwCom.writeToFNChannel(0, pos << 8);
+    fnDwCom.writeToFNChannel(0, pos & 0xFF);
 
     errorCode = 1;
+    
+    resetState();
+    
+    return result;
 }
 
-void drivewireFuji::set_directory_position()
+int drivewireFuji::set_directory_position(std::vector<uint8_t> *q)
 {
-    uint8_t h, l;
-
-    Debug_println("Fuji cmd: SET DIRECTORY POSITION");
-
-    // DAUX1 and DAUX2 hold the position to seek to in low/high order
-    h = fnDwCom.read();
-    l = fnDwCom.read();
-
-    Debug_printf("H: %02x L: %02x", h, l);
-
-    uint16_t pos = UINT16_FROM_HILOBYTES(h, l);
-
-    bool result = _fnHosts[_current_open_directory_slot].dir_seek(pos);
-
-    errorCode = (result == true);
+    int result = 0;
+    int expectedResult = 3;
+    
+    if (q->size() >= expectedResult)
+    {
+      result = expectedResult;
+    
+      uint8_t h, l;
+  
+      Debug_println("Fuji cmd: SET DIRECTORY POSITION");
+  
+      // DAUX1 and DAUX2 hold the position to seek to in low/high order
+      h = q->at(1);
+      l = q->at(2);
+  
+      Debug_printf("H: %02x L: %02x", h, l);
+  
+      uint16_t pos = UINT16_FROM_HILOBYTES(h, l);
+  
+      bool seekResult = _fnHosts[_current_open_directory_slot].dir_seek(pos);
+  
+      errorCode = (seekResult == true);
+      
+      resetState();
+    }
+        
+    return result;
 }
 
-void drivewireFuji::close_directory()
+int drivewireFuji::close_directory(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     Debug_println("Fuji cmd: CLOSE DIRECTORY");
 
     if (_current_open_directory_slot != -1)
@@ -957,11 +1166,17 @@ void drivewireFuji::close_directory()
 
     _current_open_directory_slot = -1;
     errorCode = 1;
+    
+    resetState();
+    
+    return result;
 }
 
 // Get network adapter configuration 
-void drivewireFuji::get_adapter_config()
+int drivewireFuji::get_adapter_config(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     Debug_println("Fuji cmd: GET ADAPTER CONFIG");
 
     // Response to  FUJICMD_GET_ADAPTERCONFIG
@@ -991,11 +1206,17 @@ void drivewireFuji::get_adapter_config()
 
     errorCode = 1;
     response = std::string((const char *)&cfg, sizeof(cfg));
+    
+    resetState();
+    
+    return result;
 }
 
 // Get network adapter configuration - extended
-void drivewireFuji::get_adapter_config_extended()
+int drivewireFuji::get_adapter_config_extended(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     // also return string versions of the data to save the host some computing
     Debug_printf("Fuji cmd: GET ADAPTER CONFIG EXTENDED\r\n");
     AdapterConfigExtended cfg;
@@ -1032,13 +1253,15 @@ void drivewireFuji::get_adapter_config_extended()
 
     errorCode = 1;
     response = std::string((const char *)&cfg, sizeof(cfg));
+    
+    resetState();
+    
+    return result;
 }
 
 //  Make new disk and shove into device slot
-void drivewireFuji::new_disk()
+int drivewireFuji::new_disk(std::vector<uint8_t> *q)
 {
-    Debug_println("Fuji cmd: NEW DISK");
-
     struct
     {
         unsigned char numDisks;
@@ -1047,67 +1270,95 @@ void drivewireFuji::new_disk()
         char filename[MAX_FILENAME_LEN]; // WIll set this to MAX_FILENAME_LEN, later.
     } newDisk;
 
-    fnDwCom.readBytes((uint8_t *)&newDisk, sizeof(newDisk));
-
-    Debug_printf("numDisks: %u\n",newDisk.numDisks);
-    Debug_printf("hostSlot: %u\n",newDisk.hostSlot);
-    Debug_printf("deviceSl: %u\n",newDisk.deviceSlot);
-    Debug_printf("filename: %s\n",newDisk.filename);
-
-    // A couple of reference variables to make things much easier to read...
-    fujiDisk &disk = _fnDisks[newDisk.deviceSlot];
-    fujiHost &host = _fnHosts[newDisk.hostSlot];
-
-    disk.host_slot = newDisk.hostSlot;
-    disk.access_mode = DISK_ACCESS_MODE_WRITE;
-    strlcpy(disk.filename, newDisk.filename, sizeof(disk.filename));
-
-    if (host.file_exists(disk.filename))
-    {
-        Debug_printf("drivewire_new_disk File exists: \"%s\"\n", disk.filename);
-        errorCode = 144;
-        return;
-    }
-
-    disk.fileh = host.fnfile_open(disk.filename, disk.filename, sizeof(disk.filename), "w");
-    if (disk.fileh == nullptr)
+    int result = 0;
+    int expectedResult = 1 + sizeof(newDisk);
+    
+    if (q->size() >= expectedResult)
     {
-        Debug_printf("drivewire_new_disk Couldn't open file for writing: \"%s\"\n", disk.filename);
-        return;
+      result = expectedResult;
+      Debug_println("Fuji cmd: NEW DISK");
+
+      std::vector<uint8_t> buff(256);
+      std::copy(q->begin() + 1, q->begin() + 1 + sizeof(newDisk), buff.begin());
+      memcpy((uint8_t *)&newDisk, buff.data(), sizeof(newDisk));
+  
+      Debug_printf("numDisks: %u\n",newDisk.numDisks);
+      Debug_printf("hostSlot: %u\n",newDisk.hostSlot);
+      Debug_printf("deviceSl: %u\n",newDisk.deviceSlot);
+      Debug_printf("filename: %s\n",newDisk.filename);
+  
+      // A couple of reference variables to make things much easier to read...
+      fujiDisk &disk = _fnDisks[newDisk.deviceSlot];
+      fujiHost &host = _fnHosts[newDisk.hostSlot];
+  
+      disk.host_slot = newDisk.hostSlot;
+      disk.access_mode = DISK_ACCESS_MODE_WRITE;
+      strlcpy(disk.filename, newDisk.filename, sizeof(disk.filename));
+  
+      if (host.file_exists(disk.filename))
+      {
+          Debug_printf("drivewire_new_disk File exists: \"%s\"\n", disk.filename);
+          errorCode = 144;
+          return result;
+      }
+  
+      disk.fileh = host.fnfile_open(disk.filename, disk.filename, sizeof(disk.filename), "w");
+      if (disk.fileh == nullptr)
+      {
+          Debug_printf("drivewire_new_disk Couldn't open file for writing: \"%s\"\n", disk.filename);
+          return result;
+      }
+  
+      bool ok = disk.disk_dev.write_blank(disk.fileh, newDisk.numDisks);
+  
+      errorCode = (ok == NETWORK_ERROR_SUCCESS);
+  
+      fnio::fclose(disk.fileh);
+      
+      resetState();
     }
-
-    bool ok = disk.disk_dev.write_blank(disk.fileh, newDisk.numDisks);
-
-    errorCode = (ok == NETWORK_ERROR_SUCCESS);
-
-    fnio::fclose(disk.fileh);
+    
+    return result;
 }
 
 // Unmount specified host
-void drivewireFuji::unmount_host()
+int drivewireFuji::unmount_host(std::vector<uint8_t> *q)
 {
-    Debug_println("Fuji cmd: UNMOUNT HOST");
-
-    unsigned char hostSlot = fnDwCom.read();
-
-    // Unmount any disks associated with host slot
-    for (int i = 0; i < MAX_DISK_DEVICES; i++)
+    int result = 0;
+    int expectedResult = 2;
+    
+    if (q->size() >= expectedResult)
     {
-        if (_fnDisks[i].host_slot == hostSlot)
-        {
-            _fnDisks[i].disk_dev.unmount();
-            _fnDisks[i].disk_dev.device_active = false;
-            _fnDisks[i].reset();
-        }
+      result = expectedResult;
+      Debug_println("Fuji cmd: UNMOUNT HOST");
+  
+      unsigned char hostSlot = q->at(1);
+  
+      // Unmount any disks associated with host slot
+      for (int i = 0; i < MAX_DISK_DEVICES; i++)
+      {
+          if (_fnDisks[i].host_slot == hostSlot)
+          {
+              _fnDisks[i].disk_dev.unmount();
+              _fnDisks[i].disk_dev.device_active = false;
+              _fnDisks[i].reset();
+          }
+      }
+  
+      // Unmount the host
+      _fnHosts[hostSlot].umount();
+
+      resetState();
     }
-
-    // Unmount the host
-    _fnHosts[hostSlot].umount();
+        
+    return result;
 }
 
 // Send host slot data to computer
-void drivewireFuji::read_host_slots()
+int drivewireFuji::read_host_slots(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     Debug_println("Fuji cmd: READ HOST SLOTS");
 
     char hostSlots[MAX_HOSTS][MAX_HOSTNAME_LEN];
@@ -1121,26 +1372,46 @@ void drivewireFuji::read_host_slots()
 
     response = std::string((const char *)hostSlots,256);
     errorCode = 1;
+    
+    resetState();
+    
+    return result;
 }
 
 // Read and save host slot data from computer
-void drivewireFuji::write_host_slots()
+int drivewireFuji::write_host_slots(std::vector<uint8_t> *q)
 {
-    Debug_println("Fuji cmd: WRITE HOST SLOTS");
-
     char hostSlots[MAX_HOSTS][MAX_HOSTNAME_LEN];
-    fnDwCom.readBytes((uint8_t *)&hostSlots, sizeof(hostSlots));
 
-    for (int i = 0; i < MAX_HOSTS; i++)
-        _fnHosts[i].set_hostname(hostSlots[i]);
-
-    _populate_config_from_slots();
-    Config.save();
+    int result = 0;
+    int expectedResult = 1 + sizeof(hostSlots);
+    
+    if (q->size() >= expectedResult)
+    {
+      result = expectedResult;
+      Debug_println("Fuji cmd: WRITE HOST SLOTS");
+  
+      std::vector<uint8_t> buff(256);
+      std::copy(q->begin() + 1, q->begin() + 1 + sizeof(hostSlots), buff.begin());
+      memcpy((uint8_t *)&hostSlots, buff.data(), sizeof(hostSlots));
+  
+      for (int i = 0; i < MAX_HOSTS; i++)
+          _fnHosts[i].set_hostname(hostSlots[i]);
+  
+      _populate_config_from_slots();
+      Config.save();
+
+      resetState();
+    }
+        
+    return result;
 }
 
 // Send device slot data to computer
-void drivewireFuji::read_device_slots()
+int drivewireFuji::read_device_slots(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     Debug_println("Fuji cmd: READ DEVICE SLOTS");
 
     struct disk_slot
@@ -1186,13 +1457,15 @@ void drivewireFuji::read_device_slots()
     errorCode = 1;
 
     response = std::string((const char *)&diskSlots, returnsize);
+    
+    resetState();
+    
+    return result;
 }
 
 // Read and save disk slot data from computer
-void drivewireFuji::write_device_slots()
+int drivewireFuji::write_device_slots(std::vector<uint8_t> *q)
 {
-    Debug_println("Fuji cmd: WRITE DEVICE SLOTS");
-
     struct
     {
         uint8_t hostSlot;
@@ -1200,15 +1473,30 @@ void drivewireFuji::write_device_slots()
         char filename[MAX_DISPLAY_FILENAME_LEN];
     } diskSlots[MAX_DISK_DEVICES];
 
-    fnDwCom.readBytes((uint8_t *)&diskSlots, sizeof(diskSlots));
-
-    // Load the data into our current device array
-    for (int i = 0; i < MAX_DISK_DEVICES; i++)
-        _fnDisks[i].reset(diskSlots[i].filename, diskSlots[i].hostSlot, diskSlots[i].mode);
-
-    // Save the data to disk
-    _populate_config_from_slots();
-    Config.save();
+    int result = 0;
+    int expectedResult = 1 + sizeof(diskSlots);
+    
+    if (q->size() >= expectedResult)
+    {
+      result = expectedResult;
+      Debug_println("Fuji cmd: WRITE DEVICE SLOTS");
+  
+      std::vector<uint8_t> buff(256);
+      std::copy(q->begin() + 1, q->begin() + 1 + sizeof(diskSlots), buff.begin());
+      memcpy((uint8_t *)&diskSlots, buff.data(), sizeof(diskSlots));
+  
+      // Load the data into our current device array
+      for (int i = 0; i < MAX_DISK_DEVICES; i++)
+          _fnDisks[i].reset(diskSlots[i].filename, diskSlots[i].hostSlot, diskSlots[i].mode);
+  
+      // Save the data to disk
+      _populate_config_from_slots();
+      Config.save();
+
+      resetState();
+    }
+        
+    return result;
 }
 
 // Temporary(?) function while we move from old config storage to new
@@ -1272,57 +1560,86 @@ void drivewireFuji::_populate_config_from_slots()
 }
 
 // Write a 256 byte filename to the device slot
-void drivewireFuji::set_device_filename()
+int drivewireFuji::set_device_filename(std::vector<uint8_t> *q)
 {
-    char tmp[MAX_FILENAME_LEN];
-
-    // AUX1 is the desired device slot
-    uint8_t slot = fnDwCom.read();
-    // AUX2 contains the host slot and the mount mode (READ/WRITE)
-    uint8_t host = fnDwCom.read();
-    uint8_t mode = fnDwCom.read();
-
-    fnDwCom.readBytes((uint8_t *)tmp, MAX_FILENAME_LEN);
-
-    Debug_printf("Fuji cmd: SET DEVICE SLOT 0x%02X/%02X/%02X FILENAME: %s\n", slot, host, mode, tmp);
-
-    // Handle DISK slots
-    if (slot < MAX_DISK_DEVICES)
+    int result = 0;
+    int expectedResult = 4 + MAX_FILENAME_LEN;
+    
+    if (q->size() >= expectedResult)
     {
-        memcpy(_fnDisks[slot].filename, tmp, MAX_FILENAME_LEN);
-        // If the filename is empty, mark this as an invalid host, so that mounting will ignore it too
-        if (strlen(_fnDisks[slot].filename) == 0) {
-            _fnDisks[slot].host_slot = INVALID_HOST_SLOT;
-        } else {
-            _fnDisks[slot].host_slot = host;
-        }
-        _fnDisks[slot].access_mode = mode;
-        _populate_config_from_slots();
+      result = expectedResult;
+  
+      char tmp[MAX_FILENAME_LEN];
+  
+      // AUX1 is the desired device slot
+      uint8_t slot = q->at(1);
+      // AUX2 contains the host slot and the mount mode (READ/WRITE)
+      uint8_t host = q->at(2);
+      uint8_t mode = q->at(3);
+  
+      std::vector<uint8_t> buff(256);
+      std::copy(q->begin() + 4, q->begin() + 4 + MAX_FILENAME_LEN, buff.begin());
+      memcpy((uint8_t *)&tmp, buff.data(), MAX_FILENAME_LEN);
+    
+      Debug_printf("Fuji cmd: SET DEVICE SLOT 0x%02X/%02X/%02X FILENAME: %s\n", slot, host, mode, tmp);
+  
+      // Handle DISK slots
+      if (slot < MAX_DISK_DEVICES)
+      {
+          memcpy(_fnDisks[slot].filename, tmp, MAX_FILENAME_LEN);
+          // If the filename is empty, mark this as an invalid host, so that mounting will ignore it too
+          if (strlen(_fnDisks[slot].filename) == 0) {
+              _fnDisks[slot].host_slot = INVALID_HOST_SLOT;
+          } else {
+              _fnDisks[slot].host_slot = host;
+          }
+          _fnDisks[slot].access_mode = mode;
+          _populate_config_from_slots();
+      }
+  
+      Config.save();
+    
+      resetState();
     }
-
-    Config.save();
+        
+    return result;
 }
 
 // Get a 256 byte filename from device slot
-void drivewireFuji::get_device_filename()
+int drivewireFuji::get_device_filename(std::vector<uint8_t> *q)
 {
-    char tmp[MAX_FILENAME_LEN];
-
-    // AUX1 is the desired device slot
-    uint8_t slot = fnDwCom.read();
-
-    if (slot > 7)
+    int result = 0;
+    int expectedResult = 2;
+    
+    if (q->size() >= expectedResult)
     {
-        errorCode = 144;
-    }
-
-    memcpy(tmp, _fnDisks[slot].filename, MAX_FILENAME_LEN);
-    response.clear();
-    response.shrink_to_fit();
-
-    errorCode = 1;
-
-    response = std::string(tmp, MAX_FILENAME_LEN);
+      result = expectedResult;
+      char tmp[MAX_FILENAME_LEN] = {0};
+  
+      // AUX1 is the desired device slot
+      uint8_t slot = q->at(1);
+  
+      if (slot > 7)
+      {
+          errorCode = 144;
+      }
+      else
+      {  
+          memcpy(tmp, _fnDisks[slot].filename, MAX_FILENAME_LEN);
+      }
+      response.clear();
+      response.shrink_to_fit();
+  
+      Debug_printf("Fuji cmd: GET DEVICE SLOT 0x%02X FILENAME: %s\n", slot, tmp);
+
+      errorCode = 1;
+  
+      response = std::string(tmp, MAX_FILENAME_LEN);
+      
+      resetState();
+    } 
+       
+    return result;
 }
 
 // Mounts the desired boot disk number
@@ -1360,27 +1677,56 @@ void drivewireFuji::insert_boot_device(uint8_t d)
     }
 }
 
-void drivewireFuji::base64_encode_input()
+int drivewireFuji::base64_encode_input(std::vector<uint8_t> *q)
 {
-    uint8_t lenh = fnDwCom.read();
-    uint8_t lenl = fnDwCom.read();
-    uint16_t len = lenh << 8 | lenl;
-
-    if (!len)
+    int result = 0;
+    int expectedResult = 3;
+    
+    if (q->size() >= expectedResult)
     {
-        Debug_printf("Zero length. Aborting.\n");
-        errorCode = 144;
-        return;
+      result = expectedResult;
+      uint8_t lenh = q->at(1);
+      uint8_t lenl = q->at(2);
+      base64_encode_length_var = lenh << 8 | lenl;
+
+      if (!base64_encode_length_var)
+      {
+          Debug_printf("Zero length. Aborting.\n");
+          errorCode = 144;
+          resetState();
+          return result;
+      }
+
+      _drivewire_bus->fnStateMethod = &drivewireFuji::base64_encode_input_p2;
     }
+        
+    return result;
+}
 
-    std::vector<unsigned char> p(len);
-    fnDwCom.readBytes(p.data(), len);
-    base64.base64_buffer += std::string((const char *)p.data(), len);
-    errorCode = 1;
+int drivewireFuji::base64_encode_input_p2(std::vector<uint8_t> *q)
+{
+    int result = 0;
+    int expectedResult = base64_encode_length_var;
+    
+    if (q->size() >= expectedResult)
+    {
+      result = expectedResult;
+
+      std::vector<unsigned char> p(base64_encode_length_var);
+      std::copy(q->begin(), q->begin() + base64_encode_length_var - 1, p.begin());
+      base64.base64_buffer += std::string((const char *)p.data(), base64_encode_length_var);
+      errorCode = 1;
+      
+      resetState();
+    }
+      
+    return result;
 }
 
-void drivewireFuji::base64_encode_compute()
+int drivewireFuji::base64_encode_compute(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     size_t out_len;
 
     std::unique_ptr<char[]> p = Base64::encode(base64.base64_buffer.c_str(), base64.base64_buffer.size(), &out_len);
@@ -1389,17 +1735,23 @@ void drivewireFuji::base64_encode_compute()
     {
         Debug_printf("base64_encode_compute() failed.\n");
         errorCode = 144;
-        return;
+        return result;
     }
 
     base64.base64_buffer.clear();
     base64.base64_buffer = std::string(p.get(), out_len);
 
     errorCode = 1;
+    
+    resetState();
+    
+    return result;
 }
 
-void drivewireFuji::base64_encode_length()
+int drivewireFuji::base64_encode_length(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     size_t l = base64.base64_buffer.length();
     uint8_t o[4] = 
     {
@@ -1415,52 +1767,95 @@ void drivewireFuji::base64_encode_length()
     response = std::string((const char *)&o, 4);
 
     errorCode = 1;
+
+    resetState();
+        
+    return result;
 }
 
-void drivewireFuji::base64_encode_output()
+int drivewireFuji::base64_encode_output(std::vector<uint8_t> *q)
 {
-    uint8_t lenl = fnDwCom.read();
-    uint8_t lenh = fnDwCom.read();
-    uint16_t len = lenh << 8 | lenl;
-
-    if (!len)
+    int result = 0;
+    int expectedResult = 3;
+    
+    if (q->size() >= expectedResult)
     {
-        Debug_printf("Refusing to send zero byte buffer. Exiting.");
-        errorCode = 144; 
-        return;
+      result = expectedResult;
+      uint8_t lenl = q->at(1);
+      uint8_t lenh = q->at(2);
+      uint16_t len = lenh << 8 | lenl;
+  
+      if (!len)
+      {
+          Debug_printf("Refusing to send zero byte buffer. Exiting.");
+          errorCode = 144; 
+          return result;
+      }
+  
+      std::vector<unsigned char> p(len);
+      std::memcpy(p.data(), base64.base64_buffer.data(), len);
+      base64.base64_buffer.erase(0, len);
+      base64.base64_buffer.shrink_to_fit();
+  
+      response = std::string((const char *)p.data(), len);
+      errorCode = 1; 
+    
+      resetState();
     }
-
-    std::vector<unsigned char> p(len);
-    std::memcpy(p.data(), base64.base64_buffer.data(), len);
-    base64.base64_buffer.erase(0, len);
-    base64.base64_buffer.shrink_to_fit();
-
-    response = std::string((const char *)p.data(), len);
-    errorCode = 1;    
+        
+    return result;   
 }
 
-void drivewireFuji::base64_decode_input()
+int drivewireFuji::base64_decode_input(std::vector<uint8_t> *q)
 {
-    uint8_t lenl = fnDwCom.read();
-    uint8_t lenh = fnDwCom.read();
-    uint16_t len = lenh << 8 | lenl;
-
-    if (!len)
+    int result = 0;
+    int expectedResult = 3;
+    
+    if (q->size() >= expectedResult)
     {
-        Debug_printf("Refusing to input zero length. Exiting.\n");
-        errorCode = 144;
-        return;
+      result = expectedResult;
+      uint8_t lenh = q->at(1);
+      uint8_t lenl = q->at(2);
+      base64_decode_length_var = lenh << 8 | lenl;
+
+      if (!base64_decode_length_var)
+      {
+          Debug_printf("Refusing to input zero length. Exiting.\n");
+          errorCode = 144;
+          resetState();
+          return result;
+      }
+
+      _drivewire_bus->fnStateMethod = &drivewireFuji::base64_decode_input_p2;
     }
+        
+    return result;
+}
 
-    std::vector<unsigned char> p(len);
-    fnDwCom.readBytes(p.data(), len);
-    base64.base64_buffer += std::string((const char *)p.data(), len);
-
-    errorCode = 1;
+int drivewireFuji::base64_decode_input_p2(std::vector<uint8_t> *q)
+{
+    int result = 0;
+    int expectedResult = base64_decode_length_var;
+    
+    if (q->size() >= expectedResult)
+    {
+      result = expectedResult;
+
+      std::vector<unsigned char> p(base64_decode_length_var);
+      std::copy(q->begin(), q->begin() + base64_decode_length_var - 1, p.begin());
+      base64.base64_buffer += std::string((const char *)p.data(), base64_decode_length_var);
+      errorCode = 1;
+      
+      resetState();
+    }
+    
+    return result;
 }
 
-void drivewireFuji::base64_decode_compute()
+int drivewireFuji::base64_decode_compute(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     size_t out_len;
 
     Debug_printf("FUJI: BASE64 DECODE COMPUTE\n");
@@ -1470,7 +1865,7 @@ void drivewireFuji::base64_decode_compute()
     {
         Debug_printf("base64_encode compute failed\n");
         errorCode = 144;
-        return;
+        return result;
     }
 
     base64.base64_buffer.clear();
@@ -1478,10 +1873,16 @@ void drivewireFuji::base64_decode_compute()
 
     Debug_printf("Resulting BASE64 encoded data is: %u bytes\n", out_len);
     errorCode = 1;
+    
+    resetState();
+    
+    return result;
 }
 
-void drivewireFuji::base64_decode_length()
+int drivewireFuji::base64_decode_length(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     Debug_printf("FUJI: BASE64 DECODE LENGTH\n");
 
     size_t len = base64.base64_buffer.length();
@@ -1496,7 +1897,7 @@ void drivewireFuji::base64_decode_length()
     {
         Debug_printf("BASE64 buffer is 0 bytes, sending error.\n");
         errorCode = 144;
-        return;
+        return result;
     }
 
     Debug_printf("base64 buffer length: %u bytes\n", len);
@@ -1506,101 +1907,202 @@ void drivewireFuji::base64_decode_length()
 
     response = std::string((const char *)_response, 4);
     errorCode = 1;
+    
+    resetState();
+    
+    return result;
 }
 
-void drivewireFuji::base64_decode_output()
+int drivewireFuji::base64_decode_output(std::vector<uint8_t> *q)
 {
-    Debug_printf("FUJI: BASE64 DECODE OUTPUT\n");
-
-    uint8_t lenl = fnDwCom.read();
-    uint8_t lenh = fnDwCom.read();
-    uint16_t len = lenh << 8 | lenl;
-
-    if (!len)
+    int result = 0;
+    int expectedResult = 3;
+    
+    if (q->size() >= expectedResult)
     {
-        Debug_printf("Refusing to send a zero byte buffer. Aborting\n");
-        errorCode = 144;
-        return;
+      result = expectedResult;
+      Debug_printf("FUJI: BASE64 DECODE OUTPUT\n");
+  
+      uint8_t lenl = q->at(1);
+      uint8_t lenh = q->at(2);
+      uint16_t len = lenh << 8 | lenl;
+  
+      if (!len)
+      {
+          Debug_printf("Refusing to send a zero byte buffer. Aborting\n");
+          errorCode = 144;
+          return result;
+      }
+      else if (len > base64.base64_buffer.length())
+      {
+          Debug_printf("Requested %u bytes, but buffer is only %u bytes, aborting.\n", len, base64.base64_buffer.length());
+          errorCode = 144;
+          return result;
+      }
+      else
+      {
+          Debug_printf("Requested %u bytes\n", len);
+      }
+  
+      std::vector<unsigned char> p(len);
+      memcpy(p.data(), base64.base64_buffer.data(), len);
+      base64.base64_buffer.erase(0, len);
+      base64.base64_buffer.shrink_to_fit();
+      response.clear();
+      response.shrink_to_fit();
+      response = std::string((const char *)p.data(), len);
+  
+      errorCode = 1;
+      
+      resetState();
     }
-    else if (len > base64.base64_buffer.length())
+        
+    return result;
+}
+
+int drivewireFuji::hash_input(std::vector<uint8_t> *q)
+{
+    int result = 0;
+    int expectedResult = 3;
+    
+    if (q->size() >= expectedResult)
     {
-        Debug_printf("Requested %u bytes, but buffer is only %u bytes, aborting.\n", len, base64.base64_buffer.length());
-        errorCode = 144;
-        return;
+      Debug_printf("FUJI: HASH INPUT\n");
+      result = expectedResult;
+      uint8_t lenh = q->at(1);
+      uint8_t lenl = q->at(2);
+      hash_length_var = lenh << 8 | lenl;
+
+      if (!hash_length_var)
+      {
+          Debug_printf("Invalid length. Aborting");
+          errorCode = 144;
+          resetState();
+          return result;
+      }
+
+      _drivewire_bus->fnStateMethod = &drivewireFuji::hash_input_p2;
     }
-    else
+        
+    return result;
+}
+
+int drivewireFuji::hash_input_p2(std::vector<uint8_t> *q)
+{
+    int result = 0;
+    int expectedResult = hash_length_var;
+    
+    if (q->size() >= expectedResult)
     {
-        Debug_printf("Requested %u bytes\n", len);
+      result = expectedResult;
+
+      std::vector<unsigned char> p(hash_length_var);
+      std::copy(q->begin(), q->begin() + hash_length_var - 1, p.begin());
+      hasher.add_data(p);
+      errorCode = 1;
+      
+      resetState();
     }
-
-    std::vector<unsigned char> p(len);
-    memcpy(p.data(), base64.base64_buffer.data(), len);
-    base64.base64_buffer.erase(0, len);
-    base64.base64_buffer.shrink_to_fit();
-    response.clear();
-    response.shrink_to_fit();
-    response = std::string((const char *)p.data(), len);
-
-    errorCode = 1;
+    
+    return result;
 }
 
-void drivewireFuji::hash_input()
+int drivewireFuji::state_hash_compute_true(std::vector<uint8_t> *q)
 {
-    Debug_printf("FUJI: HASH INPUT\n");
-    uint8_t lenl = fnDwCom.read();
-    uint8_t lenh = fnDwCom.read();
-    uint16_t len = lenh << 8 | lenl;
+    int result = 0;
+    int expectedResult = 2;
+    
+    if (q->size() >= expectedResult)
+    {
+      result = expectedResult;
+      hash_compute(q->at(1), true);
 
+      resetState();
+    }
+        
+    return result;
+}
 
-    if (!len)
+int drivewireFuji::state_hash_compute_false(std::vector<uint8_t> *q)
+{
+    int result = 0;
+    int expectedResult = 2;
+    
+    if (q->size() >= expectedResult)
     {
-        Debug_printf("Invalid length. Aborting");
-        errorCode = 144;
-        return;
-    }
+      result = expectedResult;
+      hash_compute(q->at(1), false);
 
-    std::vector<uint8_t> p(len);
-    fnDwCom.readBytes(p.data(), len);
-    hasher.add_data(p);
-    errorCode = 1;
+      resetState();
+    }
+        
+    return result;
 }
 
-void drivewireFuji::hash_compute(bool clear_data)
+void drivewireFuji::hash_compute(uint8_t value, bool clear_data)
 {
     Debug_printf("FUJI: HASH COMPUTE\n");
-    algorithm = Hash::to_algorithm(fnDwCom.read());
+    algorithm = Hash::to_algorithm(value);
     hasher.compute(algorithm, clear_data);
     errorCode = 1;
 }
 
-void drivewireFuji::hash_length()
+int drivewireFuji::hash_length(std::vector<uint8_t> *q)
 {
-    Debug_printf("FUJI: HASH LENGTH\n");
-    uint8_t is_hex = fnDwCom.read() == 1;
-    uint8_t r = hasher.hash_length(algorithm, is_hex);
-    response = std::string((const char *)&r, 1);
-    errorCode = 1;
+    int result = 0;
+    int expectedResult = 2;
+    
+    if (q->size() >= expectedResult)
+    {
+      result = expectedResult;
+      Debug_printf("FUJI: HASH LENGTH\n");
+      uint8_t is_hex = q->at(1) == 1;
+      uint8_t r = hasher.hash_length(algorithm, is_hex);
+      response = std::string((const char *)&r, 1);
+      errorCode = 1;
+    
+      resetState();
+    }
+        
+    return result;
 }
 
-void drivewireFuji::hash_output()
+int drivewireFuji::hash_output(std::vector<uint8_t> *q)
 {
-    Debug_printf("FUJI: HASH OUTPUT\n");
-
-    uint8_t is_hex = fnDwCom.read() == 1;
-    if (is_hex) {
-        response = hasher.output_hex();
-    } else {
-        std::vector<uint8_t> hashed_data = hasher.output_binary();
-        response = std::string(hashed_data.begin(), hashed_data.end());
-    }
-    errorCode = 1;
+    int result = 0;
+    int expectedResult = 2;
+    
+    if (q->size() >= expectedResult)
+    {
+      result = expectedResult;
+      Debug_printf("FUJI: HASH OUTPUT\n");
+  
+      uint8_t is_hex = q->at(1) == 1;
+      if (is_hex) {
+          response = hasher.output_hex();
+      } else {
+          std::vector<uint8_t> hashed_data = hasher.output_binary();
+          response = std::string(hashed_data.begin(), hashed_data.end());
+      }
+      errorCode = 1;
+      
+      resetState();
+    }      
+    
+    return result;
 }
 
-void drivewireFuji::hash_clear()
+int drivewireFuji::hash_clear(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     Debug_printf("FUJI: HASH INIT\n");
     hasher.clear();
     errorCode = 1;
+    
+    resetState();
+    
+    return result;
 }
 
 // Initializes base settings and adds our devices to the DRIVEWIRE bus
@@ -1637,14 +2139,22 @@ std::string drivewireFuji::get_host_prefix(int host_slot)
     return _fnHosts[host_slot].get_prefix();
 }
 
-void drivewireFuji::send_error()
+int drivewireFuji::send_error(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     Debug_printf("drivewireFuji::send_error(%u)\n",errorCode);
-    fnDwCom.write(errorCode);
+    fnDwCom.writeToFNChannel(0, errorCode);
+    
+    resetState();
+    
+    return result;
 }
 
-void drivewireFuji::random()
+int drivewireFuji::random(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     int r = rand();
     Debug_printf("drivewireFuji::random(%u)\n",r);
 
@@ -1653,173 +2163,210 @@ void drivewireFuji::random()
 
     // Endianness does not matter, so long as it is random.
     response = std::string((const char *)&r,sizeof(r));
+    
+    resetState();
+    
+    return result;
 }
 
-void drivewireFuji::send_response()
+int drivewireFuji::send_response(std::vector<uint8_t> *q)
 {
+    int result = 1;
+    
     // Send body
-    fnDwCom.write((uint8_t *)response.c_str(),response.length());
+    fnDwCom.writeToFNChannel(0, (uint8_t *)response.c_str(),response.length());
 
     // Clear the response
     response.clear();
-    response.shrink_to_fit();    
+    response.shrink_to_fit();
+    
+    Debug_println("Fuji cmd: SEND_RESPONSE");
+    
+    resetState();
+    
+    return result;    
 }
 
-void drivewireFuji::ready()
+int drivewireFuji::ready(std::vector<uint8_t> *q)
 {
-    fnDwCom.write(0x01); // Yes, ready.
+    int result = 1;
+    
+    fnDwCom.writeToFNChannel(0, 0x01); // Yes, ready.
+    
+    resetState();
+    
+    return result;
 }
 
-void drivewireFuji::process()
+int drivewireFuji::process(std::vector<uint8_t> *q)
 {
-    uint8_t c = fnDwCom.read();
+    int result = 0;
+    
+    uint8_t c = q->at(0);
 
     switch (c)
     {
     case FUJICMD_SEND_ERROR:
-        send_error();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::send_error;
         break;
     case FUJICMD_GET_ADAPTERCONFIG:
-        get_adapter_config();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::get_adapter_config;
         break;
     case FUJICMD_GET_ADAPTERCONFIG_EXTENDED:
-        get_adapter_config_extended();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::get_adapter_config_extended;
         break;
     case FUJICMD_GET_SCAN_RESULT:
-        net_scan_result();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::net_scan_result;
         break;
     case FUJICMD_SCAN_NETWORKS:
-        net_scan_networks();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::net_scan_networks;
         break;
     case FUJICMD_SET_SSID:
-        net_set_ssid();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::net_set_ssid;
         break;
     case FUJICMD_GET_SSID:
-        net_get_ssid();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::net_get_ssid;
         break;
     case FUJICMD_READ_HOST_SLOTS:
-        read_host_slots();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::read_host_slots;
         break;
     case FUJICMD_READ_DEVICE_SLOTS:
-        read_device_slots();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::read_device_slots;
         break;
     case FUJICMD_WRITE_DEVICE_SLOTS:
-        write_device_slots();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::write_device_slots;
         break;
     case FUJICMD_WRITE_HOST_SLOTS:
-        write_host_slots();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::write_host_slots;
         break;
     case FUJICMD_GET_WIFI_ENABLED:
-        net_get_wifi_enabled();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::net_get_wifi_enabled;
         break;
     case FUJICMD_GET_WIFISTATUS:
-        net_get_wifi_status();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::net_get_wifi_status;
         break;
     case FUJICMD_MOUNT_HOST:
-        mount_host();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::mount_host;
         break;
     case FUJICMD_OPEN_DIRECTORY:
-        open_directory();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::open_directory;
         break;
     case FUJICMD_CLOSE_DIRECTORY:
-        close_directory();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::close_directory;
         break;
     case FUJICMD_READ_DIR_ENTRY:
-        read_directory_entry();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::read_directory_entry;
         break;
     case FUJICMD_SET_DIRECTORY_POSITION:
-        set_directory_position();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::set_directory_position;
         break;
     case FUJICMD_SET_DEVICE_FULLPATH:
-        set_device_filename();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::set_device_filename;
         break;
     case FUJICMD_GET_DEVICE_FULLPATH:
-        get_device_filename();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::get_device_filename;
         break;
     case FUJICMD_MOUNT_IMAGE:
-        disk_image_mount();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::disk_image_mount;
         break;
     case FUJICMD_UNMOUNT_HOST:
-        unmount_host();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::unmount_host;
         break;
     case FUJICMD_UNMOUNT_IMAGE:
-        disk_image_umount();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::disk_image_umount;
         break;
     case FUJICMD_NEW_DISK:
-        new_disk();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::new_disk;
         break;
     case FUJICMD_SEND_RESPONSE:
-        send_response();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::send_response;
         break;
     case FUJICMD_DEVICE_READY:
-        ready();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::ready;
         break;
     case FUJICMD_OPEN_APPKEY:
-        open_app_key();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::open_app_key;
         break;
     case FUJICMD_CLOSE_APPKEY:
-        close_app_key();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::close_app_key;
         break;
     case FUJICMD_READ_APPKEY:
-        read_app_key();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::read_app_key;
         break;
     case FUJICMD_WRITE_APPKEY:
-        write_app_key();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::write_app_key;
         break;
     case FUJICMD_RANDOM_NUMBER:
-        random();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::random;
         break;
     case FUJICMD_BASE64_ENCODE_INPUT:
-        base64_encode_input();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::base64_encode_input;
         break;
     case FUJICMD_BASE64_ENCODE_COMPUTE:
-        base64_encode_compute();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::base64_encode_compute;
         break;
     case FUJICMD_BASE64_ENCODE_LENGTH:
-        base64_encode_length();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::base64_encode_length;
         break;
     case FUJICMD_BASE64_ENCODE_OUTPUT:
-        base64_encode_output();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::base64_encode_output;
         break;
     case FUJICMD_BASE64_DECODE_INPUT:
-        base64_decode_input();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::base64_decode_input;
         break;
     case FUJICMD_BASE64_DECODE_COMPUTE:
-        base64_decode_compute();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::base64_decode_compute;
         break;
     case FUJICMD_BASE64_DECODE_LENGTH:
-        base64_decode_length();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::base64_decode_length;
         break;
     case FUJICMD_BASE64_DECODE_OUTPUT:
-        base64_decode_output();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::base64_decode_output;
         break;
     case FUJICMD_HASH_INPUT:
-        hash_input();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::hash_input;
         break;
     case FUJICMD_HASH_COMPUTE:
-        hash_compute(true);
+        _drivewire_bus->fnStateMethod = &drivewireFuji::state_hash_compute_true;
         break;
     case FUJICMD_HASH_COMPUTE_NO_CLEAR:
-        hash_compute(false);
+        _drivewire_bus->fnStateMethod = &drivewireFuji::state_hash_compute_false;
         break;
     case FUJICMD_HASH_LENGTH:
-        hash_length();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::hash_length;
         break;
     case FUJICMD_HASH_OUTPUT:
-        hash_output();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::hash_output;
         break;
     case FUJICMD_HASH_CLEAR:
-        hash_clear();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::hash_clear;
         break;
     case FUJICMD_SET_BOOT_MODE:
-        set_boot_mode();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::set_boot_mode;
         break;
     case FUJICMD_MOUNT_ALL:
-        mount_all();
+        _drivewire_bus->fnStateMethod = &drivewireFuji::local_mount_all;
         break;
     default:
+        _drivewire_bus->fnStateMethod = &drivewireFuji::op_unhandled;
+        _drivewire_bus->resetState();
         break;
     }
+    
+    result = (this->*_drivewire_bus->fnStateMethod)(q);
+
+    return result;
 }
 
+int drivewireFuji::op_unhandled(std::vector<uint8_t> *q)
+{
+    int result = 1;
+    
+    resetState();
+    Debug_printv("Unhandled opcode: %02x", q->at(0));
+    
+    return result;
+}
+
+
 #endif /* BUILD_COCO */
\ No newline at end of file
diff --git a/lib/device/drivewire/fuji.h b/lib/device/drivewire/fuji.h
index 12345cfec..7f21b79c3 100755
--- a/lib/device/drivewire/fuji.h
+++ b/lib/device/drivewire/fuji.h
@@ -114,63 +114,76 @@ class drivewireFuji : public virtualDevice
 
     appkey _current_appkey;
 
+    void resetState();
+    uint16_t base64_encode_length_var;
+    uint16_t base64_decode_length_var;
+    uint16_t hash_length_var;
+
+    
 protected:
-    void reset_fujinet();          // 0xFF
-    void net_get_ssid();           // 0xFE
-    void net_scan_networks();      // 0xFD
-    void net_scan_result();        // 0xFC
-    void net_set_ssid();           // 0xFB
-    void net_get_wifi_status();    // 0xFA
-    void mount_host();             // 0xF9
-    void disk_image_mount();       // 0xF8
-    void open_directory();         // 0xF7
-    void read_directory_entry();   // 0xF6
-    void close_directory();        // 0xF5
-    void read_host_slots();        // 0xF4
-    void write_host_slots();       // 0xF3
-    void read_device_slots();      // 0xF2
-    void write_device_slots();     // 0xF1
-    void enable_udpstream();       // 0xF0
-    void net_get_wifi_enabled();   // 0xEA
-    void disk_image_umount();      // 0xE9
-    void get_adapter_config();     // 0xE8
-    void new_disk();               // 0xE7
-    void unmount_host();           // 0xE6
-    void get_directory_position(); // 0xE5
-    void set_directory_position(); // 0xE4
-    void set_hdrivewire_index();   // 0xE3
-    void set_device_filename();    // 0xE2
-    void set_host_prefix();        // 0xE1
-    void get_host_prefix();        // 0xE0
-    void set_drivewire_external_clock(); // 0xDF
-    void write_app_key();          // 0xDE
-    void read_app_key();           // 0xDD
-    void open_app_key();           // 0xDC
-    void close_app_key();          // 0xDB
-    void get_device_filename();    // 0xDA
-    void set_boot_config();        // 0xD9
-    void copy_file();              // 0xD8
-    void set_boot_mode();          // 0xD6
-    void random();                 // 0xD3
-    void base64_encode_input();    // 0xD0
-    void base64_encode_compute();  // 0xCF
-    void base64_encode_length();   // 0xCE
-    void base64_encode_output();   // 0xCD
-    void base64_decode_input();    // 0xCC
-    void base64_decode_compute();  // 0xCB
-    void base64_decode_length();   // 0xCA
-    void base64_decode_output();   // 0xC9
-    void hash_input();             // 0xC8
-    void hash_compute(bool clear_data); // 0xC7, 0xC3
-    void hash_length();            // 0xC6
-    void hash_output();            // 0xC5
-    void get_adapter_config_extended(); // 0xC4
-    void hash_clear();             // 0xC2
-
-    void send_error();             // 0x02
-    void send_response();          // 0x01
-    void ready();                  // 0x00
-    void shutdown() override;
+    int reset_fujinet(std::vector<uint8_t> *);          // 0xFF
+    int net_get_ssid(std::vector<uint8_t> *);           // 0xFE
+    int net_scan_networks(std::vector<uint8_t> *);      // 0xFD
+    int net_scan_result(std::vector<uint8_t> *);        // 0xFC
+    int net_set_ssid(std::vector<uint8_t> *);           // 0xFB
+    int net_get_wifi_status(std::vector<uint8_t> *);    // 0xFA
+    int mount_host(std::vector<uint8_t> *);             // 0xF9
+    int disk_image_mount(std::vector<uint8_t> *);       // 0xF8
+    int open_directory(std::vector<uint8_t> *);         // 0xF7
+    int read_directory_entry(std::vector<uint8_t> *);   // 0xF6
+    int close_directory(std::vector<uint8_t> *);        // 0xF5
+    int read_host_slots(std::vector<uint8_t> *);        // 0xF4
+    int write_host_slots(std::vector<uint8_t> *);       // 0xF3
+    int read_device_slots(std::vector<uint8_t> *);      // 0xF2
+    int write_device_slots(std::vector<uint8_t> *);     // 0xF1
+    int enable_udpstream(std::vector<uint8_t> *);       // 0xF0
+    int net_get_wifi_enabled(std::vector<uint8_t> *);   // 0xEA
+    int disk_image_umount(std::vector<uint8_t> *);      // 0xE9
+    int get_adapter_config(std::vector<uint8_t> *);     // 0xE8
+    int new_disk(std::vector<uint8_t> *);               // 0xE7
+    int unmount_host(std::vector<uint8_t> *);           // 0xE6
+    int get_directory_position(std::vector<uint8_t> *); // 0xE5
+    int set_directory_position(std::vector<uint8_t> *); // 0xE4
+    int set_hdrivewire_index(std::vector<uint8_t> *);   // 0xE3
+    int set_device_filename(std::vector<uint8_t> *);    // 0xE2
+    int set_host_prefix(std::vector<uint8_t> *);        // 0xE1
+    int get_host_prefix(std::vector<uint8_t> *);        // 0xE0
+    int set_drivewire_external_clock(std::vector<uint8_t> *); // 0xDF
+    int write_app_key(std::vector<uint8_t> *);          // 0xDE
+    int read_app_key(std::vector<uint8_t> *);           // 0xDD
+    int open_app_key(std::vector<uint8_t> *);           // 0xDC
+    int close_app_key(std::vector<uint8_t> *);          // 0xDB
+    int get_device_filename(std::vector<uint8_t> *);    // 0xDA
+    int set_boot_config(std::vector<uint8_t> *);        // 0xD9
+    int copy_file(std::vector<uint8_t> *);              // 0xD8
+    int set_boot_mode(std::vector<uint8_t> *);          // 0xD6
+    int random(std::vector<uint8_t> *);                 // 0xD3
+    int base64_encode_input(std::vector<uint8_t> *);    // 0xD0
+    int base64_encode_input_p2(std::vector<uint8_t> *q);
+    int base64_encode_compute(std::vector<uint8_t> *);  // 0xCF
+    int base64_encode_length(std::vector<uint8_t> *);   // 0xCE
+    int base64_encode_output(std::vector<uint8_t> *);   // 0xCD
+    int base64_decode_input(std::vector<uint8_t> *);    // 0xCC
+    int base64_decode_input_p2(std::vector<uint8_t> *);    // 0xCC
+    int base64_decode_compute(std::vector<uint8_t> *);  // 0xCB
+    int base64_decode_length(std::vector<uint8_t> *);   // 0xCA
+    int base64_decode_output(std::vector<uint8_t> *);   // 0xC9
+    int hash_input(std::vector<uint8_t> *);             // 0xC8
+    int hash_input_p2(std::vector<uint8_t> *);             // 0xC8
+    int state_hash_compute_true(std::vector<uint8_t> *); // 0xC7, 0xC3
+    int state_hash_compute_false(std::vector<uint8_t> *); // 0xC7, 0xC3
+    int hash_length(std::vector<uint8_t> *);            // 0xC6
+    int hash_output(std::vector<uint8_t> *);            // 0xC5
+    int get_adapter_config_extended(std::vector<uint8_t> *); // 0xC4
+    int hash_clear(std::vector<uint8_t> *);             // 0xC2
+    int op_unhandled(std::vector<uint8_t> *q);
+
+    int send_error(std::vector<uint8_t> *q);             // 0x02
+    int send_response(std::vector<uint8_t> *);          // 0x01
+    int ready(std::vector<uint8_t> *);                  // 0x00
+    void shutdown(void) override;
+
+    void hash_compute(uint8_t value, bool clear_data); // 0xC7, 0xC3
 
 public:
     bool boot_config = true;
@@ -200,7 +213,9 @@ class drivewireFuji : public virtualDevice
     void _populate_slots_from_config();
     void _populate_config_from_slots();
 
-    void process();
+    int process(std::vector<uint8_t> *q);
+
+    int local_mount_all(std::vector<uint8_t> *q);              // 0xD7
 
     void mount_all();              // 0xD7