Advertisement
Guest User

Untitled

a guest
Nov 2nd, 2024
150
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 49.52 KB | None | 0 0
  1. import pygame
  2. import sys
  3. import argparse
  4. from PIL import Image
  5. import logging
  6. import time
  7. import threading
  8.  
  9. # Configure logging
  10. logging.basicConfig(level=logging.INFO)
  11.  
  12. class SimpleEmulator:
  13. def __init__(self):
  14. self.display_width = 320
  15. self.display_height = 200
  16. self.memory = [0] * 0x6900000 # Ensure memory aligns with display
  17. self.frame_buffer = [[(0, 0, 0)] * self.display_width for _ in range(self.display_height)]
  18. self.registers = {
  19. 'eax': 0, 'ebx': 0, 'ecx': 0, 'edx': 0,
  20. 'esi': 0, 'edi': 0, 'esp': 0xFFFF, 'ebp': 0,
  21. 'al': 0, 'ah': 0, 'bl': 0, 'bh': 0,
  22. 'cl': 0, 'ch': 0, 'dl': 0, 'dh': 0,
  23. 'ds': 0, 'es': 0, 'cx': 0, 'si': 0, 'di': 0, 'ax': 0, 'bx': 0 # Segment registers for DOS
  24. }
  25. self.ip = 0x7C00 # Instruction pointer (start of BIOS)
  26. self.running = True
  27. self.screen = None
  28. self.maxShades = 5 # Change this if needed for performance
  29. self.keyboard_buffer = [] # Initialize the keyboard buffer
  30. self.boot_data = bytearray(b'\x00' * 512) # To initialize the virtual hard drive
  31. self.command_thread = threading.Thread(target=self.command_input) # Thread for command input
  32. self.command_thread.daemon = True # Daemon thread will exit when main program does
  33. self.devices = []
  34. self.vga_mode = "Text" # either use "Render" to show actual rendering or "Text" to only display text data
  35. self.fps = 0
  36. self.frame_count = 0
  37. self.last_time = pygame.time.get_ticks()
  38.  
  39.  
  40. def calculate_fps(self):
  41. """Calculate and return the current FPS."""
  42. current_time = pygame.time.get_ticks()
  43. self.frame_count += 1
  44.  
  45. # Update FPS every second
  46. if current_time - self.last_time >= 1000:
  47. self.fps = self.frame_count
  48. self.frame_count = 0
  49. self.last_time = current_time
  50.  
  51. return self.fps
  52.  
  53. def log(self, text):
  54. args = parser.parse_args()
  55. #print(args.verbose)
  56. if args.verbose == "-v":
  57. logging.info(text)
  58.  
  59. def warn(self, text):
  60. args = parser.parse_args()
  61. #print(args.verbose)
  62. if args.verbose == "-v":
  63. logging.warning(text)
  64.  
  65. def dump_memory(self, device_name, start, end):
  66. device = next((d for d in self.devices if d.name == device_name), None)
  67. if not device:
  68. print(f"Device '{device_name}' not found.")
  69. return
  70. #if start < 0 or end >= device.size:
  71. # print("Invalid address range.")
  72. # return
  73. print(f"Memory dump of '{device_name}' from {start:04X} to {end:04X}:")
  74. for addr in range(start, end + 1):
  75. if (addr - start) % 16 == 0:
  76. print("\n{:04X}: ".format(addr), end="")
  77. print(f"{device.memory[addr]:02X} ", end="")
  78. print()
  79.  
  80.  
  81. def handle_interrupt(self, interrupt_number):
  82. if interrupt_number == 0x10: # INT 10h for video services
  83. self.handle_video_interrupt()
  84.  
  85. def add_keypress(self, key):
  86. """Simulate a key press by adding it to the keyboard buffer."""
  87. self.keyboard_buffer.append(key)
  88. self.log(f"Key '{chr(key)}' added to keyboard buffer.")
  89.  
  90. def handle_video_interrupt(self):
  91. ah = self.registers['ah'] # Function number in AH register
  92. if ah == 0x00: # Set Video Mode
  93. self.set_video_mode()
  94. elif ah == 0x0C: # Write Pixel
  95. self.write_pixel()
  96. else:
  97. self.warn(f"Unhandled INT 10h function: {ah:02X}")
  98.  
  99. def command_input(self):
  100. """Function to handle user command input."""
  101. while self.running:
  102. command = input("root$ ")
  103. self.process_command(command)
  104.  
  105. def write_device(self, device_name, address, data):
  106. """Write data to a specific address on a specific device."""
  107. device = next((d for d in self.devices if d.name == device_name), None)
  108. if device is None:
  109. print(f"Device '{device_name}' not found.")
  110. return
  111.  
  112. if isinstance(data, str):
  113. # Write each character in the string to consecutive memory addresses
  114. for i, char in enumerate(data):
  115. device.write(address + i, ord(char))
  116. elif isinstance(data, int):
  117. # Write a single integer value
  118. device.write(address, data)
  119. else:
  120. print(f"Unsupported data type: {data}")
  121.  
  122. def read_device(self, device_name, address):
  123. """Read data from a specific address on a specific device."""
  124. device = next((d for d in self.devices if d.name == device_name), None)
  125. if device:
  126. return device.read(address)
  127. else:
  128. print(f"Device '{device_name}' not found.")
  129. return None
  130.  
  131. def process_command(self, command):
  132. """Process the command entered by the user."""
  133. parts = command.split()
  134. cmd = parts[0].lower()
  135.  
  136. if cmd == "exit":
  137. self.running = False
  138. elif cmd == "registers" or cmd == 'reg':
  139. for reg, value in self.registers.items():
  140. print(f"{reg}: {value:#04x}")
  141. elif cmd.startswith("set"):
  142. if len(parts) == 3:
  143. reg_name = parts[1].lower()
  144. value = int(parts[2], 16) # Assume hexadecimal input
  145. if reg_name in self.registers:
  146. self.registers[reg_name] = value
  147. self.log(f"Set {reg_name} to {value:#04x}")
  148. else:
  149. self.warn(f"Unknown register: {reg_name}")
  150. else:
  151. self.warn("Usage: set <register> <value>")
  152. elif cmd == "mem":
  153. address = int(parts[1], 16) if len(parts) > 1 else 0
  154. print(f"Memory at {address:#04x}: {self.memory[address]:#04x}")
  155. elif cmd == "send":
  156. address = int(parts[1], 16) if len(parts) > 1 else 0
  157. print(f"Sending {address:#04x} at {self.ip}")
  158. self.handle_opcode(address)
  159. elif cmd == "ip":
  160. print(f"Current IP: {self.ip}")
  161. elif cmd == None:
  162. print(f"Please enter a command")
  163. elif cmd == "device":
  164. name = parts[2].lower()
  165. type = parts[1].lower()
  166. if type != "usb" and type != "hd" and type != "cd" and type != "ps2":
  167. print(f"Unsupported type: '{type}'")
  168. return
  169. #self.ip = 0x7C00
  170. print(f"Adding a new: {type} with name: '{name}' at IP: {self.ip}")
  171. newdevice = VirtualDevice(name)
  172. self.devices.append(newdevice)
  173. #pygame.display.set_caption(f"Rah.py - {file}")
  174. elif cmd == "key":
  175. key = parts[1].lower()
  176. print(f"Sent key: {key} as {int(chr(key))}")
  177. self.add_keypress(chr(key))
  178. elif cmd == "write":
  179. if len(parts) < 4:
  180. print("Usage: write <device_name> <address> <data>")
  181. else:
  182. device_name = parts[1]
  183. address = int(parts[2], 16) # Assume address is in hex
  184. data = int(parts[3]) # Assume data is in decimal
  185. self.write_device(device_name, address, data)
  186. elif cmd == "dump":
  187. if len(parts) < 4:
  188. print("Usage: dump <device> <address1> <address2>")
  189. else:
  190. device_name = parts[1]
  191. address = int(parts[2], 16) # Assume address is in hex
  192. address2 = int(parts[3], 16) # Assume data is in decimal
  193. self.dump_memory(device_name, address, address2)
  194. elif cmd == "read":
  195. if len(parts) < 3:
  196. print("Usage: read <device_name> <address>")
  197. else:
  198. device_name = parts[1]
  199. address = int(parts[2], 16) # Assume address is in hex
  200. data = self.read_device(device_name, address)
  201. if data is not None:
  202. print(f"Data at {address:04X} on '{device_name}': {data}")
  203. elif cmd == "memw":
  204. address = int(parts[1], 16) if len(parts) > 1 else 0
  205. value = int(parts[2], 16)
  206. oldv = self.memory[address]
  207. if oldv == 235:
  208. print("You're trying to edit the boot sector data")
  209. print("editing it might result into breaking the vm PERMANENTLY")
  210. awns = input("Continue (Y/N)")
  211. if awns.lower() == "y":
  212. pass
  213. else:
  214. print("Canceling")
  215. return
  216. self.memory[address] = value
  217. print(f"Memory edited from: {oldv} to {value}")
  218. elif cmd == "help":
  219. print(f"-- Rah.py CPU Commands --")
  220. print(f"Lists of all the commands:")
  221. print(f"reg or registers - shows the registers and their data.")
  222. print(f"set [register] [value] - sets the specified register to the specified value.")
  223. print(f"mem [address] - shows the value of the specified address.")
  224. print(f"memw [address] [new value] - edits the value of the address in the rom. USE AT YOUR OWN RISK")
  225. print(f"send [opcode] - sends an opcode and handles it.")
  226. print(f"ip - shows the current ip in memory.")
  227. print(f"device [type] [name] - adds a device to the list")
  228. print(f"write [device] [address] [value: int] - writes data at a specific address")
  229. print(f"read [device] [address] - reads data at a specific address")
  230. print(f"dump [device] [address1] [address2] - dumps the data between the adresses")
  231. print(f"help - shows this list.")
  232. elif cmd == "ds1": # Pixel placement debugging
  233. self.debugPixelPlacement()
  234. elif cmd == "ds2": # Checkboard test
  235. self.checkboardTest()
  236. elif cmd == "ds3": # Stripes test 1
  237. self.vertical_stripes(10)
  238. elif cmd == "ds4": # Stripes test 2
  239. self.horizontal_stripes(10)
  240. elif cmd == "qds": # Update screen
  241. self.update_display()
  242. elif cmd == "rds":
  243. self.display_raw_memory()
  244. else:
  245. print(f"Unknown command: {cmd}")
  246.  
  247.  
  248.  
  249. def load_file(self, file_path, address):
  250. """Load a file into memory."""
  251. try:
  252. with open(file_path, 'rb') as f:
  253. data = f.read()
  254. for i, byte in enumerate(data):
  255. self.memory[address + i] = byte
  256. self.log(f"Loaded file '{file_path}' into memory at address {address:04X}")
  257. except FileNotFoundError:
  258. logging.error(f"Error: File '{file_path}' not found.")
  259. except Exception as e:
  260. logging.error(f"Error loading file: {e}")
  261.  
  262. def load_test_data(self):
  263. """Load some test data into memory for rendering."""
  264. # Example: Filling memory with a pattern of color indices
  265. for y in range(self.display_height):
  266. for x in range(self.display_width):
  267. self.memory[y * self.display_width + x] = (x + y) % 256 # Cycle through 256 colors
  268.  
  269. def execute(self, file):
  270. self.command_thread.start() # Start the command input thread
  271. while self.running:
  272. self.main_loop(file)
  273.  
  274. def main_loop(self, file):
  275. for event in pygame.event.get():
  276. if event.type == pygame.QUIT:
  277. self.running = False
  278.  
  279. if self.ip < len(self.memory):
  280. instruction = self.memory[self.ip]
  281. self.log(f"Executing instruction: {instruction:02X} at IP: {self.ip:04X}")
  282. self.handle_opcode(instruction)
  283. else:
  284. self.warn(f"Instruction pointer out of bounds: {self.ip}")
  285. self.ip = 0x7C00
  286.  
  287. self.calculate_fps()
  288. pygame.display.set_caption(f"Rah.py - {file} - {self.fps} FPS")
  289.  
  290. self.update_display()
  291. time.sleep(0.001) # Sleep to limit CPU usage and improve performance
  292.  
  293. def setup_display(self, file):
  294. pygame.init()
  295. # Set up display and surface for rendering
  296. self.screen = pygame.display.set_mode((self.display_width * 2, self.display_height * 2), pygame.SRCALPHA | pygame.HWSURFACE)
  297. self.frame_surface = pygame.Surface((self.display_width, self.display_height))
  298. pygame.display.set_caption(f"Rah.py - {file} - Loading...")
  299.  
  300. def handle_int_10(self, registers):
  301. """Handle BIOS interrupt 0x10 for video services."""
  302. function = registers['AL']
  303.  
  304. if function == 0x00: # Set Video Mode
  305. mode = registers['AH']
  306. if mode == 0x13: # Set 320x200x256 mode
  307. self.set_video_mode_13()
  308. elif mode == 0x03: # Set 80x25 text mode
  309. self.set_video_mode_03()
  310. else:
  311. print(f"Unsupported video mode: {mode}")
  312.  
  313. elif function == 0x0E: # Write Character and Attribute
  314. char = registers['AL']
  315. attr = registers['AH']
  316. self.write_character(char, attr)
  317.  
  318. # Add other functions as needed...
  319.  
  320. def update_display(self):
  321. # Create a back buffer for rendering
  322. back_buffer = pygame.Surface((self.display_width, self.display_height))
  323. pixel_array = pygame.PixelArray(back_buffer)
  324.  
  325. # Loop through the display memory and set pixel colors
  326. for y in range(self.display_height):
  327. for x in range(self.display_width):
  328. mem_offset = 0xA0000 + (y * self.display_width + x)
  329.  
  330. if mem_offset < len(self.memory):
  331. color_index = self.memory[mem_offset]
  332. else:
  333. color_index = 0 # Default to black
  334.  
  335. r, g, b = self.get_color(color_index)
  336. pixel_array[x, y] = (r << 16) | (g << 8) | b # Set pixel value
  337.  
  338. # Unlock the pixel array
  339. del pixel_array
  340.  
  341. # Render the back buffer to the screen
  342. self.screen.blit(back_buffer, (0, 0))
  343. pygame.display.flip()
  344.  
  345.  
  346.  
  347.  
  348.  
  349. def load_image_as_vga_memory(self, image_path):
  350. # Load an image file and convert it to grayscale VGA memory format
  351. #img = Image.open(image_path).convert("L").resize((self.display_width, self.display_height))
  352. #img_data = list(img.getdata())
  353. #return img_data # Return the pixel data as a list of grayscale color indices
  354. pass # This function was only for debug its useless for the emulator itself
  355.  
  356. def horizontal_stripes(self, stripe_height):
  357. """Create horizontal stripes across the screen."""
  358. vga_start = 0xA0000
  359. for y in range(self.display_height):
  360. for x in range(self.display_width):
  361. mem_offset = vga_start + (y * self.display_width + x)
  362. if mem_offset < len(self.memory):
  363. if (y // stripe_height) % 2 == 0:
  364. self.memory[mem_offset] = 0 # Set to black
  365. else:
  366. self.memory[mem_offset] = 15 # Set to white
  367.  
  368. def vertical_stripes(self, stripe_width):
  369. """Create vertical stripes across the screen."""
  370. vga_start = 0xA0000
  371. for y in range(self.display_height):
  372. for x in range(self.display_width):
  373. mem_offset = vga_start + (y * self.display_width + x)
  374. if mem_offset < len(self.memory):
  375. if (x // stripe_width) % 2 == 0:
  376. self.memory[mem_offset] = 0 # Set to black
  377. else:
  378. self.memory[mem_offset] = 15 # Set to white
  379.  
  380. def checkboardTest(self):
  381. for y in range(200): # Assuming height is 200
  382. for x in range(320): # Assuming width is 320
  383. # Alternate between two color indices for a checkerboard effect
  384. if (x // 32) % 2 == (y // 32) % 2: # Change 32 for the size of each square
  385. color_index = 0 # Black
  386. else:
  387. color_index = 15 # White (assuming 15 is a valid index for white in your palette)
  388.  
  389. # Set the color index in VGA memory
  390. self.memory[0xA0000 + (y * 320 + x)] = color_index
  391.  
  392. def debugPixelPlacement(self):
  393. # Initialize VGA memory with a simple color pattern for testing
  394. for i in range(320 * 200): # Fill a 320x200 framebuffer
  395. self.memory[0xA0000 + i] = i % 256 # Cycling through color indices 0-255
  396.  
  397. def display_raw_memory(self):
  398. """Display raw memory contents to visualize what's being written to video memory."""
  399. vga_start = 0xA0000
  400. for y in range(self.display_height):
  401. for x in range(self.display_width):
  402. mem_offset = vga_start + (y * self.display_width + x)
  403. if mem_offset < len(self.memory):
  404. color_index = self.memory[mem_offset]
  405. r, g, b = self.get_color(color_index) # Use your existing color mapping function
  406. self.frame_surface.set_at((x, y), (r, g, b))
  407.  
  408. self.screen.blit(self.frame_surface, (0, 0))
  409. pygame.display.flip()
  410.  
  411.  
  412.  
  413. def get_color(self, index):
  414. """Map color index to an RGB value."""
  415. if index < 64: # Grayscale shades
  416. gray_value = int(index * 4)
  417. return (gray_value, gray_value, gray_value)
  418. elif index < 128: # Red shades
  419. red_value = int((index - 64) * 4)
  420. return (red_value, 0, 0)
  421. elif index < 192: # Green shades
  422. green_value = int((index - 128) * 4)
  423. return (0, green_value, 0)
  424. else: # Blue shades
  425. blue_value = int((index - 192) * 4)
  426. return (0, 0, blue_value)
  427.  
  428. def handle_opcode(self, instruction): # This handles a custom made cpu, it should mostly work even on new oses but i wouldnt raccomend using it
  429. # Basic MOV instruction
  430. if instruction == 0xB8: # MOV EAX, imm32
  431. self.registers['eax'] = (
  432. self.memory[self.ip + 1] |
  433. (self.memory[self.ip + 2] << 8) |
  434. (self.memory[self.ip + 3] << 16) |
  435. (self.memory[self.ip + 4] << 24)
  436. )
  437. self.ip += 5
  438.  
  439. elif instruction == 0xE5: # MOV al, [address]
  440. address = (
  441. self.memory[self.ip + 1] |
  442. (self.memory[self.ip + 2] << 8)
  443. )
  444. self.registers['al'] = self.memory[address] # Move the value from memory to al
  445. self.ip += 3 # Increment IP by 3 to account for address bytes
  446.  
  447. elif instruction == 0xCD: # INT instruction
  448. self.handle_interrupt(self.memory[self.ip + 1])
  449. self.ip += 2
  450.  
  451. elif instruction == 0xE9: # JMP (absolute short)
  452. offset = self.memory[self.ip + 1] + (self.memory[self.ip + 2] << 8)
  453. self.ip += offset + 3
  454.  
  455. elif instruction == 0xEB: # JMP short (relative jump)
  456. offset = self.memory[self.ip + 1]
  457. self.ip += offset + 2
  458.  
  459. elif instruction == 0x00: # NOP
  460. self.ip += 1
  461.  
  462. elif instruction == 0xF4: # HLT
  463. self.log("Execution ending")
  464. self.running = False
  465.  
  466. elif instruction == 0xF3: # REP prefix (not implemented)
  467. self.ip += 1
  468.  
  469. elif instruction == 0xAA: # STOSB (store byte)
  470. color = self.registers['al']
  471. for _ in range(self.registers['cx']): # Store CX times
  472. if self.registers['edi'] < self.video_memory_start + (self.display_height * self.display_width):
  473. self.memory[self.video_memory_start + self.registers['edi']] = color
  474. self.registers['edi'] += 1
  475. self.ip += 1
  476.  
  477. # New Instructions
  478. elif instruction == 0x04: # ADD al, imm8
  479. immediate_value = self.memory[self.ip + 1]
  480. self.registers['al'] += immediate_value
  481. self.ip += 2
  482.  
  483. elif instruction == 0x05: # ADD EAX, imm32
  484. immediate_value = (
  485. self.memory[self.ip + 1] |
  486. (self.memory[self.ip + 2] << 8) |
  487. (self.memory[self.ip + 3] << 16) |
  488. (self.memory[self.ip + 4] << 24)
  489. )
  490. self.registers['eax'] += immediate_value
  491. self.ip += 5
  492.  
  493. elif instruction == 0x2C: # SUB al, imm8
  494. immediate_value = self.memory[self.ip + 1]
  495. self.registers['al'] -= immediate_value
  496. self.ip += 2
  497.  
  498. elif instruction == 0x2D: # SUB EAX, imm32
  499. immediate_value = (
  500. self.memory[self.ip + 1] |
  501. (self.memory[self.ip + 2] << 8) |
  502. (self.memory[self.ip + 3] << 16) |
  503. (self.memory[self.ip + 4] << 24)
  504. )
  505. self.registers['eax'] -= immediate_value
  506. self.ip += 5
  507.  
  508. elif instruction == 0x24: # AND al, imm8
  509. immediate_value = self.memory[self.ip + 1]
  510. self.registers['al'] &= immediate_value
  511. self.ip += 2
  512.  
  513. elif instruction == 0x25: # AND EAX, imm32
  514. immediate_value = (
  515. self.memory[self.ip + 1] |
  516. (self.memory[self.ip + 2] << 8) |
  517. (self.memory[self.ip + 3] << 16) |
  518. (self.memory[self.ip + 4] << 24)
  519. )
  520. self.registers['eax'] &= immediate_value
  521. self.ip += 5
  522.  
  523. elif instruction == 0x0C: # OR al, imm8
  524. immediate_value = self.memory[self.ip + 1]
  525. self.registers['al'] |= immediate_value
  526. self.ip += 2
  527.  
  528. elif instruction == 0x0D: # OR EAX, imm32
  529. immediate_value = (
  530. self.memory[self.ip + 1] |
  531. (self.memory[self.ip + 2] << 8) |
  532. (self.memory[self.ip + 3] << 16) |
  533. (self.memory[self.ip + 4] << 24)
  534. )
  535. self.registers['eax'] |= immediate_value
  536. self.ip += 5
  537.  
  538. elif instruction == 0x34: # XOR al, imm8
  539. immediate_value = self.memory[self.ip + 1]
  540. self.registers['al'] ^= immediate_value
  541. self.ip += 2
  542.  
  543. elif instruction == 0x35: # XOR EAX, imm32
  544. immediate_value = (
  545. self.memory[self.ip + 1] |
  546. (self.memory[self.ip + 2] << 8) |
  547. (self.memory[self.ip + 3] << 16) |
  548. (self.memory[self.ip + 4] << 24)
  549. )
  550. self.registers['eax'] ^= immediate_value
  551. self.ip += 5
  552.  
  553. elif instruction == 0x40: # INC EAX
  554. self.registers['eax'] += 1
  555. self.ip += 1
  556.  
  557. elif instruction == 0x48: # DEC EAX
  558. self.registers['eax'] -= 1
  559. self.ip += 1
  560.  
  561. elif instruction == 0x6A: # PUSH imm8
  562. value = self.memory[self.ip + 1]
  563. self.memory[self.registers['esp']] = value
  564. self.registers['esp'] -= 1
  565. self.ip += 2
  566.  
  567. elif instruction == 0x68: # PUSH imm32
  568. immediate_value = (
  569. self.memory[self.ip + 1] |
  570. (self.memory[self.ip + 2] << 8) |
  571. (self.memory[self.ip + 3] << 16) |
  572. (self.memory[self.ip + 4] << 24)
  573. )
  574. self.memory[self.registers['esp']] = immediate_value
  575. self.registers['esp'] -= 4
  576. self.ip += 5
  577.  
  578. elif instruction == 0x58: # POP EAX
  579. self.registers['eax'] = self.memory[self.registers['esp'] + 1]
  580. self.registers['esp'] += 4
  581. self.ip += 1
  582.  
  583. elif instruction == 0x74: # JZ (jump if zero)
  584. offset = self.memory[self.ip + 1]
  585. if self.registers['eax'] == 0:
  586. self.ip += offset + 2
  587. else:
  588. self.ip += 2
  589.  
  590. elif instruction == 0x75: # JNZ (jump if not zero)
  591. offset = self.memory[self.ip + 1]
  592. if self.registers['eax'] != 0:
  593. self.ip += offset + 2
  594. else:
  595. self.ip += 2
  596.  
  597. elif instruction == 0x3D: # CMP EAX, imm32
  598. immediate_value = (
  599. self.memory[self.ip + 1] |
  600. (self.memory[self.ip + 2] << 8) |
  601. (self.memory[self.ip + 3] << 16) |
  602. (self.memory[self.ip + 4] << 24)
  603. )
  604. self.registers['zero_flag'] = self.registers['eax'] == immediate_value
  605. self.registers['eax'] -= immediate_value # Set flags based on the result
  606. self.ip += 5
  607.  
  608. elif instruction == 0xA8: # TesT al, imm8
  609. immediate_value = self.memory[self.ip + 1]
  610. self.registers['zero_flag'] = (self.registers['al'] & immediate_value) == 0
  611. self.ip += 2
  612. elif instruction == 0x0E: # PUSH CS
  613. # Push the current value of the CS register onto the stack
  614. # CS is typically 0 in a simple emulator without segmentation
  615. cs_value = 0 # Set to the value of CS (0 in this case)
  616. self.memory[self.registers['esp']] = cs_value & 0xFF # Push low byte
  617. self.memory[self.registers['esp'] + 1] = (cs_value >> 8) & 0xFF # Push high byte
  618. self.registers['esp'] -= 2 # Decrement stack pointer by 2 bytes
  619. self.ip += 1 # Move to the next instruction
  620. elif instruction == 0x1F: # POP DS
  621. # Pop the top value from the stack into the DS register
  622. self.registers['ds'] = (
  623. self.memory[self.registers['esp'] + 1] << 8 | # High byte
  624. self.memory[self.registers['esp']] # Low byte
  625. )
  626. self.registers['esp'] += 2 # Increment esP to discard the popped value
  627. self.ip += 1
  628. elif instruction == 0xB9: # MOV ECX, imm32
  629. self.registers['ecx'] = (
  630. self.memory[self.ip + 1] |
  631. (self.memory[self.ip + 2] << 8) |
  632. (self.memory[self.ip + 3] << 16) |
  633. (self.memory[self.ip + 4] << 24)
  634. )
  635. self.ip += 5 # Move IP to the next instruction
  636. if instruction == 0xFE:
  637. sub_instruction = self.memory[self.ip + 1]
  638. if sub_instruction == 0xC0: # INC al
  639. self.registers['al'] += 1
  640. self.ip += 2
  641. elif sub_instruction == 0xC1: # INC CL
  642. self.registers['cl'] += 1
  643. self.ip += 2
  644. elif sub_instruction == 0xC2: # INC DL
  645. self.registers['dl'] += 1
  646. self.ip += 2
  647. elif sub_instruction == 0xC3: # INC BL
  648. self.registers['bl'] += 1
  649. self.ip += 2
  650. elif sub_instruction == 0xC4: # INC ah
  651. self.registers['ah'] += 1
  652. self.ip += 2
  653. elif sub_instruction == 0xC5: # INC CH
  654. self.registers['ch'] += 1
  655. self.ip += 2
  656. elif sub_instruction == 0xC6: # INC DH
  657. self.registers['dh'] += 1
  658. self.ip += 2
  659. elif sub_instruction == 0xC7: # INC BH
  660. self.registers['bh'] += 1
  661. self.ip += 2
  662. elif sub_instruction == 0xFC: # DEC al
  663. self.registers['al'] -= 1
  664. self.ip += 2
  665. else:
  666. self.warn(f"Unknown FE sub-instruction: {sub_instruction:02X}")
  667. self.ip += 1
  668. #self.running = False
  669. if instruction == 0xA4: # MOVSB
  670. # Move byte from DS:SI to es:DI
  671. byte_to_move = self.memory[self.registers['ds'] + self.registers['si']]
  672. self.memory[self.registers['es'] + self.registers['di']] = byte_to_move
  673.  
  674. # Increment SI and DI
  675. self.registers['si'] += 1
  676. self.registers['di'] += 1
  677.  
  678. # Increment IP to point to the next instruction
  679. self.ip += 1
  680. self.log(f"MOVSB: Moved byte {byte_to_move:02X} from DS:SI to es:DI")
  681. if instruction == 0xEA: # Far JMP
  682. # Read the offset and segment from memory
  683. offset_low = self.memory[self.ip + 1]
  684. offset_high = self.memory[self.ip + 2]
  685. segment_low = self.memory[self.ip + 3]
  686. segment_high = self.memory[self.ip + 4]
  687.  
  688. # Combine low and high bytes to form full offset and segment
  689. offset = (offset_high << 8) | offset_low
  690. segment = (segment_high << 8) | segment_low
  691.  
  692. # Set CS and IP for the far jump
  693. self.registers['cs'] = segment
  694. self.ip = offset
  695.  
  696. # Increment IP to point to the next instruction after the JMP
  697. self.ip += 5 # Move past the current instruction and its operands
  698. self.log(f"Far JMP to segment: {segment:04X}, offset: {offset:04X}")
  699. elif instruction == 0x7D: # JGE (Jump if Greater or Equal)
  700. offset = self.memory[self.ip + 1] # Get the offset for the jump
  701. # Check if the jump condition is met based on the Sign Flag (SF) and Overflow Flag (OF)
  702. if self.registers.get('sf', 0) == self.registers.get('of', 0):
  703. self.ip += offset + 2 # Jump to the relative address
  704. else:
  705. self.ip += 2 # Continue to the next instruction if condition not met
  706. elif instruction == 0xFD: # STD (Set Direction Flag)
  707. self.registers['df'] = 1 # Set the Direction Flag to 1
  708. self.ip += 1 # Move to the next instruction
  709. self.log("Direction Flag set to 1 (decrement)")
  710. elif instruction == 0xFF:
  711. sub_instruction = self.memory[self.ip + 1]
  712.  
  713. if sub_instruction == 0x00: # INC r/m32
  714. address = self.memory[self.ip + 2] | (self.memory[self.ip + 3] << 8) | (self.memory[self.ip + 4] << 16) | (self.memory[self.ip + 5] << 24)
  715. self.memory[address] += 1
  716. self.ip += 6
  717.  
  718. elif sub_instruction == 0x01: # DEC r/m32
  719. address = self.memory[self.ip + 2] | (self.memory[self.ip + 3] << 8) | (self.memory[self.ip + 4] << 16) | (self.memory[self.ip + 5] << 24)
  720. self.memory[address] -= 1
  721. self.ip += 6
  722.  
  723. elif sub_instruction == 0x02: # CalL r/m32
  724. address = self.memory[self.ip + 2] | (self.memory[self.ip + 3] << 8) | (self.memory[self.ip + 4] << 16) | (self.memory[self.ip + 5] << 24)
  725. # Push current IP onto stack
  726. self.memory[self.registers['esp']] = self.ip + 6 # Save the return address
  727. self.registers['esp'] -= 4 # Decrement stack pointer
  728. self.ip = address # Jump to the address
  729.  
  730. elif sub_instruction == 0x03: # JMP r/m32
  731. address = self.memory[self.ip + 2] | (self.memory[self.ip + 3] << 8) | (self.memory[self.ip + 4] << 16) | (self.memory[self.ip + 5] << 24)
  732. self.ip = address # Jump to the address
  733.  
  734. elif sub_instruction == 0x04: # POP r/m32
  735. address = self.memory[self.ip + 2] | (self.memory[self.ip + 3] << 8) | (self.memory[self.ip + 4] << 16) | (self.memory[self.ip + 5] << 24)
  736. # Pop value from stack into memory
  737. self.registers['esp'] += 4 # Increment stack pointer
  738. self.memory[address] = self.memory[self.registers['esp']] # Load the popped value into the address specified
  739. self.ip += 6
  740.  
  741. else:
  742. self.warn(f"Unknown FF sub-instruction: {sub_instruction:02X}")
  743. self.ip += 2 # Increment IP to skip the unknown instruction
  744. elif instruction == 0x0A:
  745. mod_rm_byte = self.memory[self.ip + 1]
  746.  
  747. # Decode the MOD-REG-R/M byte
  748. mod = (mod_rm_byte >> 6) & 0b11 # Bits 6-7
  749. reg = (mod_rm_byte >> 3) & 0b111 # Bits 3-5
  750. rm = mod_rm_byte & 0b111 # Bits 0-2
  751.  
  752. # Depending on the addressing mode (mod), fetch the operands
  753. if mod == 0b00:
  754. # No displacement (direct addressing)
  755. if rm == 0b110: # Addressing mode 110 means direct memory access
  756. address = self.memory[self.ip + 2] | (self.memory[self.ip + 3] << 8) # Example for 16-bit addressing
  757. operand1 = self.memory[address] # Memory operand
  758. operand2 = self.registers[reg] # Register operand
  759. result = operand1 | operand2
  760. self.memory[address] = result # Store result back to memory
  761. self.ip += 4 # Move instruction pointer to next instruction
  762.  
  763. else:
  764. # Handle other R/M addressing modes
  765. self.warn(f"Unsupported R/M mode for OR: {rm:03b}")
  766. self.ip += 2 # Skip this instruction
  767.  
  768. elif mod == 0b01:
  769. # Displacement of 8 bits
  770. if rm == 0b110:
  771. address = self.memory[self.ip + 2] + self.memory[self.ip + 3] # Example for 8-bit displacement
  772. operand1 = self.memory[address]
  773. operand2 = self.registers[reg]
  774. result = operand1 | operand2
  775. self.memory[address] = result
  776. self.ip += 4
  777.  
  778. elif mod == 0b10:
  779. # Displacement of 16 bits (or 32 bits, depending on your architecture)
  780. if rm == 0b110:
  781. address = self.memory[self.ip + 2] | (self.memory[self.ip + 3] << 8) # Example for 16-bit addressing
  782. operand1 = self.memory[address]
  783. operand2 = self.registers[reg]
  784. result = operand1 | operand2
  785. self.memory[address] = result
  786. self.ip += 4
  787.  
  788. else:
  789. self.warn(f"Unknown MOD value: {mod:02b}")
  790. self.ip += 2 # Increment IP to skip the unknown instruction
  791. elif instruction == 0x0F:
  792. # Fetch the next byte for the extended opcode
  793. extended_opcode = self.memory[self.ip + 1]
  794.  
  795. if extended_opcode == 0x01: # Example: LAR (Load Access Rights)
  796. mod_rm_byte = self.memory[self.ip + 2]
  797.  
  798. # Decode the MOD-REG-R/M byte
  799. mod = (mod_rm_byte >> 6) & 0b11 # Bits 6-7
  800. reg = (mod_rm_byte >> 3) & 0b111 # Bits 3-5
  801. rm = mod_rm_byte & 0b111 # Bits 0-2
  802.  
  803. # Handle addressing modes similar to the previous examples
  804. if mod == 0b00: # No displacement
  805. if rm == 0b110:
  806. address = self.memory[self.ip + 3] | (self.memory[self.ip + 4] << 8)
  807. # Load access rights (assuming some value from a descriptor table)
  808. operand = self.memory[address] # Example to fetch value
  809. self.registers[reg] = operand # Store the result in the register
  810. self.ip += 5 # Move instruction pointer forward
  811.  
  812. elif mod == 0b01: # 8-bit displacement
  813. # Implement this case accordingly
  814. pass
  815.  
  816. elif mod == 0b10: # 16-bit displacement
  817. # Implement this case accordingly
  818. pass
  819.  
  820. else:
  821. self.warn(f"Unknown MOD value: {mod:02b}")
  822. self.ip += 3 # Increment IP to skip the unknown instruction
  823.  
  824. elif extended_opcode == 0x20: # Example: MOVAPS
  825. # Handle MOVAPS self.logic here
  826. pass
  827.  
  828. elif extended_opcode == 0x22: # Example: MOVAPD
  829. # Handle MOVAPD self.logic here
  830. pass
  831.  
  832. # Add more extended opcodes as needed
  833. else:
  834. self.warn(f"Unsupported extended opcode: {extended_opcode:02x}")
  835. self.ip += 2 # Increment IP to skip this instruction
  836. elif instruction == 0x11: # ADC (Add with Carry)
  837. mod_rm_byte = self.memory[self.ip + 1]
  838.  
  839. # Decode the MOD-REG-R/M byte
  840. mod = (mod_rm_byte >> 6) & 0b11 # Bits 6-7
  841. reg = (mod_rm_byte >> 3) & 0b111 # Bits 3-5
  842. rm = mod_rm_byte & 0b111 # Bits 0-2
  843.  
  844. if mod == 0b00: # No displacement
  845. self.log("0bit")
  846. if rm == 0b110: # Direct addressing mode (address specified)
  847. address = self.memory[self.ip + 2] | (self.memory[self.ip + 3] << 8)
  848. value = self.memory[address] # Load value from memory
  849. result = self.registers[reg] + value + self.flags['carry'] # Add with carry
  850. self.registers[reg] = result & 0xFFFF # Store result back to register, assuming 16-bit
  851. self.update_flags(result) # Update flags based on result
  852. self.ip += 4 # Move instruction pointer forward
  853. self.ip += 2
  854.  
  855. elif mod == 0b01: # 8-bit displacement
  856. self.log("8bit")
  857. displacement = self.memory[self.ip + 2] # Fetch the displacement
  858. address = self.registers[rm] + displacement
  859. value = self.memory[address] # Load value from memory
  860. result = self.registers[reg] + value + self.flags['carry'] # Add with carry
  861. self.registers[reg] = result & 0xFFFF # Store result back to register, assuming 16-bit
  862. self.update_flags(result) # Update flags based on result
  863. self.ip += 3 # Move instruction pointer forward
  864.  
  865. elif mod == 0b10: # 16-bit displacement
  866. self.log("16bit")
  867. displacement = self.memory[self.ip + 2] | (self.memory[self.ip + 3] << 8) # Fetch the displacement
  868. address = self.registers[rm] + displacement
  869. value = self.memory[address] # Load value from memory
  870. result = self.registers[reg] + value + self.flags['carry'] # Add with carry
  871. self.registers[reg] = result & 0xFFFF # Store result back to register, assuming 16-bit
  872. self.update_flags(result) # Update flags based on result
  873. self.ip += 4 # Move instruction pointer forward
  874.  
  875. else:
  876. self.warn(f"Unknown MOD value: {mod:02b}")
  877. self.ip += 2 # Increment IP to skip the unknown instruction
  878. elif instruction == 0x13: # now there are all the interrupt calls it took so long to make them fr
  879. self.int_13h()
  880. elif instruction == 0x14:
  881. self.int_14h()
  882. self.ip += 1
  883. elif instruction == 0x15:
  884. self.int_15h()
  885. self.ip += 1
  886. elif instruction == 0x16:
  887. self.int_16h()
  888. self.ip += 1
  889. elif instruction == 0x17:
  890. self.int_17h()
  891. self.ip += 1
  892. elif instruction == 0x18:
  893. self.int_18h()
  894. self.ip += 1
  895. elif instruction == 0x19:
  896. self.int_19h() # it can finally boot
  897. #self.ip += 1
  898. elif instruction == 0x1A:
  899. self.int_1ah() # Setup system clock lox
  900. else:
  901. self.ip += 1
  902.  
  903. def int_13h(self):
  904. """Simulate INT 13h for disk operations."""
  905. # Assume the parameters are set for reading the first sector (for simplicity)
  906. # AX: Number of sectors to read (1 for simplicity)
  907. # CX: Cylinder/Head/Sector (assumed to be valid)
  908. # DX: Drive number (assumed to be 0)
  909. # bx: Destination segment:offset in memory to store the data
  910.  
  911. sector_count = self.registers['ax'] # Number of sectors to read
  912. destination_offset = (self.registers['bx'] & 0xFFFF) # Get offset from bx
  913.  
  914. # For simplicity, we read a predefined 'disk_data' into memory
  915. for i in range(sector_count):
  916. if destination_offset + i * 256 < len(self.memory):
  917. self.memory[destination_offset + i * 256 : destination_offset + (i + 1) * 256] = self.disk_data
  918. else:
  919. logging.error("Attempting to write out of memory bounds.")
  920. self.running = False
  921. return
  922.  
  923. # Update flags (in a real implementation, you would check for errors)
  924. self.registers['ah'] = 0 # Success (0)
  925. self.ip += 1
  926.  
  927. def int_14h(self):
  928. """Simulate INT 14h for serial communication."""
  929. # This is a simplified implementation; in a real emulator, you'd manage serial port state
  930. if self.registers['ah'] == 0x00: # Check for Serial Port Read
  931. # Simulate reading a character from the serial port
  932. # For demonstration, we'll use a simple self.logic to return a character
  933. # In a real implementation, you'd read from an actual buffer
  934. character = 0x41 # ASCII 'A' for example
  935. self.registers['al'] = character # Load character into al register
  936. self.registers['ah'] = 0 # Clear ah to indicate success
  937. elif self.registers['ah'] == 0x01: # Check for Serial Port Write
  938. # Simulate writing a character to the serial port
  939. # In a real implementation, you would write to a serial port buffer
  940. char_to_send = self.registers['al'] # Get character from al
  941. self.log(f"Sending character: {chr(char_to_send)}")
  942. self.registers['ah'] = 0 # Clear ah to indicate success
  943. else:
  944. self.warn("Unsupported ah value for INT 14h")
  945. self.registers['ah'] = 0xFF # Indicate an error
  946.  
  947. def int_15h(self):
  948. """Simulate INT 15h for extended services."""
  949. if self.registers['ah'] == 0x00: # Get System BIOS Information
  950. self.registers['ax'] = 0x0000 # Example value indicating the BIOS is present
  951. self.log("INT 15h: BIOS Information returned.")
  952.  
  953. elif self.registers['ah'] == 0x86: # Extended Memory Size (Get)
  954. # This returns the amount of extended memory available
  955. extended_memory_size = 0xFFFF # For example, return 64KB of extended memory
  956. self.registers['ax'] = extended_memory_size
  957. self.log(f"INT 15h: Extended memory size is {extended_memory_size} KB.")
  958.  
  959. elif self.registers['ah'] == 0x87: # Extended Memory Size (Get in KB)
  960. # This function returns the amount of extended memory in KB
  961. extended_memory_size_kb = 64 # Example: return 64KB
  962. self.registers['ax'] = extended_memory_size_kb
  963. self.log(f"INT 15h: Extended memory size in KB is {extended_memory_size_kb} KB.")
  964.  
  965. else:
  966. self.warn("Unsupported ah value for INT 15h")
  967. self.registers['ah'] = 0xFF # Indicate an error
  968.  
  969. def int_16h(self):
  970. """Simulate INT 16h for keyboard services."""
  971. if self.registers['ah'] == 0x00: # Read a character from the keyboard buffer
  972. if self.keyboard_buffer:
  973. # Read the character and remove it from the buffer
  974. char = self.keyboard_buffer.pop(0)
  975. self.registers['al'] = char # Store character in al
  976. self.registers['ah'] = 0x00 # Clear ah
  977. self.log(f"INT 16h: Read character '{chr(char)}'.")
  978. else:
  979. self.registers['ah'] = 0x01 # Indicate no character was read (error code)
  980. self.warn("INT 16h: No character in buffer.")
  981.  
  982. elif self.registers['ah'] == 0x01: # Check if a key has been pressed
  983. if self.keyboard_buffer:
  984. self.registers['zf'] = 0 # Zero flag clear, indicating key is available
  985. self.log("INT 16h: Key pressed.")
  986. else:
  987. self.registers['zf'] = 1 # Zero flag set, indicating no key pressed
  988. self.log("INT 16h: No key pressed.")
  989.  
  990. else:
  991. self.warn("Unsupported ah value for INT 16h")
  992. self.registers['ah'] = 0xFF # Indicate an error
  993.  
  994. def int_17h(self):
  995. """Simulate INT 17h for printer services."""
  996. if self.registers['ah'] == 0x00: # Print a character
  997. char = self.registers['al'] # Get character from al
  998. # Here you would send the character to the printer.
  999. # For simplicity, we will self.log the character instead.
  1000. self.log(f"INT 17h: Printing character '{(char)}'.")
  1001.  
  1002. # Simulate successful print operation
  1003. self.registers['ah'] = 0x00 # Clear ah to indicate success
  1004.  
  1005. elif self.registers['ah'] == 0x01: # Check if the printer is ready
  1006. # In a real scenario, check the printer status here.
  1007. self.registers['al'] = 0x01 # Set al to indicate printer is ready (1)
  1008. self.log("INT 17h: Printer is ready.")
  1009.  
  1010. else:
  1011. self.warn("Unsupported ah value for INT 17h")
  1012. self.registers['ah'] = 0xFF # Indicate an error
  1013.  
  1014. def int_18h(self):
  1015. """Simulate INT 18h for diskette services."""
  1016. if self.registers['ah'] == 0x00: # Read sectors
  1017. # Let's assume we're reading from a simulated disk.
  1018. # Normally we'd specify the drive, number of sectors, etc.
  1019.  
  1020. # Here you would implement the self.logic for reading sectors from a disk.
  1021. # For demonstration, let's simulate reading 1 sector into a buffer.
  1022. sectors_to_read = self.registers['al'] # Number of sectors to read
  1023. buffer_segment = self.registers['es'] # Segment where to store data
  1024. buffer_offset = self.registers['bx'] # Offset where to store data
  1025.  
  1026. self.log(f"INT 18h: Reading {sectors_to_read} sectors into segment:{buffer_segment} offset:{buffer_offset}.")
  1027.  
  1028. # Simulate reading sectors (you'd fill the buffer with data from the disk)
  1029. for i in range(sectors_to_read):
  1030. # Example: fill buffer with dummy data
  1031. self.memory[buffer_segment * 16 + buffer_offset + i] = i # Dummy data
  1032.  
  1033. self.registers['ah'] = 0x00 # Set ah to 0 to indicate success
  1034.  
  1035. else:
  1036. self.warn("Unsupported ah value for INT 18h")
  1037. self.registers['ah'] = 0xFF # Indicate an error
  1038.  
  1039. def int_19h(self):
  1040. """Simulate INT 19h for booting from a floppy disk."""
  1041. # Simulate loading the boot sector into memory at 0x7C00
  1042. boot_sector_size = 512 # Size of a boot sector
  1043. destination_offset = self.registers['bx'] # Destination offset
  1044.  
  1045. # Load simulated boot data into memory
  1046. if destination_offset < len(self.memory):
  1047. self.memory[destination_offset:destination_offset + boot_sector_size] = self.boot_data
  1048. self.log("INT 19h: Boot sector loaded into memory at offset 0x{:04X}.".format(destination_offset))
  1049. else:
  1050. logging.error("INT 19h: Attempting to write out of memory bounds.")
  1051. self.running = False
  1052. return
  1053.  
  1054. # Simulate setting up the boot process
  1055. self.registers['ah'] = 0 # Success (0)
  1056. self.log("INT 19h: Boot operation completed successfully.")
  1057.  
  1058. # Simulate moving to the next instruction
  1059. self.ip += 1
  1060.  
  1061. def int_1ah(self):
  1062. """Simulate INT 1Ah for getting the current system time."""
  1063. # Get current time
  1064. current_time = time.localtime()
  1065.  
  1066. # Fill registers with the current time
  1067. self.registers['ch'] = current_time.tm_hour # Hours
  1068. self.registers['cl'] = current_time.tm_min # Minutes
  1069. self.registers['dh'] = current_time.tm_sec # Seconds
  1070. self.registers['al'] = current_time.tm_sec // 10 # Tenths of seconds
  1071.  
  1072. self.log(f"INT 1Ah: Current Time Retrieved: {self.registers['ch']}:{self.registers['cl']}:{self.registers['dh']}."
  1073. f"{self.registers['al']} tenths of seconds.")
  1074.  
  1075. # Set carry flag to 0 (success)
  1076. self.registers['ah'] = 0
  1077.  
  1078. # Simulate moving to the next instruction
  1079. self.ip += 1
  1080.  
  1081. def run(self, file_path):
  1082. self.setup_display(file_path)
  1083. self.load_test_data() # Load test data before running the emulator
  1084. self.load_file(file_path, self.ip)
  1085. self.vgaMem = self.load_image_as_vga_memory(file_path)
  1086. self.execute(file_path)
  1087. pygame.quit()
  1088. sys.exit()
  1089.  
  1090.  
  1091. class VirtualDevice:
  1092. def __init__(self, name):
  1093. self.name = name
  1094. self.memory = bytearray(0x10000) # 64KB memory for the device
  1095.  
  1096. def read(self, address):
  1097. """Read a byte from the device's memory."""
  1098. return self.memory[address]
  1099.  
  1100. def write(self, address, value):
  1101. """Write a byte to the device's memory."""
  1102. self.memory[address] = value
  1103.  
  1104.  
  1105. if __name__ == "__main__":
  1106. parser = argparse.ArgumentParser(description="Rah.py")
  1107. parser.add_argument("file", help="Binary file to load into memory")
  1108. parser.add_argument("verbose", help="Enables self.logs")
  1109. args = parser.parse_args()
  1110.  
  1111. emulator = SimpleEmulator()
  1112. emulator.run(args.file)
  1113.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement