Обновить
80
0.1

Пользователь

Отправить сообщение

…если только тот тип на фото — сам Конрад Цузе :-D

Но это утопия, конечно. Это что ж за население там должно быть, чтобы мозги в основной массе принимали только доказательный подход, игнорируя базовые инстинкты примата напрочь. Сверхлюди какие-то, следующая ступень эволюции.

Мы примерно об одном↑. Разница небольшая:

всё остальное: люди, соцструктура, методы сбора информации, управление, полиция, армия , сельское хозяйство, погода - в целом всё, непредсказуемо и нерационально.

Вот это «несовершенство» как раз тоже учесть было бы разумно, научно и рационально, а кидаться очертя голову внедрять — типичный для бледнотиков подход.

В Чили уже пробовали - 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 совпало с собственными попытками ;) Потому что кто-то тут тоже был на финишной прямой, судя по ответам %)

Теперь о текстурировании. Пока то да сё, я попробую затекстурировать стенки «рыбьей чешуёй», которая по вертикали отмасштабирована согласно размеру столбца, а по горизонтали — столбца × то, под каким углом мы смотрим на текстуру. Вероятно, это позволит зрению лучше ощущать угол, а вариантов текстуры потребуется не особо-то и много. Передать данные про угол из рейкаста будет легко — мы знаем, какой из углов кастим, и знаем, в горизонтальную или вертикальную стенку он воткнулся (это вообще разные вызовы функции обработки конца луча).

Мне тоже казалось по названию статьи, что они его перманентно грохнут уже на «большой земле» %) а оно вот как %)

Да, весёлый всуппугай %) но он слишком хорошо летает для прототипа %)

Говорят, в поединке интеллекта ворон и кеа завершили бой ничьёй %) все паззлы решили %)

…а вот если всё равно пытаться сделать наперекор невозможности — вот тогда будет как раз антиутопия :-D

Ну, собственно, я вижу в этом камменте уверенное начало процесса :-D

Сам не видел, но народ говорит в каком-то разборе (эх, где мой поиск по камментам, хотя бы моим же) — была в поздних версиях «обратная игра», где Волка нужно обстреливать яйцами %)

Что за птица такая – Ки? Вот она, выдуманная и нарисованная на борту стратегического бомбардировщика B-29.

Мне кажется, прототипом птицы Кии послужила птица киви %) Длинная «и» — «кийи» звучит как «киви с просторечным прононсом» %)

чем имя какой-нибудь девицы или название родного городка

Который день преследует музыкальный триппер:

«Светит одинокая звезда

Предвещая нам судьбу Содома

Где-то там «Весёлая **зда»

Поднялась уже с аэродрооооома

Два друга — Толстяк и Малыш

Едут в гости в награду за смеееелоооооость

«Банзай» превращается в шиш

И враз воевать расхотеееееелось»

Говорят, единственный способ избавиться от музыкального триппера — заразить других. Вот сейчас и попробую :-D

Статья очень длинная – минимум час на прочтение

Надо будет почитать на досуге %) может, даже оффлайн сохранить, ибо %)

…пока вы все тут пытаетесь избавиться от песенки про Весёлую Энолу в голове %)

Страна, которая первой попадёт под управление реальным научным методом — доказательным, безликим, равнодушным как к имхонавтике конкретного лидера, так и к перевесу в голосах, слушающая только строгие доказательства — ей даже мир захватывать не придётся, потому что весь остальной мир даже пискнуть не успеет.

Но это утопия, конечно. Это что ж за население там должно быть, чтобы мозги в основной массе принимали только доказательный подход, игнорируя базовые инстинкты примата напрочь. Сверхлюди какие-то, следующая ступень эволюции.

Не, ну я не отрицаю ценность голосования — оно хорошо там, где мы определяемся с целью. Понять, что нам важнее — тут научный метод бессилен, потому что речь о нашем «хочу» и о том, какие «хочу» нам приоритетны. А вот «как это получить» — тут только наука, только хардкор. Как я уже раньше язвил, «заряд электрона лучше не пытаться вычислить голосованием», иначе устройства сложнее каменного века у нас не заработают по загадочной причине, как же так, все же решили, что заряд электрона равен двум курам, а почему-то не работает даже ламповое радио /s

Этот дед смекнул то же самое, но наука и «косплей учёного, Мэттам на смех» — почему-то не одно и то же /s Не думаю, что ситуация, когда харизматичный признанный лидер говорит «вы слепая толпа, не двум курам, а трём!» — исправит работу хотя бы лампового радио /яд

Два раза мыл от бетонной пыли (капремонт). После второго раза поработало пару месяцев и сдохло.

То ли пыль успела разъесть, то ли вода каждый раз подтачивала переходные отверстия, то ли просто оно устало работать в режиме «варимся в пылевой шубе — стынем с голой платой».

Но на всякий случай — сушка холодным мощным потоком воздуха, который вышибает водяные плёнки из всех капилляров (переходки, пространство под чипами…) У меня просто могло скорости воздушной струи не хватить.

А можно скрин с рендером последней версии? Самому лень все собирать и возиться с ДосБоксом.

Так там же .EXE гото… а, досбокс. Сча.

Вот оно. Основные изменения видны в динамике, причём на нативной DOS-машине, так что толку с того .EXE для 99% читателей… Закраска, правда, лживая (каждая колонка — индивидуально).
Вот оно. Основные изменения видны в динамике, причём на нативной DOS-машине, так что толку с того .EXE для 99% читателей… Закраска, правда, лживая (каждая колонка — индивидуально).

Нет, я использовал целые байты. Половинки байт не использую из-за медленного считывания, да и хватает мне оперативки пока.

Ну то есть мы в итоге написали идентичный код :) Хороший признак, если двоим мерещится — значит, не мерещится :)

Работа с массивами через переменные-индексы очень плохо работает.

Loop unrolling — наше всё, согласен. Особенно это касается скалеров, т. е. в данном случае — заполнения буфера правильными номерами тайлов :)

Для другого его использовать особо смысла нет. Скорость чтения одинаковая.

Это да, я даже удивился. Стек обычно самый быстрый (ничего вычислять не надо, все адреса знаем, а пока оперативка реагирует — «в фоне» делаем инкремент или декремент указателя). Видимо, транзисторов не хватило на услуги-люкс :)

Мой маппер переключает первые 16 килобайт ROM.

ОК, учту при выборе структуры таблиц, чтобы поменьше щёлкать страницами приходилось :) Очень уж жирной обещает быть таблица тангенсов…

В итоге я решил вернуть к более стандартному подходу. Координаты теперь будут 16-битные (8 бит на целую часть и 8 бит на дробную часть (положение внутри клетки). Ты к этому же параллельно пришел :)

Я бы даже сказал — получилось настолько одно и то же, что из моего кода можно уже добрую половину честно списать :) оно вполне себе работает, отлажено, прокомментировано и даже приведено в относительно 6502-образный вид :) Или если даже не списать, то, как минимум, сверяться с ним на предмет «о, хорошая идея, сделаю так же, о, не очень хорошая, тут я сделаю скорее уж вот так» :)

Там просто очень большой головняк был с отладкой этого вот «тут переносы бывают, а тут — не бывает», и я его уже прошёл и достиг результата, так что имеет смысл мой код хотя бы как референс держать перед собой, чтобы снова не спотыкаться на этих переносах :) это не тот путь, который хочется проходить самостоятельно :-D переносы были задорные %)

Луч теперь будет стрелять через нахождение первого пересечения с соседней клеткой (это всего 1-2 умножения и пара сложений), а следующие пересечения находятся обычным сложением.

То есть это ровно то, что я описал в той большой статье. До первого пересечения — это BrezToStart, дальше — только суммы. Всё это как раз я сейчас запостил в рабочем и отлаженном виде, разбитом по 8 бит ^_____^ Можно брать и юзать :)

Длину луча буду считать через таблицу косинусов и синусов (это еще одно умножение).

А вот её у меня пока нет. Функцию AddToZBuffer, в которой должно идти обращение к этой таблице (вместо корня Кармака), я пока не довёл до нужной степени 8-битного просветления :)

Немного не нравится, что приходится использовать тангенсы

Я им уже приставил к глотке бензопилу, но ещё не газанул. Практически после публикации сразу дошло, как сократить размер этих таблиц.

Не, ну те тангенсы, которые нужны для смещений — они уже заранее вычислены, уложены в 8 бит и лежат в «колесе», тут оно ооооочень экономит расчёты, что я первым же абзацем отметил (см. мой код). Но есть ещё один неприятный тангенс — «меньшее на большее» и один совсем мерзкий — «большее на меньшее». Нужны для вычислений этих самых «нахождение первого пересечения с соседней клеткой» и второго такого же, в конце каста. Те самые «1-2 умножения».

У нас всего 128 лучей в «колесе», ибо 32 колонки в 4 возможных стороны взгляда. Но 128×256 — слишком жирная таблица умножения. Сейчас у меня план такой:

1) Чуть подрихтовать конкретные направления лучей в «колесе», чтобы в результате отражения они совпадали хотя бы до третьего знака. Это уменьшит таблицу в 4 раза — вместо 128 тангенсов будет только 32, просто надо будет выбирать, в зависимости от квадранта, от начала таблицы или от конца смотреть (а это очень легко, учитывая, что у меня в коде 8 кейсов для разных случаев, т. е. каждый код работает только с одним случаем и обращение к таблице умножения на тангенс там будет только одно — именно для этого случая).

2) Учесть то, что в результате любого умножения, даже на дробь «большое на малое», всегда получается менее 256 (просто из геометрии следует). То есть можно хранить сугубо треугольную таблицу, где аргумент А меняется от 0 до 31 (номер луча из «колеса» с точностью до отражений), а аргумент Б — от 1 до плавающей величины, и как только результат достиг 256 и более — начинается следующая величина А, всё равно обращений к этим ячейкам не будет никогда, Б не достигает этих значений.

В итоге таблица «N умножить на тангенс» должна усохнуть примерно до 32×128, с чем уже можно будет жить :)

Без дробей все-таки обойтись у меня не получилось

Вот если я эту треугольную таблицу сделаю — прощайте, дроби :) В смысле, совсем прощайте %) ни одного умножения и тангенса в процессе каста не будет, только суммы :)

Сейчас доделываю таблицы и вношу правки в свою библиотеку дробей.

А вот библиотеки дробей у меня как раз-таки и нет :) Так что мы всё-таки не совсем «нахлестнулись», но всё равно думаю, что мой код сто́ит хорошенько почитать, прежде чем продолжать — наверняка я где-то глубже прооптимизировал (а где-то, как с дробями — наоборот, ничего ещё не оптимизировал) %)

А я пока попытаюсь добить мерзопакостные тангенсы, которые там как бельмо в глазу среди этого восьмибитного благолепия %) Фактически, у меня уже настолько в моём коде всё разбито на 8-битные переменные, что, как только я этих двух демонов верной пилой располовиню — сишный код будет переводиться в оптимизированный ассемблер 1:1. Да там уже по именам переменных и констант, думаю, всё должно быть видно насчёт этого %)

А, вот ещё забыл: сырец и собранная версия — в файле аппендикса. Остальное качать не обязательно — весь проект не выкладывал, он в общем повторяет структуру предыдущих.

Значит, баунтисорц не должен слишком светить, один чел добавил фичу или Почтенная Уважаемая Большая Организация :-D

Ей отдать можно хоть 20 килобаксов, она же Почтенная, корона при этом с корпораста не падает :-D

Вообще я не удивлён абсолютно приматным принципам принятия решений у корпорастов — мозг там не участвует, всё решает размер Самцового Пятна Вокруг Хвоста или что там у них статус обозначает, у павианов этих.

Это абсолютно нормально для структур, в которых для восхождения по лестнице важен не столько мозг, решающий всё более и более сложные управленческие задачи (а руководить хотя бы 10 людьми — та ещё пазла), сколько умение орудовать локтями и показывать остальным приматам, что имеешь это пятно ничуть не меньше, чем у них!

Собственно, это в целом и обуславливает принятие огромными корпорациями феноменально идиотских решений — их внутри, в глубине этой шоблы выделил из организма какой-то идиот с очень маленьким мозгом и очень большим пятном вокруг хвоста :-D

И мы ещё удивляемся, что с такими животными не хотят инопланетяне на контакт идти. Да с идиотизма этого вот мышления бледнотиков даже я под столом валяюсь, хотя вроде бы сам биологически того…

…а там какая версия — обычная или с (полу)легендарной «игрой АБ»?

…когда ей же и огрел — это «вигилант» называется :)

Информация

В рейтинге
3 654-й
Зарегистрирован
Активность