Skip to content

Odious Comparisons – RCA 1802 vs IBM 370

April 20, 2018

Now that I have a C compiler on the emulated /370 I of course had to benchmark it. I was faintly worried that even though the Raspberry Pi host is fast that the emulated /370 would be too slow to be fun. That turns out not to be the case at all. I ran the Dhrystone benchmark which I’ve used for the 1802/1806. The emulated 370 ran 100,000 passes in 28 seconds for a score of 3570/Dhrystones per second – nominally about two MIPS. The best I’ve gotten out of an 1802/1806 is a bit over 200 Dhrystones/sec at 12MHz. (A Z80 at 4MHz scores about 300 by the way)

I was curious about the underpinnings so i looked at the generated code for the whole of the Dhrystone suite and a couple of the procedures in it.

    1. For the whole 540 lines of C the 370 compiler generates 1360 lines of assembler vs about 3500 for the 1802. Not all of either of those counts is executed in a benchmark pass – there’s a lot of printing and labels and data definitions.
    2. The first three functions in the C code(PROC_6, PROC_7, and PROC_8) total about 70 lines of C and generate 205 lines of 370 assembly and 920 for the 1802.
    3. Because the 1802’s instruction set is so simple and regular I know it is executing right around 3600 instructions for each pass.  For the 370 I am reduced to saying that if 1757 Dhrystones/sec is one MIP then each pass is about 570 instructions.
    4. A corollary of 3. above is that the 17:1 speed advantage of the emulated 370 comes from about a 7:1 advantage in instruction power and a 2.5:1 advantage in execution rate.
    5. As a parting shot I compiled the Dhrystone benchmark native on the Raspery Pi Zero.  The smallest number of passes that had a detectable pause was 10,000,000 and i had to run 100,000,000 to actually clock it at about 770 THOUSAND Dhrystones/sec or a nominal 215 MIPS. I had seen big numbers for the Pi but i didn’t really believe them until this.
    6. The 370 load module was about 17,000 bytes, the 1802 assembly about 14,000 and the Raspberry Pi just a bit smaller at 13,500 bytes.
    7. On the Pi, because it was easy I re-ran the compile with the optimizer turned on.  This cut the time for 100,000,000 passes down to about 4 seconds(vs 13 seconds unoptimized) but you really cant use that figure for MIPS. The -O3 sped the 370 version up but not nearly as much (100,000 passes in 20 seconds vs 28) and of course the 1802 compiler was already trying as hard as it could.

 

Below is the first procedure in the benchmark(PROC_6) rendered as 31 lines of C, 80 lines of 370 assembly, and 290 lines of 1802 code. In defense of the 1802 and my compiler I’ve also included the actual LCC1802 compiler output which uses a lot of macros to make the code manageable.

Probably the most striking thing about all this(well, beside the speed of the Pi) is that it’s possible at all.  The same C code compiles and runs on wildly different architectures.

 

Proc_6 (Enum_Val_Par, Enum_Ref_Par)
/*********************************/
    /* executed once */
    /* Enum_Val_Par == Ident_3, Enum_Ref_Par becomes Ident_2 */

Enumeration  Enum_Val_Par;
Enumeration *Enum_Ref_Par;
{
  *Enum_Ref_Par = Enum_Val_Par;
  if (! Func_3 (Enum_Val_Par))
    /* then, not executed */
    *Enum_Ref_Par = Ident_4;
  switch (Enum_Val_Par)
  {
    case Ident_1:
      *Enum_Ref_Par = Ident_1;
      break;
    case Ident_2:
      if (Int_Glob > 100)
        /* then */
      *Enum_Ref_Par = Ident_1;
      else *Enum_Ref_Par = Ident_4;
      break;
    case Ident_3: /* executed */
      *Enum_Ref_Par = Ident_2;
      break;
    case Ident_4: break;
    case Ident_5:
      *Enum_Ref_Par = Ident_3;
      break;
  } /* switch */
} /* Proc_6 */
* X-func Proc_6 prologue
PROC@6   PDPPRLG CINDEX=0,FRAME=96,BASER=12,ENTRY=YES
         B     @@FEN0
         LTORG
@@FEN0   EQU   *
         DROP  12
         BALR  12,0
         USING *,12
@@PG0    EQU   *
         LR    11,1
         L     10,=A(@@PGT0)
* Function Proc_6 code
         L     2,4(11)
         MVC   0(4,2),0(11)
         MVC   88(4,13),0(11)
         LA    1,88(,13)
         L     15,=V(FUNC@3)
         BALR  14,15
         LR    2,15
         LTR   2,2
         BNE   @@L2
         L     2,4(11)
         MVC   0(4,2),=F'3'
@@L2     EQU   *
         L     2,0(11)
         LA    3,4(0,0)
         CLR   2,3
         BH    @@L3
         L     3,=A(@@L11)
         L     2,0(11)
         MH    2,=H'4'
         L     2,0(2,3)
         BR    2
         DS    0F
         DS    0F
         DS    0F
         LTORG
         DS    0F
@@L11    EQU   *
         DC    A(@@L4)
         DC    A(@@L5)
         DC    A(@@L8)
         DC    A(@@L3)
         DC    A(@@L10)
@@L4     EQU   *
         L     2,4(11)
         MVC   0(4,2),=F'0'
         B     @@L3
@@L5     EQU   *
         L     2,=A(INT@GLOB)
         L     2,0(2)
         LA    3,100(0,0)
         CR    2,3
         BNH   @@L6
         L     2,4(11)
         MVC   0(4,2),=F'0'
         B     @@L3
@@L6     EQU   *
         L     2,4(11)
         MVC   0(4,2),=F'3'
         B     @@L3
@@L8     EQU   *
         L     2,4(11)
         MVC   0(4,2),=F'1'
         B     @@L3
@@L10    EQU   *
         L     2,4(11)
         MVC   0(4,2),=F'2'
@@L3     EQU   *
         LR    15,2
* Function Proc_6 epilogue
         PDPEPIL
* Function Proc_6 literal pool
         DS    0F
         LTORG
* Function Proc_6 page table
         DS    0F
@@PGT0   EQU   *
         DC    A(@@PG0)
         DS    0F
;$$function start$$ _Proc_6
_Proc_6:                ;framesize=10
        glo     R6
        stxd
        ghi     R6
        stxd
        glo     R7
        stxd
        ghi     R7
        stxd
        dec sp
        dec sp
        dec sp
        dec sp
        glo     SP
        adi     ((10+1))#256
        plo     MEMADDR
        ghi     SP
        adci    ((10+1))>>8; was/256
        phi     MEMADDR
        ghi     R12
        str     memAddr
        inc     memAddr
        glo     R12
        str     memAddr
        inc memaddr                             ;opt16.1
        ghi     R13
        str     MEMADDR
        glo     R13
        inc     MEMADDR
        str     MEMADDR
        dec     MEMADDR
        glo     SP
        adi     ((10+1))#256
        plo     MEMADDR
        ghi     SP
        adci    ((10+1))>>8; was/256
        phi     MEMADDR
        lda     memAddr
        phi     R7
        ldn     memAddr
        plo     R7
