;---------------------------------------------------------
; Character output
; 
; Send one character to the console from Accumulator; preserve A
; Special case: crout, which prints a carriage return
;---------------------------------------------------------
crout:	; Output return character
	lda #$0d
cout:	; Character output routine (print to screen)
	sta WRITE1_DATA
	CALLOS mli_write, WRITE1_PARMS
	lda WRITE1_DATA
	rts

;---------------------------------------------------------
; init_screen
; 
; Sets up the screen for behaviors we expect
;---------------------------------------------------------
init_screen:
	; Prepare the system for our expectations
	jsr loadfnt
	CALLOS mli_open, OPEN_PARMS	; Open the console
	;jsr ERRORCK
	lda OPEN_REF
	sta WRITE_REF			; Save off our console file references
	sta WRITE1_REF
	sta CONSREAD_REF
	jsr crout

	jsr on80
	lda #80
	sta scr_width			; Davex needs to know the screen width

					; Ask for device number of the console
	CALLOS mli_get_dev_num, GET_DEV_NUM_PARMS
	;jsr ERRORCK
	lda GET_DEV_NUM_REF
	sta D_STATUS_NUM		; Save off our console device references
	sta D_CONTROL_NUM

	lda #$80
	sta D_CONTROL_DATA
	lda #$0d
	sta D_CONTROL_DATA+1
	lda #$02
	sta D_CONTROL_CODE
	CALLOS mli_d_control, D_CONTROL_PARMS	; Turn data entry termination on
	lda #$00
	sta D_CONTROL_DATA
	lda #$0f
	sta D_CONTROL_CODE
	CALLOS mli_d_control, D_CONTROL_PARMS	; Turn escape mode off
	lda #$00
	sta D_CONTROL_DATA
	lda #$05
	sta D_CONTROL_CODE	; Leave the control block set up to flush the typeahead buffer

	lda e_reg		; Read the environment register
	and #$f7		; Turn $C000-$CFFF to R/W
	ora #$40		; Turn $C000-$CFFF to I/O
	sta e_reg		; Write the environment register

; Points SOS' NMI vector at the debug routine in SOS. It normally
; points at an RTS so that hitting RESET doesn't do anything. This
; changes it so when you hit RESET, SOS enters a routine that saves all the
; important stuff, and jumps into the built in monitor. To reenter SOS, do
; a 198CG from the monitor. Known to work through SOS 1.3.
; To bank your memory in, set the bank register to the highest page
; I.e. FFEF:F6 for a 256k machine.
; Your zero page actually lives at $1A00-$1AFF.
;
; In Dav3x, to restart from the monitor, you need to reset the zero page
; and the environment registers, then jump to the restart code:
;                               Davex restart address
;                                       =====
; a000:a9 1a 8d d0 ff a9 70 8d d1 ff 4c ed 21
; a000g
; And then maybe config -4n to get back into 80 column mode, until
; the code is straightened out for setting/resetting modes
	lda $1904	; Grab low byte of NMI vector
	sec		; Make sure that carry is set.
	sbc #$07	; Fall back 7 bytes from the
	sta $1911	; byte currently pointed to
	lda $1905	; (an RTS), and store this in
	sbc #$00	; the NMI JMP instruction.
	sta $1912	; Unwrap the high byte.
	rts

memory_fail:
	jsr mess
	ascz "UNABLE TO ALLOCATE REQUIRED MEMORY."
	jmp *

set_columns:	; $00 = 80 columns, $ff = 40 columns
	lda #INIT_SCREEN_DATA_END-INIT_SCREEN_DATA
	sta WRITE_LEN
	lda #<INIT_SCREEN_DATA
	sta msgp
	lda #>INIT_SCREEN_DATA
	sta msgp+1
	CALLOS mli_write, WRITE_PARMS	; Re-using the message parameter block
	rts

on40:
	lda #columns40
	jmp prep_columns

on80:
	lda #columns80
prep_columns:
	sta INIT_SCREEN_COLUMNS
	jmp set_columns

;---------------------------------------------------------
; HOME
; 
; Clears the screen
;---------------------------------------------------------
home:
	lda #$1c
	jsr cout
	rts

;***********************************************
;
; mess -- print an in-line message
;
mess:
	pla		; Return address is on the stack - which we use to find parms
	clc
	adc #$01
	sta msgp
	sta p
	pla
	adc #$00
	sta msgp+1	; The output buffer pointer, msgp, is set to the caller's memory following mess call
	sta p+1
	ldy #$00
	sty WRITE_LEN+1	; Start with MSB of 16-bit write length to zero
get_next:		; Count the number of bytes to print
	iny
	bne no_inc
	inc p+1
	inc WRITE_LEN+1
no_inc:
	lda (p),y
	bne get_next
	sty WRITE_LEN
	CALLOS mli_write, WRITE_PARMS
	clc		; Flatten return address - take an arbitrary pointer plus length and make it an address
	tya		; Y holds the lsb of the number of bytes we counted
	adc msgp		; Add the original address' lsb 
	sta p		; Hang on to that for a second while we calculate the msb and push that
	lda msgp+1	; Get original address' msb   
	adc WRITE_LEN+1	; Add in the msb from length we counted, plus the carry (if any) from lsb addition
	pha		; Push msb of return address
	lda p		; Grab the lsb we stashed a second ago
	pha		; Push the lsb
	rts		; Return to the caller just past the point of the message

;---------------------------------------------------------
; prbyte: Print Byte routine (HEX value)
;---------------------------------------------------------
prbyte:
prhex:
	PHA		; PRINT BYTE AS 2 HEX
	LSR		; DIGITS, DESTROYS A-REG
	LSR
	LSR
	LSR
	JSR :+
	PLA
	AND #$0F	; PRINT HEX DIG IN A-REG
