The practical details depend on the platform you work with. What is constant across all of them is that you first have to think about how to split up your code into testable parts.
I know that still sounds esoteric. It takes some getting used to. For some people the jump from "writing huge blobs of spaghetti" directly to test driven development is too big.
As an intermediate step, try this exercise: Pick one of your more complex existing methods to clean up.
- Split it into parts by turning blocks in it into separate methods. It is ok if they are only called once, there is nothing wrong with that. Let the compiler worry about optimization. If you do not know how to split and what to call the new methods, existing comments explaining blocks of code can be used as guidelines.
- Remove side effects from the newly created helper methods. No manipulating any data that will still be there after the method exited except for the return value. You need that data manipulated? Return it. You already have a return value? Split further or create a return struct or class. (Exceptions: A) Methods that have the explicit purpose of causing a side effect, e.g. writing something to disk. B) Methods that have the explicit purpose of manipulating something handed in as a reference. Both of those are fine as long as that purpose is obvious.)
- Adjust the names of your new methods until they describe as precisely as possible and practical what they are doing.
- Start writing tests for those new methods. They should be testable now. Bonus points if you can come up with an obviously correct test case that fails for something that was considered working code before.