root/src/system/kernel/platform/atari_m68k/platform.cpp
/*
        Atari kernel platform code.
*/

#include <arch_platform.h>

#include <new>
#include <util/kernel_cpp.h>

#include <KernelExport.h>

#include <boot/kernel_args.h>
#include <arch/cpu.h>
//#include <platform/openfirmware/openfirmware.h>
#include <platform/atari_m68k/MFP.h>
#include <platform/atari_m68k/platform_atari_m68k.h>
#include <real_time_clock.h>
#include <timer.h>

#include "debugger_keymaps.h"

/* which MFP timer to use */
#define SYS_TDR         MFP_TADR
#define SYS_TCR         MFP_TACR
#define SYS_TCRMASK     0x0f    /* mask for timer control (0xf for A&B, 0x7 for C, 0x38 for D) */
#define SYS_TENABLE     0x01    /* delay mode with /4 prescaler: 0x01 (<<3 for timer D) */
#define SYS_TDISABLE    0x00
#define SYS_TVECTOR     13
#define MFP_PRESCALER   4

/* used for timer interrupt */
#define MFP_TIMER_RATE  (MFP_FREQ/MFP_PRESCALER)
#define MFP_MAX_TIMER_INTERVAL  (0xff * 1000000L / MFP_TIMER_RATE)

/* used for system_time() calculation */
#define MFP_SYSTEM_TIME_RATE    (MFP_FREQ/MFP_PRESCALER)


#define MFP0_BASE       0xFFFFFA00
#define MFP1_BASE       0xFFFFFA80

#define MFP0_VECTOR_BASE        64
#define MFP1_VECTOR_BASE        (MFP0_VECTOR_BASE+16)

#define TT_RTC_BASE     0xFFFF8960

#define TT_RTC_VECTOR   (MFP1_VECTOR_BASE+14)

// ?
#define SCC_C0_VECTOR_BASE      (MFP1_VECTOR_BASE+16)
// ??
#define SCC_C1_VECTOR_BASE      (0x1BC/4)

#define IKBD_BASE       0xFFFFFC00
#define IKBD_CTRL       0
#define IKBD_DATA       2
#define IKBD_STATUS_READ_BUFFER_FULL    0x01

// keyboard scancodes, very much like PC ones
// see
// http://www.classiccmp.org/dunfield/atw800/h/atw800k.jpg
// ST Mag Nr 57 page 55
enum keycodes {
        LEFT_SHIFT              = 42,
        RIGHT_SHIFT             = 54,

        LEFT_CONTROL    = 29,

        LEFT_ALT                = 56,

        CURSOR_LEFT             = 75,
        CURSOR_RIGHT    = 77,
        CURSOR_UP               = 72,
        CURSOR_DOWN             = 80,
        CURSOR_HOME             = 71,
        CURSOR_END              = 79,   // not on real atari keyboard
        PAGE_UP                 = 73,   // not on real atari keyboard XXX remap Help ?
        PAGE_DOWN               = 81,   // not on real atari keyboard XXX remap Undo ?

        DELETE                  = 83,
        F12                             = 88,   // but it's shifted

};

#define in8(a)  (*(volatile uint8 *)(a))
#define out8(v, a)  (*(volatile uint8 *)(a) = v)


namespace BPrivate {

//class MfpPIC;

// #pragma mark - Atari (Falcon)


class M68KAtari : public M68KPlatform {
public:
        class MFP {
        public:
                MFP(uint32 base, int vector);
                ~MFP();

                uint32 Base() const { return fBase; };
                int Vector() const { return fVector; };

                uint8 ReadReg(uint32 reg) { return in8(fBase + reg); };
                void WriteReg(uint32 reg, uint8 v) { out8(v, fBase + reg); };

                void EnableIOInterrupt(int32 irq);
                void DisableIOInterrupt(int32 irq);
                bool AcknowledgeIOInterrupt(int32 irq);

        private:
                uint32 fBase;
                int fVector;
        };

        class RTC {
        public:
                RTC(uint32 base, int vector);
                ~RTC();

                uint32 Base() const { return fBase; };
                int Vector() const { return fVector; };

                uint8 ReadReg(uint32 reg);
                void WriteReg(uint32 reg, uint8 v) { out8((uint8)reg,fBase+1); out8(v,fBase+3); };

        private:
                uint32 fBase;
                int fVector;
        };

        M68KAtari();
        virtual ~M68KAtari();

        void ProbeHardware(struct kernel_args *kernelArgs);

        virtual status_t Init(struct kernel_args *kernelArgs);
        virtual status_t InitSerialDebug(struct kernel_args *kernelArgs);
        virtual status_t InitPostVM(struct kernel_args *kernelArgs);
        virtual status_t InitPIC(struct kernel_args *kernelArgs);
        virtual status_t InitRTC(struct kernel_args *kernelArgs,
                struct real_time_data *data);
        virtual status_t InitTimer(struct kernel_args *kernelArgs);

        virtual char BlueScreenGetChar();

        virtual char SerialDebugGetChar();
        virtual void SerialDebugPutChar(char c);

        virtual void EnableIOInterrupt(int32 irq);
        virtual void DisableIOInterrupt(int32 irq);
        virtual bool AcknowledgeIOInterrupt(int32 irq);

        virtual uint8 ReadRTCReg(uint8 reg);
        virtual void WriteRTCReg(uint8 reg, uint8 val);
        virtual void SetHardwareRTC(uint64 seconds);
        virtual uint32 GetHardwareRTC();

        virtual void SetHardwareTimer(bigtime_t timeout);
        virtual void ClearHardwareTimer(void);

        virtual void ShutDown(bool reboot);

private:
        MFP     *MFPForIrq(int irq);
        static int32    MFPTimerInterrupt(void *data);

        MFP     *fMFP[2];

        RTC     *fRTC;

        // native features (ARAnyM emulator)
        uint32 (*nfGetID)(const char *name);
        int32 (*nfCall)(uint32 ID, ...);
        char *nfPage;
        uint32 nfDebugPrintfID;

};


}       // namespace BPrivate

using BPrivate::M68KAtari;


// #pragma mark - M68KAtari::MFP


static char sMFP0Buffer[sizeof(M68KAtari::MFP)];
static char sMFP1Buffer[sizeof(M68KAtari::MFP)];

// constructor
M68KAtari::MFP::MFP(uint32 base, int vector)
{
        fBase = base;
        fVector = vector;
}


M68KAtari::MFP::~MFP()
{
}

#warning M68K: use enable or mark register ?

void
M68KAtari::MFP::EnableIOInterrupt(int irq)
{
        uint8 bit = 1 << (irq % 8);
        // I*B[0] is vector+0, I*A[0] is vector+8
        uint32 reg = Base() + ((irq > 8) ? (MFP_IERA) : (MFP_IERB));
        uint8 val = in8(reg);
        if (val & bit == 0) {
                val |= bit;
                out8(val, reg);
        }
}


void
M68KAtari::MFP::DisableIOInterrupt(int irq)
{
        uint8 bit = 1 << (irq % 8);
        // I*B[0] is vector+0, I*A[0] is vector+8
        uint32 reg = Base() + ((irq > 8) ? (MFP_IERA) : (MFP_IERB));
        uint8 val = in8(reg);
        if (val & bit) {
                val &= ~bit;
                out8(val, reg);
        }
}


bool
M68KAtari::MFP::AcknowledgeIOInterrupt(int irq)
{
        uint8 bit = 1 << (irq % 8);
        // I*B[0] is vector+0, I*A[0] is vector+8
        uint32 reg = Base() + ((irq > 8) ? (MFP_ISRA) : (MFP_ISRB));
        uint8 val = in8(reg);
        if (val & bit) {
                val &= ~bit;
                out8(val, reg);
                return true;
        }
        return false;
}


// #pragma mark - M68KAtari::RTc


static char sRTCBuffer[sizeof(M68KAtari::RTC)];

// constructor
M68KAtari::RTC::RTC(uint32 base, int vector)
{
        fBase = base;
        fVector = vector;
}


M68KAtari::RTC::~RTC()
{
}


uint8
M68KAtari::RTC::ReadReg(uint32 reg)
{
        int waitTime = 10000;

        if (reg < 0x0a) {       // time of day stuff...
                                // check for in progress updates before accessing
                out8(0x0a, fBase+1);
                while((in8(fBase+3) & 0x80) && --waitTime);
        }

        out8((uint8)reg,fBase+1);
        return in8(fBase+3);
}


// #pragma mark - M68KAtari


// constructor
M68KAtari::M68KAtari()
        : M68KPlatform(M68K_PLATFORM_ATARI)
{
}


// destructor
M68KAtari::~M68KAtari()
{
}


void
M68KAtari::ProbeHardware(struct kernel_args *kernelArgs)
{
        dprintf("Atari hardware:\n");
        // if we are here we already know we have one
        dprintf("       ST MFP\n");
        if (m68k_is_hw_register_readable(MFP1_BASE)) {
                dprintf("       TT MFP\n");
                fMFP[1] = new(sMFP1Buffer) M68KAtari::MFP(MFP1_BASE, MFP1_VECTOR_BASE);
        }
        if (m68k_is_hw_register_readable(TT_RTC_BASE)) {
                dprintf("       TT RTC MC146818A\n");
                fRTC = new(sRTCBuffer) M68KAtari::RTC(TT_RTC_BASE,TT_RTC_VECTOR);
        } else
                panic("TT RTC required!");
}


status_t
M68KAtari::Init(struct kernel_args *kernelArgs)
{
        fMFP[0] = NULL;
        fMFP[1] = NULL;
        fRTC = NULL;

        // initialize ARAnyM NatFeatures
        nfGetID =
                kernelArgs->arch_args.plat_args.atari.nat_feat.nf_get_id;
        nfCall =
                kernelArgs->arch_args.plat_args.atari.nat_feat.nf_call;
        nfPage = (char *)
                kernelArgs->arch_args.plat_args.atari.nat_feat.nf_page;

        // probe for ST-MFP
        if (m68k_is_hw_register_readable(MFP0_BASE)) {
                fMFP[0] = new(sMFP0Buffer) M68KAtari::MFP(MFP0_BASE, MFP0_VECTOR_BASE);
        } else
                // won't really work anyway from here
                panic("You MUST have an ST MFP! Wait, is that *really* an Atari ???");

        return B_OK;
}


status_t
M68KAtari::InitSerialDebug(struct kernel_args *kernelArgs)
{
        nfDebugPrintfID =
                kernelArgs->arch_args.plat_args.atari.nat_feat.nf_dprintf_id;

#warning M68K: add real serial debug output someday

        //out8(0x11, IKBD_BASE+IKBD_DATA);

        // now we can expect to see something
        ProbeHardware(kernelArgs);

        return B_OK;
}


status_t
M68KAtari::InitPostVM(struct kernel_args *kernelArgs)
{
#if 0
        add_debugger_command("of_exit", &debug_command_of_exit,
                "Exit to the Open Firmware prompt. No way to get back into the OS!");
        add_debugger_command("of_enter", &debug_command_of_enter,
                "Enter a subordinate Open Firmware interpreter. Quitting it returns "
                "to KDL.");
#endif
        return B_NO_INIT;
        return B_OK;
}


status_t
M68KAtari::InitPIC(struct kernel_args *kernelArgs)
{
        return B_OK;
}


status_t
M68KAtari::InitRTC(struct kernel_args *kernelArgs,
        struct real_time_data *data)
{
        // XXX we should do this in the bootloader maybe...
        kernelArgs->arch_args.time_base_frequency = MFP_SYSTEM_TIME_RATE;
        return B_OK;
}


status_t
M68KAtari::InitTimer(struct kernel_args *kernelArgs)
{

        fMFP[0]->WriteReg(MFP_TACR, 0); // stop it
        install_io_interrupt_handler(fMFP[0]->Vector()+13, &MFPTimerInterrupt, this, 0);
        return B_OK;
}


char
M68KAtari::BlueScreenGetChar()
{
        /* polling the keyboard, similar to code in keyboard
         * driver, but without using an interrupt
         * taken almost straight from x86 code
         * XXX: maybe use the keymap from the _AKP cookie instead ?
         */
        static bool shiftPressed = false;
        static bool controlPressed = false;
        static bool altPressed = false;
        static uint8 special = 0;
        static uint8 special2 = 0;
        uint8 key = 0;

        if (special & 0x80) {
                special &= ~0x80;
                return '[';
        }
        if (special != 0) {
                key = special;
                special = 0;
                return key;
        }
        if (special2 != 0) {
                key = special2;
                special2 = 0;
                return key;
        }

        while (true) {
                uint8 status = in8(IKBD_BASE+IKBD_CTRL);

                if ((status & IKBD_STATUS_READ_BUFFER_FULL) == 0) {
                        // no data in keyboard buffer
                        spin(200);
                        //kprintf("no key\n");
                        continue;
                }

                spin(200);
                key = in8(IKBD_BASE+IKBD_DATA);
                /*
                kprintf("key: %02x, %sshift %scontrol %salt\n",
                        key,
                        shiftPressed?"":"!",
                        controlPressed?"":"!",
                        altPressed?"":"!");
                */

                if (key & 0x80) {
                        // key up
                        switch (key & ~0x80) {
                                case LEFT_SHIFT:
                                case RIGHT_SHIFT:
                                        shiftPressed = false;
                                        break;
                                case LEFT_CONTROL:
                                        controlPressed = false;
                                        break;
                                case LEFT_ALT:
                                        altPressed = false;
                                        break;
                        }
                } else {
                        // key down
                        switch (key) {
                                case LEFT_SHIFT:
                                case RIGHT_SHIFT:
                                        shiftPressed = true;
                                        break;

                                case LEFT_CONTROL:
                                        controlPressed = true;
                                        break;

                                case LEFT_ALT:
                                        altPressed = true;
                                        break;

                                // start escape sequence for cursor movement
                                case CURSOR_UP:
                                        special = 0x80 | 'A';
                                        return '\x1b';
                                case CURSOR_DOWN:
                                        special = 0x80 | 'B';
                                        return '\x1b';
                                case CURSOR_RIGHT:
                                        special = 0x80 | 'C';
                                        return '\x1b';
                                case CURSOR_LEFT:
                                        special = 0x80 | 'D';
                                        return '\x1b';
                                case CURSOR_HOME:
                                        special = 0x80 | 'H';
                                        return '\x1b';
                                case CURSOR_END:
                                        special = 0x80 | 'F';
                                        return '\x1b';
                                case PAGE_UP:
                                        special = 0x80 | '5';
                                        special2 = '~';
                                        return '\x1b';
                                case PAGE_DOWN:
                                        special = 0x80 | '6';
                                        special2 = '~';
                                        return '\x1b';


                                case DELETE:
                                        if (controlPressed && altPressed)
                                                arch_cpu_shutdown(true);

                                        special = 0x80 | '3';
                                        special2 = '~';
                                        return '\x1b';

                                default:
                                        if (controlPressed) {
                                                char c = kShiftedKeymap[key];
                                                if (c >= 'A' && c <= 'Z')
                                                        return 0x1f & c;
                                        }

                                        if (altPressed)
                                                return kAltedKeymap[key];

                                        return shiftPressed
                                                ? kShiftedKeymap[key] : kUnshiftedKeymap[key];
                        }
                }
        }
}


char
M68KAtari::SerialDebugGetChar()
{
        //WRITEME
        return BlueScreenGetChar();
        //return 0;
}


void
M68KAtari::SerialDebugPutChar(char c)
{
        if (nfCall && nfDebugPrintfID) {
#if 0
                static char buffer[2] = { '\0', '\0' };
                buffer[0] = c;

                nfCall(nfDebugPrintfID /*| 0*/, buffer);
#endif
                nfPage[0] = c;
                nfPage[1] = '\0';
                nfCall(nfDebugPrintfID /*| 0*/, nfPage);
        }

#warning M68K: WRITEME
        // real serial
        //panic("WRITEME");
}


void
M68KAtari::EnableIOInterrupt(int32 irq)
{
        MFP *mfp = MFPForIrq(irq);

        if (mfp)
                mfp->EnableIOInterrupt(irq - mfp->Vector());
}


void
M68KAtari::DisableIOInterrupt(int32 irq)
{
        MFP *mfp = MFPForIrq(irq);

        if (mfp)
                mfp->DisableIOInterrupt(irq - mfp->Vector());
}


bool
M68KAtari::AcknowledgeIOInterrupt(int32 irq)
{
        MFP *mfp = MFPForIrq(irq);

        if (mfp)
                return mfp->AcknowledgeIOInterrupt(irq - mfp->Vector());
        return false;
}


uint8
M68KAtari::ReadRTCReg(uint8 reg)
{
        // fake century
        // (on MC146818A it's in the RAM, but probably it's used for that purpose...)
        // but just in case, we're in 20xx now anyway :)
        if (reg == 0x32)
                return 0x20;
        return fRTC->ReadReg(reg);
}


void
M68KAtari::WriteRTCReg(uint8 reg, uint8 val)
{
        fRTC->WriteReg(reg, val);
}

void
M68KAtari::SetHardwareRTC(uint64 seconds)
{
#warning M68K: WRITEME
}


uint32
M68KAtari::GetHardwareRTC()
{
#warning M68K: WRITEME
        return 0;
}


void
M68KAtari::SetHardwareTimer(bigtime_t timeout)
{
        uint8 nextEventClocks;
        if (timeout <= 0)
                nextEventClocks = 2;
        else if (timeout < MFP_MAX_TIMER_INTERVAL)
                nextEventClocks = timeout * MFP_TIMER_RATE / 1000000;
        else
                nextEventClocks = 0xff;

        fMFP[0]->WriteReg(SYS_TDR, nextEventClocks);
        // delay mode, device by 4
        fMFP[0]->WriteReg(SYS_TCR, (fMFP[0]->ReadReg(SYS_TCR) & SYS_TCRMASK) | SYS_TENABLE);
        // enable irq
        EnableIOInterrupt(MFP1_VECTOR_BASE + SYS_TVECTOR);
}


void
M68KAtari::ClearHardwareTimer(void)
{
        // disable the irq (as on PC but I'm not sure it's needed)
        DisableIOInterrupt(MFP1_VECTOR_BASE + SYS_TVECTOR);
        // stop it, we don't want another countdown
        fMFP[0]->WriteReg(SYS_TCR, (fMFP[0]->ReadReg(SYS_TCR) & SYS_TCRMASK) | SYS_TDISABLE);
}


void
M68KAtari::ShutDown(bool reboot)
{
        panic("Bombs!");
        panic("WRITEME");
}


M68KAtari::MFP *
M68KAtari::MFPForIrq(int irq)
{
        int i;

        for (i = 0; i < 2; i++) {
                if (fMFP[i]) {
                        if (irq >= fMFP[i]->Vector() && irq < fMFP[i]->Vector() + 16)
                                return fMFP[i];
                }
        }
        return NULL;
}

int32
M68KAtari::MFPTimerInterrupt(void *data)
{
        M68KAtari *_this = (M68KAtari *)data;
        // disable the timer, else it will loop again with the same value
        _this->ClearHardwareTimer();
        // handle the timer
        return timer_interrupt();
}


// static buffer for constructing the actual M68KPlatform
static char *sM68KPlatformBuffer[sizeof(M68KAtari)];
#warning PTR HERE ???


M68KPlatform *instanciate_m68k_platform_atari()
{
        return new(sM68KPlatformBuffer) M68KAtari;
}