Posted at the MASM forum so reduce the load on Adam's forum.
X
-
Fast char type evaluation
hutch at movsd dot com
The MASM Forum - SLL Modules and PB Libraries
http://www.masm32.com/board/index.php?board=69.0Tags: None
-
Compare this pure PB approach, Steve... BTW, I very much appreciate your inline asm posts.
function char_type(char$) common as long
function = peek(byte, codeptr(tblchartype) + asc(char$))
end function
asmdata tblchartype
db 6,6,6,6,6,6,6,6,6,8,0,6,6,9,6,6
db 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6
db 7,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5
db 1,1,1,1,1,1,1,1,1,1,5,5,5,5,5,5
db 5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2
db 2,2,2,2,2,2,2,2,2,2,2,5,5,5,5,5
db 5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
db 3,3,3,3,3,3,3,3,3,3,3,5,5,5,5,5
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4
end asmdata
-
Looks good Ross and it appears to bypass the normal PB function overhead. I would be interested to see how fast the following code is,
Code:function = peek(byte, codeptr(tblchartype) + asc(char$))
Code:ch1$ = left$(char$,1) ' get the left character pchar = StrPtr(ch1$) ' get its address PREFIX "!" mov eax, pchar ; load character address mov eax, [eax] ; dereference eax lea ecx, chtable ; load table address movzx edx, BYTE PTR [ecx+eax] ; zero extend table + char offset mov FUNCTION, edx ; return the character type jmp bye
hutch at movsd dot com
The MASM Forum - SLL Modules and PB Libraries
http://www.masm32.com/board/index.php?board=69.0
Comment
-
The bottleneck was the extraction of the char offset value (I used ASC() to avoid it and the local strings)
Changing your routine to this, makes it faster than what I posted.
local ch1 as dword
ch1 = asc(char$) ' get the left char value
PREFIX "!"
mov eax, ch1 ; load char offset
lea ecx, chtable ; load table address
movzx edx, BYTE PTR [ecx+eax] ; zero extend table + char offset
mov FUNCTION, edx ; return the character type
jmp bye
Comment
-
Yes, good move, LEA is a single mnemonic which will be faster than an intrinsic function. I tried with the posted function to make it easily usable in basic but to use it in production code, it would be done in a FASTPROC with no stack frame and passing the single character in the EAX register. I may have a go at that a little later.hutch at movsd dot com
The MASM Forum - SLL Modules and PB Libraries
http://www.masm32.com/board/index.php?board=69.0
Comment
-
Hi Ross,
I used your idea of an ASMDATA block which reduces the stack overhead to access the data. It is called by a MACRO just to show it works OK but the macro is only for demonstration purposes, a technique like this is something that you would load EAX from a character stream then use code like the following,
Code:! lea ecx, chtable ; load table address ! movzx eax, BYTE PTR [ecx+eax] ; zero extend table + char offset
This makes the technique a lot harder to use but in the right context it would be very fast. For normal use the SLL version is easy enough to use and is more or less standard basic in its design.
Code:' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ #compile exe "ct2.exe" #compiler PBCC ' ------------- ' return values ' ------------- ' 1 = numbers ' 2 = upper case ' 3 = lower case ' 4 = high ascii/ansi ' 5 = punctuation ' 6 = control characters < ascii 32 ' 7 = space ' 8 = tab ' 9 = carriage return ' 0 = line feed MACRO FUNCTION GetCharType(ascnum) MACROTEMP retval LOCAL retval as DWORD ' --------------------------------- ! mov eax, ascnum ; load characters number into eax ! lea ecx, chtable ; load table address ! movzx eax, BYTE PTR [ecx+eax] ; zero extend table + char offset ' --------------------------------- ! mov retval, eax ; load eax back into basic variable END MACRO = retval ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ FUNCTION PBmain as LONG StdOut format$(GetCharType(55)) StdOut format$(GetCharType(67)) StdOut format$(GetCharType(101)) StdOut format$(GetCharType(220)) StdOut format$(GetCharType(33)) StdOut format$(GetCharType(17)) StdOut format$(GetCharType(32)) StdOut format$(GetCharType(9)) StdOut format$(GetCharType(13)) StdOut format$(GetCharType(10)) waitkey$ End FUNCTION ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ ASMDATA chtable db 6,6,6,6,6,6,6,6,6,8,0,6,6,9,6,6 db 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 db 7,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 db 1,1,1,1,1,1,1,1,1,1,5,5,5,5,5,5 db 5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 db 2,2,2,2,2,2,2,2,2,2,2,5,5,5,5,5 db 5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 db 3,3,3,3,3,3,3,3,3,3,3,5,5,5,5,5 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 END ASMDATA ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Attached Fileshutch at movsd dot com
The MASM Forum - SLL Modules and PB Libraries
http://www.masm32.com/board/index.php?board=69.0
Comment
-
Steve, that is seriously quick!
On an i7-9700 with 100M iterations:
----------------------------------------------------
Your original post was ~10 secs
The PB version was ~5 secs (it avoided some string functions)
Your MACRO version above was ~0.175 secs BOOM! (avoiding calls to ASC() and minimising stack usage)
-----------------------------------------------------
A side effect of the MACRO version is that the calling argument must be a literal number or a variable
And since this thread is about building SLL routines, I tried the fastproc below.
Timings were ~0.153 so even faster and with the added flexibility of allowing the call to be GetCharType(asc("xyz")) for example.
A bit naughty/expedient to reuse the ival argument - but necessary since we have no stack.
fastProc GetCharTypeFP(byval ival as long) as long
' ---------------------------------
! mov eax, ival ; load character number into eax
! lea ecx, chtbl ; load table address into ecx
! movzx eax, BYTE PTR [ecx+eax] ; zero extend table + char offset
' ---------------------------------
! mov ival, eax ; load eax back into return value
end fastproc = ival
EDIT....
Oh no! There's a simpler and faster way...
dim gaCharType(255) as global byte at codeptr(chtbl)
j = gaCharType(99)
Comment
-
Looks good Ross, its the old rule of do less, go faster and while the technique is genuinely fast, its not joy to use so as an SLL for general consumption, a basic friendly version is probably the best choice.
For evaluating code like "Text$",
Code:tstchar$ = right$(Text$,1)
hutch at movsd dot com
The MASM Forum - SLL Modules and PB Libraries
http://www.masm32.com/board/index.php?board=69.0
Comment
-
Wee bit of a problem.
I converted the code so it could run in either compiler, works fine with the MACRO but the SLL returns the same value (16) every call.
Can you tell me what I did wrong with this or is the access to the ASM table out of whack somehow within the SLL?
I'd like to sort this out, since I'm sure I'll run into it again.
The calling code:
Code:' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ #COMPILE EXE "ct2.exe" #IF %DEF(%PB_CC32) #CONSOLE OFF #ENDIF #LINK "GetCharType.SLL" '#compiler PBCC or PBWin ' ------------- ' return values ' ------------- ' 1 = numbers ' 2 = upper case ' 3 = lower case ' 4 = high ascii/ansi ' 5 = punctuation ' 6 = control characters < ascii 32 ' 7 = space ' 8 = tab ' 9 = carriage return ' 0 = line feed ' MACRO FUNCTION GetCharType(ascnum) ' MACROTEMP retval ' LOCAL retval as dword ' ' --------------------------------- ' ! mov eax, ascnum ; load characters number into eax ' ! lea ecx, chtable ; load table address ' ! movzx eax, BYTE PTR [ecx+eax] ; zero extend table + char offset ' ' --------------------------------- ' ! mov retval, eax ; load eax back into basic variable ' END MACRO = retval ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ FUNCTION PBMAIN AS LONG LOCAL twin AS LONG TXT.WINDOW ("GetCharType", 10, 10, 40, 50) TO twin TXT.PRINT GetCharType(55) TXT.PRINT GetCharType(67) TXT.PRINT GetCharType(101) TXT.PRINT GetCharType(220) TXT.PRINT GetCharType(33) TXT.PRINT GetCharType(17) TXT.PRINT GetCharType(32) TXT.PRINT GetCharType(9) TXT.PRINT GetCharType(13) TXT.PRINT GetCharType(10) TXT.WAITKEY$ TXT.END END FUNCTION ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ ASMDATA chtable DB 6,6,6,6,6,6,6,6,6,8,0,6,6,9,6,6 DB 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 DB 7,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 DB 1,1,1,1,1,1,1,1,1,1,5,5,5,5,5,5 DB 5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 DB 2,2,2,2,2,2,2,2,2,2,2,5,5,5,5,5 DB 5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 DB 3,3,3,3,3,3,3,3,3,3,3,5,5,5,5,5 DB 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 DB 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 DB 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 DB 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 DB 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 DB 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 DB 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 DB 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 END ASMDATA ' ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤ ' FUNCTION PBMAIN AS LONG ' ' StdOut FORMAT$(GetCharType(55)) ' StdOut FORMAT$(GetCharType(67)) ' StdOut FORMAT$(GetCharType(101)) ' StdOut FORMAT$(GetCharType(220)) ' StdOut FORMAT$(GetCharType(33)) ' StdOut FORMAT$(GetCharType(17)) ' StdOut FORMAT$(GetCharType(32)) ' StdOut FORMAT$(GetCharType(9)) ' StdOut FORMAT$(GetCharType(13)) ' StdOut FORMAT$(GetCharType(10)) ' ' WAITKEY$ ' 'END FUNCTION
Code:#COMPILE SLL "GetCharType" #DIM ALL FUNCTION GetCharType(chtable AS DWORD) COMMON AS DWORD LOCAL retval, ascnum AS DWORD ' --------------------------------- ! mov eax, ascnum ; load characters number into eax ! lea ecx, chtable ; load table address ! movzx eax, BYTE PTR [ecx+eax] ; zero extend table + char offset ' --------------------------------- ! mov retval, eax ; load eax back into basic variable FUNCTION=retval END FUNCTION
Rod
In some future era, dark matter and dark energy will only be found in Astronomy's Dark Ages.
Comment
-
Code:TXT.PRINT GetCharType(55)
Code:FUNCTION GetCharType(chtable AS DWORD) COMMON AS DWORD LOCAL retval, ascnum AS DWORD ' --------------------------------- ! mov eax, ascnum
Code:! lea ecx, chtable ; load table address
FInally, the whole concept is flawed. If you put the ASMDATA in your main executable, you have to pass its address to each call to the SLL function and having to include the data in the main application defeats the purpose of making it an SLL.
If you put the ASMDATA in the SLL, what is its address when the SLL is linked?
Last edited by Stuart McLachlan; 22 Feb 2022, 12:07 AM.
Comment
-
It works that way with the MACRO, that's where the code for the SLL came from.
I was testing the process of putting ASM into SLLs, I have no real knowledge of what's going on with ASM
Putting it in an SLL was mentioned by the other guys so I thought I'd try.
FASTPROC's cannot include the COMMON option so they're not handy as stand alone in an SLL
With the ASMDATA in the SLL I get compiler error 466 This name is already in use (chtable) so there is no address that I know of.
I will try a differ way, but not tonight.Rod
In some future era, dark matter and dark energy will only be found in Astronomy's Dark Ages.
Comment
-
Originally posted by Rodney Hicks View PostIt works that way with the MACRO, that's where the code for the SLL came from.
I was testing the process of putting ASM into SLLs
If you're not adamant about using ASMData, a simple way would be:
'Code:#COMPILE SLL "GetCharType.SLL" FUNCTION GetCharType(ascnum AS DWORD) COMMON AS DWORD LOCAL s AS STRING * 256 s = CHR$(6,6,6,6,6,6,6,6,6,8,0,6,6,9,6,6, _ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, _ 7,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, _ 1,1,1,1,1,1,1,1,1,1,5,5,5,5,5,5, _ 5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, _ 2,2,2,2,2,2,2,2,2,2,2,5,5,5,5,5, _ 5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, _ 3,3,3,3,3,3,3,3,3,3,3,5,5,5,5,5, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4) FUNCTION = ASC(s,ascnum) END FUNCTION '
Comment
-
Originally posted by Eric Pearson View PostI don't have time to play with it, but it seems like ON GOTO would be very fast.
But in the same vein, this seems to be pretty efficient
'Code:#COMPILE SLL "GetCharType.SLL" FUNCTION GetCharType(ascnum AS DWORD) COMMON AS DWORD FUNCTION = CHOOSE&(ascnum,6,6,6,6,6,6,6,6,8,0,6,6,9,6,6, _ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, _ 7,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, _ 1,1,1,1,1,1,1,1,1,1,5,5,5,5,5,5, _ 5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, _ 2,2,2,2,2,2,2,2,2,2,2,5,5,5,5,5, _ 5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, _ 3,3,3,3,3,3,3,3,3,3,3,5,5,5,5,5, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, _ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4) END FUNCTION '
Comment
-
Ross certainly understood the original and presented a number of useful design modifications but some of the comments here seem to be a bit confused in their understanding of both the assembler code and how and why you use an ASMDATA block. The original SLL link referred to a fully enclosed module that worked just fine in either compiler. The later design is a much harder to use very low level design that used an ASMDATA block to hold the table and a test macro to show it works.
For anyone who actually understand how the technique works, they would first write a byte streaming example that loaded the eax register for each character then use the following code to maximise the speed of the scan.
Code:! lea esi, chtable ; load table address
Then you only need one line of assembler code for each character.
Code:! movzx eax, BYTE PTR [esi+eax] ; zero extend table + char offset
Now Rod, as you have always been a perfect gentleman, if you need some assistance, I will help you cook up a fast one.
hutch at movsd dot com
The MASM Forum - SLL Modules and PB Libraries
http://www.masm32.com/board/index.php?board=69.0
Comment
-
Now while we are at it, this form of table works just fine, no need to start paddling around in high level code. Note that in this form inside a PREFIX block it happily sits inside a function that accesses the DB data.
Code:PREFIX "!" ; -------------------------------- ; English language character table ; -------------------------------- chtable: db 6,6,6,6,6,6,6,6,6,8,0,6,6,9,6,6 db 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 db 7,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 db 1,1,1,1,1,1,1,1,1,1,5,5,5,5,5,5 db 5,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 db 2,2,2,2,2,2,2,2,2,2,2,5,5,5,5,5 db 5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 db 3,3,3,3,3,3,3,3,3,3,3,5,5,5,5,5 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 END PREFIX
hutch at movsd dot com
The MASM Forum - SLL Modules and PB Libraries
http://www.masm32.com/board/index.php?board=69.0
Comment
-
As Rod has always been a perfect gentleman, here is how to make a remote table in an SLL. Attached in the zip file is a working demo of how to use it. You don't have to listen to claptrap, just pure PowerBASIC.Attached Fileshutch at movsd dot com
The MASM Forum - SLL Modules and PB Libraries
http://www.masm32.com/board/index.php?board=69.0
Comment
-
Thanks Steve, and everyone for their input.
Tested quickly, works like it should, I will do more testing when I get back on late Wednesday, on the highway in half an hour. .
I still have some 'what if's that I want to test, and I can chew them over while driving.Rod
In some future era, dark matter and dark energy will only be found in Astronomy's Dark Ages.
Comment
Comment