4
\$\begingroup\$

I have read and disassembled the code from an Atmel8 microcontroller from a faulty oscilloscope.

This is the start routine:

ROM:0013                 ; public __RESET
ROM:0013 __RESET:                                
ROM:0013                                         
ROM:0013                 ldi     r16, 4
ROM:0014                 out     SPH, r16        ; Stack Pointer High
ROM:0015                 ldi     r16, 0x5F
ROM:0016                 out     SPL, r16        ; Stack Pointer Low
ROM:0017                 rcall   fault_check     ; Check for faults
ROM:0018                 breq    loc_1B
ROM:0019
ROM:0019 loc_19:                                 
ROM:0019                 sbi     PORTD, PORTD7   ; Switch on fault LED
ROM:001A                 rjmp    loc_19          ; Infinite loop

It calls a routine, which I have named "fault_check". On return, if Zero is set, the program switches on the "fault" LED on bit 7 of PORTD, and enters an infinite loop.

This is the code of "fault_check":

ROM:0396                 ; public fault_check
ROM:0396 fault_check:                            
ROM:0396                 ldi     r17, 0x72  ;
ROM:0397                 ldi     r18, 9
ROM:0398                 clr     r30
ROM:0399                 clr     r31
ROM:039A                 ldi     r20, 0x80
ROM:039B                 ldi     r19, 5
ROM:039C                 lpm     r3, Z+          ; Reads the RESET vector!?
ROM:039D                 lpm     r2, Z+
ROM:039E
ROM:039E loc_39E:                                
ROM:039E                 cp      r30, r17
ROM:039F                 cpc     r31, r18
ROM:03A0                 brge    loc_3A5
ROM:03A1                 lpm     r1, Z+
ROM:03A2                 lpm     r0, Z+
ROM:03A3                 rcall   sub_3AD         ; See below
ROM:03A4                 rjmp    loc_39E
ROM:03A5 ; ------------------------------
ROM:03A5
ROM:03A5 loc_3A5:                                
ROM:03A5                 lpm     r0, Z+
ROM:03A6                 lpm     r1, Z+
ROM:03A7                 rjmp    loc_3AA
ROM:03A8 ; -------------------------------
ROM:03A8                 clr     r0
ROM:03A9                 clr     r1
ROM:03AA
ROM:03AA loc_3AA:                                
ROM:03AA                 rcall   sub_3AD
ROM:03AB                 or      r2, r3
ROM:03AC                 ret
ROM:03AC ; End of function fault_check
ROM:03AC
ROM:03AD ; Subroutine sub_3AD here
ROM:03AD
ROM:03AD sub_3AD:                                
ROM:03AD                                         
ROM:03AD                 ldi     r21, 0x11
ROM:03AE
ROM:03AE loc_3AE:                               
ROM:03AE                                         
ROM:03AE                 dec     r21
ROM:03AF                 breq    locret_3B8
ROM:03B0                 lsl     r0
ROM:03B1                 rol     r1
ROM:03B2                 rol     r2
ROM:03B3                 rol     r3
ROM:03B4                 brcc    loc_3AE
ROM:03B5                 eor     r2, r19
ROM:03B6                 eor     r3, r20
ROM:03B7                 rjmp    loc_3AE
ROM:03B8 ; ---------------------------------------------------------------------------
ROM:03B8
ROM:03B8 locret_3B8:                             
ROM:03B8                 ret
ROM:03B8 ; End of sub_3AD

These are the relevant memory regions: 0000 area

0580 area

0972 area

I have no idea of what's going on here. I understand the instructions, but what is happening overall? I believe it is processing information stored in SRAM (no IO reads here or anything hardware related). Hence, the output should be deterministic (I don't think Atmega8 allows DMA).

\$\endgroup\$
2
  • 8
    \$\begingroup\$ It calculates a CRC checksum over the first 2418 bytes in the flash to detect code manipulation or flash faults. \$\endgroup\$
    – Jens
    Aug 12, 2023 at 21:34
  • \$\begingroup\$ Oh wow! bitwise CRC \$\endgroup\$ Aug 13, 2023 at 5:27

1 Answer 1

14
\$\begingroup\$

I added some comments to explain the function.

ROM:0396                 ; public fault_check
ROM:0396 fault_check:
ROM:0396                 ldi     r17, 0x72  ; load end address 0x0972
ROM:0397                 ldi     r18, 9
ROM:0398                 clr     r30        ; load Z with start address
ROM:0399                 clr     r31
ROM:039A                 ldi     r20, 0x80  ; load CRC xor code 0x8005
ROM:039B                 ldi     r19, 5
ROM:039C                 lpm     r3, Z+     ; set CRC to first data word
ROM:039D                 lpm     r2, Z+
ROM:039E loc_39E:
ROM:039E                 cp      r30, r17   ; compare Z to end address
ROM:039F                 cpc     r31, r18
ROM:03A0                 brge    loc_3A5    ; exit if end addr reached
ROM:03A1                 lpm     r1, Z+
ROM:03A2                 lpm     r0, Z+     ; fetch next data word
ROM:03A3                 rcall   sub_3AD_CalcCRC16        ; See below
ROM:03A4                 rjmp    loc_39E    ; one more data word
ROM:03A5
ROM:03A5 loc_3A5:
ROM:03A5                 lpm     r0, Z+     ; load magic word to
ROM:03A6                 lpm     r1, Z+     ;  produce CRC zero result
ROM:03A7                 rjmp    loc_3AA
ROM:03A8 ; -------------------------------
ROM:03A8                 clr     r0         ; dead debug code fragment
ROM:03A9                 clr     r1
ROM:03AA loc_3AA:                                
ROM:03AA                 rcall   sub_3AD_CalcCRC16  ; last CRC call
ROM:03AB                 or      r2, r3     ; code valid if CRC is zero
ROM:03AC                 ret
ROM:03AC ; End of function fault_check
ROM:03AC
ROM:03AD ; Subroutine sub_3AD here
ROM:03AD
ROM:03AD sub_3AD_CalcCRC16:
ROM:03AD                                         
ROM:03AD                 ldi     r21, 0x11  ; load number of bits + 1 to exit on zero
ROM:03AE loc_3AE:
ROM:03AE                 dec     r21        ; count bit
ROM:03AF                 breq    locret_3B8 ; exit if 16 bit done
ROM:03B0                 lsl     r0     ; left shift data into CRC
ROM:03B1                 rol     r1
ROM:03B2                 rol     r2
ROM:03B3                 rol     r3
ROM:03B4                 brcc    loc_3AE    ; only do xor on carry set
ROM:03B5                 eor     r2, r19    ; xor CRC with special code
ROM:03B6                 eor     r3, r20
ROM:03B7                 rjmp    loc_3AE    ; one more bit
ROM:03B8 locret_3B8:                             
ROM:03B8                 ret
ROM:03B8 ; End of sub_3AD_CalcCRC16
\$\endgroup\$
3
  • 2
    \$\begingroup\$ Good explanation: Appending the magic word to make the CRC zero is called "augmented CRC" by some people. The "xor code" aka "special code" is typically called "polynomial". The 8005 polynomial is one of the two most widely used ones for 16-bit CRCs. It is used e.g. by ARCNET and LHA. \$\endgroup\$ Aug 13, 2023 at 7:49
  • \$\begingroup\$ @Jens - Superb explanation, thank you very much! Well, when I run the code, the CRC check fails, so perhaps the flash memory is corrupted. \$\endgroup\$
    – DrD
    Aug 13, 2023 at 9:56
  • \$\begingroup\$ Nicely done. A clue to the possible culprit is that it's called very early, so there isn't really much that it /could/ be testing. \$\endgroup\$ Aug 13, 2023 at 11:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.