From 7c25ae8a34ad4b15e83c07477b5bd9e59fac3c5f Mon Sep 17 00:00:00 2001 From: aiju Date: Fri, 13 Jun 2014 17:21:25 +0200 Subject: [PATCH] games/md: add z80, audio support --- sys/src/games/md/cpu.c | 199 ++++++++-- sys/src/games/md/dat.h | 18 +- sys/src/games/md/fns.h | 10 +- sys/src/games/md/md.c | 59 ++- sys/src/games/md/mem.c | 54 ++- sys/src/games/md/mkfile | 1 + sys/src/games/md/vdp.c | 2 + sys/src/games/md/ym.c | 420 +++++++++++++++++++++ sys/src/games/md/z80.c | 797 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 1515 insertions(+), 45 deletions(-) create mode 100644 sys/src/games/md/ym.c diff --git a/sys/src/games/md/cpu.c b/sys/src/games/md/cpu.c index 460526da7..eaf19069b 100644 --- a/sys/src/games/md/cpu.c +++ b/sys/src/games/md/cpu.c @@ -19,7 +19,7 @@ extern u32int irql[8]; u32int irqla[8]; u16int rS; static u32int op; -int trace; +int trace, tim; #define ra (r+8) static void @@ -99,16 +99,21 @@ amode(int m, int n, int s) case 1: return ~(n+8); case 2: + tim += s == 2 ? 8 : 4; return ra[n]; case 3: v = ra[n]; ra[n] += 1<> 12]; if((w & 1<<11) == 0) @@ -117,19 +122,24 @@ amode(int m, int n, int s) case 7: switch(n){ case 0: + tim += s == 2 ? 12 : 8; return (u32int)(s16int)fetch16(); case 1: + tim += s == 2 ? 16 : 12; return fetch32(); case 2: + tim += s == 2 ? 12 : 8; v = fetch16(); return (u32int)(pc + (s16int)v - 2); case 3: + tim += s == 2 ? 14 : 4; w = fetch16(); v = r[w >> 12]; if((w & 1<<11) == 0) v = (s16int)v; return (u32int)(pc + v + (s8int)w - 2); case 4: + tim += s == 2 ? 8 : 4; v = pc; pc += 1<> 8) & 7] & irq) != 0){ trap(-1, curpc); - return; + return tim; } if(stop) - return; + return 1; op = fetch16(); if(trace) print("%.6ux %.6uo %.4ux %.8ux | %.8ux %.8ux %.8ux %.8ux | %.8ux %.8ux %.8ux\n", curpc, op, rS, memread(ra[7])<<16|memread(ra[7]+2), r[0], r[1], r[2], r[3], ra[0], ra[1], ra[7]); @@ -504,6 +549,7 @@ step(void) ra[7] = asp; asp = v; } + tim += 20; break; } if((op & 0x13f) == 0x108){ /* MOVEP */ @@ -513,23 +559,27 @@ step(void) v = (u8int)rmode(a, 0) << 8; v |= (u8int)rmode(a + 2, 0); r[n] = r[n] & 0xff00 | v; + tim += 16; break; case 1: v = (u8int)rmode(a, 0) << 24; v |= (u8int)rmode(a + 2, 0) << 16; v |= (u8int)rmode(a + 4, 0) << 8; v |= (u8int)rmode(a + 6, 0); + tim += 24; r[n] = v; break; case 2: wmode(a, 0, r[n] >> 8); wmode(a + 2, 0, r[n]); + tim += 16; break; case 3: wmode(a, 0, r[n] >> 24); wmode(a + 2, 0, r[n] >> 16); wmode(a + 4, 0, r[n] >> 8); wmode(a + 6, 0, r[n]); + tim += 24; break; } break; @@ -553,11 +603,17 @@ step(void) rS |= FLAGZ; switch(s){ case 1: v ^= w; break; - case 2: v &= ~w; break; + case 2: v &= ~w; if(n == 2) tim += 2; break; case 3: v |= w; break; } - if(s != 0) + if(s != 0){ wmode(a, n, v); + tim += (op & 0x100) != 0 ? 8 : 12; + }else{ + tim += (op & 0x100) != 0 ? 4 : 8; + if(n == 2) + tim += 2; + } break; } switch(s){ @@ -576,6 +632,10 @@ step(void) case 6: rS |= FLAGZ; sub(v, w, 0, s); break; default: undef(); } + if(a < 0) + tim += s == 2 ? (n == 1 || n == 6 ? 14 : 16) : 8; + else + tim += s == 2 ? 20 : 12; if(n != 6) wmode(a, s, v); break; @@ -592,14 +652,21 @@ step(void) wmode(amode(op >> 6, op >> 9, s), s, v); if((op & 0x1c0) != 0x40) nz(v, s); + tim += 4; break; case 4: if((op & 0x1c0) == 0x1c0){ /* LEA */ ra[n] = amode(op >> 3, op, 2); break; } - if((op & 0x1c0) == 0x180) /* CHK */ - undef(); + if((op & 0x1c0) == 0x180){ /* CHK */ + a = amode(op >> 3, op, s); + v = rmode(a, s); + if((s32int)r[n] < 0 || (s32int)r[n] > (s32int)v) + trap(6, curpc); + else + tim += 10; + } if((op & 0xb80) == 0x880 && (op & 0x38) >= 0x10){ /* MOVEM */ s = (op >> 6 & 1) + 1; w = fetch16(); @@ -610,10 +677,12 @@ step(void) if((w & 1) != 0){ r[m] = rmode(a, s); a += 1<>= 1; } ra[n] = a; + tim += 12; break; } if((op & 0x38) == 0x20){ @@ -623,10 +692,12 @@ step(void) if((w & 1) != 0){ a -= 1<>= 1; } ra[n] = a; + tim += 8; break; } a = amode(op >> 3, op, s); @@ -637,17 +708,21 @@ step(void) else wmode(a, s, r[m]); a += 1<>= 1; } + tim += (op & 0x400) != 0 ? 8 : 12; break; } switch(op >> 8 & 0xf){ case 0: if(s == 3){ /* MOVE from SR */ - if((rS & FLAGS) != 0) - wmode(amode(op >> 3, op, 1), 1, rS); - else + if((rS & FLAGS) != 0){ + a = amode(op >> 3, op, 1); + wmode(a, 1, rS); + tim += a < 0 ? 6 : 8; + }else trap(8, curpc); break; } /* NEGX */ @@ -666,20 +741,25 @@ step(void) rS &= ~FLAGZ; } wmode(a, s, v); + stime(a < 0, s); break; case 2: /* CLR */ - wmode(amode(op >> 3, op, s), s, 0); + a = amode(op >> 3, op, s); + wmode(a, s, 0); nz(0, 0); + stime(a < 0, s); break; case 4: if(s == 3){ /* MOVE to CCR */ rS = rS & 0xff00 | rmode(amode(op >> 3, op, 1), 1); + tim += 12; break; } /* NEG */ a = amode(op >> 3, op, s); v = -rmode(a, s); nz(v, s); wmode(a, s, v); + stime(a < 0, s); break; case 6: if(s == 3){ /* MOVE to SR */ @@ -690,6 +770,7 @@ step(void) asp = ra[7]; ra[7] = v; } + tim += 12; }else trap(8, curpc); break; @@ -698,6 +779,7 @@ step(void) v = ~rmode(a, s); nz(v, s); wmode(a, s, v); + stime(a < 0, s); break; case 8: n = op & 7; @@ -706,18 +788,27 @@ step(void) a = amode(op >> 3, op, 0); v = rmode(a, 0); wmode(a, 0, subbcd(0, v)); + if(a < 0) + tim += 8; + else + tim += 6; break; case 1: - if((op >> 3 & 7) != 0) + if((op >> 3 & 7) != 0){ push32(amode(op >> 3, op, 0)); /* PEA */ - else + tim += 8; + }else{ nz(r[n] = r[n] >> 16 | r[n] << 16, 2); /* SWAP */ + tim += 4; + } break; case 2: /* EXT */ nz(r[n] = r[n] & 0xffff0000 | (u16int)(s8int)r[n], 1); + tim += 4; break; case 3: /* EXT */ nz(r[n] = (s16int)r[n], 2); + tim += 4; break; } break; @@ -727,10 +818,12 @@ step(void) v = rmode(a, 0); nz(v, 0); wmode(a, s, v | 0x80); + tim += a < 0 ? 4 : 14; break; } /* TST */ a = amode(op >> 3, op, s); nz(rmode(a, s), s); + tim += 4; break; case 14: v = op >> 4 & 0xf; @@ -743,9 +836,11 @@ step(void) push32(ra[n]); ra[n] = ra[7]; ra[7] += (s16int)fetch16(); + tim += 16; }else{ /* UNLK */ ra[7] = ra[n]; ra[n] = pop32(); + tim += 12; } break; }else if(v == 6){ /* MOVE USP */ @@ -754,29 +849,33 @@ step(void) ra[n] = asp; else asp = ra[n]; + tim += 4; }else trap(8, curpc); break; } if((op & 0xc0) == 0xc0){ /* JMP */ pc = amode(op >> 3, op, 2); + tim += 4; break; } if((op & 0xc0) == 0x80){ /* JSR */ a = amode(op >> 3, op, 2); push32(pc); pc = a; + tim += 12; break; } switch(op){ - case 0x4e70: break; /* RESET */ - case 0x4e71: break; /* NOP */ + case 0x4e70: tim += 132; break; /* RESET */ + case 0x4e71: tim += 4; break; /* NOP */ case 0x4e72: /* STOP */ if((rS & FLAGS) != 0){ rS = fetch16(); stop = 1; }else trap(8, curpc); + tim += 4; break; case 0x4e73: /* RTE */ if((rS & FLAGS) != 0){ @@ -788,14 +887,16 @@ step(void) asp = ra[7]; ra[7] = v; } + tim += 20; }else trap(8, curpc); break; - case 0x4e75: pc = pop32(); break; /* RTS */ - case 0x4e76: if((rS & FLAGV) != 0) trap(7, curpc); break; /* TRAPV */ + case 0x4e75: pc = pop32(); tim += 16; break; /* RTS */ + case 0x4e76: if((rS & FLAGV) != 0) trap(7, curpc); tim += 4; break; /* TRAPV */ case 0x4e77: /* RTR */ rS = rS & 0xff00 | fetch16() & 0xff; pc = pop32(); + tim += 20; break; default: undef(); } @@ -812,14 +913,23 @@ step(void) if((u16int)r[n] != 0){ r[n]--; pc = pc + v - 2; - }else + tim += 10; + }else{ r[n] |= 0xffff; - } + tim += 14; + } + }else + tim += 12; break; } if(s == 3){ /* Scc */ a = amode(op >> 3, op, 0); - wmode(a, 0, -cond(op >> 8 & 0xf)); + v = cond(op >> 8 & 0xf); + wmode(a, 0, -v); + if(a < 0) + tim += 4 + 2 * v; + else + tim += 8; break; } /* ADDQ, SUBQ */ rS |= FLAGZ; @@ -833,6 +943,10 @@ step(void) v = add(v, n, 0, s); else v = sub(v, n, 0, s); + if(a < 0) + tim += s == 2 || (op & 0x130) == 0x110 ? 8 : 4; + else + tim += s == 2 ? 12 : 8; wmode(a, s, v); break; case 6: /* BRA */ @@ -844,14 +958,19 @@ step(void) if((op & 0xf00) == 0x100){ /* BSR */ push32(pc); pc = curpc + 2 + v; + tim += 18; break; } - if(cond((op >> 8) & 0xf)) + if(cond((op >> 8) & 0xf)){ pc = curpc + 2 + v; + tim += 10; + }else + tim += (u8int)(op + 1) <= 1 ? 12 : 8; break; case 7: /* MOVEQ */ r[n] = (s8int)op; nz(r[n], 0); + tim += 4; break; case 8: if(s == 3){ /* DIVU, DIVS */ @@ -870,6 +989,7 @@ step(void) rS = rS & ~FLAGC | FLAGV; break; } + tim += 158; }else{ w = r[n] % (u16int)v; v = r[n] / (u16int)v; @@ -877,6 +997,7 @@ step(void) rS = rS & ~FLAGC | FLAGV; break; } + tim += 140; } r[n] = (u16int)v | w << 16; nz(v, 1); @@ -891,8 +1012,11 @@ step(void) w = rmode(amode(4, m, 0), 0); v = subbcd(v, w); wmode(a, 0, v); - }else + tim += 18; + }else{ r[n] = r[n] & 0xffffff00 | subbcd((u8int)r[n], (u8int)r[m]); + tim += 6; + } break; } logic: /* OR, EOR, AND */ @@ -908,6 +1032,7 @@ step(void) a = ~n; wmode(a, s, v); nz(v, s); + dtime(op, s); break; case 11: if(s == 3){ /* CMPA */ @@ -915,18 +1040,21 @@ step(void) a = amode(op >> 3, op, s); rS |= FLAGZ; sub(ra[n], rmode(a, s), 0, 2); + tim += 6; break; } if((op & 0x138) == 0x108){ /* CMPM */ m = op & 7; rS |= FLAGZ; sub(rmode(amode(3, n, s), s), rmode(amode(3, m, s), s), 0, s); + tim += s == 2 ? 20 : 12; break; } if((op & 0x100) == 0){ /* CMP */ a = amode(op >> 3, op, s); rS |= FLAGZ; sub(r[n], rmode(a, s), 0, s); + tim += s == 2 ? 6 : 4; break; } goto logic; @@ -940,6 +1068,7 @@ step(void) v = (u16int)v * (u16int)r[n]; r[n] = v; nz(v, 1); + tim += 70; break; } if((op & 0x1f0) == 0x100){ /* ABCD */ @@ -951,8 +1080,11 @@ step(void) w = rmode(amode(4, m, 0), 0); v = addbcd(v, w); wmode(a, 0, v); - }else + tim += 18; + }else{ r[n] = r[n] & 0xffffff00 | addbcd((u8int)r[n], (u8int)r[m]); + tim += 6; + } break; } @@ -963,15 +1095,20 @@ step(void) v = r[n]; r[n] = r[m]; r[m] = v; + tim += 6; break; } goto logic; case 9: case 13: if(s == 3){ /* ADDA, SUBA */ - s = 1; - if((op & 0x100) != 0) - s++; + if((op & 0x100) != 0){ + s = 2; + tim += 6; + }else{ + s = 1; + tim += 8; + } a = amode(op >> 3, op, s); if((op >> 12) == 13) ra[n] += rmode(a, s); @@ -985,10 +1122,12 @@ step(void) a = ra[n] -= 1<> 12) == 13) v = add(v, w, (rS & FLAGX) != 0, s); @@ -1010,6 +1149,7 @@ step(void) if(d) a = ~n; wmode(a, s, v); + dtime(op, s); break; case 14: /* shifts */ if(s == 3){ @@ -1037,4 +1177,5 @@ step(void) default: undef(); } + return tim; } diff --git a/sys/src/games/md/dat.h b/sys/src/games/md/dat.h index e63e9263e..3df16bb2c 100644 --- a/sys/src/games/md/dat.h +++ b/sys/src/games/md/dat.h @@ -3,12 +3,13 @@ typedef signed short s16int; typedef signed long s32int; extern u32int curpc, irq; -extern int trace; +extern int trace, debug; extern u8int reg[32]; extern u8int dma; -extern u8int z80bus; +extern u8int z80bus, z80irq; +extern u16int spc, scurpc; extern u16int ram[32768]; extern u16int *prg; @@ -21,6 +22,8 @@ extern u32int cramc[64]; extern u16int vdpstat; extern int vdpx, vdpy; +extern u8int ym[512]; + enum { MODE1 = 0x00, MODE2 = 0x01, @@ -66,3 +69,14 @@ enum { INTVBL = 1, INTHOR = 2, }; + +enum { + FREQ = 53203400, + YMDIV = 7 * 6, + CPUDIV = 7, + Z80DIV = 15, + RATE = 44100, + SAMPDIV = FREQ / RATE, + MILLION = 1000 * 1000, + BILLION = 1000 * 1000 * 1000, +}; diff --git a/sys/src/games/md/fns.h b/sys/src/games/md/fns.h index 514f4fa9e..a20577a09 100644 --- a/sys/src/games/md/fns.h +++ b/sys/src/games/md/fns.h @@ -1,12 +1,20 @@ u16int memread(u32int); void memwrite(u32int, u16int, u16int); void cpureset(void); -void step(void); +int step(void); int z80step(void); u8int z80read(u16int); void z80write(u16int, u8int); +u8int z80in(u8int); +void z80out(u8int, u8int); int intack(int); void vdpstep(void); void flush(void); void dmastep(void); void vdpmode(void); +void ymwrite(u8int, u8int, u8int); +void initaudio(void); +void audiosample(void); +int audioout(void); +void ymreset(void); +void ymstep(void); diff --git a/sys/src/games/md/md.c b/sys/src/games/md/md.c index 07ac121cb..12d95dcd9 100644 --- a/sys/src/games/md/md.c +++ b/sys/src/games/md/md.c @@ -7,11 +7,15 @@ #include "dat.h" #include "fns.h" +int debug; + u16int *prg; int nprg; int keys; +int dmaclock, vdpclock, z80clock, audioclock, ymclock; + int scale, paused; QLock pauselock; Mousectl *mc; @@ -100,6 +104,7 @@ keyproc(void *) k = 0xc00; while(*s != 0){ s += chartorune(&r, s); + if(r >= '0' && r <= '9') debug = r - '0'; switch(r){ case Kdel: close(fd); threadexitsall(nil); case 'c': k |= 0x0020; break; @@ -126,8 +131,13 @@ keyproc(void *) void threadmain(int argc, char **argv) { + int t; + scale = 1; ARGBEGIN{ + case 'a': + initaudio(); + break; case '2': scale = 2; break; @@ -152,17 +162,39 @@ threadmain(int argc, char **argv) screeninit(); cpureset(); vdpmode(); + ymreset(); for(;;){ if(paused != 0){ qlock(&pauselock); qunlock(&pauselock); } - if(dma != 1) - step(); - if(dma != 0) + if(dma != 1){ + t = step() * CPUDIV; + if(dma != 0) + dmastep(); + }else{ + t = CPUDIV; dmastep(); - z80step(); - vdpstep(); + } + z80clock -= t; + vdpclock -= t; + audioclock += t; + ymclock += t; + + while(vdpclock < 0){ + vdpstep(); + vdpclock += 8; + } + while(z80clock < 0) + z80clock += z80step() * Z80DIV; + while(audioclock >= SAMPDIV){ + audiosample(); + audioclock -= SAMPDIV; + } + while(ymclock >= YMDIV){ + ymstep(); + ymclock -= YMDIV; + } } } @@ -174,7 +206,8 @@ flush(void) Rectangle r; uchar *s; int w; - + static vlong old, delta; + vlong new, diff; if(nbrecvul(mc->resizec) > 0){ if(getwindow(display, Refnone) < 0) @@ -199,4 +232,18 @@ flush(void) } } flushimage(display, 1); + if(audioout() < 0){ + new = nsec(); + diff = 0; + if(old != 0){ + diff = BILLION/60 - (new - old) - delta; + if(diff >= MILLION) + sleep(diff/MILLION); + } + old = nsec(); + if(diff != 0){ + diff = (old - new) - (diff / MILLION) * MILLION; + delta += (diff - delta) / 100; + } + } } diff --git a/sys/src/games/md/mem.c b/sys/src/games/md/mem.c index 6b0d05a81..e874d7f24 100644 --- a/sys/src/games/md/mem.c +++ b/sys/src/games/md/mem.c @@ -15,6 +15,8 @@ u8int dma; u8int vdplatch; u16int vdpaddr, vdpdata; +u8int yma1, yma2; + u8int z80bus = RESET; u16int z80bank; @@ -38,7 +40,8 @@ regread(u16int a) return ctl[1] & 0xc0 | 0x3f; case 0x0009: case 0x000b: case 0x000d: return ctl[a-3>>1]; - case 0x1101: return (~z80bus & BUSACK) << 7; + case 0x1101: + return (~z80bus & BUSACK) >> 1; } sysfatal("read from 0xa1%.4ux (pc=%#.6ux)", a, curpc); return 0; @@ -53,10 +56,10 @@ regwrite(u16int a, u16int v) ctl[a-3>>1] = v; return; case 0x1101: - z80bus = z80bus & ~BUSREQ | v >> 8 & BUSREQ; + z80bus = z80bus & ~BUSREQ | v & BUSREQ; return; case 0x1201: - if((v & 1<<8) == 0){ + if((v & 1) == 0){ z80bus |= RESET; z80bus &= ~BUSACK; }else @@ -187,7 +190,7 @@ memwrite(u32int a, u16int v, u16int m) switch(a >> 16 & 0xff){ case 0xa0: if((z80bus & BUSACK) != 0) - z80write(a & 0x7fff, v >> 8); + z80write(a & 0xffff, v >> 8); return; case 0xa1: regwrite(a, v >> 8); @@ -318,15 +321,27 @@ dmastep(void) u8int z80read(u16int a) { + u16int v; + switch(a >> 13){ case 0: case 1: return zram[a & 0x1fff]; case 2: + return 0; case 3: - sysfatal("z80 read from %#.4x\n", a); + if(a >= 0x7f00){ + v = memread(0xc00000 | a & 0x7e); + if((a & 1) == 0) + v >>= 8; + return v; + } + sysfatal("z80 read from %#.4x (pc=%#.4x)", a, scurpc); default: - return memread(z80bank << 15 | a & 0x7fff); + v = memread(z80bank << 15 | a & 0x7ffe); + if((a & 1) == 0) + v >>= 8; + return v; } } @@ -339,13 +354,38 @@ z80write(u16int a, u8int v) zram[a & 0x1fff] = v; return; case 2: + switch(a & 3){ + case 0: yma1 = v; return; + case 1: ymwrite(yma1, v, 0); return; + case 2: yma2 = v; return; + case 3: ymwrite(yma2, v, 3); return; + } case 3: - sysfatal("z80 write to %#.4x\n", a); + if(a < 0x6100){ + z80bank = z80bank >> 1 | v << 8 & 0x100; + return; + } + if(a >= 0x7f00){ + memwrite(0xc00000 | a & 0x7e, v | v << 8, (a & 1) != 0 ? 0xff : 0xff00); + return; + } + sysfatal("z80 write to %#.4x (pc=%#.4x)", a, scurpc); default: memwrite(z80bank << 15 | a & 0x7ffe, v << 8 | v, (a & 1) != 0 ? 0xff : 0xff00); } } +u8int +z80in(u8int) +{ + return 0xff; +} + +void +z80out(u8int, u8int) +{ +} + u32int irql[8] = {[6] INTVBL, [4] INTHOR}; int diff --git a/sys/src/games/md/mkfile b/sys/src/games/md/mkfile index 1be8902af..b8363745a 100644 --- a/sys/src/games/md/mkfile +++ b/sys/src/games/md/mkfile @@ -8,6 +8,7 @@ OFILES=\ md.$O\ vdp.$O\ z80.$O\ + ym.$O\ HFILES=dat.h fns.h diff --git a/sys/src/games/md/vdp.c b/sys/src/games/md/vdp.c index c53d63c51..8230a8cdd 100644 --- a/sys/src/games/md/vdp.c +++ b/sys/src/games/md/vdp.c @@ -310,6 +310,7 @@ vdpstep(void) }else pixeldraw(vdpx, vdpy, 0xcccccc); if(++vdpx >= xmax){ + z80irq = 0; vdpx = 0; if(++vdpy >= ymax){ vdpy = 0; @@ -328,6 +329,7 @@ vdpstep(void) } if(vdpy == yvbl){ vdpstat |= STATVBL | STATINT; + z80irq = 1; if((reg[MODE2] & IE0) != 0) irq |= INTVBL; } diff --git a/sys/src/games/md/ym.c b/sys/src/games/md/ym.c new file mode 100644 index 000000000..835c1881d --- /dev/null +++ b/sys/src/games/md/ym.c @@ -0,0 +1,420 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +extern int debug; + +u8int ym[512]; +enum { + MODE = 0x27, + FDIV = 1048576 * 2 * 144, +}; + +#define min(a, b) ((a)<=(b)?(a):(b)) + +typedef struct oper oper; +typedef struct fm fm; + +struct oper { + enum {OFF, ATTACK, DECAY, SUSTAIN, RELEASE} st; + u8int *r; + int env; + u8int ar, sr, dr, rs, rr, mult; + u16int tl, sl; + u8int keyon, keyoff, rate; + int det; + u8int kc; + u32int phi, amp, dp; + int val, val0; +}; + +struct fm { + oper op[4]; + u8int st, alg, en, fbs; + float samp; +} fms[6]; +static u32int cyc; + +static short sbuf[2 * 2000], *sbufp; +static int fd; +static int sint[256], expt[256]; + +static void +calcfreq(int n) +{ + int i, fr; + fm *f; + oper *p; + uchar *r; + static u8int det[4][32] = { + {0}, + {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8}, + {1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16}, + {2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, + 8, 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22} + }; + + r = ym; + f = &fms[n]; + if(n >= 3){ + n -= 3; + r += 0x100; + } + fr = r[0xa0 + n] | r[0xa4 + n] << 8 & 0x3f00; + for(i = 0; i < 3; i++){ + p = &f->op[i]; + if(n == 2 && (ym[MODE] & 0xc0) == 0x40 && i != 0) + fr = r[0xa7+i] | r[0xab+i] << 8 & 0x3f00; + p->kc = fr >> 9 & 0x1e; + if((fr & 0x780) >= 0x380 && (fr & 0x780) != 0x400) + p->kc |= 1; + p->dp = ((fr & 0x7ff) << (fr >> 11)) >> 1; + if((p->det & 4) != 0) + p->dp -= det[p->det & 3][p->kc]; + else + p->dp += det[p->det][p->kc]; + if(p->mult != 0) + p->dp = (p->dp & 0x1ffff) * p->mult; + else + p->dp = (u16int)(p->dp >> 1); + + } +} + +void +ymwrite(u8int a, u8int v, u8int c) +{ + int ch, i; + oper *p; + fm *f; + + ym[c ? a|0x100 : a] = v; + if(a >= 0x30 && a < 0xa0){ + ch = a & 3; + if(ch == 3) + return; + ch += c; + f = &fms[ch]; + i = a >> 3 & 1 | a >> 1 & 2; + p = &f->op[i]; + switch(a & 0xf0){ + case 0x30: + p->mult = v & 0x0f; + p->det = v >> 4 & 7; + calcfreq(ch); + break; + case 0x40: + p->tl = v << 3 & 0x3f8; + break; + case 0x50: + p->ar = v << 1 & 0x3e; + p->rs = 3 - (v >> 6); + break; + case 0x60: + p->dr = v << 1 & 0x3e; + break; + case 0x70: + p->sr = v << 1 & 0x3e; + break; + case 0x80: + p->sl = v << 2 & 0x3c0; + p->rr = v << 2 & 0x3c | 0x02; + break; + }; + }else{ + ch = c + (a & 3); + switch(a){ + case MODE: + calcfreq(2); + calcfreq(5); + break; + case 0x28: + ch = v & 3; + if(ch == 3) + break; + if((v & 4) != 0) + ch += 3; + f = &fms[ch]; + for(i = 0; i < 4; i++){ + p = &f->op[i]; + if((v & 1<<4+i) != 0){ + if(p->st == OFF || p->st == RELEASE) + p->keyon++; + }else + if(p->st != OFF) + p->keyoff++; + } + break; + case 0xa0: case 0xa1: case 0xa2: + case 0xa4: case 0xa5: case 0xa6: + calcfreq(ch); + break; + case 0xa8: case 0xa9: case 0xaa: + case 0xac: case 0xad: case 0xae: + calcfreq((a & 0x100) != 0 ? 5 : 2); + break; + case 0xb0: case 0xb1: case 0xb2: + fms[ch].alg = v & 7; + fms[ch].fbs = 7 - (v >> 3 & 7); + break; + case 0xb4: case 0xb5: case 0xb6: + fms[ch].en = v; + break; + } + } +} + +static void +tables(void) +{ + int i; + double x; + + for(i = 0; i < 256; i++){ + x = sin(((i << 1) + 1) * PI / 1024); + x = -log(x)/log(2); + sint[i] = x * 256 + 0.5; + } + for(i = 0; i < 256; i++){ + x = pow(2, -(i+1)/256.0); + expt[i] = x * 2048 + 0.5; + } +} + +void +ymreset(void) +{ + int i, j; + + for(i = 0; i < 6; i++){ + fms[i].en = 0xc0; + for(j = 0; j < 4; j++) + fms[i].op[j].rs = 3; + } + tables(); +} + +static u8int +rate(oper *p, u8int r) +{ + if(r == 0) + return 0; + r += p->kc >> p->rs; + if(r > 63) + return 63; + return r; +} + +static void +env(oper *p) +{ + int v, sh, ai; + static u8int ait[64][8] = { + {0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0}, {0,1,0,1,0,1,0,1}, {0,1,0,1,0,1,0,1}, + {0,1,0,1,0,1,0,1}, {0,1,0,1,0,1,0,1}, {0,1,1,1,0,1,1,1}, {0,1,1,1,0,1,1,1}, + {0,1,0,1,0,1,0,1}, {0,1,0,1,1,1,0,1}, {0,1,1,1,0,1,1,1}, {0,1,1,1,1,1,1,1}, + {0,1,0,1,0,1,0,1}, {0,1,0,1,1,1,0,1}, {0,1,1,1,0,1,1,1}, {0,1,1,1,1,1,1,1}, + {0,1,0,1,0,1,0,1}, {0,1,0,1,1,1,0,1}, {0,1,1,1,0,1,1,1}, {0,1,1,1,1,1,1,1}, + {0,1,0,1,0,1,0,1}, {0,1,0,1,1,1,0,1}, {0,1,1,1,0,1,1,1}, {0,1,1,1,1,1,1,1}, + {0,1,0,1,0,1,0,1}, {0,1,0,1,1,1,0,1}, {0,1,1,1,0,1,1,1}, {0,1,1,1,1,1,1,1}, + {0,1,0,1,0,1,0,1}, {0,1,0,1,1,1,0,1}, {0,1,1,1,0,1,1,1}, {0,1,1,1,1,1,1,1}, + {0,1,0,1,0,1,0,1}, {0,1,0,1,1,1,0,1}, {0,1,1,1,0,1,1,1}, {0,1,1,1,1,1,1,1}, + {0,1,0,1,0,1,0,1}, {0,1,0,1,1,1,0,1}, {0,1,1,1,0,1,1,1}, {0,1,1,1,1,1,1,1}, + {0,1,0,1,0,1,0,1}, {0,1,0,1,1,1,0,1}, {0,1,1,1,0,1,1,1}, {0,1,1,1,1,1,1,1}, + {0,1,0,1,0,1,0,1}, {0,1,0,1,1,1,0,1}, {0,1,1,1,0,1,1,1}, {0,1,1,1,1,1,1,1}, + {1,1,1,1,1,1,1,1}, {1,1,1,2,1,1,1,2}, {1,2,1,2,1,2,1,2}, {1,2,2,2,1,2,2,2}, + {2,2,2,2,2,2,2,2}, {2,2,2,4,2,2,2,4}, {2,4,2,4,2,4,2,4}, {2,4,4,4,2,4,4,4}, + {4,4,4,4,4,4,4,4}, {4,4,4,8,4,4,4,8}, {4,8,4,8,4,8,4,8}, {4,8,8,8,4,8,8,8}, + {8,8,8,8,8,8,8,8}, {8,8,8,8,8,8,8,8}, {8,8,8,8,8,8,8,8}, {8,8,8,8,8,8,8,8}, + }; + if(p->keyon > 0){ + p->st = ATTACK; + p->rate = rate(p, p->ar); + p->env = 0; + p->keyon = 0; + }else if(p->keyoff > 0){ + p->st = RELEASE; + p->rate = rate(p, p->rr); + p->keyoff = 0; + } + if(p->rate > 0){ + sh = p->rate >> 2; + if(sh < 11) + sh = 11 - sh; + else + sh = 0; + if((cyc & (1 << sh) - 1) == 0){ + ai = ait[p->rate][(cyc >> sh) & 7]; + switch(p->st){ + case ATTACK: + p->env += ai * ((1024 - p->env >> 4) + 1); + if(p->env >= 1024){ + p->env = 0; + p->st = DECAY; + p->rate = rate(p, p->dr); + } + break; + case DECAY: + p->env += ai; + if(p->env >= 1024) + p->env = 1023; + if(p->env >= p->sl){ + p->st = SUSTAIN; + p->rate = rate(p, p->sr); + } + break; + case SUSTAIN: + case RELEASE: + p->env += ai; + if(p->env >= 1024){ + p->env = 1023; + p->st = OFF; + p->rate = 0; + } + break; + } + } + } + if(p->st == OFF){ + p->amp = 1023; + return; + } + v = p->env; + if(p->st == ATTACK) + v ^= 1023; + v += p->tl; + if(v > 1023) + p->amp = 1023; + else + p->amp = v; +} + +static void +opr(oper *p, int inp) +{ + int v, x, r; + + p->phi += p->dp; + v = (p->phi >> 10) + inp; + if((v & 0x100) != 0) + v ^= 0xff; + x = sint[v & 0xff] + (p->amp << 2); + r = expt[x & 0xff] << 2 >> (x >> 8); + p->val0 = p->val; + if((v & 0x200) != 0) + p->val = -r; + else + p->val = r; +} + +void +ymstep(void) +{ + static int ymch, ymop, ymcyc; + fm *f; + oper *p; + int x; + + f = fms + ymch; + p = f->op + ymop; + x = 0; + if(ymop == 0){ + if(f->fbs != 7) + x = p->val + p->val0 >> f->fbs + 2; + }else{ + switch(f->alg << 4 | ymop){ + default: x = p[-1].val; break; + case 0x11: break; + case 0x12: x = f->op[0].val + f->op[1].val; break; + case 0x21: break; + case 0x23: x = f->op[0].val + f->op[2].val; break; + case 0x32: break; + case 0x33: x = f->op[1].val + f->op[2].val; break; + case 0x42: break; + case 0x52: case 0x53: x = f->op[0].val; break; + case 0x62: case 0x63: break; + case 0x71: case 0x72: case 0x73: break; + } + x >>= 1; + } + if(ymcyc == 0) + env(p); + opr(p, x); + if(ymop == 3){ + switch(f->alg){ + default: x = p->val >> 5; break; + case 4: x = (f->op[1].val >> 5) + (p->val >> 5); break; + case 5: case 6: x = (f->op[1].val >> 5) + (f->op[2].val >> 5) + (p->val >> 5); break; + case 7: x = (f->op[0].val >> 5) + (f->op[1].val >> 5) + (f->op[2].val >> 5) + (p->val >> 5); break; + } + if(x > 256) x = 256; + if(x < -256) x = -256; + f->samp = x / 256.0; + if(++ymch == 6){ + ymch = 0; + if(++ymcyc == 3){ + cyc++; + ymcyc = 0; + } + } + ymop = 0; + }else + ymop++; +} + +void +audiosample(void) +{ + int i; + u8int x; + float v, vl, vr; + + if(sbufp == nil) + return; + vl = vr = 0; + for(i = 0; i < 6; i++){ + if(i == 5 && (ym[0x2b] & 0x80) != 0) + v = ym[0x2a] / 255.0; + else + v = fms[i].samp; + x = fms[i].en; + if((x & 0x80) != 0) + vl += v; + if((x & 0x40) != 0) + vr += v; + } + if(sbufp < sbuf + nelem(sbuf) - 1){ + *sbufp++ = vl * 5000; + *sbufp++ = vr * 5000; + } +} + +int +audioout(void) +{ + int rc; + + if(sbufp == nil) + return -1; + if(sbufp == sbuf) + return 0; + rc = write(fd, sbuf, (sbufp - sbuf) * 2); + if(rc > 0) + sbufp -= (rc+1)/2; + if(sbufp < sbuf) + sbufp = sbuf; + return 0; +} + +void +initaudio(void) +{ + fd = open("/dev/audio", OWRITE); + if(fd < 0) + return; + sbufp = sbuf; +} diff --git a/sys/src/games/md/z80.c b/sys/src/games/md/z80.c index 4d990a0fc..61a3716c5 100644 --- a/sys/src/games/md/z80.c +++ b/sys/src/games/md/z80.c @@ -4,10 +4,593 @@ #include "dat.h" #include "fns.h" +u8int s[16], ipage, intm, z80irq; +u16int ix[2]; +u16int spc, scurpc, sp; +int halt; + +enum { + FLAGC = 0x01, + FLAGN = 0x02, + FLAGV = 0x04, + FLAGH = 0x10, + FLAGZ = 0x40, + FLAGS = 0x80 +}; +enum { rB, rC, rD, rE, rH, rL, rHL, rA, rF = rHL }; +#define BC() (s[rB] << 8 | s[rC]) +#define DE() (s[rD] << 8 | s[rE]) +#define HL() (s[rH] << 8 | s[rL]) + +static u8int +fetch8(void) +{ + return z80read(spc++); +} + +static u16int +fetch16(void) +{ + u16int u; + + u = z80read(spc++); + return u | z80read(spc++) << 8; +} + +static void +push8(u8int u) +{ + z80write(--sp, u); +} + +static void +push16(u16int u) +{ + z80write(--sp, u >> 8); + z80write(--sp, u); +} + +static u8int +pop8(void) +{ + return z80read(sp++); +} + +static u16int +pop16(void) +{ + u16int v; + + v = z80read(sp++); + return v | z80read(sp++) << 8; +} + +static u16int +read16(u16int n) +{ + return z80read(n) | z80read(n+1) << 8; +} + +static void +write16(u16int n, u16int v) +{ + z80write(n++, v); + z80write(n, v >> 8); +} + +static int +parity(u8int v) +{ + return (((v * 0x0101010101010101ULL) & 0x8040201008040201ULL) % 0x1FF) & 1; +} + +static int +move(u8int dst, u8int src) +{ + if(dst == rHL){ + if(src == rHL){ + halt = 1; + return 4; + } + z80write(HL(), s[src]); + return 7; + } + if(src == rHL){ + s[dst] = z80read(HL()); + return 7; + } + s[dst] = s[src]; + return 4; +} + +static int +alu(u8int op, u8int n) +{ + u8int v4; + u8int u; + u16int v; + int t; + + switch(n){ + case 8: u = fetch8(); t = 3; break; + case 10: u = ix[0]; t = 4; break; + case 11: u = ix[1]; t = 4; break; + case 12: u = ix[0] >> 8; t = 4; break; + case 13: u = ix[1] >> 8; t = 4; break; + case 14: u = z80read(ix[0] + fetch8()); t = 15; break; + case 15: u = z80read(ix[1] + fetch8()); t = 15; break; + case rHL: + u = z80read(HL()); + t = 3; + break; + default: + u = s[n]; + t = 0; + } + v4 = 0; + switch(op){ + default: + v4 = (s[rA] & 0x0f) + (u & 0x0f); + v = s[rA] + u; + break; + case 1: + v4 = (s[rA] & 0x0f) + (u & 0x0f) + (s[rF] & 1); + v = s[rA] + u + (s[rF] & 1); + break; + case 2: + case 7: + v4 = (s[rA] & 0x0f) + (~u & 0x0f) + 1; + v = s[rA] + (u ^ 0xff) + 1; + break; + case 3: + v4 = (s[rA] & 0x0f) + (~u & 0x0f) + (~s[rF] & 1); + v = s[rA] + (u ^ 0xff) + (~s[rF] & 1); + break; + case 4: v = s[rA] & u; break; + case 5: v = s[rA] ^ u; break; + case 6: v = s[rA] | u; break; + } + s[rF] = 0; + if((u8int)v == 0) + s[rF] |= FLAGZ; + if((v & 0x80) != 0) + s[rF] |= FLAGS; + if(op < 2){ + if((v & 0x100) != 0) + s[rF] |= FLAGC; + if((v4 & 0x10) != 0) + s[rF] |= FLAGH; + if((~(s[rA] ^ u) & (s[rA] ^ v) & 0x80) != 0) + s[rF] |= FLAGV; + }else if(op < 4 || op == 7){ + s[rF] |= FLAGN; + if((v & 0x100) == 0) + s[rF] |= FLAGC; + if((v4 & 0x10) == 0) + s[rF] |= FLAGH; + if(((s[rA] ^ u) & (s[rA] ^ v) & 0x80) != 0) + s[rF] |= FLAGV; + }else{ + if(!parity(v)) + s[rF] |= FLAGV; + if(op == 4) + s[rF] |= FLAGH; + } + if(op != 7) + s[rA] = v; + return 4+t; +} + +static int +branch(int cc, int t) +{ + u16int v; + + v = (s8int)fetch8(); + if(!cc) + return t + 7; + spc += v; + return t + 12; +} + +static u8int +inc(u8int v) +{ + s[rF] &= FLAGC; + ++v; + if(v == 0) + s[rF] |= FLAGZ; + if((v & 0x80) != 0) + s[rF] |= FLAGS; + if(v == 0x80) + s[rF] |= FLAGV; + if((v & 0xf) == 0) + s[rF] |= FLAGH; + return v; +} + +static u8int +dec(u8int v) +{ + --v; + s[rF] = s[rF] & FLAGC | FLAGN; + if(v == 0) + s[rF] |= FLAGZ; + if((v & 0x80) != 0) + s[rF] |= FLAGS; + if(v == 0x7f) + s[rF] |= FLAGV; + if((v & 0xf) == 0xf) + s[rF] |= FLAGH; + return v; +} + +static int +addhl(u16int u) +{ + u32int v; + + s[rF] &= ~(FLAGN|FLAGC|FLAGH); + v = HL() + u; + if((v & 0x10000) != 0) + s[rF] |= FLAGC; + if((HL() & 0xfff) + (u & 0xfff) >= 0x1000) + s[rF] |= FLAGH; + s[rL] = v; + s[rH] = v >> 8; + return 11; +} + +static void +adchl(u16int u) +{ + u32int v, v4; + + v = HL() + u + (s[rF] & FLAGC); + v4 = (HL() & 0xfff) + (u & 0xfff) + (s[rF] & FLAGC); + s[rF] = 0; + if((v & 0x10000) != 0) + s[rF] |= FLAGC; + if((v4 & 0x1000) != 0) + s[rF] |= FLAGH; + if((u16int)v == 0) + s[rF] |= FLAGZ; + if((v & 0x8000) != 0) + s[rF] |= FLAGS; + if((~(HL() ^ u) & (HL() ^ v) & 0x8000) != 0) + s[rF] |= FLAGV; + s[rL] = v; + s[rH] = v >> 8; +} + +static void +sbchl(u16int u) +{ + u32int v, v4; + + v = HL() + (u16int)~u + (~s[rF] & FLAGC); + v4 = (HL() & 0xfff) + (~u & 0xfff) + (~s[rF] & FLAGC); + s[rF] = FLAGN; + if((v & 0x10000) == 0) + s[rF] |= FLAGC; + if((v4 & 0x1000) == 0) + s[rF] |= FLAGH; + if((u16int)v == 0) + s[rF] |= FLAGZ; + if((v & 0x8000) != 0) + s[rF] |= FLAGS; + if(((HL() ^ u) & (HL() ^ v) & 0x8000) != 0) + s[rF] |= FLAGV; + s[rL] = v; + s[rH] = v >> 8; +} + +static int +addindex(int n, u16int u) +{ + u32int v; + + s[rF] &= ~(FLAGN|FLAGC|FLAGH); + v = ix[n] + u; + if((v & 0x10000) != 0) + s[rF] |= FLAGC; + if((ix[n] & 0xfff) + (u & 0xfff) >= 0x1000) + s[rF] |= FLAGH; + ix[n] = v; + return 15; +} + +static int +jump(int cc) +{ + u16int v; + + v = fetch16(); + if(cc) + spc = v; + return 10; +} + +static int +call(u16int a, int cc) +{ + if(!cc) + return 10; + push16(spc); + spc = a; + return 17; +} + +static void +swap(u8int a) +{ + u8int v; + + v = s[a]; + s[a] = s[a + 8]; + s[a + 8] = v; +} + +static int +bits(int i) +{ + u8int op, v, n, m, c; + u16int a; + int t; + + SET(a, v, t); + if(i >= 0){ + a = ix[i] + fetch8(); + v = z80read(a); + t = 23; + } + op = fetch8(); + n = op & 7; + m = op >> 3 & 7; + if(i < 0){ + a = HL(); + if(n == 6){ + v = z80read(a); + t = 15; + }else{ + v = s[n]; + t = 8; + } + } + switch(op >> 6){ + case 0: + c = s[rF] & FLAGC; + switch(m){ + default: s[rF] = v >> 7; v = v << 1 | v >> 7; break; + case 1: s[rF] = v & 1; v = v >> 1 | v << 7; break; + case 2: s[rF] = v >> 7; v = v << 1 | c; break; + case 3: s[rF] = v & 1; v = v >> 1 | c << 7; break; + case 4: s[rF] = v >> 7; v = v << 1; break; + case 5: s[rF] = v & 1; v = v & 0x80 | v >> 1; break; + case 6: s[rF] = v >> 7; v = v << 1 | 1; break; + case 7: s[rF] = v & 1; v >>= 1; break; + } + if(v == 0) + s[rF] |= FLAGZ; + if((v & 0x80) != 0) + s[rF] |= FLAGS; + if(!parity(v)) + s[rF] |= FLAGV; + break; + case 1: + s[rF] = s[rF] & ~(FLAGN|FLAGZ) | FLAGH; + if((v & 1<> 4 | s[rA] << 4); + s[rA] = s[rA] & 0xf0 | v & 0x0f; + if(0){ + case 0x6f: + v = z80read(HL()); + z80write(HL(), v << 4 | s[rA] & 0xf); + s[rA] = s[rA] & 0xf0 | v >> 4; + } + s[rF] &= FLAGC; + if(s[rA] == 0) + s[rF] |= FLAGZ; + if((s[rA] & 0x80) != 0) + s[rF] |= FLAGS; + if(!parity(s[rA])) + s[rF] |= FLAGV; + return 18; + case 0x4a: adchl(BC()); return 15; + case 0x5a: adchl(DE()); return 15; + case 0x6a: adchl(HL()); return 15; + case 0x7a: adchl(sp); return 15; + case 0x4b: a = fetch16(); s[rC] = z80read(a++); s[rB] = z80read(a); return 20; + case 0x5b: a = fetch16(); s[rE] = z80read(a++); s[rD] = z80read(a); return 20; + case 0x6b: a = fetch16(); s[rL] = z80read(a++); s[rH] = z80read(a); return 20; + case 0x7b: sp = read16(fetch16()); return 20; + case 0x5e: intm = intm & 0xc0 | 2; return 8; + case 0x4f: return 9; + } + sysfatal("undefined z80 opcode ed%.2x at pc=%#.4x", op, scurpc); + return 0; +} + +static int +index(int n) +{ + u8int op; + u16int v; + + op = fetch8(); + switch(op){ + case 0x40: case 0x41: case 0x42: case 0x43: case 0x47: + case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4f: + case 0x50: case 0x51: case 0x52: case 0x53: case 0x57: + case 0x58: case 0x59: case 0x5a: case 0x5b: case 0x5f: + case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7f: + s[op >> 3 & 7] = s[op & 7]; + return 8; + case 0x60: case 0x61: case 0x62: case 0x63: case 0x67: + ix[n] = ix[n] & 0xff | s[op & 7] << 8; + return 8; + case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6f: + ix[n] = ix[n] & 0xff00 | s[op & 7]; + return 8; + case 0x65: ix[n] = ix[n] << 8 | ix[n] & 0xff; return 8; + case 0x6c: ix[n] = ix[n] >> 8 | ix[n] & 0xff00; return 8; + case 0x64: case 0x6d: return 8; + case 0x70: case 0x71: case 0x72: case 0x73: + case 0x74: case 0x75: case 0x77: + z80write(ix[n] + fetch8(), s[op & 7]); + return 19; + case 0x44: case 0x4c: case 0x54: case 0x5c: case 0x7c: + s[op >> 3 & 7] = ix[n] >> 8; + return 8; + case 0x45: case 0x4d: case 0x55: case 0x5d: case 0x7d: + s[op >> 3 & 7] = ix[n]; + return 8; + case 0x46: case 0x4e: case 0x56: case 0x5e: case 0x66: case 0x6e: case 0x7e: + s[op >> 3 & 7] = z80read(ix[n] + fetch8()); + return 19; + case 0x84: case 0x8c: case 0x94: case 0x9c: + case 0xa4: case 0xac: case 0xb4: case 0xbc: + return alu(op >> 3 & 7, 12 + n); + case 0x85: case 0x8d: case 0x95: case 0x9d: + case 0xa5: case 0xad: case 0xb5: case 0xbd: + return alu(op >> 3 & 7, 10 + n); + case 0x86: case 0x8e: case 0x96: case 0x9e: + case 0xa6: case 0xae: case 0xb6: case 0xbe: + return alu(op >> 3 & 7, 14 + n); + + case 0x21: ix[n] = fetch16(); return 14; + case 0xe1: ix[n] = pop16(); return 14; + case 0x22: write16(fetch16(), ix[n]); return 20; + case 0x23: ix[n]++; return 10; + case 0xe3: v = ix[n]; ix[n] = read16(sp); write16(sp, v); return 23; + case 0x24: inc(ix[n] >> 8); ix[n] += 0x100; return 8; + case 0x34: v = ix[n] + fetch8(); z80write(v, inc(z80read(v))); return 23; + case 0x25: dec(ix[n] >> 8); ix[n] -= 0x100; return 8; + case 0x35: v = ix[n] + fetch8(); z80write(v, dec(z80read(v))); return 23; + case 0xe5: push16(ix[n]); return 15; + case 0x26: ix[n] = ix[n] & 0xff | fetch8() << 8; return 11; + case 0x36: v = ix[n] + fetch8(); z80write(v, fetch8()); return 19; + case 0x09: return addindex(n, BC()); + case 0x19: return addindex(n, DE()); + case 0x29: return addindex(n, ix[n]); + case 0x39: return addindex(n, sp); + case 0xe9: spc = ix[n]; return 8; + case 0xf9: sp = ix[n]; return 10; + case 0x2a: ix[n] = read16(fetch16()); return 20; + case 0x2b: ix[n]--; return 10; + case 0xcb: return bits(n); + case 0x2c: inc(ix[n]++); return 8; + case 0x2d: dec(ix[n]--); return 8; + case 0x2e: ix[n] = ix[n] & 0xff00 | fetch8(); return 11; + } + sysfatal("undefined z80 opcode %.2x%.2x at pc=%#.4x", n ? 0xfd : 0xdd, op, scurpc); + return 0; +} + int z80step(void) { + u8int op; + u16int v, w; + if((z80bus & RESET) != 0){ + scurpc = spc = 0; + intm = 0; + ipage = 0; return 1; } if((z80bus & BUSACK) != 0){ @@ -19,5 +602,219 @@ z80step(void) z80bus |= BUSACK; return 1; } + if(z80irq != 0 && (intm & 0x80) != 0){ + push16(spc); + intm &= 0x3f; + switch(intm & 3){ + case 1: + spc = 0x38; + return 2; + default: + sysfatal("z80 interrupt in mode %d", intm & 3); + } + } + scurpc = spc; + if(0) + print("%x AF %.2x%.2x BC %.2x%.2x DE %.2x%.2x HL %.2x%.2x IX %.4x IY %.4x\n", scurpc, s[rA], s[rF], s[rB], s[rC], s[rD], s[rE], s[rH], s[rL], ix[0], ix[1]); + op = fetch8(); + switch(op >> 6){ + case 1: return move(op >> 3 & 7, op & 7); + case 2: return alu(op >> 3 & 7, op & 7); + } + switch(op){ + case 0x00: return 4; + case 0x10: return branch(--s[rB] != 0, 1); + case 0x20: return branch((s[rF] & FLAGZ) == 0, 0); + case 0x30: return branch((s[rF] & FLAGC) == 0, 0); + case 0x01: s[rC] = fetch8(); s[rB] = fetch8(); return 10; + case 0x11: s[rE] = fetch8(); s[rD] = fetch8(); return 10; + case 0x21: s[rL] = fetch8(); s[rH] = fetch8(); return 10; + case 0x31: sp = fetch16(); return 10; + case 0x02: z80write(BC(), s[rA]); return 7; + case 0x12: z80write(DE(), s[rA]); return 7; + case 0x22: v = fetch16(); z80write(v++, s[rL]); z80write(v, s[rH]); return 16; + case 0x32: z80write(fetch16(), s[rA]); return 13; + case 0x03: if(++s[rC] == 0) s[rB]++; return 6; + case 0x13: if(++s[rE] == 0) s[rD]++; return 6; + case 0x23: if(++s[rL] == 0) s[rH]++; return 6; + case 0x33: sp++; return 6; + case 0x04: inc(s[rB]++); return 4; + case 0x14: inc(s[rD]++); return 4; + case 0x24: inc(s[rH]++); return 4; + case 0x34: z80write(HL(), inc(z80read(HL()))); return 11; + case 0x05: dec(s[rB]--); return 4; + case 0x15: dec(s[rD]--); return 4; + case 0x25: dec(s[rH]--); return 4; + case 0x35: z80write(HL(), dec(z80read(HL()))); return 11; + case 0x06: s[rB] = fetch8(); return 7; + case 0x16: s[rD] = fetch8(); return 7; + case 0x26: s[rH] = fetch8(); return 7; + case 0x36: z80write(HL(), fetch8()); return 10; + case 0x07: + s[rF] = s[rF] & ~(FLAGC|FLAGN|FLAGH) | s[rA] >> 7; + s[rA] = s[rA] << 1 | s[rA] >> 7; + return 4; + case 0x17: + v = s[rF] & FLAGC; + s[rF] = s[rF] & ~(FLAGC|FLAGN|FLAGH) | s[rA] >> 7; + s[rA] = s[rA] << 1 | v; + return 4; + case 0x27: + if(s[rA] > 0x99 || (s[rF] & FLAGC) != 0){ + s[rF] |= FLAGC; + v = 0x60; + }else{ + s[rF] &= ~FLAGC; + v = 0; + } + if((s[rA] & 0xf) > 9 || (s[rF] & FLAGH) != 0) + v |= 6; + w = s[rA]; + if((s[rF] & FLAGN) != 0) + s[rA] -= v; + else + s[rA] += v; + s[rF] &= ~(FLAGV|FLAGS|FLAGZ|FLAGH); + if(((s[rA] ^ w) & 0x10) != 0) + s[rF] |= FLAGH; + if(!parity(s[rA])) + s[rF] |= FLAGV; + if(s[rA] == 0) + s[rF] |= FLAGZ; + if((s[rA] & 0x80) != 0) + s[rF] |= FLAGS; + return 4; + case 0x37: s[rF] = s[rF] & ~(FLAGN | FLAGH) | FLAGC; return 4; + case 0x08: + swap(rA); + swap(rF); + return 4; + case 0x18: return branch(1, 0); + case 0x28: return branch((s[rF] & FLAGZ) != 0, 0); + case 0x38: return branch((s[rF] & FLAGC) != 0, 0); + case 0x09: return addhl(BC()); + case 0x19: return addhl(DE()); + case 0x29: return addhl(HL()); + case 0x39: return addhl(sp); + case 0x0a: s[rA] = z80read(BC()); return 7; + case 0x1a: s[rA] = z80read(DE()); return 7; + case 0x2a: v = fetch16(); s[rL] = z80read(v++); s[rH] = z80read(v); return 16; + case 0x3a: s[rA] = z80read(fetch16()); return 13; + case 0x0b: if(s[rC]-- == 0) s[rB]--; return 6; + case 0x1b: if(s[rE]-- == 0) s[rD]--; return 6; + case 0x2b: if(s[rL]-- == 0) s[rH]--; return 6; + case 0x3b: sp--; return 6; + case 0x0c: inc(s[rC]++); return 4; + case 0x1c: inc(s[rE]++); return 4; + case 0x2c: inc(s[rL]++); return 4; + case 0x3c: inc(s[rA]++); return 4; + case 0x0d: dec(s[rC]--); return 4; + case 0x1d: dec(s[rE]--); return 4; + case 0x2d: dec(s[rL]--); return 4; + case 0x3d: dec(s[rA]--); return 4; + case 0x0e: s[rC] = fetch8(); return 7; + case 0x1e: s[rE] = fetch8(); return 7; + case 0x2e: s[rL] = fetch8(); return 7; + case 0x3e: s[rA] = fetch8(); return 7; + case 0x0f: + s[rF] = s[rF] & ~(FLAGC|FLAGN|FLAGH) | s[rA] & 1; + s[rA] = s[rA] >> 1 | s[rA] << 7; + return 4; + case 0x1f: + v = s[rF] << 7; + s[rF] = s[rF] & ~(FLAGC|FLAGN|FLAGH) | s[rA] & 1; + s[rA] = s[rA] >> 1 | v; + return 4; + case 0x2f: + s[rF] |= FLAGN|FLAGH; + s[rA] ^= 0xff; + return 4; + case 0x3f: + s[rF] = s[rF] & ~(FLAGN|FLAGH) ^ FLAGC | s[rF] << 4 & FLAGH; + return 4; + case 0xc0: if((s[rF] & FLAGZ) == 0) {spc = pop16(); return 11;} return 5; + case 0xd0: if((s[rF] & FLAGC) == 0) {spc = pop16(); return 11;} return 5; + case 0xe0: if((s[rF] & FLAGV) == 0) {spc = pop16(); return 11;} return 5; + case 0xf0: if((s[rF] & FLAGS) == 0) {spc = pop16(); return 11;} return 5; + case 0xc1: s[rC] = pop8(); s[rB] = pop8(); return 10; + case 0xd1: s[rE] = pop8(); s[rD] = pop8(); return 10; + case 0xe1: s[rL] = pop8(); s[rH] = pop8(); return 10; + case 0xf1: s[rF] = pop8(); s[rA] = pop8(); return 10; + case 0xc2: return jump((s[rF] & FLAGZ) == 0); + case 0xd2: return jump((s[rF] & FLAGC) == 0); + case 0xe2: return jump((s[rF] & FLAGV) == 0); + case 0xf2: return jump((s[rF] & FLAGS) == 0); + case 0xc3: return jump(1); + case 0xd3: z80out(fetch8(), s[rA]); return 11; + case 0xe3: + v = HL(); + s[rL] = z80read(sp); + s[rH] = z80read(sp + 1); + write16(sp, v); + return 19; + case 0xf3: intm &= 0x3f; return 4; + case 0xc4: return call(fetch16(), (s[rF] & FLAGZ) == 0); + case 0xd4: return call(fetch16(), (s[rF] & FLAGC) == 0); + case 0xe4: return call(fetch16(), (s[rF] & FLAGV) == 0); + case 0xf4: return call(fetch16(), (s[rF] & FLAGS) == 0); + case 0xc5: push8(s[rB]); push8(s[rC]); return 11; + case 0xd5: push8(s[rD]); push8(s[rE]); return 11; + case 0xe5: push8(s[rH]); push8(s[rL]); return 11; + case 0xf5: push8(s[rA]); push8(s[rF]); return 11; + case 0xc6: return alu(0, 8); + case 0xd6: return alu(2, 8); + case 0xe6: return alu(4, 8); + case 0xf6: return alu(6, 8); + case 0xc7: return call(0x00, 1); + case 0xd7: return call(0x10, 1); + case 0xe7: return call(0x20, 1); + case 0xf7: return call(0x30, 1); + case 0xc8: if((s[rF] & FLAGZ) != 0) {spc = pop16(); return 11;} return 5; + case 0xd8: if((s[rF] & FLAGC) != 0) {spc = pop16(); return 11;} return 5; + case 0xe8: if((s[rF] & FLAGV) != 0) {spc = pop16(); return 11;} return 5; + case 0xf8: if((s[rF] & FLAGS) != 0) {spc = pop16(); return 11;} return 5; + case 0xc9: spc = pop16(); return 10; + case 0xd9: + swap(rB); + swap(rC); + swap(rD); + swap(rE); + swap(rH); + swap(rL); + return 4; + case 0xe9: spc = HL(); return 4; + case 0xf9: sp = HL(); return 6; + case 0xca: return jump((s[rF] & FLAGZ) != 0); + case 0xda: return jump((s[rF] & FLAGC) != 0); + case 0xea: return jump((s[rF] & FLAGV) != 0); + case 0xfa: return jump((s[rF] & FLAGS) != 0); + case 0xcb: return bits(-1); + case 0xdb: s[rA] = z80in(fetch8()); return 11; + case 0xeb: + v = DE(); + s[rD] = s[rH]; + s[rE] = s[rL]; + s[rH] = v >> 8; + s[rL] = v; + return 4; + case 0xfb: intm |= 0xc0; return 4; + case 0xcc: return call(fetch16(), (s[rF] & FLAGZ) != 0); + case 0xdc: return call(fetch16(), (s[rF] & FLAGC) != 0); + case 0xec: return call(fetch16(), (s[rF] & FLAGV) != 0); + case 0xfc: return call(fetch16(), (s[rF] & FLAGS) != 0); + case 0xcd: return call(fetch16(), 1); + case 0xdd: return index(0); + case 0xed: return ed(); + case 0xfd: return index(1); + case 0xce: return alu(1, 8); + case 0xde: return alu(3, 8); + case 0xee: return alu(5, 8); + case 0xfe: return alu(7, 8); + case 0xcf: return call(0x08, 1); + case 0xdf: return call(0x18, 1); + case 0xef: return call(0x28, 1); + case 0xff: return call(0x38, 1); + } + sysfatal("undefined z80 opcode %#.2x at pc=%#.4x", op, scurpc); return 0; }