mirror of
https://github.com/safiire/n65.git
synced 2025-03-03 01:30:07 +00:00
Added zp suffix to explicitly force zero page instructions when using symbols
This commit is contained in:
parent
8e788a19d1
commit
0c6c8ab80e
36
README.md
36
README.md
@ -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
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user