flat assembler
Message board for the users of flat assembler.

Index > Macroinstructions > [fasm g] how do I concatenate strings

Author
Thread Post new topic Reply to topic
zhak



Joined: 12 Apr 2005
Posts: 501
Location: Belarus
zhak 20 Apr 2016, 22:33
How do I concatenate multiple strings into one?
Code:
macro cpu? type
  include 'arch/'#`type#'.inc'
end macro

cpu 8086    

doesn't seem to be working in fasm g
Post 20 Apr 2016, 22:33
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4075
Location: vpcmpistri
bitRAKE 21 Apr 2016, 03:38
I don't know if this is the intended way?
Code:
macro cpu? type*
  eval 'include "arch/',`type,'.inc"'
end macro

cpu 8086    
Yet, it seems to work. Very Happy
Post 21 Apr 2016, 03:38
View user's profile Send private message Visit poster's website Reply with quote
zhak



Joined: 12 Apr 2005
Posts: 501
Location: Belarus
zhak 21 Apr 2016, 07:05
I'm so dumb Sad I tried eval, but used it improperly. Thanks a lot!
Post 21 Apr 2016, 07:05
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 21 Apr 2016, 08:22
bitRAKE: good idea! It is a nice solution thanks to its simplicity. It is much nicer than the one I would propose:
Code:
macro cpu? type
  local path
  virtual at 0
    db 'arch/',`type,'.inc'
    load path : $ from 0
  end virtual
  include path
end macro 

cpu 8086    
The direct calculation of concatenation is also possible, though the absence of special operators for string manipulation makes it very tedious:
Code:
struc concat? first*,rest*&
        . = string first
        iterate s, rest
                local str
                str = s
                . = string . + str shl (((bsr . - 1) shr 3 + 1) shl 3)
        end iterate
end struc

macro cpu? type
  local path
  path concat 'arch/',`type,'.inc'
  include path
end macro 

cpu 8086    
When working on this example I discovered another hidden bug in the fasm g engine. Thank you! Wink


Last edited by Tomasz Grysztar on 21 Apr 2016, 08:53; edited 1 time in total
Post 21 Apr 2016, 08:22
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 21 Apr 2016, 08:47
I have uploaded the bugfix. Now that macro works correctly also in this form:
Code:
struc concat? first*,rest*&
        . = string first
        iterate s, rest
                . = string . + (s) shl (((bsr . - 1) shr 3 + 1) shl 3)
        end iterate
end struc    
Before I fixed the bug, it did not work because the iteration parameter messed up the recognition context for the subsequent "." symbol.
Post 21 Apr 2016, 08:47
View user's profile Send private message Visit poster's website Reply with quote
fabbel



Joined: 30 Oct 2012
Posts: 84
fabbel 31 Jan 2023, 07:56
Hi

Quote:

The direct calculation of concatenation is also possible,
though the absence of special operators for string manipulation makes it very tedious:


Any particular reason why not adding some string operators indeed ?

Tx
Rgds
Post 31 Jan 2023, 07:56
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 31 Jan 2023, 12:14
fabbel wrote:
Any particular reason why not adding some string operators indeed ?
It has never been a nuisance enough to get to the front of the queue of things to implement. I did have an operator for string concatenation planned - under a BAPPEND name, to complement the existing BSWAP operator. But I also knew that adding concatenation would likely invite questions about other string operations.

The reason why this never was especially pressing issue is that it tends to become hidden once you have some kind of framework established. Let me show a couple samples:
Code:
include 'inline.inc'

inlinemacro strcat? a*, b*
        return = string (a) or (b) shl (8*lengthof (a))
end inlinemacro

inlinemacro concat? head*, tail&
        local buffer
        buffer = string head
        iterate item, tail
                buffer = strcat(buffer,item)
        end iterate
        return = buffer
end inlinemacro

inlinemacro current_line
        rept 1, line:__line__
                return = concat(__file__,' [',`line,']')
        end rept
end inlinemacro

display 'Current line: ', current_line()    
Code:
include 'xcalm.inc'

calminstruction calminstruction?.err? list*&
        local buffer, item
        compute buffer, ''
    loop:
        match item=,list, list
        jno final
        compute buffer, string buffer + item shl (8*lengthof buffer)
        jump loop
    final:
        compute buffer, string buffer + list shl (8*lengthof buffer)
        asm err buffer
end calminstruction

calminstruction test
        err     'This is a',10,9,9,'multi-line message'
end calminstruction
test    
There are many interesting things happening and the lack of string operators is just a minor detail. The design of fasmg (and now CALM) has always been about providing building blocks for an emergent language, what I used to call "complex solutions with simple features" when I first articulated this concept in case of fasm 1.

To be clear: I'm not saying that specialized operators are completely unnecessary because you can emulate them with existing features. It's just that their implementation becomes less crucial because of that and I like to focus on things that open new doors. But the right hour for implementing them may still come.
Post 31 Jan 2023, 12:14
View user's profile Send private message Visit poster's website Reply with quote
Calanor



Joined: 19 Jul 2015
Posts: 45
Location: Sweden
Calanor 24 Feb 2023, 15:05
Tomasz: I've tried similar approaches as in your CALM example, which works fine - up until a point. It seems like if I end a string with a 8-bit character rather than 7-bit, things will get messed up. Example:
Code:
calminstruction append a, b
        local c
        compute c, a
        compute c, string c + b SHL (8 * lengthof c)
        publish a, c
