Compare commits
	
		
			3 Commits
		
	
	
		
			a35c316800
			...
			1aff62163a
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 1aff62163a | |
|  | 0a08fa128b | |
|  | e09f663b9b | 
|  | @ -0,0 +1,4 @@ | |||
| [analysis.config.StaticAnalysisConfig] | ||||
| 
 | ||||
| undocumented_declaration_check="disabled" | ||||
| long_line_check="disabled" | ||||
							
								
								
									
										761
									
								
								source/app.d
								
								
								
								
							
							
						
						
									
										761
									
								
								source/app.d
								
								
								
								
							|  | @ -1,560 +1,218 @@ | |||
| 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 { | ||||
| struct Chip8Status { | ||||
| 
 | ||||
| 	GLint result; | ||||
| 	struct Assembly { | ||||
| 		 | ||||
| 	(is_program) ? glGetProgramiv(shader, flag, &result) | ||||
| 		: glGetShaderiv(shader, flag, &result); | ||||
| 		import assembler; | ||||
| 
 | ||||
| 	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; | ||||
| 		alias OpCode = Assembler.OpCode; | ||||
| 		alias Instr = Assembler.Instruction; | ||||
| 		alias Arg = Assembler.Argument; | ||||
| 
 | ||||
| 		enum ViewMode { | ||||
| 			Memory, | ||||
| 			Disassembly | ||||
| 		} | ||||
| 
 | ||||
| 	return true; | ||||
| 
 | ||||
| } // checkShaderError
 | ||||
| 
 | ||||
| struct PixelBuffer { | ||||
| 
 | ||||
| 	struct Shader { | ||||
| 
 | ||||
| 		private { | ||||
| 			GLuint shader_prog; | ||||
| 		enum DisassemblyMode { | ||||
| 			Linear, | ||||
| 			Recursive, | ||||
| 			Dynamic | ||||
| 		} | ||||
| 
 | ||||
| 		@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_; | ||||
| 			Chip8* emu_; | ||||
| 			ViewMode mode_; | ||||
| 			int range_start_; | ||||
| 			int range_end_; | ||||
| 
 | ||||
| 			// disassembly state
 | ||||
| 			DisassemblyMode ass_mode_; | ||||
| 			Instr[] disassembly_; | ||||
| 
 | ||||
| 		} | ||||
| 
 | ||||
| 		@property @nogc nothrow { | ||||
| 
 | ||||
| 			int width() const { return width_; } | ||||
| 			int height() const { return height_; } | ||||
| 			GLuint handle() { return texture_; } | ||||
| 		void initialize(Chip8* emu) { | ||||
| 			mode_ = ViewMode.Disassembly; | ||||
| 			emu_ = emu; | ||||
| 		} // initialize
 | ||||
| 		 | ||||
| 		static const (char)* formatQual(Arg a, bool with_comma = false) { | ||||
| 			if (with_comma) { | ||||
| 				switch (a) with (Arg) { | ||||
| 					case Vx, Vy, V0: return "V%hu,"; | ||||
| 					case nibble: return "%01X,"; | ||||
| 					case beit: return "0x%02X,"; | ||||
| 					case addr: return "0x%03X,"; | ||||
| 					default: return "%s,"; | ||||
| 				} | ||||
| 
 | ||||
| 		@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_); | ||||
| 			} else { | ||||
| 				switch (a) with (Arg) { | ||||
| 					case Vx, Vy, V0: return "V%hu"; | ||||
| 					case nibble: return "0x%01X"; | ||||
| 					case beit: return "0x%02X"; | ||||
| 					case addr: return "0x%03X"; | ||||
| 					default: return "%s"; | ||||
| 				} | ||||
| 
 | ||||
| 		} // ~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_; | ||||
| 
 | ||||
| 			} | ||||
| 		} // formatQual
 | ||||
| 
 | ||||
| 		@disable this(this); | ||||
| 		void disassemble() { | ||||
| 			ubyte[] instrs = emu_.ram[range_start_ .. range_end_]; | ||||
| 			disassembly_ = Assembler.disassemble(instrs); | ||||
| 		} // disassemble
 | ||||
| 
 | ||||
| 		nothrow @nogc | ||||
| 		void create(in float[2][] vertices, GLenum draw_type = GL_STATIC_DRAW, GLenum primitive = GL_TRIANGLES)  { | ||||
| 		void setRange(ushort start, ushort end) { | ||||
| 			range_start_ = start; | ||||
| 			range_end_ = end; | ||||
| 		} // setRange
 | ||||
| 
 | ||||
| 			this.num_vertices_ = cast(uint)vertices.length; | ||||
| 			this.type_ = primitive; | ||||
| 		void reset() { | ||||
| 			disassembly_ = null; | ||||
| 		} // reset
 | ||||
| 
 | ||||
| 			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) { | ||||
| 		void draw() { | ||||
| 
 | ||||
| 			import std.range : chunks; | ||||
| 
 | ||||
| 		foreach (ref ubyte[] i; instructions.chunks(2)) { | ||||
| 			igBegin("Emulator Assembly"); | ||||
| 			igRadioButton("Memory View", cast(int*)&mode_, cast(int)ViewMode.Memory); igSameLine(); | ||||
| 			igRadioButton("Disassembly View", cast(int*)&mode_, cast(int)ViewMode.Disassembly); | ||||
| 
 | ||||
| 			ushort opcode = (*(cast(ushort*)(i.ptr))); | ||||
| 			if (mode_ == ViewMode.Memory) { | ||||
| 			} else if (mode_ == ViewMode.Disassembly) { | ||||
| 				igInputInt("Range Start ", &range_start_); | ||||
| 				igInputInt("Range End ", &range_end_); | ||||
| 				igNewLine(); | ||||
| 
 | ||||
| 			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!");
 | ||||
| 				igText("Disassembly Mode"); | ||||
| 				igRadioButton("Linear", cast(int*)&ass_mode_, cast(int)DisassemblyMode.Linear); igSameLine(); | ||||
| 				igRadioButton("Recursive", cast(int*)&ass_mode_, cast(int)DisassemblyMode.Recursive); igSameLine(); | ||||
| 				igRadioButton("Dynamic", cast(int*)&ass_mode_, cast(int)DisassemblyMode.Dynamic); | ||||
| 				if (igButton("Disassemble")) { | ||||
| 					final switch (ass_mode_) with (DisassemblyMode) { | ||||
| 						case Linear: | ||||
| 							disassemble(); | ||||
| 							break; | ||||
| 						case Recursive: | ||||
| 							break; | ||||
| 						case Dynamic: | ||||
| 							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; | ||||
| 			 | ||||
| 			if (range_start_ < 0) range_start_ = 0; | ||||
| 			if (range_start_ > ushort.max) range_start_ = 0; | ||||
| 
 | ||||
| 			if (range_end_ < range_start_) range_end_ = 0; | ||||
| 			if (range_end_ > ushort.max) range_end_ = 0; | ||||
| 
 | ||||
| 			if (mode_ == ViewMode.Memory) { | ||||
| 				/* | ||||
| 				foreach (ref ubyte[] b; emu_.ram[0x200 + range_start_ .. range_end_].chunks(2)) { | ||||
| 					ushort opcode = (*(cast(ushort*)(b.ptr))); | ||||
| 					igText("0x%04X", opcode); | ||||
| 				} | ||||
| 				*/ | ||||
| 			} else if (mode_ == ViewMode.Disassembly) { | ||||
| 
 | ||||
| 				int cur_offset = range_start_; | ||||
| 				foreach (ref instr; disassembly_) { | ||||
| 
 | ||||
| 					if (emu_.cpu.pc == cur_offset) { | ||||
| 						igTextColored(ImColor(0, 255, 0), "0x%03X |", cur_offset); | ||||
| 						cur_offset += 2; | ||||
| 						igSameLine(); | ||||
| 					} else { | ||||
| 						igText("0x%03X |", cur_offset); | ||||
| 						cur_offset += 2; | ||||
| 						igSameLine(); | ||||
| 					} | ||||
| 
 | ||||
| 					switch  (instr.args) { | ||||
| 						case 3: | ||||
| 
 | ||||
| 							igText("%s", instr.op); | ||||
| 							igSameLine(); | ||||
| 
 | ||||
| 							Arg a1_arg; | ||||
| 							auto a1 = instr.a1(a1_arg); | ||||
| 							igText(formatQual(a1_arg, true), a1); | ||||
| 							igSameLine(); | ||||
| 
 | ||||
| 							Arg a2_arg; | ||||
| 							auto a2 = instr.a2(a2_arg); | ||||
| 							igText(formatQual(a2_arg, true), a2); | ||||
| 							igSameLine(); | ||||
| 
 | ||||
| 							Arg a3_arg; | ||||
| 							auto a3 = instr.a3(a3_arg); | ||||
| 							igText(formatQual(a3_arg), a3); | ||||
| 
 | ||||
| 							break; | ||||
| 
 | ||||
| 						case 2: | ||||
| 							 | ||||
| 							igText("%s", instr.op); | ||||
| 							igSameLine(); | ||||
| 
 | ||||
| 							Arg a1_arg; | ||||
| 							auto a1 = instr.a1(a1_arg); | ||||
| 							igText(formatQual(a1_arg, true), a1); | ||||
| 							igSameLine(); | ||||
| 
 | ||||
| 							Arg a2_arg; | ||||
| 							auto a2 = instr.a2(a2_arg); | ||||
| 							igText(formatQual(a2_arg), a2); | ||||
| 							 | ||||
| 							break; | ||||
| 
 | ||||
| 						case 1: | ||||
| 
 | ||||
| 							igText("%s", instr.op); | ||||
| 							igSameLine(); | ||||
| 
 | ||||
| 							Arg a1_arg; | ||||
| 							auto a1 = instr.a1(a1_arg); | ||||
| 							igText(formatQual(a1_arg), a1); | ||||
| 							 | ||||
| 							break; | ||||
| 
 | ||||
| 						case 0: | ||||
| 
 | ||||
| 							igText("%s", instr.op); | ||||
| 							break; | ||||
| 						 | ||||
| 						default: | ||||
| 					writefln("unknown opcode: 0x%x", opcode); | ||||
| 							break; | ||||
| 
 | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 		return ""; | ||||
| 			igEnd(); | ||||
| 
 | ||||
| 	} // dissassemble
 | ||||
| 		} // draw
 | ||||
| 
 | ||||
| } // Assembler
 | ||||
| 
 | ||||
| struct Chip8Status { | ||||
| 	} // Assembly
 | ||||
| 
 | ||||
| 	// emu ptr
 | ||||
| 	Emulator* run_; | ||||
| 	Chip8* emu_; | ||||
| 
 | ||||
| 	// assembly/disassembly
 | ||||
| 	Assembly ass_; | ||||
| 
 | ||||
| 	// loaded program
 | ||||
| 	const (char)* loaded_program; | ||||
| 
 | ||||
|  | @ -568,6 +226,7 @@ struct Chip8Status { | |||
| 	void initialize(Emulator* run, Chip8* emu) { | ||||
| 		this.run_ = run; | ||||
| 		this.emu_ = emu; | ||||
| 		ass_.initialize(emu); | ||||
| 	} // initialize
 | ||||
| 
 | ||||
| 	alias Callback = float delegate(int idx, const char** out_text); | ||||
|  | @ -598,6 +257,7 @@ struct Chip8Status { | |||
| 		loaded_program = null; | ||||
| 
 | ||||
| 		emu_.reset(); | ||||
| 		ass_.reset(); | ||||
| 		 | ||||
| 	} // resetShortcut
 | ||||
| 
 | ||||
|  | @ -608,6 +268,8 @@ struct Chip8Status { | |||
| 		loaded_program = "chip8_picture.ch8"; | ||||
| 		auto buf = read("programs/chip8_picture.ch8"); | ||||
| 		emu_.load(0x200, buf); // do ze load yes, will copy all the data in
 | ||||
| 		ass_.setRange(cast(ushort)0x200, cast(ushort)(0x200 + cast(ushort)buf.length)); | ||||
| 		ass_.reset(); | ||||
| 
 | ||||
| 	} // loadShortcut
 | ||||
| 
 | ||||
|  | @ -637,6 +299,8 @@ struct Chip8Status { | |||
| 
 | ||||
| 	void draw() { | ||||
| 
 | ||||
| 		ass_.draw(); | ||||
| 
 | ||||
| 		if (!status_menu_) return; | ||||
| 
 | ||||
| 		if (igBeginMainMenuBar()) { | ||||
|  | @ -768,29 +432,48 @@ 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); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| struct Chip8 { | ||||
| 
 | ||||
| 	ubyte[80] chip8_fontset = [ | ||||
| 		0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
 | ||||
| 		0x20, 0x60, 0x20, 0x20, 0x70, // 1
 | ||||
| 		0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
 | ||||
| 		0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
 | ||||
| 		0x90, 0x90, 0xF0, 0x10, 0x10, // 4
 | ||||
| 		0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
 | ||||
| 		0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
 | ||||
| 		0xF0, 0x10, 0x20, 0x40, 0x40, // 7
 | ||||
| 		0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
 | ||||
| 		0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
 | ||||
| 		0xF0, 0x90, 0xF0, 0x90, 0x90, // A
 | ||||
| 		0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
 | ||||
| 		0xF0, 0x80, 0x80, 0x80, 0xF0, // C
 | ||||
| 		0xE0, 0x90, 0x90, 0x90, 0xE0, // D
 | ||||
| 		0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
 | ||||
| 		0xF0, 0x80, 0xF0, 0x80, 0x80  // F
 | ||||
| 	]; | ||||
| 
 | ||||
| 	alias OpCode = ushort; | ||||
| 	alias ProgramCounter = ushort; | ||||
| 	alias Memory = ubyte[4096]; | ||||
|  | @ -824,7 +507,6 @@ struct Chip8 { | |||
| 	KeyPad kp; | ||||
| 
 | ||||
| 	ubyte[64*32] screen_buf; | ||||
| 	ubyte[3][64*32] screen_data; | ||||
| 
 | ||||
| 	bool run_flag; | ||||
| 	bool draw_flag; | ||||
|  | @ -836,8 +518,18 @@ struct Chip8 { | |||
| 
 | ||||
| 		ram[offset .. offset + data.length] = cast(ubyte[])data[]; | ||||
| 
 | ||||
| 		loadFont(); // again
 | ||||
| 
 | ||||
| 	} // load
 | ||||
| 
 | ||||
| 	void loadFont() { | ||||
| 
 | ||||
| 		foreach (i, b; chip8_fontset) { | ||||
| 			ram[i] = b; | ||||
| 		} | ||||
| 
 | ||||
| 	} // loadFont
 | ||||
| 
 | ||||
| 	void reset() { | ||||
| 			 | ||||
| 		reset_flag = true; | ||||
|  | @ -850,9 +542,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 +626,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 +648,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 +659,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 +671,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 +689,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; | ||||
|  | @ -1032,7 +722,7 @@ struct Chip8 { | |||
| 
 | ||||
| 				import std.random : uniform; | ||||
| 				ubyte x = cpu.opcode.x; | ||||
| 				cpu.v[x] = uniform(Register.min, Register.max) & (cpu.opcode & 0x00FF); | ||||
| 				cpu.v[x] = uniform(Register.min, Register.max) & x; | ||||
| 				break; | ||||
| 
 | ||||
| 			// 0xDXYN
 | ||||
|  | @ -1043,14 +733,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) { | ||||
|  | @ -1083,6 +773,7 @@ struct Chip8 { | |||
| 
 | ||||
| 					case 0x0001: // 0xEXA1 Skips the next instruction if the key stored in VX isn't pressed.
 | ||||
| 						writefln("0xEXA1: skip instruction if VX not pressed: %x", key); | ||||
| 						pc_target += 2; | ||||
| 						break; | ||||
| 
 | ||||
| 					default: //unhandled for some reason
 | ||||
|  | @ -1124,9 +815,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 * 0x05; | ||||
| 						cpu.i = char_addr; | ||||
| 						break; | ||||
| 
 | ||||
|  | @ -1135,7 +826,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 +1011,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,424 @@ | |||
| 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 : const (char)* { | ||||
| 
 | ||||
| 		UNK = "UNK", // UNKNOWN
 | ||||
| 
 | ||||
| 		CLS = "CLS", // CLS
 | ||||
| 		RET = "RET", // RET
 | ||||
| 		CALL = "CALL", // CALL addr
 | ||||
| 
 | ||||
| 		ADD = "ADD", // ADD Vx, Vy
 | ||||
| 			 // ADD Vx, byte
 | ||||
| 			 // ADD I, Vx
 | ||||
| 
 | ||||
| 		SUB = "SUB", // SUB Vx, Vy
 | ||||
| 			 // SUB Vx, byte
 | ||||
| 
 | ||||
| 		SUBN = "SUBN", // SUBN Vx, Vy
 | ||||
| 
 | ||||
| 		SE = "SE", // SE Vx, Vy
 | ||||
| 			// SE Vx, byte
 | ||||
| 
 | ||||
| 		SNE = "SNE", // SNE Vx, byte
 | ||||
| 		SKP = "SKP", // SKP Vx
 | ||||
| 		SKNP = "SKNP", // SKNP Vx
 | ||||
| 
 | ||||
| 		SHL = "SHL", // SHL Vx {, Vy}
 | ||||
| 		SHR = "SHR", // SHR Vx {, Vy}
 | ||||
| 		XOR = "XOR", // XOR Vx, Vy
 | ||||
| 		AND = "AND", // AND Vx, Vy
 | ||||
| 		OR = "OR", // OR Vx, Vy
 | ||||
| 
 | ||||
| 		LD = "LD", // LD Vx, DT
 | ||||
| 			// LD DT, Vx
 | ||||
| 			// LD ST, Vx
 | ||||
| 			// LD Vx, K
 | ||||
| 			// LD [I], Vx
 | ||||
| 			// LD Vx, [I]
 | ||||
| 		 | ||||
| 		RND = "RND", // RND Vx, byte,
 | ||||
| 
 | ||||
| 		JP = "JP", // JP addr
 | ||||
| 			// JP V0, addr
 | ||||
| 		 | ||||
| 		DRW = "DRW", // DRW Vx, Vy, nibble
 | ||||
| 
 | ||||
| 		SYS = "SYS" // SYS addr | probably unused? but still
 | ||||
| 		 | ||||
| 	} | ||||
| 
 | ||||
| 	enum Argument { | ||||
| 
 | ||||
|         Nil, | ||||
| 
 | ||||
| 		Vx, | ||||
| 		Vy, | ||||
| 		V0, | ||||
| 
 | ||||
| 		I, | ||||
| 		I_addr, | ||||
| 		DT, | ||||
| 		ST, | ||||
| 		K, | ||||
| 		B, | ||||
| 		F, | ||||
| 		 | ||||
| 		beit, | ||||
| 		nibble, | ||||
| 		addr, | ||||
| 		 | ||||
| 	} | ||||
| 
 | ||||
| 	struct Instruction { | ||||
| 
 | ||||
| 		@property { | ||||
| 			ubyte vx() { | ||||
| 				return (v_ & 0x0F00) >> 8; | ||||
| 			} | ||||
| 
 | ||||
| 			ubyte vy() { | ||||
| 				return (v_ & 0x00F0) >> 4; | ||||
| 			} | ||||
| 
 | ||||
| 			ubyte n() { | ||||
| 				return (v_ & 0x000F); | ||||
| 			} | ||||
| 
 | ||||
| 			ubyte nn() { | ||||
| 				return (v_ & 0x00FF); | ||||
| 			} | ||||
| 
 | ||||
| 			ushort nnn() { | ||||
| 				return (v_ & 0x0FFF); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		private { | ||||
| 
 | ||||
| 			ushort v_; // raw value
 | ||||
| 
 | ||||
| 			OpCode op_; | ||||
| 			Argument a1_; | ||||
| 			Argument a2_; | ||||
| 			Argument a3_; | ||||
| 			 | ||||
| 		} | ||||
| 
 | ||||
| 		void* extract(Argument a) { | ||||
| 			final switch (a) with (Argument) { | ||||
| 
 | ||||
| 				case Vx: return cast(void*)(vx()); | ||||
| 				case Vy: return cast(void*)(vy()); | ||||
| 				case V0: return cast(void*)(0);  | ||||
| 
 | ||||
| 				case I: return cast(void*)("I".ptr); | ||||
| 				case I_addr: return cast(void*)("[I]".ptr); | ||||
| 
 | ||||
| 				case DT: return cast(void*)("DT".ptr); | ||||
| 				case ST: return cast(void*)("ST".ptr); | ||||
| 
 | ||||
| 				case B: return cast(void*)("B".ptr); | ||||
| 				case F: return cast(void*)("F".ptr); | ||||
| 				case K: return cast(void*)("K".ptr); | ||||
| 
 | ||||
| 				case nibble: return cast(void*)(n()); | ||||
| 				case beit: return cast(void*)(nn()); | ||||
| 				case addr: return cast(void*)(nnn()); | ||||
| 				 | ||||
| 				case Nil: return cast(void*)(0); | ||||
| 
 | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		@property | ||||
| 		OpCode op() { | ||||
| 			return op_; | ||||
| 		} | ||||
| 
 | ||||
| 		void* a1(out Argument a) { | ||||
| 			a = a1_; | ||||
| 			return extract(a1_); | ||||
| 		} | ||||
| 
 | ||||
| 		void* a2(out Argument a) { | ||||
| 			a = a2_; | ||||
| 			return extract(a2_); | ||||
| 		} | ||||
| 
 | ||||
| 		void* a3(out Argument a) { | ||||
| 			a = a3_; | ||||
| 			return extract(a3_); | ||||
| 		} | ||||
| 
 | ||||
| 		@property int args() { | ||||
| 			int args = 0; | ||||
| 			args += (a1_ != Argument.Nil); | ||||
| 			args += (a2_ != Argument.Nil); | ||||
| 			args += (a3_ != Argument.Nil); | ||||
| 			return args; | ||||
| 		} | ||||
| 
 | ||||
| 		this(ushort v, OpCode op, Argument a) { | ||||
| 			v_ = v; | ||||
| 			op_ = op; | ||||
| 			a1_ = a; | ||||
| 		} | ||||
| 		 | ||||
| 		this(ushort v, OpCode op, Argument a1, Argument a2) { | ||||
| 			v_ = v; | ||||
| 			op_ = op; | ||||
| 			a1_ = a1; | ||||
| 			a2_ = a2; | ||||
| 		} | ||||
| 		 | ||||
| 		this(ushort v, OpCode op, Argument a1, Argument a2, Argument a3) { | ||||
| 			v_ = v; | ||||
| 			op_ = op; | ||||
| 			a1_ = a1; | ||||
| 			a2_ = a2; | ||||
| 			a3_ = a3; | ||||
| 		} | ||||
| 
 | ||||
| 		this(ushort v, OpCode op) { | ||||
| 			v_ = v; | ||||
| 			op_ = op; | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	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
 | ||||
| 
 | ||||
| 	static Instruction[] disassemble(ubyte[] instructions) { | ||||
| 
 | ||||
| 		import std.range : chunks; | ||||
| 
 | ||||
| 		auto instr_output = Appender!(Instruction[])(); | ||||
| 
 | ||||
| 		foreach (ref ubyte[] i; instructions.chunks(2)) { | ||||
| 
 | ||||
| 			if (i.length != 2) break; | ||||
| 
 | ||||
| 			ushort opcode = i[0] << 8 | i[1]; | ||||
| 
 | ||||
| 			switch (opcode & 0xF000) { | ||||
| 				case 0x0000: | ||||
| 					switch (opcode & 0x0FFF) { | ||||
| 						case 0x00E0: // 0x00E0 Clears the screen. | CLS
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.CLS); | ||||
| 							break; | ||||
| 						case 0x00EE: // 0x00EE Returns from a subroutine. | RET
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.RET); | ||||
| 							break; | ||||
| 						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!");
 | ||||
| 							// instr_output ~= Instruction(opcode, OpCode.SYS, Argument.addr);
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.UNK); | ||||
| 							break; | ||||
| 					} | ||||
| 					break; | ||||
| 				case 0x1000: // 0x1NNN Jumps to address NNN. | JP addr
 | ||||
| 					instr_output ~= Instruction(opcode, OpCode.JP, Argument.addr); | ||||
| 					break; | ||||
| 				case 0x2000: // 0x2NNN Calls subroutine at NNN. | CALL addr
 | ||||
| 					instr_output ~= Instruction(opcode, OpCode.CALL, Argument.addr); | ||||
| 					break; | ||||
| 				case 0x3000: // 0x3XNN Skips the next instruction if VX equals NN. | SE Vx, byte
 | ||||
| 					instr_output ~= Instruction(opcode, OpCode.SE, Argument.Vx, Argument.beit); | ||||
| 					break; | ||||
| 				case 0x4000: // 0x4XNN Skips the next instruction if VX doesn't equal NN. | SNE Vx, byte
 | ||||
| 					instr_output ~= Instruction(opcode, OpCode.SNE, Argument.Vx, Argument.beit); | ||||
| 					break; | ||||
| 				case 0x5000: // 0x5XYO Skips the next instruction if VX equals VY. | SE Vx, Vy
 | ||||
| 					instr_output ~= Instruction(opcode, OpCode.SE, Argument.Vx, Argument.Vy); | ||||
| 					break; | ||||
| 				case 0x6000: // 0x6XNN Sets VX to NN. | LD Vx, byte
 | ||||
| 					instr_output ~= Instruction(opcode, OpCode.LD, Argument.Vx, Argument.beit); | ||||
| 					break; | ||||
| 				case 0x7000: // 0x7XNN Adds NN to VX. | ADD Vx, byte
 | ||||
| 					instr_output ~= Instruction(opcode, OpCode.ADD, Argument.Vx, Argument.beit); | ||||
| 					break; | ||||
| 				case 0x8000: | ||||
| 					switch (opcode) { | ||||
| 						case 0x0000: // 0x8XY0 Sets VX to the value of VY. | LD Vx, Vy
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.LD, Argument.Vx, Argument.Vy); | ||||
| 							break; | ||||
| 						case 0x0001: // 0x8XY1 Sets VX to VX or VY. | OR Vx, Vy
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.OR, Argument.Vx, Argument.Vy); | ||||
| 							break; | ||||
| 						case 0x0002: // 0x8XY2	Sets VX to VX and VY. | AND Vx, Vy
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.AND, Argument.Vx, Argument.Vy); | ||||
| 							break; | ||||
| 						case 0x0003: // 0x8XY3 Sets VX to VX xor VY. | XOR Vx, Vy
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.XOR, Argument.Vx, Argument.Vy); | ||||
| 							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. | ADD Vx, Vy
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.ADD, Argument.Vx, Argument.Vy); | ||||
| 							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. | SUB Vx, Vy
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.SUB, Argument.Vx, Argument.Vy); | ||||
| 							break; | ||||
| 						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}
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.SHR, Argument.Vx); // FIXME? what about Vy here actually?
 | ||||
| 							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. | SUBN Vx, Vy
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.SUBN, Argument.Vx, Argument.Vy); | ||||
| 							break; | ||||
| 						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}
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.SHL, Argument.Vx); // FIXME? what about Vy here actually?
 | ||||
| 							break; | ||||
| 						default: // unhandled for some reason
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.UNK); | ||||
| 							writefln("unknown opcode: 0x%x", opcode); | ||||
| 							break; | ||||
| 					} | ||||
| 					break; | ||||
| 				case 0x9000: // 0x9XYO Skips the next instruction if VX doesn't equal VY. | SNE Vx, Vy
 | ||||
| 					instr_output ~= Instruction(opcode, OpCode.SNE, Argument.Vx, Argument.Vy); | ||||
| 					break; | ||||
| 				case 0xA000: // 0xANNN  Sets I to the address NNN. | LD I, addr
 | ||||
| 					instr_output ~= Instruction(opcode, OpCode.LD, Argument.I, Argument.addr); | ||||
| 					break; | ||||
| 				case 0xB000: // 0xBNNN Jumps to the address NNN plus V0. | JP V0, addr
 | ||||
| 					instr_output ~= Instruction(opcode, OpCode.JP, Argument.V0, Argument.addr); | ||||
| 					break; | ||||
| 				case 0xC000: // 0xCXNN Sets VX to the result of a bitwise and operation on a random number and NN. | RND Vx, byte
 | ||||
| 					instr_output ~= Instruction(opcode, OpCode.RND, Argument.Vx, Argument.beit); | ||||
| 					break; | ||||
| 
 | ||||
| 				// 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: | ||||
| 					instr_output ~= Instruction(opcode, OpCode.DRW, Argument.Vx, Argument.Vy, Argument.beit); | ||||
| 					break; | ||||
| 				case 0xE000: | ||||
| 					switch (opcode & 0x000F) { | ||||
| 						case 0x000E: // 0xEX9E Skips the next instruction if the key stored in VX is pressed. | SKP Vx
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.SKP, Argument.Vx); | ||||
| 							break; | ||||
| 						case 0x0001: // 0xEXA1 Skips the next instruction if the key stored in VX isn't pressed. | SKNP Vx
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.SKNP, Argument.Vx); | ||||
| 							break; | ||||
| 						default: // unhandled for some reason
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.UNK); | ||||
| 							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
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.LD, Argument.Vx, Argument.DT); | ||||
| 							break; | ||||
| 						case 0x000A: // 0xFX0A A key press is awaited, and then stored in VX. | LD Vx, K
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.LD, Argument.Vx, Argument.K); | ||||
| 							break; | ||||
| 						case 0x0015: // 0xFX15 Sets the delay timer to VX. | LD DT, Vx
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.LD, Argument.DT, Argument.Vx); | ||||
| 							break; | ||||
| 						case 0x0018: // 0xFX18 Sets the sound timer to VX. | LD ST, Vx
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.LD, Argument.ST, Argument.Vx); | ||||
| 							break; | ||||
| 						case 0x001E: // 0xFX1E Adds VX to I. | ADD I, Vx
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.ADD, Argument.I, Argument.DT); | ||||
| 							break; | ||||
| 						case 0x0029: // 0xFX29 Sets I to the location of the sprite for the character in VX. | LD F, Vx
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.LD, Argument.F, Argument.DT); | ||||
| 							break; | ||||
| 						// 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
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.LD, Argument.B, Argument.Vx); | ||||
| 							break; | ||||
| 						case 0x0055: // 0xFX55 Stores V0 to VX in memory starting at address I. | LD [I], Vx
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.LD, Argument.I_addr, Argument.DT); | ||||
| 							break; | ||||
| 						case 0x0065: // 0xFX65 Fills V0 to VX with values from memory starting at address I. | LD Vx, [I]
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.LD, Argument.Vx, Argument.I_addr); | ||||
| 							break; | ||||
| 						default: // unhandled for some reason
 | ||||
| 							instr_output ~= Instruction(opcode, OpCode.UNK); | ||||
| 							writefln("unknown opcode: 0x%x", opcode); | ||||
| 							break; | ||||
| 					} | ||||
| 					break; | ||||
| 				default: | ||||
| 					instr_output ~= Instruction(opcode, OpCode.UNK); | ||||
| 					writefln("unknown opcode: 0x%x", opcode); | ||||
| 			} | ||||
| 		} | ||||
| 		 | ||||
| 		return instr_output.data; | ||||
| 
 | ||||
| 	} // 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