motorola - 68000 Assembly - String Concatenation Subroutine - Stack Overflow

I'm writing a subroutine in Assembly for the Motorola 68000 that concatenates two strings. The sub

I'm writing a subroutine in Assembly for the Motorola 68000 that concatenates two strings. The subroutine receives two input strings, StringA ("Hello") and StringB ("World"), and stores the concatenated result in StringC ("HelloWorld").

The code compiles without errors and seems to work, but I am not sure if the output is correct or if the implementation is logically correct.

I wrote the following code:

    ORG $8000
     
StringA DC.B 'Hello',0    ; First string with null terminator
StringB DC.B 'World',0    ; Second string with null terminator
StringC DS.B 20           ; Buffer for the concatenated string (large enough?)

START: 
      lea.l StringA,a0    ; a0 -> "Hello"
      lea.l StringB,a1    ; a1 -> "World"
      lea.l StringC,a2    ; a2 -> Buffer for concatenation
      clr.b d0            

      jsr CopyA           ; Call first subroutine 
      
      SIMHALT             

CopyA: 
      move.b (a0)+,d0     ; Load character from StringA into d0
                          ; Check if it is the null terminator
      beq.s CopyB         ; If yes, start copying StringB
      move.b d0,(a2)+     ; Otherwise, copy character into StringC
      bra CopyA           
      
CopyB:
      move.b (a1)+,d0     ; Load character from StringB into d0
      move.b d0, (a2)+    ; Copy it into StringC
      bne CopyB           ; If the character is not null, continue copying
     
     rts                  ; Return from subroutine
    
     END START

Questions:

  • Is my subroutine implementation correct? I used jsr and rts, but I would like confirmation if this is the best approach in this context.
  • Can it be optimized in any way? For example, are there any redundant instructions I could remove?

I'm writing a subroutine in Assembly for the Motorola 68000 that concatenates two strings. The subroutine receives two input strings, StringA ("Hello") and StringB ("World"), and stores the concatenated result in StringC ("HelloWorld").

The code compiles without errors and seems to work, but I am not sure if the output is correct or if the implementation is logically correct.

I wrote the following code:

    ORG $8000
     
StringA DC.B 'Hello',0    ; First string with null terminator
StringB DC.B 'World',0    ; Second string with null terminator
StringC DS.B 20           ; Buffer for the concatenated string (large enough?)

START: 
      lea.l StringA,a0    ; a0 -> "Hello"
      lea.l StringB,a1    ; a1 -> "World"
      lea.l StringC,a2    ; a2 -> Buffer for concatenation
      clr.b d0            

      jsr CopyA           ; Call first subroutine 
      
      SIMHALT             

CopyA: 
      move.b (a0)+,d0     ; Load character from StringA into d0
                          ; Check if it is the null terminator
      beq.s CopyB         ; If yes, start copying StringB
      move.b d0,(a2)+     ; Otherwise, copy character into StringC
      bra CopyA           
      
CopyB:
      move.b (a1)+,d0     ; Load character from StringB into d0
      move.b d0, (a2)+    ; Copy it into StringC
      bne CopyB           ; If the character is not null, continue copying
     
     rts                  ; Return from subroutine
    
     END START

Questions:

  • Is my subroutine implementation correct? I used jsr and rts, but I would like confirmation if this is the best approach in this context.
  • Can it be optimized in any way? For example, are there any redundant instructions I could remove?
Share edited Mar 3 at 12:11 Pato asked Mar 3 at 11:05 PatoPato 814 bronze badges 1
  • You don't need the cmp.b #0,d0 as the move.b already sets the flags. – Jester Commented Mar 3 at 12:03
Add a comment  | 

2 Answers 2

Reset to default 3

The move instruction on 68k is particularly powerful.  They have given the move instruction the fewest opcode bits, reserving many bits of the 16-bit instruction word so that it can supports two <ea> operands.  In 68k, an <ea> operand (effective address) takes 6 bits, so having two of them means 12 bits, leaving only 4 bits left for other things.  Given the size selection taking 2 bits (byte/word/long), there's a whopping 2 bits left for the move opcode itself.

Since the designers squeezed two <ea> operands into this instruction, this means that move can move memory to memory while also setting the condition codes!

Compare this to add and most other 68k instruction: these only supports one <ea> operand and a data (or address) register or an immediate as the other operand.

