mirror of
https://github.com/jtauber/applepy.git
synced 2024-12-29 00:30:44 +00:00
Merge branch 'pygame'
This commit is contained in:
commit
ecd9d00066
359
applepy.py
359
applepy.py
@ -3,7 +3,8 @@
|
||||
# originally written 2001, updated 2011
|
||||
|
||||
|
||||
import curses
|
||||
import pygame
|
||||
import colorsys
|
||||
|
||||
def signed(x):
|
||||
if x > 0x7F:
|
||||
@ -11,6 +12,232 @@ def signed(x):
|
||||
return x
|
||||
|
||||
|
||||
class Display:
|
||||
|
||||
characters = [
|
||||
[0b00000, 0b01110, 0b10001, 0b10101, 0b10111, 0b10110, 0b10000, 0b01111],
|
||||
[0b00000, 0b00100, 0b01010, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001],
|
||||
[0b00000, 0b11110, 0b10001, 0b10001, 0b11110, 0b10001, 0b10001, 0b11110],
|
||||
[0b00000, 0b01110, 0b10001, 0b10000, 0b10000, 0b10000, 0b10001, 0b01110],
|
||||
[0b00000, 0b11110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b11110],
|
||||
[0b00000, 0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b11111],
|
||||
[0b00000, 0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b10000],
|
||||
[0b00000, 0b01111, 0b10000, 0b10000, 0b10000, 0b10011, 0b10001, 0b01111],
|
||||
[0b00000, 0b10001, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001],
|
||||
[0b00000, 0b01110, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110],
|
||||
[0b00000, 0b00001, 0b00001, 0b00001, 0b00001, 0b00001, 0b10001, 0b01110],
|
||||
[0b00000, 0b10001, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010, 0b10001],
|
||||
[0b00000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111],
|
||||
[0b00000, 0b10001, 0b11011, 0b10101, 0b10101, 0b10001, 0b10001, 0b10001],
|
||||
[0b00000, 0b10001, 0b10001, 0b11001, 0b10101, 0b10011, 0b10001, 0b10001],
|
||||
[0b00000, 0b01110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110],
|
||||
[0b00000, 0b11110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000, 0b10000],
|
||||
[0b00000, 0b01110, 0b10001, 0b10001, 0b10001, 0b10101, 0b10010, 0b01101],
|
||||
[0b00000, 0b11110, 0b10001, 0b10001, 0b11110, 0b10100, 0b10010, 0b10001],
|
||||
[0b00000, 0b01110, 0b10001, 0b10000, 0b01110, 0b00001, 0b10001, 0b01110],
|
||||
[0b00000, 0b11111, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100],
|
||||
[0b00000, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110],
|
||||
[0b00000, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01010, 0b00100],
|
||||
[0b00000, 0b10001, 0b10001, 0b10001, 0b10101, 0b10101, 0b11011, 0b10001],
|
||||
[0b00000, 0b10001, 0b10001, 0b01010, 0b00100, 0b01010, 0b10001, 0b10001],
|
||||
[0b00000, 0b10001, 0b10001, 0b01010, 0b00100, 0b00100, 0b00100, 0b00100],
|
||||
[0b00000, 0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b11111],
|
||||
[0b00000, 0b11111, 0b11000, 0b11000, 0b11000, 0b11000, 0b11000, 0b11111],
|
||||
[0b00000, 0b00000, 0b10000, 0b01000, 0b00100, 0b00010, 0b00001, 0b00000],
|
||||
[0b00000, 0b11111, 0b00011, 0b00011, 0b00011, 0b00011, 0b00011, 0b11111],
|
||||
[0b00000, 0b00000, 0b00000, 0b00100, 0b01010, 0b10001, 0b00000, 0b00000],
|
||||
[0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111],
|
||||
[0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000],
|
||||
[0b00000, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00000, 0b00100],
|
||||
[0b00000, 0b01010, 0b01010, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000],
|
||||
[0b00000, 0b01010, 0b01010, 0b11111, 0b01010, 0b11111, 0b01010, 0b01010],
|
||||
[0b00000, 0b00100, 0b01111, 0b10100, 0b01110, 0b00101, 0b11110, 0b00100],
|
||||
[0b00000, 0b11000, 0b11001, 0b00010, 0b00100, 0b01000, 0b10011, 0b00011],
|
||||
[0b00000, 0b01000, 0b10100, 0b10100, 0b01000, 0b10101, 0b10010, 0b01101],
|
||||
[0b00000, 0b00100, 0b00100, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000],
|
||||
[0b00000, 0b00100, 0b01000, 0b10000, 0b10000, 0b10000, 0b01000, 0b00100],
|
||||
[0b00000, 0b00100, 0b00010, 0b00001, 0b00001, 0b00001, 0b00010, 0b00100],
|
||||
[0b00000, 0b00100, 0b10101, 0b01110, 0b00100, 0b01110, 0b10101, 0b00100],
|
||||
[0b00000, 0b00000, 0b00100, 0b00100, 0b11111, 0b00100, 0b00100, 0b00000],
|
||||
[0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00100, 0b00100, 0b01000],
|
||||
[0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000],
|
||||
[0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00100],
|
||||
[0b00000, 0b00000, 0b00001, 0b00010, 0b00100, 0b01000, 0b10000, 0b00000],
|
||||
[0b00000, 0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110],
|
||||
[0b00000, 0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110],
|
||||
[0b00000, 0b01110, 0b10001, 0b00001, 0b00110, 0b01000, 0b10000, 0b11111],
|
||||
[0b00000, 0b11111, 0b00001, 0b00010, 0b00110, 0b00001, 0b10001, 0b01110],
|
||||
[0b00000, 0b00010, 0b00110, 0b01010, 0b10010, 0b11111, 0b00010, 0b00010],
|
||||
[0b00000, 0b11111, 0b10000, 0b11110, 0b00001, 0b00001, 0b10001, 0b01110],
|
||||
[0b00000, 0b00111, 0b01000, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110],
|
||||
[0b00000, 0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b01000, 0b01000],
|
||||
[0b00000, 0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110],
|
||||
[0b00000, 0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b00010, 0b11100],
|
||||
[0b00000, 0b00000, 0b00000, 0b00100, 0b00000, 0b00100, 0b00000, 0b00000],
|
||||
[0b00000, 0b00000, 0b00000, 0b00100, 0b00000, 0b00100, 0b00100, 0b01000],
|
||||
[0b00000, 0b00010, 0b00100, 0b01000, 0b10000, 0b01000, 0b00100, 0b00010],
|
||||
[0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b11111, 0b00000, 0b00000],
|
||||
[0b00000, 0b01000, 0b00100, 0b00010, 0b00001, 0b00010, 0b00100, 0b01000],
|
||||
[0b00000, 0b01110, 0b10001, 0b00010, 0b00100, 0b00100, 0b00000, 0b00100]
|
||||
]
|
||||
|
||||
lores_colours = [
|
||||
(0, 0, 0), # black
|
||||
(208, 0, 48), # magenta / dark red
|
||||
(0, 0, 128), # dark blue
|
||||
(255, 0, 255), # purple / violet
|
||||
(0, 128, 0), # dark green
|
||||
(128, 128, 128), # gray 1
|
||||
(0, 0, 255), # medium blue / blue
|
||||
(96, 160, 255), # light blue
|
||||
(128, 80, 0), # brown / dark orange
|
||||
(255, 128 ,0), # orange
|
||||
(192, 192, 192), # gray 2
|
||||
(255, 144, 128), # pink / light red
|
||||
(0, 255, 0), # light green / green
|
||||
(255, 255, 0), # yellow / light orange
|
||||
(64, 255, 144), # aquamarine / light green
|
||||
(255, 255, 255), # white
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
self.screen = pygame.display.set_mode((560, 384))
|
||||
pygame.display.set_caption("ApplePy")
|
||||
self.mix = False
|
||||
|
||||
self.chargen = []
|
||||
for c in self.characters:
|
||||
chars = [[pygame.Surface((14, 16)), pygame.Surface((14, 16))],
|
||||
[pygame.Surface((14, 16)), pygame.Surface((14, 16))]]
|
||||
for colour in (0, 1):
|
||||
hue = (255, 255, 255) if colour else (0, 200, 0)
|
||||
for inv in (0, 1):
|
||||
pixels = pygame.PixelArray(chars[colour][inv])
|
||||
off = hue if inv else (0, 0, 0)
|
||||
on = (0, 0, 0) if inv else hue
|
||||
for row in range(8):
|
||||
b = c[row] << 1
|
||||
for col in range(7):
|
||||
bit = (b >> (6 - col)) & 1
|
||||
pixels[2 * col][2 * row] = on if bit else off
|
||||
pixels[2 * col + 1][2 * row] = on if bit else off
|
||||
del pixels
|
||||
self.chargen.append(chars)
|
||||
|
||||
def txtclr(self):
|
||||
self.text = False
|
||||
|
||||
def txtset(self):
|
||||
self.text = True
|
||||
self.colour = False
|
||||
|
||||
def mixclr(self):
|
||||
self.mix = False
|
||||
|
||||
def mixset(self):
|
||||
self.mix = True
|
||||
self.colour = True
|
||||
|
||||
def lowscr(self):
|
||||
self.page = 1
|
||||
|
||||
def hiscr(self):
|
||||
self.page = 2
|
||||
|
||||
def lores(self):
|
||||
self.high_res = False
|
||||
|
||||
def hires(self):
|
||||
self.high_res = True
|
||||
|
||||
def update(self, address, value):
|
||||
if self.page == 1:
|
||||
start_text = 0x400
|
||||
start_hires = 0x2000
|
||||
elif self.page == 2:
|
||||
start_text = 0x800
|
||||
start_hires = 0x4000
|
||||
else:
|
||||
return
|
||||
|
||||
if start_text <= address <= start_text + 0x3FF:
|
||||
base = address - start_text
|
||||
hi, lo = divmod(base, 0x80)
|
||||
row_group, column = divmod(lo, 0x28)
|
||||
row = hi + 8 * row_group
|
||||
|
||||
if row_group == 3:
|
||||
return
|
||||
|
||||
if self.text or not self.mix or not row < 20:
|
||||
mode, ch = divmod(value, 0x40)
|
||||
|
||||
inv = mode in (0, 1)
|
||||
|
||||
self.screen.blit(self.chargen[ch][self.colour][inv], (2 * (column * 7), 2 * (row * 8)))
|
||||
else:
|
||||
pixels = pygame.PixelArray(self.screen)
|
||||
if not self.high_res:
|
||||
lower, upper = divmod(value, 0x10)
|
||||
|
||||
for dx in range(14):
|
||||
for dy in range(8):
|
||||
x = column * 14 + dx
|
||||
y = row * 16 + dy
|
||||
pixels[x][y] = self.lores_colours[upper]
|
||||
for dy in range(8, 16):
|
||||
x = column * 14 + dx
|
||||
y = row * 16 + dy
|
||||
pixels[x][y] = self.lores_colours[lower]
|
||||
del pixels
|
||||
|
||||
elif start_hires <= address <= start_hires + 0x1FFF:
|
||||
if self.high_res:
|
||||
base = address - start_hires
|
||||
row8, b = divmod(base, 0x400)
|
||||
hi, lo = divmod(b, 0x80)
|
||||
row_group, column = divmod(lo, 0x28)
|
||||
row = 8 * (hi + 8 * row_group) + row8
|
||||
|
||||
if self.mix and row >= 160:
|
||||
return
|
||||
|
||||
if row < 192 and column < 40:
|
||||
|
||||
pixels = pygame.PixelArray(self.screen)
|
||||
msb = value // 0x80
|
||||
|
||||
for b in range(7):
|
||||
c = value & (1 << b)
|
||||
xx = (column * 7 + b)
|
||||
x = 2 * xx
|
||||
y = 2 * row
|
||||
|
||||
if msb:
|
||||
if xx % 2:
|
||||
pixels[x][y] = (0, 0, 0)
|
||||
# orange
|
||||
pixels[x + 1][y] = (255, 192, 0) if c else (0, 0, 0)
|
||||
else:
|
||||
# blue
|
||||
pixels[x][y] = (0, 128, 224) if c else (0, 0, 0)
|
||||
pixels[x + 1][y] = (0, 0, 0)
|
||||
else:
|
||||
if xx % 2:
|
||||
pixels[x][y] = (0, 0, 0)
|
||||
# green
|
||||
pixels[x + 1][y] = (0, 255, 0) if c else (0, 0, 0)
|
||||
else:
|
||||
# violet
|
||||
pixels[x][y] = (255, 0, 255) if c else (0, 0, 0)
|
||||
pixels[x + 1][y] = (0, 0, 0)
|
||||
|
||||
pixels[x][y + 1] = (0, 0, 0)
|
||||
pixels[x + 1][y + 1] = (0, 0, 0)
|
||||
|
||||
del pixels
|
||||
|
||||
|
||||
class RAM:
|
||||
|
||||
def __init__(self, start, size):
|
||||
@ -32,15 +259,36 @@ class RAM:
|
||||
|
||||
class SoftSwitches:
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, display):
|
||||
self.kbd = 0x00
|
||||
self.display = display
|
||||
|
||||
def read_byte(self, address):
|
||||
assert 0xC000 <= address <= 0xCFFF
|
||||
if address == 0xC000:
|
||||
return self.kbd
|
||||
if address == 0xC010:
|
||||
elif address == 0xC010:
|
||||
self.kbd = self.kbd & 0x7F
|
||||
elif address == 0xC030:
|
||||
pass # toggle speaker
|
||||
elif address == 0xC050:
|
||||
self.display.txtclr()
|
||||
elif address == 0xC051:
|
||||
self.display.txtset()
|
||||
elif address == 0xC052:
|
||||
self.display.mixclr()
|
||||
elif address == 0xC053:
|
||||
self.display.mixset()
|
||||
elif address == 0xC054:
|
||||
self.display.lowscr()
|
||||
elif address == 0xC055:
|
||||
self.display.hiscr()
|
||||
elif address == 0xC056:
|
||||
self.display.lores()
|
||||
elif address == 0xC057:
|
||||
self.display.hires()
|
||||
else:
|
||||
pass # print "%04X" % address
|
||||
return 0x00
|
||||
|
||||
|
||||
@ -67,14 +315,15 @@ class ROM:
|
||||
|
||||
class Memory:
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, display=None):
|
||||
self.display = display
|
||||
self.rom = ROM(0xD000, 0x3000)
|
||||
|
||||
# available from http://www.easy68k.com/paulrsm/6502/index.html
|
||||
self.rom.load_file(0xD000, "A2ROM.BIN")
|
||||
|
||||
self.ram = RAM(0x0000, 0xC000)
|
||||
self.softswitches = SoftSwitches()
|
||||
self.softswitches = SoftSwitches(display)
|
||||
|
||||
def load(self, address, data):
|
||||
if address < 0xC000:
|
||||
@ -100,34 +349,10 @@ class Memory:
|
||||
def write_byte(self, address, value):
|
||||
if address < 0xC000:
|
||||
self.ram.write_byte(address, value)
|
||||
if 0x400 <= address < 0x800:
|
||||
self.write_screen(address, value)
|
||||
|
||||
def write_screen(self, address, value):
|
||||
base = address - 0x400
|
||||
hi, lo = divmod(base, 0x80)
|
||||
row_group, column = divmod(lo, 0x28)
|
||||
row = hi + 8 * row_group
|
||||
|
||||
# skip if writing to row group 3
|
||||
if row_group == 3:
|
||||
return
|
||||
|
||||
c = chr(0x20 + ((value + 0x20) % 0x40))
|
||||
|
||||
if value < 0x40:
|
||||
attr = curses.A_DIM
|
||||
elif value < 0x80:
|
||||
attr = curses.A_REVERSE
|
||||
elif value < 0xA0:
|
||||
attr = curses.A_UNDERLINE
|
||||
else:
|
||||
attr = curses.A_DIM
|
||||
|
||||
try:
|
||||
self.win.addch(row, column, c, attr)
|
||||
except curses.error:
|
||||
pass
|
||||
if 0x400 <= address < 0x800 and display:
|
||||
self.display.update(address, value)
|
||||
if 0x2000 <= address < 0x5FFF and display:
|
||||
self.display.update(address, value)
|
||||
|
||||
|
||||
class Disassemble:
|
||||
@ -531,41 +756,13 @@ class CPU:
|
||||
def reset(self):
|
||||
self.program_counter = self.memory.read_word(self.RESET_VECTOR)
|
||||
|
||||
def dump(self, win, op):
|
||||
win.move(10, 50)
|
||||
win.clrtoeol()
|
||||
win.addstr(10, 50, self.disassemble.disasm(self.program_counter - 1))
|
||||
win.addstr(14, 50, "BUFFER:" +
|
||||
" ".join("%02X" % self.memory.read_byte(m) for m in range(0x200, 0x210))
|
||||
)
|
||||
win.addstr(11, 50, "A=%02X X=%02X Y=%02X S=%02X V=%02X B=%02X D=%02X I=%02X Z=%02X C=%02X PC=%04X S=%02X" % (
|
||||
self.accumulator,
|
||||
self.x_index,
|
||||
self.y_index,
|
||||
self.sign_flag,
|
||||
self.overflow_flag,
|
||||
self.break_flag,
|
||||
self.decimal_mode_flag,
|
||||
self.interrupt_disable_flag,
|
||||
self.zero_flag,
|
||||
self.carry_flag,
|
||||
self.program_counter - 1,
|
||||
self.stack_pointer))
|
||||
win.addstr(12, 50, "STACK:" +
|
||||
" ".join("%02X" % self.memory.read_byte(self.STACK_PAGE + i) for i in range(255, self.stack_pointer, -1))
|
||||
)
|
||||
|
||||
def run(self, win):
|
||||
self.memory.win = win
|
||||
win.clear()
|
||||
curses.noecho()
|
||||
win.nodelay(True)
|
||||
while True:
|
||||
def run(self):
|
||||
update_cycle = 0
|
||||
quit = False
|
||||
while not quit:
|
||||
op = self.read_pc_byte()
|
||||
# self.dump(win, op)
|
||||
func = self.ops[op]
|
||||
if func is None:
|
||||
curses.endwin()
|
||||
print "UNKNOWN OP"
|
||||
print hex(self.program_counter - 1)
|
||||
print hex(op)
|
||||
@ -573,18 +770,21 @@ class CPU:
|
||||
else:
|
||||
self.ops[op]()
|
||||
|
||||
try:
|
||||
key = ord(win.getkey())
|
||||
if key == 0xA:
|
||||
key = 0xD
|
||||
elif key == 0x7F:
|
||||
key = 0x8
|
||||
# win.addstr(15, 50, hex(key))
|
||||
self.memory.softswitches.kbd = 0x80 + key
|
||||
except curses.error:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
quit = True
|
||||
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.unicode:
|
||||
key = ord(event.unicode)
|
||||
if key == 0x7F:
|
||||
key = 0x08
|
||||
self.memory.softswitches.kbd = 0x80 + key
|
||||
|
||||
update_cycle += 1
|
||||
if update_cycle >= 1024:
|
||||
pygame.display.flip()
|
||||
update_cycle = 0
|
||||
|
||||
####
|
||||
|
||||
@ -967,7 +1167,8 @@ class CPU:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
mem = Memory()
|
||||
display = Display()
|
||||
mem = Memory(display)
|
||||
|
||||
cpu = CPU(mem)
|
||||
curses.wrapper(cpu.run)
|
||||
cpu.run()
|
||||
|
Loading…
Reference in New Issue
Block a user