LLVM Exercise I

How to make a compiler? As an exercise I have already threatened to produce at least some assembly code for the Nut CPU used in the HP-41 calculator. There will be huge compromises (it is after all just an exercise):

  1. I will copy and strip down an existing compiler backend in the LLVM project, then build some small parts up again with Nut code and new naming. This is also what the LLVM documentation recommends. I will start with Sparc as they suggest. To connect the new backend with clang all the Sparc stuff in that subproject must also be copied and renamed. This is actually not a small task, it takes like a week or so to do all of this and to get everything up building and running again…
  2. I will not care about assembler directives in the source code, I will at least for now just stay with the format inherited from the compiler backend I took as a starting point.
  3. I will not care much about about the calculator itself, just the CPU. Although I technically could produce something that could be assembled with the A41 assembler (see the SDK documentation) and run as a proper calculator function in the corresponding emulator, this is just too cumbersome at least from the very start. I do not have the hardware needed to run my own MCODE in the physical machine… I will just produce some code for a hypothetical standalone CPU running in hex mode. Actual calculator registers contain floating point numbers encoded as BCD…

So what does it take? We should probably start by defining some registers:

//===-- HP41MCODERegisterInfo.td - HP41MCODE Register defs -*- tablegen -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

//===----------------------------------------------------------------------===//
//  Declarations that describe the HP41MCODE register file
//===----------------------------------------------------------------------===//

class HP41MCODEReg<bits<16> Enc, string n> : Register {
  let HWEncoding = Enc;
  let Namespace = "HP41MCODE";
}

def A   : HP41MCODEReg< 1, "A">;
def B   : HP41MCODEReg< 2, "B">;
def C   : HP41MCODEReg< 3, "C">;

def R56 : RegisterClass<"HP41MCODE", [i32], 8, (add A, B, C )>;

For simplicity in this exercise I will only use 32 of the 56 bits in the registers, to make it easier for modern compiler infrastructure…

But what is the simplest function we can generate code for? It must be this:

void foo() {
    return;
}

For this we just need a return instruction. Some code snippets:

...

namespace HP41MCODEISD {
    enum NodeType : unsigned {
      ...
      RTN,         // A return instruction.
      ...
    };
  }

...

SDValue
HP41MCODETargetLowering::LowerReturn(SDValue Chain,
        CallingConv::ID CallConv,
        bool IsVarArg,
        const SmallVectorImpl &Outs,
        const SmallVectorImpl &OutVals,
        const SDLoc &DL, SelectionDAG &DAG) const {
  ...
  return DAG.getNode(HP41MCODEISD::RTN, DL, MVT::Other, RetOps);
}

const char *HP41MCODETargetLowering::getTargetNodeName(
        unsigned Opcode) const {
  switch ((HP41MCODEISD::NodeType)Opcode) {
  ...
  case HP41MCODEISD::RTN: return "HP41MCODEISD::RTN";
  ...
  }
  return nullptr;
}

...

