return icr;
}
-static int process_pipe(struct CIATimer *t, int cc, uae_u8 crmask, int *ovfl)
+static int process_pipe(struct CIATimer *t, int cc, uae_u8 crmask, int *ovfl, int loadednow)
{
int ccout = cc;
t->inputpipe |= CIA_PIPE_INPUT;
}
// interrupt 1 cycle early if timer is already zero
- if (t->timer == 0 && (t->inputpipe & CIA_PIPE_OUTPUT)) {
- *ovfl = 1;
+ if (t->timer == 0 && t->latch == 0 && (t->inputpipe & CIA_PIPE_OUTPUT)) {
+ *ovfl = loadednow ? 1 : 2;
}
return out;
}
for (int num = 0; num < 2; num++) {
struct CIA *c = &cia[num];
int ovfl[2], sp;
- bool loaded[2], loaded2[2];
+ bool loaded[2], loaded2[2], loaded3[3];
c->icr1 |= c->icr2;
c->icr2 = 0;
loaded[tn] = false;
loaded2[tn] = false;
+ loaded3[tn] = false;
// CIA special cases
if (t->loaddelay) {
if (ciaclocks > 1) {
abort();
}
- if (t->loaddelay & 1) {
+
+ if (t->loaddelay & 0x00000001) {
t->timer = t->latch;
t->inputpipe &= ~CIA_PIPE_CLR1;
}
- if ((t->loaddelay & 0x0100) && t->timer != 0) {
- loaded2[tn] = true;
+ // timer=0 special cases. TODO: better way to do this..
+ // delayed timer stop and interrupt (timer=0 condition)
+ if ((t->loaddelay & 0x00010000)) {
+ t->cr &= ~CR_START;
+ ovfl[tn] = 2;
}
- if ((t->loaddelay & 0x010000)) {
- if ((t->timer != 1 || t->latch != 1) && (t->inputpipe & CIA_PIPE_OUTPUT)) {
- loaded2[tn] = true;
- }
+ // Do not set START=0 until timer has started (timer==0 special case)
+ if ((t->loaddelay & 0x00000100) && t->timer == 0) {
+ loaded2[tn] = true;
}
if ((t->loaddelay & 0x01000000)) {
loaded[tn] = true;
- if (t->timer == 0) {
- ovfl[tn] = true;
- }
+ }
+ if ((t->loaddelay & 0x10000000)) {
+ loaded3[tn] = true;
}
t->loaddelay >>= 1;
- t->loaddelay &= 0x7f7f7f7f;
+ t->loaddelay &= 0x77777777;
}
}
// Timer A
int cc = 0;
if ((c->t[0].cr & (CR_INMODE | CR_START)) == CR_START || c->t[0].inputpipe) {
- cc = process_pipe(&c->t[0], ciaclocks, CR_INMODE | CR_START, &ovfl[0]);
+ cc = process_pipe(&c->t[0], ciaclocks, CR_INMODE | CR_START, &ovfl[0], loaded3[0]);
}
if (cc > 0) {
c->t[0].timer -= cc;
}
}
}
- ovfl[0] = 1;
+ ovfl[0] = 2;
}
}
assert(c->t[0].timer < 0x10000);
// Timer B
cc = 0;
if ((c->t[1].cr & (CR_INMODE | CR_INMODE1 | CR_START)) == CR_START || c->t[1].inputpipe) {
- cc = process_pipe(&c->t[1], ciaclocks, CR_INMODE | CR_INMODE1 | CR_START, &ovfl[1]);
+ cc = process_pipe(&c->t[1], ciaclocks, CR_INMODE | CR_INMODE1 | CR_START, &ovfl[1], loaded3[1]);
}
if (cc > 0) {
if ((c->t[1].timer == 0 && (c->t[1].cr & (CR_INMODE | CR_INMODE1)))) {
- ovfl[1] = 1;
+ ovfl[1] = 2;
} else {
c->t[1].timer -= cc;
if ((c->t[1].timer == 0 && !(c->t[1].cr & (CR_INMODE | CR_INMODE1)))) {
- ovfl[1] = 1;
+ ovfl[1] = 2;
}
}
}
struct CIATimer *t = &c->t[tn];
if (ovfl[tn] || t->preovfl) {
- c->icr2 |= tn ? ICR_B : ICR_A;
- t->timer = t->latch;
+ if (ovfl[tn]) {
+ if (ovfl[tn] > 1) {
+ c->icr2 |= tn ? ICR_B : ICR_A;
+ icr |= 1 << num;
+ }
+ t->timer = t->latch;
+ }
if (!loaded[tn]) {
if (t->cr & CR_RUNMODE) {
- // if oneshot timer expires exactly when
- // CR is written with START and ONESHOT set:
- // timer does not stop.
- if (!loaded2[tn]) {
+ if (loaded2[tn]) {
+ t->loaddelay |= 0x00010000;
+ } else {
t->cr &= ~CR_START;
- if (!acc_mode()) {
- t->inputpipe = 0;
- }
+ }
+ if (!acc_mode()) {
+ t->inputpipe = 0;
}
if (acc_mode()) {
t->inputpipe &= ~CIA_PIPE_CLR2;
}
}
}
- icr |= 1 << num;
t->preovfl = false;
}
}
// if accurate mode: handle delays cycle-accurately
if (!(t->cr & CR_START)) {
- t->loaddelay |= 1 << 1;
- t->loaddelay |= 1 << 2;
+ t->loaddelay |= 0x00000001 << 1;
+ t->loaddelay |= 0x00000001 << 2;
}
if (t->cr & CR_RUNMODE) {
t->cr |= CR_START;
+ t->loaddelay |= 0x00000001 << 2;
+ // timer=0 special case
t->loaddelay |= 0x01000000 << 1;
- t->loaddelay |= 1 << 2;
+ t->loaddelay |= 0x10000000 << 1;
}
+
}
}
if (val & CR_LOAD) {
val &= ~CR_LOAD;
- t->loaddelay |= 0x0001 << 2;
- t->loaddelay |= 0x0100 << 0;
+ t->loaddelay |= 0x00000001 << 2;
+ t->loaddelay |= 0x00000100 << 0;
+ t->loaddelay |= 0x00000100 << 1;
if (!(t->cr & CR_START)) {
- t->loaddelay |= 0x0001 << 1;
- }
- } else {
- if ((val & CR_START)) {
- t->loaddelay |= 0x010000 << 0;
+ t->loaddelay |= 0x00000001 << 1;
}
+ // timer=0 special case
+ t->loaddelay |= 0x10000000 << 1;
}
if (!(val & CR_START)) {