Иногда бывает нужно передать в процедуру или функцию в качестве параметра адрес другой функции или процедуры с тем, чтобы она позже могла быть вызвана.
В некоторых языках высокого уровня такая возможность предусмотрена изначально, однако в Bascom подобный механизм напрямую не предусмотрен.
Поэтому для реализации механизма CallBack нужны небольшие ухищрения (подобный механизм, например, используется в ОСРВ Aqua RTOS для вызова предварительно указанного пользовательского обработчика ошибок при возникновении ошибки в ОС).
Ниже показан пример реализации механизма CallBack. Здесь для вызова пользовательской процедуры используется подмена адреса возврата из процедуры в стеке.
Изначально на вершине стека лежит адрес, по которому МК следует вернуться после завершения процедуры. Если этот адрес подменить, то по команде RETURN (или END SUB) в регистр PC (адрес исполнения) будет извлечен[подменный] адрес из стека, и управление перейдет на процедуру пользователя.
Пример можно запустить в симуляторе, чтобы посмотреть, как все это работает.

Код:
$regfile = "m328pdef.dat"
$crystal = 16000000
$hwstack = 48
$swstack = 48
$framesize = 64

$sim

' *******************************************************************************
' задача: из библиотеки вызвать пользовательский обработчик ошибок
' адрес обработчика должен быть передан в библиотеку перед началом работы
' *******************************************************************************

config submode = old
' режим submode должен быть OLD, т.е. config submode = old

' эти процедуры находятся в нашей библиотеке
declare sub set_error_trap(bylabel pointer as word)                             ' пользователь вызывает эту процедуру, чтобы установить адрес своего обработчика по типу void SetCallBack(&sub_ptr)
declare sub raise_error(byval lib_err_num as byte)                              ' теперь библиотека через свою процедуру может вызвать пользовательский обработчик и передать номер ошибки
' эти вспомогательные переменные (на самом деле одно слово) тоже объявляются библиотеке
dim trap_ptr as word                                                            ' служебные переменные для получения точки входа в обработчик
dim trap_ptr_lo as byte at trap_ptr overlay                                     ' размещаются в библиотеке
dim trap_ptr_hi as byte at trap_ptr + 1 overlay

' этот обработчик декларирует и пишет юзер
declare sub error_trap(byval err_num as byte)

' --- Программа юзера --------------------------------

print "testing trap callback"
set_error_trap error_trap                                                       ' передаем библиотеке адрес пользовательского обработчика ошибок
raise_error 8                                                                   ' вызываем в бибилиотеке пользовательский обработчик
print "done"
end

' ----------------------------------
' эти процедуры находятся в библиотеке
' ----------------------------------

sub set_error_trap(lptr word pointer)
   trap_ptr = pointer
   shift trap_ptr , right
end sub

sub raise_error(byte lib_err_num)
   !LDS R16, {trap_ptr_lo}                                                      ' загрузим в стек адрес задачи
   !PUSH R16
   !LDS R16, {trap_ptr_hi}
   !PUSH R16
   return
   ' на самом деле будет вызван пользовательский обработчик
   ' параметр был помещен в хардовый стек по адресу у+0, у+1, и будет извлечен обработчиком,
   ' а хардовый и софтовый стеки вернутся в начальное состояние по выходу из обработчика
end sub

' ----------------------------------
' Этот обработчик находится в программе юзера
' ----------------------------------
sub error_trap(byte err_num)
   print "Trap! Error #" ; err_num
end sub

Отредактировано Cirno_9 (2020-02-21 14:00:50)