Lesson 5. The IO wire and the card tester: echo

In pictures

After this lesson the card talks over one wire. You set a number on the switches, press "send" — and a moment later the display shows the card's answer: your number plus one. The byte went down the wire, the card read it and sent it back.


5.1. The idea: one wire for two

A card has only 8 contacts, and the data goes over one of them — IO. They talk in turn:

  • whoever is silent lets go of the wire, and the resistor holds it at 1;
  • whoever talks pulls it to 0 with its transistor;
  • an ordinary output will not do here: it drives both 0 and 1, and two of them on one wire are a short circuit.

Such an output — "pull only" — is called an open drain. That is how the I/O line of real cards works.

cardprogram: ST 29testerframe, registerwire IOVCCpullGNDTPULLGNDboth silent → the resistor: 1one pulls → 0 (the resistor is weaker than a transistor)a normal output can’t go here: it also drives 1 — a short
Open drain: both the card and the tester can only pull the wire to 0 with their transistor, and a resistor holds it at 1. They talk in turn — and nobody shorts anybody. This is how the I/O line of real cards works.

5.2. The idea: a byte bit by bit

A frame: a start bit 0, 8 bits top bit first, a stop bit 1. Each bit is held for 16 clocks.

  • the card receives and sends a byte by its program: every command is one clock, the loop per bit is exactly 16 commands;
  • the tester does it in hardware: a frame counter and one shift register for receiving and sending;
  • a guard interval: you may answer only after the other side's stop bit. Without it the answer shifts by a bit.
IObitidlestartb7b6b5b4b3b2b1b0stopeach bit — 16 CLK cycles65 = 0100 0001most significant first
The byte 65 on the wire: the wire at 1, a start bit 0, eight bits most significant first, a stop bit 1. The receiver catches the fall of the start bit and reads each bit in its middle.

5.3. The idea: the "exchange — wait" lamp

After a transmission the card needs time to think. The tester holds a pause and lights lamp lamp. While it is lit — wait, the SEND button does nothing. It went off — the exchange is over, look at the answer.

5.4. Building

The same project as in lesson 4.

The card's side is small: register OUT and a transistor, the program does everything else. The terminal's side is the adapter: switches → "send" → wire → the answer on the lamps. Later it will become the ATM's port.

Step 1. The clock — faster

A byte over the wire is 10 bits of 16 clocks = 160 clocks. At 2Hz that is a minute and a half, so click the Clock body up to MAX. The lights and the display will flicker from now on — that is how it should be.

Step 2. The card's IO port

