Files
hr-dhr-character-generator/HRCHARGEN.s

596 lines
14 KiB
ArmAsm

;Hi-Res Character Generator
;v1.0 26 Jan 03--original release
;
;based on the Double Hi-Res Character Generator, v1.3
;
;Copyright (C) 1995-2003 by Scott Alfter
;scott@alfter.us
;http://alfter.us
;
;This program is free software; you can redistribute it and/or modify
;it under the terms of the GNU General Public License as published by
;the Free Software Foundation; either version 2 of the License, or
;(at your option) any later version.
;
;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.
;
;Macros and pseudo-ops used in this source code are those used
;by the MicroSPARC/MindCraft Assembler:
;
; USE include macros from file
; UEN end of includes
; MUL don't expand macros in assembler output
; HEX include one or more hexadecimal bytes
; ] local label, or forward reference to a local label
; [ backward reference to a local label
; * at start of line: comment
; in operand: current address
; ; after operand: comment
; ^ logical AND
; xxx/ take high byte of xxx
; ADDR store a 16-bit quantity in little-endian format
; DFS allocate space
; ASC store the quoted string with the high bit set
;
;System requirements: 64K Apple II+, IIe, IIc, IIc+, IIGS, or compatible.
;
;Recommended use:
;1) Relocate your BASIC program above the Hi-Res buffer with something
; like this:
; IF PEEK(103)+PEEK(104)*256<>16385 THEN POKE 103,1:POKE 104,64:
; PRINT CHR$(4)"RUN FOOBAR"
;2) Load the character generator and font at their default locations:
; PRINT CHR$(4)"BLOAD HRCHARGEN":PRINT CHR$(4)"BLOAD CHARSET"
;3) Turn on the Hi-Res display:
; CALL 2816
; NOTE: Don't use HGR to do this...it won't give you the full screen
; for graphics, and it won't enable monochrome graphics on the IIGS.
;4) Redirect I/O to the character generator:
; PRINT CHR$(4)"PR#A$B06":PRINT CHR$(4)"IN#A$B0A"
;Normal PRINT, INPUT, and GET commands work. HTAB and VTAB work. HOME
;doesn't work, replace it with CALL 2816 or PRINT CHR$(12) to clear the
;screen and move the cursor to the upper left. Scrolling and inverse text
;are implemented. MouseText is not implemented. The control characters
;BEL, BS, LF, FF, and CR are implemented. It's worth noting that you can
;use this to enable lowercase text output on a II+ without a lowercase
;character-generator ROM.
;
;Entry points:
; dec hex description
;2816 B00 switch to HR display & clear screen
;2822 B06 character-output driver
;2826 B0A character-input driver
;2830 B0E load uncompressed HR image
;2833 B11 save uncompressed HR image
;2836 B14 load compressed HR image
;2839 B17 save compressed HR image
;
;Note that the routines that save files won't create them if they don't
;already exist. Use the CREATE command to create an empty binary file,
;like this:
; CREATE FOOBAR,TBIN
;Then stuff the filename to save (or load, for that matter) into the line
;buffer at 0x0200 with the length first and call the routine you want.
;For example, to load a compressed-image file called FOOBAR, use this:
; N$="FOOBAR":POKE 512,LEN(N$):FOR I=1 TO LEN(N$):POKE 512+I,ASC(MID$(N$,
; I,1)):NEXT:CALL 2830
;
;The uncompressed save/load routines are for compatibility with DHRCHARGEN;
;BSAVE and BLOAD are simpler and do the same job. The compressed save/load
;routines could be useful, though.
;
;The routines provided by BASIC for plotting Hi-Res graphics work with
;HRCHARGEN. I'd recommend using only white (HCOLOR=3) and black (HCOLOR=0)
;to plot graphics, since the generated text looks best on a monochrome
;display. It might be possible to create a font that looks good on a
;color display, but that is an exercise left to the reader. If you want
;to experiment with it, turn AN3 off (POKE 49247,0).
;
;The compression applied here is a simple scheme that works best with
;screens that have lots of black space; as many as 256 bytes of black
;area are crunched down to just two bytes; non-black areas are stored
;as is. With the simple graphics I created, this yielded compression
;anywhere from 3:1 to 11:1. Load and save times aren't too badly affected
;when you're working with floppy-disk storage. The delay is noticeable
;when you're working with hard-disk storage on an unaccelerated system,
;but it runs well enough with a hard drive and an accelerator.
;
;The source code was written for the MicroSPARC Assembler; it should be
;possible to adapt the source code to other assemblers without too much
;difficulty.
;
USE MACLIB
UEN
FONTADDR EQU $0800 ;location of bitmapped screen font info
STORE80 EQU $C000 ;80STORE--softswitch for making aux mem use easier
PAGE2 EQU $C054 ;select page 1/page 2 for text & graphics
TEXT EQU $C050 ;select graphics/text display
MIXED EQU $C052 ;select full-screen graphics or mixed text/graphics
HIRES EQU $C056 ;select Lo-Res/Hi-Res graphics
AN3 EQU $C05E ;enable monochrome Hi-Res on IIGS
COL80 EQU $C00C ;select 40/80-column display
KBD EQU $C000 ;keyboard buffer
KBDSTRB EQU $C010 ;keyboard strobe
XPOSN EQU $24
YPOSN EQU $25
HRBASE EQU $E6 ;Hi-Res base address (high byte, used by BASIC)
TMP1 EQU $FA
TMP2 EQU $FC
TMP3 EQU $FE
SCRNPTR EQU $3E
FONTPTR EQU $42
INVFLG EQU $32 ;monitor to tell if we should use normal or inverse
RNDSEED EQU $4E ;increment this while we wait for input
MLI EQU $BF00 ;ProDOS 8 MLI entry point
OPEN EQU $C8 ;selected MLI calls
READ EQU $CA
CLOSE EQU $CC
WRITE EQU $CB
COUT1 EQU $FDF0 ;we need this for the bell
ORG $0B00
JMP HRON ;turn on the Hi-Res display
RTS ;placeholder since we don't need MAIN2AUX
NOP
NOP
CLD ;COUT replacement (hook in with PR#A$xxxx)
JMP HRCOUT
CLD ;RDKEY replacement (hook in with IN#A$xxxx)
JMP HRRDKEY
JMP LOAD ;load uncompressed HR image named by buffer @ 0x0200
JMP SAVE ;save uncompressed HR image
JMP LOADC ;load compressed HR image
JMP SAVEC ;save compressed HR image
CHAR DFS 1
SAVEX DFS 1
SAVEY DFS 1
;
;Turn on HR and clear the screen
;
HRON STA MIXED ;turn on monochrome HR and clear screen
STA HIRES+1
STA AN3
STA TEXT
LDA #$20 ;set Hi-Res base so HPLOT will work
STA HRBASE
LDA #$2000/
STA TMP1+1
LDA #$2000
STA TMP1
STA XPOSN
STA YPOSN
TAY
]LOOP STA (TMP1),Y
INC TMP1
BNE [LOOP
INC TMP1+1
LDX TMP1+1
CPX #$4000/
BNE [LOOP
RTS
;
;HR character-output routine
;
HRCOUT STA CHAR
STX SAVEX
STY SAVEY
PHP
AND #$7F ;strip high bit
CMP #$20 ;printable character?
BCS ]GO
CMP #$0D ;CR?
BEQ ]RTN
CMP #$08 ;BS?
BEQ ]BS
CMP #$0C ;FF?
BEQ ]HOME
CMP #$07 ;BEL?
BEQ ]BELL
CMP #$0A ;LF?
BEQ ]LF
PLP ;not valid, so exit
LDX SAVEX
LDY SAVEY
RTS
]RTN LDA #0 ;CR: move cursor to left edge
STA XPOSN
]LF INC YPOSN ;CR/LF: move cursor down one line
LDA YPOSN
CMP #24 ;need to scroll?
BNE ]EXIT
LDA #23 ;put cursor at bottom line and scroll
STA YPOSN
JSR SCROLL
]EXIT LDX SAVEX
LDY SAVEY
PLP
RTS
]BELL ORA #$80 ;BEL: pass to COUT1 in ROM for sys beep
JSR COUT1
JMP [EXIT
]BS DEC XPOSN ;BS: move cursor back a space, possibly to
BPL [EXIT ;previous line
LDA #79
STA XPOSN
DEC YPOSN
BPL ]EXIT
LDA #0
STA YPOSN
]HOME JSR HRON ;FF: clear screen and move cursor to top
JMP [EXIT
]GO SBC #$20 ;get index into font
STA CHAR
LDA XPOSN ;get display coordinates
STA TMP2
LDA YPOSN
STA TMP2+1
JSR BASECALC
LDA CHAR
STA FONTPTR
LDA #0
STA FONTPTR+1
LDX #3 ;multiply by 8
]LOOP ASL FONTPTR
ROL FONTPTR+1
DEX
BNE [LOOP
CLC
LDA FONTPTR ;find address of character to draw
ADC #FONTADDR
STA FONTPTR
LDA FONTPTR+1
ADC #FONTADDR/
STA FONTPTR+1
LDY #0
LDX #0
]LOOP LDA (FONTPTR),Y ;get part of character from font
BIT INVFLG ;should it be drawn in inverse?
BMI ]NORMAL
EOR #$7F ;invert it
]NORMAL STA (SCRNPTR,X) ;write it to the screen
CLC ;move to the next line
LDA SCRNPTR+1
ADC #4
STA SCRNPTR+1
INY
CPY #8 ;we have 8 lines to draw
BNE [LOOP
INC XPOSN ;move cursor to next space
LDA XPOSN
CMP #40 ;move to next line if needed
BNE ]QUIT
LDA #0
STA XPOSN
INC YPOSN
LDA YPOSN
CMP #24 ;scroll if needed
BNE ]QUIT
LDA #23
STA YPOSN
JSR SCROLL
]QUIT PLP
LDX SAVEX
LDY SAVEY
RTS
;
;Put up a cursor, get keyboard input, and erase the cursor
;
HRRDKEY PHP
STX SAVEX
STY SAVEY
LDA XPOSN ;find memory address for cursor position
STA TMP2
LDA YPOSN
STA TMP2+1
JSR BASECALC
CLC ;invert the bottom two rows...this is the cursor
LDA SCRNPTR+1
ADC #24
STA SCRNPTR+1
LDX #0
LDA (SCRNPTR,X)
EOR #$7F
STA (SCRNPTR,X)
LDA SCRNPTR+1
ADC #4
STA SCRNPTR+1
LDA (SCRNPTR,X)
EOR #$7F
STA (SCRNPTR,X)
]LOOP INC RNDSEED ;bump the random seed
BNE ]CHECK
INC RNDSEED+1
]CHECK LDA KBD ;check keyboard
BPL [LOOP ;pressed?
PHA ;yes...save it while we erase the cursor
LDA #0
STA KBDSTRB ;clear keyboard
LDA (SCRNPTR,X) ;erase cursor
EOR #$7F
STA (SCRNPTR,X)
SEC
LDA SCRNPTR+1
SBC #4
STA SCRNPTR+1
LDA (SCRNPTR,X)
EOR #$7F
STA (SCRNPTR,X)
PLA ;get keypress back
PLP
LDX SAVEX
LDY SAVEY
RTS
;
;Calculate a screen address from X- and Y-coordinates
;input: X-coordinate in TMP2, Y-coordinate in TMP2+1
;
BASECALC LDA #$2000 ;screen starts at 0x2000
STA SCRNPTR
LDA #$2000/
STA SCRNPTR+1
LDA TMP2+1 ;INT(Y/8)*8
AND #$F8
PHA ;INT(Y/8)*40
ASL
ASL
CLC
ADC SCRNPTR
STA SCRNPTR
PLA
ADC SCRNPTR
STA SCRNPTR
LDA TMP2+1 ;Y-INT(Y/8)*8
AND #7
STA TMP1
LDA #0
STA TMP1+1
LDX #7
]LOOP ASL TMP1
ROL TMP1+1
DEX
BNE [LOOP
CLC ;INT(Y/8)*40+(Y-INT(Y/8)*8)*128
LDA SCRNPTR
ADC TMP1
STA SCRNPTR
LDA SCRNPTR+1
ADC TMP1+1
STA SCRNPTR+1
LDA TMP2 ;SCRNPTR=8192+INT(Y/8)*40+(Y-INT(Y/8)*8)*128+X
CLC
ADC SCRNPTR
STA SCRNPTR
RTS
;
;scroll the screen up 1 line
;
SCROLL LDA #0 ;start at top: move line 1 to line 0, etc.
STA TMP2
STA TMP2+1
]LOOP1 JSR BASECALC ;get destination address
LDA SCRNPTR ;save it
STA TMP3
LDA SCRNPTR+1
STA TMP3+1
INC TMP2+1 ;get source address
JSR BASECALC
LDX #8 ;eight rows of pixels in each line
]LOOP2 LDY #39 ;40 bytes in each line
]LOOP3 LDA (SCRNPTR),Y
STA (TMP3),Y
DEY ;do the rest of the row of pixels
BPL [LOOP3
CLC ;move down to the next row of pixels
LDA SCRNPTR+1
ADC #4
STA SCRNPTR+1
LDA TMP3+1
ADC #4
STA TMP3+1
DEX
BNE [LOOP2 ;repeat until we've done all eight rows
LDA TMP2+1
CMP #23 ;did we just move the bottom line up?
BCC [LOOP1
LDX #8 ;clear the bottom line
]LOOP1 SEC
LDA SCRNPTR+1
SBC #4
STA SCRNPTR+1
LDA #0
LDY #39
]LOOP2 STA (SCRNPTR),Y
DEY
BPL [LOOP2
DEX
BNE [LOOP1
RTS
;
;load HR image from disk into memory (pathname at 0x0200)
;
LOAD JSR MLI ;open file
DFC OPEN
ADDR OPENB
LDA OPENB+5 ;get reference number
STA READB+1 ;save it
STA CLOSEB+1
JSR MLI ;read the file
DFC READ
ADDR READB
JSR MLI ;close the file
DFC CLOSE
ADDR CLOSEB
RTS ;done
;
;save uncompressed HR image to disk
;
SAVE JSR MLI ;open file (must already exist)
DFC OPEN
ADDR OPENB
BNE ]ERROR
LDA OPENB+5 ;get reference number
STA WRITEB+1
STA CLOSEB+1
JSR MLI ;write the data
DFC WRITE
ADDR WRITEB
JSR MLI ;close
DFC CLOSE
ADDR CLOSEB
]ERROR RTS ;exit
;
;compress HR screen contents into a file (name @ 0x0200)
;
SAVEC LDA #$2078 ;zero out screen holes to maximize compresion
STA TMP1
LDA #$2078/
STA TMP1+1
]ZERO LDA #0
LDY #7
]LOOP STA (TMP1),Y
DEY
BPL [LOOP
CLC
LDA TMP1
ADC #$80
STA TMP1
LDA TMP1+1
ADC #$80/
STA TMP1+1
CMP #$4000/
BNE [ZERO
STA $3FFF ;make sure compressor doesn't go off-screen
JSR MLI ;open file (must already exist)
DFC OPEN
ADDR OPENB
BNE ]ERROR
LDA OPENB+5 ;get reference number
STA WRITECB+1 ;save it
STA CLOSEB+1
LDA #$2000
STA TMP1
LDA #$2000/
STA TMP1+1
]LOOP LDY #0 ;get screen byte
LDA (TMP1),Y
BEQ ]CRUNCH ;if zero, count 'em
STA CBUF ;put it in the output buffer
INY ;write one byte
STY WRITECB+4
STY CBUF+1
JSR MLI
DFC WRITE
ADDR WRITECB
BEQ ]NEXT
]CRUNCH INY ;count consecutive zeros
BEQ ]CRUNCHED ;up to 256
CMP (TMP1),Y
BEQ [CRUNCH
]CRUNCHED STA CBUF ;put the results in the output buffer
STY CBUF+1
LDA #2
STA WRITECB+4
JSR MLI ;write it
DFC WRITE
ADDR WRITECB
]NEXT CLC
LDA TMP1 ;increment counter
ADC CBUF+1
STA TMP1
LDA CBUF+1
BNE ]NOT256
SEC
]NOT256 LDA TMP1+1
ADC #0
STA TMP1+1
CMP #$4000/ ;end of screen buffer?
BNE [LOOP
JSR MLI ;close file and quit
DFC CLOSE
ADDR CLOSEB
]ERROR RTS
;
;load compressed picture
;
LOADC JSR MLI ;open file
DFC OPEN
ADDR OPENB
BNE ]ERROR
LDA OPENB+5 ;get reference number
STA READCB+1 ;save it
STA CLOSEB+1
LDA #$2000 ;start at beginning of scrn area
STA TMP1
LDA #$2000/
STA TMP1+1
]LOOP JSR MLI ;get a byte
DFC READ
ADDR READCB
LDA CBUF
BEQ ]EXPAND ;if zero, expand it out
LDY #0
STA (TMP1),Y ;write it to memory
INY
STY CBUF
BNE ]NEXT
]EXPAND JSR MLI ;get number of zeros
DFC READ
ADDR READCB
LDY CBUF
LDA #0
]EXPLP DEY ;write out the zeros
STA (TMP1),Y
BNE [EXPLP
]NEXT CLC ;increment counter
LDA TMP1
ADC CBUF
STA TMP1
LDA CBUF
BNE ]NOT256
SEC
]NOT256 LDA TMP1+1
ADC #0
STA TMP1+1
CMP #$4000/ ;end of screen?
BNE [LOOP
JSR MLI ;close file
DFC CLOSE
ADDR CLOSEB
]ERROR RTS ;exit
;
;parameter blocks for MLI calls
;
OPENB DFC 3
ADDR $200 ;pathname buffer
ADDR $1C00 ;I/O buffer (just below page 1)
DFS 1 ;file reference number
READB DFC 4
DFS 1 ;file reference number
ADDR $2000 ;load into 0x2000
ADDR $2000 ;load 0x2000 bytes
DFS 2
CLOSEB DFC 1
DFS 1 ;file reference number
WRITEB DFC 4
DFS 1 ;file reference number
ADDR $2000 ;save from 0x2000
ADDR $2000 ;save 0x2000 bytes
DFS 2 ;bytes written
WRITECB DFC 4
DFS 1 ;file reference number
ADDR CBUF ;output buffer
ADDR 0 ;bytes to write
DFS 2 ;bytes written
READCB DFC 4
DFS 1 ;file reference number
ADDR CBUF ;input buffer
ADDR 1 ;bytes to read
DFS 2 ;bytes read
CBUF DFS 2 ;buffer for read/write of compressed files