You'll need to make a directory for the project. Put the second two files (The Makefile and the .cfg file) there.
Add a directory under that called "asm" and put the .asm file there.
Read the comments in the files to understand what's going on.
If posting the files this way turns out to be stupid and wrong... I'll zip them and put them in the downloads section.
These are the three files:
First, the assembly file itself, "rom_0.asm"
Code: Select all
.pc02 ; Tell the assembler to use the "65c02" opcode set.
.org $C000 ; Start the program at $c000 hex... this is the start of the rom bank space.
.segment "CODE" ; Start the segment where code will be.
; The segments are defined in the file "rom_0.cfg"
VERA_addr_low = $9f20
VERA_addr_high = $9f21
VERA_addr_bank = $9f22
VERA_data0 = $9f23
VERA_ctrl = $9f25
VERA_dc_video = $9f29
VERA_dc_hscale = $9f2a
VERA_dc_vscale = $9f2b
VERA_L1_config = $9f34
VERA_L1_mapbase = $9f35
VERA_L1_tilebase = $9f36
VRAM_L1_tilebase = $1f000 ; To $1f7ff (2K)
VRAM_L1_mapbase = $1b000 ; To $1efff (16K)
; Character set to write into "VRAM_L1_tilebase."
; We only need to spell "HELLO WORLD", so that's 8 unique characters.
; Each character requires 8 bytes of data. The order of the characters will
; be " HELOWRD". Space is first so we can just use 0 to clear the screen.
char_0: .byte $00,$00,$00,$00,$00,$00,$00,$00 ;Space
char_1: .byte $00,$66,$66,$66,$7e,$66,$66,$66 ;H
char_2: .byte $00,$7e,$60,$60,$78,$60,$60,$7e ;E
char_3: .byte $00,$60,$60,$60,$60,$60,$60,$7e ;L
char_4: .byte $00,$3c,$66,$66,$66,$66,$66,$3c ;O
char_5: .byte $00,$63,$63,$63,$63,$6b,$77,$63 ;W
char_6: .byte $00,$7c,$66,$66,$7c,$78,$6c,$66 ;R
char_7: .byte $00,$78,$6c,$66,$66,$66,$6c,$78 ;D
hello_world: .byte 1,2,3,3,4,0,5,4,6,3,7
; Execution starts here when the CPU comes off reset, it gets the address
; from the "reset" word in the vectors at the very end of the ROM.
rom_0_main:
; It's unlikely that the VERA reset and delay are needed.
; Reset VERA.
lda #1
sta VERA_ctrl
; Delay to ensure reset has finished.
ldx #0
@vera_reset_delay:
inx
bne @vera_reset_delay
stz VERA_ctrl ; Reset off, data0 selected.
; To use data1, load $01 into VERA_ctrl.
; See the Vera Programmer's Reference for specific instructions for
; each of the VERA's registers.
; Write the characters we need into the tilebase for layer 1:
; Set the VRAM address VERA will write to.
; Note: If you want to use 2 VRAM addresses at once (Ex: copying from one
; place in VRAM to another.) set one address to data0 and the other to data1.
lda #<VRAM_L1_tilebase
sta VERA_addr_low
lda #>VRAM_L1_tilebase
sta VERA_addr_high
lda #^VRAM_L1_tilebase
ora #$10 ; Use a stride of 1 and data0 as the data port.
sta VERA_addr_bank
ldx #0
@tilebase_byte_loop:
lda char_0,x
sta VERA_data0
inx
cmp #63
bne @tilebase_byte_loop
; Zero out the mapbase for layer 1:
; Set the VRAM address VERA will write to.
lda #<VRAM_L1_mapbase
sta VERA_addr_low
lda #>VRAM_L1_mapbase
sta VERA_addr_high
lda #^VRAM_L1_mapbase
ora #$10 ; Use a stride of 1 and data0 as the data port.
sta VERA_addr_bank
ldy #32 ; 32 loops of 256 = 8K entries of 2 bypes per entry (char, color) 16K total.
ldx #0
@mapbase_page_loop:
@mapbase_byte_loop:
lda #0 ; Character 0.
sta VERA_data0
lda #$b1 ; Color 0. Dark grey background, white text.
sta VERA_data0
inx
bne @mapbase_byte_loop
dey
bne @mapbase_page_loop
; Initialize VERA for layer 1 on in 16 color text mode, 8 by 8 tiles.
lda #$21 ; Layer 1 active plus VGA mode.
sta VERA_dc_video
lda #$f8 ; Base address of $1f000 plus 8 x 8 tiles.
sta VERA_L1_tilebase ; Where the tiles are stored. (Ex: The letters A, B, etc.)
lda #$d8 ; Base address of $1b000
sta VERA_L1_mapbase ; Where the tile map is stored. (Where on the screen the letters go.)
; Embiggen the characters! Set the display scale to 32 (20 x 15 visible characters.)
lda #32
sta VERA_dc_hscale
sta VERA_dc_vscale
; Write the "HELLO WORLD" message to the screen.
; Set the VRAM address VERA will write to.
lda #<VRAM_L1_mapbase
sta VERA_addr_low
lda #>VRAM_L1_mapbase
sta VERA_addr_high
lda #^VRAM_L1_mapbase
ora #$10 ; Use a stride of 1 and data0 as the data port.
sta VERA_addr_bank
ldx #0
@loop_hello_world:
lda hello_world,x
sta VERA_data0
lda #$b3
sta VERA_data0
inx
cpx #11
bne @loop_hello_world
; Loop forever.
@forever:
bra @forever
; The assembler will put a "AA" byte in every location between here and the
; "VECTORS" segment. Segments are how you put stuff exactly where you want.
; Start the segment where the reset vectors are. (nmi, reset, and irq)
.segment "VECTORS"
.word rom_0_main ; NMI wikk just restart the program.
.word rom_0_main ; Reset vector that starts execution.
.word rom_0_main ; IRQ was neven enabled.
Code: Select all
# The MEMORY section defines each segment declared in the
# SEGMENTS section. In the code, the ".segment" directive
# indicates where a segment defined in this file starts.
# There can be 255 segments in a file, and 65K segments globally.
MEMORY {
CODE: start = $C000, size = $3FFA fill=yes, fillval=$AA;
VECTORS: start = $FFFA, size = $0006 fill=yes, fillval=$AA;
}
SEGMENTS {
CODE: load = CODE, type = ro;
VECTORS: load = VECTORS, type = ro;
}
Code: Select all
# Basically, make files use rules to determine what to do, the rules are in
# a heirarchy... this rule needs that rule to be done first, etc. Make's job
# is to sort that heirarchy and run the commands in each rule in order.
# If no rule was specified on the command line, then the first one is done
# by default. By convention, it's called "all" and does a full build.
# In this makefile, all "all" has to do is run the cl65 command line and then
# copy the resulting rom.bin file to the runtime directory. NOTE: Your runtime
# directory will be different and you'll need to change the directory.
# Note that the lines under each rule MUST start with a tab character on each line.
# And yes, the commands in the rules are basically just commands you would
# type into a terminal window, but make can also do some really complex replacement
# using it's own parsing language. I like to keep my make files as simmple as possible.
all:
cl65 -C rom_0.cfg -o rom_0.bin -l rom_0.list -Ln rom_0.sym asm/rom_0.asm
cp rom_0.bin ../../runtime/r43
# See the cc65 docs for an explanation of the options themselves, but in a
# nutshell, cl65 is doing the following:
# Using the rom_0.cfg file, it compiles the asm/rom_0.asm file (And any other
# files after it) with the appropriate compiler, then links all the compiled
# object files into the output file "rom.bin." As it does this, it creates
# the listing file "rom_0.list" and the symbol table file "rom_0.sym." These
# two files aren't needed by the X16, they're just documentation.
# After you have the "rom_0.bin" file in the correect directory, run the emulator
# from a terminal with "x16emu -scale 2 -rom rom_0.bin"