I was given the following assignment:
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).
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
Thanks in advance for any tips!
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
- Can this code be shortened or further optimized
DS.W
to have stored a high zero.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.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!
- 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.
- 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]
- 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