summaryrefslogtreecommitdiff
path: root/src/dolphin/gba/GBAXfer.c
blob: 9e4298f78913a7a55dd35648c981ef3d7133210e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include "dolphin/gba/GBAPriv.h"
#include "dolphin/si/SIBios.h"

void __GBAHandler(s32 chan, u32 error, OSContext* context) {
    GBAControl* gba;
    GBATransferCallback proc;
    GBACallback callback;
    OSContext exceptionContext;

    gba = &__GBA[chan];
    if (__GBAReset != 0) {
        return;
    }

    if ((error & 0xf)) {
        gba->ret = GBA_NOT_READY;
    } else {
        gba->ret = GBA_READY;
    }

    if (gba->proc != NULL) {
        proc = gba->proc;
        gba->proc = NULL;
        proc(chan);
    }

    if (gba->callback == NULL) {
        return;
    }

    OSClearContext(&exceptionContext);
    OSSetCurrentContext(&exceptionContext);
    callback = gba->callback;
    gba->callback = NULL;
    callback(chan, gba->ret);
    OSClearContext(&exceptionContext);
    OSSetCurrentContext(context);
}

void __GBASyncCallback(s32 chan, s32 ret) {
    GBAControl* gba = &__GBA[chan];
    OSWakeupThread(&gba->threadQueue);
}

s32 __GBASync(s32 chan) {
    GBAControl* gba;
    s32 ret;
    s32 enabled;
    gba = &__GBA[chan];

    enabled = OSDisableInterrupts();
    while (gba->callback != NULL) {
        OSSleepThread(&gba->threadQueue);
    }

    ret = gba->ret;
    OSRestoreInterrupts(enabled);

    return ret;
}

void TypeAndStatusCallback(s32 chan, u32 type) {
    GBAControl* gba;
    GBATransferCallback proc;
    GBACallback callback;
    OSContext exceptionContext;
    OSContext* context;

    gba = &__GBA[chan];

    if ((type & 0xFF) != 0 || (type & 0xffff0000) != 0x40000) {
        gba->ret = GBA_NOT_READY;
    } else {
        if (SITransfer(chan, gba->output, gba->outputBytes, gba->input, gba->inputBytes,
                       __GBAHandler, gba->delay))
        {
            return;
        }
        gba->ret = GBA_BUSY;
    }

    if (gba->proc != NULL) {
        proc = gba->proc;
        gba->proc = NULL;
        proc(chan);
    }

    if (gba->callback != NULL) {
        context = OSGetCurrentContext();
        OSClearContext(&exceptionContext);
        OSSetCurrentContext(&exceptionContext);
        callback = gba->callback;
        gba->callback = NULL;
        callback(chan, gba->ret);
        OSClearContext(&exceptionContext);
        OSSetCurrentContext(context);
        __OSReschedule();
    }
}

s32 __GBATransfer(s32 chan, s32 w1, s32 w2, GBATransferCallback callback) {
    s32 enabled;
    GBAControl* gba;
    gba = &__GBA[chan];
    enabled = OSDisableInterrupts();
    gba->proc = callback;
    gba->outputBytes = w1;
    gba->inputBytes = w2;
    SIGetTypeAsync(chan, TypeAndStatusCallback);
    OSRestoreInterrupts(enabled);

    return GBA_READY;
}

OSTime __GBASetDelay(s32 chan, OSTime delay) {
    OSTime oldDelay;
    GBAControl* gba;
    gba = &__GBA[chan];
    oldDelay = gba->delay;
    gba->delay = delay;
    return oldDelay;
}