; ; Generic Atari graphics driver ; ; ****************************************************************************** ; ---------------------------------------------------------------------- ; ; Header. Includes jump table and constants. ; ; ---------------------------------------------------------------------- .segment "JUMPTABLE" ; Header .byte $74, $67, $69 ; "tgi" .byte TGI_API_VERSION ; TGI API version number .word x_res ; X resolution .word y_res ; Y resolution .byte colors ; Number of drawing colors .byte pages ; Number of screens available .byte 8 ; System font X size .byte 8 ; System font Y size .word aspect ; Aspect ratio ; Function table .addr INSTALL .addr UNINSTALL .addr INIT .addr DONE .addr GETERROR .addr CONTROL .addr CLEAR .addr SETVIEWPAGE .addr SETDRAWPAGE .addr SETCOLOR .addr SETPALETTE .addr GETPALETTE .addr GETDEFPALETTE .addr SETPIXEL .addr GETPIXEL .addr LINE .addr BAR .addr TEXTSTYLE .addr OUTTEXT .addr 0 ; IRQ entry is unused ; ****************************************************************************** ; ---------------------------------------------------------------------- ; ; Parameters ; ; ---------------------------------------------------------------------- x1 := ptr1 y1 := ptr2 x2 := ptr3 y2 := ptr4 radius := tmp1 ; ****************************************************************************** ; ---------------------------------------------------------------------- ; ; Global variables ; ; ---------------------------------------------------------------------- sptr := regsave + 2 .bss error: .res 1 ; Error code .if grmode = 9 || grmode = 11 palette = default_palette .else palette: .res colors ; The current palette .endif mask: .res 1 ; Current pixel mask griocb: .res 1 ; IOCB channel number for graphics .if pages = 2 p0scr: .res 1 ; High byte of screen address for screen page 0 p0dls: .res 1 ; High byte of display list address for screen page 0 ; Page 1's addresses are 8K higher .endif .data mag_x: .byte 1 ; Horizontal text scaling factor mag_y: .byte 1 ; Vertical text scaling factor mag_x8: .word 8 ; Horizontal text scaling factor * 8 mag_y8: .word 8 ; Vertical text scaling factor * 8 text_dir: .byte 0 ; Text direction, .code ; ****************************************************************************** .macro put_pixel ; ---------------------------------------------------------------------- ; ; Put a pixel at (sptr),y using x as the bit mask offset ; ; ---------------------------------------------------------------------- lda (sptr),y eor mask and mask_table,x eor (sptr),y sta (sptr),y .endmacro ; ****************************************************************************** .rodata screen_device: .byte "S:",$9B ; Device code for screen screen_device_length := * - screen_device .code .proc INIT ; ---------------------------------------------------------------------- ; ; INIT: Switch to graphics mode ; ; ---------------------------------------------------------------------- .code ; Initialize drawing color ldx #$FF stx mask ; Find a free IOCB clc lda #$70 search: tax ldy ICHID,x cpy #$FF beq found sbc #$10 bcs search ; No free IOCB lda #TGI_ERR_NO_IOCB sta error rts found: ; Check if enough RAM is available lda #0 sub #mem_needed cmp APPMHI + 1 bcc nomem bne switch cpy APPMHI bcs switch ; No memory nomem: lda #TGI_ERR_NO_MEM sta error rts ; Switch into graphics mode switch: lda #OPEN sta ICCOM,x lda #OPNIN | OPNOT sta ICAX1,x lda #grmode sta ICAX2,x lda #screen_device sta ICBAH,x lda #screen_device_length sta ICBLH,x jsr CIOV .if pages = 2 ; Reserve 8K of high memory lda RAMTOP sub #32 sta RAMTOP ; Close and reopen graphics lda #CLOSE sta ICCOM,x jsr CIOV ; Reopen graphics lda #OPEN sta ICCOM,x lda #OPNIN | OPNOT sta ICAX1,x lda #grmode sta ICAX2,x lda #screen_device sta ICBAH,x lda #screen_device_length sta ICBLH,x jsr CIOV ; Save screen poniters lda SAVMSC + 1 sta p0scr lda SDLSTH sta p0dls .endif stx griocb ; Reset the error code and return lda #TGI_ERR_OK sta error rts .endproc ; ****************************************************************************** .proc DONE ; ---------------------------------------------------------------------- ; ; DONE: Switch back to text mode ; ; ---------------------------------------------------------------------- .code .if pages = 2 ; Free 8K of high memory lda RAMTOP add #32 sta RAMTOP .endif ; Clear griocb lda #$FF ldx griocb sta griocb ; Close the S: device lda #CLOSE sta ICCOM,x jsr CIOV ; Reopen it in Graphics 0 lda #OPEN sta ICCOM,x lda #OPNIN | OPNOT sta ICAX1,x lda #0 sta ICAX2,x lda #screen_device sta ICBAH,x lda #screen_device_length sta ICBLH,x jsr CIOV ; Now close it again; we don't need it anymore lda #CLOSE sta ICCOM,x jmp CIOV .endproc ; ****************************************************************************** .proc GETERROR ; ---------------------------------------------------------------------- ; ; GETERROR: Return the error code in A and clear it ; ; ---------------------------------------------------------------------- .code ldx #TGI_ERR_OK lda error stx error rts .endproc ; ****************************************************************************** .proc CLEAR ; ---------------------------------------------------------------------- ; ; CLEAR: Clear the screen ; ; ---------------------------------------------------------------------- .code ; Load the screen address in sptr lda SAVMSC sta sptr lda SAVMSC + 1 sta sptr + 1 ; Fill with zero lda #0 tay ; Clear full pages if any .if >(scrsize) > 0 ldx #>(scrsize) loop1: sta (sptr),y iny bne loop1 inc sptr + 1 dex bne loop1 .endif ; Clear the rest, if any .if <(scrsize) > 0 loop2: sta (sptr),y iny cpy #<(scrsize) bne loop2 .endif rts .endproc ; ****************************************************************************** .proc GETPALETTE ; ---------------------------------------------------------------------- ; ; GETPALETTE: Return the current palette in A/X ; ; ---------------------------------------------------------------------- .code lda #palette rts .endproc ; ****************************************************************************** .proc GETDEFPALETTE ; ---------------------------------------------------------------------- ; ; GETDEFPALETTE: Return the default palette in A/X ; ; ---------------------------------------------------------------------- .code lda #default_palette rts .endproc ; ****************************************************************************** .proc SETCOLOR ; ---------------------------------------------------------------------- ; ; SETCOLOR: Set the drawing color (in A) ; ; ---------------------------------------------------------------------- .code tax .if grmode = 9 ; Map colors like this: 0 -> 0, 1 -> 15, 2 -> 1, 3 -> 2 etc. beq @cont cpx #1 bne @map ldx #16 @map: dex @cont: .endif lda masks,x sta mask rts .endproc ; ****************************************************************************** .proc CALC ; ---------------------------------------------------------------------- ; ; CALC: Calculate the screen address ; in ; x1 (ptr1) x coordinate ; y1 (ptr2) y coordinate ; out ; sptr + y screen address ; x bit mask index ; ; ---------------------------------------------------------------------- .bss temp: .res 1 .code ; calculate line offset lda y1 + 1 sta temp lda y1 .if x_res / ppb = 40 .define yrep 3 .elseif x_res / ppb = 20 .define yrep 2 .elseif x_res / ppb = 10 .define yrep 1 .endif .repeat yrep asl a rol temp .endrepeat sta sptr ldx temp stx sptr + 1 .repeat 2 asl a rol temp .endrepeat add sptr sta sptr lda temp adc sptr + 1 sta sptr + 1 ; calculate bit mask offset lda x1 and #ppb - 1 tax ; calculate row offset lda x1 + 1 sta temp lda x1 .if ppb = 8 .define xrep 3 .elseif ppb = 4 .define xrep 2 .elseif ppb = 2 .define xrep 1 .endif .repeat xrep lsr temp ror a .endrepeat tay ; sptr += SAVMSC lda SAVMSC add sptr sta sptr lda SAVMSC + 1 adc sptr + 1 sta sptr + 1 ; We're done! rts .endproc ; ****************************************************************************** .proc SETPIXEL ; ---------------------------------------------------------------------- ; ; Draw one pixel at x1, y1 ; ; ---------------------------------------------------------------------- .code jsr CALC put_pixel rts .endproc ; ****************************************************************************** .proc GETPIXEL ; ---------------------------------------------------------------------- ; ; GETPIXEL: Read the color value of a pixel and return it in A/X ; ; ---------------------------------------------------------------------- .code jsr CALC lda (sptr),y and mask_table,x .if ppb = 8 beq zero lda #1 zero: ldx #0 rts .elseif ppb = 4 loop: cpx #3 beq cont lsr a lsr a inx bne loop cont: and #$03 rts .elseif ppb = 2 dex bne shift and #$0F jmp exit shift: ldx #0 lsr a lsr a lsr a lsr a ; Mode 9 mapping exit: .if grmode = 9 ; Map colors like this: 0 -> 0, 15 -> 1, 2 -> 3, 3 -> 4 etc. beq @cont cmp #15 bne @map lda #0 @map: add #1 @cont: .endif ; Mode 10 mapping .if grmode = 10 ; Map out of range colors like this: ; 9 -> 8 ; 10 -> 8 ; 11 -> 8 ; 12 -> 0 ; 13 -> 1 ; 14 -> 2 ; 15 -> 3 cmp #8 bcs @cont sub #12 bcs @cont lda #8 @cont: .endif ; Done! rts .endif .endproc ; ****************************************************************************** .proc LINE ; ---------------------------------------------------------------------- ; ; LINE: Draw a line from x1,y1 to x2,y2 ; ; ---------------------------------------------------------------------- ; locals dx := sreg dy := y1 dx2 := x2 dy2 := y2 iy := tmp1 err := tmp3 .code ; dx = x2 - x1 lda x2 sub x1 sta dx lda x2 + 1 sbc x1 + 1 sta dx + 1 ; if dx is positive, no problem bcs dx_positive ; if dx is negative, swap x1,y1 with x2,y2 lda x1 ; x1 <-> x2, low byte ldx x2 sta x2 stx x1 lda x1 + 1 ; x1 <-> x2, high byte ldx x2 + 1 sta x2 + 1 stx x1 + 1 lda y1 ; y1 <-> y2, low byte ldx y2 sta y2 stx y1 lda y1 + 1 ; y1 <-> y2, high byte ldx y2 + 1 sta y2 + 1 stx y1 + 1 ; Calculate again jmp LINE dx_positive: ; Calculate coords jsr CALC ; dy = y2 - y1 lda y2 sub y1 sta dy lda y2 + 1 sbc y1 + 1 sta dy + 1 ; if dy is negative bcs dy_positive ; dy = -dy lda #0 sub dy sta dy lda #0 sbc dy + 1 sta dy + 1 ; iy = -row_size lda #<(65536 - x_res / ppb) sta iy lda #>(65536 - x_res / ppb) sta iy + 1 bne skip_iy_1 ; always dy_positive: ; iy = row_size lda #<(x_res / ppb) sta iy lda #>(x_res / ppb) sta iy + 1 skip_iy_1: ; dx2 = dx * 2 lda dx asl a sta dx2 lda dx + 1 rol a sta dx2 + 1 ; dy2 = dy * 2 lda dy asl a sta dy2 lda dy + 1 rol a sta dy2 + 1 ; if dx >= dy lda dx cmp dy lda dx + 1 sbc dy + 1 bcc dy_major ; dx is the major axis ; err = dy2 - dx lda dy2 sub dx sta err lda dy2 + 1 sbc dx + 1 sta err + 1 .scope loop: ; main loop put_pixel ; if err >= 0 lda err + 1 bmi err_neg ; err -= dx2 lda err sub dx2 sta err lda err + 1 sbc dx2 + 1 sta err + 1 ; move_vertical (iy) lda sptr add iy sta sptr lda sptr + 1 adc iy + 1 sta sptr + 1 err_neg: ; err += dy2 lda err add dy2 sta err lda err + 1 adc dy2 + 1 sta err + 1 ; move_right inx cpx #ppb bne end_move ldx #0 iny bne end_move inc sptr + 1 end_move: ; loop while dx-- >= 0 lda dx ora dx + 1 beq exit dec dx lda dx cmp #$FF bne loop dec dx + 1 jmp loop exit: rts .endscope dy_major: ; dy is the major axis ; err = dx2 - dy; lda dx2 sub dy sta err lda dx2 + 1 sbc dy + 1 sta err + 1 .scope loop: ; main loop put_pixel ; if err >= 0 lda err + 1 bmi end_move ; err -= dy2 lda err sub dy2 sta err lda err + 1 sbc dy2 + 1 sta err + 1 ; move_right inx cpx #ppb bne end_move ldx #0 iny bne end_move inc sptr + 1 end_move: ; err += dx2 lda err add dx2 sta err lda err + 1 adc dx2 + 1 sta err + 1 ; move_vertical(iy) lda sptr add iy sta sptr lda sptr + 1 adc iy + 1 sta sptr + 1 ; loop while dy-- >= 0 lda dy ora dy + 1 beq exit dec dy lda dy cmp #$FF bne loop dec dy + 1 jmp loop exit: rts .endscope .endproc ; ****************************************************************************** .proc clipped_bar ; ---------------------------------------------------------------------- ; ; Clip and draw bar ; ; ---------------------------------------------------------------------- .code lda y1 + 1 bne off lda y1 cmp #y_res bcs off lda x1 + 1 .if >(x_res - 1) > 0 cmp #>x_res bcc check2 .endif bne off lda x1 cmp #(x_res - 1) > 0 cmp #>x_res bcc BAR .endif bne off lda x2 cmp #(x_res - 1) > 0 lda x2 + 1 sta x1 + 1 .endif jsr CALC ; Save the values tya add sptr sta sptr bcc skips inc sptr + 1 skips: inx lda bar_table,x eor #$FF sta rmask ; Calculate memory difference between x1 and x2 lda sptr sub lmem sta dx loop: ; Main loop ldy #0 ldx dx beq same ; Left lda (lmem),y eor mask and lmask eor (lmem),y sta (lmem),y iny ; Between lda mask jmp next btwn: sta (lmem),y iny next: dex bne btwn ; Right lda (lmem),y eor mask and rmask eor (lmem),y sta (lmem),y jmp cont same: ; Same byte lda lmask and rmask sta fmask lda (lmem),y eor mask and fmask eor (lmem),y sta (lmem),y cont: ; Go to next row lda lmem add #<(x_res / ppb) sta lmem bcc skipm inc lmem + 1 skipm: ; Loop while --dy > 0 dec dy bne loop rts .endproc ; ****************************************************************************** .proc TEXTSTYLE ; ---------------------------------------------------------------------- ; ; TEXTSTYLE: Set text style. Scale factors in X and Y and direction in A ; ; ---------------------------------------------------------------------- .code stx mag_x sty mag_y ; Save text direction in bit 8 so that we can use BIT instruction later lsr a ror a sta text_dir ; Save 8 * scaling factors lda #0 sta mag_x8 + 1 sta mag_y8 + 1 ; Save 8 * mag_x txa .repeat 3 asl a rol mag_x8 + 1 .endrepeat sta mag_x8 ; Save 8 * mag_y tya .repeat 3 asl a rol mag_y8 + 1 .endrepeat sta mag_y8 ; Done! rts .endproc ; ****************************************************************************** .proc OUTTEXT ; ---------------------------------------------------------------------- ; ; OUTTEXT: Draw text at x1, y1. String is in ptr3 ; ; ---------------------------------------------------------------------- ; locals string := tmp1 cols := tmp3 pixels := tmp4 font := regsave .rodata ataint: .byte 64,0,32,96 .bss rows: .res 1 .if >(x_res - 1) > 0 oldx1: .res 2 oldx2: .res 2 .else oldx1: .res 1 oldx2: .res 1 .endif oldy1: .res 1 oldy2: .res 1 inv: .res 1 .code ; Don't draw zero sized characters lda mag_x beq @exit lda mag_y bne @cont @exit: rts @cont: ; Save string address, ptr3 is needed by BAR lda ptr3 sta string lda ptr3 + 1 sta string + 1 bit text_dir bmi vert ; Calculate x2 lda mag_x sub #1 add x1 sta x2 .if >(x_res - 1) > 0 lda x1 + 1 adc #0 sta x2 + 1 .else lda #0 sta x2 + 1 .endif ; Calculate y2 and adjust y1 dec y1 lda y1 sta y2 sub mag_y add #1 sta y1 lda #0 sta y2 + 1 jmp while ; Calculate for vertical text vert: lda x1 sub #1 sta x2 lda x1 + 1 sbc #0 sta x2 + 1 lda x1 sub mag_y sta x1 lda x1 + 1 sbc #0 sta x1 + 1 lda mag_x sub #1 add y1 sta y2 lda #0 sta y2 + 1 beq while ; Always ; Main loop loop: inc string bne skiph inc string + 1 skiph: ; Save coords jsr save_text_y ; Draw one character jsr outchar ; Restore coords jsr restore_text_y ; End of loop while: ldy #0 lda (string),y bne loop ; Check for null character rts ; -------------------- ; Output one character outchar: ; Convert to ANTIC code tay rol a rol a rol a rol a and #3 tax tya and #$9f ora ataint,x ; Save and clear inverse video bit sta inv and #$7F ; Calculate font data address sta font lda #0 sta font + 1 .repeat 3 asl font rol a .endrepeat adc CHBAS sta font + 1 ; Save old coords bit text_dir bpl @hor lda y1 sta oldy1 lda y2 sta oldy2 jmp @cont @hor: lda x1 sta oldx1 lda x2 sta oldx2 .if >(x_res - 1) > 0 lda x1 + 1 sta oldx1 + 1 lda x2 + 1 sta oldx2 + 1 .endif ; Get glyph pixels @cont: ldy #7 ; Put one row of the glyph putrow: sty rows lda (font),y bit inv bpl noinv eor #$FF noinv: sta pixels lda #7 sta cols ; Put one column of the row putcol: asl pixels bcc next_col lda x1 pha lda x1 + 1 pha jsr clipped_bar pla sta x1 + 1 pla sta x1 next_col: ; Go to next column jsr inc_x dec cols bpl putcol next_row: ; Go to next row jsr dec_y ; Restore old values bit text_dir bpl @hor lda oldy1 sta y1 lda oldy2 sta y2 jmp @cont @hor: lda oldx1 sta x1 lda oldx2 sta x2 .if >(x_res - 1) > 0 lda oldx1 + 1 sta x1 + 1 lda oldx2 + 1 sta x2 + 1 .endif ; Next row @cont: ldy rows dey bpl putrow ; We're done! rts ; ------------------------- inc_x: ; increase x coords bit text_dir bmi @vert lda mag_x add x1 sta x1 bcc @1 inc x1 + 1 @1: lda mag_x add x2 sta x2 bcc @2 inc x2 + 1 @2: rts @vert: lda y1 sub mag_x sta y1 lda y2 sub mag_x sta y2 rts ; ------------------------- dec_y: ; decrease y coords bit text_dir bmi @vert lda y1 sub mag_y sta y1 bcs @1 dec y1 + 1 @1: lda y2 sub mag_y sta y2 bcs @2 dec y2 + 1 @2: rts @vert: lda x1 sub mag_y sta x1 bcs @3 dec x1 + 1 @3: lda x2 sub mag_y sta x2 bcs @4 dec x2 + 1 @4: rts ; ------------------------- save_text_y: ; Save text's height coords bit text_dir bmi @vert ldx y1 stx oldy1 ldx y2 stx oldy2 rts @vert: ldx x1 stx oldx1 ldx x2 stx oldx2 .if >(x_res - 1) > 0 ldx x1 + 1 stx oldx1 + 1 ldx x2 + 1 stx oldx2 + 1 .endif rts ; ------------------------- restore_text_y: ; Position to next char bit text_dir bmi @vert ldx oldy1 stx y1 ldx oldy2 stx y2 ldx #0 stx y1 + 1 stx y2 + 1 lda mag_x8 add x1 sta x1 lda mag_x8 + 1 adc x1 + 1 sta x1 + 1 lda mag_x8 add x2 sta x2 lda mag_x8 + 1 adc x2 + 1 sta x2 + 1 rts @vert: ldx oldx1 stx x1 ldx oldx2 stx x2 .if >(x_res - 1) > 0 ldx oldx1 + 1 stx x1 + 1 ldx oldx2 + 1 stx x2 + 1 .endif lda y1 sub mag_x8 sta y1 lda y1 +1 sbc mag_x8 + 1 sta y1 + 1 lda y2 sub mag_x8 sta y2 lda y2 +1 sbc mag_x8 + 1 sta y2 + 1 rts .endproc .if pages = 2 ; ****************************************************************************** .proc SETVIEWPAGE ; ---------------------------------------------------------------------- ; ; SETVIEWPAGE, page in A ; ; ---------------------------------------------------------------------- .code tax beq cont lda #32 cont: add p0dls cmp SDLSTH beq done ; We're already in the desired page ldx RTCLOK + 2 sta SDLSTH ; Wait until next VBLANK wait: cpx RTCLOK + 2 beq wait ; Done done: rts .endproc ; ****************************************************************************** .proc SETDRAWPAGE ; ---------------------------------------------------------------------- ; ; SETDRAWPAGE, page in A ; ; ---------------------------------------------------------------------- .code tax beq @cont lda #32 @cont: add p0scr sta SAVMSC + 1 rts .endproc .endif ; ****************************************************************************** ; ---------------------------------------------------------------------- ; ; Unimplemented functions that require an error code ; ; ---------------------------------------------------------------------- CONTROL: lda #TGI_ERR_INV_FUNC sta error ; fall through ; ****************************************************************************** ; ---------------------------------------------------------------------- ; ; Unimplemented functions that don't require an error code ; ; ---------------------------------------------------------------------- INSTALL: UNINSTALL: .if pages = 1 SETVIEWPAGE: SETDRAWPAGE: .endif rts