summaryrefslogtreecommitdiff
path: root/src/Dolphin/GBA/GBAXfer.c
blob: c88ffd1da1a9697a514bf187fdd933f20861f599 (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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#include "dolphin/GBAPriv.h"
#include "dolphin/sipriv.h"

void __GBAHandler(s32 chan, u32 sr, OSContext* context) {
  int tmp;
  GBA* gba;
  OSContext tmpCtx;
  GBACallback callback;
  GBATransferCallback xferCallback;
  gba = &__GBA[chan];
  if (__GBAReset != 0) {
    return;
  }

  if ((sr & 0xf)) {
    gba->result = 1;
  } else {
    gba->result = 0;
  }

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

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

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

void __GBASyncCallback(s32 chan, s32 ret) { OSWakeupThread(&__GBA[chan].thread_queue); }

#ifdef FULL_FRANK
/* This actually does match, but has an epilogue swap */
s32 __GBASync(s32 chan) {
  GBA* gba;
  s32 enabled;
  s32 ret;
  gba = &__GBA[chan];

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

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

  return ret;
}
#else
extern void OSSleepThread();
/* clang-format off */
#pragma push
#pragma optimization_level 0
#pragma optimizewithasm off
asm s32 __GBASync(s32 chan) {
  nofralloc 
  mflr r0
  lis r4, __GBA@ha
  stw r0, 4(r1)
  slwi r3, r3, 8
  addi r0, r4, __GBA@l
  stwu r1, -0x18(r1)
  stw r31, 0x14(r1)
  add r31, r0, r3
  stw r30, 0x10(r1)
  bl OSDisableInterrupts
  mr r30, r3
  b lbl_803CAD50
lbl_803CAD48:
  addi r3, r31, 0x24
  bl OSSleepThread
lbl_803CAD50:
  lwz r0, 0x1c(r31)
  cmplwi r0, 0
  bne lbl_803CAD48
  lwz r31, 0x20(r31)
  mr r3, r30
  bl OSRestoreInterrupts
  mr r3, r31
  lwz r0, 0x1c(r1)
  lwz r31, 0x14(r1)
  lwz r30, 0x10(r1)
  addi r1, r1, 0x18
  mtlr r0
  blr
}
/* clang-format on */
#pragma pop
#endif

void TypeAndStatusCallback(s32 chan, u32 type) {
  s32 tmp;
  GBA* gba;
  OSContext* context;
  GBACallback callback;
  GBATransferCallback xferCallback;
  OSContext tmpContext;
  gba = &__GBA[chan];
  if (__GBAReset != 0) {
    return;
  }

  if ((type & 0xFF) != 0 || (type & 0xffff0000) != 0x40000) {
    gba->result = GBA_NOT_READY;
  } else {
    if (SITransfer(chan, &gba->command, gba->_0c, gba->dst, gba->_10, __GBAHandler, gba->delay)) {
      return;
    }
    gba->result = GBA_BUSY;
  }

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

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

s32 __GBATransfer(s32 chan, s32 w1, s32 w2, GBATransferCallback callback) {
  s32 enabled;
  GBA* gba;
  gba = &__GBA[chan];
  enabled = OSDisableInterrupts();
  gba->_38 = callback;
  gba->_0c = w1;
  gba->_10 = w2;
  SIGetTypeAsync(chan, TypeAndStatusCallback);
  OSRestoreInterrupts(enabled);

  return GBA_READY;
}

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