BRNE jump range, is this an ERROR in the Atmel AVR instruction datasheet ? - Page 1
Forum section: electronics / general

author: MathWizard
author_rank: Super Contributor
timestamp: September 20, 2024, 06:14:13 am
content: I'm decoding the opcodes from a little blink program on an ATTiny13A. And in the atmel-0856-avr-instruction-set-manual datasheet on page 54, for the "BRNE k", Branch If Not Equal command, it lists the # of Words k, forwards or backwards, the PC can jump to if the Z flag was 0. PC <-- PC +k +1But they say k is from -64 to +6, so is that really supposed to be -64 to +63 ? And so a signed SXX XXXX number ? My code hasBRNE 124dSo I think that's k = -4 = 111 1100I don't see it mentioned in the revisions, also they have the table of contents at the END, I've never seen that before.

author: voltsandjolts
author_rank: Unknown
timestamp: September 20, 2024, 06:51:57 am
content: The answer is in the image you posted.

author: magic
author_rank: Super Contributor
timestamp: September 20, 2024, 07:13:26 am
content: It's correct in my versionQuoteBut LOL, they really managed to screw it up in later revisions. The answer is of course -64<=k<64, as with other conditional branch insns.

author: westfw
author_rank: Super Contributor
timestamp: September 21, 2024, 12:23:45 am
content: The newer manual (DS40002198A) doesn't have the error...)

author: MathWizard
author_rank: Super Contributor
timestamp: September 22, 2024, 09:57:53 am
content: Yeah I should have put this in the other ATTiny thread I had. This stuff is addictive. I've been making a model of this MCU/program, in the OpenOffice spreadsheet, it doesn't really do binary or hex. I've made clocked, full BJT ALU's in LTSpice before, so for real automation I might try some of this in LTS too. Before downloading some basic MCU sim program. Now I can't wait to make some 4 or 8bit MCU with all BJT's.I'm using all NAND logic, so like open collector. With only using decimal 1,0 , 1-X=1 or 0 is an inverter, and the AND gate is X*Y=0 or 1. So I've made some register's, full 8-bit adders/ALU's, w/ inverted Carry flags, and 2's compliment were wanted, that matches the datasheet so far. And the Status SREG, for all the ALU flags.For now, I'm just making copies/new GP reg's and ALU for each instruction of interest, like the compare w/ carry instructions.So I can see in this blink program, how it made the millis function counter, using some compare commands and subtractions, and BRNE, to count down 255*165*14 ~590,000, and 255*82*7 ~146,000. I'll have to make a small # of cycles in the spreadsheet MCU, to see what it really does.There's a bunch of POKE commands in the middle of the program, IDK much about the stack counter, or how all that will tie in to the "while" command, and the bitshift it does at the start, using compares, and more BRNE's. It starts witting numbers into the data SRAM too.I expected the program to be shorter, I think I'm using the blink example in ArdIDE. Soon I'll have to try writing bare-metal Assembly for blink ON/OFF. The way they do it is probably fine, but IDK what all the rest of the middle code must be for yet.Quote

author: westfw
author_rank: Super Contributor
timestamp: September 23, 2024, 02:10:33 am
content: QuoteYou can find a lot of analyses of Arduino code in various places, explaining some of the "unexpected bloat."For simple programs (ie without Arduino), people are usually surprised by the interrupt vector table (~100 bytes) and C runtime initialization (~50 bytes)My long-ago post:https://community.element14.com/products/arduino/w/documents/7889/analysis-of-an-empty-arduino-sketch

