4 ms·
My interest, save for occasional tweak in ARM boot code, is mostly historical. Grew up on Z-80 assembly but really enjoy PDP-11 code for didactic purposes. You
by varjag 9y ago
My interest, save for occasional tweak in ARM boot code, is mostly historical. Grew up on Z-80 assembly but really enjoy PDP-11 code for didactic purposes. You can easily see that the rise of optimising C compilers was helped by divergence from PDP instruction set. Not ever since C idioms map down so nicely.
strlen():
LEN: MOV #-1, R0
1⊙ INC R0
TSTB (R1)+
BNE 1⊙
or take strcpy():
COPY: MOVB (R1)+, (R0)+
BNE COPY
versus the C version:
void strcpy(char *s, char *t)
{
while (*s++ = *t++)
;
}
It translates to the optimal machine code verbatim. It does not require any smarts of the compiler, at the expense of understanding on the side of the programmer. This is made possible by orthogonality of the instruction set, which in less well thought out designs was hacked around with special instructions (like LDIR on Z80). The price for that is you have to make compiler optimize these situations.
- rafaGuardeno 9y agoholi
- vardump 9y agoThat'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.
- oh5nxo 9y ago68xx is another case that got the conditions which set flags _just_ right. IMHO, obv