:	ORA #$B0	;  LSB'S
	CMP #$BA
	BCC :+
	ADC #$06
:	JMP cout
	rts

suspend80:
restore80:
	rts

;---------------------------------------------------------
; loadfnt: Load up the standard Apple character font
;---------------------------------------------------------

ctemp1     =        $FD

dnldcel    =        $FE
dnldchr    =        $FF

dimgptr    =        $21
dcelptr    =        $2a

cwrton     =        $C0DB
cwrtoff    =        $C0DA
cb2ctrl    =        $FFEC
cb2int     =        $FFED

loadfnt:
           lda       #$F0
           sta       $FFDF
           lda       #<fontptr
           sta       dimgptr
           lda       #>fontptr
           sta       dimgptr+1
           lda       #$00
           sta       dnldchr
           lda       #$07
           sta       dnldcel
@1:        jsr       loadchr
           dec       dnldcel
           bpl       @2
           bit       cwrton
           jsr       vretrce
           jsr       vretrce
           bit       cwrtoff
           lda       #$07
           sta       dnldcel
@2:        inc       dnldchr
           bpl       @1

loadchr:   lda       #$00                ; X will be set to 0 for this function
           tax
           tay                           ; Use Y for row counter
@1:        lda       dnldcel             ; Set up cell pointer for ASCII code
           and       #$03
           ora       dcptrl, y
           sta       dcelptr, x
           lda       dnldcel
           lsr
           lsr
           cpy       #$04
           rol
           ora       #$08
           sta       dcelptr+1, x
           lda       dnldchr             ; Store ASCII code into download cell
           sta       (dcelptr, x)
           lda       dcelptr+1, x        ; Fix cell pointer for character image
           eor       #$0C
           sta       dcelptr+1, x
           lda       (dimgptr, x)        ; Store character image
           sta       (dcelptr, x)        ;   into download cell
           inc       dimgptr, x          ; Increment the image pointer
           bne       @2
           inc       dimgptr+1, x
@2:        iny                           ; Increment the row number
           cpy       #$08
           bcc       @1                  ; Not done yet
           rts

vretrce:   sta       ctemp1              ; save bits to be stored
           lda       cb2ctrl             ; control port for "CB2"
           and       #$3F                ; reset high bits to 0
           ora       ctemp1
           sta       cb2ctrl
           lda       #$08                ; test vertical retrace
           sta       cb2int
vwait:     bit       cb2int              ; wait for retrace
           beq       vwait
           rts

dcptrl:   .byte     $78, $7C, $F8, $FC, $78, $7C, $F8, $FC 

fontptr:  .incbin   "../../src/shell/3/starfont.bin"

INIT_SCREEN_DATA:
	.byte 16
INIT_SCREEN_COLUMNS:
	.byte columns80
	.byte 21, $0f	; Make return do newline
	.byte 1	; Reset viewport
	.byte 28	; Clear screen
INIT_SCREEN_DATA_END:

; Table for write one character

WRITE1_PARMS:	.byte 3
WRITE1_REF:	.byte $FF
WRITE1_DATA_PTR:	.word WRITE1_DATA
WRITE1_LEN:	.word $0001
WRITE1_DATA:	.byte $00

; Table for write string

WRITE_PARMS:	.byte 3
WRITE_REF:	.byte $FF
WRITE_DATA_PTR:	.word msgp
WRITE_LEN:	.word $0000

; Table for console read

CONSREAD_PARMS:	.byte $04
CONSREAD_REF:	.byte $00
		.word CONSREAD_INPUT
CONSREAD_COUNT:	.word $0001
CONSREAD_XFERCT:	.word $0000
CONSREAD_INPUT:	.res $100, $00

; Table for open

OPEN_PARMS:	.byte 4
OPEN_NAME:	.addr CONSOLE
OPEN_REF:	.byte $ff
OPEN_OPT_PTR:	.addr 0
OPEN_LEN:	.byte 0

CONSOLE:	.byte CONSOLE_END-CONSOLE_BODY
CONSOLE_BODY:	.byte ".CONSOLE"
CONSOLE_END:

; Table for get device number

GET_DEV_NUM_PARMS:
		.byte $02
GET_DEV_NUM_NAME:
		.addr CONSOLE
GET_DEV_NUM_REF:
		.byte $00

; Table for device status

D_STATUS_PARMS:	.byte $03
D_STATUS_NUM:	.byte $00
D_STATUS_CODE:	.byte $00
D_STATUS_LIST:	.addr D_STATUS_DATA
D_STATUS_DATA:	.byte $00, $00

; Table for device control

D_CONTROL_PARMS:
		.byte $03
D_CONTROL_NUM:	.byte $01
D_CONTROL_CODE:	.byte $00
D_CONTROL_LIST:	.addr D_CONTROL_DATA
D_CONTROL_DATA:	.byte $00, $00

FIND_SEG_PARMS:	.byte $06	; Six parameters
FIND_SEG_MODE:	.byte $00	; In - don't cross 32k boundaries
FIND_SEG_LABEL:	.byte $10	; In - our segment "label"
FIND_SEG_PAGES:	.addr $0080	; In/Out - number of pages
FIND_SEG_BASE:	.addr $0000	; Out - origin segment addr
FIND_SEG_LIMIT:	.addr $0000	; Out - last segment addr
FIND_SEG_NUM:	.byte $00	; Out - segment "number"

GET_PREFIX_PARMS:	.byte $02
GET_PREFIX_PATH:	.addr string2-1
GET_PREFIX_LENGTH:
		.byte $80

SET_PREFIX_PARMS:	.byte $01
SET_PREFIX_PATH:	.addr string2-1	; Must be Pascal string
