Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

68000 Assembly – String Equality Check & Concatenation via Stack

I was given the following assignment:

  1. Verify if two strings A and B are equal, character by character.
  2. Create a third string C that is the concatenation of A and B.
  3. All logic must live in one or more subroutines with parameters passed via the stack.

I realized that points (2) and (3) were topics I’d already covered in previous posts, but then I paused my 68000 assembly studies… and I’m back today! :-)

My current attempt is based on the great suggestions from Sep Roland and Erik Eidt in my earlier question 68000 Assembly – Passing Parameters via Stack for String Concatenation (Mar 4).

My implementation:

          ORG    $8000

;— Data section —
StringA   DC.B   'Hello',0       ; source A
StringB   DC.B   'World',0       ; source B
StringC   DS.B   256             ; destination buffer
MatchRes  DS.W   1               ; result: 0 = equal, 1 = not equal

;— Entry point —
START:
    ; Concatenate A + B → C
    PEA.L   StringC
    PEA.L   StringB
    PEA.L   StringA
    BSR.S   ConcatStrings
    ADDA.L  #12,A7

    ; Compare A vs. B → D0 (0=equal,1≠)
    PEA.L   StringB
    PEA.L   StringA
    BSR.S   CompareStrings
    ADDA.L  #8,A7
    MOVE.B  D0,MatchRes

    SIMHALT

;— ConcatStrings(&A,&B,&C) —
ConcatStrings:
    MOVEA.L 4(A7),A0        ; A0 = &StringA
    MOVEA.L 12(A7),A1       ; A1 = &StringC
CopyA:
    MOVE.B  (A0)+,(A1)+     ; copy byte from A to C
    BNE.S   CopyA
    SUBQ.L  #1,A1           ; back up over null
    MOVEA.L 8(A7),A0        ; A0 = &StringB
CopyB:
    MOVE.B  (A0)+,(A1)+     ; copy byte from B to C
    BNE.S   CopyB
    RTS

;— CompareStrings(&A,&B) → D0 (0=equal,1≠) —
CompareStrings:
    MOVEA.L 4(A7),A0        ; A0 = &StringA
    MOVEA.L 8(A7),A1        ; A1 = &StringB
CmpLp:
    MOVE.B  (A0)+,D0        ; next char A→D0
    MOVE.B  (A1)+,D1        ; next char B→D1
    CMP.B   D1,D0           ; compare
    BNE.S   NotEq
    TST.B   D0              ; null terminator?
    BNE.S   CmpLp
    CLR.L   D0              ; equal
    RTS
NotEq:
    MOVEQ   #1,D0           ; not equal
    RTS

          END     START

Questions

  1. Can this code be shortened or further optimized?
  2. Have I correctly interpreted the assignment requirements (i.e. is the logic for both compare and concatenate routines sound)?
  3. Are there any additional checks or features (edge-case handling, error flags, etc.) I could add to make this more robust?

Thanks in advance for any tips!

Edit

          ORG    $8000

; --- data --------------------------------------------------------
StringA   DC.B  'Hello',0          ; source string A
StringB   DC.B  'World',0          ; source string B
StringC   DS.B  256                ; destination buffer (caller must ensure size)
MatchRes  DS.W  1                  ; result: 0 = equal, 1 = not equal

; --- main --------------------------------------------------------
START:
    PEA.L  StringC                 ; push &C
    PEA.L  StringB                 ; push &B
    PEA.L  StringA                 ; push &A

    BSR.S  CompareStrings          ; compare A vs. B
    MOVE.W D0,MatchRes             ; store result (word)

    BSR.S  ConcatStrings           ; concatenate A + B → C
    ADDA.L #12,A7                  ; pop A, B, C

    SIMHALT                        ; halt simulator

; --- CompareStrings(&A,&B) → D0 (0=equal,1=not equal) -------------
; D0 = 1 by default (not equal), set to 0 only if all chars match
CompareStrings:
    MOVEQ   #1,D0                  ; default: not equal
    MOVEA.L 4(A7),A0               ; A0 = address of A
    MOVEA.L 8(A7),A1               ; A1 = address of B
CmpLoop:
    MOVE.B  (A0)+,D1               ; load next char from A
    CMP.B   (A1)+,D1               ; compare with next char from B
    BNE.S   EndCompare             ; mismatch → leave D0 = 1
    TST.B   D1                     ; test for null terminator
    BNE.S   CmpLoop                ; if not null, continue
    CLR.W   D0                     ; all matched → equal (D0 = 0)
EndCompare:
    RTS                            ; return

; --- ConcatStrings(&A,&B,&C) ------------------------------------
ConcatStrings:
    MOVEA.L 4(A7),A0               ; A0 = address of A
    MOVEA.L 12(A7),A1              ; A1 = address of C
CopyA:
    MOVE.B  (A0)+,(A1)+            ; copy byte from A to C
    BNE.S   CopyA                  ; repeat until null
    SUBQ.L  #1,A1                  ; back up over null terminator
    MOVEA.L 8(A7),A0               ; A0 = address of B
CopyB:
    MOVE.B  (A0)+,(A1)+            ; copy byte from B to C
    BNE.S   CopyB                  ; repeat until null
    RTS                            ; return

          END   START
like image 730
Pato Avatar asked Feb 26 '26 04:02

Pato


1 Answers

  1. Can this code be shortened or further optimized
  • The verification code does not need to load D1 from the second string and then compare. You can compare from memory directly.
  • Shouldn't you copy a word to MatchRes in the end? For the sake of robustness, don't trust DS.W to have stored a high zero.
  • Better replace CLR.L D0 (6 clocks) by CLR.W D0 (4 clocks). You only need the .W to store in the word-sized MatchRes. Anyway, because of my ; Default to 'not equal' the whole 32 bits of D0 will be zero.
  • You can re-use the parameters on the stack. This shaves off two PEA and one ADDA.
