mirror of
https://github.com/mnaberez/py65.git
synced 2024-09-06 10:54:32 +00:00
Merge branch 'master' of https://github.com/BigEd/py65
Conflicts: src/py65/assembler.py src/py65/disassembler.py src/py65/tests/devices/test_mpu6502.py
This commit is contained in:
commit
a991a7862c
235
examples/65Org16.boot.asm
Normal file
235
examples/65Org16.boot.asm
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
.LIST
|
||||||
|
|
||||||
|
; bootrom for py65 monitor in 65Org16 mode
|
||||||
|
; based on
|
||||||
|
; Intel Hex format loader by Ross Archer (9 February 2001, freeware)
|
||||||
|
; from http: http://www.6502.org/source/monitors/intelhex/intelhex.htm
|
||||||
|
;
|
||||||
|
|
||||||
|
; use this monitor like this:
|
||||||
|
; PYTHONPATH=. python py65/monitor.py -m 65Org16
|
||||||
|
; load bootrom.bin fe00
|
||||||
|
; goto fe00
|
||||||
|
|
||||||
|
START = $FFFFFe00
|
||||||
|
|
||||||
|
; I/O is memory-mapped in py65:
|
||||||
|
PUTC = $f001
|
||||||
|
GETC = $f005 ; blocking input
|
||||||
|
|
||||||
|
; Note that Hex format for 65Org16 uses ';' not ':' as the start of record mark
|
||||||
|
; also note that some fields are now composed of 16-bit elements:
|
||||||
|
; previously:
|
||||||
|
; length offset type data checksum
|
||||||
|
; :/08/E008/00/08090A0B0C0D0E0F/xx
|
||||||
|
; now
|
||||||
|
; ;/10/E008/00/00080009000A000B000C000D000E000F/xx
|
||||||
|
|
||||||
|
; Zero-page storage
|
||||||
|
DPL = $00 ; data pointer (two bytes) used by PUTSTRI
|
||||||
|
DPH = $01 ; high of data pointer
|
||||||
|
RECLEN = $02 ; record length in bytes
|
||||||
|
START_LO = $03
|
||||||
|
START_HI = $04
|
||||||
|
RECTYPE = $05
|
||||||
|
CHKSUM = $06 ; record checksum accumulator
|
||||||
|
DLFAIL = $07 ; flag for download failure
|
||||||
|
TEMP = $08 ; save hex value
|
||||||
|
TMPHEX = $09 ; save another hex value
|
||||||
|
|
||||||
|
; where the RAM program MUST have its first instruction
|
||||||
|
ENTRY_POINT = $0200
|
||||||
|
|
||||||
|
.ORG START
|
||||||
|
|
||||||
|
sei ; disable interrupts
|
||||||
|
cld ; binary mode arithmetic
|
||||||
|
ldx #$1FFFF ; Set up the stack pointer
|
||||||
|
txs ; "
|
||||||
|
|
||||||
|
; Download Intel hex. The program you download MUST have its entry
|
||||||
|
; instruction (even if only a jump to somewhere else) at ENTRY_POINT.
|
||||||
|
HEXDNLD lda #0
|
||||||
|
sta START_HI ; store all programs in bank 0 (page 0) for now
|
||||||
|
sta DLFAIL ;Start by assuming no D/L failure
|
||||||
|
jsr PUTSTRI
|
||||||
|
.byte 13,10,13,10
|
||||||
|
.byte "Send 65Org16 code in"
|
||||||
|
.byte " variant Intel Hex format"
|
||||||
|
.byte " at 19200,n,8,1 ->"
|
||||||
|
.byte 13,10
|
||||||
|
.byte 0 ; Null-terminate unless you prefer to crash.
|
||||||
|
HDWRECS jsr GETSER ; Wait for start of record mark ';'
|
||||||
|
cmp #';'
|
||||||
|
bne HDWRECS ; not found yet
|
||||||
|
; Start of record marker has been found
|
||||||
|
lda #0
|
||||||
|
sta CHKSUM
|
||||||
|
jsr GETHEX ; Get the record length
|
||||||
|
sta RECLEN ; save it
|
||||||
|
jsr GET4HX ; Get the 16-bit offset
|
||||||
|
sta START_LO
|
||||||
|
jsr GETHEX ; Get the record type
|
||||||
|
sta RECTYPE ; & save it
|
||||||
|
bne HDER1 ; end-of-record
|
||||||
|
ldx RECLEN ; number of data bytes to write to memory
|
||||||
|
ldy #0 ; start offset at 0
|
||||||
|
HDLP1 jsr GET4HX ; Get the first/next/last data word
|
||||||
|
sta (START_LO),y ; Save it to RAM
|
||||||
|
iny ; update data pointer
|
||||||
|
dex ; decrement character count
|
||||||
|
dex ; ... twice
|
||||||
|
bne HDLP1
|
||||||
|
jsr GETHEX ; get the checksum
|
||||||
|
lda CHKSUM
|
||||||
|
bne HDDLF1 ; If failed, report it
|
||||||
|
; Another successful record has been processed
|
||||||
|
lda #'#' ; Character indicating record OK = '#'
|
||||||
|
sta PUTC ; write it out but don't wait for output
|
||||||
|
jmp HDWRECS ; get next record
|
||||||
|
HDDLF1 lda #'F' ; Character indicating record failure = 'F'
|
||||||
|
sta DLFAIL ; download failed if non-zero
|
||||||
|
sta PUTC ; write it to transmit buffer register
|
||||||
|
jmp HDWRECS ; wait for next record start
|
||||||
|
HDER1 cmp #1 ; Check for end-of-record type
|
||||||
|
beq HDER2
|
||||||
|
jsr PUTSTRI ; Warn user of unknown record type
|
||||||
|
.byte 13,10,13,10
|
||||||
|
.byte "Unknown record type $"
|
||||||
|
.byte 0 ; null-terminate unless you prefer to crash!
|
||||||
|
lda RECTYPE ; Get it
|
||||||
|
sta DLFAIL ; non-zero --> download has failed
|
||||||
|
jsr PUTHEX ; print it
|
||||||
|
lda #13 ; but we'll let it finish so as not to
|
||||||
|
jsr PUTSER ; falsely start a new d/l from existing
|
||||||
|
lda #10 ; file that may still be coming in for
|
||||||
|
jsr PUTSER ; quite some time yet.
|
||||||
|
jmp HDWRECS
|
||||||
|
; We've reached the end-of-record record
|
||||||
|
HDER2 jsr GETHEX ; get the checksum
|
||||||
|
lda CHKSUM ; Add previous checksum accumulator value
|
||||||
|
beq HDER3 ; checksum = 0 means we're OK!
|
||||||
|
jsr PUTSTRI ; Warn user of bad checksum
|
||||||
|
.byte 13,10,13,10
|
||||||
|
.byte "Bad record checksum!",13,10
|
||||||
|
.byte 0 ; Null-terminate or 6502 go bye-bye
|
||||||
|
jmp START
|
||||||
|
HDER3 lda DLFAIL
|
||||||
|
beq HDEROK
|
||||||
|
;A download failure has occurred
|
||||||
|
jsr PUTSTRI
|
||||||
|
.byte 13,10,13,10
|
||||||
|
.byte "Download Failed",13,10
|
||||||
|
.byte "Aborting!",13,10
|
||||||
|
.byte 0 ; null-terminate every string yada yada.
|
||||||
|
jmp START
|
||||||
|
HDEROK jsr PUTSTRI
|
||||||
|
.byte 13,10,13,10
|
||||||
|
.byte "Download Successful!",13,10
|
||||||
|
.byte "Jumping to location $"
|
||||||
|
.byte 0 ; by now, I figure you know what this is for. :)
|
||||||
|
lda #HI(ENTRY_POINT) ; Print the entry point in hex
|
||||||
|
jsr PUTHEX
|
||||||
|
lda #LO(ENTRY_POINT)
|
||||||
|
jsr PUTHEX
|
||||||
|
jsr PUTSTRI
|
||||||
|
.byte 13,10
|
||||||
|
.byte 0 ; stop lemming-like march of the program ctr. thru data
|
||||||
|
jmp ENTRY_POINT ; jump to canonical entry point
|
||||||
|
|
||||||
|
; For py65, the input routine will block until a character arrives
|
||||||
|
GETSER lda GETC
|
||||||
|
rts
|
||||||
|
|
||||||
|
; get four ascii chars, adding both octets into the checksum
|
||||||
|
GET4HX jsr GETHEX
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
sta TMPHEX
|
||||||
|
jsr GETHEX
|
||||||
|
ora TMPHEX
|
||||||
|
rts
|
||||||
|
|
||||||
|
; get two ascii chars, add into the checksum
|
||||||
|
GETHEX jsr GETSER
|
||||||
|
jsr MKNIBL ; Convert to 0..F numeric
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
asl a ; This is the upper nibble
|
||||||
|
and #$F0
|
||||||
|
sta TEMP
|
||||||
|
jsr GETSER
|
||||||
|
jsr MKNIBL
|
||||||
|
ora TEMP
|
||||||
|
sta TEMP
|
||||||
|
clc
|
||||||
|
adc CHKSUM ; Add in the checksum
|
||||||
|
and #$ff
|
||||||
|
sta CHKSUM ;
|
||||||
|
lda TEMP
|
||||||
|
rts ; return with the nibble received
|
||||||
|
|
||||||
|
; Convert the ASCII nibble to numeric value from 0-F:
|
||||||
|
MKNIBL cmp #'9'+1 ; See if it's 0-9 or 'A'..'F' (no lowercase yet)
|
||||||
|
bcc MKNNH ; If we borrowed, we lost the carry so 0..9
|
||||||
|
sbc #7+1 ; Subtract off extra 7 (sbc subtracts off one less)
|
||||||
|
; If we fall through, carry is set unlike direct entry at MKNNH
|
||||||
|
MKNNH sbc #'0'-1 ; subtract off '0' (if carry clear coming in)
|
||||||
|
and #$0F ; no upper nibble no matter what
|
||||||
|
rts ; and return the nibble
|
||||||
|
|
||||||
|
; Put byte in A as hexydecascii
|
||||||
|
PUTHEX pha ;
|
||||||
|
lsr a
|
||||||
|
lsr a
|
||||||
|
lsr a
|
||||||
|
lsr a
|
||||||
|
jsr PRNIBL
|
||||||
|
pla
|
||||||
|
PRNIBL and #$0F ; strip off the low nibble
|
||||||
|
cmp #$0A
|
||||||
|
bcc NOTHEX ; if it's 0-9, add '0' else also add 7
|
||||||
|
adc #6 ; Add 7 (6+carry=1), result will be carry clear
|
||||||
|
NOTHEX adc #'0' ; If carry clear, we're 0-9
|
||||||
|
; Write the character in A as ASCII:
|
||||||
|
PUTSER sta PUTC
|
||||||
|
rts
|
||||||
|
|
||||||
|
;Put the string following in-line until a NULL out to the console
|
||||||
|
PUTSTRI pla ; Get the low part of "return" address (data start address)
|
||||||
|
sta DPL
|
||||||
|
pla
|
||||||
|
sta DPH ; Get the high part of "return" address
|
||||||
|
; (data start address)
|
||||||
|
; Note: actually we're pointing one short
|
||||||
|
PSINB ldy #1
|
||||||
|
lda (DPL),y ; Get the next string character
|
||||||
|
inc DPL ; update the pointer
|
||||||
|
bne PSICHO ; if not, we're pointing to next character
|
||||||
|
inc DPH ; account for page crossing
|
||||||
|
PSICHO ora #0 ; Set flags according to contents of Accumulator
|
||||||
|
beq PSIX1 ; don't print the final NULL
|
||||||
|
jsr PUTSER ; write it out
|
||||||
|
jmp PSINB ; back around
|
||||||
|
PSIX1 inc DPL ;
|
||||||
|
bne PSIX2 ;
|
||||||
|
inc DPH ; account for page crossing
|
||||||
|
PSIX2 jmp (DPL) ; return to byte following final NULL
|
||||||
|
;
|
||||||
|
; Dummy interrupt handlers
|
||||||
|
GOIRQ
|
||||||
|
GONMI RTI
|
||||||
|
|
||||||
|
; vectors
|
||||||
|
.ORG $FFFFFFFA
|
||||||
|
NMIENT .word GONMI
|
||||||
|
RSTENT .word START
|
||||||
|
IRQENT .word GOIRQ
|
||||||
|
.end ; finally. das Ende.
|
BIN
examples/65Org16.boot.rom
Normal file
BIN
examples/65Org16.boot.rom
Normal file
Binary file not shown.
12
examples/README.txt
Normal file
12
examples/README.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
Some example code and usage:
|
||||||
|
|
||||||
|
For linux:
|
||||||
|
|
||||||
|
(no need to install or build - just unpack)
|
||||||
|
|
||||||
|
$ cd src
|
||||||
|
$ env PYTHONPATH=. python py65/monitor.py -m 65Org16 -r ../examples/65Org16.boot.rom
|
||||||
|
|
||||||
|
Then paste in a hex file, such as ../examples/swapcase.hex
|
||||||
|
|
||||||
|
(Type in some mixed-case input and it will echo it back with the upper and lowercase swapped)
|
28
examples/swapcase.asm
Normal file
28
examples/swapcase.asm
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
; trivial demo program: read input, swap case and write out
|
||||||
|
|
||||||
|
; assemble using Anton Treuenfels' HXA assembler
|
||||||
|
; with I6502.A macro file
|
||||||
|
; HXA_TW.EXE swapcase.asm
|
||||||
|
; (runs on Linux if you have WINE installed)
|
||||||
|
; which will make
|
||||||
|
; SWAPCASE.HEX
|
||||||
|
; where the first line should not be input to the hexloader
|
||||||
|
|
||||||
|
.hexfile
|
||||||
|
|
||||||
|
.cpu T_32_M16
|
||||||
|
.assume BIT32=1032, BIT32R=3210
|
||||||
|
.include "i6502.a"
|
||||||
|
|
||||||
|
; I/O is memory-mapped in py65:
|
||||||
|
PUTC = $f001
|
||||||
|
GETC = $f005 ; blocking input
|
||||||
|
|
||||||
|
; the py65 hexload boot ROM will only load to $0200
|
||||||
|
.ORG $200
|
||||||
|
|
||||||
|
another
|
||||||
|
lda GETC
|
||||||
|
eor #$20 ; swap upper and lower case as a demo
|
||||||
|
sta PUTC
|
||||||
|
jmp another
|
3
examples/swapcase.hex
Normal file
3
examples/swapcase.hex
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
;020000040000FA
|
||||||
|
;1202000000A5F005004900200085F001004C0200000025
|
||||||
|
;00000001FF
|
@ -6,8 +6,8 @@ class Assembler:
|
|||||||
Statement = re.compile(r'^([A-z]{3}\s+'
|
Statement = re.compile(r'^([A-z]{3}\s+'
|
||||||
r'\(?\s*)([^,\s\)]+)(\s*[,xXyY\s]*\)?'
|
r'\(?\s*)([^,\s\)]+)(\s*[,xXyY\s]*\)?'
|
||||||
r'[,xXyY\s]*)$')
|
r'[,xXyY\s]*)$')
|
||||||
|
|
||||||
Addressing = [
|
Addressing8 = [
|
||||||
['zpi', re.compile(r'^\(\$00([0-9A-F]{2})\)$')], # "($0012)"
|
['zpi', re.compile(r'^\(\$00([0-9A-F]{2})\)$')], # "($0012)"
|
||||||
['zpx', re.compile(r'^\$00([0-9A-F]{2}),X$')], # "$0012,X"
|
['zpx', re.compile(r'^\$00([0-9A-F]{2}),X$')], # "$0012,X"
|
||||||
['zpy', re.compile(r'^\$00([0-9A-F]{2}),Y$')], # "$0012,Y"
|
['zpy', re.compile(r'^\$00([0-9A-F]{2}),Y$')], # "$0012,Y"
|
||||||
@ -24,7 +24,25 @@ class Assembler:
|
|||||||
['acc', re.compile(r'^A$')], # "A"
|
['acc', re.compile(r'^A$')], # "A"
|
||||||
['imm', re.compile(r'^#\$([0-9A-F]{2})$')] # "#$12"
|
['imm', re.compile(r'^#\$([0-9A-F]{2})$')] # "#$12"
|
||||||
]
|
]
|
||||||
|
Addressing16 = [
|
||||||
|
['zpi', re.compile(r'^\(\$0000([0-9A-F]{4})\)$')], # "($00001234)"
|
||||||
|
['zpx', re.compile(r'^\$0000([0-9A-F]{4}),X$')], # "$00001234,X"
|
||||||
|
['zpy', re.compile(r'^\$0000([0-9A-F]{4}),Y$')], # "$00001234,Y"
|
||||||
|
['zpg', re.compile(r'^\$0000([0-9A-F]{4})$')], # "$00001234"
|
||||||
|
['inx', re.compile(r'^\(\$0000([0-9A-F]{4}),X\)$')], # "($00001234,X)"
|
||||||
|
['iny', re.compile(r'^\(\$0000([0-9A-F]{4})\),Y$')], # "($00001234),Y"
|
||||||
|
['ind', re.compile(r'^\(\$([0-9A-F]{4})([0-9A-F]{4})\)$')], # "($12345678)"
|
||||||
|
['abx', re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4}),X$')], # "$12345678,X"
|
||||||
|
['aby', re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4}),Y$')], # "$12345678,Y"
|
||||||
|
['abs', re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4})$')], # "$12345678"
|
||||||
|
['rel', re.compile(r'^\$([0-9A-F]{4})([0-9A-F]{4})$')], # "$12345678"
|
||||||
|
['imp', re.compile(r'^$')], # ""
|
||||||
|
['acc', re.compile(r'^$')], # ""
|
||||||
|
['acc', re.compile(r'^A$')], # "A"
|
||||||
|
['imm', re.compile(r'^#\$([0-9A-F]{4})$')] # "#$1234"
|
||||||
|
]
|
||||||
|
Addressing = Addressing8
|
||||||
|
|
||||||
def __init__(self, mpu, address_parser=None):
|
def __init__(self, mpu, address_parser=None):
|
||||||
""" If a configured AddressParser is passed, symbolic addresses
|
""" If a configured AddressParser is passed, symbolic addresses
|
||||||
may be used in the assembly statements.
|
may be used in the assembly statements.
|
||||||
@ -35,13 +53,25 @@ class Assembler:
|
|||||||
self._mpu = mpu
|
self._mpu = mpu
|
||||||
self._address_parser = address_parser
|
self._address_parser = address_parser
|
||||||
|
|
||||||
|
self.addrWidth = mpu.addrWidth
|
||||||
|
self.byteWidth = mpu.byteWidth
|
||||||
|
self.addrFmt = mpu.addrFmt
|
||||||
|
self.byteFmt = mpu.byteFmt
|
||||||
|
self.addrMask = mpu.addrMask
|
||||||
|
self.byteMask = mpu.byteMask
|
||||||
|
|
||||||
|
if self.byteWidth == 8:
|
||||||
|
self.Addressing = self.Addressing8
|
||||||
|
else:
|
||||||
|
self.Addressing = self.Addressing16
|
||||||
|
|
||||||
def assemble(self, statement, pc=0000):
|
def assemble(self, statement, pc=0000):
|
||||||
""" Assemble the given assembly language statement. If the statement
|
""" Assemble the given assembly language statement. If the statement
|
||||||
uses relative addressing, the program counter (pc) must also be given.
|
uses relative addressing, the program counter (pc) must also be given.
|
||||||
The result is a list of bytes, or None if the assembly failed.
|
The result is a list of bytes, or None if the assembly failed.
|
||||||
"""
|
"""
|
||||||
opcode, operands = self.normalize_and_split(statement)
|
opcode, operands = self.normalize_and_split(statement)
|
||||||
|
|
||||||
for mode, pattern in self.Addressing:
|
for mode, pattern in self.Addressing:
|
||||||
match = pattern.match(operands)
|
match = pattern.match(operands)
|
||||||
|
|
||||||
@ -57,8 +87,8 @@ class Assembler:
|
|||||||
# relative branch
|
# relative branch
|
||||||
absolute = int(''.join(operands), 16)
|
absolute = int(''.join(operands), 16)
|
||||||
relative = (absolute - pc) - 2
|
relative = (absolute - pc) - 2
|
||||||
relative = relative & 0xFF
|
relative = relative & self.byteMask
|
||||||
operands = [ ("%02x" % relative) ]
|
operands = [ (self.byteFmt % relative) ]
|
||||||
|
|
||||||
elif len(operands) == 2:
|
elif len(operands) == 2:
|
||||||
# swap bytes
|
# swap bytes
|
||||||
@ -70,7 +100,7 @@ class Assembler:
|
|||||||
|
|
||||||
# assembly failed
|
# assembly failed
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def normalize_and_split(self, statement):
|
def normalize_and_split(self, statement):
|
||||||
""" Given an assembly language statement like "lda $c12,x", normalize
|
""" Given an assembly language statement like "lda $c12,x", normalize
|
||||||
the statement by uppercasing it, removing unnecessary whitespace,
|
the statement by uppercasing it, removing unnecessary whitespace,
|
||||||
@ -86,19 +116,19 @@ class Assembler:
|
|||||||
# target is an immediate number
|
# target is an immediate number
|
||||||
if target.startswith('#'):
|
if target.startswith('#'):
|
||||||
number = self._address_parser.number(target[1:])
|
number = self._address_parser.number(target[1:])
|
||||||
if (number < 0x00) or (number > 0xFF):
|
if (number < 0x00) or (number > self.byteMask):
|
||||||
raise OverflowError
|
raise OverflowError
|
||||||
statement = '%s#$%02x' % (before, number)
|
statement = before + '#$' + self.byteFmt % number
|
||||||
|
|
||||||
# target is the accumulator
|
# target is the accumulator
|
||||||
elif target in ('a', 'A'):
|
elif target in ('a', 'A'):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# target is an address or label
|
# target is an address or label
|
||||||
else:
|
else:
|
||||||
address = self._address_parser.number(target)
|
address = self._address_parser.number(target)
|
||||||
statement = '%s$%04x%s' % (before, address, after)
|
statement = before + '$' + self.addrFmt % address + after
|
||||||
|
|
||||||
# strip unnecessary whitespace
|
# strip unnecessary whitespace
|
||||||
opcode = statement[:3].upper()
|
opcode = statement[:3].upper()
|
||||||
operand = ''.join(statement[3:].split()).upper().strip()
|
operand = ''.join(statement[3:].split()).upper().strip()
|
||||||
|
@ -17,11 +17,19 @@ class MPU:
|
|||||||
ZERO = 2
|
ZERO = 2
|
||||||
CARRY = 1
|
CARRY = 1
|
||||||
|
|
||||||
def __init__(self, memory=None, pc=0x0000, debug=False):
|
def __init__(self, memory=None, pc=0x0000, debug=False, byteWidth=8, addrWidth=16, addrFmt="%04x", byteFmt="%02x"):
|
||||||
# config
|
# config
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
self.name = '6502'
|
self.name = '6502'
|
||||||
|
self.byteWidth = byteWidth
|
||||||
|
self.byteMask = ((1<<byteWidth)-1)
|
||||||
|
self.addrWidth = addrWidth
|
||||||
|
self.addrMask = ((1<<addrWidth)-1)
|
||||||
|
self.addrHighMask = (self.byteMask<<byteWidth)
|
||||||
|
self.addrFmt=addrFmt
|
||||||
|
self.byteFmt=byteFmt
|
||||||
|
self.spBase = 1<<byteWidth
|
||||||
|
|
||||||
# vm status
|
# vm status
|
||||||
self.excycles = 0
|
self.excycles = 0
|
||||||
self.addcycles = False
|
self.addcycles = False
|
||||||
@ -35,30 +43,32 @@ class MPU:
|
|||||||
# init
|
# init
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
|
def reprformat(self):
|
||||||
|
return ("%s PC AC XR YR SP NV-BDIZC\n" + \
|
||||||
|
"%s: %04x %02x %02x %02x %02x %s"
|
||||||
|
)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
flags = itoa(self.p, 2).rjust(8, '0')
|
flags = itoa(self.p, 2).rjust(self.byteWidth, '0')
|
||||||
indent = ' ' * (len(self.name) + 2)
|
indent = ' ' * (len(self.name) + 2)
|
||||||
|
|
||||||
out = "%s PC AC XR YR SP NV-BDIZC\n" + \
|
return self.reprformat() % (indent, self.name,
|
||||||
"%s: %04x %02x %02x %02x %02x %s"
|
|
||||||
|
|
||||||
return out % (indent, self.name,
|
|
||||||
self.pc, self.a, self.x, self.y, self.sp, flags)
|
self.pc, self.a, self.x, self.y, self.sp, flags)
|
||||||
|
|
||||||
def step(self):
|
def step(self):
|
||||||
instructCode = self.ImmediateByte()
|
instructCode = self.ImmediateByte()
|
||||||
self.pc +=1
|
self.pc +=1
|
||||||
self.pc &=0xffff
|
self.pc &=self.addrMask
|
||||||
self.excycles = 0
|
self.excycles = 0
|
||||||
self.addcycles = self.extracycles[instructCode]
|
self.addcycles = self.extracycles[instructCode]
|
||||||
self.instruct[instructCode](self)
|
self.instruct[instructCode](self)
|
||||||
self.processorCycles += self.cycletime[instructCode]+self.excycles
|
self.processorCycles += self.cycletime[instructCode]+self.excycles
|
||||||
self.pc &= 0xffff
|
self.pc &= self.addrMask
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self.pc = self.start_pc
|
self.pc = self.start_pc
|
||||||
self.sp = 255
|
self.sp = self.byteMask
|
||||||
self.a = 0
|
self.a = 0
|
||||||
self.x = 0
|
self.x = 0
|
||||||
self.y = 0
|
self.y = 0
|
||||||
@ -71,11 +81,11 @@ class MPU:
|
|||||||
return self.memory[addr]
|
return self.memory[addr]
|
||||||
|
|
||||||
def WordAt(self, addr):
|
def WordAt(self, addr):
|
||||||
return self.ByteAt(addr) + (self.ByteAt(addr + 1) << 8)
|
return self.ByteAt(addr) + (self.ByteAt(addr + 1) << self.byteWidth)
|
||||||
|
|
||||||
def WrapAt(self, addr):
|
def WrapAt(self, addr):
|
||||||
wrap = lambda x: (x & 0xff00) + ((x + 1) & 0xff)
|
wrap = lambda x: (x & self.addrHighMask) + ((x + 1) & self.byteMask)
|
||||||
return self.ByteAt(addr) + (self.ByteAt(wrap(addr)) << 8)
|
return self.ByteAt(addr) + (self.ByteAt(wrap(addr)) << self.byteWidth)
|
||||||
|
|
||||||
def ProgramCounter(self):
|
def ProgramCounter(self):
|
||||||
return self.pc
|
return self.pc
|
||||||
@ -89,23 +99,23 @@ class MPU:
|
|||||||
return self.ByteAt(self.pc)
|
return self.ByteAt(self.pc)
|
||||||
|
|
||||||
def ZeroPageXAddr(self):
|
def ZeroPageXAddr(self):
|
||||||
return 255 & (self.x + self.ByteAt(self.pc))
|
return self.byteMask & (self.x + self.ByteAt(self.pc))
|
||||||
|
|
||||||
def ZeroPageYAddr(self):
|
def ZeroPageYAddr(self):
|
||||||
return 255 & (self.y + self.ByteAt(self.pc))
|
return self.byteMask & (self.y + self.ByteAt(self.pc))
|
||||||
|
|
||||||
def IndirectXAddr(self):
|
def IndirectXAddr(self):
|
||||||
return self.WrapAt( 255 & (self.ByteAt(self.pc) + self.x))
|
return self.WrapAt( self.byteMask & (self.ByteAt(self.pc) + self.x))
|
||||||
|
|
||||||
def IndirectYAddr(self):
|
def IndirectYAddr(self):
|
||||||
if self.addcycles:
|
if self.addcycles:
|
||||||
a1 = self.WrapAt(self.ByteAt(self.pc))
|
a1 = self.WrapAt(self.ByteAt(self.pc))
|
||||||
a2 = (a1+self.y) & 0xffff
|
a2 = (a1+self.y) & self.addrMask
|
||||||
if (a1 & 0xff00) != (a2 & 0xff00):
|
if (a1 & self.addrHighMask) != (a2 & self.addrHighMask):
|
||||||
self.excycles += 1
|
self.excycles += 1
|
||||||
return a2
|
return a2
|
||||||
else:
|
else:
|
||||||
return (self.WrapAt(self.ByteAt(self.pc))+self.y)&0xffff
|
return (self.WrapAt(self.ByteAt(self.pc))+self.y)&self.addrMask
|
||||||
|
|
||||||
def AbsoluteAddr(self):
|
def AbsoluteAddr(self):
|
||||||
return self.WordAt(self.pc)
|
return self.WordAt(self.pc)
|
||||||
@ -113,57 +123,57 @@ class MPU:
|
|||||||
def AbsoluteXAddr(self):
|
def AbsoluteXAddr(self):
|
||||||
if self.addcycles:
|
if self.addcycles:
|
||||||
a1 = self.WordAt(self.pc)
|
a1 = self.WordAt(self.pc)
|
||||||
a2 = (a1 + self.x) & 0xffff
|
a2 = (a1 + self.x) & self.addrMask
|
||||||
if (a1 & 0xff00) != (a2 & 0xff00):
|
if (a1 & self.addrHighMask) != (a2 & self.addrHighMask):
|
||||||
self.excycles += 1
|
self.excycles += 1
|
||||||
return a2
|
return a2
|
||||||
else:
|
else:
|
||||||
return (self.WordAt(self.pc)+self.x)&0xffff
|
return (self.WordAt(self.pc)+self.x)&self.addrMask
|
||||||
|
|
||||||
def AbsoluteYAddr(self):
|
def AbsoluteYAddr(self):
|
||||||
if self.addcycles:
|
if self.addcycles:
|
||||||
a1 = self.WordAt(self.pc)
|
a1 = self.WordAt(self.pc)
|
||||||
a2 = (a1 + self.y) & 0xffff
|
a2 = (a1 + self.y) & self.addrMask
|
||||||
if (a1 & 0xff00) != (a2 & 0xff00):
|
if (a1 & self.addrHighMask) != (a2 & self.addrHighMask):
|
||||||
self.excycles += 1
|
self.excycles += 1
|
||||||
return a2
|
return a2
|
||||||
else:
|
else:
|
||||||
return (self.WordAt(self.pc)+self.y)&0xffff
|
return (self.WordAt(self.pc)+self.y)&self.addrMask
|
||||||
|
|
||||||
def BranchRelAddr(self):
|
def BranchRelAddr(self):
|
||||||
self.excycles += 1
|
self.excycles += 1
|
||||||
addr = self.ImmediateByte()
|
addr = self.ImmediateByte()
|
||||||
self.pc += 1
|
self.pc += 1
|
||||||
|
|
||||||
if addr & 128:
|
if addr & self.NEGATIVE:
|
||||||
addr = self.pc - (addr ^ 0xFF) - 1
|
addr = self.pc - (addr ^ self.byteMask) - 1
|
||||||
else:
|
else:
|
||||||
addr = self.pc + addr
|
addr = self.pc + addr
|
||||||
|
|
||||||
if (self.pc & 0xff00) != (addr & 0xff00):
|
if (self.pc & self.addrHighMask) != (addr & self.addrHighMask):
|
||||||
self.excycles += 1
|
self.excycles += 1
|
||||||
|
|
||||||
self.pc = addr & 0xffff
|
self.pc = addr & self.addrMask
|
||||||
|
|
||||||
# stack
|
# stack
|
||||||
|
|
||||||
def stPush(self,z):
|
def stPush(self,z):
|
||||||
self.memory[self.sp+256] = z&255
|
self.memory[self.sp+self.spBase] = z&self.byteMask
|
||||||
self.sp -= 1
|
self.sp -= 1
|
||||||
self.sp &= 255
|
self.sp &= self.byteMask
|
||||||
|
|
||||||
def stPop(self):
|
def stPop(self):
|
||||||
self.sp += 1
|
self.sp += 1
|
||||||
self.sp &= 255
|
self.sp &= self.byteMask
|
||||||
return self.ByteAt(self.sp+256)
|
return self.ByteAt(self.sp+self.spBase)
|
||||||
|
|
||||||
def stPushWord(self, z):
|
def stPushWord(self, z):
|
||||||
self.stPush((z>>8)&255)
|
self.stPush((z>>self.byteWidth)&self.byteMask)
|
||||||
self.stPush(z&255)
|
self.stPush(z&self.byteMask)
|
||||||
|
|
||||||
def stPopWord(self):
|
def stPopWord(self):
|
||||||
z = self.stPop()
|
z = self.stPop()
|
||||||
z += 256*self.stPop()
|
z += self.stPop()<<self.byteWidth
|
||||||
return z
|
return z
|
||||||
|
|
||||||
def FlagsNZ(self, value):
|
def FlagsNZ(self, value):
|
||||||
@ -188,12 +198,12 @@ class MPU:
|
|||||||
|
|
||||||
self.p &= ~(self.CARRY + self.NEGATIVE + self.ZERO)
|
self.p &= ~(self.CARRY + self.NEGATIVE + self.ZERO)
|
||||||
|
|
||||||
if tbyte & 128:
|
if tbyte & self.NEGATIVE:
|
||||||
self.p |= self.CARRY
|
self.p |= self.CARRY
|
||||||
tbyte = (tbyte << 1) & 0xFF
|
tbyte = (tbyte << 1) & self.byteMask
|
||||||
|
|
||||||
if tbyte:
|
if tbyte:
|
||||||
self.p |= tbyte & 128
|
self.p |= tbyte & self.NEGATIVE
|
||||||
else:
|
else:
|
||||||
self.p |= self.ZERO
|
self.p |= self.ZERO
|
||||||
|
|
||||||
@ -250,7 +260,7 @@ class MPU:
|
|||||||
self.p &=~(self.ZERO+self.NEGATIVE+self.OVERFLOW)
|
self.p &=~(self.ZERO+self.NEGATIVE+self.OVERFLOW)
|
||||||
if (self.a & tbyte) == 0:
|
if (self.a & tbyte) == 0:
|
||||||
self.p |= self.ZERO
|
self.p |= self.ZERO
|
||||||
self.p |= tbyte&(128+64)
|
self.p |= tbyte&(self.NEGATIVE+self.OVERFLOW)
|
||||||
|
|
||||||
def opROL(self, x):
|
def opROL(self, x):
|
||||||
if x is None:
|
if x is None:
|
||||||
@ -260,16 +270,16 @@ class MPU:
|
|||||||
tbyte = self.ByteAt(addr)
|
tbyte = self.ByteAt(addr)
|
||||||
|
|
||||||
if self.p & self.CARRY:
|
if self.p & self.CARRY:
|
||||||
if tbyte & 128:
|
if tbyte & self.NEGATIVE:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self.p &= ~self.CARRY
|
self.p &= ~self.CARRY
|
||||||
tbyte = (tbyte << 1) | 1
|
tbyte = (tbyte << 1) | 1
|
||||||
else:
|
else:
|
||||||
if tbyte & 128:
|
if tbyte & self.NEGATIVE:
|
||||||
self.p |= self.CARRY
|
self.p |= self.CARRY
|
||||||
tbyte = tbyte << 1
|
tbyte = tbyte << 1
|
||||||
tbyte &= 0xFF
|
tbyte &= self.byteMask
|
||||||
self.FlagsNZ(tbyte)
|
self.FlagsNZ(tbyte)
|
||||||
|
|
||||||
if x is None:
|
if x is None:
|
||||||
@ -313,7 +323,7 @@ class MPU:
|
|||||||
self.p |= aluresult & self.NEGATIVE
|
self.p |= aluresult & self.NEGATIVE
|
||||||
if decimalcarry == 1:
|
if decimalcarry == 1:
|
||||||
self.p |= self.CARRY
|
self.p |= self.CARRY
|
||||||
if ( ~(self.a ^ data) & (self.a ^ aluresult) ) & 0x80:
|
if ( ~(self.a ^ data) & (self.a ^ aluresult) ) & self.NEGATIVE:
|
||||||
self.p |= self.OVERFLOW
|
self.p |= self.OVERFLOW
|
||||||
self.a = (nibble1 << 4) + nibble0
|
self.a = (nibble1 << 4) + nibble0
|
||||||
else:
|
else:
|
||||||
@ -323,12 +333,12 @@ class MPU:
|
|||||||
tmp = 0
|
tmp = 0
|
||||||
result = data + self.a + tmp
|
result = data + self.a + tmp
|
||||||
self.p &= ~(self.CARRY+self.OVERFLOW+self.NEGATIVE+self.ZERO)
|
self.p &= ~(self.CARRY+self.OVERFLOW+self.NEGATIVE+self.ZERO)
|
||||||
if ( ~(self.a ^ data) & (self.a ^ result) ) & 0x80:
|
if ( ~(self.a ^ data) & (self.a ^ result) ) & self.NEGATIVE:
|
||||||
self.p |= self.OVERFLOW
|
self.p |= self.OVERFLOW
|
||||||
data = result
|
data = result
|
||||||
if data > 255:
|
if data > self.byteMask:
|
||||||
self.p |= self.CARRY
|
self.p |= self.CARRY
|
||||||
data &=255
|
data &=self.byteMask
|
||||||
if data == 0:
|
if data == 0:
|
||||||
self.p |= self.ZERO
|
self.p |= self.ZERO
|
||||||
else:
|
else:
|
||||||
@ -347,7 +357,7 @@ class MPU:
|
|||||||
pass # {}
|
pass # {}
|
||||||
else:
|
else:
|
||||||
self.p &=~ self.CARRY
|
self.p &=~ self.CARRY
|
||||||
tbyte=(tbyte>>1)|128
|
tbyte=(tbyte>>1)|self.NEGATIVE
|
||||||
else:
|
else:
|
||||||
if tbyte & 1:
|
if tbyte & 1:
|
||||||
self.p |= self.CARRY
|
self.p |= self.CARRY
|
||||||
@ -395,10 +405,10 @@ class MPU:
|
|||||||
adjust1 = 10 << 4
|
adjust1 = 10 << 4
|
||||||
|
|
||||||
# the ALU outputs are not decimally adjusted
|
# the ALU outputs are not decimally adjusted
|
||||||
aluresult = self.a + (~data & 0xFF) + (self.p & self.CARRY)
|
aluresult = self.a + (~data & self.byteMask) + (self.p & self.CARRY)
|
||||||
if aluresult > 0xff:
|
if aluresult > self.byteMask:
|
||||||
decimalcarry = 1
|
decimalcarry = 1
|
||||||
aluresult &= 0xff
|
aluresult &= self.byteMask
|
||||||
|
|
||||||
# but the final result will be adjusted
|
# but the final result will be adjusted
|
||||||
nibble0 = (aluresult + adjust0) & 0xf
|
nibble0 = (aluresult + adjust0) & 0xf
|
||||||
@ -411,7 +421,7 @@ class MPU:
|
|||||||
self.p |= aluresult & self.NEGATIVE
|
self.p |= aluresult & self.NEGATIVE
|
||||||
if decimalcarry == 1:
|
if decimalcarry == 1:
|
||||||
self.p |= self.CARRY
|
self.p |= self.CARRY
|
||||||
if ( (self.a ^ data) & (self.a ^ aluresult) ) & 0x80:
|
if ( (self.a ^ data) & (self.a ^ aluresult) ) & self.NEGATIVE:
|
||||||
self.p |= self.OVERFLOW
|
self.p |= self.OVERFLOW
|
||||||
self.a = (nibble1 << 4) + nibble0
|
self.a = (nibble1 << 4) + nibble0
|
||||||
else:
|
else:
|
||||||
@ -420,14 +430,14 @@ class MPU:
|
|||||||
else:
|
else:
|
||||||
borrow = 1
|
borrow = 1
|
||||||
|
|
||||||
result = self.a + (~data & 0xFF) + (self.p & self.CARRY)
|
result = self.a + (~data & self.byteMask) + (self.p & self.CARRY)
|
||||||
self.p &= ~(self.CARRY + self.ZERO + self.OVERFLOW + self.NEGATIVE)
|
self.p &= ~(self.CARRY + self.ZERO + self.OVERFLOW + self.NEGATIVE)
|
||||||
if ( (self.a ^ data) & (self.a ^ result) ) & 0x80:
|
if ( (self.a ^ data) & (self.a ^ result) ) & self.NEGATIVE:
|
||||||
self.p |= self.OVERFLOW
|
self.p |= self.OVERFLOW
|
||||||
data = result & 0xFF
|
data = result & self.byteMask
|
||||||
if data == 0:
|
if data == 0:
|
||||||
self.p |= self.ZERO
|
self.p |= self.ZERO
|
||||||
if result & 0x100:
|
if result > self.byteMask:
|
||||||
self.p |= self.CARRY
|
self.p |= self.CARRY
|
||||||
self.p |= data & self.NEGATIVE
|
self.p |= data & self.NEGATIVE
|
||||||
self.a = data
|
self.a = data
|
||||||
@ -440,7 +450,7 @@ class MPU:
|
|||||||
tbyte = self.ByteAt(addr)
|
tbyte = self.ByteAt(addr)
|
||||||
|
|
||||||
self.p &= ~(self.ZERO + self.NEGATIVE)
|
self.p &= ~(self.ZERO + self.NEGATIVE)
|
||||||
tbyte = (tbyte - 1) & 0xFF
|
tbyte = (tbyte - 1) & self.byteMask
|
||||||
if tbyte:
|
if tbyte:
|
||||||
self.p |= tbyte & self.NEGATIVE
|
self.p |= tbyte & self.NEGATIVE
|
||||||
else:
|
else:
|
||||||
@ -459,7 +469,7 @@ class MPU:
|
|||||||
tbyte = self.ByteAt(addr)
|
tbyte = self.ByteAt(addr)
|
||||||
|
|
||||||
self.p &= ~(self.ZERO + self.NEGATIVE)
|
self.p &= ~(self.ZERO + self.NEGATIVE)
|
||||||
tbyte = (tbyte + 1) & 0xFF
|
tbyte = (tbyte + 1) & self.byteMask
|
||||||
if tbyte:
|
if tbyte:
|
||||||
self.p |= tbyte & self.NEGATIVE
|
self.p |= tbyte & self.NEGATIVE
|
||||||
else:
|
else:
|
||||||
@ -499,7 +509,7 @@ class MPU:
|
|||||||
|
|
||||||
@instruction(name="BRK", mode="imp", cycles=7)
|
@instruction(name="BRK", mode="imp", cycles=7)
|
||||||
def inst_0x00(self):
|
def inst_0x00(self):
|
||||||
pc = (self.pc + 1) & 0xFFFF # The pc has already been increased one
|
pc = (self.pc + 1) & self.addrMask # The pc has already been increased one
|
||||||
self.stPushWord(pc)
|
self.stPushWord(pc)
|
||||||
|
|
||||||
self.p |= self.BREAK
|
self.p |= self.BREAK
|
||||||
@ -586,7 +596,7 @@ class MPU:
|
|||||||
|
|
||||||
@instruction(name="JSR", mode="abs", cycles=6)
|
@instruction(name="JSR", mode="abs", cycles=6)
|
||||||
def inst_0x20(self):
|
def inst_0x20(self):
|
||||||
self.stPushWord((self.pc+1)&0xffff)
|
self.stPushWord((self.pc+1)&self.addrMask)
|
||||||
self.pc=self.WordAt(self.pc)
|
self.pc=self.WordAt(self.pc)
|
||||||
|
|
||||||
@instruction(name="AND", mode="inx", cycles=6)
|
@instruction(name="AND", mode="inx", cycles=6)
|
||||||
@ -870,7 +880,7 @@ class MPU:
|
|||||||
@instruction(name="DEY", mode="imp", cycles=2)
|
@instruction(name="DEY", mode="imp", cycles=2)
|
||||||
def inst_0x88(self):
|
def inst_0x88(self):
|
||||||
self.y -= 1
|
self.y -= 1
|
||||||
self.y&=255
|
self.y&=self.byteMask
|
||||||
self.FlagsNZ(self.y)
|
self.FlagsNZ(self.y)
|
||||||
|
|
||||||
@instruction(name="TXA", mode="imp", cycles=2)
|
@instruction(name="TXA", mode="imp", cycles=2)
|
||||||
@ -1077,7 +1087,7 @@ class MPU:
|
|||||||
@instruction(name="INY", mode="imp", cycles=2)
|
@instruction(name="INY", mode="imp", cycles=2)
|
||||||
def inst_0xc8(self):
|
def inst_0xc8(self):
|
||||||
self.y += 1
|
self.y += 1
|
||||||
self.y &= 255
|
self.y &= self.byteMask
|
||||||
self.FlagsNZ(self.y)
|
self.FlagsNZ(self.y)
|
||||||
|
|
||||||
@instruction(name="CMP", mode="imm", cycles=2)
|
@instruction(name="CMP", mode="imm", cycles=2)
|
||||||
@ -1088,7 +1098,7 @@ class MPU:
|
|||||||
@instruction(name="DEX", mode="imp", cycles=2)
|
@instruction(name="DEX", mode="imp", cycles=2)
|
||||||
def inst_0xca(self):
|
def inst_0xca(self):
|
||||||
self.x -= 1
|
self.x -= 1
|
||||||
self.x &= 255
|
self.x &= self.byteMask
|
||||||
self.FlagsNZ(self.x)
|
self.FlagsNZ(self.x)
|
||||||
|
|
||||||
@instruction(name="CPY", mode="abs", cycles=4)
|
@instruction(name="CPY", mode="abs", cycles=4)
|
||||||
@ -1172,7 +1182,7 @@ class MPU:
|
|||||||
@instruction(name="INX", mode="imp", cycles=2)
|
@instruction(name="INX", mode="imp", cycles=2)
|
||||||
def inst_0xe8(self):
|
def inst_0xe8(self):
|
||||||
self.x+=1
|
self.x+=1
|
||||||
self.x&=255
|
self.x&=self.byteMask
|
||||||
self.FlagsNZ(self.x)
|
self.FlagsNZ(self.x)
|
||||||
|
|
||||||
@instruction(name="SBC", mode="imm", cycles=2)
|
@instruction(name="SBC", mode="imm", cycles=2)
|
||||||
@ -1236,4 +1246,3 @@ class MPU:
|
|||||||
def inst_0xfe(self):
|
def inst_0xfe(self):
|
||||||
self.opINCR(self.AbsoluteXAddr)
|
self.opINCR(self.AbsoluteXAddr)
|
||||||
self.pc += 2
|
self.pc += 2
|
||||||
|
|
||||||
|
41
src/py65/devices/mpu65Org16.py
Normal file
41
src/py65/devices/mpu65Org16.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
from py65.devices import mpu6502
|
||||||
|
from py65.utils.devices import make_instruction_decorator
|
||||||
|
|
||||||
|
# The 65Org16 is a derivative of the 6502 architecture
|
||||||
|
# - with 32-bit address space (by using 16-bit bytes)
|
||||||
|
# - with no specific support for 8-bit bytes
|
||||||
|
# - with BCD mode not supported
|
||||||
|
# - and otherwise all opcodes and addressing modes are like the NMOS 6502
|
||||||
|
# - sign bit is bit 15, overflow bit is bit 14
|
||||||
|
#
|
||||||
|
# One implementation can be found here: https://github.com/BigEd/verilog-6502/wiki
|
||||||
|
|
||||||
|
class MPU(mpu6502.MPU):
|
||||||
|
|
||||||
|
def __init__(self, byteWidth=16, addrWidth=32, addrFmt="%08x", byteFmt="%04x", *args, **kwargs):
|
||||||
|
mpu6502.MPU.__init__(self, byteWidth=byteWidth, addrWidth=addrWidth, addrFmt=addrFmt, byteFmt=byteFmt, *args, **kwargs)
|
||||||
|
self.name = '65Org16'
|
||||||
|
self.waiting = False
|
||||||
|
self.IrqTo = (1<<self.addrWidth)-2
|
||||||
|
self.ResetTo = (1<<self.addrWidth)-4
|
||||||
|
self.NMITo = (1<<self.addrWidth)-6
|
||||||
|
self.NEGATIVE = 1 << 15
|
||||||
|
self.OVERFLOW = 1 << 14
|
||||||
|
|
||||||
|
def step(self):
|
||||||
|
if self.waiting:
|
||||||
|
self.processorCycles += 1
|
||||||
|
else:
|
||||||
|
mpu6502.MPU.step(self)
|
||||||
|
return self
|
||||||
|
|
||||||
|
# Make copies of the lists
|
||||||
|
instruct = mpu6502.MPU.instruct[:]
|
||||||
|
cycletime = mpu6502.MPU.cycletime[:]
|
||||||
|
extracycles = mpu6502.MPU.extracycles[:]
|
||||||
|
disassemble = mpu6502.MPU.disassemble[:]
|
||||||
|
|
||||||
|
def reprformat(self):
|
||||||
|
return ("%s PC AC XR YR SP NV---------BDIZC\n" + \
|
||||||
|
"%s: %08x %04x %04x %04x %04x %s"
|
||||||
|
)
|
@ -8,6 +8,13 @@ class Disassembler:
|
|||||||
self._mpu = mpu
|
self._mpu = mpu
|
||||||
self._address_parser = address_parser
|
self._address_parser = address_parser
|
||||||
|
|
||||||
|
self.addrWidth = mpu.addrWidth
|
||||||
|
self.byteWidth = mpu.byteWidth
|
||||||
|
self.addrFmt = mpu.addrFmt
|
||||||
|
self.byteFmt = mpu.byteFmt
|
||||||
|
self.addrMask = mpu.addrMask
|
||||||
|
self.byteMask = mpu.byteMask
|
||||||
|
|
||||||
def instruction_at(self, pc):
|
def instruction_at(self, pc):
|
||||||
""" Disassemble the instruction at PC and return a tuple
|
""" Disassemble the instruction at PC and return a tuple
|
||||||
containing (instruction byte count, human readable text)
|
containing (instruction byte count, human readable text)
|
||||||
@ -22,94 +29,94 @@ class Disassembler:
|
|||||||
|
|
||||||
elif addressing == 'abs':
|
elif addressing == 'abs':
|
||||||
address = self._mpu.WordAt(pc + 1)
|
address = self._mpu.WordAt(pc + 1)
|
||||||
address_or_label = self._address_parser.label_for(address,
|
address_or_label = self._address_parser.label_for(address,
|
||||||
'$%04x' % address)
|
'$' + self.addrFmt % address)
|
||||||
disasm += ' ' + address_or_label
|
disasm += ' ' + address_or_label
|
||||||
length = 3
|
length = 3
|
||||||
|
|
||||||
elif addressing == 'abx':
|
elif addressing == 'abx':
|
||||||
address = self._mpu.WordAt(pc + 1)
|
address = self._mpu.WordAt(pc + 1)
|
||||||
address_or_label = self._address_parser.label_for(address,
|
address_or_label = self._address_parser.label_for(address,
|
||||||
'$%04x' % address)
|
'$' + self.addrFmt % address)
|
||||||
disasm += ' %s,X' % address_or_label
|
disasm += ' %s,X' % address_or_label
|
||||||
length = 3
|
length = 3
|
||||||
|
|
||||||
elif addressing == 'aby':
|
elif addressing == 'aby':
|
||||||
address = self._mpu.WordAt(pc + 1)
|
address = self._mpu.WordAt(pc + 1)
|
||||||
address_or_label = self._address_parser.label_for(address,
|
address_or_label = self._address_parser.label_for(address,
|
||||||
'$%04x' % address)
|
'$' + self.addrFmt % address)
|
||||||
disasm += ' %s,Y' % address_or_label
|
disasm += ' %s,Y' % address_or_label
|
||||||
length = 3
|
length = 3
|
||||||
|
|
||||||
elif addressing == 'imm':
|
elif addressing == 'imm':
|
||||||
byte = self._mpu.ByteAt(pc + 1)
|
byte = self._mpu.ByteAt(pc + 1)
|
||||||
disasm += ' #$%02x' % byte
|
disasm += ' #$' + self.byteFmt % byte
|
||||||
length = 2
|
length = 2
|
||||||
|
|
||||||
elif addressing == 'imp':
|
elif addressing == 'imp':
|
||||||
length = 1
|
length = 1
|
||||||
|
|
||||||
elif addressing == 'ind':
|
elif addressing == 'ind':
|
||||||
address = self._mpu.WordAt(pc + 1)
|
address = self._mpu.WordAt(pc + 1)
|
||||||
address_or_label = self._address_parser.label_for(address,
|
address_or_label = self._address_parser.label_for(address,
|
||||||
'$%04x' % address)
|
'$' + self.addrFmt % address)
|
||||||
disasm += ' (%s)' % address_or_label
|
disasm += ' (%s)' % address_or_label
|
||||||
length = 3
|
length = 3
|
||||||
|
|
||||||
elif addressing == 'iny':
|
elif addressing == 'iny':
|
||||||
zp_address = self._mpu.ByteAt(pc + 1)
|
zp_address = self._mpu.ByteAt(pc + 1)
|
||||||
address_or_label = self._address_parser.label_for(zp_address,
|
address_or_label = self._address_parser.label_for(zp_address,
|
||||||
'$%02x' % zp_address)
|
'$' + self.byteFmt % zp_address)
|
||||||
disasm += ' (%s),Y' % address_or_label
|
disasm += ' (%s),Y' % address_or_label
|
||||||
length = 2
|
length = 2
|
||||||
|
|
||||||
elif addressing == 'inx':
|
elif addressing == 'inx':
|
||||||
zp_address = self._mpu.ByteAt(pc + 1)
|
zp_address = self._mpu.ByteAt(pc + 1)
|
||||||
address_or_label = self._address_parser.label_for(zp_address,
|
address_or_label = self._address_parser.label_for(zp_address,
|
||||||
'$%02x' % zp_address)
|
'$' + self.byteFmt % zp_address)
|
||||||
disasm += ' (%s,X)' % address_or_label
|
disasm += ' (%s,X)' % address_or_label
|
||||||
length = 2
|
length = 2
|
||||||
|
|
||||||
elif addressing == 'rel':
|
elif addressing == 'rel':
|
||||||
opv = self._mpu.ByteAt(pc + 1)
|
opv = self._mpu.ByteAt(pc + 1)
|
||||||
targ = pc + 2
|
targ = pc + 2
|
||||||
if opv & (1<<(8-1)):
|
if opv & (1<<(self.byteWidth-1)):
|
||||||
targ -= (opv ^ 0xFF) + 1
|
targ -= (opv ^ self.byteMask) + 1
|
||||||
else:
|
else:
|
||||||
targ += opv
|
targ += opv
|
||||||
targ &= 0xffff
|
targ &= self.addrMask
|
||||||
|
|
||||||
address_or_label = self._address_parser.label_for(targ,
|
address_or_label = self._address_parser.label_for(targ,
|
||||||
'$%04x' % targ)
|
'$' + self.addrFmt % targ)
|
||||||
disasm += ' ' + address_or_label
|
disasm += ' ' + address_or_label
|
||||||
length = 2
|
length = 2
|
||||||
|
|
||||||
elif addressing == 'zpi':
|
elif addressing == 'zpi':
|
||||||
zp_address = self._mpu.ByteAt(pc + 1)
|
zp_address = self._mpu.ByteAt(pc + 1)
|
||||||
address_or_label = self._address_parser.label_for(zp_address,
|
address_or_label = self._address_parser.label_for(zp_address,
|
||||||
'($%02x)' % zp_address)
|
'($' + self.byteFmt % zp_address + ')' )
|
||||||
disasm += ' %s' % address_or_label
|
disasm += ' %s' % address_or_label
|
||||||
length = 2
|
length = 2
|
||||||
|
|
||||||
elif addressing == 'zpg':
|
elif addressing == 'zpg':
|
||||||
zp_address = self._mpu.ByteAt(pc + 1)
|
zp_address = self._mpu.ByteAt(pc + 1)
|
||||||
address_or_label = self._address_parser.label_for(zp_address,
|
address_or_label = self._address_parser.label_for(zp_address,
|
||||||
'$%02x' % zp_address)
|
'$' + self.byteFmt % zp_address)
|
||||||
disasm += ' %s' % address_or_label
|
disasm += ' %s' % address_or_label
|
||||||
length = 2
|
length = 2
|
||||||
|
|
||||||
elif addressing == 'zpx':
|
elif addressing == 'zpx':
|
||||||
zp_address = self._mpu.ByteAt(pc + 1)
|
zp_address = self._mpu.ByteAt(pc + 1)
|
||||||
address_or_label = self._address_parser.label_for(zp_address,
|
address_or_label = self._address_parser.label_for(zp_address,
|
||||||
'$%02x' % zp_address)
|
'$' + self.byteFmt % zp_address)
|
||||||
disasm += ' %s,X' % address_or_label
|
disasm += ' %s,X' % address_or_label
|
||||||
length = 2
|
length = 2
|
||||||
|
|
||||||
elif addressing == 'zpy':
|
elif addressing == 'zpy':
|
||||||
zp_address = self._mpu.ByteAt(pc + 1)
|
zp_address = self._mpu.ByteAt(pc + 1)
|
||||||
address_or_label = self._address_parser.label_for(zp_address,
|
address_or_label = self._address_parser.label_for(zp_address,
|
||||||
'$%02x' % zp_address)
|
'$' + self.byteFmt % zp_address)
|
||||||
disasm += ' %s,Y' % address_or_label
|
disasm += ' %s,Y' % address_or_label
|
||||||
length = 2
|
length = 2
|
||||||
|
|
||||||
return (length, disasm)
|
return (length, disasm)
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
|
|
||||||
class ObservableMemory:
|
class ObservableMemory:
|
||||||
def __init__(self, subject=None):
|
def __init__(self, subject=None, addrWidth=16):
|
||||||
|
self.physMask = 0xffff
|
||||||
|
if addrWidth > 16:
|
||||||
|
# even with 32-bit address space, model only 256k memory
|
||||||
|
self.physMask = 0x3ffff
|
||||||
if subject is None:
|
if subject is None:
|
||||||
subject = 0x10000 * [0x00]
|
subject = (self.physMask+1) * [0x00]
|
||||||
|
|
||||||
self._subject = subject
|
self._subject = subject
|
||||||
|
|
||||||
self._read_subscribers = {}
|
self._read_subscribers = {}
|
||||||
self._write_subscribers = {}
|
self._write_subscribers = {}
|
||||||
|
|
||||||
def __setitem__(self, address, value):
|
def __setitem__(self, address, value):
|
||||||
|
address &= self.physMask
|
||||||
callbacks = self._write_subscribers.get(address, [])
|
callbacks = self._write_subscribers.get(address, [])
|
||||||
|
|
||||||
for callback in callbacks:
|
for callback in callbacks:
|
||||||
@ -19,6 +24,7 @@ class ObservableMemory:
|
|||||||
self._subject[address] = value
|
self._subject[address] = value
|
||||||
|
|
||||||
def __getitem__(self, address):
|
def __getitem__(self, address):
|
||||||
|
address &= self.physMask
|
||||||
callbacks = self._read_subscribers.get(address, [])
|
callbacks = self._read_subscribers.get(address, [])
|
||||||
final_result = None
|
final_result = None
|
||||||
|
|
||||||
@ -37,15 +43,18 @@ class ObservableMemory:
|
|||||||
|
|
||||||
def subscribe_to_write(self, address_range, callback):
|
def subscribe_to_write(self, address_range, callback):
|
||||||
for address in address_range:
|
for address in address_range:
|
||||||
|
address &= self.physMask
|
||||||
callbacks = self._write_subscribers.setdefault(address, [])
|
callbacks = self._write_subscribers.setdefault(address, [])
|
||||||
if callback not in callbacks:
|
if callback not in callbacks:
|
||||||
callbacks.append(callback)
|
callbacks.append(callback)
|
||||||
|
|
||||||
def subscribe_to_read(self, address_range, callback):
|
def subscribe_to_read(self, address_range, callback):
|
||||||
for address in address_range:
|
for address in address_range:
|
||||||
|
address &= self.physMask
|
||||||
callbacks = self._read_subscribers.setdefault(address, [])
|
callbacks = self._read_subscribers.setdefault(address, [])
|
||||||
if callback not in callbacks:
|
if callback not in callbacks:
|
||||||
callbacks.append(callback)
|
callbacks.append(callback)
|
||||||
|
|
||||||
def write(self, start_address, bytes):
|
def write(self, start_address, bytes):
|
||||||
|
start_address &= self.physMask
|
||||||
self._subject[start_address:start_address+len(bytes)] = bytes
|
self._subject[start_address:start_address+len(bytes)] = bytes
|
||||||
|
@ -8,6 +8,7 @@ import sys
|
|||||||
from asyncore import compact_traceback
|
from asyncore import compact_traceback
|
||||||
from py65.devices.mpu6502 import MPU as NMOS6502
|
from py65.devices.mpu6502 import MPU as NMOS6502
|
||||||
from py65.devices.mpu65c02 import MPU as CMOS65C02
|
from py65.devices.mpu65c02 import MPU as CMOS65C02
|
||||||
|
from py65.devices.mpu65Org16 import MPU as V65Org16
|
||||||
from py65.disassembler import Disassembler
|
from py65.disassembler import Disassembler
|
||||||
from py65.assembler import Assembler
|
from py65.assembler import Assembler
|
||||||
from py65.utils.addressing import AddressParser
|
from py65.utils.addressing import AddressParser
|
||||||
@ -16,13 +17,60 @@ from py65.utils.conversions import itoa
|
|||||||
from py65.memory import ObservableMemory
|
from py65.memory import ObservableMemory
|
||||||
|
|
||||||
class Monitor(cmd.Cmd):
|
class Monitor(cmd.Cmd):
|
||||||
|
mpulist = {'6502': NMOS6502, '65C02': CMOS65C02, '65Org16': V65Org16}
|
||||||
def __init__(self, mpu_type=NMOS6502, completekey='tab', stdin=None, stdout=None):
|
|
||||||
self._reset(mpu_type)
|
def __init__(self, mpu_type=NMOS6502, completekey='tab', stdin=None, stdout=None, argv=None):
|
||||||
|
self.mpu_type=mpu_type
|
||||||
|
if argv is None:
|
||||||
|
argv = sys.argv
|
||||||
|
self._reset(self.mpu_type)
|
||||||
self._width = 78
|
self._width = 78
|
||||||
self._update_prompt()
|
self._update_prompt()
|
||||||
self._add_shortcuts()
|
self._add_shortcuts()
|
||||||
cmd.Cmd.__init__(self, completekey, stdin, stdout)
|
cmd.Cmd.__init__(self, completekey, stdin, stdout)
|
||||||
|
self.parseArgs(argv)
|
||||||
|
|
||||||
|
def parseArgs(self, argv):
|
||||||
|
import getopt
|
||||||
|
try:
|
||||||
|
options, args = getopt.getopt(argv[1:], 'hm:l:r:g:',
|
||||||
|
['help', 'mpu=', 'load=', 'rom=', 'goto='])
|
||||||
|
except getopt.GetoptError, err:
|
||||||
|
print str(err)
|
||||||
|
self.usage()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
for opt, value in options:
|
||||||
|
if opt in ('-l','--load'):
|
||||||
|
self.do_load(value)
|
||||||
|
if opt in ('-r','--rom'):
|
||||||
|
# load a ROM and run from the reset vector
|
||||||
|
self.do_load("%s %d" % (value, -1))
|
||||||
|
physMask = self._mpu.memory.physMask
|
||||||
|
ResetTo = self._mpu.ResetTo & physMask
|
||||||
|
ResetDestination = self._mpu.memory[ResetTo] + (self._mpu.memory[ResetTo+1] << self.byteWidth)
|
||||||
|
self.do_goto("%08x" % ResetDestination)
|
||||||
|
if opt in ('-g','--goto'):
|
||||||
|
self.do_goto(value)
|
||||||
|
if opt in ('-m','--mpu'):
|
||||||
|
if not self.mpulist.get(value, None):
|
||||||
|
print "Fatal: no such mpu. Available MPUs:", ', '.join(self.mpulist.keys())
|
||||||
|
sys.exit(1)
|
||||||
|
self.do_mpu(value)
|
||||||
|
elif opt in ("-h", "--help"):
|
||||||
|
self.usage()
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
def usage(self):
|
||||||
|
print """\
|
||||||
|
\rUsage: monitor.py [options]
|
||||||
|
\rOptions:
|
||||||
|
-h, --help : Show this message
|
||||||
|
-m, --mpu <device> : Choose which MPU to emulate (default is 6502)
|
||||||
|
-l, --load <file> : Load a file at address 0
|
||||||
|
-r, --rom <file> : Load a rom at the top of address space and reset into it
|
||||||
|
-g, --goto <address> : Perform a goto command after loading any files
|
||||||
|
"""
|
||||||
|
|
||||||
def onecmd(self, line):
|
def onecmd(self, line):
|
||||||
line = self._preprocess_line(line)
|
line = self._preprocess_line(line)
|
||||||
@ -42,6 +90,12 @@ class Monitor(cmd.Cmd):
|
|||||||
|
|
||||||
def _reset(self, mpu_type):
|
def _reset(self, mpu_type):
|
||||||
self._mpu = mpu_type()
|
self._mpu = mpu_type()
|
||||||
|
self.addrWidth = self._mpu.addrWidth
|
||||||
|
self.byteWidth = self._mpu.byteWidth
|
||||||
|
self.addrFmt = self._mpu.addrFmt
|
||||||
|
self.byteFmt = self._mpu.byteFmt
|
||||||
|
self.addrMask = self._mpu.addrMask
|
||||||
|
self.byteMask = self._mpu.byteMask
|
||||||
self._install_mpu_observers()
|
self._install_mpu_observers()
|
||||||
self._address_parser = AddressParser()
|
self._address_parser = AddressParser()
|
||||||
self._disassembler = Disassembler(self._mpu, self._address_parser)
|
self._disassembler = Disassembler(self._mpu, self._address_parser)
|
||||||
@ -112,9 +166,13 @@ class Monitor(cmd.Cmd):
|
|||||||
byte = 0
|
byte = 0
|
||||||
return byte
|
return byte
|
||||||
|
|
||||||
m = ObservableMemory()
|
def blocking_getc(address):
|
||||||
|
return ord(console.getch(self.stdin))
|
||||||
|
|
||||||
|
m = ObservableMemory(addrWidth=self.addrWidth)
|
||||||
m.subscribe_to_write([0xF001], putc)
|
m.subscribe_to_write([0xF001], putc)
|
||||||
m.subscribe_to_read([0xF004], getc)
|
m.subscribe_to_read([0xF004], getc)
|
||||||
|
m.subscribe_to_read([0xF005], blocking_getc)
|
||||||
|
|
||||||
self._mpu.memory = m
|
self._mpu.memory = m
|
||||||
|
|
||||||
@ -147,18 +205,16 @@ class Monitor(cmd.Cmd):
|
|||||||
self._reset(mpu_type=klass)
|
self._reset(mpu_type=klass)
|
||||||
|
|
||||||
def do_mpu(self, args):
|
def do_mpu(self, args):
|
||||||
mpus = {'6502': NMOS6502, '65C02': CMOS65C02}
|
|
||||||
|
|
||||||
def available_mpus():
|
def available_mpus():
|
||||||
mpu_list = ', '.join(mpus.keys())
|
mpu_list = ', '.join(self.mpulist.keys())
|
||||||
self._output("Available MPUs: %s" % mpu_list)
|
self._output("Available MPUs: %s" % mpu_list)
|
||||||
|
|
||||||
if args == '':
|
if args == '':
|
||||||
self._output("Current MPU is %s" % self._mpu.name)
|
self._output("Current MPU is %s" % self._mpu.name)
|
||||||
available_mpus()
|
available_mpus()
|
||||||
else:
|
else:
|
||||||
requested = args.upper()
|
requested = args
|
||||||
new_mpu = mpus.get(requested, None)
|
new_mpu = self.mpulist.get(requested, None)
|
||||||
if new_mpu is None:
|
if new_mpu is None:
|
||||||
self._output("Unknown MPU: %s" % args)
|
self._output("Unknown MPU: %s" % args)
|
||||||
available_mpus()
|
available_mpus()
|
||||||
@ -201,7 +257,7 @@ class Monitor(cmd.Cmd):
|
|||||||
else:
|
else:
|
||||||
end = start + len(bytes)
|
end = start + len(bytes)
|
||||||
self._mpu.memory[start:end] = bytes
|
self._mpu.memory[start:end] = bytes
|
||||||
self.do_disassemble('%04x:%04x' % (start, end))
|
self.do_disassemble((self.addrFmt+":"+self.addrFmt) % (start, end))
|
||||||
|
|
||||||
def help_assemble(self):
|
def help_assemble(self):
|
||||||
self._output("assemble <address> <statement>")
|
self._output("assemble <address> <statement>")
|
||||||
@ -220,7 +276,7 @@ class Monitor(cmd.Cmd):
|
|||||||
assembling = True
|
assembling = True
|
||||||
|
|
||||||
while assembling:
|
while assembling:
|
||||||
prompt = "\r$%04x " % (start)
|
prompt = "\r$" + ( self.addrFmt % start ) + " " + (" " * (1 + self.byteWidth/4) * 3)
|
||||||
line = console.line_input(prompt,
|
line = console.line_input(prompt,
|
||||||
stdin=self.stdin, stdout=self.stdout)
|
stdin=self.stdin, stdout=self.stdout)
|
||||||
|
|
||||||
@ -231,7 +287,7 @@ class Monitor(cmd.Cmd):
|
|||||||
# assemble into memory
|
# assemble into memory
|
||||||
bytes = self._assembler.assemble(line, pc=start)
|
bytes = self._assembler.assemble(line, pc=start)
|
||||||
if bytes is None:
|
if bytes is None:
|
||||||
self.stdout.write("\r$%04x ???\n" % start)
|
self.stdout.write("\r$" + (self.addrFmt % start) + " ???\n")
|
||||||
continue
|
continue
|
||||||
end = start + len(bytes)
|
end = start + len(bytes)
|
||||||
self._mpu.memory[start:end] = bytes
|
self._mpu.memory[start:end] = bytes
|
||||||
@ -258,9 +314,11 @@ class Monitor(cmd.Cmd):
|
|||||||
def _format_disassembly(self, address, bytes, disasm):
|
def _format_disassembly(self, address, bytes, disasm):
|
||||||
mem = ''
|
mem = ''
|
||||||
for byte in self._mpu.memory[address:address+bytes]:
|
for byte in self._mpu.memory[address:address+bytes]:
|
||||||
mem += '%02x ' % byte
|
mem += self.byteFmt % byte + " "
|
||||||
|
|
||||||
return "$%04x %-10s%s" % (address, mem, disasm)
|
fieldwidth = 1 + (1 + self.byteWidth/4) * 3
|
||||||
|
fieldfmt = "%%-%ds" % fieldwidth
|
||||||
|
return "$" + self.addrFmt % address + " " + fieldfmt % mem + disasm
|
||||||
|
|
||||||
def help_disassemble(self):
|
def help_disassemble(self):
|
||||||
self._output("disassemble <address_range>")
|
self._output("disassemble <address_range>")
|
||||||
@ -272,7 +330,7 @@ class Monitor(cmd.Cmd):
|
|||||||
|
|
||||||
def do_step(self, args):
|
def do_step(self, args):
|
||||||
self._mpu.step()
|
self._mpu.step()
|
||||||
self.do_disassemble('%04x' % self._mpu.pc)
|
self.do_disassemble(self.addrFmt % self._mpu.pc)
|
||||||
|
|
||||||
def help_return(self):
|
def help_return(self):
|
||||||
self._output("return")
|
self._output("return")
|
||||||
@ -338,7 +396,7 @@ class Monitor(cmd.Cmd):
|
|||||||
return
|
return
|
||||||
|
|
||||||
self._output("+%u" % num)
|
self._output("+%u" % num)
|
||||||
self._output("$%02x" % num)
|
self._output("$" + self.byteFmt % num)
|
||||||
self._output("%04o" % num)
|
self._output("%04o" % num)
|
||||||
self._output(itoa(num, 2).zfill(8))
|
self._output(itoa(num, 2).zfill(8))
|
||||||
|
|
||||||
@ -360,9 +418,9 @@ class Monitor(cmd.Cmd):
|
|||||||
self._output("Invalid register: %s" % register)
|
self._output("Invalid register: %s" % register)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
intval = self._address_parser.number(value) & 0xFFFF
|
intval = self._address_parser.number(value) & self.addrMask
|
||||||
if len(register) == 1:
|
if len(register) == 1:
|
||||||
intval &= 0xFF
|
intval &= self.byteMask
|
||||||
setattr(self._mpu, register, intval)
|
setattr(self._mpu, register, intval)
|
||||||
except KeyError, why:
|
except KeyError, why:
|
||||||
self._output(why[0])
|
self._output(why[0])
|
||||||
@ -399,6 +457,7 @@ class Monitor(cmd.Cmd):
|
|||||||
|
|
||||||
filename = split[0]
|
filename = split[0]
|
||||||
if len(split) == 2:
|
if len(split) == 2:
|
||||||
|
# if the start address is -1, we will adjust it later
|
||||||
start = self._address_parser.number(split[1])
|
start = self._address_parser.number(split[1])
|
||||||
else:
|
else:
|
||||||
start = self._mpu.pc
|
start = self._mpu.pc
|
||||||
@ -412,7 +471,16 @@ class Monitor(cmd.Cmd):
|
|||||||
self._output(msg)
|
self._output(msg)
|
||||||
return
|
return
|
||||||
|
|
||||||
self._fill(start, start, map(ord, bytes))
|
# if the start address was -1, we load to top of memory
|
||||||
|
if start == -1:
|
||||||
|
start = self.addrMask - len(bytes)/(self.byteWidth/8) + 1
|
||||||
|
|
||||||
|
if self.byteWidth==8:
|
||||||
|
bytes=map(ord, bytes)
|
||||||
|
elif self.byteWidth==16:
|
||||||
|
bytes=map(lambda msb,lsb: (ord(msb)<<8)+ord(lsb),bytes[0::2],bytes[1::2])
|
||||||
|
|
||||||
|
self._fill(start, start, bytes)
|
||||||
|
|
||||||
def do_save(self, args):
|
def do_save(self, args):
|
||||||
split = shlex.split(args)
|
split = shlex.split(args)
|
||||||
@ -428,7 +496,9 @@ class Monitor(cmd.Cmd):
|
|||||||
try:
|
try:
|
||||||
f = open(filename, 'wb')
|
f = open(filename, 'wb')
|
||||||
for byte in bytes:
|
for byte in bytes:
|
||||||
f.write(chr(byte))
|
# output each octect from msb first
|
||||||
|
for shift in range (self.byteWidth-8,-1,-8):
|
||||||
|
f.write(chr((byte>>shift) & 0xff))
|
||||||
f.close()
|
f.close()
|
||||||
except (OSError, IOError), why:
|
except (OSError, IOError), why:
|
||||||
msg = "Cannot save file: [%d] %s" % (why[0], why[1])
|
msg = "Cannot save file: [%d] %s" % (why[0], why[1])
|
||||||
@ -465,19 +535,20 @@ class Monitor(cmd.Cmd):
|
|||||||
|
|
||||||
if start == end:
|
if start == end:
|
||||||
end = start + length - 1
|
end = start + length - 1
|
||||||
if (end > 0xFFFF):
|
if (end > self.addrMask):
|
||||||
end = 0xFFFF
|
end = self.addrMask
|
||||||
|
|
||||||
while address <= end:
|
while address <= end:
|
||||||
address &= 0xFFFF
|
address &= self.addrMask
|
||||||
self._mpu.memory[address] = (filler[index] & 0xFF)
|
self._mpu.memory[address] = (filler[index] & self.byteMask)
|
||||||
index += 1
|
index += 1
|
||||||
if index == length:
|
if index == length:
|
||||||
index = 0
|
index = 0
|
||||||
address += 1
|
address += 1
|
||||||
|
|
||||||
fmt = (end - start + 1, start, end)
|
fmt = (end - start + 1, start, end)
|
||||||
self._output("Wrote +%d bytes from $%04x to $%04x" % fmt)
|
starttoend = "$" + self.addrFmt + " to $" + self.addrFmt
|
||||||
|
self._output(("Wrote +%d bytes from " + starttoend) % fmt)
|
||||||
|
|
||||||
def help_mem(self):
|
def help_mem(self):
|
||||||
self._output("mem <address_range>")
|
self._output("mem <address_range>")
|
||||||
@ -486,15 +557,15 @@ class Monitor(cmd.Cmd):
|
|||||||
def do_mem(self, args):
|
def do_mem(self, args):
|
||||||
start, end = self._address_parser.range(args)
|
start, end = self._address_parser.range(args)
|
||||||
|
|
||||||
line = "%04x:" % start
|
line = self.addrFmt % start + ":"
|
||||||
for address in range(start, end+1):
|
for address in range(start, end+1):
|
||||||
byte = self._mpu.memory[address]
|
byte = self._mpu.memory[address]
|
||||||
more = " %02x" % byte
|
more = " " + self.byteFmt % byte
|
||||||
|
|
||||||
exceeded = len(line) + len(more) > self._width
|
exceeded = len(line) + len(more) > self._width
|
||||||
if exceeded:
|
if exceeded:
|
||||||
self._output(line)
|
self._output(line)
|
||||||
line = "%04x:" % address
|
line = self.addrFmt % address + ":"
|
||||||
line += more
|
line += more
|
||||||
self._output(line)
|
self._output(line)
|
||||||
|
|
||||||
@ -524,7 +595,7 @@ class Monitor(cmd.Cmd):
|
|||||||
byaddress = zip(values, keys)
|
byaddress = zip(values, keys)
|
||||||
byaddress.sort()
|
byaddress.sort()
|
||||||
for address, label in byaddress:
|
for address, label in byaddress:
|
||||||
self._output("%04x: %s" % (address, label))
|
self._output(self.addrFmt % address + ": " + label)
|
||||||
|
|
||||||
def help_delete_label(self):
|
def help_delete_label(self):
|
||||||
self._output("delete_label <label>")
|
self._output("delete_label <label>")
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user