Вредительство в виде запланированного устаревания — это не более чем приятный побочный эффект для упырей.
Основные — дешевле на один разъём, больше места под радиатор, радиатор стал дешевле, маркетологи прокукарекали про тонкую тонкость и увеличили продажи… а что проц нельзя поменять — тут скорее так. Осторожно попробовали воду… строчка в счёте «Прокатило, 5000» прокатила… бить сразу не начали… ура, теперь можно делать так! Гов… теперь новая норма!
И таки да, чтобы вернуть съёмные процы, придётся довольно серьёзно побороться за место. Возможно, делать нижнюю стенку (в области проца) двухслойной и заполненной сеточкой с теплоносителем, а процессорную плату прикручивать прямо к ней, «лбом об стену». И собрав таким образом корпусо-радиаторо-процессор, насаживать на его коннекторы материнку и всю её барахляндию. У — Уё… удобство :-D
Но в целом всё вполне реально без превращения буков в кирпичи 386-й эпохи. Корпус достаточной толщины, чтобы оно не гнулось в рюкзаке от соседства с бумажной книгой, ломая к чертям матрицу и материнку — легко вмещает и сокет под проц, и 2.5" ширококарту, и съёмную батарею, особенно если её сделать без отдельного корпуса — снизу откручивается крышка, а на неё изнутри банки наклеены. Размер точно как у современных несъёмных, но вот только она-то снимается — крышку открутил, снял, хлястик сдёрнул…
Intel выкатил в индустрию вариант своего видения стандарта
…причём после этого рейтинг моей гневной статьи из -10 превратился в -5 практически мгновенно. Типа «о, сам Интел что-то выкатил, похоже, эта точка зрения не настолько прямо уж маргинальна, как нам сначала показалось!»
Не, ну я знал, на что шёл — я писал то, что я не мог не высказать, а не то, что всем хотелось бы услышать :) но, надо сказать, внезапный «вотэтоповорот!» с Интелом и резким изменением рейтинга уже давно «устаканившейся» статьи был для меня сюрпризом.
Какую модульность хотите видеть и использовать лично Вы, можем пообсуждать в комментариях.
В целом, как мне кажется, возможность снять 80% сливок с предложенной мной «толстокарты» уже упущена. Не, ну она перманентно актуальна — всегда будет проблема тепловыделения с толстых шинных SSD, и она потребует какого-то крупного форм-фактора, а зачем изобретать велосипед, если есть 2.5" и есть ExpressCard, который можно дополнить описанным мной образом. Но самый лучший момент был даже не год назад, когда я это выкатил, а годика эдак три как минимум. Да, концепт оброс новыми фишками, вплоть до втыкания туда модуля-смартфона (проверено рынком, было популярно) и выноса его из-под клавиатуры, чтобы можно было верх ему вообще открытым сделать (минус немного пресловутой толщины, на которой все так рехнулись, плюс возможность взаимодействия с модулем не только с торца), но лучше быть своевременным на троечку, чем опоздавшим на праздник жизни крутым. Да, это всё ещё можно сделать. Но это уже не главное.
Главное сейчас — проблема длины линий от проца до памяти, она же проблема «не можем не распаивать». Решение её я выкатывал ещё чёрт знает когда, лень даже логи постов рыть, во всяком случае, годы назад. Суть: процессорная плата и материнская — разные вещи. Материнская — это PCIe-хаб, куча всякого обвеса, слоты, если хочется — в том числе и слот под внешнее видео, или распаянное на материнке видео, или уповаем на видео-ядро проца, или делаем слот под «толстокарту», или вообще делаем материнку для десктопа… Процессорная — это сокет под камень, короткие линии и распаянная память, которую не приходится «приводить к общему знаменателю» в виде SODIMM. Ну, и линии PCIe Root с камня на материнку, конечно же. В итоге проц спокойно вынимается из процессорной платы в случае любого апгрейда или ремонта, а если надо память апнуть (или её ухитрились спалить) — меняется «плата-прокладка» с перестановкой проца в новую. В плане же длины линий — что сокет, что распайка, на скорость оно не влияет, в отличие от SODIMM. С таким же успехом можно было бы и память расставить в «кроватки», но памяти-то много, а проц — один :)
Что же до баллов, то тут концепция «толстокарты» явно просится снова в бой… распаивать самим что SODIMM с памятью, что эту мою процессорную плату с ней же — по баллам, по идее, одинаково выходит. А вот обвешать его со всех сторон «толстокартовыми» слотами, в которые можно поставить как обычный ExpressCard-осциллограф с Али, так и куда более быструю «толстокартовую» плату собственного производства (таки экспресс х16, не х1) — уже совсем другое дело. Как я в первой статье писал, стандарт довольно гибкий выходит — место там найдётся и картам поменьше (гигабитка, Wi-Fi, да хоть под оптоволокно, если сисадмину удобнее иметь всё в корпусе, а не болтающееся на проводах), и картам побольше (видео, шинные SSD с пудовым радиатором), и даже старому доброму харду (но харды баллов не дают — в наших краях не гнездятся, предпочитают жаркие страны :-D) в общем, можно наворотить изрядный ассортимент, а «балловость» ноута, как я понимаю, определяется количеством специфичных плат, которые тупо вставляются в слоты перед продажей? АЦП для цифрового осциллографа, свитч-хаб с 4 гигабитками, просто дополнительная батарея, пачка лишнихнелишних!!! USB 3.0, RS-485 для электронщиков, оооо, можно столько комплектаций намутить… %)
Меня только смущает, кто будет для этого дела UEFI писать и каких закладок напихать может. Модульность без открытой прошивки, которую может пересобрать и проверить админ на конечном предприятии — в целом скорее пшик, чем модульность.
Оно работает только до тех пор, пока под столом у них есть возможность давить каблуком на сустав большого пальца соседу. Как показал опыт IBM в 80-е — как только кто-то может безнаказанно делать модернизируемые, модульные машины, все «спектрумы» и «коммодоры» тут же с громким «фпух» превращаются в тыкву. Триумфальный марш «пека» не остановил даже конский ценник — за модульность люди охотно платили втрое.
Хотя, конечно, юзеры тогда были дальновиднее и себе на уме. Сейчас они дрессированные — что им показали, то и купят, а то, что через год менять надо будет — считают нормой. Но в эту игру можно играть и вдвоём — есть же всякие экологические движения, которые передрессировывают (с ума сойти, убунтовская проверка орфографии знает это слово!) юзеров под себя, а кто не с нами — тот х, г и разрушает планету. Но это работает только тогда, когда у юзеров есть выбор — а пока гранды держат друг другу каблук на болевой точке ноги и никто не может «сыграть в IBM 80-х» назло остальным, подвижек не будет.
Сделать DOS-подобную операционку, опирающуюся на все готовые абстракции железа, предоставляемые как BIOS, так и (забытый ныне термин!) BIOS Extensions (которые были на каждой уважающей себя плате в виде отдельной микрухи ROM) — не так уж и сложно.
Он офигенен в другом: он выкинул из головы всё, что было сделано до него, включая файловые системы и файловые форматы, и отмочил что-то своё, самобытное, в собственной парадигме. Айти с нуля. Взял и попробовал файл типа «база данных по всем разнородным данным, которые объединяет только одно — проект, для которого они предназначены». Захотел и сделал, не спрашивая, насколько это здоровый подход. И правильно поступил, ящтаю.
А трудоёь… трудоёмкость современных ОС в основном в том, что UEFI уёфищен и железо приходится поддерживать ручками, через стотыщмильонов дров. Это совсем не концепция старой славной IBM. Это концепция M$, испохабившей платформу, как только IBM зазевалась (ну не сделали вовремя BIOS32 с аналогичными BIOS Ext, что поделаешь).
Возможно, сложность современных дров превзошла возможности этого подхода, конечно. А может, и нет. Мы ведь не попробовали. VESA BIOS Ext (VBE) были поскрёбышем технологии, и там уже 16-битность сильно «жала в плечах». Пытались преодолеть, но… как-то не договорились, что ли.
Вопрос хороший. Обычно при снятии давления такие вещи начинают медленно «таять».
Это сильно безопаснее обычного баллона, потому что трещину-то оно может своим конским давлением обеспечить, но дальше эта трещина не развивается в большой бабах, а просто начинает медленно уныло свистеть, пока не высвистит всё содержимое.
То есть сгореть чуть ли не сложнее, чем на обычном бензобаке.
Да я понимаю :) Правда, на бывших органистов они тоже не очень похожи :-D
Кстати, избавиться от песенки в голове не получилось :-D
«Тихий наш японский городок!
Мицубиси здесь клепает танки¹…
Но, увы, распорядился рок,
Что про танки те прознали яяяяяяяяяяяянки!
Два друга — Толстяк и Малыш
Прутся в гости в награду за смеееелооооость…»
¹Вообще-то торпеды и орудия. Но они не рифмуются. И скинет на них второго «друга» уже не «Весёлая Энола», а «Козловагончик» (Bockscar, в честь капитана Bock'a — то есть «Козлова» на наши деньги).
Там вопрос скорее в том, сколько циклов перезарядки оно держит, не отравляясь.
Оно не для «мировых объёмов» — оно для заправки ГБО без создания таких давлений, какие никто не захочет иметь у себя под задницей.
У метана октановое число 110. Может, теплота сгорания так себе, но он позволяет такие сжатия и такие КПД, что он неплох даже сам по себе, не учитывая цену. Если бы он сжижался так же, как пропан, на нём бы давно уже ездили бы так же.
И тут газовые гидраты такие — «подержите моё пи воду». Если опасные давления требуются только в момент его «зарядки», а дальше он без большой просьбы не отдаёт метан (или отдаёт медленно) — это ровно то, что нужно.
Всё, залил на Итч, имя старое. Кроме всего, там быстрый фикс ошибки отрисовки (в той функции, что всё равно под нож, но пусть пока хотя бы не сбивает с толку глюками) и попытка сделать текстуру, хотя бы в одном квадранте, где всё в плюс :)
Там же, кстати, есть и передача угла, под которым мы видим стенку — она ещё пригодится, для косо срезанных тайлов, конечно :)
В общем и целом, удалось столько тактов освободить, что, может, лучше вообще статусбар не делать и полностью заюзать поле? Тогда базовый вариант (смещать финальную сцену на -4..+3) вообще даёт идеальную камеру, полоска в пол-тайла слева-справа экрана пусть будет какая хочет, а скорость получается просто космос, вероятны полные 60 фпс и куча времени на подновление части полигонов :)
Думаю, на фулскрин теперь-то уж у нас такты есть ^__________^ Смотрю сам на результат и тащусь, как питон :-D
UPD: жду вопросов по коду, ну и если удастся .EXE запустить на стареньком четырёхъядернике с XP, чтобы оценить всё великолепие тщательно замаскированных 8 бит и 32 лучей — было бы тоже неплохо :) это прямо мышкой ощутить надо, вживую, чтобы разделить со мной мои визги восторга :-D
Библиотеку дробей я изначально писал, так как у СС65 они вообще никак не реализованы, только целые типы.
…и ещё сто раз пригодится…
Значит у тебя сейчас лучевая шестеренка на 128 зубьев? А как же рывки?
А рывков-то и нету ^_________^ шестерёнка же делает эквивалентным смещение вдоль экрана и смещение по углу :) Разрешение больше, чем одна колонка тайлов — всё равно не получим, ну разве что делать тайлы-половинки, но это ад. При поле зрения в 90° — 4 раза по 32, то есть 128 лучей у нас всего может в принципе существовать. Остальное — излишество, всё равно не увидим, только таблица распухнет. Ну как бы, если их так и оставить 32, картинка немножко дёрганая (терпимо, но нежелательно), но я ж опосля смещаю на -4..+3 пиксела всю сцену, и получается совершенно идеальная плавность %) Думаю, можно и другими способами выравнивать на доли луча, но я уже смирился с дёрганым статусбаром, в картинку шлема всё обернуть и пускай дёргается на здоровье %) очень уж реакция на мышку приятная, а зрение всё равно занято сценой. Но если очень претит — можно другие варианты сглаживания продумывать. Главное — больше 32 из 128 кастов нам физически не нужно, не увидим. А цена их — очень высока.
проще знак хранить в таблице, чем генерировать его через свитч
Скорость. Просто скорость. Я вот таки допинал оптимизацию до полной однобайтности. Такты нам очень пригодятся — полигоны, геймплей…
Но вычисления, связанные с тангенсом, в однобайтные числа как закладывать? Тангенс около 90 градусов может быть +- 160
А вот так, как я это там и сделал. Меньше шести килобайт готовых произведений любого 8-битного на любой из 32 неповторяющихся (ко)тангенсов, дающих результаты не более 8-битного.
Да, это очень геморно. Поэтому и взялся — паззла же. И на выходе — ещё куча тактов сэкономлена.
В общем, я считаю там тангенсы, умножаю на них величины от о до 255 и как только результат разбух больше байта (по геометрии такого не бывает, значит, не понадобится) — его в таблицу не кладу и перехожу к следующему тангенсу. Получается таблица из 32 указателей (увы, в пекашную память — пока не ассемблировал под 6502, но это тривиальный перевод), которые указывают на стартовый нолик (очередной тангенс из 32 уникальных, умноженный на 0).
Достаточно взять указатель на тангенс, соответствующий лучу (я их, конечно, тоже в шестерёнку положил, чтобы быстро извлекать) и по нему взять смещение, равное умножаемому числу (подразумевая, что число < 256 и результат умножения < 256, что всегда так в силу геометрии). И там будет лежать готовое произведение, и всего 6 кб, даже меньше ^__________^
Таким образом, стартовый прыжок к стенке клетки и финишный «недо-шаг», если впилились по смещению — берутся из таблицы одним обращением ^___________^
Ага, я сейчас тоже думаю, как бы выдумать такую таблицу расстояний (точнее, сразу размеров текстуры), в которой на маленьких расстояниях (где важен каждый пиксел) шаг входных значений маленький, а на больших (где даже шаг в целую клетку не всегда меняет размер текстуры хотя бы на пиксел) — большой :)
И при этом чтобы обращение к этой таблице не требовало кучу вычислений позиции старшего бита и вот это вот всё %) а в идеале — её ещё как-то с Factor скрестить, чтобы и лишних операций не было, и стенки не выпучивались %)
Этих операций хоть и всего 32 на кадр, причём независимо от дистанции каста — но очень уж сами операции жирные %)
UPD:
меня такой результат устроил бы вполне
Охохо, он устраивает даже меня! А я — придирчивый фанат шутеров :)
А уж со скошенными углами и освещением… да с полигональными роботами… ммммм ^______^
А камера таки идеально плавная сейчас, ценой статусбара %) Хотя если хватит скорости всё поле залить — можно статусбар вообще выкинуть, кек :)
Поясняю, поелику мне по реакции показалось, что никто ничего не понял :)
Речь не о «госплане на стероидах», а о той парадигме, при помощи которой Вы как раз и решили мне продемонстрировать несостоятельность «госплана на стероидах».
Чем и продемонстрировали состоятельность как раз этой парадигмы. А речь-то я вёл как раз о ней! А уже какие именно методы ей доказываются и опровергаются — это дело десятое и сто десятое.
получил за то, что научным же методом показал:
Ключевые слова: «научным методом показал».
Я как раз про то, что показывать, в далёкой эволюционной перспективе развития человеческого мозга, будут именно научным методом.
А не путём акробатической имхонавтики генеральной линии партии вокруг шеста, что аж змея хребет сломает, как в советском анекдоте. Даже если эта партия вместе с лидером тысячу раз выбрана максимально демократично. Всё равно это всё — субъективное мнение, и нет никакой гарантии, что они не скажут, мол, «мы считаем, что экономические акторы рациональны, и ничего вы с этим не поделаете, потому что сами за нас единогласно голосовали».
А «научным методом показал» — это объективная истина, не субъективная. Нерациональны и точка, хоть обголосуйтесь, заряд электрона этим не изменить.
Вот я про что.
Просто очень мало людей в популяции готовы принять это и поставить выше инстинктов примата. Конкретно в этом треде — ровно двое нас.
UPD: слегка косячит AddToZBuffer8Bit, переполнением байта страдает и не всегда верно считает высоту. Но это не имеет значения, всё равно он-то идёт под нож в любом случае — там же прямые вычисления, а это всё табличное тоже должно быть.
Но это утопия, конечно. Это что ж за население там должно быть, чтобы мозги в основной массе принимали только доказательный подход, игнорируя базовые инстинкты примата напрочь. Сверхлюди какие-то, следующая ступень эволюции.
Мы примерно об одном↑. Разница небольшая:
всё остальное: люди, соцструктура, методы сбора информации, управление, полиция, армия , сельское хозяйство, погода - в целом всё, непредсказуемо и нерационально.
Вот это «несовершенство» как раз тоже учесть было бы разумно, научно и рационально, а кидаться очертя голову внедрять — типичный для бледнотиков подход.
В Чили уже пробовали - Cybersyn.
Оно бы не дошло на пушечный выстрел до «попробовать», если бы
мозги в основной массе принимали только доказательный подход
Просто потому, что доказательство работоспособности любой подобной системы представить ширнармассам не удалось бы по причине его отсутствия, по причине отсутствия работоспособности.
Я ведь не про «госплан на стероидах». Я про глобальную парадигму «чьё мнение правильное».
Реверснуть прошивку от «АБ» — это был бы реальный прорыв, потому что люди смогут прикоснуться к раритету, которого почти ни у кого не было.
А учитывая то, что эмуляция ТОЧНАЯ, а не «я художник, я так вижу» — это было бы больше, чем просто прорыв. Это — натуральное сохранение подлинного культурного наследия. Не рэп-ремейков Моцарта, а Моцарта.
#include <iostream.h>
#include <conio.h>
#include <stdlib.h>
#include <math.h>
char const * Blt256 (void __far * Body)
{
unsigned long pval = (unsigned long)Body;
unsigned short Segm = pval>>16;
unsigned short Offs = pval;
Segm+=Offs>>4;
Offs&=0x000F;
// if (Offs > 15) return "Error! Paragraph address should never be inside next paragraphs"; вместо того, чтобы жаловаться, мы сами это гарантировали парой строк выше.
__asm
{
push ds
mov ds, Segm
mov si, Offs
mov ax, 0xA000
mov es, ax
mov di, 0
mov cx, 32000
SR: mov ax,ds:[si]
inc si //That's why we requre addresses loaded in the segment part
inc si
mov es:[di], ax
inc di
inc di
loop SR
pop ds
}
return NULL;
}
#define GRUDAK 48
#define MAP(Y,X) (Map[(Y)*256+(X)]) //Map is 32x256
int DoClipping (unsigned short __far* X, unsigned short __far* Y, signed short StepX, signed short StepY, unsigned char __far *Map) //Wolf3d Clone
{
unsigned short MinX, MaxX, MinY, MaxY;
MinX = *X-GRUDAK+StepX;
MaxX = *X+GRUDAK+StepX;
MinY = *Y-GRUDAK+StepY;
MaxY = *Y+GRUDAK+StepY;
if (MAP(MinY>>8&31,MinX>>8&255)!=' '|
MAP(MinY>>8&31,MaxX>>8&255)!=' '|
MAP(MaxY>>8&31,MinX>>8&255)!=' '|
MAP(MaxY>>8&31,MaxX>>8&255)!=' ')
{
if (!StepX || !StepY) return 0;
return DoClipping(X, Y, StepX, 0, Map) || DoClipping(X, Y, 0, StepY, Map);
}
*X+=StepX;
*Y+=StepY;
return 1;
}
float TheFamousFastRSqRt (float Val)
{
long i;
float f=Val;
i = *(long *)&f;
i = 0x5f3759df - (i>>1);
f = *(float *)&i;
return f * (1.5 - Val*.5*f*f);
}
short BhaskaraI_Sine_0_to_180deg(short Arg) //Arg 512 = 180 deg.
{
signed long Temp,Temp2;
Temp=Temp2=Arg;
Temp2=511-Temp2;
Temp*=4096*Temp2;
Temp/=327680-((signed long)Arg)*Temp2;
return Temp; //Sine X 1024
}
struct
{
unsigned short ColumnHeight;
unsigned char IsMOb, TexturePos, TextureNum;
} NanoZBuffer[1]; //A single wall.
static unsigned short FactorGPU[32]; //С "колесом" эту поправку можно и вовсе выкинуть -- что так нелинейно, что так. Не такой уж и страшный рыбоглаз получается.
int AddToZBuffer8Bit ( unsigned char FactorIndex,
unsigned char CameraXCell, unsigned char CameraXPixel, unsigned char CameraYCell, unsigned char CameraYPixel,
unsigned char XCell, unsigned char XPixel, unsigned char YCell, unsigned char YPixel,
unsigned char Type)
{
signed long DistX, DistY;
//if (Type<128) {ProcessMObMarker(Type)} else {
//Следующий крупный шаг -- конечно, свести весь этот матан в готовое табличное представление.
DistX = XCell*256+XPixel, DistY = YCell*256+YPixel;
DistX -= CameraXCell*256+CameraXPixel, DistY -= CameraYCell*256+CameraYPixel;
DistX*=DistX, DistY*=DistY;
NanoZBuffer[0].ColumnHeight = (float)FactorGPU[FactorIndex] * TheFamousFastRSqRt (DistX+DistY); //С поправкой на фишай
// NanoZBuffer[0].ColumnHeight = 32768L * TheFamousFastRSqRt (DistX+DistY); //Без поправки (фишай как он есть)
NanoZBuffer[0].IsMOb = 0;
NanoZBuffer[0].TexturePos = XPixel+YPixel; //Silly placeholder instead of a real texture position
NanoZBuffer[0].TextureNum = Type; //We have colors instead of textures here.
//return 0;}
return 1;
}
void DrawColumnOfGPUTiles (unsigned char __far FBuff[200][320], int SpriteColumn, int Angle_Last_3bit)
{
int Column;
int ScreenOffset = SpriteColumn*8+32-4+(Angle_Last_3bit); //Ну, это для VGA. Реально он развалится на две величины -- "в какой тайл писать" и "на сколько их все смещать".
unsigned short Height = NanoZBuffer[0].ColumnHeight;
for (int i=0; i<8; i++)
{
//Тут мы будем условно считать, что у нас неограниченное количество тайлов, то есть мы можем сделать любые варианты косо срезанных углов.
//Тем более, что я всё это пока что отключил -- ну вообще фиговые углы получились, лучше пока никаких не будет. А уж на колонки нецелой высоты нам точно хватит спрайтов, там всего 7 вариантов, ну или 14, если нижние делать отдельно.
Column = ScreenOffset + i;
for (int y=0; y<200; y++) //In DOSBox, this loop takes... forever.
{
// if (y<NanoZBuffer[0].ColumnHeight) FBuff[y][Column]=255; else FBuff[y][Column]=0;
int TexY;
if (Height) TexY = (signed long)(y-100)*256L / (signed long)(Height) + 128;
else TexY=256-128*(y==100);
if (TexY<0 || TexY>255) FBuff[y][Column]=24;
// else FBuff[y][Column]=WallTexture[TexY][NanoZBuffer[0].TexturePos];
else FBuff[y][Column]=NanoZBuffer[0].TextureNum-'0'+1; //Условимся считать, что мы можем раскрашивать каждый тайл (что не так, можно только пачки 2х2).
/*
//Фиговатенько, но идею отражает
if (Height>80 && Height<=120)
{
if ((y+Column)&1) FBuff[y][Column]=24; //Попробуем как-то затекстурировать в зависимости от размера
}
if (Height>120)
{
if ((y/2+Column/2)&1) FBuff[y][Column]=24; //Попробуем как-то затекстурировать в зависимости от размера
}
*/
}
}
}
#define MAP_PAGE_6502 2 //Страница 0 -- квазирегистры, страница 1 -- стек, страница 2 -- с места в карьер, карта. Остальное пусть за ней лежит :) Потом переложим как надо.
static unsigned char Memory6502[256][256]=
{
"", //квази-регистры
"", //стек
//Дальше карта 32x256, дёшево и сердито -- адреса никакие не надо вычислять, старший байт 16-битного адреса сразу даёт строку, а младший -- столбец.
//Соответственно, можно просто использовать переменные координат ячейки и не страдать :) Как вариант -- столбец брать из регистра, вроде такая команда у 6502-го есть (16-битный адрес из квази-регистра плюс 8-битная "добавочка" из настоящего).
"111111111111111111111111",
"1 111111111111111111",
"11112 111111111111111111",
"11112 111111111111111111",
"11112 333333311111111111",
"11 11111111111",
"1111133333 1111111111111",
"1111111111 2111111111111",
"1111 11111 2111111111111",
"1111 11000 2111111111111",
"111 2111111111111",
"1111 91111 1111111111111",
"1111 9111111111111111111",
"1111 9111211111111111111",
"1111 111111111111",
"111111111211111111111111"
};
static unsigned char X6502, Y6502, A6502;
unsigned long Ticks6502 = 0;
//#define LDA_immediate(X) {A6502=X; Ticks6502+=2;} //Вот по такому типу будем считать такты
void main (void)
{
int x,y,i;
char const * Err;
static unsigned char __far Body256[200][320];
int IsMouse, MouseX, MouseY, MouseB;
__asm
{
mov ax, 0x0013 //VGA 320x200
int 0x10
}
__asm
{
mov ax,0x0000
int 0x33
mov i,ax
}
IsMouse = (i==-1);
//Сейчас пусть будут дублироваться, для отладки так проще. Потом закомментирую старые пекашные переменные и оставлю только 6502-е.
unsigned short PlayerX=256*1.5, PlayerY=256*1.5; //inside the Cell 1x1
signed short PlayerA=0; //Angle+-32768 = +-180 deg.
//Здесь мы распределим первые 256 байт (квази-регистры) под самые "горячие" переменные.
#define PLAYER_X_CELL 0x80 //Начнём со 128, думаю, остального хватит по уши :) Этот -- координата игрока X, старший байт (номер клетки в лабиринте)
#define PLAYER_X_PIXEL 0x81 //Это -- младший (номер пиксела в клетке)
#define PLAYER_Y_CELL 0x82 //Старший по Y
#define PLAYER_Y_PIXEL 0x83 //Младший по Y
#define PLAYER_A_RAY 0x84 //Старший байт угла игрока, указывает угол с точностью до кастуемого луча (используется 7 бит, потому что полный оборот = 128, а поле зрения = 32, по числу колонок экрана).
#define PLAYER_A_PIXEL 0x85 //Младший байт угла игрока, указывает остаток в пикселах от 0 до 7 (т. е. три бита), чтобы аппаратно довернуть всю сцену уже в тайлах.
// signed short BrezStep, BrezShift, BrezToStart, BrezToFinish;
//BrezStep не используем, вместо этого делаем не две ветви, а четыре (каждую ветвь для горизонтального/вертикального шага делим на ветви для положительного/отрицательного).
#define BREZ_SHIFT 0x86 //Их тоже делим пополам (положительное и отрицательное смещение), итого -- 8 кейсов, каждый заточен под свой случай и решает его максимально быстро.
#define BREZ_TOST 0x87
#define BREZ_TOFIN 0x88 //...и многое другое, которое сюда придётся вписать по ходу дела, потому что промежуточных переменных будет много, а регистров -- только три.
// unsigned short BrezPos, BrezPrev, BrezCell;
//Вот эта парочка должна идти подряд! Они вместе образуют в квазирегистровой области памяти полный 16-битный адрес, по которому мы можем проверить наличие стенки при рейкасте всего одной командой LDA!
//Никаких суммирований с началом карты в памяти, никаких сложений, копирований, переносов -- после любого смещения и/или шага правильный адрес уже там. Быстрее КМК просто нереально :)
//Но! Поскольку, в зависимости от того, шагаем мы по Y или по X, в этих переменных будет лежать XY или YX -- при ассемблировании в половине случаев их придётся взаимно переименовать, чтобы всегда Y был в старшем
//байте, а X -- в младшем. Это чисто синтаксическое переименование, и оно, в зависимости от выбранного тут порядка следования, будет или в case 0..3, или в case 4..7. Оставим это на "сладкое" (на момент фактического перевода на ассемблер 6502).
#define BREZ_CELL 0x89 //тут нам хватит 8 бит, мы сразу избавляемся от остатка "до края следующей ячейки" и дальше шагаем сугубо по ячейке.
#define BREZ_POSCELL 0x8A //а вот смещение состоит из 2 байт. Этот -- номер ячейки.
#define BREZ_POSPIX 0x8B //Этот -- номер пиксела.
#define BREZ_PREVPIX 0x8C //Тут хранится только номер пиксела, потому что номер ячейки для вычислений остатка шага не важен.
// int ColNum;
#define COLNUM 0x8D //Ну и эту штуку заодно, раз уж пошло такое дело... хотя она вообще не принципиальная, пока не началось реальное ассемблирование.
unsigned char WallNum_IF_Temp; //The value is assigned inside the comparsion. You've been warned.
// signed short RayDX, RayDY; //Упорные Бивис и Баттхед, прибить было нелегко :)
signed short DX=1024, DY=0; //cos and sin of PlayerA X 1024.
signed short A_0_180; //В рейкасте больше не участвует, только в окружающем его пекашном отладочном коде.
unsigned short RayIndex;
static struct
{
// signed short RayDX, RayDY;
unsigned char Case; //Который из 8 случаев, в смысле комбинации соотношения модулей и знаков.
unsigned char BrezShift; //Заранее посчитанное смещение (шаг-то понятно, что 256 без вариантов)
unsigned char *tgPtr, *ctgPtr; //Таблицы умножения величин от 0 до 255 на тангенс и котангенс данного угла, без учёта знака. Таблица обрывается ранее, чем результат умножения достигнет 256.
} Swamp_Dok_Angle_Wheel_Fixed_Tile[128]; //Убрал на фиг запас по разрешению, ну и углы для косо срезанных тайлов тоже пока выкинул -- не до них.
#define TOTAL_TG 5910 //Экспериментально подсчитал :)
static unsigned char MulByTg[TOTAL_TG], *tgPtr[32]; //MulByTg стопудово надо будет класть внутри Memory6502 и "указатели" делать уже сугубо 6502-е.
for (i=-64,RayIndex=0; i<64; i++,RayIndex++) //Эту таблицу посчитали и в ROM положили. На 6502 этого кода вообще не будет, только его выхлоп.
{
signed short BrezShift; //Чисто временная переменная теперь, для расчётов на пека готовых таблиц.
#define PI 3.1415926535897932384626433832795
// Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDX = 1024.0 * cos((i+.5)*PI/64.0);
// Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDY = 1024.0 * sin((i+.5)*PI/64.0);
double RayDX = cos((i+.5)*PI/64.0); //Они больше не входят в таблицу и будут вычисляться только временно, на пека, для заполнения этой таблицы.
double RayDY = sin((i+.5)*PI/64.0);
//cout<<256L*Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDX/Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDY<<endl;
//Тут мы заранее заполним все возможные случаи из 8. То есть все сочетания знаков шага/смещения и соотношения модулей (кто из них шаг, а кто -- смещение).
//Ветвиться внутри каста -- смерть, потому что ветвление у 6502-го очень унылое. А вот switch{case} у него аппаратный и по всей памяти. Его и используем :)
// if (abs(Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDX) > abs(Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDY))
if (fabs(RayDX) > fabs(RayDY))
{ //Главное деление -- кто шаг, а кто смещение.
Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].Case = 0; //Шаг по X
// if (Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDX < 0) //Шаг X, отрицательный
if (RayDX < 0) //Шаг X, отрицательный
{ //Знак шага. Позволяет вовсе BrezStep выкинуть.
Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].Case += 2;
BrezShift = -256.0*sin((i+.5)*PI/64.0)/cos((i+.5)*PI/64.0);
} else { //Шаг X, положительный
BrezShift = 256.0*sin((i+.5)*PI/64.0)/cos((i+.5)*PI/64.0);
}
} else {
Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].Case = 4; //Шаг по Y
// if (Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDY < 0) //Шаг Y, отрицательный
if (RayDY < 0) //Шаг Y, отрицательный
{ //Знак шага. Позволяет вовсе BrezStep выкинуть.
Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].Case += 2;
BrezShift = -256.0*cos((i+.5)*PI/64.0)/sin((i+.5)*PI/64.0);
} else { //Шаг Y, положительный
BrezShift = 256.0*cos((i+.5)*PI/64.0)/sin((i+.5)*PI/64.0);
}
}
if (BrezShift < 0) Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].Case++;
BrezShift = abs(BrezShift);
if (BrezShift>255) BrezShift=255;
Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].BrezShift = BrezShift;
}
for (i=-16; i<16; i++) //only 32 columns per screen
{
// FactorGPU[i+16] = 33554432 / Swamp_Dok_Angle_Wheel_Fixed_Tile[i+64].RayDX; //Опять же, если не решим оставить рыбоглаз.
FactorGPU[i+16] = 32768 / cos((i+.5)*PI/64.0);
}
for (i=0,RayIndex=0; i<32; i++) //Ну вот и вишенка на торте -- тангенсы считаем :)
{
double TG = sin((i+.5)*PI/64.0) / cos((i+.5)*PI/64.0);
tgPtr[i] = MulByTg+RayIndex;
for (long i=0; i<256; i++) if (i*TG < 256.0)
{
if (RayIndex >= TOTAL_TG) return; //Алярм, упячка, голактеко опасносте, произошло невозможное, пыщ-пыщ!
MulByTg[RayIndex] = i*TG;
RayIndex++;
}
//cout<<256L*TG<<" "<<RayIndex<<endl;
}
for (i=-64,RayIndex=0; i<64; i++,RayIndex++)
{
if (i<-32)
{
Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].tgPtr = tgPtr[i+64];
Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].ctgPtr = tgPtr[-33-i];
//cout<<6*Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDY/Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDX<<" "<<(int)Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].tgPtr[6]<<endl;
//cout<<6*Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDX/Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDY<<" "<<(int)Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].ctgPtr[6]<<endl;
//cout<<endl;
continue;
}
if (i<0)
{
Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].tgPtr = tgPtr[-1-i];
Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].ctgPtr = tgPtr[32+i];
//cout<<6*Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDY/Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDX<<" "<<0-Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].tgPtr[6]<<endl;
//cout<<6*Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDX/Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDY<<" "<<0-Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].ctgPtr[6]<<endl;
//cout<<endl;
continue;
}
if (i<32)
{
Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].tgPtr = tgPtr[i];
Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].ctgPtr = tgPtr[31-i];
//cout<<6*Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDY/Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDX<<" "<<(int)Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].tgPtr[6]<<endl;
//cout<<6*Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDX/Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDY<<" "<<(int)Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].ctgPtr[6]<<endl;
//cout<<endl;
continue;
}
Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].tgPtr = tgPtr[63-i];
Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].ctgPtr = tgPtr[i-32];
//cout<<6*Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDY/Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDX<<" "<<0-Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].tgPtr[6]<<endl;
//cout<<6*Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDX/Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].RayDY<<" "<<0-Swamp_Dok_Angle_Wheel_Fixed_Tile[RayIndex].ctgPtr[6]<<endl;
//cout<<endl;
}
//Закончили заполнение таблиц, дальше идёт код для Денди.
for (;;)
{
//Эта часть нам вообще нужна только для всякой беготни, мы её даже трогать не будем. Примем её как часть отладочного кода.
A_0_180 = PlayerA>>6;
if (A_0_180>0) DY=BhaskaraI_Sine_0_to_180deg(A_0_180);
else DY=-BhaskaraI_Sine_0_to_180deg(-A_0_180);
A_0_180 = PlayerA+16384; //Native overflow to a correct angle.
A_0_180 >>= 6;
if (A_0_180>0) DX=BhaskaraI_Sine_0_to_180deg(A_0_180);
else DX=-BhaskaraI_Sine_0_to_180deg(-A_0_180);
int WasFlag; //Позволяет запомнить факт наличия флага и тем самым поддерживать в коде структуру "как в 6502-м ассемблере".
//Когда код уже будет окончательно разбит на 6502-е команды, "идентичные натуральным", вместо этой переменной тоже будет флаг из регистра флагов.
Memory6502[0][PLAYER_X_CELL] = PlayerX>>8;
Memory6502[0][PLAYER_X_PIXEL] = PlayerX&0xFF;
Memory6502[0][PLAYER_Y_CELL] = MAP_PAGE_6502 + (PlayerY>>8); //А давайте смотреть на вещи проще! Пусть логически вся память будет картой, просто не будем героя выпускать за пределы области памяти, где реально карта лежит. Меньше придётся адресов прибавлять :)
Memory6502[0][PLAYER_Y_PIXEL] = PlayerY&0xFF;
Memory6502[0][PLAYER_A_RAY] = ((long)PlayerA+32768) >> 9;
Memory6502[0][PLAYER_A_PIXEL] = (((long)PlayerA+32768) >> 6)&7; //Ну, это тоже типа как часть отладки. Допустим, что по итогам игрового процесса эти переменные у нас поддерживаются актуальными, а вовсе не копируются каждый раз из сишного "отладочного обрамления".
for (Memory6502[0][COLNUM]=0; Memory6502[0][COLNUM]<32; Memory6502[0][COLNUM]++) //Это тут пусть пока тоже побудет по-сишному, сначала перепишем сам каст (одного луча).
{
X6502 = (Memory6502[0][PLAYER_A_RAY] + 16 - Memory6502[0][COLNUM] + 128) % 128;
// RayDX = Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].RayDX; //Вот эти двое и особенно их пропорция в вычислениях всяких ToStart и ToFinish -- буквально
// RayDY = Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].RayDY; //нарываются быть следующими на расправу. Прямо-таки просятся их тоже загнать в какую-то таблицу.
//Но таблица эта непростая, ибо диапазон их пропорции весьма широк, а точность требуется солидная -- таки тангенс, он умеет малой мелочью давать большую разницу!
switch (Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].Case)
{
case 0:
Memory6502[0][BREZ_POSCELL] = Memory6502[0][PLAYER_Y_CELL];
Memory6502[0][BREZ_POSPIX] = Memory6502[0][PLAYER_Y_PIXEL];
Memory6502[0][BREZ_CELL] = Memory6502[0][PLAYER_X_CELL];
Memory6502[0][BREZ_SHIFT] = Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].BrezShift;
//1st step. Must reach the very beginning of a nearest cell in the Step direction, unless a wall in Shift direction blocks our ray.
// BrezToStart = 256-PlayerX%256;
Memory6502[0][BREZ_TOST] = 256 - Memory6502[0][PLAYER_X_PIXEL];
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]+Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOST]]); //Carry flag
Memory6502[0][BREZ_POSPIX]+=Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOST]]; //Разумеется, в настоящем 6502-м мы получим флаг сразу по итогу этой операции.
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]++;
//Если не было переноса по координате смещения -- то и нечего вообще проверять, мы всё в той же ячейке.
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the Wall/MOb after first Shift
{
Memory6502[0][BREZ_TOFIN] = 256-Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][PLAYER_X_CELL],Memory6502[0][PLAYER_X_PIXEL] + Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOFIN]], Memory6502[0][BREZ_POSCELL], 0, WallNum_IF_Temp-1) //Вообще блок может иметь 4 разных цвета для разных граней. Ограничимся двумя -- обычным и декрементированным. Так же было и в Wolf3D, кстати!
) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]++;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the wall/MOb after getting to Step position, even before first Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_CELL], 0, Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], WallNum_IF_Temp) ) goto HitOpaque;
}
for (i=0;i<256;i++)
{
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]+Memory6502[0][BREZ_SHIFT]); //Carry flag
Memory6502[0][BREZ_POSPIX] += Memory6502[0][BREZ_SHIFT];
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]++;
//Аналогично, зачем лезть в память два раза в одно и то же место, если не было переноса :)
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the wall/MOb after Shift
{
Memory6502[0][BREZ_TOFIN] = 256-Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_CELL], Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOFIN]], Memory6502[0][BREZ_POSCELL], 0, WallNum_IF_Temp-1) ) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]++;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the wall/MOb after Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_CELL], 0, Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], WallNum_IF_Temp) ) goto HitOpaque;
}
}
//You'll probably never get here but it's place for processing "too far" distances (fog?)
break;
case 1:
Memory6502[0][BREZ_POSCELL] = Memory6502[0][PLAYER_Y_CELL];
Memory6502[0][BREZ_POSPIX] = Memory6502[0][PLAYER_Y_PIXEL];
Memory6502[0][BREZ_CELL] = Memory6502[0][PLAYER_X_CELL];
Memory6502[0][BREZ_SHIFT] = Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].BrezShift;
//1st step. Must reach the very beginning of a nearest cell in the Step direction, unless a wall in Shift direction blocks our ray.
// BrezToStart = 256-PlayerX%256;
Memory6502[0][BREZ_TOST] = 256 - Memory6502[0][PLAYER_X_PIXEL];
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]-Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOST]]); //Borrow flag
Memory6502[0][BREZ_POSPIX]-=Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOST]];
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]--;
//Если не было отрицательного переноса по смещению -- то и нечего вообще проверять, мы всё в той же ячейке.
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the Wall/MOb after first Shift
{
Memory6502[0][BREZ_TOFIN] = 1+Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL]+Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOFIN]], Memory6502[0][BREZ_POSCELL], 255, WallNum_IF_Temp-1) ) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]++;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the wall/MOb after getting to Step position, even before first Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_CELL], 0, Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], WallNum_IF_Temp) ) goto HitOpaque;
}
for (i=0;i<256;i++)
{
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]-Memory6502[0][BREZ_SHIFT]); //Borrow flag
Memory6502[0][BREZ_POSPIX]-=Memory6502[0][BREZ_SHIFT];
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]--;
//В общем, разумная последовательность выполнения проверок экономит не только один перенос, а ещё и кучу всего :)
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the wall/MOb after Shift
{
Memory6502[0][BREZ_TOFIN] = 1+Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_CELL], Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOFIN]], Memory6502[0][BREZ_POSCELL], 255, WallNum_IF_Temp-1) ) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]++;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the wall/MOb after Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_CELL], 0, Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], WallNum_IF_Temp) ) goto HitOpaque;
}
}
//You'll probably never get here but it's place for processing "too far" distances (fog?)
break;
case 2:
Memory6502[0][BREZ_POSCELL] = Memory6502[0][PLAYER_Y_CELL];
Memory6502[0][BREZ_POSPIX] = Memory6502[0][PLAYER_Y_PIXEL];
Memory6502[0][BREZ_CELL] = Memory6502[0][PLAYER_X_CELL];
Memory6502[0][BREZ_SHIFT] = Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].BrezShift;
//1st step. Must reach the very beginning of a nearest cell in the Step direction, unless a wall in Shift direction blocks our ray.
// BrezToStart = -1-PlayerX%256;
Memory6502[0][BREZ_TOST] = 1 + Memory6502[0][PLAYER_X_PIXEL];
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]+Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOST]]); //Carry flag
Memory6502[0][BREZ_POSPIX]+=Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOST]];
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]++;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the Wall/MOb after first Shift
{
Memory6502[0][BREZ_TOFIN] = 256-Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL] - Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOFIN]], Memory6502[0][BREZ_POSCELL], 0, WallNum_IF_Temp-1) ) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]--;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the wall/MOb after getting to Step position, even before first Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_CELL], 255, Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], WallNum_IF_Temp) ) goto HitOpaque;
}
for (i=0;i<256;i++)
{
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]+Memory6502[0][BREZ_SHIFT]); //Carry flag
Memory6502[0][BREZ_POSPIX] += Memory6502[0][BREZ_SHIFT];
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]++;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the wall/MOb after Shift
{
Memory6502[0][BREZ_TOFIN] = 256-Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_CELL], 255 - Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOFIN]], Memory6502[0][BREZ_POSCELL], 0, WallNum_IF_Temp-1) ) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]--;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the wall/MOb after Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_CELL], 255, Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], WallNum_IF_Temp) ) goto HitOpaque;
}
}
//You'll probably never get here but it's place for processing "too far" distances (fog?)
break;
case 3:
Memory6502[0][BREZ_POSCELL] = Memory6502[0][PLAYER_Y_CELL];
Memory6502[0][BREZ_POSPIX] = Memory6502[0][PLAYER_Y_PIXEL];
Memory6502[0][BREZ_CELL] = Memory6502[0][PLAYER_X_CELL];
Memory6502[0][BREZ_SHIFT] = Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].BrezShift;
//1st step. Must reach the very beginning of a nearest cell in the Step direction, unless a wall in Shift direction blocks our ray.
// BrezToStart = -1-PlayerX%256;
Memory6502[0][BREZ_TOST] = 1 + Memory6502[0][PLAYER_X_PIXEL];
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]-Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOST]]); //Borrow flag
Memory6502[0][BREZ_POSPIX]-=Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOST]];
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]--;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the Wall/MOb after first Shift
{
Memory6502[0][BREZ_TOFIN] = 1+Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL] - Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOFIN]], Memory6502[0][BREZ_POSCELL], 255, WallNum_IF_Temp-1) ) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]--;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the wall/MOb after getting to Step position, even before first Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_CELL], 255, Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], WallNum_IF_Temp) ) goto HitOpaque;
}
for (i=0;i<256;i++)
{
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]-Memory6502[0][BREZ_SHIFT]); //Borrow flag
Memory6502[0][BREZ_POSPIX]-=Memory6502[0][BREZ_SHIFT];
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]--;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the wall/MOb after Shift
{
Memory6502[0][BREZ_TOFIN] = 1+Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_CELL], 255 - Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOFIN]], Memory6502[0][BREZ_POSCELL], 255, WallNum_IF_Temp-1) ) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]--;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_POSCELL]][Memory6502[0][BREZ_CELL]])!=' ') //Hit the wall/MOb after Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_CELL], 255, Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], WallNum_IF_Temp) ) goto HitOpaque;
}
}
//You'll probably never get here but it's place for processing "too far" distances (fog?)
break;
case 4:
Memory6502[0][BREZ_POSCELL] = Memory6502[0][PLAYER_X_CELL];
Memory6502[0][BREZ_POSPIX] = Memory6502[0][PLAYER_X_PIXEL];
Memory6502[0][BREZ_CELL] = Memory6502[0][PLAYER_Y_CELL];
Memory6502[0][BREZ_SHIFT] = Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].BrezShift;
//1st step. Must reach the very beginning of a nearest cell in the Step direction, unless a wall in Shift direction blocks our ray.
// BrezToStart = 256-PlayerY%256;
Memory6502[0][BREZ_TOST] = 256 - Memory6502[0][PLAYER_Y_PIXEL];
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]+Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOST]]); //Carry flag
Memory6502[0][BREZ_POSPIX]+=Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOST]];
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]++;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after first Shift
{
Memory6502[0][BREZ_TOFIN] = 256-Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], 0, Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL] + Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOFIN]], WallNum_IF_Temp) ) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]++;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after getting to Step position, even before first Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], Memory6502[0][BREZ_CELL], 0, WallNum_IF_Temp-1) ) goto HitOpaque;
}
for (i=0;i<256;i++)
{
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]+Memory6502[0][BREZ_SHIFT]); //Carry flag
Memory6502[0][BREZ_POSPIX] += Memory6502[0][BREZ_SHIFT];
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]++;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after Shift
{
Memory6502[0][BREZ_TOFIN] = 256-Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], 0, Memory6502[0][BREZ_CELL], Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOFIN]], WallNum_IF_Temp) ) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]++;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], Memory6502[0][BREZ_CELL], 0, WallNum_IF_Temp-1) ) goto HitOpaque;
}
}
//You'll probably never get here but it's place for processing "too far" distances (fog?)
break;
case 5:
Memory6502[0][BREZ_POSCELL] = Memory6502[0][PLAYER_X_CELL];
Memory6502[0][BREZ_POSPIX] = Memory6502[0][PLAYER_X_PIXEL];
Memory6502[0][BREZ_CELL] = Memory6502[0][PLAYER_Y_CELL];
Memory6502[0][BREZ_SHIFT] = Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].BrezShift;
//1st step. Must reach the very beginning of a nearest cell in the Step direction, unless a wall in Shift direction blocks our ray.
// BrezToStart = 256-PlayerY%256;
Memory6502[0][BREZ_TOST] = 256 - Memory6502[0][PLAYER_Y_PIXEL];
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]-Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOST]]); //Borrow flag
Memory6502[0][BREZ_POSPIX]-=Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOST]];
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]--;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after first Shift
{
Memory6502[0][BREZ_TOFIN] = 1+Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], 255, Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL] + Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOFIN]], WallNum_IF_Temp) ) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]++;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after getting to Step position, even before first Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], Memory6502[0][BREZ_CELL], 0, WallNum_IF_Temp-1) ) goto HitOpaque;
}
for (i=0;i<256;i++)
{
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]-Memory6502[0][BREZ_SHIFT]); //Borrow flag
Memory6502[0][BREZ_POSPIX]-=Memory6502[0][BREZ_SHIFT];
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]--;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after Shift
{
Memory6502[0][BREZ_TOFIN] = 1+Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], 255, Memory6502[0][BREZ_CELL], Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOFIN]], WallNum_IF_Temp) ) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]++;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], Memory6502[0][BREZ_CELL], 0, WallNum_IF_Temp-1) ) goto HitOpaque;
}
}
//You'll probably never get here but it's place for processing "too far" distances (fog?)
break;
case 6:
Memory6502[0][BREZ_POSCELL] = Memory6502[0][PLAYER_X_CELL];
Memory6502[0][BREZ_POSPIX] = Memory6502[0][PLAYER_X_PIXEL];
Memory6502[0][BREZ_CELL] = Memory6502[0][PLAYER_Y_CELL];
Memory6502[0][BREZ_SHIFT] = Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].BrezShift;
//1st step. Must reach the very beginning of a nearest cell in the Step direction, unless a wall in Shift direction blocks our ray.
// BrezToStart = -1-PlayerY%256;
Memory6502[0][BREZ_TOST] = 1 + Memory6502[0][PLAYER_Y_PIXEL];
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]+Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOST]]); //Carry flag
Memory6502[0][BREZ_POSPIX]+=Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOST]];
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]++;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after first Shift
{
Memory6502[0][BREZ_TOFIN] = 256-Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], 0, Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL] - Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOFIN]], WallNum_IF_Temp) ) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]--;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after getting to Step position, even before first Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], Memory6502[0][BREZ_CELL], 255, WallNum_IF_Temp-1) ) goto HitOpaque;
}
for (i=0;i<256;i++)
{
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]+Memory6502[0][BREZ_SHIFT]); //Carry flag
Memory6502[0][BREZ_POSPIX] += Memory6502[0][BREZ_SHIFT];
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]++;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after Shift
{
Memory6502[0][BREZ_TOFIN] = 256-Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], 0, Memory6502[0][BREZ_CELL], 255 - Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOFIN]], WallNum_IF_Temp) ) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]--;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], Memory6502[0][BREZ_CELL], 255, WallNum_IF_Temp-1) ) goto HitOpaque;
}
}
//You'll probably never get here but it's place for processing "too far" distances (fog?)
break;
case 7:
Memory6502[0][BREZ_POSCELL] = Memory6502[0][PLAYER_X_CELL];
Memory6502[0][BREZ_POSPIX] = Memory6502[0][PLAYER_X_PIXEL];
Memory6502[0][BREZ_CELL] = Memory6502[0][PLAYER_Y_CELL];
Memory6502[0][BREZ_SHIFT] = Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].BrezShift;
//1st step. Must reach the very beginning of a nearest cell in the Step direction, unless a wall in Shift direction blocks our ray.
// BrezToStart = -1-PlayerY%256;
Memory6502[0][BREZ_TOST] = 1 + Memory6502[0][PLAYER_Y_PIXEL];
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]-Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOST]]); //Borrow flag
Memory6502[0][BREZ_POSPIX]-=Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].ctgPtr[Memory6502[0][BREZ_TOST]];
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]--;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after first Shift
{
Memory6502[0][BREZ_TOFIN] = 1+Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], 255, Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL] - Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOFIN]], WallNum_IF_Temp) ) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]--;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after getting to Step position, even before first Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], Memory6502[0][BREZ_CELL], 255, WallNum_IF_Temp-1) ) goto HitOpaque;
}
for (i=0;i<256;i++)
{
Memory6502[0][BREZ_PREVPIX] = Memory6502[0][BREZ_POSPIX];
WasFlag = 256&(Memory6502[0][BREZ_POSPIX]-Memory6502[0][BREZ_SHIFT]); //Borrow flag
Memory6502[0][BREZ_POSPIX]-=Memory6502[0][BREZ_SHIFT];
if (WasFlag)
{
Memory6502[0][BREZ_POSCELL]--;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after Shift
{
Memory6502[0][BREZ_TOFIN] = 1+Memory6502[0][BREZ_PREVPIX];
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], 255, Memory6502[0][BREZ_CELL], 255 - Swamp_Dok_Angle_Wheel_Fixed_Tile[X6502].tgPtr[Memory6502[0][BREZ_TOFIN]], WallNum_IF_Temp) ) goto HitOpaque;
}
}
Memory6502[0][BREZ_CELL]--;
if ((WallNum_IF_Temp=Memory6502[Memory6502[0][BREZ_CELL]][Memory6502[0][BREZ_POSCELL]])!=' ') //Hit the wall/MOb after Step
{
if ( AddToZBuffer8Bit (Memory6502[0][COLNUM], Memory6502[0][PLAYER_X_CELL], Memory6502[0][PLAYER_X_PIXEL], Memory6502[0][PLAYER_Y_CELL], Memory6502[0][PLAYER_Y_PIXEL],
Memory6502[0][BREZ_POSCELL], Memory6502[0][BREZ_POSPIX], Memory6502[0][BREZ_CELL], 255, WallNum_IF_Temp-1) ) goto HitOpaque;
}
}
//You'll probably never get here but it's place for processing "too far" distances (fog?)
break;
}
HitOpaque: DrawColumnOfGPUTiles(Body256, Memory6502[0][COLNUM], Memory6502[0][PLAYER_A_PIXEL]);
}
Blt256 (Body256[0]);
if (kbhit())
{
char In=getch(); //Of course, real games use BIOS keyboard interrupt.
if (In=='q') break;
if (In=='a') PlayerA += 2048;
if (In=='d') PlayerA -= 2048;
if (In=='w')
{
DoClipping (&PlayerX, &PlayerY, DX/8, DY/8, &(Memory6502[MAP_PAGE_6502][0]) );
}
if (In=='s')
{
DoClipping (&PlayerX, &PlayerY, -DX/8, -DY/8, &(Memory6502[MAP_PAGE_6502][0]) );
}
}
if (IsMouse)
{
__asm
{
mov ax,0x03
int 0x33
mov MouseB,bx //mouse buttons
mov ax,0x0b
int 0x33
mov MouseX,cx //mouse x
mov MouseY,dx //mouse y
}
PlayerA-=MouseX*16;
if (MouseB&2) //RStrafe
{
DoClipping( &PlayerX, &PlayerY,
- DX*((signed long)MouseY)/256 + DY/16,
- DY*((signed long)MouseY)/256 - DX/16,
&(Memory6502[MAP_PAGE_6502][0]) );
}
else if (MouseB&4||MouseB&1)//LStrafe
{
DoClipping( &PlayerX, &PlayerY,
- DX*((signed long)MouseY)/256 - DY/16,
- DY*((signed long)MouseY)/256 + DX/16,
&(Memory6502[MAP_PAGE_6502][0]) );
} else {
DoClipping( &PlayerX, &PlayerY,
- DX*((signed long)MouseY)/256,
- DY*((signed long)MouseY)/256,
&(Memory6502[MAP_PAGE_6502][0]) );
}
}
__asm //Limiting FPS to 18.2 per second (for 80286 it could be very good!)
{ //In DOSBox, you'll barely have more than 1 FPS, but on i5/WinXP this code prevents from hitting walls instantly.
mov ax, 0
mov es,ax
mov ah,es:[0x046c] //system timer
WaitForTick: mov al,es:[0x046c]
cmp ah, al
je WaitForTick
}
}
while (kbhit()) getch();
}
@Swamp_Dok, «призываю заклинанием» в тред для сдачи этапа работ.
Получилось.
Вот сырец, который можно переводить в ассемблер практически 1:1. И, раз уж я больше недели как знаком с 6502-м — быстрее и лучше, чем я сейчас там сделал, вряд ли вообще в принципе возможно (да, я действительно вот так вот ориентируюсь в лоу-левеле, даже в новой для себя архитектуре. Это пятиминутка гордыни, но не преувеличение).
Скринов не будет — я не пожертвовал вообще ничем от слова «совсем», то есть скрины не изменились.
Пояснения и поддержка — разумеется, будет. Это хоть формально и си для пека, но в душе оно уже давно 6502-й ассемблер, поэтому читабельность (относительно первого примера со «школьным» кодом) просела НЬ МАЛЪ АЖ ОФИГЕТЬ. Всё поясню, на все вопросы отвечу.
Здесь был сам код, но он не влез в каммент вместе с текстом
Всё это можно смело ассемблировать, корректируя конкретные адреса сообразно мапперу и моей поддержке, и не тратить больше силы на 2.5D, а сразу делать свои полигоны. В них я не настолько копенгаген и не шибко помогу, в отличие от вот здесь вот вот это вот.
Синусов там больше нет. Ну то есть они есть — для физики. Для рейкаста всё посчитано заранее и лежит в таблице «колеса».
Тангенсов тоже больше нет. Есть заранее посчитанная таблица умножения на тангенс, для каждого из 32 возможных углов. Котангенсы и всё остальное для остальных 96 углов получаются индексами. Поскольку из геометрии следует, что результат умножения всегда меньше 256 — таблица неполная, то есть как только результат доходит до 256, она обрывается, обращений туда всё равно не будет. В итоге получилось меньше даже, чем одна страничка маппера.
Карта имеет размер 32×256, чтобы её можно было проверить за 5 тактов.
Я имею в виду — ВСЕГО за 5 тактов. Не сшивая две однобайтных переменных в адрес. Не добавляя адрес начала карты. «Птичка уже в скворечнике», когда мы хотим чекнуть ячейку карты — адрес уже сложился в квазирегистровой области сам собой. Мы просто делаем LDA. Переноса между байтами там тоже не будет. Кастрировать карту до 16×16 — не нужно. Скорости это не добавит, а, вероятно, ещё и убавит, потому что переменные придётся собирать в адрес. Размеры помещений, чтобы не кастить слишком далеко, мы просто ограничиваем при рисовании карт (в Wolf3D тоже не было залов на пол-уровня).
Нет, реально. Я свёл весь рейкаст к 8-битной арифметике между переменными, лежащими в 0-й странице, и обращениям к относительно небольшим таблицам. Быстрее КМК даже пытаться не стоит, трата времени.
Самое смешное, если это всё 1:1 совпало с собственными попытками ;) Потому что кто-то тут тоже был на финишной прямой, судя по ответам %)
Теперь о текстурировании. Пока то да сё, я попробую затекстурировать стенки «рыбьей чешуёй», которая по вертикали отмасштабирована согласно размеру столбца, а по горизонтали — столбца × то, под каким углом мы смотрим на текстуру. Вероятно, это позволит зрению лучше ощущать угол, а вариантов текстуры потребуется не особо-то и много. Передать данные про угол из рейкаста будет легко — мы знаем, какой из углов кастим, и знаем, в горизонтальную или вертикальную стенку он воткнулся (это вообще разные вызовы функции обработки конца луча).
Вредительство в виде запланированного устаревания — это не более чем приятный побочный эффект для упырей.
Основные — дешевле на один разъём, больше места под радиатор, радиатор стал дешевле, маркетологи прокукарекали про тонкую тонкость и увеличили продажи… а что проц нельзя поменять — тут скорее так. Осторожно попробовали воду… строчка в счёте «Прокатило, 5000» прокатила… бить сразу не начали… ура, теперь можно делать так! Гов… теперь новая норма!
И таки да, чтобы вернуть съёмные процы, придётся довольно серьёзно побороться за место. Возможно, делать нижнюю стенку (в области проца) двухслойной и заполненной сеточкой с теплоносителем, а процессорную плату прикручивать прямо к ней, «лбом об стену». И собрав таким образом корпусо-радиаторо-процессор, насаживать на его коннекторы материнку и всю её барахляндию. У — Уё… удобство :-D
Но в целом всё вполне реально без превращения буков в кирпичи 386-й эпохи. Корпус достаточной толщины, чтобы оно не гнулось в рюкзаке от соседства с бумажной книгой, ломая к чертям матрицу и материнку — легко вмещает и сокет под проц, и 2.5" ширококарту, и съёмную батарею, особенно если её сделать без отдельного корпуса — снизу откручивается крышка, а на неё изнутри банки наклеены. Размер точно как у современных несъёмных, но вот только она-то снимается — крышку открутил, снял, хлястик сдёрнул…
…мозг программиста :-D
Есть такой автор. Тут такой автор :)
…причём после этого рейтинг моей гневной статьи из -10 превратился в -5 практически мгновенно. Типа «о, сам Интел что-то выкатил, похоже, эта точка зрения не настолько прямо уж маргинальна, как нам сначала показалось!»
Не, ну я знал, на что шёл — я писал то, что я не мог не высказать, а не то, что всем хотелось бы услышать :) но, надо сказать, внезапный «вотэтоповорот!» с Интелом и резким изменением рейтинга уже давно «устаканившейся» статьи был для меня сюрпризом.
В целом, как мне кажется, возможность снять 80% сливок с предложенной мной «толстокарты» уже упущена. Не, ну она перманентно актуальна — всегда будет проблема тепловыделения с толстых шинных SSD, и она потребует какого-то крупного форм-фактора, а зачем изобретать велосипед, если есть 2.5" и есть ExpressCard, который можно дополнить описанным мной образом. Но самый лучший момент был даже не год назад, когда я это выкатил, а годика эдак три как минимум. Да, концепт оброс новыми фишками, вплоть до втыкания туда модуля-смартфона (проверено рынком, было популярно) и выноса его из-под клавиатуры, чтобы можно было верх ему вообще открытым сделать (минус немного пресловутой толщины, на которой все так рехнулись, плюс возможность взаимодействия с модулем не только с торца), но лучше быть своевременным на троечку, чем опоздавшим на праздник жизни крутым. Да, это всё ещё можно сделать. Но это уже не главное.
Главное сейчас — проблема длины линий от проца до памяти, она же проблема «не можем не распаивать». Решение её я выкатывал ещё чёрт знает когда, лень даже логи постов рыть, во всяком случае, годы назад. Суть: процессорная плата и материнская — разные вещи. Материнская — это PCIe-хаб, куча всякого обвеса, слоты, если хочется — в том числе и слот под внешнее видео, или распаянное на материнке видео, или уповаем на видео-ядро проца, или делаем слот под «толстокарту», или вообще делаем материнку для десктопа… Процессорная — это сокет под камень, короткие линии и распаянная память, которую не приходится «приводить к общему знаменателю» в виде SODIMM. Ну, и линии PCIe Root с камня на материнку, конечно же. В итоге проц спокойно вынимается из процессорной платы в случае любого апгрейда или ремонта, а если надо память апнуть (или её ухитрились спалить) — меняется «плата-прокладка» с перестановкой проца в новую. В плане же длины линий — что сокет, что распайка, на скорость оно не влияет, в отличие от SODIMM. С таким же успехом можно было бы и память расставить в «кроватки», но памяти-то много, а проц — один :)
Что же до баллов, то тут концепция «толстокарты» явно просится снова в бой… распаивать самим что SODIMM с памятью, что эту мою процессорную плату с ней же — по баллам, по идее, одинаково выходит. А вот обвешать его со всех сторон «толстокартовыми» слотами, в которые можно поставить как обычный ExpressCard-осциллограф с Али, так и куда более быструю «толстокартовую» плату собственного производства (таки экспресс х16, не х1) — уже совсем другое дело. Как я в первой статье писал, стандарт довольно гибкий выходит — место там найдётся и картам поменьше (гигабитка, Wi-Fi, да хоть под оптоволокно, если сисадмину удобнее иметь всё в корпусе, а не болтающееся на проводах), и картам побольше (видео, шинные SSD с пудовым радиатором), и даже старому доброму харду (но харды баллов не дают — в наших краях не гнездятся, предпочитают жаркие страны :-D) в общем, можно наворотить изрядный ассортимент, а «балловость» ноута, как я понимаю, определяется количеством специфичных плат, которые тупо вставляются в слоты перед продажей? АЦП для цифрового осциллографа, свитч-хаб с 4 гигабитками, просто дополнительная батарея, пачка
лишнихнелишних!!! USB 3.0, RS-485 для электронщиков, оооо, можно столько комплектаций намутить… %)Меня только смущает, кто будет для этого дела UEFI писать и каких закладок напихать может. Модульность без открытой прошивки, которую может пересобрать и проверить админ на конечном предприятии — в целом скорее пшик, чем модульность.
Оно работает только до тех пор, пока под столом у них есть возможность давить каблуком на сустав большого пальца соседу. Как показал опыт IBM в 80-е — как только кто-то может безнаказанно делать модернизируемые, модульные машины, все «спектрумы» и «коммодоры» тут же с громким «фпух» превращаются в тыкву. Триумфальный марш «пека» не остановил даже конский ценник — за модульность люди охотно платили втрое.
Хотя, конечно, юзеры тогда были дальновиднее и себе на уме. Сейчас они дрессированные — что им показали, то и купят, а то, что через год менять надо будет — считают нормой. Но в эту игру можно играть и вдвоём — есть же всякие экологические движения, которые передрессировывают (с ума сойти, убунтовская проверка орфографии знает это слово!) юзеров под себя, а кто не с нами — тот х, г и разрушает планету. Но это работает только тогда, когда у юзеров есть выбор — а пока гранды держат друг другу каблук на болевой точке ноги и никто не может «сыграть в IBM 80-х» назло остальным, подвижек не будет.
Сделать DOS-подобную операционку, опирающуюся на все готовые абстракции железа, предоставляемые как BIOS, так и (забытый ныне термин!) BIOS Extensions (которые были на каждой уважающей себя плате в виде отдельной микрухи ROM) — не так уж и сложно.
Он офигенен в другом: он выкинул из головы всё, что было сделано до него, включая файловые системы и файловые форматы, и отмочил что-то своё, самобытное, в собственной парадигме. Айти с нуля. Взял и попробовал файл типа «база данных по всем разнородным данным, которые объединяет только одно — проект, для которого они предназначены». Захотел и сделал, не спрашивая, насколько это здоровый подход. И правильно поступил, ящтаю.
А трудоёь… трудоёмкость современных ОС в основном в том, что UEFI уёфищен и железо приходится поддерживать ручками, через стотыщмильонов дров. Это совсем не концепция старой славной IBM. Это концепция M$, испохабившей платформу, как только IBM зазевалась (ну не сделали вовремя BIOS32 с аналогичными BIOS Ext, что поделаешь).
Возможно, сложность современных дров превзошла возможности этого подхода, конечно. А может, и нет. Мы ведь не попробовали. VESA BIOS Ext (VBE) были поскрёбышем технологии, и там уже 16-битность сильно «жала в плечах». Пытались преодолеть, но… как-то не договорились, что ли.
Вопрос хороший. Обычно при снятии давления такие вещи начинают медленно «таять».
Это сильно безопаснее обычного баллона, потому что трещину-то оно может своим конским давлением обеспечить, но дальше эта трещина не развивается в большой бабах, а просто начинает медленно уныло свистеть, пока не высвистит всё содержимое.
То есть сгореть чуть ли не сложнее, чем на обычном бензобаке.
Природные стабильны только при совершенно абсурдных давлениях :)
Да я понимаю :) Правда, на бывших органистов они тоже не очень похожи :-D
Кстати, избавиться от песенки в голове не получилось :-D
«Тихий наш японский городок!
Мицубиси здесь клепает танки¹…
Но, увы, распорядился рок,
Что про танки те прознали яяяяяяяяяяяянки!
Два друга — Толстяк и Малыш
Прутся в гости в награду за смеееелооооость…»
¹Вообще-то торпеды и орудия. Но они не рифмуются. И скинет на них второго «друга» уже не «Весёлая Энола», а «Козловагончик» (Bockscar, в честь капитана Bock'a — то есть «Козлова» на наши деньги).
Там вопрос скорее в том, сколько циклов перезарядки оно держит, не отравляясь.
Оно не для «мировых объёмов» — оно для заправки ГБО без создания таких давлений, какие никто не захочет иметь у себя под задницей.
У метана октановое число 110. Может, теплота сгорания так себе, но он позволяет такие сжатия и такие КПД, что он неплох даже сам по себе, не учитывая цену. Если бы он сжижался так же, как пропан, на нём бы давно уже ездили бы так же.
И тут газовые гидраты такие — «подержите моё
пиводу». Если опасные давления требуются только в момент его «зарядки», а дальше он без большой просьбы не отдаёт метан (или отдаёт медленно) — это ровно то, что нужно.Всё, залил на Итч, имя старое. Кроме всего, там быстрый фикс ошибки отрисовки (в той функции, что всё равно под нож, но пусть пока хотя бы не сбивает с толку глюками) и попытка сделать текстуру, хотя бы в одном квадранте, где всё в плюс :)
Там же, кстати, есть и передача угла, под которым мы видим стенку — она ещё пригодится, для косо срезанных тайлов, конечно :)
В общем и целом, удалось столько тактов освободить, что, может, лучше вообще статусбар не делать и полностью заюзать поле? Тогда базовый вариант (смещать финальную сцену на -4..+3) вообще даёт идеальную камеру, полоска в пол-тайла слева-справа экрана пусть будет какая хочет, а скорость получается просто космос, вероятны полные 60 фпс и куча времени на подновление части полигонов :)
Думаю, на фулскрин теперь-то уж у нас такты есть ^__________^ Смотрю сам на результат и тащусь, как питон :-D
UPD: жду вопросов по коду, ну и если удастся .EXE запустить на стареньком четырёхъядернике с XP, чтобы оценить всё великолепие тщательно замаскированных 8 бит и 32 лучей — было бы тоже неплохо :) это прямо мышкой ощутить надо, вживую, чтобы разделить со мной мои визги восторга :-D
Отлично, сча оформлю свои чешуйчатые текстуры, чтобы не плодили неверного понимания, и прицеплю :)
UPD:
У меня было вдохновение, я давно так приятно не кодил.
А когда у меня включается этот режим — nothing stops the Spacemarine.
…и ещё сто раз пригодится…
А рывков-то и нету ^_________^ шестерёнка же делает эквивалентным смещение вдоль экрана и смещение по углу :) Разрешение больше, чем одна колонка тайлов — всё равно не получим, ну разве что делать тайлы-половинки, но это ад. При поле зрения в 90° — 4 раза по 32, то есть 128 лучей у нас всего может в принципе существовать. Остальное — излишество, всё равно не увидим, только таблица распухнет. Ну как бы, если их так и оставить 32, картинка немножко дёрганая (терпимо, но нежелательно), но я ж опосля смещаю на -4..+3 пиксела всю сцену, и получается совершенно идеальная плавность %) Думаю, можно и другими способами выравнивать на доли луча, но я уже смирился с дёрганым статусбаром, в картинку шлема всё обернуть и пускай дёргается на здоровье %) очень уж реакция на мышку приятная, а зрение всё равно занято сценой. Но если очень претит — можно другие варианты сглаживания продумывать. Главное — больше 32 из 128 кастов нам физически не нужно, не увидим. А цена их — очень высока.
Скорость. Просто скорость. Я вот таки допинал оптимизацию до полной однобайтности. Такты нам очень пригодятся — полигоны, геймплей…
А вот так, как я это там и сделал. Меньше шести килобайт готовых произведений любого 8-битного на любой из 32 неповторяющихся (ко)тангенсов, дающих результаты не более 8-битного.
Да, это очень геморно. Поэтому и взялся — паззла же. И на выходе — ещё куча тактов сэкономлена.
В общем, я считаю там тангенсы, умножаю на них величины от о до 255 и как только результат разбух больше байта (по геометрии такого не бывает, значит, не понадобится) — его в таблицу не кладу и перехожу к следующему тангенсу. Получается таблица из 32 указателей (увы, в пекашную память — пока не ассемблировал под 6502, но это тривиальный перевод), которые указывают на стартовый нолик (очередной тангенс из 32 уникальных, умноженный на 0).
Достаточно взять указатель на тангенс, соответствующий лучу (я их, конечно, тоже в шестерёнку положил, чтобы быстро извлекать) и по нему взять смещение, равное умножаемому числу (подразумевая, что число < 256 и результат умножения < 256, что всегда так в силу геометрии). И там будет лежать готовое произведение, и всего 6 кб, даже меньше ^__________^
Таким образом, стартовый прыжок к стенке клетки и финишный «недо-шаг», если впилились по смещению — берутся из таблицы одним обращением ^___________^
Ага, я сейчас тоже думаю, как бы выдумать такую таблицу расстояний (точнее, сразу размеров текстуры), в которой на маленьких расстояниях (где важен каждый пиксел) шаг входных значений маленький, а на больших (где даже шаг в целую клетку не всегда меняет размер текстуры хотя бы на пиксел) — большой :)
И при этом чтобы обращение к этой таблице не требовало кучу вычислений позиции старшего бита и вот это вот всё %) а в идеале — её ещё как-то с Factor скрестить, чтобы и лишних операций не было, и стенки не выпучивались %)
Этих операций хоть и всего 32 на кадр, причём независимо от дистанции каста — но очень уж сами операции жирные %)
UPD:
Охохо, он устраивает даже меня! А я — придирчивый фанат шутеров :)
А уж со скошенными углами и освещением… да с полигональными роботами… ммммм ^______^
А камера таки идеально плавная сейчас, ценой статусбара %) Хотя если хватит скорости всё поле залить — можно статусбар вообще выкинуть, кек :)
Поясняю, поелику мне по реакции показалось, что никто ничего не понял :)
Речь не о «госплане на стероидах», а о той парадигме, при помощи которой Вы как раз и решили мне продемонстрировать несостоятельность «госплана на стероидах».
Чем и продемонстрировали состоятельность как раз этой парадигмы. А речь-то я вёл как раз о ней! А уже какие именно методы ей доказываются и опровергаются — это дело десятое и сто десятое.
Ключевые слова: «научным методом показал».
Я как раз про то, что показывать, в далёкой эволюционной перспективе развития человеческого мозга, будут именно научным методом.
А не путём акробатической имхонавтики генеральной линии партии вокруг шеста, что аж змея хребет сломает, как в советском анекдоте. Даже если эта партия вместе с лидером тысячу раз выбрана максимально демократично. Всё равно это всё — субъективное мнение, и нет никакой гарантии, что они не скажут, мол, «мы считаем, что экономические акторы рациональны, и ничего вы с этим не поделаете, потому что сами за нас единогласно голосовали».
А «научным методом показал» — это объективная истина, не субъективная. Нерациональны и точка, хоть обголосуйтесь, заряд электрона этим не изменить.
Вот я про что.
Просто очень мало людей в популяции готовы принять это и поставить выше инстинктов примата. Конкретно в этом треде — ровно двое нас.
UPD: слегка косячит
AddToZBuffer8Bit,
переполнением байта страдает и не всегда верно считает высоту. Но это не имеет значения, всё равно он-то идёт под нож в любом случае — там же прямые вычисления, а это всё табличное тоже должно быть.Его я ещё, можно сказать, не трогал :(
…если только тот тип на фото — сам Конрад Цузе :-D
Мы примерно об одном↑. Разница небольшая:
Вот это «несовершенство» как раз тоже учесть было бы разумно, научно и рационально, а кидаться очертя голову внедрять — типичный для бледнотиков подход.
Оно бы не дошло на пушечный выстрел до «попробовать», если бы
Просто потому, что доказательство работоспособности любой подобной системы представить ширнармассам не удалось бы по причине его отсутствия, по причине отсутствия работоспособности.
Я ведь не про «госплан на стероидах». Я про глобальную парадигму «чьё мнение правильное».
Реверснуть прошивку от «АБ» — это был бы реальный прорыв, потому что люди смогут прикоснуться к раритету, которого почти ни у кого не было.
А учитывая то, что эмуляция ТОЧНАЯ, а не «я художник, я так вижу» — это было бы больше, чем просто прорыв. Это — натуральное сохранение подлинного культурного наследия. Не рэп-ремейков Моцарта, а Моцарта.
Держу пальцы за.
Вот код (чуть урезал карту, ибо простыня)
@Swamp_Dok, «призываю заклинанием» в тред для сдачи этапа работ.
Получилось.
Вот сырец, который можно переводить в ассемблер практически 1:1. И, раз уж я больше недели как знаком с 6502-м — быстрее и лучше, чем я сейчас там сделал, вряд ли вообще в принципе возможно (да, я действительно вот так вот ориентируюсь в лоу-левеле, даже в новой для себя архитектуре. Это пятиминутка гордыни, но не преувеличение).
Скринов не будет — я не пожертвовал вообще ничем от слова «совсем», то есть скрины не изменились.
Пояснения и поддержка — разумеется, будет. Это хоть формально и си для пека, но в душе оно уже давно 6502-й ассемблер, поэтому читабельность (относительно первого примера со «школьным» кодом) просела НЬ МАЛЪ АЖ ОФИГЕТЬ. Всё поясню, на все вопросы отвечу.
Здесь был сам код, но он не влез в каммент вместе с текстом
Всё это можно смело ассемблировать, корректируя конкретные адреса сообразно мапперу и моей поддержке, и не тратить больше силы на 2.5D, а сразу делать свои полигоны. В них я не настолько копенгаген и не шибко помогу, в отличие от вот здесь вот вот это вот.
Синусов там больше нет. Ну то есть они есть — для физики. Для рейкаста всё посчитано заранее и лежит в таблице «колеса».
Тангенсов тоже больше нет. Есть заранее посчитанная таблица умножения на тангенс, для каждого из 32 возможных углов. Котангенсы и всё остальное для остальных 96 углов получаются индексами. Поскольку из геометрии следует, что результат умножения всегда меньше 256 — таблица неполная, то есть как только результат доходит до 256, она обрывается, обращений туда всё равно не будет. В итоге получилось меньше даже, чем одна страничка маппера.
Карта имеет размер 32×256, чтобы её можно было проверить за 5 тактов.
Я имею в виду — ВСЕГО за 5 тактов. Не сшивая две однобайтных переменных в адрес. Не добавляя адрес начала карты. «Птичка уже в скворечнике», когда мы хотим чекнуть ячейку карты — адрес уже сложился в квазирегистровой области сам собой. Мы просто делаем LDA. Переноса между байтами там тоже не будет. Кастрировать карту до 16×16 — не нужно. Скорости это не добавит, а, вероятно, ещё и убавит, потому что переменные придётся собирать в адрес. Размеры помещений, чтобы не кастить слишком далеко, мы просто ограничиваем при рисовании карт (в Wolf3D тоже не было залов на пол-уровня).
Нет, реально. Я свёл весь рейкаст к 8-битной арифметике между переменными, лежащими в 0-й странице, и обращениям к относительно небольшим таблицам. Быстрее КМК даже пытаться не стоит, трата времени.
Самое смешное, если это всё 1:1 совпало с собственными попытками ;) Потому что кто-то тут тоже был на финишной прямой, судя по ответам %)
Теперь о текстурировании. Пока то да сё, я попробую затекстурировать стенки «рыбьей чешуёй», которая по вертикали отмасштабирована согласно размеру столбца, а по горизонтали — столбца × то, под каким углом мы смотрим на текстуру. Вероятно, это позволит зрению лучше ощущать угол, а вариантов текстуры потребуется не особо-то и много. Передать данные про угол из рейкаста будет легко — мы знаем, какой из углов кастим, и знаем, в горизонтальную или вертикальную стенку он воткнулся (это вообще разные вызовы функции обработки конца луча).