Some notes on the CADR "principles of operation"

    Brad Parker
    10/17/04
    brad@heeltoe.com

Notes on (my) confusion or discrepancies in MIT AI Memo 528

While writing a CADR emulator I ran into some issues and/or discrepancies in the available documentation. I've tried to list them here.

[These are my conclusions; If you read them and disagree, please send me email and set me straight. I do not have a functional hardware description (i.e. verilog) yet, so some things are assuptions based on quick scans of the schematics]

- The description of the ALU mode selection using IR7 seems inverted. IR7=0 should select the 'logic' side of the ALU. IR7=1 should select the "math" side.

- The modification of the rotation count due to "misc function 3", in 16 bit mode talks about XOR of IR4 and LC1. I think this is inverted, i.e. POS4 := NOT (IR4 XOR LC1)

- The modification of the dispatch address due to L2 map bits talks about bits 14 & 15. In fact the map bits select L2 bit 18 & 19.

- The modification of the dispatch address due to L2 map bits talks about replacing bit 0. In fact the l2 map result is or'd with the existing bit 0 (see msi 2F01)

- The PDL pointer, when used in PDL push, increments *before* the write. Conversely, the PDL pointer, when used in PDL pop, decrements *after* the read.

- The SPC stack pointer, on a push, increments *before* the write. Conversly, the SPC stack pointer, on a pop, increments *before* the write.

- Here's an interesting quote:

      		;NOTE THAT THE LC HARDWARE IGNORES THE LOW BIT
      		;AND READS IT BACK AS ZERO IN HALFWORD MODE

- It seems a page fault should be generated on a write access if the read access bit is not set, even if the write access bit is set.

- If the "length of byte" field in a dispatch is zero, the mask is zero and no bits contribute to the dispatch offset.

- The microcode uses the fact that the MD is valid (for one additional cycle) with the result of a previous memory-read even when a new memory read has been started. (so the memory read pipe must be modeled)

- The dispatch instruction is affected by the instruction pipeline. Below some of the cases are enumerated for clarity.

First, let me quote from "lmdocs: popj.2", a document which I found illuminating.

    POPJ-AFTER-NEXT in combination with JUMP and DISPATCH			[CONTRL]

    Dispatch	R	P	N	PC	SPC	NOP
    		0	0	0	jump	pop	no
    		0	0	1	jump	pop	yes
    		0	1	0	jump	push	no
    		0	1	1	jump	push	yes
    		1	0	0	popj	pop	no
    		1	0	1	popj	pop	yes
    		1	1	0	popj	pop	no
    		1	1	1	popj	pop	yes

    Jump		R	P	N	PC	SPC	NOP
    (cond		0	0	0	jump	pop	no
     satisfied)	0	0	1	jump	pop	yes
    		0	1	0	jump	push	no
    		0	1	1	jump	push	yes
    		1	0	0	popj	pop	no
    		1	0	1	popj	pop	yes
    		1	1	0	iwrite
    		1	1	1	iwrite

    Jump with condition not satisfied is similar to ALU and BYTE instructions.

    Possible mods to increase usefulness:
    A dispatch-call or jump-call in combination with popj-after-next
    should neither popj nor pushj but just jump.  If it's xct-next
    the right thing happens.  If the next instruction is inhibited, well
    that's OK.  However, if the next instruction is not inhibited,
    and it's a CALL, normally (without the previous call) would be
    like tail recursive, but here will end up with two return addresses
    on pdl, however, the top one is the address of the place called
    in the first instruction and the one under that is the original
    return so that's right too.

Dispatch example - POPJ+DISPATCH, enumerating cases

    001 LABEL	(CHECK-PAGE-READ)
    002	(POPJ-AFTER-NEXT DISPATCH TRANSPORT READ-MEMORY-DATA) ;FOLLOW ALL INVZ
    003       ((M-T) Q-TYPED-POINTER READ-MEMORY-DATA)	;RETURN C(E) IN M-T
    004
    005

    dispatch yields N=0,P=0,R=0

    - exec check-page-read jump		fetch 002/popj
    - exec popj + dispatch			fetch 003/alu
       new pc<- dispatch address
       (disable popj)
    - exec alu                              fetch new pc (dispatch address)

    dispatch yields N=0,P=0,R=1

    - exec check-page-read jump		fetch 002/popj
    - exec popj + dispatch			fetch 003/alu
       new pc <- spc[spc-ptr--]
       (disable popj)
    - exec alu                              fetch new pc (from stack)

    dispatch yields N=0,P=1,R=0

    - exec check-page-read jump		fetch 002/popj
    - exec popj + dispatch			fetch 003/alu
       spc[++spc-ptr] <- 004
       new pc <- dispatch address
       (disable popj)
    - exec alu                              fetch new pc (dispatch address)

    dispatch yields N=0,P=1,R=1

    - exec check-page-read jump		fetch 002/popj
    - exec popj + dispatch			fetch 003/alu
       ignore dispatch, do popj
       new pc <- spc[spc-ptr--]
       if pc14, advance-lc
    - exec alu                              fetch new pc

    dispatch yields N=1,P=0,R=0

    - exec check-page-read jump		fetch 002/popj
    - exec popj + dispatch			fetch 003/alu
       new pc<- dispatch address
       (disable popj)
    - (nop - pipe flush)                    fetch new pc

    dispatch yields N=1,P=0,R=1

    - exec check-page-read jump		fetch 002/popj
    - exec popj + dispatch			fetch 003/alu
       new pc <- spc[spc-ptr--]
       if pc14, advance-lc
       (disable popj)
    - (nop - pipe flush)                    fetch new pc

    dispatch yields N=1,P=1,R=0

    - exec check-page-read jump		fetch 002/popj
    - exec popj + dispatch			fetch 003/alu
       spc[++spc-ptr] <- 005
       new pc <- dispatch address
       (disable popj)
    - exec alu                              fetch new pc

Another Dispatch example - POPJ+DISPATCH with CALL, enumerating cases

    001 LABEL	(CHECK-PAGE-READ)
    002	(POPJ-AFTER-NEXT DISPATCH TRANSPORT READ-MEMORY-DATA) ;FOLLOW ALL INVZ
    003    	(CALL-XCT-NEXT FUNC)
    004
    005

    dispatch yields N=0,P=0,R=0

    - exec check-page-read jump		fetch 002/popj
    - exec popj + dispatch			fetch 003/call
       new pc<- dispatch address
       (disable popj)
    - exec call                             fetch new pc (dispatch address)
       spc[++spc-ptr] <- new pc+1
       new pc<- call address
    - exec 1st inst @ dispatch address	fetch call address
    - exec 1st inst @ call address

    [help! I'm confused here. I don't think the action above is correct.  When the call returns
    it will return to new pc + 1...]

    dispatch yields N=0,P=0,R=1

    - exec check-page-read jump		fetch 002/popj
    - exec popj + dispatch			fetch 003/call
       new pc <- spc[spc-ptr--]
       (disable popj)
    - exec call                             fetch new pc (from stack)
       spc[++spc-ptr] <- new pc+1
       new pc<- call address
    - exec 1st inst @ new pc		fetch call address
    - exec 1st inst @ call address

    [again; I don't think the action above is correct.  When the call returns it will
    return to new pc + 1...]

    dispatch yields N=0,P=1,R=0a

    - exec check-page-read jump		fetch 002/popj
    - exec popj + dispatch			fetch 003/call
       spc[++spc-ptr] <- 004
       new pc <- dispatch address
       (disable popj)
    - exec call                             fetch new pc (dispatch address)
       spc[++spc-ptr] <- new pc+1
       new pc<- call address
    - exec 1st inst @ dispatch address	fetch call address
    - exec 1st inst @ call address

    dispatch yields N=0,P=1,R=1

    - exec check-page-read jump		fetch 002/popj
    - exec popj + dispatch			fetch 003/call
       ignore dispatch, do popj
       new pc <- spc[spc-ptr--]
       if pc14, advance-lc
    - exec call                             fetch new pc (from stack)
       spc[++spc-ptr] <- new pc+1
       new pc<- call address
    - exec 1st inst @ new pc		fetch call address
    - exec 1st inst @ call address

    dispatch yields N=1,P=0,R=0

    - exec check-page-read jump		fetch 002/popj
    - exec popj + dispatch			fetch 003/call
       new pc<- dispatch address
       (disable popj)
    - (nop - pipe flush)                    fetch new pc (dispatch address)
    - exec 1st inst @ new pc		fetch new pc+1

    dispatch yields N=1,P=0,R=1

    - exec check-page-read jump		fetch 002/popj
    - exec popj + dispatch			fetch 003/call
       new pc <- spc[spc-ptr--]
       if pc14, advance-lc
       (disable popj)
    - (nop - pipe flush)                    fetch new pc (from stack)
    - exec 1st inst @ new pc		fetch new pc+1

    dispatch yields N=1,P=1,R=0

    - exec check-page-read jump		fetch 002/popj
    - exec popj + dispatch			fetch 003/call
       spc[++spc-ptr] <- 005
       new pc <- dispatch address
       (disable popj)
    - exec call                             fetch new pc (dispatch address)
       spc[++spc-ptr] <- dispatch address+1
       new pc<- call address
    - exec 1st inst @ new pc		fetch call address
    - exec 1st inst @ call address

Another Dispatch example - with CALL, enumerating cases

    001	(NOP)
    002	(DISPATCH TRANSPORT READ-MEMORY-DATA) ;FOLLOW ALL INVZ
    003    	(CALL FUNC)
    004
    005

    dispatch yields N=0,P=0,R=0

    - exec check-page-read jump		fetch 002/dispatch
    - exec dispatch				fetch 003/call
       new pc <- dispatch address
    - exec call w/n-bit			fetch new pc (dispatch address)
       spc[++spc-ptr] <- new pc+1
       new pc<- call address
    - (nop - pipe flush)			fetch call address
    - exec 1nd inst @ call address

    The call pushes the dispatch address + 1 and the pipe stalls because
    the call has the N bit set, followed by the 1st instruction at the
    call address.