end calminstruction    

Calling the code like this will at first seem to work:
Code:
a = "A"
b = "€"
append a, b    

However, the resulting length will be reported as 3 rather than 2. If I then perform an additional append to the result...
Code:
c = "B"
append a, c    

...I'll get a null between "€" and "B", messing up the result. The string expressed as a sequence of hexadecimal values ends up as 42008041h.

Any suggestions on how I can solve this problem and, perhaps more importantly, what's causing it?
Post 24 Feb 2023, 15:05
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20454
Location: In your JS exploiting you and your system
revolution 24 Feb 2023, 15:11
Maybe b isn't what you thought?
Code:
~ cat euro.asm 
b = "€"
dd b
~ fasm euro.asm && hd euro.bin 
flat assembler  version 1.73.08  (4028172 kilobytes memory)
1 passes, 4 bytes.
00000000  e2 82 ac 00                                       |....|
00000004    
Edit: I see that code page 1250 has Euro as 0x80. So perhaps the extra leading null is to ensure it isn't a negative number.
Post 24 Feb 2023, 15:11
View user's profile Send private message Visit poster's website Reply with quote
Calanor



Joined: 19 Jul 2015
Posts: 45
Location: Sweden
Calanor 24 Feb 2023, 15:52
revolution: Good point regarding negative values. With this in mind I think I'll find a workaround, but if anyone has some nifty solution then by all means feel free to post it! Smile
Post 24 Feb 2023, 15:52
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 24 Feb 2023, 16:19
When you use "b" as a number, it is interpreted as a positive one, and then if it has the highest bit set, it requires additional byte to be stored properly, that's why converting it back to string brings an additional zero byte.

You could get around it by detecting a specific case:
Code:
calminstruction append a, b
        local c

        check b & lengthof b shl 3 = bsr b + 1
        jno ok
        compute b, -b
      ok:

        compute c, a
        compute c, string c + b SHL (8 * lengthof c)
        publish a, c
end calminstruction    

Although, with some of the recently added CALM commands, I would actually recommend another approach:
Code:
virtual at 0
  StringBuffer:: rb 10000h
end virtual

calminstruction append a, b
        local   c
        store   StringBuffer:0, lengthof a, a
        store   StringBuffer:lengthof a, lengthof b, b
        load    c, StringBuffer:0, lengthof a + lengthof b
        publish a, c
end calminstruction    
You could also make something like a "string builder" macro, that would keep collecting appended pieces into a virtual block and only load the final value when it's ready. This would have the best performance for strings built from many pieces, as it would only have to deal with a really long string once at the end.
Post 24 Feb 2023, 16:19
View user's profile Send private message Visit poster's website Reply with quote
Calanor



Joined: 19 Jul 2015
Posts: 45
Location: Sweden
Calanor 24 Feb 2023, 16:46
Oh, you've added load and store to CALM? Excellent! Thanks for the example, it's much appreciated! Somewhat off-topic, but is there any change log somewhere? I often find it hard to tell what's changed between different versions of fasm/g.
Post 24 Feb 2023, 16:46
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 24 Feb 2023, 16:51
I prepared a StringBuilder example to showcase the current abilities of CALM:
Code:
virtual at 0
  StringBuilder:: rb 10000h
  .POSITION = 0
end virtual

calminstruction .append s
        store   StringBuilder:StringBuilder.POSITION, lengthof s, s
        compute StringBuilder.POSITION, StringBuilder.POSITION + lengthof s
end calminstruction

calminstruction .init s
        compute StringBuilder.POSITION, 0
        call    StringBuilder.append, s
end calminstruction

calminstruction .get target
        local   s
        load    s, StringBuilder:0, StringBuilder.POSITION
        publish target, s
end calminstruction


; Test:
StringBuilder.init "A"
StringBuilder.append "€"
StringBuilder.get a
db a    


Calanor wrote:
Somewhat off-topic, but is there any change log somewhere? I often find it hard to tell what's changed between different versions of fasm/g.
The only changelog is in form of my commits to repository.
Post 24 Feb 2023, 16:51
View user's profile Send private message Visit poster's website Reply with quote
Calanor



Joined: 19 Jul 2015
Posts: 45
Location: Sweden
Calanor 24 Feb 2023, 21:59
Thanks, Tomasz! I'm still encountering problems in some situations. One block of code that would use append/concat is my base64 decoding, where I append decoded text one character at the time to the resulting string. As I use bitshifting etc to decode, the character is once again treated as a possibly signed value. Creating an alternative CALM-instruction that will only store one byte from the "text" that's to be appended works, though it doesn't feel like a very good solution.
Post 24 Feb 2023, 21:59
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 26 Feb 2023, 16:00
I have finally implemented the BAPPEND operator as planned. I have no other plans for string-related additions at the moment, but this one should be quite helpful.

The problem that started this thread now has a pretty straightforward solution:
Code:
_ equ bappend

macro cpu? type
  include 'arch/' _ `type _ '.inc'
end macro

cpu 8086    
Post 26 Feb 2023, 16:00
View user's profile Send private message Visit poster's website Reply with quote
Calanor



Joined: 19 Jul 2015
Posts: 45
Location: Sweden
Calanor 27 Feb 2023, 19:42
BAPPEND seems to work very well with both explicit strings and ASCII values. Thanks again! [Edit] Oops, spoke to soon - that issue with signed values still pops up, but BAPPEND will still be useful.
Post 27 Feb 2023, 19:42
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.