Wifi.cpp 33.4 KB
Newer Older
StapleButter's avatar
StapleButter committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
    Copyright 2016-2017 StapleButter

    This file is part of melonDS.

    melonDS is free software: you can redistribute it and/or modify it under
    the terms of the GNU General Public License as published by the Free
    Software Foundation, either version 3 of the License, or (at your option)
    any later version.

    melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with melonDS. If not, see http://www.gnu.org/licenses/.
*/

#include <stdio.h>
#include <string.h>
#include "NDS.h"
StapleButter's avatar
StapleButter committed
22
#include "SPI.h"
StapleButter's avatar
StapleButter committed
23
#include "Wifi.h"
StapleButter's avatar
StapleButter committed
24
#include "Platform.h"
StapleButter's avatar
StapleButter committed
25
26
27
28
29


namespace Wifi
{

StapleButter's avatar
StapleButter committed
30
u8 RAM[0x2000];
31
32
33
u16 IO[0x1000>>1];

#define IOPORT(x) IO[(x)>>1]
StapleButter's avatar
StapleButter committed
34
35
36

u16 Random;

37
38
u64 USCounter;
u64 USCompare;
39
bool BlockBeaconIRQ14;
40

StapleButter's avatar
StapleButter committed
41
42
u32 CmdCounter;

StapleButter's avatar
StapleButter committed
43
44
45
46
47
u16 BBCnt;
u8 BBWrite;
u8 BBRegs[0x100];
u8 BBRegsRO[0x100];

StapleButter's avatar
StapleButter committed
48
49
50
51
52
53
u8 RFVersion;
u16 RFCnt;
u16 RFData1;
u16 RFData2;
u32 RFRegs[0x40];

54
55
56
57
58
59
60
61
62
63
64
65
typedef struct
{
    u16 Addr;
    u16 Length;
    u8 Rate;
    u8 CurPhase;
    u32 CurPhaseTime;

} TXSlot;

TXSlot TXSlots[6];

StapleButter's avatar
StapleButter committed
66
u8 RXBuffer[2048];
StapleButter's avatar
StapleButter committed
67
68
u32 RXTime;
u16 RXEndAddr;
StapleButter's avatar
StapleButter committed
69

70
71
72
73
u32 ComStatus; // 0=waiting for packets  1=receiving  2=sending
u32 TXCurSlot;
u32 RXCounter;

StapleButter's avatar
StapleButter committed
74
75
bool MPInited;

StapleButter's avatar
StapleButter committed
76

StapleButter's avatar
StapleButter committed
77

StapleButter's avatar
StapleButter committed
78
79
80
81
// multiplayer host TX sequence:
// 1. preamble
// 2. IRQ7
// 3. send data
82
83
84
85
86
// 4. optional IRQ1
// 5. wait for client replies (duration: 112 + ((10 * CMD_REPLYTIME) * numclients))
// 6. IRQ7
// 7. send ack (16 bytes, 1Mbps)
// 8. optional IRQ1, along with IRQ12 if the transfer was successful or if
87
88
89
90
91
92
93
//    there's no time left for a retry
//
// if the transfer has to be retried (for example, didn't get replies from all clients)
// and there is time, it repeats the sequence
//
// if there isn't enough time left on CMD_COUNT, IRQ12 is triggered alone when
// CMD_COUNT is 10, and the packet txheader[0] is set to 5
StapleButter's avatar
StapleButter committed
94
95
96
//
// RFSTATUS values:
// 0 = initial
97
// 1 = waiting for incoming packets
StapleButter's avatar
StapleButter committed
98
99
100
101
102
103
// 2 = switching from RX to TX
// 3 = TX
// 4 = switching from TX to RX
// 5 = MP host data sent, waiting for replies (RFPINS=0x0084)
// 6 = RX
// 7 = ??
104
// 8 = MP client sending reply, MP host sending ack (RFPINS=0x0046)
StapleButter's avatar
StapleButter committed
105
106
107
108
109
110
111
// 9 = idle


// wifi TODO:
// * work out how power saving works, there are oddities


StapleButter's avatar
StapleButter committed
112
113
114
115
116
117
118
119
120
121
122
123
124
bool Init()
{
    MPInited = false;

    return true;
}

void DeInit()
{
    if (MPInited)
        Platform::MP_DeInit();
}

StapleButter's avatar
StapleButter committed
125
126
void Reset()
{
StapleButter's avatar
StapleButter committed
127
    memset(RAM, 0, 0x2000);
128
    memset(IO, 0, 0x1000);
StapleButter's avatar
StapleButter committed
129

130
    Random = 1;
StapleButter's avatar
StapleButter committed
131

StapleButter's avatar
StapleButter committed
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
    memset(BBRegs, 0, 0x100);
    memset(BBRegsRO, 0, 0x100);

    #define BBREG_FIXED(id, val)  BBRegs[id] = val; BBRegsRO[id] = 1;
    BBREG_FIXED(0x00, 0x6D);
    BBREG_FIXED(0x0D, 0x00);
    BBREG_FIXED(0x0E, 0x00);
    BBREG_FIXED(0x0F, 0x00);
    BBREG_FIXED(0x10, 0x00);
    BBREG_FIXED(0x11, 0x00);
    BBREG_FIXED(0x12, 0x00);
    BBREG_FIXED(0x16, 0x00);
    BBREG_FIXED(0x17, 0x00);
    BBREG_FIXED(0x18, 0x00);
    BBREG_FIXED(0x19, 0x00);
    BBREG_FIXED(0x1A, 0x00);
    BBREG_FIXED(0x27, 0x00);
    BBREG_FIXED(0x4D, 0x00); // 00 or BF
    BBREG_FIXED(0x5D, 0x01);
    BBREG_FIXED(0x5E, 0x00);
    BBREG_FIXED(0x5F, 0x00);
    BBREG_FIXED(0x60, 0x00);
    BBREG_FIXED(0x61, 0x00);
    BBREG_FIXED(0x64, 0xFF); // FF or 3F
    BBREG_FIXED(0x66, 0x00);
    for (int i = 0x69; i < 0x100; i++)
    {
        BBREG_FIXED(i, 0x00);
    }
    #undef BBREG_FIXED
StapleButter's avatar
StapleButter committed
162
163
164

    RFVersion = SPI_Firmware::GetRFVersion();
    memset(RFRegs, 0, 4*0x40);
165

166
167
168
169
170
171
172
173
174
175
176
    u8 console = SPI_Firmware::GetConsoleType();
    if (console == 0xFF)
        IOPORT(0x000) = 0x1440;
    else if (console == 0x20)
        IOPORT(0x000) = 0xC340;
    else
    {
        printf("wifi: unknown console type %02X\n", console);
        IOPORT(0x000) = 0x1440;
    }

177
178
    memset(&IOPORT(0x018), 0xFF, 6);
    memset(&IOPORT(0x020), 0xFF, 6);
StapleButter's avatar
StapleButter committed
179
180
181

    USCounter = 0;
    USCompare = 0;
182
    BlockBeaconIRQ14 = false;
StapleButter's avatar
StapleButter committed
183

184
185
186
187
    ComStatus = 0;
    TXCurSlot = -1;
    RXCounter = 0;

StapleButter's avatar
StapleButter committed
188
    CmdCounter = 0;
StapleButter's avatar
StapleButter committed
189
190
191
}


192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
void SetIRQ(u32 irq)
{
    u32 oldflags = IOPORT(W_IF) & IOPORT(W_IE);

    IOPORT(W_IF) |= (1<<irq);
    u32 newflags = IOPORT(W_IF) & IOPORT(W_IE);

    if ((oldflags == 0) && (newflags != 0))
        NDS::SetIRQ(1, NDS::IRQ_Wifi);
}

void SetIRQ13()
{
    SetIRQ(13);

    if (!(IOPORT(W_PowerTX) & 0x0002))
    {
        IOPORT(0x034) = 0x0002;
        // TODO: 03C
        IOPORT(W_RFPins) = 0x0046;
        IOPORT(W_RFStatus) = 9;
    }
}

216
void SetIRQ14(int source) // 0=USCOMPARE 1=BEACONCOUNT 2=forced
217
{
218
219
220
221
222
223
224
225
    if (source != 2)
        IOPORT(W_BeaconCount1) = IOPORT(W_BeaconInterval);

    if (BlockBeaconIRQ14 && source == 1)
        return;
    if (!(IOPORT(W_USCompareCnt)) & 0x0001)
        return;

226
227
    SetIRQ(14);

228
    if (source == 2)
229
        printf("wifi: weird forced IRQ14\n");
230
231

    IOPORT(W_BeaconCount2) = 0xFFFF;
232
    IOPORT(W_TXReqRead) &= 0xFFF2;
233

StapleButter's avatar
StapleButter committed
234
235
    if (IOPORT(W_TXSlotBeacon) & 0x8000)
    {
StapleButter's avatar
StapleButter committed
236
        StartTX_Beacon();
StapleButter's avatar
StapleButter committed
237
    }
238
239
240
241

    if (IOPORT(W_ListenCount) == 0)
        IOPORT(W_ListenCount) = IOPORT(W_ListenInterval);

StapleButter's avatar
StapleButter committed
242
    IOPORT(W_ListenCount)--;
243
244
245
246
247
248
249
}

void SetIRQ15()
{
    SetIRQ(15);

    if (IOPORT(W_PowerTX) & 0x0001)
StapleButter's avatar
StapleButter committed
250
    {
251
252
253
254
255
256
        IOPORT(W_RFPins) |= 0x0080;
        IOPORT(W_RFStatus) = 1;
    }
}


StapleButter's avatar
StapleButter committed
257
258
259
260
261
262
bool MACEqual(u8* a, u8* b)
{
    return (*(u32*)&a[0] == *(u32*)&b[0]) && (*(u16*)&a[4] == *(u16*)&b[4]);
}


263
264
265
266
267
268
269
270
271
// TODO: set RFSTATUS/RFPINS

int PreambleLen(int rate)
{
    if (rate == 1) return 192;
    if (IOPORT(W_Preamble) & 0x0004) return 96;
    return 192;
}

StapleButter's avatar
StapleButter committed
272
273
274
275
276
277
278
279
280
281
void StartTX_LocN(int nslot, int loc)
{
    TXSlot* slot = &TXSlots[nslot];

    if (IOPORT(W_TXSlotLoc1 + (loc*4)) & 0x7000)
        printf("wifi: unusual loc%d bits set %04X\n", loc, IOPORT(W_TXSlotLoc1 + (loc*4)));

    slot->Addr = (IOPORT(W_TXSlotLoc1 + (loc*4)) & 0x0FFF) << 1;
    slot->Length = *(u16*)&RAM[slot->Addr + 0xA] & 0x3FFF;

StapleButter's avatar
StapleButter committed
282
    u8 rate = RAM[slot->Addr + 0x8];
StapleButter's avatar
StapleButter committed
283
284
285
286
287
288
    if (rate == 0x14) slot->Rate = 2;
    else              slot->Rate = 1;

    slot->CurPhase = 0;
    slot->CurPhaseTime = PreambleLen(slot->Rate);

289
290
291
    //int txlen = Platform::MP_SendPacket(&RAM[slot->Addr], 12 + slot->Length);
    //printf("wifi: sent %d/%d bytes of loc%d packet. framectl=%04X\n",
    //       txlen, 12+slot->Length, loc, *(u16*)&RAM[slot->Addr + 0xC]);
StapleButter's avatar
StapleButter committed
292
293
}

StapleButter's avatar
StapleButter committed
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
void StartTX_Cmd()
{
    TXSlot* slot = &TXSlots[1];

    // TODO: cancel the transfer if there isn't enough time left (check CMDCOUNT)

    slot->Addr = (IOPORT(W_TXSlotCmd) & 0x0FFF) << 1;
    slot->Length = *(u16*)&RAM[slot->Addr + 0xA] & 0x3FFF;

    u8 rate = RAM[slot->Addr + 0x8];
    if (rate == 0x14) slot->Rate = 2;
    else              slot->Rate = 1;

    slot->CurPhase = 0;
    slot->CurPhaseTime = PreambleLen(slot->Rate);

310
311
312
    //int txlen = Platform::MP_SendPacket(&RAM[slot->Addr], 12 + slot->Length);
    //printf("wifi: sent %d/%d bytes of cmd packet. clients=%04X\n", txlen, 12+slot->Length, *(u16*)&RAM[slot->Addr + 0xC + 26]);
//printf("%08X%08X | %04X %04X\n", (u32)(USCounter>>32), (u32)USCounter, slot->Addr, *(u16*)&RAM[slot->Addr + 0xC]);
StapleButter's avatar
StapleButter committed
313
314
}

315
316
317
318
319
320
321
void StartTX_Beacon()
{
    TXSlot* slot = &TXSlots[4];

    slot->Addr = (IOPORT(W_TXSlotBeacon) & 0x0FFF) << 1;
    slot->Length = *(u16*)&RAM[slot->Addr + 0xA] & 0x3FFF;

StapleButter's avatar
StapleButter committed
322
    u8 rate = RAM[slot->Addr + 0x8];
323
324
325
326
327
328
329
330
331
    if (rate == 0x14) slot->Rate = 2;
    else              slot->Rate = 1;

    slot->CurPhase = 0;
    slot->CurPhaseTime = PreambleLen(slot->Rate);

    u64 oldval = *(u64*)&RAM[slot->Addr + 0xC + 24];
    *(u64*)&RAM[slot->Addr + 0xC + 24] = USCounter;

332
    //int txlen = Platform::MP_SendPacket(&RAM[slot->Addr], 12 + slot->Length);
StapleButter's avatar
StapleButter committed
333
    //printf("wifi: sent %d/%d bytes of beacon packet\n", txlen, 12+slot->Length);
334

335
    //*(u64*)&RAM[slot->Addr + 0xC + 24] = oldval;
StapleButter's avatar
StapleButter committed
336
337

    IOPORT(W_TXBusy) |= 0x0010;
338
339
}

340
// TODO eventually: there is a small delay to firing TX
StapleButter's avatar
StapleButter committed
341
342
343
void FireTX()
{
    u16 txbusy = IOPORT(W_TXBusy);
344
345

    u16 txreq = IOPORT(W_TXReqRead);
StapleButter's avatar
StapleButter committed
346
347
348
349
350
351
352
353
    u16 txstart = 0;
    if (IOPORT(W_TXSlotLoc1) & 0x8000) txstart |= 0x0001;
    if (IOPORT(W_TXSlotCmd ) & 0x8000) txstart |= 0x0002;
    if (IOPORT(W_TXSlotLoc2) & 0x8000) txstart |= 0x0004;
    if (IOPORT(W_TXSlotLoc3) & 0x8000) txstart |= 0x0008;

    txstart &= txreq;
    txstart &= ~txbusy;
354

StapleButter's avatar
StapleButter committed
355
    IOPORT(W_TXBusy) = txbusy | txstart;
356

StapleButter's avatar
StapleButter committed
357
    if (txstart & 0x0008)
StapleButter's avatar
StapleButter committed
358
    {
359
360
        StartTX_LocN(3, 2);
        return;
StapleButter's avatar
StapleButter committed
361
362
    }

StapleButter's avatar
StapleButter committed
363
    if (txstart & 0x0004)
StapleButter's avatar
StapleButter committed
364
    {
365
366
        StartTX_LocN(2, 1);
        return;
StapleButter's avatar
StapleButter committed
367
368
    }

StapleButter's avatar
StapleButter committed
369
370
371
372
373
    if (txstart & 0x0002)
    {
        StartTX_Cmd();
        return;
    }
374

StapleButter's avatar
StapleButter committed
375
    if (txstart & 0x0001)
StapleButter's avatar
StapleButter committed
376
    {
377
378
        StartTX_LocN(0, 0);
        return;
StapleButter's avatar
StapleButter committed
379
380
381
    }
}

382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
void SendMPReply(u16 clienttime)
{
    TXSlot* slot = &TXSlots[5];

    slot->Addr = (IOPORT(W_TXSlotReply2) & 0x0FFF) << 1;
    slot->Length = *(u16*)&RAM[slot->Addr + 0xA] & 0x3FFF;

    u8 rate = RAM[slot->Addr + 0x8];
    if (rate == 0x14) slot->Rate = 2;
    else              slot->Rate = 1;

    slot->CurPhase = 0;
    slot->CurPhaseTime = 32 + ((clienttime + 10) * (IOPORT(W_AIDLow) - 1));

    IOPORT(W_TXBusy) |= 0x0080;
}

void SendMPAck()
{
    u8 ack[12 + 32];

    *(u16*)&ack[0xA] = 32; // length

    // rate
    if (TXSlots[1].Rate == 2) ack[0x8] = 0x14;
    else                      ack[0x8] = 0xA;

	*(u16*)&ack[0xC + 0x00] = 0x0218;
	*(u16*)&ack[0xC + 0x02] = 0;
	*(u16*)&ack[0xC + 0x04] = 0x0903;
	*(u16*)&ack[0xC + 0x06] = 0x00BF;
	*(u16*)&ack[0xC + 0x08] = 0x0300;
	*(u16*)&ack[0xC + 0x0A] = IOPORT(W_BSSID0);
	*(u16*)&ack[0xC + 0x0C] = IOPORT(W_BSSID1);
	*(u16*)&ack[0xC + 0x0E] = IOPORT(W_BSSID2);
	*(u16*)&ack[0xC + 0x10] = IOPORT(W_MACAddr0);
	*(u16*)&ack[0xC + 0x12] = IOPORT(W_MACAddr1);
	*(u16*)&ack[0xC + 0x14] = IOPORT(W_MACAddr2);
420
	*(u16*)&ack[0xC + 0x16] = IOPORT(W_TXSeqNo) << 4;
421
422
423
424
425
426
427
428
	*(u16*)&ack[0xC + 0x18] = 0x0033; // ???
	*(u16*)&ack[0xC + 0x1A] = 0;
	*(u32*)&ack[0xC + 0x1C] = 0;

	int txlen = Platform::MP_SendPacket(ack, 12+32);
	printf("wifi: sent %d/44 bytes of MP ack\n", txlen);
}

StapleButter's avatar
StapleButter committed
429
430
431
432
433
434
435
436
437
438
u32 NumClients(u16 bitmask)
{
    u32 ret = 0;
    for (int i = 1; i < 16; i++)
    {
        if (bitmask & (1<<i)) ret++;
    }
    return ret;
}

439
bool CheckRX(bool block);
440

441
bool ProcessTX(TXSlot* slot, int num)
442
443
{
    slot->CurPhaseTime--;
444
    if (slot->CurPhaseTime > 0) return false;
445
446
447
448
449
450
451
452
453
454
455
456

    switch (slot->CurPhase)
    {
    case 0: // preamble done
        {
            u32 len = slot->Length;
            if (slot->Rate == 2) len *= 4;
            else                 len *= 8;

            slot->CurPhase = 1;
            slot->CurPhaseTime = len;

457
458
459
            // CHECKME
            // hardware seems to do this automatically?
            // I saw it done on captured packets, but saw no code to do it
460
            /*if (num == 1)
461
462
463
464
465
            {
                if (slot->Length > 32)
                {
                    *(u16*)&RAM[slot->Addr + 0xC + (slot->Length-6)] = *(u16*)&RAM[slot->Addr + 0x26];
                }
466
            }*/
467

468
469
            if (num != 5) SetIRQ(7);
            *(u16*)&RAM[slot->Addr + 0xC + 22] = IOPORT(W_TXSeqNo) << 4;
470
            IOPORT(W_TXSeqNo) = (IOPORT(W_TXSeqNo) + 1) & 0x0FFF;
471
472

            int txlen = Platform::MP_SendPacket(&RAM[slot->Addr], 12 + slot->Length);
473
474
            if (num != 4) printf("wifi: sent %d/%d bytes of slot%d packet, framectl=%04X, %04X %04X\n",
                                 txlen, slot->Length+12, num, *(u16*)&RAM[slot->Addr + 0xC], *(u16*)&RAM[slot->Addr + 0x24], *(u16*)&RAM[slot->Addr + 0x26]);
475
476
477
478
479
480
        }
        break;

    case 1: // transmit done
        {
            // checkme
481
            if(num!=5)*(u16*)&RAM[slot->Addr] = 0x0001;
482
483
            RAM[slot->Addr + 5] = 0;

StapleButter's avatar
StapleButter committed
484
485
            if (num == 1)
            {
486
487
                u16 clientmask = *(u16*)&RAM[slot->Addr + 12 + 24 + 2];
                u32 nclients = NumClients(clientmask);
StapleButter's avatar
StapleButter committed
488
489
490

                slot->CurPhase = 2;
                slot->CurPhaseTime = 112 + ((10 + IOPORT(W_CmdReplyTime)) * nclients);
491
printf("tx done. listen to replies\n");
492
                if (CheckRX(true)) ComStatus |= 0x2;
StapleButter's avatar
StapleButter committed
493
494
495
496
497

                // TODO: RFSTATUS/RFPINS

                break;
            }
498
499
500
501
502
503
504
            else if (num == 5)
            {
                IOPORT(W_TXBusy) &= ~0x80;
                IOPORT(W_TXSlotReply2) &= 0x7FFF;
                FireTX();
                break;
            }
StapleButter's avatar
StapleButter committed
505

StapleButter's avatar
StapleButter committed
506
            IOPORT(W_TXBusy) &= ~(1<<num);
507

StapleButter's avatar
StapleButter committed
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
            switch (num)
            {
            case 0:
            case 2:
            case 3:
                IOPORT(W_TXStat) = 0x0001 | ((num?(num-1):0)<<12);
                SetIRQ(1);
                IOPORT(W_TXSlotLoc1 + ((num?(num-1):0)*4)) &= 0x7FFF;
                break;

            case 4: // beacon
                if (IOPORT(W_TXStatCnt) & 0x8000)
                {
                    IOPORT(W_TXStat) = 0x0301;
                    SetIRQ(1);
                }
                break;
            }

            FireTX();
528
        }
529
        return true;
StapleButter's avatar
StapleButter committed
530
531
532
533
534

    case 2: // MP host transfer done
        {
            SetIRQ(7);

535
536
537
538
            if (slot->Rate == 2) slot->CurPhaseTime = 32 * 4;
            else                 slot->CurPhaseTime = 32 * 8;

            SendMPAck();
StapleButter's avatar
StapleButter committed
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562

            slot->CurPhase = 3;
        }
        break;

    case 3: // MP host ack transfer (reply wait done)
        {
            // checkme
            IOPORT(W_TXBusy) &= ~(1<<1);
            IOPORT(W_TXSlotCmd) &= 0x7FFF; // confirmed

            SetIRQ(12);
            IOPORT(W_TXSeqNo) = (IOPORT(W_TXSeqNo) + 1) & 0x0FFF;

            if (IOPORT(W_TXStatCnt) & 0x2000)
            {
                IOPORT(W_TXStat) = 0x0B01;
                SetIRQ(1);
            }
            else if (IOPORT(W_TXStatCnt) & 0x4000)
            {
                IOPORT(W_TXStat) = 0x0801; // checkme
                SetIRQ(1);
            }
563
printf("MP TX over\n");
StapleButter's avatar
StapleButter committed
564
565
            FireTX();
        }
566
        return true;
567
    }
568
569

    return false;
570
571
572
}


573
bool CheckRX(bool block)
StapleButter's avatar
StapleButter committed
574
{
StapleButter's avatar
StapleButter committed
575
    if (!(IOPORT(W_RXCnt) & 0x8000))
576
        return false;
StapleButter's avatar
StapleButter committed
577

578
579
580
581
582
    u16 framelen;
    u16 framectl;
    u8 txrate;
    bool bssidmatch;
    u16 rxflags;
StapleButter's avatar
StapleButter committed
583

584
    for (;;)
StapleButter's avatar
StapleButter committed
585
    {
586
587
588
        int rxlen = Platform::MP_RecvPacket(RXBuffer, block);
        if (rxlen == 0) return false;
        if (rxlen < 12+24) continue;
StapleButter's avatar
StapleButter committed
589

590
591
592
593
594
595
596
        framelen = *(u16*)&RXBuffer[10];
        if (framelen != rxlen-12)
        {
            printf("bad frame length\n");
            continue;
        }
        framelen -= 4;
StapleButter's avatar
StapleButter committed
597

598
599
        framectl = *(u16*)&RXBuffer[12+0];
        txrate = RXBuffer[8];
StapleButter's avatar
StapleButter committed
600

601
602
603
        u32 a_src, a_dst, a_bss;
        rxflags = 0x0010;
        switch (framectl & 0x000C)
StapleButter's avatar
StapleButter committed
604
        {
605
        case 0x0000: // management
StapleButter's avatar
StapleButter committed
606
607
608
            a_src = 10;
            a_dst = 4;
            a_bss = 16;
609
610
            if ((framectl & 0x00F0) == 0x0080)
                rxflags |= 0x0001;
StapleButter's avatar
StapleButter committed
611
            break;
612
613

        case 0x0004: // control
StapleButter's avatar
StapleButter committed
614
            printf("blarg\n");
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
            continue;

        case 0x0008: // data
            switch (framectl & 0x0300)
            {
            case 0x0000: // STA to STA
                a_src = 10;
                a_dst = 4;
                a_bss = 16;
                break;
            case 0x0100: // STA to DS
                a_src = 10;
                a_dst = 16;
                a_bss = 4;
                break;
            case 0x0200: // DS to STA
                a_src = 16;
                a_dst = 4;
                a_bss = 10;
                break;
            case 0x0300: // DS to DS
                printf("blarg\n");
                continue;
            }
            framectl &= 0xE7FF;
            if (framectl == 0x0228) rxflags |= 0x000C;
            else if (framectl == 0x0218) rxflags |= 0x000D;
            else if (framectl == 0x0118) rxflags |= 0x000E; // checkme
            else if (framectl == 0x0158) rxflags |= 0x000F; // wild guess. those two might be swapped
            else rxflags |= 0x0008;
            break;
StapleButter's avatar
StapleButter committed
646
        }
647

648
649
        if (MACEqual(&RXBuffer[12 + a_src], (u8*)&IOPORT(W_MACAddr0)))
            continue; // oops. we received a packet we just sent.
StapleButter's avatar
StapleButter committed
650

651
652
653
654
655
656
657
658
659
        bssidmatch = MACEqual(&RXBuffer[12 + a_bss], (u8*)&IOPORT(W_BSSID0));
        if (!(IOPORT(W_BSSID0) & 0x0001) && !(RXBuffer[12 + a_bss] & 0x01) &&
            !bssidmatch)
        {
            printf("received packet %04X but it didn't pass the BSSID check\n", framectl);
            continue;
        }

        break;
660
    }
StapleButter's avatar
StapleButter committed
661

662
663
    //if (framectl != 0x0080 && framectl != 0x0228)
        printf("wifi: received packet FC:%04X SN:%04X CL:%04X\n", framectl, *(u16*)&RXBuffer[12+4+6+6+6], *(u16*)&RXBuffer[12+4+6+6+6+2+2]);
StapleButter's avatar
StapleButter committed
664
665

    // make RX header
666
    // TODO: make it upon RX end
StapleButter's avatar
StapleButter committed
667
668
669
670
671
672
673
674
675
676

    if (bssidmatch) rxflags |= 0x8000;

    *(u16*)&RXBuffer[0] = rxflags;
    *(u16*)&RXBuffer[2] = 0x0040; // ???
    *(u16*)&RXBuffer[4] = 0x7777;
    *(u16*)&RXBuffer[6] = txrate;
    *(u16*)&RXBuffer[8] = framelen;
    *(u16*)&RXBuffer[10] = 0x4080; // min/max RSSI. dunno

677
    RXTime = 16;//framelen * ((txrate==0x14) ? 4:8);
StapleButter's avatar
StapleButter committed
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694

    // TODO: write packet progressively?
    // TODO: RX/TX addr register
    framelen += 12;
    u16 addr = IOPORT(W_RXBufWriteCursor) << 1;
    for (int i = 0; i < framelen; i += 2)
    {
        *(u16*)&RAM[addr] = *(u16*)&RXBuffer[i];
        addr += 2;

        if (addr == (IOPORT(W_RXBufEnd) & 0x1FFE))
            addr = (IOPORT(W_RXBufBegin) & 0x1FFE);

        if (addr == (IOPORT(W_RXBufReadCursor) << 1))
        {
            printf("wifi: RX buffer full\n");
            // TODO: proper error management
695
            return false;
StapleButter's avatar
StapleButter committed
696
697
698
        }
    }

699
700
701
702
703
    if (addr & 0x2) addr += 2;
    if (addr == (IOPORT(W_RXBufEnd) & 0x1FFE))
        addr = (IOPORT(W_RXBufBegin) & 0x1FFE);

    RXEndAddr = (addr & ~0x3) >> 1;
StapleButter's avatar
StapleButter committed
704
705

    SetIRQ(6);
706
    return true;
StapleButter's avatar
StapleButter committed
707
708
709
}


710
711
712
713
void MSTimer()
{
    if (IOPORT(W_USCompareCnt))
    {
714
715
716
717
718
719
720
721
722
723
724
        if (USCounter == USCompare)
        {
            BlockBeaconIRQ14 = false;
            SetIRQ14(0);
        }
    }

    IOPORT(W_BeaconCount1)--;
    if (IOPORT(W_BeaconCount1) == 0)
    {
        SetIRQ14(1);
725
726
727
728
729
730
731
    }

    if (IOPORT(W_BeaconCount2) != 0)
    {
        IOPORT(W_BeaconCount2)--;
        if (IOPORT(W_BeaconCount2) == 0) SetIRQ13();
    }
StapleButter's avatar
StapleButter committed
732

733
734
    //if (!IOPORT(W_TXBusy))
    //    CheckRX(false);
735
}
736
u64 mpreplywindow;
737
738
739
740
741
742
743
744
745
746
747
748
749
750
void USTimer(u32 param)
{
    if (IOPORT(W_USCountCnt))
    {
        USCounter++;
        u32 uspart = (USCounter & 0x3FF);

        if (IOPORT(W_USCompareCnt))
        {
            u32 beaconus = (IOPORT(W_BeaconCount1) << 10) | (0x3FF - uspart);
            if (beaconus == IOPORT(W_PreBeacon)) SetIRQ15();
        }

        if (!uspart) MSTimer();
751

752
        //if (!(uspart & 0x1FF)) CheckRX(false);
753
754
    }

StapleButter's avatar
StapleButter committed
755
756
757
758
759
760
761
762
    if (IOPORT(W_CmdCountCnt) & 0x0001)
    {
        if (CmdCounter > 0)
        {
            CmdCounter--;
        }
    }

763
764
765
    if (IOPORT(W_ContentFree) != 0)
        IOPORT(W_ContentFree)--;

766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
    if (ComStatus == 0)
    {
        u16 txbusy = IOPORT(W_TXBusy);
        if (txbusy)
        {
            ComStatus = 0x2;
            if      (txbusy & 0x0080) TXCurSlot = 5;
            else if (txbusy & 0x0010) TXCurSlot = 4;
            else if (txbusy & 0x0008) TXCurSlot = 3;
            else if (txbusy & 0x0004) TXCurSlot = 2;
            else if (txbusy & 0x0002) TXCurSlot = 1;
            else if (txbusy & 0x0001) TXCurSlot = 0;
        }
        else
        {
            if ((!(RXCounter & 0x1FF)))
            {
                if (CheckRX(false))
                    ComStatus = 0x1;
            }

            RXCounter++;
        }
    }

    if (ComStatus & 0x2)
StapleButter's avatar
StapleButter committed
792
    {
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
        bool finished = ProcessTX(&TXSlots[TXCurSlot], TXCurSlot);
        if (finished)
        {
            // transfer finished, see if there's another slot to do
            // checkme: priority order of beacon/reply
            u16 txbusy = IOPORT(W_TXBusy);
            if      (txbusy & 0x0080) TXCurSlot = 5;
            else if (txbusy & 0x0010) TXCurSlot = 4;
            else if (txbusy & 0x0008) TXCurSlot = 3;
            else if (txbusy & 0x0004) TXCurSlot = 2;
            else if (txbusy & 0x0002) TXCurSlot = 1;
            else if (txbusy & 0x0001) TXCurSlot = 0;
            else
            {
                TXCurSlot = -1;
                ComStatus = 0;
                RXCounter = 0;
            }
        }
StapleButter's avatar
StapleButter committed
812
    }
813
    if (ComStatus & 0x1)
StapleButter's avatar
StapleButter committed
814
    {
StapleButter's avatar
StapleButter committed
815
        // TODO: make sure it isn't possible to send and receive at the same time
StapleButter's avatar
StapleButter committed
816
817
818
819
820
        RXTime--;
        if (RXTime == 0)
        {
            IOPORT(W_RXBufWriteCursor) = RXEndAddr;
            SetIRQ(0);
821

822
823
824
825
826
827
            if (TXCurSlot == -1)
            {
                ComStatus = 0;
                RXCounter = 0;
            }

828
829
830
831
832
            if ((RXBuffer[0] & 0x0F) == 0x0C)
            {
                u16 clientmask = *(u16*)&RXBuffer[0xC + 26];
                if (clientmask & (1 << IOPORT(W_AIDLow)))
                {
833
834
835
                    printf("MP: attempting to reply: %04X %04X, delay=%04X\n",
                           IOPORT(W_TXSlotReply1), IOPORT(W_TXSlotReply2), *(u16*)&RXBuffer[0xC + 24]);
                    mpreplywindow = USCounter;
836
837
838
839
840
841
842
843
                    // this is a big fat guess
                    if (IOPORT(W_TXSlotReply1) & 0x8000)
                    {
                        IOPORT(W_TXSlotReply2) = IOPORT(W_TXSlotReply1);
                        IOPORT(W_TXSlotReply1) = 0;

                        SendMPReply(*(u16*)&RXBuffer[0xC + 24]);
                    }
844
845
                    //if (IOPORT(W_TXSlotReply2) & 0x8000)
                    //    SendMPReply(*(u16*)&RXBuffer[0xC + 24]);
846
847
                }
            }
StapleButter's avatar
StapleButter committed
848
849
        }
    }
850

851
852
853
854
855
856
    // TODO: make it more accurate, eventually
    // in the DS, the wifi system has its own 22MHz clock and doesn't use the system clock
    NDS::ScheduleEvent(NDS::Event_Wifi, true, 33, USTimer, 0);
}


StapleButter's avatar
StapleButter committed
857
858
void RFTransfer_Type2()
{
StapleButter's avatar
StapleButter committed
859
    u32 id = (IOPORT(W_RFData2) >> 2) & 0x1F;
StapleButter's avatar
StapleButter committed
860

StapleButter's avatar
StapleButter committed
861
    if (IOPORT(W_RFData2) & 0x0080)
StapleButter's avatar
StapleButter committed
862
863
    {
        u32 data = RFRegs[id];
StapleButter's avatar
StapleButter committed
864
865
        IOPORT(W_RFData1) = data & 0xFFFF;
        IOPORT(W_RFData2) = (IOPORT(W_RFData2) & 0xFFFC) | ((data >> 16) & 0x3);
StapleButter's avatar
StapleButter committed
866
867
868
    }
    else
    {
StapleButter's avatar
StapleButter committed
869
        u32 data = IOPORT(W_RFData1) | ((IOPORT(W_RFData2) & 0x0003) << 16);
StapleButter's avatar
StapleButter committed
870
871
872
873
874
875
        RFRegs[id] = data;
    }
}

void RFTransfer_Type3()
{
StapleButter's avatar
StapleButter committed
876
    u32 id = (IOPORT(W_RFData1) >> 8) & 0x3F;
StapleButter's avatar
StapleButter committed
877

StapleButter's avatar
StapleButter committed
878
    u32 cmd = IOPORT(W_RFData2) & 0xF;
StapleButter's avatar
StapleButter committed
879
880
    if (cmd == 6)
    {
StapleButter's avatar
StapleButter committed
881
        IOPORT(W_RFData1) = (IOPORT(W_RFData1) & 0xFF00) | (RFRegs[id] & 0xFF);
StapleButter's avatar
StapleButter committed
882
883
884
    }
    else if (cmd == 5)
    {
StapleButter's avatar
StapleButter committed
885
        u32 data = IOPORT(W_RFData1) & 0xFF;
StapleButter's avatar
StapleButter committed
886
887
        RFRegs[id] = data;
    }
StapleButter's avatar
StapleButter committed
888
889
890
}


StapleButter's avatar
StapleButter committed
891
892
// TODO: wifi waitstates

StapleButter's avatar
StapleButter committed
893
894
u16 Read(u32 addr)
{
895
896
897
    if (addr >= 0x04810000)
        return 0;

898
899
    addr &= 0x7FFE;
    //printf("WIFI: read %08X\n", addr);
StapleButter's avatar
StapleButter committed
900
901
    if (addr >= 0x4000 && addr < 0x6000)
    {
902
        return *(u16*)&RAM[addr & 0x1FFE];
StapleButter's avatar
StapleButter committed
903
    }
904
    if (addr >= 0x2000 && addr < 0x4000)
StapleButter's avatar
StapleButter committed
905
        return 0xFFFF;
906
907

    bool activeread = (addr < 0x1000);
StapleButter's avatar
StapleButter committed
908

StapleButter's avatar
StapleButter committed
909
910
    switch (addr)
    {
StapleButter's avatar
StapleButter committed
911
    case W_Random: // random generator. not accurate
StapleButter's avatar
StapleButter committed
912
913
914
        Random = (Random & 0x1) ^ (((Random & 0x3FF) << 1) | (Random >> 10));
        return Random;

StapleButter's avatar
StapleButter committed
915
916
    case W_Preamble:
        return IOPORT(W_Preamble) & 0x0003;
917

918
919
920
921
922
923
924
925
926
927
    case W_USCount0: return (u16)(USCounter & 0xFFFF);
    case W_USCount1: return (u16)((USCounter >> 16) & 0xFFFF);
    case W_USCount2: return (u16)((USCounter >> 32) & 0xFFFF);
    case W_USCount3: return (u16)(USCounter >> 48);

    case W_USCompare0: return (u16)(USCompare & 0xFFFF);
    case W_USCompare1: return (u16)((USCompare >> 16) & 0xFFFF);
    case W_USCompare2: return (u16)((USCompare >> 32) & 0xFFFF);
    case W_USCompare3: return (u16)(USCompare >> 48);

StapleButter's avatar
StapleButter committed
928
929
    case W_CmdCount: return (CmdCounter + 9) / 10;

StapleButter's avatar
StapleButter committed
930
931
    case W_BBRead:
        if ((IOPORT(W_BBCnt) & 0xF000) != 0x6000)
StapleButter's avatar
StapleButter committed
932
        {
StapleButter's avatar
StapleButter committed
933
            printf("WIFI: bad BB read, CNT=%04X\n", IOPORT(W_BBCnt));
StapleButter's avatar
StapleButter committed
934
935
            return 0;
        }
StapleButter's avatar
StapleButter committed
936
        return BBRegs[IOPORT(W_BBCnt) & 0xFF];
StapleButter's avatar
StapleButter committed
937

StapleButter's avatar
StapleButter committed
938
    case W_BBBusy:
StapleButter's avatar
StapleButter committed
939
        return 0; // TODO eventually (BB busy flag)
StapleButter's avatar
StapleButter committed
940
    case W_RFBusy:
StapleButter's avatar
StapleButter committed
941
        return 0; // TODO eventually (RF busy flag)
942
943
944
945

    case W_RXBufDataRead:
        if (activeread)
        {
946
            u32 rdaddr = IOPORT(W_RXBufReadAddr);
947
948
949
950
951
952

            u16 ret = *(u16*)&RAM[rdaddr];

            rdaddr += 2;
            if (rdaddr == (IOPORT(W_RXBufEnd) & 0x1FFE))
                rdaddr = (IOPORT(W_RXBufBegin) & 0x1FFE);
953
            if (rdaddr == IOPORT(W_RXBufGapAddr))
954
            {
955
                rdaddr += (IOPORT(W_RXBufGapSize) << 1);
956
957
                if (rdaddr >= (IOPORT(W_RXBufEnd) & 0x1FFE))
                    rdaddr = rdaddr + (IOPORT(W_RXBufBegin) & 0x1FFE) - (IOPORT(W_RXBufEnd) & 0x1FFE);
958
959
960

                if (IOPORT(0x000) == 0xC340)
                    IOPORT(W_RXBufGapSize) = 0;
961
            }
962
963
964

            IOPORT(W_RXBufReadAddr) = rdaddr & 0x1FFE;
            IOPORT(W_RXBufDataRead) = ret;
965
966
967
968
969
970
971

            if (IOPORT(W_RXBufCount) > 0)
            {
                IOPORT(W_RXBufCount)--;
                if (IOPORT(W_RXBufCount) == 0)
                    SetIRQ(9);
            }
972
973
        }
        break;
974
975
976
977

    case W_TXBusy:
        return IOPORT(W_TXBusy) & 0x001F; // no bit for MP replies. odd

978
979
        //case 0x214: NDS::debug(0); break;
        //case 0x040: NDS::debug(0); break;
980
        //case 0x54: printf("wifi: read WRCSR -> %04X\n", IOPORT(0x54)); break;
StapleButter's avatar
StapleButter committed
981
982
    }

983
984
    //printf("WIFI: read %08X\n", addr);
    return IOPORT(addr&0xFFF);
StapleButter's avatar
StapleButter committed
985
986
987
988
}

void Write(u32 addr, u16 val)
{
989
990
991
    if (addr >= 0x04810000)
        return;

992
993
    addr &= 0x7FFE;
    //printf("WIFI: write %08X %04X\n", addr, val);
StapleButter's avatar
StapleButter committed
994
995
    if (addr >= 0x4000 && addr < 0x6000)
    {
996
        *(u16*)&RAM[addr & 0x1FFE] = val;
StapleButter's avatar
StapleButter committed
997
998
        return;
    }
999
1000
    if (addr >= 0x2000 && addr < 0x4000)
        return;
StapleButter's avatar
StapleButter committed
1001

StapleButter's avatar
StapleButter committed
1002
1003
    switch (addr)
    {
StapleButter's avatar
StapleButter committed
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
    case W_ModeReset:
        {
            u16 oldval = IOPORT(W_ModeReset);

            if (!(oldval & 0x0001) && (val & 0x0001))
            {
                IOPORT(0x034) = 0x0002;
                IOPORT(W_RFPins) = 0x0046;
                IOPORT(W_RFStatus) = 9;
                IOPORT(0x27C) = 0x0005;
                // TODO: 02A2??
            }
            else if ((oldval & 0x0001) && !(val & 0x0001))
            {
                IOPORT(0x27C) = 0x000A;
            }

            if (val & 0x2000)
            {
                IOPORT(W_RXBufWriteAddr) = 0;
                IOPORT(W_CmdTotalTime) = 0;
                IOPORT(W_CmdReplyTime) = 0;
                IOPORT(0x1A4) = 0;
                IOPORT(0x278) = 0x000F;
                // TODO: other ports??
            }
            if (val & 0x4000)
            {
                IOPORT(W_ModeWEP) = 0;
                IOPORT(W_TXStatCnt) = 0;
                IOPORT(0x00A) = 0;
                IOPORT(W_MACAddr0) = 0;
                IOPORT(W_MACAddr1) = 0;
                IOPORT(W_MACAddr2) = 0;
                IOPORT(W_BSSID0) = 0;
                IOPORT(W_BSSID1) = 0;
                IOPORT(W_BSSID2) = 0;
                IOPORT(W_AIDLow) = 0;
                IOPORT(W_AIDFull) = 0;
                IOPORT(W_TXRetryLimit) = 0x0707;
                IOPORT(0x02E) = 0;
                IOPORT(W_RXBufBegin) = 0x4000;
                IOPORT(W_RXBufEnd) = 0x4800;
                IOPORT(W_TXBeaconTIM) = 0;
                IOPORT(W_Preamble) = 0x0001;
                IOPORT(W_RXFilter) = 0x0401;
                IOPORT(0x0D4) = 0x0001;
                IOPORT(W_RXFilter2) = 0x0008;
                IOPORT(0x0EC) = 0x3F03;
                IOPORT(W_TXHeaderCnt) = 0;
                IOPORT(0x198) = 0;
                IOPORT(0x1A2) = 0x0001;
                IOPORT(0x224) = 0x0003;
                IOPORT(0x230) = 0x0047;
            }
        }
        break;

    case W_ModeWEP:
1063
1064
1065
        val &= 0x007F;
        break;

StapleButter's avatar
StapleButter committed
1066
    case W_IF:
1067
1068
1069
1070
1071
        IOPORT(W_IF) &= ~val;
        return;
    case W_IFSet:
        IOPORT(W_IF) |= (val & 0xFBFF);
        printf("wifi: force-setting IF %04X\n", val);
1072
1073
        return;

StapleButter's avatar
StapleButter committed
1074
1075
    case W_PowerState:
        if (val & 0x0002)
StapleButter's avatar
StapleButter committed
1076
        {
1077
1078
            // TODO: delay for this
            SetIRQ(11);
StapleButter's avatar
StapleButter committed
1079
            IOPORT(W_PowerState) = 0x0000;
1080
1081
1082
1083

            // checkme
            IOPORT(W_RFPins) = 0x00C6;
            IOPORT(W_RFStatus) = 9;
StapleButter's avatar
StapleButter committed
1084
1085
        }
        return;
StapleButter's avatar
StapleButter committed
1086
    case W_PowerForce:
1087
        if ((val&0x8001)==0x8000) printf("WIFI: forcing power %04X\n", val);
StapleButter's avatar
StapleButter committed
1088
1089
1090
1091
1092
1093
        val &= 0x8001;
        if (val == 0x8001)
        {
            IOPORT(0x034) = 0x0002;
            IOPORT(W_PowerState) = 0x0200;
            IOPORT(W_TXReqRead) = 0;
1094
            IOPORT(W_RFPins) = 0x0046;
StapleButter's avatar
StapleButter committed
1095
1096
1097
            IOPORT(W_RFStatus) = 9;
        }
        break;
1098
1099
1100
1101
    case W_PowerUS:
        // schedule timer event when the clock is enabled
        // TODO: check whether this resets USCOUNT (and also which other events can reset it)
        if ((IOPORT(W_PowerUS) & 0x0001) && !(val & 0x0001))
StapleButter's avatar
StapleButter committed
1102
        {
1103
            NDS::ScheduleEvent(NDS::Event_Wifi, true, 33, USTimer, 0);
StapleButter's avatar
StapleButter committed
1104
1105
1106
1107
1108
1109
            if (!MPInited)
            {
                Platform::MP_Init();
                MPInited = true;
            }
        }
1110
        else if (!(IOPORT(W_PowerUS) & 0x0001) && (val & 0x0001))
StapleButter's avatar
StapleButter committed
1111
        {
1112
            NDS::CancelEvent(NDS::Event_Wifi);
StapleButter's avatar
StapleButter committed
1113
        }
1114
1115
1116
1117
        break;

    case W_USCountCnt: val &= 0x0001; break;
    case W_USCompareCnt:
1118
        if (val & 0x0002) SetIRQ14(2);
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
        val &= 0x0001;
        break;

    case W_USCount0: USCounter = (USCounter & 0xFFFFFFFFFFFF0000) | (u64)val; return;
    case W_USCount1: USCounter = (USCounter & 0xFFFFFFFF0000FFFF) | ((u64)val << 16); return;
    case W_USCount2: USCounter = (USCounter & 0xFFFF0000FFFFFFFF) | ((u64)val << 32); return;
    case W_USCount3: USCounter = (USCounter & 0x0000FFFFFFFFFFFF) | ((u64)val << 48); return;

    case W_USCompare0:
        USCompare = (USCompare & 0xFFFFFFFFFFFF0000) | (u64)(val & 0xFC00);
1129
        if (val & 0x0001) BlockBeaconIRQ14 = true;
1130
1131
1132
1133
        return;
    case W_USCompare1: USCompare = (USCompare & 0xFFFFFFFF0000FFFF) | ((u64)val << 16); return;
    case W_USCompare2: USCompare = (USCompare & 0xFFFF0000FFFFFFFF) | ((u64)val << 32); return;
    case W_USCompare3: USCompare = (USCompare & 0x0000FFFFFFFFFFFF) | ((u64)val << 48); return;
StapleButter's avatar
StapleButter committed
1134

StapleButter's avatar
StapleButter committed
1135
1136
    case W_CmdCount: CmdCounter = val * 10; return;

StapleButter's avatar
StapleButter committed
1137
1138
1139
1140
1141
1142
1143
1144
    case W_BBCnt:
        IOPORT(W_BBCnt) = val;
        if ((IOPORT(W_BBCnt) & 0xF000) == 0x5000)
        {
            u32 regid = IOPORT(W_BBCnt) & 0xFF;
            if (!BBRegsRO[regid])
                BBRegs[regid] = IOPORT(W_BBWrite) & 0xFF;
        }
StapleButter's avatar
StapleButter committed
1145
        return;
StapleButter's avatar
StapleButter committed
1146

StapleButter's avatar
StapleButter committed
1147
1148
    case W_RFData2:
        IOPORT(W_RFData2) = val;
StapleButter's avatar
StapleButter committed
1149
1150
1151
        if (RFVersion == 3) RFTransfer_Type3();
        else                RFTransfer_Type2();
        return;
StapleButter's avatar
StapleButter committed
1152
1153
1154
    case W_RFCnt:
        val &= 0x413F;
        break;
1155

1156

StapleButter's avatar
StapleButter committed
1157
1158
1159
1160
    case W_RXCnt:
        if (val & 0x0001)
        {
            IOPORT(W_RXBufWriteCursor) = IOPORT(W_RXBufWriteAddr);
1161
            printf("wifi: force WRCSR to %04X\n", IOPORT(W_RXBufWriteCursor));
StapleButter's avatar
StapleButter committed
1162
1163
1164
        }
        if (val & 0x0080)
        {
StapleButter's avatar
StapleButter committed
1165
            printf("wifi: latching shit\n");
StapleButter's avatar
StapleButter committed
1166
1167
1168
1169
1170
1171
1172
            IOPORT(W_TXSlotReply2) = IOPORT(W_TXSlotReply1);
            IOPORT(W_TXSlotReply1) = 0;
        }
        val &= 0xFF0E;
        if (val & 0x7FFF) printf("wifi: unknown RXCNT bits set %04X\n", val);
        break;

1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
    case W_RXBufDataRead:
        printf("wifi: writing to RXBUF_DATA_READ. wat\n");
        if (IOPORT(W_RXBufCount) > 0)
        {
            IOPORT(W_RXBufCount)--;
            if (IOPORT(W_RXBufCount) == 0)
                SetIRQ(9);
        }
        return;

    case W_RXBufReadAddr:
    case W_RXBufGapAddr:
        val &= 0x1FFE;
        break;
    case W_RXBufGapSize:
    case W_RXBufCount:
StapleButter's avatar
StapleButter committed
1189
1190
    case W_RXBufWriteAddr:
    case W_RXBufReadCursor:
1191
1192
1193
1194
        val &= 0x0FFF;
        break;


StapleButter's avatar
StapleButter committed
1195
1196
1197
1198
1199
1200
1201
1202
    case W_TXReqReset:
        IOPORT(W_TXReqRead) &= ~val;
        return;
    case W_TXReqSet:
        IOPORT(W_TXReqRead) |= val;
        FireTX();
        return;

1203
1204
1205
1206
1207
1208
1209
1210
    case W_TXSlotReset:
        if (val & 0x0001) IOPORT(W_TXSlotLoc1) &= 0x7FFF;
        if (val & 0x0002) IOPORT(W_TXSlotCmd) &= 0x7FFF;
        if (val & 0x0004) IOPORT(W_TXSlotLoc2) &= 0x7FFF;
        if (val & 0x0008) IOPORT(W_TXSlotLoc3) &= 0x7FFF;
        // checkme: any bits affecting the beacon slot?
        if (val & 0x0040) IOPORT(W_TXSlotReply2) &= 0x7FFF;
        if (val & 0x0080) IOPORT(W_TXSlotReply1) &= 0x7FFF;
StapleButter's avatar
StapleButter committed
1211
        if ((val & 0xFF30) && (val != 0xFFFF)) printf("unusual TXSLOTRESET %04X\n", val);
1212
1213
        val = 0; // checkme (write-only port)
        break;
StapleButter's avatar
StapleButter committed
1214

1215
1216
    case W_TXBufDataWrite:
        {
1217
            u32 wraddr = IOPORT(W_TXBufWriteAddr);
1218
1219
1220
            *(u16*)&RAM[wraddr] = val;

            wraddr += 2;
1221
1222
1223
1224
1225
            if (wraddr == IOPORT(W_TXBufGapAddr))
                wraddr += (IOPORT(W_TXBufGapSize) << 1);

            //if (IOPORT(0x000) == 0xC340)
            //    IOPORT(W_TXBufGapSize) = 0;
1226
1227

            IOPORT(W_TXBufWriteAddr) = wraddr & 0x1FFE;
1228
1229
1230
1231
1232
1233
1234

            if (IOPORT(W_TXBufCount) > 0)
            {
                IOPORT(W_TXBufCount)--;
                if (IOPORT(W_TXBufCount) == 0)
                    SetIRQ(8);
            }
1235
1236
1237
        }
        return;

1238
1239
1240
1241
1242
1243
1244
    case W_TXBufWriteAddr:
    case W_TXBufGapAddr:
        val &= 0x1FFE;
        break;
    case W_TXBufGapSize:
    case W_TXBufCount:
        val &= 0x0FFF;
1245
1246
        break;

1247
1248
1249
    case W_TXSlotLoc1:
    case W_TXSlotLoc2:
    case W_TXSlotLoc3:
StapleButter's avatar
StapleButter committed
1250
    case W_TXSlotCmd:
1251
1252
1253
1254
1255
1256
        // checkme: is it possible to cancel a queued transfer that hasn't started yet
        // by clearing bit15 here?
        IOPORT(addr&0xFFF) = val;
        FireTX();
        return;

StapleButter's avatar
StapleButter committed
1257
    case 0x094:
1258
1259
        printf("wifi: trying to send packet. %08X=%04X. TXREQ=%04X. delay=%08X\n",
               addr, val, IOPORT(W_TXReqRead), (u32)(USCounter-mpreplywindow));
StapleButter's avatar
StapleButter committed
1260
1261
        break;

1262
1263
1264
1265
1266
    case 0x228:
    case 0x244:
        printf("wifi: write port%03X %04X\n", addr, val);
        break;

1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
    // read-only ports
    case 0x000:
    case 0x044:
    case 0x054:
    case 0x0B0:
    case 0x0B6:
    case 0x0B8:
    case 0x15C:
    case 0x15E:
    case 0x180:
    case 0x19C:
    case 0x1A8:
    case 0x1AC:
    case 0x1C4:
    case 0x210:
    case 0x214:
    case 0x268:
        return;
StapleButter's avatar
StapleButter committed
1285
1286
    }

1287
1288
    //printf("WIFI: write %08X %04X\n", addr, val);
    IOPORT(addr&0xFFF) = val;
StapleButter's avatar
StapleButter committed
1289
1290