please dont rip this site
; 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 14:48, local time: 2024/11/25 02:02,
TOP NEW HELP FIND: 
18.191.62.68:LOG IN

 ©2024 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?
Please DO link to this page! Digg it! / MAKE!

<A HREF="http://massmind.org/techref/scenix/lib/io/dev/video/servid_asm.htm"> scenix lib io dev video servid_asm</A>

After you find an appropriate page, you are invited to your to this massmind site! (posts will be visible only to you before review) Just type a nice message (short messages are blocked as spam) in the box and press the Post button. (HTML welcomed, but not the <A tag: Instead, use the link box to link to another page. A tutorial is available Members can login to post directly, become page editors, and be credited for their posts.


Link? Put it here: 
if you want a response, please enter your email address: 
Attn spammers: All posts are reviewed before being made visible to anyone other than the poster.
Did you find what you needed?

 

Welcome to massmind.org!

 

Welcome to massmind.org!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  .