; SERVID - Serial video display using Ubicom SX microcontroller
; $Id: servid.asm,v 1.34 2001/01/31 07:25:31 eric Exp $
;
; Copyright 2000, 2001 Eric Smith <eric@brouhaha.com>
;
; Home page:
; http://www.brouhaha.com/ubicom/servid/
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License version 2 as published
; by the Free Software Foundation. Note that permission is not granted
; to redistribute this program under the terms of any other version of the
; General Public License.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
;
; NOTE: it is sometimes claimed that compliance with the GPL is
; awkward for commercial interests. Licenses for non-GPL use of this
; program may be negotiated with the author.
;
; This program is written to be assembled with the GPASM assembler,
; version 0.8.14 or newer:
; http://gpasm.sourceforge.net/
; NOTE: there are references in this code to PAL and NTSC. Technically
; those are color standards. In most cases the references to PAL and NTSC
; are really intended to refer to 625/50 and 525/59.94 scanning, or (in
; non-interlaced mode) 312/25 and 262/29.97 scanning.
;---------------------------------------------------------------------------
; feature test switches
;---------------------------------------------------------------------------
ft_sx28 equ 0 ; 0 for SX18/SX20, 1 for SX28
ft_pal_video equ 0 ; 0 for NTSC 525/60, 1 for PAL 625/50
; (approximate timing only)
; (not well tested)
ft_interlace equ 1 ; 1 for interlaced video
ft_color equ 0 ; 1 for color burst (NTSC only)
ft_serial_input equ 1 ; 1 for normal serial input,
; 0 to omit (when replaced with user
; application code)
ft_ser_noninv equ 1 ; 0 for "normal" TTL-level serial,
; mark = low, space = high
; 1 for non-inverted serial (the
; crude resistor-only method)
; mark = high, space = low
ft_splash equ 1 ; 1 for splash screen
;---------------------------------------------------------------------------
; processor definitions and assembler settings
;---------------------------------------------------------------------------
if ft_sx28
processor SX28
else
processor SX18
endif
radix dec
errorlevel -305 ; don't want default destination warning
errorlevel -224 ; don't want deprecated instruction warnings,
; since on an SX or PIC16C5X there's no other
; way to set the TRIS or OPTION registers
include "sxdefs.inc"
device equ pins28+pages4+banks8+oschs+optionx+stackx+bor40+turbo
; for instruction destination argument
f equ 1
w equ 0
int_off equ 0xc3 ; RTCC internal clock, prescale by 16,
; RTCC interrupt off, WDT disabled
int_on equ 0x83 ; RTCC internal clock, prescale by 16,
; RTCC interrupt on, WDT disabled
;---------------------------------------------------------------------------
; other includes
;---------------------------------------------------------------------------
include "ascii.inc"
;---------------------------------------------------------------------------
; video definitions
;---------------------------------------------------------------------------
; Display size in characters. Note that simply changing these definitions
; won't have the desired effect.
rows equ 4
columns equ 20
; osc = 42.9545 MHz = 12 * color burst
; tCYC = 23.2804 ns
; theoretical total width = 12 * 227.5 = 2730 cycles = 2 * 3 * 5 * 91 = 15 * 182
;
; The RTCC prescaler can only be set for powers of two, and we need the
; count to be a little under 256, so we use a prescaler of 16 and a divisor
; of 171, for an actual scan line width of 2736 cycles (63.7 us).
h equ 2736 ; 63.7 us
hsync_pulse_width equ 201 ; 4.7 us
equalization_pulse_width equ 98 ; 2.3 us
serration_pulse_width equ 201 ; 4.7 us
vsync_pulse_width equ (h/2)-serration_pulse_width
front_porch_width equ 64 ; 1.5 us
back_porch_width equ 193 ; 4.5 us
; safe area = 40 us = 1718 cycles
; 20 chars wide * (5+2) = 139 pixels wide, 12.4 cycles per pixel
; ("rounded" up to 13)
;
; for 4:3 aspect ratio, display should be 90 pixels tall, so make
; a pixel be 3 scan lines.
scan_lines_per_vpixel equ 3
vpixels_per_char equ 10
chars_per_row equ 20
if ft_pal_video
total_active_lines equ 287
else
total_active_lines equ 242
endif
active_video_lines equ rows*vpixels_per_char*scan_lines_per_vpixel
top_border equ (total_active_lines-active_video_lines)/2
bottom_border equ total_active_lines-(top_border+active_video_lines)
;---------------------------------------------------------------------------
; I/O port definitions
;---------------------------------------------------------------------------
rxd_bit equ 0
pzt_bit equ 1
txd_bit equ 2 ; not used
mode_button_bit equ 3 ; not used
#define rxd porta,rxd_bit
#define pzt porta,pzt_bit
#define txd porta,txd_bit
#define mode_button porta,mod_button_bit
trisa equ 0x01 ; RxD is our only input
inita equ 0x00
trisb equ 0x00 ; all outputs
initb equ vid_sync
if ft_sx28
trisc equ 0x00 ; all outputs
initc equ 0x00 ; drive low
endif
;---------------------------------------------------------------------------
; composite video definitions
;---------------------------------------------------------------------------
vid_port equ portb ; D/A converter
; DAC 0 = ground (sync tip), 255 = 1.25V into 75 ohm load
; one DAC step = 1.25/255 V = 4.902 mV
; there are 140 IRE units to 1.0V, so an IRE unit is 7.143 mV = 1.457 DAC steps
vid_sync equ 0 ; -40 IRE
vid_blank equ 58 ; 58.29 = 0 IRE
vid_black equ 69 ; 69.21 = 7.5 IRE
vid_white equ 204 ; 204.00 = 100 IRE
vid_max_chroma equ 249 ; 249.17 = 131 IRE
burst_amplitude equ 58 ; 58.29 = 40 IREs
;---------------------------------------------------------------------------
; bell definitions
;---------------------------------------------------------------------------
; The bell tone is nominally around 500 Hz for 200 ms (100 cycles).
; This works out to a period of 32 scan lines.
bell_half_period equ 16 ; lines
bell_duration equ 200 ; half-periods
;---------------------------------------------------------------------------
; serial definitions
;---------------------------------------------------------------------------
; While serial line idle, sample every scan line. Once start bit is
; detected, delay 6 scan lines, then sample every 13. This results
; in a 1208 bps rate, 0.6% fast.
lines_per_serial_sample equ 13
skip_on_ser_rx_mark macro
if ft_ser_noninv
btfss rxd
else
btfsc rxd
endif
endm
skip_on_ser_rx_space macro
if ft_ser_noninv
btfsc rxd
else
btfss rxd
endif
endm
;---------------------------------------------------------------------------
; memory utilization
;---------------------------------------------------------------------------
rambase equ 08h ; start of RAM
rombase equ 0000h ; beginning of program
romsize equ 0800h
chargen equ romsize-384
resetvec equ romsize-1 ; reset vector
intvec equ 0000h ; interrupt vector
main_page equ 0000h
int_page equ 0200h
;---------------------------------------------------------------------------
; shared variables
;---------------------------------------------------------------------------
org rambase ; start of RAM
g_field_count: res 1 ; field down-counter
g_mtemp: res 1 ; global temp for main
; "DelM" uses "DelMCnt" in the interupt. Do NOT use DelM or DelMCnt in main!
DelMCnt: res 1 ; counter used for cycle delays
Five: res 1
Fifteen res 1
;---------------------------------------------------------------------------
; variables for main
;---------------------------------------------------------------------------
org 010h
main_vars:
temp: res 3
char: res 1 ; character being processed
escape_state: res 1 ; 0 = normal
; 1 = ESC seen, waiting for 2nd char
; 2 = ESC-Y seen, waiting for <col>
; 3 = ESC-Y <col> seen, waiting for <row>
esc_Y_col: res 1
; cursor
cursor_col: res 1
cursor_row: res 1
cursor_loc: res 1
; for scrolling
src_addr equ temp
dest_addr equ temp+1
move_count equ temp+2
;---------------------------------------------------------------------------
; variables for interrupt
;---------------------------------------------------------------------------
org 030h
int_vars:
line_type: res 1 ; type of scan line we're working on
; 2 * [0 .. line_types-1]
line_count: res 1 ; how many lines of this type to do
if ft_color
burst_phase: res 1 ; LSB used for burst phase
int_temp: res 1 ; general use in interrupt
endif
line_start: res 1 ; start buffer loc of currently displayed line
char_ptr: res 1 ; pointer to currently displayed character
chargen_ptr: res 2 ; pointer into character generator
inverse_flag: res 1 ; bit 7 indicates current char inverse
vpix_cnt: res 1 ; vertical pixel counter
scanline_cnt: res 1 ; vertical scan line counter (per pixel)
char_cnt: res 1
pixels: res 1 ; pixels of current char
; bell
bell_half_cyc: res 1 ; bell half-cycle in lines
bell_line_cnt: res 1 ; bell half-cycle down-counter
bell_dur_cnt: res 1 ; bell duration
;---------------------------------------------------------------------------
; variables for serial receive
;---------------------------------------------------------------------------
if ft_serial_input
org 050h
ser_vars:
ser_rx_state: res 1
ser_rx_byte: res 1
ser_rx_samp_cnt: res 1
ser_rx_bit_cnt: res 1
ser_rx_char: res 1
ser_rx_flag: res 1
endif
;---------------------------------------------------------------------------
; video buffer
;---------------------------------------------------------------------------
; NOTE: subtract offset of 20h (space) before storing characters into
; video buffer
video_buffer equ 070h ; 80 characters, uses last five banks
; *must* start on a bank boundary
; reserve RAM, skipping over banks as needed
res_bank_ram macro count
local c
c set count
while c>0
res 1
if ($ & 010h)==0
org $+010h
endif
c set c-1
endw
endm
org video_buffer
line_0: res_bank_ram columns
line_1: res_bank_ram columns
line_2: res_bank_ram columns-1
line_2_end: res_bank_ram 1
line_3: res_bank_ram columns-1
line_3_end: res_bank_ram 1
org rombase
page interrupt ; 0
goto interrupt ; 1
escape_state_table:
movf escape_state,w
addwf pcl
goto esc_not_seen
goto esc_seen
goto esc_Y_col_seen
goto esc_Y_row_seen
control_char_table:
movf char,w
addwf pcl
goto null ; 00 - NUL - null - don't do anything
goto null ; 01 -
goto null ; 02 -
goto null ; 03 -
goto null ; 04 -
goto null ; 05 -
goto null ; 06 -
goto bell ; 07 - BEL - bell
goto backspace ; 08 - BS - backspace
goto null ; 09 -
goto line_feed ; 0a - LF - line feed
goto null ; 0b -
goto form_feed ; 0c - FF - form feed - clear screen
goto carriage_return ; 0d - CR - carriage return
goto null ; 0e
goto null ; 0f
goto null ; 10
goto null ; 11
goto null ; 12
goto null ; 13
goto null ; 14
goto null ; 15
goto null ; 16
goto null ; 17
goto null ; 18
goto null ; 19
goto null ; 1a
goto escape ; 1b - ESC - escape
goto null ; 1c
goto null ; 1d
goto null ; 1e
goto null ; 1f
esc_char_table:
addwf pcl
goto bad_escape ; 40 - @
goto cursor_up ; 41 - A - cursor up
goto cursor_down ; 42 - B - cursor down
goto cursor_left ; 43 - C - cursor left
goto cursor_right ; 44 - D - cursor right
goto bad_escape ; 45 - E
goto bad_escape ; 46 - F
goto bad_escape ; 47 - G
goto home_cursor ; 48 - H - cursor home
goto rev_line_feed ; 49 - I - reverse line feed (can scroll)
goto clear_eop ; 4A - J - clear to end of screen
goto clear_eol ; 4B - K - clear to end of line
goto bad_escape ; 4C - L
goto bad_escape ; 4D - M
goto bad_escape ; 4E - N
goto bad_escape ; 4F - O
goto bad_escape ; 50 - P
goto bad_escape ; 51 - Q
goto bad_escape ; 52 - R
goto bad_escape ; 53 - S
goto bad_escape ; 54 - T
goto bad_escape ; 55 - U
goto bad_escape ; 56 - V
goto bad_escape ; 57 - W
goto bad_escape ; 58 - X
goto esc_Y ; 59 - Y - cursor positioning
goto bad_escape ; 5A - Z
goto bad_escape ; 5B - [
goto bad_escape ; 5C - \
goto bad_escape ; 5D - ]
goto bad_escape ; 5E - ^
goto bad_escape ; 5F - _
show_cursor:
movf cursor_loc,w
movwf fsr
bsf indf,7
bank main_vars
return
hide_cursor:
movf cursor_loc,w
movwf fsr
bcf indf,7
bank main_vars
return
; delay until either the number of fields specified in W have been
; displayed (zero flag set), or a serial character is received
; (zero flag clear)
delay_fields:
bank ser_vars
movwf g_field_count
df_loop:
if ft_serial_input
movf ser_rx_flag ; check serial receive flag
btfss status,zf ; character received?
goto df_return ; yes, return to caller
endif
movf g_field_count ; has field count decremented to zero?
btfss status,zf
goto df_loop ; no, keep looping
df_return:
bank main_vars
return
home_cursor:
clrf cursor_row
carriage_return:
clrf cursor_col
compute_cursor_loc:
movf cursor_row,w ; cursor_loc = 20 * cursor_row
movwf cursor_loc
bcf status,cf
rlf cursor_loc
rlf cursor_loc
swapf cursor_row,w
addwf cursor_loc
movf cursor_col,w ; cursor_loc += cursor_col
addwf cursor_loc
movf cursor_loc,w ; shift high nibble left one bit
andlw 0f0h
addwf cursor_loc
movlw video_buffer ; add in base address
addwf cursor_loc
null: return
; output a character from W to the display
output_char:
andlw 07fh ; strip MSB (parity?) and save
movwf char
goto escape_state_table ; process character
esc_not_seen:
movf char,w
andlw 060h ; is it a control character?
btfsc status,zf
goto control_char_table ; yes, process and return
movf char,w ; is it a DEL
xorlw asc_del
btfsc status,zf
return ; yes, do nothing
movf char,w
; fall into printable_char
printable_char:
movwf g_mtemp ; save character
movlw -' ' ; remove offset
addwf g_mtemp
movf cursor_loc,w ; store character
movwf fsr
movf g_mtemp,w
movwf indf
bank main_vars
; fall into cursor_advance
cursor_advance:
incf cursor_col
movf cursor_col,w
xorlw columns
btfss status,zf
goto compute_cursor_loc
crlf: clrf cursor_col
line_feed:
incf cursor_row
movf cursor_row,w
xorlw rows
btfss status,zf
goto compute_cursor_loc
decf cursor_row ; restore
call compute_cursor_loc
scroll_up:
movlw line_1
movwf src_addr
movlw line_0
movwf dest_addr
movlw (rows-1)*columns
movwf move_count
call block_move_up
movlw line_3 ; clear freed space
movwf temp+1
movlw columns
movwf temp
goto clear_chars
block_move_up:
movf src_addr,w
movwf fsr
movf indf,w
movwf g_mtemp
bank main_vars
incf src_addr
bsf src_addr,4
movf dest_addr,w
movwf fsr
movf g_mtemp,w
movwf indf
bank main_vars
incf dest_addr
bsf dest_addr,4
decfsz move_count
goto block_move_up
return
backspace:
decf cursor_col
btfss cursor_col,7
goto compute_cursor_loc
movlw columns-1
movwf cursor_col
rev_line_feed:
decf cursor_row
btfss cursor_row,7
goto compute_cursor_loc
incf cursor_row ; restore
call compute_cursor_loc
scroll_down:
movlw line_2_end
movwf src_addr
movlw line_3_end
movwf dest_addr
movlw (rows-1)*columns
movwf move_count
call block_move_down
movlw line_0 ; clear freed space
movwf temp+1
movlw columns
movwf temp
goto clear_chars
block_move_down:
movf src_addr,w
movwf fsr
movf indf,w
movwf g_mtemp
bank main_vars
decf src_addr
btfsc src_addr,4
goto bmd_1
movlw 010h
subwf src_addr
bmd_1: movf dest_addr,w
movwf fsr
movf g_mtemp,w
movwf indf
bank main_vars
decf dest_addr
btfsc dest_addr,4
goto bmd_2
movlw 010h
subwf dest_addr
bmd_2: decfsz move_count
goto block_move_down
return
clear_eol:
movlw columns ; compute number of chars to clear:
movwf temp ; temp := columns - cursor_col
movf cursor_col,w
subwf temp
movf cursor_loc,w
movwf temp+1
; clear temp chars starting at loc temp+1
clear_chars:
movlw ' '-020h
movwf g_mtemp
; fill temp chars starting at loc temp+1 to value temp+2
fill_chars:
movf temp+1,w
movwf fsr
movf g_mtemp,w
movwf indf
bank main_vars
incf temp+1
bsf temp+1,4
decfsz temp
goto fill_chars
return
form_feed:
call home_cursor
clear_eop:
call clear_eol ; clear to end of current line
movlw rows-1 ; compute additional rows to clear:
movwf temp ; temp := (rows - 1) - cursor_row
movf cursor_row,w
subwf temp
btfsc status,zf ; any rows to clear?
return ; no
bcf status,cf ; multiply temp by 20 to get char count
rlf temp
bcf status,cf
rlf temp
movf temp,w
bcf status,cf
rlf temp
bcf status,cf
rlf temp
addwf temp
goto fill_chars
cursor_up:
decf cursor_row
movlw rows-1
btfsc cursor_row,7
movwf cursor_row
goto compute_cursor_loc
cursor_down:
incf cursor_row
btfsc cursor_row,2 ; hard-coded for 4 rows
clrf cursor_row
goto compute_cursor_loc
cursor_left:
decf cursor_col
movlw columns-1
btfsc cursor_col,7
movwf cursor_col
goto compute_cursor_loc
cursor_right:
incf cursor_col
movf cursor_col,w
xorlw columns
btfsc status,zf
clrf cursor_row
goto compute_cursor_loc
esc_Y:
movlw 2
movwf escape_state
bad_escape:
return
esc_Y_col_seen:
movlw ' '
subwf char,w
movwf esc_Y_col
incf escape_state
return
escape:
incf escape_state
return
esc_Y_row_seen:
movlw (256-' ')-rows ; range check the row (still has ' ' offset)
addwf char,w
btfsc status,cf
goto bad_row
movlw ' ' ; move cursor to specified column
subwf char,w
movwf cursor_row
bad_row
movlw 256-columns ; range check the column
addwf esc_Y_col,w
btfsc status,cf
goto bad_col
movf esc_Y_col,w ; move cursor to specified column
movwf cursor_col
bad_col:
clrf escape_state
goto compute_cursor_loc
esc_seen:
clrf escape_state ; assume only two-char sequence
movlw '@'
subwf char,w
movwf temp
andlw 060h ; check for range 40-5F
btfss status,zf
goto bad_escape
movf temp,w
goto esc_char_table
bell: bank int_vars ; start a bell
movlw bell_half_period
movwf bell_half_cyc
movwf bell_line_cnt
movlw bell_duration
movwf bell_dur_cnt
bank main_vars
return
reset: mode 0fh ; paranoia
movlw int_off
option
bank main_vars
movlw inita
movwf porta
movlw trisa
tris porta
movlw initb
movwf portb
movlw trisb
tris portb
if ft_sx28
movlw initc
movwf portc
movlw trisc
tris portc
endif
clrf escape_state
call form_feed
bank int_vars
movlw 5 ; set up for DelM macro
movwf Five
movlw 15
movwf Fifteen
clrf line_type
incf line_type,w ; get initial line count
page line_dispatch
call line_dispatch
page $
movwf line_count
if ft_serial_input
bank ser_vars
clrf ser_rx_state
movlw 1
movwf ser_rx_samp_cnt
clrf ser_rx_flag
endif
bank main_vars
movlw int_on
option
if ft_splash
page splash
call splash
endif
; call home_cursor
main_loop:
call show_cursor
movlw 30
call delay_fields
if ft_serial_input
btfss status,zf
goto got_char
endif
call hide_cursor
movlw 30
call delay_fields
if ft_serial_input
btfss status,zf
goto got_char
endif
goto main_loop
if ft_serial_input
got_char:
call hide_cursor ; hide cursor during character processing
bank ser_vars ; get character and clear rx flag
movf ser_rx_char,w
clrf ser_rx_flag
bank main_vars
call output_char
goto main_loop
endif
;---------------------------------------------------------------------------
; interrupt handler
;---------------------------------------------------------------------------
org 0200h
include "delm.inc"
scanln macro count,function
goto function
retlw count
endm
; table of line type function pointers and counts
; when called for function, takes cycles 58-63
line_dispatch:
addwf pcl ; 58
if ft_pal_video
; PAL lines
scanln 2,equalization_line ; 624-625
scanln 2,vsync_line ; 1-2
scanln 1,vsync_eq_line ; 3
scanln 2,equalization_line ; 4-5
scanln 17,vblank_line ; 6-22
scanln 1,vblank_black_line ; 23
scanln top_border,black_video_line ; 24-106
scanln active_video_lines,active_video_line ; 107-226
scanln bottom_border-1,black_video_line ; 227-309
if ft_interlace
scanln 1,black_video_line ; 310
scanln 2,equalization_line ; 311-312
scanln 1,eq_vsync_line ; 313
scanln 2,vsync_line ; 314-315
scanln 2,equalization_line ; 316-317
scanln 1,eq_vblank_line ; 318
scanln 17,vblank_line ; 319-335
scanln top_border,black_video_line ; 336-418
scanln active_video_lines,active_video_line ; 419-538
scanln bottom_border,black_video_line ; 539-622
endif
scanln 1,black_eq_line ; 310 or 623
else
; ; NTSC lines
scanln 3,equalization_line ; 1-3
scanln 3,vsync_line ; 4-6
scanln 3,equalization_line ; 7-9
scanln 11,vblank_line ; 10-20
scanln top_border,black_video_line ; 21-81
scanln active_video_lines,active_video_line ; 82-201
scanln bottom_border,black_video_line ; 202-262
if ft_interlace
scanln 1,black_eq_line ; 263
scanln 2,equalization_line ; 264-265
scanln 1,eq_vsync_line ; 266
scanln 2,vsync_line ; 267-268
scanln 1,vsync_eq_line ; 269
scanln 2,equalization_line ; 270-271
scanln 1,eq_vblank_line ; 272
scanln 10,vblank_line ; 273-282
scanln 1,vblank_black_line ; 283
scanln top_border,black_video_line ; 284-344
scanln active_video_lines,active_video_line ; 345-464
scanln bottom_border,black_video_line ; 465-525
endif
endif
line_types equ (($-line_dispatch)-1)/2
if ft_serial_input
; serial input state machine dispatch
serial_state_table:
movf ser_rx_state,w ; 12
addwf pcl ; 13-15
goto ser_idle ; 16-18
goto ser_data_bit ; 16-18
goto ser_stop_bit ; 16-18
endif
bell_48:
bcf pzt ; 48
delm 1 ; 49
bell_50:
delm 1 ; 50
goto bell_done ; 51-53
interrupt:
movlw vid_blank ; 4 start front porch
movwf vid_port ; 5
if ft_serial_input
bank ser_vars ; 6
decfsz ser_rx_samp_cnt ; 7
goto skip_serial ; 8-10
call serial_state_table ; 9-11
goto serial_done ; 39-41
skip_serial:
delm 31 ; 11-41
serial_done:
else
delm 36 ; 6-41
endif
bank int_vars ; 42
movf bell_dur_cnt ; 43 - bell active?
btfsc status,zf ; 44
goto bell_48 ; 45-47 - no
decfsz bell_line_cnt ; 46 - time to toggle PZT?
goto bell_50 ; 47-49 - no
movlw 1<<pzt_bit ; 48 - toggle PZT
xorwf porta ; 49
decf bell_dur_cnt ; 50 - decrement duration
movf bell_half_cyc,w ; 51 - reinit line count
movwf bell_line_cnt ; 52
delm 1 ; 53
bell_done:
movf line_type,w ; 54
call line_dispatch ; 55-57
decfsz line_count ; any more lines of the current type?
goto interrupt_done ; no, done
incf line_type ; advance to next line type
incf line_type
movf line_type,w ; end of field?
xorlw line_types*2
btfss status,zf
goto get_line_count ; no
clrf line_type ; yes, start new field
movf g_field_count ; decrement field counter
btfss status,zf
decf g_field_count
get_line_count:
incf line_type,w ; new line type, how many lines?
call line_dispatch
movwf line_count
interrupt_done:
movlw 256-171 ; all done with this scan line
retiw
; at some point within the frame before the first active video line,
; call this subroutine to initialize the pointers
display_frame_setup:
movlw video_buffer
movwf line_start
clrf vpix_cnt
movlw scan_lines_per_vpixel
movwf scanline_cnt
return
if ft_color
burst_l equ vid_blank-(burst_amplitude/2)
burst_h equ vid_blank+(burst_amplitude/2)
burst_x equ burst_l^burst_h
; burst starts at 228 cycles from horizontal reference point,
; which is 292 cycles from our start of back porch.
color_burst:
delm 14 ; 270-283
; $$$ actually, don't toggle burst phase, because our current
; line timing is an integral multiple of the color carrier
movlw 0 ; 284 - toggle burst phase
xorwf burst_phase ; 285
movlw 18 ; 286 - 18 half-cycles of burst
movwf int_temp ; 287
movlw burst_h ; 288 - assume leading edge high
btfsc burst_phase,0 ; 289
movlw burst_l ; 290
burst_loop:
xorlw burst_x ; 291 399
movwf vid_port ; 292 400
decfsz int_temp ; 293 401
goto burst_loop ; 294-296 402
delm 2 ; 403
movlw vid_blank ; 405
movwf vid_port ; 406
delm 48 ; 407
return ; 455-457
else
color_burst:
delm 185 ; 270-454
return ; 455-457
endif
equalization_line:
movlw vid_sync ; 64 - start equalizing pulse
movwf vid_port
delm equalization_pulse_width-2
movlw vid_blank ; end equalizing pulse
movwf vid_port
delm ((h/2)-equalization_pulse_width)-2
eq_second_half:
movlw vid_sync ; start equalizing pulse
movwf vid_port
delm equalization_pulse_width-2
movlw vid_blank ; end equalizing pulse
movwf vid_port
return
vsync_line:
movlw vid_sync ; 64 - start vsync pulse
movwf vid_port
delm vsync_pulse_width-2
movlw vid_blank ; end vsync pulse - start serration
movwf vid_port
delm serration_pulse_width-2
vsync_second_half:
movlw vid_sync ; start vsync pulse
movwf vid_port
delm vsync_pulse_width-2
movlw vid_blank ; end vsync pulse - start serration
movwf vid_port
return
vblank_line:
movlw vid_sync ; 64 - start hsync pulse
movwf vid_port ; 65
delm hsync_pulse_width-2 ; 66-264
movlw vid_blank ; 265 - end hsync pulse
movwf vid_port ; 266
call color_burst ; 267-269
return
black_video_line:
movlw vid_sync ; 64 - start hsync pulse
movwf vid_port ; 65
delm hsync_pulse_width-2 ; 66-264
movlw vid_blank ; 265 - end hsync pulse, start back porch
movwf vid_port ; 266
call color_burst ; 267-269
movlw vid_black ; 458 - end back porch, start active video
movwf vid_port ; 459
goto display_frame_setup ; $$$ not the best place for this?
; return
if ft_interlace|ft_pal_video
; NTSC line 263, PAL line 623 (interlaced), PAL line 310 (non-interlaced)
black_eq_line:
movlw vid_sync ; 64 - start hsync pulse
movwf vid_port ; 65
delm hsync_pulse_width-2 ; 66-264
movlw vid_blank ; 265 - end hsync pulse, start back porch
movwf vid_port ; 266
call color_burst ; 267-269
movlw vid_black ; 458 - end back porch, start active video
movwf vid_port ; 459
delm ((h/2)-(hsync_pulse_width+back_porch_width))-5
goto eq_second_half
endif
if ft_interlace
; NTSC line 266, PAL line 313
eq_vsync_line:
movlw vid_sync ; 64 - start equalizing pulse
movwf vid_port
delm equalization_pulse_width-2
movlw vid_blank ; end equalizing pulse
movwf vid_port
delm ((h/2)-equalization_pulse_width)-5
goto vsync_second_half
endif
if ft_interlace|ft_pal_video
; NTSC line 269, PAL line 3
vsync_eq_line:
movlw vid_sync ; 64 - start vsync pulse
movwf vid_port
delm vsync_pulse_width-2
movlw vid_blank ; end vsync pulse - start serration
movwf vid_port
delm serration_pulse_width-5
goto eq_second_half
endif
if ft_interlace
; NTSC line 272 - like an equalization, but a full line with only one pulse
; PAL line 318
eq_vblank_line:
movlw vid_sync ; 64 - start equalizing pulse
movwf vid_port
delm equalization_pulse_width-2
movlw vid_blank ; end equalizing pulse
movwf vid_port
return
endif
if ft_interlace|ft_pal_video
; NTSC line 283, PAL line 23
vblank_black_line:
movlw vid_sync ; 64 - start hsync pulse
movwf vid_port ; 65
delm hsync_pulse_width-2 ; 66-264
movlw vid_blank ; 265 - end hsync pulse
movwf vid_port ; 266
call color_burst ; 267-269
delm ((h/2)+front_porch_width)-458 ; 458-1431
movlw vid_black ; 1432 - start active video
movwf vid_port
return
endif
active_video_line:
movlw vid_sync ; 64 - start hsync pulse
movwf vid_port ; 65
delm hsync_pulse_width-2 ; 66-264
movlw vid_blank ; 265 - end hsync pulse, start back porch
movwf vid_port ; 266
call color_burst ; 267-269
movlw vid_black ; 458 - end back porch, start active video
movwf vid_port ; 459
btfsc vpix_cnt,3 ; vpixel >= 8?
goto active_video_line_done
delm 100
movf line_start,w
movwf char_ptr
movlw chars_per_row+1
movwf char_cnt
; leading dummy character is always blank, allows us to fill
; the pixel pipeline
clrf pixels
character:
; pixel 0
movlw vid_black ; 0
btfsc pixels,0 ; 1
movlw vid_white ; 2
movwf vid_port ; 3
movf char_ptr,w ; 4 - get next character
movwf fsr ; 5
movf indf,w ; 6
bank int_vars ; 7
movwf inverse_flag ; 8
andlw 07fh ; 9
movwf chargen_ptr ; 10
decf char_cnt,w ; 11 - increment buffer pointer
btfss status,zf ; 12 - unless we're at end of line
incf char_ptr ; 13 - (due to pipeline, we pass through
bsf char_ptr,4 ; 14 - here columns+1 times)
; pixel 1
movlw vid_black ; 0
btfsc pixels,1 ; 1
movlw vid_white ; 2
movwf vid_port ; 3
movlw (chargen/4)&0ffh ; 4 - add low part of chargen base
addwf chargen_ptr ; 5
movlw (chargen/4)>>8 ; 6 - add high part of chargen base
movwf chargen_ptr+1 ; 7
btfsc status,cf ; 8
incf chargen_ptr+1 ; 9
bcf status,cf ; 10 - rotate high bit of vpix_cnt into
btfsc vpix_cnt,2 ; 11 - table address
bsf status,cf ; 12
rlf chargen_ptr ; 13
rlf chargen_ptr+1 ; 14
; pixel 2
movlw vid_black ; 0
btfsc pixels,2 ; 1
movlw vid_white ; 2
movwf vid_port ; 3
bcf status,cf ; 4 - rotate next bit of vpix_cnt into
btfsc vpix_cnt,1 ; 5 - table address
bsf status,cf ; 6
rlf chargen_ptr ; 7
rlf chargen_ptr+1 ; 8
delm 6 ; 9-14
; pixel 3
movlw vid_black ; 0
btfsc pixels,3 ; 1
movlw vid_white ; 2
movwf vid_port ; 3
DelM 11 ; 4-14
; pixel 4
movlw vid_black ; 0
btfsc pixels,4 ; 1
movlw vid_white ; 2
movwf vid_port ; 3
movf chargen_ptr+1,w ; 4
movwm ; 5
movf chargen_ptr,w ; 6
iread ; 7-10
movwf pixels ; 11
movmw ; 12
mode 0fh ; 13
movwf chargen_ptr ; 14 - now use chargen_ptr as a temp
; intercharacter space
btfsc vpix_cnt,0 ; 0 - get left four pixels into bits 0..3
swapf pixels ; 1
movlw vid_black ; 2
movwf vid_port ; 3
bcf pixels,4 ; 4
btfsc vpix_cnt,0 ; 5 - get rightmost pixel into bit 4
rrf chargen_ptr ; 6
btfsc chargen_ptr,0 ; 7
bsf pixels,4 ; 8
movlw 01fh ; 9 - invert if needed
btfsc inverse_flag,7 ; 10
xorwf pixels ; 11
; $$$ add more inter-character spacing here?
decfsz char_cnt ; 12
goto character ; 13-15
active_video_line_done:
decfsz scanline_cnt ; more scan lines for this pixel row?
return
movlw scan_lines_per_vpixel
movwf scanline_cnt
incf vpix_cnt ; more pixels for this character row?
movf vpix_cnt,w
xorlw vpixels_per_char
btfss status,zf
return
clrf vpix_cnt
movf char_ptr,w ; advance buffer pointer to next character row
movwf line_start
return
;---------------------------------------------------------------------------
; serial receive routine
;---------------------------------------------------------------------------
if ft_serial_input
ser_idle:
movlw 1 ; 19 - sample every line
movwf ser_rx_samp_cnt ; 20
skip_on_ser_rx_mark ; 21 - start bit detected?
goto ser_ret_25 ; 22-24 - no, return
movlw lines_per_serial_sample * 3 / 2 ; 23
movwf ser_rx_samp_cnt ; 24 - skip start bit and sample first data bit
; in middle of bit time
movlw 8 ; 25 - set up to receive 8 chars
movwf ser_rx_bit_cnt ; 26
clrf ser_rx_byte ; 27 - not needed for 8-bit chars
incf ser_rx_state ; 28
incf ser_rx_flag ; 29 - signal main
movlw (lines_per_serial_sample * 11)/8 ; 30 - set up for stop bit
movwf ser_rx_samp_cnt ; 31
incf ser_rx_state ; 32 - advance to next state
goto ser_ret_36 ; 33-35
ser_stop_bit:
movlw 1 ; 19 - sample every line
movwf ser_rx_samp_cnt ; 20
skip_on_ser_rx_mark ; 21 - line idle?
clrf ser_rx_state ; 22 - yes, back to idle
ser_ret_23: delm 2 ; 23-24
ser_ret_25: delm 4 ; 25-28
ser_ret_29: delm 3 ; 29-31
ser_ret_32: delm 4 ; 32-35
ser_ret_36: return ; 36-38
endif
;---------------------------------------------------------------------------
; splash screen
;---------------------------------------------------------------------------
if ft_splash
org 400h
splash_table:
addwf pcl
dt asc_bel
; 01234567890123456789
dt "SERVID 0.2 Copyright"
dt "2001 Eric Smith and", asc_cr, asc_lf
dt "Richard Ottosen", asc_cr, asc_lf
dt "(SXLIST challenge) "
dt 0
splash:
clrf temp+2
splash_loop:
movf temp+2,w
call splash_table
xorlw 0
btfsc status,zf
retp
page output_char
call output_char ; $$$ change to end with retp?
page $
incf temp+2
goto splash_loop
endif
;---------------------------------------------------------------------------
; character generator macros
;---------------------------------------------------------------------------
org chargen
cg_row1 macro pixels
local char
local col
char set pixels
r1_bits set 0
col set 0
while col<5
r1_bits set (r1_bits*2)+(char%10)
char set char/10
col set col+1
endw
endm
cg_row2 macro pixels
local char
local col
char set pixels
r2_bits set 0
col set 0
while col<5
r2_bits set (r2_bits*2)+(char%10)
char set char/10
col set col+1
endw
dw ((r1_bits&010h)<<4)+(r1_bits&0fh)+((r2_bits&010h)<<5)+((r2_bits&0fh)<<4)
endm
include "charset.inc"
org resetvec
goto reset
end
See: