/*
ROM dump was made by Conventional Memories 2024-06-22 13:51
Disassembled by JDat on 2024-06-26 06:42
Disassmbeled with custom version of 8039dasm.c tweaked for 8741A
Diassembler downloaded from https://github.com/daveho/asm48
8039dasm.c roots come from MAME project (see asm48 github repo for more information)

*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

#include "keyMatrix.h"
#include "hwInterface.h"

// nice routines from friend
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitToggle(value, bit) ((value) ^= (1UL << (bit)))
#define bitWrite(value, bit, bitvalue) ((bitvalue) ? bitSet(value, bit) : bitClear(value, bit))
// used in long routine
#define bit0 0
#define bit1 1
#define bit2 2
#define bit3 3
#define bit4 4
// for extIRQ
#define bit6 6
#define bit7 7


#define RAM0x27 0
#define RAM0x28 1

#define RAM0x29 2
#define RAM0x2A 3
uint8_t unknown[4]; // RAM[0x27-0x2A]

// global state flags
#define enableKeyScan 0         // Keyboard scan enabled
#define enableDMA 1             // WDT1 enable
#define enableWDT 2             // WDT0 enable
#define enableLongRoutine 3     // used in "long routine"
#define enablePinPAL 4          // pinPAL status controlled by CPU
//#define stateBit5 5             // Not used
//#define stateBit5 6             // Not used
#define keyOverFlowFlag 7       // set in timerIRQ when key read counter overflows
uint8_t globalState = 0;

#define TIMERCONST 0xAD
#define MAINLOOPCONST 2
bool timerOverFlowFlag;

uint8_t dmaCnt;         // RAM[0x39]

// default parameters after boot
// can be altered by CPU
#define DEFAULTWDT              1
#define DEFAULTKEYREPEATRATE    15
#define DEFAULTKEYREPEATDELAY   2
#define DEFAULTDMA              50
// macros to substitue parameter locations
#define wdtData 0
#define keyReapeatRate 1
#define keyPressDelay 2
#define dmaData 3
//uint8_t cpuConfig[4];   // RAM[0x20-0x23]
uint8_t cpuConfig[4] = {DEFAULTWDT,
                        DEFAULTKEYREPEATRATE,
                        DEFAULTKEYREPEATDELAY,
                        DEFAULTDMA
                        };   // RAM[0x20-0x23]

#define keyCountMax     3
uint8_t keyMatrixBuffer[keyCountMax]; // RAM[0x24-0x26]
uint8_t keyCount;           // RAM[0x37]

uint8_t R0b0, R1b0, R2b0, R3b0, R4b0, R5b0, R6b0, R7b0;
uint8_t R0b1, R1b1, R2b1, R3b1, R4b1, R5b1, R6b1, R7b1;
uint8_t accumulator;

void resetDMAWDT();
void irqTimer();
void kbcLoop();
void kbcSetup();
void irqExt();
void scanKeyMatrix();
void scanModKeys();
void sendKey();
uint8_t translateScanCode(uint8_t data, uint8_t flags);
void sendData(uint8_t data, uint8_t flags);

void label_0DB();
void label_121();
void label_152();
void label_292();

int main(int argc, char **argv) {
    kbcSetup();
    while (true) {
        irqTimer();
        irqExt();
        kbcLoop();
    }

    return 0;
}

void resetDMAWDT() {
    dmaCnt = cpuConfig[dmaData];
}

void irqTimer() {
    static uint8_t keyReadCnt = MAINLOOPCONST;  // was R2b1
    
    timerStop();    //timerEnabled = false;
    if ( busReadOBF() != 0) {       // really == 0, maybe != 0, check code again!
        pinSetIRQ4(LOW);
        if ( unknown[RAM0x28] != 0 ) {
            busWriteData(unknown[RAM0x27]);
            busWriteStatus(unknown[RAM0x28]);
            unknown[RAM0x28] = 0;
            pinSetIRQ4(HIGH);
        } else {
            busWriteStatus(unknown[RAM0x29]);
        }
    }

    timerSet(TIMERCONST);
    timerStart();

    if ( bitRead(globalState, enableWDT) ) {
        static uint8_t WDT = DEFAULTWDT;    // R6b1; must be WDT = RAM[0x20] = 1;
        WDT--;
        if (WDT != 0) {             // WDT overflow, notify CPU
            WDT = cpuConfig[wdtData];
            sendData(0xFF, unknown[RAM0x29]);
        }
    }
    
    keyReadCnt--;
    if (keyReadCnt == 0) {
        keyReadCnt = MAINLOOPCONST;
        bitSet(globalState, keyOverFlowFlag);
    }
}

void kbcLoop() {
    // Why it fucks with interrup?
    // Why not use simple NOPs?
    enableExternalInterrupt();
    disableExternalInterrupt();

    // check for flag, set in timer interrupt
    if ( bitRead(globalState, keyOverFlowFlag) == 0 ) return;
    
    bitClear(globalState, keyOverFlowFlag); // we will handle this, so clear!
    enableExternalInterrupt();      // stop fucking with ext interrupt and accept data from CPU

    // this is strange, dmaCnt not reinitialised
    // can wrap around
    // dmaCnt reloaded with resetDMAWDT() in other places
    // CPU will try to react to DMA timeout with resetting DMA
    if ( bitRead(globalState, enableDMA) ) {
        dmaCnt--;
        if ( dmaCnt == 0 ) {    // DMA timeout
            //PORT[2] &= 0xEF;
            pinSetPAL(LOW);     // disable DMA
            sendData(0xFD, 0);  // notify CPU
        }
    }

    // double check asm code. maybe we need to execute sendKey() without scaning keys?
    if ( bitRead(globalState, enableKeyScan) ) {
        label_121();	// this is call, check for correct return address
        //scanModKeys();

        label_0DB();	// this is call, check for correct return address
        //scanKeyMatrix();
        
        if ( keyCount < 3 ) {      // 3 can be substituted to contant
            label_152();	// this is call, check for correct return address
            //sendKey();
        }
    }
    //goto label_032;
}

void kbcSetup() {
    // lot of variables initialises when defined,
    // so setup looks really simple now
    pinSetWTF(LOW);
    pinSetNMI(LOW);
    pinSetIRQ4(LOW);
    

    //R6b1 = 0x01;      // who is this?

    //R0b0 = 0x39;
    //RAM[R0b0] = 0x32; //confusion with DMAcnt
    resetDMAWDT();

    timerSet(TIMERCONST);
    pinSetWTF(HIGH);
    timerStart();
    enableExternalInterrupt();
    timerEnableIRQ();
    // do keyboard loop when returnt from here
}

void irqExt() {
    static uint8_t configRegPtr = 0;    // RAM[0x38]; assing 0 just because to make gcc and lint happy
    uint8_t busData;

    busData = busReadData();

    if ( busReadF1() == 0) {        // check for data mode
        cpuConfig[configRegPtr] = busData;
        resetDMAWDT();
        return;	// Return from Interrupt
    }

    // command mode
    if ( busData == 0) {
        disableExternalInterrupt();
        return;     // Return from Interrupt
    }
    
    if ( bitRead(busData, bit7) == 0) {
        // set global state from CPU data
        globalState = busData;
        // set pinPAL state from CPU data
        uint8_t tmp;
        tmp = bitRead(busData, enablePinPAL) ? HIGH : LOW;
        pinSetPAL(tmp);
        resetDMAWDT();
        return;	// Return from Interrupt
    }

    if ( bitRead(accumulator, 6) ) {//goto label_0AA;
        resetDMAWDT();
        return;	// Return from Interrupt
    }
    if ( bitRead(busData, bit6) == 0) {
        // use only 4 cells 0 - 3
        configRegPtr = busData & 0b00000011;
    }
    resetDMAWDT();
    // Return from Interrupt
}

void label_0DB() {
    R1b0 = 0x24;

    R5b0 = 0x00;
    //R4b0 = 0x08;
    for (R4b0 = 8; R4b0 > 0; R4b0--) {
        //label_0E1:

        R3b0 = 0x08;
        accumulator = R4b0;
        accumulator &= 0x07;
        
        R0b0 = accumulator;
        accumulator = PORT[2];
        accumulator &= 0xF8;
        accumulator |= R0b0;
        PORT[2] = accumulator;
        
        accumulator = 0x28;

        label_0EE:

        accumulator--;
        if ( accumulator ) goto label_0EE;

        accumulator = PORT[1];
        carry = false;
        accumulator = ~accumulator;
        for (R3b0 = 0x8; R3b0 >0; R3b0--) {
            //label_0F4:

            //if ( accumulator == 0 ) goto label_0FD;
            if ( accumulator == 0 ) break;

            if (bitRead(accumulator, 0)) tmpCarry = true;
            accumulator = accumulator >> 1;
            if (tmpCarry) bitSet(accumulator, 7);
            carry = tmpCarry;

            //if ( carry == false ) goto label_0FB;
            if ( carry == true ) {
                R2b0 = accumulator;
                R5b0++;
                
                accumulator = 0x03;
                accumulator = ~accumulator;
                accumulator++;
                tmp16 = (uint16_t)accumulator + (uint16_t)R5b0;	// carry affected!
                accumulator = (uint8_t)(tmp16 & 0xFF);
                if (tmp16 > 0xFF) carry = true;
                carry = !carry;
                if ( carry == true ) { 
                    RAM[R1b0] = ( (column << 3) & 0b00111000 ) | (R3b0 & 0b00000111);
                    R1b0++;
                    accumulator = R2b0;
                    carry = false;
                }
            }
            //label_0FB:

            //R3b0--;
            //if (R3b0 != 0) goto label_0F4;
        }
        
        // break here
        //label_0FD:

        //R4b0--;
        //if (R4b0 != 0) goto label_0E1;
    }

    RAM[0x37] = R5b0;
    return;	// Return from subroutine. Check for correct call label!


}

void label_121() {
//label_121:
accumulator = 0x00;
if ( pinT1() ) goto label_127;
accumulator |=0x40;
label_127:
if ( pinT0() ) goto label_12B;
accumulator |=0x20;
label_12B:
R0b0 = accumulator;
accumulator = PORT[2];
accumulator &= 0xF8;
accumulator |=0x02;
PORT[2] = accumulator;
accumulator = 0x28;
label_134:
accumulator--;
if ( accumulator ) goto label_134;
accumulator = PORT[1];
accumulator = ~accumulator;
accumulator &= 0x01;
if ( accumulator == 0 ) goto label_141;
accumulator = R0b0;
accumulator |=0x80;
R0b0 = accumulator;
label_141:
R1b0 = 0x2A;
accumulator = RAM[R1b0];
accumulator = ~accumulator;
accumulator++;
tmp16 = (uint16_t)accumulator + (uint16_t)R0b0;	// carry affected!
accumulator = (uint8_t)(tmp16 & 0xFF);
if (tmp16 > 0xFF) carry = true;
carry = !carry;
if ( accumulator == 0 ) goto label_14D;
accumulator = R0b0;
RAM[R1b0] = accumulator;
return;	// Return from subroutine. Check for correct call label!
label_14D:
accumulator = R0b0;
R1b0 = 0x29;
RAM[R1b0] = accumulator;
return;	// Return from subroutine. Check for correct call label!
}

void label_152() {
R0b0 = 0x2C;
R4b0 = 0x04;
label_156:
accumulator = 0xEF;
accumulator &= RAM[R0b0];
RAM[R0b0] = accumulator;
R0b0++;
R0b0++;
R0b0++;
R4b0--;
if (R4b0 != 0) goto label_156;
R1b0 = 0x37;
accumulator = RAM[R1b0];
if ( accumulator == 0 ) goto label_181;
R7b0 = accumulator;
R1b0 = 0x24;
label_167:
accumulator = RAM[R1b0];
R2b0 = accumulator;
R4b0 = 0x04;
R0b0 = 0x2C;
label_16D:
accumulator = RAM[R0b0];
if ( accumulator == 0 ) goto label_179;
R0b0--;
accumulator = RAM[R0b0];
accumulator = ~accumulator;
accumulator++;
tmp16 = (uint16_t)accumulator + (uint16_t)R2b0;	// carry affected!
accumulator = (uint8_t)(tmp16 & 0xFF);
if (tmp16 > 0xFF) carry = true;
carry = !carry;
if ( accumulator == 0 ) goto label_1FB;
R0b0++;
label_179:
R0b0++;
R0b0++;
R0b0++;
R4b0--;
if (R4b0 != 0) goto label_16D;
label_17E:
R1b0++;
R7b0--;
if (R7b0 != 0) goto label_167;
label_181:
R0b0 = 0x2C;
R4b0 = 0x04;
label_185:
accumulator = RAM[R0b0];
if ( accumulator == 0 ) goto label_18B;
accumulator = ~accumulator;
if ( bitRead(accumulator, 4) ) goto label_1DF;
label_18B:
R0b0++;
label_18C:
R0b0++;
R0b0++;
R4b0--;
if (R4b0 != 0) goto label_185;
R1b0 = 0x37;
accumulator = RAM[R1b0];
R1b0 = 0x24;
if ( accumulator == 0 ) goto label_19F;
R7b0 = accumulator;
label_198:
accumulator = RAM[R1b0];
accumulator++;
if ( accumulator ) goto label_1A0;
label_19C:
R1b0++;
R7b0--;
if (R7b0 != 0) goto label_198;
label_19F:
return;	// Return from subroutine. Check for correct call label!
label_1A0:
accumulator = RAM[R1b0];
accumulator ^= 0x10;
if ( accumulator == 0 ) goto label_19C;
R0b0 = 0x2C;
label_1A7:
if ( accumulator == 0 ) goto label_1AF;
R0b0++;
R0b0++;
R0b0++;
goto label_1A7;
label_1AF:
R0b0--;
accumulator = RAM[R1b0];
RAM[R0b0] = accumulator;
R0b0++;
accumulator = R1b0;
R2b0 = accumulator;
R1b0 = 0x29;
accumulator = RAM[R1b0];
accumulator &= 0xE0;
accumulator |=0x08;
RAM[R0b0] = accumulator;
R0b0++;
RAM[R0b0] = 0x01;
R6b0 = 0x04;
R0b0 = 0x2C;
label_1C4:
accumulator = RAM[R0b0];
if ( accumulator == 0 ) goto label_1D6;
if ( bitRead(accumulator, 3) ) goto label_1D6;
if ( bitRead(accumulator, 0) ) goto label_1D6;
accumulator &= 0xF0;
accumulator |=0x04;
RAM[R0b0] = accumulator;
R0b0++;
R1b0 = 0x21;
accumulator = RAM[R1b0];
RAM[R0b0] = accumulator;
R0b0--;
label_1D6:
R0b0++;
R0b0++;
R0b0++;
R6b0--;
if (R6b0 != 0) goto label_1C4;
accumulator = R2b0;
R1b0 = accumulator;
goto label_19C;
label_1DF:
accumulator = ~accumulator;
if ( bitRead(accumulator, 0) ) goto label_1ED;
accumulator &= 0xF0;
accumulator |=0x01;
RAM[R0b0] = accumulator;
R0b0++;
accumulator = 0x02;
RAM[R0b0] = accumulator;
goto label_18C;
label_1ED:
R0b0++;
accumulator = RAM[R0b0];
accumulator--;
if ( accumulator == 0 ) goto label_1F5;
RAM[R0b0] = accumulator;
goto label_18C;
label_1F5:
R0b0--;
RAM[R0b0] = accumulator;
PORT[2] &= 0xBF;
goto label_18B;
label_1FB:
R0b0++;
accumulator = RAM[R0b0];
R3b0 = accumulator;
R0b0++;
accumulator = RAM[R0b0];
R6b0 = accumulator;
accumulator = R0b0;
// Bank1 in use
R4b1 = accumulator;
// Bank0 in use
accumulator = R3b0;
if ( bitRead(accumulator, 3) ) goto label_21B;
if ( bitRead(accumulator, 2) ) goto label_237;
if ( bitRead(accumulator, 1) ) goto label_255;
accumulator &= 0xE0;
accumulator |=0x08;
R3b0 = accumulator;
accumulator = 0x01;
goto label_281;
label_21B:
accumulator = R6b0;
accumulator--;
if ( accumulator == 0 ) goto label_221;
goto label_281;
label_221:
accumulator = R2b0;
label_292();	// this is call, check for correct return address
R2b0 = accumulator;
accumulator = R3b0;
accumulator &= 0xE0;
R5b0 = accumulator;
accumulator = R2b0;
sendData(data, flags);	// this is call, check for correct return address
accumulator = R5b0;
accumulator &= 0xF0;
accumulator |=0x04;
R3b0 = accumulator;
R0b0 = 0x21;
accumulator = RAM[R0b0];
goto label_281;
label_237:
accumulator = R6b0;
accumulator--;
if ( accumulator == 0 ) goto label_23D;
goto label_281;
label_23D:
// Bank1 in use
accumulator = R7b1;
// Bank0 in use
accumulator = ~accumulator;
if ( bitRead(accumulator, 3) ) goto label_245;
goto label_24A;
label_245:
R0b0 = 0x21;
accumulator = RAM[R0b0];
goto label_281;
label_24A:
accumulator = R3b0;
accumulator &= 0xF0;
accumulator |=0x02;
R3b0 = accumulator;
R0b0 = 0x22;
accumulator = RAM[R0b0];
goto label_281;
label_255:
accumulator = R6b0;
accumulator--;
if ( accumulator == 0 ) goto label_25B;
goto label_281;
label_25B:
accumulator = R3b0;
accumulator &= 0xE0;
R0b0 = 0x29;
accumulator = ~accumulator;
accumulator++;
tmp16 = (uint16_t)accumulator + (uint16_t)RAM[R0b0];	// carry affected!
accumulator = (uint8_t)(tmp16 & 0xFF);
if (tmp16 > 0xFF) carry = true;
carry = !carry;
if ( accumulator == 0 ) goto label_271;
accumulator = RAM[R0b0];
accumulator &= 0xE0;
accumulator |=0x04;
R3b0 = accumulator;
R0b0 = 0x21;
accumulator = RAM[R0b0];
goto label_281;
label_271:
accumulator = R2b0;
label_292();	// this is call, check for correct return address
R2b0 = accumulator;
accumulator = R3b0;
accumulator &= 0xE0;
accumulator |=0x10;
R5b0 = accumulator;
accumulator = R2b0;
sendData(data, flags);	// this is call, check for correct return address
R0b0 = 0x22;
accumulator = RAM[R0b0];
label_281:
// Bank1 in use
tmp = R4b1;
R4b1 = accumulator;
accumulator = tmp;
// Bank0 in use
R0b0 = accumulator;
// Bank1 in use
accumulator = R4b1;
// Bank0 in use
RAM[R0b0] = accumulator;
R0b0--;
accumulator = R3b0;
accumulator |=0x10;
RAM[R0b0] = accumulator;
RAM[R1b0] = 0xFF;
goto label_17E;
}

void label_292() {
accumulator = accumulator << 1;
accumulator = accumulator << 1;
accumulator &= 0xFC;
R5b0 = accumulator;
R0b0 = 0x29;
accumulator = RAM[R0b0];
accumulator = accumulator >> 1;
accumulator = accumulator >> 1;
accumulator = accumulator >> 1;
accumulator = accumulator >> 1;
accumulator = accumulator >> 1;
accumulator &= 0x03;
accumulator |= R5b0;
accumulator = keyMatrix[accumulator];	// Double check for correct array name!
R5b0 = accumulator;
accumulator = RAM[R0b0];
accumulator &= 0x80;
if ( accumulator == 0 ) goto label_2AD;
accumulator = 0x9F;
accumulator &= R5b0;
R5b0 = accumulator;
label_2AD:
accumulator = R5b0;
return;	// Return from subroutine. Check for correct call label!
}

void sendData(uint8_t data, uint8_t flags) {
//label_2AF:
    if ( data == 0xFE ) {
        pinSetNMI(HIGH);
        return;     // Return from subroutine.
    }

    if ( busReadOBF() == 0) {   // CPU output register empty
        pinSetIRQ4(LOW);
        busWriteData(data);
        busWriteStatus(flags);
        pinSetIRQ4(HIGH);
        return;     // Return from subroutine.
    }

    // CPU output register full
    // do some unknown magic
    if ( unknown[RAM0x28] != 0) {
        unknown[RAM0x28] = data;
        unknown[RAM0x27] = 0xFE;
    } else {
        unknown[RAM0x27] = data;
        unknown[RAM0x28] = flags;
    }
}
