Просмотр полной версии : Использование мыши в восьмибитной Атари (Ассемблер).
Прежде всего, нужно вспомнить, что в начале 90-х на какой-то крупной ежегодной компьютерной выставке, фирмой Атари был представлен неизменённый компьютер Atari 130XE с мышью STM1 от Atari ST. То-есть, планы производства - БЫЛИ!
Кроме этого, в этом посте я буду называть "Joystick Port" Атари - "Controller Jack" (CJ), так как он используется не только для джойстика и говорить здесь мы будем о мыши.
Со стороны Атари:
CJ имеет 9 штырьков.
64130
Пины 1,2,3,4 - цифровые(один ниббл), в порядке N S W E (компас)
5,9 - аналоговые(декодируются встроенными в Атари АЦП),
Пин 6 - кнопка триггера,
пин 7 - +5v, пин 8 - земля (питание контроллера от Атари).
Для описания взаимодействия Атари-8 и STM1 мы должны различать старые и новые модели.
В старых моделях (до XL серии) 4 CJ, в новых только 2.
Каждая пара CJ передаёт данные в PIA (Peripheral Interface Adapter).
В памяти Атари, PIA представлен в HW регистрах PORTA и PORTB.
Таким образом, CJ 1,2 маппируются соответственно в младший и старший ниббл PORTA, а CJ 3,4 маппируются соответственно в младший и старший ниббл PORTB.
В новых моделях PORTB не используется для передачи данных с контроллеров.
В остальном они идентичны.
Далее речь ведётся о новых моделях...
Пусть мы хотим передавать данные с мыши в CJ2, то есть в старший ниббл регистра PORTA... (Логично, если ещё используется Джойстик...)
64131
В соответствии со стандартом Атари, каждый ниббл должен передавать в:
- младшем дибайте(полуниббле) - вертикальное смещение.
Пин 1-N, 2-S (Север-Юг), т.е. Ось Y
- старшем дибайте - горизонтальное смещение.
Пин 3-W, 4-E (Запад-Восток), т.е. Ось Х
...
Со стороны STM1: Обратите внимание на различие координатных пинов в Джойстике и мыши!!!
64132
Мышь STM1 представляет собой два покоординатных инкрементальных кольцевых цифровых энкодера аналоговой позиции контроллера. Валкодеры мыши Атари СТМ1 производят преобразование аналогого движения мыши в цифровой сигнал посредством энкодера согласно Коду Грея.
Обычный двоичный код является взвешенным или позиционным кодом, в котором позиция отражает вес бита. Если взять седьмой бит, его вес будет 2^7, то-есть 128, в десятичном виде. Таким образом, если мы передаём байт и пропадает 7-й бит, то ошибка составит 128(dec). Это неприемлемо для позиционного датчика.
Код Грея
- является двоичным юнитдистантным невзвешенным отраженным циклическим кодом.
Разберём:
Двоичный - понятно
Юнитдистантный - Раз в двоичном коде юнитом является бит, значит расстояние между любыми последовательными значениями - тоже будет 1 бит.
Невзвешенный - Непозиционный (перекликается в чем-то с юнитдистантным)
Отраженный - Получается путём отражения
Циклический - может быть закольцован причём остальные свойства не будут нарушены
Основная цель кода Грея - в минимизации ошибок в цифровом тракте до одного бита при любом изменении данных датчика.
Тривиальный код Грея это - последовательность (0 1).
Эта последовательность может быть зациклена, причем, как при прямом, так и при возвратном движении по циклу, разница между соседними значениями сигнала не превышает 1 бита.
Чтобы сохранить вышеуказанные свойства при увеличении разрядности цифрового сигнала, Грей сначала отражает код, чтобы обеспечить юнитдистантность при любом количестве разрядов, а потом в добавляемом старшем разряде записывает нули для прямого и единицы для отраженного кода, чтобы получить уникальные значения датчика.
Таким образом код преображается в следующий:
(0 1) -> (0 1 1 0) - отражение
В отраженном коде пропала уникальность значений!
Добавим старший разряд:
(0 0 1 1) - старший разряд
(0 1 1 0) - младший разряд
то есть, в порядке старший-младший, на выходе мы получаем следующие значения:
00 01 11 10
Этот код имеет все свойства кода Грея.
Именно эти значения используются валкодером мыши для кодирования данных дибайта как для оси Х, так и для оси У.
Пример четырёхбитового валкодера.
64133
На сегодня - всё, zen
А компьютер не подвисает из за просчёта данных от мыши интересно ?
Здравствуйте, atariki!
Как всегда, у Вас весьма существенные вопросы. :)
Чтение мыши заключается в некой предподготовке...
мы должны прочесть только мышь, а не месиво битов.
Мы должны прочесть и учесть только данные по одной координате, затем по другой.
Затем, только, как-то это всё обработать.
Я уже тыщу раз настаивал на том, что машкоды НЕ УМЕЮТ НИЧЕГО, кроме как гонять байты туда и сюда!
Посмотрите на картинку из предыдущего поста Atari-8 PORTA register.
Если мы хотим считать только горизонтальные данные из CJ2, мы должны:
1. Считать PORTA
2. Очистить CJ1
3. Очистить Ver для CJ2
Так мы получим Hor данные.
Взяв старые Hor данные, мы моделируем (из старых и новых [оба Грейкоды]) уникальное (Sic! - так как Грей) смещение по таблице движений.
Здесь совсем нет расчётов. Только перемещения битов. Всё в Аккумуляторе. Для хранения используется стек и ячейки на нулевой странице.
Всё - не только О-о-очень быстро, но и вполне выполнимо. Это - ответ на Ваш вопрос.
Теперь о существенности вопроса.
Проблемы ЕСТЬ!
1. Прежде всего из-за отсутствия прерываний по мыши. То есть код должен постоянно исполняться, а не так как в PC только по прерыванию мыши. Это - расточительно!
(У меня есть идейка, спросить ГУРов о возможности использования прерываний по клавиатуре, так как в 130XE есть 4 неиспользуемых клавиши: F1,F2,F3,F4, которые используются в 1200XL ...)
2. Затем проблема ГДЕ?!!
Где? В каких прерываниях исполняется код?
Если в Таймере, то это может мешать звуку в играх. (Там всё еле-еле. Едва хватает, просто для качественного звука.)
Если в DLI, то это может зверски мешать DOSу, который имеет CRITIC действия.
Я это к тому, что ВСЁ зависит от Вашей программы.
Тем не менее, алгоритм, который выдаёт только покоординатные смещения вполне и просто реализуем и работоспособен как в DLI, так и в TIMER прерываниях.
Я писал в TIMER, так как мне нужен был одновременно работающий DOS.
Ладно.
Подскажите, с чего начать.
Демка содержит четыре раздела.
1. Чтение Мыши
2. Таймер
3. PM курсор
4. Реакция на кнопку.
Абсолютно не знаю с чего начать.!
zen
Приступим, помолясь.
Давате пройдём вдоль алгоритма...
ВНИМАНИЕ!
В действительности здесь ПО ОШИБКЕ описаны вычисления по оси Y, а алгоритм касается оси X. (Различие пинов для Джойстика и Мыши!)
Сам алгоритм верный. См. последнее примечание!
rdport
1. В Акумулятор, данные порта грузятся командой
LDA (LoaD Accumulator)
lda PORTA ; %EWSNewsn
2. Теперь надо избавится от данных CJ1. Делается это командой
LSR (Logical Shift Right) она сдвигает байт на один бит вправо, заполняя слева недостающий бит слева нулём ...
lsr a
lsr a
lsr a
lsr a ; %0000EWSN
3. Очищенный от CJ1 байт сохраняем на стек командой
PHA (PusH Accumulator)
Теперь байт и в Аккумуляторе и на стеке
pha
4. Теперь избавляемся от данных по оси X содержащихся в старшем дибайте ниббла командой
AND (AND with Accumulator)
Эта команда сохраняет всё, что под единицами маски и обнуляет остальное
and #%00000011 ; %000000SN
5. Пора формировать СМЕЩЕНИЕ по таблице движений movtab.
Данная таблица содержит значения (-1),0,(+1), при этом в Атарьской знаковой системе (-1)=255.
(Просто начиная со 128, автоматически устанавливается флаг процессора N - Negative), итак:
(-1) - движения нет, просто сохраняем Y в ypos
0 - движение вниз, значит координату экрана по y надо уменьшить
1 - движение вверх, значит координату экрана по y надо увеличить
Важная заметка!
переменная oldy имеет следующую структуру, полученную при инициализации/формировании: %0000sn00. Увидите далее...
ORA (OR with Accumulator) устанавливает соответствующие маске биты в 1
ora oldy ; %0000snSN, где sn - старое значение из oldy
То есть из двух Грейкодов, старого и нового, мы сформировали в Аккумуляторе однозначный ниббл, (значения от 0 до 15) являющийся смещением в таблице movtab.
6. Смещение будем задавать в регистре X командой
TAX (Transfer Accumulator to X)
tax ; %0000snSN в X
7. Смещение задано. Избавимся от старшего дибайта в Аккумуляторе.
and #%00000011 ; %000000SN
8. Формируем новое значение oldy (См. Заметку...) командой
ASL (Arythmetic Shift Left) - Сдвигает влево биты в аккумуляторе, заполняя недостающие справа нулями.
(Арифметический, потому, что учитывает переполнение, но нам это не нужно.)
asl a
asl a ; %0000sn00
9. Инициализируем переменную этим значением командой
STA (STore Accumulator to memory)
sta oldy
10. Всё готово для обработки данных экрана и клиппирования области экрана!
Заметка:
Клиппирование - это проверка на невыход курсора за область экрана.
; Загружаем координату по Y в Y-рег
ldy ypos
; Индекс в таблице задан в X-рег ранее, читаем данные в таблице
lda movtab,x
; Учитывая инфо из пункта 5. исследуем полученное и осуществляем соответствующую обработку
bmi story ; (-1) - просто сохраняем
bne decry ; (+1) - уменьшаем координату
incry
iny ; (0) - иначе, увеличиваем координату
cpy maxy ; Сравниваем с MAX значением...
bcc story ; ... если в зоне экрана, просто сохраняем
decry
dey ; ... при выходе за экран, уменьшаем координату (возвращаем в экран)
cpy miny ; Те же действия с MIN значением
bcc incry ; ... при выходе за экран, увеличиваем координату
story
sty ypos ; Просто сохраняем координату
...
Вот такой вот алгоритм для обработки оси Y
Чтобы обработать ось X, нужно сначала прочитать со стека сохранённое значение, затем избавиться от данных по оси Y и далее, действовать по этому же алгоритму
Примечание:
Дьявол прячется в мелочах.
Я честно запрограммировал с нуля хендлер, но он внезапно стал алгоритм по Y применять к X и наоборот.
Когда я догадался посмотреть на выложенные в предыдущих постах картинки портов я заметил, что в STM1 - другое расположение дибайтов по направлениям, чем в джойстике!!! Посмотрите сами!
Вот правильный код:
; This handler must be in interrupt (DLI or TIMER)
; It may read mouse near 800 times/sec
rdport
;
; handler for X
lda PORTA
lsr a
lsr a
lsr a
lsr a
pha
and #3
ora oldx
tax
and #3
asl a
asl a
sta oldx
ldy xpos
lda movtab,x
bmi storx
bne decrx
incrx
iny
cpy maxx
bcc storx
decrx
dey
cpy minx
bcc incrx
storx
sty xpos ; Datum for PMG
;
; The same for Y
pla
lsr a
lsr a
and #3
ora oldy
tax
and #3
asl a
asl a
sta oldy
ldy ypos
lda movtab,x
bmi story
bne decry
incry
iny
jmp checky
decry
dey
checky
cpy miny
bcc incry
cpy maxy
bcs decry
story
sty ypos ; Datum for PMG
...
; Program Variables
oldx .by 126
oldy .by 128
xpos .by 126
ypos .by 128
maxx .by 208
maxy .by 224
minx .by 48
miny .by 32
;
; The Indextable (-1)=no action!
movtab
.by 255,1,0,255,0,255,255,1,1,255,255,0,255,0,1,255
blackmirror
24.02.2018, 13:15
ezswift, не знаю насколько эффективно это можно перевести на ассемблер, но для сигналов энкодера есть такой алгоритм обработки:
if(Bit0!=Old0 || Bit1!=Old1){
if(Bit0!=Old1)
++Coo;
else
--Coo;
Old0=Bit0; Old1=Bit1;
}
Здравствуйте, blackmirror!
Я не совсем понимаю смысл вопроса.
Буду отвечать по частям.
Эффективна ли реализация эмуляции ЛЮБОГО языка высокого уровня на ассемблере? Да!
Можно ли на Ассемблере реализовать более эффективно алгоритм? Да!
Отвлечёмся...
Насколько я знаю, для Атари эффективно НЕ реализован ни один Си-подобный язык.
Те, два, которые претендуют на ANSI C и на К&R С (1-издание), а именно CC65 и CC8 либо неприемлемо медленны (СС65 называют улиткой из рода черепах), либо недостаточно наварочены. Всё упирается в 8-bit и 1,79 MHz.
Лично мне милее CC8... Я на нём даже написал библиотеку работы с кучей(HEAP) и небезызвестный Гуру FJC написал мне, что даже и не слышал о таком языке и думал, что структуры реализованы только в CC65.
Знаю также то, что современные проекты для Атари пишутся на Ассемблере (ну, на кросс-ассемблерах)...
Я-то люлю писать на Атари всё, что для Атари... (Ну, может коды на ПиСи, а Ассемб/Компи-лирую на железе)
И вообще, мой любимый язык для Атари - это PL65. (раньше был Interlisp65)
Что касается алгоритма, то все проверки на несоответствие старых и новых значений в нём реализованы.
Обновление данных для чтения - тоже. Кроме этого, самая сугубая фишка данного кода - LUT.
То-есть использование заранее заданных таблиц, вместо вычислений. Перенёс данные за 1/2 такта и ответ в кармане.
А то, что на Си можно написать короткий код, мы понимаем.
А длина машкода этого кода, увы зависит от реализации ЯВУ. А здесь нет данных, кроме дезассемблирования.
Ассемблер - это всего лишь машкоды!
Не обессудьте, если я чего не понял. Потому как в начале признался...
zen
А давайте-ка мы займёмся Player-Missile Graphics (PMG) ...
Я, лично, до этого дорос, увы!.. Старый, - я.
Итак, после включения исправной Атари, Вы имеете полностью готовую систему прорисовки PFG (Play Field Graphics)! Для неё в Атари включены логические S: и E: девайсы!!! Далее, всё сабо-самой мазуреется!
Что же включено в систему для прорисовки PMG?
Правильно, Ничего!
Делаем главный вывод: - ЭТО НЕСИСТЕМНАЯ ФУНКЦИЯ графического сопроцессора!
То есть программист PMG должен быть системщиком. А системщик знает массу бяков:
- ограничения
- условия
- невозможности
...
Итак, зная, что так или иначе, картинка рисуется при помощи:
ANTIC (Alpha-Numeric Television Interface Cоntroller) ...
-, а Вы думали, что ANTIC - это просто примочка? НЕТ!
- Это полноценный ЧПУ (и само слово микроконтроллер именно отсюда), управляющий 2D графикой.
Просто у него, исходя из задач, вывод в память заменён выводом в шину GTIA.
...И, системный DMA, поэтому, = только ЧИТАТЕЛЬ!!!
А вот GTIA (Graphics Television Interface Adapter), это = переходник, просто - ЦАП!
- Сильно усовершенствованный, кстати...
Оцвечивает графику самостоятельно (по возможности) и может PMG отрисовать...
Просто, у ANTIC оказалось ещё аж ДВА неиспользуемых канала DMA (для Игроков и Ракет) и это и есть изначальная суть PMG. (В смысле обработки она ведёт себя так же как PFG.)
Не заморачиваясь на конкретности, можно сказать, что ANTIC готовит цифровой поток байтов для GTIA, а GTIA готовит потоковый аналоговый сигнал для TV.
И теперь, что мы имеем с гуся?
1. Регистры управлением Чипами
2. Регистры, дающие нам инфо
и фантазию...
64711
64712
64713
64714
64715
Теперь мы - системщики!
Наша машинка(PMG) работает сама, но данные получает с помошью DMA из памяти.
Памяти игроков или памяти ракет.
Мы, обязательно создадим её... Присущую нам PMG!
Займёмся ОГРАНИЧЕНИЯМИ:
1. ANTIC умеет читать данные из памяти только:
- с границы 2k - для SLR(Single Line Resolution)
- с границы 1k - для DLR(Double Line Resolution)
2. GTIA имеет только 3-bit шину связи с ANTIC...
ЭТО ЗНАЧИТ,
- при приёме потока в виде ДАННЫХ, = просто 1 байт. (Ограничение по размеру Игрока 0-7 битов)
- при приёме потока как АДРЕСА, 1 байт адресует 256 байтов пространства. (0-255 байтов в видеобуфере по X или Y)
Для отрисовки PFG в регистре SDMCTL(System Direct Memory Control) управления DMA изначально включены:
- Бит включения DL DMA
- Биты размера PFG (Узкое, Нормальное, Широкое поле графики)
Также есть:
- Бит включения DMA для Игроков
- Бит включения DMA для Ракет
- Бит вертикального разрешения (Нормально=DLR) Двойная-Одинарная линия сканирования на экране TV.
Заодно, стоит отметить, что также как и в GR.0, в PMG горизонтальное разрешение составляет 2 бита (как ширина вертикальных линий у букв).
Ну, и теперь радостное...
Атари совершенно НИЧЕГО не знает о PMG, так как это просто видеооверлей!!!
Если Вы правильно выведете на экран PMG, Атари сможет убрать его только софт или колд ресетом.
ПРОЕКТИРОВАНИЕ:
1. Так как нам нужен МАЛЫЙ объект графики - стрелка курсора, мы выбираем самые высокие разрешения и по горизонтали и по вертикали.
То есть - NPW(Normal Player Width) и SLR(Single Line Resolution)
2. Для SLR мы должны выделить 2к памяти на границе кратной 2к. Неважно где, но если хотите работать с ДОСом, то лучше сверху, чтобы не повышать LOMEM.
3. Где-то в памяти задать имидж игрока для автоматического его считывания при помощи DMA.
Моя стрелка курсора такая: (системно будет удвоена по ширине и станет КРАСИВШЕ!)
%Ж_______
%_ЖЖ_____
%_ЖЖЖЖ___
%__ЖЖЖЖЖ_
%__ЖЖЖЖ__
%___ЖЖЖ__
%___Ж__Ж_
%_______Ж
4. Инициализировать всё для DMA в SDMCTL
5. Разрешить отрисовку данных из DMA в оверлей (DMA = только ЧИТАТЕЛЬ!) в GRACTL.
2 = отрисовывает только Игроков, 3 = отрисовывает и Игроков и Ракеты.
GRACTL, фактически и запустит то, что ребятишки называют системой PMG. :)
Что же включено в систему для прорисовки PMG?
Правильно, Ничего!
Делаем главный вывод: - ЭТО НЕСИСТЕМНАЯ ФУНКЦИЯ графического сопроцессора!
То есть программист PMG должен быть системщиком. А системщик знает массу бяков:
Ваши речи иногда зажигательны
Вот текст Демки:
; pl0.com
; Draws player above working DOS screen
.ou pl0.com
; ====================
; Z-page Variables
zppy = $cc ; Z-page pointer for y
zppx = $ce ; Z-page pointer for x
xini = $d0 ; initial x value
yini = $d1 ; initial y value
; --------------------
; Included LIBS
.in syseq.mae
.in pmgeq.mae
.in macro.mae
; --------------------
; Variables
stotop = $100 ; storage (adress)
; ====================
; Main PROG
.or $0600
start
; --------------------
; Bump Down RAMTOP
mab RAMTOP stotop ; Save old RAMTOP
sec
sbc #$08
sta RAMTOP ; Set new RAMTOP
sta PMBASE ; Tell ANTIC
sta 1+zppx ; for erase SUBR
mib 0 zppx
; --------------------
; Reset GR mode immediately
ofs 6
bfa sname
lda GRMODE
jsr setgr
; --------------------
; Init player/missile graphics
mib 126 xini
mib 128 yini
mib $3a SDMCTL ; Set SLR,PLDMA=on
; --------------------
; Clear out pm area of ram
ldy #0 ; Reverse counter
erase
lda #0 ; Eraser byte
sta (zppx),y
dey ; 255->0 counter
bne erase ; Wait for 0 in counter
inc 1+zppx ; Set next page
lda 1+zppx ; Here checks are we done?
cmp stotop ; Are we on old RAMTOP?
beq erase ; Yes! Clear it from 255->0
bcc erase ; No! Continue...
; --------------------
; Calculate y position
lda RAMTOP
clc
adc #4 ; Go to P0 page in SLR
sta 1+zppy ; Set up P0 area
mab yini zppy ; y-pos = offset from P0
; Draw shape
ldy #0 ; Normal counter
draw
lda shape,y ; get byte of shape
sta (zppy),y ; put it in place
iny
cpy #shlen
bne draw
mab xini HPOSP0
sta zppx
mib $0e PCOLR0 ; Not Utilized!
; Color needed to be refreshed every VBI!
mib $02 GRACTL
;
rts ; Return to DOS
; ====================
; Used Subroutines
; --------------------
; Set Up Graphics Mode
setgr
pha ; Mode
jsr close
pla
tay ; Y=Opt (AUX1)
and #16+32 ; TXTWIN+NoClear
eor #16 ; flip TXTWIN
ora #OPUPD ; Open rw
jmp open
; --------------------
; Included SUBS
.in iolib.mae
; Needed data
sname .by "S:",$9b
shape .by %10000000
.by %01100000
.by %01111000
.by %00111110
.by %00111100
.by %00011100
.by %00010010
.by %00000001
shlen = *-shape-1
; ====================
; Setup Run Address
.or RUNAD
.wo start
;
.en
; >>> End Of File <<< ;
А во вложении образ диска Атари с MAE Macroassembler и моими библиотеками. :)
64716
А вот и рисуночек:
64717
- - - Добавлено - - -
Ваши речи иногда зажигательны
Хочется писать интересно даже о структуре веника...
И моё занудство можно рассматривать (при перенормировке координат) как лаконичность, только наоборот. :)
Меня всегда удручало то, что во всех руководствах про PMG специально скрыты его корни.
Даются либо общие правила "ходи туда, клади что взял", либо описываются сверхподробные шажки, абсолютно несистемные, а их же куча!
В общем, PMG - это непросто.
PMG весело, да. Когда у нас в городе Атарями заведовал кооператив "Формат", то выдавить инфу о спрайтах удалось только с помощью жесткого троллинга.
насчет мыши: было бы хорошо, если бы она была приспособлена к Koala(есть версия для джойстик), или, например, сделать подобие текстового редактора: клик кнопкой - перенос курсора, даже в Бейсике.
Я думаю, что самое нужное на сегодня - это 64-колоночный редактор программ. С мышью, разумеется. :)
Во первых он и русское Ж вместит,.. а во вторых и длинные комментарии к коду.
Самый лучший и привычный (от MS-DOSa), на сегодня - это редактор MAE.
Разумеется, с Русским он не в ладах.
Вот два изображения разных редакторов:
64718
64719
Мне они больше всего нравятся.
Но я никогда не знал ничего о разноколоночном отображении букв!
Никогда я не программировал текстовых редакторов.
Зато, вот моя демка мыши.
Её код и данные полностью находятся в области PMG!!! и если мы защитим PMG от ресета, то он будет жив и при перезагрузке.
Он движет мышь и мигает курсором при нажатии LFB мыши (то есть реагирует на действия).
Значит, можно использовать соответствующие подпрограммы... Далее, можно использовать даже систему столкновений в ANTIC!
Увы, этот код не написан на основе библиотек MAE (так просто ляпял сдуру на MAC/65), а теперь переводить его - нихоцца!
64720
Создание текстового редактора - интересная задача.
ram_scan
23.03.2018, 17:29
Создание текстового редактора - интересная задача.
Она интересная ровно до того момента как додумаешься что текст надо хранить в памяти двумя кусками, от начала памяти до курсора, потом дырка, и хвост до конца памяти. Но это выдумано уже до нас =) Это добавляет сложностей в код навигации, текст от курсора надо рисовать снизу вверх до начала экрана и сверху вниз до его конца. И немного усложняет код сохранения, особенно если в руках нет путней 16битной арифметики. Зато снимает весь остальной геморрой по вставке чегойта в середину. Да, переход из начала в конец документа будет подтупливать, потому-что надо его весь по памяти перетащить чтоб перетащить "дырку". Зато вставка всегда происходит в пустое место, а при навигации "страница вверх"/"страница вниз" нужно двигать по памяти максимум одну страницу, и торопливость этого дела критического значения уже не имеет.
На Atari реализовать все же проще. Более замороченно с операциями над текстом.
Хотим мы этого, или не хотим, но самым хардверным артефактом в PMG является жесткая зависимость от GTIA. По факту - от устанавливаемого нами вертикального Разрешения PMG.
Для двойного разрешения, поток обработки задерживается на время одной линии сканирования и при тех же входных данных, GTIA прорисовывает строку дважды.
Все вертикальные координаты должны быть сокращены вдвое, иначе, изображения уйдут за экран. Также, изменяется структура памяти.
Из чего исходить?
Из жесткой связки - Вертикальное разрешение-Распределение памяти...
Именно поэтому, во всех руководствах нам рекомендуют:
1. Сначала, выделить область PMG для конкретного разрешения.
Вот код на PL65, который правильно устанавливает соотношения. Фактически, это - программа инициализации глобальных данных.
!- Globals ----------
BYTE pmRes,pmZone,plOffs,vPos
! Resolution may be Single=1 or Double=2 lines
PROC pmSetMem(pmRes)
BEGIN
IF pmRes=1 THEN
pmZone=RAMTOP-16
plOffs=$0400
plAddr=pmZone*$0100+plOffs
ELSE
pmZone=RAMTOP-8
plOffs=$0200
plAddr=pmZone*$0100+plOffs
vPos=vPos/2
ENDIF
END
2. Затем, установить требуемые режимы DMA
Стандартные установки реализуют DMA-чтение DL и PF, в режиме Нормальной ширины экрана.
Фактически, нам нужно задать Разрешение PMG и к стандартным установкам (SDMCTL=34) добавить чтение Игроков, Ракет, или обоих.
Порядок инициализации - несущественен, так как оверлей прорисовывается не средствами ANTIC, а средствами GTIA. Я это - к тому, что пока не включена трансляция GTIA, на экран ничего не выводится.
Для отрисовки курсора нам нужен один Игрок P0.
Осталось инициализировать DMA-чтение Игроков с заданным разрешением (SDMCTL=58) и, затем, дополнительные параметры отображения. (цвета, ширину и пр.).
PROC pmSetDma()
CONST mask=%00011000 ! (SGL=16)+(PLDMA=8)
BEGIN SDMCTL=SDMCTL+mask END
Вот рабочий код Демки на PL65:
!====================================!
! PMGR.PRG !
!------------------------------------!
! Evgeny Zolotarev,(aka 576XE), 2018 !
!====================================!
!= Global Data ======================!
!====================================!
!- Equates --------------------------!
BYTE RAMTOP=$6A,COLPF2=$02C6
BYTE PMBASE=$D407,SDMCTL=$022F,GRACTL=$D01D
BYTE HPOSP0=$D000,SIZEP0=$D008,PCOLR0=$02C0
!- Player Bitmap --------------------!
BYTE PLAYER[8]
DATA
%10000000,
%01100000,
%01111000,
%00111110,
%00111100,
%00011100,
%00010010,
%00000001;
!- Program Variables ---------------!
BYTE pmZone,hPos,vPos
INT p0offs,p0addr
!= Procedures & Functions ===========!
!====================================!
!- Main Procedure -------------------!
MAIN()
BEGIN
GRACTL=0 ! На всякий случай...
hPos=126 HPOSP0=hPos
PCOLR0=52 COLPF2=128 SIZEP0=0
pmZone=RAMTOP-16 p0offs=1024
p0addr=pmZone*256+p0offs
FILL(p0addr,256,0) ! Очистка p0zone
vPos=128 MOVE(.PLAYER,8,p0addr+vPos) ! Прорисовка в нужном месте
PMBASE=pmZone ! Доложили ANTICу
SDMCTL=58 ! Добавили plDma+SLR
GRACTL=2 ! Транслируем Игроков без Ракет
END
!====================================!
!------- >>> End Of File <<< --------!
Вскоре напишем на Ассемблере :)
zen
мелькала ссылка на пример PMG, да трэд пропал
Чтобы нарисовать в ассемблере Игрока, мы совсем не обязаны производить многие 'стандартные' действия!
Вот три пути отрисовки PMG:
I. Рисуем под Дисплей Листом, а значит, заведомо сохраняем данные PF графики.
Считаем:
PF занимает ~1k памяти в режиме GR.0
- Так как нам всё равно надо выравнивать графику PMG по границам, кратным 1-page, так и примем размер PF как 1k. (4 страницы)
- В SLR размер области PMG добавит ещё 2k, то есть (8 страниц).
В сумме это = 12 страниц.
Сугубо не кратно 8 (то-есть 2k)!!!
Добавляем ещё 4 страницы и Вуаля... Получаем наши (RAMTOP-16).
II. В случае, если мы хотим расположить 2k PMG над RAMTOP, мы так или иначе должны сместить вектор RAMTOP вниз, на размер PM-области, чтобы основной текст тоже как-то отрисовывать!
И тогда, мы должны ресетировать GR.0, при этом выравнивание стандартной графики АВТОМАТИЧЕСКИ установит GR.0 ниже RAMTOP. Вот для чего нужен ресет!
III. В случае, когда Вы сами выбираете адрес базирования PMG (достаточно удалённый от обоих границ!) Вы СОВСЕМ не обязаны ресетировать режим графики, что и показывает Демка с использованием мыши.
В ней мы прекрасно выводим на экран директорию диска, имея на экране работающую PMG.
Новая Демка преследует цель ВЫЧИСЛЯТЬ адрес PMG. Затем по-честному Ресетировать графику и отрисовывать курсор.
; pl0.com
; Draws player above working DOS screen
.ou pl0.com
; ====================
; Z-page Variables
zppy = $cc ; Z-page pointer for y
zppx = $ce ; Z-page pointer for x
xini = $d0 ; initial x value
yini = $d1 ; initial y value
; --------------------
; Included LIBS
.in syseq.mae
.in pmgeq.mae
.in macro.mae
; --------------------
; Variables
stotop = $100 ; Temporary storage(adress)
; ====================
; Main PROG
.or $0600
start
; --------------------
; Bump Down RAMTOP
mab RAMTOP stotop ; Save old RAMTOP
sec
sbc #$08
sta RAMTOP ; Set new RAMTOP
sta PMBASE ; Tell ANTIC
sta 1+zppx ; for erase SUBR
mib 0 zppx
; --------------------
; Reset GR mode immediately
ofs 6
bfa sname
lda GRMODE
jsr setgr
; --------------------
; Init player/missile graphics
mib 126 xini
mib 128 yini
mib $3a SDMCTL ; Set SLR,PLDMA=on
; --------------------
; Clear out p0 area of ram
ldy #0 ; Reverse counter
erase
mib $00 (zppx),y
dey ; 255->0 counter
bne erase ; Wait for 0 in counter
inc 1+zppx ; Set next page
lda 1+zppx ; Here checks are we done?
cmp stotop ; Are we on old RAMTOP?
beq erase ; Yes! Clear it from 255->0
bcc erase ; No! Continue...
; --------------------
; Calculate y position
lda RAMTOP
clc
adc #4 ; Go to P0 page in SLR
sta 1+zppy ; Set up P0 area
mab yini zppy ; y-pos = offset from P0
; Draw shape
ldy #0 ; Normal counter
draw
mab shape,y (zppy),y
iny
cpy #shlen
bne draw
mab xini HPOSP0
sta zppx
mib $0e PCOLR0 ; Needed to be refreshed in VBI!
mib $02 GRACTL
;
rts ; Return to DOS
; ====================
; Used Subroutines
; --------------------
; Set Up Graphics Mode
setgr
pha ; Mode
jsr close
pla
tay ; Y=Opt (AUX1)
and #16+32 ; TXTWIN+NoClear
eor #16 ; flip TXTWIN
ora #OPUPD ; Open rw
jmp open
; --------------------
; Included SUBS
.in iolib.mae
; Needed data
sname .by "S:",$9b
shape .by %10000000
.by %01100000
.by %01111000
.by %00111110
.by %00111100
.by %00011100
.by %00010010
.by %00000001
shlen = *-shape-1
; ====================
; Setup Run Address
.or RUNAD
.wo start
;
.en
; >>> End Of File <<< ;
Всем Атаристам, ПРИВЕТ!
Уточнение:
Я говорил о 64 колонном редакторе! Это существенно.
Нужно писать именно программистский редактор, а не издательскую систему с проверкой слов и различными выводами на печать...
Видать, это - не тривиальная задача, так как даже под новейшими CON 64 (Зверски быстрые функции (прокрутка и пр.) , бесквирковая работа [квирки в МАЕ - обычная вещь!]), для SDX, редакторов-то, как раз и нет!
2ram_scan
Если Вы заметили, мышка в примере работает с максимальной графической для PM точностью.
То есть графический Y отражается точно, а X , будучи в размере удвоенным, ездит по экрану через две КолорКлоки, то есть с удвоенной скоростью.
Можно специально загрубить ВОСПРИЯТИЕ передвижения курсора мышки до байта высоты строки и байта ширины знакоместа, тогда Инверсный курсор будет прыгать с центра одного знакоместа на центр другого (при плавном движении стрелки на экране).
Видел, ранее, как люди делают.
Однако, ясно, что должнЫ существовать и ЛОГИЧЕСКИЕ структурные переменные текста, такие как - текст До_Курсора и После_Курсора, или - текст До_Экрана и После_Экрана...
В общем, не зная, как ляпать 64-колоночную панель, я - не в курсе даже как к этому приступить. Ну, не программист я!
Сейчас, с трудом, занят унификацией интерфейсов контроллеров.
Где-то словил логическую ошибку, и по осям движение ПОЧТИ предсказуемое, однако пока глуповастое...
Исправлял костылями и где-то улучшил, а где-то испортил.
Такие вот дела.
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions, Inc. All rights reserved. Перевод: zCarot