From 2f8bea17b784a3a17489790fe66649ba32536a1f Mon Sep 17 00:00:00 2001 From: KurtE Date: Mon, 9 May 2022 13:42:49 -0700 Subject: [PATCH] Allow Windows builds using Visual Studio. As I mentioned in the Readme file, I am currently building using the Visual Studio 2022 release and probably needed to install the WDK. Also need to build for x86. Note: This started off with has some of my experimental stuff for working with CircuitPython to see why their sketches wipe out the Filesystem that is installed at the 1MB address So some of the debug stuff is still in this version teensy_loader_cli.c - -d option dumps the ranges of memory that the .hex file is setup to upload to. -f option filled holes in memory ranges with 0s. Can/should probably remove this as this turned out not the reason why everything was erased --- .gitignore | 3 + Makefile | 1 - README.md | 8 + Teensy_loader_cli.sln | 31 +++ Teensy_loader_cli.vcxproj | 150 ++++++++++ Teensy_loader_cli.vcxproj.filters | 22 ++ Teensy_loader_cli.vcxproj.user | 4 + teensy_loader_cli.c | 443 +++++++++++++++++++++++------- 8 files changed, 563 insertions(+), 99 deletions(-) create mode 100644 Teensy_loader_cli.sln create mode 100644 Teensy_loader_cli.vcxproj create mode 100644 Teensy_loader_cli.vcxproj.filters create mode 100644 Teensy_loader_cli.vcxproj.user diff --git a/.gitignore b/.gitignore index ea0c43c..778c321 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +Debug +Release +.vs teensy_loader_cli teensy_loader_cli.exe* *.swp diff --git a/Makefile b/Makefile index 2d1bda1..2f2a8b0 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,6 @@ CFLAGS ?= -O2 -Wall teensy_loader_cli: teensy_loader_cli.c $(CC) $(CFLAGS) $(CPPFLAGS) -s -DUSE_LIBUSB -o teensy_loader_cli teensy_loader_cli.c -lusb $(LDFLAGS) - else ifeq ($(OS), WINDOWS) CC ?= i586-mingw32msvc-gcc CFLAGS ?= -O2 -Wall diff --git a/README.md b/README.md index 5be3f18..8be33cf 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,14 @@ Optional command line parameters: `-v` : Verbose output. Normally teensy_loader_cli prints only error messages if any operation fails. This enables verbose output, which can help with troubleshooting, or simply show you more status information. +## Building with Visual Studio for Windows + +Experimental Added support to build for windows using Visual Studio. + +I am currently using Visual Studio 2022, but belive it would work with 2019 as well. You will probably need to install the windows WDK to build +this. You will also need to x86 and not x64 as I don't believe all of the HID and setup APIS are available for x64. Or at least my few attempts +to build using them failed in the link phase. + ## System Specific Setup Linux requires UDEV rules for non-root users. diff --git a/Teensy_loader_cli.sln b/Teensy_loader_cli.sln new file mode 100644 index 0000000..9791d27 --- /dev/null +++ b/Teensy_loader_cli.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32228.430 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Teensy_loader_cli", "Teensy_loader_cli.vcxproj", "{F6BBBADD-8524-41F7-80B2-28DAB07CE994}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Debug|x64.ActiveCfg = Debug|x64 + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Debug|x64.Build.0 = Debug|x64 + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Debug|x86.ActiveCfg = Debug|Win32 + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Debug|x86.Build.0 = Debug|Win32 + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Release|x64.ActiveCfg = Release|x64 + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Release|x64.Build.0 = Release|x64 + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Release|x86.ActiveCfg = Release|Win32 + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {01112176-E808-4C22-BCE1-02F372C38038} + EndGlobalSection +EndGlobal diff --git a/Teensy_loader_cli.vcxproj b/Teensy_loader_cli.vcxproj new file mode 100644 index 0000000..3609441 --- /dev/null +++ b/Teensy_loader_cli.vcxproj @@ -0,0 +1,150 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {f6bbbadd-8524-41f7-80b2-28dab07ce994} + Teensyloadercli + 10.0 + Teensy_loader_cli + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level2 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + hid.lib;setupapi.lib;winmm.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;VSTUDIO;%(PreprocessorDefinitions) + true + + + Console + true + true + true + hid.lib;setupapi.lib;winmm.lib;%(AdditionalDependencies) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/Teensy_loader_cli.vcxproj.filters b/Teensy_loader_cli.vcxproj.filters new file mode 100644 index 0000000..a7ba3e2 --- /dev/null +++ b/Teensy_loader_cli.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/Teensy_loader_cli.vcxproj.user b/Teensy_loader_cli.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/Teensy_loader_cli.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/teensy_loader_cli.c b/teensy_loader_cli.c index 84e5400..88f75eb 100644 --- a/teensy_loader_cli.c +++ b/teensy_loader_cli.c @@ -16,14 +16,14 @@ * along with this program. If not, see http://www.gnu.org/licenses/ */ -/* Want to incorporate this code into a proprietary application?? - * Just email paul@pjrc.com to ask. Usually it's not a problem, - * but you do need to ask to use this code in any way other than - * those permitted by the GNU General Public License, version 3 */ + /* Want to incorporate this code into a proprietary application?? + * Just email paul@pjrc.com to ask. Usually it's not a problem, + * but you do need to ask to use this code in any way other than + * those permitted by the GNU General Public License, version 3 */ -/* For non-root permissions on ubuntu or similar udev-based linux - * http://www.pjrc.com/teensy/49-teensy.rules - */ + /* For non-root permissions on ubuntu or similar udev-based linux + * http://www.pjrc.com/teensy/49-teensy.rules + */ #include @@ -31,19 +31,26 @@ #include #include #include -#include +//#include + +#if defined(WIN32) +#include +#endif +//#include "setup_hid.h" -void usage(const char *err) +void usage(const char* err) { - if(err != NULL) fprintf(stderr, "%s\n\n", err); + if (err != NULL) fprintf(stderr, "%s\n\n", err); fprintf(stderr, - "Usage: teensy_loader_cli --mcu= [-w] [-h] [-n] [-b] [-v] \n" + "Usage: teensy_loader_cli --mcu= [-w] [-h] [-n] [-b] [-v] [-f] [-d] \n" "\t-w : Wait for device to appear\n" "\t-r : Use hard reboot if device not online\n" "\t-s : Use soft reboot if device not online (Teensy 3.x & 4.x)\n" "\t-n : No reboot after programming\n" "\t-b : Boot only, do not program\n" "\t-v : Verbose output\n" + "\t-f : Fill memory region holes with 0s\n" + "\t-d : dump hex file memory ranges\n" "\nUse `teensy_loader_cli --list-mcus` to list supported MCUs.\n" "\nFor more information, please visit:\n" "http://www.pjrc.com/teensy/loader_cli.html\n"); @@ -58,17 +65,18 @@ int hard_reboot(void); int soft_reboot(void); // Intel Hex File Functions -int read_intel_hex(const char *filename); +int read_intel_hex(const char* filename); +void fill_the_holes(); int ihex_bytes_within_range(int begin, int end); -void ihex_get_data(int addr, int len, unsigned char *bytes); +void ihex_get_data(int addr, int len, unsigned char* bytes); int memory_is_blank(int addr, int block_size); // Misc stuff -int printf_verbose(const char *format, ...); +int printf_verbose(const char* format, ...); void delay(double seconds); -void die(const char *str, ...); -void parse_options(int argc, char **argv); -void boot(unsigned char *buf, int write_size); +void die(const char* str, ...); +void parse_options(int argc, char** argv); +void boot(unsigned char* buf, int write_size); // options (from user via command line args) int wait_for_device_to_appear = 0; @@ -78,7 +86,9 @@ int reboot_after_programming = 1; int verbose = 0; int boot_only = 0; int code_size = 0, block_size = 0; -const char *filename=NULL; +int fill_holes = 0; +int dump_memory_ranges = 0; +const char* filename = NULL; /****************************************************************/ @@ -87,12 +97,12 @@ const char *filename=NULL; /* */ /****************************************************************/ -int main(int argc, char **argv) +int main(int argc, char** argv) { unsigned char buf[2048]; int num, addr, r, write_size; - int first_block=1, waited=0; + int first_block = 1, waited = 0; // parse command line arguments parse_options(argc, argv); @@ -102,11 +112,12 @@ int main(int argc, char **argv) if (!code_size) { usage("MCU type must be specified"); } - printf_verbose("Teensy Loader, Command Line, Version 2.2\n"); + printf_verbose("Teensy Loader, Command Line, Version 2.S\n"); if (block_size == 512 || block_size == 1024) { write_size = block_size + 64; - } else { + } + else { write_size = block_size + 2; }; @@ -114,6 +125,7 @@ int main(int argc, char **argv) // read the intel hex file // this is done first so any error is reported before using USB num = read_intel_hex(filename); + if (fill_holes) fill_the_holes(); if (num < 0) die("error reading intel hex file \"%s\"", filename); printf_verbose("Read \"%s\": %d bytes, %.1f%% usage\n", filename, num, (double)num / (double)code_size * 100.0); @@ -157,7 +169,7 @@ int main(int argc, char **argv) num = read_intel_hex(filename); if (num < 0) die("error reading intel hex file \"%s\"", filename); printf_verbose("Read \"%s\": %d bytes, %.1f%% usage\n", - filename, num, (double)num / (double)code_size * 100.0); + filename, num, (double)num / (double)code_size * 100.0); } // program the data @@ -176,19 +188,22 @@ int main(int argc, char **argv) buf[1] = (addr >> 8) & 255; ihex_get_data(addr, block_size, buf + 2); write_size = block_size + 2; - } else if (block_size == 256) { + } + else if (block_size == 256) { buf[0] = (addr >> 8) & 255; buf[1] = (addr >> 16) & 255; ihex_get_data(addr, block_size, buf + 2); write_size = block_size + 2; - } else if (block_size == 512 || block_size == 1024) { + } + else if (block_size == 512 || block_size == 1024) { buf[0] = addr & 255; buf[1] = (addr >> 8) & 255; buf[2] = (addr >> 16) & 255; memset(buf + 3, 0, 61); ihex_get_data(addr, block_size, buf + 64); write_size = block_size + 64; - } else { + } + else { die("Unknown code/block size\n"); } r = teensy_write(buf, write_size, first_block ? 5.0 : 0.5); @@ -521,6 +536,177 @@ int soft_reboot(void) return 0; } +#elif defined(WIN32) +// Visual studio build +#include +#include +#include +#include +#include +#include +#include +static HANDLE rx_event = NULL; +static HANDLE tx_event = NULL; +static CRITICAL_SECTION rx_mutex; +static CRITICAL_SECTION tx_mutex; + +HANDLE open_usb_device(int vid, int pid) +{ + GUID guid; + HDEVINFO info; + DWORD index, required_size; + SP_DEVICE_INTERFACE_DATA iface; + SP_DEVICE_INTERFACE_DETAIL_DATA* details; + HIDD_ATTRIBUTES attrib; + HANDLE h; + BOOL ret; + + if (!rx_event) { + rx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + tx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + InitializeCriticalSection(&rx_mutex); + InitializeCriticalSection(&tx_mutex); + } + + HidD_GetHidGuid(&guid); + info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (info == INVALID_HANDLE_VALUE) return NULL; + for (index = 0; 1; index++) { + iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + ret = SetupDiEnumDeviceInterfaces(info, NULL, &guid, index, &iface); + if (!ret) { + SetupDiDestroyDeviceInfoList(info); + break; + } + SetupDiGetInterfaceDeviceDetail(info, &iface, NULL, 0, &required_size, NULL); + details = (SP_DEVICE_INTERFACE_DETAIL_DATA*)malloc(required_size); + if (details == NULL) continue; + memset(details, 0, required_size); + details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + ret = SetupDiGetDeviceInterfaceDetail(info, &iface, details, + required_size, NULL, NULL); + if (!ret) { + free(details); + continue; + } + h = CreateFile(details->DevicePath, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + free(details); + if (h == INVALID_HANDLE_VALUE) continue; + attrib.Size = sizeof(HIDD_ATTRIBUTES); + ret = HidD_GetAttributes(h, &attrib); + if (!ret) { + CloseHandle(h); + continue; + } + if (attrib.VendorID != vid || attrib.ProductID != pid) { + CloseHandle(h); + continue; + } + SetupDiDestroyDeviceInfoList(info); + return h; + } + return NULL; +} + +int write_usb_device(HANDLE h, void* buf, int len, int timeout) +{ + static HANDLE event = NULL; + unsigned char tmpbuf[1089]; + OVERLAPPED ov; + DWORD n, r; + + if (len > sizeof(tmpbuf) - 1) return 0; + if (event == NULL) { + event = CreateEvent(NULL, TRUE, TRUE, NULL); + if (!event) return 0; + } + ResetEvent(&event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = event; + tmpbuf[0] = 0; + memcpy(tmpbuf + 1, buf, len); + if (!WriteFile(h, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) return 0; + r = WaitForSingleObject(event, timeout); + if (r == WAIT_TIMEOUT) { + CancelIo(h); + return 0; + } + if (r != WAIT_OBJECT_0) return 0; + } + if (!GetOverlappedResult(h, &ov, &n, FALSE)) return 0; + if (n <= 0) return 0; + return 1; +} + +void print_win32_err(void) +{ + char buf[256]; + DWORD err; + + err = GetLastError(); + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, + 0, buf, sizeof(buf), NULL); + printf("err %ld: %s\n", err, buf); +} + +static HANDLE win32_teensy_handle = NULL; + +int teensy_open(void) +{ + teensy_close(); + win32_teensy_handle = open_usb_device(0x16C0, 0x0478); + if (win32_teensy_handle) return 1; + return 0; +} + +int teensy_write(void* buf, int len, double timeout) +{ + int r; + uint32_t begin, now, total; + + if (!win32_teensy_handle) return 0; + total = (uint32_t)(timeout * 1000.0); + begin = timeGetTime(); + now = begin; + do { + r = write_usb_device(win32_teensy_handle, buf, len, total - (now - begin)); + if (r > 0) return 1; + Sleep(10); + now = timeGetTime(); + } while (now - begin < total); + return 0; + +} + +void teensy_close(void) +{ + if (!win32_teensy_handle) return; + CloseHandle(win32_teensy_handle); + win32_teensy_handle = NULL; +} + +int hard_reboot(void) +{ + HANDLE rebootor; + int r; + + rebootor = open_usb_device(0x16C0, 0x0477); + if (!rebootor) return 0; + r = write_usb_device(rebootor, (void*)"reboot", 6, 100); + CloseHandle(rebootor); + return r; +} + +int soft_reboot(void) +{ + printf("Soft reboot is not implemented for Win32\n"); + return 0; +} + + #endif @@ -843,27 +1029,38 @@ int soft_reboot(void) static unsigned char firmware_image[MAX_MEMORY_SIZE]; static unsigned char firmware_mask[MAX_MEMORY_SIZE]; -static int end_record_seen=0; +static int end_record_seen = 0; static int byte_count; static unsigned int extended_addr = 0; -static int parse_hex_line(char *line); +static unsigned int actual_extended_addr = 0; +static unsigned int start_addr = 0; +static unsigned int end_addr = 0; +static int parse_hex_line(char* line); + +static unsigned int min_address = MAX_MEMORY_SIZE; +static unsigned int max_address = 0; -int read_intel_hex(const char *filename) +int read_intel_hex(const char* filename) { - FILE *fp; - int i, lineno=0; + FILE* fp; + int i, lineno = 0; char buf[1024]; byte_count = 0; end_record_seen = 0; - for (i=0; i= MAX_MEMORY_SIZE) return 0; ptr += 2; sum = (len & 255) + ((addr >> 8) & 255) + (addr & 255) + (code & 255); if (code != 0) { if (code == 1) { + if (dump_memory_ranges && (start_addr != end_addr)) { + printf("%8x - %8x\n$$\n", actual_extended_addr + start_addr, actual_extended_addr + end_addr); + } + if (verbose && actual_extended_addr >= (0x60000000u)) { + printf("\nFlashconfig\n"); + uint32_t* flash_config = (uint32_t*)(&firmware_image[0]); + for (unsigned int i = 0; i < 128; i += 4) { + printf("\t%04x:%08x %08x %08x %08x\n", i * 4, flash_config[i], flash_config[i + 1], + flash_config[i + 2], flash_config[i + 3]); + } + + uint32_t* ivt = (uint32_t*)(&firmware_image[0x1000]); + printf("\nInterrupt Vector\n"); + for (unsigned int i = 0; i < 8; i += 4) { + printf("\t%04x:%08x %08x %08x %08x\n", i * 4, ivt[i], ivt[i + 1], + ivt[i + 2], ivt[i + 3]); + } + + uint32_t* boot_data = (uint32_t*)(&firmware_image[0x1020]); + printf("\nBoot data: %08x %08x(%u) %08x\n", boot_data[0], boot_data[1], boot_data[1], boot_data[2]); + } end_record_seen = 1; return 1; } if (code == 2 && len == 2) { - if (!sscanf(ptr, "%04x", &i)) return 1; + if (!SSCANF(ptr, "%04x", &i)) return 1; ptr += 4; sum += ((i >> 8) & 255) + (i & 255); - if (!sscanf(ptr, "%02x", &cksum)) return 1; + if (!SSCANF(ptr, "%02x", &cksum)) return 1; if (((sum & 255) + (cksum & 255)) & 255) return 1; - extended_addr = i << 4; + actual_extended_addr = extended_addr = i << 4; //printf("ext addr = %05X\n", extended_addr); } if (code == 4 && len == 2) { - if (!sscanf(ptr, "%04x", &i)) return 1; + if (!SSCANF(ptr, "%04x", &i)) return 1; ptr += 4; sum += ((i >> 8) & 255) + (i & 255); - if (!sscanf(ptr, "%02x", &cksum)) return 1; + if (!SSCANF(ptr, "%02x", &cksum)) return 1; if (((sum & 255) + (cksum & 255)) & 255) return 1; - extended_addr = i << 16; + if (dump_memory_ranges && (start_addr != end_addr)) printf("%8x - %8x\n", actual_extended_addr + start_addr, actual_extended_addr + end_addr); + actual_extended_addr = extended_addr = i << 16; + start_addr = 0; + end_addr = 0; if (code_size > 1048576 && block_size >= 1024 && - extended_addr >= 0x60000000 && extended_addr < 0x60000000 + code_size) { + extended_addr >= 0x60000000u && extended_addr < 0x60000000u + code_size) { // Teensy 4.0 HEX files have 0x60000000 FlexSPI offset extended_addr -= 0x60000000; } @@ -945,8 +1170,13 @@ parse_hex_line(char *line) return 1; // non-data line } byte_count += len; + if (addr != (end_addr)) { + if (dump_memory_ranges && (start_addr != end_addr)) printf("%8x - %8x\n", actual_extended_addr + start_addr, actual_extended_addr + end_addr); + start_addr = addr; + } + end_addr = addr + len; while (num != len) { - if (sscanf(ptr, "%02x", &i) != 1) return 0; + if (SSCANF(ptr, "%02x", &i) != 1) return 0; i &= 255; firmware_image[addr + extended_addr + num] = i; firmware_mask[addr + extended_addr + num] = 1; @@ -955,39 +1185,56 @@ parse_hex_line(char *line) (num)++; if (num >= 256) return 0; } - if (!sscanf(ptr, "%02x", &cksum)) return 0; + // keep track of minimum address and max address + if ((addr + extended_addr) < min_address) min_address = addr + extended_addr; + if ((addr + extended_addr + len) > max_address) max_address = addr + extended_addr + len; + + if (!SSCANF(ptr, "%02x", &cksum)) return 0; if (((sum & 255) + (cksum & 255)) & 255) return 0; /* checksum error */ return 1; } +void fill_the_holes() +{ + unsigned int addr; + for (addr = min_address; addr < max_address; addr++) { + if (firmware_mask[addr] == 0) { + firmware_mask[addr] = 1; + firmware_image[addr] = 0; + } + + } +} + int ihex_bytes_within_range(int begin, int end) { int i; if (begin < 0 || begin >= MAX_MEMORY_SIZE || - end < 0 || end >= MAX_MEMORY_SIZE) { + end < 0 || end >= MAX_MEMORY_SIZE) { return 0; } - for (i=begin; i<=end; i++) { + for (i = begin; i <= end; i++) { if (firmware_mask[i]) return 1; } return 0; } -void ihex_get_data(int addr, int len, unsigned char *bytes) +void ihex_get_data(int addr, int len, unsigned char* bytes) { int i; if (addr < 0 || len < 0 || addr + len >= MAX_MEMORY_SIZE) { - for (i=0; i