The devs typed it both ways
func_801cdf70 walks to the end of the intrusive list from lesson 002 and hooks a new node on. The walk is a pointer chase you've done before — lw, test, bnezl back around. What's new is subtler, and it's a pure matching lesson.
The node-plus-offset address you learned comes out as an addu. Addition commutes, so the original programmer could write node + offset or offset + node — and in this function's source, they did both, in different spots. IDO keeps the source order of + operands. Two functions, identical except for the flip:
lh t6, 2(a0) # c->linkOffset
addu t7, a1, t6 # node + offset — node on the left
lw v0, 0(t7)
jr ra
nop
lh t6, 2(a0) # c->linkOffset
addu t7, t6, a1 # offset + node — the SAME address, flipped encoding
lw v0, 0(t7)
jr ra
nop
Same math, different bytes. The diff compares bytes.
This is why the context gives you two macros: LINKED_LIST_NEXT_FIELD adds node-first, LINKED_LIST_NEXT_FIELD2 adds offset-first. The second exists for no reason except that the original source typed the expression that way in some places — the real header says exactly that in a comment. Your job at each of the target's addu sites: read the operand order, pick the macro that produces it.
Beyond that, the function is tier-2 material: a null-head special case, a walk loop whose bnezl re-runs its slot only when continuing, a trailing-node hookup, a null store into the new node, and the count bump. Take the branches one at a time.
Your task
Write func_801cdf70 to reproduce the target assembly.