add stat of basic dissassemble
This commit is contained in:
		
							parent
							
								
									a35c316800
								
							
						
					
					
						commit
						e09f663b9b
					
				| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
[analysis.config.StaticAnalysisConfig]
 | 
			
		||||
 | 
			
		||||
undocumented_declaration_check="disabled"
 | 
			
		||||
long_line_check="disabled"
 | 
			
		||||
							
								
								
									
										598
									
								
								source/app.d
								
								
								
								
							
							
						
						
									
										598
									
								
								source/app.d
								
								
								
								
							| 
						 | 
				
			
			@ -1,554 +1,11 @@
 | 
			
		|||
import std.stdio;
 | 
			
		||||
import derelict.sdl2.sdl;
 | 
			
		||||
import derelict.imgui.imgui;
 | 
			
		||||
import glad.gl.all;
 | 
			
		||||
 | 
			
		||||
import gl : PixelBuffer;
 | 
			
		||||
import window;
 | 
			
		||||
import imgui;
 | 
			
		||||
 | 
			
		||||
bool checkShaderError(GLuint shader, GLuint flag, bool is_program, in char[] shader_path) nothrow {
 | 
			
		||||
 | 
			
		||||
	GLint result;
 | 
			
		||||
 | 
			
		||||
	(is_program) ? glGetProgramiv(shader, flag, &result)
 | 
			
		||||
		: glGetShaderiv(shader, flag, &result);
 | 
			
		||||
 | 
			
		||||
	if (result == GL_FALSE) {
 | 
			
		||||
 | 
			
		||||
		GLchar[256] log; //FIXME this is potentially fatal
 | 
			
		||||
		(is_program) ? glGetProgramInfoLog(shader, log.sizeof, null, log.ptr)
 | 
			
		||||
			: glGetShaderInfoLog(shader, log.sizeof, null, log.ptr);
 | 
			
		||||
 | 
			
		||||
		printf("[OpenGL] Error in %s: %s\n", shader_path.ptr, log.ptr);
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
 | 
			
		||||
} // checkShaderError
 | 
			
		||||
 | 
			
		||||
struct PixelBuffer {
 | 
			
		||||
 | 
			
		||||
	struct Shader {
 | 
			
		||||
 | 
			
		||||
		private {
 | 
			
		||||
			GLuint shader_prog;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@disable this(this);
 | 
			
		||||
 | 
			
		||||
		void compile(const (GLchar*)* vs_source, const (GLchar*)* fs_source) {
 | 
			
		||||
			
 | 
			
		||||
			import core.stdc.stdlib : exit;
 | 
			
		||||
 | 
			
		||||
			GLuint new_vs_shader = glCreateShader(GL_VERTEX_SHADER);
 | 
			
		||||
			glShaderSource(new_vs_shader, 1, vs_source, null);
 | 
			
		||||
			glCompileShader(new_vs_shader);
 | 
			
		||||
			if (!checkShaderError(new_vs_shader, GL_COMPILE_STATUS, false, "vertex_shader")) {
 | 
			
		||||
				exit(-1);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			GLuint new_fs_shader = glCreateShader(GL_FRAGMENT_SHADER);
 | 
			
		||||
			glShaderSource(new_fs_shader, 1, fs_source, null);
 | 
			
		||||
			glCompileShader(new_fs_shader);
 | 
			
		||||
			if (!checkShaderError(new_fs_shader, GL_COMPILE_STATUS, false, "fragment_shader")) {
 | 
			
		||||
				exit(-1);
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			GLuint new_shader = glCreateProgram();
 | 
			
		||||
			glAttachShader(new_shader, new_vs_shader);
 | 
			
		||||
			glAttachShader(new_shader, new_fs_shader);
 | 
			
		||||
 | 
			
		||||
			// glBindAttribLocation(new_shader, 0, "screen_size");
 | 
			
		||||
 | 
			
		||||
			glLinkProgram(new_shader);
 | 
			
		||||
			if (!checkShaderError(new_shader, GL_LINK_STATUS, true, "shader_program")) {
 | 
			
		||||
				exit(-1);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			glValidateProgram(new_shader);
 | 
			
		||||
			if (!checkShaderError(new_shader, GL_VALIDATE_STATUS, true, "shader_program")) {
 | 
			
		||||
				exit(-1);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			shader_prog = new_shader;
 | 
			
		||||
			
 | 
			
		||||
		} // compile
 | 
			
		||||
 | 
			
		||||
		void setUniforms(int w, int h) {
 | 
			
		||||
			auto attr_loc = glGetUniformLocation(shader_prog, "screen_size");
 | 
			
		||||
			glUniform2f(attr_loc, cast(float)w, cast(float)h);
 | 
			
		||||
		} // setUniforms
 | 
			
		||||
 | 
			
		||||
		void bind() {
 | 
			
		||||
			glUseProgram(shader_prog);
 | 
			
		||||
		} // bind
 | 
			
		||||
 | 
			
		||||
		void unbind() {
 | 
			
		||||
			glUseProgram(0);
 | 
			
		||||
		} // unbind
 | 
			
		||||
 | 
			
		||||
	} // Shader
 | 
			
		||||
 | 
			
		||||
	struct Texture {
 | 
			
		||||
 | 
			
		||||
		import derelict.sdl2.sdl;
 | 
			
		||||
		import derelict.sdl2.image;
 | 
			
		||||
 | 
			
		||||
		private {
 | 
			
		||||
 | 
			
		||||
			GLuint texture_; //OpenGL handle for texture
 | 
			
		||||
			GLenum input_format_, output_format_, data_type_;
 | 
			
		||||
			int width_, height_;
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@property @nogc nothrow {
 | 
			
		||||
 | 
			
		||||
			int width() const { return width_; }
 | 
			
		||||
			int height() const { return height_; }
 | 
			
		||||
			GLuint handle() { return texture_; }
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@disable this(this);
 | 
			
		||||
 | 
			
		||||
		nothrow @nogc
 | 
			
		||||
		this(int width, int height, GLenum input_format, GLenum output_format, GLenum unpack_alignment) {
 | 
			
		||||
 | 
			
		||||
			width_ = width;
 | 
			
		||||
			height_ = height;
 | 
			
		||||
			input_format_ = input_format;
 | 
			
		||||
			output_format_ = output_format;
 | 
			
		||||
			data_type_ = GL_UNSIGNED_BYTE;
 | 
			
		||||
 | 
			
		||||
			glGenTextures(1, &texture_);
 | 
			
		||||
			glBindTexture(GL_TEXTURE_2D, texture_);
 | 
			
		||||
 | 
			
		||||
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 | 
			
		||||
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 | 
			
		||||
 | 
			
		||||
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 | 
			
		||||
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 | 
			
		||||
 | 
			
		||||
			glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment);
 | 
			
		||||
			glTexImage2D(GL_TEXTURE_2D, 0, input_format_, width_, height_, 0, output_format_, GL_UNSIGNED_BYTE, cast(void*)0);
 | 
			
		||||
 | 
			
		||||
			glBindTexture(GL_TEXTURE_2D, 0);
 | 
			
		||||
 | 
			
		||||
		} //this
 | 
			
		||||
 | 
			
		||||
		nothrow @nogc
 | 
			
		||||
		void create(void* pixels, int width, int height, GLenum input_format = GL_RGB, GLenum output_format = GL_RGB, GLenum data_type = GL_UNSIGNED_BYTE) {
 | 
			
		||||
 | 
			
		||||
			width_ = width;
 | 
			
		||||
			height_ = height;
 | 
			
		||||
			input_format_ = input_format;
 | 
			
		||||
			output_format_ = output_format;
 | 
			
		||||
			data_type_ = data_type;
 | 
			
		||||
 | 
			
		||||
			//generate single texture, put handle in texture
 | 
			
		||||
			glGenTextures(1, &texture_);
 | 
			
		||||
 | 
			
		||||
			//normal 2d texture, bind to our texture handle
 | 
			
		||||
			glBindTexture(GL_TEXTURE_2D, texture_);
 | 
			
		||||
 | 
			
		||||
			//set texture parameters in currently bound texture, controls texture wrapping (or GL_CLAMP?)
 | 
			
		||||
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 | 
			
		||||
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 | 
			
		||||
 | 
			
		||||
			//linearly interpolate between pixels, MIN if texture is too small for drawing area, MAG if drawing area is smaller than texture
 | 
			
		||||
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 | 
			
		||||
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 | 
			
		||||
 | 
			
		||||
			//texture type, level, format to store as, width, height, border, format loaded in
 | 
			
		||||
			glTexImage2D(GL_TEXTURE_2D, 0, input_format_, width_, height_, 0, output_format_, data_type_, pixels);
 | 
			
		||||
 | 
			
		||||
			//UNBIND
 | 
			
		||||
			glBindTexture(GL_TEXTURE_2D, 0);
 | 
			
		||||
 | 
			
		||||
		} // this
 | 
			
		||||
 | 
			
		||||
		~this() nothrow @nogc {
 | 
			
		||||
 | 
			
		||||
			if (texture_ != 0) {
 | 
			
		||||
				glDeleteTextures(1, &texture_);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} // ~this
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		* Binds the texture handle, takes an argument for which texture unit to use.
 | 
			
		||||
		*/
 | 
			
		||||
		nothrow @nogc
 | 
			
		||||
		void bind(int unit) {
 | 
			
		||||
 | 
			
		||||
			assert(unit >= 0 && unit <= 31);
 | 
			
		||||
			glActiveTexture(GL_TEXTURE0 + unit); //since this is sequential, this works
 | 
			
		||||
			glBindTexture(GL_TEXTURE_2D, texture_);
 | 
			
		||||
 | 
			
		||||
		} // bind
 | 
			
		||||
 | 
			
		||||
		nothrow @nogc
 | 
			
		||||
		void unbind() {
 | 
			
		||||
 | 
			
		||||
			glBindTexture(GL_TEXTURE_2D, 0);
 | 
			
		||||
 | 
			
		||||
		} // unbind
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		* Updates the texture in place given the new texture buffer.
 | 
			
		||||
		* Takes an optional offset to update only a part of the texture.
 | 
			
		||||
		**/
 | 
			
		||||
		nothrow @nogc
 | 
			
		||||
		void update(void[] pixels, size_t offset = 0) {
 | 
			
		||||
 | 
			
		||||
			bind(0);
 | 
			
		||||
			glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, input_format_, data_type_, pixels.ptr);
 | 
			
		||||
			unbind();
 | 
			
		||||
 | 
			
		||||
		} // update
 | 
			
		||||
 | 
			
		||||
	} // Texture
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	* Generic VertexArray structure, used to upload data of any given vertex type to the GPU.
 | 
			
		||||
	*/
 | 
			
		||||
	struct VertexArray {
 | 
			
		||||
 | 
			
		||||
		private {
 | 
			
		||||
 | 
			
		||||
			GLuint vao_;
 | 
			
		||||
			GLuint vbo_;
 | 
			
		||||
			GLenum type_; // type of vertex data, GL_TRIANGLES etc
 | 
			
		||||
			uint num_vertices_;
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@disable this(this);
 | 
			
		||||
 | 
			
		||||
		nothrow @nogc
 | 
			
		||||
		void create(in float[2][] vertices, GLenum draw_type = GL_STATIC_DRAW, GLenum primitive = GL_TRIANGLES)  {
 | 
			
		||||
 | 
			
		||||
			this.num_vertices_ = cast(uint)vertices.length;
 | 
			
		||||
			this.type_ = primitive;
 | 
			
		||||
 | 
			
		||||
			glGenVertexArrays(1, &vao_);
 | 
			
		||||
			glBindVertexArray(vao_);
 | 
			
		||||
 | 
			
		||||
			glGenBuffers(1, &vbo_);
 | 
			
		||||
			glBindBuffer(GL_ARRAY_BUFFER, vbo_);
 | 
			
		||||
			glBufferData(GL_ARRAY_BUFFER, vertices.length * vertices[0].sizeof, vertices.ptr, draw_type);
 | 
			
		||||
 | 
			
		||||
			// pos
 | 
			
		||||
			glEnableVertexAttribArray(0);
 | 
			
		||||
			glVertexAttribPointer(0,
 | 
			
		||||
				2,
 | 
			
		||||
				GL_FLOAT,
 | 
			
		||||
				GL_FALSE,
 | 
			
		||||
				float.sizeof * 2,
 | 
			
		||||
				null
 | 
			
		||||
			);
 | 
			
		||||
 | 
			
		||||
			glBindVertexArray(0);
 | 
			
		||||
 | 
			
		||||
		} // this
 | 
			
		||||
 | 
			
		||||
		~this() nothrow @nogc {
 | 
			
		||||
			if (vao_ != 0) {
 | 
			
		||||
				glDeleteVertexArrays(1, &vao_);
 | 
			
		||||
			}
 | 
			
		||||
		} // ~this
 | 
			
		||||
 | 
			
		||||
		void bind() nothrow @nogc {
 | 
			
		||||
			glBindVertexArray(vao_);
 | 
			
		||||
		} // bind
 | 
			
		||||
 | 
			
		||||
		void draw() nothrow @nogc {
 | 
			
		||||
			glDrawArrays(type_, 0, num_vertices_);
 | 
			
		||||
		} // draw
 | 
			
		||||
 | 
			
		||||
		void unbind() nothrow @nogc {
 | 
			
		||||
			glBindVertexArray(0);
 | 
			
		||||
		} // unbind
 | 
			
		||||
 | 
			
		||||
	} // VertexArray
 | 
			
		||||
 | 
			
		||||
	const char* vs_shader = q{
 | 
			
		||||
		#version 330 core
 | 
			
		||||
 | 
			
		||||
		uniform vec2 screen_size;
 | 
			
		||||
 | 
			
		||||
		layout(location = 0) in vec2 pos;
 | 
			
		||||
 | 
			
		||||
		out vec2 frag_uv;
 | 
			
		||||
 | 
			
		||||
		void main() {
 | 
			
		||||
			gl_Position = vec4(pos, 0.0, 1.0);
 | 
			
		||||
			frag_uv = clamp(pos, vec2(0.0, 0.0), vec2(1.0, 1.0));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const char* fs_shader = q{
 | 
			
		||||
		#version 330 core
 | 
			
		||||
 | 
			
		||||
		uniform sampler2D tex;
 | 
			
		||||
 | 
			
		||||
		in vec2 frag_uv;
 | 
			
		||||
 | 
			
		||||
		out vec4 out_col;
 | 
			
		||||
 | 
			
		||||
		void main() {
 | 
			
		||||
			float v = texture(tex, frag_uv.st).r * 255;
 | 
			
		||||
			out_col = vec4(v, v, v, 1.0);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Shader shader;
 | 
			
		||||
	VertexArray vao;
 | 
			
		||||
	Texture tex;
 | 
			
		||||
 | 
			
		||||
	void create(void* pixels, int w, int h) {
 | 
			
		||||
 | 
			
		||||
		float[2][6] rect = [
 | 
			
		||||
			[-1.0f, -1.0f], // top left
 | 
			
		||||
			[1.0f, -1.0f], // top right
 | 
			
		||||
			[1.0f, 1.0f], // bottom right
 | 
			
		||||
 | 
			
		||||
			[-1.0f, -1.0f], // top left
 | 
			
		||||
			[-1.0f, 1.0f], // bottom left
 | 
			
		||||
			[1.0f, 1.0f], // bottom right
 | 
			
		||||
		];
 | 
			
		||||
 | 
			
		||||
		vao.create(rect[]);
 | 
			
		||||
		tex.create(pixels, w, h, GL_RED, GL_RED);
 | 
			
		||||
		shader.compile(&vs_shader, &fs_shader);
 | 
			
		||||
 | 
			
		||||
	} // create
 | 
			
		||||
 | 
			
		||||
	void draw(int screen_w, int screen_h) {
 | 
			
		||||
 | 
			
		||||
		vao.bind();
 | 
			
		||||
		shader.bind();
 | 
			
		||||
		shader.setUniforms(screen_w, screen_h);
 | 
			
		||||
		tex.bind(0);
 | 
			
		||||
		vao.draw();
 | 
			
		||||
		
 | 
			
		||||
	} // draw
 | 
			
		||||
 | 
			
		||||
	nothrow @nogc
 | 
			
		||||
	void update(void[] pixels, size_t offset = 0) {
 | 
			
		||||
		tex.update(pixels, offset);
 | 
			
		||||
	} // update
 | 
			
		||||
 | 
			
		||||
} // PixelBuffer
 | 
			
		||||
 | 
			
		||||
struct Assembler {
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	Instruction Glossary:
 | 
			
		||||
		0x0nnn - SYS addr
 | 
			
		||||
		0x00E0 - CLS
 | 
			
		||||
		0x00EE - RET
 | 
			
		||||
		0x1nnn - JP addr
 | 
			
		||||
		0x2nnn - CALL addr
 | 
			
		||||
		0x3xkk - SE Vx, byte
 | 
			
		||||
		0x4xkk - SNE Vx, byte
 | 
			
		||||
		0x5xy0 - SE Vx, Vy
 | 
			
		||||
		0x6xkk - LD Vx, byte
 | 
			
		||||
		0x7xkk - ADD Vx, byte
 | 
			
		||||
		0x8xy0 - LD Vx, Vy
 | 
			
		||||
		0x8xy1 - OR Vx, Vy
 | 
			
		||||
		0x8xy2 - AND Vx, Vy
 | 
			
		||||
		0x8xy3 - XOR Vx, Vy
 | 
			
		||||
		0x8xy4 - ADD Vx, Vy
 | 
			
		||||
		0x8xy5 - SUB Vx, Vy
 | 
			
		||||
		0x8xy6 - SHR Vx {, Vy}
 | 
			
		||||
		0x8xy7 - SUBN Vx, Vy
 | 
			
		||||
		0x8xyE - SHL Vx {, Vy}
 | 
			
		||||
		0x9xy0 - SNE Vx, Vy
 | 
			
		||||
		0xAnnn - LD I, addr
 | 
			
		||||
		0xBnnn - JP V0, addr
 | 
			
		||||
		0xCxkk - RND Vx, byte
 | 
			
		||||
		0xDxyn - DRW Vx, Vy, nibble
 | 
			
		||||
		0xEx9E - SKP Vx
 | 
			
		||||
		0xExA1 - SKNP Vx
 | 
			
		||||
		0xFx07 - LD Vx, DT
 | 
			
		||||
		0xFx0A - LD Vx, K
 | 
			
		||||
		0xFx15 - LD DT, Vx
 | 
			
		||||
		0xFx18 - LD ST, Vx
 | 
			
		||||
		0xFx1E - ADD I, Vx
 | 
			
		||||
		0xFx29 - LD F, Vx
 | 
			
		||||
		0xFx33 - LD B, Vx
 | 
			
		||||
		0xFx55 - LD [I], Vx
 | 
			
		||||
		0xFx65 - LD Vx, [I]
 | 
			
		||||
	*/
 | 
			
		||||
 | 
			
		||||
	enum OpCode {
 | 
			
		||||
 | 
			
		||||
		CLS, // CLS
 | 
			
		||||
		RET, // RET
 | 
			
		||||
		CALL, // CALL addr
 | 
			
		||||
 | 
			
		||||
		ADD, // ADD Vx, Vy
 | 
			
		||||
			 // ADD Vx, byte
 | 
			
		||||
			 // ADD I, Vx
 | 
			
		||||
 | 
			
		||||
		SUB, // SUB Vx, Vy
 | 
			
		||||
			 // SUB Vx, byte
 | 
			
		||||
 | 
			
		||||
		SUBN, // SUBN Vx, Vy
 | 
			
		||||
 | 
			
		||||
		SE, // SE Vx, Vy
 | 
			
		||||
			// SE Vx, byte
 | 
			
		||||
 | 
			
		||||
		SNE, // SNE Vx, byte
 | 
			
		||||
		SKP, // SKP Vx
 | 
			
		||||
		SKNP, // SKNP Vx
 | 
			
		||||
 | 
			
		||||
		SHL, // SHL Vx {, Vy}
 | 
			
		||||
		SHR, // SHR Vx {, Vy}
 | 
			
		||||
		XOR, // XOR Vx, Vy
 | 
			
		||||
		AND, // AND Vx, Vy
 | 
			
		||||
		OR, // OR Vx, Vy
 | 
			
		||||
 | 
			
		||||
		LD, // LD Vx, DT
 | 
			
		||||
			// LD DT, Vx
 | 
			
		||||
			// LD ST, Vx
 | 
			
		||||
			// LD Vx, K
 | 
			
		||||
			// LD [I], Vx
 | 
			
		||||
			// LD Vx, [I]
 | 
			
		||||
		
 | 
			
		||||
		RND // RND Vx, byte
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	enum Argument {
 | 
			
		||||
 | 
			
		||||
		Vx,
 | 
			
		||||
		Vy,
 | 
			
		||||
 | 
			
		||||
		DT,
 | 
			
		||||
		ST,
 | 
			
		||||
		K,
 | 
			
		||||
		
 | 
			
		||||
		addr,
 | 
			
		||||
		val
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	struct Instruction {
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	import std.array;
 | 
			
		||||
 | 
			
		||||
	Appender!string dissassembly_data;
 | 
			
		||||
 | 
			
		||||
	void assemble(const char* input) {
 | 
			
		||||
 | 
			
		||||
	} // assemble
 | 
			
		||||
 | 
			
		||||
	void assemble(const char[] input) {
 | 
			
		||||
 | 
			
		||||
	} // assemble
 | 
			
		||||
 | 
			
		||||
	const (char)[] disassemble(ubyte[] instructions) {
 | 
			
		||||
 | 
			
		||||
		import std.range : chunks;
 | 
			
		||||
 | 
			
		||||
		foreach (ref ubyte[] i; instructions.chunks(2)) {
 | 
			
		||||
 | 
			
		||||
			ushort opcode = (*(cast(ushort*)(i.ptr)));
 | 
			
		||||
 | 
			
		||||
			switch (opcode & 0xF000) {
 | 
			
		||||
				case 0x0000:
 | 
			
		||||
					switch (opcode & 0x0FFF) {
 | 
			
		||||
						case 0x00E0: // 0x00E0 Clears the screen.
 | 
			
		||||
 | 
			
		||||
						case 0x00EE: // 0x00EE Returns from a subroutine.
 | 
			
		||||
						default: // 0x0NNN Calls RCA 1802 program at address NNN. Not necessary for most ROMs.
 | 
			
		||||
							//assert(0, "0x0NNN RCA 1802 program opcode not implemented!");
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				case 0x1000: // 0x1NNN Jumps to address NNN.
 | 
			
		||||
				case 0x2000: // 0x2NNN Calls subroutine at NNN.
 | 
			
		||||
				case 0x3000: // 0x3XNN Skips the next instruction if VX equals NN.
 | 
			
		||||
				case 0x4000: // 0x4XNN Skips the next instruction if VX doesn't equal NN.
 | 
			
		||||
				case 0x5000: // 0x5XYO Skips the next instruction if VX equals VY.
 | 
			
		||||
				case 0x6000: // 0x6XNN Sets VX to NN.
 | 
			
		||||
				case 0x7000: // 0x7XNN Adds NN to VX.
 | 
			
		||||
				case 0x8000:
 | 
			
		||||
					switch (opcode) {
 | 
			
		||||
						case 0x0000: // 0x8XY0 Sets VX to the value of VY.
 | 
			
		||||
						case 0x0001: // 0x8XY1 Sets VX to VX or VY.
 | 
			
		||||
						case 0x0002: // 0x8XY2	Sets VX to VX and VY.
 | 
			
		||||
						case 0x0003: // 0x8XY3 Sets VX to VX xor VY.
 | 
			
		||||
						case 0x0004: // 0x8XY4 Adds VY to VX. VF is set to 1 when there's a carry, and to 0 when there isn't.
 | 
			
		||||
						case 0x0005: // 0x8XY5 VY is subtracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
 | 
			
		||||
						case 0x0006: // 0x8XY6 Shifts VX right by one. VF is set to the value of the least significant bit of VX before the shift.
 | 
			
		||||
						case 0x0007: // 0x8XY7 Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
 | 
			
		||||
						case 0x000E: // 0x8XYE Shifts VX left by one. VF is set to the value of the most significant bit of VX before the shift.
 | 
			
		||||
						default: // unhandled for some reason
 | 
			
		||||
							writefln("unknown opcode: 0x%x", opcode);
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				case 0x9000: // 0x9XYO Skips the next instruction if VX doesn't equal VY.
 | 
			
		||||
				case 0xA000: // 0xANNN  Sets I to the address NNN.
 | 
			
		||||
				case 0xB000: // 0xBNNN Jumps to the address NNN plus V0.
 | 
			
		||||
				case 0xC000: // 0xCXNN Sets VX to the result of a bitwise and operation on a random number and NN.
 | 
			
		||||
 | 
			
		||||
				// 0xDXYN
 | 
			
		||||
				// Sprites stored in memory at location in index register (I), 8bits wide.
 | 
			
		||||
				// Wraps around the screen. If when drawn, clears a pixel, register VF is set to 1 otherwise it is zero.
 | 
			
		||||
				// All drawing is XOR drawing (i.e. it toggles the screen pixels).
 | 
			
		||||
				// Sprites are drawn starting at position VX, VY. N is the number of 8bit rows that need to be drawn.
 | 
			
		||||
				// If N is greater than 1, second line continues at position VX, VY+1, and so on.
 | 
			
		||||
				case 0xD000:
 | 
			
		||||
				case 0xE000:
 | 
			
		||||
					switch (opcode & 0x000F) {
 | 
			
		||||
						case 0x000E: // 0xEX9E Skips the next instruction if the key stored in VX is pressed.
 | 
			
		||||
						case 0x0001: // 0xEXA1 Skips the next instruction if the key stored in VX isn't pressed.
 | 
			
		||||
						default: // unhandled for some reason
 | 
			
		||||
							writefln("unknown opcode: 0x%x", opcode);
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				case 0xF000:
 | 
			
		||||
					switch (opcode & 0x00FF) {
 | 
			
		||||
						case 0x0007: // 0xFX07 Sets VX to the value of the delay timer.
 | 
			
		||||
						case 0x000A: // 0xFX0A A key press is awaited, and then stored in VX.
 | 
			
		||||
						case 0x0015: // 0xFX15 Sets the delay timer to VX.
 | 
			
		||||
						case 0x0018: // 0xFX18 Sets the sound timer to VX.
 | 
			
		||||
						case 0x001E: // 0xFX1E Adds VX to I.
 | 
			
		||||
						case 0x0029: // 0xFX29 Sets I to the location of the sprite for the character in VX.
 | 
			
		||||
						// 0xFX33 Stores the Binary-coded decimal representation of VX,
 | 
			
		||||
						//  with the most significant of three digits at the address in I,
 | 
			
		||||
						//  the middle digit at I plus 1, and the least significant digit at I plus 2.
 | 
			
		||||
						case 0x0033:
 | 
			
		||||
						case 0x0055: // 0xFX55 Stores V0 to VX in memory starting at address I.
 | 
			
		||||
						case 0x0065: // 0xFX65 Fills V0 to VX with values from memory starting at address I.
 | 
			
		||||
						default: // unhandled for some reason
 | 
			
		||||
							writefln("unknown opcode: 0x%x", opcode);
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				default:
 | 
			
		||||
					writefln("unknown opcode: 0x%x", opcode);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		return "";
 | 
			
		||||
 | 
			
		||||
	} // dissassemble
 | 
			
		||||
 | 
			
		||||
} // Assembler
 | 
			
		||||
 | 
			
		||||
struct Chip8Status {
 | 
			
		||||
 | 
			
		||||
	// emu ptr
 | 
			
		||||
| 
						 | 
				
			
			@ -768,23 +225,23 @@ struct Chip8Status {
 | 
			
		|||
 | 
			
		||||
pure
 | 
			
		||||
@property {
 | 
			
		||||
	ubyte x(ref ushort oc) {
 | 
			
		||||
	ubyte x(in ushort oc) {
 | 
			
		||||
		return (oc & 0x0F00) >> 8;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ubyte y(ref ushort oc) {
 | 
			
		||||
	ubyte y(in ushort oc) {
 | 
			
		||||
		return (oc & 0x00F0) >> 4;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ubyte n(ref ushort oc) {
 | 
			
		||||
	ubyte n(in ushort oc) {
 | 
			
		||||
		return oc & 0x000F;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ubyte nn(ref ushort oc) {
 | 
			
		||||
	ubyte nn(in ushort oc) {
 | 
			
		||||
		return (oc & 0x00FF);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ushort nnn(ref ushort oc) {
 | 
			
		||||
	ushort nnn(in ushort oc) {
 | 
			
		||||
		return (oc & 0x0FFF);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -824,7 +281,6 @@ struct Chip8 {
 | 
			
		|||
	KeyPad kp;
 | 
			
		||||
 | 
			
		||||
	ubyte[64*32] screen_buf;
 | 
			
		||||
	ubyte[3][64*32] screen_data;
 | 
			
		||||
 | 
			
		||||
	bool run_flag;
 | 
			
		||||
	bool draw_flag;
 | 
			
		||||
| 
						 | 
				
			
			@ -850,9 +306,7 @@ struct Chip8 {
 | 
			
		|||
 | 
			
		||||
		run_flag = run_flag.init;
 | 
			
		||||
		draw_flag = draw_flag.init;
 | 
			
		||||
 | 
			
		||||
		screen_buf = screen_buf.init;
 | 
			
		||||
		screen_data = screen_data.init;
 | 
			
		||||
 | 
			
		||||
	} // reset
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -936,8 +390,8 @@ struct Chip8 {
 | 
			
		|||
 | 
			
		||||
			case 0x8000:
 | 
			
		||||
 | 
			
		||||
				ubyte x = cpu.opcode.x;
 | 
			
		||||
				ubyte y = cpu.opcode.y;
 | 
			
		||||
				immutable ubyte x = cpu.opcode.x;
 | 
			
		||||
				immutable ubyte y = cpu.opcode.y;
 | 
			
		||||
 | 
			
		||||
				switch (cpu.opcode.n) {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -958,8 +412,8 @@ struct Chip8 {
 | 
			
		|||
						break;
 | 
			
		||||
 | 
			
		||||
					case 0x0004: // 0x8XY4 Adds VY to VX. VF is set to 1 when there's a carry, and to 0 when there isn't.
 | 
			
		||||
						ubyte vx = cpu.v[x];
 | 
			
		||||
						ubyte vy = cpu.v[y];
 | 
			
		||||
						immutable ubyte vx = cpu.v[x];
 | 
			
		||||
						immutable ubyte vy = cpu.v[y];
 | 
			
		||||
						if (cast(ushort)vx + cast(ushort)vy > 255) {
 | 
			
		||||
							cpu.v[0xF] = 1;
 | 
			
		||||
						} else { 
 | 
			
		||||
| 
						 | 
				
			
			@ -969,8 +423,8 @@ struct Chip8 {
 | 
			
		|||
						break;
 | 
			
		||||
 | 
			
		||||
					case 0x0005: // 0x8XY5 VY is subtracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
 | 
			
		||||
						ubyte vx = cpu.v[x];
 | 
			
		||||
						ubyte vy = cpu.v[y];
 | 
			
		||||
						immutable ubyte vx = cpu.v[x];
 | 
			
		||||
						immutable ubyte vy = cpu.v[y];
 | 
			
		||||
						if (vx > vy) {
 | 
			
		||||
							cpu.v[0xF] = 1;
 | 
			
		||||
						} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -981,14 +435,14 @@ struct Chip8 {
 | 
			
		|||
 | 
			
		||||
					case 0x0006: // 0x8XY6 Shifts VX right by one. VF is set to the value of the least significant bit of VX before the shift.
 | 
			
		||||
 | 
			
		||||
						ubyte vx = cpu.v[x];
 | 
			
		||||
						immutable ubyte vx = cpu.v[x];
 | 
			
		||||
						cpu.v[0xF] = vx & 0x1;
 | 
			
		||||
						cpu.v[x] >>= 1;
 | 
			
		||||
						break;
 | 
			
		||||
 | 
			
		||||
					case 0x0007: // 0x8XY7 Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
 | 
			
		||||
						ubyte vx = cpu.v[x];
 | 
			
		||||
						ubyte vy = cpu.v[y];
 | 
			
		||||
						immutable ubyte vx = cpu.v[x];
 | 
			
		||||
						immutable ubyte vy = cpu.v[y];
 | 
			
		||||
						if (vy > vx) {
 | 
			
		||||
							cpu.v[0xF] = 1;
 | 
			
		||||
						} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -999,7 +453,7 @@ struct Chip8 {
 | 
			
		|||
 | 
			
		||||
					case 0x000E: // 0x8XYE Shifts VX left by one. VF is set to the value of the most significant bit of VX before the shift.
 | 
			
		||||
 | 
			
		||||
						ubyte vx = cpu.v[x];
 | 
			
		||||
						immutable ubyte vx = cpu.v[x];
 | 
			
		||||
						cpu.v[0xF] = vx >> 7;
 | 
			
		||||
						cpu.v[x] <<= 1;
 | 
			
		||||
						break;
 | 
			
		||||
| 
						 | 
				
			
			@ -1043,14 +497,14 @@ struct Chip8 {
 | 
			
		|||
			// If N is greater than 1, second line continues at position VX, VY+1, and so on.
 | 
			
		||||
			case 0xD000:
 | 
			
		||||
 | 
			
		||||
				ProgramCounter spr_addr = cpu.i;
 | 
			
		||||
				ubyte x = cpu.opcode.x;
 | 
			
		||||
				ubyte y = cpu.opcode.y;
 | 
			
		||||
				ubyte n = cpu.opcode.n;
 | 
			
		||||
				immutable ProgramCounter spr_addr = cpu.i;
 | 
			
		||||
				immutable ubyte x = cpu.opcode.x;
 | 
			
		||||
				immutable ubyte y = cpu.opcode.y;
 | 
			
		||||
				immutable ubyte n = cpu.opcode.n;
 | 
			
		||||
 | 
			
		||||
				foreach(int row; 0 .. n) {
 | 
			
		||||
 | 
			
		||||
					ushort pixel = ram[spr_addr + row];
 | 
			
		||||
					immutable ushort pixel = ram[spr_addr + row];
 | 
			
		||||
 | 
			
		||||
					foreach (int col; 0 .. 8) {
 | 
			
		||||
						if ((pixel & (0x80 >> col)) != 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1124,9 +578,9 @@ struct Chip8 {
 | 
			
		|||
 | 
			
		||||
					case 0x0029: // 0xFX29 Sets I to the location of the sprite for the character in VX.
 | 
			
		||||
 | 
			
		||||
						ubyte vx = cpu.v[cpu.opcode.x];
 | 
			
		||||
						// ushort char_addr = 0x200 + (vx * 40); // base of char sprites + value of vx * bits per character
 | 
			
		||||
						ushort char_addr = vx * 0x5;
 | 
			
		||||
						immutable ubyte vx = cpu.v[cpu.opcode.x];
 | 
			
		||||
						immutable ushort char_addr = 0x200 + (vx * 40); // base of char sprites + value of vx * bits per character
 | 
			
		||||
						// immutable ushort char_addr = vx * 0x5;
 | 
			
		||||
						cpu.i = char_addr;
 | 
			
		||||
						break;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1135,7 +589,7 @@ struct Chip8 {
 | 
			
		|||
					//  the middle digit at I plus 1, and the least significant digit at I plus 2.
 | 
			
		||||
					case 0x0033:
 | 
			
		||||
 | 
			
		||||
						ubyte vx = cpu.v[cpu.opcode.x];
 | 
			
		||||
						immutable ubyte vx = cpu.v[cpu.opcode.x];
 | 
			
		||||
						ram[cpu.i] = vx / 100;
 | 
			
		||||
						ram[cpu.i + 1] = (vx / 10) % 10;
 | 
			
		||||
						ram[cpu.i + 2] = (vx % 100) % 10;
 | 
			
		||||
| 
						 | 
				
			
			@ -1320,8 +774,8 @@ struct Emulator {
 | 
			
		|||
		int w, h;
 | 
			
		||||
		window.windowSize(w, h);
 | 
			
		||||
		if (emu.draw_flag || emu.reset_flag) {
 | 
			
		||||
			if (emu.reset_flag) emu.reset_flag = false;
 | 
			
		||||
			buf.update(emu.screen_buf);
 | 
			
		||||
			emu.reset_flag = false;
 | 
			
		||||
			emu.draw_flag = false;
 | 
			
		||||
		}
 | 
			
		||||
		buf.draw(w, h);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,223 @@
 | 
			
		|||
module assembler;
 | 
			
		||||
 | 
			
		||||
import std.stdio : writefln;
 | 
			
		||||
 | 
			
		||||
struct Assembler {
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	Instruction Glossary:
 | 
			
		||||
		0x0nnn - SYS addr
 | 
			
		||||
		0x00E0 - CLS
 | 
			
		||||
		0x00EE - RET
 | 
			
		||||
		0x1nnn - JP addr
 | 
			
		||||
		0x2nnn - CALL addr
 | 
			
		||||
		0x3xkk - SE Vx, byte
 | 
			
		||||
		0x4xkk - SNE Vx, byte
 | 
			
		||||
		0x5xy0 - SE Vx, Vy
 | 
			
		||||
		0x6xkk - LD Vx, byte
 | 
			
		||||
		0x7xkk - ADD Vx, byte
 | 
			
		||||
		0x8xy0 - LD Vx, Vy
 | 
			
		||||
		0x8xy1 - OR Vx, Vy
 | 
			
		||||
		0x8xy2 - AND Vx, Vy
 | 
			
		||||
		0x8xy3 - XOR Vx, Vy
 | 
			
		||||
		0x8xy4 - ADD Vx, Vy
 | 
			
		||||
		0x8xy5 - SUB Vx, Vy
 | 
			
		||||
		0x8xy6 - SHR Vx {, Vy}
 | 
			
		||||
		0x8xy7 - SUBN Vx, Vy
 | 
			
		||||
		0x8xyE - SHL Vx {, Vy}
 | 
			
		||||
		0x9xy0 - SNE Vx, Vy
 | 
			
		||||
		0xAnnn - LD I, addr
 | 
			
		||||
		0xBnnn - JP V0, addr
 | 
			
		||||
		0xCxkk - RND Vx, byte
 | 
			
		||||
		0xDxyn - DRW Vx, Vy, nibble
 | 
			
		||||
		0xEx9E - SKP Vx
 | 
			
		||||
		0xExA1 - SKNP Vx
 | 
			
		||||
		0xFx07 - LD Vx, DT
 | 
			
		||||
		0xFx0A - LD Vx, K
 | 
			
		||||
		0xFx15 - LD DT, Vx
 | 
			
		||||
		0xFx18 - LD ST, Vx
 | 
			
		||||
		0xFx1E - ADD I, Vx
 | 
			
		||||
		0xFx29 - LD F, Vx
 | 
			
		||||
		0xFx33 - LD B, Vx
 | 
			
		||||
		0xFx55 - LD [I], Vx
 | 
			
		||||
		0xFx65 - LD Vx, [I]
 | 
			
		||||
	*/
 | 
			
		||||
 | 
			
		||||
	enum OpCode {
 | 
			
		||||
 | 
			
		||||
		UNK, // UNKNOWN
 | 
			
		||||
 | 
			
		||||
		CLS, // CLS
 | 
			
		||||
		RET, // RET
 | 
			
		||||
		CALL, // CALL addr
 | 
			
		||||
 | 
			
		||||
		ADD, // ADD Vx, Vy
 | 
			
		||||
			 // ADD Vx, byte
 | 
			
		||||
			 // ADD I, Vx
 | 
			
		||||
 | 
			
		||||
		SUB, // SUB Vx, Vy
 | 
			
		||||
			 // SUB Vx, byte
 | 
			
		||||
 | 
			
		||||
		SUBN, // SUBN Vx, Vy
 | 
			
		||||
 | 
			
		||||
		SE, // SE Vx, Vy
 | 
			
		||||
			// SE Vx, byte
 | 
			
		||||
 | 
			
		||||
		SNE, // SNE Vx, byte
 | 
			
		||||
		SKP, // SKP Vx
 | 
			
		||||
		SKNP, // SKNP Vx
 | 
			
		||||
 | 
			
		||||
		SHL, // SHL Vx {, Vy}
 | 
			
		||||
		SHR, // SHR Vx {, Vy}
 | 
			
		||||
		XOR, // XOR Vx, Vy
 | 
			
		||||
		AND, // AND Vx, Vy
 | 
			
		||||
		OR, // OR Vx, Vy
 | 
			
		||||
 | 
			
		||||
		LD, // LD Vx, DT
 | 
			
		||||
			// LD DT, Vx
 | 
			
		||||
			// LD ST, Vx
 | 
			
		||||
			// LD Vx, K
 | 
			
		||||
			// LD [I], Vx
 | 
			
		||||
			// LD Vx, [I]
 | 
			
		||||
		
 | 
			
		||||
		RND // RND Vx, byte
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	enum Argument {
 | 
			
		||||
 | 
			
		||||
        Nil,
 | 
			
		||||
 | 
			
		||||
		Vx,
 | 
			
		||||
		Vy,
 | 
			
		||||
 | 
			
		||||
		DT,
 | 
			
		||||
		ST,
 | 
			
		||||
		K,
 | 
			
		||||
		
 | 
			
		||||
		addr,
 | 
			
		||||
		val
 | 
			
		||||
		
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	struct Instruction {
 | 
			
		||||
        OpCode op;
 | 
			
		||||
        Argument a1;
 | 
			
		||||
        Argument a2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	import std.array;
 | 
			
		||||
 | 
			
		||||
    private {
 | 
			
		||||
 | 
			
		||||
        Appender!(ubyte[]) assembled_data;
 | 
			
		||||
	    Appender!string dissassembled_data;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @disable this(this);
 | 
			
		||||
 | 
			
		||||
	ubyte[] assemble(const char* input) {
 | 
			
		||||
 | 
			
		||||
        return [];
 | 
			
		||||
 | 
			
		||||
	} // assemble
 | 
			
		||||
 | 
			
		||||
	ubyte[] assemble(const char[] input) {
 | 
			
		||||
 | 
			
		||||
        return [];
 | 
			
		||||
 | 
			
		||||
	} // assemble
 | 
			
		||||
 | 
			
		||||
	const (char)[] disassemble(ubyte[] instructions) {
 | 
			
		||||
 | 
			
		||||
		import std.range : chunks;
 | 
			
		||||
 | 
			
		||||
		foreach (ref ubyte[] i; instructions.chunks(2)) {
 | 
			
		||||
 | 
			
		||||
			ushort opcode = (*(cast(ushort*)(i.ptr)));
 | 
			
		||||
 | 
			
		||||
			switch (opcode & 0xF000) {
 | 
			
		||||
				case 0x0000:
 | 
			
		||||
					switch (opcode & 0x0FFF) {
 | 
			
		||||
						case 0x00E0: // 0x00E0 Clears the screen. | CLS
 | 
			
		||||
 | 
			
		||||
						case 0x00EE: // 0x00EE Returns from a subroutine. | RET
 | 
			
		||||
						default: // 0x0NNN Calls RCA 1802 program at address NNN. Not necessary for most ROMs. | SYS addr
 | 
			
		||||
							//assert(0, "0x0NNN RCA 1802 program opcode not implemented!");
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				case 0x1000: // 0x1NNN Jumps to address NNN. | JP addr
 | 
			
		||||
				case 0x2000: // 0x2NNN Calls subroutine at NNN. | CALL addr
 | 
			
		||||
				case 0x3000: // 0x3XNN Skips the next instruction if VX equals NN. | SE Vx, byte
 | 
			
		||||
				case 0x4000: // 0x4XNN Skips the next instruction if VX doesn't equal NN. | SNE Vx, byte
 | 
			
		||||
				case 0x5000: // 0x5XYO Skips the next instruction if VX equals VY. | SE Vx, Vy
 | 
			
		||||
				case 0x6000: // 0x6XNN Sets VX to NN. | LD Vx, byte
 | 
			
		||||
				case 0x7000: // 0x7XNN Adds NN to VX. | AD Vx, byte
 | 
			
		||||
				case 0x8000:
 | 
			
		||||
					switch (opcode) {
 | 
			
		||||
						case 0x0000: // 0x8XY0 Sets VX to the value of VY. | LD Vx, Vy
 | 
			
		||||
						case 0x0001: // 0x8XY1 Sets VX to VX or VY. | OR Vx, Vy
 | 
			
		||||
						case 0x0002: // 0x8XY2	Sets VX to VX and VY. | AND Vx, Vy
 | 
			
		||||
						case 0x0003: // 0x8XY3 Sets VX to VX xor VY. | XOR Vx, Vy
 | 
			
		||||
						case 0x0004: // 0x8XY4 Adds VY to VX. VF is set to 1 when there's a carry, and to 0 when there isn't. | ADD Vx, Vy
 | 
			
		||||
						case 0x0005: // 0x8XY5 VY is subtracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't. | SUB Vx, Vy
 | 
			
		||||
						case 0x0006: // 0x8XY6 Shifts VX right by one. VF is set to the value of the least significant bit of VX before the shift. | SHR Vx {, Vy}
 | 
			
		||||
						case 0x0007: // 0x8XY7 Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't. | SUBN Vx, Vy
 | 
			
		||||
						case 0x000E: // 0x8XYE Shifts VX left by one. VF is set to the value of the most significant bit of VX before the shift. | SHL Vx {, Vy}
 | 
			
		||||
						default: // unhandled for some reason
 | 
			
		||||
							writefln("unknown opcode: 0x%x", opcode);
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				case 0x9000: // 0x9XYO Skips the next instruction if VX doesn't equal VY. | SNE Vx, Vy
 | 
			
		||||
				case 0xA000: // 0xANNN  Sets I to the address NNN. | LD I, addr
 | 
			
		||||
				case 0xB000: // 0xBNNN Jumps to the address NNN plus V0. | JP V0, addr
 | 
			
		||||
				case 0xC000: // 0xCXNN Sets VX to the result of a bitwise and operation on a random number and NN. | RND Vx, byte
 | 
			
		||||
 | 
			
		||||
				// 0xDXYN | DRW Vx, Vy, nibble
 | 
			
		||||
				// Sprites stored in memory at location in index register (I), 8bits wide.
 | 
			
		||||
				// Wraps around the screen. If when drawn, clears a pixel, register VF is set to 1 otherwise it is zero.
 | 
			
		||||
				// All drawing is XOR drawing (i.e. it toggles the screen pixels).
 | 
			
		||||
				// Sprites are drawn starting at position VX, VY. N is the number of 8bit rows that need to be drawn.
 | 
			
		||||
				// If N is greater than 1, second line continues at position VX, VY+1, and so on.
 | 
			
		||||
				case 0xD000:
 | 
			
		||||
				case 0xE000:
 | 
			
		||||
					switch (opcode & 0x000F) {
 | 
			
		||||
						case 0x000E: // 0xEX9E Skips the next instruction if the key stored in VX is pressed. | SKP Vx
 | 
			
		||||
						case 0x0001: // 0xEXA1 Skips the next instruction if the key stored in VX isn't pressed. | SKNP Vx
 | 
			
		||||
						default: // unhandled for some reason
 | 
			
		||||
							writefln("unknown opcode: 0x%x", opcode);
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				case 0xF000:
 | 
			
		||||
					switch (opcode & 0x00FF) {
 | 
			
		||||
						case 0x0007: // 0xFX07 Sets VX to the value of the delay timer. | LD Vx, DT
 | 
			
		||||
						case 0x000A: // 0xFX0A A key press is awaited, and then stored in VX. | LD Vx, K
 | 
			
		||||
						case 0x0015: // 0xFX15 Sets the delay timer to VX. | LD DT, Vx
 | 
			
		||||
						case 0x0018: // 0xFX18 Sets the sound timer to VX. | LD ST, Vx
 | 
			
		||||
						case 0x001E: // 0xFX1E Adds VX to I. | ADD I, Vx
 | 
			
		||||
						case 0x0029: // 0xFX29 Sets I to the location of the sprite for the character in VX. | LD F, Vx
 | 
			
		||||
						// 0xFX33 Stores the Binary-coded decimal representation of VX,
 | 
			
		||||
						//  with the most significant of three digits at the address in I,
 | 
			
		||||
						//  the middle digit at I plus 1, and the least significant digit at I plus 2.
 | 
			
		||||
						case 0x0033: // 0xFX33 ??? FIXME? | LD B, Vx
 | 
			
		||||
						case 0x0055: // 0xFX55 Stores V0 to VX in memory starting at address I. | LD [I], Vx
 | 
			
		||||
						case 0x0065: // 0xFX65 Fills V0 to VX with values from memory starting at address I. | LD Vx, [I]
 | 
			
		||||
						default: // unhandled for some reason
 | 
			
		||||
							writefln("unknown opcode: 0x%x", opcode);
 | 
			
		||||
							break;
 | 
			
		||||
					}
 | 
			
		||||
					break;
 | 
			
		||||
				default:
 | 
			
		||||
					writefln("unknown opcode: 0x%x", opcode);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		return "";
 | 
			
		||||
 | 
			
		||||
	} // dissassemble
 | 
			
		||||
 | 
			
		||||
} // Assembler
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,344 @@
 | 
			
		|||
module gl;
 | 
			
		||||
 | 
			
		||||
import glad.gl.all;
 | 
			
		||||
import core.stdc.stdio;
 | 
			
		||||
 | 
			
		||||
bool checkShaderError(GLuint shader, GLuint flag, bool is_program, in char[] shader_path) nothrow {
 | 
			
		||||
 | 
			
		||||
	GLint result;
 | 
			
		||||
 | 
			
		||||
	(is_program) ? glGetProgramiv(shader, flag, &result)
 | 
			
		||||
		: glGetShaderiv(shader, flag, &result);
 | 
			
		||||
 | 
			
		||||
	if (result == GL_FALSE) {
 | 
			
		||||
 | 
			
		||||
		GLchar[256] log; //FIXME this is potentially fatal
 | 
			
		||||
		(is_program) ? glGetProgramInfoLog(shader, log.sizeof, null, log.ptr)
 | 
			
		||||
			: glGetShaderInfoLog(shader, log.sizeof, null, log.ptr);
 | 
			
		||||
 | 
			
		||||
		printf("[OpenGL] Error in %s: %s\n", shader_path.ptr, log.ptr);
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
 | 
			
		||||
} // checkShaderError
 | 
			
		||||
 | 
			
		||||
struct PixelBuffer {
 | 
			
		||||
 | 
			
		||||
	struct Shader {
 | 
			
		||||
 | 
			
		||||
		private {
 | 
			
		||||
			GLuint shader_prog;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@disable this(this);
 | 
			
		||||
 | 
			
		||||
		void compile(const (GLchar*)* vs_source, const (GLchar*)* fs_source) {
 | 
			
		||||
			
 | 
			
		||||
			import core.stdc.stdlib : exit;
 | 
			
		||||
 | 
			
		||||
			GLuint new_vs_shader = glCreateShader(GL_VERTEX_SHADER);
 | 
			
		||||
			glShaderSource(new_vs_shader, 1, vs_source, null);
 | 
			
		||||
			glCompileShader(new_vs_shader);
 | 
			
		||||
			if (!checkShaderError(new_vs_shader, GL_COMPILE_STATUS, false, "vertex_shader")) {
 | 
			
		||||
				exit(-1);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			GLuint new_fs_shader = glCreateShader(GL_FRAGMENT_SHADER);
 | 
			
		||||
			glShaderSource(new_fs_shader, 1, fs_source, null);
 | 
			
		||||
			glCompileShader(new_fs_shader);
 | 
			
		||||
			if (!checkShaderError(new_fs_shader, GL_COMPILE_STATUS, false, "fragment_shader")) {
 | 
			
		||||
				exit(-1);
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			GLuint new_shader = glCreateProgram();
 | 
			
		||||
			glAttachShader(new_shader, new_vs_shader);
 | 
			
		||||
			glAttachShader(new_shader, new_fs_shader);
 | 
			
		||||
 | 
			
		||||
			// glBindAttribLocation(new_shader, 0, "screen_size");
 | 
			
		||||
 | 
			
		||||
			glLinkProgram(new_shader);
 | 
			
		||||
			if (!checkShaderError(new_shader, GL_LINK_STATUS, true, "shader_program")) {
 | 
			
		||||
				exit(-1);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			glValidateProgram(new_shader);
 | 
			
		||||
			if (!checkShaderError(new_shader, GL_VALIDATE_STATUS, true, "shader_program")) {
 | 
			
		||||
				exit(-1);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			shader_prog = new_shader;
 | 
			
		||||
			
 | 
			
		||||
		} // compile
 | 
			
		||||
 | 
			
		||||
		void setUniforms(int w, int h) {
 | 
			
		||||
			auto attr_loc = glGetUniformLocation(shader_prog, "screen_size");
 | 
			
		||||
			glUniform2f(attr_loc, cast(float)w, cast(float)h);
 | 
			
		||||
		} // setUniforms
 | 
			
		||||
 | 
			
		||||
		void bind() {
 | 
			
		||||
			glUseProgram(shader_prog);
 | 
			
		||||
		} // bind
 | 
			
		||||
 | 
			
		||||
		void unbind() {
 | 
			
		||||
			glUseProgram(0);
 | 
			
		||||
		} // unbind
 | 
			
		||||
 | 
			
		||||
	} // Shader
 | 
			
		||||
 | 
			
		||||
	struct Texture {
 | 
			
		||||
 | 
			
		||||
		import derelict.sdl2.sdl;
 | 
			
		||||
		import derelict.sdl2.image;
 | 
			
		||||
 | 
			
		||||
		private {
 | 
			
		||||
 | 
			
		||||
			GLuint texture_; //OpenGL handle for texture
 | 
			
		||||
			GLenum input_format_, output_format_, data_type_;
 | 
			
		||||
			int width_, height_;
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@property @nogc nothrow {
 | 
			
		||||
 | 
			
		||||
			int width() const { return width_; }
 | 
			
		||||
			int height() const { return height_; }
 | 
			
		||||
			GLuint handle() { return texture_; }
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@disable this(this);
 | 
			
		||||
 | 
			
		||||
		nothrow @nogc
 | 
			
		||||
		this(int width, int height, GLenum input_format, GLenum output_format, GLenum unpack_alignment) {
 | 
			
		||||
 | 
			
		||||
			width_ = width;
 | 
			
		||||
			height_ = height;
 | 
			
		||||
			input_format_ = input_format;
 | 
			
		||||
			output_format_ = output_format;
 | 
			
		||||
			data_type_ = GL_UNSIGNED_BYTE;
 | 
			
		||||
 | 
			
		||||
			glGenTextures(1, &texture_);
 | 
			
		||||
			glBindTexture(GL_TEXTURE_2D, texture_);
 | 
			
		||||
 | 
			
		||||
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 | 
			
		||||
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 | 
			
		||||
 | 
			
		||||
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 | 
			
		||||
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 | 
			
		||||
 | 
			
		||||
			glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment);
 | 
			
		||||
			glTexImage2D(GL_TEXTURE_2D, 0, input_format_, width_, height_, 0, output_format_, GL_UNSIGNED_BYTE, cast(void*)0);
 | 
			
		||||
 | 
			
		||||
			glBindTexture(GL_TEXTURE_2D, 0);
 | 
			
		||||
 | 
			
		||||
		} //this
 | 
			
		||||
 | 
			
		||||
		nothrow @nogc
 | 
			
		||||
		void create(void* pixels, int width, int height, GLenum input_format = GL_RGB, GLenum output_format = GL_RGB, GLenum data_type = GL_UNSIGNED_BYTE) {
 | 
			
		||||
 | 
			
		||||
			width_ = width;
 | 
			
		||||
			height_ = height;
 | 
			
		||||
			input_format_ = input_format;
 | 
			
		||||
			output_format_ = output_format;
 | 
			
		||||
			data_type_ = data_type;
 | 
			
		||||
 | 
			
		||||
			//generate single texture, put handle in texture
 | 
			
		||||
			glGenTextures(1, &texture_);
 | 
			
		||||
 | 
			
		||||
			//normal 2d texture, bind to our texture handle
 | 
			
		||||
			glBindTexture(GL_TEXTURE_2D, texture_);
 | 
			
		||||
 | 
			
		||||
			//set texture parameters in currently bound texture, controls texture wrapping (or GL_CLAMP?)
 | 
			
		||||
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 | 
			
		||||
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 | 
			
		||||
 | 
			
		||||
			//linearly interpolate between pixels, MIN if texture is too small for drawing area, MAG if drawing area is smaller than texture
 | 
			
		||||
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 | 
			
		||||
			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 | 
			
		||||
 | 
			
		||||
			//texture type, level, format to store as, width, height, border, format loaded in
 | 
			
		||||
			glTexImage2D(GL_TEXTURE_2D, 0, input_format_, width_, height_, 0, output_format_, data_type_, pixels);
 | 
			
		||||
 | 
			
		||||
			//UNBIND
 | 
			
		||||
			glBindTexture(GL_TEXTURE_2D, 0);
 | 
			
		||||
 | 
			
		||||
		} // this
 | 
			
		||||
 | 
			
		||||
		~this() nothrow @nogc {
 | 
			
		||||
 | 
			
		||||
			if (texture_ != 0) {
 | 
			
		||||
				glDeleteTextures(1, &texture_);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		} // ~this
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		* Binds the texture handle, takes an argument for which texture unit to use.
 | 
			
		||||
		*/
 | 
			
		||||
		nothrow @nogc
 | 
			
		||||
		void bind(int unit) {
 | 
			
		||||
 | 
			
		||||
			assert(unit >= 0 && unit <= 31);
 | 
			
		||||
			glActiveTexture(GL_TEXTURE0 + unit); //since this is sequential, this works
 | 
			
		||||
			glBindTexture(GL_TEXTURE_2D, texture_);
 | 
			
		||||
 | 
			
		||||
		} // bind
 | 
			
		||||
 | 
			
		||||
		nothrow @nogc
 | 
			
		||||
		void unbind() {
 | 
			
		||||
 | 
			
		||||
			glBindTexture(GL_TEXTURE_2D, 0);
 | 
			
		||||
 | 
			
		||||
		} // unbind
 | 
			
		||||
 | 
			
		||||
		/**
 | 
			
		||||
		* Updates the texture in place given the new texture buffer.
 | 
			
		||||
		* Takes an optional offset to update only a part of the texture.
 | 
			
		||||
		**/
 | 
			
		||||
		nothrow @nogc
 | 
			
		||||
		void update(void[] pixels, size_t offset = 0) {
 | 
			
		||||
 | 
			
		||||
			bind(0);
 | 
			
		||||
			glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, input_format_, data_type_, pixels.ptr);
 | 
			
		||||
			unbind();
 | 
			
		||||
 | 
			
		||||
		} // update
 | 
			
		||||
 | 
			
		||||
	} // Texture
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	* Generic VertexArray structure, used to upload data of any given vertex type to the GPU.
 | 
			
		||||
	*/
 | 
			
		||||
	struct VertexArray {
 | 
			
		||||
 | 
			
		||||
		private {
 | 
			
		||||
 | 
			
		||||
			GLuint vao_;
 | 
			
		||||
			GLuint vbo_;
 | 
			
		||||
			GLenum type_; // type of vertex data, GL_TRIANGLES etc
 | 
			
		||||
			uint num_vertices_;
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@disable this(this);
 | 
			
		||||
 | 
			
		||||
		nothrow @nogc
 | 
			
		||||
		void create(in float[2][] vertices, GLenum draw_type = GL_STATIC_DRAW, GLenum primitive = GL_TRIANGLES)  {
 | 
			
		||||
 | 
			
		||||
			this.num_vertices_ = cast(uint)vertices.length;
 | 
			
		||||
			this.type_ = primitive;
 | 
			
		||||
 | 
			
		||||
			glGenVertexArrays(1, &vao_);
 | 
			
		||||
			glBindVertexArray(vao_);
 | 
			
		||||
 | 
			
		||||
			glGenBuffers(1, &vbo_);
 | 
			
		||||
			glBindBuffer(GL_ARRAY_BUFFER, vbo_);
 | 
			
		||||
			glBufferData(GL_ARRAY_BUFFER, vertices.length * vertices[0].sizeof, vertices.ptr, draw_type);
 | 
			
		||||
 | 
			
		||||
			// pos
 | 
			
		||||
			glEnableVertexAttribArray(0);
 | 
			
		||||
			glVertexAttribPointer(0,
 | 
			
		||||
				2,
 | 
			
		||||
				GL_FLOAT,
 | 
			
		||||
				GL_FALSE,
 | 
			
		||||
				float.sizeof * 2,
 | 
			
		||||
				null
 | 
			
		||||
			);
 | 
			
		||||
 | 
			
		||||
			glBindVertexArray(0);
 | 
			
		||||
 | 
			
		||||
		} // this
 | 
			
		||||
 | 
			
		||||
		~this() nothrow @nogc {
 | 
			
		||||
			if (vao_ != 0) {
 | 
			
		||||
				glDeleteVertexArrays(1, &vao_);
 | 
			
		||||
			}
 | 
			
		||||
		} // ~this
 | 
			
		||||
 | 
			
		||||
		void bind() nothrow @nogc {
 | 
			
		||||
			glBindVertexArray(vao_);
 | 
			
		||||
		} // bind
 | 
			
		||||
 | 
			
		||||
		void draw() nothrow @nogc {
 | 
			
		||||
			glDrawArrays(type_, 0, num_vertices_);
 | 
			
		||||
		} // draw
 | 
			
		||||
 | 
			
		||||
		void unbind() nothrow @nogc {
 | 
			
		||||
			glBindVertexArray(0);
 | 
			
		||||
		} // unbind
 | 
			
		||||
 | 
			
		||||
	} // VertexArray
 | 
			
		||||
 | 
			
		||||
	const char* vs_shader = q{
 | 
			
		||||
		#version 330 core
 | 
			
		||||
 | 
			
		||||
		uniform vec2 screen_size;
 | 
			
		||||
 | 
			
		||||
		layout(location = 0) in vec2 pos;
 | 
			
		||||
 | 
			
		||||
		out vec2 frag_uv;
 | 
			
		||||
 | 
			
		||||
		void main() {
 | 
			
		||||
			gl_Position = vec4(pos, 0.0, 1.0);
 | 
			
		||||
			frag_uv = clamp(pos, vec2(0.0, 0.0), vec2(1.0, 1.0));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const char* fs_shader = q{
 | 
			
		||||
		#version 330 core
 | 
			
		||||
 | 
			
		||||
		uniform sampler2D tex;
 | 
			
		||||
 | 
			
		||||
		in vec2 frag_uv;
 | 
			
		||||
 | 
			
		||||
		out vec4 out_col;
 | 
			
		||||
 | 
			
		||||
		void main() {
 | 
			
		||||
			float v = texture(tex, frag_uv.st).r * 255;
 | 
			
		||||
			out_col = vec4(v, v, v, 1.0);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Shader shader;
 | 
			
		||||
	VertexArray vao;
 | 
			
		||||
	Texture tex;
 | 
			
		||||
 | 
			
		||||
	void create(void* pixels, int w, int h) {
 | 
			
		||||
 | 
			
		||||
		float[2][6] rect = [
 | 
			
		||||
			[-1.0f, -1.0f], // top left
 | 
			
		||||
			[1.0f, -1.0f], // top right
 | 
			
		||||
			[1.0f, 1.0f], // bottom right
 | 
			
		||||
 | 
			
		||||
			[-1.0f, -1.0f], // top left
 | 
			
		||||
			[-1.0f, 1.0f], // bottom left
 | 
			
		||||
			[1.0f, 1.0f], // bottom right
 | 
			
		||||
		];
 | 
			
		||||
 | 
			
		||||
		vao.create(rect[]);
 | 
			
		||||
		tex.create(pixels, w, h, GL_RED, GL_RED);
 | 
			
		||||
		shader.compile(&vs_shader, &fs_shader);
 | 
			
		||||
 | 
			
		||||
	} // create
 | 
			
		||||
 | 
			
		||||
	void draw(int screen_w, int screen_h) {
 | 
			
		||||
 | 
			
		||||
		vao.bind();
 | 
			
		||||
		shader.bind();
 | 
			
		||||
		shader.setUniforms(screen_w, screen_h);
 | 
			
		||||
		tex.bind(0);
 | 
			
		||||
		vao.draw();
 | 
			
		||||
		
 | 
			
		||||
	} // draw
 | 
			
		||||
 | 
			
		||||
	nothrow @nogc
 | 
			
		||||
	void update(void[] pixels, size_t offset = 0) {
 | 
			
		||||
		tex.update(pixels, offset);
 | 
			
		||||
	} // update
 | 
			
		||||
 | 
			
		||||
} // PixelBuffer
 | 
			
		||||
							
								
								
									
										193
									
								
								source/imgui.d
								
								
								
								
							
							
						
						
									
										193
									
								
								source/imgui.d
								
								
								
								
							| 
						 | 
				
			
			@ -23,11 +23,11 @@ auto bindDelegate(T, string file = __FILE__, size_t line = __LINE__)(T t) if(isD
 | 
			
		|||
struct Imgui {
 | 
			
		||||
 | 
			
		||||
    // OpenGL data, cleanup soon
 | 
			
		||||
    GLuint       g_FontTexture = 0;
 | 
			
		||||
    int          g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
 | 
			
		||||
    int          g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0;
 | 
			
		||||
    int          g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0;
 | 
			
		||||
    uint         g_VboHandle = 0, g_VaoHandle = 0, g_ElementsHandle = 0;
 | 
			
		||||
    GLuint font_texture_ = 0;
 | 
			
		||||
    int shander_handle_ = 0, vs_handle_ = 0, fs_handle_ = 0;
 | 
			
		||||
    int shader_attrib_loc_tex_ = 0, shader_attrib_loc_projmtx_ = 0;
 | 
			
		||||
    int shader_attrib_loc_pos_ = 0, shader_attrib_loc_uv_ = 0, shader_attrib_loc_color_ = 0;
 | 
			
		||||
    uint vao_ = 0, vbo_ = 0, ebo_ = 0;
 | 
			
		||||
 | 
			
		||||
    // imgui input related state
 | 
			
		||||
    bool[3] mouse_buttons_pressed;
 | 
			
		||||
| 
						 | 
				
			
			@ -74,14 +74,14 @@ struct Imgui {
 | 
			
		|||
        // upload texture to graphics system
 | 
			
		||||
        GLint last_texture;
 | 
			
		||||
        glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
 | 
			
		||||
        glGenTextures(1, &g_FontTexture);
 | 
			
		||||
        glBindTexture(GL_TEXTURE_2D, g_FontTexture);
 | 
			
		||||
        glGenTextures(1, &font_texture_);
 | 
			
		||||
        glBindTexture(GL_TEXTURE_2D, font_texture_);
 | 
			
		||||
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 | 
			
		||||
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 | 
			
		||||
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
 | 
			
		||||
 | 
			
		||||
        // store our identifier
 | 
			
		||||
        ImFontAtlas_SetTexID(io.Fonts, cast(void*)g_FontTexture);
 | 
			
		||||
        ImFontAtlas_SetTexID(io.Fonts, cast(void*)font_texture_);
 | 
			
		||||
 | 
			
		||||
        // restore state
 | 
			
		||||
        glBindTexture(GL_TEXTURE_2D, last_texture);
 | 
			
		||||
| 
						 | 
				
			
			@ -122,36 +122,36 @@ struct Imgui {
 | 
			
		|||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        g_ShaderHandle = glCreateProgram();
 | 
			
		||||
        g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
 | 
			
		||||
        g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
 | 
			
		||||
        glShaderSource(g_VertHandle, 1, &vertex_shader, null);
 | 
			
		||||
        glShaderSource(g_FragHandle, 1, &fragment_shader, null);
 | 
			
		||||
        glCompileShader(g_VertHandle);
 | 
			
		||||
        glCompileShader(g_FragHandle);
 | 
			
		||||
        glAttachShader(g_ShaderHandle, g_VertHandle);
 | 
			
		||||
        glAttachShader(g_ShaderHandle, g_FragHandle);
 | 
			
		||||
        glLinkProgram(g_ShaderHandle);
 | 
			
		||||
        shander_handle_ = glCreateProgram();
 | 
			
		||||
        vs_handle_ = glCreateShader(GL_VERTEX_SHADER);
 | 
			
		||||
        fs_handle_ = glCreateShader(GL_FRAGMENT_SHADER);
 | 
			
		||||
        glShaderSource(vs_handle_, 1, &vertex_shader, null);
 | 
			
		||||
        glShaderSource(fs_handle_, 1, &fragment_shader, null);
 | 
			
		||||
        glCompileShader(vs_handle_);
 | 
			
		||||
        glCompileShader(fs_handle_);
 | 
			
		||||
        glAttachShader(shander_handle_, vs_handle_);
 | 
			
		||||
        glAttachShader(shander_handle_, fs_handle_);
 | 
			
		||||
        glLinkProgram(shander_handle_);
 | 
			
		||||
 | 
			
		||||
        g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
 | 
			
		||||
        g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
 | 
			
		||||
        g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position");
 | 
			
		||||
        g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV");
 | 
			
		||||
        g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color");
 | 
			
		||||
        shader_attrib_loc_tex_ = glGetUniformLocation(shander_handle_, "Texture");
 | 
			
		||||
        shader_attrib_loc_projmtx_ = glGetUniformLocation(shander_handle_, "ProjMtx");
 | 
			
		||||
        shader_attrib_loc_pos_ = glGetAttribLocation(shander_handle_, "Position");
 | 
			
		||||
        shader_attrib_loc_uv_ = glGetAttribLocation(shander_handle_, "UV");
 | 
			
		||||
        shader_attrib_loc_color_ = glGetAttribLocation(shander_handle_, "Color");
 | 
			
		||||
 | 
			
		||||
        glGenBuffers(1, &g_VboHandle);
 | 
			
		||||
        glGenBuffers(1, &g_ElementsHandle);
 | 
			
		||||
        glGenBuffers(1, &vbo_);
 | 
			
		||||
        glGenBuffers(1, &ebo_);
 | 
			
		||||
 | 
			
		||||
        glGenVertexArrays(1, &g_VaoHandle);
 | 
			
		||||
        glBindVertexArray(g_VaoHandle);
 | 
			
		||||
        glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
 | 
			
		||||
        glEnableVertexAttribArray(g_AttribLocationPosition);
 | 
			
		||||
        glEnableVertexAttribArray(g_AttribLocationUV);
 | 
			
		||||
        glEnableVertexAttribArray(g_AttribLocationColor);
 | 
			
		||||
        glGenVertexArrays(1, &vao_);
 | 
			
		||||
        glBindVertexArray(vao_);
 | 
			
		||||
        glBindBuffer(GL_ARRAY_BUFFER, vbo_);
 | 
			
		||||
        glEnableVertexAttribArray(shader_attrib_loc_pos_);
 | 
			
		||||
        glEnableVertexAttribArray(shader_attrib_loc_uv_);
 | 
			
		||||
        glEnableVertexAttribArray(shader_attrib_loc_color_);
 | 
			
		||||
 | 
			
		||||
        glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, ImDrawVert.sizeof, cast(void*)ImDrawVert.pos.offsetof);
 | 
			
		||||
        glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, ImDrawVert.sizeof, cast(void*)ImDrawVert.uv.offsetof);
 | 
			
		||||
        glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, ImDrawVert.sizeof, cast(void*)ImDrawVert.col.offsetof);
 | 
			
		||||
        glVertexAttribPointer(shader_attrib_loc_pos_, 2, GL_FLOAT, GL_FALSE, ImDrawVert.sizeof, cast(void*)ImDrawVert.pos.offsetof);
 | 
			
		||||
        glVertexAttribPointer(shader_attrib_loc_uv_, 2, GL_FLOAT, GL_FALSE, ImDrawVert.sizeof, cast(void*)ImDrawVert.uv.offsetof);
 | 
			
		||||
        glVertexAttribPointer(shader_attrib_loc_color_, 4, GL_UNSIGNED_BYTE, GL_TRUE, ImDrawVert.sizeof, cast(void*)ImDrawVert.col.offsetof);
 | 
			
		||||
 | 
			
		||||
        createFontTexture();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -162,6 +162,82 @@ struct Imgui {
 | 
			
		|||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    struct GLState {
 | 
			
		||||
        GLenum last_active_texture;
 | 
			
		||||
        GLint last_program;
 | 
			
		||||
        GLint last_texture;
 | 
			
		||||
        GLint last_array_buffer;
 | 
			
		||||
        GLint last_element_array_buffer;
 | 
			
		||||
        GLint last_vertex_array;
 | 
			
		||||
        GLint[4] last_viewport;
 | 
			
		||||
        GLint[4] last_scissor_box;
 | 
			
		||||
        GLenum last_blend_src_rgb;
 | 
			
		||||
        GLenum last_blend_dst_rgb;
 | 
			
		||||
        GLenum last_blend_src_alpha;
 | 
			
		||||
        GLenum last_blend_dst_alpha;
 | 
			
		||||
        GLenum last_blend_equation_rgb;
 | 
			
		||||
        GLenum last_blend_equation_alpha;
 | 
			
		||||
        GLboolean last_enable_blend;
 | 
			
		||||
        GLboolean last_enable_cull_face;
 | 
			
		||||
        GLboolean last_enable_depth_test;
 | 
			
		||||
        GLboolean last_enable_scissor_test;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    nothrow @nogc
 | 
			
		||||
    GLState backupGLState() {
 | 
			
		||||
 | 
			
		||||
        GLState gl_state;
 | 
			
		||||
 | 
			
		||||
        // backup GL state
 | 
			
		||||
        with (gl_state) {
 | 
			
		||||
            glGetIntegerv(GL_ACTIVE_TEXTURE, cast(GLint*)&last_active_texture);
 | 
			
		||||
            glActiveTexture(GL_TEXTURE0);
 | 
			
		||||
            glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
 | 
			
		||||
            glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
 | 
			
		||||
            glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
 | 
			
		||||
            glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer);
 | 
			
		||||
            glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
 | 
			
		||||
            glGetIntegerv(GL_VIEWPORT, last_viewport.ptr);
 | 
			
		||||
            glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box.ptr);
 | 
			
		||||
            glGetIntegerv(GL_BLEND_SRC_RGB, cast(GLint*)&last_blend_src_rgb);
 | 
			
		||||
            glGetIntegerv(GL_BLEND_DST_RGB, cast(GLint*)&last_blend_dst_rgb);
 | 
			
		||||
            glGetIntegerv(GL_BLEND_SRC_ALPHA, cast(GLint*)&last_blend_src_alpha);
 | 
			
		||||
            glGetIntegerv(GL_BLEND_DST_ALPHA, cast(GLint*)&last_blend_dst_alpha);
 | 
			
		||||
            glGetIntegerv(GL_BLEND_EQUATION_RGB, cast(GLint*)&last_blend_equation_rgb);
 | 
			
		||||
            glGetIntegerv(GL_BLEND_EQUATION_ALPHA, cast(GLint*)&last_blend_equation_alpha);
 | 
			
		||||
            last_enable_blend = glIsEnabled(GL_BLEND);
 | 
			
		||||
            last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
 | 
			
		||||
            last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
 | 
			
		||||
            last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return gl_state;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    nothrow @nogc
 | 
			
		||||
    void restoreGLState(GLState state) {
 | 
			
		||||
 | 
			
		||||
        // restore modified GL state
 | 
			
		||||
        with (state) {
 | 
			
		||||
            glUseProgram(last_program);
 | 
			
		||||
            glBindTexture(GL_TEXTURE_2D, last_texture);
 | 
			
		||||
            glActiveTexture(last_active_texture);
 | 
			
		||||
            glBindVertexArray(last_vertex_array);
 | 
			
		||||
            glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
 | 
			
		||||
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer);
 | 
			
		||||
            glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
 | 
			
		||||
            glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
 | 
			
		||||
            if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
 | 
			
		||||
            if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
 | 
			
		||||
            if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
 | 
			
		||||
            if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
 | 
			
		||||
            glViewport(last_viewport[0], last_viewport[1], cast(GLsizei)last_viewport[2], cast(GLsizei)last_viewport[3]);
 | 
			
		||||
            glScissor(last_scissor_box[0], last_scissor_box[1], cast(GLsizei)last_scissor_box[2], cast(GLsizei)last_scissor_box[3]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    extern(C) nothrow
 | 
			
		||||
    void renderDrawLists(ImDrawData* draw_data) {
 | 
			
		||||
        
 | 
			
		||||
| 
						 | 
				
			
			@ -173,25 +249,7 @@ struct Imgui {
 | 
			
		|||
        draw_data.ScaleClipRects(io.DisplayFramebufferScale);
 | 
			
		||||
 | 
			
		||||
        // backup GL state
 | 
			
		||||
        GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, cast(GLint*)&last_active_texture);
 | 
			
		||||
        glActiveTexture(GL_TEXTURE0);
 | 
			
		||||
        GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
 | 
			
		||||
        GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
 | 
			
		||||
        GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
 | 
			
		||||
        GLint last_element_array_buffer; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer);
 | 
			
		||||
        GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
 | 
			
		||||
        GLint[4] last_viewport; glGetIntegerv(GL_VIEWPORT, last_viewport.ptr);
 | 
			
		||||
        GLint[4] last_scissor_box; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box.ptr);
 | 
			
		||||
        GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, cast(GLint*)&last_blend_src_rgb);
 | 
			
		||||
        GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, cast(GLint*)&last_blend_dst_rgb);
 | 
			
		||||
        GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, cast(GLint*)&last_blend_src_alpha);
 | 
			
		||||
        GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, cast(GLint*)&last_blend_dst_alpha);
 | 
			
		||||
        GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, cast(GLint*)&last_blend_equation_rgb);
 | 
			
		||||
        GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, cast(GLint*)&last_blend_equation_alpha);
 | 
			
		||||
        GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
 | 
			
		||||
        GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
 | 
			
		||||
        GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
 | 
			
		||||
        GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
 | 
			
		||||
        GLState last_state = backupGLState();
 | 
			
		||||
 | 
			
		||||
        // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled
 | 
			
		||||
        glEnable(GL_BLEND);
 | 
			
		||||
| 
						 | 
				
			
			@ -210,10 +268,10 @@ struct Imgui {
 | 
			
		|||
            [-1.0f,                  1.0f,                   0.0f, 1.0f ],
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        glUseProgram(g_ShaderHandle);
 | 
			
		||||
        glUniform1i(g_AttribLocationTex, 0);
 | 
			
		||||
        glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
 | 
			
		||||
        glBindVertexArray(g_VaoHandle);
 | 
			
		||||
        glUseProgram(shander_handle_);
 | 
			
		||||
        glUniform1i(shader_attrib_loc_tex_, 0);
 | 
			
		||||
        glUniformMatrix4fv(shader_attrib_loc_projmtx_, 1, GL_FALSE, &ortho_projection[0][0]);
 | 
			
		||||
        glBindVertexArray(vao_);
 | 
			
		||||
 | 
			
		||||
        foreach (int n; 0 .. draw_data.CmdListsCount) {
 | 
			
		||||
            ImDrawList* cmd_list = draw_data.CmdLists[n];
 | 
			
		||||
| 
						 | 
				
			
			@ -222,10 +280,10 @@ struct Imgui {
 | 
			
		|||
            auto countVertices = ImDrawList_GetVertexBufferSize(cmd_list);
 | 
			
		||||
            auto countIndices = ImDrawList_GetIndexBufferSize(cmd_list);
 | 
			
		||||
 | 
			
		||||
            glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
 | 
			
		||||
            glBindBuffer(GL_ARRAY_BUFFER, vbo_);
 | 
			
		||||
            glBufferData(GL_ARRAY_BUFFER, countVertices * ImDrawVert.sizeof, cast(GLvoid*)ImDrawList_GetVertexPtr(cmd_list,0), GL_STREAM_DRAW);
 | 
			
		||||
 | 
			
		||||
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
 | 
			
		||||
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_);
 | 
			
		||||
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, countIndices * ImDrawIdx.sizeof, cast(GLvoid*)ImDrawList_GetIndexPtr(cmd_list,0), GL_STREAM_DRAW);
 | 
			
		||||
 | 
			
		||||
            auto cmdCnt = ImDrawList_GetCmdSize(cmd_list);
 | 
			
		||||
| 
						 | 
				
			
			@ -244,21 +302,8 @@ struct Imgui {
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Restore modified GL state
 | 
			
		||||
        glUseProgram(last_program);
 | 
			
		||||
        glBindTexture(GL_TEXTURE_2D, last_texture);
 | 
			
		||||
        glActiveTexture(last_active_texture);
 | 
			
		||||
        glBindVertexArray(last_vertex_array);
 | 
			
		||||
        glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
 | 
			
		||||
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer);
 | 
			
		||||
        glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
 | 
			
		||||
        glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
 | 
			
		||||
        if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
 | 
			
		||||
        if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
 | 
			
		||||
        if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
 | 
			
		||||
        if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
 | 
			
		||||
        glViewport(last_viewport[0], last_viewport[1], cast(GLsizei)last_viewport[2], cast(GLsizei)last_viewport[3]);
 | 
			
		||||
        glScissor(last_scissor_box[0], last_scissor_box[1], cast(GLsizei)last_scissor_box[2], cast(GLsizei)last_scissor_box[3]);
 | 
			
		||||
        // restore old state
 | 
			
		||||
        restoreGLState(last_state);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,8 @@ import glad.gl.all;
 | 
			
		|||
 * Converts an integer representing a colour, for example 0x428bca into a 4 element
 | 
			
		||||
 * int array for passing to OpenGL.
 | 
			
		||||
*/
 | 
			
		||||
GLfloat[4] to(T : GLfloat[4])(int color, ubyte alpha = 255) nothrow @nogc pure {
 | 
			
		||||
nothrow @nogc pure
 | 
			
		||||
GLfloat[4] to(T : GLfloat[4])(int color, ubyte alpha = 255) {
 | 
			
		||||
 | 
			
		||||
	GLfloat[4] gl_color = [ //mask out r, g, b components from int
 | 
			
		||||
		cast(float)cast(ubyte)(color>>16)/255,
 | 
			
		||||
| 
						 | 
				
			
			@ -73,8 +74,7 @@ struct Window {
 | 
			
		|||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        import glad.gl.loader;
 | 
			
		||||
        import std.functional;
 | 
			
		||||
        import glad.gl.loader : gladLoadGL;
 | 
			
		||||
 | 
			
		||||
        // Check OpenGL properties
 | 
			
		||||
        printf("OpenGL loaded\n");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue