3niti alpha simu1 - троичный компьютер на двоичном пике
Moderator: Shaos
-
- Retired
- Posts: 1474
- Joined: 03 Aug 2003 22:37
- Location: Moscow
-
- Admin
- Posts: 23989
- Joined: 08 Jan 2003 23:22
- Location: Silicon Valley
Очередь тут:Mac Buster wrote:Где тут очередь? Я первый?
https://www.tindie.com/products/trc/3niti-alpha-simu1/
Я тут за главного - если что шлите мыло на me собака shaos точка net
-
- Admin
- Posts: 23989
- Joined: 08 Jan 2003 23:22
- Location: Silicon Valley
-
- Admin
- Posts: 23989
- Joined: 08 Jan 2003 23:22
- Location: Silicon Valley
код троичного инвертирования (он просто переставляет местами чётные и нечётные биты):
код снятия состояние переключателей и высвечивания этого состояния на светодиодах:
P.S. вышеприведённый код работает не с представлением трита как пары битов с состояниями 01,00,10 а с состояниями 10,11,01 (т.е. двоично инвертированными)
P.P.S. подпрограмма tri_neg может корректно троично инвертировать как двоично-инвертированную триаду, так и "нормальную"
Code: Select all
tri_neg: ; 14 cycles with call
movwf tmp1 ; сохраняем байт в tmp1
andlw b'01010101' ; берём по маске каждый чётный бит
movwf tmp2 ; сохраняем в tmp2
movf tmp1,W ; берём tmp1 (то что пришло на вход функции)
andlw b'10101010' ; берём по маске каждый нечётный бит
movwf tmp1 ; сохраняем в tmp1
bcf STATUS,C ; пишем 0 во флаг C
rlf tmp2,F ; сдвигаем влево tmp2
rrf tmp1,W ; сдвигаем вправо tmp1 (надо бы перед ним тоже C обнулить, хотя с 3-тритными словами работать должно)
iorwf tmp2,W ; складываем по или с tmp2
return ; возвращаем байт с переставленными чётными и нечётными битами
Code: Select all
loop:
movlw b'11111110' ; устанавливаем бегущий ноль в нулевой бит
movwf mask ; сохраняем в переменной mask
movlw 5
movwf count ; устанавливаем счётчик в 5
loop0:
movf mask,w
movwf PORTA ; послать маску в порт A (чтобы выбрать 3 переключателя/светодиода)
_delay1 10,cnt1 ; ждём
movf PORTB,w ; читаем байт из порта B (состояние очередных 3 троичных переключателей)
call tri_neg ; троично инвертируем (переключатели вверх-ногами припаяны ;)
movwf PORTC ; посылаем получившееся в порт C (на очередные 3 троичных светодиода)
_delay1 0,cnt1 ; долго ждём
bsf STATUS,C ; флаг C = 1
rlf mask,f ; сдвигаем наш ноль влево
decfsz count,f ; декрементируем счётчик и проверяем, что он ещё не ноль
goto loop0 ; переход на следующую итерацию (из пяти)
goto loop ; после пяти итераций идём к началу...
P.P.S. подпрограмма tri_neg может корректно троично инвертировать как двоично-инвертированную триаду, так и "нормальную"
Last edited by Shaos on 19 Nov 2011 09:16, edited 4 times in total.
Я тут за главного - если что шлите мыло на me собака shaos точка net
-
- Admin
- Posts: 23989
- Joined: 08 Jan 2003 23:22
- Location: Silicon Valley
Ниже представлен код троичного инкремента триады из второго теста. Триада - это три трита, каждый из которых представлен двумя битами - младшим битом минуса и старшим битом плюса. Триады у нас перевёрнуты из-за особенностей схемы "3niti alpha simu1", т.е. в старших битах находится младший трит, а в младших - старший:
bit 0 - установлен в "1", если старший трит триады отрицателен
bit 1 - установлен в "1", если старший трит триады положителен
bit 2 - установлен в "1", если средней трит триады отрицателен
bit 3 - установлен в "1", если средней трит триады положителен
bit 4 - установлен в "1", если младшей трит триады отрицателен
bit 5 - установлен в "1", если младшей трит триады положителен
bit 6 - должен быть нулевым
bit 7 - должен быть нулевым
bit 0 - установлен в "1", если старший трит триады отрицателен
bit 1 - установлен в "1", если старший трит триады положителен
bit 2 - установлен в "1", если средней трит триады отрицателен
bit 3 - установлен в "1", если средней трит триады положителен
bit 4 - установлен в "1", если младшей трит триады отрицателен
bit 5 - установлен в "1", если младшей трит триады положителен
bit 6 - должен быть нулевым
bit 7 - должен быть нулевым
Code: Select all
; Ternary increment of W (used tmp1), C if overflow
tri_inc:
bcf STATUS,C ; обнуляем флаг переноса C (это у нас признак переполнения при возврате управления из подпрограммы)
movwf tmp1 ; сохраняем инкрементируемую триаду в переменной tmp1
; инкремент младшего трита:
tri_i5: btfss tmp1,5 ; проверяем взведён ли бит 5 в нашей триаде (это плюс младшего трита)
goto tri_i4 ; если взведён, то пропускаем переход на tri_i4 и идём дальше
bcf tmp1,5 ; обнуляем бит 5 (бит плюса младшей триады)
bsf tmp1,4 ; взводим бит 4 (бит минуса младшей триады)
goto tri_i3 ; в результате получаем -1 вместо +1 в младшей триаде и идём на инкремент следующего трита (перенос)
tri_i4: btfss tmp1,4 ; проверяем взведён ли бит 4 в нашей триаде (это минус младшего трита)
bsf tmp1,5 ; если не взведён (т.е. младший трит хранил 0), то устанавливаем бит 5 (плюс младшего трита)
bcf tmp1,4 ; обнуляем бит 4 (сбрасываем минус младшего трита, если он был)
movf tmp1,w ; копируем получившееся представление триады в аккумулятор W
return ; возвращаем управление из подпрограммы
; инкремент среднего трита:
tri_i3: btfss tmp1,3 ; проверяем взведён ли бит 3 в нашей триаде (это плюс среднего трита)
goto tri_i2 ; если взведён, то пропускаем переход на tri_i2 и идём дальше
bcf tmp1,3 ; обнуляем бит 3 (бит плюса средней триады)
bsf tmp1,2 ; взводим бит 2 (бит минуса средней триады)
goto tri_i1 ; в результате получаем -1 вместо +1 в средней триаде и идём на инкремент следующего трита (перенос)
tri_i2: btfss tmp1,2 ; проверяем взведён ли бит 2 в нашей триаде (это минус среднего трита)
bsf tmp1,3 ; если не взведён (т.е. средний трит хранил 0), то устанавливаем бит 3 (плюс среднего трита)
bcf tmp1,2 ; обнуляем бит 2 (сбрасываем минус среднего трита, если он был)
movf tmp1,w ; копируем получившееся представление триады в аккумулятор W
return ; возвращаем управление из подпрограммы
; инкремент старшего трита:
tri_i1: btfss tmp1,1 ; проверяем взведён ли бит 1 в нашей триаде (это плюс старшего трита)
goto tri_i0 ; если взведён, то пропускаем переход на tri_i0 и идём дальше
bcf tmp1,1 ; обнуляем бит 1 (бит плюса старшей триады)
bsf tmp1,0 ; взводим бит 0 (бит минуса старшей триады)
goto tri_ic ; в результате получаем -1 вместо +1 в старшей триаде и идём на взведение флага переноса (переполнение)
tri_i0: btfss tmp1,0 ; проверяем взведён ли бит 0 в нашей триаде (это минус старшего трита)
bsf tmp1,1 ; если не взведён (т.е. старший трит хранил 0), то устанавливаем бит 1 (плюс старшего трита)
bcf tmp1,0 ; обнуляем бит 0 (сбрасываем минус старшего трита, если он был)
movf tmp1,w ; копируем получившееся представление триады в аккумулятор W
return ; возвращаем управление из подпрограммы
; обработка переполнения:
tri_ic: movf tmp1,w ; копируем получившееся представление триады в аккумулятор W
bsf STATUS,C ; взводим флаг C (признак переполнения)
return ; возвращаем управление из подпрограммы
Я тут за главного - если что шлите мыло на me собака shaos точка net
-
- Admin
- Posts: 23989
- Joined: 08 Jan 2003 23:22
- Location: Silicon Valley
Рассмотрим принципиальную схему и печатную плату - там видно, что при положении переключателя "вверх" (плюс), контакт 4 подключен к 3, что передаёт сканирующий ноль порта A на чётные биты порта B, при положении "вниз" (минус) - контакт 1 подключен к 3, что задействует нечётные биты порта B, а при среднем положении (ноль) никуда не подключенный контакт 2 идёт на 3, что оставляет на обоих битах высокий уровень (т.к. на порту B включены внутренние подтягивающие резисторы), т.е.:
Как видно, чтобы превратить это представление в двоично закодированную троичность (binary coded ternary), нужно заменить пару 11 на пару 00:
Как видно бинарное инвертирование (comf var,w) и затем троичное инвертирование (call tri_neg) будет работать быстрее (как собственно и сделано во втором тесте с часами).
В случае светодиодов ноль в чётном бите подсвечивает красный светодиод (минус), а в нечётном - зелёный (плюс). А чтобы двойной светодиод не горел, оба бита должны содержать "1" (в случае обоих нулей светодиоды дадут жёлтый свет):
Значит чтобы отобразить триаду на светодиодах её достаточно бинарно инвертировать:
При этом PORTA дожен держать низкий уровень в одном из пяти рядов (через RA0,RA1,RA2,RA3,RA4), соответствующих тройкам светодиодов, которую мы собираемся подсветить.
Code: Select all
- 0 +
-------------
bit 0 = 1 1 0
bit 1 = 0 1 1
Code: Select all
; Fix triad W (used tmp1)
tri_fix: ; 15-24 cycles with call
movwf tmp1
tri_f2: ; старший трит
btfss tmp1,0 ; проверить что бит 0 взведён
goto tri_f1 ; если нет, то идём на следующую пару бит
btfss tmp1,1 ; проверить что бит 1 взведён
goto tri_f1 ; если нет, то идём на следующую пару бит
bcf tmp1,0 ; сбросить бит 0
bcf tmp1,1 ; сбросить бит 1
tri_f1: ; средний трит
btfss tmp1,2 ; проверить что бит 2 взведён
goto tri_f0 ; если нет, то идём на следующую пару бит
btfss tmp1,3 ; проверить что бит 3 взведён
goto tri_f0; если нет, то идём на следующую пару бит
bcf tmp1,2 ; сбросить бит 2
bcf tmp1,3 ; сбросить бит 3
tri_f0: ; младший трит
btfss tmp1,4 ; проверить что бит 4 взведён
goto tri_fe ; если нет, то делаем возврат из подпрограммы
btfss tmp1,5 ; проверить что бит 5 взведён
goto tri_fe ; если нет, то делаем возврат из подпрограммы
bcf tmp1,4 ; сбросить бит 4
bcf tmp1,5 ; сбросить бит 5
tri_fe: ; возвращаем скорректированую триаду обратно в W
movf tmp1,w
return ; возврат из подпрограммы
В случае светодиодов ноль в чётном бите подсвечивает красный светодиод (минус), а в нечётном - зелёный (плюс). А чтобы двойной светодиод не горел, оба бита должны содержать "1" (в случае обоих нулей светодиоды дадут жёлтый свет):
Code: Select all
- 0 +
-------------
bit 0 = 0 1 1
bit 1 = 1 1 0
Code: Select all
comf outvar,w
movwf PORTC
Last edited by Shaos on 19 Nov 2011 14:59, edited 1 time in total.
Я тут за главного - если что шлите мыло на me собака shaos точка net
-
- Admin
- Posts: 23989
- Joined: 08 Jan 2003 23:22
- Location: Silicon Valley
Если таким байтом адресоваться к 27-триадному блоку данных, то в двоичной памяти оно расположится так:Shaos wrote:Триада - это три трита, каждый из которых представлен двумя битами - младшим битом минуса и старшим битом плюса. Триады у нас перевёрнуты из-за особенностей схемы "3niti alpha simu1", т.е. в старших битах находится младший трит, а в младших - старший:
bit 0 - установлен в "1", если старший трит триады отрицателен
bit 1 - установлен в "1", если старший трит триады положителен
bit 2 - установлен в "1", если средней трит триады отрицателен
bit 3 - установлен в "1", если средней трит триады положителен
bit 4 - установлен в "1", если младшей трит триады отрицателен
bit 5 - установлен в "1", если младшей трит триады положителен
bit 6 - должен быть нулевым
bit 7 - должен быть нулевым
Code: Select all
0x00 (00000000) OOO = 0
0x01 (00000001) NOO = -9
0x02 (00000010) POO = +9
0x03 (00000011)
0x04 (00000100) ONO = -3
0x05 (00000101) NNO = -12
0x06 (00000110) PNO = +6
0x07 (00000111)
0x08 (00001000) OPO = +3
0x09 (00001001) NPO = -6
0x0A (00001010) PPO = +12
0x0B (00001011)
0x0C (00001100)
0x0D (00001101)
0x0E (00001110)
0x0F (00001111)
0x10 (00010000) OON = -1
0x11 (00010001) NON = -10
0x12 (00010010) PON = +8
0x13 (00010011)
0x14 (00010100) ONN = -4
0x15 (00010101) NNN = -13
0x16 (00010110) PPN = +5
0x17 (00010111)
0x18 (00011000) OPN = +2
0x19 (00011001) NPN = -7
0x1A (00011010) PPN = +11
0x1B (00011011)
0x1C (00011100)
0x1D (00011101)
0x1E (00011110)
0x1F (00011111)
0x20 (00100000) OOP = +1
0x21 (00100001) NOP = -8
0x22 (00100010) POP = +10
0x23 (00100011)
0x24 (00100100) ONP = -2
0x25 (00100101) NNP = -11
0x26 (00100110) PNP = +7
0x27 (00100111)
0x28 (00101000) OPP = +4
0x29 (00101001) NPP = -5
0x2A (00101010) PPP = +13
0x2B (00101011)
0x2C (00101100)
0x2D (00101101)
0x2E (00101110)
0x2F (00101111)
В первой версии эмулятора для платы "3niti alpha simu1" предлагается держать 27-триад кода в EEPROM (как бы троичный ROM) с вышеуказанными адресами и 27-триад данных в регистровой памяти (как бы троичный RAM) с вышеуказанными адресами, к которым прибавлено число 0x40, т.е. диапазон переменных 0x40...0x6F (нулевой банк файла региcтров в пике).
В терминах длинных адресов "3niti alpha", этот 27-триадный ROM будет отображаться на область OOOOOOxxx, а 27-триадный RAM - на область OPNNNNxxx (начиная с адреса +1093). В текущей прошивке, над которой я сейчас работаю, внешняя память пока использоваться небудет (её можно будет добавить позже - у схемы есть возможность обновления прошивки через бутлоадер).
P.S. Глядя на этот маппинг троичных адресов на двоичные у меня возникла идея - что если прямо так и хранить во внешней памяти? Можно читать по 48 байт (тогда потеряно будет 44% памяти), убрав последние 5 байт получим 43 байта (37% потерь) или даже брать блоками по 11 байт три раза, где будет всего 2 однобайтных дырки (18% потерь). Проблема лишь в том, что придётся адресоваться к памяти через умножение - надо будет умножать на 48 (00110000), 43 (00101011) или 11 (00001011) - хотя точнее надо бы сказать 33 (00100001), т.к. 3 раза по 11. Формула уможения на 48: (x<<5)+(x<<4). Формула умножения на 43: (x<<5)+(x<<3)+(x<<2)+x. Формула умножения на 11: (x<<3)+(x<<1)+x или (x<<3)+(x<<2)-x (т.е. 12*x-x). Формула умножения на 33: (x<<5)+x. Хотя блоками по 27 тоже умножать надо, причём на некрасивое число 00011011...
P.P.S. Прикинул, что при 3x11-байтовом представлении 27-триадной страницы теоретически будет читаться-писаться по 300 страниц в секунду при 100кГц или 1200 страниц при 400кГц. Но т.к. при записи будет до 10 мс задержка на запись очередной порции (до 32 байт длиной) - то это уменьшит скорость записи до 30 страниц в секунду при 100кГц или 120 страниц при 400 кГц. Напомню, что первоначально оговоренный размер блоков RAM и ROM у 3niti alpha составляет (1093*2+1)/27=81 страница, которые можно прочитать меньше чем за треть секунды и записать за 2.7 или за 1.35 секунды в зависимости от тактовой частоты ПЗУ. Байты "потерянные" при такой оптимизации можно использовать для хранения адреса страницы (для проверки корректности места чтения) и её контрольной суммы (для проверки целостности данных).
P.P.P.S. Внимательно прочитал даташит на I2C память - страничная запись подразумевает запись в 32-байтовые области с фиксированными адресами - т.е. придётся таки писать ровно по 32 байта...
Я тут за главного - если что шлите мыло на me собака shaos точка net
-
- Admin
- Posts: 23989
- Joined: 08 Jan 2003 23:22
- Location: Silicon Valley
Троичный инкремент 9-тритового регистра PC (program counter):
Печать триады через функцию печати в терминал из бутлоадера (макрос _serial_send_):
Вот код циклического инкремента PC с печатью и результат его работы:
OOOOOOOOP
OOOOOOOPN
OOOOOOOPO
OOOOOOOPP
OOOOOOPNN
OOOOOOPNO
OOOOOOPNP
OOOOOOPON
OOOOOOPOO
OOOOOOPOP
OOOOOOPPN
OOOOOOPPO
OOOOOOPPP
OOOOOPNNN
OOOOOPNNO
OOOOOPNNP
OOOOOPNON
OOOOOPNOO
OOOOOPNOP
OOOOOPNPN
OOOOOPNPO
OOOOOPNPP
OOOOOPONN
OOOOOPONO
OOOOOPONP
OOOOOPOON
OOOOOPOOO
OOOOOPOOP
OOOOOPOPN
OOOOOPOPO
OOOOOPOPP
OOOOOPPNN
OOOOOPPNO
OOOOOPPNP
OOOOOPPON
OOOOOPPOO
OOOOOPPOP
OOOOOPPPN
OOOOOPPPO
OOOOOPPPP
OOOOPNNNN
OOOOPNNNO
и т.д.
P.S. Замерил без печати - PC полностью пробегает свой диапазон (19683) за 0.104 секунды (точнее это было 64 пробега за 6.66 секунд) - что означает примерно 189000 9-тритовых инкрементов в секунду - это и есть предельная скорость работы нашего эмулируемого процессора "3niti alpha" - 189000 операций в секунду...
P.P.S. Хотя если инкремент триады сделать не процедурным, а табличным, то будет побыстрее...
Code: Select all
; Ternary increment of 9-trit PC
inc_pc3:
movf PC_l,w ; берём млашую триаду PC
call tri_inc ; троично инкрементируем её
movwf PC_l ; сохраняем обратно
btfss STATUS,C ; проверяем флаг переноса C
return ; если он не взведён - выходим из подпрограммы
movf PC_m,w ; берём среднюю триаду PC
call tri_inc ; троично инкрементируем её
movwf PC_m ; сохраняем обратно
btfss STATUS,C ; проверяем флаг переноса c
return ; если он не взведён - выходим из подпрограммы
movf PC_h,w ; берём старшую триаду PC
call tri_inc ; троично инкрементируем её
movwf PC_h ; сохраняем обратно
return ; выходим из подпровграммы
Code: Select all
; Serial print triad from W (used tmp0, cnt1)
print_triad:
movwf tmp0
movlw 3
movwf cnt1
ptriad: btfsc tmp0,0
goto ptriadn
btfsc tmp0,1
goto ptriadp
_serial_send_ 'O'
goto ptriad1
ptriadp:
_serial_send_ 'P'
goto ptriad1
ptriadn:
_serial_send_ 'N'
ptriad1:
rrf tmp0,f
rrf tmp0,f
decfsz cnt1,f
goto ptriad
return
Code: Select all
loop: call inc_pc3
movf PC_h,w
call print_triad
movf PC_m,w
call print_triad
movf PC_l,w
call print_triad
_serial_print_nl
goto loop
OOOOOOOPN
OOOOOOOPO
OOOOOOOPP
OOOOOOPNN
OOOOOOPNO
OOOOOOPNP
OOOOOOPON
OOOOOOPOO
OOOOOOPOP
OOOOOOPPN
OOOOOOPPO
OOOOOOPPP
OOOOOPNNN
OOOOOPNNO
OOOOOPNNP
OOOOOPNON
OOOOOPNOO
OOOOOPNOP
OOOOOPNPN
OOOOOPNPO
OOOOOPNPP
OOOOOPONN
OOOOOPONO
OOOOOPONP
OOOOOPOON
OOOOOPOOO
OOOOOPOOP
OOOOOPOPN
OOOOOPOPO
OOOOOPOPP
OOOOOPPNN
OOOOOPPNO
OOOOOPPNP
OOOOOPPON
OOOOOPPOO
OOOOOPPOP
OOOOOPPPN
OOOOOPPPO
OOOOOPPPP
OOOOPNNNN
OOOOPNNNO
и т.д.
P.S. Замерил без печати - PC полностью пробегает свой диапазон (19683) за 0.104 секунды (точнее это было 64 пробега за 6.66 секунд) - что означает примерно 189000 9-тритовых инкрементов в секунду - это и есть предельная скорость работы нашего эмулируемого процессора "3niti alpha" - 189000 операций в секунду...
P.P.S. Хотя если инкремент триады сделать не процедурным, а табличным, то будет побыстрее...
Я тут за главного - если что шлите мыло на me собака shaos точка net
-
- Admin
- Posts: 23989
- Joined: 08 Jan 2003 23:22
- Location: Silicon Valley
Как ни странно, но табличный метод оказался даже чуть медленнее (примерно 174000 9-тритовых инкрементов в секунду):
Code: Select all
; Fast ternary increment of the triad W, C is overflow (used tmp1)
ORG 0100h
tri_inc_table:
retlw tOOP
retlw tNOP
retlw tPOP
retlw 0xFF
retlw tONP
retlw tNNP
retlw tPNP
retlw 0xFF
retlw tOPP
retlw tNPP
retlw tPPP
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw tOOO
retlw tNOO
retlw tPOO
retlw 0xFF
retlw tONO
retlw tNNO
retlw tPNO
retlw 0xFF
retlw tOPO
retlw tNPO
retlw tPPO
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw tOPN
retlw tNPN
retlw tPPN
retlw 0xFF
retlw tOON
retlw tNON
retlw tPON
retlw 0xFF
retlw tPNN
retlw tONN
retlw tNNN ; overflow
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
retlw 0xFF
tri_inc_fast_:
bsf PCLATH,0
movwf PCL
tri_inc_fast: ; 18 with call
andlw 0x3F
call tri_inc_fast_
movwf tmp1
sublw tNNN
bcf STATUS,C
btfsc STATUS,Z
bsf STATUS,C
movf tmp1,w
return
Я тут за главного - если что шлите мыло на me собака shaos точка net
-
- Admin
- Posts: 23989
- Joined: 08 Jan 2003 23:22
- Location: Silicon Valley
Самая сложная подпрограмма, необходимая для работы эмулятора "3niti alpha" - сложение двух триад с учётом флага переноса-заёма - реализация частично табличная (для сложения трёх троичных цифр), частично процедурная (для сложения триад):
Сделал замеры - если эмулятор не только бежит по PC, но и постоянно складывает две триады (взятые из PC), то скорость проседает до 33000 операций в секунду! Принимаются предложения по оптимизации этого кода
P.S. Похоже проблема решается намного проще - надо всего-то навсего перевести слагаемые из троичной системы в двоичную, сложить средствами самого пика и потом вернуть результат обратно в троичную...
Code: Select all
tri_add_3trits: ; 6 cycles with call
addwf PCL,f ; jump to proper value
retlw b'00000000' ; 000000 -> 0+0+0= 0 S=O C=O
retlw b'00000001' ; 000001 -> 0+0-1=-1 S=N C=O
retlw b'00000010' ; 000010 -> 0+0+1= 1 S=P C=O
retlw b'00010000' ; 000011 -> invalid
retlw b'00000001' ; 000100 -> 0-1+0=-1 S=N C=O
retlw b'00000110' ; 000101 -> 0-1-1=-2 S=P C=N
retlw b'00000000' ; 000110 -> 0-1+1= 0 S=O C=O
retlw b'00010000' ; 000111 -> invalid
retlw b'00000010' ; 001000 -> 0+1+0= 1 S=P C=O
retlw b'00000000' ; 001001 -> 0+1-1= 0 S=O C=O
retlw b'00001001' ; 001010 -> 0+1+1= 2 S=N C=P
retlw b'00010000' ; 001011 -> invalid
retlw b'00010000' ; 001100 -> invalid
retlw b'00010000' ; 001101 -> invalid
retlw b'00010000' ; 001110 -> invalid
retlw b'00010000' ; 001111 -> invalid
retlw b'00000001' ; 010000 -> -1+0+0=-1 S=N C=O
retlw b'00000110' ; 010001 -> -1+0-1=-2 S=P C=N
retlw b'00000000' ; 010010 -> -1+0+1= 0 S=O C=O
retlw b'00010000' ; 010011 -> invalid
retlw b'00000110' ; 010100 -> -1-1+0=-2 S=P C=N
retlw b'00000100' ; 010101 -> -1-1-1=-3 S=O C=N
retlw b'00000001' ; 010110 -> -1-1+1=-1 S=N C=O
retlw b'00010000' ; 010111 -> invalid
retlw b'00000000' ; 011000 -> -1+1+0= 0 S=O C=O
retlw b'00000001' ; 011001 -> -1+1-1=-1 S=N C=O
retlw b'00000010' ; 011010 -> -1+1+1= 1 S=P C=O
retlw b'00010000' ; 011011 -> invalid
retlw b'00010000' ; 011100 -> invalid
retlw b'00010000' ; 011101 -> invalid
retlw b'00010000' ; 011110 -> invalid
retlw b'00010000' ; 011111 -> invalid
retlw b'00000010' ; 100000 -> 1+0+0= 1 S=P C=O
retlw b'00000000' ; 100001 -> 1+0-1= 0 S=O C=O
retlw b'00001001' ; 100010 -> 1+0+1= 2 S=N C=P
retlw b'00010000' ; 100011 -> invalid
retlw b'00000000' ; 100100 -> 1-1+0= 0 S=O C=O
retlw b'00000001' ; 100101 -> 1-1-1=-1 S=N C=O
retlw b'00000010' ; 100110 -> 1-1+1= 1 S=P C=O
retlw b'00010000' ; 100111 -> invalid
retlw b'00001001' ; 101000 -> 1+1+0= 2 S=N C=P
retlw b'00000010' ; 101001 -> 1+1-1= 1 S=P C=O
retlw b'00001000' ; 101010 -> 1+1+1= 3 S=O C=P
retlw b'00010000' ; 101011 -> invalid
retlw b'00010000' ; 101100 -> invalid
retlw b'00010000' ; 101101 -> invalid
retlw b'00010000' ; 101110 -> invalid
retlw b'00010000' ; 101111 -> invalid
retlw b'00010000' ; 110000 -> invalid
retlw b'00010000' ; 110001 -> invalid
retlw b'00010000' ; 110010 -> invalid
retlw b'00010000' ; 110011 -> invalid
retlw b'00010000' ; 110100 -> invalid
retlw b'00010000' ; 110101 -> invalid
retlw b'00010000' ; 110110 -> invalid
retlw b'00010000' ; 110111 -> invalid
retlw b'00010000' ; 111000 -> invalid
retlw b'00010000' ; 111001 -> invalid
retlw b'00010000' ; 111010 -> invalid
retlw b'00010000' ; 111011 -> invalid
retlw b'00010000' ; 111100 -> invalid
retlw b'00010000' ; 111101 -> invalid
retlw b'00010000' ; 111110 -> invalid
retlw b'00010000' ; 111111 -> invalid
tri_add_test MACRO B
movlw B
call tri_add_3trits
ENDM
; Ternary adder W+A_reg+BCF=A_reg and BCF (used tmp0,tmp1,tmp2,tmp3,tmp4,cnt1)
tri_adder:
movwf tmp0
clrf tmp4
movlw 3
movwf cnt1
movf F_reg,w
andlw b'00110000'
movwf tmp1
tri_add_loop:
movf tmp0,w
andlw b'00110000'
movwf tmp2
bcf STATUS,C
rrf tmp2,f
rrf tmp2,w
iorwf tmp1,f
movf A_reg,w
andlw b'00110000'
movwf tmp2
swapf tmp2,w
iorwf tmp1,w
call tri_add_3trits
movwf tmp3
andlw b'00000011'
iorwf tmp4,f
movf tmp3,w
andlw b'00001100'
movwf tmp1
rlf tmp1,f
rlf tmp1,f
decfsz cnt1,f
goto tri_add_next
movf F_reg,w
andlw b'00001111'
iorwf tmp1,w
movwf F_reg
movf tmp4,w
movwf A_reg
return
tri_add_next:
rlf tmp4,f
rlf tmp4,f
rlf tmp0,f
rlf tmp0,f
rlf A_reg,f
rlf A_reg,f
goto tri_add_loop

P.S. Похоже проблема решается намного проще - надо всего-то навсего перевести слагаемые из троичной системы в двоичную, сложить средствами самого пика и потом вернуть результат обратно в троичную...
Я тут за главного - если что шлите мыло на me собака shaos точка net
-
- Admin
- Posts: 23989
- Joined: 08 Jan 2003 23:22
- Location: Silicon Valley
Вот функция процедурного декодера кода операции:
Сделал предварительные замеры - простейшие команды типа SAB выполняются порядка 82000 раз в секунду (примерно 12 мкс), чуть посложнее типа OPA или ADD - порядка 27000 раз в секунду (примерно 37 мкс), совсем сложные типа ADI - порядка 21000 раз в секунду (примерно 48 мкс). Это если запускать программу в пределах одной страницы, находящейся в регистрах - если же страница сидит в EEPROM, то к примеру производительность того же ADI падает до 16500 раз в секунду (60 мкс) - это из-за того что процесс чтения из EEPROM (которых в команде ADI два) это штука нетривиальная и требует некоторых странных телодвижений, отсутствующих при обращении к регистрам...
Code: Select all
; Perform single step of the program
tri_step:
movlw PC_h
movwf FSR
call tri_read
movwf C_reg
call tri_inc_pc3
btfss C_reg,0
goto tri_step0
; Nxx
btfss C_reg,2
goto tri_step_0
; NNx
btfss C_reg,4
goto tri_step__0
; NNN (-13) SAN
movlw DPn_h
movwf FSR
movf A_reg,w
call tri_write
return
tri_step__0:
btfsc C_reg,5
goto tri_step__1
; NNO (-12) SAO
movlw DPo_h
movwf FSR
movf A_reg,w
call tri_write
return
tri_step__1:
; NNP (-11) SAP
movlw DPp_h
movwf FSR
movf A_reg,w
call tri_write
return
tri_step_0:
btfsc C_reg,3
goto tri_step_1
; NOx
btfss C_reg,4
goto tri_step_00
; NON (-10) SAF
movf A_reg,w
movwf F_reg
return
tri_step_00:
btfsc C_reg,5
goto tri_step_01
; NOO (-9) SPCD
call tri_cur_dp
movf PC_h,w
movwf INDF
incf FSR,f
movf PC_m,w
movwf INDF
incf FSR,f
movf PC_l,w
movwf INDF
return
tri_step_01:
; NOP (-8) SAB
movf A_reg,w
movwf B_reg
return
tri_step_1:
; NPx
btfss C_reg,4
goto tri_step_10
; NPN (-7) SAL
call tri_cur_dp
goto tri_step_sal
tri_step_10:
btfsc C_reg,5
goto tri_step_11
; NPO (-6) SAM
call tri_cur_dp
goto tri_step_sam
tri_step_11:
; NPP (-5) SAH
call tri_cur_dp
goto tri_step_sah
tri_step_sal:
incf FSR,f
tri_step_sam:
incf FSR,f
tri_step_sah:
movf A_reg,w
movwf INDF
return
tri_step0:
btfsc C_reg,1
goto tri_step1
; Oxx
btfss C_reg,2
goto tri_step00
; ONx
btfss C_reg,4
goto tri_step0_0
; ONN (-4) RLA
bcf STATUS,C
rlf A_reg,f
rlf A_reg,f
btfsc F_reg,5
bsf A_reg,1
btfsc F_reg,4
bsf A_reg,0
bcf F_reg,5
btfsc A_reg,7
bsf F_reg,5
bcf A_reg,7
bcf F_reg,4
btfsc A_reg,6
bsf F_reg,4
bcf A_reg,6
call tri_rsf
return
tri_step0_0:
btfsc C_reg,5
goto tri_step0_1
; ONO (-3) ADD
movf B_reg,W
call tri_adder
call tri_rsf
return
tri_step0_1:
; ONP (-2) RRA
btfsc F_reg,5
bsf A_reg,7
btfsc F_reg,4
bsf A_reg,6
bcf STATUS,C
rrf A_reg,f
btfsc STATUS,C
bsf F_reg,4
bcf STATUS,C
rrf A_reg,f
btfsc STATUS,C
bsf F_reg,5
call tri_rsf
return
tri_step00:
btfsc C_reg,3
goto tri_step01
; OOx
btfss C_reg,4
goto tri_step000
; OON (-1) LAI #
call tri_read1pc
movf tmp0,w
movwf A_reg
return
tri_step000:
btfsc C_reg,5
goto tri_step001
; OOO (0) ADI #
call tri_read1pc
movf tmp0,w
call tri_adder
call tri_rsf
return
tri_step001:
; OOP (1) OPA #
call tri_read1pc
call tri_opa
movf tmp1,w
movwf A_reg
call tri_rsf
return
tri_step01:
; OPx
btfss C_reg,4
goto tri_step010
; OPN (2) LDI # # #
call tri_read3pc
call tri_cur_dp
movf tmp1,w
movwf INDF
incf FSR,f
movf tmp2,w
movwf INDF
incf FSR,f
movf tmp3,w
movwf INDF
return
tri_step010:
btfsc C_reg,5
goto tri_step011
; OPO (3) JMP # # #
call tri_read3pc
movlw PC_h
movwf FSR
movf tmp1,w
movwf INDF
incf FSR,f
movf tmp2,w
movwf INDF
incf FSR,f
movf tmp3,w
movwf INDF
return
tri_step011:
; OPP (4) OPB # # #
call tri_read3pc
call tri_opb
movf tmp0,w
movwf A_reg
call tri_rsf
return
tri_step1:
; Pxx
btfss C_reg,2
goto tri_step10
; PNx
btfss C_reg,4
goto tri_step1_0
; PNN (5) LAN
movlw DPn_h
movwf FSR
call tri_read
movwf A_reg
return
tri_step1_0:
btfsc C_reg,5
goto tri_step1_1
; PNO (6) LAO
movlw DPo_h
movwf FSR
call tri_read
movwf A_reg
return
tri_step1_1:
; PNP (7) LAP
movlw DPp_h
movwf FSR
call tri_read
movwf A_reg
return
tri_step10:
btfsc C_reg,3
goto tri_step11
; POx
btfss C_reg,4
goto tri_step100
; PON (8) LAF
movf F_reg,w
movwf A_reg
return
tri_step100:
btfsc C_reg,5
goto tri_step101
; POO (9) LPCD
call tri_cur_dp
movf INDF,w
movwf PC_h
incf FSR,f
movf INDF,w
movwf PC_m
incf FSR,f
movf INDF,w
movwf PC_l
return
tri_step101:
; POP (10) LAB
movf B_reg,w
movwf A_reg
return
tri_step11:
; NPx
btfss C_reg,4
goto tri_step110
; PPN (11) LAL
call tri_cur_dp
goto tri_step_lal
tri_step110:
btfsc C_reg,5
goto tri_step111
; PPO (12) LAM
call tri_cur_dp
goto tri_step_lam
tri_step111:
; PPP (13) LAH
call tri_cur_dp
goto tri_step_lah
tri_step_lal:
incf FSR,f
tri_step_lam:
incf FSR,f
tri_step_lah:
movf INDF,w
movwf A_reg
return
Last edited by Shaos on 25 Nov 2011 02:41, edited 1 time in total.
Я тут за главного - если что шлите мыло на me собака shaos точка net
-
- Admin
- Posts: 23989
- Joined: 08 Jan 2003 23:22
- Location: Silicon Valley
Вот подпрограммы для осуществления универсальных операций OPA и OPB:
в железе они реализуются значительно проще...
замеры по скорости:
OPA - 26800 раз в секунду или 37 мкс
OPB - 11600 раз в секунду или 86 мкс
Code: Select all
; Perform OPA command on A_reg with code tmp0 and save result in tmp1 (used cnt1)
tri_opa:
movlw 3
movwf cnt1
clrf tmp1
tri_opa_:
btfss A_reg,0
goto tri_opa1
btfsc tmp0,0
bsf tmp1,6
btfsc tmp0,1
bsf tmp1,7
goto tri_opa_loop
tri_opa1:
btfss A_reg,1
goto tri_opa0
btfsc tmp0,4
bsf tmp1,6
btfsc tmp0,5
bsf tmp1,7
goto tri_opa_loop
tri_opa0:
btfsc tmp0,2
bsf tmp1,6
btfsc tmp0,3
bsf tmp1,7
tri_opa_loop:
bcf STATUS,C
rrf tmp1,f
rrf tmp1,f
rrf A_reg,f
rrf A_reg,f
decfsz cnt1,f
goto tri_opa_
return
; Perform OPB command on A_reg and B_reg with code tmp1,tmp2,tmp3 and save result in tmp0 (used tmp4,cnt1)
tri_opb:
movf tmp1,w
movwf tmp4
movf tmp3,w
movwf tmp0
call tri_opa
movf tmp1,w
movwf tmp3
movf tmp2,w
movwf tmp0
call tri_opa
movf tmp1,w
movwf tmp2
movf tmp4,w
movwf tmp0
call tri_opa
clrf tmp0
btfss B_reg,0
goto tri_opb_h1
btfsc tmp1,0
bsf tmp0,0
btfsc tmp1,1
bsf tmp0,1
goto tri_opb_m
tri_opb_h1:
btfsc B_reg,1
goto tri_opb_h0
btfsc tmp3,0
bsf tmp0,0
btfsc tmp3,1
bsf tmp0,1
goto tri_opb_m
tri_opb_h0:
btfsc tmp2,0
bsf tmp0,0
btfsc tmp2,1
bsf tmp0,1
tri_opb_m:
btfss B_reg,2
goto tri_opb_m1
btfsc tmp1,2
bsf tmp0,2
btfsc tmp1,3
bsf tmp0,3
goto tri_opb_l
tri_opb_m1:
btfsc B_reg,3
goto tri_opb_m0
btfsc tmp3,2
bsf tmp0,2
btfsc tmp3,3
bsf tmp0,3
goto tri_opb_l
tri_opb_m0:
btfsc tmp2,2
bsf tmp0,2
btfsc tmp2,3
bsf tmp0,3
tri_opb_l:
btfss B_reg,4
goto tri_opb_l1
btfsc tmp1,4
bsf tmp0,4
btfsc tmp1,5
bsf tmp0,5
return
tri_opb_l1:
btfsc B_reg,5
goto tri_opb_l0
btfsc tmp3,4
bsf tmp0,4
btfsc tmp3,5
bsf tmp0,5
return
tri_opb_l0:
btfsc tmp2,4
bsf tmp0,4
btfsc tmp2,5
bsf tmp0,5
return
замеры по скорости:
OPA - 26800 раз в секунду или 37 мкс
OPB - 11600 раз в секунду или 86 мкс
Я тут за главного - если что шлите мыло на me собака shaos точка net
-
- Admin
- Posts: 23989
- Joined: 08 Jan 2003 23:22
- Location: Silicon Valley
Ещё дополнительные подпрограммы для работы декодера команд:
P.S. Полный исходник смотреть тут:
https://gitlab.com/nedopc/pix/-/blob/master/16/t1_test3.asm
он почти в тысячу строк получился...
P.P.S. Подпрограмма tri_fail должна будет вводить систему в состояние фатальной ошибки - например путём переключения всех светодиодов в жёлтый цвет и зависания в этом состоянии...
Code: Select all
; Read one triad from the address stored in 3 triads started from FSR and save result to W
tri_read:
; skip middle address for now...
movf INDF,w
subwf CA_h,w
btfsc STATUS,Z
goto tri_read_eep
movf INDF,w
subwf DA_h,w
btfsc STATUS,Z
goto tri_read_ram
call tri_fail
tri_read_eep:
incf FSR,f
incf FSR,f
movf INDF,w
call eeprom_read
_bank0
return
tri_read_ram:
incf FSR,f
incf FSR,f
movf INDF,w
addlw 0x40
movwf FSR
movf INDF,w
return
; Write one triad W to the address stored in 3 triads started from FSR (used tmp0)
tri_write:
; skip middle address for now...
movwf tmp0
movf INDF,w
subwf CA_h,w
btfsc STATUS,Z
goto tri_write_eep
movf INDF,w
subwf DA_h,w
btfsc STATUS,Z
goto tri_write_ram
call tri_fail
tri_write_eep:
incf FSR,f
incf FSR,f
movf tmp0,w
movwf eeprom_write_value
movf INDF,w
call eeprom_write
return
tri_write_ram:
incf FSR,f
incf FSR,f
movf INDF,w
addlw 0x40
movwf FSR
movf tmp0,w
movwf INDF
return
; Set current DP in FSR register
tri_cur_dp:
btfss F_reg,2
goto tri_cur_dp0
movlw DPn_h
goto tri_cur_dp_
tri_cur_dp0:
btfsc F_reg,3
goto tri_cur_dp1
movlw DPo_h
goto tri_cur_dp_
tri_cur_dp1:
movlw DPp_h
tri_cur_dp_:
movwf FSR
return
; Set RSF (higher trit of the register F) based of the sign of the value in the register A
tri_rsf:
btfsc A_reg,0
goto tri_rsf_p
btfsc A_reg,1
goto tri_rsf_n
btfsc A_reg,2
goto tri_rsf_p
btfsc A_reg,3
goto tri_rsf_n
btfsc A_reg,4
goto tri_rsf_p
btfsc A_reg,5
goto tri_rsf_n
tri_rsf_o:
bcf F_reg,0
bcf F_reg,1
return
tri_rsf_n:
bsf F_reg,0
bcf F_reg,1
return
tri_rsf_p:
bcf F_reg,0
bsf F_reg,1
return
; Read 1 immediate triad and save it in tmp0
tri_read1pc:
movlw PC_h
movwf FSR
call tri_read
movwf tmp0
call tri_inc_pc3
return
; Read 3 immediate triads and save them in tmp1,tmp2,tmp3
tri_read3pc:
movlw PC_h
movwf FSR
call tri_read
movwf tmp1
call tri_inc_pc3
movlw PC_h
movwf FSR
call tri_read
movwf tmp2
call tri_inc_pc3
movlw PC_h
movwf FSR
call tri_read
movwf tmp3
call tri_inc_pc3
return
https://gitlab.com/nedopc/pix/-/blob/master/16/t1_test3.asm
он почти в тысячу строк получился...
P.P.S. Подпрограмма tri_fail должна будет вводить систему в состояние фатальной ошибки - например путём переключения всех светодиодов в жёлтый цвет и зависания в этом состоянии...
Last edited by Shaos on 25 Nov 2011 09:22, edited 1 time in total.
Я тут за главного - если что шлите мыло на me собака shaos точка net
-
- Admin
- Posts: 23989
- Joined: 08 Jan 2003 23:22
- Location: Silicon Valley
Вот полная таблица длительностей выполнения каждой инструкции 3niti alpha в микросекундах из регистровой памяти (тут также можно видеть визуальное отличие длительностей):
Как видно самой быстрой командой является SAF (точное значение - 11.4 мкс или примерно 87400 раз в секунду), а самой медленной - OPB (точное значение - 86.2 мкс или примерно 11600 раз в секунду).
А вот тоже самое, но при чтении из EEPROM:
Как видно однотриадные команды стали дольше примерно на 7 мкс (одно чтение из EEPROM на команду), двухтриадные - на 14 мкс (два чтения из EEPROM на команду), а четырёхтриадные - на 28 мкс (четыре чтения из EEPROM на команду). Исключение составляют лишь команды LAN,LAO,LAP, которые содержат в себе неявное второе обращение к EEPROM. Также есть странная аномалия у RLA и RRA - они удлиннились не на 7, а на 9 мкс. Ещё интересно, что из-за этого неравномерного удлиннения команда ADI стала быстрее чем LDI и JMP. Теперь самая быстрая команда SAF выполняется 18.3 мкс (примерно 54400 раз в секунду), а самая долгая OPB - 113.3 мкс (примерно 8800 раз в секунду).
P.S. Обработка состояния переключателей и подсветка светодиодов добавляют около 7 мкс к каждой команде
P.P.S. Интересно, что прошивка, выполняющая все инструкции "3niti alpha" в двух страницах памяти и обрабатывающая почти всё управление (кроме возврата в главную программу M) влезла в оставленные от бутлоадера 671 байт практически в притык...
Code: Select all
NNN (SAN) 14 =====
NNO (SAO) 15 =====
NNP (SAP) 15 =====
NON (SAF) 11 ====
NOO (SPCD) 16 =====
NOP (SAB) 12 ====
NPN (SAL) 15 =====
NPO (SAM) 15 =====
NPP (SAH) 15 =====
ONN (RLA) 16 =====
ONO (ADD) 38 =============
ONP (RRA) 16 =====
OON (LAI #) 20 =======
OOO (ADI #) 48 ================
OOP (OPA #) 37 ============
OPN (LDI ###) 41 ==============
OPO (JMP ###) 38 =============
OPP (OPB ###) 86 =============================
PNN (LAN) 14 =====
PNO (LAO) 15 =====
PNP (LAP) 15 =====
PON (LAF) 12 ====
POO (LPCD) 17 ======
POP (LAB) 13 ====
PPN (LAL) 16 =====
PPO (LAM) 16 =====
PPP (LAH) 16 =====
А вот тоже самое, но при чтении из EEPROM:
Code: Select all
NNN (SAN) 21 =======
NNO (SAO) 21 =======
NNP (SAP) 22 =======
NON (SAF) 18 ======
NOO (SPCD) 23 =======
NOP (SAB) 19 ======
NPN (SAL) 22 =======
NPO (SAM) 22 =======
NPP (SAH) 22 =======
ONN (RLA) 25 ========
ONO (ADD) 46 ===============
ONP (RRA) 25 ========
OON (LAI #) 34 ===========
OOO (ADI #) 62 =====================
OOP (OPA #) 51 =================
OPN (LDI ###) 69 =======================
OPO (JMP ###) 66 ======================
OPP (OPB ###)113 ======================================
PNN (LAN) 28 =========
PNO (LAO) 29 =========
PNP (LAP) 29 =========
PON (LAF) 19 ======
POO (LPCD) 24 ========
POP (LAB) 20 =======
PPN (LAL) 22 =======
PPO (LAM) 23 ========
PPP (LAH) 23 ========
P.S. Обработка состояния переключателей и подсветка светодиодов добавляют около 7 мкс к каждой команде
P.P.S. Интересно, что прошивка, выполняющая все инструкции "3niti alpha" в двух страницах памяти и обрабатывающая почти всё управление (кроме возврата в главную программу M) влезла в оставленные от бутлоадера 671 байт практически в притык...
Я тут за главного - если что шлите мыло на me собака shaos точка net
-
- Admin
- Posts: 23989
- Joined: 08 Jan 2003 23:22
- Location: Silicon Valley
Эпопею с выкатыванием первой версии эмулятора можно считать законченной:
http://nedopc.org/nedopc/16/t1_test3.hex (3K)
http://nedopc.org/nedopc/16/t1_test3.asm (21K)
http://nedopc.org/nedopc/16/PDBLv1.zip (54K)
Прогон команды ADI # (код OOO) по всей памяти в 19,683 триад даёт примерно 52 мкс на команду (это при том, что реально данные читаются не во всех страницах). Также удалось впихнуть в прошивку хак - если переключатели W/R и I/M оба находятся в нижнем положении, то управление передаётся на бутлоадер PDBLv1, через который можно посмотреть состояние переменных и EEPROM.

http://nedopc.org/nedopc/16/t1_test3.hex (3K)
http://nedopc.org/nedopc/16/t1_test3.asm (21K)
http://nedopc.org/nedopc/16/PDBLv1.zip (54K)
Прогон команды ADI # (код OOO) по всей памяти в 19,683 триад даёт примерно 52 мкс на команду (это при том, что реально данные читаются не во всех страницах). Также удалось впихнуть в прошивку хак - если переключатели W/R и I/M оба находятся в нижнем положении, то управление передаётся на бутлоадер PDBLv1, через который можно посмотреть состояние переменных и EEPROM.

Я тут за главного - если что шлите мыло на me собака shaos точка net