mirror of https://github.com/mnaberez/py65.git
Compare commits
9 Commits
9401f73807
...
b817d51a47
Author | SHA1 | Date |
---|---|---|
Ethan Sifferman | b817d51a47 | |
Mike Naberezny | 74e2576894 | |
Mike Naberezny | 9455a5c70e | |
Mike Naberezny | 1870d65982 | |
Mike Naberezny | 8dce37f6b8 | |
Mike Naberezny | 85ed46fd68 | |
Mike Naberezny | 2b837bbd4f | |
Mike Naberezny | 6ccdbe9c07 | |
Ethan Sifferman | 84e87293d7 |
|
@ -15,17 +15,20 @@ jobs:
|
|||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Show Python version
|
||||
run: python -V
|
||||
|
||||
- name: Install dependencies
|
||||
run: $PIP install setuptools
|
||||
run: $PIP install setuptools pexpect
|
||||
|
||||
- name: Run the tests
|
||||
run: python setup.py test -q
|
||||
|
||||
- name: Run the end-to-end tests
|
||||
run: END_TO_END=1 python setup.py test -q
|
||||
|
||||
build_py34:
|
||||
runs-on: ubuntu-20.04
|
||||
container: python:3.4
|
||||
|
@ -33,17 +36,25 @@ jobs:
|
|||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v3
|
||||
# does not work with actions/checkout@v4:
|
||||
# /usr/bin/docker exec 289170dbefc90d2ba94a09f3dcb70c42c1e1b29a5e877cd533b26ebf73ca82fa sh -c "cat /etc/*release | grep ^ID"
|
||||
# /__e/node20/bin/node: /lib/x86_64-linux-gnu/libm.so.6: version `GLIBC_2.27' not found (required by /__e/node20/bin/node)
|
||||
# /__e/node20/bin/node: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found (required by /__e/node20/bin/node)
|
||||
# /__e/node20/bin/node: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.25' not found (required by /__e/node20/bin/node)
|
||||
|
||||
- name: Show Python version
|
||||
run: python -V
|
||||
|
||||
- name: Install dependencies
|
||||
run: $PIP install setuptools
|
||||
run: $PIP install setuptools pexpect
|
||||
|
||||
- name: Run the tests
|
||||
run: python setup.py test -q
|
||||
|
||||
- name: Run the end-to-end tests
|
||||
run: END_TO_END=1 python setup.py test -q
|
||||
|
||||
build_py35:
|
||||
runs-on: ubuntu-20.04
|
||||
container: python:3.5
|
||||
|
@ -51,17 +62,20 @@ jobs:
|
|||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Show Python version
|
||||
run: python -V
|
||||
|
||||
- name: Install dependencies
|
||||
run: $PIP install setuptools
|
||||
run: $PIP install setuptools pexpect
|
||||
|
||||
- name: Run the tests
|
||||
run: python setup.py test -q
|
||||
|
||||
- name: Run the end-to-end tests
|
||||
run: END_TO_END=1 python setup.py test -q
|
||||
|
||||
build_py3x:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
@ -72,10 +86,10 @@ jobs:
|
|||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
|
@ -83,7 +97,10 @@ jobs:
|
|||
run: python -V
|
||||
|
||||
- name: Install dependencies
|
||||
run: $PIP install setuptools
|
||||
run: $PIP install setuptools pexpect
|
||||
|
||||
- name: Run the tests
|
||||
run: python setup.py test -q
|
||||
|
||||
- name: Run the end-to-end tests
|
||||
run: END_TO_END=1 python setup.py test -q
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
1.2.0.dev0 (Next Release)
|
||||
1.3.0.dev0 (Next Release)
|
||||
-------------------------
|
||||
|
||||
1.2.0 (2024-04-12)
|
||||
------------------
|
||||
|
||||
- Fixed a bug with character input that would cause characters to be
|
||||
dropped when pasting in larger amounts of text. This makes it possible
|
||||
to paste programs into EhBASIC and Taliforth. Patch by SamCoVT.
|
||||
|
||||
- Fixed interactive assembly on Python 3.
|
||||
|
||||
- Fixed regular expression warnings on Python 3.12.
|
||||
|
||||
- The ``fill`` command in the monitor now shows an error message if an
|
||||
address or value is out of range.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2008-2018, Mike Naberezny and contributors.
|
||||
Copyright (c) 2008-2024, Mike Naberezny and contributors.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
@ -49,7 +49,7 @@ copyright = u'2008-%d, Mike Naberezny and contributors' % year
|
|||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '1.1.0.dev0'
|
||||
version = '1.3.0.dev0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ class Assembler:
|
|||
and parsing the address part using AddressParser. The result of
|
||||
the normalization is a tuple of two strings (opcode, operand).
|
||||
"""
|
||||
statement = ' '.join(str.split(statement))
|
||||
statement = ' '.join(statement.split())
|
||||
|
||||
# normalize target in operand
|
||||
match = self.Statement.match(statement)
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import sys
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
|
||||
if PY2:
|
||||
unicode = unicode
|
||||
|
||||
def as_string(s, encoding='utf-8'):
|
||||
if isinstance(s, unicode):
|
||||
return s
|
||||
else:
|
||||
return s.decode(encoding)
|
||||
|
||||
else:
|
||||
unicode = str
|
||||
|
||||
def as_string(s, encoding='utf-8'):
|
||||
if isinstance(s, str):
|
||||
return s
|
||||
else:
|
||||
return s.decode(encoding)
|
|
@ -163,8 +163,8 @@ class Monitor(cmd.Cmd):
|
|||
result = cmd.Cmd.onecmd(self, line)
|
||||
except KeyboardInterrupt:
|
||||
self._output("Interrupt")
|
||||
except Exception as e:
|
||||
error = ''.join(traceback.format_exception(e))
|
||||
except Exception:
|
||||
error = ''.join(traceback.format_exception(*sys.exc_info()))
|
||||
self._output(error)
|
||||
|
||||
if not line.startswith("quit"):
|
||||
|
@ -239,7 +239,7 @@ class Monitor(cmd.Cmd):
|
|||
line = command
|
||||
break
|
||||
|
||||
pattern = '^%s\s+' % re.escape(shortcut)
|
||||
pattern = r'^%s\s+' % re.escape(shortcut)
|
||||
matches = re.match(pattern, line)
|
||||
if matches:
|
||||
start, end = matches.span()
|
||||
|
@ -507,19 +507,22 @@ class Monitor(cmd.Cmd):
|
|||
|
||||
if not breakpoints:
|
||||
while True:
|
||||
mpu.step()
|
||||
if mem[mpu.pc] in stopcodes:
|
||||
mpu.step()
|
||||
break
|
||||
mpu.step()
|
||||
else:
|
||||
while True:
|
||||
mpu.step()
|
||||
pc = mpu.pc
|
||||
if mem[pc] in stopcodes:
|
||||
mpu.step()
|
||||
break
|
||||
if pc in breakpoints:
|
||||
msg = "Breakpoint %d reached."
|
||||
self._output(msg % self._breakpoints.index(pc))
|
||||
mpu.step()
|
||||
break
|
||||
mpu.step()
|
||||
|
||||
# Switch back to the previous input mode.
|
||||
console.restore_mode()
|
||||
|
@ -580,7 +583,7 @@ class Monitor(cmd.Cmd):
|
|||
if args == '':
|
||||
return
|
||||
|
||||
pairs = re.findall('([^=,\s]*)=([^=,\s]*)', args)
|
||||
pairs = re.findall(r'([^=,\s]*)=([^=,\s]*)', args)
|
||||
if pairs == []:
|
||||
return self._output("Syntax error: %s" % args)
|
||||
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
import os
|
||||
import signal
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from py65.compat import unicode
|
||||
|
||||
# end-to-test tests are slow so only run them when asked
|
||||
if 'END_TO_END' in os.environ:
|
||||
if sys.platform == "win32":
|
||||
raise NotImplementedError()
|
||||
else:
|
||||
import pexpect
|
||||
BaseTestCase = unittest.TestCase
|
||||
else:
|
||||
BaseTestCase = object
|
||||
|
||||
|
||||
class EndToEndTests(BaseTestCase):
|
||||
|
||||
def _spawn(self):
|
||||
mon = pexpect.spawn(
|
||||
sys.executable,
|
||||
['-u', '-m', 'py65.monitor'],
|
||||
encoding='utf-8'
|
||||
)
|
||||
mon.expect_exact(unicode("Py65 Monitor"))
|
||||
self.addCleanup(mon.kill, signal.SIGINT)
|
||||
return mon
|
||||
|
||||
def test_putc(self):
|
||||
mon = self._spawn()
|
||||
|
||||
mon.sendline(unicode("add_label f001 putc"))
|
||||
|
||||
mon.sendline(unicode("a c000 lda #'H"))
|
||||
mon.sendline(unicode("a c002 sta putc"))
|
||||
mon.sendline(unicode("a c005 lda #'I"))
|
||||
mon.sendline(unicode("a c007 sta putc"))
|
||||
mon.sendline(unicode("a c00a brk"))
|
||||
|
||||
mon.sendline(unicode("g c000"))
|
||||
mon.expect_exact(unicode("HI"))
|
||||
mon.sendline(unicode("q"))
|
||||
|
||||
def test_getc(self):
|
||||
mon = self._spawn()
|
||||
|
||||
mon.sendline(unicode("add_label f004 getc"))
|
||||
|
||||
mon.sendline(unicode("a c000 ldx #0"))
|
||||
mon.sendline(unicode("a c002 lda getc"))
|
||||
mon.sendline(unicode("a c005 beq c002"))
|
||||
mon.sendline(unicode("a c007 cmp #'!"))
|
||||
mon.sendline(unicode("a c009 bne c00c"))
|
||||
mon.sendline(unicode("a c00b brk"))
|
||||
mon.sendline(unicode("a c00c sta 1000,x"))
|
||||
mon.sendline(unicode("a c00f inx"))
|
||||
mon.sendline(unicode("a c010 jmp c002"))
|
||||
|
||||
mon.sendline(unicode("g c000"))
|
||||
mon.send(unicode("HELLO!"))
|
||||
mon.expect_exact(unicode("6502:"))
|
||||
mon.sendline(unicode("m 1000:1004"))
|
||||
mon.expect_exact(unicode("48 45 4c 4c 4f"))
|
||||
|
||||
def test_assemble_interactive(self):
|
||||
mon = self._spawn()
|
||||
|
||||
mon.sendline(unicode("assemble 0"))
|
||||
mon.expect_exact(unicode("$0000"))
|
||||
|
||||
mon.sendline(unicode("lda $1234"))
|
||||
mon.expect_exact(unicode("ad 34 12"))
|
||||
|
||||
mon.expect_exact(unicode("$0003"))
|
||||
mon.sendline(unicode("sta $4567"))
|
||||
mon.expect_exact(unicode("8d 67 45"))
|
||||
|
||||
mon.sendline(unicode("invalid"))
|
||||
mon.expect_exact(unicode("?Syntax"))
|
||||
|
||||
mon.sendline()
|
||||
mon.sendline(unicode("quit"))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -61,7 +61,7 @@ class AddressParser(object):
|
|||
return self.labels[num]
|
||||
|
||||
else:
|
||||
matches = re.match('^([^\s+-]+)\s*([+\-])\s*([$+%]?\d+)$', num)
|
||||
matches = re.match(r'^([^\s+-]+)\s*([+\-])\s*([$+%]?\d+)$', num)
|
||||
if matches:
|
||||
label, sign, offset = matches.groups()
|
||||
|
||||
|
@ -88,7 +88,7 @@ class AddressParser(object):
|
|||
"""Parse a string containing an address or a range of addresses
|
||||
into a tuple of (start address, end address)
|
||||
"""
|
||||
matches = re.match('^([^:,]+)\s*[:,]+\s*([^:,]+)$', addresses)
|
||||
matches = re.match(r'^([^:,]+)\s*[:,]+\s*([^:,]+)$', addresses)
|
||||
if matches:
|
||||
start, end = map(self.number, matches.groups(0))
|
||||
else:
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import sys
|
||||
|
||||
from py65.compat import as_string
|
||||
|
||||
if sys.platform[:3] == "win":
|
||||
import msvcrt
|
||||
|
||||
|
@ -24,10 +26,7 @@ if sys.platform[:3] == "win":
|
|||
is available. Does not echo the character. The stdin argument is
|
||||
for function signature compatibility and is ignored.
|
||||
"""
|
||||
c = msvcrt.getch()
|
||||
if isinstance(c, bytes): # Python 3
|
||||
c = c.decode('latin-1')
|
||||
return c
|
||||
return as_string(msvcrt.getch())
|
||||
|
||||
def getch_noblock(stdin):
|
||||
""" Read one character from the Windows console without blocking.
|
||||
|
@ -36,8 +35,8 @@ if sys.platform[:3] == "win":
|
|||
available, an empty string is returned.
|
||||
"""
|
||||
if msvcrt.kbhit():
|
||||
return getch(stdin)
|
||||
return ''
|
||||
return as_string(getch(stdin))
|
||||
return u''
|
||||
|
||||
else:
|
||||
import termios
|
||||
|
@ -157,7 +156,7 @@ else:
|
|||
# use select to make sure there is at least one char to read.
|
||||
rd,wr,er = select([stdin], [], [], 0.01)
|
||||
if rd != []:
|
||||
char = stdin.read(1)
|
||||
char = as_string(stdin.read(1))
|
||||
except KeyboardInterrupt:
|
||||
# Pass along a CTRL-C interrupt.
|
||||
raise
|
||||
|
@ -180,7 +179,7 @@ else:
|
|||
# use select to make sure there is at least one char to read.
|
||||
rd,wr,er = select([stdin], [], [], 0.01)
|
||||
if rd != []:
|
||||
char = stdin.read(1)
|
||||
char = as_string(stdin.read(1))
|
||||
except KeyboardInterrupt:
|
||||
# Pass along a CTRL-C interrupt.
|
||||
raise
|
||||
|
@ -200,6 +199,7 @@ def line_input(prompt='', stdin=sys.stdin, stdout=sys.stdout):
|
|||
is useful in modes like the interactive assembler.
|
||||
"""
|
||||
stdout.write(prompt)
|
||||
stdout.flush()
|
||||
line = ''
|
||||
while True:
|
||||
char = getch(stdin)
|
||||
|
@ -211,6 +211,7 @@ def line_input(prompt='', stdin=sys.stdin, stdout=sys.stdout):
|
|||
line = line[:-1]
|
||||
stdout.write("\r%s\r%s%s" %
|
||||
(' ' * (len(prompt + line) + 5), prompt, line))
|
||||
stdout.flush()
|
||||
elif code == 0x1b: # escape
|
||||
pass
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue