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

Combine with gbit and pass all the test #15

Merged
merged 5 commits into from
Jan 25, 2021
Merged

Combine with gbit and pass all the test #15

merged 5 commits into from
Jan 25, 2021

Conversation

RinHizakura
Copy link
Collaborator

@RinHizakura RinHizakura commented Jan 20, 2021

Finally, after this pull request, jitboy can now pass all instruction tests on gbit!

image

I think I should try to explain some detail here since many small tricks are applied to get over the jitboy's limitation.

  • Although gbit says that we should reference our emulator's memory to instruction_mem but not copy the contents. It will cause some errors if we directly do this on jitboy. Adding some extra codes may help, but it takes time to do this. So I come up with another solution: When executing the callback function mycpu_set_state in test_cpu.c, it will adjust vm->memory.mem to meet the content that gbit wants. So we can do gbit test correctly although this solution can lead to more executing time.
  • For jitboy, the implementation of those instructions is not expected to execute one by one, which is how gbit tests the instructions. If we want to force jitboy to do this, we can't always get the correct pc on the JIT codes' return. That's why line 181 in test_cpu.c is there for "faking" the correct pc
  • In gbit, mymmu_write is expected to be called every time we change the content in memory. But jitboy may sometimes just write memory by jit code and doesn't go through any C code function. To also call mymmu_write for gbit, you can see that some extra write_byte macro will be used.
  • 0xfc and 0xfd are two invalid opcodes in Game Boy, and I borrow them for the status flag's setting and loading. So we can set the state and also retrieve it back as gbit wishes.

How to run gbit

$ make gbit
$ ./build/gbit.out

Copy link
Contributor

@jserv jserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not copy GBIT source files. Instead, use git submodule and apply necessary patches if any.

Copy link
Contributor

@jserv jserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain the instruction test in the top-level README.md

Makefile Outdated Show resolved Hide resolved
Makefile Outdated Show resolved Hide resolved
gbit_src/gbit.c Outdated Show resolved Hide resolved
src/dasm_macros.inc Outdated Show resolved Hide resolved
.gitignore Outdated Show resolved Hide resolved
src/emit.dasc Outdated Show resolved Hide resolved
Makefile Outdated Show resolved Hide resolved
src/memory.h Outdated Show resolved Hide resolved
Makefile Outdated Show resolved Hide resolved
Makefile Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
tests/test_cpu.c Outdated Show resolved Hide resolved
tests/test_cpu.c Outdated Show resolved Hide resolved
@RinHizakura
Copy link
Collaborator Author

RinHizakura commented Jan 21, 2021

Now the code emission for the instruction tester is put in compile_and_run. But you can see that it takes many pointers for returning value. I'll try to find out if there's a better solution.

README.md Outdated Show resolved Hide resolved
src/gbz80.c Outdated Show resolved Hide resolved
src/gbz80.c Outdated Show resolved Hide resolved
src/gbz80.c Show resolved Hide resolved
tests/test_cpu.h Outdated Show resolved Hide resolved
@RinHizakura
Copy link
Collaborator Author

More comments are added for the instruction tester now.

Makefile Outdated
@@ -19,12 +19,30 @@ else
endif

OUT ?= build
SHELL_HACK := $(shell mkdir -p $(OUT))
INSTR_TEST_OUT ?= build/tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace INSTR_TEST_OUT ?= build/tests with INSTR_TEST_OUT := $(OUT)/tests because it was not intended to be set directly.

README.md Outdated
@@ -597,6 +597,29 @@ one of these RAM banks would be chosen to mapped at `0xA000` to `0xBFFF` by MBC,
state can be restored. When closing jitboy, contents in RAM banks should be copied to the file
with the name we mentioned before.

## Instruction test
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change it to "Instruction compliance tests"

README.md Outdated
However, some limitations on jitboy cause problems to integrate with it. So we need to apply
some approaches to get over them.

* For jitboy, instructions are not expected to be executed one by one, which is how the tester
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The statement "For jitboy, instructions are not expected to be executed one by one, which is how the tester tests the instructions." is not informative. Instead, you can say, "Due to the JIT compilation, not each instruction is executed at one time."

README.md Outdated
write the same value in the same position again.
* For instructions that write two bytes, another macro `ld16` will be used for `gbz80_mmu_write`
calling
* We borrow `0xfc` and `0xfd` which are the two invalid opcodes in Game Boy for the status
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should explain how the original opcodes work and why you picked them up.

src/gbz80.c Outdated
* build it at the init stage and then cache it for reusing to avoid spending
* time on avoidable codegen.
*/

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't leave the blank line. The comment should be very close to the corresponding implementation.

@jserv
Copy link
Contributor

jserv commented Jan 23, 2021

By the way, we can make use of GitHub Actions for CI/CD after the pull request is merged.

Makefile Outdated
$(INSTR_TEST_OUT)/%.o: tests/%.c
$(CC) -o $@ -c $(CFLAGS) $< -MMD -MF $@.d

$(INSTR_TEST_OUT)/%.o: src/%.c
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dislike the build rules on src directory. Can you eliminate them? The dependency of the source files in src directory should be already scratched.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confusing about this. Since if this rule is removed, then we can't build the target for the instruction test.

Note that I have separated the object files for jitboy and the instruction tester for the INSTRUCTION_TEST definition, although some of the object files are the same. And this is for if someone executes make all before make check, he/she can still retrieve the correct build target. Please tell me if my approach is not good for solving this problem.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not necessary to create a new directory $(INSTR_TEST_OUT). You can use String Substitution to filter-out and replace the names of object files. e.g. build/emit-instr-test.o Of course, you have to generate the corresponding build/emit-instr-test.c

Copy link
Collaborator Author

@RinHizakura RinHizakura Jan 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we don't need emit-instr-test.c? Since the file emit.c which is generated by DynASM hasn't been preprocessed, it will be fine to share the same emit.c between jitboy and instruction tester binary?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My idea is to share the built objects as possible as we can. We can benefit from shorter build time and cleaner rules. However, we have to consider the scenario when instruction tester is about to be built right after target "all" is done.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. But now I stuck on trading off between cleaner rules and the ability to share the built objects since I'm not so familiar with Makefile. I'll try to figure out the better approach and asking you to review it after that. 😢

tests/test_cpu.c Outdated Show resolved Hide resolved
src/gbz80.h Outdated
@@ -11,4 +11,24 @@ bool compile(gb_block *block,

bool optimize_block(GList **instructions, int opt_level);

#ifdef INSTRUCTION_TEST
struct inst_info {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use instr_info rather than inst_info.
inst is a common term. See: https://en.wikipedia.org/wiki/Inst

printf(" -h, --help Show this help.\n");
}

static int parse_args(int argc, char **argv)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use bool instead of plain int.

build_load_flag_block(load_flag_block);
}

void gbz80_close()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declare this function as "static."

src/gbz80.c Outdated
gbz80_inst *inst = g_new(gbz80_inst, 1);

uint8_t opcode = mem->mem[pc];
bool iscb = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is_cb would be better for readability.

GList *instructions = NULL;

/* run the fake instruction for flag setting */
gbz80_inst *inst_before = g_new(gbz80_inst, 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Later, you can submit a new pull request for gbz80_instr name scheme consistency. It was my fault.

static gb_vm *vm = NULL;

/* since jitboy only update pc when doing instruction jmp/call
* so we trickly use a fake one and count it independently to pass gbit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wording "a fake one" might be misleading. You can say, "introduce an indirect layer for memory/register manipulation."

|| gbz80_mmu_write(addr + 1, (value >> 8) & 0xff);
|| }


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cut this blank line.

src/gbz80.c Outdated
#ifdef INSTRUCTION_TEST
/* To do code generating for flag setting and flag reloading which are
* required for the instruction tester. Opcodes `0xfc` and `0xfd` which
* are invalid opcodes in original Game Boy are borrowed for the purpose */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace "borrowed" with "diverted."

uint16_t ret = block->func(&vm->state);

/* Due to the JIT compilation, not each instruction is executed at one time,
* which is how the tester tests the instructions. If we want to do this,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace "which is how the tester tests the instructions" with "that violates the expectation of instructions tester."

Makefile Show resolved Hide resolved
*/
static uint16_t pc;
static uint8_t flag;
/* flag_args is used for carry the arguments for initial status flag
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Append a new line for pretty print.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix the grammar error in the above comment.

/* the block for flag loading will be created at the initial and then cached */
static gb_block *load_flag_block;


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this blank line.

src/gbz80.h Outdated
@@ -11,4 +11,24 @@ bool compile(gb_block *block,

bool optimize_block(GList **instructions, int opt_level);

#ifdef INSTRUCTION_TEST
struct instr_info {
bool iscb;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename iscb to is_cb or is_codeblock.

@jserv jserv merged commit aeb7aa5 into sysprog21:master Jan 25, 2021
@jserv jserv mentioned this pull request Jan 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants