COMPM% ====== Compare two values of any type stored in memory Compatibility: ============== SBASIC (ie SMSQ/E) or compiled (all systems). Usage: ====== result% = COMPM%((X), (Y), <comp%>) Where result% is either 1 => (X) > (Y) or 0 => (X) = (Y) or -1 => (X) < (Y) unless reversed (X) and (Y) are pointers to (= addresses of) the two values stored somewhere in memory that are to be compared. These values must be of the same type as no coersion (conversion) takes place with direct memory values. <comp%> = <sign><cmptype.b><vartype.b) <cmptype.b> in bits 0 to 2 in the most significant byte [msb] of the word is the Qdos code for string comparision types 0..3 (see below for details) C-strings and N-strings only accept 0 or <> 0 as comparison types, with 0 => case sensitive and <> 0 => case agnostice (more details below). Apart from the top bit, <cmptype> is only significant for string comparisions and is otherwise ignored. For C- and N-strings you can also specify a skip number. This is the number of C- or N-strings that must be skipped to reach the string we want. The figure can be 0 (default) to 15, and is supplied in bit 3 to 6 in the msb of the parameter. Finally, if bit #7 of the msb is set, the result of the comparison is reversed. <vartype.b> in the least significant byte of the parameter is a code defining the kind of parameters to be compared: Variable types (vartype%): ========================== sbyte = $00 = 0 signed byte ubyte = $02 = 2 unsigned byte sword = $04 = 4 signed 16 bit word uword = $06 = 6 unsigned 16 bit word slong = $08 = 8 signed 32 bit long word ulong = $0A = 10 unsigned 32 bit long word fltpt = $0C = 12 48 bit float qstrg = $0E = 14 Q-string (len.w + bytes) cstrg = $10 = 16 C-string (zero-terminated) nstrg = $12 = 18 Name string (1 byte length) Note: Except for types 0, 2, 16 and 18, (byte, unsigned byte and C- and N-strings) all addresses must be even! (Does not necessarily apply where the CPU >= MC68020, but for the sake of compatibility better stick with that rule.) Note: S*BASIC doesnt normally deal with unsigned integer word or longword, so, for example, the unsigned word 64302 has to be entered as -31534 (= 32768 - 64302). Bytes in S*BASIC are normally treated as unsiged. COMP% lets you treat them as signed or unsigned. A few other types are planned, such as negative types => the current pair of variables are just long word pointers to two fields of abs(type). This would be most useful for q-strings of widely variable lengths kept outside the record, but might also have other uses.. String comparison (cmptype%): ============================= Comparisons may be: Type 0 Made directly on a character by character basis Type 1 Made ignoring the case of the letters Type 2 Made using the value of any embedded numbers Type 3 Both ignoring the case of letters and using the value of embedded numbers. More detail of the order of characters etc, may be found in the various QL Concepts manuals, or in the text accompanying my CMP% keyword at Knoware.no Examples: ========= If case% = 1 (case agostic string comparison) and var% = 14 (variable type Q-string), and if adr1 -> 03,'abc' and adr2 -> 03,'ABC' then use type% = case% * 256 + var% r% = COMPM%(adr1, adr2, case% * 256 + var%) returns r% = 0 and r% = COMPM%(adr1, adr2, var%) returns r% = -1 (case% = 0: case sensitive) C-strings and N-strings can only take 0 and 1 as comparison type codes. 0 => case sensitive and 1 => case agnostic. These are not lexical comparisons as for the Qdos types above, but straight character-by- character comparisions. cmptype% example: ----------------- Variable type = $10 - c-string = 16 Comparison type = 1 - case agnostic Skip strings = 2 - we want the third string after the pointer In the formula below the result of the comparison can be straight or reversed. Straight: (a2z = 0) type% = (skip% * 8 + case% - a2z) * 256 + var% => type% = 4368 = $1110 = %0001000100010000 If the result is to be reversed (eg for sorting purposes): Reversed: (a2z = $80, ie -1 in sbyte) type% = (skip% * 8 + case% - a2z) * 256 + var% => type% = -28400 = $9110 = %1001000100010000 For non-string comparisons, to reverse the order of the comparison: eg: var% = 4: a2z = $8000 (= 32768 = -1 in sword) type% = var% - a2z => -32764 = $8004 = %1000000000000100 = signed word reverse comp Example of use: =============== Included in the zip with this toolkit is an SBASIC program called Sort_bas. It is not a complete and working program. (It works perfectly in the context where it is being used!) To make it work you need some data. The data would consist of Records, like in a database. Each record contains a number of fields, such as First_name, Surname, Address, Country, Telephone, Customer number, etc. Fields may be of different types - some text, some numeric, some of fixed length and some of variable length. Each record is stored some place in memory, it may be convenient that records are not of fixed size nor stored in consecutive locations in memory. So, to keep track of them, you need to make an index, which is updated each time a new record is created or loaded from disk. So, base is some heap space in memory that contains the index - a series of long words. Each long word points to (is the address of) the base of a record in some other heap in memory. Offset from the base of each record are the different fields. The field offsets and their respective types are the same for every record. +-------> [rec 0] | : offs 00 [field 1] (float) location index | : offs 06 [field 2] (uword) - - - - - - - - - | : offs 08 [field 3] (qstrg, variable length) base + 00 [rec 0] ---+ +--------------------> [rec 2] | : offs 00 base + 04 [rec 1] ------)--+ : offs 06 | | : offs 08 | | base + 08 [rec 2] ------+ +---> [rec 1] : ... : : To find the Q-string of record #1, you start with the index base, which is known by the program or person that created it, and go to its 1st (one past the 0th) posistion: ptr = peek_l(base + #1 * 4). ptr now contains the address of record #1. Add to that the offset to Q-strings (which is known to the creator of the database) to find the Q-string: qstr$ = peekstr$(ptr + [offs 08]) To search for stuff you need to be able to compare fields against your search criteria. The same if you need to order your records for whatever reason (making searches faster is one very good reason.) COMPM% can help with all that, provided you tell it where your records are, how much each field is offset from the base of each record, and what type of data each field contains. The sort routine, BISM, makes use of that information to perform a simple Binary Insertion Sort in Memory. It doesnt rearrange any records, but it does rearrange the pointers to those record (the index) based on the results of the comparisons. rem + ------------------------------------------------------------------------ + rem |< BISM >| rem + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + rem | Binary Insertion Sort (Memory) | rem | | rem | TAOCP 5.2.1. Modified to sort a long word memory index The indexes | rem | point to records, also in memory. Individual fields are offset from | rem | the main record address. | rem | | rem | ix is the base of the index | rem | n is the number of items in index (0..N) | rem | ofs is an array of offsets pointing to the various fields | rem | to be included in the sort | rem | typ% is an array of corresponding types for those fields | rem | In the case of strings the typ also specifies the | rem | comparison type in the msb of the lsw. | rem | | rem | Dependencies: McmpMo% and tk COMPM% | rem + ------------------------------------------------------------------------ + rem | V0.01, pjw, 2023 Jul 31 | rem | V0.02, pjw, 2023 Aug 08, changed to nstrg, COMPM% has new parameters | rem + ------------------------------------------------------------------------ + : DEFine PROCedure BISM(ix, n, ofs, typ%) LOCal j, i, sl, t FOR j = 1 TO n - 1 rem t$ = arr(j%): i% = j% - 1 t = PEEK_L(ix + j * 4): i = j - 1 REPeat sl rem IF t$ >= arr(i%): EXIT sl IF McmpMo%(t, PEEK_L(ix + i * 4)) >= 0: EXIT sl rem arr(i% + 1) = arr(i%) POKE_L ix + (i + 1) * 4, PEEK_L(ix + i * 4) rem i% = i% - 1: IF i% <= -1: EXIT sl i = i - 1: IF i <= -1: EXIT sl END REPeat sl rem arr(i% + 1) = t$ POKE_L ix + (i + 1) * 4, t END FOR j END DEFine BISM : : rem + ------------------------------------------------------------------------ + rem |< McmpMo% >| rem + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + rem | Compare multiple elements stored in memory using offsets | rem | | rem | Subroutine of BISM | rem | | rem | Compare each element/field in a record: Comparison ends when the | rem | result is either "greater" or "less" or until there are no further | rem | elements left to compare in which case the result is "equal". | rem | | rem | baseX/Y are abs location of pointers to a record | rem | ofs is an array containing offsets to fields within record | rem | typ% is an array holding the type of each pair of fields | rem | | rem | Dependency: COMPM% | rem + ------------------------------------------------------------------------ + rem | V0.01, pjw, 2023 Jul 30 | rem | V0.02, pjw, 2023 Aug 08, changed to nstrg, COMPM% has new parameters | rem + ------------------------------------------------------------------------ + : DEFine FuNction McmpMo%(baseX, baseY) LOCal i%, r% FOR i% = 0 TO DIMN(ofs) r% = COMPM%(baseX + ofs(i%), baseY + ofs(i%), typ%(i%)) IF r%: RETurn r%: rem Dont return yet if zero.. END FOR i% RETurn 0: rem Equals! END DEFine McmpMo% : : Binary Insertion Sorts are not terribly efficient on lots of unsorted data, but for a small number of records - or a large number of records that are nearly in order - it is reasonably fast. For the purpose of illustration the main point here is that it is simple. ToDo: ===== The next step will be to modify my Quicksort toolkit work with the same sort of data types as COMPM% does. That should make for a fast and versatile sorting solution. Status of This software: ======================== V0.01, pjw, 2023 Jul 28, first release Conditions and DISCLAIMER as per Knoware.no
Generated by QuickHTM, 2023 Sep 19