author: 44kgk1lkf6u
author_rank: Regular Contributor
timestamp: September 23, 2024, 03:35:32 am
content: How long a binary did you get? I compiled with avr-gcc (Fedora 14.1.0-1.fc40) 14.1.0, with the options -std=gnu2x -Os -mmcu=attiny13 -DF_CPU=1'000'000. The binary is 80 bytes long with no store instructions.Code:Disassembly of section .text:00000000 <__vectors>:0:09 c0rjmp.+18; 0x14 <__ctors_end>2:0e c0rjmp.+28; 0x20 <__bad_interrupt>4:0d c0rjmp.+26; 0x20 <__bad_interrupt>6:0c c0rjmp.+24; 0x20 <__bad_interrupt>8:0b c0rjmp.+22; 0x20 <__bad_interrupt>a:0a c0rjmp.+20; 0x20 <__bad_interrupt>c:09 c0rjmp.+18; 0x20 <__bad_interrupt>e:08 c0rjmp.+16; 0x20 <__bad_interrupt>10:07 c0rjmp.+14; 0x20 <__bad_interrupt>12:06 c0rjmp.+12; 0x20 <__bad_interrupt>00000014 <__ctors_end>:14:11 24eorr1, r116:1f beout0x3f, r1; 6318:cf e9ldir28, 0x9F; 1591a:cd bfout0x3d, r28; 611c:02 d0rcall.+4; 0x22 <main>1e:16 c0rjmp.+44; 0x4c <_exit>00000020 <__bad_interrupt>:20:ef cfrjmp.-34; 0x0 <__vectors>00000022 <main>:22:88 e0ldir24, 0x08; 824:87 bbout0x17, r24; 2326:18 baout0x18, r1; 2400000028 <.L2>:28:88 bbout0x18, r24; 242a:2f e9ldir18, 0x9F; 1592c:36 e8ldir19, 0x86; 1342e:91 e0ldir25, 0x01; 100000030 <.L1^B1>:30:21 50subir18, 0x01; 132:30 40sbcir19, 0x00; 034:90 40sbcir25, 0x00; 036:e1 f7brne.-8; 0x30 <.L1^B1>38:00 c0rjmp.+0; 0x3a <L0^A>0000003a <L0^A>:3a:00 00nop3c:18 baout0x18, r1; 243e:e3 e2ldir30, 0x23; 3540:f4 efldir31, 0xF4; 24400000042 <.L1^B2>:42:31 97sbiwr30, 0x01; 144:f1 f7brne.-4; 0x42 <.L1^B2>46:00 c0rjmp.+0; 0x48 <L0^A>00000048 <L0^A>:48:00 00nop4a:ee cfrjmp.-36; 0x28 <.L2>0000004c <_exit>:4c:f8 94cli0000004e <__stop_program>:4e:ff cfrjmp.-2; 0x4e <__stop_program>

author: westfw
author_rank: Super Contributor
timestamp: September 24, 2024, 04:47:10 am
content: Quotethe code shown isn't "the Arduino blink example"; Arduino will likely add some additional overhead.(Hmm. "microcore" compiles the standard blink example to 90 bytes, which is better than I thought it would be!Differences seems to be: initialize TCNT0, Turn on Interrupts, anddelay()is implemented as a loop around_delay_ms(1))(IIRC, there was another ATtiny13 "core" that was substantially less optimized than microcore.)

author: MathWizard
author_rank: Super Contributor
timestamp: September 26, 2024, 08:18:00 am
content: IDK for sure were I got this blink code, I might have copy/pasted it, from some ATTiny tutorial. But it's from 0-87, so 88 WORDS, 176 Bytes of program flash.I just did up another spreadsheet to show what I think the prog does overall. But it doesn't lead to any of the middle stuff. This spreadsheet doesn't load the REG's and do all the ALU math, besides the program counter.I don't know much about the STACK, so I thought for a while that maybe the DDRB 1<<3, and the "while" command was doing some sort of PWM, overtop of the LED blink, to save power. This thing has bitwise operations, I expected a direct leftshift command. So I thought somehow all that was related to the RAM addresses that get used with the Store function and Xreg/XPointer, and the RCALL routine.But no I don't see anything like that. And DDRB is just for setting the port as input or output, so that's a bad way to try PWM. And they only ever write R1=0 into the start of RAM locations, so yeah I need to see some Stack and SP examples.But does this look about right ? Yeah IDK what all the middle code is for yet, nothing seems to lead there, if I got this right. It looks messy in a table so here's a pic

author: westfw
author_rank: Super Contributor
timestamp: September 26, 2024, 10:43:58 am
content: QuoteThe bitshifts, since they have constants for both arguments, happen at compile time and won’t appear in the binary.

author: MathWizard
author_rank: Super Contributor
timestamp: September 28, 2024, 12:22:16 am
content: Oh ok, and I see the final assembly from 44kgk1lkf6u isn't the same, so that's down to the compiler doing it differently. In win10, I used ArduinoIDE, w/ an Uno as the ISP, to program the ATTiny13A, I'm not sure which avrdude got used.And I see the RCALL isn't just a RJMP, there's a return address, from after the routine is done. But the only command I see like that is the RETI, in the section I'll mention below.Here's the full assembly, and here's the 1st bit of IntelHex, :20 0000 00 09C0 16C0 15C0 15C0 13C0, those 2x RJMP 21 are correct.0 RJMP 91 RJMP 222 RJMP 213 RJMP 214 RJMP 195 RJMP 186 RJMP 177 RJMP 168 RJMP 159 RJMP 1410 EOR R1,R111 OUT 63,R112 LDI R28,15913 OUT 61,R2814 LDI R18,015 LDI R26,9616 LDI R27,017 RJMP 118 ST X+,R119 CPI R26,10020 CPC R27,R1821 BRNE -422 RCALL 3923 RJMP 6224 RJMP-2525 PUSH R126 PUSH R027 IN R0,6328 PUSH R029 CLR R1730 PUSH R2431 PUSH R2532 PUSH R2633 PUSH R2734 LDS R24,9636 LDS R25,9738 LDS R26,9840 LDS R27,9942 ADIW R24+1:R24,143 ADC R26,R144 ADC R27,R145 STS 96,R2447 STS 97,R2549 STS 98,R2651 STS 99,R2753 POP R2754 POP R2655 POP R2556 POP R2457 POP R058 OUT 63,R059 POP R060 POP R161 RETI62 LDI R24,863 OUT 23,R2464 OUT 24,R165 OUT 24,R2466 SER R1867 LDI R19,16568 LDI R25,1469 SUBI R18,170 SBCI R19,071 SBCI R25,072 BRNE -473 RJMP 074 NOP75 OUT 24,R176 LDI R18,25577 LDI R19,8278 LDI R25,779 SUBI R18,180 SBCI R19,081 SBCI R25,082 BRNE 12483 RJMP 084 NOP85 RJMP -2186 BCLR 787 RJMP -1So the only thing that leads to Word 25, the 1st PUSH, is the RJMP 21, from the 0x03 interrupt location for the Timer/Counter OverFlow interrupt. But there was no setup at all for the T/C, so why bother if it's all off ?Then all the PUSH onto stack, LDS from RAM, a bit more math, STS to RAM, POP from stack, then there's an actual RETI.Isn't it correct, that R1 only ever equals, and then their ST X+, R1 , with X=0x60 to start, just stores, 0, to the 1st 4 places of RAM ? So aren't they just using ST X+, just to do an increment ? But they also seem to store R1=0 onto the stack, then moments later, POP the stored 0, back into R1, but R1 should never change anyways. So why bother ?? And R0 was never set to anything, but they PSUH it to 0x9F the top of the stack (at that time), so is it assumed it starts 0 ? They purposefully set R1=0.Assuming RAM 0x60-0x63 does have 0 in them, so are they using this to clear R24-R27, after just storing their values onto the STACK ? Then do some math to those R24-R27, then reload the stored values back from the STACK ??And whats the timer interrupt doing, and all that section ? It seemed to me, the BRNE's and RJMP's would be enough to reload all REG's to the programs initial conditions, and then do the 2 countdowns, and loop forever.All this is making more sense as I go, but yeah some of it seems silly, like an RJMP 0, followed by a NOP. If it's just for some timing reason, why not use 2x NOP, or 2x RJMP 0, or does it trigger something useful behind the scenes ??

