 |
forums.ps2dev.org Homebrew PS2, PSP & PS3 Development Discussions
|
| View previous topic :: View next topic |
| Author |
Message |
be2003
Joined: 20 Apr 2006 Posts: 144
|
Posted: Mon Nov 20, 2006 4:22 am Post subject: z80 emulator flags |
|
|
im writing a ti-83 plus emulator and was wondering what is the best way to handle the issue of flags. _________________ - be2003
blog |
|
| Back to top |
|
 |
J.F.
Joined: 22 Feb 2004 Posts: 2906
|
Posted: Mon Nov 20, 2006 4:48 am Post subject: |
|
|
"Best" depends on what you want. Do you want it fast? Then do it in assembly language and test if the flags need to be computed at all (check the next X instructions for flag usage). If fast isn't a consideration (and it probably isn't for a calculator emulation), just do the most straightforward C code you can so that it's easy to read, debug, and maintain.
For example, maybe you keep a bool for each flag in the Z80, and then after doing an operation on A that affects the flags, you do something like
z_flag = (A_reg == 0);
Simple, easy to understand, and slow as hell. :)
You can find already written z80 emulation all over the net if you don't care to write your own. Any SEGA GG/MS/Genesis emulation will have a Z80 emulator core in it. The MSX was also a Z80 if I remember correctly. All those Timex/Sinclair computers were Z80 based. |
|
| Back to top |
|
 |
Jim

Joined: 02 Jul 2005 Posts: 487 Location: Sydney
|
Posted: Mon Nov 20, 2006 7:20 am Post subject: |
|
|
For many of the flags, you only need a 256 byte (for the result) lookup table. For some others you need 256x256 byte table (for each operand). Lookups are the only way for speed. Basically I'm saying pre-compute all the possible flags for all the possible results. It's a tiny amount of memory.
Jim _________________ http://www.dbfinteractive.com |
|
| Back to top |
|
 |
Shine
Joined: 03 Dec 2004 Posts: 728 Location: Germany
|
Posted: Mon Nov 20, 2006 8:25 am Post subject: |
|
|
| Jim wrote: | For many of the flags, you only need a 256 byte (for the result) lookup table. For some others you need 256x256 byte table (for each operand). Lookups are the only way for speed. Basically I'm saying pre-compute all the possible flags for all the possible results. It's a tiny amount of memory.
|
The ti-83 plus was clocked with 15 MHz, so should be no problem without optimization to run it in real time.
But are you sure that the memory access for a 64 Kbyte table is faster than executing some instructions, which are maybe piplined and in instruction cache? |
|
| Back to top |
|
 |
J.F.
Joined: 22 Feb 2004 Posts: 2906
|
Posted: Mon Nov 20, 2006 10:25 am Post subject: |
|
|
He's right - with instruction caches and such, doing several opcodes to algorithmically generate flags is much faster than lookup tables. Tables stopped being faster for most emulation purposes almost a decade ago.
Some CPUs are actually capable of executing dozens of opcodes in the same time as one memory fetch (not from L2 cache). Using BIG tables actually hurts your emulation speed as the caches are flooded with mostly useless data. |
|
| Back to top |
|
 |
be2003
Joined: 20 Apr 2006 Posts: 144
|
Posted: Mon Nov 20, 2006 12:22 pm Post subject: |
|
|
i decided to make each flag an int, either 0 or 1, now i just need help using shifts... << or >> _________________ - be2003
blog |
|
| Back to top |
|
 |
