~aquaratixc/j1-cpu

629a203f73d923fe8c99213d54c8ae417f0fe24b — Bagomot 6 years ago 65dd9db master
Added cpu code and examples
3 files changed, 290 insertions(+), 3 deletions(-)

M .gitignore
M source/app.d
A source/j1_cpu.d
M .gitignore => .gitignore +2 -0
@@ 4,3 4,5 @@ __dummy.html
*.o
*.obj
__test__*__

*.exe
\ No newline at end of file

M source/app.d => source/app.d +22 -3
@@ 1,6 1,25 @@
import std.stdio;
import j1_cpu;

void main()
{
	writeln("Edit source/app.d to start your project.");
}
	J1_CPU j1 = new J1_CPU;

	uint16[32_768] ram = new uint16[32_768];

	ram[0] = 0x4003;     // call 3     вызвать подпрограмму по адресу 3
	ram[1] = 0x8008;     // push 8     положить 8 на стек
	ram[2] = 0x0000;     // halt       остановить программу
	ram[3] = 0x8001;     // push 1     положить 1 на стек
	ram[4] = 0x8002;     // push 2     положить 2 на стек
	ram[5] = 0x8003;     // push 3     положить 8 на стек
	ram[6] = 0x6146;     // >r         скопировать вершину стека данных в стек возвратов
	ram[7] = 0x6180;     // swap       поменять местами два верхних элемента стека данных
	ram[8] = 0x6b89;     // r>         поместить вершину стека возвратов в стек данных
	ram[9] = 0x7180;     // swap       поменять местами два верхних элемента стека данных

	j1.setMemory(ram);
	j1.executeProgram();

	j1.print;

}
\ No newline at end of file

A source/j1_cpu.d => source/j1_cpu.d +266 -0
@@ 0,0 1,266 @@
import std.stdio;
import std.conv : to;

// 16-разрядное беззнаковое целое
alias int16 = short;
// 16-разрядное беззнаковое целое
alias uint16 = ushort;

