Радиомикрофоны
October 18

Реверс инжиниринг «Трёшки»‬ от Eddy71 1.1

Дизассемблирование прошивки Treshka1822.hex от Eddy71 с vrtp.ru для PIC12F1822 и GSM модуля SIM800C. Исходник «Трёшки»‬ на макроассемблере.

— Посмотри мою программу. Где у меня ошибка?
— Посмотрел.
— Ну, и где?
— В ДНК.

"Трешка" от Eddy71 с vrtp.ru

Данная конструкция - это красноречивый пример, как НЕ НАДО делать:

  1. Ответы SIM800C не проверяются
  2. Входной вызов (RING) "проверяется" только по двум символам 'R', 'I'
  3. Список звонков (CLCC) не проверяется никак вообще
  4. Постоянные глюки из-за специфической разводки платы, отсутствия множества "лишних" конденсаторов и противовеса (экранирование цифровой части тоже бы не помешало)

"Секретные" функции:

  1. Три телефонных номера (ячейки с адресами 0x10, 0x20, 0x30 в EEPROM)
  2. Третий номер невозможно удалить
  3. Входящие звонки различаются только по первым 12 знакам, включая "+"

Осторожно! Работа не закончена, ошибок (как авторских, так и моих) много.

Декомпиляция, редактирование, сборка:

apt install gputils
gpdasm -p pic12f1822 -csony Treshka1822.hex > Treshka1822_gpdasm.asm
cc -o encode_commands encode_commands.c && ./encode_commands
gpasm Treshka1822_gpdasm_edited.asm -o Treshka1822_gpdasm_edited.hex

Cтроки AT-команд специальным образом кодируются (у PIC 14 бит в слове, помещается два 7-битных символа): encode_commands создает файлы command1.inc...command9.inc.

Директива DA делает то же самое, но порядок байт обратный.

Перекомпилированная прошивка почему-то отличается несколькими строками:

Добавлены:

:020000040000FA
:02000E003CFEB6
:02001000FCDC16

Удалена:

:04000E003C3EFC1C5C

Напишите в комментариях, кто знает, почему.

"Treshka" by Eddy71 Disassembled (Source Code)

        processor p12f1822
        radix dec

        include p12f1822.inc

; The recognition of labels and registers is not always good, therefore
; be treated cautiously the results.

        CONFIG  FOSC     = INTOSC
        CONFIG  WDTE     = ON
        CONFIG  PWRTE    = OFF
        CONFIG  MCLRE    = OFF
        CONFIG  CP       = ON
        CONFIG  CPD      = ON
        CONFIG  BOREN    = ON
        CONFIG  CLKOUTEN = OFF
        CONFIG  IESO     = ON
        CONFIG  FCMEN    = ON
        CONFIG  WRT      = ALL
        CONFIG  PLLEN    = OFF
        CONFIG  STVREN   = OFF
        CONFIG  BORV     = LO
        CONFIG  DEBUG    = OFF
        CONFIG  LVP      = OFF

;===============================================================================

#define TRUE 1
#define FALSE 0

#define PHONE_LEN 12            ; число символов в телефонном номере

#define CLCC_PHONE_OFFSET 18    ; смещение номера в ответе на AT+CLCC

#define CLCC_MIN_RESPONSE (26 + PHONE_LEN)    ; минимальная длина ответа

#define CELLS 3                 ; потому и "Трешка", что три телефонных номера
#define MAXCELL (CELLS-1)       ; последняя ячейка

#define BAUD 9600               ; также см. AT-команду command1
#define F_OSC 1800

;===============================================================================
; DATA address definitions

Common_RAM      equ     0x0070                              ; size: 16 bytes

LINEAR_DATA_MEMORY      equ     0x2000  ; см. стр. 46, 48 даташита PIC12F1822
; получить адрес переменной в Linear Data Memory
#define LDM(var) ((var) - 0x20 + LINEAR_DATA_MEMORY)

saved_rcsta     equ     0x28            ; сохраненное значение регистра RCSTA

xflags          equ     0x29            ; первая группа флагов
FLAG_GOT_RESP   equ     0               ; ответ SIM800 - в буфере
; ответ на AT+CLCC имеет длину >= CLCC_MIN_RESPONSE
FLAG_CLCC_OK    equ     1

bufptr          equ     0x2a            ; индекс свободного байта в буфере

ringtime        equ     0x2b            ; измерение продолжительности звонка

wait_clcc       equ     0x2c            ; счетчик ожидания ответа на AT+CLCC

cell            equ     0x2e            ; номер свободной ячейки

; переполнение буфера!
buffer          equ     0x2f            ; принятый ответ на AT-команду
; выходит за пределы 0x63 - будет затирать другие переменные!
BUFSZ           equ     (0x7d - buffer)

holdbtn_l       equ     0x63            ; измерение длительности нажатия кнопки
holdbtn_h       equ     0x64

; адреса у этих переменных одинаковы:
blink_arg       equ     0x65            ; аргумент blink(n) - мигать n раз
erase_address   equ     0x65            ; указатель на затираемый байт
i               equ     0x65            ; счетчик
yflags          equ     0x65            ; вторая группа флагов
; 1. телефон хозяина задан
; 2. звонящий - хозяин
FLAG_GOT_PHONE  equ     0

blink_counter   equ     0x66            ; счетчик миганий
j               equ     0x66            ; счетчик
digit_index     equ     0x66            ; индекс цифры при поиске

delay_arg       equ     0x67            ; аргумент функции delay(n)
; счетчик ячеек в процедуре сравнения номера звонящего
cur_cell        equ     0x67
txchar          equ     0x67            ; временная transmit_at_command()

numptr          equ     0x68            ; сохранение номера: адрес цифры в ячейке
cur_dig         equ     0x68            ; сравниваемая цифра при поиске
txadrl          equ     0x68            ; временная transmit_at_command()

clcc_digit      equ     0x69            ; сохраняемая в EEPROM цифра номера
txadrh          equ     0x69            ; временная transmit_at_command()

received_byte   equ     0x6c

k               equ     (Common_RAM + 7); счетчик
saved_intcon    equ     (Common_RAM + 7); INTCON

m               equ     (Common_RAM + 8); счетчик
; 0x78 - временная переменная для хранения принятого байта
tmp_byte        equ     (Common_RAM + 8)
search_result   equ     (Common_RAM + 8); номер позвонившего обнаружен в ячейках?

; адреса трех ячеек в EEPROM
EEPROM_PHONE1   equ     0x10
EEPROM_PHONE2   equ     0x20
EEPROM_PHONE3   equ     0x30

;===============================================================================
; PINS usage definitions

BUTTON          equ     RA3
RING            equ     RA5
LED             equ     LATA2
DTR             equ     LATA4

;===============================================================================
; Макросы

movwfb  MACRO file
        BANKSEL (file)
        movwf   (file)
        ENDM

movfb MACRO file
        BANKSEL (file)
        movf    (file), W
        ENDM

movlfb  MACRO value, file
        movlw   (value)
        movwfb  (file)
        ENDM

movlf   MACRO value, file
        movlw   (value)
        movwf   (file)
        ENDM

movff   MACRO src, dst
        movf    (src), W
        movwf   (dst)
        ENDM

movffb  MACRO src, dst
        movf    (src), W
        movwfb  (dst)
        ENDM

movffbb MACRO src, dst
        movfb   (src)
        movwfb  (dst)
        ENDM

movfbf  MACRO src, dst
        movfb   (src)
        movwf   (dst)
        ENDM

clrfb   MACRO file
        BANKSEL (file)
        clrf    (file)
        ENDM

; 16-bit increment
incf16  MACRO file
        incf    (file), F
        btfsc   STATUS, Z
        incf    (file) + 1, F
        ENDM

gotop   MACRO address
        movlp   0
        goto    (address)
        ENDM

IFL     MACRO   var, n, label
        movf    (var), W
        sublw   (n)
        btfsc   STATUS, C
        goto    (label)
        ENDM

IFG     MACRO   var, n, label
        movf    (var), W
        sublw   (n)
        btfss   STATUS, C
        goto    (label)
        ENDM

IFNE    MACRO   var, n, label
        movf    (var), W
        sublw   (n)
        btfss   STATUS, Z
        goto    (label)
        ENDM

IFEQ    MACRO   var, n, label
        movf    (var), W
        sublw   (n)
        btfsc   STATUS, Z
        goto    (label)
        ENDM

IFZ     MACRO   var, label
        movf    (var), W
        btfsc   STATUS, Z
        goto    (label)
        ENDM

IFNZ    MACRO   var, label
        movf    (var), F
        btfss   STATUS, Z
        goto    (label)
        ENDM

transmit_at_command MACRO command
        movlfb  low (command), EEADRL
        movlf   upper (command), EEADRH
        movlb   0
        call    function_transmit
        ENDM

transmit MACRO char
        movlw   (char)
        clrwdt
        btfss   PIR1, TXIF                                  ; reg: 0x011, bit: 4
        goto    $-2
        movwfb  TXREG                                       ; reg: 0x19a
        ENDM

transmitb MACRO char
        LOCAL label_021
        LOCAL label_022
        movlw   (char)
label_021:
        clrwdt
        movlb   0
        btfsc   PIR1, TXIF                                  ; reg: 0x011, bit: 4
        goto    label_022
        BANKSEL TXREG
        goto    label_021
label_022:
        movwfb  TXREG                                       ; reg: 0x19a
        ENDM

blinkb MACRO n
        movlfb  (n), blink_arg
        call    function_blink
        ENDM

blink MACRO n
        movlf   (n), blink_arg
        call    function_blink
        ENDM

delay MACRO n
        movlw   (n)
        movwf   delay_arg                                   ; reg: 0x067
        call    function_delay
        ENDM

delayb MACRO n
        movlfb  (n), delay_arg                             ; reg: 0x067
        call    function_delay
        ENDM

led_on MACRO
        BANKSEL LATA
        bcf     LATA, LED                                   ; reg: 0x10c, bit: 2
        ENDM

led_off MACRO
        BANKSEL LATA
        bsf     LATA, LED                                   ; reg: 0x10c, bit: 2
        ENDM

longdelay MACRO v, x, y
        LOCAL label_035
        movlf   (x), (v)
label_035:
        delay   (y)
        decfsz  (v), F
        goto    label_035
        ENDM

; небольшая задержка
shortdelay MACRO n
        movlf   (n), k                                      ; reg: 0x077
        decfsz  k, F                                        ; reg: 0x077
        goto    $-1
        ENDM

;===============================================================================
; CODE area

        ; code

        org     __CODE_START                                ; address: 0x0000

vector_reset:                                               ; address: 0x0000

        gotop   reset_vector
        nop

        ; code

        org     __VECTOR_INT                                ; address: 0x0004

vector_int:                                                 ; address: 0x0004
        ; обработчик прерывания от последовательного порта
        clrf    STATUS                                      ; reg: 0x003
        movlp   0

        ; сохраняемся
        movlb   0
        movff   (Common_RAM + 7), 0x20                      ; reg: 0x077
        movff   (Common_RAM + 8), 0x21                      ; reg: 0x021
        movff   (Common_RAM + 9), 0x22                      ; reg: 0x022
        movff   (Common_RAM + 10), 0x23                     ; reg: 0x023

        movffbb EEADRL, 0x24                                ; reg: 0x024
        movffbb EEADRH, 0x25                                ; reg: 0x024
        movffbb EEDATL, 0x26                                ; reg: 0x193
        movffbb EEDATH, 0x27                                ; reg: 0x193

        ; зачем косвенное обращение?
        clrf    FSR0H                                       ; reg: 0x005
        movlf   PIE1, FSR0L                                 ; reg: 0x004
        btfss   INDF0, RCIE                                 ; reg: 0x000
        goto    restore

        btfsc   PIR1, RCIF                                  ; необработанное
        goto    byte_arrived                                ; прерывание

