;; ;; JpegView - JPEG image viewer ;; ;; Copyright 2003, Raphael Espino ;; last updated 17-Oct-03 ;; ;; ;; Assemble the decoder first to create jpy1223-8.o or pgmdecdr.o, then ;; ;; to assemble JpegView: ;; ca65 jpegview.asm ;; ld65 jpegview.o jpy1223-8.o -o jpegview -t atari ;; ;; to assemble PgmView: ;; ca65 -D PGM jpegview.asm ;; ld65 jpegview.o pgmdecdr.o -o pgmview -t atari ;; ;; ---------- viewer zero page addresses, 192 and up are available rendpt = 192 ; 2 byte pointer to image data filtpt = 194 ; 2 byte filter pointer drawtemp = 196 ; 2 temporary storage bytes devpt = 198 ; 2 byte pointer to device name menupt = 200 ; 2 byte pointer to current menu options fnlen = 202 ; 1 byte filename length nxtline = 203 ; 2 byte pointer for dithering vfiltpt = 205 ; 2 byte vertical filter pointer scrpt1 = 207 scrpt2 = 209 ;; ---------- end of viewer zero page addresses LODCHN = 2 ; IOCB to use for loading file SAVECHN = 3 ; IOCB to use for saving to file KEYBCHN = 4 ; IOCB to use for reading from keyboard MAXLEN = 64 ; max file name length ROWCRS = 84 ; current cursor row COLCRS = 85 ; current cursor column SAVMSC = 88 OLDCHR = 93 ; character under cursor at previous position OLDADR = 94 ; address of previous cursor position VDSLST = 512 ; DLI vector VKEYBD = 520 ; keyboard IRQ vector SDMCTL = 559 ; shadow DMA control address SDLSTL = 560 GPRIOR = 623 ; priority register, enable GR.9 TABMAP = 675 ; Tab positions PCOLR0 = 704 ; colour shadow addresses COLOR0 = 708 COLOR1 = 709 COLOR2 = 710 COLOR3 = 711 COLOR4 = 712 HELPFG = 732 ; HELP key flag CRSINH = 752 ; cursor inhibit flag CH = 764 ; last keypress shadow address ;; IOCB addresses ICCOM = 834 ICSTA = 835 ICBAL = 836 ICBAH = 837 ICBLL = 840 ICBLH = 841 ICAX1 = 842 ICAX2 = 843 HPOSP0 = 53248 ; Player 0 horiz pos HPOSM0 = 53252 ; Missile 0 horiz pos SIZEP0 = 53256 ; Player 0 size SIZEM = 53260 ; Missile 0 size GRACTL = 53277 ; enable/disable PMGs COLPF1 = 53271 ; playfield 1 colour register COLPF2 = 53272 ; playfield 2 colour register PRIOR = 53275 ; priority register, enable GR.9/10 KBCODE = 53769 ; hardware keyboard code address PORTB = 54017 ; OS RAM control on XL/XE DMACTL = 54272 ; DMA control PMBASE = 54279 ; PMG base address WSYNC = 54282 ; wait for scan line sync NMIEN = 54286 ; NMI enable CIOV = 58454 ; CIO vector SETVBV = 58460 ; Set VBI vector EXITIM = 58463 ; Exit Immediate VBI vector masknm = $580 ; directory mask filenm = masknm+MAXLEN ; file name DLADR = $630 ; display list address SCRADR = $A010 ; 1st screen address SCR2ADR = $5010 ; 2nd screen address .if .defined(PGM) SCR2TMP = SCR2ADR ; PGM decoder has enough space for both .else SCR2TMP = $E010 ; temporary storage for screen 2 until ; decoder finishes .endif PMGBASE = $70 ; put PMG's at $7000 DEBUG = 0 ; enable debugging code? TOPLINE = 7 ; vertical position of graphics mode menu .if .defined(PGM) .define jsr_osramon ; PGM viewer doesn't user RAM under OS .define jsr_osramoff .define jmp_osramoff rts .else .define jsr_osramon jsr OSRAMON ; JPEG viewer uses RAM under OS .define jsr_osramoff jsr OSRAMOFF .define jmp_osramoff jmp OSRAMOFF .endif ;; set up viewer jmp vectors .addr segvector .addr segvectorend-1 .org $0620 segvector: ;; next 12 bytes should be JMP's to viewer's init, start, draw ;; and end code JMP RafRendInit ; init viewer JMP RafRendStart ; image data about to arrive JMP RafRendDraw ; 8 lines of image data available JMP RafRendEnd ; image completed JMP UnusedVec ; unused for now, point at an RTS segvectorend: .addr filemask .addr filemaskend-1 .org masknm filemask: .if .defined(PGM) .byte "D1:*.PGM",155 ; default file mask for PGMs .else .byte "D1:*.JPG",155 ; default file mask for JPEGs .endif filemaskend: ;; header for viewer .addr segcode .addr segcodeend-1 ;; viewer has area from $7900 upwards for itself and screen .org $7900 ;; ;; Init code, this will be called when decoder starts or ;; when it is re-run. Viewer should display start up information, ;; open an IOCB for the decoder to read the JPEG data from, ;; and store the (IOCB number * 16) in IocbNum ;; segcode: RafRendInit: jsr SetNMIEN ; make sure DLIs are disabled @diragn: jsr ClrDirScr lda #1 sta ctrrow sta ctrcol lda #0 ldx #2 stx CRSINH jsr SetRowCol ;; display viewer information jsr strout .if .defined(PGM) .byte "PgmView 0.1" .else .byte "JpegView 0.9" .endif .byte "(17Oct03) Raphael Espino",0 ldx #2 lda #3 jsr SetRowCol lda repeat ; if we are redisplaying bne @readfl ; then don't display directory jsr DispMask jsr DrawScr ;; make sure IOCB is available jsr CloseInFile lda #READDIR ; do a directory ldx #(LODCHN*16) ;; get ready to open the file stx IocbNum ; tell decoder what IOCB to use ldy #24 jsr OpenFile bpl @notdend lda #1 ; couldn't open directory, so flag end of dir .byte $2C @notdend: lda #0 ; directory end not found yet sta dirend jsr RestMask jsr DispDir jsr CloseInFile lsr redodir bcs @diragn @readfl: lda #0 sta repeat sta CRSINH lda #READ ; open the file ldx #(LODCHN*16) ldy #30 jsr OpenFile bmi @error jsr strout .byte 125, "Loading ",0 jmp DispFN ; display filename and return to decoder ;; an error occured, display error code and restart @error: ldx #(LODCHN*16) lda ICSTA,X ; read status value pha ; remember error code jsr CloseInFile ; close file jsr NLStrOut .byte "Error ",0 pla tax jsr DecOut ; display error code ; display file name too jsr strout .byte " - ",0 jsr DispFN ; display file name jmp WaitandRun ;; ;; Viewer start code, will be called immediately before ;; the image data is about to start arriving. Viewer ;; should open graphics mode, open output file, etc. here. ;; This is the first point that the WIDTH and HEIGHT information ;; is valid. Viewer should set up FirstCol, FirstRow, ;; LastCol, LastRow information here. ;; RafRendStart: lda WIDTH sta numcols lda WIDTH+1 ldy #3 @colshft: lsr ror numcols bcc @noincx inc numcols ; round up any fractional parts @noincx: dey bne @colshft lda HEIGHT sta numrows lda HEIGHT+1 ldy #3 @rowshft: lsr ; divide pixels by 8 to convert into rows ror numrows bcc @noinrx inc numrows ; round up any fractional parts @noinrx: dey bne @rowshft lda #40 ; default to 320 pixels (40*8) sta dispcols lda #25 ; default to 200 lines (25*8) sta decdrrows ;; display menu lda #0 sta TABMAP sta TABMAP+1 sta SAVEMODE sta COLOR4 lda #4 ; set position of tab stop for the menu sta TABMAP+2 ldx #1 @cpoff: lda FirstCol,x sta PrevColOff,x ; remember previous column offset dex bpl @cpoff jsr OpenKeyb lda prevopt ; default to previous display mode sta tmpcuropt askagain: ;; calculate filter values to display number of rows/cols this ;; image will use ldy tmpcuropt lda #255 ; default to no vertical filter sta USEVFILT lda @moderatio,y ; set up horizontal filter based Gr. mode sta USEFILT lda modecols,y ; set up number of cols this mode can do sta dispcols ldx @redcopt ; did user specify a filter? bne @mult2 ; yes, so we don't need to calculate it ; X is 0 here ; need to calculate best filter option here jsr SubRowCol lda #25 ; all Gr. modes have 25 rows sta tmpcalcbest lda #40 ; Filter will automatically be set to give ldx #1 ; the equivalent of 40 columns @cmpcols: cmp tmpnumcols bcs @ckrows ; if all cols will fit on screen inx cpx #3 bcs @mult2 ; reached max filter value asl tmpcalcbest asl ; increase size and try again bcc @cmpcols @ckrows: lda tmpcalcbest @cmprows: cmp tmpnumrows bcs @mult2 ; all rows fit on screen too inx cpx #3 bcs @mult2 ; reached max filter value asl bcc @cmprows @mult2: dex beq @endflt lda USEFILT cmp #1 ; have we reached max reduction factor? beq @endflt inc USEVFILT inc USEFILT jmp @mult2 @endflt: lda #125 ; clear the screen jsr PRINT1BYTE ;; start displaying the menu jsr strout .byte 'O'+128,"ffset Col:",0 lda ctrcol beq @ntcctr lda #'C' jsr PRINT1BYTE jmp @skcctr @ntcctr: lda FirstCol jsr AdjRedc ; adjust displayed offset if using filters @skcctr: ldy dispprev ; don't display previous col and row info bmi @firsttm ; the first time lda #0 ; display previous column info jsr DispPrevRowCol @firsttm: jsr strout .byte 127,"Row:",0 lda ctrrow beq @ntrctr lda #'C' jsr PRINT1BYTE jmp @skrctr @ntrctr: jsr AdjRedcRow ; adjust displayed offset if using filters @skrctr: ldy dispprev ; don't display previous col and row info bmi @firsttm2 ; the first time lda #1 ; display previous row info jsr DispPrevRowCol @firsttm2: jsr PrintNL lda #('S'*2) ; 'S' shifted left by 1 bit ldx #3 ; display 'S' in normal video if one of cpx tmpcuropt ; the save modes is selected since it won't ror ; toggle in those modes jsr PRINT1BYTE jsr strout .byte "creen: ",0 lda tmpcuropt cmp #4 ; screen is always off when saving bcs @dmaoff lda SAVEDM beq @dmaon @dmaoff: jsr strout .byte "Off",127,0 jmp @skon @dmaon: jsr strout .byte "On",127,0 @skon: lda DO2SCR beq @skscr2on lda #('F'*2) ; 'F' shifted left by 1 bit ldx #2 ; display 'F' in normal video of HIP or one of cpx tmpcuropt ; the save modes is selected since it won't ror ; toggle in those modes jsr PRINT1BYTE jsr strout .byte "licker: ", 0 lda tmpcuropt cmp #3 ; HIP mode always uses flicker beq @scr2on cmp #4 ; HIP save uses 2 screens too beq @scr2on bcs @scr2off ; MIC + PGM save use 1 screen lda USE2SCR ; if only using 1 screen, then flicker beq @scr2off ; is off @scr2on: lda #'Y' .byte $2C @scr2off: lda #'N' jsr PRINT1BYTE @skscr2on: jsr PrintNL lda #('R'+128) ldx #2 ; display 'R' in normal video if GR.9 mode cpx tmpcuropt ; since it won't toggle in those modes bne @invr ; otherwise display in reverse video lda #'R' @invr: jsr PRINT1BYTE jsr strout .byte "eduction: ",0 ldy tmpcuropt cpy #2 ; is this Gr.9? bne @notgr9 lda #255 bmi @dispnone ; always display 'None' in Gr.9 mode @notgr9: lda @redcopt beq @best cmp @maxredc,y bcc @redcok lda @maxredc,y @redcok: sec sbc #2 @dispnone: jsr DispRedcA ; display current reduction option jmp @skbest @best: jsr strout .byte "Best",0 @skbest: jsr strout .byte 127, 'E'+128, 's'+128, 'c'+128, " Run again" .byte 155,155,0 ;; draw a horizontal line across the screen lda #82 ; control-r in internal code ldy #159 @horizlp: sta (SAVMSC),y dey cpy #120 bcs @horizlp jsr strout .byte "Screen: 40x25 Image:",0 jsr DispWidHei ; display # of cols + rows in image jsr PrintNL jsr DispFN lda #TOPLINE sta ROWCRS jsr NLStrOut .byte 'S'+128," TOGGLES SCREEN WHILE DECODING",155 .byte '1'+128,": GR.8 ",155 .byte '2'+128,": GR.15",155 .byte '3'+128,": GR.9 ",155,0 lda DO2SCR ; is this a 64K machine? beq @noram ; if so then display 64K options too jsr strout .byte '4'+128,": HIP ",155,0 @noram: lda #6 ; 7 options on menu sta optlen jsr NLStrOut .byte "Save As:",155 ; display the save options too .byte 'H'+128,": HIP",155 .byte 'M'+128,": Micropainter", 155 .byte 'P'+128,": PGM", 155, 155 .byte "Choice?" .byte 0 ldy tmpcuropt ; display current mode selection lda menuopts,y sta RENDMODE ;; Set up ready to display prompt sta @displast ; display character for last menu selection .if .not .defined(PGM) cmp #'4' ; HIP always needs 64K beq @disp64k bcs @nodisp64 ; 'H' 'M' and 'P' save modes don't need 64K lda USE2SCR beq @nodisp64 @disp64k: jsr strout .byte " (64K)",0 ; display "(64K)" for 64K modes @nodisp64: .endif lda ROWCRS sta tmpsvrowcrs ldx dispprev ; don't display last mode indicator if bmi @noprev ; this is the first time sec ; start from top of menu ;; ;; find position of previous mode on screen so that we can ;; display a '>' next to it ;; ldx DO2SCR bne @is64K sbc #9 ; 48K menu is 10 lines high .byte $2C @is64K: sbc #10 ; 64K menu is 11 lines high clc adc prevopt ; then find line of previous option ;; save options are a couple of lines further down cmp #TOPLINE+6 bcc @ls6 ldx DO2SCR cpx #1 ; C set if 64K, C clear if 48K adc #1 @ls6: ldx #1 ; position cursor and display a '>' next to jsr SetRowCol ; previous display mode jsr strout .byte ">",0 @noprev: ldx #9 ; position cursor at correct place to lda tmpsvrowcrs ; show selected mode jsr SetRowCol jsr strout @displast: .byte " ",30,0 @nextkp: jsr GetKpNoRet ; get user selection cmp #'4' ; HIP mode only available on 64K machines bne @not4 ldx DO2SCR ; '4' not a valid keypress on 48K machines @beqnextkp: beq @nextkp ; so go get another one @not4: cmp #27 ; Esc key - run again bne @notesc jmp runagain @notesc: cmp #'S' ; S key - turn screen on/off bne @nots lda tmpcuropt ; ignore 'S' key if this is one of the cmp #4 ; save options - can't switch screen on @bcsnextkp: bcs @nextkp ; during save lda SAVEDM eor #1 sta SAVEDM @again: jmp askagain @nots: cmp #'O' ; O key - row/col offset bne @noto lda #0 sta ctrcol sta ctrrow jsr NLStrOut .byte 155,"Offset Col:C",30,0 jsr GetNum ; get number from user rol ctrcol ; if image to be centred, then carry set bne @ctrcl jsr AdjRowCol ; adjust row and column values to accout for sta FirstCol ; filters @ctrcl: jsr strout .byte "Offset Row:C",30,0 jsr GetNum ; get number from user rol ctrrow bne @again jsr AdjRowCol sta FirstRow jmp @again @noto: cmp #'F' bne @notf ; it wasn't the F) option ldx DO2SCR ; if we can't display 2 screens, then @beqnextkp2: beq @beqnextkp ; don't process F) Flicker option ldx tmpcuropt cpx #3 ; don't process flicker for HIP or save bcs @bcsnextkp ; options lda USE2SCR eor #1 sta USE2SCR jmp askagain @notf: cmp #'R' ; R key - change reduction value bne @notr ldy tmpcuropt cpy #2 ; is this GR.9 mode? beq @beqnextkp2 ; if so, then no reduction available ldx @redcopt inx txa cmp @maxredc,y bcc @sk0 beq @sk0 ldx #0 @sk0: stx @redcopt jmp askagain @notr: cmp #155 ; return key beq @retpress jsr GetAdr ; Returns address in Y, Acc untouched bcs @validsel jmp @nextkp ; invalid selection, get another keypress @validsel: tya ; valid selection, store mode lsr sta tmpcuropt ; remember index too jmp askagain ; redisplay screen ;; return key pressed, go display image @retpress: jsr CloseKeyb ; finished with keyboard lda #SCR2TMP sta scrpt2+1 jsr SetScrPt1 ; set up pointer to 1st screen lda tmpcuropt asl ; now jump to the correct routine for the tay ; selected mode iny lda @optadr,y pha dey lda @optadr,y pha rts @redcopt: .byte 0 ; Reduction value (0=best, 1=none, 2=x2, 3=x4) @optadr: .word @gr8-1, @gr15-1, @gr9-1 .word sethip-1, SaveToFile-1, SaveToFileMic-1, SaveToFile-1 ; Horizontal/vertical ratio for modes 8(x1),15(x2),9(x4), HIP(x2). ; Save HIP(x2), Save Mic(x2), Save PGM(x1) @moderatio: .byte 255, 0, 1, 0, 0, 0, 255 ; max reduction values for modes 8(x4), 15(x2), 9(x1), HIP(x2), Save HIP(x2) ; Save Mic(x2), Save PGM(x4) @maxredc: .byte 3, 2, 1, 2, 2, 2, 3 @gr9: lda #(GREY31-GREYTBL) ; set up grey levels for dithering ldx USE2SCR bne @g31 lda #(GREY16-GREYTBL) @g31: sta MINGREYPOS jsr OpenGr15 lda #65 sta GPRIOR ; enable gr.9 bne exitstup @gr8: lda #(GREY4-GREYTBL) ; set up grey levels for dithering sta MINGREYPOS ; don't have to worry about non-flicker mode ; 'cos it doesn't use MINGREYPOS jsr OpenGr15 ; open graphics mode ;; graphics 8 colours are in registers 1 and 2 lda #0 sta COLOR2 lda #8 sta COLOR1 bne exitstup ; forced branch @gr15: ldx #(GREY4-GREYTBL) ; set up grey levels for dithering lda USE2SCR beq @g4 ldx #(GREY9-GREYTBL) @g4: stx MINGREYPOS lda #14 ; Graphics 15 -> DL mode 14 jsr OpenGr ; open graphics mode lda #= display size, use difference lda #0 ; if image size < display size, use 0 @nor0: lsr sta FirstCol,x @noctr: dex bpl @centr lda FirstCol ; calculate last column value from 1st column clc ; and display size adc decdrcols sta LastCol lda FirstRow ; calculate last row value from 1st row clc ; and display size adc decdrrows sta LastRow jsr CalcWidth lda #dithbuff sta nxtline+1 ; when dithering, 128 represents '0' lda #128 ; x < 128 represents negative values ldy #0 ; x > 128 represents positive values @clrlp: sta (nxtline),y iny bne @clrlp inc nxtline+1 @clrlp2: sta (nxtline),y iny cpy #<(DITHBUFLEN+NXTBUFLEN) ; clear dither buffer and nxtline buffer bcc @clrlp2 @noinid: ;; set up our own keyboard IRQ to toggle DMA if key pressed lda SAVEMODE ; don't touch DMA if saving bne @dmaon lda SAVEDM beq @dmaon lda SDMCTL sta SAVEDM jsr SetDMAOff @dmaon: ldx #DMAToggle SetKeyIRQ: lda VKEYBD ; save current keyboard IRQ for later sta OSKEYBIRQ+1 lda VKEYBD+1 sta OSKEYBIRQ+2 sei ; inhibit IRQs stx VKEYBD ; set up keyboard IRQ to toggle DMA on/off sty VKEYBD+1 cli ; enable interrupts IRQrts: rts ;; ;; Set up for drawing in HIP mode ;; sethip: ;; don't need to set up dithering table here as that will be done ;; when rendering, HIP needs to use GREY8 for one screen and GREY16 ;; for the other ldy #1 sty USE2SCR ; HIP always uses 2 screens dey ; store 0 in sethipsv, don't adjust HIP sty hipsvoff ; colour values if not saving jsr OpenGr15 lda #65 sta GPRIOR ; enable gr.9/10 ldx #8 @sthipc: ; set colour registers up for HIP txa ; these will be: asl ; 0,2,4,6,8,10,12,14,0 and #%00001111 sta 704,x dex bpl @sthipc jmp exitstup SVFNOFF=287 ; offset to start of save filename ;; ;; Save the data to a file ;; SaveToFileMic: ; save image as data is being decoded lda #(GREY4-GREYTBL) ; set up grey levels for dithering sta MINGREYPOS SaveToFile: lda #1 sta SAVEMODE lda #0 ; we only ever use 1 screen when saving sta USE2SCR ; even in HIP mode ;; Acc should be 0 when calling SavePt1 jsr SavePt1 bcc @savedok jmp RafRendStart ; didn't save it, go back to menu @savedok: jmp exitstup ;; ;; Open file and write file header to it ;; if Acc=0 then saving as we decode ;; if Acc!=0 then saving from image already displayed ;; ;; Returns with Carry clear if save successful, otherwise carry set. SavePt1: sta tmpsavdisp ; save image already displayed? SavePt2: lda #125 ; clear the screen jsr PRINT1BYTE jsr RestFN jsr PrintNL jsr DispFN ; display filename jsr NLStrOut .byte "Save as ",0 ; tell user what mode we're saving in lda RENDMODE cmp #'M' beq @mic ; saving in Micropainter format cmp #'2' beq @mic cmp #'H' beq @hip ; saving in HIP format cmp #'4' beq @hip jsr strout ; must be PGM .byte "PGM",0 lda #8 bne @dpress @mic: jsr strout .byte "Micropainter",0 lda #0 beq @dpress ; forced branch @hip: jsr DispHIP lda #4 @dpress: ; remember save mode so we can get the sta tmpsvmode ; correct filename extension later lda #5 ; make sure prompt is always on the same line sta ROWCRS ; otherwise TAB completion won't work correctly jsr NLStrOut .byte "Press ",'T'+128,'a'+128,'b'+128," for default filename" .byte 155,"File:D:",0 ;; remove existing extension if there is one ;; and/or add the new one lda #'.' ldx fnlen ; filename len, including device name ldy #4 ; check last 4 characters for extension @extlp: cmp RDBUF,x beq @addext ; found '.' replace old extension dex beq @noext ; end of name, add extension dey bne @extlp ; if no '.' in last 4 chars, then no extension @noext: ldx fnlen inx @addext: ldy tmpsvmode lda #3 sta tmpsavval @extlp2: lda @extensions,y ; copy correct extension to end of filename sta RDBUF,x iny inx dec tmpsavval bpl @extlp2 lda #155 sta RDBUF,x lda SAVMSC clc adc #SVFNOFF sta rendpt+1 lda #SVFNOFF/40 ; tell TabIRQ which line prompt is on sta drawtemp ldx #TabIRQ ; tab key, then display old filename jsr SetKeyIRQ jsr GetLine ; get filename from user jsr GetFileNM jsr ClrKeyIRQ lda RDBUF+2 ; check if a filename was entered cmp #':' ; if 3rd char is ':' then check 4th char bne @ckret ; this handles 'D1:' case lda RDBUF+3 @ckret: cmp #155 ; if it is a return char, then no filename bne @cont sec rts @cont: ;; make sure IOCB is available jsr CloseOutFile bpl @ok @jmsver2: jmp SaveErr2 ;; default extensions for save file formats @extensions: @micext: .byte ".MIC" @hipext: .byte ".HIP" @pgmext: .byte ".PGM" @ok: ;; now open the file lda #WRITE ldy #46 jsr OpenFile bmi @jmsver2 lda tmpsavdisp ; if saving from image already on screen bne @noclr ; then don't clear it, or switch DMA off jsr SetDMAOff lda #>SCRADR jsr ClrScr jsr CalcWidth @noclr: lda tmpsvmode cmp #4 beq @savhip ; HIP is 4 bcc @savmic ; MIC is 0, PGM is 8 @savpgm: ; save in PGM format lda dispwidth+1 lsr ldx dispwidth jsr DecOutRes ; convert width to ASCII ldy #4 @cprw: lda reslt,y ; copy width in ASCII to PGM header sta pgmwdt,y dey bpl @cprw ldx dispheight stx savelines ; number of lines to save in PGM format clc jsr DecOutRes ; convert height to ASCII ldy #4 @cprh: lda reslt,y ; copy height in ASCII to PGM header sta pgmhgt,y dey bpl @cprh ldy #50 ; save PGM bne @savhdr ; forced branch @savhip: lda #200 ; save 200 lines in HIP mode sta savecount lda #TMPSAVEBUF ; strip worth sta scrpt2+1 lda #0 tay ldx #>TMPSAVEBUFLEN @clrlp: sta (scrpt2),y iny bne @clrlp inc scrpt2+1 dex bne @clrlp @clrlp2: sta (scrpt2),y iny cpy #TMPSAVEBUF ; reset to start of buffer sta scrpt2+1 lda #%00010001 ; adjust colour values when saving sta hipsvoff ; forced branch ldy #8 ; save HIP header 1 or PGM header @savhdr: ldx #(SAVECHN*16) jsr SetICBPut bpl @xit jsr CloseOutFile lda tmpsavdisp beq @nsverr2 jmp SaveErr2 @nsverr2: jmp SaveErr @xit: clc rts @savmic: ;; save in micropainter format lda #192 ; save 192 lines in Micropainter mode sta savecount dec decdrrows ; set decdrrows to 24 (24*8=192) clc rts ;; ;; Draw image data. This gets called when a buffer full of image ;; data has been read and decoded from the image. Viewer ;; should display/save/etc the lines as it sees fit. Address ;; of data is in Acc (lo) and Y (hi). Data is 256 levels of ;; greyscale (8 bit luminance), 1 byte per pixel. Data is arranged ;; as BUFLINES lines of 320 pixels. ;; If WIDTH < 320 then remaining data in line will be empty, i.e. ;; if WIDTH = 80 then each line will be 80 bytes of image ;; data followed by 240 bytes that should be ignored ;; RafRendDraw: sta rendpt ; save data buffer address sty rendpt+1 stx rendline lda #0 sta drawcount ; not drawn any lines so far jsr SaveScrPt jsr SetColCnt lda BUFCOUNT asl bne @ntfirst ;; first buffer full for this set of lines, clear out nxtdith buffer lda #128 ldy #NXTBUFLEN @clrlp: dey sta nxtdith,y bne @clrlp lda #<(dithbuff-1) ; move back to start of dither buffer sta nxtval lda #>(dithbuff-1) sta nxtval+1 @ntfirst: lda RENDMODE ; jump to correct routine for current jsr GetAdr ; display mode lda @optadr2,y pha dey lda @optadr2,y pha rts ; jump to render routine @optadr2: .word rendgr8-1, rendgr15-1, rendgr9-1, rendhip-1 .word rendhip-1, rendgr15-1, savefile-1 ;; ;; Draw data in gr.9 mode ;; rendgr9: lda USE2SCR beq @onescr jmp rendgr92 @onescr: asl colcnt ; there are 4 bytes for every column in gr.9 asl colcnt @nextline: jsr Filter ; reduce pixels horizontally jsr SetDith ; initialise dithering variables @gr9loop: lda #0 sta drawtemp jsr DithPix16B php ; save carry bit for later ;; acc will return with current value of drawtemp asl asl asl asl sta drawtemp plp bcs @drawpix ; reached end of line, leave last pixel blank jsr DithPix16B @drawpix: ldy tmpx sta (scrpt1),y ; put pixel on screen lsr dithend bcs @skgrlp iny sty tmpx cpy colcnt ; is this the last column? bcc @gr9loop @skgrlp: jsr add1scr40 ; move data pointer onto next line jsr addtopt320 ; add 320 onto rendpt ldy rendline ; save last error value into buffer in lda nxterr ; case we have another horizontal block sta nxtdith-1,y ; of the image to come dec rendline ; have all lines been drawn? beq @rts jmp @nextline ; no, go back and do the rest @rts: rts ;; ;; Draw data in HIP mode ;; rendhip: asl colcnt ; there are 2 bytes for every column in HIP lda SAVEMODE bne @nextline ; don't toggle OS RAM if saving to disk jsr_osramon @nextline: jsr Filter ; reduce pixels horizontally jsr SetDith ; initialise dithering variables @hiploop: lda #0 sta drawtemp sta drawtemp+1 jsr DithPix16 php ; save carry bit for later ;; acc will return with current value of drawtemp asl asl asl asl sta drawtemp plp bcs @drawpix ; reached end of line, leave last pixel blank jsr DithPix8 php asl asl asl asl sta drawtemp+1 plp bcs @drawpix jsr DithPix16 bcs @drawpix jsr DithPix8 @drawpix: ldy tmpx lda drawtemp sta (scrpt1),y ; put pixel on screen lda drawtemp+1 ; HIP viewers have colours shifted 1 position clc ; to this implementation, so shift back if adc hipsvoff ; we are saving to HIP format sta (scrpt2),y lsr dithend bcs @skhiplp iny sty tmpx cpy colcnt ; is this the last column? bcc @hiploop @skhiplp: ;; move onto next line jsr addscr40 jsr addtopt320 ; add 320 onto rendpt, moving it onto next line ldy rendline ; save last error value into buffer in lda nxterr ; case we have another horizontal block sta nxtdith-1,y ; of the image to come inc drawcount dec rendline ; have all lines been drawn? bne @nextline ; no, go back and do the rest lda SAVEMODE beq @hipend ; not saving image to disk jsr SetScrPos lda BUFCOUNT bpl @rts lda savecount ; subtract lines drawn this time round sec ; from total number of lines to save sbc drawcount sta savecount lda #TMPSAVEBUF sty scrpt2+1 jsr SaveBlkAdr bpl @rts jmp SaveErr @rts: rts @hipend: jsr SetScrPos jmp_osramoff hipsvoff: .byte 0 ; adjust HIP colours if saving ;; ;; Draw data in gr.15 mode ;; rendgr15: lda USE2SCR beq @onescr jmp rendgr152 @onescr: asl colcnt ; there are 2 bytes for every column in gr.15 @nextline: jsr Filter jsr SetDith ; initialise dither variables @gr15loop: lda #0 sta drawtemp @pixlp: ldy tmpy lda (rendpt),y ; get next pixel clc adc nxterr bcc @lt256 bpl @fndnr sec rol drawtemp sec rol drawtemp clc adc #1 bmi @ditpx ; forced branch @lt256: bpl @dodit ldx #0 .byte $2C @fndnr: ldx #2 jsr FNDNR ; find nearest available value to the cmp #2 ; requested one rol drawtemp ; move low 2 bits into drawtemp lsr rol drawtemp tya ldy tmpy jmp @ditpx @dodit: asl drawtemp asl drawtemp @ditpx: jsr DITHPIX ; distribute error amongst surrounding pixels bcc @noexit @rollop: tya ; this is the last byte of image, some pixels and #%00000011 ; may not be set beq @nojmpix asl drawtemp ; pad out any remaining pixels with 0's asl drawtemp iny bne @rollop ; forced branch @noexit: tya and #%00000011 sty tmpy bne @pixlp ; not finished current byte, go get next pixel @nojmpix: lda drawtemp sty tmpy ldy tmpx sta (scrpt1),y ; display pixel on screen lsr dithend bcs @skgrlp iny sty tmpx cpy colcnt ; is this the last column? bcc @gr15loop ; if not, got back and do some more @skgrlp: ; move screen pointer onto next line jsr add1scr40 jsr addtopt320 ; add 320 onto rendpt ldy rendline ; save last error value into buffer in lda nxterr ; case we have another horizontal block sta nxtdith-1,y ; of the image to come inc drawcount dec rendline ; have we done all the lines yet? beq @skiplp jmp @nextline ; if not then go do the rest @skiplp: lda SAVEMODE beq @rts lda BUFCOUNT ; only save the block when we get to the bpl @rts ; last horizontal buffer full lda savecount ; subtract lines drawn this time round sec ; from total number of lines to save sbc drawcount sta savecount jsr SetScrPt1 jsr SaveBlock ; dump lines just drawn to disk bmi @sverr rts @sverr: jmp SaveErr @rts: jmp SetScrPos nxtval: .res 2 ;; ;; TABLE OF GREY SCALE VALUES, SPECIFYING THE COLOUR VALUES ;; AVAILABLE. USED FOR DITHERING GREYTBL: GREY4: ; KEEP GREY4 & GREYTBL TOGETHER .byte 0,85,170,255 ; 4 COLOUR MODE VALUES GREY8: .byte 0,36,73,109,146,182,219,255 GREY9: .byte 0,28,57,85,113,142,170,227,255 ;; 0, 1, 2, 3, 4, 5, 6, 8, 9 - corresponds to getclr values ;; proportional to values available in gr.15 x 2 screens GREY16: .byte 0,17,34,51,68,85,102,119,136,153 .byte 170,187,204,221,238,255 GREY31: .byte 0,8,17,25,34,42,51,59,68,76,85,93,102 .byte 110,119,127,136,144,153,161,170,178,187 .byte 195,204,212,221,229,238,246,255 ;; ;; TABLE OF CUTOFF POINTS FOR GREYS IN GREYTBL. TO FIND THE CLOSEST ;; GREY TO THE ONE SPECIFIED, WE SEARCH THROUGH THE CUTOFF TABLE FOR ;; THE 1ST VALUE THAT IS GREATER THAN THE VALUE WE ARE LOOKING FOR. ;; THIS IS QUICKER THAN HAVING TO CALCULATE THE DISTANCE BETWEEN THE ;; VALUE WE ARE LOOKING FOR AND THE VALUES IN GREYTBL TO FIND ;; SHORTEST DISTANCE ;; CUTOFF: .byte 42,127,212,255 .byte 18,54,91,127,164,200,237,255 .byte 14,42,71,99,127,156,184,241,255 .byte 9,26,43,60,77,94,111,128,145,162 .byte 179,196,213,230,247,255 .byte 4,12,21,29,38,46,55,63,72,80,89,97 .byte 106,114,123,131,140,148,157,165,174 .byte 182,191,199,208,216,225,233,242,250,255 ;; ;; Get next pixel to draw and apply dithering to it using 16 colours ;; ;; returns C set for end of line, clear otherwise ;; DithPix16: ldy #(GREY16-GREYTBL) sty MINGREYPOS DithPix16B: ldy tmpy lda (rendpt),y ; get next pixel clc adc nxterr bcc @lt256 bpl @fndnr tax lda #15 ; value is greater than maximum, so set ora drawtemp ; pixel to max value sta drawtemp ; X >= 128 and error is X+1 this is because: ; if X=128 (0), result=256, value used=255, err=1 ; if X=129 (+1), result=257, value used=255, err=2 ; if X=130 (+2), result=258, value used=255, err=3 ; etc. inx txa bmi @ditpx ; forced branch @lt256: bpl @ditpx ; value is less than minimum @fndnr: ; find nearest value to the requested one tay ; calculate hash value into colour table eor #128 lsr lsr lsr lsr clc adc #(GREY16-GREYTBL) ; use 16 colour table tax tya jsr FNDNR ora drawtemp ; store bottom 4 bits into current pixel sta drawtemp tya ldy tmpy @ditpx: jsr DITHPIX ; distribute error to surrounding pixels ;; don't modify carry below this point lda drawtemp inc tmpy rts ;; ;; Get next pixel to draw and apply dithering to it using 9 colours ;; ;; returns C set for end of line, clear otherwise ;; DithPix8: ldy #(GREY8-GREYTBL) sty MINGREYPOS ldy tmpy lda (rendpt),y ; get next pixel clc adc nxterr bcc @lt256 bpl @fndnr tax lda #7 ; value is greater than maximum, so set ora drawtemp+1 ; pixel to max value sta drawtemp+1 inx txa bmi @ditpx ; forced branch @lt256: bpl @ditpx @fndnr: tay ; calculate hash value into colour table eor #128 asl rol rol rol and #%00000111 clc adc #(GREY8-GREYTBL) ; use 9 colour table tax tya jsr FNDNR ; find nearest value to the requested one ora drawtemp+1 ; store bottom 4 bits into current pixel sta drawtemp+1 tya ldy tmpy @ditpx: jsr DITHPIX ; distribute error to surrounding pixels ;; don't modify carry below this point lda drawtemp+1 inc tmpy rts tmpx: .byte 0 tmpy: .byte 0 dithend: .byte 0 ; end of line flag for dithering widhi: .byte 0 ;; keep decdrcols and decdrrows together decdrcols: .byte 0 ; width of display in columns decdrrows: .byte 0 ; height of display in rows dispcols: .byte 0 ; number of columns passed to decoder ;; ;; Draw data in gr.8 mode ;; rendgr8: lda USE2SCR ; are we using 2 screens? beq @dogr8 jsr_osramon ; if so, turn on the RAM under the OS @dogr8: jsr Filter jsr SetDith ; initialise dithering variables @gr8loop: lda #0 sta drawtemp sta drawtemp+1 @pixlp: ldy tmpy lda (rendpt),y ; get next pixel clc adc nxterr ; add in the corresponding error php ; 128+128 = 256, so 256 is '0', hence ldx USE2SCR ; carry determines if pixel is on or off beq @onepix ; not using 2 screens, 1 bit per pixel plp bcc @lt256 bpl @fndnr sec ; requested value is greater than max rol drawtemp ; set pixel to max value sec rol drawtemp+1 clc adc #1 bmi @ditpx ; forced branch @lt256: bpl @dodit ldx #0 .byte $2C @fndnr: ldx #2 jsr FNDNR ; find nearest value to the one requested lsr rol drawtemp+1 ; shift low 2 bits into position lsr rol drawtemp tya ldy tmpy jmp @ditpx @dodit: asl drawtemp ; requested value was less than minimum asl drawtemp+1 ; set to 0 jmp @ditpx @onepix: rol drawtemp plp adc #0 @ditpx: jsr DITHPIX ; distribute error to surrounding pixels bcc @noexit .byte $24 @rollop: iny ; if we're reached the right edge of the tya ; image, but we haven't got a full byte and #%00000111 ; 8 pixels in 1 byte beq @nojmpix asl drawtemp ; then rest of byte with 0's asl drawtemp+1 jmp @rollop @noexit: tya and #%00000111 ; process 8 gr.8 pixels (1 byte worth) sty tmpy bne @pixlp @nojmpix: lda drawtemp sty tmpy ldy tmpx sta (scrpt1),y ; display pixel on 1st screen lda USE2SCR beq @gr8noram lda drawtemp+1 sta (scrpt2),y ; display pixel on 2nd screen @gr8noram: lsr dithend bcs @skgr8lp iny sty tmpx cpy colcnt ; is this the last column? bcs @skgr8lp jmp @gr8loop @skgr8lp: jsr addscr40 lda #64 jsr addtopt ; move data pointer onto next line lda widhi bne @noirp inc rendpt+1 @noirp: ldy rendline ; save last error value into buffer in lda nxterr ; case we have another horizontal block sta nxtdith-1,y ; of the image to come dec rendline beq @exit jmp @dogr8 @exit: jsr SetScrPos lda USE2SCR ; are we using 2 screens? beq @rts jsr_osramoff ; yes, so disable OS RAM @rts: rts ;; ;; Copy the second gr.15 screen to under the OS ;; rendgr152: jsr_osramon asl colcnt ; there are 2 bytes for every column in gr.15 @dogr152: jsr Filter jsr SetDith ; initialise dithering variables @gr152loop: @pixlp: ldy tmpy lda (rendpt),y ; get next pixel clc adc nxterr bcc @lt256 bpl @fndnr sec ; value is greater than max, so set pixel rol drawtemp ; to max value sec rol drawtemp sec rol drawtemp+1 sec rol drawtemp+1 clc adc #1 bmi @ditpx ; forced branch @lt256: bpl @dodit @fndnr: tay ; calculate hash value into colour table eor #128 asl rol rol rol and #%00000111 clc adc #(GREY9-GREYTBL) ; use 9 colour table tax tya jsr FNDNR ; find nearest value to the requested one jsr getclr cmp #2 ; shift low 2 bits into drawtemp rol drawtemp lsr rol drawtemp txa cmp #2 ; shift low 2 bits into drawtemp+1 rol drawtemp+1 lsr rol drawtemp+1 tya ldy tmpy jmp @ditpx @dodit: asl drawtemp ; value less than minimum, set pixel to 0 asl drawtemp asl drawtemp+1 asl drawtemp+1 @ditpx: jsr DITHPIX ; distribute error amongst surrounding pixels bcc @noexit @rollop: tya and #%00000011 ; 4 pixels per byte beq @nojmpix asl drawtemp ; if this is last byte, then pad any extra asl drawtemp ; bits with 0 asl drawtemp+1 asl drawtemp+1 iny bne @rollop @noexit: tya and #%00000011 ; if not last pixel in this byte sty tmpy bne @pixlp ; then go and get another one @nojmpix: lda drawtemp pha lda drawtemp+1 sty tmpy ldy tmpx sta (scrpt2),y ; display pixel on 2nd screen pla sta (scrpt1),y ; display pixel on 1st screen lsr dithend bcs @skgrlp iny sty tmpx cpy colcnt ; is this the last column bcs @skgrlp jmp @gr152loop @skgrlp: ; move screen pointer onto next line jsr addscr40 jsr addtopt320 ; add 320 onto rendpt, moving it onto next line ldy rendline ; save last error value into buffer in lda nxterr ; case we have another horizontal block sta nxtdith-1,y ; of the image to come dec rendline ; have we done all the lines yet? beq @skpjmp jmp @dogr152 ; if not then go do the rest @skpjmp: jsr_osramoff jmp SetScrPos ;; ;; Return colour values for both GR.15 screens, converts 9 colour ;; values into 2 sets of 4 colours, one for each screen ;; ;; Parameters: ;; X = colour value to convert (0-8) ;; ;; Returns: ;; A = converted colour value for screen 1 (0-3) ;; X = converted colour value for screen 2 (0-3) ;; getclr: tax lda @lowclrtab,x pha lda @hiclrtab,x tax pla rts ;; ;; Values to use on 2 GR.15 screens to get 9 colours ;; ;; 0, 1, 2, 3, 4, 5, 6, 8, 9 - apparent grey values @lowclrtab: .byte 0, 0, 1, 1, 2, 2, 3, 2, 3 @hiclrtab: .byte 0, 1, 1, 2, 1, 2, 2, 3, 3 ;; ;; Render using two gr.9 screens ;; rendgr92: jsr_osramon asl colcnt ; there are 4 bytes for every column in gr.9 asl colcnt @nextline: jsr Filter ; reduce pixels horizontaly and/or vertically jsr SetDith ; initialise dithering variables @gr9loop2: lda #0 sta drawtemp sta drawtemp+1 @nextpix: ldy tmpy lda (rendpt),y ; get next pixel clc adc nxterr bcc @lt256 bpl @fndnr tax lda #15 ; value is greater than maximum, so set ora drawtemp ; pixel to max value sta drawtemp ; X >= 128 and error is X+1 this is because: ; if X=128 (0), result=256, value used=255, err=1 ; if X=129 (+1), result=257, value used=255, err=2 ; if X=130 (+2), result=258, value used=255, err=3 ; etc. inx txa bmi @ditpx ; forced branch @lt256: bpl @ditpx ; value is less than minimum @fndnr: ; find nearest value to the requested one tay ; calculate hash value into colour table eor #128 lsr lsr lsr and #%00011110 ; only 31 colours (0-30), not 32 (0-31) clc ; make sure we never index to pos.31 adc #(GREY31-GREYTBL) ; use 31 colour table tax tya jsr FNDNR lsr ; now copy xxx1111x bits into drawtemp bcc @noinc ; if low bit is set, and not at max pixel inc drawtemp+1 ; levels of grey @noinc: ora drawtemp ; store remaining 4 bits into current pixel sta drawtemp tya ; move error value into acc ldy tmpy @ditpx: jsr DITHPIX ; distribute error to surrounding pixels ldx #0 bcc @noinx inx ; set x to 1 if at end of line @noinx: tya lsr bcc @drawpix ; got 2 pixels, put them on the screen lda drawtemp ; make space for next pixel asl asl asl asl sta drawtemp lda drawtemp+1 ; make space for next pixel on 2nd screen asl asl asl asl sta drawtemp+1 sty tmpy txa beq @nextpix ; go do next pixel iny @drawpix: sty tmpy ldy tmpx lda drawtemp sta (scrpt1),y clc ; pixel for 2nd screen will be either adc drawtemp+1 ; same as 1st screen, or 1st screen sta (scrpt2),y ; pixel + 1 for mininum flicker lsr dithend bcs @skgrlp iny sty tmpx cpy colcnt ; is this the last column? @skgrlp: bcc @gr9loop2 jsr addscr40 ; move data pointer onto next line skip the other 240 bytes as they ; are empty jsr addtopt320 ; add 320 onto rendpt ldy rendline ; save last error value into buffer in lda nxterr ; case we have another horizontal block sta nxtdith-1,y ; of the image to come dec rendline ; have all lines been drawn? beq @exit jmp @nextline ; no, go back and do the rest @exit: jmp_osramoff ;; ;; End of data. Gets called when image has finished. Viewer ;; should close files, restore system back to original state, ;; wait for user to press a key and then return. After returning ;; decoder will exit back to environment (DOS). To restart decoder ;; instead do JMP (RERUN) instead of RTS. If decoder failed to ;; decode image then this will be called with Error > 0 ;; RafRendEnd: jsr Reset ; reset IRQ and DMA jsr CloseInFile ; close input file ldx #255 ; clear last key press stx CH inx stx COLOR4 ; set background colour to black stx tmpscron lda Error ; check for error or user abort bne @endsave lda RENDMODE ldx SAVEMODE bne @dosave ; we are saving the image to disk cmp #'4' bne @nhip ; only enable PMGs in HIP mode jsr ShowPMGs ldy #0 lda #255 @setpmg: ; initialise Player and Missile 0's data area dey sta PMGBASE*256+384,y bne @setpmg @nhip: jmp notsave @dosave: cmp #'M' beq @micsave ; saving in Micropainter format bcs @endsave ; P > M, so must be PGM format @hipsave: lda savecount ; make sure we've saved enough data beq @svhip2 cmp #25 ; if less than 25 lines, we can do it bcc @lastblkh ; in 1 block lda #24 ; otherwise, just do 25 lines @lastblkh: sta drawcount lda #>SCR2ADR ldx #4 ; clear 4 pages worth jsr ClrArea ; clear temporary data area for save @hiplp: lda #SCR2ADR jsr SaveBlkAdr bmi @sverr jsr calccount bne @hipsave @svhip2: ldx #(SAVECHN*16) ldy #12 ; save HIP header 2 jsr SetICBPut bmi @sverr ldy #0 jsr SetICBPut ; save 2nd screen to disk bmi @sverr bpl @endsave @micsave: ; saving in Micropainter format lda savecount ; make sure we've saved enough data beq @saveclr cmp #25 ; if less than 25 lines, we can do it bcc @lastblk ; in 1 block lda #24 ; otherwise, just do 25 lines @lastblk: sta drawcount lda #>SCRADR ldx #4 ; clear 4 pages worth jsr ClrArea ; clear temporary data area for save @savlp: jsr SaveBlock bpl @ok @sverr: jmp SaveErr @ok: jsr calccount bne @micsave @saveclr: jsr SaveMicClrs ; save colours (grey levels) for image bmi @sverr @endsave: ;; we are saving to disk, so close file jsr CloseOutFile lda Error bne waitmsg jsr OpenGr0 jsr NLStrOut .byte "Saved!",0 waitmsg: jsr PrintNL lda Error beq WRunMenu ; save completed cmp #ABORTCD ; user aborted, rerun beq beqrerun WaitandRun: ldx #0 ; don't show Redisplay option .byte $2c WRunMenu: ldx #1 ; show Redisplay option ;; drops down into WAITKEYP ;; ;; wait for key press ;; WAITKEYP: stx tmpwaitkeyp txa beq @noredis bpl @nocont jsr NLStrOut .byte 'A'+128, "-Try again",0 @nocont: jsr NLStrOut .byte 'C'+128,"-Change image options",0 @noredis: jsr NLStrOut .byte 'E'+128,'s'+128,'c'+128, "-Run again",155 .byte 'X'+128, "-Exit to DOS",155,0 nomenu: jsr GetKey cmp #28 ; Esc key beqrerun: beq runagain cmp #22 ; X key bne @notdos jsr CloseInFile jmp DOS @notdos: ldx tmpwaitkeyp beq nomenu ; no A or C options on menu bpl @notcont ; no A option on menu cmp #63 ; A key bne @notcont rts @notcont: cmp #18 ; 'C' key bne nomenu inc repeat ; redisplay current image runagain: jsr CloseInFile jmp (RERUN) ; rerun decoder DOS: jsr strout .byte 125,0 ldx STACKPT txs ldy #0 sty CRSINH rts .if DEBUG ; debug code for testing correct positioning mvm0: ; of PMGs in HIP mode ldx hpm0 cmp #5 beq @mvm0r dex .byte $24 @mvm0r: inx stx hpm0 stx HPOSM0 jmp waitkp mvp0: ldx hpp0 cmp #2 beq @mvp0r dex .byte $24 @mvp0r: inx stx hpp0 stx HPOSP0 jmp waitkp .endif notsave: jsr VBION ; set up immediate VBI to switch screens jsr SetNMIEN ; make sure DLIs are disabled sta tmpnmien lda USE2SCR beq waitkp ; if we copied data to OS RAM jsr CopyScr ; then copy it back down again .if DEBUG lda #0 sta vbiscr ; reset VBI to display both screens .endif lda RENDMODE cmp #'1' beq @gr8dli ; this is GR.8 mode cmp #'4' beq @hipdli ; this is HIP mode cmp #'2' bne waitkp lda #2 ; this is a GR.15 mode sta COLOR0 asl ; Acc = 4 sta dlicol1 asl ; Acc = 8 sta COLOR2 lda #6 sta COLOR1 lda #10 sta dlicol2 lda #GR15DLI bne @stupdl @gr8dli: lda #0 sta COLOR2 sta dlicol2 lda #4 sta dlicol1 lda #8 sta COLOR1 lda #GR8DLI bne @stupdl @hipdli: lda #HIPDLI @stupdl: sta VDSLST stx VDSLST+1 jsr SetDLI lda #192 sta NMIEN ; enable DLIs sta tmpnmien ;; get key press waitkp: jsr GetKey cmp #28 ; ESC key - rerun decoder beq @exit cmp #33 ; SPACE BAR - show help bne @notinf @infsc: jmp InfoScr @notinf: cmp #17 ; HELP key - display image info/help beq @infsc cmp #39 ; Inverse vid/Atari key - disp image info/help beq @infsc cmp #38 beq @infsc ; '/' key (shifted is '?' key) image info/help cmp #22 ; 'X' key - exit to DOS beq @exit cmp #18 ; 'C' change options for this image beq @repeat cmp #40 ; 'R' key repeats same image again beq @repeat cmp #62 ; 'S' save to file .if DEBUG bne @sksv jmp SaveScr @sksv: .else beq SaveScr .endif cmp #57 ; 'H' - display image info/help beq @infsc .if DEBUG ;; disable this for release mode, only for debugging cmp #31 ; '1' key - display screen 1 in flicker modes beq @scr1 cmp #30 ; '2' key - display screen 2 in flicker modes beq @scr2 cmp #26 ; '3' key - display both scr in flicker modes beq @scr12 cmp #1 ; 'J' key bne @ntmv01 jmp mvm0 @ntmv01: cmp #5 ; 'K' key bne @ntmv02 jmp mvm0 @ntmv02: cmp #0 ; 'L' key bne @ntmv03 jmp mvp0 @ntmv03: cmp #2 ; ';' key bne @ntmv04 jmp mvp0 @ntmv04: .endif cmp #6 ; + key beq @incclr cmp #14 ; - key bne waitkp ; decrease colour number used ; leave luminance unchanged lda COLOR4 sec sbc #16 jmp @dorest .if DEBUG @scr1: lda #1 sta vbiscr bne waitkp ; forced branch @scr2: lda #255 sta vbiscr bne waitkp ; forced branch @scr12: lda #0 sta vbiscr beq waitkp ; forced branch .endif @repeat: inc repeat ; redisplay current image @exit: pha jsr VBDLIOFF jsr HidePMGs pla cmp #22 ; if this was X key, then go do to DOS beq @rts jmp (RERUN) @rts: jmp DOS @incclr: lda COLOR4 ; increase colour number used clc ; leave luminance unchanged adc #16 @dorest: sta COLOR4 inc changeclr bnewaitkp: .if DEBUG ;; avoid range error jmp waitkp .else bne waitkp ; forced branch .endif ;; ;; Update savecount to account for lines about to be saved to disk ;; calccount: lda savecount sec sbc drawcount sta savecount rts ;; ;; Save currently displayed image to a file ;; SaveScr: ; check if we can save in this mode lda RENDMODE cmp #'4' beq @dosaveh ; saving in HIP format cmp #'2' bne bnewaitkp lda USE2SCR ; don't save if in GR.15 Flicker mode bne bnewaitkp @dosaveh: jsr Scr0on lda #1 jsr SavePt1 ; open file for write and save header info bcs @showhlp ; if file not created, C will be set jsr SetDMAOff jsr SwapScr ldx #(SAVECHN*16) lda RENDMODE cmp #'4' beq @svhip ; saving in HIP format ;; saving in MIC format ldy #54 ; save screen data jsr SetICBPut bmi @notsvd jsr SaveMicClrs ; save colour data bmi @notsvd @saved: jsr RestoreScr jsr NLStrOut .byte "File Saved!",0 @showhlp: jmp ShowHelp ; display help screen @notsvd: ; error while saving jsr RestoreScr jsr SaveErr2 ; display error message ; and try again, decoder will restart if jmp ShowHelp ; user selected anything other than try again @svhip: lda #1 ; HIP data is distributed over 2 screens sta tmpsavept2 ; with data alternating between them lda #SCR2ADR ; at a time from the correct screen sta rendpt+1 lda #<(SCRADR+40) sta filtpt lda #>(SCRADR+40) sta filtpt+1 lda #%00010001 sta tmphipoff @hiplp2: lda #100 ; save 100 lines from each screen (200 total) sta tmpsavept1 @hiplp: ldy #39 ; process next line (40 bytes worth) @hiplp4: lda (rendpt),y clc adc tmphipoff ; shift pixel values up by one for save sta tmphipbuf,y dey bpl @hiplp4 ldy #39 @hiplp3: lda (filtpt),y clc adc tmphipoff sta tmphipbuf+40,y dey bpl @hiplp3 ldy #58 jsr SetICBPut ; save 80 bytes (one line) of data to file bmi @notsvd ; error occured during save lda #80 ; move ahead 2 lines, skipping 1 jsr addtopt ; the skipped lines will be processed on lda #80 ; the other pass jsr addtofl dec tmpsavept1 bne @hiplp ldy #12 ; save header for 2nd HIP screen jsr SetICBPut bmi @notsvd lda #SCRADR sta rendpt+1 lda #<(SCR2ADR+40) sta filtpt lda #>(SCR2ADR+40) sta filtpt+1 lda #0 sta tmphipoff dec tmpsavept2 bpl @hiplp2 jmp @saved ; finished saving ;; ;; close output file and show GR.0 screen ;; RestoreScr: jsr CloseOutFile ; finished save jsr SwapScr lda #34 ; re-enable DMA jmp SetDMA .if DEBUG hpm0: .byte 0 hpp0: .byte 0 .endif ;; ;; Display image information screen ;; InfoScr: jsr Scr0on jsr strout .byte "Image: ",0 ; display all info for this image jsr RestFN jsr DispFN jsr NLStrOut .byte "Mode: ",0 lda RENDMODE cmp #'4' beq @hip jsr strout .byte "GR.",0 lda RENDMODE cmp #'2' bcc @gr8 ; if it's less than '2' if must be GR.8 beq @gr15 ; this is GR.15 lda #'9' .byte $2C @gr8: lda #'8' jsr PRINT1BYTE jmp @dispflick @gr15: jsr strout .byte "15",0 jmp @dispflick @hip: jsr DispHIP @dispflick: lda USE2SCR beq @noflick jsr strout .byte " Flicker",0 @noflick: jsr NLStrOut .byte "Size:",0 jsr DispWidHei jsr NLStrOut .byte "Offset: ",0 jsr DispRowCol jsr NLStrOut .byte "Reduction: ",0 jsr DispRedc lda SKIPERR ; tell user about any decoding errors beq ShowHelp ; that were skipped while decoding jsr NLStrOut .byte 155, "*** CORRUPTED FILE ***",0 ShowHelp: ; display help information too jsr NLStrOut .byte 155,155,"Help:" .byte 155,'C'+128, "-Change options", 155 .byte 'E'+128, 's'+128, 'c'+128, "-Run again", 155 .byte 'S'+128, 'p'+128, 'a'+128, 'c'+128, 'e' + 128 .byte "-Back to image", 155 .byte 'S'+128, "-Save (GR.15",0 lda DO2SCR beq @not64K jsr strout .byte " and HIP",0 @not64K: jsr strout .byte " only)",155 .byte 'X'+128, "-Exit to DOS",155 .byte '+'+128, '/', '-'+128, "-Change image colours",155,0 @nomenu: jsr GetKey ; now wait for user to press a key cmp #40 beq @redisp cmp #18 ; 'C' key bne @notr @redisp: inc repeat ; change parameters for current image @rerun: jsr Scr0off ; reset screen jsr HidePMGs ; PMGs jsr VBDLIOFF ; VBIs and DLIs jmp (RERUN) @notr: cmp #33 ; Space key - go back to image beq ShowImg cmp #28 ; Esc key - start again beq @rerun cmp #62 ; 'S' save to file bne @ntsv jmp SaveScr @ntsv: cmp #22 ; X key - exit to DOS bne @nomenu jmp DOS ShowImg: ; return to image jsr Scr0off jmp waitkp ; and wait for user to press a key ;; ;; Display "HIP" on screen ;; DispHIP: jsr strout .byte "HIP",0 rts ;; ;; Open an IOCB ;; Acc = 4 -> open file for read ;; Acc = 6 -> open for directory read ;; Acc = 8 -> open file for write ;; OpenFile: sta ICAX1,x jmp SetICBOpen devlen: .byte 2 ; length of device + directory name in filename ;; ;; IOCB ICBAL/H ICBLL/H set up data, used by the SetICB routines ;; address, length ;; icbdat: .word SCRADR, 8000 ; y=0 rdbuf2io: .word RDBUF+2, MAXLEN-6 ; y=4 .word hiphdr10, 6 ; y=8 .word hiphdr9, 6 ; y=12 micclrdata: .word micclrs, 6 ; y=16 .word K ; y=20 - 2 bytes eio: .word E ; y=22 - 2 bytes bufio: .word masknm ; y=24 - 2 bytes, keep 24 and 26 together .word 0 ; y=26 - 2 bytes, keep 24 and 26 together .word 40 ; y=28 - 2 bytes .word filenm, MAXLEN ; y=30 putadr2: .word 0 ; y=34 dispwidth: .word 320*8 putadr3: .word 0, 0 ; y=38 .word masknm ; y=42 masklen: .word 8 .word RDBUF, MAXLEN ; y=46 .word pgmhdr,pgmen-pgmhdr ; y=50 .word SCRADR, 40*192 ; y=54 .word tmphipbuf, 80 ; y=58 wild: .byte 0 ;; ;; Display the disk directory using an open IOCB ;; FNCHAR1 = 4 ; position of first character in filename MAXDIRLN = 18 ; max number of directory lines to display on 1 screen DispDir: jsr OpenKeyb lda #0 sta dircount ; no filenames read from directory yet lda #<(SCRADR+2) sta rendpt lda #>(SCRADR+2) sta rendpt+1 @dirlp: ldx #(LODCHN*16) @notlast: ldy #GETLNE ; first read directory into buffer jsr SetRend ldy #28 jsr SetICBL php ldy ICBLL,x iny lda #155 ; make sure there is a return char at end sta (rendpt),y ; of each filename jsr addtopt40 ; move onto next filename slot inc dircount ; and increase file count by one plp bpl @notlast dec dircount lda #SCRADR sta rendpt+1 @doagain: lda rendpt ; save start of screen full of data so sta filtpt ; we can find it again later lda rendpt+1 sta filtpt+1 lda dircount sta svdircount lda #'A' ; menu entries start at 'A' sta lastdirc @nxtline: dec dircount bmi @dirend ; exit when end of list is reached bne @dsline inc dirend ; this is the last line, (FREE SECTORS line) @dsline: jsr DispLine ; display the line on screen inc lastdirc ; increase menu selection letter lda lastdirc cmp #'A'+MAXDIRLN ; menu letters run from A-T bcc @nxtline @dirend: @prompt: ;; display instructions on right hand side of screen lda #4 ; row for top line of instructions sta @rowtmp jsr @updtpos jsr strout .byte '1'+128, '-', '9'+128," Dir",0 jsr @updtpos jsr strout .byte 'E'+128, 's'+128, 'c'+128, " Quit",0 jsr @updtpos jsr strout .byte 'T'+128, 'a'+128, 'b'+128, " Mask",0 lda dirend ; is this the end of the directory? bne @nonext ; if so, then don't display 'Next' option jsr @updtpos jsr strout .byte 'S'+128, 'p'+128, 'a'+128, 'c'+128, 'e'+128, " Next",0 @nonext: ldx filtpt+1 ; are we on 1st screen? cpx #>SCRADR beq @noprev ; this is 1st screen, don't show previous jsr @updtpos jsr strout .byte '-'+128, " Prev",0 @noprev: lda devlen cmp #3 ; we're not in a subdirectory, so can't move bcc @nodir ; up one level jsr @updtpos jsr strout .byte '<'+128, " Up Dir",0 @nodir: @getagain: jsr GetKpNoRet ; get keypress without waiting for return cmp #'<' bne @notudir ldx devlen cpx #3 ; we're not in a subdirectory, so can't move bcc @getagain ; up one level txa tay dex jsr FindDevLenX inx iny inc redodir ; redisplay directory jmp @cpdrct ; copy mask to end of dir name @notudir: cmp #'-' ; move to previous screen? bne @notprv ldx filtpt+1 ; are we on 1st screen? cpx #>SCRADR beq @getagain ; this is 1st screen, ignore keypress lda filtpt sec sbc #<(MAXDIRLN*40) ; update current top of screen position sta rendpt ; this should be on the previous screenfull now txa sbc #>(MAXDIRLN*40) sta rendpt+1 clc lda svdircount adc #MAXDIRLN sta dircount ; update current position in filename list lsr dirend ; reset dirend to 0 beq @nextscr ; display next screen @notprv: cmp #32 ; is this a space? bne @ntspc lda dirend ; is this the end of the directory? bne @getagain @nextscr: jsr ClrDirScr ; clear screen ready for next screenfull jsr DrawVLine ; redraw vertical line jmp @doagain ; display next screenfull @ntspc: cmp #27 ; is this the Esc key? bne @skjmpx jmp DOS ; yes, so exit to DOS @skjmpx: cmp #127 ; check for TAB key bne @nottab ldx masklen inx inx lda #3 jsr SetRowCol lda #0 sta CRSINH jsr strout .byte 30,31,0 jsr GetLine jsr ReadMask sec rol CRSINH jmp @redodrct @nottab: cmp #'1' ; check for numbers 1-9 bcc @jmpagain cmp #'9'+1 bcs @chklet ;; user pressed a number 1-9, redo directory for that drive sta masknm+1 @redodrct: inc redodir jmp @exit @chklet: cmp #'A' ; did user press a menu key? bcc @jmpagain ; unknown key, go get another one cmp lastdirc ; key is above last valid keypress bcc @letok @jmpagain: jmp @getagain ; go get another one @letok: sec ; find position of user's selection sbc #'A' ; in the directory list tax lda filtpt ; point us back at start of this screen sta rendpt lda filtpt+1 sta rendpt+1 @findlp: dex ; now find line user selected bmi @found jsr addtopt40 ; add 40 onto rendpt jmp @findlp @found: ; we found the line the user selected jsr RestMask ldx devlen ; and copy filename/dirname after inx ; last colon ldy #FNCHAR1-1 lda #':' cmp (rendpt),y bne @nodrct ; user didn't select a directory inc redodir ; this is a directory, need to redisplay it @nodrct: iny @cplp: lda (rendpt),y ; copy file from storage area into last cmp #32 ; filename, skip any spaces beq @skspc sta RDBUF,x inx @skspc: iny cpy #8+FNCHAR1 ; and don't do any more than 8 characters bcc @cplp lda (rendpt),y ; if there is a space here, then there isn't cmp #'D'+128 ; an extension bne @ntdir lda #'>' ldy devlen sta masknm,y inc redodir bne @cpdrdv @ntdir: lda redodir ; don't add '.' for directory name bne @cplp2 lda #'.' ; otherwise add '.' char for extender sta RDBUF,x inx @cplp2: lda (rendpt),y ; now copy extender into filename cmp #32 ; if we find a space then finished beq @noext ; with extender sta RDBUF,x inx iny cpy #13+FNCHAR1 ; no more than 8+1+3 chars for filename bcc @cplp2 @noext: lda redodir beq @norddir @cpdrdv: ldy devlen stx devlen @cpdrct: lda masknm,y sta RDBUF,x iny inx cmp #155 bne @cpdrct dex stx masklen jsr SaveMask jmp @exit @norddir: lda #155 ; end last filename with RETURN char sta RDBUF,x dex stx fnlen jsr SaveFN @exit: jmp CloseKeyb ;; update cursor pos for next instruction line @updtpos: ldx #25 lda @rowtmp clc adc #2 sta @rowtmp jmp SetRowCol @rowtmp: .byte 0 ; current instruction line lastdirc: .byte 0 ; last character used in directory menu dirend: .byte 0 ; has end of directory been reached? dircount: .byte 0 ; number of entries in directory list svdircount: .byte 0 ; save of entries on current screen redodir: .byte 0 ; do directory again ;; ;; Open keyboard for input ;; OpenKeyb: ldx #(KEYBCHN*16) ; open keyboard so we can read single keypress jsr CloseChX LDA #4 STA ICAX1,x ldy #20 ; open K: jmp SetICBOpen ; this will set ICBLL/H as well, but they are ; ignored anyway ;; ;; Close keyboard again ;; CloseKeyb: ldx #(KEYBCHN*16) ; close keyboard and exit jmp CloseChX ;; ;; Get keypress without waiting for return key ;; ;; Returns ;; Acc = ATASCII key value, with high bit clear (inverse key changed ;; to normal) ;; GetKpNoRet: ldx #(KEYBCHN*16) lda #GETBUF ldy #24 ; set ICBLL/H to 0, don't care about ICBAL/H jsr SetICBICC ; get keypress cmp #155 beq @noinv ; don't clear top bit for return key and #%01111111 ; ignore inverse video @noinv: rts ;; ;; Display character on directory menu ;; DispLine: lda dirend ; is this last entry in directory? bne @lstlne lda lastdirc ; if not last entry then put letter ldx #')' ; and ')' on screen bne @notlst @lstlne: dec lastdirc ; don't display letter next to last lda #' ' ; directory entry (# of free sectors) tax @notlst: ldy #0 sta (rendpt),y iny txa sta (rendpt),y ldx #0 ldy #PUTLNE jsr SetRend ldy #30 jsr SetICBL jmp addtopt40 ;; ;; Draw lines on screen ;; DrawScr: lda #82 ; control-r in internal code ldy #119 @horizlp: sta (SAVMSC),y dey cpy #80 bcs @horizlp ldy #199 @horizlp2: sta (SAVMSC),y dey cpy #160 bcs @horizlp2 ldy #183 lda #87 ; control-w in internal sta (SAVMSC),y DrawVLine: clc lda SAVMSC adc #205 sta drawtemp lda SAVMSC+1 adc #0 sta drawtemp+1 ldy #MAXDIRLN ; 18 lines worth @vertlp: lda #124 ; vertical bar char in internal code sta (drawtemp),y clc lda drawtemp adc #41 sta drawtemp bcc @noinhi inc drawtemp+1 @noinhi: dey bpl @vertlp ldx #2 lda #5 ; drops through to SetRowCol SetRowCol: stx COLCRS sta ROWCRS rts ;; ;; Adjust row and col offset input if using filters ;; AdjRowCol: ldx USEVFILT bmi @rts beq @x2 asl ; multiply by 4 @x2: asl ; multiply by 2 @rts: rts ;; ;; Subtract row and col offset from number of rows and columns to ;; display, results stored in tmpnumrows and tmpnumcols ;; SubRowCol: ldx #1 @loop: lda numcols,x ldy ctrcol,x ; if image is being centred, offset will bne @ok ; be calculated from image size sec sbc FirstCol,x bcs @ok ; check for negative result lda #0 ; make sure it's never less than 0 @ok: sta tmpnumcols,x dex bpl @loop rts ;; ;; Clear directory display area on screen ;; ClrDirScr: lda SAVMSC clc adc #200 sta drawtemp lda SAVMSC+1 adc #3 sta drawtemp+1 ldx #4 lda #0 ldy #71 @clrlp: dey sta (drawtemp),y bne @clrlp dec drawtemp+1 dex bne @clrlp rts ;; ;; Read new mask value from screen ;; MASKPOS = 122 ; starting position of mask on screen ReadMask: ldy #MASKPOS @findcl: lda (SAVMSC),y ; search for first ':' or '>' character and #%11111011 cmp #26 ; ':' and ('>' AND 11111011) in internal beq @fndcln iny cpy #MASKPOS+38 bcc @findcl bcs @cpmskpos ; no colon found, copy whole name after dev @fndcln: cpy #MASKPOS+1 ; is colon 2nd character? bmi @ispos0 ; is colon 1st character? bne @notpos1 dey lda (SAVMSC),y ; yes, so copy first letter as dev name jsr int2asc sta masknm ; leave dev number alone and copy rest lda #'1' ; set device number to 1 sta masknm+1 iny ; of mask after colon @ispos0: iny bne @cpdirdv @notpos1: cpy #MASKPOS+2 ; is colon 3rd character? bne @notpos2 ldx #0 ldy #MASKPOS ; yes, so just copy the whole thing across bne @cpdirn @notpos2: @cpmskpos: ldy #MASKPOS ; if colon is at position 4 or greater then @cpdirdv: ldx #3 ; copy the whole thing after device @cpdirn: lda (SAVMSC),y beq @spc ; found a space, end of filename jsr int2asc sta masknm,x iny inx bne @cpdirn @spc: lda #155 ; make sure we've got a return char at end sta masknm,x stx masklen ;; ;; Update position of last device+directory name, ':' or '>' used ;; to separate directory names ;; FindDevLen: ldx masklen FindDevLenX: @schcln: lda masknm,x ; ':' is 58, '>' is 62 and #%11111011 ; converts 62 to 58 cmp #':' beq @fndcln dex bpl @schcln @fndcln: stx devlen rts ;; ;; Read filename from screen ;; GetFileNM: lda SAVMSC clc adc #SVFNOFF sta drawtemp+1 ldx #0 ldy #0 @cpdirn: lda (drawtemp),y beq @spc ; found a space, end of filename jsr int2asc sta RDBUF,x iny inx bne @cpdirn @spc: lda #155 ; make sure we've got a return char at end sta RDBUF,x rts ;; ;; Save filename for later ;; SaveFN: ldx #MAXLEN dex @cpflnam: lda RDBUF,x ; copy filename from buffer to storage area sta filenm,x dex bpl @cpflnam rts ;; ;; Restore previously saved file name ;; RestFN: ldx #MAXLEN dex @cpflnam: lda filenm,x ; copy filename from buffer to storage area sta RDBUF,x dex bpl @cpflnam rts repeat: .byte 0 ;; ;; Display file name ;; DispFN: ldy #30 lda #PUTLNE ; write out file name setx0: ldx #0 jmp SetICBICC ;; ;; Save directory mask for later ;; SaveMask: ldx #MAXLEN dex @cpmsnam: lda RDBUF,x ; copy filename from buffer to storage area sta masknm,x dex bpl @cpmsnam rts ;; ;; Restore previously saved mask ;; RestMask: ldx #MAXLEN dex @cpflnam: lda masknm,x ; copy filename from buffer to storage area sta RDBUF,x dex bpl @cpflnam rts ;; ;; Display mask value ;; DispMask: ldy #159 lda #0 @clrlne: ; clear out line first sta (SAVMSC),y dey cpy #120 bcs @clrlne ldy #42 lda #PUTBUF bne setx0 ; forced branch ;; ;; Get address of render routine for selected mode ;; ;; Params ;; Acc = keypress to search for ;; ;; Returns ;; Y = index into options ;; Acc = keypress searched for (same as input) GetAdr: ldx #menuopts stx menupt+1 ldy optlen @optloop: cmp (menupt),y ; check if key is on menu beq @optfnd dey bpl @optloop clc rts @optfnd: tax tya asl tay txa iny sec rts .include "common.asm" ; include common routines ;; recognised options for main menu menuopts: .byte "1", "2", "3" .byte "4", "H", "M", "P" ; number of columns mode is capable of without any reduction modecols: .byte 40, 20, 10, 20, 20, 20, 40 optlen: .byte 0 ;; PGM file format header information pgmhdr: .byte "P5 #JpegView-0.9",10 pgmwdt: .byte "00000 " ; image width (pixels) pgmhgt: .byte "00000 " ; image height (pixels) pgmlevs: .byte "255",10 ; number of grey levels pgmen: reslt: ; use first 5 bytes of dithering buffer as temp storage for ; conversion to decimal routine decfirst = reslt + 5 declowb = decfirst + 1 deciocb = declowb + 1 tmpsavmsc = deciocb + 1 tmpsdlstl = tmpsavmsc + 2 tmpnmien = tmpsdlstl + 2 tmpgprior = tmpnmien + 1 tmpwaitkeyp = tmpgprior + 1 tmppcolr0 = tmpwaitkeyp + 1 ; 9 bytes tmpsavval = tmppcolr0 + 9 tmpsavdisp = tmpsavval + 1 ; 0 if image is being saved while decoding ; 1 if image is saved from screen tmpsavept1 = tmpsavdisp + 1 tmpsavept2 = tmpsavept1 + 1 tmpsvmode = tmpsavept2 + 1 tmphipoff = tmpsvmode + 1 tmpscron = tmphipoff + 1 tmpcalcwid = tmpscron + 1 ; 2 bytes tmpheight = tmpcalcwid + 2 ; 2 bytes tmpcuropt = tmpheight + 2 tmpcalcbest = tmpcuropt + 1 tmpsvrowcrs = tmpcalcbest + 1 tmpnumcols = tmpsvrowcrs + 1 ; keep tmpnumcols and tmpnumrows together!!! tmpnumrows = tmpnumcols + 1 ; keep tmpnumcols and tmpnumrows together!!! tmphipbuf = tmpnumrows + 1 ; 80 byte buffer for saving HIP files RDBUF = tmphipbuf + 80 dithbuff: ; .RES 321 - there are 321 bytes here for dithering line buffer ; don't put the .RES in though, as that adds 321 bytes to the ; executable file! Could add it to BSS segment, but that ; causes problems with detecting an overrun into screen area DITHBUFLEN=325 NXTBUFLEN=24 nxtdith = dithbuff + DITHBUFLEN ;; ;; Temporary save buffer, this is used to hold data while saving ;; in HIP mode. We don't want to use the RAM under the OS for ;; this so that 48K machines can at least save to HIP even ;; if they can't view it with JPEGVIEW. This is a bit wasteful ;; but it is the easiest way to do it and we have enough free RAM ;; right now. If we run out again, then there are other ways ;; of doing this. We could use half of the input buffer (rendpt) ;; after the filter has been applied (horiz filter always used ;; in HIP), if we have multiple buffers we won't have all the ;; data come through in one go, but in that case we could copy ;; the data to the space available in the screen area (only half ;; of it will be used if we have multiple buffers) while we get ;; the next buffer full and then copy it back/save it when ;; we've got a whole line. It's a bit tricky, but should work TMPSAVEBUF = nxtdith + NXTBUFLEN TMPSAVEBUFLEN = 24*40 segcodeend: .org TMPSAVEBUF + TMPSAVEBUFLEN ;; make sure code doesn't run into the screen area, if it does ;; things will go badly wrong .if ( * >= (SCRADR/256)*256) .error ("Error - Code overrun into Screen area ", .string(*)) .else .out .concat("Code ends at ",.string(*)," Max=",.string((SCRADR/256)*256-1)) .endif ;; header for init coder .addr initcode .addr initcodeend-1 ;; We only need to do this once at the start, so it is ok to ;; overwrite this code later .org $B000 initcode: ;; ;; Check if there is any RAM under the OS ;; OSRTST = 65000 ; check this address to see if it is RAM or not Init: lda #0 sta FirstCol sta FirstRow lda #3 ; index of mode '4' (HIP) in menuopts sta prevopt ; default display mode for >= 64K machines .if .defined(PGM) lda #1 ; can always do 2 screen modes with PGM viewer sta DO2SCR rts .else jsr_osramon ldy OSRTST ; PICK LOC. IN OS TO TEST ldx #255 stx OSRTST ; store $FF to test if it is RAM cpx OSRTST bne @NTRAM ; not RAM inx stx OSRTST ; store 0 to test if it is RAM cpx OSRTST bne @NTRAM ; not RAM sty OSRTST ; restore original value ldy #1 ; 1 FLAGS IT IS RAM bne @ISRAM @NTRAM: sty OSRTST ; RESTORE JUST IN CASE lda #2 ; mode '3' GR.9 sta prevopt ; set default display mode for 48K ldy #0 @ISRAM: sty DO2SCR jmp_osramoff .endif initcodeend: .addr 738 .addr 739 .word initcode ; run init code as soon as it is loaded