1
0
mirror of https://github.com/safiire/n65.git synced 2024-06-09 23:29:53 +00:00

Added zp suffix to explicitly force zero page instructions when using symbols

This commit is contained in:
Safiire 2015-03-12 08:54:59 -07:00
parent 8e788a19d1
commit 0c6c8ab80e
3 changed files with 72 additions and 88 deletions

View File

@ -42,8 +42,8 @@ An NES assembler for the 6502 microprocessor written in Ruby
I hoped to make writing NES libraries more effective since you can basically
namespace your symbols into your own file and not mess with anyone
else's code. I also have a feeling that this will have some use in
creating in memory data structures similar to a C struct in the future.
else's code. I also have also been able to use this to create C style
structs in the memory layout, ie `sprite.x`.
The assembler does two passes over your code, any symbols that are used
which it hasn't seen the definition for yet return a "promise", that
@ -60,22 +60,9 @@ An NES assembler for the 6502 microprocessor written in Ruby
![Scrolling NES Demo](github_images/assembler_demo.png)
Some Todos:
- Get an NSF file playing
- I may make this into a Rubygem
- Maybe I can put some better error messages.
- I would like to add some Macros to generate settings for
the PPU and APU, (values for locations like $2000 and $2001,
the $4000s, etc.) Put into a library.
- Support binary literals ie %10101010
- Give this project a better name.
- Create an interactive read eval compile loop?
- Add scoping directives
- Add struct directive
- Add macro/symbol define directive, name memory
- Make an interactive mode
Some new additions:
# Some new additions:
- C Style in memory structs using .scope and .space directives
- Explicit usage of zero page instructions with the zp suffix
- Split the Parser into its own class
- New MemorySpace class
- Rewrote the Assembler class
@ -83,6 +70,7 @@ An NES assembler for the 6502 microprocessor written in Ruby
- Rewrote all directive's classes
- Split the assembler from the commandline front-end
- Scoped Symbol Table
- Anonymous Scopes
- Lower case mnemonics and hex digits
- Ported NES101 tutor to this assembler.
- Added msb and lsb byte selectors on address labels
@ -92,9 +80,21 @@ An NES assembler for the 6502 microprocessor written in Ruby
- added .incbin directive
- added .ascii directive
- added .segment directive
- added .scope directive
- added .space directive
- Invented my own iNES header directive that is JSON
- Split the project up into separate files per class
- Wrote some more unit tests
- Added OptionParser for commandline opts
- Tested a ROM with Sound output
- Tested a ROM that changes background color
# Some Todos:
- Support binary %10101010 addresses and literals
- Create a library which names important NES addresses
- Make macros that can be used interchangably inline or as a subroutine
- Create a library for common operations, DMA, sound, etc both inline and subroutine options
- Give this project a better name.
- Create an interactive read eval compile loop?
- Make an interactive mode

View File

@ -10,10 +10,17 @@
.ines {"prog": 1, "char": 1, "mapper": 0, "mirror": 0}
;;;;
; Here is a good spot to associate zero page memory addresses
; with quick access variables in the program.
; Open the prog section bank 0
.segment prog 0
;;;;
; Here is a good spot to associate zero page memory addresses
; to symbols that we can use throughout the program.
.org $0000
.space dx 1
.space a_button 1
.space scroll 1
;;;;
; We can use scope to declare a C like struct at $0200
@ -112,7 +119,7 @@
; Initialize the controller input, keeping track of the A button
.scope init_input
lda #$00
sta $01 ; $01 = A button
sta a_button zp
rts
.
@ -143,14 +150,14 @@
; initialize Sprite 0
lda #$70
sta sprite.y ; Store sprite y coordinate ;sta $0200 ; sprite Y coordinate
sta sprite.y ; Initialize the y value of sprite
lda #$01
sta sprite.pattern ; sta $0201 ; sprite + 1 Pattern number
sta sprite.x ;sta $0203 ; sprite + 3 X coordinate, sprite + 2, color, stays 0.
sta sprite.pattern ; Pattern number 1
sta sprite.x ; X value also 1, and leave color 0
; Set initial value of dx
lda #$01
sta $00 ; dx = $00
sta dx zp ; Initialize delta x value to 1
rts
.
@ -198,22 +205,24 @@
ldy #$00
ldx #$04
lda #$00
back:
sta $2007
iny
bne back
dex
bne back
.scope ; We can reuse loop, in an anonymous scope, if we want
loop:
sta $2007
iny
bne loop
dex
bne loop
.
rts
.
;;;;
; This initializes the scrolling storing the scroll
; value in the zero page variable $02
; This initializes the scrolling value in the zero page
; So that we begin offscreen and can scroll down
.scope init_scrolling
lda #$F0
sta $02
sta scroll zp
rts
.
@ -229,18 +238,18 @@ update_sprite:
bne edge_done
; Hit right
ldx #$FF
stx $00 ; dx
stx dx zp
jsr high_c
jmp edge_done
hit_left:
ldx #$01
stx $00 ; dx
stx dx zp
jsr high_c
edge_done: ; update X and store it.
clc
adc $00 ; dx
adc dx zp
sta sprite.x
rts
@ -251,15 +260,15 @@ react_to_input:
sta $4016
lda $4016 ; Is the A button down?
AND #$01
and #$01
beq not_a
ldx $01 ; a
ldx a_button zp
bne a_done ; Only react if the A button wasn't down last time.
sta $01 ; Store the 1 in local variable 'a' so that we this is
sta a_button zp ; Store the 1 in local variable 'a' so that we this is
jsr reverse_dx ; only called once per press.
jmp a_done
not_a:
sta $01 ; A has been released, so put that zero into 'a'.
sta a_button zp ; A has been released, so put that zero into 'a'.
a_done:
lda $4016 ; B does nothing
lda $4016 ; Select does nothing
@ -287,10 +296,10 @@ react_to_input:
reverse_dx:
lda #$FF
eor $00 ; dx Toggles between 0x1 and 0xfe (-1)
eor dx zp ; dx Toggles between 0x1 and 0xfe (-1)
clc
adc #$01 ; add dx, and store to variable
sta $00 ; dx
sta dx zp
jsr low_c
rts
@ -299,10 +308,10 @@ scroll_screen:
stx $2006
stx $2006
ldx $02 ; scroll ; Do we need to scroll at all?
ldx scroll zp ; scroll ; Do we need to scroll at all?
beq no_scroll
dex
stx $02 ; scroll
stx scroll zp ; scroll
lda #$00
sta $2005 ; Write 0 for Horiz. Scroll value
stx $2005 ; Write the value of 'scroll' for Vert. Scroll value
@ -407,7 +416,7 @@ bg:
;;;;
; This is CHR-ROM page 1, which starts at 0x0000, but I'm skipping the first bit because
; This is CHR-ROM bank 0, which starts at 0x0000, but I'm skipping the first $0200 because
; the first bunch of ASCII characters are not represented. This is the commodore 64's
; character ROM.
.segment char 0

View File

@ -12,6 +12,7 @@ module Assembler6502
class UnresolvedSymbols < StandardError; end
class InvalidAddressingMode < StandardError; end
class AddressOutOfRange < StandardError; end
class ArgumentTooLarge < StandardError; end
Mnemonic = '([A-Za-z]{3})'
Hex8 = '\$([A-Fa-f0-9]{2})'
@ -46,19 +47,22 @@ module Assembler6502
:zero_page => {
:example => 'AAA $FF',
:display => '%s $%.2X',
:regex => /^#{Mnemonic}\s+#{Hex8}$/
:regex => /^#{Mnemonic}\s+#{Hex8}$/,
:regex_label => /^#{Mnemonic}\s+#{Sym}\s+zp$/
},
:zero_page_x => {
:example => 'AAA $FF, X',
:display => '%s $%.2X, X',
:regex => /^#{Mnemonic}\s+#{Hex8}\s?,\s?#{XReg}$/
:regex => /^#{Mnemonic}\s+#{Hex8}\s?,\s?#{XReg}$/,
:regex_label => /^#{Mnemonic}\s+#{Sym}\s?,\s?#{XReg}\s+zp$/
},
:zero_page_y => {
:example => 'AAA $FF, Y',
:display => '%s $%.2X, Y',
:regex => /^#{Mnemonic}\s+#{Hex8}\s?,\s?#{YReg}$/
:regex => /^#{Mnemonic}\s+#{Hex8}\s?,\s?#{YReg}$/,
:regex_label => /^#{Mnemonic}\s+#{Sym}\s?,\s?#{YReg} zp$/
},
:absolute => {
@ -187,6 +191,13 @@ module Assembler6502
end
####
## Return if this instruction is a zero page instruction
def zero_page_instruction?
[:zero_page, :zero_page_x, :zero_page_y].include?(@mode)
end
####
## Execute writes the emitted bytes to virtual memory, and updates PC
## If there is a symbolic argument, we can try to resolve it now, or
@ -248,6 +259,9 @@ module Assembler6502
when 1
[@hex]
when 2
if zero_page_instruction? && @arg < 0 || @arg > 0xff
fail(ArgumentTooLarge, "For #{@op} in #{@mode} mode, only 8-bit values are allowed")
end
[@hex, @arg]
when 3
[@hex] + break_16(@arg)
@ -257,45 +271,6 @@ module Assembler6502
end
####
## Resolve symbols
=begin
def resolve_symbols(symbols)
if unresolved_symbols?
if symbols[@arg].nil?
fail(SyntaxError, "Unknown symbol #{@arg.inspect}")
end
## It is possible to resolve a symbol to a 16-bit address and then
## use byte_selector to select the msb or lsb
unless @byte_selector.nil?
arg_16 = symbols[@arg].address
@arg = case @byte_selector
when :>
high_byte(arg_16)
when :<
low_byte(arg_16)
end
return @arg
end
## Based on this instructions length, we should resolve the address
## to either an absolute one, or a relative one. The only relative addresses
## are the branching ones, which are 2 bytes in size, hence the extra 2 byte difference
case @length
when 2
@arg = symbols[@arg].address - @address - 2
when 3
@arg = symbols[@arg].address
else
fail(SyntaxError, "Probably can't use symbol #{@arg.inspect} with #{@op}")
end
end
end
=end
####
## Pretty Print
def to_s