restore:                                                    ; address: 0x0026
        ; восстанавливаемся
        movff   0x20, (Common_RAM + 7)                      ; reg: 0x077
        movff   0x21, (Common_RAM + 8)                      ; reg: 0x078
        movff   0x22, (Common_RAM + 9)                      ; reg: 0x079
        movff   0x23, (Common_RAM + 10)                     ; reg: 0x07a
        movffb  0x24, EEADRL                                ; reg: 0x191
        movffbb 0x25, EEADRH                                ; reg: 0x192
        movffbb 0x26, EEDATL                                ; reg: 0x193
        movffbb 0x27, EEDATH                                ; reg: 0x194
        ; выход из обработчика прерывания
        retfie

byte_arrived:                                               ; address: 0x003e
        ; зачем снова проверять?
        clrwdt
        btfss   PIR1, RCIF                                  ; reg: 0x011, bit: 5
        goto    $-2

        ; сохраняем RCSTA
        movffbb RCSTA, saved_rcsta                          ; reg: 0x028

        ; сохраняем принятый байт во временной переменной
        movfbf  RCREG, tmp_byte                             ; reg: 0x1f8

        BANKSEL saved_rcsta                                 ; unusable code
        btfss   saved_rcsta, OERR                           ; reg: 0x028
        goto    noerror

        BANKSEL RCSTA
        bcf     RCSTA, CREN                                 ; reg: 0x19d, bit: 4
        bsf     RCSTA, CREN                                 ; reg: 0x19d, bit: 4
        BANKSEL saved_rcsta

        ; если ошибка приема, то используем ошибочный байт!

noerror:
        ; перевод строки?
        movff   tmp_byte, received_byte                     ; reg: 0x06c
        IFNE    received_byte, '\n', not_a_line_feed

        ; буфер не пуст?
        movf    bufptr, F                                   ; reg: 0x02a
        btfss   STATUS, Z                                   ; reg: 0x003, bit: 2
        bsf     xflags, FLAG_GOT_RESP                       ; reg: 0x029

not_a_line_feed:                                            ; address: 0x0058
        ; код символа меньше, чем возврат каретки?
        IFL     received_byte, '\r', the_end

        ; буфер не пуст?
        btfsc   xflags, FLAG_GOT_RESP                       ; reg: 0x029
        goto    the_end

        ; принятый ответ на AT-команду сохраняется в буфере:
        ; buffer[bufptr] = received_byte;
        movlw   low LDM(buffer)
        addwf   bufptr, W                                   ; reg: 0x02a
        movwf   FSR0L                                       ; reg: 0x004
        movlf   high LDM(buffer), FSR0H                     ; reg: 0x005
        btfsc   STATUS, C                                   ; reg: 0x003, bit: 0
        incf    FSR0H, F                                    ; reg: 0x005
        movff   received_byte, INDF0                        ; reg: 0x000

        ; проверка на переполнение буфера
        ; переполнение все равно произойдет, т.к. BUFSZ выбрана неправильно
        IFG     bufptr, BUFSZ, buffer_full

        incf    bufptr, F                                   ; reg: 0x02a
        goto    the_end

buffer_full:                                                ; address: 0x006d
        ; используем обрезанный буфер
        bsf     xflags, FLAG_GOT_RESP                       ; reg: 0x029

the_end:                                                    ; address: 0x006e

        bcf     PIR1, RCIF                                  ; reg: 0x011, bit: 5
        gotop   restore

; end of interrupt handler

; строки AT-команд хранятся в запакованном виде
; (два 7-ми битных символа как 14-битное слово)
; файлы *.inc создаются программой encode_commands.c
; (директива DA делает то же, но использует обратный порядок байт)

command1:
        include "command1.inc"

command2:
        include "command2.inc"

command3:
        include "command3.inc"

command4:
        include "command4.inc"

command5:
        include "command5.inc"

command6:
        include "command6.inc"

command7:
        include "command7.inc"

command8:
        include "command8.inc"

command9:
        include "command9.inc"

; функция задержки

function_delay:                                             ; address: 0x00a5

        ; нулевая задержка?
        movlf   high LDM(delay_arg), FSR0H                  ; reg: 0x005
        movlf   low LDM(delay_arg), FSR0L                   ; reg: 0x004
        IFZ     INDF0, no_delay

label_008:                                                  ; address: 0x00ac

        movlf   2, m                                        ; reg: 0x078

label_009:                                                  ; address: 0x00ae
        movlf   0xbf, k                                     ; reg: 0x077

        clrwdt
        decfsz  k, F                                        ; reg: 0x077
        goto    $-2

        decfsz  m, F                                        ; reg: 0x078
        goto    label_009

        shortdelay 0x96
        goto    $+1                                         ; зачем?

        clrwdt
        decfsz  INDF0, F                                    ; reg: 0x000
        goto    label_008

no_delay:                                                   ; address: 0x00bd

        return

; функция мигания светодиодом

function_blink:                                             ; address: 0x00be

        clrf    blink_counter                               ; reg: 0x066

blink_more:                                                 ; address: 0x00bf

        movf    blink_arg, W                                ; reg: 0x065
        subwf   blink_counter, W                            ; reg: 0x066
        btfsc   STATUS, C                                   ; reg: 0x003, bit: 0
        goto    stop_blinking

        led_on
        delayb(150)

        led_off
        delayb(150)

        incf    blink_counter, F                            ; reg: 0x066
        goto    blink_more

stop_blinking:                                              ; address: 0x00d1

        led_on
        delayb(100)

        return

; передача AT команды

function_transmit:                                          ; address: 0x00d8

        movf    INTCON, W                                   ; reg: 0x00b
        bcf     INTCON, GIE                                 ; reg: 0x00b, bit: 7
        BANKSEL EECON1
        bsf     EECON1, EEPGD                               ; reg: 0x195, bit: 7
        bsf     EECON1, RD                                  ; reg: 0x195, bit: 0
        nop
        nop
        btfsc   WREG, GIE                                   ; reg: 0x009
        bsf     INTCON, GIE                                 ; reg: 0x00b, bit: 7

        ; распаковываем первый символ
        movf    EEDATL, W                                   ; reg: 0x193
        andlw   0x7f
        ; завершающий ноль?
        btfsc   STATUS, Z                                   ; reg: 0x003, bit: 2
        goto    end_of_string
        movwfb  txchar

        ; сохраняем адрес строки
        movffbb EEADRL, txadrl
        movffbb EEADRH, txadrh

        movf    txchar, W

        clrwdt
        btfss   PIR1, TXIF                                  ; reg: 0x011, bit: 4
        goto    $-2

        movwfb  TXREG                                       ; передаем первый

        ; восстанавливаем адрес строки
        movffbb txadrl, EEADRL
        movffbb txadrh, EEADRH

        movf    INTCON, W                                   ; reg: 0x00b
        bcf     INTCON, GIE                                 ; reg: 0x00b, bit: 7
        bsf     EECON1, EEPGD                               ; reg: 0x195, bit: 7
        bsf     EECON1, RD                                  ; reg: 0x195, bit: 0
        nop
        nop
        btfsc   WREG, GIE                                   ; reg: 0x009
        bsf     INTCON, GIE                                 ; reg: 0x00b, bit: 7

        ; распаковываем второй байт из 14-битного слова
        rlf     EEDATL, W                                   ; reg: 0x193
        rlf     EEDATH, W                                   ; reg: 0x194
        andlw   0x7f

        btfsc   STATUS, Z                                   ; завершающий ноль?
        goto    end_of_string

        movwfb  txchar

        movffbb EEADRL, txadrl
        movffbb EEADRH, txadrh

        movf    txchar, W

        clrwdt
        btfss   PIR1, TXIF                                  ; reg: 0x011, bit: 4
        goto    $-2

        movwfb  TXREG                                       ; передаем второй

        movffbb txadrl, EEADRL
        movffbb txadrh, EEADRH

        incf16  EEADRL                                      ; следующие 2 символа

        movlb   0
        goto    function_transmit

        movlb   3                                           ; unreachable code

; строка передана
end_of_string:                                              ; address: 0x0128

        movlb   0
        return

; инициализация GSM модуля SIM800C

init_sim800:                                                ; address: 0x012a

        blink(5)
        transmit('A')
        transmitb('T')
        transmitb('\r')

        blinkb(20)
        transmit('A')
        transmitb('T')
        transmitb('\r')

        blinkb(3)
        transmit_at_command(command1)   ; "ATE0+IPR=9600\r"
        blink(3)
        transmit_at_command(command2)   ; "AT+CPBS=\"SM\"\r"
        blink(3)
        transmit_at_command(command3)   ; "AT+CSCLK=1\r"
        blink(3)
        transmit_at_command(command4)   ; "AT+CGMR\r"
        blink(15)

        gotop   after_sim800_init

select_empty_phone_cell:                                    ; address: 0x018f
        ; выбор ячейки (от 1 до 3), куда будет записан номер хозяина
        clrf    cell                                        ; cell = 0;

        movlfb  EEPROM_PHONE1, EEADRL                       ; reg: 0x191
        bcf     EECON1, EEPGD                               ; reg: 0x195, bit: 7
        bsf     EECON1, RD                                  ; reg: 0x195, bit: 0

        ; первый байт 1-й ячейки пустой?
        IFNE    EEDATL, 0xff, maybe_two

        movlfb  1, cell                                     ; cell = 1;

        goto    select_empty_phone_cell_done
        movlb   3                                           ; unreachable

maybe_two:                                                  ; address: 0x019e

        movlf   EEPROM_PHONE2, EEADRL
        bcf     EECON1, EEPGD
        bsf     EECON1, RD

        ; первый байт 2-й ячейки пустой?
        IFNE    EEDATL, 0xff, maybe_three

        movlfb  2, cell                                     ; cell = 2;

        goto    select_empty_phone_cell_done
        movlb   3                                           ; unreachable code

maybe_three:                                                ; address: 0x01ab

        movlf   EEPROM_PHONE3, EEADRL
        bcf     EECON1, EEPGD
        bsf     EECON1, RD

        IFNE    EEDATL, 0xff, all_phone_cells_used

        movlfb  3, cell                                     ; cell = 3;

select_empty_phone_cell_done:                               ; address: 0x01b6

        movlb   3                                           ; unusable code

all_phone_cells_used:                                       ; address: 0x01b7

        movlp   0
        movlb   0
        goto    after_cell_search

; получает список звонков в формате:
; +CLCC: 1,1,0,0,0,"+70123456789",0
; см. SIM800 AT Commands Manual V1.11, стр. 75

; формат ответа не проверяется!
; номер берется просто по смещению CLCC_PHONE_OFFSET!

function_get_calls:                                         ; address: 0x01ba

        led_on
        bcf     LATA, DTR                                   ; сбросить DTR
        delayb(200)

        ; очищаем буфер ответа SIM800
        clrf    bufptr                                      ; reg: 0x02a
        bcf     xflags, FLAG_GOT_RESP                       ; reg: 0x029

        transmit_at_command(command7)                       ; "AT+CLCC\r"

        ; ожидаем ответ 5000 попугаев
        clrf    j

wait_clcc_result:                                           ; address: 0x01cb

        IFG     j, 99, have_result_or_timeout

        btfsc   xflags, FLAG_GOT_RESP                       ; reg: 0x029
        goto    have_result_or_timeout

        delay(50)

        incf    j, F                                        ; reg: 0x066
        goto    wait_clcc_result

have_result_or_timeout:                                     ; address: 0x01d6

        delay(200)

        return

; сбросить звонок

function_cancel_call:                                       ; address: 0x01da

        longdelay j, 2, 150
        transmit_at_command(command8)                       ; "ATH\r"
        longdelay j, 4, 250

        return

cell1_empty:                                                ; address: 0x01f0
        ; не записан ни один телефонный номер

        bcf     yflags, FLAG_GOT_PHONE                      ; reg: 0x065

        led_on
        bcf     LATA, DTR                                   ; сбросить DTR
        delayb(60)

        BANKSEL PIE1
        bsf     PIE1, RCIE                                  ; разрешать прерывания
        movlw   (1 << GIE) | (1 << PEIE)
        iorwf   INTCON, F                                   ; reg: 0x00b

        clrfb   bufptr                                      ; reg: 0x02a
        bcf     xflags, FLAG_GOT_RESP                       ; reg: 0x029

wait_first_call:                                            ; address: 0x01ff
        ; позвонивший номер будет записан в 1-ю ячейку

        delay(50)

        ; ответ получен?
        btfss   xflags, FLAG_GOT_RESP                       ; reg: 0x029
        goto    next_response

        ; при входящем звонке приходит "RING",
        ; но проверяется только "RI"
        IFNE    buffer,     'R', not_a_ring
        IFNE    buffer + 1, 'I', not_a_ring

        call    function_get_calls

        ; сохранение номера позвонившего (никакой проверки, что это номер)

        ; цикл для каждой цифры телефонного номера
        clrf    j                                           ; reg: 0x066

save_next_digit:                                            ; address: 0x020e

        IFG     j, PHONE_LEN, phone_saved

        ; преобразуем номер ячейки в ее адрес в EEPROM (1 - 0x10, 2 - 0x20, 3 - 0x30)
        swapf   cell, W                                     ; reg: 0x02e
        movwf   k                                           ; reg: 0x077
        movlw   0xf0
        andwf   k, F                                        ; reg: 0x077
        movf    k, W                                        ; reg: 0x077

        ; добавляем счетчик и получаем адрес текущей цифры в ячейке
        addwf   j, W                                        ; reg: 0x066
        movwf   numptr                                      ; reg: 0x068

        ; загружаем в FSR адрес текущей цифры в ответе на запрос AT+CLCC
        movlw   CLCC_PHONE_OFFSET
        addwf   j, W                                        ; reg: 0x066
        addlw   low LDM(buffer)
        movwf   FSR0L                                       ; reg: 0x004
        movlf   high LDM(buffer), FSR0H                     ; reg: 0x005
        btfsc   STATUS, C                                   ; reg: 0x003, bit: 0
        incf    FSR0H, F                                    ; reg: 0x005

        ; цифра ответа
        movff   INDF0, clcc_digit                           ; reg: 0x069

        ; запрещаем прерывания
        movff   INTCON, saved_intcon                        ; reg: 0x077
        bcf     INTCON, GIE                                 ; reg: 0x00b, bit: 7

        ; записываем в EEPROM
        movffb  numptr, EEADRL                                      ; reg: 0x191
        movffbb clcc_digit, EEDATL                                      ; reg: 0x193

        bcf     EECON1, EEPGD                               ; reg: 0x195, bit: 7
        bsf     EECON1, WREN                                ; reg: 0x195, bit: 2

        ; Flash programming unlock sequence (p. 108)
        movlf   0x55, EECON2
        movlf   0xaa, EECON2

        ; записываем в EEPROM и ждем, пока запишется
        bsf     EECON1, WR                                  ; reg: 0x195, bit: 1
        btfsc   EECON1, WR                                  ; reg: 0x015
        goto    $-1

        bcf     EECON1, WREN                                ; reg: 0x015
        movf    saved_intcon, W                             ; reg: 0x077
        iorwf   INTCON, F                                   ; reg: 0x00b

        BANKSEL j
        incf    j, F                                        ; reg: 0x066
        goto    save_next_digit

phone_saved:                                                ; address: 0x023c
        ; телефон позвонившего запомнен

        call    function_cancel_call                        ; сбросить звонок

        bsf     yflags, FLAG_GOT_PHONE                      ; reg: 0x065

        goto    next_response

not_a_ring:                                                 ; address: 0x023f

        clrf    bufptr                                      ; reg: 0x02a
        bcf     xflags, FLAG_GOT_RESP                       ; reg: 0x029

next_response:                                              ; address: 0x0241
        ; ожидать первого звонка хозяина
        btfss   yflags, FLAG_GOT_PHONE                      ; reg: 0x065
        goto    wait_first_call

        ; телефон хозяина сохранен - нормальный режим работы
        gotop   cell1_contains_number

long_button_press:                                          ; address: 0x0245

; стирает EEPROM 0x10...0x2f, а третий номер в 0x30 остается

        movlf   EEPROM_PHONE1, erase_address                ; reg: 0x065

erase_byte:                                                 ; address: 0x0247

        IFG     erase_address, (EEPROM_PHONE3 - 1), erase_done

        ; запрещаем прерывания
        movff   INTCON, saved_intcon                        ; reg: 0x077
        bcf     INTCON, GIE                                 ; reg: 0x00b, bit: 7

        ; готовим байт для записи (стр. 105)
        movffb  erase_address, EEADRL                       ; reg: 0x191
        movlf   0xff, EEDATL                                ; reg: 0x193
        ;bcf     EECON1, CFGS                               ; небыло в оригинале
        bcf     EECON1, EEPGD                               ; Point to DATA memory
        bsf     EECON1, WREN                                ; разрешить запись

        ; Flash programming unlock sequence (p. 108)
        movlf   0x55, EECON2
        movlf   0xaa, EECON2

        ; стираем и ждем, пока сотрется
        bsf     EECON1, WR                                  ; начало записи
        btfsc   EECON1, WR                                  ; проверка завершения
        goto    $-1

        bcf     EECON1, WREN                                ; запретить запись

        movf    saved_intcon, W                             ; восстанавливаем
        iorwf   INTCON, F                                   ; прерывания

        BANKSEL erase_address
        incf    erase_address, F
        goto    erase_byte                                  ; стираем следующий байт

erase_done:                                                 ; address: 0x0262

        clrf    i                                           ; reg: 0x065

blink_40_times:                                             ; address: 0x0263

        IFG     i, 39, blink_forever2

        led_on
        delayb(50)
        led_off
        delayb(50)

        incf    i, F                                        ; reg: 0x065
        goto    blink_40_times

blink_forever2:                                             ; address: 0x0275

        gotop   blink_forever1

; входящий звонок

incoming_call:                                              ; address: 0x0277

        led_on
        bcf     LATA, DTR                                   ; сбросить DTR
        delayb(100)

        BANKSEL PIE1
        bsf     PIE1, RCIE                                  ; reg: 0x091, bit: 5
        movlw   (1 << GIE) | (1 << PEIE)
        iorwf   INTCON, F                                   ; reg: 0x00b
        movlb   0

        call    function_get_calls

        ; принято 2 символа?
        IFNE    bufptr, 2, search_start

        ; подразумевается "\r\n", но не проверяется
        movlf   FALSE, search_result

        goto    search_done

search_start:                                               ; address: 0x028b

        ; текущая ячейка
        clrf    cur_cell                                    ; reg: 0x067

next_phone_cell:                                            ; address: 0x028c

        IFG     cur_cell, MAXCELL, last_cell

        clrf    digit_index                                 ; reg: 0x066

compare_next_digit:                                         ; address: 0x0291

        IFG     digit_index, PHONE_LEN, compare_next_phone

        ; адрес текущей цифры номера в буфере
        movlw   CLCC_PHONE_OFFSET
        addwf   digit_index, W                              ; reg: 0x066
        addlw   low LDM(buffer)
        movwf   FSR0L                                       ; reg: 0x004
        movlw   high LDM(buffer)
        movwf   FSR0H                                       ; reg: 0x005
        btfsc   STATUS, C                                   ; reg: 0x003, bit: 0
        incf    FSR0H, F                                    ; reg: 0x005

        ; текущая цифра
        movff   INDF0, cur_dig

        ; преобразовать номер ячейки в ее адрес
        movlw   1
        addwf   cur_cell, W                                 ; reg: 0x067
        movwf   k                                           ; reg: 0x077
        swapf   k, F                                        ; reg: 0x077
        movlw   0xf0
        andwf   k, F                                        ; reg: 0x077
        movf    k, W                                        ; reg: 0x077
        addwf   digit_index, W                              ; reg: 0x066
        movwf   clcc_digit                                  ; reg: 0x069
        movffb  clcc_digit, EEADRL                          ; reg: 0x191

        ; читаем текущую цифру текущей ячейки из EEPROM
        bcf     EECON1, EEPGD                               ; reg: 0x195, bit: 7
        bsf     EECON1, RD                                  ; reg: 0x195, bit: 0
        movf    EEDATL, W                                   ; reg: 0x193
        movlb   0

        ; сравниваем с текущей цифрой номера позвонившего
        subwf   cur_dig, W                                  ; reg: 0x068
        btfss   STATUS, Z                                   ; reg: 0x003, bit: 2
        goto    compare_next_phone

        ; следующая цифра
        incf    digit_index, F                              ; reg: 0x066
        goto    compare_next_digit

compare_next_phone:                                         ; address: 0x02b4
        ; последняя цифра номера?
        IFNE    digit_index, (PHONE_LEN + 1), next_phone_cell_continue

        ; все цифры совпали
        movlf   TRUE, search_result
        goto    search_done

next_phone_cell_continue:                                   ; address: 0x02bb
        ; следующая ячейка
        incf    cur_cell, F                                 ; reg: 0x067
        goto    next_phone_cell

last_cell:                                                  ; address: 0x02bd

        IFNE    cur_cell, CELLS, search_done

        ; номер не обнаружен ни в одной ячейке
        movlf   FALSE, search_result                        ; reg: 0x078

search_done:                                                ; address: 0x02c3

        bcf     yflags, FLAG_GOT_PHONE                      ; reg: 0x065

        btfsc   search_result, 0                            ; reg: 0x078
        ; звонящий - хозяин
        bsf     yflags, FLAG_GOT_PHONE                      ; reg: 0x065

        btfss   yflags, FLAG_GOT_PHONE                      ; reg: 0x065
        goto    cancel_call

        ; звонит хозяин - поднять трубку
        transmit_at_command(command9)   ; "ATA\r"
        delay(200)
        transmit_at_command(command5)   ; "AT+CMICBIAS=1\r"
        delay(200)
        transmit_at_command(command6)   ; "AT+CMIC=2,15\r"
        delay(200)

        goto    call_accepted_or_not

cancel_call:                                                ; address: 0x02e7

        call    function_cancel_call

        longdelay j, 2, 250

call_accepted_or_not:                                       ; address: 0x02ef
        ; приняли или сбросили звонок - в любом случае попадаем сюда
        gotop    call_accepted_or_not2

check_clcc_len:                                             ; address: 0x02f1

        delay(50)

        incf    wait_clcc, F
        IFL     wait_clcc, 30, main_cycle5

        clrf    wait_clcc                                   ; reg: 0x02c

        call    function_get_calls                          ; список звонков

        IFG     bufptr, CLCC_MIN_RESPONSE, clcc_length_ok

        bcf     xflags, FLAG_CLCC_OK                        ; reg: 0x029

        clrf    i                                           ; reg: 0x065

blink_50_times:                                             ; address: 0x0301

        IFG     i, 49, main_cycle4

        led_off
        delayb(50)

        led_on
        delayb(50)

        incf    i, F                                        ; reg: 0x065
        goto    blink_50_times

main_cycle4:                                                ; address: 0x0313

        led_off
        bsf     LATA, DTR                                   ; поднять DTR

        goto    main_cycle3
        movlb   0

clcc_length_ok:                                             ; address: 0x0318
        delay(100)

main_cycle5:                                                ; address: 0x031b

        movlb   2                                           ; unusable code

main_cycle3:                                                ; address: 0x031c

        movlp   0
        movlb   0
        goto    main_cycle2

reset_vector:                                               ; address: 0x031f

        ; очистка 87+1 байт памяти 0x20...0x76 и 0x77
        movlf   0x57, k                                     ; reg: 0x077
        movlf   0x20, FSR0L                                 ; reg: 0x004
        movlf   0, FSR0H                                    ; reg: 0x005

label_068:                                                  ; address: 0x0325
        clrf    INDF0                                       ; reg: 0x000
        incf    FSR0L, F                                    ; reg: 0x004
        decfsz  k, F                                        ; reg: 0x077
        goto    label_068

        ; инициализация
        clrf    (Common_RAM + 8)                            ; reg: 0x078
        clrf    (Common_RAM + 9)                            ; reg: 0x079
        clrf    (Common_RAM + 10)                           ; reg: 0x07a
        clrf    (Common_RAM + 11)                           ; reg: 0x07b
        clrf    (Common_RAM + 12)                           ; reg: 0x07c
        clrf    (Common_RAM + 13)                           ; reg: 0x07d
        clrf    (Common_RAM + 14)                           ; reg: 0x07e

        ; еще 32 байта
        movlf   32, k                                       ; reg: 0x077
        movlf   0xa0, FSR0L                                 ; reg: 0x004
        movlf   0, FSR0H                                    ; reg: 0x005
label_069:                                                  ; address: 0x0336
        clrf    INDF0                                       ; reg: 0x000
        incf    FSR0L, F                                    ; reg: 0x004
        decfsz  k, F                                        ; reg: 0x077
        goto    label_069

        ; инициализация регистров
        clrf    0x20                                        ; reg: 0x020

        ; bit 7: 4xPLL disabled
        ; bit 6-3: 1110 = 8 MHz or 32 MHz HF(see Section 5.2.2.1 “HFINTOSC”)
        ; bit 1-0: 1x = Internal oscillator block
        movlfb  0x72, OSCCON

        clrfb   saved_rcsta                                 ; reg: 0x028

        ; 0 = 8-bit Baud Rate Generator is used
        BANKSEL BAUDCON
        bcf     BAUDCON, BRG16

        movlf   (F_OSC/(BAUD/64)), SPBRGL                   ; reg: 0x19b

        ; CSRC: synchronous mode: master (p. 288)
        ; TXEN: transmit enabled
        ; TRMT: Transmit Shift Register Status bit: TSR empty
        movlf   (1 << CSRC) | (1 << TXEN) | (1 << TRMT), TXSTA

        ; asynchronous (p. 284)
        ; SPEN: enable EUSART
        movlf   (1 << SPEN) | (1 << CREN), RCSTA            ; reg: 0x19d

        clrfb   0x62                                        ; reg: 0x062
        clrf    0x61                                        ; reg: 0x061

        clrfb   ANSELA                                      ; reg: 0x18c

        clrfb   CM1CON1                                     ; reg: 0x112
        clrf    CM1CON0                                     ; reg: 0x111

        ; bit 7: 4xPLL disabled
        ; bit 6-3: 1110 = 8 MHz or 32 MHz HF(see Section 5.2.2.1 “HFINTOSC”)
        ; bit 1-0: 00 = Clock determined by FOSC<2:0> in Configuration Word 1
        movlfb  0x70, OSCCON                                ; ускоряемся

        ; p. 101
        ; SWDTEN: Software Enable/Disable for Watchdog Timer bit: WDT on
        ; 01001 = 1:16384 (Interval 512 ms typ)
        movlf   (9 << 1) | (1 << SWDTEN), WDTCON            ; включен

        ; p. 123: WPUA: WEAK PULL-UP PORTA REGISTER
        ; WPUA3: RA3 (BUTTON)
        movlfb  (1 << WPUA3), WPUA                          ; подтяжка кнопки

        BANKSEL OPTION_REG
        bcf     OPTION_REG, NOT_WPUEN                       ; reg: 0x095, bit: 7
        bcf     ADCON1, ADPREF0                             ; reg: 0x09e, bit: 0
        bcf     ADCON1, ADPREF1                             ; reg: 0x09e, bit: 1

        movlfb  0, ANSELA

        BANKSEL ADCON0
        bcf     ADCON0, ADON                                ; reg: 0x09d, bit: 0

        clrfb   CM1CON1                                     ; reg: 0x112
        clrf    CM1CON0                                     ; reg: 0x111
        clrf    FVRCON                                      ; reg: 0x117

        ; входы: RA5 (RING), RA3 (BUTTON), RA1 (RX)
        movlfb  (1 << TRISA5) | (1 << TRISA3) | (1 << TRISA1), TRISA

        clrfb   LATA                                        ; DTR, LED

        movfb   OPTION_REG                                  ; reg: 0x095
        ; TMR0CS: 0 = Internal instruction cycle clock (FOSC/4)
        ; TMR0SE: 0 = Increment on low-to-high transition on RA2/T0CKI pin
        andlw   ~((1 << PSA) | (1 << TMR0CS) | (1 << TMR0SE))
        ; 1 = Prescaler is not assigned to the Timer0 module
        iorlw   (1 << PSA)
        movwf   OPTION_REG                                  ; reg: 0x095

        BANKSEL APFCON
        bcf     APFCON, T1GSEL                              ; reg: 0x11d, bit: 3

        ; page 174:
        ; The oscillator circuit is enabled by setting the
        ; T1OSCEN bit of the T1CON register. The oscillator will
        ; continue to run during Sleep.

        ; page 180:
        ; T1SYNC: Timer1 External Clock Input Synchronization Control bit
        ; 1 = Do not synchronize external clock input

        ; T1CKPS<1:0>: Timer1 Input Clock Prescale Select bits
        ; 01 = 1:2 Prescale value
        movlfb  (0x01 << 4) | (1 << 2) | (1 << TMR1ON), T1CON
        clrf    T1GCON                                  ; Timer1 Gate disabled

        movlf   0, tmp_byte                                 ; reg: 0x078

        ; таймер 2 не используется
        movwf   T2CON
        movlf   0, PR2

        longdelay i, 8, 250

        goto    init_sim800

after_sim800_init:                                          ; address: 0x0382

        goto    select_empty_phone_cell

after_cell_search:                                          ; address: 0x0383

        decfsz  cell, W                                     ; reg: 0x02e
        goto    cell1_contains_number
        goto    cell1_empty

cell1_contains_number:                                      ; address: 0x0386

        led_off
        bsf     LATA, DTR                                   ; поднять DTR

main_cycle:                                                 ; address: 0x0389

        clrwdt

        shortdelay 206

        ; bit 7: 4xPLL disabled
        ; bit 6-3: 1110 = 8 MHz or 32 MHz HF(see Section 5.2.2.1 “HFINTOSC”)
        ; bit 1-0: 00 = Clock determined by FOSC<2:0> in Configuration Word 1
        movlfb  0x70, OSCCON                                ; ускоряемся

        clrwdt

        BANKSEL xflags
        btfsc   xflags, FLAG_CLCC_OK                        ; reg: 0x029
        goto    call_accepted_or_not3

        btfsc   PORTA, BUTTON                               ; reg: 0x00c, bit: 3
        goto    not_pressed

        ; нажата кнопка
        incf16  holdbtn_l

        goto    button_is_holding

not_pressed:                                                ; address: 0x039b
        ; не нажата
        clrf    holdbtn_h                                   ; reg: 0x064
        clrf    holdbtn_l                                   ; reg: 0x063

button_is_holding:                                          ; address: 0x039d
        ; кнопка давно нажата?
        IFNZ    holdbtn_h, long_button_press1
        IFL     holdbtn_l, 200, not_a_long_press

long_button_press1:                                         ; address: 0x03a4

        goto    long_button_press

blink_forever1:                                             ; address: 0x03a5

        clrf    holdbtn_h                                   ; reg: 0x064
        clrf    holdbtn_l                                   ; reg: 0x063

blink_forever:                                              ; address: 0x03a7

        movlf   4, blink_arg                                ; reg: 0x065
        call    function_blink
        goto    blink_forever

not_a_long_press:                                           ; address: 0x03ab

        btfsc   PORTA, RING                                 ; reg: 0x00c, bit: 5
        goto    ring_is_high

        incf    ringtime, F                                 ; reg: 0x02b
        IFG     ringtime, 1, incoming_call

call_accepted_or_not2:                                      ; address: 0x03b2

        goto    call_accepted_or_not3

ring_is_high:                                               ; address: 0x03b3

        clrf    ringtime                                    ; reg: 0x02b

call_accepted_or_not3:                                      ; address: 0x03b4

        btfsc   xflags, FLAG_CLCC_OK                        ; reg: 0x029
        goto    check_clcc_len1

        BANKSEL PIE1
        bcf     PIE1, RCIE                                  ; reg: 0x091, bit: 5
        bcf     INTCON, PEIE                                ; reg: 0x00b, bit: 6

        bcf     INTCON, GIE                                 ; reg: 0x00b, bit: 7
        btfsc   INTCON, GIE                                 ; reg: 0x00b, bit: 7
        goto    $-2

        led_off
        bsf     LATA, DTR                                   ; поднять DTR


        ; bit 7: 4xPLL disabled
        ; bit 6-3: 0101 = 125 kHz MF
        ; bit 1-0: 00 = Clock determined by FOSC<2:0> in Configuration Word 1
        movlfb  0x28, OSCCON                                ; замедляемся

        goto    main_cycle1
        movlb   0

check_clcc_len1:                                            ; address: 0x03c4

        goto    check_clcc_len

main_cycle2:                                                ; address: 0x03c5

        movlb   1                                           ; unusable code

main_cycle1:                                                ; address: 0x03c6

        movlb   2
        goto    main_cycle
        sleep

;===============================================================================
; EEDATA area

        ; eeprom

        org     __EEPROM_START                              ; address: 0xf000

        db      0x00
        db      0x00

        end

Исходник encode_commands.c

Уберите комментарии, чтобы воспроизвести авторскую ошибку и получить идентичную прошивку:

#include <stdio.h>
#include <string.h>

// PIC word is 14-bit wide
void encode_command(const char *path, const unsigned char *cmd)
{
    unsigned char a, b = 0;
    unsigned short w;
    FILE *fp = fopen(path, "w+");
    if (fp) {
        for (; *cmd; cmd += 2) {
            a = (*cmd & 0x7f) | (cmd[1] << 7);
            b = (cmd[1] & 0x7f) >> 1;
            w = (b << 8) | a;
            fprintf(fp, "        dw 0x%04X\n", (unsigned) w);
            if (!cmd[1])
                break;
        }
        if (b) {
            // bug?
            //if (!strcmp(path, "command5.inc"))
            //    fprintf(fp, "        dw 0x0100\n");
            //else
                fprintf(fp, "        dw 0x0000\n");
        }
        fclose(fp);
    } else {
        fprintf(stderr, "error: can't open \"%s\" for writing\n", path);
    }
}

int main()
{
    encode_command("command1.inc", "ATE0+IPR=9600\r");
    encode_command("command2.inc", "AT+CPBS=\"SM\"\r");
    encode_command("command3.inc", "AT+CSCLK=1\r");
    encode_command("command4.inc", "AT+CGMR\r");
    encode_command("command5.inc", "AT+CMICBIAS=1\r");
    encode_command("command6.inc", "AT+CMIC=2,15\r");
    encode_command("command7.inc", "AT+CLCC\r");
    encode_command("command8.inc", "ATH\r");
    encode_command("command9.inc", "ATA\r");
    return 0;
}

Продолжения не будет. Мне скучно.

© Copyright 2025 Badradio. All rights reserved.

Все права защищены. Копирование материалов сайта запрещено. При цитировании ставить ссылку на первоисточник.