1 module gbaid.gba.gpio; 2 3 import std.conv : to; 4 import std.meta : AliasSeq; 5 6 import gbaid.util; 7 8 public enum uint GPIO_ROM_START_ADDRESS = 0xC4; 9 public enum uint GPIO_ROM_END_ADDRESS = 0xCA; 10 11 public alias IoPinOut = bool delegate(); 12 public alias IoPinIn = void delegate(bool pin); 13 14 public struct GpioChip { 15 mixin declareFields!(IoPinOut, false, "readPin", null, 4); 16 mixin declareFields!(IoPinIn, false, "writePin", null, 4); 17 } 18 19 public struct GpioPort { 20 public bool enabled = false; 21 private bool readable = false; 22 private ubyte directionFlags = 0b0000; 23 private short _valueAtCa = 0; 24 private GpioChip _chip; 25 26 @property public void valueAtCa(short value) { 27 _valueAtCa = value; 28 } 29 30 @property public void chip(GpioChip chip) { 31 _chip = chip; 32 } 33 34 public T get(T)(uint address) { 35 // 32 bit reads must be aligned at 4 instead of 2 36 static if (is(T == int) || is(T == uint)) { 37 uint alignedAddress = address & ~0b11; 38 } else { 39 uint alignedAddress = address & ~0b1; 40 } 41 // Read the value from the register 42 short shortValue = void; 43 switch (alignedAddress) { 44 case 0xC4: 45 shortValue = data; 46 break; 47 case 0xC6: 48 shortValue = direction; 49 break; 50 case 0xC8: 51 shortValue = control; 52 break; 53 case 0xCA: 54 shortValue = _valueAtCa; 55 break; 56 default: 57 throw new Exception("Invalid GPIO address: " ~ address.to!string); 58 } 59 // Convert the register value to the correct format 60 static if (is(T == byte) || is(T == ubyte)) { 61 return cast(byte) (shortValue >>> (address & 0b1) * 8); 62 } else static if (is(T == short) || is(T == ushort)) { 63 return shortValue; 64 } else static if (is(T == int) || is(T == uint)) { 65 // For ints, we must do a second read for the upper bits 66 return get!short(alignedAddress + 2) << 16 | shortValue & 0xFFFF; 67 } else { 68 static assert (0); 69 } 70 } 71 72 public void set(T)(uint address, T value) { 73 // Convert the value to a short based on the address, and align the address to the correct register 74 static if (is(T == byte) || is(T == ubyte)) { 75 short shortValue = cast(short) ((value & 0xFF) << (address & 0b1) * 8); 76 address &= ~0b1; 77 } else static if (is(T == short) || is(T == ushort)) { 78 short shortValue = value; 79 address &= ~0b1; 80 } else static if (is(T == int) || is(T == uint)) { 81 short shortValue = cast(short) value; 82 address &= ~0b11; 83 // For ints, we must do a second write for the upper bits 84 set!short(address + 2, cast(short) (value >>> 16)); 85 } else { 86 static assert (0); 87 } 88 // Write the value 89 switch (address) { 90 case 0xC4: 91 data = shortValue; 92 break; 93 case 0xC6: 94 direction = shortValue; 95 break; 96 case 0xC8: 97 control = shortValue; 98 break; 99 case 0xCA: 100 break; 101 default: 102 throw new Exception("Invalid GPIO address: " ~ address.to!string); 103 } 104 } 105 106 @property private short control() { 107 if (!readable) { 108 return 0; 109 } 110 return cast(short) readable.checkBit(0); 111 } 112 113 @property private void control(short value) { 114 readable = value.checkBit(0); 115 } 116 117 @property private short direction() { 118 if (!readable) { 119 return 0; 120 } 121 return cast(short) directionFlags.getBits(0, 3); 122 } 123 124 @property private void direction(short value) { 125 directionFlags = cast(ubyte) value.getBits(0, 3); 126 } 127 128 @property private short data() { 129 if (!readable) { 130 return 0; 131 } 132 // Read from the input pins 133 int data = 0; 134 foreach (pin; AliasSeq!(0, 1, 2, 3)) { 135 if (!directionFlags.checkBit(pin)) { 136 data.setBit(pin, _chip.readPin!pin()); 137 } 138 } 139 return cast(short) data; 140 } 141 142 @property private void data(short value) { 143 // Write to the output pins 144 foreach (pin; AliasSeq!(0, 1, 2, 3)) { 145 if (directionFlags.checkBit(pin)) { 146 _chip.writePin!pin(value.checkBit(pin)); 147 } 148 } 149 } 150 }