python 2to3 updates

This commit is contained in:
nik gaffney 2023-10-28 13:22:41 +02:00
parent 04a34eaf44
commit 6ace9e95b3
13 changed files with 522 additions and 211 deletions

View file

@ -18,16 +18,16 @@ fun to have an emulator 2000 lines of boring, by the book code. Would it? :-)
Additional Requirements
------------------------------
1. Pygame
2. Numpy
Instructions
-------------
Run with python orao.py, enter BASIC using "BC". When asked about the memory
size, press ENTER unless you don't want BASIC to use the entire available
memory.
memory.
WAV files containing programs / games should be located in the wav folder.
@ -37,7 +37,7 @@ will be created. Load it back using LOAD "filename".
DMEM "filename", ADDR, LENGTH can also be used to save a fragment of memory,
beginning at address ADDR and of length LENGTH (in decimal). LMEM "filename"
loads it back.
Clicking the POWER button does a warm reboot. You can then go back to BASIC
with BW to keep the memory intact, or re-initialize with BC. The enclosed PDF
describes the monitor mode and commands, but it is in Croatian only (Google
@ -62,7 +62,5 @@ Then press F8 to load and start program.
Known bugs
----------
- Pygame and Pulseaudio crash occasionally. - it seems that this
fork is less prone to lock-ups and **pygame*** crashes.
Please report any issues you have.
- Pygame and Pulseaudio crash occasionally. Please report any issues you have.
- no error checking for input `.prg` file

83
orao.py
View file

@ -1,5 +1,5 @@
#!/usr/bin/python2
# -*- coding: utf8 -*-
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pygame, numpy, sys, datetime
from orao.cpu import CPU
@ -27,7 +27,7 @@ pygame.init()
pygame.time.set_timer(pygame.USEREVENT + 1, 40)
# create CPU
cpu = CPU(bytearray([0xFF]*0xC000) + bytearray(open('ORAO13.ROM', 'rb').read()))
cpu = CPU(bytearray([0xFF] * 0xC000) + bytearray(open("ORAO13.ROM", "rb").read()))
cpu.channel = pygame.mixer.Channel(0)
cpu.store_mem_listeners.append(video_mem_listener)
cpu.store_mem_listeners.append(timer_mem_listener)
@ -38,39 +38,46 @@ chargen_init(cpu.memory[0xE000:])
view_cpu_state = CPUState()
# ram zero page & stack
view_zp = MicroMemView(start_addr=0x0000, size=0x0200, caption='ZP & stack', disp_width=64)
view_zp = MicroMemView(
start_addr=0x0000, size=0x0200, caption="ZP & stack", disp_width=64
)
view_zp.listen(cpu)
# user ram view
view_ram = MicroMemView(start_addr=0x0200, size=0x5E00, caption='RAM', disp_width=128)
view_ram = MicroMemView(start_addr=0x0200, size=0x5E00, caption="RAM", disp_width=128)
view_ram.listen(cpu)
# rom access view
view_rom = MicroMemView(start_addr=0xC000, size=0x4000, caption='ROM', disp_width=256)
view_rom = MicroMemView(start_addr=0xC000, size=0x4000, caption="ROM", disp_width=256)
view_rom.listen(cpu)
view_screen = MicroMemView(start_addr=0x6000, size=0x2000, disp_width=32)
view_screen.listen(cpu)
# status lines
status_line = pygame.Surface((64 * 8, 4*8), depth=24)
status_line = pygame.Surface((64 * 8, 4 * 8), depth=24)
status_line.fill((0, 0, 0))
chargen_draw_str(status_line, 0, 0, 'Orao Emulator v0.1')
chargen_draw_str(status_line, 0, 0, "Orao Emulator v0.1")
# setup screen
screen = pygame.display.set_mode((
terminal.get_width() * 2 + 2 + int(max(view_rom.width, view_cpu_state.width * 2)),
terminal.get_height() * 2 + 3*8 + 2 + 30
))
pygame.display.set_caption('Orao Emulator v0.1')
screen = pygame.display.set_mode(
(
terminal.get_width() * 2
+ 2
+ int(max(view_rom.width, view_cpu_state.width * 2)),
terminal.get_height() * 2 + 3 * 8 + 2 + 30,
)
)
pygame.display.set_caption("Orao Emulator v0.1")
lc = (0xff, 0xcc, 0x00)
chargen_draw_str(status_line, 0, 16, 'F12:', color=lc)
chargen_draw_str(status_line, 24+8, 16, ' SCREENSHOT')
lc = (0xFF, 0xCC, 0x00)
chargen_draw_str(status_line, 0, 16, "F12:", color=lc)
chargen_draw_str(status_line, 24 + 8, 16, " SCREENSHOT")
if MEM_LOAD_PRG is not None:
chargen_draw_str(status_line, 0, 24, 'F8:', color=lc)
chargen_draw_str(status_line, 24, 24, ' %s' % MEM_LOAD_PRG)
chargen_draw_str(status_line, 0, 24, "F8:", color=lc)
chargen_draw_str(status_line, 24, 24, " %s" % MEM_LOAD_PRG)
def render_frame(frame_time_ms):
view_cpu_state.render(cpu, frame_time_ms)
@ -82,8 +89,8 @@ def render_frame(frame_time_ms):
# blit
screen.fill((0, 0, 0))
screen.blit(pygame.transform.scale(terminal, (512, 512)), [0, 0])
chargen_draw_str(status_line, 0, 8, 'Speed: {0:.2f} MHz'.format(ratio))
screen.blit(status_line, [0, 512+1])
chargen_draw_str(status_line, 0, 8, "Speed: {0:.2f} MHz".format(ratio))
screen.blit(status_line, [0, 512 + 1])
x = 512 + 1
y = 0
cx, y = view_cpu_state.blit(screen, [x, y], scale=2)
@ -95,12 +102,13 @@ def render_frame(frame_time_ms):
y2 += 5
_, _ = view_rom.blit(screen, [x, y2], scale=1)
screen.blit(pygame.transform.scale(view_screen.read_map.surf, (512,512)), [0,0])
screen.blit(pygame.transform.scale(view_screen.write_map.surf, (512,512)), [0,0])
screen.blit(pygame.transform.scale(view_screen.read_map.surf, (512, 512)), [0, 0])
screen.blit(pygame.transform.scale(view_screen.write_map.surf, (512, 512)), [0, 0])
# finish rendering
pygame.display.flip()
clock = pygame.time.Clock()
while running:
@ -109,7 +117,7 @@ while running:
for i in range(5000):
cpu.step()
time_elapsed = (datetime.datetime.now()-before).microseconds + 1
time_elapsed = (datetime.datetime.now() - before).microseconds + 1
clock.tick()
for event in pygame.event.get():
@ -118,8 +126,8 @@ while running:
if event.type == pygame.MOUSEBUTTONDOWN:
x, y = event.pos
if 650 < x < 700 and 720 < y < 790: # Reset button
cpu.__init__(cpu.memory[:]) # Warm reset
if 650 < x < 700 and 720 < y < 790: # Reset button
cpu.__init__(cpu.memory[:]) # Warm reset
orao_kbd_listener(event, cpu)
@ -131,13 +139,13 @@ while running:
if pkeys[pygame.K_F8]:
if MEM_LOAD_PRG is None:
break
print("LOADING: %s" % MEM_LOAD_PRG)
print(("LOADING: %s" % MEM_LOAD_PRG))
ba = bytearray(open(MEM_LOAD_PRG, "rb").read())
# read load address
addr = ba[1] * 256 + ba[0]
ba = ba[2:]
print('Loadaddr: %04x' % addr)
print(("Loadaddr: %04x" % addr))
# load file to memory
for i in range(0, len(ba)):
@ -150,22 +158,33 @@ while running:
if pkeys[pygame.K_F12]:
now = datetime.datetime.now() # current date and time
pygame.image.save(screen, "assets/screenshot-%s.png" % now.strftime("%Y%m%d-%H%M%S"))
pygame.image.save(
screen, "assets/screenshot-%s.png" % now.strftime("%Y%m%d-%H%M%S")
)
if event.type == pygame.USEREVENT + 1:
render_frame(clock.get_time())
cpu.tape_out = None if cpu.cycles - cpu.last_sound_cycles > 20000 else cpu.tape_out
cpu.tape_out = (
None if cpu.cycles - cpu.last_sound_cycles > 20000 else cpu.tape_out
)
if len(cpu.sndbuf) > 4096 or cpu.sndbuf and cpu.cycles - cpu.last_sound_cycles > 20000:
if (
len(cpu.sndbuf) > 4096
or cpu.sndbuf
and cpu.cycles - cpu.last_sound_cycles > 20000
):
while cpu.channel.get_queue():
if time_elapsed > 10000: break
if time_elapsed > 10000:
break
cpu.channel.queue(pygame.sndarray.make_sound(numpy.uint8(cpu.sndbuf)))
cpu.sndbuf = []
overshoot = cpu.cycles - previous_loop_cycles - time_elapsed
pygame.time.wait((overshoot > 0) * overshoot // 1000) # Pričekaj da budemo cycle exact
pygame.time.wait(
(overshoot > 0) * overshoot // 1000
) # Pričekaj da budemo cycle exact
ratio = 1.0 * (cpu.cycles - previous_loop_cycles) / time_elapsed

View file

@ -1,22 +1,22 @@
# -*- coding: utf-8 -*-
chars = None
def chargen_init(char_data):
global chars
chars = char_data
global chars
chars = char_data
def chargen_draw_char(surf, px, py, ch, color=(0, 255, 0), bg=(0, 0, 0)):
char_loc = (ord(ch) - 32) * 8
for y in range(8):
c = chars[char_loc + y]
for x in range(8):
if (c >> x) & 1:
surf.set_at((px + x, py + y), color)
else:
surf.set_at((px + x, py + y), bg)
char_loc = (ord(ch) - 32) * 8
for y in range(8):
c = chars[char_loc + y]
for x in range(8):
if (c >> x) & 1:
surf.set_at((px + x, py + y), color)
else:
surf.set_at((px + x, py + y), bg)
def chargen_draw_str(surf, px, py, str, color=(0, 255, 0), bg=(0, 0, 0)):
for x in range(len(str)):
chargen_draw_char(surf, px + x * 8, py, str[x], color=color, bg=bg)
for x in range(len(str)):
chargen_draw_char(surf, px + x * 8, py, str[x], color=color, bg=bg)

View file

@ -1,61 +1,196 @@
# -*- coding: utf8 -*-
# -*- coding: utf-8 -*-
import wave
class CPU(object):
CARRY, ZERO, INTERRUPT, DECIMAL, BREAK, UNUSED, OVERFLOW, NEGATIVE = [2**i for i in range(8)]
CARRY, ZERO, INTERRUPT, DECIMAL, BREAK, UNUSED, OVERFLOW, NEGATIVE = [
2**i for i in range(8)
]
alphaarray = None
store_mem_listeners = []
read_mem_listeners = []
def __init__(self, memory):
s, self.tape_out, self.filename, self.samples = self, None, None, 0
s.memory, s.tape, s.flipflop, s.last_sound_cycles, s.sndbuf = memory, None, 0, -20000, []
s.memory, s.tape, s.flipflop, s.last_sound_cycles, s.sndbuf = (
memory,
None,
0,
-20000,
[],
)
s.cycles, s.pc, s.flags, s.sp, s.a, s.x, s.y = 0, 0xFF89, 48, 0xFF, 0, 0, 0
s.executed = []
s._opcodes = { 0x00:(s.BRK,s.no,7), 0x01:(s.ORA,s.ix,6), 0x05:(s.ORA,s.zp,3),
0x06:(s.ASL,s.zp,5), 0x08:(s.PHP,s.no,3), 0x09:(s.ORA,s.im,2), 0x0a:(s.ASL,s.no,2),
0x0d:(s.ORA,s.ab,4), 0x0e:(s.ASL,s.ab,6), 0x10:(s.BPL,s.re,2), 0x11:(s.ORA,s.iy,6),
0x15:(s.ORA,s.zx,4), 0x16:(s.ASL,s.zx,6), 0x18:(s.CLC,s.no,2), 0x19:(s.ORA,s.ay,5),
0x1d:(s.ORA,s.ax,5), 0x1e:(s.ASL,s.ax,7), 0x20:(s.JSR,s.jm,6), 0x21:(s.AND,s.ix,6),
0x24:(s.BIT,s.zp,3), 0x25:(s.AND,s.zp,3), 0x26:(s.ROL,s.zp,5), 0x28:(s.PLP,s.no,4),
0x29:(s.AND,s.im,2), 0x2a:(s.ROL,s.no,2), 0x2c:(s.BIT,s.ab,4), 0x2d:(s.AND,s.ab,4),
0x2e:(s.ROL,s.ab,6), 0x30:(s.BMI,s.re,2), 0x31:(s.AND,s.iy,6), 0x35:(s.AND,s.zx,4),
0x36:(s.ROL,s.zx,6), 0x38:(s.SEC,s.no,2), 0x39:(s.AND,s.ay,5), 0x3d:(s.AND,s.ax,5),
0x3e:(s.ROL,s.ax,7), 0x40:(s.RTI,s.no,6), 0x41:(s.EOR,s.ix,6), 0x45:(s.EOR,s.zp,3),
0x46:(s.LSR,s.zp,5), 0x48:(s.PHA,s.no,3), 0x49:(s.EOR,s.im,2), 0x4a:(s.LSR,s.no,2),
0x4c:(s.JMP,s.jm,3), 0x4d:(s.EOR,s.ab,4), 0x4e:(s.LSR,s.ab,6), 0x50:(s.BVC,s.re,2),
0x51:(s.EOR,s.iy,6), 0x55:(s.EOR,s.zx,4), 0x56:(s.LSR,s.zx,6), 0x58:(s.CLI,s.no,2),
0x59:(s.EOR,s.ay,5), 0x5d:(s.EOR,s.ax,5), 0x5e:(s.LSR,s.ax,7), 0x60:(s.RTS,s.no,6),
0x61:(s.ADC,s.ix,6), 0x65:(s.ADC,s.zp,3), 0x66:(s.ROR,s.zp,5), 0x68:(s.PLA,s.no,4),
0x69:(s.ADC,s.im,2), 0x6a:(s.ROR,s.no,2), 0x6c:(s.JMP,s.id,5), 0x6d:(s.ADC,s.ab,4),
0x6e:(s.ROR,s.ab,6), 0x70:(s.BVS,s.re,2), 0x71:(s.ADC,s.iy,6), 0x75:(s.ADC,s.zx,4),
0x76:(s.ROR,s.zx,6), 0x78:(s.SEI,s.no,2), 0x79:(s.ADC,s.ay,5), 0x7d:(s.ADC,s.ax,5),
0x7e:(s.ROR,s.ax,7), 0x81:(s.STA,s.ix,6), 0x84:(s.STY,s.zp,3), 0x85:(s.STA,s.zp,3),
0x86:(s.STX,s.zp,3), 0x88:(s.DEY,s.no,2), 0x8a:(s.TXA,s.no,2), 0x8c:(s.STY,s.ab,4),
0x8d:(s.STA,s.ab,4), 0x8e:(s.STX,s.ab,4), 0x90:(s.BCC,s.re,2), 0x91:(s.STA,s.iy,6),
0x94:(s.STY,s.zx,4), 0x95:(s.STA,s.zx,4), 0x96:(s.STX,s.zy,4), 0x98:(s.TYA,s.no,2),
0x99:(s.STA,s.ay,5), 0x9a:(s.TXS,s.no,2), 0x9d:(s.STA,s.ax,5), 0xa0:(s.LDY,s.im,2),
0xa1:(s.LDA,s.ix,6), 0xa2:(s.LDX,s.im,2), 0xa4:(s.LDY,s.zp,3), 0xa5:(s.LDA,s.zp,3),
0xa6:(s.LDX,s.zp,3), 0xa8:(s.TAY,s.no,2), 0xa9:(s.LDA,s.im,2), 0xaa:(s.TAX,s.no,2),
0xac:(s.LDY,s.ab,4), 0xad:(s.LDA,s.ab,4), 0xae:(s.LDX,s.ab,4), 0xb0:(s.BCS,s.re,2),
0xb1:(s.LDA,s.iy,6), 0xb4:(s.LDY,s.zx,4), 0xb5:(s.LDA,s.zx,4), 0xb6:(s.LDX,s.zy,4),
0xb8:(s.CLV,s.no,2), 0xb9:(s.LDA,s.ay,4), 0xba:(s.TSX,s.no,2), 0xbc:(s.LDY,s.ax,4),
0xbd:(s.LDA,s.ax,4), 0xbe:(s.LDX,s.ay,4), 0xc0:(s.CPY,s.im,2), 0xc1:(s.CMP,s.ix,6),
0xc4:(s.CPY,s.zp,3), 0xc5:(s.CMP,s.zp,3), 0xc6:(s.DEC,s.zp,5), 0xc8:(s.INY,s.no,2),
0xc9:(s.CMP,s.im,2), 0xca:(s.DEX,s.no,2), 0xcc:(s.CPY,s.ab,4), 0xcd:(s.CMP,s.ab,4),
0xce:(s.DEC,s.ab,6), 0xd0:(s.BNE,s.re,2), 0xd1:(s.CMP,s.iy,6), 0xd5:(s.CMP,s.zx,4),
0xd6:(s.DEC,s.zx,6), 0xd8:(s.CLD,s.no,2), 0xd9:(s.CMP,s.ay,5), 0xdd:(s.CMP,s.ax,5),
0xde:(s.DEC,s.ax,7), 0xe0:(s.CPX,s.im,2), 0xe1:(s.SBC,s.ix,6), 0xe4:(s.CPX,s.zp,3),
0xe5:(s.SBC,s.zp,3), 0xe6:(s.INC,s.zp,5), 0xe8:(s.INX,s.no,2), 0xe9:(s.SBC,s.im,2),
0xea:(s.NOP,s.no,2), 0xec:(s.CPX,s.ab,4), 0xed:(s.SBC,s.ab,4), 0xee:(s.INC,s.ab,6),
0xf0:(s.BEQ,s.re,2), 0xf1:(s.SBC,s.iy,6), 0xf5:(s.SBC,s.zx,4), 0xf6:(s.INC,s.zx,6),
0xf8:(s.SED,s.no,2), 0xf9:(s.SBC,s.ay,5), 0xfd:(s.SBC,s.ax,5), 0xfe:(s.INC,s.ax,7)}
s._opcodes = {
0x00: (s.BRK, s.no, 7),
0x01: (s.ORA, s.ix, 6),
0x05: (s.ORA, s.zp, 3),
0x06: (s.ASL, s.zp, 5),
0x08: (s.PHP, s.no, 3),
0x09: (s.ORA, s.im, 2),
0x0A: (s.ASL, s.no, 2),
0x0D: (s.ORA, s.ab, 4),
0x0E: (s.ASL, s.ab, 6),
0x10: (s.BPL, s.re, 2),
0x11: (s.ORA, s.iy, 6),
0x15: (s.ORA, s.zx, 4),
0x16: (s.ASL, s.zx, 6),
0x18: (s.CLC, s.no, 2),
0x19: (s.ORA, s.ay, 5),
0x1D: (s.ORA, s.ax, 5),
0x1E: (s.ASL, s.ax, 7),
0x20: (s.JSR, s.jm, 6),
0x21: (s.AND, s.ix, 6),
0x24: (s.BIT, s.zp, 3),
0x25: (s.AND, s.zp, 3),
0x26: (s.ROL, s.zp, 5),
0x28: (s.PLP, s.no, 4),
0x29: (s.AND, s.im, 2),
0x2A: (s.ROL, s.no, 2),
0x2C: (s.BIT, s.ab, 4),
0x2D: (s.AND, s.ab, 4),
0x2E: (s.ROL, s.ab, 6),
0x30: (s.BMI, s.re, 2),
0x31: (s.AND, s.iy, 6),
0x35: (s.AND, s.zx, 4),
0x36: (s.ROL, s.zx, 6),
0x38: (s.SEC, s.no, 2),
0x39: (s.AND, s.ay, 5),
0x3D: (s.AND, s.ax, 5),
0x3E: (s.ROL, s.ax, 7),
0x40: (s.RTI, s.no, 6),
0x41: (s.EOR, s.ix, 6),
0x45: (s.EOR, s.zp, 3),
0x46: (s.LSR, s.zp, 5),
0x48: (s.PHA, s.no, 3),
0x49: (s.EOR, s.im, 2),
0x4A: (s.LSR, s.no, 2),
0x4C: (s.JMP, s.jm, 3),
0x4D: (s.EOR, s.ab, 4),
0x4E: (s.LSR, s.ab, 6),
0x50: (s.BVC, s.re, 2),
0x51: (s.EOR, s.iy, 6),
0x55: (s.EOR, s.zx, 4),
0x56: (s.LSR, s.zx, 6),
0x58: (s.CLI, s.no, 2),
0x59: (s.EOR, s.ay, 5),
0x5D: (s.EOR, s.ax, 5),
0x5E: (s.LSR, s.ax, 7),
0x60: (s.RTS, s.no, 6),
0x61: (s.ADC, s.ix, 6),
0x65: (s.ADC, s.zp, 3),
0x66: (s.ROR, s.zp, 5),
0x68: (s.PLA, s.no, 4),
0x69: (s.ADC, s.im, 2),
0x6A: (s.ROR, s.no, 2),
0x6C: (s.JMP, s.id, 5),
0x6D: (s.ADC, s.ab, 4),
0x6E: (s.ROR, s.ab, 6),
0x70: (s.BVS, s.re, 2),
0x71: (s.ADC, s.iy, 6),
0x75: (s.ADC, s.zx, 4),
0x76: (s.ROR, s.zx, 6),
0x78: (s.SEI, s.no, 2),
0x79: (s.ADC, s.ay, 5),
0x7D: (s.ADC, s.ax, 5),
0x7E: (s.ROR, s.ax, 7),
0x81: (s.STA, s.ix, 6),
0x84: (s.STY, s.zp, 3),
0x85: (s.STA, s.zp, 3),
0x86: (s.STX, s.zp, 3),
0x88: (s.DEY, s.no, 2),
0x8A: (s.TXA, s.no, 2),
0x8C: (s.STY, s.ab, 4),
0x8D: (s.STA, s.ab, 4),
0x8E: (s.STX, s.ab, 4),
0x90: (s.BCC, s.re, 2),
0x91: (s.STA, s.iy, 6),
0x94: (s.STY, s.zx, 4),
0x95: (s.STA, s.zx, 4),
0x96: (s.STX, s.zy, 4),
0x98: (s.TYA, s.no, 2),
0x99: (s.STA, s.ay, 5),
0x9A: (s.TXS, s.no, 2),
0x9D: (s.STA, s.ax, 5),
0xA0: (s.LDY, s.im, 2),
0xA1: (s.LDA, s.ix, 6),
0xA2: (s.LDX, s.im, 2),
0xA4: (s.LDY, s.zp, 3),
0xA5: (s.LDA, s.zp, 3),
0xA6: (s.LDX, s.zp, 3),
0xA8: (s.TAY, s.no, 2),
0xA9: (s.LDA, s.im, 2),
0xAA: (s.TAX, s.no, 2),
0xAC: (s.LDY, s.ab, 4),
0xAD: (s.LDA, s.ab, 4),
0xAE: (s.LDX, s.ab, 4),
0xB0: (s.BCS, s.re, 2),
0xB1: (s.LDA, s.iy, 6),
0xB4: (s.LDY, s.zx, 4),
0xB5: (s.LDA, s.zx, 4),
0xB6: (s.LDX, s.zy, 4),
0xB8: (s.CLV, s.no, 2),
0xB9: (s.LDA, s.ay, 4),
0xBA: (s.TSX, s.no, 2),
0xBC: (s.LDY, s.ax, 4),
0xBD: (s.LDA, s.ax, 4),
0xBE: (s.LDX, s.ay, 4),
0xC0: (s.CPY, s.im, 2),
0xC1: (s.CMP, s.ix, 6),
0xC4: (s.CPY, s.zp, 3),
0xC5: (s.CMP, s.zp, 3),
0xC6: (s.DEC, s.zp, 5),
0xC8: (s.INY, s.no, 2),
0xC9: (s.CMP, s.im, 2),
0xCA: (s.DEX, s.no, 2),
0xCC: (s.CPY, s.ab, 4),
0xCD: (s.CMP, s.ab, 4),
0xCE: (s.DEC, s.ab, 6),
0xD0: (s.BNE, s.re, 2),
0xD1: (s.CMP, s.iy, 6),
0xD5: (s.CMP, s.zx, 4),
0xD6: (s.DEC, s.zx, 6),
0xD8: (s.CLD, s.no, 2),
0xD9: (s.CMP, s.ay, 5),
0xDD: (s.CMP, s.ax, 5),
0xDE: (s.DEC, s.ax, 7),
0xE0: (s.CPX, s.im, 2),
0xE1: (s.SBC, s.ix, 6),
0xE4: (s.CPX, s.zp, 3),
0xE5: (s.SBC, s.zp, 3),
0xE6: (s.INC, s.zp, 5),
0xE8: (s.INX, s.no, 2),
0xE9: (s.SBC, s.im, 2),
0xEA: (s.NOP, s.no, 2),
0xEC: (s.CPX, s.ab, 4),
0xED: (s.SBC, s.ab, 4),
0xEE: (s.INC, s.ab, 6),
0xF0: (s.BEQ, s.re, 2),
0xF1: (s.SBC, s.iy, 6),
0xF5: (s.SBC, s.zx, 4),
0xF6: (s.INC, s.zx, 6),
0xF8: (s.SED, s.no, 2),
0xF9: (s.SBC, s.ay, 5),
0xFD: (s.SBC, s.ax, 5),
0xFE: (s.INC, s.ax, 7),
}
s.ticks = {s.im: 1, s.zp: 1, s.zx: 1, s.zy: 1, s.ab: 2, s.ax: 2, s.no: 0,
s.ay: 2, s.jm: 2, s.id: 2, s.ix: 1, s.iy: 1, s.re: 1}
s.ticks = {
s.im: 1,
s.zp: 1,
s.zx: 1,
s.zy: 1,
s.ab: 2,
s.ax: 2,
s.no: 0,
s.ay: 2,
s.jm: 2,
s.id: 2,
s.ix: 1,
s.iy: 1,
s.re: 1,
}
s.addr_fmt = {
s.im: lambda addr: "#$%02X" % s.memory[addr],
@ -73,7 +208,8 @@ class CPU(object):
s.re: lambda addr: "$%04X" % (addr + self.byte2signed(s.memory[addr]) + 1),
}
def get_flag(self, flag): return self.flags & flag != 0
def get_flag(self, flag):
return self.flags & flag != 0
def set_flag(self, flag, boolean):
self.flags = self.flags | flag if boolean else self.flags & ~(flag)
@ -83,32 +219,38 @@ class CPU(object):
self.set_flag(self.NEGATIVE, src & 0x80)
return src
def get_word(self, addr): return 256 * self.get_byte(addr + 1) + self.get_byte(addr)
def get_word(self, addr):
return 256 * self.get_byte(addr + 1) + self.get_byte(addr)
def get_filename(self): return 'wav/{0}.WAV'.format(str(self.memory[592:602]).rstrip())
def get_filename(self):
return "wav/{0}.WAV".format(str(self.memory[592:602]).rstrip())
def speaker(self):
self.flipflop ^= 1
self.samples = int((self.cycles - self.last_sound_cycles) / 22.675)
if 1 < self.samples < 1000: # Ogranici frekventno podrucje
if 1 < self.samples < 1000: # Ogranici frekventno podrucje
self.sndbuf += [255] * self.samples + [0] * self.samples
self.last_sound_cycles = self.cycles
def get_byte(self, addr):
if addr == 0x87FF: # Adresa ulaza kasetofona
if addr == 0x87FF: # Adresa ulaza kasetofona
if not self.tape:
self.tape = (255*(ord(j)>128) for i in \
wave.open(self.get_filename()).readframes(2**24) for j in 2*i)
self.tape = (
255 * (ord(j) > 128)
for i in wave.open(self.get_filename()).readframes(2**24)
for j in 2 * i
)
try:
return self.tape.next()
return next(self.tape)
except StopIteration:
self.tape = None
return 0x00
if addr == 0x8800: self.speaker() # Zvucnik
if addr == 0x8800:
self.speaker() # Zvucnik
if addr is not None:
for listener in self.read_mem_listeners:
@ -117,11 +259,12 @@ class CPU(object):
return self.memory[addr] if addr is not None else self.a
def store_byte(self, addr, val):
if addr is None: # Akumulator
if addr is None: # Akumulator
self.a = val & 0xFF
return
if addr == 0x8800: self.speaker() # Zvucnik
if addr == 0x8800:
self.speaker() # Zvucnik
self.memory[addr] = val & 0xFF
@ -136,23 +279,49 @@ class CPU(object):
self.sp = (self.sp + 1) & 0xFF
return self.get_byte(256 + self.sp)
def stack_push_word(self, val): map(self.stack_push, [(val >> 8) & 0xFF, val & 0xFF])
def stack_push_word(self, val):
list(map(self.stack_push, [(val >> 8) & 0xFF, val & 0xFF]))
def stack_pop_word(self):
return self.stack_pop() + (self.stack_pop() << 8)
def stack_pop_word(self): return self.stack_pop() + (self.stack_pop() << 8)
###########################################################################
# Adresni nacini
def im(self): return self.pc
def zp(self): return self.get_byte(self.pc)
def zx(self): return (self.zp() + self.x) & 0xFF
def zy(self): return (self.zp() + self.y) & 0xFF
def ab(self): return self.get_word(self.pc)
def ax(self): return self._ab(self.x)
def ay(self): return self._ab(self.y)
def ix(self): return self.get_word((self.zp() + self.x) & 0xFF)
def iy(self): return (self.get_word(self.zp()) + self.y) & 0xFFFF
def id(self): return self.get_word(self.ab())
def jm(self): return self.ab()
def no(self): return None
def im(self):
return self.pc
def zp(self):
return self.get_byte(self.pc)
def zx(self):
return (self.zp() + self.x) & 0xFF
def zy(self):
return (self.zp() + self.y) & 0xFF
def ab(self):
return self.get_word(self.pc)
def ax(self):
return self._ab(self.x)
def ay(self):
return self._ab(self.y)
def ix(self):
return self.get_word((self.zp() + self.x) & 0xFF)
def iy(self):
return (self.get_word(self.zp()) + self.y) & 0xFFFF
def id(self):
return self.get_word(self.ab())
def jm(self):
return self.ab()
def no(self):
return None
def _ab(self, diff):
a1 = self.ab()
@ -171,39 +340,89 @@ class CPU(object):
###########################################################################
# Instrukcije
def TAX(self, d): self.x = self.set_nz(self.a)
def TXA(self, d): self.a = self.set_nz(self.x)
def TAY(self, d): self.y = self.set_nz(self.a)
def TYA(self, d): self.a = self.set_nz(self.y)
def TSX(self, d): self.x = self.set_nz(self.sp)
def TXS(self, d): self.sp = self.x
def TAX(self, d):
self.x = self.set_nz(self.a)
def LDA(self, addr): self.a = self.set_nz(self.get_byte(addr))
def LDX(self, addr): self.x = self.set_nz(self.get_byte(addr))
def LDY(self, addr): self.y = self.set_nz(self.get_byte(addr))
def STA(self, addr): self.store_byte(addr, self.a)
def STX(self, addr): self.store_byte(addr, self.x)
def STY(self, addr): self.store_byte(addr, self.y)
def TXA(self, d):
self.a = self.set_nz(self.x)
def AND(self, addr): self.a = self.set_nz(self.get_byte(addr) & self.a)
def ORA(self, addr): self.a = self.set_nz(self.get_byte(addr) | self.a)
def EOR(self, addr): self.a = self.set_nz(self.get_byte(addr) ^ self.a)
def TAY(self, d):
self.y = self.set_nz(self.a)
def CLC(self, d): self.set_flag(self.CARRY, False)
def SEC(self, d): self.set_flag(self.CARRY, True)
def CLD(self, d): self.set_flag(self.DECIMAL, False)
def SED(self, d): self.set_flag(self.DECIMAL, True)
def CLI(self, d): self.set_flag(self.INTERRUPT, False)
def SEI(self, d): self.set_flag(self.INTERRUPT, True)
def CLV(self, d): self.set_flag(self.OVERFLOW, False)
def TYA(self, d):
self.a = self.set_nz(self.y)
def INX(self, d): self.x = self.set_nz((self.x + 1) & 0xFF)
def INY(self, d): self.y = self.set_nz((self.y + 1) & 0xFF)
def DEX(self, d): self.x = self.set_nz((self.x - 1) & 0xFF)
def DEY(self, d): self.y = self.set_nz((self.y - 1) & 0xFF)
def TSX(self, d):
self.x = self.set_nz(self.sp)
def INC(self, addr): self.store_byte(addr, self.set_nz(self.get_byte(addr) + 1))
def DEC(self, addr): self.store_byte(addr, self.set_nz(self.get_byte(addr) - 1))
def TXS(self, d):
self.sp = self.x
def LDA(self, addr):
self.a = self.set_nz(self.get_byte(addr))
def LDX(self, addr):
self.x = self.set_nz(self.get_byte(addr))
def LDY(self, addr):
self.y = self.set_nz(self.get_byte(addr))
def STA(self, addr):
self.store_byte(addr, self.a)
def STX(self, addr):
self.store_byte(addr, self.x)
def STY(self, addr):
self.store_byte(addr, self.y)
def AND(self, addr):
self.a = self.set_nz(self.get_byte(addr) & self.a)
def ORA(self, addr):
self.a = self.set_nz(self.get_byte(addr) | self.a)
def EOR(self, addr):
self.a = self.set_nz(self.get_byte(addr) ^ self.a)
def CLC(self, d):
self.set_flag(self.CARRY, False)
def SEC(self, d):
self.set_flag(self.CARRY, True)
def CLD(self, d):
self.set_flag(self.DECIMAL, False)
def SED(self, d):
self.set_flag(self.DECIMAL, True)
def CLI(self, d):
self.set_flag(self.INTERRUPT, False)
def SEI(self, d):
self.set_flag(self.INTERRUPT, True)
def CLV(self, d):
self.set_flag(self.OVERFLOW, False)
def INX(self, d):
self.x = self.set_nz((self.x + 1) & 0xFF)
def INY(self, d):
self.y = self.set_nz((self.y + 1) & 0xFF)
def DEX(self, d):
self.x = self.set_nz((self.x - 1) & 0xFF)
def DEY(self, d):
self.y = self.set_nz((self.y - 1) & 0xFF)
def INC(self, addr):
self.store_byte(addr, self.set_nz(self.get_byte(addr) + 1))
def DEC(self, addr):
self.store_byte(addr, self.set_nz(self.get_byte(addr) - 1))
def ASL(self, addr):
operand = self.get_byte(addr)
@ -217,11 +436,20 @@ class CPU(object):
self.set_flag(self.NEGATIVE, op & self.NEGATIVE)
self.set_flag(self.OVERFLOW, op & self.OVERFLOW)
def PHP(self, d): self.stack_push(self.flags | self.BREAK | self.UNUSED)
def PHA(self, d): self.stack_push(self.a)
def PLP(self, d): self.flags = self.stack_pop()
def PLA(self, d): self.a = self.set_nz(self.stack_pop())
def NOP(self, d): pass
def PHP(self, d):
self.stack_push(self.flags | self.BREAK | self.UNUSED)
def PHA(self, d):
self.stack_push(self.a)
def PLP(self, d):
self.flags = self.stack_pop()
def PLA(self, d):
self.a = self.set_nz(self.stack_pop())
def NOP(self, d):
pass
def ROR(self, addr):
value = self.get_byte(addr) >> 1 | self.get_flag(self.CARRY) * 128
@ -240,14 +468,17 @@ class CPU(object):
self.SEI(0)
self.pc = self.get_word(0xFFFE) # IRQ
def JMP(self, addr): self.pc = addr - 2
def JMP(self, addr):
self.pc = addr - 2
def JSR(self, addr):
if addr == 0xE7B7 and self.pc > 0xC000: # samo i jedino ako rutinu poziva ROM
if addr == 0xE7B7 and self.pc > 0xC000: # samo i jedino ako rutinu poziva ROM
if not self.tape_out:
self.tape_out = wave.open(self.get_filename(), 'w')
self.tape_out.setparams((1, 1, 44100, 0, 'NONE', 'not compressed'))
self.tape_out.writeframes(chr(255 * self.flipflop) * (1 + (self.y > 15)) * 10)
self.tape_out = wave.open(self.get_filename(), "w")
self.tape_out.setparams((1, 1, 44100, 0, "NONE", "not compressed"))
self.tape_out.writeframes(
chr(255 * self.flipflop) * (1 + (self.y > 15)) * 10
)
self.stack_push_word((self.pc + 1) & 0xFFFF)
self.pc = addr - 2
@ -267,49 +498,80 @@ class CPU(object):
self.pc = (self.pc + 1) & 0xFFFF
def ADDITION(self, arg):
result = (arg & 0XFF) + self.a + self.get_flag(self.CARRY)
result = (arg & 0xFF) + self.a + self.get_flag(self.CARRY)
self.set_flag(self.OVERFLOW, (~(arg ^ self.a)) & (self.a ^ result) & 0x80)
self.set_flag(self.CARRY, result > 255)
self.a = self.set_nz(result) & 0xFF
def ADC(self, addr): self.ADDITION(self.get_byte(addr))
def SBC(self, addr): self.ADDITION(~self.get_byte(addr))
def ADC(self, addr):
self.ADDITION(self.get_byte(addr))
def SBC(self, addr):
self.ADDITION(~self.get_byte(addr))
def COMPARE(self, what, addr):
self.set_flag(self.CARRY, self.set_nz(what - self.get_byte(addr)) >= 0)
def CMP(self, addr): self.COMPARE(self.a, addr)
def CPX(self, addr): self.COMPARE(self.x, addr)
def CPY(self, addr): self.COMPARE(self.y, addr)
def CMP(self, addr):
self.COMPARE(self.a, addr)
def CPX(self, addr):
self.COMPARE(self.x, addr)
def CPY(self, addr):
self.COMPARE(self.y, addr)
def BRANCH(self, addr, flag, condition):
if self.get_flag(flag) is condition:
self.pc = addr
self.cycles += 1 # Ako se grana, to je 1 ekstra ciklus
self.cycles += 1 # Ako se grana, to je 1 ekstra ciklus
def BCS(self, addr):
self.BRANCH(addr, self.CARRY, True)
def BCS(self, addr): self.BRANCH(addr, self.CARRY, True)
def BCC(self, addr): self.BRANCH(addr, self.CARRY, False)
def BEQ(self, addr): self.BRANCH(addr, self.ZERO, True)
def BNE(self, addr): self.BRANCH(addr, self.ZERO, False)
def BMI(self, addr): self.BRANCH(addr, self.NEGATIVE, True)
def BPL(self, addr): self.BRANCH(addr, self.NEGATIVE, False)
def BVS(self, addr): self.BRANCH(addr, self.OVERFLOW, True)
def BVC(self, addr): self.BRANCH(addr, self.OVERFLOW, False)
def BCC(self, addr):
self.BRANCH(addr, self.CARRY, False)
def BEQ(self, addr):
self.BRANCH(addr, self.ZERO, True)
def BNE(self, addr):
self.BRANCH(addr, self.ZERO, False)
def BMI(self, addr):
self.BRANCH(addr, self.NEGATIVE, True)
def BPL(self, addr):
self.BRANCH(addr, self.NEGATIVE, False)
def BVS(self, addr):
self.BRANCH(addr, self.OVERFLOW, True)
def BVC(self, addr):
self.BRANCH(addr, self.OVERFLOW, False)
# dissasemble address
def disasm(self, addr):
instruction, addressing, cycles = self._opcodes[self.memory[addr]]
return "%04X %s %s" % (addr, instruction.__name__.lower(), self.addr_fmt[addressing](addr+1)), addr + self.ticks[addressing] + 1
return (
"%04X %s %s"
% (addr, instruction.__name__.lower(), self.addr_fmt[addressing](addr + 1)),
addr + self.ticks[addressing] + 1,
)
def step(self):
opcode = self.memory[self.pc]
if opcode not in self._opcodes:
print('HALT')
print("HALT")
for addr in self.executed:
code, _ = self.disasm(addr)
print(" - %s" % code)
print(" > %04x %02x %02x" % (self.pc, self.memory[self.pc], self.memory[self.pc+1]))
print((" - %s" % code))
print(
(
" > %04x %02x %02x"
% (self.pc, self.memory[self.pc], self.memory[self.pc + 1])
)
)
self.executed.append(self.pc)
self.pc = self.pc + 1 & 0xFFFF
@ -322,4 +584,3 @@ class CPU(object):
self.executed = self.executed[1:]
self.cycles += cycles

View file

@ -1,26 +1,37 @@
# -*- coding: utf8 -*-
# -*- coding: utf-8 -*-
# defines orao keyboard
import pygame, numpy
_kbd = {
0x83FE: (112, 240, 185, 39), 0x83FF: (45, 48), # [p đ š ;] [- 0]
0x85FE: (232, 230, 190, 43), 0x85FF: (8, 94), # [č ć ž :] [BS ^]
0x86FE: (102, 104, 103, 110), 0x86FF: (98, 118), # [f h g n] [b v]
0x877E: (100, 97, 115, 122), 0x877F: (120, 99), # [d a s z] [x c]
0x87BE: (108, 106, 107, 109), 0x87BF: (44, 46), # [l j k m] [, .]
0x87DE: (101, 113, 119, 49), 0x87DF: (50, 51), # [e q w l] [2 3]
0x87EE: (111, 105, 117, 55), 0x87EF: (56, 57), # [o i u 7] [8 9]
0x87F6: (114, 121, 116, 54), 0x87F7: (53, 52), # [r y t 6] [5 4]
0x87FA: (282, 283, 284, pygame.K_RCTRL), 0x87FD: (13, pygame.K_LCTRL), # [f1f2f3f4] [cr l_ctrl]
0x87FC: (pygame.K_LEFT, pygame.K_UP, pygame.K_DOWN, pygame.K_RIGHT),
0x87FB: (32, pygame.K_RSHIFT) # [spc l_shift]
0x83FE: (112, 240, 185, 39),
0x83FF: (45, 48), # [p đ š ;] [- 0]
0x85FE: (232, 230, 190, 43),
0x85FF: (8, 94), # [č ć ž :] [BS ^]
0x86FE: (102, 104, 103, 110),
0x86FF: (98, 118), # [f h g n] [b v]
0x877E: (100, 97, 115, 122),
0x877F: (120, 99), # [d a s z] [x c]
0x87BE: (108, 106, 107, 109),
0x87BF: (44, 46), # [l j k m] [, .]
0x87DE: (101, 113, 119, 49),
0x87DF: (50, 51), # [e q w l] [2 3]
0x87EE: (111, 105, 117, 55),
0x87EF: (56, 57), # [o i u 7] [8 9]
0x87F6: (114, 121, 116, 54),
0x87F7: (53, 52), # [r y t 6] [5 4]
0x87FA: (282, 283, 284, pygame.K_RCTRL),
0x87FD: (13, pygame.K_LCTRL), # [f1f2f3f4] [cr l_ctrl]
0x87FC: (pygame.K_LEFT, pygame.K_UP, pygame.K_DOWN, pygame.K_RIGHT),
0x87FB: (32, pygame.K_RSHIFT), # [spc l_shift]
}
def listener(event, cpu):
if event.type in [pygame.KEYDOWN, pygame.KEYUP]:
for address, keycodes in _kbd.iteritems():
keys = map(pygame.key.get_pressed().__getitem__, keycodes)
cpu.memory[address] = ~numpy.dot(keys, [16, 32, 64, 128][:len(keys)]) & 0xFF
def listener(event, cpu):
if event.type in [pygame.KEYDOWN, pygame.KEYUP]:
for address, keycodes in _kbd.items():
keys = list(map(pygame.key.get_pressed().__getitem__, keycodes))
cpu.memory[address] = (
~numpy.dot(keys, [16, 32, 64, 128][: len(keys)]) & 0xFF
)

View file

@ -1,14 +1,25 @@
# -*- coding: utf-8 -*-
timer = {}
timer_pos = {}
def mem_listener(addr, val, cpu):
if addr >= 0xa000 and addr <= 0xa0ff:
timer_ix = addr & 0xff
if timer_ix in timer:
print('timer(%3d:%04x-%04x):duration %d cy' % (timer_ix, timer_pos[timer_ix], cpu.pc-1, cpu.cycles - timer[timer_ix] - 4))
del timer[timer_ix]
del timer_pos[timer_ix]
else:
timer[timer_ix] = cpu.cycles
timer_pos[timer_ix] = cpu.pc+2
if addr >= 0xA000 and addr <= 0xA0FF:
timer_ix = addr & 0xFF
if timer_ix in timer:
print(
(
"timer(%3d:%04x-%04x):duration %d cy"
% (
timer_ix,
timer_pos[timer_ix],
cpu.pc - 1,
cpu.cycles - timer[timer_ix] - 4,
)
)
)
del timer[timer_ix]
del timer_pos[timer_ix]
else:
timer[timer_ix] = cpu.cycles
timer_pos[timer_ix] = cpu.pc + 2

View file

@ -1,11 +1,14 @@
# -*- coding: utf-8 -*-
import pygame
terminal = pygame.Surface((256, 256), pygame.SRCALPHA, depth=32)
terminal.fill((255, 255, 255))
alphaarray = pygame.surfarray.pixels_alpha(terminal)
def mem_listener(addr, val, cpu):
if 0x6000 <= addr <= 0x7FFF: # Video RAM
y, x = divmod((addr - 0x6000) * 8, 256)
for i in range(8):
alphaarray[x + i, y] = 255 if (val >> i) & 1 else 40 # Transparency mask
if 0x6000 <= addr <= 0x7FFF: # Video RAM
y, x = divmod((addr - 0x6000) * 8, 256)
for i in range(8):
alphaarray[x + i, y] = 255 if (val >> i) & 1 else 40 # Transparency mask

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
import pygame
from .view import View

View file

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
import pygame
from .view import View
from ..chargen import chargen_draw_str
class CPUState(View):
def __init__(self):
self.init_surface(pygame.Surface((2 * 8 * 8, 3 * 8), depth=24))
#self.set_smooth_scale()

View file

@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
import pygame
import numpy
from .view import View
@ -30,4 +32,3 @@ class MemHeatmap(View):
mem = cpu.memory[self.start_addr:self.start_addr + (w * h)]
arr = numpy.reshape(mem, (h, w))
pygame.surfarray.blit_array(self.surf, numpy.transpose(arr))

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from .heatmap import MemHeatmap
from .access_map import AccessMap

View file

@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
import pygame
from .view import View
from ..chargen import chargen_draw_str

View file

@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
import pygame
class View: