Восстановление исходников глючного GSM микрофона на SIM900 и PIC24FJ64GA004 от Tired/STG
Прошло 14 лет, исписано 30 страниц форума, но этот код с VRTP так и остался кривым.
Что будет, если не читать книги по программированию Кнута, Вирта и Дейкстры? Результат перед вами. Как говорится в таких случаях, не вздумайте повторять увиденное дома.
Исходники GSM радиожучков на других модулях: SIM300 (PIC16F648A), SIM800 (PIC12F1822, очень кривой).
Оригинальная статья (без исходников, разные версии прошивки):
https://web.archive.org/web/20221208224213/https://vrtp.ru/index.php?showtopic=20314
Похабная прошивка от Tired (№844) и STG (№176) имеет классические ошибки безопасности. Прочтите комментарии в листинге, чтобы научиться их избегать.
Прекратите повторять конструкции с закрытым исходным кодом или без расчетов. Вы не знаете, какие ошибки, глюки и бэкдоры в них содержатся.
Вот только самые грубые косяки этого GSM радиомикрофона на SIM900:
- Неправильно разведена печатная плата (уже проходили в «Трёшке»)
- Нет автоматической перезагрузки GSM модуля при потере сети (виснет в течение суток)
- Нет развязки SIM900 по питанию с помощью дросселя (глюки и жужжание)
- Контролирующие шлейфы входы не зашунтированы конденсаторами (реагирует на любые импульсные помехи, в том числе от себя)
- По достижении BUFSZ (127) символов буфер перезаписывается. Например, подобрав длину SMS (может быть до 160 байт плюс заголовки SIM900) с текстом “RING”, можно заставить устройство подумать, будто идет входящий вызов. Кто раскрутит это до эксплуатируемой уязвимости?
- Ответы SIM900 проверяются «приблизительно»: не на равенство, а на вхождение подстроки
- Успешность дозвона и отправки SMS не учитывается
- Нестандартные ситуации (отсутствует SIM-карта, удалены номера) не отслеживаются
- Если ПИН-код неправильный, устройство введет его несколько раз, и карта заблокируется!
Отсутствует резистор R2 для вывода MCLR контроллера (см. стр. 17 даташита).
Кроме того, даже не имея исходников и не зная, что за «шедевр» перед ними, форумчане VRTP выявили следующие недостатки:
- При обрыве шлейфа идет звонок за звонком, высаживается батарея
- Владелец никак не узнает про обрыв связи в результате зависания GSM модуля, а это случается регулярно
- Наличие средств на счету не отслеживается
Кстати, на форуме VRTP есть множество ценнейших комментариев по конструированию GSM радиомикрофонов, но все они были проигнорированы. Лишний раз убеждаешся, что цель VRTP — не помочь и научить, а подставить и обломать.
Примечательно, что среди изученных мною прошивок ни в одной не приняты меры против «продвинутого» нарушителя, который попытается имитировать садящуюся батарею или выход ее из строя, вызвать ложные срабатывания или зависание устройства. Ничто не позволит различить: ошибку в прошивке, отказ GSM модуля, неполадки на стороне оператора, случаи медленно и внезапно севшей батареи и неполадки с питанием от постороннего вмешательства.
Начинающим следует изучить несколько схем и прошивок промышленных ОПС, прежде чем изобретать свой велосипед. Чтение руководства по процессору, даташитов, аппнот и доступного открытого кода — обязательно.
Осторожно! В листинге еще масса несоответствий, проверяйте все вызывающие подозрение места. Некоторые участки дизассемблер naken_util дизассемблировать не смог.
Оригинальный исходник написан на Си.
mic.asm (первая версия прошивки)
; Disassembler: naken_util - by Michael Kohn
; Joe Davisson
; Web: https://www.mikekohn.net/
; Email: mike@mikekohn.net
;
; Version: May 25, 2025
;
; Loaded mic.hex of type hex / dspic from 0x0000 to 0x157ff
; Type help for a list of commands.
;
; Addr Opcode Instruction Cycles
; ------- ---------- ---------------------------------- ------
; Compilation:
; naken_asm -o mic_recompiled.hex -type hex mic.asm
; Также см. https://www.watelectronics.com/sim900-module/
; PIC24FJ64GA004
.PIC24
;===============================================================================
#define TRUE 1
#define FALSE 0
; смещение номера в ответе на AT+CLCC
#define CLCC_PHONE_OFFSET 18
; число символов в телефонном номере
#define PHONE_LEN 39
BUFSZ equ 127
; получить адрес переменной в Linear Data Memory
LINEAR_DATA_MEMORY equ 0x8000
#define LDM(var) #(var) + LINEAR_DATA_MEMORY
; alternate interrupt vector table (59)
ALTIVT_START equ 0x0104
; маска для AND
#define MASK(bit) (1 << (bit))
; если операнд - байт
#define HIREG(reg) (reg + 1)
#define HIBIT(bit) (bit - 8)
#define HIMASK(bit) MASK(HIBIT(bit))
#include "PIC24FJ64GA004.inc"
;===============================================================================
; DATA address definitions
buffer equ 0x0c11 ; принятый ответ на AT-команду
;after equ 0x0c90 ; первый байт после буфера
phone1 equ 0x1157 ; номера из записной книги SIM
phone2 equ 0x117f
phone3 equ 0x11a7
callee equ 0x11cf ; номер звонящего абонента
;cafter equ 0x11f6 ; первый байт после номера
active1 equ 0x11f8 ; флаги наличия номеров в записной книге SIM
active2 equ 0x11fa
active3 equ 0x11fc
unknown equ 0x11fe
state equ 0x1202 ; конечный автомат
#define STATE_RESET 4
#define STATE_GSMON 5
#define STATE_PIN_OK 6
bufptr equ 0x1208 ; индекс свободного байта в буфере
tm4h equ 0x121C ; ограничитель времени звонка?
var_a equ 0x1230 ; ? - только чтение
lowbat equ 0x1232 ; батарея разряжена
pollcnt equ 0x1234 ; ? - только запись
calling equ 0x1236 ; активен звонок
alert equ 0x1238 ; шлейф сигнализации в обрыве
charged equ 0x123a ; аккумулятор полностью заряжен
lbsent equ 0x123c ; сообщение о разрядившейся батарее отправлено
temp equ 0x123e ; временная переменная для многих операций
STKPTR equ 0x27f0 ; стек
; indirect
i equ 20
j equ 14
k equ 16
m equ 10
pbk equ 18 ; прочитанный из записной книги номер
cbcpos equ 12
search_result equ 6 ; номер позвонившего обнаружен на SIM?
callee_offset equ 8
;===============================================================================
; PINS usage definitions
LED_PORT equ PORTB ; светодиод
LED equ RB2
MIC_PORT equ PORTB ; управление чувствительностью микрофона
MICLEVEL equ RB0
SENSOR_PORT equ PORTB ; шлейф сигнализации
SENSOR equ RB8
CHARGE_PORT equ PORTC ; контроллер заряда
CHARGE equ RC1
PWRKEY_PORT equ PORTC ; кнопка включения SIM900
PWRKEY equ RC9 ; pin 1 of SIM900, pin 5 of PIC
STATUS_PORT equ PORTB ; SIM900 STATUS
STATUS equ RB14
; PIC - SIM900
; 2 RC6 - 10 RXD
; 3 RC7 - 9 TXD
DTR_PORT equ PORTC ; SIM900 DTR
DTR equ RC8 ; pin 3 of SIM900, pin 4 of PIC
#include "macro.asm"
.org 0
goto main
; таблица векторов прерываний
#include "ivt.asm"
.org 0x200
main:
mov #temp, w15
; стек
mov #STKPTR, w0
mov w0, SPLIM
nop
rcall setup1
; инициализированные переменные
invoke2(intialize, #initialized_data, #0)
; ?
mov #0, w0
cp0 w0
bra z, skip
call 0
skip:
call start
sleep_
reset
setup1:
.scope
; Program Space Visibility in Data Space Enable bit:
; Program space is not visible in data space
bclr CORCON, #PSV
; 8 trap vectors + interrupt vectors
mov #(2 * (8 + 14) + ALTIVT_START), w0
cp0 w0
bra z, done
mov #0, w0
mov w0, PSVPAG
; Program space is visible in data space
bset CORCON, #PSV
done:
return
.ends
#include "idata.asm"
#include "at.asm"
start:
lnk #0x00
; A/D PORT CONFIGURATION REGISTER (199)
setm w0
mov w0, AD1PCFG
; Peripheral Output Function is Assigned to RP22 Output Pin bits (123)
bset RPOR11, #0
bset RPOR11, #1
bclr RPOR11, #2
bclr RPOR11, #3
bclr RPOR11, #4
; Assign UART1 Receive (U1RX) to the Corresponding RPn Pin bits
bset RPINR18, #0
bset RPINR18, #1
bset RPINR18, #2
; U1RXR3
bclr RPINR18, #3
bset RPINR18, #4
; TON: Timery On bit - 1 = Starts 16-bit Timery
; Timery Input Clock Prescale Select bits: 11 = 1:256
mov #(TON | TCKPS1 | TCKPS0), w0
mov w0, T4CON
; период таймера 4
movlf(15200, PR4)
mov #(TON | TCKPS1 | TCKPS0), w0
mov w0, T3CON
; период таймера 3
movlf(760, PR3)
bclr IFS1, #T4IF
; T4IE: Timer4 Interrupt Enable bit
bset IEC1, #T4IE
movlf(STATE_RESET, state)
delay(#1000)
bclr TRISB, #TRISB2
; Change Notice 4 Pull-Up Enable
bset CNPU1, #CN4PUE
loop:
rcall SIM900_power
rcall function_sim900_init
rcall function_register
bra loop
#include "atoi.asm"
interrupt_timer3: ; 0x05f8
.scope
push.d w0
push w2
push PSVPAG
mov #0, w0
mov w0, PSVPAG
lnk #0
bclr IFS0, #T3IF ; прерывание обработано
IFNE(calling, TRUE, exit) ; если соединение не активно, выход
IBLINK
exit:
ulnk
pop PSVPAG
pop w2
pop.d w0
retfie
.ends
interrupt_timer4:
.scope
push.d w0
push w2
push PSVPAG
mov #0, w0
mov w0, PSVPAG
lnk #0x00
bclr IFS1, #T4IF ; прерывание обработано
; увеличение счетчика времени в tm4h:initialized_data
inc16(tm4h, initialized_data)
IFNE(var_a, TRUE, exit) ; мигаем, если var_a не ноль
IBLINK
exit:
ulnk
pop PSVPAG
pop w2
pop.d w0
retfie
.ends
#include "utils.asm"
#include "uart.asm"
; кнопка включения модуля SIM900
SIM900_power:
.scope
lnk #0x00
IFNE(state, STATE_RESET, exit)
delay(#400)
bclr TRISC, #TRISC9
bclr PWRKEY_PORT, #PWRKEY
delay(#2000)
bset PWRKEY_PORT, #PWRKEY
bset TRISB, #TRISB14
wait: ; ожидание SIM900 STATUS (66)
mov.b HIREG(STATUS_PORT), wreg
and.b #HIMASK(STATUS), w0
CP0_W0
bra z, wait
rcall setup_uart
movlf(STATE_GSMON, state)
exit:
ulnk
return
.ends
function_sim900_init:
.scope
lnk #0x02
IFNE(state, STATE_GSMON, exit)
movlf(0, lbsent)
loop:
ATCMD(at) ; отправляем "AT"
blink ; мигаем
blink
unless_buffer_contains(ok + 1, retry)
rcall clear_buffer
bra autobauded ; ответ содержит "OK"
retry:
ATCMD(at)
blink
blink
blink
blink
unless_buffer_contains(ok + 1, more)
rcall clear_buffer
bra autobauded
more:
ATCMD(at)
delay(#250)
led_off
delay(#250)
blink
blink
blink
blink
blink
blink
blink
unless_buffer_contains(ok + 1, loop)
rcall clear_buffer
autobauded: ; получен "OK"
clr w0
mov w0, [w14]
repeat_pin:
ATCMD(at_cpin) ; нужен ли SIM PIN?
blink
blink
if_buffer_contains(cpin_sim_pin + 1, pin_ready_or_sim_pin)
unless_buffer_contains(cpin_ready, repeat_pin)
pin_ready_or_sim_pin:
unless_buffer_contains(cpin_sim_pin + 1, enter_pin)
movlo(TRUE, 0) ; требуется ввод пин-кода
enter_pin:
rcall clear_buffer
mov #TRUE, w0 ; установлен пин-код?
subr w0, [w14], [w15]
bra z, repeat_enter_pin
bra pin_accepted_or_no_pin
; если пин-код неправильный, зачем вводить его снова?
; SIM-карта будет заблокирована!
repeat_enter_pin:
ATCMD0(at_cpin_2000 + 1) ; вводим пин-код "2000"
rcall clear_buffer
delay(#1000)
if_buffer_contains(ok + 1, pin_accepted_or_no_pin)
bra repeat_enter_pin
pin_accepted_or_no_pin: ; пин-код принят
movlf(STATE_PIN_OK, state)
exit:
ulnk
return
.ends
function_register:
lnk #0x16
IFNE(state, STATE_PIN_OK, wrong_state)
; ожидание регистрации в сети
registration_loop:
ATCMD(at_creg)
blink
unless_buffer_contains(creg_01 + 1, registration_loop)
delay(#500)
; чтение трех номеров из телефонной книги SIM карты
rcall clear_buffer
movlf(FALSE, active1)
movlf(FALSE, active2)
movlf(FALSE, active3)
ATCMD0(at_cpbs_sm)
delay(#2000)
read_parse_phonebook(at_cpbr_1 + 1, phone1, active1)
read_parse_phonebook(at_cpbr_2, phone2, active2)
read_parse_phonebook(at_cpbr_3, phone3, active3)
ATCMD0(at_cmgf_1) ; переключаем SIM900 в текстовый режим
led_on ; мигаем
delay(#2000)
led_off
delay(#2000)
; пин MICLEVEL управляет выбором чувствительности микрофона
mov.b MIC_PORT, wreg
and.b w0, #MASK(MICLEVEL), w0
CP0_W0
bra nz, mic_sensitivity_high
ATCMD0(at_cmic00) ; установить низкую чувствительность микрофона
mic_sensitivity_high:
mov.b MIC_PORT, wreg
and.b w0, #MASK(MICLEVEL), w0
CP0_W0
bra z, mic_sensitivity_low
ATCMD0(at_cmic015) ; установить высокую чувствительность микрофона
mic_sensitivity_low:
; отправка SMS по каждому номеру
; успешность отправки SMS не проверяется!
IFNE(active1, TRUE, skip_number1_sms_ready)
delay(#1000)
SEND_SMS(#phone1, Ready + 1, i)
skip_number1_sms_ready:
IFNE(active2, TRUE, skip_number2_sms_ready)
delay(#5000)
SEND_SMS(#phone2, Ready + 1, i)
skip_number2_sms_ready:
IFNE(active3, TRUE, skip_number3_sms_ready)
delay(#5000)
SEND_SMS(#phone3, Ready + 1, i)
skip_number3_sms_ready:
delay(#1000)
bclr IFS1, #CNIF ; Change Notice Interrupt Flag
mov #HIREG(IPC4), w1 ; INTERRUPT PRIORITY CONTROL REGISTER 4
mov.b [w1], w1
mov_w0(0x8f) ; clear CNIP<2:0>
and.b w1, w0, w0
mov_w1(0x40) ; set CNIP<2:0> to 100 - priority 4? (82)
ior.b w0, w1, w0
mov.b wreg, HIREG(IPC4)
bset IEC1, #T1IE ; Timer1 Interrupt Enable bit
bset CNEN2, #CN22IE
bset CNEN1, #CN8IE
bset CNPU1, #CN8PUE
movlf(FALSE, lowbat)
movlf(2, pollcnt)
delay(#1000)
for (k, repeat, repeat)
; k++
inco(w14, k)
mov [w14+k], w0
sub w0, #20, [w15]
bra le, local11
movlo(0, k)
; получить заряд батареи
ATCMD(at_cbc + 1)
delay(#500)
; проверяем наличие чего-то, похожего на ответ
unless_buffer_contains(cbc, skip_sms_lowbat)
; позиция "ответа"
invoke_strnpos(cbc)
mov.b w0, [w14+cbcpos]
; преобразуем строку в число
get_digit(14, w2)
get_digit(13, w5)
get_digit(12, w4)
get_digit(11, w0)
mov.b w2, w3
mov.b w5, w2
mov.b w4, w1
rcall function_atoi
; напряжение 3.5В
; temp = 3500 - atoi(cbcpos + 11)
mov w0, w1
mov #3500, w0
sub w1, w0, [w15]
bra gtu, more3_5V
movlf(TRUE, lowbat)
mov lbsent, w0
sub w0, #1, [w15]
bra gtu, skip_sms_lowbat
incf(lbsent)
IFNE(active1, TRUE, skip_number1_sms_lowbat)
SEND_SMS(#phone1, Low_battery, i)
skip_number1_sms_lowbat:
IFNE(active2, TRUE, skip_number2_sms_lowbat)
delay(#5000)
SEND_SMS(#phone2, Low_battery, i)
skip_number2_sms_lowbat:
IFNE(active3, TRUE, skip_number3_sms_lowbat)
delay(#5000)
SEND_SMS(#phone3, Low_battery, i)
skip_number3_sms_lowbat:
delay(#1000)
bra skip_sms_lowbat
more3_5V:
movlf(FALSE, lowbat)
skip_sms_lowbat:
rcall clear_buffer
local11:
delay(#380)
delay(#380)
incf(pollcnt)
delay(#380)
delay(#380)
delay(#380)
led_on
delay(#380)
delay(#380)
led_off
; входящий звонок?
unless_buffer_contains(ring, local4)
; определить номер
ATCMD(at_clcc + 1)
delay(#380)
delay(#380)
delay(#380)
delay(#380)
delay(#380)
for(m, local1, local2)
erase(callee, m)
endfor(m, PHONE_LEN, local1, local2)
; ошибка: даже если номер не определен,
; сравнение выполняется
unless_buffer_contains(clcc + 1, check_phone)
invoke_strnpos(clcc + 1)
; callee_offset = strnpos(clcc) + CLCC_PHONE_OFFSET
ze w0, w0
add w0, #CLCC_PHONE_OFFSET, w0
mov w0, [w14+callee_offset]
; m = 0
movlo(0, m)
bra local3
local5:
; buffer[callee_offset + m] == '"'?
mov [w14+callee_offset], w1
mov [w14+m], w0
add w1, w0, w1
mov #buffer, w0
add w1, w0, w0
mov.b [w0], w1
mov_w0('"')
sub.b w1, w0, [w15]
; закрывающая кавычка
bra z, check_phone
; копируем номер звонящего:
; callee[m] = buffer[callee_offset + m]
mov [w14+m], w2
mov [w14+callee_offset], w1
mov [w14+m], w0
add w1, w0, w1
mov #buffer, w0
add w1, w0, w0
mov.b [w0], w1
mov #callee, w0
add w2, w0, w0
mov.b w1, [w0]
endfor(m, PHONE_LEN, local3, local5)
check_phone:
; звонит владелец?
; search_result = FALSE
movlo(FALSE, search_result)
; 1. если сигнатура CLCC не обнаружена, в callee будет номер предыдущего звонка
; (почти всегда номер хозяина)
; 2. то же при пустом номере "" (запрет определения номера)
check_number(active1, phone1)
check_number(active2, phone2)
check_number(active3, phone3)
mov [w14+search_result], w0
sub w0, #TRUE, [w15]
bra nz, drop_call
; поднять трубку
ATCMD(ata)
mul.uu w0, #0, w0
mov w0, initialized_data
mov w1, tm4h
bclr IFS0, #T3IF
bset IEC0, #T3IE
movlf(TRUE, calling)
bra local4
drop_call:
; сбросить звонок
ATCMD0(ath)
local4:
; tm4h:initialized_data < 0xf00?
mov initialized_data, w2
mov tm4h, w3
mov #15, w0
mov #0, w1
sub w2, w0, [w15]
subb w3, w1, [w15]
bra leu, not_calling
IFNE(calling, TRUE, not_calling)
mov.b MIC_PORT, wreg
and.b w0, #MASK(MICLEVEL), w0
CP0_W0
bra nz, not_calling
; повесить трубку
ATCMD(ath)
; сбросить флаг активного звонка
movlf(0, calling)
bclr IFS0, #T3IF
bclr IEC0, #T3IE
not_calling:
; нет связи?
unless_buffer_contains(no_carrier, local7)
rcall clear_buffer
bclr IFS0, #T3IF
bclr IEC0, #T3IE
; сбросить флаг активного звонка
movlf(0, calling)
local7:
unless_buffer_contains(ath2 + 1, check_alert)
rcall clear_buffer
bclr IFS0, #T3IF
bclr IEC0, #T3IE
; сбросить флаг активного звонка
movlf(0, calling)
check_alert:
IFNE(alert, TRUE, dont_call)
; сбрасываем флаг тревоги
movlf(0, alert)
IFNE(active1, TRUE, number1_inactive)
; звоним по первому номеру
CALL(phone1, 4)
bra call_done
number1_inactive:
IFNE(active2, TRUE, number2_inactive)
delay(#5000)
CALL(phone2, 2)
bra call_done
number2_inactive:
IFNE(active3, TRUE, call_done)
delay(#5000)
; звоним по третьему номеру
CALL(phone3, 0)
call_done:
delay(#1000)
dont_call:
IFNE(charged, TRUE, repeat)
movlf(0, charged)
IFNE(active1, TRUE, sms_number1_inactive)
SEND_SMS(#phone1, High_battery + 1, j)
sms_number1_inactive:
IFNE(active2, TRUE, sms_number2_inactive)
delay(#5000)
SEND_SMS(#phone2, High_battery + 1, j)
sms_number2_inactive:
IFNE(active3, TRUE, sms_number3_inactive)
delay(#5000)
SEND_SMS(#phone3, High_battery + 1, j)
sms_number3_inactive:
delay(#1000)
bra repeat
wrong_state:
ulnk
return
#include "itoa_digit.asm"
; прерывание от охранного шлейфа
interrupt_sensor:
.scope
push RCOUNT
push.d w0
push.d w2
push.d w4
push.d w6
push PSVPAG
mov #0, w0
mov w0, PSVPAG
lnk #0x00
; Input Change Notification Interrupt Flag Status bit
bclr IFS1, #CNIF
; контроль охранного шлейфа сигнализации
mov.b HIREG(SENSOR_PORT), wreg
and.b w0, #HIMASK(SENSOR), w0
CP0_W0
bra z, sensor_ok
; за это время можно и успеть восстановить шлейф (быстро войти и закрыть дверь)
delay(#600)
mov.b HIREG(SENSOR_PORT), wreg
and.b w0, #HIMASK(SENSOR), w0
CP0_W0
bra z, sensor_ok
; тревога!
movlf(TRUE, alert)
sensor_ok:
; аккумулятор полностью заряжен?
mov.b CHARGE_PORT, wreg
and.b w0, #CHARGE, w0
CP0_W0
bra nz, exit
delay(#600)
mov.b CHARGE_PORT, wreg
and.b w0, #CHARGE, w0
CP0_W0
bra nz, exit
; аккумулятор заряжен
movlf(TRUE, charged)
exit:
ulnk
pop PSVPAG
pop.d w6
pop.d w4
pop.d w2
pop.d w0
pop RCOUNT
retfie
.ends
initialized_data:
; инициализированные данные
#include "idata2.asm"
; все неиспользуемые прерывания
interrupt_reset:
reset
uart.asm
; антипаттерн работы с последовательным портом
interrupt_uart: ; оригинальный адрес 0x0748
.scope
push.d w0
push PSVPAG
mov #0, w0
mov w0, PSVPAG
lnk #0x02
; прерывание обработано
; бит нужно сбрасывать в конце обработчика, а не в начале
; https://www.joelw.id.au/PicMcuTips
bclr IFS0, #U1RXIF
; этот цикл - источник зависаний
; не проверяются FERR, PERR, OERR
wait:
mov.b U1STA, wreg
; Receive buffer has data; at least one more character can be read
and.b w0, #MASK(URXDA), w0
CP0_W0
bra z, wait
; принятый байт (а в буфере USART могут быть еще)
mov U1RXREG, w0
; временно сохраняем
mov.b w0, [w14]
; buffer[bufptr++] = c
mov bufptr, w1
mov #buffer, w0
add w1, w0, w0
mov.b [w14], [w0]
incf(bufptr)
; буфер не заполнен?
IFNE2(bufptr, BUFSZ, end)
; первые байты перезаписываются новыми,
; что может привести к интересным уязвимостям
movlf(0, bufptr)
end:
ulnk
pop PSVPAG
pop.d w0
retfie
.ends
transmit_char:
lnk #0x02
mov.b w0, [w14]
mov.b U1STA + 1, wreg
and.b w0, #2, w0
CP0_W0
bra nz, $-6
se [w14], w0
mov w0, U1TXREG
ulnk
return
setup_uart:
lnk #0x00
movlf(103, U1BRG) ; Baud Rate Generator Prescaler Register (160)
movlf(0, U1MODE)
bset U1MODE, #BRGH
bset U1MODE, #UARTEN ; UARTx Enable bit (162)
; URXISEL = 0
; Interrupt is set when any character is received and transferred from the
; RSR to the receive buffer;
; receive buffer has one or more characters (164)
movlf(0, U1STA)
; Transmit is enabled, UxTX pin is controlled by UARTx (164)
bset U1STA, #UTXEN
bclr IFS0, #U1RXIF
bset IEC0, #T4IE ; разрешить прерывания от таймера 4
ulnk
return
transmit_at_command:
.scope
lnk #0x02
mov w0, [w14]
bra loop
next_char:
mov.b U1STA + 1, wreg
and.b w0, #2, w0
CP0_W0
bra nz, next_char
mov [w14], w0
mov.b [w0], w0
se w0, w0
mov w0, U1TXREG
inc [w14], [w14]
loop:
mov [w14], w0
mov.b [w0], w0
CP0_W0
bra nz, next_char
ulnk
return
.ends
macro.asm
;===============================================================================
; Макросы
#include "naken.asm"
.macro for(var, label_start, label_next)
movlo(0, var)
bra label_start
label_next:
.endm
.macro endfor(var, limit, label_start, label_next)
inco(w14, var)
label_start:
.if var == 0
mov #limit, w0
subr w0, [w14], [w15]
.else
mov [w14+var], w1
mov #limit, w0
sub w1, w0, [w15]
.endif
bra le, label_next
.endm
; for до конца строки
.macro endforc(buf, pos, label_start, label_next)
inco(w14, pos)
label_start:
getc(buf, pos)
add.b w0, #1, [w15]
bra nz, label_next
.endm
.macro endforc2(buf, pos, limit, label_start, label_next)
inco(w14, pos)
label_start:
getc(buf, pos)
add.b w0, #1, [w15]
bra z, break
IFLEO(w14, pos, limit, label_next)
break:
.endm
.macro inc16(high, low)
mov low, w0
mov high, w1
add w0, #1, w0
addc w1, #0, w1
mov w0, low
mov w1, high
.endm
.macro erase(buf, var)
movor(w14, var, w1)
mov #buf, w0
add w1, w0, w1
; 0xff
setm.b w0
mov.b w0, [w1]
.endm
; reg = base[pos]
.macro getc_(base, pos, reg)
movor(w14, pos, w1)
mov base, w0
add w1, w0, w0
mov.b [w0], reg
.endm
; w0 = base[pos]
.macro getc(base, pos)
getc_(base, pos, w0)
.endm
.macro delay(n)
mov n, w0
rcall function_delay
.endm
; отправить AT-команду без предварительной очистки буфера ответа
.macro ATCMD0(cmd)
mov LDM(cmd), w0
rcall transmit_at_command
.endm
.macro ATCMD(cmd)
rcall clear_buffer
ATCMD0(cmd)
.endm
; отправить символ
.macro SEND(char)
mov_w0(char)
rcall transmit_char
.endm
.macro SEND_SMS(number, text, ii)
.scope
; AT+CMGS="recipient_number"
ATCMD0(at_cmgs + 1)
for(ii, start, next)
getc(number, ii)
rcall transmit_char
endforc(number, ii, start, next)
ATCMD0(Ready - 1)
SEND('\r')
SEND('\n')
delay(#1000)
ATCMD0(text)
; ^Z - символ конца сообщения
SEND(0x1a)
.ends
.endm
.macro led_on
bset LED_PORT, #LED
.endm
.macro led_off
bclr LED_PORT, #LED
.endm
.macro blink
led_on
delay(#250)
led_off
delay(#250)
.endm
.macro IBLINK
mov.b LED_PORT, wreg ; мигаем
ze w0, w0
lsr w0, #LED, w0
and.b w0, #1, w0
com.b w0, w0
and.b w0, #1, w0
and.b w0, #1, w0 ; что за ерунда?
sl w0, #LED, w2
mov #LED_PORT, w1
mov.b [w1], w1
mov_w0(~MASK(LED))
and.b w1, w0, w0
ior.b w0, w2, w0
mov.b wreg, LED_PORT
.endm
.macro CALL(number, tmp)
.scope
ATCMD0(atd + 1)
for(tmp, start, next)
getc(#number, tmp)
rcall transmit_char
endforc2(#number, tmp, PHONE_LEN, start, next)
ATCMD0(tail + 1)
.ends
.endm
.macro invoke2(func, arg1, arg2)
mov arg1, w0
mov arg2, w1
rcall initialize
.endm
.macro invoke_strnpos(str)
mov LDM(str), w1
mov #BUFSZ, w2
mov #buffer, w0
rcall strnpos
.endm
.macro unless_buffer_contains(str, label)
invoke_strnpos(str)
CP0_W0
bra lt, label
.endm
.macro if_buffer_contains(str, label)
invoke_strnpos(str)
CP0_W0
bra ge, label
.endm
; читать номер из записной книги SIM карты
.macro read_parse_phonebook(cmd, number, flag_active)
.scope
ATCMD(cmd)
delay(#1500)
; заполнить номер 0xff
for(i, start1, next1)
erase(number, i)
endfor(i, PHONE_LEN, start1, next1)
; проверка корректности ответа
unless_buffer_contains(cpbr + 1, error)
invoke_strnpos(cpbr + 1)
ze w0, w0
add w0, #10, w0
mov w0, [w14+pbk]
for (i, start, next)
mov [w14+pbk], w1
mov [w14+i], w0
add w1, w0, w1
mov #buffer, w0
add w1, w0, w0
mov.b [w0], w1
; до закрывающей кавычки
mov_w0('"')
sub.b w1, w0, [w15]
bra z, break
; сохраняем номер в number
mov [w14+i], w2
mov [w14+pbk], w1
mov [w14+i], w0
add w1, w0, w1
mov #buffer, w0
add w1, w0, w0
mov.b [w0], w1
mov #number, w0
add w2, w0, w0
mov.b w1, [w0]
endfor(i, PHONE_LEN, start, next)
break:
; первый номер прочитан с SIM карты, пометить как активный
movlf(TRUE, flag_active)
error:
delay(#2000)
.ends
.endm
.macro check_number(flag_active, number)
.scope
mov flag_active, w0
sub w0, #TRUE, [w15]
bra nz, exit
for(m, start, next)
; number[m] == callee[m]?
getc_(#number, m, w2)
getc_(#callee, m, w0)
sub.b w2, w0, [w15]
bra nz, break
endfor(m, PHONE_LEN, start, next)
break:
; number[m] != callee[m]
mov [w14+m], w1
mov #PHONE_LEN + 1, w0
sub w1, w0, [w15]
bra nz, exit
movlo(TRUE, 6)
exit:
.ends
.endm
.macro get_digit(ndigit, dest)
mov.b [w14+cbcpos], w0
ze w0, w0
add w0, #ndigit, w1
mov #buffer, w0
add w1, w0, w0
mov.b [w0], dest
.endm
utils.asm
clear_buffer:
.scope
lnk #0x02
for(0, check_cond, repeat)
erase(buffer, 0)
endfor(0, BUFSZ, check_cond, repeat)
movlf(0, bufptr)
ulnk
return
.ends
function_delay:
.scope
lnk #0x02
mov w0, [w14]
movlf(0x8000, T2CON)
bra start
loop:
movlf(0, TMR2)
inner:
mov TMR2, w1
mov #3999, w0
sub w1, w0, [w15]
bra leu, inner
start:
dec [w14], [w14]
setm w0
subr w0, [w14], [w15]
bra nz, loop
ulnk
return
.ends
; не используется
timer_delay:
.scope
lnk #0x02
mov w0, [w14]
movlf(0x8000, T2CON)
bra start
repeat:
movlf(0, TMR2)
wait:
mov TMR2, w1
mov #PHONE_LEN, w0
sub w1, w0, [w15]
bra leu, wait
start:
dec [w14], [w14]
setm w0
subr w0, [w14], [w15]
bra nz, repeat
ulnk
return
.ends
strnpos:
.scope
lnk #0x0c
mov w0, [w14+4] ; haystack
mov w1, [w14+6] ; needle
mov w2, [w14+8] ; максимальная длина (i + j)
for(2, check, nextchar) ; i (2) - позиция в haystack
movlo(0, 0) ; j (0) - позиция в needle
bra compare
match:
inco(w14, 0) ; j++
; needle[j] == '\0'?
mov [w14], w1
mov [w14+6], w0
add w1, w0, w0
mov.b [w0], w0
CP0_W0
bra nz, more
; нашли needle
; возвращаем i - позицию в haystack
mov [w14+2], w0
ze w0, w0
mov w0, [w14+10]
bra exit
more:
; еще не конец needle
mov [w14+2], w0
add w0, [w14], w1 ; i + j
mov [w14+8], w0 ; максимальная длина
sub w1, w0, [w15]
bra ltu, compare
; подстрока не найдена
; достигнут предел суммы позиций в haystack и needle
mov #0xff, w0
mov w0, [w14+10]
bra exit
compare:
mov [w14+2], w0 ; i
add w0, [w14], w0 ; j
mov w0, w1
mov [w14+4], w0 ; haystack
add w1, w0, w0
mov.b [w0], w2 ; w2 = haystack[i + j]
mov [w14], w1 ; j
mov [w14+6], w0 ; needle
add w1, w0, w0
mov.b [w0], w0 ; w0 = needle[j]
sub.b w2, w0, [w15]
bra z, match
; символы не совпали
; i++ (следующая позиция в haystack)
inco(w14, 2)
check:
; достигли конца haystack?
mov [w14+2], w1 ; i
mov [w14+8], w0 ; предел haystack
sub w1, w0, [w15]
bra leu, nextchar
; конец haystack
movlo(0xff, 10) ; -1 - не найдено
exit:
mov [w14+10], w0 ; результат
ulnk
return
.ends
at.asm
org 0x290
; AT команды и ответы SIM900
.align_bits 8
at:
; "AT\r\n"
db "AT",0,0,"\r\n",0,0,0
ok:
; "OK"
db 'O',0,0,'K',0,0,0
at_cpin:
; "AT+CPIN?\r\n"
db "AT",0,0,"+C",0,0,"PI",0,0,"N?",0,0,"\r\n",0,0,0
cpin_sim_pin:
; "+CPIN: SIM PIN"
db '+',0,0,"CP",0,0,"IN",0,0,": ",0,0,"SI",0,0,"M ",0,0,"PI",0,0,'N',0,0,0
cpin_ready:
; "+CPIN: READY"
db "+C",0,0,"PI",0,0,"N:",0,0," R",0,0,"EA",0,0,"DY",0,0,0
at_cpin_2000:
; "AT+CPIN=2000\r\n"
db 'A',0,0,"T+",0,0,"CP",0,0,"IN",0,0,"=2",0,0,"00",0,0,"0\r",0,0,'\n',0,0,0
at_creg:
; "AT+CREG?\r\n"
db "AT",0,0,"+C",0,0,"RE",0,0,"G?",0,0,"\r\n",0,0,0
creg_01:
; "+CREG: 0,1"
db '+',0,0,"CR",0,0,"EG",0,0,": ",0,0,"0,",0,0,'1',0,0,0
at_cpbs_sm:
; AT+CPBS="SM"\r\n
db "AT",0,0,"+C",0,0,"PB",0,0,"S=",0,0,"\"S",0,0,"M\"",0,0,"\r\n",0,0,0
at_cpbr_1:
; "AT+CPBR=1\r\n"
db 'A',0,0,"T+",0,0,"CP",0,0,"BR",0,0,"=1",0,0,"\r\n",0,0,0
cpbr:
; "+CPBR:"
db '+',0,0,"CP",0,0,"BR",0,0,':',0,0,0
at_cpbr_2:
; "AT+CPBR=2\r\n"
db "AT",0,0,"+C",0,0,"PB",0,0,"R=",0,0,"2\r",0,0,'\n',0,0,0
at_cpbr_3:
; "AT+CPBR=3\r\n"
db "AT",0,0,"+C",0,0,"PB",0,0,"R=",0,0,"3\r",0,0,'\n',0,0,0
at_cmgf_1:
; AT+CMGF=1\r\n
db "AT",0,0,"+C",0,0,"MG",0,0,"F=",0,0,"1\r",0,0,'\n',0,0,0
; AT+CMIC=<channel>,<gainlevel>
; https://www.espruino.com/datasheets/SIM900_AT.pdf
; <channel> 0 main audio handset channel
; 1 aux audio headset channel
; 2 main audio handfree channel
; 3 aux audio handfree channel
; <gainlevel> int: 0-15
; animals:
; на канале 1 чувствительность кажется получше, но качество звука идеальное, чистый без искажений звук
; на 2 канале отличий не увидел, всё также как и канале 0
at_cmic00:
; "AT+CMIC=0,0\r\n"
db "AT",0,0,"+C",0,0,"MI",0,0,"C=",0,0,"0,",0,0,"0\r",0,0,'\n',0,0,0
at_cmic015:
; "AT+CMIC=0,15\r\n"
db "AT",0,0,"+C",0,0,"MI",0,0,"C=",0,0,"0,",0,0,"15",0,0,"\r\n",0,0,0
at_cmgs:
; AT+CMGS="\x00"
db 'A',0,0,"T+",0,0,"CM",0,0,"GS",0,0,"=\"",0,0,"\0\"",0,0,0
Ready:
; "Ready"
db 'R',0,0,"ea",0,0,"dy",0,0,0
at_cbc:
; "AT+CBC\r\n"
db 'A',0,0,"T+",0,0,"CB",0,0,"C\r",0,0,'\n',0,0,0
cbc:
; "+CBC:"
db "+C",0,0,"BC",0,0,':',0,0,0
Low_battery:
; "Low battery"
db "Lo",0,0,"w ",0,0,"ba",0,0,"tt",0,0,"er",0,0,'y',0,0,0
ring:
; "RING"
db "RI",0,0,"NG",0,0,0
at_clcc:
; "AT+CLCC\r\n"
db 'A',0,0,"T+",0,0,"CL",0,0,"CC",0,0,"\r\n",0,0,0
clcc:
; "+CLCC:"
db '+',0,0,"CL",0,0,"CC",0,0,':',0,0,0
ata:
db "AT",0,0,"A\r",0,0,'\n',0,0,0
ath:
; "ATH\r\n"
db "AT",0,0,"H\r",0,0,'\n',0,0,0
no_carrier:
; "NO CARRIER"
db "NO",0,0," C",0,0,"AR",0,0,"RI",0,0,"ER",0,0,0
; дубликат строки
ath2:
; "ATH"
db 'A',0,0,"TH",0,0,0
atd:
; "ATD"
db 'A',0,0,"TD",0,0,0
tail:
; ";\x00\r\n"
db ";\0",0,"\r\n",0,0,0
High_battery:
; "High battery 100%"
db 'H',0,0,"ig",0,0,"h ",0,0,"ba",0,0,"tt",0,0,"er",0,0,"y ",0,0,"10",0,0,"0%",0,0,0
db 0,0,0
atoi.asm (преобразование строки в число)
.macro atoi_case(n, offset, label, nobra)
movl(n, w0)
movro(w0, w14, offset)
.if nobra == 0
bra label
.endif
.endm
; avoid naken_asm 1024 chars macro limit
.macro atoi_subround(label, tmp1, tmp2, tmp3)
; select
mov [w14+tmp1], w0
mov [w14+tmp2], w1
bra w0
; case
bra $+20
bra $+2*(10+1*2)
bra $+2*(10+2*2)
bra $+2*(10+3*2)
bra $+2*(10+4*2)
bra $+2*(10+5*2)
bra $+2*(10+6*2)
bra $+2*(10+7*2)
bra $+2*(10+8*2)
bra $+2*(10+9*2)
atoi_case(0, tmp3, label, 0)
atoi_case(1, tmp3, label, 0)
atoi_case(2, tmp3, label, 0)
atoi_case(3, tmp3, label, 0)
atoi_case(4, tmp3, label, 0)
atoi_case(5, tmp3, label, 0)
atoi_case(6, tmp3, label, 0)
atoi_case(7, tmp3, label, 0)
atoi_case(8, tmp3, label, 0)
atoi_case(9, tmp3, label, 1)
.endm
.macro atoi_round(tmp0, tmp1, tmp2, tmp3, label)
mov.b [w14+tmp0], w0
ze w0, w0
mul.su w0, #1, w2
mov #-'0', w0
mov #-1, w1
add w0, w2, w2
addc w1, w3, w3
mov w2, [w14+tmp1]
mov w3, [w14+tmp2]
mov #9, w0
mov #0, w1
mov [w14+tmp1], w2
mov [w14+tmp2], w3
sub w2, w0, [w15]
subb w3, w1, [w15]
bra gtu, label
atoi_subround(label, tmp1, tmp2, tmp3)
label:
.endm
function_atoi:
.scope
lnk #0x1c
mov.b w0, [w14+8]
mov.b w1, [w14+9]
mov.b w2, [w14+10]
mov.b w3, [w14+11]
atoi_round(8, 12, 14, 6, overflow1)
atoi_round(9, 16, 18, 4, overflow2)
atoi_round(10, 20, 22, 2, overflow3)
atoi_round(11, 24, 26, 0, overflow4)
mov [w14+6], w1
mov #1000, w0
mul.ss w1, w0, w0
mov w0, w2
mov [w14+4], w1
mov #100, w0
mul.ss w1, w0, w0
add w2, w0, w2
mov [w14+2], w0
mul.su w0, #10, w0
add w2, w0, w0
add w0, [w14], w0
ulnk
return
.ends
naken.asm
.macro sleep_
; unknown opcode
db 0, 0x40, 0xda, 0
.endm
; константа - регистр
.macro movl(value, reg)
.if (value) == 0
clr reg
.else
mov #(value), reg
.endif
.endm
; константа - файл
.macro movlf(value, file)
movl(value, w0)
mov w0, (file)
.endm
; [reg] and [reg+0] are not the same
.macro movro(src, dst, offset)
.if (offset) == 0
mov src, [dst]
.else
mov src, [dst+offset]
.endif
.endm
.macro movor(src, offset, dst)
.if (offset) == 0
mov [src], dst
.else
mov [src+offset], dst
.endif
.endm
.macro movlo(value1, var1)
movl(value1, w0)
movro(w0, w14, var1)
.endm
.macro inco(reg, offset)
.if offset == 0
inc [reg], [reg]
.else
mov [reg+offset], w0
inc w0, w0
mov w0, [reg+offset]
.endif
.endm
; если [reg+offset] < n, перейти на label
.macro IFLEO(reg, offset, n, label)
.if offset == 0
mov #n, w0
subr w0, [reg], [w15]
.else
mov [reg+offset], w1
mov #n, w0
sub w1, w0, [w15]
.endif
bra le, label
.endm
.macro incf(file)
mov file, w0
inc w0, w0
mov w0, file
.endm
; naken_asm bugs fix
;cp0 w0
.macro CP0_W0
db 0, 4, 0xe0, 0
.endm
; mov #b, w0
.macro mov_w0(b)
db (b & 0x0f) << 4, 0xc0 | ((b >> 4) & 0x0F), 0xb3, 0
.endm
; mov #b, w1
.macro mov_w1(b)
db ((b & 0x0f) << 4) | 1, 0xc0 | ((b >> 4) & 0x0F), 0xb3, 0
.endm
; если file не равно value, перейти на метку label
.macro IFNE(file, value, label)
mov (file), w0
sub w0, #(value), [w15]
bra nz, (label)
.endm
.macro IFNE2(file, value, label)
mov (file), w1
mov #(value), w0
sub w1, w0, [w15]
bra nz, (label)
.endm
PIC24FJ64GA004.inc
;=============================================================================== ; Самодельный PIC24FJ64GA004 для naken_asm ; см. include/dspic/convert.py для преобразования include-файлов ; из формата MPLAB в формат naken_asm (добавлено вручную - возможны ошибки!) RPOR11 equ 0x06d6 RPINR18 equ 0x06a4 AD1PCFG equ 0x032c PORTB equ 0x02CA RB14 equ 14 ; pin 14 RB8 equ 8 ; pin 44 RB2 equ 2 ; pin 23 RB0 equ 0 ; pin 21 TRISB equ 0x02C8 TRISB14 equ 14 TRISB2 equ 2 PORTC equ 0x02D2 RC9 equ 9 ; pin 5 RC1 equ 1 ; pin 26 TRISC equ 0x02D0 TRISC9 equ 9 U1BRG equ 0x0228 U1RXREG equ 0x0226 U1TXREG equ 0x0224 U1STA equ 0x0222 UTXEN equ 10 URXDA equ 0 U1MODE equ 0x0220 UARTEN equ 15 BRGH equ 3 T4CON equ 0x011E PR4 equ 0x011A T3CON equ 0x0112 T2CON equ 0x0110 TMR2 equ 0x0106 TON equ 0x8000 TCKPS0 equ 16 TCKPS1 equ 32 PR3 equ 0x010E IPC4 equ 0x00AC IEC1 equ 0x0096 T4IE equ 11 T1IE equ 3 IEC0 equ 0x0094 T3IE equ 8 IFS1 equ 0x0086 T4IF equ 11 CNIF equ 3 IFS0 equ 0x0084 U1RXIF equ 11 T3IF equ 8 CNPU1 equ 0x0068 CN4PUE equ 4 CN8PUE equ 8 CNEN1 equ 0x0060 CN8IE equ 8 CNEN2 equ 0x0062 CN22IE equ 6 CORCON equ 0x0044 PSV equ 2 RCOUNT equ 0x0036 PSVPAG equ 0x0034 TBLPAG equ 0x0032 SPLIM equ 0x0020
idata.asm
; initialized data
initialize:
.scope
; Table Memory Page Address Register
mov w1, TBLPAG
mov w0, w1
clr w0
bra local1
local2:
add w1, #2, w1
addc TBLPAG
tblrdl [w1], w3
add w1, #2, w1
addc TBLPAG
tblrdl [w1], w5
add w1, #2, w1
addc TBLPAG
clr w4
lsr w5, #7, w6
and #0x7f, w5
cp.b w5, #0
bra nz, local3
loop:
clr.b [w2++]
dec w3, w3
bra gtu, loop
bra local1
local3:
cp w5, #1
bra z, skip
setm w4
skip:
rcall function_026c
local1:
; reading the lower word
tblrdl [w1], w2
cp0 w2
bra nz, local2
return
.ends
function_026c:
.scope
start:
tblrdl.b [w1++], [w2++]
dec w3, w3
bra z, exit
tblrdl.b [w1--], [w2++]
dec w3, w3
bra z, next
cp0 w4
bra nz, local1
local2:
add w1, #2, w1
addc TBLPAG
bra start
local1:
tblrdh.b [w1], [w2++]
dec w3, w3
bra nz, local2
next:
inc w1, w1
exit:
add w1, #1, w1
addc TBLPAG
return
.ends
ivt.asm
; таблица векторов прерываний
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_timer3,interrupt_reset, interrupt_reset, interrupt_uart
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_sensor
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_timer4
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, 0, 0
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_timer3,interrupt_reset, interrupt_reset, interrupt_uart
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_sensor
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_timer4
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
dd interrupt_reset, interrupt_reset, interrupt_reset, interrupt_reset
idata2.asm (инициализированные данные)
dd unknown, 0x40, 2, 0
dd 0, 0, 0, 0
dd 0, 0, 0, 0
dd 0, 0, 0, 0
dd 0, 1, 0, 0
dd 0, 1, 0, 0
dd 0, 0x800, 0x9FE, 0
dd 0Подпишитесь на мой блог, не дайте фуфлыжникам шанса!
© Copyright 2025 Badradio. All rights reserved.
Все права защищены. Копирование материалов сайта запрещено.
При цитировании ставить кликабельную ссылку на первоисточник.