;{
;  *Enum_Ref_Par = Enum_Val_Par;
        glo     SP
        adi     ((12+1))#256
        plo     MEMADDR
        ghi     SP
        adci    ((12+1))>>8; was/256
        phi     MEMADDR
        lda     memAddr
        phi     R11
        ldn     memAddr
        plo     R11
        ghi     R7
        str     R11
        glo     R7
        inc     R11
        str     R11
        dec     R11
;  if (! Func_3 (Enum_Val_Par))
        glo     R7
        plo     R12
        ghi     R7
        phi     R12
        sep     RCALL
        dw      _FUNC_3
        glo     R15
        bnz    L8
        ghi     R15
        bnz    L8
;    *Enum_Ref_Par = Ident_4;
        glo     SP
        adi     ((12+1))#256
        plo     MEMADDR
        ghi     SP
        adci    ((12+1))>>8; was/256
        phi     MEMADDR
        lda     memAddr
        phi     R11
        ldn     memAddr
        plo     R11
        ldi     (3)>>8; top byte
        str     R11
        inc     R11
        ldi     (3) & 255;low byte
        str     R11
        dec     R11
L8:
;  switch (Enum_Val_Par)
        glo     R7
        plo     R6
        ghi     R7
        phi     R6
        glo     R6
        smi     (0)#256
        ghi     R6
        smbi    (0)>>8; was/256      ;that's a standard signed subtraction
        ghi     R6 ;
        xri     (0)>>8; was/256      ;sets the top bit if the signs are different
        shlc          ;the original df is now in bit 0 and df=1 if signs were different
        lsnf    ;bypass the df flip if signs were the same
        xri     01     ;invert original df if signs were different
        shrc           ;put it back in df
        LBNF    L10  ;execute
        glo     R6
        sdi     (4)#256      ;subtract d FROM immediate value
        ghi     R6
        sdbi    (4)>>8; was/256      ;that's a standard signed subtraction (of register FROM immediate)
        ghi     R6 ;
        xri     (4)>>8; was/256      ;sets the top bit if the signs are different
        shlc          ;the original df is now in bit 0 and df=1 if signs were different
        lsnf    ;bypass the df flip if signs were the same
        xri     01     ;invert original df if signs were different
        shrc           ;put it back in df
        LBNF    L10  ;execute
        glo     R6
        shl
        plo     R11
        ghi     R6
        shlc
        phi     R11
        glo     R11
        adi     ((L20))#256
        plo     MEMADDR
        ghi     R11
        adci    ((L20))>>8; was/256
        phi     MEMADDR
        lda     memAddr
        phi     R11
        ldn     memAddr
        plo     R11
        glo     R6
        stxd
        ghi     R6
        stxd
        glo     R11
        plo     R6
        ghi     R11
        phi     R6
        sep     RRET
L20:
        dw L13
        dw L14
        dw L17
        dw L11
        dw L19
;  {
L13:
;      *Enum_Ref_Par = Ident_1;
        glo     SP
        adi     ((12+1))#256
        plo     MEMADDR
        ghi     SP
        adci    ((12+1))>>8; was/256
        phi     MEMADDR
        lda     memAddr
        phi     R11
        ldn     memAddr
        plo     R11
        ldi     (0)>>8; top byte
        str     R11
        inc     R11
        ldi     (0) & 255;low byte
        str     R11
        dec     R11
;      break;
        br L11
L14:
;      if (Int_Glob > 100)
        ldi     ((_INT_GLOB))&255
        plo     MEMADDR
        ldi     ((_INT_GLOB))>>8; was/256
        phi     MEMADDR
        lda     memAddr
        phi     R11
        ldn     memAddr
        plo     R11
        glo     R11
        sdi     (100)#256      ;subtract d FROM immediate value
        ghi     R11
        sdbi    (100)>>8; was/256      ;that's a standard signed subtraction (of register FROM immediate)
        ghi     R11 ;
        xri     (100)>>8; was/256      ;sets the top bit if the signs are different
        shlc          ;the original df is now in bit 0 and df=1 if signs were different
        lsnf    ;bypass the df flip if signs were the same
        xri     01     ;invert original df if signs were different
        shrc           ;put it back in df
        bdf    L15  ;execute
;      *Enum_Ref_Par = Ident_1;
        glo     SP
        adi     ((12+1))#256
        plo     MEMADDR
        ghi     SP
        adci    ((12+1))>>8; was/256
        phi     MEMADDR
        lda     memAddr
        phi     R11
        ldn     memAddr
        plo     R11
        ldi     (0)>>8; top byte
        str     R11
        inc     R11
        ldi     (0) & 255;low byte
        str     R11
        dec     R11
        br L11
L15:
;      else *Enum_Ref_Par = Ident_4;
        glo     SP
        adi     ((12+1))#256
        plo     MEMADDR
        ghi     SP
        adci    ((12+1))>>8; was/256
        phi     MEMADDR
        lda     memAddr
        phi     R11
        ldn     memAddr
        plo     R11
        ldi     (3)>>8; top byte
        str     R11
        inc     R11
        ldi     (3) & 255;low byte
        str     R11
        dec     R11
;      break;
        br L11
L17:
;      *Enum_Ref_Par = Ident_2;
        glo     SP
        adi     ((12+1))#256
        plo     MEMADDR
        ghi     SP
        adci    ((12+1))>>8; was/256
        phi     MEMADDR
        lda     memAddr
        phi     R11
        ldn     memAddr
        plo     R11
        ldi     (1)>>8; top byte
        str     R11
        inc     R11
        ldi     (1) & 255;low byte
        str     R11
        dec     R11
;      break;
        br L11
;    case Ident_4: break;
L19:
;      *Enum_Ref_Par = Ident_3;
        glo     SP
        adi     ((12+1))#256
        plo     MEMADDR
        ghi     SP
        adci    ((12+1))>>8; was/256
        phi     MEMADDR
        lda     memAddr
        phi     R11
        ldn     memAddr
        plo     R11
        ldi     (2)>>8; top byte
        str     R11
        inc     R11
        ldi     (2) & 255;low byte
        str     R11
        dec     R11
;      break;
L10:
L11:
        ldi     0
        plo     R15
        phi     R15
;} /* Proc_6 */
L7:
        inc sp
        inc sp
        inc sp
        inc sp
        inc     sp
        lda     sp
        phi     R7
        lda     sp
        plo     R7
        lda     sp
        phi     R6
        ldn     sp
        plo     R6
        sep     RRET
;$$function end$$ _Proc_6
;$$function start$$ _Proc_6
_Proc_6:		;framesize=10
	pushr R6
	pushr R7
	reserve 4; save room for outgoing arguments
	st2 R12,'O',sp,(10+1); flag1
	st2 R13,'O',sp,(12+1); flag1
	ld2 R7,'O',sp,(10+1) ;reg:INDIRI2(addr)
;{
;  *Enum_Ref_Par = Enum_Val_Par;
	ld2 R11,'O',sp,(12+1) ;reg:INDIRP2(addr)
	st2 R7,'O',R11,0; ASGNI2(addr,reg)
;  if (! Func_3 (Enum_Val_Par))
	cpy2 R12,R7 ;LOADI2(reg)
	Ccall _Func_3; CALLI2(ar)
	jnzU2 R15,L8; NE 0
;    *Enum_Ref_Par = Ident_4;
	ld2 R11,'O',sp,(12+1) ;reg:INDIRP2(addr)
	st2I 3,'O',R11,0; ASGNI2(addr,acon)
L8:
;  switch (Enum_Val_Par)
	cpy2 R6,R7 ;LOADI2(reg)
	jcI2I R6,0,lbnf,L10  ;LT=lbnf i.e. subtract immedB from A and jump if borrow
	jnI2I R6,4,lbnf,L10; GT reverse  the subtraction
	cpy2 R11,R6
	shl2I R11,1
	ld2 R11,'O',R11,(L20) ;reg:INDIRP2(addr)
	jumpv R11; JUMPV(reg)
L20:
	dw L13
	dw L14
	dw L17
	dw L11
	dw L19
;  {
L13:
;      *Enum_Ref_Par = Ident_1;
	ld2 R11,'O',sp,(12+1) ;reg:INDIRP2(addr)
	st2I 0,'O',R11,0; ASGNI2(addr,acon)
;      break;
	lbr L11
L14:
;      if (Int_Glob > 100)
	ld2 R11,'D',(_Int_Glob),0 ;reg:INDIRI2(addr)
	jnI2I R11,100,lbdf,L15 ;LEI2 100 11 L15; LE is flipped test & subtraction
;      *Enum_Ref_Par = Ident_1;
	ld2 R11,'O',sp,(12+1) ;reg:INDIRP2(addr)
	st2I 0,'O',R11,0; ASGNI2(addr,acon)
	lbr L11
L15:
;      else *Enum_Ref_Par = Ident_4;
	ld2 R11,'O',sp,(12+1) ;reg:INDIRP2(addr)
	st2I 3,'O',R11,0; ASGNI2(addr,acon)
;      break;
	lbr L11
L17:
;      *Enum_Ref_Par = Ident_2;
	ld2 R11,'O',sp,(12+1) ;reg:INDIRP2(addr)
	st2I 1,'O',R11,0; ASGNI2(addr,acon)
;      break;
	lbr L11
;    case Ident_4: break;
L19:
;      *Enum_Ref_Par = Ident_3;
	ld2 R11,'O',sp,(12+1) ;reg:INDIRP2(addr)
	st2I 2,'O',R11,0; ASGNI2(addr,acon)
;      break;
L10:
L11:
	ld2z R15
;} /* Proc_6 */
L7:
	release 4; release room for outgoing arguments
	popr R7
	popr R6
	Cretn

;$$function end$$ _Proc_6
Advertisements
Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

%d bloggers like this: