1 module dcpu16.emulator.devices.keyboard; 2 3 import dcpu16.emulator.idevice; 4 import dcpu16.emulator; 5 6 class Keyboard : IDevice 7 { 8 override uint id() const pure { return 0x30cf7406; }; 9 override uint manufacturer() const pure { return 0; }; 10 override ushort ver() const pure { return 1; }; 11 12 private Computer comp; 13 private ubyte[] buf; 14 private size_t maxBufLength; 15 private CheckKeyIsPressed checkKeyPressed; 16 private ushort interruptsMsg; 17 private bool enableMemMapping; 18 19 alias CheckKeyIsPressed = bool delegate(ubyte ascii_or_enum_Key); 20 21 this(Computer c, CheckKeyIsPressed dg, bool enable0x9000Mapping = true, size_t keyBufferLength = 8) 22 { 23 comp = c; 24 checkKeyPressed = dg; 25 enableMemMapping = enable0x9000Mapping; 26 maxBufLength = keyBufferLength; 27 } 28 29 override void reset() 30 { 31 buf = null; 32 } 33 34 override void handleHardwareInterrupt(in Computer _unused) 35 { 36 assert(comp == _unused); 37 38 with(InterruptActions) 39 with(comp) 40 with(cpu.regs) 41 switch(A) 42 { 43 case CLEAR_BUFFER: 44 buf = null; 45 return; 46 47 case GET_NEXT: 48 if(buf.length == 0) 49 { 50 comp.cpu.regs.c = 0; 51 } 52 else 53 { 54 comp.cpu.regs.c = buf[0]; 55 buf = buf[1 .. $]; 56 } 57 return; 58 59 case CHECK_KEY: 60 ubyte b = comp.cpu.regs.B & ubyte.max; 61 if(b != comp.cpu.regs.B) return; 62 comp.cpu.regs.c = checkKeyPressed(b) ? 1 : 0; 63 return; 64 65 case SET_INT: 66 interruptsMsg = comp.cpu.regs.B; 67 return; 68 69 default: 70 break; 71 } 72 } 73 74 void keyPressed(ubyte ascii_or_enum_Key) 75 { 76 assert(ascii_or_enum_Key != 0); 77 // TODO: add more checks 78 79 // A 16-word buffer [0x9000 to 0x900e] holds the most recently input characters in a ring buffer, one word per character. 80 if(enableMemMapping) 81 { 82 static ushort mapIdx = 0x9000; 83 84 comp.mem[mapIdx] = ascii_or_enum_Key; 85 mapIdx++; 86 87 if(mapIdx > 0x900e) 88 mapIdx = 0x9000; 89 } 90 91 if(buf.length <= maxBufLength) 92 buf ~= ascii_or_enum_Key; 93 94 if(interruptsMsg) 95 comp.cpu.addInterruptOrBurnOut(interruptsMsg); 96 } 97 } 98 99 unittest 100 { 101 // 0x9010 holds end of buffer 102 ushort[] createRingBuff= [ 103 0x7f01, 0x0000, 0x7f81, 0x0004, 0x7821, 0x9010, 0x4401, 0x9000, 104 0x8621, 0x9000, 0x8822, 0xc428, 0x07c1, 0x9010, 0x6381 105 ]; 106 107 auto comp = new Computer(); 108 comp.load(createRingBuff); 109 110 foreach(_; 0 .. 4000) 111 { 112 //~ import std.stdio; 113 //~ comp.machineState.writeln; 114 //~ comp.memDump(0x9000).writeln; 115 //~ comp.memDump(0x9010).writeln; 116 comp.cpu.step; 117 } 118 } 119 120 enum InterruptActions : ushort 121 { 122 CLEAR_BUFFER, 123 GET_NEXT, 124 CHECK_KEY, 125 SET_INT, /// If register B is non-zero, turn on interrupts with message B. If B is zero, disable interrupts. 126 } 127 128 enum Key : ubyte 129 { 130 none, 131 Backspace = 0x10, 132 Return, 133 Insert, 134 Delete, 135 ArrowUp = 0x80, 136 ArrowDown, 137 ArrowLeft, 138 ArrowRight, 139 Shift = 0x90, 140 Control, 141 Alt, /// Unofficial 142 }