diff --git a/Marlin/src/gcode/parser.cpp b/Marlin/src/gcode/parser.cpp index ebe9d3b2cd..6eb56126e1 100644 --- a/Marlin/src/gcode/parser.cpp +++ b/Marlin/src/gcode/parser.cpp @@ -28,6 +28,146 @@ #include "../MarlinCore.h" +#ifdef __AVR__ + + static FORCE_INLINE uint32_t mult10(uint32_t val) { + uint32_t tmp = val; + __asm__ __volatile__ ( + "add %A[tmp], %A[tmp]\n" + "adc %B[tmp], %B[tmp]\n" + "adc %C[tmp], %C[tmp]\n" + "adc %D[tmp], %D[tmp]\n" + "add %A[tmp], %A[tmp]\n" + "adc %B[tmp], %B[tmp]\n" + "adc %C[tmp], %C[tmp]\n" + "adc %D[tmp], %D[tmp]\n" + "add %A[val], %A[tmp]\n" + "adc %B[val], %B[tmp]\n" + "adc %C[val], %C[tmp]\n" + "adc %D[val], %D[tmp]\n" + "add %A[val], %A[val]\n" + "adc %B[val], %B[val]\n" + "adc %C[val], %C[val]\n" + "adc %D[val], %D[val]\n" + : [val] "+&r" (val), + [tmp] "+&r" (tmp) + ); + return val; + } + +#else + + static FORCE_INLINE uint32_t mult10(uint32_t val) { return val * 10; } + +#endif + +// cheap base-10 strto(u)l. +// does not check for errors. +int32_t parse_int32(const char *buf) { + char c; + + // Get a char, skipping leading spaces + do { c = *buf++; } while (c == ' '); + + // check for sign + bool is_negative = (c == '-'); + if (is_negative || c == '+') + c = *buf++; + + // optimization for first digit (no multiplication) + uint8_t uc = c - '0'; + if (uc > 9) return 0; + + // read unsigned value + uint32_t uval = uc; + while (true) { + c = *buf++; + uc = c - '0'; + if (uc > 9) break; + uval = mult10(uval) + uc; + } + + return is_negative ? -uval : uval; +} + +// cheap strtof. +// does not support nan/infinity or exponent notation. +// does not check for errors. +float parse_float(const char *buf) { + char c; + + // Get a char, skipping leading spaces + do { c = *buf++; } while (c == ' '); + + // check for sign + bool is_negative = (c == '-'); + if (is_negative || c == '+') + c = *buf++; + + // read unsigned value and decimal point + uint32_t uval; + uint8_t exp_dec; + uint8_t uc = c - '0'; + if (uc <= 9) { + uval = uc; + exp_dec = 0; + } + else { + if (c != '.') return 0; + uval = 0; + exp_dec = 1; + } + + int8_t exp = 0; + while (true) { + c = *buf++; + uc = c - '0'; + if (uc <= 9) { + exp -= exp_dec; + uval = mult10(uval) + uc; + if (uval >= (UINT32_MAX - 9) / 10) { + // overflow. keep reading digits until decimal point. + while (exp_dec == 0) { + c = *buf++; + uc = c - '0'; + if (uc > 9) break; + exp++; + } + goto overflow; + } + } + else { + if (c != '.' || exp_dec != 0) break; + exp_dec = 1; + } + } + + // early return for 0 + if (uval == 0) return 0; + + overflow: + + // convert to float and apply sign + float fval = uval; + if (is_negative) fval *= -1; + + // apply exponent (up to 1e-15 / 1e+15) + if (exp < 0) { + if (exp <= -8) { fval *= 1e-8; exp += 8; } + if (exp <= -4) { fval *= 1e-4; exp += 4; } + if (exp <= -2) { fval *= 1e-2; exp += 2; } + if (exp <= -1) { fval *= 1e-1; exp += 1; } + } + else if (exp > 0) { + if (exp >= 8) { fval *= 1e+8; exp -= 8; } + if (exp >= 4) { fval *= 1e+4; exp -= 4; } + if (exp >= 2) { fval *= 1e+2; exp -= 2; } + if (exp >= 1) { fval *= 1e+1; exp -= 1; } + } + + return fval; +} + // Must be declared for allocation and to satisfy the linker // Zero values need no initialization. diff --git a/Marlin/src/gcode/parser.h b/Marlin/src/gcode/parser.h index b21c930cfa..ed503f8a90 100644 --- a/Marlin/src/gcode/parser.h +++ b/Marlin/src/gcode/parser.h @@ -42,6 +42,10 @@ typedef enum : uint8_t { LINEARUNIT_MM, LINEARUNIT_INCH } LinearUnit; #endif + +int32_t parse_int32(const char *buf); +float parse_float(const char *buf); + /** * GCode parser * @@ -256,29 +260,12 @@ public: // The value as a string static inline char* value_string() { return value_ptr; } - // Float removes 'E' to prevent scientific notation interpretation - static inline float value_float() { - if (value_ptr) { - char *e = value_ptr; - for (;;) { - const char c = *e; - if (c == '\0' || c == ' ') break; - if (c == 'E' || c == 'e') { - *e = '\0'; - const float ret = strtof(value_ptr, nullptr); - *e = c; - return ret; - } - ++e; - } - return strtof(value_ptr, nullptr); - } - return 0; - } + // Code value as float + static inline float value_float() { return value_ptr ? parse_float(value_ptr) : 0.0; } // Code value as a long or ulong - static inline int32_t value_long() { return value_ptr ? strtol(value_ptr, nullptr, 10) : 0L; } - static inline uint32_t value_ulong() { return value_ptr ? strtoul(value_ptr, nullptr, 10) : 0UL; } + static inline int32_t value_long() { return value_ptr ? parse_int32(value_ptr) : 0L; } + static inline uint32_t value_ulong() { return value_ptr ? parse_int32(value_ptr) : 0UL; } // Code value for use as time static inline millis_t value_millis() { return value_ulong(); } diff --git a/Marlin/src/gcode/queue.cpp b/Marlin/src/gcode/queue.cpp index a79909917e..eb7ad79102 100644 --- a/Marlin/src/gcode/queue.cpp +++ b/Marlin/src/gcode/queue.cpp @@ -445,7 +445,7 @@ void GCodeQueue::get_serial_commands() { if (process_line_done(serial.input_state, serial.line_buffer, serial.count)) continue; - char* command = serial.line_buffer; + char *command = serial.line_buffer; while (*command == ' ') command++; // Skip leading spaces char *npos = (*command == 'N') ? command : nullptr; // Require the N parameter to start the line @@ -459,7 +459,7 @@ void GCodeQueue::get_serial_commands() { if (n2pos) npos = n2pos; } - const long gcode_N = strtol(npos + 1, nullptr, 10); + const long gcode_N = parse_int32(npos + 1); if (gcode_N != serial.last_N + 1 && !M110) { // In case of error on a serial port, don't prevent other serial port from making progress @@ -471,7 +471,7 @@ void GCodeQueue::get_serial_commands() { if (apos) { uint8_t checksum = 0, count = uint8_t(apos - command); while (count) checksum ^= command[--count]; - if (strtol(apos + 1, nullptr, 10) != checksum) { + if (parse_int32(apos + 1) != checksum) { // In case of error on a serial port, don't prevent other serial port from making progress gcode_line_error(PSTR(STR_ERR_CHECKSUM_MISMATCH), p); break; @@ -500,7 +500,7 @@ void GCodeQueue::get_serial_commands() { if (IsStopped()) { char* gpos = strchr(command, 'G'); if (gpos) { - switch (strtol(gpos + 1, nullptr, 10)) { + switch (parse_int32(gpos + 1)) { case 0: case 1: #if ENABLED(ARC_SUPPORT) case 2: case 3: diff --git a/Marlin/src/lcd/extui/lib/mks_ui/wifi_module.cpp b/Marlin/src/lcd/extui/lib/mks_ui/wifi_module.cpp index a74ecd2f1e..ac07dc0102 100644 --- a/Marlin/src/lcd/extui/lib/mks_ui/wifi_module.cpp +++ b/Marlin/src/lcd/extui/lib/mks_ui/wifi_module.cpp @@ -1799,7 +1799,7 @@ void get_wifi_commands() { if (IsStopped()) { char* gpos = strchr(command, 'G'); if (gpos) { - switch (strtol(gpos + 1, nullptr, 10)) { + switch (parse_int32(gpos + 1)) { case 0 ... 1: #if ENABLED(ARC_SUPPORT) case 2 ... 3: