├── src ├── crc16.h ├── header.h ├── crc16.c ├── gpio.h ├── memory.h ├── gpio.cpp ├── main.cpp └── memory.cpp ├── README.md └── Makefile /src/crc16.h: -------------------------------------------------------------------------------- 1 | unsigned short gen_crc16(const unsigned char *data, unsigned short size); 2 | 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### raspiadvrw is GBA cartridge reader writer software tool for Raspberry Pi ADVANCE Expansion Board. 2 | 3 | It is expansion board for reading and writing GBA cartridges with Raspberry Pi. 4 | Since it has a communication port, it supports multi-boot. 5 | 6 | https://cubic-style.jp/rpa_exp/ 7 | 8 | ![attach board](https://user-images.githubusercontent.com/20790149/39209319-80b22904-4840-11e8-961f-5b97fc933c2c.jpg) 9 | -------------------------------------------------------------------------------- /src/header.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef struct 4 | { 5 | uint32_t start_code; 6 | uint8_t logo[156]; 7 | uint8_t game_title[12]; 8 | uint32_t game_code; 9 | uint16_t maker_code; 10 | uint8_t fixed_value; // 0x96 11 | uint8_t unit_code; // 0x00 12 | uint8_t device_type; 13 | uint8_t unused[7]; 14 | uint8_t version; 15 | uint8_t complement_check; 16 | uint16_t reserve; // 0x0000 17 | } Header; 18 | 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CPP=g++ 3 | CFLAGS=-I/usr/local/include -L/usr/local/lib -Wall -g 4 | INCS= 5 | SRCDIR= src 6 | SOURCES = $(wildcard $(SRCDIR)/*.cpp) 7 | OBJS=$(SOURCES:%.cpp=%.o) 8 | LIBS=-lwiringPi 9 | TARGET=rpa 10 | 11 | all:$(TARGET) 12 | 13 | %.o: %.cpp $(INCS) 14 | $(CPP) -c -O1 -o $@ $< 15 | 16 | $(TARGET): $(OBJS) 17 | $(CPP) -o $@ $(OBJS) $(LIBS) 18 | 19 | clean: 20 | rm -rf $(TARGET) $(SRCDIR)/*.o 21 | 22 | pkg: 23 | cp $(TARGET) deb_pkg/raspiadvrw/usr/local/bin/ 24 | dpkg-deb -b deb_pkg/raspiadvrw/ raspiadvrw.deb 25 | 26 | -------------------------------------------------------------------------------- /src/crc16.c: -------------------------------------------------------------------------------- 1 | #define CRC16 0x8005 2 | 3 | unsigned short gen_crc16(const unsigned char *data, unsigned short size) 4 | { 5 | unsigned short out = 0; 6 | unsigned short crc = 0; 7 | int bits_read = 0, bit_flag; 8 | 9 | /* Sanity check: */ 10 | if(data == 0) 11 | return 0; 12 | 13 | while(size > 0) 14 | { 15 | bit_flag = out >> 15; 16 | 17 | /* Get next bit: */ 18 | out <<= 1; 19 | out |= (*data >> bits_read) & 1; // item a) work from the least significant bits 20 | 21 | /* Increment bit counter: */ 22 | bits_read++; 23 | if(bits_read > 7) 24 | { 25 | bits_read = 0; 26 | data++; 27 | size--; 28 | } 29 | 30 | /* Cycle check: */ 31 | if(bit_flag) 32 | out ^= CRC16; 33 | 34 | } 35 | 36 | // item b) "push out" the last 16 bits 37 | int i; 38 | for (i = 0; i < 16; ++i) { 39 | bit_flag = out >> 15; 40 | out <<= 1; 41 | if(bit_flag) 42 | out ^= CRC16; 43 | } 44 | 45 | // item c) reverse the bits 46 | i = 0x8000; 47 | int j = 0x0001; 48 | for (; i != 0; i >>=1, j <<= 1) { 49 | if (i & out) crc |= j; 50 | } 51 | 52 | return crc; 53 | } 54 | -------------------------------------------------------------------------------- /src/gpio.h: -------------------------------------------------------------------------------- 1 | #ifndef __GPIO_H__ 2 | #define __GPIO_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | enum MEMORY_SELECT { 9 | MEM_ROM, 10 | MEM_BACKUP 11 | }; 12 | 13 | enum DIRECTION_RW{ 14 | DIR_WRITE, 15 | DIR_READ 16 | }; 17 | 18 | class Gpio{ 19 | private: 20 | static Gpio* instance; 21 | MEMORY_SELECT mem; 22 | DIRECTION_RW dir; 23 | int32_t addrbuff; 24 | 25 | void wait(int32_t n); 26 | void setPinMode(int32_t pin, int32_t d); 27 | void setDigitalWrite(int32_t pin, int32_t w); 28 | int32_t getDigitalRead(int32_t pin); 29 | void setDiretion(DIRECTION_RW d); 30 | 31 | public: 32 | Gpio(); 33 | ~Gpio(); 34 | void init(MEMORY_SELECT m); 35 | void setAdd(uint32_t addr); 36 | void setData(uint16_t data); 37 | uint16_t getData(); 38 | void csHigh(); 39 | void csLow(); 40 | void csFalling(); 41 | void rdHigh(); 42 | void rdLow(); 43 | void wrHigh(); 44 | void wrLow(); 45 | 46 | static Gpio& getInstance(); 47 | static Gpio& create(); 48 | }; 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /src/memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "gpio.h" 4 | 5 | typedef struct 6 | { 7 | uint32_t start_code; 8 | uint8_t logo[156]; 9 | uint8_t game_title[12]; 10 | uint32_t game_code; 11 | uint16_t maker_code; 12 | uint8_t fixed_value; // 0x96 13 | uint8_t unit_code; // 0x00 14 | uint8_t device_type; 15 | uint8_t unused[7]; 16 | uint8_t version; 17 | uint8_t complement_check; 18 | uint16_t reserve; // 0x0000 19 | } RomHeader; 20 | 21 | 22 | typedef struct 23 | { 24 | uint32_t mbit; //memory size Mbit 25 | uint16_t manufacturer_id; 26 | uint16_t deveice_id[3]; 27 | char device_name[16]; 28 | uint32_t sector_size; // word size 29 | uint32_t sector_mask; 30 | uint32_t write_buffer; // word size 31 | uint32_t buff_prg_wait; 32 | } FlashMemoryInfo; 33 | 34 | // Factory Class 35 | class Memory{ 36 | private: 37 | Memory* m; 38 | 39 | protected: 40 | Gpio &gpio = Gpio::create(); 41 | Memory(); 42 | ~Memory(); 43 | public: 44 | static Memory* create(MEMORY_SELECT ms = MEM_ROM); 45 | /* 46 | virtual bool find(); 47 | virtual int32_t getDeviceCode(uint16_t *code); 48 | */ 49 | }; 50 | 51 | enum ERomType{ 52 | ROM_NONE, ROM_MASK, ROM_FLASH, ROM_FLASH_CUBIC 53 | }; 54 | 55 | // MainROMの基底クラス 56 | class MemoryRom : public Memory{ 57 | protected: 58 | uint32_t mbit; //memory size Mbit 59 | char* typstr; 60 | ERomType type; 61 | 62 | public: 63 | RomHeader header; 64 | //virtual bool getHeader(); 65 | virtual int32_t write(uint32_t wadd, uint16_t dat); 66 | virtual int32_t read(uint32_t wadd, uint16_t *dat); 67 | virtual int32_t seqRead(uint32_t wadd, uint16_t *dat, uint32_t len); 68 | 69 | virtual int32_t program(uint32_t wadd, uint16_t dat){ 70 | return -1; 71 | }; 72 | virtual int32_t seqProgram(uint32_t wadd, uint16_t *dat, uint32_t len){ 73 | return -1; 74 | }; 75 | virtual int32_t seqProgramWriteCommand(uint32_t wadd, uint16_t *dat, uint32_t len){ 76 | return -1; 77 | }; 78 | virtual int32_t secErase(uint32_t wadd, uint32_t num){ 79 | return -1; 80 | }; 81 | virtual int32_t chipErase(){ 82 | return -1; 83 | }; 84 | virtual int32_t checkGbaHeader(); 85 | virtual int32_t getGbaHeader(); 86 | 87 | virtual char * getDeviceName(){ 88 | return (char *)"\0"; 89 | }; 90 | 91 | char * getTypeStr(){ 92 | return typstr; 93 | }; 94 | 95 | ERomType getType(){ 96 | return type; 97 | }; 98 | 99 | uint32_t getMemoryMbit(){ 100 | return mbit; 101 | }; 102 | /* 103 | virtual int32_t verfy(uint32_t address, uint16_t *dat, uint32_t len); 104 | virtual int32_t duplicate(); 105 | virtual int32_t reset(); 106 | */ 107 | MemoryRom(){ 108 | mbit = 0; 109 | typstr = (char *)""; 110 | gpio.init(MEM_ROM); 111 | } 112 | 113 | MemoryRom(uint32_t rom_size){ 114 | mbit = rom_size; 115 | typstr = (char *)""; 116 | gpio.init(MEM_ROM); 117 | } 118 | }; 119 | 120 | enum EBackupType{ 121 | BACKUP_NONE, BACKUP_SRAM, BACKUP_FLASH, BACKUP_FLASH_LARGE, BACKUP_FLASH_CUBIC, BACKUP_EEPROM 122 | }; 123 | 124 | // BackupROMの基底クラス 125 | class MemoryBackup : public Memory{ 126 | protected: 127 | uint32_t kbyte; //memory size Kbit 128 | char* typstr; 129 | EBackupType type; 130 | 131 | public: 132 | virtual int32_t write(uint32_t badd, uint8_t dat); 133 | virtual int32_t read(uint32_t badd, uint8_t *dat); 134 | virtual int32_t csf(); 135 | virtual int32_t save(uint32_t badd, uint8_t *dat, uint32_t len){ 136 | return -1; 137 | } 138 | virtual int32_t load(uint32_t badd, uint8_t *dat, uint32_t len); 139 | 140 | virtual int32_t chipErase(){ 141 | return -1; 142 | }; 143 | char * getTypeStr(){ 144 | return typstr; 145 | }; 146 | 147 | EBackupType getType(){ 148 | return type; 149 | }; 150 | 151 | uint32_t getMemoryKb(){ 152 | return kbyte; 153 | }; 154 | 155 | MemoryBackup(){ 156 | kbyte = 0; 157 | gpio.init(MEM_BACKUP); 158 | typstr = (char *)"NONE"; 159 | type = BACKUP_NONE; 160 | } 161 | }; 162 | 163 | -------------------------------------------------------------------------------- /src/gpio.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "gpio.h" 10 | 11 | #define CS 26 12 | #define CS2 2 13 | #define RD 20 14 | #define WR 3 15 | #define RST 23 16 | 17 | #define MCP_BASE 123 18 | #define MCP_WAIT 50 19 | #define CS_WAIT 3 //2 20 | #define RD_WAIT 4 21 | #define WR_WAIT 5 //3 22 | #define WR_WAIT_B 1 23 | 24 | union CART_PIN{ 25 | uint16_t io[24]; 26 | struct ROM_BUS{ 27 | uint16_t low[16]; 28 | uint16_t high[8]; 29 | } rom; 30 | struct BACKUP_BUS{ 31 | uint16_t add[16]; 32 | uint16_t dat[8]; 33 | } backup; 34 | } cart = {16, 19, 13, 12, 6, 5, 25, 24, 22, 27, 18, 17, 15, 14, 4, 21}; 35 | 36 | Gpio* Gpio::instance = NULL; 37 | 38 | Gpio::Gpio(){ 39 | 40 | wiringPiSetupGpio(); 41 | 42 | pinMode(CS, OUTPUT); 43 | digitalWrite(CS, 1); 44 | pinMode(CS2, OUTPUT); 45 | digitalWrite(CS2, 1); 46 | pinMode(RD, OUTPUT); 47 | digitalWrite(RD, 1); 48 | pinMode(WR, OUTPUT); 49 | digitalWrite(WR, 1); 50 | pinMode(RST, OUTPUT); 51 | digitalWrite(RST, 1); 52 | 53 | mcp23s08Setup (MCP_BASE, 1, 0); 54 | 55 | for(int i=0; i<8; i++) 56 | cart.io[i + 16] = MCP_BASE + i; 57 | } 58 | 59 | Gpio::~Gpio(){ 60 | digitalWrite(CS, 1); 61 | digitalWrite(CS2, 1); 62 | digitalWrite(RD, 1); 63 | digitalWrite(WR, 1); 64 | digitalWrite(RST, 1); 65 | } 66 | 67 | void Gpio::wait(int32_t n) 68 | { 69 | delayMicroseconds(n); 70 | } 71 | 72 | // mcpのIOと設定を分ける場合を考えて別に定義する 73 | void Gpio::setPinMode(int32_t pin, int32_t d){ 74 | if(pin >= MCP_BASE){ 75 | pinMode(pin, d); 76 | } 77 | else{ 78 | pinMode(pin, d); 79 | } 80 | } 81 | 82 | void Gpio::setDigitalWrite(int32_t pin, int32_t w){ 83 | if(pin >= MCP_BASE){ 84 | digitalWrite(pin, w); 85 | //delay(5); 86 | } 87 | else{ 88 | digitalWrite(pin, w); 89 | } 90 | } 91 | 92 | int32_t Gpio::getDigitalRead(int32_t pin){ 93 | int32_t d; 94 | if(pin >= MCP_BASE){ 95 | d = digitalRead(pin); 96 | //delay(1); 97 | } 98 | else{ 99 | d = digitalRead(pin); 100 | } 101 | return d; 102 | } 103 | 104 | void Gpio::setDiretion(DIRECTION_RW d){ 105 | int i; 106 | if(mem == MEM_ROM){ 107 | if(d == DIR_READ) 108 | for(i=0; i<16; i++) 109 | pinMode(cart.rom.low[i], INPUT); 110 | else if(d==DIR_WRITE) 111 | for(i=0; i<16; i++) 112 | pinMode(cart.rom.low[i], OUTPUT); 113 | } 114 | else if(mem == MEM_BACKUP){ 115 | if(d == DIR_READ){ 116 | for(i=0; i<8; i++){ 117 | pinMode(cart.backup.dat[i], INPUT); 118 | //pullUpDnControl (cart.backup.dat[i], PUD_UP) ; 119 | } 120 | //wait(MCP_WAIT); 121 | } 122 | else if(d==DIR_WRITE) 123 | for(i=0; i<8; i++) 124 | pinMode(cart.backup.dat[i], OUTPUT); 125 | } 126 | dir = d; 127 | } 128 | 129 | void Gpio::init(MEMORY_SELECT m) 130 | { 131 | 132 | addrbuff = -1; 133 | mem = m; 134 | 135 | // 念のため mcp をリセット 136 | digitalWrite(RST, 0); 137 | wait(20); 138 | digitalWrite(RST, 1); 139 | wait(20); 140 | 141 | // ずっと向きが変わらないもの 142 | if(mem == MEM_ROM){ 143 | for(int i=0; i<8; i++) 144 | setPinMode(cart.rom.high[i], OUTPUT); 145 | } 146 | else if(mem == MEM_BACKUP){ 147 | for(int i=0; i<16; i++) 148 | setPinMode(cart.backup.add[i], OUTPUT); 149 | } 150 | setDiretion(DIR_READ); 151 | } 152 | 153 | void Gpio::setAdd(uint32_t addr){ 154 | if(mem == MEM_ROM){ 155 | if(dir != DIR_WRITE) 156 | setDiretion(DIR_WRITE); 157 | 158 | for(int i=0; i<16; i++){ 159 | setDigitalWrite(cart.rom.low[i], (addr >> i) & 1 ); 160 | } 161 | 162 | uint32_t hadd = ((addr >> 16) & 0xff); 163 | // mcp への digialWrite は時間がかかるので、変えるときだけしかやらない 164 | for(int i=0; i<8; i++){ 165 | if( addrbuff < 0 || ( ( (hadd >> i) & 1) != ( (addrbuff >> i) & 1) ) ) 166 | setDigitalWrite(cart.rom.high[i], (hadd >> i) & 1 ); 167 | } 168 | addrbuff = hadd; 169 | } 170 | else if(mem == MEM_BACKUP){ 171 | for(int i=0; i<16; i++) 172 | setDigitalWrite(cart.backup.add[i], (addr >> i) & 1 ); 173 | } 174 | } 175 | 176 | void Gpio::setData(uint16_t data){ 177 | if(mem == MEM_ROM){ 178 | if(dir != DIR_WRITE) 179 | setDiretion(DIR_WRITE); 180 | 181 | for(int i=0; i<16; i++) 182 | setDigitalWrite(cart.rom.low[i], (data >> i) & 1 ); 183 | } 184 | else if(mem == MEM_BACKUP){ 185 | if(dir != DIR_WRITE) 186 | setDiretion(DIR_WRITE); 187 | 188 | for(int i=0; i<8; i++) 189 | setDigitalWrite(cart.backup.dat[i], (data >> i) & 1 ); 190 | } 191 | } 192 | 193 | uint16_t Gpio::getData(){ 194 | uint16_t data = 0; 195 | if(mem == MEM_ROM){ 196 | if(dir != DIR_READ) 197 | setDiretion(DIR_READ); 198 | 199 | for(int i=0; i<16; i++){ 200 | data |= (getDigitalRead(cart.rom.low[i]) << i); 201 | } 202 | } 203 | else if(mem == MEM_BACKUP){ 204 | if(dir != DIR_READ) 205 | setDiretion(DIR_READ); 206 | 207 | for(int i=0; i<8; i++) 208 | data |= (getDigitalRead(cart.backup.dat[i]) << i); 209 | } 210 | return data; 211 | } 212 | 213 | void Gpio::csHigh(){ 214 | if(mem == MEM_ROM) 215 | digitalWrite(CS, 1); 216 | else if(mem == MEM_BACKUP){ 217 | digitalWrite(CS2, 1); 218 | } 219 | wait(CS_WAIT); 220 | } 221 | 222 | void Gpio::csLow(){ 223 | if(mem == MEM_ROM) 224 | digitalWrite(CS, 0); 225 | else if(mem == MEM_BACKUP){ 226 | digitalWrite(CS2, 0); 227 | } 228 | wait(CS_WAIT); 229 | } 230 | 231 | void Gpio::csFalling(){ 232 | csHigh(); 233 | csLow(); 234 | } 235 | 236 | void Gpio::rdHigh(){ 237 | digitalWrite(RD, 1); 238 | wait(RD_WAIT); 239 | } 240 | 241 | void Gpio::rdLow(){ 242 | digitalWrite(RD, 0); 243 | wait(RD_WAIT); 244 | } 245 | 246 | void Gpio::wrHigh(){ 247 | wait(WR_WAIT_B); 248 | digitalWrite(WR, 1); 249 | wait(WR_WAIT); 250 | } 251 | 252 | void Gpio::wrLow(){ 253 | wait(WR_WAIT_B); 254 | digitalWrite(WR, 0); 255 | wait(WR_WAIT); 256 | } 257 | 258 | Gpio& Gpio::getInstance() { 259 | return *instance; 260 | } 261 | 262 | Gpio& Gpio::create() { 263 | if ( !instance ) { 264 | instance = new Gpio; 265 | } 266 | return *instance; 267 | } 268 | 269 | 270 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Raspberry Pi ADVANCE reader writer 3 | * Copyright (C) 2018 CUBIC STYLE 4 | * 5 | * This program is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU General Public License 7 | * as published by the Free Software Foundation; either version 2 8 | * of the License, or (at your option) any later version. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | #include "gpio.h" 26 | #include "memory.h" 27 | 28 | #define VERSION "1.17" 29 | 30 | using namespace std; 31 | 32 | enum MODE { NONE, READ, WRITE, INFO, BLOCK_ERASE, DUMP, 33 | CHIP_ERASE, DUPLICATE, VERIFY, BLANK, TEST, TEST2 }; 34 | 35 | #define LOGO_CRC 0x2e03 36 | 37 | void exit_func(int i){ 38 | exit(1); 39 | } 40 | 41 | void exit_func2(){ 42 | exit(1); 43 | } 44 | 45 | void print_version(){ 46 | printf("Raspberry Pi ADVANCE Reader Writer v%s\n",VERSION); 47 | printf("Copyright (C) 2022 CUBIC STYLE\n\n"); 48 | } 49 | 50 | void print_help(){ 51 | printf("rpa [-w gbarom] [-r dstfile] [-l size] [-L size(MB)]-c [-a address]\n"); 52 | printf(" -s \t\t\tBackup memory mode (Sram, Fram, Flash, ...Not yet supported EEPROM)\n\n"); 53 | 54 | printf(" -c \t\t\tROM info\n"); 55 | printf(" -r \tRead ROM(or backup memory)\n"); 56 | printf(" -w \tWrite ROM(or backup memory)\n"); 57 | printf(" -e \t\t\tblock erase\n"); 58 | printf(" -E \t\t\tchip erase\n"); 59 | printf(" -B \t\t\tblank check\n"); 60 | printf(" -d \t\t\tdump\n"); 61 | 62 | printf(" -a \t\t\trom address\n"); 63 | printf(" -b \t\t\tblock address\n"); 64 | printf(" -n \t\t\terase block num\n"); 65 | printf(" -h \t\t\tthis help\n\n"); 66 | } 67 | 68 | void print_cartinfo(MemoryRom* m){ 69 | printf(" Cartridge type: %s\n", m->getTypeStr()); 70 | if(m->getMemoryMbit() > 0) 71 | printf(" Size: %d Mbit\n", m->getMemoryMbit()); 72 | if(m->getType() == ROM_FLASH_CUBIC) 73 | printf(" Device: %s\n", m->getDeviceName()); 74 | if(m->checkGbaHeader() > 0){ 75 | m->getGbaHeader(); 76 | uint8_t title[13]; 77 | memset(title, 0, 13); 78 | strncpy((char *)title, (char *)m->header.game_title, 12); 79 | printf(" Game title: %s\n",title); 80 | } 81 | } 82 | 83 | bool nl_flag = false; 84 | void draw_progress(uint32_t p) { // p:0-100 85 | if(p > 100) 86 | printf("draw_progress p > 100 p:%d\n",p); 87 | 88 | uint32_t i=0; 89 | char buf[128], *b; 90 | b = buf; 91 | *b = '\0'; 92 | setbuf(stdout, NULL); 93 | 94 | b += sprintf(b, "["); 95 | for(; i

1){ 135 | for(i=1; i\n"); 283 | MemoryRom *m = (MemoryRom *)Memory::create(); 284 | if(m == nullptr){ 285 | printf("cartridge can not be detected or not supported.\n"); 286 | exit(1); 287 | } 288 | 289 | if(len%2==1) 290 | len++; 291 | 292 | try{ 293 | buffer = new uint8_t[len]; 294 | memset(buffer, 0, len); 295 | } 296 | catch (exception& e) 297 | { 298 | cout << e.what() << '\n'; 299 | printf("error : new error \n"); 300 | exit(1); 301 | } 302 | 303 | if(mode == INFO){ 304 | print_cartinfo(m); 305 | } 306 | else if(mode == READ){ 307 | printf("READ MODE =>\n"); 308 | m->seqRead(0, (uint16_t *)buffer, len/2); 309 | write(fd, buffer, len); 310 | close(fd); 311 | fclose(fp); 312 | printf("read finish\n"); 313 | } 314 | else if(mode == DUMP){ 315 | printf("DUMP MODE (0x%x) =>\n", addr); 316 | 317 | try{ 318 | m->seqRead(0, (uint16_t *)buffer, 256); 319 | printf("secRead:\n"); 320 | for(i=0; i<256; i++){ 321 | printf("%02x ", buffer[i] ); 322 | if(i%16==15) 323 | printf("\n"); 324 | } 325 | } 326 | catch(char *str) { 327 | printf("%s\n",str); 328 | } 329 | } 330 | 331 | else if(mode == BLOCK_ERASE){ 332 | printf(" BLOCK ERASE START =>\n"); 333 | m->secErase(block* 0x10000 , block_num); 334 | printf("block erase finish\n"); 335 | } 336 | 337 | else if(mode == CHIP_ERASE){ 338 | printf("CHIP ERASE START =>\n"); 339 | ret = m->chipErase(); 340 | printf("chip erase finish!(%d)\n",ret); 341 | } 342 | 343 | else if(mode == WRITE) 344 | { 345 | len = fread( buffer, sizeof( unsigned char ), len, fp ); 346 | //printf("\nfread : %d\n",len); 347 | printf("WRITE START =>\n"); 348 | m->seqProgram(0, (uint16_t *)buffer, len/2); 349 | 350 | printf("write finish!\n"); 351 | close(fd); 352 | fclose(fp); 353 | } 354 | 355 | else if(mode == DUPLICATE) 356 | { 357 | print_cartinfo(m); 358 | 359 | // ERASE 360 | printf("CHIP ERASE START =>\n"); 361 | ret = m->chipErase(); 362 | printf("chip erase finish!(%d)\n",ret); 363 | 364 | // WRITE 365 | len = fread( buffer, sizeof( unsigned char ), len, fp ); 366 | //printf("\nfread : %d\n",len); 367 | printf("WRITE START =>\n"); 368 | m->seqProgram(0, (uint16_t *)buffer, len/2); 369 | printf("write finish!\n"); 370 | 371 | // VERIFY 372 | printf("VERIFY START =>\n"); 373 | uint8_t * buffer2, b[2]; 374 | uint32_t error_num = 0; 375 | try{ 376 | buffer2 = new uint8_t[len]; 377 | m->seqRead(0, (uint16_t *)buffer2, len/2); 378 | } 379 | catch(char *str) { 380 | printf("%s\n",str); 381 | } 382 | 383 | for(i=0; iread(i/2, (uint16_t *)b); 388 | if(( b[i%2] & 0xff) == buffer[i] ) 389 | continue; 390 | 391 | if(error_num < 32) 392 | printf("%6x: %02x != %02x (rom)\n", i , buffer[i], buffer2[i] ); 393 | error_num++; 394 | } 395 | } 396 | close(fd); 397 | fclose(fp); 398 | printf("verify finish!\n"); 399 | 400 | if(error_num==0) 401 | printf("DUPLICATE SUCCESS!\n"); 402 | else 403 | printf("[WARN] error:%d\n",error_num); 404 | } 405 | 406 | else if(mode == VERIFY) 407 | { 408 | printf("VERIFY START =>\n"); 409 | uint8_t * buffer2, b[2]; 410 | uint32_t error_num = 0; 411 | try{ 412 | len = fread( buffer, sizeof( unsigned char ), len, fp ); 413 | buffer2 = new uint8_t[len]; 414 | m->seqRead(0, (uint16_t *)buffer2, len/2); 415 | } 416 | catch(char *str) { 417 | printf("%s\n",str); 418 | } 419 | 420 | for(i=0; iread(i/2, (uint16_t *)b); 425 | if(( b[i%2] & 0xff) == buffer[i] ) 426 | continue; 427 | 428 | if(error_num < 32) 429 | printf("%6x: %02x != %02x (rom)\n", i , buffer[i], buffer2[i] ); 430 | error_num++; 431 | } 432 | } 433 | close(fd); 434 | fclose(fp); 435 | printf("verify finish!\n"); 436 | printf("error:%d\n",error_num); 437 | } 438 | 439 | else if(mode == BLANK){ 440 | uint8_t b[2]; 441 | uint32_t error_num = 0; 442 | try{ 443 | len = m->getMemoryMbit() * 1024 * 1024 / 8; 444 | if(buffer) 445 | delete[] buffer; 446 | buffer = new uint8_t[len]; 447 | } 448 | catch(char *str) { 449 | printf("%s\n",str); 450 | } 451 | 452 | printf("BLANK CHECK=>\n"); 453 | 454 | m->seqRead(0, (uint16_t *)buffer, len/2); 455 | for(i=0; iread(i/2, (uint16_t *)b); 460 | if(( b[i%2] & 0xff) == 0xff ){ 461 | continue; 462 | } 463 | 464 | if(error_num < 32) 465 | printf("%06x: %02x != 0xff\n", i , buffer[i]); 466 | error_num++; 467 | } 468 | } 469 | printf("error_num:%d\n",error_num); 470 | } 471 | 472 | else if(mode == TEST) 473 | { 474 | 475 | } 476 | } 477 | else if(backup){ 478 | printf("Backup memory mode =>\n"); 479 | MemoryBackup *b = (MemoryBackup *)Memory::create(MEM_BACKUP); 480 | if(b == nullptr){ 481 | printf("cartridge can not be detected or not supported.\n"); 482 | exit(1); 483 | } 484 | printf(" Memory type: %s\n", b->getTypeStr()); 485 | printf(" Size: %d KB\n", b->getMemoryKb()); 486 | 487 | try{ 488 | len = b->getMemoryKb() * 1024; 489 | buffer = new uint8_t[len]; 490 | memset(buffer, 0, len); 491 | } 492 | catch (exception& e) 493 | { 494 | cout << e.what() << '\n'; 495 | printf("error : new error \n"); 496 | exit(1); 497 | } 498 | 499 | if(mode == DUMP){ 500 | for(i=0; i<256; i++){ 501 | b->read(i, (uint8_t *)buffer); 502 | printf("%02x ", buffer[0] ); 503 | if(i%16==15) 504 | printf("\n"); 505 | } 506 | } 507 | else if(mode == READ){ 508 | printf("READ START =>\n"); 509 | uint8_t *buf = (uint8_t *)buffer; 510 | b->load(0, buf, len); 511 | write(fd, buf, len); 512 | close(fd); 513 | fclose(fp); 514 | printf("read finish\n"); 515 | } 516 | else if(mode == WRITE){ 517 | printf("WRITE START =>\n"); 518 | uint8_t *buf = (uint8_t *)buffer; 519 | if(filesize > len) 520 | printf("WARNING: file size is over %d ( > %d )\n", filesize, len); 521 | 522 | EBackupType backup_type = b->getType(); 523 | if( backup_type >= BACKUP_FLASH && backup_type <= BACKUP_FLASH_CUBIC){ 524 | printf("Blank check...\n"); 525 | b->load(0, buf, len); 526 | for(i=0; ichipErase(); 530 | break; 531 | } 532 | } 533 | } 534 | 535 | len = fread( buf, sizeof( unsigned char ), len, fp ); 536 | b->save(0, buf, len); 537 | printf("write finish!\n"); 538 | } 539 | else if(mode == CHIP_ERASE){ 540 | printf("CHIP ERASE START =>\n"); 541 | ret = b->chipErase(); 542 | printf("chip erase finish!(%d)\n",ret); 543 | } 544 | else if(mode == BLANK){ 545 | printf("BLANK CHECK=>\n"); 546 | int32_t error=0; 547 | uint8_t *buf = (uint8_t *)buffer; 548 | b->load(0, buf, len); 549 | for(i=0; i\n"); 560 | int32_t error=0; 561 | uint8_t *buf = (uint8_t *)buffer; 562 | // ERASE TEST 563 | b->chipErase(); 564 | b->load(0, buf, len); 565 | for(i=0; i 0) 573 | goto end; 574 | 575 | // WRITE TEST 0xAA 576 | memset(buf, 0xaa, len); 577 | b->save(0, buf, len); 578 | 579 | for(i=0; i 0) 587 | goto end; 588 | 589 | 590 | // WRITE TEST 0x55 591 | b->chipErase(); 592 | memset(buf, 0x55, len); 593 | b->save(0, buf, len); 594 | 595 | for(i=0; i 0) 603 | goto end; 604 | 605 | b->chipErase(); 606 | 607 | printf("BACKUP MEMOEY TEST is Sucess\n"); 608 | } 609 | else if(mode == TEST2) 610 | { 611 | printf("BACKUP MEMORY TEST2 START=>\n"); 612 | int32_t error=0; 613 | uint8_t *buf = (uint8_t *)buffer; 614 | // ERASE TEST 615 | b->chipErase(); 616 | b->load(0, buf, len); 617 | for(i=0; i 0) 625 | goto end; 626 | 627 | // WRITE TEST 628 | memset(buf, 0x31, len); 629 | b->save(0, buf, len/2); 630 | memset(buf, 0x32, len); 631 | b->save(len/2, buf, len/2); 632 | } 633 | } 634 | 635 | end: 636 | if(buffer) 637 | delete[] buffer; 638 | 639 | return 0; 640 | } 641 | 642 | 643 | -------------------------------------------------------------------------------- /src/memory.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Raspberry Pi ADVANCE reader writer 3 | * Copyright (C) 2018 CUBIC STYLE 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "memory.h" 11 | #include "gpio.h" 12 | #include "header.h" 13 | #include "crc16.c" 14 | #include "crc16.h" 15 | #define LOGO_CRC 0x2e03 16 | 17 | void draw_progress(uint32_t p); 18 | #define PROGRESS(d,x) if(d) draw_progress(x); 19 | 20 | Memory::Memory() 21 | { 22 | } 23 | 24 | 25 | Memory::~Memory() 26 | { 27 | } 28 | 29 | // 対応させるメモリを書いていく 30 | // mask rom 31 | class MemoryMaskRom : public MemoryRom{ 32 | public: 33 | MemoryMaskRom(){ 34 | type = ROM_MASK; 35 | typstr = (char *)"MASK ROM"; 36 | memset(&header, 0, sizeof(RomHeader)); 37 | } 38 | static int32_t find(){ 39 | MemoryRom m; 40 | if(m.checkGbaHeader() < 0) 41 | return -1; 42 | return 1; 43 | } 44 | 45 | int32_t getDeviceCode(uint16_t *code){ 46 | return -1; 47 | } 48 | 49 | int32_t write(uint32_t wadd, uint16_t dat){ 50 | return -1; 51 | } 52 | }; 53 | 54 | 55 | class MemoryCubicFlash : public MemoryRom{ 56 | public: 57 | MemoryCubicFlash(uint32_t rom_size){ 58 | mbit = rom_size; 59 | type = ROM_FLASH_CUBIC; 60 | typstr = (char *)"Cubic Flash Cartridge"; 61 | } 62 | 63 | FlashMemoryInfo info; 64 | 65 | static int32_t getChipId(uint16_t *code){ 66 | MemoryRom m; 67 | m.write(0x0000, 0x00F0); 68 | 69 | m.write(0x0555, 0x00aa); 70 | m.write(0x02aa, 0x0055); 71 | m.write(0x0555, 0x0090); 72 | // silicon code 73 | m.read(0x0000, &code[0]); 74 | // read Device code 1,2,3 75 | m.read(0x0001, &code[1]); 76 | m.read(0x000e, &code[2]); 77 | m.read(0x000f, &code[3]); 78 | 79 | // command reset 80 | m.write(0x0000, 0x00F0); 81 | 82 | return 4; 83 | } 84 | 85 | static void setFlashMemoryInfo(FlashMemoryInfo* info, uint16_t *dev_code){ 86 | if(info == NULL) 87 | return; 88 | 89 | info->manufacturer_id = dev_code[0]; 90 | info->deveice_id[0] = dev_code[1]; 91 | info->deveice_id[1] = dev_code[2]; 92 | info->deveice_id[2] = dev_code[3]; 93 | info->write_buffer = 32; 94 | info->buff_prg_wait = 120; 95 | 96 | // mx29gl256 97 | if((dev_code[0] & 0xff) == 0xC2 && dev_code[1] == 0x227E 98 | && dev_code[2] == 0x2222 && dev_code[3] == 0x2201){ 99 | info->mbit = 256; 100 | info->sector_size = 0x10000; // 64K Word 101 | info->sector_mask = 0xFFFF0000; 102 | strcpy(info->device_name, "MX29GL256F"); 103 | } 104 | // m29w128 105 | else if((dev_code[0] & 0xff) == 0x20 && dev_code[1] == 0x227E 106 | && dev_code[2] == 0x2221 && dev_code[3] == 0x2200){ 107 | info->mbit = 128; 108 | info->sector_size = 0x10000; // 64K Word 109 | info->sector_mask = 0xFFFF0000; 110 | strcpy(info->device_name, "M29W128G"); 111 | } 112 | // S29GL032 113 | else if((dev_code[0] & 0xff) == 0x01 && dev_code[1] == 0x227E 114 | && dev_code[2] == 0x221a && dev_code[3] == 0x2200){ 115 | info->mbit = 32; 116 | info->sector_size = 0x8000; // 32K Word 117 | info->sector_mask = 0xFFFF8000; 118 | info->write_buffer = 16; 119 | strcpy(info->device_name, "S29GL032"); 120 | } 121 | // S29GL064S 122 | else if((dev_code[0] & 0xff) == 0x01 && dev_code[1] == 0x227E 123 | && dev_code[2] == 0x220c && dev_code[3] == 0x2201){ 124 | info->mbit = 64; 125 | info->sector_size = 0x8000; // 64K Word 126 | info->sector_mask = 0xFFFF8000; 127 | info->write_buffer = 128; // Warning: S29GL064N is 16word 128 | info->buff_prg_wait = 600; // 128word: type 300us max 1200us 129 | strcpy(info->device_name, "S29GL064S"); 130 | } 131 | // S29GL128N 132 | else if((dev_code[0] & 0xff) == 0x01 && dev_code[1] == 0x227E 133 | && dev_code[2] == 0x2221 && dev_code[3] == 0x2201){ 134 | info->mbit = 128; 135 | info->sector_size = 0x8000; // 32K Word 136 | info->sector_mask = 0xFFFF8000; 137 | info->write_buffer = 16; 138 | info->buff_prg_wait = 240; 139 | strcpy(info->device_name, "S29GL128N"); 140 | } 141 | // MT28EW256ABA 142 | else if((dev_code[0] & 0xff) == 0x89 && dev_code[1] == 0x227E 143 | && dev_code[2] == 0x2222 && dev_code[3] == 0x2201){ 144 | info->mbit = 256; 145 | info->sector_size = 0x10000; // 64K Word 146 | info->sector_mask = 0xFFFF0000; 147 | strcpy(info->device_name, "MT28EW256"); 148 | } 149 | // MT28EW128ABA 150 | else if((dev_code[0] & 0xff) == 0x89 && dev_code[1] == 0x227E 151 | && dev_code[2] == 0x2221 && dev_code[3] == 0x2201){ 152 | info->mbit = 128; 153 | info->sector_size = 0x10000; // 64K Word 154 | info->sector_mask = 0xFFFF0000; 155 | strcpy(info->device_name, "MT28EW128"); 156 | } 157 | 158 | #ifdef __DEBUG__ 159 | printf("\nFlash Memory Info=>\n"); 160 | printf(" Density : %d MBit\n", info->mbit); 161 | printf(" manufacturer id: %04x\n", info->manufacturer_id ); 162 | printf(" deveice id 1: %04x\n", info->deveice_id[0] ); 163 | printf(" 2: %04x\n", info->deveice_id[1] ); 164 | printf(" 3: %04x\n", info->deveice_id[2] ); 165 | printf("\n"); 166 | #endif 167 | 168 | } 169 | 170 | 171 | static int32_t find(FlashMemoryInfo* info = NULL){ 172 | uint16_t dev_code[4]; 173 | getChipId(dev_code); 174 | #ifdef __DEBUG__ 175 | for(int i=0; i<4; i++) { 176 | printf("dev code %d: %04x\n", i, dev_code[i]); 177 | } 178 | #endif 179 | setFlashMemoryInfo(info, dev_code); 180 | 181 | // mx29gl256 182 | if((dev_code[0] & 0xff) == 0xC2 && dev_code[1] == 0x227E 183 | && dev_code[2] == 0x2222 && dev_code[3] == 0x2201){ 184 | return 256; 185 | } 186 | // m29w128 187 | else if((dev_code[0] & 0xff) == 0x20 && dev_code[1] == 0x227E 188 | && dev_code[2] == 0x2221 && dev_code[3] == 0x2200){ 189 | return 128; 190 | } 191 | // S29GL032 192 | else if((dev_code[0] & 0xff) == 0x01 && dev_code[1] == 0x227E 193 | && dev_code[2] == 0x221a && dev_code[3] == 0x2200){ 194 | return 32; 195 | } 196 | // S29GL064 197 | else if((dev_code[0] & 0xff) == 0x01 && dev_code[1] == 0x227E 198 | && dev_code[2] == 0x220c && dev_code[3] == 0x2201){ 199 | return 64; 200 | } 201 | // S29GL128 202 | else if((dev_code[0] & 0xff) == 0x01 && dev_code[1] == 0x227E 203 | && dev_code[2] == 0x2221 && dev_code[3] == 0x2201){ 204 | return 128; 205 | } 206 | // MT28EW256ABA 207 | else if((dev_code[0] & 0xff) == 0x89 && dev_code[1] == 0x227E 208 | && dev_code[2] == 0x2222 && dev_code[3] == 0x2201){ 209 | return 256; 210 | } 211 | // MT28EW128ABA 212 | else if((dev_code[0] & 0xff) == 0x89 && dev_code[1] == 0x227E 213 | && dev_code[2] == 0x2221 && dev_code[3] == 0x2201){ 214 | return 128; 215 | } 216 | // Intel 217 | else if((dev_code[0] & 0xff) == 0x8a && dev_code[1] == 0x8902){ 218 | return 128; 219 | } 220 | 221 | else 222 | return -1; 223 | } 224 | 225 | char *getDeviceName(){ 226 | return info.device_name; 227 | } 228 | 229 | int32_t seqProgram(uint32_t wadd, uint16_t *dat, uint32_t len){ 230 | //const uint32_t buffer_size = 32; 231 | 232 | uint32_t sa = wadd & info.sector_mask, a = wadd; 233 | uint32_t i = 0, wl, rl = len, timeout_count = 0;; 234 | uint16_t status, k; 235 | 236 | // write buffer programming / 32 word 237 | while(rl > 0){ 238 | wl = rl > info.write_buffer ? info.write_buffer : rl; 239 | if(sa + info.sector_size < a + wl) // cant write across sectors 240 | wl = sa + info.sector_size - a; 241 | 242 | write(0x0555, 0x00aa); 243 | write(0x02aa, 0x0055); 244 | write(sa , 0x0025); 245 | write(sa , wl - 1); 246 | for(int j=0; j> 8){ 458 | case 0x16 : // 28F320J3A 459 | mbit = 32; 460 | break; 461 | case 0x17 : // 28F640J3A 462 | mbit = 64; 463 | break; 464 | case 0x18 : // 28F128J3A 465 | mbit = 128; 466 | break; 467 | default: 468 | turbo = true; 469 | // new cart 470 | switch(rom_size & 0xff){ 471 | case 0x16: // 2 x 28F320J3A 472 | mbit = 64; 473 | break; 474 | case 0x17: // 2 x 28F640J3A 475 | mbit = 128; 476 | break; 477 | case 0x18: // 2 x 28F128J3A 478 | mbit = 256; 479 | break; 480 | } 481 | } 482 | typstr = (char *)"Flash Advance Cartridge"; 483 | } 484 | 485 | 486 | static int32_t getChipId(uint16_t *code){ 487 | MemoryRom m; 488 | unlockRegister(m); 489 | 490 | // Identifier mode 491 | m.write(0x000000, 0x0090); 492 | 493 | // Manufacturer ID 494 | m.read(0x000000, &code[0]); 495 | // Device ID 496 | m.read(0x000001, &code[1]); 497 | m.read(0x000002, &code[2]); 498 | 499 | // command reset 500 | m.write(0x0000, 0x00ff); 501 | m.write(0x0000, 0x00ff); 502 | 503 | return 3; 504 | } 505 | 506 | static int32_t find(){ 507 | uint16_t dev_code[3]; 508 | getChipId(dev_code); 509 | 510 | #ifdef __DEBUG__ 511 | for(int i=0; i<3; i++) 512 | printf("dev code %d: %04x\n", i, dev_code[i]); 513 | #endif 514 | 515 | if(dev_code[0] == 0x0089 && ( (dev_code[1] >= 0x16 && dev_code[1] <= 0x18) 516 | || (dev_code[2] >= 0x16 && dev_code[2] <= 0x18) ) ) 517 | return (dev_code[1] & 0xff) << 8 | (dev_code[2] & 0xff); 518 | else 519 | return -1; 520 | } 521 | 522 | int32_t waitEraseReady(uint32_t wadd){ 523 | uint16_t a, b, i=0; 524 | // check progress 525 | while(1){ 526 | read(wadd, &a); 527 | a &= 0x80; 528 | if( turbo ){ 529 | read(wadd + 1, &b); 530 | b &= 0x80; 531 | } 532 | else 533 | b = 0x80; 534 | 535 | if( (a + b) == 0x100 ) 536 | break; 537 | 538 | //28F128j3a block erase typ:1sec max:5sec 539 | if(i++>110){ // timeout 540 | // printf("timeout! %x\n", wadd); 541 | break; 542 | } 543 | delay(50); 544 | } 545 | } 546 | 547 | int32_t waitWriteReady(uint32_t wadd){ 548 | uint16_t a, b, i=0; 549 | // check progress 550 | while(1){ 551 | read(wadd, &a); 552 | a &= 0x80; 553 | if( turbo ){ 554 | read(wadd + 1, &b); 555 | b &= 0x80; 556 | } 557 | else 558 | b = 0x80; 559 | 560 | //printf("%08x a:%02x b:%02x\n", wadd, a, b); 561 | //printf("."); 562 | 563 | if( (a + b) == 0x100 ) 564 | break; 565 | 566 | //28F128j3a write buffer typ:218us max:654us 567 | if(i++>5){ // timeout 568 | //printf("timeout! %x\n", wadd); 569 | break; 570 | } 571 | delayMicroseconds(220); 572 | } 573 | } 574 | 575 | 576 | int32_t secErase(uint32_t wadd, uint32_t num){ // turbo : block size: 2 * 0x10000 WB 577 | uint32_t a; 578 | for(int32_t i=0; i 0){ 632 | wl = rl > buffer_size ? buffer_size : rl; 633 | if(sa + 0x10000 < a + wl) // cant write across sectors 634 | wl = sa + 0x10000 - a; 635 | 636 | if(turbo){ 637 | write(a, 0x00E8); 638 | write(a, wl/2 + wl%2 - 1); 639 | if(wl > 1){ 640 | write(a+1, 0x00E8); 641 | write(a+1, wl/2 - 1); 642 | } 643 | } 644 | else{ 645 | write(a, 0x00E8); 646 | write(a, wl - 1); 647 | } 648 | 649 | waitWriteReady(a); 650 | 651 | for(int j=0; jlogo, sizeof(header->logo)) != LOGO_CRC){ 741 | #ifdef __DEBUG__ 742 | printf(" Header n logo not match!\n"); 743 | #endif 744 | return -1; 745 | } 746 | return 1; 747 | } 748 | 749 | // Backup 750 | int32_t MemoryBackup::read(uint32_t badd, uint8_t *dat) 751 | { 752 | gpio.setAdd(badd); 753 | gpio.csLow(); 754 | gpio.rdLow(); 755 | *dat = (uint8_t)gpio.getData(); 756 | gpio.rdHigh(); 757 | gpio.csHigh(); 758 | return 1; 759 | } 760 | 761 | int32_t MemoryBackup::write(uint32_t badd, uint8_t dat) 762 | { 763 | gpio.setAdd(badd); 764 | gpio.csLow(); 765 | gpio.setData(dat); 766 | gpio.wrLow(); 767 | gpio.wrHigh(); 768 | gpio.csHigh(); 769 | return 1; 770 | } 771 | 772 | int32_t MemoryBackup::load(uint32_t badd, uint8_t *dat, uint32_t len){ 773 | for(int i=0; i 0){ 1169 | MemoryCubicFlash *m = new MemoryCubicFlash(size); 1170 | m->info = info; 1171 | return (Memory *)m; 1172 | } 1173 | if((size = MemoryF2aFlash::find()) > 0 ){ 1174 | return (Memory *)new MemoryF2aFlash(size); 1175 | } 1176 | if(MemoryMaskRom::find() < 0) 1177 | return nullptr; 1178 | 1179 | return (Memory *)new MemoryMaskRom(); 1180 | } 1181 | else if(ms == MEM_BACKUP){ 1182 | if(MemoryBackupSram::find() > 0){ 1183 | return (Memory *)new MemoryBackupSram(); 1184 | } 1185 | else if((size = MemoryBackupCubic::find()) > 0){ 1186 | return (Memory *)new MemoryBackupCubic(size); 1187 | } 1188 | else if((size = MemoryBackupFlash::find()) > 0){ 1189 | return (Memory *)new MemoryBackupFlash(size); 1190 | } 1191 | else if((size = MemoryBackupFlashLarge::find()) > 0){ 1192 | return (Memory *)new MemoryBackupFlashLarge(size); 1193 | } 1194 | 1195 | return nullptr; 1196 | } 1197 | return nullptr; 1198 | } 1199 | --------------------------------------------------------------------------------