You can check for repeating a previous state by running two copies in lockstep, one running twice as fast as the other. If the states of the two match, you're in an infinite loop. In the 1970s, a batch system intended for beginning programmers actually did that. The college had discovered that the successful jobs ran about a second, and the ones in a loop ran out the 30 second timer. So it was a win to take a 2x speed penalty to catch simple loops.
You can make a program cycle through a very large number of states, so you don't always get a practical halting check, but in theory, and often in practice, you do repeat a state eventually.
Undecidability requires infinities.