Announcement

Collapse
No announcement yet.

Improving Choose&()

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Improving Choose&()

    Here's an idea to reduce the size of your executable if the program uses a lot of Choose&() functions where all the entries are fixed numbers.
    Choose&(34,6,8,19,63, ... )
    The trouble is that when compiled each entry, even it's a one byte number, takes up 13 bytes in the exe file, so, for example, 10 such functions with 30 entries each would take – just the data – almost 4K.

    If all the entries are within the range 0 to 223 here’s an easy way to reduce that to one byte each, or in the example, 300.

    For each of the Choose& functions paste it in the program
    Code:
    Declare Function OpenClipboard  Lib "USER32.DLL" Alias "OpenClipboard" (ByVal hWnd As Dword) As Long
    Declare Function CloseClipboard Lib "USER32.DLL" Alias "CloseClipboard" () As Long
    Declare Function EmptyClipboard Lib "USER32.DLL" Alias "EmptyClipboard" () As Long
    Declare Function SetClipboardData Lib "USER32.DLL" Alias "SetClipboardData" (ByVal dwFormat As Dword, ByVal hMem As Dword) As Dword
    Declare Function GlobalAlloc Lib "KERNEL32.DLL" Alias "GlobalAlloc" (ByVal wFlags As Dword, ByVal dwBytes As Dword) As Long
    Declare Function GlobalLock Lib "KERNEL32.DLL" Alias "GlobalLock" (ByVal hMem As Dword) As Dword
    Declare Function GlobalUnlock Lib "KERNEL32.DLL" Alias "GlobalUnlock" (ByVal hMem As Dword) As Long
    Declare Function GlobalFree Lib "KERNEL32.DLL" Alias "GlobalFree" (ByVal hMem As Dword) As Long
    %GHND    = &h42     '%GMEM_MOVEABLE Or %GMEM_ZEROINIT
    %CF_TEXT = 1
    
    %Nenries = 24
    
    Function PBMain
     Local s As String, n As Long, k As Long
     Local lpMem As Asciiz Ptr, hMem  As Dword
    
     For n = 1 To %Nenries
      k = Choose&(n, 18,20,24,30,32,36,40,44,46,50,50,50,50,46,44,44,42,40,38,36,36,34,32,30)
      s = s + Chr$(k + 32)
     Next
     s = $Dq+s+$Dq
     Print s
     hMem  = GlobalAlloc(%GHND, Len(s) + 1)     'Allocate Global mem block
     lpMem = GlobalLock(hMem)                   'Lock & get pointer
     @lpMem = s                                 'Copy text to mem block
     GlobalUnlock hMem                          'Unlock
     OpenClipboard 0                            'Associate current task with clipboard
     EmptyClipboard
     SetClipboardData %CF_TEXT, hMem            'put s in it
     CloseClipboard
     GlobalFree hMem
     Print "copied into clipboard."
     WaitKey$
    End Function
    Run the program. Then paste the resulting string into the program that wants the Choose$ statement, as in this example:
    Code:
    %Nentries = 24
    
    Global choosestring As String*(%Nentries+1)
    Global Wptr As Byte Pointer
    
    Function GetValue(ByVal n As Long) As Long
     Function = @Wptr[n - 1] - 32
    End Function
    
    Function PBMain () As Long
     Wptr = VarPtr(choosestring)
     choosestring = "248>@DHLNRRRRNLLJHFDDB@>"
     'for example
     Print GetValue(7)
     WaitKey$
    End Function
    For other ranges you can modify this idea. Even if you must use integers (two bytes) or longs (four bytes) and use an integer or word pointer is an improvement over 13 bytes.
    Politically incorrect signatures about immigration patriots are forbidden. Googling “immigration patriots” is forbidden. Thinking about Googling ... well, don’t even think about it.

  • #2
    I don't know if it makes any difference but I was compiling with PBCC 4.

    You can use a usual dynamic string instead of an asciiz string; just replace VarPtr with StrPtr and before you define the pointer set the string to Nul(%Nentries).
    Politically incorrect signatures about immigration patriots are forbidden. Googling “immigration patriots” is forbidden. Thinking about Googling ... well, don’t even think about it.

    Comment


    • #3
      For later versions of PB you can use ASMDATA

      Code:
      FUNCTION PBMAIN () AS LONG
      
      LOCAL p AS LONG PTR        'the type of data being pointed to is a LONG. Change to BYTE of you really want to use bytes
      LOCAL r AS LONG
      
      p = CODEPTR(MyData)
      
      'look up the required data:
      FOR r = 1 TO 6
          PRINT r, @p[r]
      
      NEXT
      
      WAITKEY$
      
      END FUNCTION
      
      ASMDATA MyData
       DD 00,11,22,33,44,55,66,77,88,99    'add as many as you like here. I use DD to allow 32 bit numbers but you could use DB if you really only wanted bytes
      
      END ASMDATA

      For earlier versions of PB without ASMDATA you can use data in ASM but beware when debugging as blocks of data done this way might get interspersed with breakpoints .. which is why ASMDATA was introduced later.
      Code:
      FUNCTION PBMAIN () AS LONG
      
      LOCAL p AS LONG PTR        'the type of data being pointed to is a LONG. Change to BYTE of you really want to use bytes
      LOCAL r AS LONG
      
      p = CODEPTR(MyData)
      
      'look up the required data:
      FOR r = 1 TO 6
          PRINT r, @p[r]
      
      NEXT
      
      WAITKEY$
      
      EXIT FUNCTION  'don't execute the data
      MyData:
      !dd 00,11,22,33,44,55,66,77,88,99    'add as many as you like here. I use DD to allow 32 bit numbers but you could use DB if you really only wanted bytes
      
      END FUNCTION

      Comment


      • #4
        Insert any data. Thanks, Paul. Click image for larger version

Name:	poke.png
Views:	82
Size:	773 Bytes
ID:	779528
        Code:
        FUNCTION PBMAIN () AS LONG
         DIM b(0 TO 2) AS BYTE
         LOCAL p       AS BYTE PTR
         p = CODEPTR(MyData)
         POKE$ VARPTR(b(0)),PEEK$(p,3) 'copy 3 bytes into array
         ? JOIN$(b(),BINARY),,USING$("# # #",b(0),b(1),b(2))
        EXIT FUNCTION
        MyData:
        !db 65,66,67
        END FUNCTION
        https://duckduckgo.com instead of google

        Comment


        • #5
          Paul,

          Thanks for this, it works just fine.

          Besides being much easier to set up: 1. You can point to the same data more than once, and 2. If you have a lot of these things you could have a “pointer to the pointer”, that is a dword pointer to the very first byte (or whatever) pointer and access the others by an offset.

          Politically incorrect signatures about immigration patriots are forbidden. Googling “immigration patriots” is forbidden. Thinking about Googling ... well, don’t even think about it.

          Comment


          • #6
            MACRO GetBytes to display any range of ASMData or ARRAY ASSIGN with a byte array
            Code:
            MACRO GetBytes(StartByte,Length) = PEEK$(CODEPTR(MyData)+StartByte,Length) 'asm data
            MACRO GetBytes2(StartByte,Length) = PEEK$(VARPTR(b(0))+StartByte,Length)   'byte array
            
            FUNCTION PBMAIN () AS LONG 'GetBytes.bas  3/16/19
            ' https://forum.powerbasic.com/forum/user-to-user-discussions/programming/779515-improving-choose
             ? CHR$(_
               GetBytes(0,1),$CR, _        'A
               GetBytes(0,2),$CR, _        'AB
               GetBytes(0,3),$CR, _        'ABC
               GetBytes(1,1),$CR, _        'B
               GetBytes(1,2),$CR, _        'BC
               GetBytes(2,1)),,"GetBytes"  'C
            
            
              'another way ARRAY ASSIGN
               REDIM b(0 TO 2) AS BYTE
               ARRAY ASSIGN b() = 65,66,67
              ? CHR$(_
               GetBytes2(0,1),$CR, _        'A
               GetBytes2(0,2),$CR, _        'AB
               GetBytes2(0,3),$CR, _        'ABC
               GetBytes2(1,1),$CR, _        'B
               GetBytes2(1,2),$CR, _        'BC
               GetBytes2(2,1)),,"GetBytes"  'C
            EXIT FUNCTION
            MyData:
            !db 65,66,67
            END FUNCTION
            https://duckduckgo.com instead of google

            Comment

            Working...
            X