Atenus
Joined: 05 Nov 2006 Posts: 9 Location: Argentina
|
Posted: Wed Nov 22, 2006 7:35 am Post subject: |
|
|
i know nearly nothing bout the z80 (i made a 6502 emulator) and since it is a 8 bit cpu the best way i think is as you said use a plain "int" for each flag, and have a "byte" (usigned char) for the emulating the flag register itself.
case 1: Only one variable (byte).
Suppouse you only use a byte to emulate your cpu flags and that flag "Zero" is the bit 0 (lsbit) of the byte, and you need to set/clear it
You would do:
| Code: |
//this set
if (val == 0) //set "zero flag"
Cpu.Flags |= 0x01;
else
Cpu.Flags &= ~0x01; //clear zero flag
//Tessting for "set"
if (Cpu.Flags & 0x01)
//instruction dependent of the flag here
//Tessting for "clear"
if (!(Cpu.Flags & 0x01))
//instruction dependent of the flag here
|
As you can see you have to use an "if" on the value, wich as we now normaly the compiler, in this case will generate condition and branch on the target cpu (the cpu that you want you emulated one to run).
case 2: one varible for each flag, and a byte variable for the emulated one.
Now look at the code if you use a variable (int) for each flag:
| Code: |
//this set
Cpu.Zero = ! value; set to "1" if value is 0, otherwise clear it
//Tessting for "set"
if (Cpu.Zero)
//instruction dependent of the flag here
//Tessting for "clear"
if (! Cpu.Flags)
//instruction dependent of the flag here
|
As you can see in this code you eliminated the "if" and some logical operatos (much faster);
Then when there is a instruction that push/pop the flag to the stack you just have to "reconstruct" your flags (i currently dont know z80 flag positions, but the important thing here is the "concept").
| Code: |
Cpu.Flags = 0;
if (Cpu.Zero)
Cpu.Flags |= 0x01;
if (Cpu.Carry)
Cpu.Flags |= 0x02;
if (Cpu.Overflow)
Cpu.Flags |= 0x04;
|
You could ask yourself, that is slow, but think that emulation of the processor maybe expend 10% in pushing/poping flags and a 90% setting/clearing them.
case 3: precalculated flags using only one var (byte).
you can precalculate you flags creating an array of the total of the values that a BYTE (a 8 bit processor) can hold. ie = 256 elements in the array. and each entry in the array hast a "1" or "0", that tells if the value (array position) the flag shold be set or clear.
Lets look the code:
| Code: |
//global array
unsigned char flag_zero_sign_lut[256];
then you can make a function, that is called only once in your program to "precalculate" the flags.
for (int i = 0; i < 256; i++)
{
flag_zero_sign_lut[i] = (i == 0 ? 1 : 0) | (i & 0x80 ? 1 : 0) ; // this test if the value is zero and or it with the test if the value is negative
}
and when you need to set or clear a flag:
Cpu.Flag &= ~0x81; //we clear here since we dont know the prev. value
Cpu.Flags |= flag_zero_sign_lut[value];
|
This method is not too bad, is faster than 1 but slower than 2 and you use a little bit of memory.
so i suggest method 2 (case 2).
i hope this help. _________________ - Ateneo - |
|
| Back to top |
|
 |
Shine
Joined: 03 Dec 2004 Posts: 728 Location: Germany
|
Posted: Wed Nov 22, 2006 7:54 am Post subject: |
|
|
| Atenus wrote: | As you can see you have to use an "if" on the value, wich as we now normaly the compiler, in this case will generate condition and branch on the target cpu (the cpu that you want you emulated one to run).
|
C allows you to define a struct with bit fields (see http://www.google.com/search?q=struct+bit+fields ), with which you could represent all flags with one byte and set a bit with one assignment, without if's or shift's, but I have never used it and I don't know if GCC optimize it with fast MIPS instructions. |
|
| Back to top |
|
 |
Atenus
Joined: 05 Nov 2006 Posts: 9 Location: Argentina
|
Posted: Wed Nov 22, 2006 8:04 am Post subject: |
|
|
| Quote: |
C allows you to define a struct with bit fields (see http://www.google.com/search?q=struct+bit+fields ), with which you could represent all flags with one byte and set a bit with one assignment, without if's or shift's, but I have never used it and I don't know if GCC optimize it with fast MIPS instructions.
|
yes i know and i have used them, but i can tell you they are a pain in the a*s regarding code generation. _________________ - Ateneo - |
|
| Back to top |
|
 |
cheriff Regular
Joined: 23 Jun 2004 Posts: 262 Location: Sydney.au
|
Posted: Wed Nov 22, 2006 8:27 am Post subject: |
|
|
Bah. I dont know the difference between reoply and edit.
Read on ...
(ps - whats with the delay between posts/edits?) _________________ Damn, I need a decent signature!
Last edited by cheriff on Wed Nov 22, 2006 8:38 am; edited 1 time in total |
|
| Back to top |
|
 |
cheriff Regular
Joined: 23 Jun 2004 Posts: 262 Location: Sydney.au
|
Posted: Wed Nov 22, 2006 8:36 am Post subject: |
|
|
| cheriff wrote: | Personally, I avoid bitfields like the plague.
They are compiler and endian dependant, which cause headaches with regard to portability.
I've also read they can cause gcc to generate icky code, although I personally haven't examined this to make sure, I just never use them :)
Instead:
| Code: |
#define GET_FIELD(x, shift, mask) ( (x>>shift)&mask )
#define SHIFT_Z (1)
#define MASK_Z (1)
#define SHIFT_NEG (2)
#define MASK_NEG (1)
#define GET(field, x) GET_FIELD(x, SHIFT_##field, MASK_##field)
...
if (GET(NEG, flags))
// do something
|
Its a little bit more work but works anywhere.
A simiar macro could be done for SET field too.
Probably not important if you stick to one compiler on a single platform, since it is at least consistient, but worth a look. |
_________________ Damn, I need a decent signature! |
|
| Back to top |
|
 |
hlide
Joined: 10 Sep 2006 Posts: 750
|
Posted: Wed Nov 22, 2006 9:21 am Post subject: |
|
|
| Atenus wrote: | | Quote: |
C allows you to define a struct with bit fields (see http://www.google.com/search?q=struct+bit+fields ), with which you could represent all flags with one byte and set a bit with one assignment, without if's or shift's, but I have never used it and I don't know if GCC optimize it with fast MIPS instructions.
|
yes i know and i have used them, but i can tell you they are a pain in the a*s regarding code generation. |
normally gcc should use ext/ins instructions which are designed for this purpose. But here it would be more likely a or to set or an and to reset. |
|
| Back to top |
|
 |
hlide
Joined: 10 Sep 2006 Posts: 750
|
Posted: Wed Nov 22, 2006 9:38 am Post subject: |
|
|
| Code: |
#define GET_FIELD(x, lsb, bits) ({ int res; asm volatile("ext %0, %1, %2, %3" : "="(res) : "r"(x), "i"(lsb), "i"(bits)); res; })
#define SHIFT_Z (1)
#define BITS_Z (1)
#define SHIFT_NEG (2)
#define BITS_NEG (1)
#define GET(field, x) GET_FIELD(x, SHIFT_##field, BITS_##field)
...
if (GET(NEG, flags))
// do something
...
#define SET_FIELD(x, y, lsb, bits) ({ asm volatile("ins %0, %1, %2, %3" : "+r"(x) : "r"(y), "i"(lsb), "i"(bits)); })
|
if BITS equals to 1 then the right flag in X will be set with the value of Y (i.e, bit 0 of Y). |
|
| Back to top |
|
 |
cheriff Regular
Joined: 23 Jun 2004 Posts: 262 Location: Sydney.au
|
Posted: Wed Nov 22, 2006 10:05 am Post subject: |
|
|
| hlide wrote: | | if BITS equals to 1 then the right flag in X will be set with the value of Y (i.e, bit 0 of Y). | Yeah, I was contributing to the discussion on potential use of C bitfileds, and less on the best way for this particular task.
My asm is a bit rusty, but for setting and clearing indivitual bits hlid's code would be the way to go.
I need portability and wider fields.. so each to his own :) _________________ Damn, I need a decent signature! |
|
| Back to top |
|
 |
Atenus
Joined: 05 Nov 2006 Posts: 9 Location: Argentina
|
Posted: Wed Nov 22, 2006 5:25 pm Post subject: |
|
|
| Quote: |
Bah. I dont know the difference between reoply and edit.
Read on ...
|
Sorry, i writed the post on the fly and i tried to change 2 things in my first reply, so that could explain the collition.
So, taking into account that i will add/correct here to myself:
Correction:
The lookup table generation code, it would be:
| Code: |
for (int i = 0; i < 256; i++)
{
flag_zero_sign_lut[i] = (i == 0 ? 1 : 0) | (i & 0x80 ? 0x80 : 0) ; // this test if the value is zero and or it with the test if the value is negative
}
|
The second expression ored at the "right hand" of the assigment, it should return 0x80 (your sign flag could be bit 7 or msbit), so when you use the table to reference the value the right bit is set.
Add:
You should use a reconstruct() function (explained above) for the flag register when its pushed in.
And a deconstruct() function to values popped out since flag register changes.
That would be:
| Code: |
if (Cpu.Flags & 0x01)
Cpu.Zero = 1;
else
Cpu.Zero = 0
if (Cpu.Flags & 0x02)
Cpu.Carry = 1;
else
Cpu.Carry = 0;
// OR knowing the position of flags, this code (much faster):
Cpu.Zero = (Cpu.Flags & 0x01);
Cpu.Carry = (Cpu.Flags & 0x02);
//and so on ...
//note: it dont cares if a int flag changes values to 1 to 2, or 2 to 4 (talking decimaly) since the compiler will evaluate codition "true" != 0;
|
As you can see reconstruct() and deconstruct() can be thought in 2 ways if you "reconstruct byte flag register from int flags" and "deconstruct byte flag register to int flags" OR "reconstruct int flags from byte flag register" and "deconstruct int flags to byte flag register".
This according the side you look at, but dont take this into account.
thats all. _________________ - Ateneo - |
|
| Back to top |
|
 |
be2003
Joined: 20 Apr 2006 Posts: 144
|
Posted: Wed Nov 22, 2006 5:35 pm Post subject: |
|
|
will this work:
| Code: |
byte R; // the flag register
int a,b,c,d,e,f,g,h;
//h being bit 7 and a as bit 0
#define MKFLAG() R = (h<<7|g<<6|f<<5|e<<4|d<<3|c<<2|b<<1|a<<0)
|
_________________ - be2003
blog |
|
| Back to top |
|
 |
hlide
Joined: 10 Sep 2006 Posts: 750
|
Posted: Wed Nov 22, 2006 7:08 pm Post subject: |
|
|
| be2003 wrote: | will this work:
| Code: |
byte R; // the flag register
int a,b,c,d,e,f,g,h;
//h being bit 7 and a as bit 0
#define MKFLAG() R = (h<<7|g<<6|f<<5|e<<4|d<<3|c<<2|b<<1|a<<0)
|
|
supposedly you have a,b,c,d,e,f,g and h stored in registers, you will have 15 instructions to execute (1 MOV, 7 SLL, 7 OR) :
| Code: |
move v0, s0
sll v1, s1, 1
or v0, v0, v1
sll v1, s2, 2
or v0, v0, v1
sll v1, s3, 3
or v0, v0, v1
sll v1, s4, 3
or v0, v0, v1
sll v1, s5, 5
or v0, v0, v1
sll v1, s6, 6
or v0, v0, v1
sll v1, s7, 7
or v0, v0, v1
|
for PSP, you can do it that way :
| Code: |
andi v0, s0, 1
ins v0, s1, 1, 1
ins v0, s2, 2, 1
ins v0, s3, 3, 1
ins v0, s4, 4, 1
ins v0, s5, 5, 1
ins v0, s6, 6, 1
ins v0, s7, 7, 1
|
that is 8 instructions.
So far as you are not concerned by a portable code, of course.
EDIT: 7 AND can be removed indeed since you use int and not a boolean :/
Last edited by hlide on Wed Nov 22, 2006 7:54 pm; edited 2 times in total |
|
| Back to top |
|
 |
hlide
Joined: 10 Sep 2006 Posts: 750
|
Posted: Wed Nov 22, 2006 7:19 pm Post subject: |
|
|
since v0 is a read/write register you may need to avoid to execute two 'ins' on the same register (i'm not sure about that nonetheless):
| Code: |
andi v0, s0, 1
ins v1, s4, 4, 1
ins v0, s1, 1, 1
ins v1, s5, 5, 1
ins v0, s2, 2, 1
ins v1, s6, 6, 1
ins v0, s3, 3, 1
ins v1, s7, 7, 1
ins v0, v1, 4, 4
|
9 instructions still |
|
| Back to top |
|
 |
Atenus
Joined: 05 Nov 2006 Posts: 9 Location: Argentina
|
Posted: Thu Nov 23, 2006 10:32 am Post subject: |
|
|
will this work:
| Code: |
byte R; // the flag register
int a,b,c,d,e,f,g,h;
//h being bit 7 and a as bit 0
#define MKFLAG() R = (h<<7|g<<6|f<<5|e<<4|d<<3|c<<2|b<<1|a<<0)
|
yep in will work, but if you take as a constant that your emulator will store in int flags always 1 or 0.
the (a << 0) can be removed since its already 0 or 1. _________________ - Ateneo - |
|
| Back to top |
|
 |
be2003
Joined: 20 Apr 2006 Posts: 144
|
Posted: Thu Nov 23, 2006 1:03 pm Post subject: |
|
|
| Atenus wrote: | will this work:
| Code: |
byte R; // the flag register
int a,b,c,d,e,f,g,h;
//h being bit 7 and a as bit 0
#define MKFLAG() R = (h<<7|g<<6|f<<5|e<<4|d<<3|c<<2|b<<1|a<<0)
|
yep in will work, but if you take as a constant that your emulator will store in int flags always 1 or 0.
the (a << 0) can be removed since its already 0 or 1. |
thank god, i was looking for the simplest code without asm... i put the a<<0 for concept completeness _________________ - be2003
blog |
|
| Back to top |
|
 |
Atenus
Joined: 05 Nov 2006 Posts: 9 Location: Argentina
|
Posted: Thu Nov 23, 2006 1:54 pm Post subject: |
|
|
| Quote: |
i put the a<<0 for concept completeness
|
ups!! i was wrong when i said "the (a << 0) can be removed ", maybe a should say that you can remove the "<< 0" shif left 0 to a and live only "a".
Talking about asm/c/c++ code, you can use all c bitwise operations and you dont need assembly. I dont know too much about MIPS32 (Allegrex) architecture (im just a beginner, im studying it), but i made a NES emulator for Windows in VC (plain c code), then i tried nearly all compilers for win32 on my code (ported it) and the best one was MINGW, wich is based on gcc, and i can say its a good compiler (anyway microsoft do his o.system and vc compiler is not more that 1% better than gcc for creating fast win applications).
Since an emulator (console, computer, etc) is an application that needs very good performance the key sometimes is:
- the emulation itself (cpu, graphics, etc) should be done in asm or plain c (procedural). Asm is inpractical since code portability, but if you think your emu. for just one platform asm could be a choice, but you maybe will win no more than 2 or 3 %).
- you could use OOP for things that dont play significant rules in the emulation itself, like if you are going to do a menu system for the emulator (thinking that psp wasnt thought for GUI or user interfaces).
And then mix c/c++ code.
- know about the target machine, so your take it to the limit when running your program (im weack here since im a beginner to psp).
Well there could be (and surely there are) more than this ones, but its more like a "general rule".
Also remember that psp is capable of running an 8-bit computer emulator, but you will need to optimize code and find the best code. _________________ - Ateneo - |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|