I can’t claim to fully understand the appeal of Lisp (though I gather its something like what I appreciate about Haskell!), but I have found some documentation on how some hardware Lisp machine worked!
Specifically the CADR machine designed by the MIT AI lab & sold by Symbolics & LMI.
Lisp & lisp machines innovated many of the techniques which are now routine in software engineering!
CADR had a (early) self-hosted compiler & split their opcodes into ALU, BYTE, JUMP, & DISPATCH sections.
An ALU opcode identifies 2 inputs, 1 output, whether to shift the output left or right, whether this is a “conditional” multicycle ALU opcode, & whether to shift based on result. The ALU itself judging by available opcodes is little more than bitwise logic & an adder.
Though as stated previously they made sure that it was easy to implement multiply/divide upon it.
There’s a single “analytics” register available.
BYTE opcodes serve to copy memory around.
BYTE opcodes specify 2 inputs, 1 output, whether to “rotate” the bitmask indicating which input is used for each word & whether to rotate the A input, length of byte, & rotation count. Allowing for operating upon arrays wether or not you treat them linkedlists.
Control opcodes manipulates registers for the Program Counter, PC Stack, & stackpointer as well as 2k worth of RAM. Has BRANCH, CALL, RETURN, & FALLTHROUGH/IMEM-WRITE opcodes.
Includes minimal pipelining.
Control opcodes also have a bit which inhibits the next instruction.
All opcodes (not just control) have an additional bit representing a function return.
Dispatch opcodes specify a constant (or A source), input, whether to tailcall, whether to enable “instruction-stream” hardware for more concise microcode, address in dispatch memory, whether to dispatch based on GC flags, bytelength & input rotation.
As for JUMP instructions…
JUMP opcodes consist of 2 inputs, new program counter, whether to instead pop the new program counter off the stack, whether to push the existing one onto the stack, whether to inhibit next instruction if jump is successful, whether to negate condition, whether to test internal flags or second input, & which flag to branch upon. For internal flags these can be <, <=, =, pagefault, pagefault or interrupt, pagefault or interrupt or sequence break, or unconditional.
CADR had virtual memory, & include in its input/output memory map several “functional registers” which altered how the machine behaved.