The command ST 29 (address $1D) puts bit 0 of A into register OUT. OUT = 1 — the transistor pulls the IO wire to 0, OUT = 0 — lets go. The wire is read by the command LD 28 ($1C) — that is AND(IO, selIO) from lesson 3. The card itself never pulls the wire to 1: the pull-up belongs to the terminal (the adapter's resistor).

PartPinLabel
ANDI0
I1
O0
oST
selIO
wOUT
RBITD
LD
nR
CLK
Q
acc0
wOUT
nR
CLK
pull
nmosgate
drain
source
pull
IO
GND

Step 3. The adapter: line pull-up and panel

Put the adapter as a separate group to the right of the card, and move Clock and the RST switch there too: in lesson 8 you will select the whole card with a frame and fold it into a chip, and they will stay outside. The adapter is the terminal's side, another device on the same CLK clock. So it has its own NOT(RST) — nRT. The resistor holds the IO wire at 1 while nobody pulls it; its own transistor TPULL is like the card's. Switches T0–T7 are the byte, button SEND is "send".

PartPinLabel
NOTI0
O0
RST
nRT
Resistorend 1
end 2
VCC
IO
nmosgate
drain
source
TPULL
IO
GND
Input (switch)outT0
Input (switch)outT1
Input (switch)outT2
Input (switch)outT3
Input (switch)outT4
Input (switch)outT5
Input (switch)outT6
Input (switch)outT7
ButtonoutSEND

Step 4. The button: a new press and the start

sendS is the button latched by the clock; sendP is the same one a clock earlier. A press = sendS and not sendP: exactly one clock, however long you hold it. The start of sending goTX — only when the adapter is free (free, the "exchange" lamp is off); the start of receiving goRX — not busy and the wire has dropped to 0 (the card has started talking).

PartPinLabel
DFFD
CLK
Q
SEND
CLK
sendS
DFFD
CLK
Q
sendS
CLK
sendP
NOTI0
O0
sendP
nSendP
ORI0
I1
O0
TXB
RXB
busy
NOTI0
O0
busy
idle
AND3A
B
C
Y
sendS
nSendP
free
goTX
NOTI0
O0
IO
ioLow
ANDI0
I1
O0
ioLow
idle
goRX

Step 5. The frame counter

While busy, counter cn steps +1 every clock, otherwise it stays at 0. The top 4 bits are the bit number in the frame (0 start, 1–8 data, 9 stop), the low ones are the clock inside the bit (0–15). Bit 0 simply flips on +1 — that is nc0, the same NOT that is needed below for the clock number: we do not place a second one.

PartPinLabel
HAA
B
S
C
cn1
cn0
ci1
cc2
HAA
B
S
C
cn2
cc2
ci2
cc3
HAA
B
S
C
cn3
cc3
ci3
cc4
HAA
B
S
C
cn4
cc4
ci4
cc5
HAA
B
S
C
cn5
cc5
ci5
cc6
HAA
B
S
C
cn6
cc6
ci6
cc7
HAA
B
S
C
cn7
cc7
ci7
cc8
ORI0
I1
O0
busy
WTB
lamp
NOTI0
O0
lamp
free
OR3A
B
C
Y
eTX
eRX
goRX
cz
NOTI0
O0
cz
ncz
AND3A
B
C
Y
lamp
nRT
ncz
cntOn
ANDI0
I1
O0
nc0
cntOn
cd0
ANDI0
I1
O0
ci1
cntOn
cd1
ANDI0
I1
O0
ci2
cntOn
cd2
ANDI0
I1
O0
ci3
cntOn
cd3
ANDI0
I1
O0
ci4
cntOn
cd4
ANDI0
I1
O0
ci5
cntOn
cd5
ANDI0
I1
O0
ci6
cntOn
cd6
ANDI0
I1
O0
ci7
cntOn
cd7
DFFD
CLK
Q
cd0
CLK
cn0
DFFD
CLK
Q
cd1
CLK
cn1
DFFD
CLK
Q
cd2
CLK
cn2
DFFD
CLK
Q
cd3
CLK
cn3
DFFD
CLK
Q
cd4
CLK
cn4
DFFD
CLK
Q
cd5
CLK
cn5
DFFD
CLK
Q
cd6
CLK
cn6
DFFD
CLK
Q
cd7
CLK
cn7
NOTI0
O0
cn0
nc0
NOTI0
O0
cn1
nc1
NOTI0
O0
cn2
nc2
NOTI0
O0
cn3
nc3
NOTI0
O0
cn4
nc4
NOTI0
O0
cn5
nc5
NOTI0
O0
cn6
nc6
NOTI0
O0
cn7
nc7

Step 6. Bit number and end of frame

ph0, ph9 are bit 0 and bit 9; inData is the data bits; sub15, sub7 are the last and the middle clock of a bit. Sending (TXB) ends on the last clock of the stop bit, receiving (RXB) in the middle of the stop bit.

PartPinLabel
AND4A
B
C
D
Y
nc4
nc5
nc6
nc7
ph0
AND4A
B
C
D
Y
cn4
nc5
nc6
cn7
ph9
NORI0
I1
O0
ph0
ph9
inData
AND4A
B
C
D
Y
cn0
cn1
cn2
cn3
sub15
AND4A
B
C
D
Y
cn0
cn1
cn2
nc3
sub7
ANDI0
I1
O0
ph9
sub15
endTX
ANDI0
I1
O0
ph9
sub7
endRX
NOTI0
O0
endTX
nEndTX
NOTI0
O0
endRX
nEndRX
ANDI0
I1
O0
TXB
nEndTX
txKeep
ORI0
I1
O0
goTX
txKeep
txN
ANDI0
I1
O0
txN
nRT
txD
DFFD
CLK
Q
txD
CLK
TXB
ANDI0
I1
O0
RXB
nEndRX
rxKeep
ORI0
I1
O0
goRX
rxKeep
rxN
ANDI0
I1
O0
rxN
nRT
rxD
DFFD
CLK
Q
rxD
CLK
RXB

Step 7. The shift register

One register for receiving and sending: the line goes one way at a time anyway. The start of sending takes the switches; then a shift left: when sending — at the end of a bit, when receiving — in the middle, and the line bit comes in at the bottom (bitIn).

PartPinLabel
AND3A
B
C
Y
TXB
inData
sub15
shTX
AND3A
B
C
Y
RXB
inData
sub7
shRX
ORI0
I1
O0
shTX
shRX
sh
ANDI0
I1
O0
RXB
IO
bitIn
MUX2A
B
S
Y
sr0
bitIn
sh
sa0
MUX2A
B
S
Y
sr1
sr0
sh
sa1
MUX2A
B
S
Y
sr2
sr1
sh
sa2
MUX2A
B
S
Y
sr3
sr2
sh
sa3
MUX2A
B
S
Y
sr4
sr3
sh
sa4
MUX2A
B
S
Y
sr5
sr4
sh
sa5
MUX2A
B
S
Y
sr6
sr5
sh
sa6
MUX2A
B
S
Y
sr7
sr6
sh
sa7
MUX2A
B
S
Y
sa0
T0
goTX
sb0
MUX2A
B
S
Y
sa1
T1
goTX
sb1
MUX2A
B
S
Y
sa2
T2
goTX
sb2
MUX2A
B
S
Y
sa3
T3
goTX
sb3
MUX2A
B
S
Y
sa4
T4
goTX
sb4
MUX2A
B
S
Y
sa5
T5
goTX
sb5
MUX2A
B
S
Y
sa6
T6
goTX
sb6
MUX2A
B
S
Y
sa7
T7
goTX
sb7
DFFD
CLK
Q
sb0
CLK
sr0
DFFD
CLK
Q
sb1
CLK
sr1
DFFD
CLK
Q
sb2
CLK
sr2
DFFD
CLK
Q
sb3
CLK
sr3
DFFD
CLK
Q
sb4
CLK
sr4
DFFD
CLK
Q
sb5
CLK
sr5
DFFD
CLK
Q
sb6
CLK
sr6
DFFD
CLK
Q
sb7
CLK
sr7

Step 8. Line, lamps and display

Sending: start bit — pull, data bit 0 — pull (the top bit of the register), stop — let go. The lamps and the display are the register: after receiving it holds the card's answer, and the display shows it as a number right away.

PartPinLabel
NOTI0
O0
sr7
nsr7
ANDI0
I1
O0
inData
nsr7
d0
ORI0
I1
O0
ph0
d0
pl
ANDI0
I1
O0
TXB
pl
TPULL
LEDinsr7
LEDinsr6
LEDinsr5
LEDinsr4
LEDinsr3
LEDinsr2
LEDinsr1
LEDinsr0
Display⚙width 8, DEC, bus sr

Step 9. The "exchange — wait" lamp

While the exchange is going on, lamp lamp is lit — wait. After its own transmission the adapter waits for the answer for another 256 clocks (WTB): the card needs time to think. If it starts answering — we receive; if it does not (a command of several bytes, the card waits for the next one) — the pause ends and the lamp goes off. After the answer — the same pause while the card finishes. The lamp is off — you can do the next action; while it is lit, SEND does nothing. lamp = busy or waiting (OR), free is the opposite; the frame counter also runs in the pause (cntOn), and at the end of a frame and at the start of receiving it starts from zero (cz). eTX, eRX are the end of a frame, only while one is going: in the pause the counter also passes 151 and 159, but that is not the end.

PartPinLabel
ANDI0
I1
O0
TXB
endTX
eTX
ANDI0
I1
O0
RXB
endRX
eRX
AND4A
B
C
D
Y
cn4
cn5
cn6
cn7
hi15
ANDI0
I1
O0
sub15
hi15
c255
NOTI0
O0
c255
nc255
ANDI0
I1
O0
WTB
nc255
wKeep
OR3A
B
C
Y
eTX
eRX
wKeep
wN
NOTI0
O0
goRX
nGoRX
AND3A
B
C
Y
wN
nGoRX
nRT
wD
DFFD
CLK
Q
wD
CLK
WTB
LEDinlamp

Step 10. The program in the ROM

⚙ on ROM → button ⬆ → file 05-echo.rom.json. The main thing in it is receiving and sending a byte by the program: every command is one clock, so the loop per bit is exactly 16 commands (spare clocks are NOP, the empty command). These are the same rx and tx that will be in the card's program.

; Lesson 5 — echo over one wire: the card receives a byte over IO and answers with the same byte + 1.; Receiving and sending are done by the program: bit by bit, exactly 16 clocks per bit (every command is one clock).; --- RAM (8 bytes) ---.equ B    $00      ; byte: received / sent.equ N    $01      ; bit counter.equ D    $02      ; delay counter.equ H    $03      ; first argument byte / high byte of the cipher.equ L    $04      ; second argument byte / low byte of the cipher.equ OK   $05      ; 1 = PIN is correct in this session; --- permanent memory (on transistors) ---.equ ACC  $10      ; account number.equ PINH $11      ; PIN, digits 1–2.equ PINL $12      ; PIN, digits 3–4.equ K0   $13      ; key, 4 bytes.equ K1   $14.equ K2   $15.equ K3   $16; --- try register and port ---.equ TRY  $18      ; wrong tries in a row (3 = blocked).equ IN   $1C      ; bit 0 = the IO line.equ OUT  $1D      ; bit 0 = 1 → pull the line to 0 start:  LD #0        ST OUT           ; let go of the line        ST B             ; RAM at power-on is garbage: write cell B first, then read it (rx shifts it)loop:   CALL rx          ; receive a byte → B        LD B        ADD #1        ST B             ; B + 1        CALL tx          ; send B        JMP loop ; --- receiving a byte: wait for the start bit, then 8 bits top first, one every 16 clocks ---rx:     LD IN        AND #1        JNZ rx           ; line is 1 — wait        LD #8        ST N        .rept 10         ; get to the middle of the first bit        NOP        .endrrxbit:  NOP        NOP        NOP        NOP        NOP        LD B        ADD B            ; shift B left        ST B        LD IN            ; ← here we read the bit        AND #1        ADD B        ST B        LD N        SUB #1        ST N        JNZ rxbit        ; the loop is exactly 16 clocks        LD #6        ST Drxg:    LD D             ; guard: wait out the terminal's stop bit (2 + 6 × 4 = 26 clocks)        SUB #1        ST D        JNZ rxg        RET ; --- sending byte B: start 0, 8 bits top first, stop 1 ---tx:     LD #1        ST OUT           ; start bit        .rept 7        NOP        .endr        LD #8        ST Ntxbit:  LD B        ADD B            ; top bit → C        ST B        LD #0        ADC #0           ; A = C        XOR #1           ; bit 1 → do not pull, 0 → pull        ST OUT           ; ← here the bit goes out on the line        NOP        NOP        NOP        NOP        NOP        LD N        SUB #1        ST N        JNZ txbit        ; the loop is exactly 16 clocks        NOP        NOP        NOP        NOP        NOP        LD #0        ST OUT           ; stop bit        LD #8        ST Ddly:    LD D             ; guard interval: 2 + 8 × 4 = 34 clocks        SUB #1        ST D        JNZ dly        RET

5.5. Checking

Go down the rows from top to bottom: one row is one action. After SEND wait until the "exchange" lamp goes off (lamp), and compare with the "Lamps" column: the card added 1 to your number and sent it back.

№What to doLamps (● lit)What it is
1Clock — MAX (click on the body)
2RST on and off
3switches T7…T0 = 0100 0001, press SEND○●○○○○●○ 6665 + 1 = 66
4switches T7…T0 = 0000 0000, press SEND○○○○○○○● 10 + 1 = 1
5switches T7…T0 = 1111 1111, press SEND○○○○○○○○ 0255 + 1 = 0 (8 bits: 256 → 0)
6switches T7…T0 = 1010 0101, press SEND●○●○○●●○ 166165 + 1 = 166
7switches T7…T0 = 0111 1111, press SEND●○○○○○○○ 128127 + 1 = 128
  • While the "exchange" lamp is lit, the sr lamps blink: the register pushes the switches out into the wire, and the answer slides in from the other end. It went off — the exchange is over, you can do the next one.
  • Nothing changes — check the pull-up: a Resistor between VCC and IO; without it the wire "floats" in Real (orange).
  • Memory at power-on is garbage (in Real unwritten latches are orange, Z). So the program first writes cell B (ST B) and only then reads it. Forget it — and in Real A turns orange on the very first ADD B.
  • In Real the adapter's lamps are orange until the first transmission: register sr has no reset and has not remembered anything yet. After the first SEND they are normal.

Do not fold into a chip. Leave the adapter on the canvas next to it — in lesson 8 the card goes into a chip, and the adapter stays outside.

⚠ Common mistakes. (1) No AND → txD: flip-flop TXB "floats", the whole tester turns orange, and after it the wire and the card. (2) The register outputs are labelled something other than sr — the lamps and the line do not see the register. (3) In Real the tester lamps are orange until the first transmission — that is normal: the register has no reset.

Lesson 5 paste (Cmd+I)

On top of lesson 4.

# Lesson 5 — the IO wire: the card port and the adapter (tester). On top of lesson 4; Clock MAX; then load 05-echo.rom.json into ROM.AND(oST, selIO, wOUT)RBIT(D=acc0, LD=wOUT, nR=nR, CLK=CLK, Q=pull)nmos(pull, IO, GND) NOT(RST, nRT)resistor(VCC, IO)nmos(TPULL, IO, GND)input(Out=T0)input(Out=T1)input(Out=T2)input(Out=T3)input(Out=T4)input(Out=T5)input(Out=T6)input(Out=T7)button(Out=SEND) DFF(D=SEND, CLK=CLK, Q=sendS)DFF(D=sendS, CLK=CLK, Q=sendP)NOT(sendP, nSendP)OR(TXB, RXB, busy)NOT(busy, idle)AND3(A=sendS, B=nSendP, C=free, Y=goTX)NOT(IO, ioLow)AND(ioLow, idle, goRX) HA(A=cn1, B=cn0, S=ci1, C=cc2)HA(A=cn2, B=cc2, S=ci2, C=cc3)HA(A=cn3, B=cc3, S=ci3, C=cc4)HA(A=cn4, B=cc4, S=ci4, C=cc5)HA(A=cn5, B=cc5, S=ci5, C=cc6)HA(A=cn6, B=cc6, S=ci6, C=cc7)HA(A=cn7, B=cc7, S=ci7, C=cc8)OR(busy, WTB, lamp)NOT(lamp, free)OR3(A=eTX, B=eRX, C=goRX, Y=cz)NOT(cz, ncz)AND3(A=lamp, B=nRT, C=ncz, Y=cntOn)AND(nc0, cntOn, cd0)AND(ci1, cntOn, cd1)AND(ci2, cntOn, cd2)AND(ci3, cntOn, cd3)AND(ci4, cntOn, cd4)AND(ci5, cntOn, cd5)AND(ci6, cntOn, cd6)AND(ci7, cntOn, cd7)DFF(D=cd0, CLK=CLK, Q=cn0)DFF(D=cd1, CLK=CLK, Q=cn1)DFF(D=cd2, CLK=CLK, Q=cn2)DFF(D=cd3, CLK=CLK, Q=cn3)DFF(D=cd4, CLK=CLK, Q=cn4)DFF(D=cd5, CLK=CLK, Q=cn5)DFF(D=cd6, CLK=CLK, Q=cn6)DFF(D=cd7, CLK=CLK, Q=cn7)NOT(cn0, nc0)NOT(cn1, nc1)NOT(cn2, nc2)NOT(cn3, nc3)NOT(cn4, nc4)NOT(cn5, nc5)NOT(cn6, nc6)NOT(cn7, nc7) AND4(A=nc4, B=nc5, C=nc6, D=nc7, Y=ph0)AND4(A=cn4, B=nc5, C=nc6, D=cn7, Y=ph9)NOR(ph0, ph9, inData)AND4(A=cn0, B=cn1, C=cn2, D=cn3, Y=sub15)AND4(A=cn0, B=cn1, C=cn2, D=nc3, Y=sub7)AND(ph9, sub15, endTX)AND(ph9, sub7, endRX)NOT(endTX, nEndTX)NOT(endRX, nEndRX)AND(TXB, nEndTX, txKeep)OR(goTX, txKeep, txN)AND(txN, nRT, txD)DFF(D=txD, CLK=CLK, Q=TXB)AND(RXB, nEndRX, rxKeep)OR(goRX, rxKeep, rxN)AND(rxN, nRT, rxD)DFF(D=rxD, CLK=CLK, Q=RXB) AND3(A=TXB, B=inData, C=sub15, Y=shTX)AND3(A=RXB, B=inData, C=sub7, Y=shRX)OR(shTX, shRX, sh)AND(RXB, IO, bitIn)MUX2(A=sr0, B=bitIn, S=sh, Y=sa0)MUX2(A=sr1, B=sr0, S=sh, Y=sa1)MUX2(A=sr2, B=sr1, S=sh, Y=sa2)MUX2(A=sr3, B=sr2, S=sh, Y=sa3)MUX2(A=sr4, B=sr3, S=sh, Y=sa4)MUX2(A=sr5, B=sr4, S=sh, Y=sa5)MUX2(A=sr6, B=sr5, S=sh, Y=sa6)MUX2(A=sr7, B=sr6, S=sh, Y=sa7)MUX2(A=sa0, B=T0, S=goTX, Y=sb0)MUX2(A=sa1, B=T1, S=goTX, Y=sb1)MUX2(A=sa2, B=T2, S=goTX, Y=sb2)MUX2(A=sa3, B=T3, S=goTX, Y=sb3)MUX2(A=sa4, B=T4, S=goTX, Y=sb4)MUX2(A=sa5, B=T5, S=goTX, Y=sb5)MUX2(A=sa6, B=T6, S=goTX, Y=sb6)MUX2(A=sa7, B=T7, S=goTX, Y=sb7)DFF(D=sb0, CLK=CLK, Q=sr0)DFF(D=sb1, CLK=CLK, Q=sr1)DFF(D=sb2, CLK=CLK, Q=sr2)DFF(D=sb3, CLK=CLK, Q=sr3)DFF(D=sb4, CLK=CLK, Q=sr4)DFF(D=sb5, CLK=CLK, Q=sr5)DFF(D=sb6, CLK=CLK, Q=sr6)DFF(D=sb7, CLK=CLK, Q=sr7) NOT(sr7, nsr7)AND(inData, nsr7, d0)OR(ph0, d0, pl)AND(TXB, pl, TPULL)led(sr7)led(sr6)led(sr5)led(sr4)led(sr3)led(sr2)led(sr1)led(sr0)display{bits=8, base="DEC", busSource="sr"}() AND(TXB, endTX, eTX)AND(RXB, endRX, eRX)AND4(A=cn4, B=cn5, C=cn6, D=cn7, Y=hi15)AND(sub15, hi15, c255)NOT(c255, nc255)AND(WTB, nc255, wKeep)OR3(A=eTX, B=eRX, C=wKeep, Y=wN)NOT(goRX, nGoRX)AND3(A=wN, B=nGoRX, C=nRT, Y=wD)DFF(D=wD, CLK=CLK, Q=WTB)led(lamp)

¿No te sale? Algo no funciona →