def HP41MCODERTN : SDNode<"HP41MCODEISD::RTN", SDTNone,
                          [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
...

let isReturn=1, isTerminator=1, isBarrier=1 in {

def RTN : HP41MCODEInst<0x3E0, (outs), (ins), "RTN",
                        [(HP41MCODERTN)]>;

}

...

So what does that produce? A function that just returns, but in MCODE assembly… I realize this is a very small step for humanity, but it is a giant leap for someone just starting to learn about LLVM:

	.file	"hello.c"
	.text
	.globl	foo                     ! -- Begin function foo
	.type	foo,@function
foo:                                    ! @foo
! %bb.0:                                ! %entry
	RTN
.Lfunc_end0:
	.size	foo, .Lfunc_end0-foo
                                        ! -- End function
	.ident	"clang version 20.0.0git (https://github.com/llvm/llvm-project.git 1a75416092746b127fb7cfec3fcbe37ab765da58)"
	.section	".note.GNU-stack"
	.addrsig

Categories: Uncategorized | Comments Off on LLVM Exercise I

HP-41CV Microcode

The HP-41 calculators can be programmed with microcode, aka MCODE. This is how their normal instructions are implemented. The emulator even has an MCODE console where  you can step though its instructions. On the physical machine running your own MCODE requires making ROMs (or something that pretends to be a ROM…). I am not familiar with how this works with the emulator, but I have found an SDK.

Microcode is of course very low level so it could be interesting to compile e.g. C to MCODE. This is also so utterly useless that it would be insanely fun to at least make a tiny prototype or a starting point for such a compiler. Imagine creating e.g. MCODE assembly with clang/llvm:

./clang.exe --target=hp41 -S hello.c
Categories: Uncategorized | Comments Off on HP-41CV Microcode

More Synthetic Programming on the HP-41CV

If you have the PPC ROM synthesizing instructions is much easier, and you don’t have to “jailbreak” the calculator. I believe assemblers/compilers that also work with the emulator makes this even easier. I have actually not tested this, but I do think they support synthetic instructions.

Categories: Uncategorized | Comments Off on More Synthetic Programming on the HP-41CV

Synthetic Programming on the HP-41CV

One of the most fun things to do on an HP-41 is to start utilizing instructions you cannot key in but that still are actually functioning codes. Slicing bytecodes together to make these instructions is called “Synthetic Programming”, as they will have to be “synthesized” in some more or less artificial manner than using the keyboard. I have a couple of books on this, these and others are now freely available for download at HP Calculator Literature.

Categories: Uncategorized | Comments Off on Synthetic Programming on the HP-41CV

More Calculator Emulators

There is actually an online emulator for my very first programmable calculator, the TI-51 III, aka TI-55. Just start pushing buttons! Even the on/off switch works.

Categories: Uncategorized | Comments Off on More Calculator Emulators

Calculator Emulators

The TI screenshots below were downloaded from the “TI-SmartView CE-T Emulator Software”, as getting screenshots from the calculator itself does not work regardless of connection method.

The HP screenshots were taken in the “V41” virtual HP-41. Programs were also downloaded from the emulator and decompiled using “HP41UC“, an ancient 16-bit HP-41 compiler/decompiler that today must be run in DOSBox.

Categories: Uncategorized | Comments Off on Calculator Emulators

Tower of Hanoi on a HP-41CV

It was much more fun to program this on my old HP calculator (not necessarily an optimal solution, but I wrote this around 40 years ago, my very first version…):

LBL "HANOI"
"N?"
PROMPT
STO 01
"A"
ASTO 02
"B"
ASTO 03
"C"
ASTO 04
10
STO 00
XEQ "INFIX"
STOP
LBL "INFIX"
1
RCL 01
X=Y?
GTO 01
XEQ "PUSH"
RCL 02
XEQ "PUSH"
RCL 03
XEQ "PUSH"
RCL 04
XEQ "PUSH"
1
ST- 01
RCL 04
X<> 03
STO 04
XEQ "INFIX"
XEQ "POP"
STO 04
XEQ "POP"
STO 03
XEQ "POP"
STO 02
XEQ "POP"
STO 01
LBL 01
CLA
ARCL 02
>"-"
ARCL 03
AVIEW
1
RCL 01
X=Y?
RTN
XEQ "PUSH"
RCL 02
XEQ "PUSH"
RCL 03
XEQ "PUSH"
RCL 04
XEQ "PUSH"
1
ST- 01
RCL 04
X<> 02
STO 04
XEQ "INFIX"
XEQ "POP"
STO 04
XEQ "POP"
STO 03
XEQ "POP"
STO 02
XEQ "POP"
STO 01
END
LBL "PUSH"
STO IND 00
1
ST+ 00
RTN
LBL "POP"
1
ST- 00
RCL IND 00
END

I called the pegs “A”, “B” and “C” this time:

Categories: Uncategorized | Comments Off on Tower of Hanoi on a HP-41CV

Tower of Hanoi on a TI-84 Plus CE-T

The Tower of Hanoi is something I “have to” do on any calculator I encounter, but using Python feels almost like cheating, it is just too easy. Unfortunately there is little else of interest for this task on this calculator.

def hanoi(n,a,b,c):
  if n==1:
    print(a," >> ",b)
  else:
   hanoi(n-1,a,c,b)
   hanoi(1,a,b,c)
   hanoi(n-1,c,b,a)

hanoi(3,1,2,3)

To fit it on one screen we are just moving 3 disks from peg 1 to peg 2, using peg 3 as a helper:

Categories: Uncategorized | Comments Off on Tower of Hanoi on a TI-84 Plus CE-T

Quantum Teleportation Part IV

Here you can see the program running:

The numbers can of course be complex, so let us try a different input:

Categories: Uncategorized | Comments Off on Quantum Teleportation Part IV

Quantum Teleportation Part III

Believe it or not, even with their strict limitations, the fairly simplistic functions from the last post are sufficient building blocks to demonstrate a simple quantum circuit. Now we just have to declare the necessary quantum gates and initialize the three input qubits and we are ready to actually “build” the circuit.

This is the matrix definition of the quantum gates needed (see the drawing in the first post, “I” is needed to expand the circuit to more bits as all the gates have either a one or two bit input):

X=[[0,1],[1,0]]
Z=[[1,0],[0,-1]]
I=[[1,0],[0,1]]
H=[[1/sqrt(2),1/sqrt(2)],[1/sqrt(2),-1/sqrt(2)]]
CNOT=[[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]

The usual suspects (aka “Alice” and “Bob”) can now start doing their qubit dance routine. Let us give Alice a nice little qubit, then initialize the inputs with it (other inputs are zeroed):

#Alice qubit
q=[[-1/sqrt(2)],[-1/sqrt(2)]]
print("Alice qubit:",q)

#Initialize inputs
q=[q[0],[0],[0],[0],q[1],[0],[0],[0]]

Creating the circuit:

#Build circuit
U=kron(I,kron(H,I))
U=mult(kron(I,CNOT),U)
U=mult(kron(CNOT,I),U)
U=mult(kron(H,kron(I,I)),U)

Running the circuit on the three inputs:

#Apply circuit
q=mult(U,q)

Alice now measures her qubits, the result will be made available to Bob:

#Alice measurements
b0=qbit(0,M(q))
q=qbitset(0,b0,q)
b1=qbit(1,M(q))
q=qbitset(1,b1,q)
print("Alice measurements:",b0,b1)

Bob now, based on the measurements from Alice, can do some operations on his entangled qubit (using a small one qubit input circuit, finally turning his qubit into something matching the one Alice started with):

#Bob qubit
r=[[0],[0]]
for i in range(len(q)):
  if i&1==1:
    r[1][0]+=q[i][0]
  else:
    r[0][0]+=q[i][0]

#Conditionally apply X and/or Z
if b1==1:
  r=mult(X,r)
if b0==1:
  r=mult(Z,r)
print("Bob qubit:",r)
Categories: Uncategorized | Comments Off on Quantum Teleportation Part III