├── .gitignore ├── LICENSE.txt ├── arduino └── arduino.ino ├── gb ├── __init__.py ├── conn.py └── mbc.py └── parallel ├── CHANGES.rst ├── LICENSE.txt ├── README.rst ├── __init__.py ├── inpout32.dll ├── inpoutx64.dll ├── parallelppdev.py ├── parallelppi.py └── parallelwin32.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | build/ 4 | dist/ 5 | *.egg* 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Vicki Pfau 2 | All Rights Reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /arduino/arduino.ino: -------------------------------------------------------------------------------- 1 | void setup() { 2 | Serial.begin(9600); 3 | pinMode(PIN_SPI_SCK, OUTPUT); 4 | pinMode(PIN_SPI_MOSI, OUTPUT); 5 | pinMode(PIN_SPI_MISO, INPUT); 6 | digitalWrite(PIN_SPI_SCK, HIGH); 7 | SPCR = (1<> i) & 1 14 | self.p.set_bit(self.SO, bit) 15 | self.p.set_bit(self.SC, 0) 16 | rx |= self.p.get_bit(self.SI) << i 17 | self.p.set_bit(self.SC, 1) 18 | return rx 19 | 20 | def rx(self): 21 | return self.tx(0) 22 | 23 | def rxb(self, n=1): 24 | bstring = b'' 25 | for i in range(size): 26 | bstring += struct.pack("B", self.rx()) 27 | return bstring 28 | 29 | class LinkSerial: 30 | SPAN = 0x400 31 | DEBUG = False 32 | 33 | def __init__(self, p): 34 | self.p = p 35 | 36 | def tx(self, byte): 37 | self.p.write(bytes([byte])) 38 | b = self.p.read(1)[0] 39 | if self.DEBUG: 40 | print("%02X-%02X" % (byte, b)) 41 | return b 42 | 43 | def rx(self): 44 | return self.tx(0) 45 | 46 | def txb(self, block): 47 | self.p.write(bytes(block)) 48 | return self.p.read(len(block)) 49 | 50 | def rxb(self, n=1): 51 | bstring = b'' 52 | while n > 0: 53 | self.p.write(bytes([0] * min(self.SPAN, n))) 54 | bstring += self.p.read(min(self.SPAN, n)) 55 | n -= self.SPAN 56 | return bstring 57 | -------------------------------------------------------------------------------- /gb/conn.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import struct 3 | import time 4 | 5 | class LinkDL: 6 | DELAY = 0.0004 7 | 8 | def __init__(self, link): 9 | self.link = link 10 | self.connected = False 11 | self.carttype = None 12 | self.romsize = None 13 | self.ramsize = None 14 | self.checksum = None 15 | self.gamename = None 16 | self.rom = None 17 | 18 | def _connect(self): 19 | connected = False 20 | for i in range(0x800): 21 | if self._write8(0x9A) == 0xB4: 22 | connected = True 23 | break 24 | 25 | if not connected: 26 | return False 27 | connected = False 28 | time.sleep(0.001) 29 | 30 | for i in range(100): 31 | if self._write8(0x9A) == 0x1D: 32 | connected = True 33 | break 34 | 35 | return connected 36 | 37 | def _read8(self): 38 | time.sleep(self.DELAY) 39 | return self.link.rx() 40 | 41 | def _write8(self, value): 42 | time.sleep(self.DELAY) 43 | return self.link.tx(value) 44 | 45 | def _read16(self): 46 | return (self._read8() << 8) | self._read8() 47 | 48 | def _read_string(self, size): 49 | bstring = [] 50 | for i in range(size): 51 | bstring.append(self._read8()) 52 | return ''.join([chr(c) for c in bstring]) 53 | 54 | def _read_header(self): 55 | self.carttype = self._read8() 56 | self.romsize = self._read8() 57 | self.ramsize = self._read8() 58 | self.checksum = self._read16() 59 | self.gamename = self._read_string(16) 60 | 61 | def _read_bytestring(self, size): 62 | time.sleep(self.DELAY) 63 | return self.link.rxb(size) 64 | 65 | def connect(self): 66 | if not self._connect(): 67 | return False 68 | self._read_header() 69 | if self._read8() != 0: 70 | return False 71 | if self._read8() != 0xFF: 72 | return False 73 | self.connected = True 74 | self.rom = self._read_bytestring(0x4000) 75 | return True 76 | 77 | def read(self, address, length=1): 78 | self._write8(0x59) 79 | self._write8(address >> 8) 80 | self._write8(address & 0xFF) 81 | self._write8(length >> 8) 82 | self._write8(length & 0xFF) 83 | return self._read_bytestring(length) 84 | 85 | def read_ec(self, address, size=1, limit=None): 86 | bstrings = [] 87 | for x in range(address, address + size, 0x800): 88 | hashes = set() 89 | retries = 0 90 | while True: 91 | data = self.read(x, 0x800 if size >= 0x800 else size) 92 | h = hashlib.sha1(data).digest() 93 | if h in hashes: 94 | bstrings.append(data) 95 | break 96 | hashes.add(h) 97 | retries += 1 98 | if limit is not None and limit > 0 and retries > limit: 99 | return None 100 | size -= 0x800 101 | return b''.join(bstrings) 102 | 103 | def write(self, address, value): 104 | self._write8(0x49) 105 | self._write8(address >> 8) 106 | self._write8(address & 0xFF) 107 | self._write8(value) 108 | 109 | def write_ec(self, address, value): 110 | while True: 111 | self.write(address, value) 112 | if ord(self.read(address)) == value: 113 | break 114 | 115 | def mark_busy(self, busy=True): 116 | pass 117 | 118 | def mark_idle(self): 119 | self.mark_busy(False) 120 | 121 | 122 | class Link2(LinkDL): 123 | def _connect(self): 124 | connected = False 125 | for i in range(0x400): 126 | if self._write8(0x99) == 0xB4: 127 | connected = True 128 | break 129 | 130 | if not connected: 131 | return False 132 | connected = False 133 | time.sleep(0.001) 134 | 135 | for i in range(100): 136 | if self._write8(0x99) == 0x1D: 137 | connected = True 138 | break 139 | 140 | return connected 141 | 142 | def connect(self): 143 | if not self._connect(): 144 | return False 145 | self._read_header() 146 | if self._read8() != 0: 147 | return False 148 | if self._read8() != 0xFF: 149 | return False 150 | self.connected = True 151 | return True 152 | 153 | def mark_busy(self, busy=True): 154 | if busy: 155 | self._write8(0x89) 156 | else: 157 | self._write8(0x8A) 158 | time.sleep(0.02) 159 | 160 | 161 | def detect_link(link): 162 | dl = Link2(link) 163 | if dl.connect(): 164 | return dl 165 | if dl._write8(0) == 0xB4: 166 | if dl.connect(): 167 | return dl 168 | dl = LinkDL(link) 169 | if dl.connect(): 170 | return dl 171 | return None -------------------------------------------------------------------------------- /gb/mbc.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import time 3 | 4 | class MBC(object): 5 | ROM_BANK_SIZE = 0x4000 6 | RAM_BANK_SIZE = 0x2000 7 | def __init__(self, conn): 8 | self.conn = conn 9 | if conn.romsize < 8: 10 | self.nbanks = (0x8000 // self.ROM_BANK_SIZE) << conn.romsize 11 | elif conn.romsize == 0x52: 12 | self.nbanks = 72 13 | elif conn.romsize == 0x53: 14 | self.nbanks = 80 15 | elif conn.romsize == 0x54: 16 | self.nbanks = 96 17 | 18 | if conn.ramsize == 1: 19 | self.ramsize = 0x800 20 | elif conn.ramsize == 2: 21 | self.ramsize = 0x2000 22 | elif conn.ramsize == 3: 23 | self.ramsize = 0x8000 24 | elif conn.ramsize == 4: 25 | self.ramsize = 0x20000 26 | elif conn.ramsize == 5: 27 | self.ramsize = 0x10000 28 | else: 29 | self.ramsize = 0 30 | 31 | def unlock_ram(self, unlock=True): 32 | self.conn.write(0x0000, 0xA if unlock else 0) 33 | 34 | def select_rom_bank(self, bank): 35 | self.conn.write(0x2100, bank) 36 | 37 | def select_ram_bank(self, bank): 38 | self.conn.write(0x4000, bank) 39 | 40 | def dump_rom(self, cb=None, retries=None): 41 | self.conn.mark_busy() 42 | if retries != 0: 43 | rom = [self.conn.read_ec(0x0, self.ROM_BANK_SIZE, retries)] 44 | else: 45 | rom = [self.conn.read(0x0, self.ROM_BANK_SIZE)] 46 | if cb: 47 | cb(0, rom[0]) 48 | for i in range(1, self.nbanks): 49 | self.select_rom_bank(i) 50 | if retries != 0: 51 | rom.append(self.conn.read_ec(0x4000, self.ROM_BANK_SIZE, retries)) 52 | else: 53 | rom.append(self.conn.read(0x4000, self.ROM_BANK_SIZE)) 54 | if cb: 55 | cb(i, rom[-1]) 56 | self.conn.mark_idle() 57 | return b''.join(rom) 58 | 59 | def dump_ram(self): 60 | self.conn.mark_busy() 61 | ram = [] 62 | for i in range(self.ramsize // 0x800): 63 | if not i & ((self.RAM_BANK_SIZE // 0x800) - 1): 64 | self.select_ram_bank(i // (self.RAM_BANK_SIZE // 0x800)) 65 | ram.append(self.conn.read_ec(0xA000 + (i & ((self.RAM_BANK_SIZE // 0x800) - 1)) * 0x800, 0x800)) 66 | self.conn.mark_idle() 67 | return b''.join(ram) 68 | 69 | def restore_ram(self, ram): 70 | self.conn.mark_busy() 71 | for i in range(self.ramsize): 72 | if not i & (self.RAM_BANK_SIZE - 1): 73 | self.select_ram_bank(i // self.RAM_BANK_SIZE) 74 | self.conn.write_ec(0xA000 + (i & (self.RAM_BANK_SIZE - 1)), ram[i]) 75 | self.conn.mark_idle() 76 | 77 | class MBC1(MBC): 78 | BANK_MODE_ROM = 0 79 | BANK_MODE_RAM = 1 80 | def set_bank_mode(self, mode): 81 | self.conn.write(0x6000, mode) 82 | 83 | def select_rom_bank(self, bank): 84 | self.set_bank_mode(self.BANK_MODE_RAM) 85 | super(MBC1, self).select_rom_bank(bank & 0x1F) 86 | self.conn.write(0x4000, bank >> 5) 87 | 88 | def select_ram_bank(self, bank): 89 | self.set_bank_mode(self.BANK_MODE_RAM) 90 | super(MBC1, self).select_ram_bank(bank) 91 | 92 | def dump_rom(self, cb=None, retries=None): 93 | self.conn.mark_busy() 94 | if retries != 0: 95 | rom = [self.conn.read_ec(0x0, 0x4000, retries)] 96 | else: 97 | rom = [self.conn.read(0x0, 0x4000)] 98 | if cb: 99 | cb(0, rom[0]) 100 | for i in range(1, self.nbanks): 101 | self.select_rom_bank(i) 102 | if retries != 0: 103 | rom.append(self.conn.read_ec(0x4000 if i & 0x1F else 0x0000, 0x4000, retries)) 104 | else: 105 | rom.append(self.conn.read(0x4000 if i & 0x1F else 0x0000, 0x4000)) 106 | if cb: 107 | cb(i, rom[-1]) 108 | self.conn.mark_idle() 109 | return b''.join(rom) 110 | 111 | class MBC2(MBC): 112 | def __init__(self, conn): 113 | super(MBC2, self).__init__(conn) 114 | self.ramsize = 0x100 115 | 116 | def dump_ram(self): 117 | self.conn.mark_busy() 118 | bs = self.conn.read_ec(0xA000, 0x200) 119 | ram = [] 120 | for i in range(self.ramsize): 121 | ram.append(((bs[i * 2 + 1] & 0xF) << 4) | (bs[i * 2] & 0xF)) 122 | self.conn.mark_idle() 123 | return bytes(ram) 124 | 125 | def restore_ram(self, ram): 126 | self.conn.mark_busy() 127 | for i in range(len(ram)): 128 | b = ram[i] 129 | self.conn.write_ec(0xA000 + i * 2, 0xF0 | (b & 0xF)) 130 | self.conn.write_ec(0xA001 + i * 2, 0xF0 | (b >> 4)) 131 | self.conn.mark_idle() 132 | 133 | class MBC3(MBC): 134 | def latch_rtc(self): 135 | self.conn.write(0x6000, 0) 136 | self.conn.write(0x6000, 1) 137 | 138 | def read_rtc(self, reg): 139 | self.select_ram_bank(8 + reg) 140 | return self.conn.read(0xA000) 141 | 142 | def get_time(self, latch=True): 143 | if latch: 144 | self.latch_rtc() 145 | hour = ord(self.read_rtc(2)) 146 | minute = ord(self.read_rtc(1)) 147 | second = ord(self.read_rtc(0)) 148 | return datetime.time(hour, minute, second) 149 | 150 | class MBC5(MBC): 151 | def select_rom_bank(self, bank): 152 | super(MBC5, self).select_rom_bank(bank & 0xFF) 153 | self.conn.write(0x3000, bank >> 8) 154 | 155 | def set_rumble(self, on=True): 156 | self.select_ram_bank(8 * on) 157 | 158 | class MBC6(MBC): 159 | ROM_BANK_SIZE = 0x2000 160 | RAM_BANK_SIZE = 0x1000 161 | def select_rom_bank(self, bank, block=0): 162 | self.conn.write(0x27FF + block * 0x1000, bank) 163 | self.conn.write(0x2800 + block * 0x1000, 0) 164 | 165 | def select_ram_bank(self, bank, block=0): 166 | self.conn.write(0x400 + block * 0x400, bank) 167 | 168 | def select_flash_bank(self, bank, block=0): 169 | self.conn.write(0x27FF + block * 0x1000, bank) 170 | self.conn.write(0x2800 + block * 0x1000, 8) 171 | 172 | def unlock_flash(self, unlock=True): 173 | self.conn.write(0x1000, 1) 174 | self.conn.write(0x0C00, int(unlock)) 175 | self.conn.write(0x1000, 0) 176 | 177 | def send_flash_command(self, cmd, bank=2, address=0x7555): 178 | self.select_flash_bank(2, 1) 179 | self.conn.write(0x7555, 0xAA) 180 | self.select_flash_bank(1, 1) 181 | self.conn.write(0x6AAA, 0x55) 182 | self.select_flash_bank(bank, 1) 183 | print(hex(bank), hex(address), hex(cmd)) 184 | self.conn.write(address, cmd) 185 | 186 | def flash_jedec_id(self): 187 | self.unlock_flash(True) 188 | self.send_flash_command(0x90) 189 | self.select_flash_bank(0, 1) 190 | mfg, dev = self.conn.read_ec(0x6000, 2) 191 | self.send_flash_command(0xF0) 192 | return mfg, dev 193 | 194 | def flash_erase_block(self, bank, address): 195 | self.conn.mark_busy() 196 | self.unlock_flash(True) 197 | self.conn.write(0x1000, 1) 198 | self.send_flash_command(0x80) 199 | self.send_flash_command(0x30, bank, 0x6000 + (address & ~0x7F)) 200 | while not self.conn.read(0x6000)[0] & 0x80: 201 | pass 202 | self.send_flash_command(0xF0, bank, 0x6000 + (address & ~0x7F)) 203 | self.send_flash_command(0xF0, bank, 0x6000 + (address & ~0x7F)) 204 | self.unlock_flash(False) 205 | self.conn.mark_idle() 206 | 207 | def flash_write_block(self, bank, address, block): 208 | self.conn.mark_busy() 209 | self.unlock_flash(True) 210 | self.send_flash_command(0xA0) 211 | self.select_flash_bank(bank, 1) 212 | base = 0x6000 + (address & ~0x7F) 213 | self.conn.write(0x1000, 1) 214 | for i, b in enumerate(block): 215 | self.conn.write(base + i, b) 216 | self.conn.write(base + len(block) - 1, 0x00) 217 | while not self.conn.read(base + len(block) - 1)[0] & 0x80: 218 | pass 219 | self.conn.write(base + len(block) - 1, 0xF0) 220 | self.conn.write(base + len(block) - 1, 0xF0) 221 | self.conn.read(base, len(block)) 222 | self.conn.write(0x1000, 0) 223 | self.conn.mark_idle() 224 | 225 | class MBC7(MBC): 226 | ACCEL_OFFSET = 0x81D0 227 | 228 | EWDS = (0, 0, 0, 0) 229 | WRAL = (0, 0, 0, 1) 230 | ERAL = (0, 0, 1, 0) 231 | EWEN = (0, 0, 1, 1) 232 | WRITE = (0, 1) 233 | READ = (1, 0) 234 | ERASE = (1, 1) 235 | 236 | def unlock_ram(self): 237 | super(MBC7, self).unlock_ram() 238 | self.select_ram_bank(0x40) 239 | 240 | def sample_accel(self): 241 | self.conn.write(0xA000, 0x55) 242 | self.conn.write(0xA010, 0xAA) 243 | self.accel_x_raw = ord(self.conn.read(0xA020)) | (ord(self.conn.read(0xA030)) << 8) 244 | self.accel_y_raw = ord(self.conn.read(0xA040)) | (ord(self.conn.read(0xA050)) << 8) 245 | self.accel_x = self.accel_x_raw - self.ACCEL_OFFSET 246 | self.accel_y = self.accel_y_raw - self.ACCEL_OFFSET 247 | 248 | def eeprom_init(self): 249 | self.conn.write(0xA080, 0) 250 | self.conn.write(0xA080, 0x80) 251 | 252 | def eeprom_send_command(self, header, address=0): 253 | self.eeprom_init() 254 | self.eeprom_shift_ins((0, 1)) 255 | if len(header) == 2: 256 | self.eeprom_shift_ins(header) 257 | self.eeprom_shift_address(address) 258 | else: 259 | self.eeprom_shift_ins(header) 260 | self.eeprom_shift_ins([0] * (10 - len(header))) 261 | 262 | def eeprom_shift_in(self, b): 263 | self.conn.write(0xA080, 0x80 | ((b & 1) << 1)) 264 | self.conn.write(0xA080, 0xC0 | ((b & 1) << 1)) 265 | 266 | def eeprom_shift_inout(self, b): 267 | self.eeprom_shift_in(b) 268 | return ord(self.conn.read(0xA080)) & 1 269 | 270 | def eeprom_shift_out(self): 271 | self.eeprom_shift_inout(0) 272 | 273 | def eeprom_shift_ins(self, bs): 274 | for b in bs: 275 | self.eeprom_shift_in(b) 276 | 277 | def eeprom_shift_inouts(self, bs): 278 | o = [] 279 | for b in bs: 280 | o.append(self.eeprom_shift_inout(b)) 281 | return tuple(o) 282 | 283 | def eeprom_shift_outs(self, n): 284 | return self.eeprom_shift_inouts([0] * n) 285 | 286 | def eeprom_shift_address(self, addr): 287 | for i in range(7, -1, -1): 288 | self.eeprom_shift_in(addr >> i) 289 | 290 | def eeprom_wait(self): 291 | # Clock the EEPROM while it's still doing stuff 292 | while self.eeprom_shift_inout(0) == (0,): 293 | pass 294 | 295 | def ram_read(self, address): 296 | self.eeprom_send_command(self.READ, address) 297 | bs = self.eeprom_shift_outs(16) 298 | b = 0 299 | for i in range(len(bs)): 300 | b |= bs[i] << (15 - i) 301 | return b 302 | 303 | def enable_write(self): 304 | self.eeprom_send_command(self.EWEN) 305 | 306 | def disable_write(self): 307 | self.eeprom_send_command(self.EWDS) 308 | 309 | def ram_write(self, address, word): 310 | self.eeprom_send_command(self.WRITE, address) 311 | # FIXME: Is there a prettier way to do this? 312 | bs = [ord(x) - ord('0') for x in '{:016b}'.format(word)] 313 | self.eeprom_shift_ins(bs) 314 | self.eeprom_wait() 315 | 316 | def dump_ram(self): 317 | self.conn.mark_busy() 318 | bstring = b'' 319 | for i in range(0x80): 320 | word = self.ram_read(i) 321 | bstring += chr(word >> 8) 322 | bstring += chr(word & 0xFF) 323 | self.conn.mark_idle() 324 | return bstring 325 | 326 | def restore_ram(self, ram): 327 | self.conn.mark_busy() 328 | self.enable_write() 329 | for i in range(0x80): 330 | word = ram[i * 2] << 8 331 | word += ram[i * 2 + 1] 332 | self.ram_write(i, word) 333 | self.disable_write() 334 | self.conn.mark_idle() 335 | 336 | class GBCamera(MBC): 337 | def select_camera(self): 338 | self.select_ram_bank(0x10) 339 | 340 | def set_camera_defaults(self): 341 | self.set_exposure(0x1000) 342 | 343 | def set_dither_matrix(self, matrix): 344 | self.select_camera() 345 | for x in range(4): 346 | for y in range(4): 347 | for l in range(3): 348 | self.conn.write(0xA006 + y * 3 + x * 12 + l, matrix[y][x][l]) 349 | 350 | def set_exposure(self, value): 351 | self.select_camera() 352 | self.conn.write(0xA002, value >> 8) 353 | self.conn.write(0xA003, value & 0xFF) 354 | 355 | def take_photo(self): 356 | self.conn.mark_busy() 357 | self.select_camera() 358 | self.conn.write(0xA000, 1) 359 | while ord(self.conn.read_ec(0xA000)) & 1: 360 | time.sleep(0.05) 361 | self.conn.mark_idle() 362 | 363 | class TAMA5(MBC): 364 | def __init__(self, conn): 365 | super(TAMA5, self).__init__(conn) 366 | self.ramsize = 0x20 367 | 368 | def unlock_ram(self): 369 | self.conn.write(0xA001, 0xA) 370 | for _ in range(200): 371 | if ord(self.conn.read_ec(0xA000)) & 3 == 1: 372 | break 373 | 374 | def select_rom_bank(self, bank): 375 | self.unlock_ram() 376 | self.conn.write(0xA001, 0) 377 | self.conn.write(0xA000, bank & 0xF) 378 | self.conn.write(0xA001, 1) 379 | self.conn.write(0xA000, bank >> 4) 380 | 381 | def select_ram_bank(self, bank): 382 | pass 383 | 384 | def dump_ram(self): 385 | self.conn.mark_busy() 386 | self.unlock_ram() 387 | ram = [] 388 | for i in range(self.ramsize): 389 | self.conn.write(0xA001, 6) 390 | self.conn.write(0xA000, (i >> 4) + 2) 391 | self.conn.write(0xA001, 7) 392 | self.conn.write(0xA000, i & 0xF) 393 | self.conn.write(0xA001, 0xD) 394 | hi = ord(self.conn.read_ec(0xA000)) & 0xF 395 | self.conn.write(0xA001, 0xC) 396 | lo = ord(self.conn.read_ec(0xA000)) & 0xF 397 | ram.append((hi << 4) | lo) 398 | self.conn.mark_idle() 399 | return bytes(ram) 400 | 401 | def restore_ram(self, ram): 402 | self.conn.mark_busy() 403 | self.unlock_ram() 404 | for i in range(self.ramsize): 405 | self.conn.write(0xA001, 4) 406 | self.conn.write(0xA000, i & 0xF) 407 | self.conn.write(0xA001, 5) 408 | self.conn.write(0xA000, i >> 4) 409 | self.conn.write(0xA001, 6) 410 | self.conn.write(0xA000, i >> 4) 411 | self.conn.write(0xA001, 7) 412 | self.conn.write(0xA000, i & 0xF) 413 | self.conn.mark_idle() 414 | 415 | class HuC1(MBC): 416 | pass 417 | 418 | class HuC3(HuC1): 419 | def select_rom_bank(self, bank): 420 | self.conn.write(0x2000, bank) 421 | 422 | 423 | MAPPINGS = { 424 | 0x00: MBC, 425 | 0x01: MBC1, 426 | 0x02: MBC1, 427 | 0x03: MBC1, 428 | 0x05: MBC2, 429 | 0x06: MBC2, 430 | 0x08: MBC, 431 | 0x09: MBC, 432 | #0x0B: MMM01, 433 | #0x0C: MMM01, 434 | #0x0D: MMM01, 435 | 0x0F: MBC3, 436 | 0x10: MBC3, 437 | 0x11: MBC3, 438 | 0x12: MBC3, 439 | 0x13: MBC3, 440 | 0x19: MBC5, 441 | 0x1A: MBC5, 442 | 0x1B: MBC5, 443 | 0x1C: MBC5, 444 | 0x1D: MBC5, 445 | 0x1E: MBC5, 446 | 0x20: MBC6, 447 | 0x22: MBC7, 448 | 0xFC: GBCamera, 449 | 0xFD: TAMA5, 450 | 0xFE: HuC3, 451 | 0xFF: HuC1 452 | } 453 | 454 | def detect(conn): 455 | if conn.carttype not in MAPPINGS: 456 | return None 457 | return MAPPINGS[conn.carttype](conn) 458 | -------------------------------------------------------------------------------- /parallel/CHANGES.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | pyParallel Release Notes 3 | ========================== 4 | 5 | Version 0.1 29 Jul 2002 6 | --------------------------- 7 | - added to CVS 8 | 9 | Version 0.2 27 Jan 2005 10 | --------------------------- 11 | - Windows version now using ctypes 12 | 13 | Version ... ... 14 | --------------------------- 15 | - add setDataDir to Windows backend 16 | - [SF 2785532] add getData for ppdev backend 17 | 18 | -------------------------------------------------------------------------------- /parallel/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2001-2015 Chris Liechti 2 | All Rights Reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | --------------------------------------------------------------------------- 33 | Note: 34 | Individual files contain the following tag instead of the full license text. 35 | 36 | SPDX-License-Identifier: BSD-3-Clause 37 | 38 | This enables machine processing of license information based on the SPDX 39 | License Identifiers that are here available: http://spdx.org/licenses/ 40 | -------------------------------------------------------------------------------- /parallel/README.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | pyParallel [in development] 3 | ============================ 4 | 5 | .. image:: https://travis-ci.org/pyserial/pyparallel.svg?branch=master 6 | :target: https://travis-ci.org/pyserial/pyparallel 7 | :alt: Build Status 8 | 9 | .. image:: https://img.shields.io/pypi/dw/pyparallel.svg 10 | :target: https://pypi.python.org/pypi/pyparallel/ 11 | :alt: PyPI Downloads 12 | 13 | .. image:: https://img.shields.io/pypi/v/pyparallel.svg 14 | :target: https://pypi.python.org/pypi/pyparallel/ 15 | :alt: Latest PyPI version 16 | 17 | .. image:: https://img.shields.io/pypi/l/pyparallel.svg 18 | :target: https://pypi.python.org/pypi/pyparallel/ 19 | :alt: License 20 | 21 | Overview 22 | ======== 23 | This module encapsulates the access for the parallel port. It provides 24 | backends for Python running on Windows and Linux. Other platforms are 25 | possible too but not yet integrated. 26 | 27 | This module is still under development. But it may be useful for 28 | developers. 29 | The Windows version needs a compiled extension and the giveio.sys driver 30 | for Windows NT/2k/XP. It uses ``ctypes`` to access functions in a prebuilt 31 | DLL. 32 | 33 | It is released under a free software license, see LICENSE.rst for more 34 | details. 35 | 36 | Copyright (C) 2001-2016 Chris Liechti cliechti@gmx.net 37 | 38 | Homepage: https://github.com/pyserial/pyparallel 39 | 40 | 41 | Features 42 | ======== 43 | - same class based interface on all supported platforms 44 | - port numbering starts at zero, no need to know the port name in the 45 | user program 46 | - port string (device name) can be specified if access through numbering 47 | is inappropriate 48 | 49 | 50 | Requirements 51 | ============ 52 | - Python 2.2 or newer 53 | - "Java Communications" (JavaComm) extension for Java/Jython 54 | 55 | 56 | Installation 57 | ============ 58 | Extract files from the archive, open a shell/console in that directory and 59 | let Distutils do the rest: 60 | 61 | .. code-block:: bash 62 | 63 | $ python setup.py install 64 | 65 | 66 | Short introduction 67 | ------------------ 68 | 69 | .. code-block:: python 70 | 71 | >>> import parallel 72 | >>> p = parallel.Parallel() # open LPT1 or /dev/parport0 73 | >>> p.setData(0x55) 74 | 75 | 76 | Examples 77 | ======== 78 | Please look in the GIT Repository. There is an example directory where you 79 | can find a simple terminal and more. 80 | https://github.com/pyserial/pyparallel/tree/master/examples 81 | 82 | 83 | References 84 | ========== 85 | - Python: http://www.python.org 86 | 87 | -------------------------------------------------------------------------------- /parallel/__init__.py: -------------------------------------------------------------------------------- 1 | # portable parallel port access with python 2 | # this is a wrapper module for different platform implementations 3 | # 4 | # (C)2001-2002 Chris Liechti 5 | # 6 | # SPDX-License-Identifier: BSD-3-Clause 7 | 8 | import sys 9 | 10 | class IParallel(object): 11 | STROBE = 0x01 12 | LINEFEED = 0x02 13 | RESET = 0x04 14 | SELECT_PRINTER = 0x08 15 | 16 | ERROR = 0x08 17 | SELECT = 0x10 18 | PAPER_OUT = 0x20 19 | ACK = 0x40 20 | BUSY = 0x80 21 | 22 | def set_bit(self, pin, b): 23 | if pin < 1: 24 | raise IndexError 25 | if pin == 1: 26 | reg = self.get_control() & ~self.STROBE 27 | if not b: 28 | reg |= self.STROBE 29 | self.set_control(reg) 30 | elif pin <= 9: 31 | pin -= 2 32 | reg = self.get_data() & ~(1 << pin) 33 | if b: 34 | reg |= 1 << pin 35 | self.set_data(reg) 36 | elif pin == 14: 37 | reg = self.get_control() & ~self.LINEFEED 38 | if not b: 39 | reg |= self.LINEFEED 40 | self.set_control(reg) 41 | elif pin == 16: 42 | reg = self.get_control() & ~self.RESET 43 | if b: 44 | reg |= self.RESET 45 | self.set_control(reg) 46 | elif pin == 17: 47 | reg = self.get_control() & ~self.SELECT_PRINTER 48 | if not b: 49 | reg |= self.SELECT_PRINTER 50 | self.set_control(reg) 51 | else: 52 | raise IndexError 53 | 54 | def get_bit(self, pin): 55 | if pin < 1: 56 | raise IndexError 57 | if pin == 1: 58 | reg = self.get_control() & self.STROBE 59 | return not reg 60 | if pin <= 9: 61 | pin -= 2 62 | reg = self.get_data() & (1 << pin) 63 | return bool(reg) 64 | if pin == 10: 65 | reg = self.get_status() & self.ACK 66 | return bool(reg) 67 | if pin == 11: 68 | reg = self.get_status() & self.BUSY 69 | return not reg 70 | if pin == 12: 71 | reg = self.get_status() & self.PAPER_OUT 72 | return bool(reg) 73 | if pin == 13: 74 | reg = self.get_status() & self.SELECT 75 | return bool(reg) 76 | if pin == 14: 77 | reg = self.get_control() & self.LINEFEED 78 | return not reg 79 | if pin == 15: 80 | reg = self.get_status() & self.ERROR 81 | return bool(reg) 82 | if pin == 16: 83 | reg = self.get_control() & self.RESET 84 | return bool(reg) 85 | if pin == 17: 86 | reg = self.get_control() & self.SELECT_PRINTER 87 | return not reg 88 | raise IndexError 89 | 90 | # choose an implementation, depending on os 91 | if sys.platform == 'win32': 92 | from parallel.parallelwin32 import Parallel # noqa 93 | elif sys.platform.startswith('linux'): 94 | from parallel.parallelppdev import Parallel # noqa 95 | elif sys.platform.startswith('freebsd'): 96 | from parallel.parallelppi import Parallel # noqa 97 | else: 98 | raise "Sorry no implementation for your platform available." 99 | -------------------------------------------------------------------------------- /parallel/inpout32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endrift/gblinkpy/5e66049022a8b5876135f3736c0dc277523fd96f/parallel/inpout32.dll -------------------------------------------------------------------------------- /parallel/inpoutx64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endrift/gblinkpy/5e66049022a8b5876135f3736c0dc277523fd96f/parallel/inpoutx64.dll -------------------------------------------------------------------------------- /parallel/parallelppdev.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # parallel port access using the ppdev driver 3 | 4 | import sys 5 | import struct 6 | import fcntl 7 | import os 8 | 9 | from . import IParallel 10 | 11 | # Generated by h2py 0.1.1 from , 12 | # then cleaned up a bit by Michael P. Ashton and then a gain by chris ;-) 13 | # Changes for Python2.2 support (c) September 2004 Alex.Perry@qm.com 14 | 15 | 16 | def sizeof(type): 17 | return struct.calcsize(type) 18 | 19 | 20 | def _IOC(dir, type, nr, size): 21 | return int((dir << _IOC_DIRSHIFT) | (type << _IOC_TYPESHIFT) | 22 | (nr << _IOC_NRSHIFT) | (size << _IOC_SIZESHIFT)) 23 | 24 | 25 | def _IO(type, nr): 26 | return _IOC(_IOC_NONE, type, nr, 0) 27 | 28 | 29 | def _IOR(type, nr, size): 30 | return _IOC(_IOC_READ, type, nr, sizeof(size)) 31 | 32 | 33 | def _IOW(type, nr, size): 34 | return _IOC(_IOC_WRITE, type, nr, sizeof(size)) 35 | 36 | _IOC_SIZEBITS = 14 37 | _IOC_SIZEMASK = (1 << _IOC_SIZEBITS) - 1 38 | _IOC_NRSHIFT = 0 39 | _IOC_NRBITS = 8 40 | _IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS 41 | _IOC_TYPEBITS = 8 42 | _IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS 43 | IOCSIZE_MASK = _IOC_SIZEMASK << _IOC_SIZESHIFT 44 | IOCSIZE_SHIFT = _IOC_SIZESHIFT 45 | 46 | # Python 2.2 uses a signed int for the ioctl() call, so ... 47 | if sys.version_info[0] < 3 or sys.version_info[1] < 3: 48 | _IOC_WRITE = 1 49 | _IOC_READ = -2 50 | _IOC_INOUT = -1 51 | else: 52 | _IOC_WRITE = 1 53 | _IOC_READ = 2 54 | _IOC_INOUT = 3 55 | 56 | _IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS 57 | IOC_INOUT = _IOC_INOUT << _IOC_DIRSHIFT 58 | IOC_IN = _IOC_WRITE << _IOC_DIRSHIFT 59 | IOC_OUT = _IOC_READ << _IOC_DIRSHIFT 60 | 61 | _IOC_NONE = 0 62 | PP_IOCTL = ord('p') 63 | PPCLAIM = _IO(PP_IOCTL, 0x8b) 64 | PPCLRIRQ = _IOR(PP_IOCTL, 0x93, 'i') 65 | 66 | PPDATADIR = _IOW(PP_IOCTL, 0x90, 'i') 67 | PPEXCL = _IO(PP_IOCTL, 0x8f) 68 | PPFCONTROL = _IOW(PP_IOCTL, 0x8e, 'BB') 69 | PPGETFLAGS = _IOR(PP_IOCTL, 0x9a, 'i') 70 | PPGETMODE = _IOR(PP_IOCTL, 0x98, 'i') 71 | PPGETMODES = _IOR(PP_IOCTL, 0x97, 'I') 72 | PPGETPHASE = _IOR(PP_IOCTL, 0x99, 'i') 73 | PPGETTIME = _IOR(PP_IOCTL, 0x95, 'll') 74 | PPNEGOT = _IOW(PP_IOCTL, 0x91, 'i') 75 | PPRCONTROL = _IOR(PP_IOCTL, 0x83, 'B') 76 | PPRDATA = _IOR(PP_IOCTL, 0x85, 'B') 77 | # 'OBSOLETE__IOR' undefined in 'PPRECONTROL' 78 | PPRELEASE = _IO(PP_IOCTL, 0x8c) 79 | # 'OBSOLETE__IOR' undefined in 'PPRFIFO' 80 | PPRSTATUS = _IOR(PP_IOCTL, 0x81, 'B') 81 | PPSETFLAGS = _IOW(PP_IOCTL, 0x9b, 'i') 82 | PPSETMODE = _IOW(PP_IOCTL, 0x80, 'i') 83 | PPSETPHASE = _IOW(PP_IOCTL, 0x94, 'i') 84 | PPSETTIME = _IOW(PP_IOCTL, 0x96, 'll') 85 | PPWCONTROL = _IOW(PP_IOCTL, 0x84, 'B') 86 | PPWCTLONIRQ = _IOW(PP_IOCTL, 0x92, 'B') 87 | PPWDATA = _IOW(PP_IOCTL, 0x86, 'B') 88 | # 'OBSOLETE__IOW' undefined in 'PPWECONTROL' 89 | # 'OBSOLETE__IOW' undefined in 'PPWFIFO' 90 | # 'OBSOLETE__IOW' undefined in 'PPWSTATUS' 91 | PPYIELD = _IO(PP_IOCTL, 0x8d) 92 | PP_FASTREAD = 1 << 3 93 | PP_FASTWRITE = 1 << 2 94 | PP_W91284PIC = 1 << 4 95 | PP_FLAGMASK = PP_FASTWRITE | PP_FASTREAD | PP_W91284PIC 96 | PP_MAJOR = 99 97 | _ASMI386_IOCTL_H = None 98 | _IOC_DIRBITS = 2 99 | _IOC_DIRMASK = (1 << _IOC_DIRBITS) - 1 100 | _IOC_NRMASK = (1 << _IOC_NRBITS) - 1 101 | _IOC_TYPEMASK = (1 << _IOC_TYPEBITS) - 1 102 | 103 | 104 | def _IOC_DIR(nr): 105 | return (nr >> _IOC_DIRSHIFT) & _IOC_DIRMASK 106 | 107 | 108 | def _IOC_NR(nr): 109 | return (nr >> _IOC_NRSHIFT) & _IOC_NRMASK 110 | 111 | 112 | def _IOC_SIZE(nr): 113 | return (nr >> _IOC_SIZESHIFT) & _IOC_SIZEMASK 114 | 115 | 116 | def _IOC_TYPE(nr): 117 | return (nr >> _IOC_TYPESHIFT) & _IOC_TYPEMASK 118 | 119 | 120 | def _IOWR(type, nr, size): 121 | return _IOC(_IOC_READ | _IOC_WRITE, type, nr, sizeof(size)) 122 | 123 | # Constants from 124 | 125 | PARPORT_CONTROL_STROBE = 0x1 126 | PARPORT_CONTROL_AUTOFD = 0x2 127 | PARPORT_CONTROL_INIT = 0x4 128 | PARPORT_CONTROL_SELECT = 0x8 129 | PARPORT_STATUS_ERROR = 8 130 | PARPORT_STATUS_SELECT = 0x10 131 | PARPORT_STATUS_PAPEROUT = 0x20 132 | PARPORT_STATUS_ACK = 0x40 133 | PARPORT_STATUS_BUSY = 0x80 134 | 135 | IEEE1284_MODE_NIBBLE = 0 136 | IEEE1284_MODE_BYTE = 1 137 | IEEE1284_MODE_COMPAT = 1 << 8 138 | IEEE1284_MODE_BECP = 1 << 9 139 | IEEE1284_MODE_ECP = 1 << 4 140 | IEEE1284_MODE_ECPRLE = IEEE1284_MODE_ECP | (1 << 5) 141 | IEEE1284_MODE_ECPSWE = 1 << 10 142 | IEEE1284_MODE_EPP = 1 << 6 143 | IEEE1284_MODE_EPPSL = 1 << 11 144 | IEEE1284_MODE_EPPSWE = 1 << 12 145 | IEEE1284_DEVICEID = 1 << 2 146 | IEEE1284_EXT_LINK = 1 << 14 147 | 148 | IEEE1284_ADDR = 1 << 13 149 | IEEE1284_DATA = 0 150 | 151 | PARPORT_EPP_FAST = 1 152 | PARPORT_W91284PIC = 2 153 | 154 | 155 | class Parallel(IParallel): 156 | """Class for controlling the pins on a parallel port 157 | 158 | This class provides bit-level access to the pins on a PC parallel 159 | port. It is primarily designed for programs which must control 160 | special circuitry - most often non-IEEE-1284-compliant devices 161 | other than printers - using 'bit-banging' techniques. 162 | 163 | The current implementation makes ioctl() calls to the Linux ppdev 164 | driver, using the Python fcntl library. It might be rewritten in 165 | C for extra speed. This particular implementation is written for 166 | Linux; all of the upper-level calls can be ported to Windows as 167 | well. 168 | 169 | On Linux, the ppdev device driver, from the Linux 2.4 parallel 170 | port subsystem, is used to control the parallel port hardware. 171 | This driver must be made available from a kernel compile. The 172 | option is called "Support user-space parallel-port drivers". When 173 | using the module, be sure to unload the lp module first: usually 174 | the lp module claims exclusive access to the parallel port, and if 175 | it is loaded, this class will fail to open the parallel port file, 176 | and throw an exception. 177 | 178 | The primary source of information about the Linux 2.4 parallel 179 | port subsystem is Tim Waugh's documentation, the source for which 180 | is available in the kernel tree. This document (called, 181 | appropriately enough, "The Linux 2.4 Parallel Port Subsystem"), 182 | thoroughly describes the parallel port drivers and how to use 183 | them. 184 | 185 | This class provides a method for each of the ioctls supported by 186 | the ppdev module. The ioctl methods are named, in uppercase, the 187 | same as the ioctls they invoke. The documentation for these 188 | methods was taken directly from the documentation for their 189 | corresponding ioctl, and modified only where necessary. 190 | 191 | Unless you have special reason to use the Linux ioctls, you should 192 | use instead the upper-level functions, which are named in 193 | lowerCase fashion and should be portable between Linux and 194 | Windows. This way, any code you write for this class will (or 195 | should) also work with the Windows version of this class. 196 | 197 | """ 198 | def __init__(self, port=0): 199 | if isinstance(port, int): 200 | self.device = "/dev/parport%d" % port 201 | else: 202 | self.device = port 203 | self._fd = None 204 | self._fd = os.open(self.device, os.O_RDWR) 205 | try: 206 | self.PPEXCL() 207 | self.PPCLAIM() 208 | self.set_data_dir(1) 209 | self.set_data(0) 210 | except IOError: 211 | os.close(self._fd) 212 | self._fd = None 213 | raise 214 | 215 | def __del__(self): 216 | if self._fd is not None: 217 | self.PPRELEASE() 218 | os.close(self._fd) 219 | 220 | def PPCLAIM(self): 221 | """ 222 | Claims access to the port. As a user-land device driver 223 | writer, you will need to do this before you are able to 224 | actually change the state of the parallel port in any 225 | way. Note that some operations only affect the ppdev driver 226 | and not the port, such as PPSETMODE; they can be performed 227 | while access to the port is not claimed. 228 | """ 229 | fcntl.ioctl(self._fd, PPCLAIM) 230 | 231 | def PPEXCL(self): 232 | """ 233 | Instructs the kernel driver to forbid any sharing of the port 234 | with other drivers, i.e. it requests exclusivity. The PPEXCL 235 | command is only valid when the port is not already claimed for 236 | use, and it may mean that the next PPCLAIM ioctl will fail: 237 | some other driver may already have registered itself on that 238 | port. 239 | 240 | Most device drivers don't need exclusive access to the 241 | port. It's only provided in case it is really needed, for 242 | example for devices where access to the port is required for 243 | extensive periods of time (many seconds). 244 | 245 | Note that the PPEXCL ioctl doesn't actually claim the port 246 | there and then---action is deferred until the PPCLAIM ioctl is 247 | performed. 248 | """ 249 | fcntl.ioctl(self._fd, PPEXCL) 250 | 251 | def PPRELEASE(self): 252 | """ 253 | Releases the port. Releasing the port undoes the effect of 254 | claiming the port. It allows other device drivers to talk to 255 | their devices (assuming that there are any). 256 | """ 257 | fcntl.ioctl(self._fd, PPRELEASE) 258 | 259 | def PPDATADIR(self, out): 260 | """ 261 | Controls the data line drivers. Normally the computer's 262 | parallel port will drive the data lines, but for byte-wide 263 | transfers from the peripheral to the host it is useful to turn 264 | off those drivers and let the peripheral drive the 265 | signals. (If the drivers on the computer's parallel port are 266 | left on when this happens, the port might be damaged.) 267 | This is only needed in conjunction with PPWDATA or PPRDATA. 268 | The 'out' parameter indicates the desired port direction. If 269 | 'out' is true or non-zero, the drivers are turned on (forward 270 | direction); otherwise, the drivers are turned off (reverse 271 | direction). 272 | """ 273 | if out: 274 | msg = struct.pack('i', 0) 275 | else: 276 | msg = struct.pack('i', 1) 277 | fcntl.ioctl(self._fd, PPDATADIR, msg) 278 | 279 | def set_data_dir(self, out): 280 | """Activates or deactivates the data bus line drivers (pins 2-9)""" 281 | self._dataDir = out 282 | self.PPDATADIR(out) 283 | 284 | def set_control(self, lines): 285 | fcntl.ioctl(self._fd, PPWCONTROL, struct.pack('B', lines)) 286 | 287 | def get_control(self): 288 | ret = struct.pack('B', 0) 289 | ret = fcntl.ioctl(self._fd, PPRCONTROL, ret) 290 | return struct.unpack('B', ret)[0] 291 | 292 | def get_status(self): 293 | ret = struct.pack('B', 0) 294 | ret = fcntl.ioctl(self._fd, PPRSTATUS, ret) 295 | return struct.unpack('B', ret)[0] 296 | 297 | def set_data(self, byte): 298 | fcntl.ioctl(self._fd, PPWDATA, struct.pack('B', byte)) 299 | 300 | def get_data(self): 301 | ret = struct.pack('B', 0) 302 | ret = fcntl.ioctl(self._fd, PPRDATA, ret) 303 | return struct.unpack('B', ret)[0] 304 | -------------------------------------------------------------------------------- /parallel/parallelppi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # parallel port access using the ppi driver 3 | 4 | import sys 5 | import struct 6 | import fcntl 7 | import os 8 | 9 | from . import IParallel 10 | 11 | def sizeof(type): 12 | return struct.calcsize(type) 13 | 14 | def _IOC(inout, group, num, len): 15 | return int(((inout) | (((len) & IOCPARM_MASK) << 16) | ((group) << 8) | (num))) 16 | 17 | def _IO(group, num): 18 | return _IOC(_IOC_VOID, group, num, 0) 19 | 20 | def _IOR(group, num, size): 21 | return _IOC(_IOC_OUT, group, num, sizeof(size)) 22 | 23 | def _IOW(group, num, size): 24 | return _IOC(_IOC_IN, group, num, sizeof(size)) 25 | 26 | IOCPARM_SHIFT = 13 27 | IOCPARM_MASK = ((1 << IOCPARM_SHIFT) - 1) 28 | 29 | _IOC_VOID = 0x20000000 30 | _IOC_OUT = 0x40000000 31 | _IOC_IN = 0x80000000 32 | _IOC_INOUT = (_IOC_IN|_IOC_OUT) 33 | 34 | PP_IOCTL = ord('P') 35 | 36 | PPIGDATA = _IOR(PP_IOCTL, 10, 'B') 37 | PPIGSTATUS = _IOR(PP_IOCTL, 11, 'B') 38 | PPIGCTRL = _IOR(PP_IOCTL, 12, 'B') 39 | PPIGEPPD = _IOR(PP_IOCTL, 13, 'B') 40 | PPIGECR = _IOR(PP_IOCTL, 14, 'B') 41 | PPIGFIFO = _IOR(PP_IOCTL, 15, 'B') 42 | 43 | PPISDATA = _IOW(PP_IOCTL, 16, 'B') 44 | PPISSTATUS = _IOW(PP_IOCTL, 17, 'B') 45 | PPISCTRL = _IOW(PP_IOCTL, 18, 'B') 46 | PPISEPPD = _IOW(PP_IOCTL, 19, 'B') 47 | PPISECR = _IOW(PP_IOCTL, 20, 'B') 48 | PPISFIFO = _IOW(PP_IOCTL, 21, 'B') 49 | 50 | PPIGEPPA = _IOR(PP_IOCTL, 22, 'B') 51 | PPISEPPA = _IOR(PP_IOCTL, 23, 'B') 52 | 53 | 54 | class Parallel(IParallel): 55 | def __init__(self, port=0): 56 | if isinstance(port, int): 57 | self.device = "/dev/ppi%d" % port 58 | else: 59 | self.device = port 60 | self._fd = None 61 | self._fd = os.open(self.device, os.O_RDWR) 62 | try: 63 | self.set_data(0) 64 | except IOError: 65 | os.close(self._fd) 66 | self._fd = None 67 | raise 68 | 69 | def __del__(self): 70 | if self._fd is not None: 71 | os.close(self._fd) 72 | 73 | def set_control(self, lines): 74 | fcntl.ioctl(self._fd, PPISCTRL, struct.pack('B', lines)) 75 | 76 | def get_control(self): 77 | ret = struct.pack('B', 0) 78 | ret = fcntl.ioctl(self._fd, PPIGCTRL, ret) 79 | return struct.unpack('B', ret)[0] 80 | 81 | def get_status(self): 82 | ret = struct.pack('B', 0) 83 | ret = fcntl.ioctl(self._fd, PPIGSTATUS, ret) 84 | return struct.unpack('B', ret)[0] 85 | 86 | def set_data(self, byte): 87 | fcntl.ioctl(self._fd, PPISDATA, struct.pack('B', byte)) 88 | 89 | def get_data(self): 90 | ret = struct.pack('B', 0) 91 | ret = fcntl.ioctl(self._fd, PPIGDATA, ret) 92 | return struct.unpack('B', ret)[0] 93 | -------------------------------------------------------------------------------- /parallel/parallelwin32.py: -------------------------------------------------------------------------------- 1 | # pyparallel driver for win32 2 | # see __init__.py 3 | # 4 | # (C) 2002 Chris Liechti 5 | # 6 | # SPDX-License-Identifier: BSD-3-Clause 7 | # 8 | # thanks to Dincer Aydin dinceraydin@altavista.net for his work on the 9 | # winioport module: www.geocities.com/dinceraydin/ the graphic below is 10 | # borrowed form him ;-) 11 | 12 | 13 | # LPT1 = 0x0378 or 0x03BC 14 | # LPT2 = 0x0278 or 0x0378 15 | # LPT3 = 0x0278 16 | # 17 | # Data Register (base + 0) ........ outputs 18 | # 19 | # 7 6 5 4 3 2 1 0 20 | # . . . . . . . * D0 ........... (pin 2), 1=High, 0=Low (true) 21 | # . . . . . . * . D1 ........... (pin 3), 1=High, 0=Low (true) 22 | # . . . . . * . . D2 ........... (pin 4), 1=High, 0=Low (true) 23 | # . . . . * . . . D3 ........... (pin 5), 1=High, 0=Low (true) 24 | # . . . * . . . . D4 ........... (pin 6), 1=High, 0=Low (true) 25 | # . . * . . . . . D5 ........... (pin 7), 1=High, 0=Low (true) 26 | # . * . . . . . . D6 ........... (pin 8), 1=High, 0=Low (true) 27 | # * . . . . . . . D7 ........... (pin 9), 1=High, 0=Low (true) 28 | # 29 | # Status Register (base + 1) ...... inputs 30 | # 31 | # 7 6 5 4 3 2 1 0 32 | # . . . . . * * * Undefined 33 | # . . . . * . . . Error ........ (pin 15), high=1, low=0 (true) 34 | # . . . * . . . . Selected ..... (pin 13), high=1, low=0 (true) 35 | # . . * . . . . . No paper ..... (pin 12), high=1, low=0 (true) 36 | # . * . . . . . . Ack .......... (pin 10), high=1, low=0 (true) 37 | # * . . . . . . . Busy ......... (pin 11), high=0, low=1 (inverted) 38 | # 39 | # ctrl Register (base + 2) ..... outputs 40 | # 41 | # 7 6 5 4 3 2 1 0 42 | # . . . . . . . * Strobe ....... (pin 1), 1=low, 0=high (inverted) 43 | # . . . . . . * . Auto Feed .... (pin 14), 1=low, 0=high (inverted) 44 | # . . . . . * . . Initialize ... (pin 16), 1=high,0=low (true) 45 | # . . . . * . . . Select ....... (pin 17), 1=low, 0=high (inverted) 46 | # * * * * . . . . Unused 47 | import ctypes 48 | import os 49 | from . import IParallel 50 | 51 | LPT1 = 0 52 | LPT2 = 1 53 | 54 | LPT1_base = 0x0378 55 | LPT2_base = 0x0278 56 | 57 | # need to patch PATH so that the DLL can be found and loaded 58 | os.environ['PATH'] = os.environ['PATH'] + ';' + os.path.abspath(os.path.dirname(__file__)) 59 | try: 60 | inpout = ctypes.windll.inpoutx64 61 | except: 62 | inpout = ctypes.windll.inpout32 63 | 64 | 65 | class Parallel(IParallel): 66 | def __init__(self, port=LPT1): 67 | if port == LPT1: 68 | self.dataRegAdr = LPT1_base 69 | elif port == LPT2: 70 | self.dataRegAdr = LPT2_base 71 | else: 72 | self.dataRegAdr = port 73 | self.statusRegAdr = self.dataRegAdr + 1 74 | self.ctrlRegAdr = self.dataRegAdr + 2 75 | 76 | def set_data(self, value): 77 | inpout.Out32(self.dataRegAdr, value) 78 | 79 | def get_data(self): 80 | return inpout.Inp32(self.dataRegAdr) 81 | 82 | def set_data_dir(self, level): 83 | """set for port as input, clear for output""" 84 | ctrlReg = self.get_control() 85 | if level: 86 | ctrlReg |= 0x20 87 | else: 88 | ctrlReg &= ~0x20 89 | inpout.Out32(self.ctrlRegAdr, ctrlReg) 90 | 91 | def get_status(self): 92 | return inpout.Inp32(self.statusRegAdr) 93 | 94 | def set_control(self, value): 95 | inpout.Out32(self.ctrlRegAdr, value) 96 | 97 | def get_control(self): 98 | return inpout.Inp32(self.ctrlRegAdr) 99 | --------------------------------------------------------------------------------