/* Borland far-string and memory helpers reconstructed from Code2. */ #include "tdkpin_strings.h" #include typedef struct { Win16FarPtr after; uint16_t remaining; bool found; } FarNulScan; static FarNulScan scan_nul(Win16FarPtr string, uint16_t maximum) { Win16FarPtr current = string; uint16_t remaining = maximum; bool found = false; while (remaining != 0) { uint8_t value = win16_read_u8(current); current = win16_far_add_offset(current, 1); remaining--; if (value == 0) { found = true; break; } } return (FarNulScan){current, remaining, found}; } /* 1020:0891 — unconditional forward CLD/REP MOVSB far copy. */ void borland_far_copy( Win16FarPtr destination, Win16FarPtr source, uint16_t count) { for (uint16_t index = 0; index != count; index++) { win16_write_u8( win16_far_add_offset(destination, index), win16_read_u8(win16_far_add_offset(source, index))); } } /* 1008:2e92 — REPNE SCASB strlen with CX=ffff. */ uint16_t borland_far_strlen(Win16FarPtr string) { FarNulScan scan = scan_nul(string, 0xffff); return (uint16_t)(0xfffe - scan.remaining); } /* 1008:2ea9 — pointer to the NUL found by the same bounded scan. */ Win16FarPtr borland_far_strend(Win16FarPtr string) { FarNulScan scan = scan_nul(string, 0xffff); return win16_far_add_offset(scan.after, 0xffff); } /* * 1008:2ec0 — far memmove. * Direction is selected solely by the unsigned source/destination offsets; * selectors are deliberately not compared, exactly like CMP SI,DI. */ Win16FarPtr borland_far_memmove( Win16FarPtr destination, Win16FarPtr source, uint16_t count) { if (win16_far_offset(source) < win16_far_offset(destination)) { for (uint16_t remaining = count; remaining != 0; remaining--) { uint16_t index = (uint16_t)(remaining - 1); win16_write_u8( win16_far_add_offset(destination, index), win16_read_u8(win16_far_add_offset(source, index))); } } else { for (uint16_t index = 0; index != count; index++) { win16_write_u8( win16_far_add_offset(destination, index), win16_read_u8(win16_far_add_offset(source, index))); } } return destination; } /* 1008:2ee5 — copy through and including the bounded NUL. */ Win16FarPtr borland_far_strcpy(Win16FarPtr destination, Win16FarPtr source) { FarNulScan scan = scan_nul(source, 0xffff); uint16_t count = (uint16_t)~scan.remaining; for (uint16_t index = 0; index != count; index++) { win16_write_u8( win16_far_add_offset(destination, index), win16_read_u8(win16_far_add_offset(source, index))); } return destination; } /* * 1008:2f07 — bounded copy followed by an unconditional extra NUL. * If the source NUL lies within maximum, that NUL is copied and a second one is * appended; if not, maximum bytes plus one NUL are written. */ Win16FarPtr borland_far_strncpy( Win16FarPtr destination, Win16FarPtr source, uint16_t maximum) { FarNulScan scan = scan_nul(source, maximum); uint16_t count = (uint16_t)(maximum - scan.remaining); for (uint16_t index = 0; index != count; index++) { win16_write_u8( win16_far_add_offset(destination, index), win16_read_u8(win16_far_add_offset(source, index))); } win16_write_u8(win16_far_add_offset(destination, count), 0); return destination; } /* 1008:2f2f — Turbo Pascal short string to NUL-terminated far string. */ Win16FarPtr borland_short_to_c_string( Win16FarPtr destination, Win16FarPtr short_string) { uint8_t length = win16_read_u8(short_string); for (uint16_t index = 0; index < length; index++) { win16_write_u8( win16_far_add_offset(destination, index), win16_read_u8(win16_far_add_offset(short_string, (uint16_t)(index + 1)))); } win16_write_u8(win16_far_add_offset(destination, length), 0); return destination; } /* 1008:2f4d — append source at destination's bounded NUL. */ Win16FarPtr borland_far_strcat(Win16FarPtr destination, Win16FarPtr source) { (void)borland_far_strcpy(borland_far_strend(destination), source); return destination; } static uint8_t ascii_upper(uint8_t value) { return value >= 'a' && value <= 'z' ? (uint8_t)(value - 0x20) : value; } /* * 1008:2f70 — ASCII-only case-insensitive comparison of at most maximum bytes. * Comparison length is min(maximum, strlen(second)+1), so the second string's * NUL participates when it lies inside the bound. */ int16_t borland_far_strnicmp( Win16FarPtr first, Win16FarPtr second, uint16_t maximum) { if (maximum == 0) { return 0; } FarNulScan scan = scan_nul(second, maximum); uint16_t count = (uint16_t)(maximum - scan.remaining); for (uint16_t index = 0; index < count; index++) { uint8_t first_byte = win16_read_u8(win16_far_add_offset(first, index)); uint8_t second_byte = win16_read_u8(win16_far_add_offset(second, index)); if (first_byte != second_byte) { int16_t difference = (int16_t)ascii_upper(first_byte) - ascii_upper(second_byte); if (difference != 0) { return difference; } } } return 0; } /* 1008:2fbb — forward character search including the terminating NUL. */ Win16FarPtr borland_far_strchr(Win16FarPtr string, uint8_t character) { FarNulScan scan = scan_nul(string, 0xffff); uint16_t count = (uint16_t)~scan.remaining; for (uint16_t index = 0; index < count; index++) { Win16FarPtr current = win16_far_add_offset(string, index); if (win16_read_u8(current) == character) { return current; } } return 0; } /* 1008:2fe3 — reverse character search including the terminating NUL. */ Win16FarPtr borland_far_strrchr(Win16FarPtr string, uint8_t character) { FarNulScan scan = scan_nul(string, 0xffff); uint16_t count = (uint16_t)~scan.remaining; Win16FarPtr current = win16_far_add_offset(scan.after, 0xffff); for (uint16_t remaining = count; remaining != 0; remaining--) { if (win16_read_u8(current) == character) { return current; } current = win16_far_add_offset(current, 0xffff); } return 0; } /* * 1008:300a — NUL-terminated far string to Turbo Pascal short string. * CL alone becomes the length byte, but REP MOVSB still uses the full 16-bit * length; this preserves the original overflow behavior for strings >255. */ void borland_c_to_short_string(Win16FarPtr destination, Win16FarPtr source) { FarNulScan scan = scan_nul(source, 0xffff); uint16_t length = (uint16_t)(~scan.remaining - 1); win16_write_u8(destination, (uint8_t)length); for (uint16_t index = 0; index < length; index++) { win16_write_u8( win16_far_add_offset(destination, (uint16_t)(index + 1)), win16_read_u8(win16_far_add_offset(source, index))); } } /* 1008:302c — allocate and copy one nonempty far C string. */ Win16FarPtr borland_far_strdup(Win16FarPtr source) { if (source == 0 || win16_read_u8(source) == 0) { return 0; } uint16_t bytes = (uint16_t)(borland_far_strlen(source) + 1); Win16FarPtr copy = borland_get_mem(bytes); return copy == 0 ? 0 : borland_far_memmove(copy, source, bytes); } /* 1008:3099 — release one allocated far C string including its NUL. */ void borland_far_strdispose(Win16FarPtr string) { if (string != 0) { borland_free_mem(string, (uint16_t)(borland_far_strlen(string) + 1)); } }