# # Dr. Stuart Madnick devised the Little Man Computer # in 1965 as a parable to explain low level machine # operations. This program is a simulator that emulates # his conception in software as a VonNeuman RISC machine. # # This is a structural diagram of my implementation: # _____ # | | # | | # | |<-------[TAPE] # | LMC | # _____ | CPU |<------>[CONSOLE] # | | ___ | | # | RAM |/ \| | # | 8Kb |\___/| | # |_____| |_____| # # There really is no "ROM". The entire address range is programable. # Part of the array I call _Ram is pre-loaded with a crude loader. # # # OPCODES with INSTRUCTION FORMAT # # SINGLE BYTE INSTRUCTIONS # HLT 0x00 Halt LMC (exit simulator) # INP 0x93 Input single byte to calculator from stdin # OUT 0x94 Outoput calculator byte to stdin # RDT 0x95 Read byte from ./tape into stdin # LSR 0xf2 Logical Shift Right the calculator # LSL 0xf3 Logical Shift Left the calculator # INC 0xf4 Increment the calculator # DEC 0xf5 Decrement the calculator # NOP 0xff No Op # # THREE BYTE INSTRUCTIONS # ADD 0x10 0xhi 0xlo calculator += mailbox[hilo] # SUB 0x20 0xhi 0xlo calculator -= mailbox[hilo] # STA 0x30 0xhi 0xlo mailbox[hilo] = calculator # AND 0x40 0xhi 0xlo calculator &= mailbox[hilo] # LDA 0x50 0xhi 0xlo calculator = mailbox[hilo] # JMP 0x60 0xhi 0xlo progcounter = mailbox[hilo] # BRZ 0x70 0xhi 0xlo if calculator = 0 then progcounter = mailbox[hilo] # BRB 0x7b 0xhi 0xlo progcounter -= mailbox[hilo] # BRF 0x7f 0xhi 0xlo progcounter += mailbox[hilo] # # import sys import time # # Initialize Variables _PC = 0x00 # AN INTEGER Program Counter _Cal = 0x00 # A BYTE VALUE Calculator ( or Accumulator ) _Offset = 0x00 # AN INTEGER Offset of Relative Branch _Address = 0x00 # AN INTEGER Historically called MailBox _Ram = [0x00] # AN ARRAY The LMC's Memory _RamSize = 0x1fff # AN INTEGER The Size of Memory in Bytes _Opcode = 0xff # AN INTEGER The Opcode (preset to non-zero) _Monitor = "OFF" # call monitor routine (Trace data) if "ON" # Establish the Memory Map. # (in the original these were called the 'mailboxes') for i in range(1,_RamSize): # Establish the Memory Map _Ram.append(0x00) # Ready the 'Tape Drive' for input f= open("tape","r") ##### ##### ##### BEGIN BOOT LOADER ##### ##### # ...Loads the binary file './tape' into Ram # # [WARNING] # This 20 byte loader will only load code # that fits between 0x0100 and 0x01ff! # _Ram[0x00] = 0x95 # RDT Input byte from 'Tape' _Ram[0x01] = 0x30 # STA Store byte at pointer _Ram[0x02] = 0x01 # (user code starts at 0x15) _Ram[0x03] = 0x00 # (following boot loader) _Ram[0x04] = 0x20 # SUB Check for {EOT} _Ram[0x05] = 0x00 _Ram[0x06] = 0x14 _Ram[0x07] = 0x70 # BRZ If {EOT} break loop and _Ram[0x08] = 0x01 # jump to start of loaded program _Ram[0x09] = 0x00 _Ram[0x0a] = 0x50 # LDA Pointer _Ram[0x0b] = 0x00 _Ram[0x0c] = 0x03 _Ram[0x0d] = 0xf4 # INC Pointer _Ram[0x0e] = 0x30 # STA and store it again _Ram[0x0f] = 0x00 _Ram[0x10] = 0x03 _Ram[0x11] = 0x60 # JMP Next byte _Ram[0x12] = 0x00 _Ram[0x13] = 0x00 _Ram[0x14] = 0x04 # {EOT} 0x04 ##### ##### ##### END OF BOOT LOADER ##### ##### # # OpCode 0x93 # Wait for single keypress # and store in _Cal # incrementing _PC # def x93(_Cal, _PC): import termios, sys, tty fd = sys.stdin.fileno() old = termios.tcgetattr(fd) new = termios.tcgetattr(fd) new[3] = new[3] & ~termios.ECHO termios.tcsetattr(fd, termios.TCSADRAIN, new) tty.setraw(fd) _Cal = sys.stdin.read(1) termios.tcsetattr(fd, termios.TCSADRAIN, old) _PC = _PC + 1 _Cal = ord(_Cal) return _Cal, _PC; # # OPCODE 0x94 # Output single byte in Calculator # Calculator is unchanged # Program Counter incremented by 1 # def x94(_Cal, _PC): sys.stdout.write(chr(_Cal)) # sys.stdout.flush() _PC = _PC + 1 return _Cal, _PC; # # MONITOR SYSTEM STATUS # def monitor(_Cal, _PC, _Opcode): if _PC >= 0x100: print ("\nPC:%4.4X OP:%2.2X CAL:%2.2X " % (_PC, _Opcode, _Cal)), time.sleep(.2) return # # HOUSEKEEPING FOR 3 BYTE INSTRUCTION # Compute value of _Address as 0xlohi # and add 3 to _PC to point it at next instruction # def address(_Address, _PC, _Ram): _Address = _Ram[_PC + 1] * 256 # Shift high order byte into address word _Address = _Address + _Ram[_PC + 2] # and combine with L.O. byte. _PC = _PC + 3 # ADD is a three byte instruction. Next Opcode is at PC+3 return _Address, _PC, _Ram; # ## ## # # ## MAIN PROGRAM ## # # ## ## # if _Monitor == 'ON': print "Monitor is ON" while _Opcode != 0x00: # Opcode 0x00 is HLT _Opcode = _Ram[_PC] if _Monitor == 'ON': monitor(_Cal, _PC, _Opcode) if _Cal > 0xff: # Wrap Calculator so it remains 8 bits _Cal = _Cal - 0xff elif _Cal < 0x00: _Cal = _Cal + 0xff if _Opcode == 0x10: # ADD 0x10 # _Cal = _Cal + _Ram[_Address] _Address, _PC, _Ram = address(_Address, _PC, _Ram) _Cal = _Cal + _Ram[_Address] # Perform the ADD elif _Opcode == 0x20: # SUB 0x20 # _Cal = _Cal - _Ram[_Address] _Address, _PC, _Ram = address(_Address, _PC, _Ram) _Cal = _Cal - _Ram[_Address] elif _Opcode == 0x30: # STA 0x30 # _Ram[_Address] = _Cal _Address, _PC, _Ram = address(_Address, _PC, _Ram) _Ram[_Address] = _Cal elif _Opcode == 0x40: # AND 0x40 # _Cal &= _Ram[_Address] _Address, _PC, _Ram = address(_Address, _PC, _Ram) _Cal &= _Ram[_Address] elif _Opcode == 0x50: # LDA 0x50 # _Cal = _Ram[_Address] _Address, _PC, _Ram = address(_Address, _PC, _Ram) _Cal = _Ram[_Address] elif _Opcode == 0x60: # JMP 0x60 _Address, _PC, _Ram = address(_Address, _PC, _Ram) _PC = _Address elif _Opcode == 0x70: # BRZ 0x70 # if _Cal==0 then _PC=_Ram[_Address] if _Cal == 0: _Address, _PC, _Ram = address(_Address, _PC, _Ram) _PC = _Address else: # _Cal != 0 Go to next Opcode _PC = _PC + 3 elif _Opcode == 0x7b: # BRB 0x7b # _PC = _PC - _Offset _Offset = _Ram[_PC + 1] * 256 _Offset = _Offset + _Ram[_PC + 2] _PC = _PC - _Offset elif _Opcode == 0x7f: # BRF 0x7F # _PC = _PC + _Offset _Offset = _Ram[_PC + 1] * 256 _Offset = _Offset + _Ram[_PC + 2] _PC = _PC + _Offset elif _Opcode == 0x93: # INP 0x93 _Cal, _PC = x93(_Cal, _PC) elif _Opcode == 0x94: # OUT 0x94 _Cal, _PC = x94(_Cal, _PC) elif _Opcode == 0x95: # RDT 0x95 'READ TAPE' _Cal = f.read(1) _Cal = ord(_Cal) _PC = _PC + 1 elif _Opcode == 0xf2: # LSR 0xf2 _Cal = _Cal / 2 _Opcode = 0x00 elif _Opcode == 0xf3: # LSL 0xf3 _Cal = _Cal * 2 elif _Opcode == 0xf4: # INC 0xf4 _Cal = _Cal + 1 _PC = _PC + 1 elif _Opcode == 0xf5: # DEC 0xf5 _Cal = _Cal - 1 _PC = _PC + 1 elif _Opcode == 0xff: # NOP 0xff _PC = _PC + 1 elif _Opcode == 0x04: # END OF TAPE *NOT* AN OPCODE print '{EOT} Encountered!' _Opcode = 0x00 else: # Undefined Opcode Encountered. Terminate Emulator. if _Opcode != 0x00: print ('ERROR! %x is an Unknown Opcode.' % _Opcode) print ('LMC Aborting at PC %x' % _PC) _Opcode = 0x00 # END of LOOP "while _Opcode != 0x00:" # LMC HALTED - Clean-up and exit simulator print ("") # I hope you enjoy my crude toy! # You can reach me here... gopher://tildecow.com