2021-01-19 08:36:01 +01:00
|
|
|
; *******************************************
|
|
|
|
; * PAT80 COMPOSITE PAL VIDEO ADAPTER *
|
|
|
|
; * Character generator module *
|
|
|
|
; *******************************************
|
|
|
|
|
|
|
|
; This module generates the character pixels using the font present in rom
|
2021-01-24 11:27:40 +01:00
|
|
|
; and adds it on the framebuffer in the position indicated by POS_COARSE (Y).
|
2021-01-22 08:58:11 +01:00
|
|
|
|
2021-02-12 18:48:42 +01:00
|
|
|
.equ LINE_COLUMNS = 46 ; number of columns (characters or chunks) per line
|
2021-01-24 11:27:40 +01:00
|
|
|
|
|
|
|
; Draws character in register A to the screen at current coords (Y)
|
2021-02-25 23:26:13 +01:00
|
|
|
; @param (HIGH_ACCUM) ascii code to display
|
|
|
|
; @modifies r0 (A), r1, r2, r3, r17, HIGH_ACCUM, Y, Z
|
2021-01-24 11:27:40 +01:00
|
|
|
draw_char:
|
2021-02-25 23:26:13 +01:00
|
|
|
; Check char is valid
|
|
|
|
cpi HIGH_ACCUM, 0x7f
|
|
|
|
brlo draw_char_valid
|
|
|
|
ret
|
|
|
|
|
|
|
|
draw_char_valid:
|
2021-01-24 11:27:40 +01:00
|
|
|
; Glyph's first byte is at:
|
|
|
|
; glyph_pointer = font_starting_mem_pos + (ascii_code * number_of_bytes_per_font)
|
|
|
|
; But all the fonts are 1 byte large, so a glyph is 1*height bytes:
|
|
|
|
; glyph_pointer = FONT + (ascii_code * FONT_HEIGHT)
|
|
|
|
|
|
|
|
; Load first glyph position on Z
|
2021-01-25 08:46:01 +01:00
|
|
|
ldi ZH, high(FONT<<1)
|
|
|
|
ldi ZL, low(FONT<<1)
|
|
|
|
; Obtain offset multiplying ascii_code * number_of_bytes_per_font
|
|
|
|
ldi r17, FONT_HEIGHT
|
|
|
|
mul HIGH_ACCUM, r17 ; result overwrites r0 and r1!
|
2021-01-26 23:22:55 +01:00
|
|
|
; 16-bit addition between gliph's first byte position and offset (and store result in Z) to obtain our glyph position
|
2021-01-25 08:46:01 +01:00
|
|
|
add ZL, r0
|
2021-01-24 11:27:40 +01:00
|
|
|
adc ZH, r1
|
|
|
|
; Z contain our glyph's first byte position: draw it
|
2021-02-22 19:55:53 +01:00
|
|
|
; Obtain drawing position in framebuffer memory (in Y)
|
|
|
|
call update_mem_pointer
|
2021-01-24 11:27:40 +01:00
|
|
|
; The drawing consist of FONT_HEIGHT cycles. Every glyph byte is placed on its own line
|
|
|
|
; on screen. To do this, we place it LINE_COLUMNS bytes after the previous one.
|
|
|
|
ldi HIGH_ACCUM, FONT_HEIGHT
|
|
|
|
draw_char_loop:
|
|
|
|
; Load glyph line byte from program memory (and point to the next)
|
|
|
|
lpm r1, Z+
|
|
|
|
; Write glyph line to framebuffer at chunk cursor position (Y)
|
|
|
|
st Y, r1
|
|
|
|
; Increment chunk cursor position (Y) to next line of the same char column
|
|
|
|
adiw YH:YL,LINE_COLUMNS
|
|
|
|
; Decrement loop counter and exit if reached 0
|
|
|
|
dec HIGH_ACCUM
|
|
|
|
brne draw_char_loop
|
2021-01-26 23:22:55 +01:00
|
|
|
|
|
|
|
; Char drawing is complete. Increment cursor position
|
|
|
|
inc POS_COLUMN
|
|
|
|
; Check if end of line
|
2021-02-15 21:23:12 +01:00
|
|
|
cpi POS_COLUMN, LINE_COLUMNS
|
2021-02-22 19:55:53 +01:00
|
|
|
brsh draw_char_eol
|
2021-01-24 11:27:40 +01:00
|
|
|
ret
|
2021-01-26 23:22:55 +01:00
|
|
|
draw_char_eol:
|
2021-02-15 22:54:54 +01:00
|
|
|
; end of line
|
2021-02-20 11:21:15 +01:00
|
|
|
clr POS_COLUMN ; reset column to 0
|
2021-02-22 19:55:53 +01:00
|
|
|
; Move cursor to next line
|
2021-02-20 11:21:15 +01:00
|
|
|
ldi HIGH_ACCUM, FONT_HEIGHT
|
|
|
|
add POS_ROWP, HIGH_ACCUM
|
2021-02-22 19:55:53 +01:00
|
|
|
; check if reached end of screen
|
|
|
|
cpi POS_ROWP, SCREEN_HEIGHT
|
|
|
|
brsh draw_char_eos
|
2021-02-20 11:21:15 +01:00
|
|
|
ret
|
|
|
|
draw_char_eos:
|
|
|
|
; end of screen: scroll screen but leave line pointer to last line
|
|
|
|
call scroll_screen
|
2021-01-26 23:22:55 +01:00
|
|
|
ret
|
2021-01-25 08:46:01 +01:00
|
|
|
|
|
|
|
; Sets the cursor to 0,0 and clears fine position
|
|
|
|
cursor_pos_home:
|
2021-01-26 23:22:55 +01:00
|
|
|
; Set all positions to 0
|
|
|
|
clr POS_COLUMN
|
|
|
|
clr POS_ROWP
|
|
|
|
ret
|
|
|
|
|
|
|
|
; Draws a newline
|
|
|
|
; Moves cursor to start of following screen line
|
|
|
|
; Takes care of particular cases, i.e. end of screen (shifts all screen up by one line)
|
2021-02-22 20:01:19 +01:00
|
|
|
draw_carriage_return:
|
|
|
|
; Move cursor to line start
|
|
|
|
ldi POS_COLUMN, 0
|
|
|
|
; Move cursor to next line
|
|
|
|
ldi HIGH_ACCUM, FONT_HEIGHT
|
|
|
|
add POS_ROWP, HIGH_ACCUM
|
|
|
|
; Check if end of screen
|
|
|
|
cpi POS_ROWP, SCREEN_HEIGHT
|
|
|
|
brsh draw_carriage_return_eos
|
|
|
|
ret
|
|
|
|
draw_carriage_return_eos:
|
|
|
|
call scroll_screen
|
|
|
|
ret
|
2021-01-26 20:52:53 +01:00
|
|
|
|
2021-02-15 22:54:54 +01:00
|
|
|
; Scrolls the screen by one line (=LINE_COLUMNS*FONT_HEIGHT bytes)
|
|
|
|
; and clears the last line (FRAMEBUFFER_END - LINE_COLUMNS*FONT_HEIGHT bytes)
|
2021-02-20 11:21:15 +01:00
|
|
|
; @uses A, Z
|
2021-02-15 22:54:54 +01:00
|
|
|
scroll_screen:
|
|
|
|
; "Read" Pointer to first char of second line
|
2021-02-22 13:59:55 +01:00
|
|
|
ldi YH, high(FRAMEBUFFER+(LINE_COLUMNS*FONT_HEIGHT))
|
|
|
|
ldi YL, low(FRAMEBUFFER+(LINE_COLUMNS*FONT_HEIGHT))
|
2021-02-15 22:54:54 +01:00
|
|
|
; "Write" Pointer to first char of first line
|
2021-02-22 13:59:55 +01:00
|
|
|
ldi ZH, high(FRAMEBUFFER)
|
|
|
|
ldi ZL, low(FRAMEBUFFER)
|
2021-02-15 22:54:54 +01:00
|
|
|
; Copy data
|
|
|
|
scroll_screen_copy_loop:
|
|
|
|
ld A, Y+
|
|
|
|
st Z+, A
|
2021-02-22 19:55:53 +01:00
|
|
|
cpi YH, high(FRAMEBUFFER_END)
|
2021-02-15 22:54:54 +01:00
|
|
|
brne scroll_screen_copy_loop
|
2021-02-22 19:55:53 +01:00
|
|
|
cpi YL, low(FRAMEBUFFER_END)
|
2021-02-15 22:54:54 +01:00
|
|
|
brne scroll_screen_copy_loop
|
|
|
|
; All the lines have been "shifted" up by one line.
|
|
|
|
; The first line is lost and the last is duplicate. Clear the last.
|
|
|
|
clr A
|
|
|
|
scroll_screen_clear_loop:
|
|
|
|
st Z+, A
|
|
|
|
cpi r31, high(FRAMEBUFFER_END)
|
|
|
|
brne scroll_screen_clear_loop
|
|
|
|
cpi r30, low(FRAMEBUFFER_END)
|
|
|
|
brne scroll_screen_clear_loop
|
2021-02-22 19:55:53 +01:00
|
|
|
; Last line cleared. Set cursor position
|
|
|
|
clr POS_COLUMN ; cursor to first column
|
|
|
|
ldi POS_ROWP, SCREEN_HEIGHT-FONT_HEIGHT
|
2021-02-15 22:54:54 +01:00
|
|
|
ret
|
2021-01-26 23:22:55 +01:00
|
|
|
|
2021-02-22 19:55:53 +01:00
|
|
|
; Sets the Y register to point to the cursor's first line memory position
|
|
|
|
; The cursor's position is represented by registers POS_COLUMN and POS_ROWP
|
|
|
|
update_mem_pointer:
|
|
|
|
; Compute memory pointer offset: offset = (LINE_COLUMNS*POS_ROWP)+POS_COLUMN
|
|
|
|
; LINE_COLUMNS*POS_ROWP
|
|
|
|
ldi HIGH_ACCUM, LINE_COLUMNS
|
|
|
|
mul HIGH_ACCUM, POS_ROWP ; result overwrites r0 and r1!
|
|
|
|
; ...+POS_COLUMN
|
|
|
|
add r0, POS_COLUMN
|
|
|
|
clr HIGH_ACCUM
|
2021-02-25 23:26:13 +01:00
|
|
|
adc r1, HIGH_ACCUM
|
2021-02-22 19:55:53 +01:00
|
|
|
; Set pointer to start of framebuffer
|
|
|
|
ldi YL, low(FRAMEBUFFER)
|
|
|
|
ldi YH, high(FRAMEBUFFER)
|
|
|
|
; Add offset to pointer
|
|
|
|
add YL, r0
|
|
|
|
adc YH, r1
|
|
|
|
ret
|
2021-02-25 23:26:13 +01:00
|
|
|
|
|
|
|
clear_screen:
|
|
|
|
ldi YH, high(FRAMEBUFFER)
|
|
|
|
ldi YL, low(FRAMEBUFFER)
|
|
|
|
load_mem_loop:
|
|
|
|
clr HIGH_ACCUM
|
|
|
|
;ser HIGH_ACCUM
|
|
|
|
st Y+, HIGH_ACCUM
|
|
|
|
; if reached the last framebuffer byte, exit cycle
|
|
|
|
cpi YH, high(FRAMEBUFFER_END)
|
|
|
|
brne load_mem_loop ; if not 0, repeat h_picture_loop
|
|
|
|
cpi YL, low(FRAMEBUFFER_END)
|
|
|
|
brne load_mem_loop ; if not 0, repeat h_picture_loop
|
|
|
|
ret
|