z80 emulator flags

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
be2003
Posts: 144
Joined: Thu Apr 20, 2006 2:46 pm

z80 emulator flags

Post by be2003 »

im writing a ti-83 plus emulator and was wondering what is the best way to handle the issue of flags.
- be2003
blog
J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

"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.
User avatar
Jim
Posts: 476
Joined: Sat Jul 02, 2005 10:06 pm
Location: Sydney
Contact:

Post by Jim »

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
Shine
Posts: 728
Joined: Fri Dec 03, 2004 12:10 pm
Location: Germany

Post by Shine »

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?
J.F.
Posts: 2906
Joined: Sun Feb 22, 2004 11:41 am

Post by J.F. »

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.
be2003
Posts: 144
Joined: Thu Apr 20, 2006 2:46 pm

Post by be2003 »

i decided to make each flag an int, either 0 or 1, now i just need help using shifts... << or >>
- be2003
blog
Atenus
Posts: 9
Joined: Sun Nov 05, 2006 6:37 pm
Location: Argentina

Post by Atenus »

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: Select all

//this set
if &#40;val == 0&#41;           //set "zero flag"
    Cpu.Flags |= 0x01;  
else
    Cpu.Flags &= ~0x01; //clear zero flag

//Tessting for "set"
if &#40;Cpu.Flags & 0x01&#41;
    //instruction dependent of the flag here

//Tessting for "clear"
if &#40;!&#40;Cpu.Flags & 0x01&#41;&#41;
    //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: Select all

//this set
    Cpu.Zero = ! value;  set to "1" if value is 0, otherwise clear it

//Tessting for "set"
if &#40;Cpu.Zero&#41;
    //instruction dependent of the flag here

//Tessting for "clear"
if &#40;! Cpu.Flags&#41;
    //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: Select all


Cpu.Flags = 0;

if &#40;Cpu.Zero&#41;
 Cpu.Flags |= 0x01;

if &#40;Cpu.Carry&#41;
 Cpu.Flags |= 0x02;

if &#40;Cpu.Overflow&#41;
 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: Select all


//global array
unsigned char flag_zero_sign_lut&#91;256&#93;;

then you can make a function, that is called only once in your program to "precalculate" the flags.

for &#40;int i = 0; i < 256; i++&#41;
&#123;
  flag_zero_sign_lut&#91;i&#93; = &#40;i == 0 ? 1 &#58; 0&#41; | &#40;i & 0x80 ? 1 &#58; 0&#41; ;  // this test if the value is zero and or it with the test if the value is negative
&#125;

and when you need to set or clear a flag&#58;

Cpu.Flag &= ~0x81;    //we clear here since we dont know the prev. value
Cpu.Flags |= flag_zero_sign_lut&#91;value&#93;;

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 -
Shine
Posts: 728
Joined: Fri Dec 03, 2004 12:10 pm
Location: Germany

Post by Shine »

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.
Atenus
Posts: 9
Joined: Sun Nov 05, 2006 6:37 pm
Location: Argentina

Post by Atenus »

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 -
cheriff
Regular
Posts: 258
Joined: Wed Jun 23, 2004 5:35 pm
Location: Sydney.au

Post by cheriff »

Bah. I dont know the difference between reoply and edit.
Read on ...

(ps - whats with the delay between posts/edits?)
Last edited by cheriff on Wed Nov 22, 2006 8:38 am, edited 1 time in total.
Damn, I need a decent signature!
cheriff
Regular
Posts: 258
Joined: Wed Jun 23, 2004 5:35 pm
Location: Sydney.au

Post by cheriff »

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: Select all

#define GET_FIELD&#40;x, shift, mask&#41; &#40; &#40;x>>shift&#41;&mask &#41;

#define SHIFT_Z  &#40;1&#41;
#define  MASK_Z  &#40;1&#41;

#define SHIFT_NEG &#40;2&#41;
#define MASK_NEG &#40;1&#41;
#define GET&#40;field, x&#41; GET_FIELD&#40;x, SHIFT_##field, MASK_##field&#41;
 ...

if &#40;GET&#40;NEG, flags&#41;&#41; 
   // 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!
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

Atenus wrote:
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.
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

Code: Select all

#define GET_FIELD&#40;x, lsb, bits&#41; &#40;&#123; int res; asm volatile&#40;"ext %0, %1, %2, %3" &#58; "="&#40;res&#41; &#58; "r"&#40;x&#41;, "i"&#40;lsb&#41;, "i"&#40;bits&#41;&#41;; res; &#125;&#41;

#define SHIFT_Z  &#40;1&#41;
#define  BITS_Z  &#40;1&#41;

#define SHIFT_NEG &#40;2&#41;
#define BITS_NEG &#40;1&#41;
#define GET&#40;field, x&#41; GET_FIELD&#40;x, SHIFT_##field, BITS_##field&#41;
 ...

if &#40;GET&#40;NEG, flags&#41;&#41;
   // do something 

...
#define SET_FIELD&#40;x, y, lsb, bits&#41; &#40;&#123; asm volatile&#40;"ins %0, %1, %2, %3" &#58; "+r"&#40;x&#41; &#58; "r"&#40;y&#41;, "i"&#40;lsb&#41;, "i"&#40;bits&#41;&#41;; &#125;&#41;
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).
cheriff
Regular
Posts: 258
Joined: Wed Jun 23, 2004 5:35 pm
Location: Sydney.au

Post by cheriff »

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!
Atenus
Posts: 9
Joined: Sun Nov 05, 2006 6:37 pm
Location: Argentina

Post by Atenus »

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: Select all

for &#40;int i = 0; i < 256; i++&#41; 
&#123; 
  flag_zero_sign_lut&#91;i&#93; = &#40;i == 0 ? 1 &#58; 0&#41; | &#40;i & 0x80 ? 0x80 &#58; 0&#41; ;  // this test if the value is zero and or it with the test if the value is negative 
&#125; 
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: Select all

if &#40;Cpu.Flags & 0x01&#41;
    Cpu.Zero = 1;
else
    Cpu.Zero = 0

if &#40;Cpu.Flags & 0x02&#41;
   Cpu.Carry = 1;
else
   Cpu.Carry = 0;

// OR knowing the position of flags, this code &#40;much faster&#41;&#58;

Cpu.Zero = &#40;Cpu.Flags & 0x01&#41;;
Cpu.Carry = &#40;Cpu.Flags & 0x02&#41;;
//and so on ...

//note&#58; it dont cares if a int flag changes values to 1 to 2, or 2 to 4 &#40;talking decimaly&#41; 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 -
be2003
Posts: 144
Joined: Thu Apr 20, 2006 2:46 pm

Post by be2003 »

will this work:

Code: Select all

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&#40;&#41; R = &#40;h<<7|g<<6|f<<5|e<<4|d<<3|c<<2|b<<1|a<<0&#41;
- be2003
blog
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

be2003 wrote:will this work:

Code: Select all

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&#40;&#41; R = &#40;h<<7|g<<6|f<<5|e<<4|d<<3|c<<2|b<<1|a<<0&#41;
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: Select all

  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: Select all

  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.
hlide
Posts: 739
Joined: Sun Sep 10, 2006 2:31 am

Post by hlide »

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: Select all

  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
Atenus
Posts: 9
Joined: Sun Nov 05, 2006 6:37 pm
Location: Argentina

Post by Atenus »

will this work:

Code: Select all

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&#40;&#41; R = &#40;h<<7|g<<6|f<<5|e<<4|d<<3|c<<2|b<<1|a<<0&#41; 
 
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 -
be2003
Posts: 144
Joined: Thu Apr 20, 2006 2:46 pm

Post by be2003 »

Atenus wrote:will this work:

Code: Select all

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&#40;&#41; R = &#40;h<<7|g<<6|f<<5|e<<4|d<<3|c<<2|b<<1|a<<0&#41; 
 
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
Atenus
Posts: 9
Joined: Sun Nov 05, 2006 6:37 pm
Location: Argentina

Post by Atenus »

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 -
Post Reply