1
0
mirror of https://github.com/mnaberez/py65.git synced 2024-09-06 10:54:32 +00:00
Conflicts:
	src/py65/assembler.py
	src/py65/disassembler.py
	src/py65/tests/devices/test_mpu6502.py
This commit is contained in:
Mike Naberezny 2012-01-01 14:59:55 -08:00
commit a991a7862c
12 changed files with 997 additions and 552 deletions

235
examples/65Org16.boot.asm Normal file
View 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

Binary file not shown.

12
examples/README.txt Normal file
View 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
View 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
View File

@ -0,0 +1,3 @@
;020000040000FA
;1202000000A5F005004900200085F001004C0200000025
;00000001FF

View File

@ -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()

View File

@ -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

View 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"
)

View File

@ -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)

View File

@ -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

View File

@ -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