Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*=====================================================================
  2.   z80.c -> Main File related to the Z80 emulation code.
  3.  
  4.   Please read documentation files to know how this works :)
  5.  
  6.   Thanks go to Marat Fayzullin (read z80.h for more info), Raúl Gomez
  7.   (check his great R80 Spectrum emulator!), Philip Kendall (some code
  8.   of this emulator, such as the flags lookup tabled are from his fuse
  9.   Spectrum emulator) and more people I forget to name here ...
  10.  
  11.  This program is free software; you can redistribute it and/or modify
  12.  it under the terms of the GNU General Public License as published by
  13.  the Free Software Foundation; either version 2 of the License, or
  14.  (at your option) any later version.
  15.  
  16.  This program is distributed in the hope that it will be useful,
  17.  but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  GNU General Public License for more details.
  20.  
  21.  You should have received a copy of the GNU General Public License
  22.  along with this program; if not, write to the Free Software
  23.  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24.  
  25.  Copyright (c) 2000 Santiago Romero Iglesias.
  26.  Email: sromero@escomposlinux.org
  27.  ======================================================================*/
  28.  
  29. #include "z80.h"
  30. #include "tables.h"
  31.  
  32.  
  33. /* RAM variable, debug toggle variable, pressed key and
  34.    row variables for keyboard emulation                   */
  35. extern byte *RAM;
  36. extern int debug, main_tecla, scanl;
  37.  
  38. extern int fila[5][5];
  39.  
  40. //extern char *tapfile;
  41. //extern FILE *tapfile;
  42. extern char *tfont;
  43.  
  44. #include "macros.c"
  45.  
  46.  
  47. /*====================================================================
  48.   void Z80Reset( Z80Regs *regs, int cycles )
  49.  
  50.   This function simulates a z80 reset by setting the registers
  51.   to the values they are supposed to take on a real z80 reset.
  52.   You must pass it the Z80 register structure and the number
  53.   of cycles required to check for interrupts and do special
  54.   hardware checking/updating.
  55.  ===================================================================*/
  56. void Z80Reset( Z80Regs *regs, int int_cycles )
  57. {
  58.    /* reset PC and the rest of main registers: */
  59.    regs->PC.W = regs->R.W = 0x0000;
  60.  
  61.    regs->AF.W = regs->BC.W = regs->DE.W = regs->HL.W =
  62.    regs->AFs.W = regs->BCs.W = regs->DEs.W = regs->HLs.W =
  63.    regs->IX.W = regs->IY.W = 0x0000;
  64.  
  65.    /* Make the stack point to $F000 */
  66.    regs->SP.W = 0xF000;
  67.  
  68.    /* reset variables to their default values */
  69.    regs->I = 0x00;
  70.    regs->IFF1 = regs->IFF2 = regs->IM = regs->halted = 0x00;
  71.    regs->ICount = regs->IPeriod = int_cycles;
  72.  
  73.    regs->IRequest = INT_NOINT;
  74.    regs->we_are_on_ddfd = regs->dobreak = regs->BorderColor = 0;
  75.  
  76. //#ifdef _DEBUG_
  77.    regs->DecodingErrors = 1;
  78. //#endif
  79.  
  80. }
  81.  
  82.  
  83. /*====================================================================
  84.   word Z80Run( Z80Regs *regs, int numopcodes )
  85.  
  86.   This function does the whole Z80 simulation. It consists on a
  87.   for(;;) loop (as stated on Marat's Fayzullin HOWTO -How to
  88.   Write a Computer Emulator-) which fetchs the next opcode,
  89.   interprets it (using a switch statement) and then it's
  90.   executed in the right CASE: of that switch. I've put the different
  91.   case statements into C files included here with #include to
  92.   make this more readable (and programming easier! :).
  93.  
  94.   This function will change regs->ICount register and will execute
  95.   an interrupt when it reaches 0 (or <0). You can then do anything
  96.   related to your machine emulation here, using the Z80Hardware()
  97.   function. This function must be filled by yourself: put there
  98.   the code related to the emulated machine hardware, such as
  99.   screen redrawing, sound playing and so on. This functions can
  100.   return an special value to make Z80Run stop the emulation (and
  101.   return to the caller): that's INT_QUIT. If there is time to
  102.   execute an interrupt, please return INT_IRQ or INT_NMI. Return
  103.   INT_NOINT if there is no time for an interrupt :) .
  104.  
  105.   Z80Execute() will change PC and all the z80 registers acording
  106.   to the executed opcode, and those values will be returned when
  107.   a INT_QUIT is received.
  108.  
  109.   Pass as numcycles the number of clock cycle you want to execute
  110.   z80 opcodes for or < 0 (negative) to execute "infinite" opcodes.
  111.  ===================================================================*/
  112. word Z80Run( Z80Regs *regs, int numcycles )
  113. {
  114.    /* opcode and temp variables */
  115.    register byte opcode;
  116.    eword tmpreg, ops, mread, tmpreg2;
  117.    unsigned long tempdword;
  118.    register int loop;
  119.    unsigned short tempword;
  120.  
  121.    /* emulate <numcycles> cycles */
  122.    loop = (regs->ICount - numcycles);
  123.  
  124.    /* this is the emulation main loop */
  125.    while( regs->ICount > loop )
  126.    {
  127.    #ifdef DEBUG
  128.       /* test if we have reached the trap address */
  129.       if( regs->PC.W == regs->TrapAddress && regs->dobreak != 0 )
  130.          return(regs->PC.W);
  131.    #endif
  132.  
  133.      if( regs->halted == 1 )
  134.      { r_PC--; AddCycles(4); }
  135.  
  136.        /* read the opcode from memory (pointed by PC) */
  137.        opcode = Z80ReadMem(regs->PC.W);
  138.        regs->PC.W++;
  139.  
  140.        /* increment the R register and decode the instruction */
  141.        AddR(1);
  142.        switch(opcode)
  143.        {
  144.              #include "opcodes.c"
  145.           case PREFIX_CB:
  146.              AddR(1);
  147.              #include "op_cb.c"
  148.              break;
  149.           case PREFIX_ED:
  150.              AddR(1);
  151.              #include "op_ed.c"
  152.              break;
  153.           case PREFIX_DD:
  154.           case PREFIX_FD:
  155.              AddR(1);
  156.              if( opcode == PREFIX_DD )
  157.              {
  158.                #define REGISTER regs->IX
  159.                regs->we_are_on_ddfd = WE_ARE_ON_DD;
  160.                #include "op_dd_fd.c"
  161.                #undef  REGISTER
  162.              }
  163.              else
  164.              {
  165.                #define REGISTER regs->IY
  166.                regs->we_are_on_ddfd = WE_ARE_ON_FD;
  167.                #include "op_dd_fd.c"
  168.                #undef  REGISTER
  169.              }
  170.              regs->we_are_on_ddfd = 0;
  171.              break;
  172.        }
  173.  
  174.      /* patch ROM loading routine */
  175.      // address contributed by Ignacio Burgueño :)
  176. //     if( r_PC == 0x0569 )      
  177.      if( r_PC >= 0x0556 && r_PC <= 0x056c )
  178.           Z80Patch( regs );
  179.  
  180.      /* check if it's time to do other hardware emulation */
  181.      if( regs->ICount <= 0 )
  182.      {
  183.           tmpreg.W = Z80Hardware(regs);
  184.           regs->ICount += regs->IPeriod;
  185.           loop = regs->ICount + loop;
  186.  
  187.           /* check if we must exit the emulation or there is an INT */
  188.           if( tmpreg.W == INT_QUIT )
  189.           return( regs->PC.W );
  190.           if( tmpreg.W != INT_NOINT )
  191.              Z80Interrupt( regs, tmpreg.W );
  192.      }
  193.    }
  194.  
  195.    return(regs->PC.W);
  196. }
  197.  
  198.  
  199.  
  200. /*====================================================================
  201.   void Z80Interrupt( Z80Regs *regs, word ivec )
  202.  ===================================================================*/
  203. void Z80Interrupt( Z80Regs *regs, word ivec )
  204. {
  205.    word intaddress;
  206.  
  207.    /* unhalt the computer */
  208.    if( regs->halted == 1 )
  209.       regs->halted = 0;
  210.    
  211.    if( regs->IFF1 )
  212.    {
  213.       PUSH(PC);
  214.       regs->IFF1 = 0;
  215.       switch(regs->IM)
  216.       {
  217.         case 0: r_PC = 0x0038; AddCycles(12); break;
  218.         case 1: r_PC = 0x0038; AddCycles(13); break;
  219.         case 2: intaddress = (((regs->I & 0xFF)<<8) | 0xFF);
  220.                 regs->PC.B.l = Z80ReadMem(intaddress);
  221.                 regs->PC.B.h = Z80ReadMem(intaddress+1);
  222.                 AddCycles(19);
  223.                 break;
  224.       }
  225.  
  226.    }
  227.  
  228. }
  229.  
  230.  
  231. /*====================================================================
  232.   word  Z80Hardware(register Z80Regs *regs)
  233.  
  234.   Do here your emulated machine hardware emulation. Read Z80Execute()
  235.   to know about how to quit emulation and generate interrupts.
  236.  ===================================================================*/
  237. word  Z80Hardware( register Z80Regs *regs )
  238. {
  239.   if(
  240.         debug != 1  // && scanl >= 224
  241.         )
  242.   {
  243.       ;
  244.   }
  245.   return( INT_IRQ );
  246. }
  247.  
  248.  
  249. /*====================================================================
  250.   void Z80Patch( register Z80Regs *regs )
  251.  
  252.   Write here your patches to some z80 opcodes that are quite related
  253.   to the emulated machines (i.e. maybe accessing to the I/O ports
  254.   and so on), such as ED_FE opcode:
  255.  
  256.      case ED_FE:     Z80Patch(regs);
  257.                      break;
  258.  
  259.   This allows "BIOS" patching (cassette loading, keyboard ...).
  260.  ===================================================================*/
  261. void Z80Patch( register Z80Regs *regs )
  262. {
  263.  
  264. ///!!!   if( tapfile != NULL )
  265. ///!!!   {
  266. ///!!!        LoadTAP( regs, tapfile );
  267. ///!!!        POP(PC);
  268. ///!!!   }
  269.  
  270.    /*
  271.         if( strlen(tapfile) != 0 )
  272.         {
  273.            if( LoadTapFile( regs, tapfile ) )
  274.            {  POP(PC);  }
  275.         }
  276.         else
  277.         {
  278.            FileMenu( tfont, 3, tapfile );
  279.            if( strlen(tapfile) != 0 )
  280.              if( LoadTapFile( regs, tapfile ) )
  281.                 {  POP(PC);  }
  282.         }
  283.    */
  284. }
  285.  
  286.  
  287. /*====================================================================
  288.   byte Z80Debug( register Z80Regs *regs )
  289.  
  290.   This function is written for debugging purposes (it's supposed to
  291.   be a debugger by itself!). It will debug a single opcode, given
  292.   by the current PC address.
  293.  
  294.   Return DEBUG_OK to state success and DEBUG_QUIT to quit emulation.
  295.  ===================================================================*/
  296. byte Z80Debug( register Z80Regs *regs )
  297. {
  298.    return( DEBUG_QUIT );
  299. }
  300.  
  301.  
  302.  
  303. /*====================================================================
  304.   byte Z80MemRead( register word address )
  305.  
  306.   This function reads from the given memory address. It is not inlined,
  307.   and it's written for debugging purposes.
  308.  ===================================================================*/
  309. byte Z80MemRead( register word address, Z80Regs *regs )
  310. {
  311.   return(Z80ReadMem(address));
  312. }
  313.  
  314.  
  315. /*====================================================================
  316.   void Z80MemWrite( register word address, register byte value )
  317.  
  318.   This function writes on memory the given value. It is not inlined,
  319.   ands it's written for debugging purposes.
  320.  ===================================================================*/
  321. void Z80MemWrite( register word address, register byte value, Z80Regs *regs )
  322. {
  323.   Z80WriteMem( address, value, regs );
  324. }
  325.  
  326.  
  327. /*====================================================================
  328.   byte Z80InPort( register word port )
  329.  
  330.   This function reads from the given I/O port. It is not inlined,
  331.   and it's written for debugging purposes.
  332.  ===================================================================*/
  333. byte Z80InPort( register word port )
  334. {
  335.   int porth;
  336.   int code = 0xFF;
  337.  
  338.   porth = port >> 8;
  339.  
  340.   if (!(porth & 0x01)) code &= fila[4][1];
  341.   if (!(porth & 0x02)) code &= fila[3][1];
  342.   if (!(porth & 0x04)) code &= fila[2][1];
  343.   if (!(porth & 0x08)) code &= fila[1][1];
  344.   if (!(porth & 0x10)) code &= fila[1][2];
  345.   if (!(porth & 0x20)) code &= fila[2][2];
  346.   if (!(porth & 0x40)) code &= fila[3][2];
  347.   if (!(porth & 0x80)) code &= fila[4][2];
  348.  
  349.   /*
  350.      issue 2 emulation, thx to Raul Gomez!!!!!
  351.      I should implement this also:
  352.      if( !ear_on && mic_on )
  353.         code &= 0xbf;
  354.      where earon = bit 4 of the last OUT to the 0xFE port
  355.      and   micon = bit 3 of the last OUT to the 0xFE port
  356.   */
  357.   code &= 0xbf;
  358.  
  359.   if( (port & 0xFF) == 0xFF )
  360.   {
  361.       if( (rand() % 10) > 7 ) return(0xff);
  362.       else return( rand()%0xFF );
  363.   }
  364.  
  365.   return( code );
  366. }
  367.  
  368.  
  369. /*====================================================================
  370.   void Z80OutPort( register word port, register byte value )
  371.  
  372.   This function outs a value to a given I/O port. It is not inlined,
  373.   and it's written for debugging purposes.
  374.  ===================================================================*/
  375. void Z80OutPort( register Z80Regs *regs,
  376.                  register word port, register byte value )
  377. {
  378.     /* change border colour */
  379.     if( ! (port & 0x01) )
  380.         regs->BorderColor = (value & 0x07);
  381. }
  382.  
  383.  
  384.  
  385. /*====================================================================
  386.    static void Z80FlagTables ( void );
  387.  
  388.    Creates a look-up table for future flag setting...
  389.    Taken from fuse's sources. Thanks to Philip Kendall.
  390.  ===================================================================*/
  391. void Z80FlagTables(void)
  392. {
  393.   int i,j,k;
  394.   byte parity;
  395.  
  396.   for(i=0;i<0x100;i++) {
  397.     sz53_table[i]= i & ( FLAG_3 | FLAG_5 | FLAG_S );
  398.     j=i; parity=0;
  399.     for(k=0;k<8;k++) { parity ^= j & 1; j >>=1; }
  400.     parity_table[i]= ( parity ? 0 : FLAG_P );
  401.     sz53p_table[i] = sz53_table[i] | parity_table[i];
  402.   }
  403.  
  404.   sz53_table[0]  |= FLAG_Z;
  405.   sz53p_table[0] |= FLAG_Z;
  406. }
  407.  
  408.