As others have mentioned, this is best done in assembly. Here is some code to try: void delay_sub(unsigned char i) { // convert 20, 21, 22 etc to count in R7 of 1, 2, 3 (extra cycle added if i is odd) ; cycles rrc A ; 1 c = 1 if odd jnc ; 2 or 4 extra 2 cycles if branch taken (spoils cache) nop ; 1 nop ; 1 clc ; 1 even: subb A,#6 ; 1 mov R7,A ; 1 R7 now = (i / 2) - 6 //while (i--); loop: djnz R7, loop ; 2 loop address should be in cache, so no extra cycles needed ret ; 6 } if i even: 5+7+R7*2+6 = minimum of 20 22 24 ... R7 = 1, 2, 3 if i odd: 5+8+R7*2+6 = minimum of 21 23 25 ... R7 = 1, 2, 3 It assumes a call is made like LCALL(nn), where nn is a constant or a variable in a byte variable, so that the paramter can be passed using a MOV A, instruction. The call will take 5 cycles. The fixed overhead in the routine is either 7 cycles if i is even, or 8 if i is odd. A count is generated and put into R7, which is then decremented in a tight loop (two cycles per count). Then there is a fixed cycle count of 6 for the return. The behavior of the jump instructions are based on the datasheet for the C8051F38x as I understand them (in terms of when the instruction cache is spoiled or not). This may be different for other versions of the 8051.