[ASM] Problem writing to TCNT1

Hi,

I have a piece of C code I am converting to ASM. I have a problem assigning values to TCNT1.

I tried:

    ; set timer
    ldi r16, 255                                
    out	_SFR_IO_ADDR(TCNT1H), r16 
    ldi r16, 200                                
    out	_SFR_IO_ADDR(TCNT1L), r16

getting "Error: number must be positive and less than 64". Tried:

    ; set timer
    ldi r16, 255                                
    out TCNT1H, r16 
    ldi r16, 200                                
    out TCNT1L, r16

same error.

Anyone has any suggestions as to how I can set TCNT1 in assembler ? Tried the interwebs, it seems the above solution "should be correct" - but alas, it does not work. Am I missing some defines?

See the datasheet:

When using the I/O specific commands IN and OUT, the I/O addresses 0x00 - 0x3F must be used. When addressing I/O Registers as data space using LD and ST instructions, 0x20 must be added to these addresses. The ATmega48A/PA/88A/PA/168A/PA/328/P is a complex microcontroller with more peripheral units than can be supported within the 64 location reserved in Opcode for the IN and OUT instructions. For the Extended I/O space from 0x60 - 0xFF in SRAM, only the ST/STS/STD and LD/LDS/LDD instructions can be used.

In other words, you can't use OUT with TCNT1 (that is addresses 0x84 and 0x85). Use STS instead.

The code generated by the C compiler to load up TCNT1 with a number (0x1234) is:

  be: 84 e3       ldi r24, 0x34 ; 52
  c0: 92 e1       ldi r25, 0x12 ; 18
  c2: 90 93 85 00 sts 0x0085, r25
  c6: 80 93 84 00 sts 0x0084, r24

Hi Nick,

Thank you very much for your reply. That is strange, I hav found it in plenty of examples. Maybe these examples were for other ATmel-chips and I did not notice.

Thanks a bunch ! - i will see how the code handles the 4 extra cycles 8)

I doubt you found setting TCNT1 for the AVR in the examples. TCNT0, for example, would have worked. You may have assumed that if TCNT0 worked, then TCNT1 would also.

I suppose you don't want to hear this, but you can see that the C compiler generated just as good code as your assembler would be. (I don't mean your assembler, but anyone's).

As a general rule, using assembler won't save you time, however if you are doing absolutely time-critical code, and you need to count cycles, then yes, it can be useful.

By examples on the interwebs i meant "random code i found, that random people made - working on what seemed to be arduinos". They are not officials.

'
I know - optimizing with inline asm kinda went away when we started getting enough registers. Oh the good old days...

which is exactly what I am doing - I would rather do the entire thing in asm, than decompile my binaries all the time, to count cycles. + its fun :slight_smile:

I doubt you found setting TCNT1 for the AVR in the examples.

That is strange, I hav found it in plenty of examples.

TCNT1 is very likely in-range on some processors.

chunklady:
which is exactly what I am doing - I would rather do the entire thing in asm, than decompile my binaries all the time, to count cycles. + its fun :slight_smile:

What I often do - when the occasion arises - is to decompile the generated C code. That's easy to do, and you can count cycles as well.

(edit) Re-reading your post, it looks like you are doing exactly that. :slight_smile:

If it's any help here is my table of cycles for the Atmega328, as a Lua table:

cycles = {

    -- ARITHMETIC AND LOGIC INSTRUCTIONS
    
    ADC = "1",
    ADD = "1",
    ADIW = "2",
    AND = "1",
    ANDI = "1",
    CBR = "1",
    CLR = "1",
    COM = "1",
    DEC = "1",
    EOR = "1",
    FMUL = "2",
    FMULS = "2",
    FMULSU = "2",
    INC = "1",
    MUL = "2",
    MULS = "2",
    MULSU = "2",
    NEG = "1",
    OR = "1",
    ORI = "1",
    SBC = "1",
    SBCI = "1",
    SBIW = "2",
    SBR = "1",
    SER = "1",
    SUB = "1",
    SUBI = "1",
    TST = "1",

    
    -- BRANCH INSTRUCTIONS
    
    BRBC = "1/2",
    BRBS = "1/2",
    BRCC = "1/2",
    BRCS = "1/2",
    BREQ = "1/2",
    BRGE = "1/2",
    BRHC = "1/2",
    BRHS = "1/2",
    BRID = "1/2",
    BRIE = "1/2",
    BRLO = "1/2",
    BRLT = "1/2",
    BRMI = "1/2",
    BRNE = "1/2",
    BRPL = "1/2",
    BRSH = "1/2",
    BRTC = "1/2",
    BRTS = "1/2",
    BRVC = "1/2",
    BRVS = "1/2",
    CALL = "4",
    CP = "1",
    CPC = "1",
    CPI = "1",
    CPSE = "1/2/3",
    ICALL = "3",
    IJMP = "2",
    JMP = "3",
    RCALL = "3",
    RET = "4",
    RETI = "4",
    RJMP = "2",
    SBIC = "1/2/3",
    SBIS = "1/2/3",
    SBRC = "1/2/3",
    SBRS = "1/2/3",

    
    -- BIT AND BIT-TEST INSTRUCTIONS
    
    ASR = "1",
    BCLR = "1",
    BLD = "1",
    BSET = "1",
    BST = "1",
    CBI = "2",
    CLC = "1",
    CLH = "1",
    CLI = "1",
    CLN = "1",
    CLS = "1",
    CLT = "1",
    CLV = "1",
    CLZ = "1",
    LSL = "1",
    LSR = "1",
    ROL = "1",
    ROR = "1",
    SBI = "2",
    SEC = "1",
    SEH = "1",
    SEI = "1",
    SEN = "1",
    SES = "1",
    SET = "1",
    SEV = "1",
    SEZ = "1",
    SWAP = "1",

    
    -- DATA TRANSFER INSTRUCTIONS
    
    IN = "1",
    LD = "2",
    LDD = "2",
    LDI = "1",
    LDS = "2",
    LPM = "3",
    MOV = "1",
    MOVW = "1",
    OUT = "1",
    POP = "2",
    PUSH = "2",
    ST = "2",
    STD = "2",
    STS = "2",

    -- MCU CONTROL INSTRUCTIONS
    
    NOP = "1",
    SLEEP = "1",
    WDR = "1",

}  -- end of cycles table

A bit of scanning of the assembler, a regular expression or two, and you can output the cycle count next to each line. For example, for the posted snippet above:

  be: 84 e3       ldi r24, 0x34 ; 52   (1)
  c0: 92 e1       ldi r25, 0x12 ; 18   (1)
  c2: 90 93 85 00 sts 0x0085, r25   (2)
  c6: 80 93 84 00 sts 0x0084, r24   (2)

Cycles in brackets.