START:
    PEA.L   StringC
    PEA.L   StringB
    PEA.L   StringA
    BSR.S   CompareStrings    ; -> D0 (D1 A0 A1)
    MOVE.W  D0,MatchRes
    BSR.S   ConcatStrings     ; -> (A0 A1)
    ADDA.L  #12,A7

    SIMHALT

    ...

CompareStrings:
    MOVEQ   #1,D0           ; Default to 'not equal'
    MOVEA.L 4(A7),A0        ; A0 = &StringA
    MOVEA.L 8(A7),A1        ; A1 = &StringB
CmpLp:
    MOVE.B  (A0)+,D1        ; next char A→D1
    CMP.B   (A1)+,D1        ; compare to next char B
    BNE.S   NotEq
    TST.B   D1              ; null terminator?
    BNE.S   CmpLp
    CLR.W   D0              ; equal
NotEq:
    RTS                     ; One RTS was shaved off!
  1. Have I correctly interpreted the assignment requirements

If an assignment tells you to 1. Verify ... and 2. Create ..., then I would definitely do it in that order. You currently are doing it in the opposite order.

  1. Are there any additional checks or features

Supplying a large-enough buffer for the concatenation result is by far the easiest way to avoid buffer overflow.
And don't just test the program with 2 strings that have the same length. Try out all combinations of shorter, longer, same length, and empty (one or both). Then you'll know the program is correct.


[EDIT]

  1. All logic must live in one or more subroutines with parameters passed via the stack.

The assignment explicitly allows you to solve the task using a single subroutine, and Peter seems to advocate it. Therefore, below is my version of that approach albeit still character by character. This will be a bit faster for when the strings A and B are equal to each other or at least share some of the same characters from the start:

          ORG   $8000
; --- data --------------------------------------------------------
StringA   DC.B  'I say Hello',0    ; source string A
StringB   DC.B  'I say World',0    ; source string B
StringC   DS.B  256                ; destination buffer (caller must ensure size)
MatchRes  DS.W  1                  ; result: 0 = equal, 1 = not equal

; --- main --------------------------------------------------------
START:
    PEA.L  StringC                 ; push &C
    PEA.L  StringB                 ; push &B
    PEA.L  StringA                 ; push &A
    BSR.S  CompareAndConcatStrings ; compare A vs. B, and also concatenate A + B → C
    MOVE.W D0, MatchRes            ; store result (word)
    ADDA.L #12, A7                 ; pop A, B, C

    SIMHALT                        ; halt simulator

; IN (stack) OUT (D0) MOD (D1,A0,A1,A2)
; D0 = 1 by default (not equal), set to 0 only if all chars match
CompareAndConcatStrings:
    MOVEQ   #1, D0                 ; default: not equal
    MOVEA.L 4(A7), A0              ; A0 = address of A
    MOVEA.L 8(A7), A1              ; A1 = address of B
    MOVEA.L 12(A7), A2             ; A2 = address of C
CmpLoop:
    MOVE.B  (A0)+, D1              ; load next char from A
    MOVE.B  D1, (A2)+              ; early start on the concatenation
    CMP.B   (A1)+, D1              ; compare with next char from B
    BNE.S   EndCompare             ; mismatch → leave D0 = 1
    TST.B   D1                     ; test for null terminator
    BNE.S   CmpLoop                ; if not null, continue
    CLR.W   D0                     ; all matched → equal (D0 = 0)
    ;;BRA.S   SkipNull    (omitted; I prefer 2 bytes less)
EndCompare:
    TST.B   D1
    BEQ.S   SkipNull
CopyRemainderA:
    MOVE.B  (A0)+, (A2)+           ; copy byte from A to C
    BNE.S   CopyRemainderA         ; repeat until null
SkipNull:
    SUBQ.L  #1, A2                 ; back up over null terminator
    MOVEA.L 8(A7), A1              ; A0 = address of B
CopyB:
    MOVE.B  (A1)+, (A2)+           ; copy byte from B to C
    BNE.S   CopyB                  ; repeat until null
    RTS                            ; return

          END   START

[Edit 2]

As suggested in a comment, we could use CMPM but it will not run faster and it will require some more bytes:

    ...

CmpLoop:
    MOVE.B  (A0), (A2)+            ; early start on the concatenation
    CMPM.B  (A0)+, (A1)+           ; compare char from A with char from B
    BNE.S   EndCompare             ; mismatch → leave D0 = 1
    TST.B   -1(A0)                 ; test for null terminator
    BNE.S   CmpLoop                ; if not null, continue
    CLR.W   D0                     ; all matched → equal (D0 = 0)
    ;;BRA.S   SkipNull    (omitted; I prefer 2 bytes less)
EndCompare:
    TST.B   -1(A0)
    BEQ.S   SkipNull
CopyRemainderA:
    MOVE.B  (A0)+, (A2)+           ; copy byte from A to C
    BNE.S   CopyRemainderA         ; repeat until null
SkipNull:
    SUBQ.L  #1, A2                 ; back up over null terminator
    MOVEA.L 8(A7), A1              ; A0 = address of B
CopyB:
    MOVE.B  (A1)+, (A2)+           ; copy byte from B to C
    BNE.S   CopyB                  ; repeat until null
    RTS                            ; return

          END   START
like image 137
Sep Roland Avatar answered Mar 01 '26 21:03

Sep Roland