3 ms·
That's remarkably close to 68k str-functions. Page 133 http://www.atarimania.com/documents/Asm_Lang_Prog_68K_Family.pdf http://www.atarimania.com/documents/Asm
by vardump 9y ago
That's remarkably close to 68k str-functions.
Page 133 http://www.atarimania.com/documents/Asm_Lang_Prog_68K_Family.pdf http://www.atarimania.com/documents/Asm_Lang_Prog_68K_Family...
* STRLEN - RETURNS LENGTH OP NULL TERMINATED STRING IN D0
* A0 -> STRING
STRLEN: MOVE.L A0,-(SP) SAVE REG
CLR.L D0 INITIALIZE
STRLENI:TST.B (A0)+ NULL?
BEQ STRLENR YES, RETURN
ADDQ.L #1, D0 BUMB COUNT
BRA STRLENI LOOP
STRLENR:MOVE.L (SP)+,A0 RESTORE REG
RTS
We might also want to copy a string:
* STRCPY - COPY A NULL TERMINATED STRING
* A0 -> SOURCE STRING
* A1 -> DESTINATION STRING
STRCPY: MOVEM.L A0-A1,-(SP) SAVE REGS
STRCPY1:MOVE.B (A0)+,(A1)+ MOVE A BYTE
BNE STRCPY1 GET ANOTHER IF NOT NULL
MOVEM.L (SP)+/A0-A1 RESTORE REGS
RTS
Next, we will want to compare two strings:
* STRCMP - COMPARE TWO NULL TERMINATED STRINGS
* A0 -> STRING 1
* A1 -> STRING 2
STRCMP: MOVEM.L A0-A1,-(SP) SAVE REGS
STRCMP1:CMPM.B (A0)+,(A1)+ COMPARE BYTES
BNE STRRET RETURN IF DIFFERENT
TST.B -1(A0) HAVE WE HIT A NULL?
BNE STRCMP1 NOW MORE BYTES LEFT
STRRET: MOVEM.L (SP)+,A0-A1 RESTORE REGS
RTS
Although I guess I'd implement strlen more like this (not sure if it works, been a long time since I last wrote anything for 68k):
strlen: movem.l a0-a1,-(sp)
move.l a0, a1 ; copy a0 to a1
slenloop: tst.b (a0)+
bne slenloop
addq.l #1, a1
sub.l a0, a1
move.l a1, d0
movem.l (sp)+,a0-a1
rts
Just two instructions in the inner loop. But not sure whether address registers supported addq etc.
- varjag 9y agoWow it's close indeed, even stack manipulation looks the same. Would be MOV R0, -(SP) .... MOV (SP)+, R0 for the PDP code.
- vardump 9y agoMy faster (?, see [0]) strlen seems to compile (using MIT 68k syntax): > cat > test.asm strlen: movem.l %a0-%a1,-(%sp) movl %a0, %a1 ;# copy a0 to a1 slenloop: tst.b (%a0)+ bne.b slenloop addq.l #1, %a1 sub.l %a0, %a1 move.l %a1, %d0 movem.l (%sp)+,%a0-%a1 rts ^D > m68k-linux-gnu-as test.asm && m68k-linux-gnu-objdump -d a.out a.out: file format elf32-m68k Disassembly of section .text: 00000000 <strlen>: 0: 48e7 00c0 moveml %a0-%a1,%sp@- 4: 2248 moveal %a0,%a1 00000006 <slenloop>: 6: 4a18 tstb %a0@+ 8: 66fc bnes 6 <slenloop> a: 5289 addql #1,%a1 c: 93c8 subal %a0,%a1 e: 2009 movel %a1,%d0 10: 4cdf 0300 moveml %sp@+,%a0-%a1 14: 4e75 rts I wonder whether it actually works... I think same idea should be translatable to PDP-11 as well. [0]: It runs slower for short strings, though. Not sure where the break even point is.
- vardump 9y agoPrevious one didn't work, because I mixed up a0 and a1 pointers when subtracting. But that gave me a new idea: copy pointer to d0 and complement the difference instead of addq, saving a1 register (+stack operations) and a move-instruction: strlen: move.l %a0,-(%sp) movl %a0, %d0 ;# d0 = a0; slenloop: tst.b (%a0)+ ;# test *a0, post incr bneb slenloop ;# loop if non-zero sub.l %a0, %d0 ;# d0 = d0 - a0; not.l %d0 ;# d0 = ~d0; move.l (%sp)+,%a0 rts ;# d0 is the return value Equivalent C-code (http://cpp.sh/2oecd http://cpp.sh/2oecd): #include <stdio.h> #include <stdint.h> size_t strlen68k(char* a0) { int zero_flag; uintptr_t d0 = (uintptr_t) a0; do { // tstb (%a0)+ if (*a0 == 0) zero_flag = 1; else zero_flag = 0; a0++; // (%a0)+ (post increment) } while(!zero_flag); // bneb slenloop d0 = d0 - (uintptr_t) a0; // sub.l %a0, %d0 d0 = ~d0; // not.l %d0 return d0; } void strlen_test(char* str) { printf("str \"%s\" length is %lu\n", str, strlen68k(str)); } int main(void) { strlen_test(""); strlen_test("a"); strlen_test("abcd"); return 0; } Again, untested, but I think it probably works. I guess this is also faster for all string lengths >0 as well. This idea might not work on PDP-11 anymore, depending on how binary arithmetic is implemented.