Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1814 yogev_ezra 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  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