Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can I use this on my project #4

Closed
leap0x7b opened this issue Oct 25, 2021 · 6 comments
Closed

How can I use this on my project #4

leap0x7b opened this issue Oct 25, 2021 · 6 comments
Labels

Comments

@leap0x7b
Copy link

Hi, I'm making a emulator for my hobby computer using the 6502 CPU. I'm using this library for my emulator CPU emulation. The only thing is, I don't know how to use this. Do I need to power the CPU on and then run the CPU with specified cycles?

@redcode
Copy link
Owner

redcode commented Oct 30, 2021

Yes. It's very easy. The m6502_power() sets the registers to the values they would be after a real CPU is powered ON (or OFF).
Passing state as TRUE initializes the registers with the values after power ON. Passing FALSE clears the registers (that's good to show them as 0 in a debugger, for example).

The following example emulates a fictional machine. A linear video framebuffer is assumed to be located at address F000h, with indexed colors, palette of 16 colors, 1 byte per index. The machine produces an IRQ during the VBLANK interval:

#define MACHINE_SCREEN_WIDTH        352
#define MACHINE_SCREEN_HEIGHT       296
#define MACHINE_VISIBLE_SCANLINES   MACHINE_SCREEN_HEIGHT
#define MACHINE_VBLANK_SCANLINES    16
#define MACHINE_VRAM_ADDRESS	    0xF000

#define MACHINE_CYCLES_PER_FRAME    69888
#define MACHINE_CYCLES_PER_IRQ	    32

#define MACHINE_CYCLES_PER_SCANLINE \
	(MACHINE_CYCLES_PER_FRAME / (MACHINE_VISIBLE_SCANLINES + MACHINE_VBLANK_SCANLINES))

#define MACHINE_CYCLES_PER_VBLANK \
	(MACHINE_CYCLES_PER_SCANLINE * MACHINE_VBLANK_SCANLINES)

#define MACHINE_CYCLES_AT_IRQ               24
#define MACHINE_CYCLES_AT_IRQ_END           (MACHINE_CYCLES_AT_IRQ + MACHINE_CYCLES_PER_IRQ)
#define MACHINE_CYCLES_AT_VISIBLE_SCANLINES MACHINE_CYCLES_PER_VBLANK


#define RGBA(r, g, b) 0x##FF##b##g##r

zuint32 const color_palette[16] = {
	RGBA(00, 00, 00), RGBA(00, 00, D7), RGBA(D7, 00, 00), RGBA(D7, 00, D7),
	RGBA(00, D7, 00), RGBA(00, D7, D7), RGBA(D7, D7, 00), RGBA(D7, D7, D7),
	RGBA(00, 00, 00), RGBA(00, 00, FF), RGBA(FF, 00, 00), RGBA(FF, 00, FF),
	RGBA(00, FF, 00), RGBA(00, FF, FF), RGBA(FF, FF, 00), RGBA(FF, FF, FF)
};


typedef struct {
	M6502 cpu;
	zuint8 memory[65535];
	zusize frame_cycles;
	zuint32 *video_output_buffer;
} Machine;


zuint8 cpu_read(Machine *self, zuint16 address)
	{return self->memory[address];}


void cpu_write(Machine *self, zuint16 address, zuint8 value)
	{self->memory[address] = value;}


void machine_initialize(Machine *self, zuint32 *video_output_buffer)
	{
	self->cpu.context = self;
	self->cpu.read = cpu_read;
	self->cpu.write = cpu_write;
	m6502_power(&self->cpu, TRUE);
	memset(self->memory, 0, 65535);
	self->video_output_buffer = video_output_buffer;
	self->cycles = 0;
	}


void machine_run_frame(Machine *self)
	{
	zuint    y, x;
	zuint8*  vram_input = &self->memory[MACHINE_VRAM_ADDRESS];
	zuint32* video_output = self->video_output_buffer;

	/* VBLANK before IRQ */
	self->cycles += m6502_run(&self->cpu, MACHINE_CYCLES_AT_IRQ - self->cycles);

	/* IRQ */
	m6502_irq(&self->cpu, TRUE);
	self->cycles += m6502_irq(&self->cpu, MACHINE_CYCLES_AT_IRQ_END - self->cycles);
	m6502_irq(&self->cpu, FALSE);

	/* VBLANK after IRQ */
	self->cycles += m6502_run(&self->cpu, MACHINE_CYCLES_AT_VISIBLE_SCANLINES - self->cycles);

	/* Visible scanlines */

	for (y = 0; y < MACHINE_VISIBLE_SCANLINES; y++)
		{
		self->cycles = m6502_run(
			&self->cpu,
			MACHINE_CYCLES_AT_VISIBLE_SCANLINES     +
			((y + 1) * MACHINE_CYCLES_PER_SCANLINE) -
			self->cycles);

		/* Draw scanline pixels in the output buffer */
		for (x = 0; x < MACHINE_SCREEN_WIDTH; x++)
			*video_output++ = color_palette[*vram_input++];
		}

	self->cycles -= MACHINE_CYCLES_PER_FRAME;
	}


int main(int argc, char *argv)
	{
	zuint32 video_buffer[MACHINE_SCREEN_WIDTH * MACHINE_SCREEN_HEIGHT];
	Machine machine;

	machine_initialize(&machine, video_buffer);

	while (1)
		{
		machine_run_frame(&machine);

		/* Draw frame */
		...
		}

	return 0;
	}

@redcode
Copy link
Owner

redcode commented Oct 30, 2021

If you have more doubts, feel free to join my Discord server:
https://discord.gg/NeTytxBh
The link will be valid for 7 days.

@leap0x7b
Copy link
Author

leap0x7b commented Nov 4, 2021

main.c:35:15: error: invalid operands to binary expression ('size_t' (aka 'unsigned long') and 'void')
     self->cycles += m6502_irq(&self->cpu, MACHINE_CYCLES_AT_IRQ_END - self->cycles);
     ~~~~~~~~~~~~ ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

@waltje
Copy link

waltje commented Mar 26, 2023

This code could indeed use a "simple emulator" framework that can be used with the CPU backend. Possibly this could also be used for the Z80 backend, as they function similarly.

@redcode
Copy link
Owner

redcode commented Mar 26, 2023

I have another example for the Z80 documentation: https://zxe.io/software/Z80/documentation/latest/Usage.html

The documentation is not finished yet, but it will be soon.

@waltje
Copy link

waltje commented Mar 26, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants