LLVM Exercise IV

How about calling another function:

int foo() {
    return 0xFF;
}

int bar() {
    return foo();
}

Code snippets:

...

def CC_HP41MCODE : CallingConv<[]>;

...

def SDT_HP41MCODENCXQ : SDTypeProfile<0, -1,
                                      [SDTCisVT<0, iPTR>]>;
def HP41MCODENCXQ : SDNode<"HP41MCODEISD::NCXQ",
                           SDT_HP41MCODENCXQ,
                           [SDNPHasChain, SDNPOptInGlue,
                            SDNPOutGlue, SDNPVariadic]>;

...

def calltarget : Operand;

...

let isCall=1 in {
    def NCXQ : HP41MCODEInst<0x001, (outs),
                             (ins calltarget:$addr),
                             "?NC XQ $addr",
                             [(HP41MCODENCXQ
                              tglobaladdr:$addr)]>;
}

...

NCXQ,         // A call instruction.

...

SDValue
HP41MCODETargetLowering::LowerCall(
        TargetLowering::CallLoweringInfo &CLI,
        SmallVectorImpl &InVals) const {
    ...

    CCInfo.AnalyzeCallOperands(Outs, CC_HP41MCODE);

    ...

    Chain = DAG.getNode(HP41MCODEISD::NCXQ, DL, NodeTys, Ops);

    ...

    return Chain;
}

...

The following assembly is produced:

	.file	"hello.c"
	.text
	.globl	foo                     ! -- Begin function foo
	.type	foo,@function
foo:                                    ! @foo
! %bb.0:                                ! %entry
	LDI S&X HEX: 0FF
	RTN
.Lfunc_end0:
	.size	foo, .Lfunc_end0-foo
                                        ! -- End function
	.globl	bar                     ! -- Begin function bar
	.type	bar,@function
bar:                                    ! @bar
! %bb.0:                                ! %entry
	?NC XQ foo
	RTN
.Lfunc_end1:
	.size	bar, .Lfunc_end1-bar
                                        ! -- End function
	.ident	"clang version 20.0.0git (https://github.com/llvm/llvm-project.git ea1dfd50bfdfbd75969fd7653bc71c81f2a2350f)"
	.section	".note.GNU-stack"
	.addrsig
	.addrsig_sym foo

Categories: Uncategorized | Comments Off on LLVM Exercise IV

Superdense Coding Emulation on a TI-84 Plus CE-T

This is sort of the opposite of the Quantum Teleportation we have covered earlier. Here you use quantum bits to transfer classical bits:

We have all the building blocks we need from the previous episode, so let me just show you the additional code:

#Alice bits
a1=1
a2=1
print("Alice bits:",a1,a2)

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

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

#Encode bits
if a2==1:
  U=mult(kron(X,I),U)
if a1==1:
  U=mult(kron(Z,I),U)

#Run circuit
q=mult(U,q)

#Bob applies gates
q=mult(CNOT,q)
q=mult(kron(H,I),q)

#Bob measurements
b1=qbit(0,M(q))
q=qbitset(0,b1,q)
b2=qbit(1,M(q))
q=qbitset(1,b2,q)

print("Bob measurements:",b1,b2)

So as an example we try to give Bob the bits 1 and 1. Bob does his measurements and this is the result:

Categories: Uncategorized | Comments Off on Superdense Coding Emulation on a TI-84 Plus CE-T

LLVM Exercise III

Let us return a larger constant:

int foo() {
    return 0xFF;
}

For numbers up to 12 bits we can use the LDI S&X instruction. Some code snippets:

def immSExt12 : PatLeaf<(imm),
                    [{ return isInt<12>(N->getSExtValue()); }]>;

def : Pat<(i32 immSExt12:$in), (LDI imm:$in)>;

def LDI : HP41MCODEInst<0x130, (outs RC:$r), (ins i32imm:$sx),
                        "LDI S&X HEX: $sx",
                        [(set RC:$r, (i32 immSExt12:$sx))]>;

This produces:

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

Categories: Uncategorized | Comments Off on LLVM Exercise III

LLVM Exercise II

A tiny addition to the empty function. Returning zero:

int foo() {
    return 0;
}

Remember, we will not care about returning values to the calculator registers (at not least for now). We will only generate functions callable from MCODE. So as a convention we will choose to return values in the C register.

So we will need an instruction to load 0 into the C register:

def Ceq0ALL : HP41MCODEInst<0x04E, (outs RC:$r), (ins),
                            "C=0 ALL", [(set RC:$r, 0)]>;

This replacement pattern will make sure a constant 0 is loaded with this instruction:

def : Pat<(i32 0), (Ceq0ALL)>;

A register class for just this register (the instruction is only used with the C register, i.e. implicit addressing):

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

Defining the calling convention to return values in the C register:

def RetCC_HP41MCODE : CallingConv<[
      CCAssignToReg<[C]>
    ]>;

Adding this calling convention to the LowerReturn() function mentioned earlier, to analyze returns:

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

That gives us this MCODE assembly:

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

Categories: Uncategorized | Comments Off on LLVM Exercise II

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