; 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
| file: /Techref/scenix/lib/io/dev/video/servid_asm.htm, 32KB, , updated: 2001/10/10 15:48, local time: 2025/10/26 01:04,
216.73.216.22,10-3-83-201:LOG IN
|
| ©2025 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://www.piclist.com/Techref/scenix/lib/io/dev/video/servid_asm.htm"> scenix lib io dev video servid_asm</A> |
| Did you find what you needed? |
|
o List host: MIT, Site host massmind.org, Top posters @none found - Page Editors: James Newton, David Cary, and YOU! * Roman Black of Black Robotics donates from sales of Linistep stepper controller kits. * Ashley Roll of Digital Nemesis donates from sales of RCL-1 RS232 to TTL converters. * Monthly Subscribers: Gregg Rew. on-going support is MOST appreciated! * Contributors: Richard Seriani, Sr. |
|
Ashley Roll has put together a really nice little unit here. Leave off the MAX232 and keep these handy for the few times you need true RS232! |
.