class J1_CPU
{
	private
	{
		enum RAM_SIZE = 32_768;
		// стек данных
		uint16[33] dataStack;
		// стек вызовов
		uint16[32] returnStack;
		// память
		uint16[RAM_SIZE] RAM;
		// указатель на вершину стека данных
		int16 dataPointer;
		// указатель на вершину стека вызовов
		int16 returnPointer;
		// счетчик инструкций
		uint16 programCounter;

		// маски для различения типов инструкций j1
		enum J1_INSTRUCTION : uint16
		{
			JMP    =  0x0000,
			JZ     =  0x2000,
			CALL   =  0x4000,
			ALU    =  0x6000,
			LIT    =  0x8000
		};

		// маски для различения аргументов инструкций j1
		enum J1_DATA : uint16
		{
			LITERAL = 0x7fff,
			TARGET  = 0x1fff
		};

		// исполнение команд АЛУ
		auto executeALU(uint16 instruction)
		{
			uint16 q;
			uint16 t;
			uint16 n;
			uint16 r;

			// вершина стека
			if (dataPointer > 0)
			{
				t = dataStack[dataPointer];
			}

			// элемент под вершиной стека
			if (dataPointer > 0)
			{
				n = dataStack[dataPointer - 1];
			}

			// предыдущий адрес возврата
			if (returnPointer > 0)
			{
				r = returnStack[returnPointer - 1];
			}

			// увеличить счетчик инструкций
			programCounter++;

			// извлечение кода операции АЛУ
			uint16 operationCode = (instruction & 0x0f00) >> 8;

			// опознание операций
			switch (operationCode)
			{
				case 0:
					q = t;
					break;
				case 1:
					q = n;
					break;
				case 2:
					q = to!uint16(t + n);
					break;
				case 3:
					q = t & n;
					break;
				case 4:
					q = t | n;
					break;
				case 5:
					q = t ^ n;
					break;
				case 6:
					q = to!uint16(~to!int(t));
					break;
				case 7:
					q = (t == n) ? 1 : 0;
					break;
				case 8:
					q = (to!int16(n) < to!int16(t)) ? 1 : 0; break; case 9: q = n >> t;
					break;
				case 10:
					q = to!uint16(t - 1);
					break;
				case 11:
					q = returnStack[returnPointer];
					break;
				case 12:
					q = RAM[t];
					break;
				case 13:
					q = to!uint16(n << t);
					break;
				case 14:
					q = to!uint16(dataPointer + 1u);
					break;
				case 15:
					q = (n < t) ? 1 : 0; break; default: break; } // код действия с указателем на стек данных // (+1 - увеличить указатель, 0 - не трогать, -1 уменьшить (= 2 в двоичном коде)) uint16 ds = instruction & 0x0003; // код действия с указателем на стек возвратов // (+1 - увеличить указатель, 0 - не трогать, -1 уменьшить (= 2 в двоичном коде)) uint16 rs = (instruction & 0x000c) >> 2;

			// код действия с указателем на стек данных
			// (+1 - увеличить указатель,  0 - не трогать, -1 уменьшить (= 2 в двоичном коде))
			uint16 ds = instruction & 0x0003;
			// код действия с указателем на стек возвратов
			// (+1 - увеличить указатель,  0 - не трогать, -1 уменьшить (= 2 в двоичном коде))
			uint16 rs = (instruction & 0x000c) >> 2;

			switch (ds)
			{
				case 1:
					dataPointer++;
					break;
				case 2:
					dataPointer--;
					break;
				default:
					break;
			}

			switch (rs)
			{
				case 1:
					returnPointer++;
					break;
				case 2:
					returnPointer--;
					break;
				default:
					break;
			}

			// флаг NTI
			if ((instruction & 0x0020) != 0)
			{
				RAM[t] = n;
			}

			// флаг TR
			if ((instruction & 0x0040) != 0)
			{
				returnStack[returnPointer] = t;
			}

			// флаг TR
			if ((instruction & 0x0080) != 0)
			{
				dataStack[dataPointer-1] = t;
			}

			// флаг RPC
			if ((instruction & 0x1000) != 0)
			{
				programCounter = returnStack[returnPointer];
			}

			if (dataPointer >= 0)
			{
				dataStack[dataPointer] = q;
			}
		}
	}

	public
	{
		auto execute(uint16 instruction)
		{
			// опознать тип инструкции
			uint16 instructionType = instruction & 0xe000;
			// операнд над которым осуществляется инструкция
			uint16 operand = instruction & J1_DATA.TARGET;

			// распознать конкретную инструкцию процессора
			switch (instructionType)
			{
				// безусловный переход
				case J1_INSTRUCTION.JMP:
					programCounter = operand;
					break;
				// переход на адрес, если на вершине стека 0
				case J1_INSTRUCTION.JZ:
					if (dataStack[dataPointer] == 0)
					{
						programCounter = operand;
					}
					dataPointer--;
					break;
				// передать управление на адрес
				case J1_INSTRUCTION.CALL:
					returnPointer++;
					returnStack[returnPointer] = to!uint16(programCounter + 1);
					programCounter = operand;
					break;
				// выполнить инструкцию АЛУ
				case J1_INSTRUCTION.ALU:
					executeALU(operand);
					break;
				// положить на стек литерал
				case J1_INSTRUCTION.LIT:
					operand = instruction & J1_DATA.LITERAL;
					dataPointer++;
					dataStack[dataPointer] = operand;
					programCounter++;
					break;
				default:
					break;
			}
		}

		this()
		{
			this.RAM = new uint16[RAM_SIZE];

			this.dataPointer = -1;
			this.returnPointer = -1;
			this.programCounter = 0;
		}

		void print()
		{
			dataStack.writeln;
			//returnStack.writeln;
		}

		void executeProgram()
		{
			while (RAM[programCounter] != 0)
			{
				execute(RAM[programCounter]);
				print;
			}
		}

		void setMemory(uint16[32_768] ram)
		{
			this.RAM = ram;
		}

		auto getMemory()
		{
			return RAM;
		}
	}
}