Thus, string copying should be done using an instruction like move (a0)+, (a2)+¹.

Since this instruction sets the condition codes, you can follow it directly with a branch on condition (e.g. backwards on non-zero/non-null).

The algorithm will have to change just slightly (as illustrated in suggestions by @SepRoland), because using this move form, the null byte from the first string will be copied into the target buffer, so have to back up a2 by 1 byte before copying the 2nd string (but this time you do want the null copied to terminate string C so move (a1)+,(a2)+ is perfect and will not require any adjustment).


¹There is also the dbcc instruction, that can be used to form a two instruction loop that moves memory limited by both count and also null terminator — these will run particularly fast on 68010 as it has a mode for dbcc branching backwards by one instruction to run without instruction fetch.  (Later 68ks have a more general instruction cache so can do larger loops all without instruction fetch.)

I am not sure if the output is correct.

What does the debugger's screen show you? If somewhere it has "HelloWorld" then that would seem it had worked, wouldn't it?

StringC DS.B 20           ; Buffer for the concatenated string (large enough?)

It a simple matter of adding both lengths and allowing an extra byte for the one terminating zero. So here 11 bytes would nail it. But let's not be stingy and allocate a decent size for a general-purpose buffer. And it is very good "assembly-style" if you use numbers that are a power of two.

Is my subroutine implementation correct? I used jsr and rts, but I would like confirmation if this is the best approach in this context.

The best? Well no, because your subroutine is very near to the call site (distance from -128 to +127), you can reach it with less bytes using the bsr.s instruction. It has the relative displacement built in the instruction word, unlike jsr that has to add it after the instruction word. The rts remains just like you wrote it. This is not faster, but it is required if one desires position independent code (with no absolute memory references).

Can it be optimized in any way? For example, are there any redundant instructions I could remove?

  • The clr.b d0 before calling the subroutine seems redundant.
  • The unconditional bra CopyA should certainly be avoided. I suggest you process the 1st string similarly to how you process the 2nd string, but that you subtract 1 from A2 in between both copy operations. That will remove the zero byte simply by having it overwritten with the first char from the second string (or zero if the second string is an empty one).

    ORG $8000
     
StringA DC.B 'Hello',0    ; First string with null terminator
StringB DC.B 'World',0    ; Second string with null terminator
StringC DS.B 256          ; Buffer for the concatenated string

START: 
      lea.l  StringA, a0   ; a0 -> "Hello"
      lea.l  StringB, a1   ; a1 -> "World"
      lea.l  StringC, a2   ; a2 -> Buffer for concatenation        

      bsr.s  CopyA         ; Call first subroutine 
      
      SIMHALT             

CopyA:
      move.b (a0)+, d0     ; Load character from StringA into d0
      move.b d0, (a2)+     ; Copy it into StringC
      bne.s  CopyA         ; If the character is not null, continue copying
      subq.l #1, a2        ; Remove the null
CopyB:
      move.b (a1)+, d0     ; Load character from StringB into d0
      move.b d0, (a2)+     ; Copy it into StringC
      bne.s  CopyB         ; If the character is not null, continue copying
      rts                  ; Return from subroutine
    
END START

And this is an interesting variation:

    ORG $8000
     
StringA DC.B 'Hello',0    ; First string with null terminator
StringB DC.B 'World',0    ; Second string with null terminator
StringC DS.B 256          ; Buffer for the concatenated string

START: 
      lea.l   StringA, a0   ; a0 -> "Hello"
      lea.l   StringB, a1   ; a1 -> "World"
      lea.l   StringC, a2   ; a2 -> Buffer for concatenation        

      bsr.s   CopyA         ; Call first subroutine 
      
      SIMHALT
           
; IN (a0,a1,a2)
CopyA:
      bsr.s   Copy
      subq.l  #1, a2        ; Remove the null
      movea.l a1, a0
Copy: move.b  (a0)+, d0     ; Load character from StringB into d0
      move.b  d0, (a2)+     ; Copy it into StringC
      bne.s   Copy          ; If the character is not null, continue copying
      rts                   ; Return from subroutine
    
END START

For the 1st string, the local Copy subroutine gets called normally, and for the 2nd string the execution falls through into those Copy instructions. The first execution of rts returns locally, but the second execution of rts returns to the main program.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745097710a4611081.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信