ZX Format #06
29 июля 1997

Программистам - MMD - драйвер. Описание структуры драйвера модема для терминальной программы MMD.

  Структура драйвера модема
для программы MMD 2.20

music by DNK
(C) MAS
_______________________________

Для  работы  терминалки  "Macro Modem
v2.20"  необходим  драйвер модема. Данный
драйвер  выполняет все функции приёма/пе-
редачи  информации,  а также обрабатывает
состояние  телефонной  линии  и системных
часов.

Драйвер  находиться на 6-й странице с
адреса 49152 (#C000). Под его нужды отво-
диться  все  16Кб данной страницы. Вход в
драйвер  производиться через ТОЧКИ ВХОДА,
расположеные  в  самом  начале  драйвера,
т.е. с адреса 49152.



           Внимание!
      ДРАЙВЕР_НЕ_ДОЛЖЕН:

 - портить  содержимое  регистров IX, IY,
SP;
 - изменять любые  ячейки памяти основной
программы за исключением специально выде-
леного буфера для обмена информацией;
, - менять режим прерывания (статус преры-
вания  можно менять - всё равно оно будет
запрещено).



 Входные точки драйвера:

      УСТАНОВКА ДРАЙВЕРА

D_INSTAL   EQU #C000
При  вызове функции в регитровой паре
[HL]  будет находиться адрес рабочего бу-
фера  для  приема/передачи данных. Данный
буфер  расположен  в  основной  памяти, и
имеет размер около 270 байт вверх, и око-
ло  30  вниз от указаного значения. Обмен
данными между основной программой и драй-
вером производиться только через этот бу-
фер.

При  инсталяци  в  этот буфер драйвер
должен  записать  в  ASCII форме значение
существующих  скоростей.  На  каждую ско-
рость  отводиться 5 байт, всего скоростей
- 8. Должна быть описана КАЖДАЯ скорость,
если  драйвер  поддерживает меньшее коли-
чество,  то описатели несуществующих ско-
ростей заполняются кодом #00. Пример опи-
сателя скоростей:

DEFB " 600 " ;1-я скорость 600 бод
DEFB "1800 " ;2-я скорость 1800 бод
DEFB "2400 " ;3-я скорость 2400 бод
DEFS 25      ;всего 3 скорости,
                 ;остальные 5 отсутствуют



В  программе  учтено  то, что драйвер
модема  может быть упакован. Поэтому про-
цедура инсталяции вызывается ДВАЖДЫ. Т.е.
при  первом  вызове происходит распаковка
драйвера,  а  при  втором его инсталяция.
Программа  использует  фляг  переноса для
того, чтобы сообщить драйверу какой вызов
происходит:
 - если флаг переноса установлен - первый
                                   вызов
 - если флаг переноса сброшен - второй.

ПОЛУЧЕНИЕ ИНФОРМАЦИИ О ДРАЙВЕРЕ

D_COPYREQU #C003
Данная функция помещает в рабочий бу-
фер  информацию о драйвере. Данной инфор-
мацией  является  название драйвера и имя
его  автора.  Размер  буфера  -  50 байт,
строка  должна  заканчиваться  кодом  #00
Пример функции D_COPYR:

 LD DE,(WORK_ADR)  ;адрес буфера
        LD HL,TEXT        ;адрес строки
        LD BC,ETEXT-TEXT  ;длина строки
        LDIR              ;перенесли и
        RET               ;вернулись
TEXTDEFB "Vicomm-driver v2.95 " 
        DEFB "(C) 1997 *MAS*" 
        DEFB 0 
ETEXT 



 ЧТЕНИЕ СТАТУСА ЛИНИИ/ДРАЙВЕРА

D_RD_STAT  EQU #C006
Данная  функция  возвращает параметры
драйвера и линии, а также производит кор-
  рекцию данных для порта #FE.
При вызове данной функции в [A] нахо-
дится текущий цвет бордюра.

 При возврате из функции:
-  В регистре [B] различные биты харак-
теризуют:

 bit0 - статус линии: "1" модем подключен
         к линии, "0" -отключен
 bit1 
 bit2 
, bit3 если "1", то драйвер может набирать
        номер тоновым способом.
 bit4  если  "1"  -  нужно выключать TUR-
       BO-режим  при вызове драйвера (для
       всех модемов кроме XTR - нужно)
 bit5 если "1", то драйвер позволяет уп-
       равлять линией (подключать модем к
       линии и отключать)
- bit6 если "1",то в драйвере имеются часы
- bit7 если "1",то в драйвере имеется тай-
       мер

  В регистре [A] будут находится данные
для  порта  #FE.  Если  драйвер для своих
нужд  не использует порт #FE, то содержи-
мое  аккумулятора  не  должно изменяться.
Например: Vicomm с "прибамбасом" v1.2 пе-
редает  данные  и  управляет линией через
порт #FE, и при вызове данная функция ус-
тановит 3-й и 5-й биты аккумулятора в со-
ответствии со своими нуждами.



 УСТАНОВКА ПЕРЕД ОПРОСОМ ЛИНИИ

D_WAIT_INS EQU #C009
Данная  функция вызывается непосредс-
твенно перед циклом вызовов функций опро-
са линии. Если в драйвере необходимо выс-
тавить   какие-либо  переменные,  то  эта
функция этим и занимается.
При вызове данной функции в аккумуля-
торе  будет  находиться число от 0 до 15,
характеризующее громкость с которой функ-
ции опроса линии должны выводить звук ли-
нии на встроеный динамик.
Выдор  устройства для вывода звука не
оговаривается (можно использовать beeper,
AY, COVOX, и т.д.) При выводе звука на AY
есть одно ограничение: если звук выводить
не  нужно,  т.е. [A]=#00, то изменять со-
держимое регистров музпроцессора не реко-
мендуется.



          ОПРОС ЛИНИИ

D_WAIT_LN  EQU #C00C
Данная функция производит опрос линии
в течении некоторого небольшого промежут-
ка времени.

Данная функция возвращает на выходе:

Если флаг переноса установлен, то ни-
чего  не  произошло,  при этом в регистре
[B]  указывается количество произведенных
опросов линии, а в регистре [C] - сколько
всого  должно  быть опросов. Данные в ре-
гистрах  [B]  и [C] позволяют более-менее
ритмично  мигать  курсором  и  опрашивать
клавиатуру.
Например,  если  функция осуществляет
15  опросов  линии,  и при наличии шума в
линии  в любом из циклов опроса возвраща-
ется. При этом в регистре [B] указывается
сколько опросов функция успела выполнить.
Если  функция  опроса  линии  выполняется
всегда  строго  фиксированое  время, то в
регистр [B] заносится #01
Содержимое регистра [C] позволяет ре-
гулировать  частору мугания курсора и оп-
роса  клавиш.  Чем  больше это число, тем
 реже и ритмичнее мигает курсор.

Если при возврате из функции D_WAIT_LN
флаг  переноса  сброшен, то произошло ка-
кое-то событие. В регистре [A] указывает-
ся его код:

  #01: был принят блок. Принятых блок на-
ходится  в  буфере  модема, а его длина в
регистровой  паре [BC] В регистре [D] на-
ходится  скорость, на которой был передан
данный  блок. Если скорость определить не
 удалось, то в [D] заноситься #80
  #02: были обнаружены короткие гудки
  #03: был обнаружен вызов (звонок)



        ДОЛГОЕ ОЖИДАНИЕ

D_WAIT_BLC EQU #C00F
Данная  функция производит долгий оп-
рос  линии  примерно  1...4  секунды. При
возврате функции:

Если флаг  переноса установлен, то ни-
чего не было принято
,Если флаг переноса сброшен, то приняли
блок:
   Принятых блок находится в буфере моде-
ма, а его длина в регистровой паре [BC] В
регистре [D] находится скорость, на кото-
рой  был  передан  данный блок. Если ско-
рость определить не удалось, то в [D] за-
носиться #80



        ПЕРЕДАЧА ДАННЫХ

D_TRANSMIT EQU #C012
Данная  функция передает блок данных.
Сами данные находяться в буфере модема, в
регистровой паре [BC] находится длина пе-
редаваемого блока, в [A] - скорость пере-
дачи.



   ПОДКЛЮЧИТЬ МОДЕМ К ЛИНИИ

D_ON_LINE  EQU #C015

   ОТКЛЮЧИТЬ МОДЕМ ОТ ЛИНИИ

D_OFF_LINE EQU #C018

Данные  функции  просто "снимают" или
"кладут"  трубку. В функцию ничего не пе-
редается и функции ничего не возвращают.



         НАБРАТЬ НОМЕР

D_CALL     EQU #C01B
Данная функция осуществляет набор но-
мера.  При вызове функции модем уже подк-
лючен  к линии, и в регистровой паре [HL]
находится адрес буфера с телефонным номе-
ром,   который   следует  набрать.  Номер
представляет собой ASCII строку, заканчи-
вающуюся  кодом #00. Если в строке встре-
тится символ "-", то при наборе его необ-
ходимо  пропустить. Во время набора необ-
ходимо  анализировать  нажатие на клавиши
<ПРОБЕЛ>, и при её нажатии произвести вы-
од из функции.

 Также при входе в функцию:
в  регистре  [A]  указан способ набора
номера:  "0"  - импульсный, "1" - тональ-
ный.
,в регистре [B] указан  период импульса
в ms
-в регистре [C] межцифровая пауза ms/10

 Данная функция возвращает:
Флаг переноса сброшен - набор номера
     произведён успешно.
Флаг переноса установлен - ошибка,
     код ошибки в [A]:
   #00 ОШИБКА НАБОРА
   #01 НЕТ ДЛИННОГО ГУДКА
   #02 ВЫПОЛНЕНИЕ ПРЕРВАНО,нажат <ПРОБЕЛ>



   ДАННАЯ ФУНКЦИЯ ВЫЗЫВАЕТСЯ
  ПРИ ОБНАРУЖЕНИИ ЗВОНКА.
 МОДЕМ ПОДКЛЮЧАЕТСЯ К ЛИНИИ И
      ОПРЕДЕЛЯЕТСЯ НОМЕР.

D_AON      EQU #C01E
           При вызове функции:
 [A] число запровое
   [B] пауза перед запросом
 [C] количество цифр в номере
 [D] чувствительность к ответу АТС
 [E] чувствительность линии

При выходе из функции в буфере модема
находиться ASCII строка с сообщением, за-
канчивающаяся кодом #00.

     УСТАНОВКА СОЕДИНЕНИЯ

           АКТИВНАЯ

SET_CON_A  EQU #C021

           ПАСИВНАЯ

SET_CON_P  EQU #C024

Данные  функции пытаются выбрать наи-
лудшую  скорость обмена. Функция активной
установки  передает  на  разных скоростях
данные,  а потом проверяет как они приня-
лись. Функция пассивной установки вначале
принимает  данные, а затем сообщает об их
приеме.   Конктерная   реализация  данных
функций не оговаривается.

Обе  функции возвращают текстовое со-
общение  о  выбраной  скорости. Сообщение
находиться в буфере модема и заканчивает-
ся кодом #00. Также при возврате из функ-
ции в аккумулятор записывается номер выб-
раной  скорости,  или  #80, если скорость
выбрать не удалось.
В   случае  невозможности  выполнения
функции возвращаются с установленным фла-
гом переноса.

       УСТАНОВИТЬ ВРЕМЯ

D_SET_TIME EQU #C027
Данная  функция  позволяет установить
текущее  время. При вызове функции данные
находится  в регистрах [A], [B], [C]: [A]
- часы, [B] - минуты, [C] - секунды. Дан-
ные представлены в двоично-десятичном ви-
де.
В  случае ошибки функция возвращается
с установленным флагом переноса.

         СЧИТАТЬ ВРЕМЯ

D_RD_TIME  EQU #C02A
Данная функция позволяет узнать теку-
щее время. При возврате из функции данные
должны  находится  в  регистрах [A], [B],
[C]  в  двоично-десятичном  виде. В [A] -
часы, [B] - минуты, [C] - секунды. В слу-
чае  ошибки  функция возвращается с уста-
новленным флагом переноса.

       КОРРЕКЦИЯ ВРЕМЕНИ
D_CALC_TIM EQU #C02D
Данная  функция  вызывается несколько
раз  в  минуту. Если используемые часы не
являются  полностью автономными (например
часы представляют собой счетчик секундных
импульсов,  подключенный  к  паралельному
порту), и требуется коррекция времени, то
для этого используется данная функция.
Внимание!  Данная функция должна сох-
 ранять значение ВСЕХ регистров!

       УСТАНОВИТЬ ТАЙМЕР

D_SET_ALR  EQU #C036
Если  драйвер поддерживает режим тай-
мера, то данная функция должна установить
таймер  на  указаное  в  регистровой паре
[BC] секунд.
В  случае ошибки функция возвращается
с установленным флагом переноса.

      ДОБАВИТЬ К ТАЙМЕРУ

D_ADD_ALR  EQU #C039
Данная функция добавляет к оставшему-
ся  времени  указаное  в регистровой паре
[BC] секунд.
В  случае ошибки функция возвращается
с установленным флагом переноса.

        ВРЕМЯ ИСТЕКЛО?

D_RD_ALR   EQU #C03C
Данная функция проверяет подошло-ли к
концу время, установленое в таймере. Если
время  вышло,  то  функция возвращается с
установленым флагом переноса.



Внимание! 
В программе Macro Modem v2.20 функции
работы с часами/таймером не используются,
они зарезервированы под будующие версии.

             _____

 Мы  предлагаем  Вашему  вниманию  пример
драйвера  под  самый  простейший "Модем",
распространенный в С-Пб.



CALLING EQU 01 

;ФЛАГ РАЗРЕШЕНИЯ НАБОРА НОМЕРА 
;ЕСЛИ "1", ТО ДРАЙВЕР БУДЕТ НАБИРАТЬ 
;НОМЕР И РЕАГИРОВАТЬ НА ЗВОНКИ 
;ЕСЛИ "0" - НЕ БУДЕТ, НО И НЕ БУДЕТ 
;ЗАВИСАТЬ ПРИ ОТСУТСТВИИ "ПРИБАМБАСА" 

 ORG #C000 

;КЕРНАЛЬ ПОДПРОГРАММ 

 JP D_INSTAL 
        JP D_NAME 
        JP D_RD_STAT 
        JP D_WAIT_INS 
        JP D_WAIT_LN 
        JP D_WAIT_BLC 
        JP D_TRANSMIT 
        JP D_ON_LINE 
        JP D_OFF_LINE 
        JP D_CALL 
 JP D_AON 
        JP SET_CON_A 
        JP SET_CON_P 
        JP D_SET_TIME 
        JP D_RD_TIME 
        JP D_CAL_TIM 
        JP D_SET_ALR 
        JP D_ADD_ALR 
        JP D_RD_ALR 

;РАБОЧИЕ ПЕРЕМЕННЫЕ ДРАЙВЕРА 

LINE_STATUS DB 0
SPEED       DB 0
LCOUNT_ADB 0
LCOUNT_BDB 0
LCOUNT_CDB 0
ZERO_BYTE   DB 0 ;БАЙТ ИДУЩИЙ ПЕРЕД 
                 ;ДЛИНОЙ В БЛОКЕ 
SYNC_TABL   DS 15 

;УСТАНОВКА ДРАЙВЕРА 
 ;in: [HL] АДРЕС БУФЕРА МОДЕМА 
;out: В БУФЕРЕ В ASCII ФОРМЕ ЗНАЧЕНИЯ 
;СКОРОСТЕЙ. 
;НА КАЖДУЮ СКОРОСТЬ ПО 4 БАЙТА, 
;КОЛ-ВО СКОРОСТЕЙ 8, ЗАКАНЧИВАЕТСЯ #00 

D_INSTAL RET C 

;ЕСЛИ ДРАВЕР ВЫЗЫВАЕТСЯ С УСТАНОВЛЕНЫМ 
 ;ФЛАГОМ CY - ТО ЭТО ЗАПУСК ДЛЯ 
;РАСПАКОВКИ, И ЕГО ИГНОРИРУЕМ 

 LD (MOD_BUFF1),HL ;ЗАПИСАЛИ В 
        LD (MOD_BUFF2),HL ;НУЖНЫЕ МЕСТА
        LD (MOD_BUFF3),HL ;АДРЕС 
        LD (MOD_BUFF4),HL ;БУФЕРА 

;ПЕРЕБРАСЫВАЕМ В БУФЕР СКОРОСТИ ДРАЙВЕРА

 EX DE,HL 
        LD HL,SPD_MODEM 
        LD BC,ESPD_MODEM-SPD_MODEM 
        LDIR 
        RET 

SPD_MODEM 
        DB " 600 ","1800 ","2400 " 
        DS 5*5
ESPD_MODEM 

;ЧТЕНИЕ ИНФОРМАЦИИ О ДРАЙВЕРЕ 
;out: ПО [DE] ЗАПИСЫВАЕТСЯ НАЗВАНИЕ 
;ДРАЙВЕРА (ДО 80-ТИ СИМВОЛОВ), 
;ЗАКАНЧИВАЕТСЯ КОДОМ #00 

D_NAME   LD HL,NAME_MODEM 
         LD BC,ENAME_MODEM-NAME_MODEM 
         LD DE,(MOD_BUFF1) 
         LDIR 
         RET 

NAME_MODEM 
        DB "Vicomm-modem driver v1.04  "
        DB "(C) 1997 *MAS*",0 
GENAME_MODEM 

;ЧТЕНИЕ СТАТУСА ЛИНИИ/ДРАЙВЕРА 
;in: [A] ЦВЕТ БОРДЮРА 

;out: [A] - ДАННЫЕ ДЛЯ ПОРТА #FE 
;     Т.К. ЦВЕТ БОРДЮРА + БИТЫ УПРАВЛЕНИЯ
  ;     ЛИНИЕЙ И ВЫХОД ЛИНИИ 
;[B]: ПО БИТАМ: 
;   0 СТАТУС ЛИНИИ (СВОБОДНА/ЗАНЯТА) 
;   3 наличие тонового набора 
,;   4 ВЫКЛЮЧАТЬ TUBRO ПРИ ВЫЗОВЕ ДРАЙВЕРА
;   5 УПРАВЛЕНИЕ ЛИНИЕЙ 
;   6 НАЛИЧИЕ ТАЙМЕРА 
;   7 НАЛИЧИЕ ЧАСОВ 

D_RD_STAT 
        AND 7: LD C,A 
        LD A,(LINE_STATUS) 
        LD B,A: OR C 
        OUT (#FE),A: LD (BORDER_0+1),A 
        OR  #08: LD (BORDER_1+1),A 
        PUSH AF 
        XOR A: INC B: DEC B 
        JR Z,$+3: INC A 

 IF CALLING 

;ВЫКЛЮЧАТЬ TURBO, ЕСТЬ УПРАВЛЕНИЕ ЛИНИЕЙ,
;НЕТ ЧАСОВ, ТАЙМЕРА 

 OR %00110000 
      ELSE 

;ВЫКЛЮЧАТЬ TURBO, НЕТ УПРАВЛЕНИЯ ЛИНИЕЙ,
;НЕТ ЧАСОВ, ТАЙМЕРА, ТРУБКА СНЯТА 

 OR %00010001 
      ENDIF 
        LD B,A 
        POP AF: RET 

;СНЯТЬ ТРУБКУ 

D_ON_LINE  LD A,#20: JR ON_OFF_L 

;ПОЛОЖИТЬ ТРУБКУ 

D_OFF_LINE XOR A 

ON_OFF_L 

;ИЗМЕНЕНИЕ СТАТУСА ЛИНИИ, ЕСЛИ НЕТ 
;УПРАВЛЕНИЯ ЛИНИЕЙ - СРАЗУ ВОЗВРАТ 

 IF CALLING 
         LD (LINE_STATUS),A 
         LD B,A: LD A,(BORDER_0+1) 
         OR B: LD (BORDER_0+1),A 
         OUT (#FE),A 
         OR #08: LD (BORDER_1+1),A 
      ENDIF 
         RET 

;НАБРАТЬ НОМЕР, ТРУБКА УЖЕ СНЯТА 
; in: 
; [HL] - АДРЕС БУФЕРА С НОМЕРОМ 
;  [A] - 0 pulse,  1 tone 
;  [B] период импульса ms, станд. 100 
;  [C] межцифровая пауза ms/10 
; CY=1 ОШИБКА, КОД ОШИБКИ В [A]: 
;    0 ОШИБКА НАБОРА 
;    1 НЕТ ДЛИННОГО ГУДКА 
;    2 ПРЕРЫВАНИЕ 
; CY=0 СОЕДИНЕНИЕ УСТАНОВЛЕНО: 
;ВОЗВРАЩАЮТСЯ ДАННЫЕ КАК ИЗ SET_CON_P 

D_CALL 
      IF CALLING 
        LD (ID_CALL+1),HL 
        PUSH BC 

;УСТАНАВЛИВАЕМ П/П СКАНИРОВАНИЯ ЛИНИИ 

 XOR A: CALL D_WAIT_INS 
        POP BC 
        LD A,C: LD (CDEL_COD+1),A 

;ЗАДАН ПЕРИОД ИМПУЛЬСА, ПО СТАНДАРТУ 
;ДАЕТСЯ 2/5 ПЕРИОДА ЛИНИЯ ЗАМКНУТА, 
;И 3/5 ПЕРИОДА ЛИНИЯ РАЗОМКНУТА 
;ДЕЛИМ ПЕРИОД НА 5 

 LD A,B: LD B,0 
DIV5SUB 5: JR C,EDIV5 
        INC B: JR DIV5 
-EDIV5   INC B: DEC B: JR NZ,$+3: INC B 

 LD A,B: RLCA;2/5 ПЕРИОДА 
        LD (CDEL_1+1),A 

 ADD A,B         ;3/5 ПЕРИОДА 
        LD (CDEL_0+1),A 

;ЗАПИСЫВАЕМ ЗНАЧЕНИЯ ВЫДАВАЕМЫЕ В ПОРТ 
;#FE ДЛЯ ЗАМЫКАНИЯ/РАЗМЫКАНИЯ ЛИНИИ 

 LD A,(BORDER_0+1): AND #0F 

        LD (DCAL_OFF+1),A 
        LD A,(BORDER_1+1): OR #20 
        LD (DCAL_ON+1),A 

;МАСЕНЬКАЯ ПАУЗА ПЕРЕД НАБОРОМ 

 EI: LD B,45: HALT: DJNZ $-1 

;КОНТРОЛЬ ДЛИННОГО ГУДКА: 
;ДЕЛАЕМ 200 ОПРОСОВ ЛИНИИ, ЕСЛИ 
;ПОЙМАЕМ 30 РАЗ ПОДРЯД ГУДОК - ВСЕ ОК 

 LD BC,200*256+30 
WAIT_DIA CALL WAIT_CALL 
          JR C,DWAIT_R   ;ГУДКА НЕТ 
         DEC C 
         JR Z,ID_CALL ;ПОЙМАЛИ 30 РАЗ 
         JR DWAIT_N 

;ЕСЛИ ХОТЬ РАЗ ПОТЕРЯЛИ ГУДОК - УСТАНО- 
;ВИТЬ СЧЕТЧИК ЗАНОВО НА 30 

DWAIT_R  LD C,30 

;ЕСЛИ НАЖАТ ПРОБЕЛ - ВЫХОД 

DWAIT_N LD A,#7F: IN A,(#FE) 
        RRCA: JR NC,BRK_CALL 
        DJNZ WAIT_DIA 

NO_DIAL   LD A,1: SCF: RET ;НЕТ СИГНАЛА
-BRK_CALL  LD A,2: SCF: RET ;ПРЕРЫВАНИЕ 
ERRD_CALL XOR A: SCF: RET  ;ОШИБКА 

ED_CALL XOR A: RET ;НОМЕР НАБРАН

;СОБСТВЕННО НАБОР НОМЕРА
/ID_CALL LD HL,0 ;ЗДЕСЬ БУДЕТ АДРЕС БУФЕРА
        LD A,(HL): INC HL
        LD (ID_CALL+1),HL

 OR A         ;НАЙДЯ КОД #00
        JR Z,ED_CALL ;ОКАНЧИВАЕМ НАБОР
;СИМВОЛЫ "-", "(", ")", " " ИГНОРИРУЕМ
        CP "-": JR Z,ID_CALL
        CP "(": JR Z,ID_CALL
        CP ")": JR Z,ID_CALL
        CP " ": JR Z,ID_CALL

 SUB "0": JR C,ERRD_CALL
;ЕСЛИ ЭТО НЕ ЦИФРА - ОШИБКА НАБОРА

;ЦИФРЕ "0" СООТВЕТСТВУЕТ 10 ИМПУЛЬСОВ
        JR NZ,$+4: LD A,10
        CP 11: JR NC,ERRD_CALL

 LD B,A

;НАБИРАЕМ ЦИФРУ, В [B] КОЛ-ВО ИМПУЛЬСОВ

CALL_C 

;РАЗРЫВ ЛИНИИ НА 60ms (3/5)

DCAL_OFF LD A,0
         OUT (#FE),A

CDEL_0   LD A,0
DDEL_0   LD C,248
         DEC C: JP NZ,$-1
         DEC A: JP NZ,DDEL_0

;ЗАМЫКАНИЕ ЛИНИИ И ПАУЗА 40ms (2/5)

DCAL_ON LD A,0
        OUT (#FE),A

;ЗАДЕРЖКА НА [A] ms
CDEL_1  LD A,0
DDEL_1  LD C,248
        DEC C: JP NZ,$-1
        DEC A: JP NZ,DDEL_1

  DJNZ CALL_C

;МЕЖЦИФРОВАЯ ПАУЗА В 600ms

CDEL_COD LD B,0
 W_CDEL  NOP: NOP: LD DE,1457
        DEC DE: LD A,D
        OR E: JP NZ,$-3
        DJNZ W_CDEL

;ЕСЛИ НАЖАТ ПРОБЕЛ - ВЫХОД,ИНАЧЕ НАБИРАЕМ
;НОМЕР ДАЛЬШЕ
        LD A,#7F: IN A,(#FE)
        RRCA: JR NC,BRK_CALL
 JR ID_CALL

;ОПРАШИВАЕМ ЛИНИЮ 32 РАЗА

WAIT_CALL DI: PUSH BC
        LD B,32
        LD HL,0

CALL_DIAL LD DE,#0B59
         CALL SCAN_IN
         CALL DELAY_37
          LD E,A: LD D,0: ADD HL,DE
         DJNZ CALL_DIAL

  POP BC
;КОНТРОЛЬ ГУДКА, СРЕДН. АРИФМ.
 ;ЗНАЧЕНИЕ ЧАСТОТЫ ЗА 32 ОПРОСА
        SRA H: RR L : SRA H: RR L
        SRA H: RR L : SRA H: RR L
        SRA H: RR L
        LD A,L: SRA A

;В ИНТЕРВАЛЕ 150...250 - ЧАСТОТА ГУДКА
        CP 150: RET C
        CP 250: CCF
        RET

        XOR A: SCF: RET
      ENDIF

;ВЫЗЫВАЕТСЯ ПРИ ОБНАРУЖЕНИИ ЗВОНКА.
;СНИМАЕТСЯ ТРУБКА. + ОПРЕДЕЛЕНИЕ НОМЕРА
;(ЗДЕСЬ ОТСУТСТВУЕТ!)

;out: MOD_BUFF НОМЕР В ASCII, В КОНЦЕ 0

D_AON   CALL D_ON_LINE
        LD B,75: EI: HALT: DJNZ $-2: DI
;ПОСЛЕ СНЯТИЯ ТРУБКИ ПАУЗА В 1.5 СЕК.
;ЕСЛИ ЕСТЬ АОН, ТО ОН МОЖЕТ ОПРЕДЕЛИТЬ
;НОМЕР

;ПЕРЕБРАСЫВАЕМ В БУФЕР МОДЕМА СООБЩЕНИЕ
;О СНЯТИИ ТРУБКИ

MOD_BUFF3 EQU $+1: LD DE,#1111
         LD HL,AON_TXT
           LD BC,EAON_TXT-AON_TXT
         LDIR
         RET

AON_TXT 
DB 13,"Модем подключен к линии",13,0
EAON_TXT 

;ДВЕ НИЖЕСЛЕДУЮЩИЕ П/П ИСПОЛЬЗУЮТСЯ
;КАК "ЗАГЛУШКИ", Т.К. АЛГОРИТМ АВТОВЫБОРА
;СКОРОСТИ ЕЩЕ НЕ ОБДУМЫВАЛСЯ

;установить соединение, передача
;Т.Е. ВНАЧАЛЕ "ПЕРЕДАЕТ", ПОТОМ "СЛУШАЕТ"

SET_CON_A 

;установить соединение, прием
;Т.Е. ВНАЧАЛЕ "СЛУШАЕТ", ПОТОМ "ПЕРЕДАЕТ"

SET_CON_P 
MOD_BUFF4 EQU $+1: LD DE,0
      IF CALLING
        PUSH HL
         LD (HL),13: LD BC,1: XOR A
        CALL D_TRANSMIT
        POP HL
      ENDIF

;УСТАНОВКА СОЕДИНЕНИЯ
;В MOD_BUFF ВОЗВРАЩАЕТСЯ ТЕКСТОВОЕ СООБЩ.
 ;О СОЕДИНЕНИИ, В КОНЦЕ - КОД #00
;В [A] УСТАНОВЛЕНАЯ СКОРОСТЬ, ИЛИ #80
;ЕСЛИ ОНА НЕОПРЕДЕЛЕНА
         LD HL,CARR_TXT
         LD BC,ECARR_TXT-CARR_TXT
         LDIR
         LD A,#80 ;СКОРОСТЬ НЕОПРЕДЕЛЕНА
         RET

CARR_TXT 
DB 13,"Соедение установлено",13,0
ECARR_TXT 

;ИНСТАЛЯЦИЯ ПЕРЕД СКАНИРОВАНИЕМ ЛИНИИ
;В [A] - ГРОМКОСТЬ ВЫВОДА ЗВУКА ЛИНИИ
;ЧЕРЕЗ AY

D_WAIT_INS 
        LD HL,#4000: LD (LCOUNT_A),HL
        AND #0F: SET 6,A: LD (SOUND+1),A
        JR Z,D_WAIT_INS1
         LD BC,#FFFD: LD A,7: OUT (C),A
         LD B,#BF: LD A,#3F: OUT (C),A

GD_WAIT_INS1 
        XOR A: LD (LAST_DT+1),A
        LD (COUNT0+1),A
        LD (COUNT1+1),A
        LD (LAST0+1),A
        LD (OFFCOU+1),A
        RET

;СКАНИРОВАНИЕ ЛИНИИ
;out
;CY=1: НИЧЕГО
 ; [B] КОЛИЧЕСТВО ЦИКЛОВ ОПРОСА
; [C] СКОЛЬКО ВСЕГО ДОЛЖНО БЫТЬ ЦИКЛОВ
;CY=0, ;[A] - КОД:
;1 - ПРИНЯТ БЛОК, 2 - ЗАНЯТО, 3 - ВЫЗОВ

D_WAIT_LN 
        DI
        LD HL,0
        LD DE,16*256+12  ;11

 LD BC,#FFFD: LD A,8: OUT (C),A
        LD A,(SOUND+1)
        AND #0F: JR Z,$+4: LD B,#BF

;ОПРОС ЛИНИИ НА СИНХРОСИГНАЛ БЛОКА И
;КОНТРОЛЬ КОТОТКИХ ГУДКОВ, ВНАЧАЛЕ
;СЧИТАЕМ СРЕДН. АРИФМ. ЗНАЧЕНИЕ ЧАСТОТЫ
;ЗА 16 ОПРОСОВ

MDLOOP1 
        IN A,(#FE): AND #20

;АНАЛИЗ/НЕТ СИГНАЛА ВЫЗОВА
    IF CALLING
        JP Z,RING : ELSE: JP $+3
    ENDIF

 CALL IN_LINE_F
        CP 11: JP C,MDLOOP2
        CP 89: JP NC,MDLOOP2
        DEC E: JP Z,INPUT_BLOCK ;БЛОК?
MDLOOP2 ADD A,L: LD L,A
        JR NC,$+3: INC H
        DEC D: JP NZ,MDLOOP1

 SRA H: RR L : SRA H: RR L
        SRA H: RR L : SRA H: RR L
        LD A,L: SRA A

;В ИНТЕРВАЛЕ 150...250 - ЧАСТОТА ГУДКА
;ЕСЛИ ГУДОК, ТО А=1 ИНАЧЕ А=0
        CP 150: JR C,TST_OFFL
        CP 250: JR C,TST_ONL

TST_OFFL XOR A: JR $+4
TST_ONL  LD A,1

;ЕСЛИ СОСТ. СИГНАЛА НЕ ИЗМЕНИЛОСЬ, ТО
 ;ПРОСТО УВЕЛИЧИТЬ СЧЕТЧИК. ЕСЛИ
;ИЗМЕНИЛОСЬ, ТО ПЕРЕКЛЮЧИТЬ СЧЕТЧИК

LAST_DT  CP 0: LD (LAST_DT+1),A
         JR NZ,NEW_DTL

ADR_IDL  LD HL,0: INC (HL): LD A,(HL)
         CP 200: CALL NC,D_WAIT_INS1
         JR E_WAIT_LN

;ЧАСТОТА ИЗМЕНИЛАСЬ:

NEW_DTL DEC A: JR Z,NEW_DTL1

;СТАЛА "0": ПЕРЕКЛЮЧАЕМ СЧЕТЧИК
         LD HL,COUNT0+1 : LD (HL),0
        LD (ADR_IDL+1),HL

;ЕСЛИ БЫЛО >4 ЕДЕНИЦ И >4 НУЛЕЙ ПОДРЯД,
;ЗНАЧИТ ПОЙМАЛИ СИГНАЛ ОТБОЯ

COUNT1  LD A,0: CP 4: JP C,RES_OFF
LAST0   LD A,0: CP 4: JP C,RES_OFF

;ПОЙМАВ 3 ГУДКА ОТКЛЮЧИТСЯ

OFFCOU  LD A,0: INC A: LD (OFFCOU+1),A
        CP 3: JR Z,BUSY_FOUND

;ЧАСТОТА СТАЛА "1": ПЕРЕКЛЮЧАЕМ СЧЕТЧИК
NEW_DTL1 LD HL,COUNT1+1 : LD (HL),0
        LD (ADR_IDL+1),HL
COUNT0  LD A,0
JPE_WAIT_LN LD (LAST0+1),A
        JR E_WAIT_LN

RES_OFF XOR A
        LD (COUNT0+1),A
        LD (COUNT1+1),A
        JR JPE_WAIT_LN

BUSY_FOUND
        OR A: LD A,2 ;CY=0, A=2 "ЗАНЯТО"
        RET

;ПОЙМАЛИ ЗВОНОК, ПРОВЕРКА НА РЕАЛЬНОСТЬ
;ЕСЛИ СИГНАЛ ВЫЗОВА БУДЕТ ДЕРЖАТЬСЯ
;НЕКОТОРОЕ ВРЕМЯ - ЗНАЧИТ ВЫЗОВ В НАТУРЕ

GRINGLD C,2
RING1   LD B,30: NOP: DJNZ $-1
        IN A,(#FE): AND #20
        JP NZ,D_WAIT_LN
        DEC C: JR NZ,RING1

;ЗВОНОК ЕСТЬ, ОЖИДАЕМ ЕГО ОКОНЧАНИЯ

RING2   IN A,(#FE): AND #20: JR Z,RING2
         LD A,3   ;CY=0, A=3 "ЗВОНОК"
        RET

E_WAIT_LN LD BC,#0101
          SCF: RET  ;CY=1 - НИЧЕГО НЕТ

;ОПРОС ВХОДА: В [A] БУДЕТ ЧАСТОТА СИГНАЛА

IN_LINE_F PUSH HL: PUSH DE
          LD DE,(LCOUNT_A)
SOUND     LD L,0

LP_TIN_F INC E: JP Z,END_TIN_F
         INC E: JP Z,END_TIN_F
         IN A,(#FE)
          RLCA: RLCA: SBC A,A
          AND L: NOP: OUT (C),A
         AND #40: XOR D: JP Z,LP_TIN_F
        XOR D: LD (LCOUNT_B),A

RET_TIN_F LD A,E
        LD HL,LCOUNT_A
        SUB (HL): LD (HL),A
        LD A,E
        POP DE: POP HL
        RET

END_TIN_F DEC E: JR RET_TIN_F

;ПОЙМАЛИ СИНХРОСИГНАЛ, ПРИНИМАЕМ БЛОК

INPUT_BLOCK
        CALL IN_LINE
        CALL CONTR_SPEED
        CALL LOAD_DATA
        RET C

E_LOAD_DT LD A,(SPEED): LD D,A
BLOCK_LEN EQU $+1: LD BC,#0101
;CY=0   УСПЕШНО ПРИНЯЛИ БЛОК
          OR A: LD A,1
          RET

;ДОЛГОЕ ОЖИДАНИЕ БЛОКА И ПРИЕМ БЛОКА

;out: CY НЕТ БЛОКА
;[BC] ДЛИНА
;[D] СКОРОСТЬ (#80 НЕОПРЕДЕЛЕНА)

D_WAIT_BLC
        CALL WAIT_LINE: RET C
        CALL LOAD_DATA
        CALL LINE_FREE
        JR E_LOAD_DT

SCAN_IN CALL IN_LINE
        CP D : RET C
 CP E : CCF
        RET

IN_LINE_D;ЗАДЕРЖКА И ОПРОС ВХОДА
        PUSH BC : POP BC

;ОПРОС ВХОДА

IN_LINE PUSH HL : PUSH BC
        LD A,(LCOUNT_A) : LD C,A
        LD A,(LCOUNT_B) : LD B,A

LOOP_TIN INC C : JP Z,END_TIN
         IN A,(#FE) : AND #40
         XOR B : JP Z,LOOP_TIN

 XOR B : LD (LCOUNT_B),A
RET_TIN LD A,C
        LD HL,LCOUNT_A
        SUB (HL) : LD (HL),A
        LD A,C
        POP BC : POP HL
        RET

END_TIN DEC C: JR RET_TIN



;ОЖИДАНИЕ ОСВОБОЖДЕНИЯ ЛИНИИ, ВЕРТИМСЯ
;ЗДЕСЬ ДО ТЕХ ПОР, ПОКА ИЗ ЛИНИИ НЕ
;ИСЧЕЗНЕТ СИГНАЛ АБОНЕНТА

LINE_FREE
        PUSH AF : PUSH DE : PUSH BC
          XOR A : LD (LCOUNT_A),A
         LD A,#40 : LD (LCOUNT_B),A
        CALL IN_LINE
        LD DE,#0570;#0180
        NOP : NOP
LN_FREE1 NOP: NOP
LN_FREE2 LD B,4
         CALL DELAY_37
         CALL SCAN_IN
         NOP : JP  NC,LN_FREE1

LN_FREE3 DEC B : JP Z,LINE_FREE4
        CP #FF : JP NC,LINE_FREE4
        CALL DELAY_37
        CALL SCAN_IN
        JP C,LN_FREE3
        JP LN_FREE2

;ЛИНИЯ СВОБОДНА

LINE_FREE4
        POP BC : POP DE : POP AF
        RET



;СИНХРОНИЗАЦИЯ БЛОКА

SYNCD1_IN JP SYNC1_IN
SYNC1_IN  NOP : NOP
        CALL IN_LINE
        CP D : JP C,ED_SYNCD
        CP E : CCF: JP C,ED_SYNCD
        LD B,A
        LD A,(HL)
        OR A : JP Z,SYNC1_I1
        ADD A,B : LD (HL),A
SYNC1_I2 ADD A,0
         INC HL
         RET
SYNC1_I1 JP SYNC1_I2

SYNCD2_IN JP SYNC2_IN
SYNC2_IN  NOP : NOP
SYNC2_I6 CALL IN_LINE
        CP D: JP C,ED_SYNCD
        CP E: CCF: JP C,ED_SYNCD
        LD B,A
        LD A,(HL)
        SUB B
        JP P,SYNC2_I2
        XOR A : ADD A,0
SYNC2_I1 LD (HL),A
         INC HL
         RET
SYNC2_I2 JP SYNC2_I1

ED_SYNCD INC SP : INC SP
  ERR_SYNC POP DE : POP HL
         RET

;ЗАДЕРЖКИ НА СООТВ. ЧИСЛО ТАКТОВ
DELAY_57   JP DELAY_47
DELAY_47   JP DELAY_37
DELAY_37   CALL DELAY_27
DELAY_27   RET
DELAY_68   CALL DELAY_27
           CALL DELAY_27
           NOP
           RET

DELAY_C LD B,#FE
         CALL DELAY_47
         CALL DELAY_47
        DJNZ DELAY_C+2
        DEC C : JP NZ,DELAY_C
        RET

;ОЖИДАНИЕ СИГНАЛА АБОНЕНТА
;В ТЕЧЕНИИ 2...4 СЕКУНД

WAIT_LINE DI
          LD HL,#4000 : LD (LCOUNT_A),HL
          LD D,3
WT_LINE1 LD E,3
WT_LINE2 LD H,1
WT_LINE3 LD B,16
         LD A,H: LD (LCOUNT_C),A
LP_WLINE CALL IN_LINE
         CP MIN+1 : JP C,WAIT_CONTR1
         CP #2D : JP C,WAIT_SPEED
WAIT_CONTR
         ADD A,L : JP NC,$+4 : INC H
         ADD A,6 : LD L,A
         JP NC,$+4 : INC H
         LD A,(LCOUNT_C)
         CP H
         JP C,WT_LINE3
         JP Z,WAIT_CONTR2
           DEC E : JP NZ,WT_LINE2
           DEC D : JP NZ,WT_LINE1
         SCF: RET

WAIT_CONTR1 CP 5
        JP WAIT_CONTR

WAIT_CONTR2 LD B,16
        JP LP_WLINE

;КОНТРОЛЬ СКОРОСТИ ПРИ ОЖИДАНИИ

WAIT_SPEED
        CALL DELAY_47: NOP
        DEC B : JP NZ,LP_WLINE
        CALL IN_LINE

;КОНТРОЛЬ СКОРОСТИ. ПОДСЧЕТ ЧИСЛА ИМПУЛЬ-
;СОВ => СКОРОСТЬ

CONTR_SPEED
 LD BC,#0006
        CALL IN_LINE
        CALL IN_LINE_D
        CALL IN_LINE_D
        PUSH BC: POP BC
C_SPEED CALL IN_LINE
        ADD A,B: LD B,A
        DEC C: JR NZ,C_SPEED
        OR A
        RET

;ПРИЕМ БЛОКА.
;В [B] ЧИСЛО ИМПУЛЬСОВ (СКОРОСТЬ)

LOAD_DATA
MOD_BUFF1 EQU $+1: LD HL,0
        LD A,195 : CP B
         JP C,INP_600;?>195 = 600
        LD A,130 : CP B
        JP C,INP_1800   ;?>130 = 1800
        LD A,60 : CP B
        CCF : RET C     ;?>060 = 2400

INP_FAST LD A,2
         LD (SPEED),A ;СКОРОСТЬ 2400
        CALL SYNC_FAST: RET C
        CALL LOAD_BYTE_FAST : RET C

;ПРИНИМАЕМ ДЛИНУ БЛОКА: ЕСЛИ 0-Й БАЙТ
;РАВЕН #08, ТО ДЛИНА БЛОКА БОЛЬШЕ НА
;256 БАЙТ
        LD (ZERO_BYTE),A
        LD D,0
        CP 8: JR NZ,$+3: INC D
;ПРИНИМАЕМ МЛ. БАЙТ ДЛИНЫ
         CALL LOAD_BYTE_FAST: RET C
        LD E,A: OR A: JR NZ,$+3: INC D

 LD (BLOCK_LEN),DE

;ПРИНИМАЕМ САМ БЛОК, ДЛИНОЙ [DE]

GLD_BLOCK_FAST
        CALL LOAD_BYTE_FAST: RET C
        LD (HL),A
        INC HL: DEC DE
        LD A,D: OR E
        JP NZ,LD_BLOCK_FAST
        RET

;ПРИНИМАЕМ БАЙТ

LOAD_BYTE_FAST
        LD B,8   ;ЛОВИМ 8 БИТ
        CALL IN_LINE

;ОПРЕДЕЛИЛИ ЧАСТОТУ, ЕСЛИ ОНА ЛЕЖИТ
;В ДИАПАЗОНЕ ОТ MIN ДО MAX, ТО БИТ
;ПРИНЯТ, ИНАЧЕ - ОШИБКА
        CP MIN : JP C,ERR_LDFAST
        CP MAX : JP NC,ERR_LDFAST

;ЕСЛИ ЧАСТОТА БОЛЬШЕ ZERO - ПРИНЯЛИ
;ЕДИНИЦУ, МЕНЬШЕ - НУЛЬ
        CP ZERO
        RL C ;ПРИНЯЛИ ИНВЕРСНЫЙ БИТ
        CALL DELAY_47   ;27
        DEC B: JP Z,_LOAD_BYTE_FAST
        CALL IN_LINE
        JP LOAD_BYTE_FAST+2

_LOAD_BYTE_FAST
        LD A,C: CPL ;ИНВЕРТИРОВАЛИ БАЙТ
        PUSH AF
        CALL IN_LINE
        POP AF
          OR A: RET ;БАЙТ ПРИНЯТ

ERR_LDFAST        ;ОШИБКА ПРИЕМА
        POP BC
        SCF: RET

;СИНХРОНИЗАЦИЯ БЛОКА

SYNC_FAST PUSH HL
        LD DE,MIN*256+ZERO-3
        LD A,1
        LD (SYNC_TABL),A
        LD (SYNC_TABL+1),A
        CALL IN_LINE
        CALL DELAY_68
         CALL IN_LINE
S1_FAST CALL DELAY_57
        CALL IN_LINE
        CP D : JP C,ERR_SYNC
        CP E : JP C,S1_FAST

 LD E,MAX-1
        LD HL,SYNC_TABL
        CALL DELAY_47
        CALL IN_LINE
        CALL DELAY_68
        CALL DELAY_27
         CALL IN_LINE
         CALL DELAY_68
         CALL DELAY_27
        CALL IN_LINE
        CALL DELAY_47
        CALL DELAY_27
        CALL SYNCD1_IN
        CALL SYNCD1_IN
         LD HL,SYNC_TABL
         CALL SYNC2_IN
         CALL SYNCD2_IN
        LD HL,SYNC_TABL
        CALL SYNC1_IN
        CALL SYNCD1_IN
         LD HL,SYNC_TABL
         CALL SYNC2_IN
         CALL SYNCD2_IN
        LD HL,SYNC_TABL
        CALL SYNC1_IN
        LD C,A
        CALL SYNCD1_IN
        CP C : JP M,S2_FAST
         CALL IN_LINE
         CALL DELAY_68
         CALL DELAY_27

S2_FAST CALL IN_LINE
        CALL DELAY_68
        CALL IN_LINE
        POP HL
        OR A: RET



;!!! ДЛЯ СКОРОСТЕЙ 1800/600 ВСЕ ОЧЕНЬ
;ПОХОЖЕ, ПОЭТОМУ БЕЗ КОМЕНТАРИЕВ

INP_1800 LD A,1: LD (SPEED),A
        CALL IN_LINE
        CALL DELAY_27
        NOP
        LD DE,#0E19
        LD DE,#0E19
        CALL SYNC_1800: RET C
        LD D,0
         CALL LOAD_BYTE_1800: RET C
        LD (ZERO_BYTE),A
        CP 8: JR NZ,$+3: INC D
;ПРИНИМАЕМ ДЛИНУ
         CALL LOAD_BYTE_1800: RET C
        LD E,A: OR A: JR NZ,$+3: INC D

 LD (BLOCK_LEN),DE
GLD_BLOCK_1800
        CALL LOAD_BYTE_1800: RET C
        LD (HL),A
        INC HL: DEC DE
        LD A,D: OR E
        JP NZ,LD_BLOCK_1800
        RET

LOAD_BYTE_1800
         PUSH BC : LD B,8
        CALL IN_LINE
          CP #0F: JP C,ERR_LD1800
        CP #2D: JP NC,ERR_LD1800
        CP #1C: LD A,C: RLA: LD C,A
        CALL DELAY_27
          DEC B: JP NZ,NEXT_B1800
        LD A,C: CPL
        POP BC
        PUSH AF
        CALL IN_LINE
        POP AF
        OR A: RET

NEXT_B1800
        CALL DELAY_27
        NOP
        CALL IN_LINE
        CALL DELAY_57
        CALL DELAY_27
        NOP
        JP LOAD_BYTE_1800+3

ERR_LD1800
        POP BC
        SCF: RET

SYNC_1800 PUSH HL
        PUSH DE
        OR A : LD A,1
        LD (SYNC_TABL),A
        LD (SYNC_TABL+1),A
        CALL IN_LINE
        CALL DELAY_68
        CALL DELAY_27
        NOP
        CALL IN_LINE
        CALL DELAY_27
        NOP
S1_1800 CALL DELAY_68
        CALL IN_LINE
        NOP
        CP D: JP C,ERR_SYNC
        CP E: JP C,S1_1800
        LD DE,#0E2E
        LD HL,SYNC_TABL
        CALL DELAY_47
        CALL IN_LINE
        CALL DELAY_68
        CALL DELAY_27
        NOP
        CALL IN_LINE
        CALL DELAY_68
        CALL DELAY_27
        NOP
        CALL IN_LINE
        CALL DELAY_47
        CALL DELAY_27
        ADD A,0
        CALL SYNCD1_IN
        CALL SYNCD1_IN
        LD HL,SYNC_TABL
        CALL SYNC2_IN
        CALL SYNCD2_IN
        LD HL,SYNC_TABL
        CALL SYNC1_IN
        CALL SYNCD1_IN
        LD HL,SYNC_TABL
        CALL SYNC2_IN
        CALL SYNCD2_IN
        LD HL,SYNC_TABL
        CALL SYNC1_IN
        LD C,A
        ADD A,0
        CALL SYNCD1_IN
        CP C: JP M,S2_1800
        CALL IN_LINE
        CALL DELAY_68
        CALL DELAY_27
        NOP
S2_1800 CALL IN_LINE
        CALL DELAY_68
CALL DELAY_27
        NOP
        CALL IN_LINE
        POP DE
        POP HL
        OR A: RET

INP_600 XOR A: LD (SPEED),A
        CALL IN_LINE
        NOP
        LD DE,#1728
        LD DE,#1728
        CALL SYNC_600: RET C
        LD D,0
         CALL LOAD_BYTE_600 : RET C
        LD (ZERO_BYTE),A
          CP 8: JR NZ,$+3 : INC D
;ПРИНИМАЕМ ДЛИНУ
         CALL LOAD_BYTE_600 : RET C
        LD E,A: OR A: JR NZ,$+3: INC D

 LD (BLOCK_LEN),DE
LD_BLOCK_600
        CALL LOAD_BYTE_600: RET C
        LD (HL),A
        INC HL: DEC DE
        LD A,D: OR E
        JP NZ,LD_BLOCK_600
        RET



GLOAD_BYTE_600
         PUSH DE: PUSH BC
         LD B,8
        CALL IN_LINE
        CALL IN_LINE
        CALL IN_LINE
        CP #17: JP C,ERR_LD600
          CP #59: JP NC,ERR_LD600
        CP #2D: LD A,E: RLA: LD E,A
        CALL IN_LINE
        DEC B
        JP NZ,LOAD_BYTE_600+4
        LD A,E: CPL: OR A
        POP BC
        POP DE
        RET

ERR_LD600 POP BC: POP DE
        SCF: RET

SYNC_600 PUSH HL: PUSH DE
        OR A
        LD A,1
        LD (SYNC_TABL),A
        LD (SYNC_TABL+1),A
        LD (SYNC_TABL+2),A
        LD (SYNC_TABL+3),A
        CALL IN_LINE
        CALL DELAY_68
        CALL DELAY_27
        NOP
        CALL IN_LINE
        CALL DELAY_27
        NOP
S1_600  CALL DELAY_68
        CALL IN_LINE
        NOP
        CP D: JP C,ERR_SYNC
        CP E: JP C,S1_600
        LD A,(SYNC_TABL+4)
        INC A
        LD (SYNC_TABL+4),A
        ADD A,0
        LD DE,#0E59
        LD HL,SYNC_TABL
        CALL SYNCD1_IN
        CALL SYNCD1_IN
        CALL SYNCD1_IN
        CALL SYNCD1_IN
        LD HL,SYNC_TABL
        CALL SYNC2_IN
        CALL SYNCD2_IN
        CALL SYNCD2_IN
        CALL SYNCD2_IN
        LD HL,SYNC_TABL
        CALL SYNC1_IN
        CALL SYNCD1_IN
        CALL SYNCD1_IN
        CALL SYNCD1_IN
        LD HL,SYNC_TABL
        CALL SYNC2_IN
        CALL SYNCD2_IN
        CALL SYNCD2_IN
        CALL SYNCD2_IN
        LD HL,SYNC_TABL
        CALL SYNC1_IN
        CALL SYNCD1_IN
        CALL SYNCD1_IN
        CALL SYNCD1_IN
        LD HL,SYNC_TABL
        CALL SYNC2_IN
        CALL SYNCD2_IN
        CALL SYNCD2_IN
        CALL SYNCD2_IN
        LD HL,SYNC_TABL
        CALL SYNC1_IN
        CALL SYNCD1_IN
        CALL SYNCD1_IN
        CALL SYNCD1_IN
        LD HL,SYNC_TABL
        CALL SYNC2_IN
        LD C,A
        ADD A,0
        CALL SYNC2_IN
        CP C : JP M,EXT_SYNC
        LD C,A
        CALL SYNC2_I6
        CP C : JP M,EXT_SYNC
        LD C,A
        CALL SYNC2_I6
        CP C : JP M,EXT_SYNC
        OR A : JP Z,ERR_SYNC
        NOP
        CALL IN_LINE
EXT_SYNC POP DE: POP HL
  OR A: RET



;ПЕРЕДАЧА БЛОКА ИЗ MOD_BUFF,
;BC - ДЛИНА, A - СКОРОСТЬ

D_TRANSMIT DI
        LD (SPEED),A
        PUSH AF  ;СОХРАНИЛИ СКОРОСТЬ
MOD_BUFF2 EQU $+1: LD HL,0
        DEC HL
        PUSH HL
        ADD HL,BC
        EX (SP),HL ;АДРЕС ПОСЛ. БАЙТА

 LD (HL),C  ;МЛ. БАЙТ ДЛИНЫ
        DEC HL: LD (HL),7
        LD A,B: OR A
        JR Z,$+3: INC (HL) ;ДЛИНА >256

 DEC HL
        LD (HL),#AA ;СИНХРО БАЙТ
        XOR A
        DEC HL: LD (HL),A
        DEC HL: LD (HL),A
        DEC HL

 POP DE  ;АДРЕС ПОСЛЕДНЕГО БАЙТА
         POP AF  ;СКОРОСТЬ ПЕРЕДАЧИ
          OR  A: JP Z,TRANSMIT_600
        DEC A: JP Z,TRANSMIT_1800

GTRANSMIT_FAST
        XOR  A: LD (HL),A ;СИНХРО
        DEC HL: LD (HL),A
        DEC HL: LD (HL),A
        DEC HL: LD (HL),A
        DEC HL: LD (HL),A

 CALL LINE_FREE
        PUSH BC
        LD C,1: CALL DELAY_C
        POP BC
        CALL OUTPUT_FAST
        PUSH HL
        PUSH DE
        LD BC,#020C
        LD A,0
        NOP: NOP: NOP: NOP
        JP OUT_BIT2600

;ПЕРЕДАЕМ БЛОК

OUTPUT_FAST
        LD A,(HL)         ;ПЕРЕДАЕМ БАЙТ
        CALL OU_BYTE_FAST
        NOP
        CALL COMP_HL_DE
        RET Z          ;БЛОК ПЕРЕДАН
        INC HL: JP OUTPUT_FAST

;ПЕРЕДАЧА БАЙТА

OU_BYTE_FAST
        PUSH HL: PUSH DE
        LD BC,#080C
OUT_BIT2600
        PUSH AF
        RLA : JP C,BIT1_FAST
          LD HL,D0_FAST : JP OB_FAST ;=0
.BIT1_FAST LD HL,D1_FAST : JP OB_FAST ;=1

;ПРИ ПЕРЕДАЧИ 1-ГО БИТА MIC OFF МЕНЬШЕ НА
;12 (168 ТАКТОВ),Т.К. 168 ТАКТОВ НУЖНЫ
;ДЛЯ ПОЛУЧЕНИЯ СЛЕД. БАЙТА,КОНТРОЛЯ,...

GOB_FAST RRA
        LD A,(HL)
          SUB C : LD D,A : INC HL
        LD E,(HL) : INC HL
        CALL OUT_DATA
        POP AF
        RLCA
        DEC B
        LD C,0 : LD C,0 : NOP
        JP NZ,OUT_BIT2600
        POP DE : POP HL
        RET

;ДЛЯ 1800/600 ПОХОЖЕ

GTRANSMIT_1800
        XOR  A : LD (HL),A
        DEC HL : LD (HL),A

 CALL LINE_FREE
        PUSH BC
        LD C,1 : CALL DELAY_C
        POP BC
        CALL OUTPUT_1800
        PUSH HL : PUSH DE
        LD BC,#020C
        LD A,0
        NOP : NOP : NOP : NOP
        JP OUT_BIT1800

OUTPUT_1800
        LD A,(HL)
        CALL OU_BYTE_1800
        NOP
        CALL COMP_HL_DE
        RET Z
        INC HL: JP OUTPUT_1800

OU_BYTE_1800
        PUSH HL: PUSH DE
        LD BC,#080C
OUT_BIT1800
        PUSH AF
        RLA: JP C,BIT1_1800
          LD HL,D0_1800: JP OB_1800 ;=0
-BIT1_1800 LD HL,D1_1800: JP OB_1800 ;=1

GOB_1800 RRA
        LD A,(HL)
        SUB C: LD D,A: INC HL
        LD E,(HL): INC HL
        CALL OUT_DATA
        POP AF
        RLCA
        DEC B
        LD C,0: LD C,0: NOP
        JP NZ,OUT_BIT1800
        POP DE: POP HL
        RET

TRANSMIT_600
        CALL LINE_FREE
        PUSH BC
        LD C,1: CALL DELAY_C
        POP BC
        CALL OUTPUT_600
        PUSH HL: PUSH DE
        LD BC,#020D
        LD A,0
        NOP: NOP: NOP: NOP
        JP OUT_BIT600

OUTPUT_600
        LD A,(HL)
        CALL OU_BYTE_600
        RLA: LD B,A
        CALL COMP_HL_DE
        LD A,B: RRA
        RET Z
        INC HL: JP OUTPUT_600

OU_BYTE_600
        PUSH HL: PUSH DE
        LD BC,#080D
        NOP

OUT_BIT600
        PUSH AF
        RLA: JP C,B71_600 ;B7=1
        RRA: JP C,B70_B01 ;B7=0,B0 =1
        LD HL,D00_600 ;B7 =0 B0 =0
        JP OB_600

B71_600 RRA: JP NC,B71_B01
        LD HL,D10_600   ;B7=1 B0=0
        JP OB_600

B71_B01 LD HL,D11_600   ;B7=1 B0=1
        JP OB_600

B70_B01 LD HL,D01_600   ;B7=0 B0=1
        JP OB_600

OB_600  NOP
        LD A,(HL)
        SUB C: LD D,A: INC HL
        LD  E,(HL): INC HL
        CALL OUT_DATA
        CALL OUT_DATA
        POP AF
        RLCA
        DEC B
        LD C,0: NOP : NOP
        JP NZ,OUT_BIT600
        POP DE:  POP HL
        RET

;СРАВНИТЬ HL И DE
COMP_HL_DE
        NOP
        LD A,H : CP D
        JP NZ,$+6: LD A,L: CP E: RET
        NOP: NOP: RET

;ПЕРЕДАЧА БИТА
OUT_DATA
        DEC D: JP NZ,$-1 ;ЗАДЕРЖКА D

BORDER_0 LD A,#00   ;MIC OFF
        JP $+3
        OUT (#FE),A
        DEC E: JP NZ,$-1 ;ЗАДЕРЖКА E

BORDER_1 LD A,#08   ;MIC ON
         JP $+3
         OUT (#FE),A
        LD D,(HL): INC HL
        LD E,(HL): INC HL
        NOP
        RET

;ТАБЛИЦЫ ДЛЯ ПЕРЕДАТЧИКА

;1800
D0_1800 DB 36,36+13 ;ЧАСТОТЫ ДЛЯ "0"
D1_1800 DB 67,67+13 ;ЧАСТОТЫ ДЛЯ "1"

D00_600 DB #36,#44,#40,#44 ;B7 =0 B0 =0
-D01_600 DB #57,#58,#49,#44 ;B7 =0 B0 =1
-D10_600 DB #57,#65,#61,#65 ;B7 =1 B0 =0
-D11_600 DB #36,#4D,#54,#65 ;B7 =1 B0 =1



D0_FAST DB 28,28+13  ;КОДА ПЕРЕДАЧИ "0"
D1_FAST DB 51,51+13  ;SPEED FAST"1"

MIN     EQU 11
MAX     EQU 37
GZEROEQU 23

;ЧИСЛО ТАКТОВ В ПЕРЕДАЧИ БИТА
;T=(2*D+13)*14+56  D - ЧИСЛО ИЗ ТАБЛИЦЫ
;D0_?? ДЛЯ ПЕРЕДАЧИ "0" И D1_?? ДЛЯ "1"

;СРЕДНЯЯ СКОРОСТЬ :
;SPEED=(SPD0+SPD1)/2
;SPD0=3500000/T0  SPD1=3500000/T1

;ПРИЕМНИК :
;ЕСЛИ С ЛИНИИ ПРИНЯТО ЧИСЛО < MIN -ОШИБКА
;ЕСЛИ > MAX : ОШИБКА
;ЕСЛИ MIN < ?? < ZERO : ПРИНЯЛИ "0"
;ЕСЛИ ZERO < ?? < MAX : ПРИНЯЛИ "1"

;ZERO=T0/46
;MAX=T1/46
;MIN=ZERO-11..13

;В ДАННОМ ДРАЙВЕРЕ П/П РАБОТЫ С ЧАСАМИ
;ОТСУТСТВУЮТ, И СТОЯТ "ЗАГЛУШКИ"

;УСТАНОВИТЬ ВРЕМЯ [A]-[B]-[C]

D_SET_TIME

;СЧИТАТЬ ВРЕМЯ [A]-[B]-[C]

D_RD_TIME

;ЕСЛИ ЧАСЫ ПОЛУАВТОНОМНЫ, ТО ЭТА П/П
  ;ПРОИЗВОДИТ ПОДСЧЕТ ВРЕМЕНИ

D_CAL_TIM

;УСТАНОВИТЬ ТАЙМЕР НА [BC] МИНУТ

D_SET_ALR

;ДОБАВИТЬ К ТАЙМЕРУ [BC] МИНУТ

D_ADD_ALR
 SCF: RET 

;ЕСЛИ ВРЕМЯ ИСТЕКЛО, CY=1 

D_RD_ALR XOR A: RET 

END

_______________________________




Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Смешно? - извесно, что все население Земли делится на ноpмальных людей и тех, кто занимается компьютеpом.
Совет от ГУРУ - Оптимизация программ по времени исполнения.
От редакции.

В этот день...   19 октября