Overhaul the template.

- DATA and RODATA segments added to computer.cfg
 - RAM memory configuration now starts at  to not overwrite the cpu stack
 - hardware.inc added to handle hardware related constants
 - constants removed from via.s
 - constants removed from serial.s
 - serial_delay from serial.s is now dynamic, dependant on CPU_HZ and baud rate
 - serial_baud_* functions added to setup 19200 and 9600 baud
 - mem_copy.s added. data copy functions are to be implemented here
 - copy_data (mem_copy.s) implemented to copy the DATA segment from ROM to RAM
 - propper system setup in main.s
 - Makefile now generates a .map file with the memory map information
This commit is contained in:
RuiFPB committed 2026-09-29 12:14:05 +01:00
1 parent a8caf9db75
commit 3801459e1e
8 files changed
+202 -73

No files matched your search

+3
View File
@@ -31,3 +31,6 @@ bin/
.idea/ .idea/
symon*.jar
*.bin
*.map
+3 -1
View File
@@ -1,6 +1,8 @@
OUTPUT := example.bin OUTPUT := example.bin
TARGET_CPU := 65c02 TARGET_CPU := 65c02
MEM_MAP_FILE := $(OUTPUT:.bin=.map)
# Tool list # Tool list
CC65 := cc65 CC65 := cc65
CA65 := ca65 CA65 := ca65
@@ -20,7 +22,7 @@ all: $(OUTPUT)
$(OUTPUT): $(C_SOURCES) $(ASM_SOURCES) $(OUTPUT): $(C_SOURCES) $(ASM_SOURCES)
@mkdir -p $(dir $@) @mkdir -p $(dir $@)
$(CL65) -t none --cpu $(TARGET_CPU) -C computer.cfg -O -o $@ $^ $(CL65) -t none --cpu $(TARGET_CPU) -C computer.cfg -m $(MEM_MAP_FILE) -O -o $@ $^
clean: clean:
rm -f $(OBJECTS) rm -f $(OBJECTS)
+3 -1
View File
@@ -1,7 +1,7 @@
# Physical Memory configuration # Physical Memory configuration
MEMORY { MEMORY {
ZP: start = $00, size = $0100, type = rw, file = ""; ZP: start = $00, size = $0100, type = rw, file = "";
RAM: start = $0100, size = $7e00, type = rw, file = ""; RAM: start = $0200, size = $7d00, type = rw, file = "";
PRG: start = $C000, size = $4000, type = ro, file = %O, fill = yes, fillval = $00; PRG: start = $C000, size = $4000, type = ro, file = %O, fill = yes, fillval = $00;
} }
@@ -10,5 +10,7 @@ SEGMENTS {
ZEROPAGE: load = ZP, type = zp; ZEROPAGE: load = ZP, type = zp;
BSS: load = RAM, type = bss; BSS: load = RAM, type = bss;
CODE: load = PRG, type = ro, start = $C000; CODE: load = PRG, type = ro, start = $C000;
DATA: load = PRG, run = RAM, type = rw, define = yes;
RODATA: load = PRG, type = ro;
VECTORS: load = PRG, type = ro, start = $FFFA; VECTORS: load = PRG, type = ro, start = $FFFA;
} }
+31
View File
@@ -0,0 +1,31 @@
.ifndef HARDWARE_INC
HARDWARE_INC = 1
; CPU settings
CPU_HZ = 1843200
; Serial/ACIA addresses
SERIAL_DATA = $8400
SERIAL_STATUS = $8401
SERIAL_COMMAND = $8402
SERIAL_CONTROL = $8403
; VIA addresses
VIA_PORTB = $8000
VIA_PORTA = $8001
VIA_DDRB = $8002
VIA_DDRA = $8003
VIA_T1C_L = $8004
VIA_T1C_H = $8005
VIA_T1L_L = $8006
VIA_T1L_H = $8007
VIA_T2C_L = $8008
VIA_T2C_H = $8009
VIA_SR = $800a
VIA_ACR = $800b
VIA_PCR = $800c
VIA_IFR = $800d
VIA_IER = $800e
VIA_PORTA_2 = $800f
.endif
+59 -29
View File
@@ -1,14 +1,38 @@
; Serial port helper function ; Serial port helper function
; Serial is handled by the 65c51n (this thing has a few bugs) ; Serial is handled by the 65c51n (this thing has a few bugs)
.export serial_setup .export serial_setup, serial_recv_char, serial_print_char, serial_baud_19200, serial_baud_9600
.export serial_recv_char
.export serial_print_char
SERIAL_DATA = $8400 .include "hardware.inc"
SERIAL_STATUS = $8401
SERIAL_COMMAND = $8402
SERIAL_CONTROL = $8403
; -----------------------------
; Active wait loop calculations
; -----------------------------
INNER_LOOP = 9 ; Inner loop count (sets granularity of the waiting period)
; cycles per outer pass: ldx(2) + innerLoop(5*INNER_LOOP-1) + dey(2) + bne(3)
PASS_CYC = INNER_LOOP * 5 + 6
; passes for each 10bit frame (8N1), rounded up
PASSES_19200 = (10 * CPU_HZ/19200 + PASS_CYC - 1) / PASS_CYC
PASSES_9600 = (10 * CPU_HZ/9600 + PASS_CYC - 1) / PASS_CYC
.assert PASSES_9600 < 256, error, "CPU clock too fast for this delay; raise INNER_LOOP"
; ACIA control register presets: internal BAUD, 8N1
SERIAL_CTRL_19200 = %00011111
SERIAL_CTRL_9600 = %00011110
; ||||````- 19200/9600 baud
; |||`----- Receiver clock is baud rate
; |``------ 8bit word
; `-------- 1 stop bit
; -----------------------------
; -----------------------------
.zeropage
tx_passes: .res 1 ; Variable to store outer loop passes
.code
serial_setup: serial_setup:
; 65c51n has a few bugs. check http://forum.6502.org/viewtopic.php?f=4&t=2543&start=30#p29795 ; 65c51n has a few bugs. check http://forum.6502.org/viewtopic.php?f=4&t=2543&start=30#p29795
lda #$00 lda #$00
@@ -21,19 +45,21 @@ serial_setup:
; |`------ Receiver normal mode ; |`------ Receiver normal mode
; `------- Parity disabled ; `------- Parity disabled
lda #$1f ; %0001 1111 jsr serial_baud_19200
sta SERIAL_CONTROL ; |||| ````- 19200 baud
; |||`------ Receiver clock is baud rate
; |``------- 8bit word
; `--------- 1 stop bit
rts rts
serial_print_char: serial_baud_19200:
pha ; Save A to stack (character) lda #SERIAL_CTRL_19200
lda SERIAL_STATUS ; read status register and ignore the value sta SERIAL_CONTROL
pla ; return character from stack lda #PASSES_19200
sta SERIAL_DATA ; send character sta tx_passes
jsr serial_delay ; comment out for older 6551/65c51 rts
serial_baud_9600:
lda #SERIAL_CTRL_9600
sta SERIAL_CONTROL
lda #PASSES_9600
sta tx_passes
rts rts
serial_recv_char: serial_recv_char:
@@ -43,18 +69,22 @@ serial_recv_char:
lda SERIAL_DATA ; get byte lda SERIAL_DATA ; get byte
rts rts
serial_print_char:
sta SERIAL_DATA ; send character
;fall through to the delay routine
serial_delay: serial_delay:
phy ; save y reg phy ; save y reg
phx ; save x register phx ; save x register
ldy #6 ; delay value (clock rate in mhz 2 clock cycles) ldy tx_passes ; outer passes
@ydelay: @ydelay:
ldx #$68 ; Init x register ldx #INNER_LOOP ; Init x register
@xdelay: @xdelay: ; loop time was calculated above by the assembler
dex ; decrement low index dex ; decrement low index
bne @xdelay ; loop back until x is 0 bne @xdelay ; loop back until x is 0
dey ; decrement high dey ; decrement high
bne @ydelay ; loop until y is 0 bne @ydelay ; loop until y is 0
@delay_done: @delay_done:
plx ; return x plx ; return x
ply ; return y ply ; return y
rts rts
+1 -18
View File
@@ -1,18 +1 @@
; Addresses for the VIA 65c22 .include "hardware.inc"
VIA_PORTB = $8000
VIA_PORTA = $8001
VIA_DDRB = $8002
VIA_DDRA = $8003
VIA_T1C_L = $8004
VIA_T1C_H = $8005
VIA_T1L_L = $8006
VIA_T1L_H = $8007
VIA_T2C_L = $8008
VIA_T2C_H = $8009
VIA_SR = $800a
VIA_ACR = $800b
VIA_PCR = $800c
VIA_IFR = $800d
VIA_IER = $800e
VIA_PORTA_2 = $800f
+56 -24
View File
@@ -1,40 +1,72 @@
.import serial_setup .import serial_setup, serial_print_char, serial_recv_char
.import serial_print_char .import copy_data
.segment "ZEROPAGE" ; ---------------
; ZP
; ---------------
.zeropage
; Zero-page variables ($0000 - $00ff) ; Zero-page variables ($0000 - $00ff)
.segment "BSS" ; ---------------
; RAM variables ($0100 - $7fff) ; BSS
; ---------------
.bss
; RAM variables ($0200 - $7fff)
.segment "CODE" ; ---------------
; DATA
; ---------------
.data
; Initialized RAM variables ($0200 - $7fff)
; ---------------
; CODE
; ---------------
.code
; Actual program code ($c000 - $fff9) ; Actual program code ($c000 - $fff9)
reset: ; Program starts here
; System setup
sei ; disable interrupts while setting things up
cld ; clear BCD mode flag (undefined on NMOS 6502, auto cleared on 65C02)
ldx #$ff
txs ; Reset stack pointer
reset: ; Program starts here jsr copy_data ; copy data variables from ROM to RAM
jsr serial_setup
jsr serial_setup ; setup serial interface (65C51N). 19200 baud set as default
print_hello_world:
ldx #0 ldx #0
hello_world_loop: @loop:
lda str_hello_world,x ; get char lda str_hello_world,x ; get char
beq hello_world_end ; IF char is 0, done beq @end ; IF char is 0, done
jsr serial_print_char ; send char to console jsr serial_print_char ; send char to console
inx ; increment x (next char) inx ; increment x (next char)
jmp hello_world_loop jmp @loop
hello_world_end: @end:
jmp hello_world_end jmp @end
irq: ; IRQ Handler irq_handler: ; IRQ Handler
rti ; Do nothing rti ; Do nothing
nmi: ; NMI handler nmi_handler: ; NMI handler
rti ; Do nothing rti ; Do nothing
str_hello_world:
.asciiz "Hello, World!"
; ---------------
; RODATA
; ---------------
.rodata
; Constants
str_hello_world: .asciiz "Hello, World!"
; ---------------
; VECTORS
; ---------------
.segment "VECTORS" .segment "VECTORS"
; NMI, RESET and IRQ vectors ; NMI, RESET and IRQ vectors
.word nmi .word nmi_handler
.word reset .word reset
.word irq .word irq_handler
+46
View File
@@ -0,0 +1,46 @@
.import __DATA_LOAD__, __DATA_RUN__, __DATA_SIZE__
.export copy_data
.zeropage
src: .res 2
dst: .res 2
.code
copy_data:
; Copy data segment from ROM to final RAM location
lda #<__DATA_LOAD__ ; get source address low byte
sta src
lda #>__DATA_LOAD__ ; get source address high byte
sta src+1
lda #<__DATA_RUN__ ; get runtime final address low byte
sta dst
lda #>__DATA_RUN__ ; get runtime final address high byte
sta dst+1
ldx #>__DATA_SIZE__ ; number of 256 byte pages to copy (high byte)
beq @tail ; if no full pages to copy, skip
ldy #0
@page:
lda (src),y ; grab byte
sta (dst),y ; store byte
iny
bne @page ; for 256 cycles
inc src+1 ; increment source high byte (next memory page)
inc dst+1 ; increment destination high byte (next memory page)
dex ; decrement full pages counter
bne @page ; if no more full pages, stop
@tail: ; copy remaining non full page
ldy #0
cpy #<__DATA_SIZE__ ; check if there is data to copy
beq @done ; if no data, done
@byte:
lda (src),y
sta (dst),y
iny
cpy #<__DATA_SIZE__ ; when y == __DATA_SIZE__, done
bne @byte ; else, continue
@done:
rts