author: sleemanj
author_rank: Super Contributor
timestamp: September 28, 2024, 03:45:06 am
content: Quote from: westfw on September 24, 2024, 04:47:10 amThere was "core13" but very ancient.MyATTinyCore forkcompiles standard blink for Tiny13A in 84 bytes. I don't think you can get much shorter than that without losing Arduino abilities (actually, that is already technically sacrificing millis() and tone(), but blink isn't using it, so no problem).Code::1000000009C00EC00DC00CC00BC00AC009C008C09A:1000100007C006C011241FBECFE9CDBF02D018C053:10002000EFCF86E886B9BB9A2AE439E0C39A89EE15:1000300093E0019749F4C39889EE93E00197B1F3F7:10004000F9013197F1F7FACFF9013197F1F7F1CFD3:04005000F894FFCF52:00000001FFLED on PB3 (pin 2)

author: westfw
author_rank: Super Contributor
timestamp: September 28, 2024, 06:38:15 am
content: I don't know why you continue to do this the hard way, when several people have pointed out easier ways (and existing analyses.)Also, recent versions of AVRDUDE (v8) have a nice "disassemble" feature that generates useful target labels for jmp/call instructions.Quote from: MathWizard on September 28, 2024, 12:22:16 amWell, main() doesn't return, and delay_ms() is a macro (ok "built-in function"), so why would there be any return instructions?QuoteThe C compiler has no knowlege of interrupt hardware, so there's no way for it to know that the ISR isn't called.I believe more recent cores have the ability to omit the timer ISR. NerdRalph's "picocore" keeps the ISR count in registers, so it takes less code and is faster.QuoteJust for timing, I think. _delay_ms() expands to __builtin_delay_cycles(), which will (hopefully) delay EXACTLY that many cycles.RJMP 0 is a two-cycle nop, while NOP is single-cycle, so the combination delays 3 cycles in the minimum code size.Here's the whole thing, annotated.Code:0 RJMP 9 ;; vectors1 RJMP 22 ;; jump destinations pretty useless.2 RJMP 213 RJMP 214 RJMP 195 RJMP 186 RJMP 177 RJMP 168 RJMP 159 RJMP 14;;; Beginning of C runtime initialization10 EOR R1,R1 ;; set up "known zero."11 OUT 63,R1 ;; clear status register12 LDI R28,159 ;; set up stack13 OUT 61,R2814 LDI R18,0 ;; probly the clear bss code.15 LDI R26,9616 LDI R27,017 RJMP 118 ST X+,R119 CPI R26,100 ;; note X == r27:r2620 CPC R27,R1821 BRNE -422 RCALL 39 ;; "rcall main"23 RJMP 62 ;; goto "exit"24 RJMP-25 ;; bad interrupt, restart by jump to 025 PUSH R1 ;; timer ISR26 PUSH R0 ;; save R027 IN R0,63 ;; save SREG28 PUSH R029 CLR R17 ;; no sure about this one!30 PUSH R24 ;; save registers for "long temp" in ISR31 PUSH R2532 PUSH R2633 PUSH R2734 LDS R24,96 ;; temp = milliscount36 LDS R25,9738 LDS R26,9840 LDS R27,9942 ADIW R24+1:R24,1 ;; increment temp43 ADC R26,R144 ADC R27,R145 STS 96,R24 ;; store new milliscount47 STS 97,R2549 STS 98,R2651 STS 99,R2753 POP R27 ;; restore registers54 POP R2655 POP R2556 POP R2457 POP R0 ;; restore SREG58 OUT 63,R059 POP R060 POP R161 RETI ;; end of ISR.;; main() starts here62 LDI R24,8 ;; 1<<3 to register.63 OUT 23,R24 ;; DDRB = 1<<364 OUT 24,R1 ;; PORTB = 0;; while loop starts here65 OUT 24,R24 ;; PORTB = 1<<3 (still in r24)66 SER R18 ;; load constants for _delay_ms in r25:R19:r1867 LDI R19,16568 LDI R25,1469 SUBI R18,1 ;; delayloop1: busy loop subtract for _delay_ms70 SBCI R19,071 SBCI R25,072 BRNE -4 ;; Keep looping.73 RJMP 0 ;; a couple extra cycles to make it exactly 500ms74 NOP75 OUT 24,R1 ;; PORTB = 076 LDI R18,255 ;; delayloop2: load constants for __delay_ms;; (interesting that this isn't diassembler as SER)77 LDI R19,8278 LDI R25,779 SUBI R18,1 ;; decrements80 SBCI R19,081 SBCI R25,082 BRNE 124 ;; looping for 2nd delay83 RJMP 0 ;; exactly 250ms84 NOP85 RJMP -21 ;; back to beginning of while loop.86 BCLR 7 ;; cli (turn off interrupts)87 RJMP -1 ;; infinite loop (this is "exit"), probably.

author: MathWizard
author_rank: Super Contributor
timestamp: September 29, 2024, 12:06:32 pm
content: Ok thanks westfw, I have a MCU sim program called SimuIDE, it does AVR and PCI and more. I'm just learning the basics of it, but for instance it can simulate an ATmega328 voltmeter, and it shows all the IntelHex, aswell as an animated voltmeter, and a knob to turn. So I want to try the blink code in that.So it is the Arduino way of doing things that's tripping me up. I'm still not sure if it's using the actual timer/counter. I see you said PUSH R1, becomes the timer interrupt service somehow. I'll have to look up about the arduino core, see where that even is. I have the EEPROM flash too, but never looked at it yet.So in the sim, I want to just upload the part countdown code that I think should work on it's own, and find out.Yeah I started to decode the IntelHex of the voltmeter stored in the AVR/examples in the SimuIDE program. It's on an ATMEGA328. That's taking way too long, it's some 2892 words long. It really seems to have a lot of gibberish for the 1st few lines. And the action starts around word 97.So yeah I'm not finishing that by hand. But I still want to try this blink code stripped down, and then make my own 3-digit voltmeter on an ATTiny, to get a better feel for it all. I made my own VM w/ the ArduinoIDE once, now I can try it with just the assembly math.And I need to get a newer datasheet for the assembly codes, there's more mistakes, I saw another place where #31 became just #3

author: MathWizard
author_rank: Super Contributor
timestamp: September 30, 2024, 07:27:44 pm
content: Hey I got it working in SimulIDE and on a real ATTiny13A. I worked out all the opcodes and the IntelHex in a speadsheet. Now for a DMM assembly program, I'll figure out which one of my programs will convert it. For now I just assume not use an IDE.So I left in the RJMP and NOP's, but the flash for the Blink prog is 37*2=74 Bytes. Maybe I don't need to set the SPL at all either.0 RJMP 91 RJMP 82 RJMP 73 RJMP 64 RJMP 55 RJMP 46 RJMP 37 RJMP 28 RJMP 19 RJMP 010 EOR R0,R011 LDI R16,812 LDI R17,15913 OUT 63,R014 OUT 61,R1715 OUT 23,R1616 OUT 24,R1617 SER R2418 LDI R22,16519 LDI R20,1420 SUBI R24,121 SBCI R22,022 SBCI R20,023 BRNE -424 RJMP 025 NOP26 OUT 24,R027 SER R2428 LDI R22,4229 LDI R20,530 SUBI R24,131 SBCI R22,032 SBCI R20,033 BRNE -434 RJMP 035 NOP36 RJMP -21:1000000009C008C007C006C005C004C003C002C0C4:1000100001C000C0002408E01FE90FBE1DBF07BBE0:1000200008BB8FEF65EA4EE0815060404040E1F749:1000300000C0000008BA8FEF6AE245E081506040DE:0A0040004040E1F700C00000EBCFE4:00000001FF