diff --git a/codes/ControllerInputDisplay/@code.xml b/codes/ControllerInputDisplay/@code.xml
new file mode 100644
index 0000000..1937073
--- /dev/null
+++ b/codes/ControllerInputDisplay/@code.xml
@@ -0,0 +1,311 @@
+
+ controller
+ metadata
+ Controller Input Display
+ コントローラ入力表示
+ sup39(サポミク)
+ 0.1
+ Feb 05, 2023
+
+ Display controller input.
+
+
+ コントローラ入力を表示します。
+
+
+
+
+
+
diff --git a/codes/ControllerInputDisplay/b.s b/codes/ControllerInputDisplay/b.s
new file mode 100644
index 0000000..ac1d52c
--- /dev/null
+++ b/codes/ControllerInputDisplay/b.s
@@ -0,0 +1,12 @@
+.GXBegin:
+ li r4, 0
+ lis r12, GXBegin@ha
+ la r12, GXBegin@l(r12)
+
+.call:
+ mtctr r12
+ bctr
+
+.draw.epilogue:
+ mtlr rLR
+ blr
diff --git a/codes/ControllerInputDisplay/config.s b/codes/ControllerInputDisplay/config.s
new file mode 100644
index 0000000..76fbad7
--- /dev/null
+++ b/codes/ControllerInputDisplay/config.s
@@ -0,0 +1,60 @@
+.include "./macros.s"
+.section .config # this section is not included in the generated C2 code
+.conf:
+
+# align % 4 == 3
+.conf.lineWidth:
+ .byte 20
+.conf.mtx.scale:
+ .long (127-logQ)<<23 # 2**-logQ
+.conf.mtx.xy:
+ .short 16, 330-16
+
+.conf.bg.color:
+ .long 0x7f
+.conf.trigger.fill:
+ .long 0xdfdfdfbf
+.conf.trigger.stroke:
+ .long 0xeeeeeebf
+
+/** .Rect and .Ngon should be contiguous from this point */
+.conf.bg:
+ .Rect 0, 0, 182, 120
+
+.conf.button:
+ .Ngon 138, 66, 18, SHIFT_A, 0x2ee5b8bf
+ .Ngon 113, 89, 9, SHIFT_B, 0xff1a1abf
+ .Ngon 164, 50, 8, SHIFT_X, 0xeeeeeebf
+ .Ngon 119, 41, 8, SHIFT_Y, 0xeeeeeebf
+ .Ngon 144, 34, 6, SHIFT_Z, 0x9494ffbf
+ .Ngon 91, 64, 5, SHIFT_S, 0xeeeeeebf
+.conf.button.end:
+
+.set TriggerXL, 12
+.set TriggerXR, 170
+.set TriggerY0, 10
+.set TriggerY1, 18
+.set TriggerW, 64
+.set TriggerWA, 56
+.conf.trigger:
+# {Fill, Info, Stroke}
+## L
+ .Rect TriggerXL, TriggerY0, TriggerXL+TriggerW, TriggerY1
+ .TriggerInfo SHIFT_L, TriggerWA
+ .Rect TriggerXL, TriggerY0, TriggerXL+TriggerW, TriggerY1
+## R
+ .Rect TriggerXR, TriggerY0, TriggerXR-TriggerW, TriggerY1
+ .TriggerInfo SHIFT_R, -TriggerWA
+ .Rect TriggerXR, TriggerY0, TriggerXR-TriggerW, TriggerY1
+.conf.trigger.end:
+
+.conf.stick:
+# MStick Fill (*, *, r, rMove, color)
+ .Ngon -1, -1, 12, 14, 0xeeeeeeef
+# MStick Stroke (x, y, r, *, color)
+ .Ngon 32, 52, 19, -1, 0xeeeeeeef
+# CStick Fill
+ .Ngon -1, -1, 12, 14, 0xffd300ef
+# CStick Stroke
+ .Ngon 64, 92, 19, -1, 0xffd300ef
+.conf.stick.end:
diff --git a/codes/ControllerInputDisplay/drawNgon.s b/codes/ControllerInputDisplay/drawNgon.s
new file mode 100644
index 0000000..88f0a09
--- /dev/null
+++ b/codes/ControllerInputDisplay/drawNgon.s
@@ -0,0 +1,52 @@
+/**
+ * r3: GXPrimitive
+ * r5: n (MUST be 2**n or 2**n+1)
+ * rData: {x: u16, y: u16, r: u16, mask: u16, color: u32}
+ *
+ * rData += .Ngon.size
+ */
+
+.set rIdx, rFVar0
+.set rShift, rFVar1
+.set fXY, 2
+.set fR, 3
+
+drawNgon:
+ addi rIdx, r5, -1
+ mflr rLR
+
+ /**
+ * cntlzw(n) = 31-log(n)
+ * shift = 16-log(n)-jmaSinShift+2
+ * = cntlzw(n)-(31-18+jmaSinShift)
+ */
+ cntlzw rShift, r5
+ subi rShift, rShift, 31-18+jmaSinShift
+
+## GXBegin()
+ bl .GXBegin
+
+## load (x, y), (r), (color)
+ psq_l fXY, .Ngon.$x(rData), 0, gqr.q
+ psq_l fR, .Ngon.$r(rData), 1, gqr.q
+ lwz rColor, .Ngon.$color(rData)
+
+drawNgon.loop:
+ rlwnm r0, rIdx, rShift, 32-18+jmaSinShift, 31-2
+ lfsx f0, rCosTable, r0
+ lfsx f1, rSinTable, r0
+ ps_merge00 f0, f0, f1
+## (x, y) = (cos, sin) * r.ps0 + (x0, y0)
+ ps_madds0 f0, f0, fR, fXY
+ psq_st f0, 0(rGX), 0, gqr.dq
+## z = 0
+ sth rZero, 0(rGX)
+## color
+ stw rColor, 0(rGX)
+## next
+ addic. rIdx, rIdx, -1
+ bge+ drawNgon.loop
+
+drawNgon.done:
+ addi rData, rData, .Ngon.size
+ b .draw.epilogue
diff --git a/codes/ControllerInputDisplay/drawRect.s b/codes/ControllerInputDisplay/drawRect.s
new file mode 100644
index 0000000..1933f17
--- /dev/null
+++ b/codes/ControllerInputDisplay/drawRect.s
@@ -0,0 +1,38 @@
+/**
+ * r3: GXPrimitive
+ * r5: n (MUST be 4 or 5)
+ * rData: {x0: u8, y0: u8, x1: u8, y1: u8}
+ *
+ * rData += .Rect.size
+ */
+
+.set rIdx, rFVar0
+
+drawRect:
+ addi rIdx, r5, 0
+ mflr rLR
+
+## GXBegin()
+ bl .GXBegin
+
+drawRect.loop:
+## x
+ rlwinm r0, rIdx, 0, 0x2
+ psq_lx f0, rData, r0, 1, gqr.q
+ psq_st f0, 0(rGX), 1, gqr.dq
+## y
+ addic. rIdx, rIdx, -1
+ rlwinm r0, rIdx, 0, 0x2
+ ori r0, r0, 1
+ psq_lx f0, rData, r0, 1, gqr.q
+ psq_st f0, 0(rGX), 1, gqr.dq
+## z = 0
+ sth rZero, 0(rGX)
+## color
+ stw rColor, 0(rGX)
+## next
+ bgt+ drawRect.loop
+
+drawRect.done:
+ addi rData, rData, .Rect.size
+ b .draw.epilogue
diff --git a/codes/ControllerInputDisplay/info.xml b/codes/ControllerInputDisplay/info.xml
new file mode 100644
index 0000000..e556dc9
--- /dev/null
+++ b/codes/ControllerInputDisplay/info.xml
@@ -0,0 +1,15 @@
+
+ controller
+ metadata
+ Controller Input Display
+ コントローラ入力表示
+ sup39(サポミク)
+ 0.1
+ Feb 05, 2023
+
+ Display controller input.
+
+
+ コントローラ入力を表示します。
+
+
diff --git a/codes/ControllerInputDisplay/macros.s b/codes/ControllerInputDisplay/macros.s
new file mode 100644
index 0000000..3e804b7
--- /dev/null
+++ b/codes/ControllerInputDisplay/macros.s
@@ -0,0 +1,88 @@
+.set logQ, 6 # power of paired single quantized scale
+
+.set rFVar0, 31
+.set rFVar1, 30
+.set rSinTable, 29
+.set rCosTable, 28
+.set rGX, 27
+.set rZero, 26
+.set rLR, 25
+.set rConf, 24
+.set rData, 23
+.set rDataEnd, 22
+.set rPad, 21
+.set rBtn, 20
+.set rColor, 19
+.set gqr.u8, 2 # GQR2 = 0x4004
+.set gqr.u16, 3 # GQR3 = 0x5005
+.set gqr.s8, 4 # GQR4 = 0x6006
+.set gqr.s16, 5 # GQR5 = 0x7007
+
+.set gqr.q, 7
+.set gqr.dq, gqr.u16
+
+.set spAdd, 0x150
+.set spReg, 19
+.set spRegOff, 0x8
+.set spOffG, 0x50
+
+.set jmaSinShift, 4
+.set GX_LINESTRIP, 0xB0
+.set GX_TRIANGLEFAN, 0xA0
+
+.set PRESS_Z, 0x0010
+.set PRESS_R, 0x0020
+.set PRESS_L, 0x0040
+.set PRESS_A, 0x0100
+.set PRESS_B, 0x0200
+.set PRESS_X, 0x0400
+.set PRESS_Y, 0x0800
+.set PRESS_S, 0x1000
+.set SHIFT_Z, 32-4
+.set SHIFT_R, 32-5
+.set SHIFT_L, 32-6
+.set SHIFT_A, 32-8
+.set SHIFT_B, 32-9
+.set SHIFT_X, 32-10
+.set SHIFT_Y, 32-11
+.set SHIFT_S, 32-12
+
+mPadButton$mPadStatus = 0x30;
+mPadMStick$mPadStatus = 0xf0;
+mPadSStick$mPadStatus = 0x130;
+
+.macro li32 reg val
+ lis \reg, \val@h
+ ori \reg, \reg, \val@l
+.endm
+
+.macro .setDataEnd l
+ .set aData0, aData1
+ .set aData1, \l
+ addi rDataEnd, rData, aData1-aData0
+.endm
+
+.macro .Ngon x y r m color
+ .byte \x, \y, \r, \m
+ .long \color
+.endm
+.set .Ngon.size, 0x8
+.set .Ngon.nCircle, 32
+.set .Ngon.$x, 0
+.set .Ngon.$r, 2
+.set .Ngon.$m, 3
+.set .Ngon.$color, 4
+
+.macro .Rect x0 y0 x1 y1
+ .byte \x0, \y0, \x1, \y1
+.endm
+.set .Rect.size, 0x4
+
+.macro .TriggerInfo shift wa
+ .byte \shift, \wa
+.endm
+.set .TriggerInfo.size, 0x2
+.set .TriggerInfo$fill.x0, 0
+.set .TriggerInfo$fill.x1, 2
+.set .TriggerInfo$width.analog, 5
+.set .TriggerInfo$stroke.x1, .Rect.size+.TriggerInfo.size+.TriggerInfo$fill.x1
diff --git a/codes/ControllerInputDisplay/main.s b/codes/ControllerInputDisplay/main.s
new file mode 100644
index 0000000..5ca50f8
--- /dev/null
+++ b/codes/ControllerInputDisplay/main.s
@@ -0,0 +1,165 @@
+.include "./config.s"
+.align 2
+.section .text
+
+.drawAlways:
+## orig
+ blrl
+## setup
+ stwu r1, -spAdd(r1)
+ stmw spReg, spRegOff(r1)
+
+ lis r0, (0x40-logQ)<<8|4 # (-logQ, u8)
+ mtspr 912+gqr.q, r0
+
+ lis rConf, $conf@ha
+ la rConf, $conf@l(rConf)
+
+### sin, cos table
+ lwz rSinTable, jmaSinTable$r13(r13)
+ mr. rSinTable, rSinTable
+ beq- .done
+ lwz rCosTable, jmaCosTable$r13(r13)
+
+## b
+ b .b.end
+ .include "b.s"
+ .include "./drawNgon.s"
+ .include "./drawRect.s"
+.b.end:
+
+### setup GX params
+ addi r3, r1, spOffG
+ mr r4, r22 # r22: JUTRect {0, 0, video.width, video.height}
+ lis r12, J2DOrthoGraph_new_0@ha
+ la r12, J2DOrthoGraph_new_0@l(r12)
+ bl .call # return this
+### line width
+ lbz r0, .conf.lineWidth-.conf(rConf)
+ stb r0, 0x38(r3)
+### mtx
+ lwz r0, .conf.mtx.scale-.conf(rConf)
+ stw r0, 0x84+0x10*0+0x4*0(r3) # Sxx [0][0]
+ stw r0, 0x84+0x10*1+0x4*1(r3) # Syy [1][1]
+ psq_l f0, .conf.mtx.xy-.conf(rConf), 0, gqr.s16
+ ps_merge10 f1, f0, f0
+ psq_st f0, 0x84+0x10*0+0x4*3(r3), 1, gqr0 # dx [0][3]
+ psq_st f1, 0x84+0x10*1+0x4*3(r3), 1, gqr0 # dy [1][3]
+### setup2D
+ lis r12, J2DOrthoGraph_setPort@ha
+ la r12, J2DOrthoGraph_setPort@l(r12)
+ bl .call
+### GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR)
+ li r3, -1
+ li r4, 1
+ lis r12, GC2D_Hx_GxInit@ha
+ la r12, GC2D_Hx_GxInit@l(r12)
+ bl .call
+
+### var
+ li rZero, 0
+ lis rGX, 0xCC00
+ ori rGX, rGX, 0x8000
+ lis rPad, mPadStatus@ha
+ lhzu rBtn, mPadStatus@l(rPad)
+.set aPad0, 0
+
+.draw.bg:
+ .set aData0, .conf.bg
+ la rData, aData0-.conf(rConf)
+ li r3, GX_TRIANGLEFAN
+ li r5, 4
+ lwz rColor, .conf.bg.color-aData0(rData)
+ bl drawRect
+
+.draw.button:
+ .set aData1, .conf.button
+ .setDataEnd .conf.button.end
+.draw.button.loop:
+### fill
+ lbz r4, .Ngon.$m(rData) # mask
+ rlwnm. r4, rBtn, r4, 0x1
+ beq+ .draw.button.loop.stroke
+ li r3, GX_TRIANGLEFAN
+ li r5, .Ngon.nCircle
+ bl drawNgon
+ ### go back to the same Ngon
+ subi rData, rData, .Ngon.size
+.draw.button.loop.stroke:
+### stroke
+ li r3, GX_LINESTRIP
+ li r5, .Ngon.nCircle+1
+ bl drawNgon
+.draw.button.loop.next:
+ cmplw rData, rDataEnd
+ blt+ .draw.button.loop
+
+.draw.trigger:
+ .setDataEnd .conf.trigger.end
+ .set aPadU, 4
+ .set aPad1, mPadButton$mPadStatus+0x10-aPadU # [0x10]=L(float)
+ la rPad, aPad1-aPad0(rPad)
+.draw.trigger.loop:
+### fill
+ lbz r4, 4(rData) # mask
+ rlwnm. r4, rBtn, r4, 0x1
+ psq_l f0, .TriggerInfo$stroke.x1(rData), 1, gqr.u8
+ lfsu f1, 4(rPad)
+ bne- .draw.trigger.loop.fill.draw
+#### analog
+/**
+ * f0 = x0
+ * f1 = input
+ * f2 = w
+ * ans = w*input+x0 = f1*f2+f0
+ */
+ psq_l f0, .TriggerInfo$fill.x0(rData), 1, gqr.u8
+ psq_l f2, .TriggerInfo$width.analog(rData), 1, gqr.s8
+ fmadds f0, f1, f2, f0
+.draw.trigger.loop.fill.draw:
+ psq_st f0, .TriggerInfo$fill.x1(rData), 1, gqr.u8
+ li r3, GX_TRIANGLEFAN
+ li r5, 4
+ lwz rColor, .conf.trigger.fill-.conf(rConf)
+ bl drawRect
+ addi rData, rData, .TriggerInfo.size
+### stroke
+ li r3, GX_LINESTRIP
+ li r5, 5
+ lwz rColor, .conf.trigger.stroke-.conf(rConf)
+ bl drawRect
+.draw.trigger.loop.next:
+ cmplw rData, rDataEnd
+ blt+ .draw.trigger.loop
+.set aPad0, aPad1+aPadU*2
+
+.draw.stick:
+ .setDataEnd .conf.stick.end
+ .set aPadU, mPadSStick$mPadStatus-mPadMStick$mPadStatus
+ .set aPad1, mPadMStick$mPadStatus-aPadU
+ la rPad, aPad1-aPad0(rPad)
+.draw.stick.loop:
+### fill
+#### update (x, y)
+ psq_l f0, .Ngon.$x+.Ngon.size(rData), 0, gqr.u8 # (x0, y0) (from next Ngon)
+ psq_lu f1, aPadU(rPad), 0, gqr0 # input
+ ps_neg f2, f1 # -input
+ ps_merge01 f1, f1, f2 # (input.x, -input.y)
+ psq_l f2, .Ngon.$m(rData), 1, gqr.u8 # StickMove
+ ps_madds0 f0, f1, f2, f0 # input*StickMove+(x0, y0)
+ psq_st f0, .Ngon.$x(rData), 0, gqr.u8
+#### draw
+ li r3, GX_TRIANGLEFAN
+ li r5, .Ngon.nCircle
+ bl drawNgon
+### stroke
+ li r3, GX_LINESTRIP
+ li r5, 8+1
+ bl drawNgon
+.draw.stick.loop.next:
+ cmplw rData, rDataEnd
+ blt .draw.stick.loop
+
+.done:
+ lmw spReg, spRegOff(r1)
+ addi r1, r1, spAdd
diff --git a/codes/ControllerInputDisplay/make.py b/codes/ControllerInputDisplay/make.py
new file mode 100644
index 0000000..f421650
--- /dev/null
+++ b/codes/ControllerInputDisplay/make.py
@@ -0,0 +1,9 @@
+from supSMSGecko import make_xml, symbols
+
+def main(g, ver):
+ S = symbols[ver]
+ g.C2(0x378 + S['TApplication_gameLoop'], 'main.s', extra_as_input=[
+ '.set $conf, 0x817f04c3',
+ ]);
+
+make_xml(main)