576 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			576 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include <stdlib.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <time.h>
 | 
						|
#include <fstream>
 | 
						|
#include <iomanip>
 | 
						|
#include <vector>
 | 
						|
#include <algorithm>
 | 
						|
#include <cassert>
 | 
						|
 | 
						|
#include "spi.h"
 | 
						|
#include "cpu.h"
 | 
						|
#include "inih/INIReader.h"
 | 
						|
 | 
						|
#ifndef SPIVERBOSE
 | 
						|
# define SPIVERBOSE 0
 | 
						|
#endif
 | 
						|
 | 
						|
namespace Spi {
 | 
						|
 | 
						|
  // ---- bus composition
 | 
						|
 | 
						|
  static Device *spi0 = 0;
 | 
						|
  static Device *spi1 = 0;
 | 
						|
 | 
						|
  void clock(uint16_t b, uint16_t a) {
 | 
						|
    if (spi0)
 | 
						|
      spi0->clock(b, a);
 | 
						|
    if (spi1)
 | 
						|
      spi1->clock(b, a);
 | 
						|
  }
 | 
						|
  
 | 
						|
  bool config(INIReader &reader, const std::string §ionString) {
 | 
						|
    if (sectionString == "SD0") {
 | 
						|
      spi0 = new SDCard(0);
 | 
						|
      return spi0->config(reader, sectionString);
 | 
						|
    }
 | 
						|
    if (sectionString == "SD1") {
 | 
						|
      spi1 = new SDCard(1);
 | 
						|
      return spi1->config(reader, sectionString);
 | 
						|
    }
 | 
						|
    if (sectionString == "MCP1") {
 | 
						|
      fprintf(stderr, "Spi::Device: MCP device is not yet supported\n");
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
  // ---- generic device
 | 
						|
  
 | 
						|
  Device::Device(int num)
 | 
						|
    : cs((num >= 0 && num < 4) ? (0x4 << num) : 0), mask(0)
 | 
						|
  {
 | 
						|
    if (num < 0 || num > 3)
 | 
						|
      fprintf(stderr, "Spi::Device: port number must be in range 0..3\n");
 | 
						|
  }
 | 
						|
 | 
						|
  uint8_t Device::spiselect(void)
 | 
						|
  {
 | 
						|
    // first byte returned after selecting the device
 | 
						|
    return 0xff;
 | 
						|
  }
 | 
						|
  
 | 
						|
  void Device::clock(uint16_t b, uint16_t a)
 | 
						|
  {
 | 
						|
    bool selected = (cs && !(a & cs));
 | 
						|
    // cs was just asserted
 | 
						|
    if (selected && (b & cs)) {
 | 
						|
#if SPIVERBOSE
 | 
						|
      fprintf(stderr, "sdi%d: selected\n", ffs(cs)-3);
 | 
						|
#endif
 | 
						|
      mask = 0x80;
 | 
						|
      miso_byte = spiselect();
 | 
						|
      Cpu::setXIN(miso_byte & mask ? 0xf : 0);
 | 
						|
    }
 | 
						|
#if SPIVERBOSE
 | 
						|
    if (!selected && !(b & cs))
 | 
						|
      fprintf(stderr, "sdi%d: deselected\n", ffs(cs)-3);
 | 
						|
#endif
 | 
						|
    // clock change
 | 
						|
    if (selected && ((a ^ b) & 1))
 | 
						|
      {
 | 
						|
        if (a & 1)
 | 
						|
          {
 | 
						|
            // clock rising (latch)
 | 
						|
            if (a & 0x8000)
 | 
						|
              mosi_byte |= mask;
 | 
						|
            else
 | 
						|
              mosi_byte &= ~mask;
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            // clock falling (shift)
 | 
						|
            if (! (mask >>= 1)) {
 | 
						|
              mask = 0x80;
 | 
						|
#if SPIVERBOSE
 | 
						|
              fprintf(stderr,"sdi%d: sent 0x%02x recv 0x%02x\n", ffs(cs)-3, miso_byte, mosi_byte);
 | 
						|
#endif
 | 
						|
              miso_byte = spibyte(mosi_byte);
 | 
						|
            }
 | 
						|
            Cpu::setXIN((miso_byte & mask) ? 0xf : 0);
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
  
 | 
						|
  // ---- sdcard device
 | 
						|
 | 
						|
 | 
						|
  static uint8_t crc7(const uint8_t *ptr, uint32_t count)
 | 
						|
  {
 | 
						|
    int i;
 | 
						|
    uint8_t crc = 0;
 | 
						|
    while (count--)
 | 
						|
      {
 | 
						|
        crc = crc ^ *ptr++;
 | 
						|
        for (i=0; i<8; i++) {
 | 
						|
          if (crc & 0x80)
 | 
						|
            crc ^= 0x89;
 | 
						|
          crc = (crc << 1);
 | 
						|
        } 
 | 
						|
      }
 | 
						|
    // byte { crc7, 1 }
 | 
						|
    return crc | 1;
 | 
						|
  }
 | 
						|
  
 | 
						|
  static uint16_t crc16(const uint8_t *ptr, uint32_t count)
 | 
						|
  {
 | 
						|
    int i;
 | 
						|
    uint16_t crc = 0;
 | 
						|
    while (count--)
 | 
						|
      {
 | 
						|
        crc = crc ^ (((uint16_t)(*ptr++)) << 8);
 | 
						|
        for (i=0; i<8; i++) {
 | 
						|
          if (crc & 0x8000)
 | 
						|
            crc = (crc << 1) ^ 0x1021;
 | 
						|
          else
 | 
						|
            crc = (crc << 1);
 | 
						|
        } 
 | 
						|
      }
 | 
						|
    return crc;
 | 
						|
  }
 | 
						|
  
 | 
						|
  SDCard::SDCard(int num)
 | 
						|
    : Device(num), idle(1), state(0), count(0), len(0), fd(0)
 | 
						|
  {
 | 
						|
    buffer = new uint8_t[512];
 | 
						|
  }
 | 
						|
 | 
						|
  SDCard::~SDCard()
 | 
						|
  {
 | 
						|
    delete [] buffer;
 | 
						|
    if (fd)
 | 
						|
      fclose((FILE*)(fd));
 | 
						|
  }
 | 
						|
  
 | 
						|
  bool SDCard::config(INIReader &reader, const std::string §ion)
 | 
						|
  {
 | 
						|
    std::string result;
 | 
						|
    std::map<std::string, Type> typemap;
 | 
						|
    typemap["NONE"] = NONE;
 | 
						|
    typemap["MMC"] = MMC;
 | 
						|
    typemap["SDSC"] = SDSC;
 | 
						|
    typemap["SDHC"] = SDHC;
 | 
						|
 | 
						|
    type = NONE;
 | 
						|
    result = reader.Get(section, "Type", "NONE");
 | 
						|
    if (typemap.find(result) == typemap.end())
 | 
						|
      fprintf(stderr, "Spi::SDCard: card type must be one of NONE, MMC, SDSC, SDHC\n");
 | 
						|
    else
 | 
						|
      type = typemap[result];
 | 
						|
    if (type != NONE) {
 | 
						|
      filename = reader.Get(section, "Filename", std::string());
 | 
						|
      if (filename.empty()) {
 | 
						|
        fprintf(stderr, "Spi:SDCard: no filename\n");
 | 
						|
        type = NONE;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (type != NONE) {
 | 
						|
      fd = (File*)fopen(filename.c_str(), "rb+");
 | 
						|
      if (! fd) {
 | 
						|
        fprintf(stderr, "Spi::SDCard: cannot open file: %s\n", filename.c_str());
 | 
						|
        type = NONE;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (type != NONE && fd) {
 | 
						|
      filelen = 0;
 | 
						|
#ifdef _MSC_VER
 | 
						|
      if (_fseeki64((FILE*)fd, 0, SEEK_END) >= 0)
 | 
						|
        filelen = (long long)_ftelli64((FILE*)fd);
 | 
						|
#elif defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L
 | 
						|
      if (fseeko((FILE*)fd, 0, SEEK_END) >= 0)
 | 
						|
        filelen = (long long)ftello((FILE*)fd);
 | 
						|
#else
 | 
						|
      if (fseek((FILE*)fd, 0, SEEK_END) >= 0)
 | 
						|
        filelen = (long long)ftell((FILE*)fd);
 | 
						|
#endif
 | 
						|
      if (filelen <= 0) {
 | 
						|
        fprintf(stderr, "Spi::SDCard: cannot measure file size: %s\n", filename.c_str());
 | 
						|
        fclose((FILE*)fd);
 | 
						|
        type = NONE;
 | 
						|
        fd = 0;
 | 
						|
      } else if (filelen % (256 * 1024)) {
 | 
						|
        fprintf(stderr, "Spi::SDCard: file size should be a multiple of 256K: %s\n", filename.c_str());
 | 
						|
        fclose((FILE*)fd);
 | 
						|
        type = NONE;
 | 
						|
        fd = 0;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  uint8_t SDCard::spiselect(void)
 | 
						|
  {
 | 
						|
    // This function is called whenever the spi port is selected (/SSx
 | 
						|
    // falling edge).  Its return value is the byte transmitted to the
 | 
						|
    // host during the next 8 clock cycles.
 | 
						|
    //    In the case of a SD card, it also resets the state to CMD
 | 
						|
    // (listening for a command) except during a long action (during a
 | 
						|
    // read) or when the state is INIT or APPCMD which are also
 | 
						|
    // listening for a command (but while uninitialized or after a
 | 
						|
    // CMD55.)
 | 
						|
    Context ctx = context();
 | 
						|
    if (action() == BUSY)
 | 
						|
      return 0x00;
 | 
						|
    if (ctx != INIT && ctx != APPCMD)
 | 
						|
      ctx = CMD;
 | 
						|
    set_wait_state(ctx);
 | 
						|
    return 0xff;
 | 
						|
  }
 | 
						|
  
 | 
						|
  uint8_t SDCard::spibyte(uint8_t in)
 | 
						|
  {
 | 
						|
    // This function is called whenever a byte is exchanged.  Its
 | 
						|
    // argument is the byte received by the slave.  Its return value
 | 
						|
    // is the next byte to be sent by the slave.
 | 
						|
    //    In the case of a SD card, what happens depends on the
 | 
						|
    // card state represented by the (context,action) pair.
 | 
						|
    Context c = context();
 | 
						|
    Action a = action();
 | 
						|
    // consistent action behavior
 | 
						|
    switch(a)
 | 
						|
      {
 | 
						|
      case WAIT:
 | 
						|
        if (in == 0xff || type == NONE) // waiting for master data
 | 
						|
          return 0xff;
 | 
						|
        break;
 | 
						|
      case RECV:
 | 
						|
        buffer[count] = in;
 | 
						|
        if (++count < len)
 | 
						|
          return 0xff;
 | 
						|
        break;
 | 
						|
      case SEND:
 | 
						|
        if (c == READM && in == 64 + 12) // received CMD12!
 | 
						|
          break;
 | 
						|
        if (count < len)
 | 
						|
          return buffer[count++];
 | 
						|
        break;
 | 
						|
      case BUSY: // busy state for len bytes
 | 
						|
        if (count++ < len)
 | 
						|
          return 0;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
    switch(c)
 | 
						|
      {
 | 
						|
      case INIT:
 | 
						|
      case CMD:
 | 
						|
      case APPCMD:
 | 
						|
        {
 | 
						|
          if (a == WAIT)      // got first byte
 | 
						|
            set_recv_state(c, 6, in);
 | 
						|
          else if (a == SEND || a == BUSY) // just sent reply
 | 
						|
            set_wait_state(c);
 | 
						|
          else                // got command
 | 
						|
            sdcommand(c);
 | 
						|
          return 0xff;
 | 
						|
        }
 | 
						|
      case REG:
 | 
						|
        {
 | 
						|
          assert(a == SEND);
 | 
						|
          buffer[0] = 0xfe;
 | 
						|
          buffer[16] = crc7(buffer+1, 15);
 | 
						|
          uint16_t crc = crc16(buffer+1, 16);
 | 
						|
          buffer[17] = (crc >> 8);
 | 
						|
          buffer[18] = (crc & 0xff);
 | 
						|
          set_send_state(CMD, 16+3);
 | 
						|
          return 0xff;
 | 
						|
        }
 | 
						|
      case READ:
 | 
						|
        {
 | 
						|
          assert(a == SEND);
 | 
						|
          if (! read_data())
 | 
						|
            set_send_r1_state(CMD, 9); // send error token
 | 
						|
          else
 | 
						|
            set_send_state(CMD, 512+3);
 | 
						|
          return 0xff;
 | 
						|
        }
 | 
						|
      case READM:
 | 
						|
        {
 | 
						|
          assert(a == SEND);
 | 
						|
          if (in == 64 + 12)
 | 
						|
            set_recv_state(CMD, 6, in);
 | 
						|
          else if (! read_data())
 | 
						|
            set_send_r1_state(CMD, 9); // send error token
 | 
						|
          else
 | 
						|
            set_send_state(READM, 512+3);
 | 
						|
          offset += 512;
 | 
						|
          return 0xff;
 | 
						|
        }
 | 
						|
      case WRITE:
 | 
						|
        {
 | 
						|
          assert (a == SEND);
 | 
						|
          set_wait_state(WRITE1);
 | 
						|
          return 0xff;
 | 
						|
        }
 | 
						|
      case WRITE1:
 | 
						|
        {
 | 
						|
          if (a == WAIT) {
 | 
						|
            set_recv_state(WRITE1, 512+3, in);
 | 
						|
          } else {
 | 
						|
            set_busy_state(CMD, 4);
 | 
						|
            if (buffer[0] == 0xfe && write_data())
 | 
						|
              return 0x5;
 | 
						|
            else
 | 
						|
              return 0xd;
 | 
						|
          }
 | 
						|
          return 0xff;
 | 
						|
        }
 | 
						|
      case WRITEM:
 | 
						|
        {
 | 
						|
          assert(a == SEND || a == BUSY);
 | 
						|
          set_wait_state(WRITEM1);
 | 
						|
          return 0xff;
 | 
						|
        }
 | 
						|
      case WRITEM1:
 | 
						|
        {
 | 
						|
          if (a == WAIT && in == 0xfd) {
 | 
						|
            set_busy_state(CMD, 4);   // stop tran
 | 
						|
          } else if (a == WAIT) {
 | 
						|
            set_recv_state(WRITEM1, 512+3, in);
 | 
						|
          } else if (buffer[0] == 0xfc && write_data()) {
 | 
						|
            offset += 512;
 | 
						|
            set_busy_state(WRITEM, 4);
 | 
						|
            return 0x5;
 | 
						|
          } else {
 | 
						|
            set_busy_state(CMD, 4);
 | 
						|
            return 0xd;
 | 
						|
          }
 | 
						|
          return 0xff;
 | 
						|
        }
 | 
						|
      default:
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    set_send_r1_state(CMD, 4);
 | 
						|
    return 0xff;
 | 
						|
  }
 | 
						|
 | 
						|
  void SDCard::sdcommand(Context context)
 | 
						|
  {
 | 
						|
    int cmd = buffer[0] & 0x3f;
 | 
						|
    if (context == INIT && memcmp(buffer, "\x40\0\0\0\0\x95", 6))
 | 
						|
      {
 | 
						|
        // In the init state, the only accepted command is command 0
 | 
						|
        set_send_r1_state(INIT, 5);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
    if (buffer[0] != cmd + 64)
 | 
						|
      {
 | 
						|
        set_send_r1_state(CMD, 5);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
    if (context == APPCMD)
 | 
						|
      {
 | 
						|
        cmd += 128;
 | 
						|
      }
 | 
						|
    if (idle)
 | 
						|
      {
 | 
						|
        if (cmd != 0 && cmd != 1 && cmd != 8 && cmd != 128+41 && cmd != 55 && cmd != 58)
 | 
						|
          cmd = 0xff;
 | 
						|
      }
 | 
						|
    switch(cmd)
 | 
						|
      {
 | 
						|
      case 128+41: // ACMD41: APP_SEND_OP_COND
 | 
						|
            {
 | 
						|
              if (type < SDSC)
 | 
						|
                set_send_r1_state(CMD, 4 + idle);
 | 
						|
              else {
 | 
						|
                idle = 0;
 | 
						|
                set_send_r1_state(CMD, 0);
 | 
						|
              }
 | 
						|
              break;
 | 
						|
            }
 | 
						|
      case 128+23: // ACMD22: SET_WR_BLOCK_ERASE_COUNT
 | 
						|
        {
 | 
						|
          // ignored but not illegal
 | 
						|
          set_send_r1_state(CMD, 0);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      case 0:  // CMD0: GO_IDLE_STATE
 | 
						|
        {
 | 
						|
          idle = 1;
 | 
						|
          set_send_r1_state(CMD, idle);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      case 1:  // CMD1: SEND_OP_COND
 | 
						|
        {
 | 
						|
          idle = 0;
 | 
						|
          set_send_r1_state(CMD, 0);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      case 8:  // CMD8: SEND_IF_COND
 | 
						|
        {
 | 
						|
          buffer[0] = idle;
 | 
						|
          if (type < SDSC)
 | 
						|
            set_send_r1_state(CMD, 4 + idle);
 | 
						|
          else
 | 
						|
            set_send_state(CMD, 5);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      case 9:  // CMD9: SEND_CSD
 | 
						|
        { 
 | 
						|
          long size = filelen / (512 * 1024);
 | 
						|
          if (type >= SDHC) {  //1   2   3   4   5   6   7   8   9   0   1   2   3   4   5   6
 | 
						|
            memcpy(buffer, "\0" "\x40\x0e\x00\x32\x5b\x59\x00\x00\x00\x00\x7f\x80\x0a\x40\x40\xf1" "XX", 16 + 3);
 | 
						|
            buffer[10] = (size & 0xff);
 | 
						|
            buffer[9] = (size & 0xff00) >> 8;
 | 
						|
            buffer[8] = (size & 0xcf0000) >> 16;
 | 
						|
          } else {
 | 
						|
            memcpy(buffer, "\0" "\x00\x0e\x00\x32\x5b\x59\x00\x00\x00\x00\x7f\x80\x0a\x40\x40\xf1" "XX", 16 + 3);
 | 
						|
            buffer[7]  |= (size & 0xc00) >> 10;
 | 
						|
            buffer[8]  |= (size & 0x3fc) >> 2;
 | 
						|
            buffer[9]  |= (size & 0x3) << 6;
 | 
						|
          }
 | 
						|
          set_send_state(REG, 1);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      case 10: // CMD10: SEND_CID
 | 
						|
        {
 | 
						|
          memcpy(buffer, "\0" "\xbbSD00000\x11\0\0\0\0\0\1\xf1" "XX", 16+3);
 | 
						|
          set_send_state(REG, 1);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      case 12: // CMD12: STOP_TRANSMISSION
 | 
						|
        {
 | 
						|
          set_busy_state(CMD, 3);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      case 16: // CMD16: SET_BLOCK_LENGTH
 | 
						|
        {
 | 
						|
          long bl = (buffer[1]<<24)|(buffer[2]<<16)|(buffer[3]<<8)|(buffer[4]);
 | 
						|
          if (bl != 512)
 | 
						|
            set_send_r1_state(CMD, 64);
 | 
						|
          else
 | 
						|
            set_send_r1_state(CMD, 0);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      case 17: // CMD17: READ_SINGLE_BLOCK
 | 
						|
        {
 | 
						|
          offset = (buffer[1]<<24)|(buffer[2]<<16)|(buffer[3]<<8)|(buffer[4]);
 | 
						|
          if (type >= SDHC)
 | 
						|
            offset *= 512;
 | 
						|
          if (offset > filelen - 512)
 | 
						|
            set_send_r1_state(CMD, 64);
 | 
						|
          else
 | 
						|
            set_send_r1_state(READ, 0);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      case 18: // CMD18: READ_MULTIPLE_BLOCK
 | 
						|
        {
 | 
						|
          offset = (buffer[1]<<24)|(buffer[2]<<16)|(buffer[3]<<8)|(buffer[4]);
 | 
						|
          if (type >= SDHC)
 | 
						|
            offset *= 512;
 | 
						|
          if (offset > filelen - 512)
 | 
						|
            set_send_r1_state(CMD, 64);
 | 
						|
          else
 | 
						|
            set_send_r1_state(READM, 0);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      case 23: // CMD23: SET_BLOCK_COUNT (MMC only)
 | 
						|
        {
 | 
						|
          set_send_r1_state(CMD, (type == MMC) ? 64 : 4); // unsupported
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      case 24: // CMD24: WRITE_SINGLE_BLOCK
 | 
						|
        {
 | 
						|
          offset = (buffer[1]<<24)|(buffer[2]<<16)|(buffer[3]<<8)|(buffer[4]);
 | 
						|
          if (type >= SDHC)
 | 
						|
            offset *= 512;
 | 
						|
          if (offset > filelen - 512)
 | 
						|
            set_send_r1_state(CMD, 64);
 | 
						|
          else
 | 
						|
            set_send_r1_state(WRITE, 0);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      case 25: // CMD18: WRITE_MULTIPLE_BLOCK
 | 
						|
        {
 | 
						|
          offset = (buffer[1]<<24)|(buffer[2]<<16)|(buffer[3]<<8)|(buffer[4]);
 | 
						|
          if (type >= SDHC)
 | 
						|
            offset *= 512;
 | 
						|
          if (offset > filelen - 512)
 | 
						|
            set_send_r1_state(CMD, 64);
 | 
						|
          else
 | 
						|
            set_send_r1_state(WRITEM, 0);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      case 55: // CMD55: APP_CMD
 | 
						|
        {
 | 
						|
          set_send_r1_state(APPCMD, idle);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      case 58: // CMD58: READ_OCR
 | 
						|
        {
 | 
						|
          buffer[0] = 0;
 | 
						|
          buffer[1] = (type >= SDHC) ? 0x40 : 0;
 | 
						|
          buffer[2] = 0xff;
 | 
						|
          buffer[3] = 0x80;
 | 
						|
          buffer[4] = 0;
 | 
						|
          set_send_state(CMD, 5);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      default:
 | 
						|
        {
 | 
						|
          set_send_r1_state(CMD, 4 + idle);
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  bool SDCard::read_data()
 | 
						|
  {
 | 
						|
    uint16_t crc;
 | 
						|
#if SPIVERBOSE
 | 
						|
    fprintf(stderr, "sdi0: reading block at offset 0x%llx\n", offset);
 | 
						|
#endif
 | 
						|
#ifdef _MSC_VER
 | 
						|
    if (fd == 0 || offset != (long long)(__int64)offset || _fseeki64((FILE*)fd, (__int64)offset, SEEK_SET) < 0)
 | 
						|
      return false;
 | 
						|
#elif defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L
 | 
						|
    if (fd == 0 || offset != (long long)(off_t)offset || fseeko((FILE*)fd, (off_t)offset, SEEK_SET) < 0)
 | 
						|
      return false;
 | 
						|
#else
 | 
						|
    if (fd == 0 || offset != (long long)(long)offset || fseek((FILE*)fd, (long)offset, SEEK_SET) < 0)
 | 
						|
      return false;
 | 
						|
#endif
 | 
						|
    if (fread((void*)(buffer+1), 1, 512, (FILE*)fd) != 512)
 | 
						|
      return false;
 | 
						|
    crc = crc16(buffer+1, 512);
 | 
						|
    buffer[0] = 0xfe;
 | 
						|
    buffer[513] = (crc >> 8);
 | 
						|
    buffer[514] = (crc & 0xff);
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  
 | 
						|
  bool SDCard::write_data()
 | 
						|
  {
 | 
						|
#if SPIVERBOSE
 | 
						|
    fprintf(stderr, "sdi0: writing block at offset 0x%llx\n", offset);
 | 
						|
#endif
 | 
						|
#ifdef _MSC_VER
 | 
						|
    if (fd == 0 || offset != (long long)(__int64)offset || _fseeki64((FILE*)fd, (__int64)offset, SEEK_SET) < 0)
 | 
						|
      return false;
 | 
						|
#elif defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L
 | 
						|
    if (fd == 0 || offset != (long long)(off_t)offset || fseeko((FILE*)fd, (off_t)offset, SEEK_SET) < 0)
 | 
						|
      return false;
 | 
						|
#else
 | 
						|
    if (fd == 0 || offset != (long long)(long)offset || fseek((FILE*)fd, (long)offset, SEEK_SET) < 0)
 | 
						|
      return false;
 | 
						|
#endif
 | 
						|
    if (fwrite((void*)(buffer+1), 1, 512, (FILE*)fd) != 512)
 | 
						|
      return false;
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
  
 | 
						|
 
 | 
						|
}  // namespace Spi
 |