Files
SyncHome/trunk/workspace/AVR-Computer/avr-vic20/AVRCODE/VIC20.asm
2023-03-13 08:36:51 +00:00

698 lines
11 KiB
NASM

.include "m128def.inc"
;.equ DEBUG = 1
;------------------------------------------------------
;BIAS CONTROL
.equ bias_port = PORTG
.equ bias_ctrl = 3
.equ bias_adj = 4
;LCD
.equ A0 = 7
.equ WR = 5
.equ LCS = 6
.equ RD = 4
.equ LCD_RST = 7 (port D)
.equ lcd_pins = PORTE
.equ lcd_data = PORTB
;constants
.equ chars_line = 80 ;characters per line on lcd (virtual screen)
.equ lines_screen= 25 ;number of lines on screen
.def buff_head = r9
.def buff_tail = r10
.def cus_x = r11
.def cus_y = r12
;SRAM VARIABLES
.dseg
.org 0x100
serial_buff: .BYTE 256 ;buffers
vram_text: .BYTE chars_line*lines_screen ;one screen of text, should be in sync with display
vram_address: .BYTE 2 ;16 bit address of current pos in vram, should be same as display cursor address
;------EQU and DEF for Emulator---
.equ kernal_start = 0xE000
.equ basic_start = 0xC000
.equ char_start = 0x8000
.equ game_start = 0xA000
.equ Os = 6 ;overflow bit
.equ Ds = 3 ;Decimal
.equ Is = 7 ;Interupt
.equ Bs = 4 ;Break
.equ Cs = 0
.equ Zs = 1
.equ Ns = 2
.def stack = r15 ;stack pointer
.def Pstat1 = r13 ;the Processor status byte is split into 2 registers, this reg deals with C,Z,N
.def Pstat2 = r14 ;..this one deals with Int,Dec, Break ,(and now O(V) bit)
;this is done so I can simply save the AVR Status Reg to r13 for speed, and use native branch instructions
;bit 0=O
.def A = r21 ;accumulator
.def Xr = r22 ;X reg
.def Yr = r23 ;Y reg
.def PL = r26 ;XL
.def PH = r27 ;XH
.def retL = r24
.def retH = r25
.def zero_reg= r3 ;always 0
;DEBUG
.def breakL = r4
.def breakH = r5
emu_status: .BYTE 1 ;various bits to run emulator
;0 = LCD refresh toggle (refreshes every other interupt (30fps)
;1 = Next instuction is do interupt
;DEBUG
last_I: .BYTE 4 ;last 4 executed instructions
last_PC: .BYTE 2
last_rsbyte: .BYTE 1 ;last rs232 byte recived
.include "macros.asm"
.cseg
.org 0x00
rjmp start
;setup stack
.org 0x1E
rjmp timer0_cmp
.org 0x0024 ;received a rs232
rjmp rx_rs232_0
start:
LDI R16, HIGH(RAMEND) ;Upper byte
OUT SPH,R16 ;to stack pointer
LDI R16, LOW(RAMEND) ;Lower byte
OUT SPL,R16
k:
add r16,r17
; rjmp k
; rjmp skip
rcall init_hardware
rcall load_chars_lcd
rcall load_kernal
rcall load_basic
rcall load_chars
rcall load_game
skip:
;---setup timer--
ldi r16,7
out TCCR0,r16
ldi r16,2
out TIMSK,r16
; sei
clr zero_reg
ldi r16,0xff
mov stack,r16
sts last_rsbyte,r16
sts emu_status,zero_reg
ldi ZL,0xFC
ldi ZH,0xFF
ld PL,Z+
ld PH,Z
; ldi PL,0x00
; ldi PH,0x11
ldi r16,'G'
; rcall rs232_0_tx
ldi r16,'O'
; rcall rs232_0_tx
lds r16,0xffff
; rcall rs232_0_tx
lds r16,0xffff
; rcall rs232_0_tx
lds r16,0xe000
; rcall rs232_0_tx
lds r16,0xfffe
; rcall rs232_0_tx
LDI R17,0X40
CLR ZH
ldi ZL, 0xc5
st_cpuZ r17
ldi ZL, 0xcB
st_cpuZ r17
in r16,SREG
mov Pstat1,r16
next_op:
;rjmp op_ADC
sei
clr r16
lds r16,emu_status ;check if Interupt
sbrc r16,1
rcall Interupt_IRQ
ldi ZL,low(opcode_jmps<<1)
ldi ZH,high(opcode_jmps<<1)
.IFDEF DEBUG
cp PL,breakL
cpc PH,breakH
breq breakp
sts last_PC,PL
sts last_PC+1,PH
.ENDIF
breakpret:
get_pc_mem retL ;GET OPCODE
.IFDEF DEBUG
lds r16,last_I+2
sts last_I+3,r16
lds r16,last_I+1
sts last_I+2,r16
lds r16,last_I
sts last_I+1,r16
sts last_I,retL
.ENDIF
clr r16
lsl retL ;mul opcde by 2 because jump table is 2 bytes per entry
adc r16,zero_reg ;if carry, then save carry to r16
add ZL,retL
adc ZH,r16 ;add carry from last add, also add any carry from lsl
lpm YL,Z+ ;have to use Z for both lpm and IJMP unfortunatly
lpm YH,Z
movw ZL,YL ;copy value to ZL
cli
ijmp ;now jump to copcode subroutine
breakp:
nop
nop
nop
rjmp breakpret
;-------LOAD KERNAL INTO MEMORY------------
load_kernal:
ldi ZL,low(kernal_rom<<1)
ldi ZH,high(kernal_rom<<1)
ldi YL,low(kernal_start)
ldi YH,high(kernal_start)
clr r16
load_kernal_loop1:
ldi r17,32
load_kernal_loop2:
lpm r18,Z+
st Y+,r18
dec r17
brne load_kernal_loop2
dec r16
brne load_kernal_loop1
ret
;-------LOAD BASIC INTO MEMORY------------
load_basic:
ldi ZL,low(basic_rom<<1)
ldi ZH,high(basic_rom<<1)
ldi YL,low(basic_start)
ldi YH,high(basic_start)
clr r16
load_basic_loop1:
ldi r17,32
load_basic_loop2:
lpm r18,Z+
st Y+,r18
dec r17
brne load_basic_loop2
dec r16
brne load_basic_loop1
ret
;-------LOAD GAME INTO MEMORY------------
load_game:
ldi ZL,low(game_rom<<1)
ldi ZH,high(game_rom<<1)
ldi YL,low(game_start)
ldi YH,high(game_start)
clr r16
load_game_loop1:
ldi r17,32
load_game_loop2:
lpm r18,Z+
st Y+,r18
dec r17
brne load_game_loop2
dec r16
brne load_game_loop1
ret
;-------LOAD CHARACTER INTO MEMORY------------
load_chars:
ldi ZL,low(char_rom<<1)
ldi ZH,high(char_rom<<1)
ldi YL,low(char_start)
ldi YH,high(char_start)
clr r16
load_char_loop1:
ldi r17,16
load_char_loop2:
lpm r18,Z+
st Y+,r18
dec r17
brne load_char_loop2
dec r16
brne load_char_loop1
ret
;-------LOAD CHARACTER INTO LCD MEMORY------------
load_chars_lcd:
ldi ZL,low(char_rom<<1)
ldi ZH,high(char_rom<<1)
ldi YL,low(char_start)
ldi YH,high(char_start)
;-- set address to start of CGRAM
ldi r17,1 ;command
ldi r16,0x46
rcall lcd_send_data
clr r17
ldi r16,0x00
rcall lcd_send_data
ldi r16,0xf0
rcall lcd_send_data
;--
ldi r17,1
ldi r16,0x42 ;send data to cgram
rcall lcd_send_data
clr r17
clr r19
load_chars_lcd_loop1:
ldi r18,16
load_chars_lcd_loop2:
lpm r16,Z+
rcall lcd_send_data
dec r18
brne load_chars_lcd_loop2
dec r19
brne load_chars_lcd_loop1
ret
;----------------------------------------------------------------------
;lcd_refresh
;purpose: copies VRAM to LCD
;Takes: na
;Give: na
;----------------------------------------------------------------------
;----------------------------------------------------------------------
lcd_refresh:
push r0
push r1
push r16
push r17
push r18
push r19
push r20
push ZL
push ZH
ldi ZL,LOW(0x1000)
ldi ZH,HIGH(0x1000)
ldi r19,22
ldi r20,23
clr cus_y
lcd_refresh_loop1:
clr cus_x
rcall cursor_address
rcall lcd_set_cursor
ldi r17,1 ;setup to send data
ldi r16,0x42
rcall lcd_send_data
clr r17
lcd_refresh_loop2:
ld_cpuZp r18
mov r16,r18
rcall lcd_send_data
inc cus_x
cp cus_x,r19
brne lcd_refresh_loop2
inc cus_y
cp cus_y,r20
brne lcd_refresh_loop1
pop ZH
pop ZL
pop r20
pop r19
pop r18
pop r17
pop r16
pop r1
pop r0
ret
timer0_cmp:
push r1
in r1,SREG ;preserve main OS status reg.
push r16
push r17
lds r16,emu_status
sbrs r16,0 ;toggle lcd bit
rjmp PC + 3
cbr r16,1
rjmp PC+2
sbr r16,1
sbrs r16,0
rcall lcd_refresh
mov r17,Pstat2 ;check if interupts enabled
cbr r16,2
sbrs r17,Is
sbr r16,2 ;if Interupt disable is 0, then set bit 1 in emu_status to next intruction is interupt
sts emu_status,r16
pop r17
pop r16
out SREG,r1
pop r1
reti
rx_rs232_0:
push r1
in r1,SREG ;preserve main OS status reg.
push r16
push r17
push ZL
push ZH
in r17,UDR0 ;get the incomming char
; sts last_rsbyte,r17
ldi ZL,low(0x277)
ldi ZH,high(0x277)
st_cpuZ r17
ldi r17,1
ldi ZL,low(0xc6)
ldi ZH,high(0xc6)
st_cpuZ r17
; mov r16,PL
; rcall rs232_0_tx
; mov r16,PH
; rcall rs232_0_tx
qwe:
pop ZH
pop ZL
pop r17
pop r16
out SREG,r1 ;restore previous status reg
pop r1
reti
;return to normal pgm status.
rs232_0_tx:
sbis UCSR0A,UDRE0
rjmp rs232_0_tx
out UDR0,r16
rcall delay_1ms
ret
;----------------------------------------------------------------------
;----------------------------------------------------------------------
;Interupt_IRQ
;purpose: called when IRQ
;Takes: na
;Give: na
;----------------------------------------------------------------------
;----------------------------------------------------------------------
Interupt_IRQ:
stack_push PH
stack_push PL
rcall stack_build
stack_push retL
mov r16,Pstat2 ;clear interupt so cant do interupt in interupt
sbr r16,EXP2(Is)
mov Pstat2,r16
lds r16,emu_status
cbr r16,2
sts emu_status,r16
ldi YL,low(0xFFFE)
ldi YH,high(0xFFFE)
ld PL,Y+
ld PH,Y
ret
;----------------------------------------------------------------------
;----------------------------------------------------------------------
;vias_access
;purpose: called when a read to the vias has occurded (from Z)
;Takes: Z
;Give: retL is result
;----------------------------------------------------------------------
;----------------------------------------------------------------------
vias_access:
push r17
push r18
push r19
cpi ZL,0x21 ;keyboard scan being read
breq keyboard_read
ld retL,Z
vias_access_exit:
pop r19
pop r18
pop r17
ret
keyboard_read:
ldi retL,0xff
lds r17,last_rsbyte ;should be 0-63
cpi r17,255
breq vias_access_exit
mov r18,r17
lsr r18 ;divide by 8 to get which column
lsr r18
lsr r18
lds r19,0x9120 ;read column byte
keyboard_read_loop1:
tst r18 ;test if 18(prob dont need)
breq keyboard_read_loop1_exit
lsr r19 ;shift right, so column to check is at 0
dec r18
rjmp keyboard_read_loop1
keyboard_read_loop1_exit:
sbrs r19,0 ;test if key column is 0
rjmp keyboard_read_set_row ;so set appropriate row
; ldi retL,0xff ; if here, then a colum with no key is pressed
rjmp vias_access_exit
keyboard_read_set_row:
andi r17,0b00000111 ;get lower 3 bits
ldi retL,0xFE ;set bit 0
keyboard_read_loop2:
tst r17
breq keyboard_read_loop2_exit
sec
rol retL
dec r17
rjmp keyboard_read_loop2
keyboard_read_loop2_exit:
rjmp vias_access_exit
;-------------------------------
;----------------------------------------------------------------------
;----------------------------------------------------------------------
;delay_1ms
;purpose: delays 1ms
;Takes: na
;Give: na
;----------------------------------------------------------------------
;----------------------------------------------------------------------
delay_1ms:
push r19
push r18
ldi r18,20
delay_1ms1:
ldi r19,133
delay_1ms1_loop:
dec r19
nop
nop
nop
brne delay_1ms1_loop
dec r18
brne delay_1ms1
pop r18
pop r19
ret
delay_100ms:
push r18
ldi r18,100
delay_100ms_loop:
rcall delay_1ms
dec r18
brne delay_100ms_loop
pop r18
ret
.include "6502cpu.asm"
.include "io.asm"
.include "init.asm"
.CSEG
;.org 0x7000
opcode_jmps:
.include "dbops.asm"
kernal_rom:
.include "kernal.asm"
basic_rom:
.include "basic.asm"
char_rom:
.include "chars.asm"
game_rom:
.include "game.asm"