;; ;; Image viewer, interfaces to jpeg decoder and displays image ;; in several different modes, also has output ;; of data to file option ;; ;; Copyright 2001, Raphael Espino ;; last updated 25-Apr-01 ;; ;; ---------- renderer zero page addresses, 192 and up are available rendpt = 192 ; pointer to image data - 2 bytes filtpt = 194 ; filter pointer - 2 bytes 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 ;; ---------- end of renderer zero page addresses ;; ------------- decoder information addresses ------------------ DISPCOLS = $600 ; (# of columns to display)/8 must be <= 40 DISPROWS = $601 ; (# of rows to display)/8 coloff = $602 ; Coloumn offset to display image from rowoff = $603 ; Row offset to display image from numcols = $604 ; image width, 1 column = 8 pixels (pixels/8) numrows = $605 ; image height 1 row = 8 pixels (pixels/8) IOCBNUM = $606 ; IOCB to read jpeg data from STACKPT = $607 ; stack pointer at program start, use to return ; to DOS at any point ERROR = $608 ; non 0 if error ocurred decoding jpeg ; error codes are as defined in decoder RERUN = $609 ; 2 bytes - restart address, use to rerun decoder VERSION = $60B ; decoder version number MemTop = $60C ; End of decoder memory, memory >= MemTop is ; free for viewer when RendStart is called ; MemTop guaranteed to be below $8C00 ;; page 6 addresses from $630 up are available to renderer ;; ------------- end of decoder info 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 ABORTCD = 128 ; code to tell decoder to abort ROWCRS = 84 ; current cursor row COLCRS = 85 ; current cursor column SAVMSC = 88 OLDROW = 90 ; old cursor row OLDCOL = 91 ; old cursor column OLDCHR = 93 ; character under cursor at previous position OLDADR = 94 ; address of previous cursor position VDSLST = 512 ; DLI vector VKEYBD = 520 ; keyboard IRQ vector SDLSTL = 560 GPRIOR = 623 ; priority register, enable GR.9 SDMCTL = 559 ; shadow DMA control address PCOLR0 = 704 ; colour shadow addresses COLOR0 = 708 COLOR1 = 709 COLOR2 = 710 COLOR3 = 711 COLOR4 = 712 CH = 764 ; last keypress shadow address ;; IOCB addresses ICCOM = 834 ICSTA = 835 ICBAL = 836 ICBAH = 837 ICBLL = 840 ICBLH = 841 ICAX1 = 842 ICAX2 = 843 MAXLEN = 64 ; max file name length RDBUF = $630 ; use page 6 for file name buffer COLPF1 = 53271 ; playfield 1 colour register COLPF2 = 53272 ; playfield 2 colour register PRIOR = 53275 ; priority register, enable GR.9/10 CONSOL = 53279 ; Console keys (Option, Select, Start) KBCODE = 53769 ; hardware keyboard code address PORTB = 54017 ; OS RAM control on XL/XE DMACTL = 54272 ; DMA control 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 lastfn = $580 ;; ICCOM values OPEN = 3 ; open channel GETLNE = 5 ; get line GETBUF = 7 PUTLNE = 9 PUTBUF = 11 ; put buffer CLOSE = 12 ; close channel ;; IOCB open modes READ = 4 ; open for read READDIR= 6 ; open for directory read WRITE = 8 ; open for write DEBUG = 0 ;; set up viewer jmp vectors .ADDR segvector .ADDR segvectorend-1 .ORG $0620 segvector: ;; next 12 bytes should be JMP's to renderer's init, start, draw ;; and end code JMP RafRendInit ; init renderer 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: ;; header for viewer .ADDR segcode .ADDR segcodeend-1 ;; renderer has area from $8C00 upwards for itself and screen .ORG $8C00 ;; ;; init code, this will be called when decoder starts or ;; when it is re-run. Renderer 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 ($606) ;; The rowoff and coloff values can optionally be set here ;; or in RendDraw. If they are not set they will default to 0 ;; DISPCOLS and DISPROWS can also optionally be set here or in ;; RendDraw. If not set they default to DISPCOLS = 40 (320 pixels) ;; DISPROWS = 24 (192 pixels) ;; *** DISPCOLS should NEVER be set to > 40 *** ;; segcode: RafRendInit: lda #64 sta NMIEN ; make sure DLIs are disabled lda #0 sta ROWCRS sta ctrrow sta ctrcol ;; display rendered information jsr strout .BYTE "a8jdpeg 0.6(25Apr01) Raphael Espino",155,155,155,0 jsr RestFN ; restore previous filename lda repeat ; don't ask for filename if we are redisplaying beq @newfn jsr strout .BYTE "Displaying ",0 jsr DispFN ; display filename too lda #0 ; ask user for filename next time round sta repeat ; by default beq chkext ; forced branch @newfn: jsr strout .BYTE "Add extra ':' at end for directory",155 .BYTE "File:D:",0 ldx #tabirq ; tab key, then display old filename jsr SetKeyIRQ clc jsr RDLINE ; get filename from user jsr ClrKeyIRQ ; remove keyboard IRQ lda RDBUF+2 ; return to environment if no cmp #155 ; filename entered bne cont DOS: ldx STACKPT txs ldy #0 rtscmd: rts cont: ;; make sure IOCB is available jsr CloseInFile ;; get ready to open the file stx IOCBNUM ; tell decoder what IOCB to use jsr SaveFN chkext: jsr CheckExt ; check if extension needs to be added to name ; also checks for ':' for directory lda wild bne dirop openfl: lda #READ ; open the file .BYTE $2C ; ignore next 2 bytes dirop: lda #READDIR ; do a directory ldx #(LODCHN*16) jsr OpenFile bmi @error lda wild beq rtscmd jsr DispDir jsr CloseInFile jsr strout ; clear screen ready for next dir listing .byte 125,0 lsr redodir ; redo directory bcs chkext lda repeat beq @rerun jsr SaveFN ; filename selected from directory list @rerun: jmp (RERUN) ; so remember it and restart ;; 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 strout .BYTE 155, "Error ",0 pla jsr DecOut ; display error code ; display file name too jsr strout .BYTE " - ",0 jsr DispFN ; display file name jmp WaitandRun ;; ;; renderer start code, will be called immediately before ;; the image data is about to start arriving. Renderer ;; should open graphics mode, open output file, etc. here. ;; This is the first point that numcols and numrows information ;; is valid. Renderer can optionally setup DISPCOLS, DISPROWS, ;; coloff, rowoff information here. ;; *** DISPCOLS should NEVER be set to > 40 *** ;; RafRendStart: lda #25 sta DISPROWS jsr strout ; display image size .BYTE 155,"Width=",0 lda numcols jsr DecOut jsr strout .BYTE ", Height=",0 lda numrows jsr DecOut ;; get column and row offsets from user ldy prevopt bmi @firsttm jsr strout .byte 155, "Prev col=", 0 lda coloff jsr DecOut jsr strout .byte " row=", 0 lda rowoff jsr DecOut @firsttm: jsr strout .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 sta coloff ; so move carry into ctrcol to set it @ctrcl: jsr strout .BYTE "Offset row:C",30,0 jsr getnum ; get number from user rol ctrrow bne askagain sta rowoff askagain: ;; display menu jsr strout .BYTE 155,"S TOGGLES SCREEN WHILE DECODING",155 .BYTE "1) GR.8 40x25 2g 1/1",155 .BYTE "2) GR.15 20x25 4g 2/1",155 .BYTE "3) GR.15 40x25 4g 1/1",155 .BYTE "4) GR.9 10x25 16g 4/1",155 .BYTE "5) GR.9 20x25 16g 2/1", 155 .BYTE "6) GR.9 20x12 16g 1/1", 155 .BYTE 0 lda #7 ; 8 options available on 48K machines sta optlen lda OSRAM bne @doram ; if so then display 64K options too jmp @noram @doram: jsr strout .BYTE "7) GR.8 40x25 4g 1/1 (64K)", 155 .BYTE "8) GR.15 20x25 9g 2/1 (64K)", 155 .BYTE "9) GR.15 40x25 9g 1/1 (64K)", 155 .BYTE "A) GR.9 10x25 31g 4/1 (64K)", 155 .BYTE "B) GR.9 20x25 31g 2/1 (64K)", 155 .BYTE "C) GR.9 20x12 31g 1/1 (64K)", 155 .BYTE "D) HIP 20x25 30g 2/1 (64K)", 155 .BYTE "E) HIP 40x25 30g 1/1 (64K)", 155 .BYTE 0 lda #15 ; 16 options available on 64K machines sta optlen @noram: jsr strout .BYTE "N) Run again",155 .BYTE "S) Save",155 .BYTE "Choice?" .BYTE 0 ldy prevopt ; if first time, then no previous selection bmi @noprev lda menuopts,y jsr SetPrmt ldx OSRAM bne @is64K sbc #8 ; 48K menu is 9 lines high .byte $2C @is64K: sbc #16 ; 64K menu is 17 lines high clc adc prevopt ; then find line of previous option ldx OSRAM beq @no64k ; position is already right for 48K mode cmp #15 ; options >=15 (64K options) are 2 lines higher bcc @ls15 sbc #2 ; carry is already set bne @no64k ; forced branch @ls15: cmp #13 ; options at pos < 13 are same 48K & 64K bcc @no64k clc ; options at pos 13-14 are 8 lines lower adc #8 ; there are 8 64K options @no64k: jsr DispPrmt @noprev: lda #0 sta USE2SCR sta USEFILT jsr getmenu ; get user selection sta RENDMODE ; remember user selection jsr getaddr bcs @optfnd jmp askagain @optfnd: tya lsr cmp #6 ; don't remember last option for rerun beq @nosav sta prevopt @nosav: cpy #16 ; options >= 7-9 and A-E 2 screens rol USE2SCR lda @optadr,y pha dey lda @optadr,y pha rts @optadr: .WORD @gr8-1, @gr15-1, @gr15a-1, @gr9-1, @gr9a-1, @gr9a-1 .WORD runagain-1, savetofile-1 .WORD @gr8-1, @gr15-1, @gr15a-1, @gr9-1, @gr9a-1, @gr9a-1 .WORD sethip-1, sethipa-1 @gr9a: inc USEFILT lda #20 .BYTE $2C @gr9: lda #10 ; 80/8 = 10 columns sta DISPCOLS jsr OpenGr15 lda #SCR2TMP sta gr9scr22+2 ldx #SCRADR sty gr9scr+2 sty gr9scr2+2 lda RENDMODE ; check if image is to be doubled vertically cmp #'6' ; modes 6 and 13 are beq @dogrd cmp #'C' bne @skgrd @dogrd: txa clc adc #40 sta gr9scrd+1 ; set up addresses for doubled data sta gr9scr2d+1 bcc @noiny iny @noiny: sty gr9scrd+2 sty gr9scr2d+2 ldy gr9scr22+2 lda gr9scr22+1 clc adc #40 sta gr9scr22d+1 bcc @no92d iny @no92d: sty gr9scr22d+2 lda #$9D ; enable doubling of data sta gr9scrd sta gr9scr2d sta gr9scr22d lda #80 sta LINEBYTES lda #12 sta DISPROWS bne @sknop @skgrd: lda #$EA ; NOP operation ldy #2 @ea1: sta gr9scrd,y ; disable doubling of data dey bpl @ea1 ldy #2 @ea2: sta gr9scr2d,y dey bpl @ea2 ldy #2 @ea3: sta gr9scr22d,y dey bpl @ea3 lda #40 sta LINEBYTES @sknop: lda #64 sta GPRIOR ; enable gr.9 bne exitstup @gr8: jsr OpenGr15 ; open graphics mode lda #SCRADR sta gr8scr+2 lda #SCR2TMP sta gr8scr2+2 ;; graphics 8 colours are 0 and 1 lda #0 sta COLOR2 lda #8 sta COLOR1 bne exitstup ; forced branch @gr15: lda #20 ; 160/8 = 20 columns sta DISPCOLS bne @skipfl @gr15a: ; leave DISPCOLS at default inc USEFILT @skipfl: lda #14 ; Graphics 15 -> DL mode 14 jsr OpenGr ; open graphics mode lda #SCRADR sta gr15scr+2 sta gr15scr2+2 lda #SCR2TMP sta gr15scr2b+2 ;; graphics 15 colours are 0, 1, and 2 + background (4) ldx #2 @setclr: lda micclrs+1,x sta COLOR0,x dex bpl @setclr exitstup: ldx #1 @centr: lda ctrcol,x ; if user wants to centre image beq @noctr sec lda numcols,x ; subtract display size from image size sbc DISPCOLS,x bcs @nor0 ; if image size >= display size, use difference lda #0 ; if image size < display size, use 0 @nor0: lsr sta coloff,x @noctr: dex bpl @centr ;; set up our own keyboard IRQ to toggle DMA if key pressed lda #0 sta SAVEDM 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 rts ;; save the data to a file savetofile: jsr strout .BYTE 125,155,"1) Micropainter 2/1",155 .BYTE "2) Micropainter 1/1",155 .BYTE "3) HIP 2/1",155 .BYTE "4) HIP 1/1",155 .BYTE "5) 40x25 256g 1/1",155 .BYTE "N) Run again", 155 .BYTE "Choice?" .BYTE 0 ldy prevsave ; if first time, then no previous selection bmi @svnoprev lda svmenuopts,y jsr SetPrmt sbc #6 ; menu is 7 lines high clc adc prevsave ; then find line of previous option jsr DispPrmt @svnoprev: jsr getmenu ; get user selection cmp #'N' bne @save jmp runagain @save: sta SAVEMODE ; remember save mode for next time jsr getsvaddr bcs @svoptfnd jmp savetofile ; unknown keypress, ask for another one @svoptfnd: tya lsr cmp #6 ; don't remember last option for rerun beq @svnosav sta prevsave @svnosav: jsr strout .BYTE 155,"Save file:D:",0 sec JSR RDLINE ; get filename from user ;; make sure IOCB is available JSR CloseOutFile BPL @ok @jmsver2: jmp saveerr2 @ok: ;; now open the file LDA #WRITE jsr OpenFile bmi @jmsver2 lda #0 ; switch screen off during save sta SDMCTL sta DMACTL lda #>SCRADR jsr clrscr lda #40 ; 320/8 = 40 columns sta DISPCOLS ldx SAVEMODE ; if lsbit is clear, then use filter dex ; set lsb for filter modes (2 and 4) txa ; it will be clear for rest (1, 3 and 5) lsr ; move lsb into carry rol USEFILT ; and then into USEFILT cmp #24 ; ((49 or 50)-1)/2 = 24 -> Micropainter beq @savmic cmp #25 ; ((51 or 52)-1)/2 = 25 -> HIP bne @jmexit sta savecount ; save 25 blocks worth (200 lines) lda #SCRADR sta hipscr2+2 ldx #(SAVECHN*16) ldy #8 ; save HIP header 1 jsr SetICBPut @jmexit: jmp exitstup @savmic: ;; if saving in micropainter format, then save header lda #20 ; 160/8 = 20 columns sta DISPCOLS lda #24 sta DISPROWS sta savecount ; need to save 24 blocks worth lda #SCRADR sta gr15scr+2 jmp exitstup ;; set up for drawing in HIP mode sethip: lda #20 ; 160/8 = 20 columns sta DISPCOLS bne nohipfilt sethipa: inc USEFILT nohipfilt: jsr OpenGr15 lda #SCR2TMP sta hipscr2+2 lda #SCRADR sta hipscr+2 lda #64 sta GPRIOR ; enable gr.9/10 ldx #8 @sthipc: lda @hipclrs,x ; set colour registers up for HIP sta 704,x dex bpl @sthipc jmp exitstup @hipclrs: .BYTE 0,2,4,6,8,10,12,14,0 ; HIP colour values for regs 704-712 runagain: jsr CloseInFile ; close opened file jmp (RERUN) ; and rerun ;; handle keypresses during decoding of image dmatoggle: lda KBCODE and #%00111111 ; ignore Shift and Control keys cmp #40 ; 'r' aborts and reruns with same file bne @ckesc inc repeat bne @abort ; forced branch @ckesc: cmp #28 ; ESC key aborts bne @ckdma @abort: lda #ABORTCD ; tell decoder to abort sta ERROR bne OSKEYBIRQ ; forced branch @ckdma: cmp #62 ; toggle DMA if 's' key is pressed bne OSKEYBIRQ ; pass key press to OS txa pha lda SDMCTL ; toggle DMA value ldx SAVEDM stx SDMCTL stx DMACTL sta SAVEDM pla tax OSKEYBIRQ: jmp 30000 ; gets changed - jump to previous keyb IRQ FNOFF = 167 ; offset from screen top left to start of filename tabirq: lda KBCODE cmp #44 ; use tab key to display previouis filename bne OSKEYBIRQ txa pha ldx #0 stx OLDCHR ; reset old character under cursor to space txa @lp2: sta 48192+FNOFF+1,x ; clear 255 bytes on screen to make sure there inx ; isn't any junk left on screen bne @lp2 @lp: lda lastfn,x ; copy last filename to screen cmp #155 ; return char marks end of filename beq @exit jsr asc2int ; convert to internal sta 48192+FNOFF,x inx bpl @lp @exit: lda #128 ; cursor won't reappear until we move it sta 48192+FNOFF,x ; so simulate one with an inverse space txa adc #6 ; 6 characters for File: sta COLCRS lda #4 ; old row is now 4 sta ROWCRS txa clc adc #<(48192+FNOFF) ; move old cursor position too sta OLDADR ; so that inverse space will get lda #0 ; overwritten when cursor moves adc #>(48192+FNOFF) sta OLDADR+1 pla tax pla ; ignore keypress rti SAVEDM: .BYTE 0 ; save previous DMA value RENDMODE: .BYTE 0 ; graphics mode menu selection SAVEMODE: .BYTE 0 ; last save operation type USE2SCR: .BYTE 0 ; use 2 screens? USEFILT: .BYTE 0 ; use filter? LINEBYTES: .BYTE 0 ; number of bytes to skip for next GR.9 line savecount: .BYTE 0 ; number of blocks saved so far prevopt: .BYTE 255 ; previous display option selected prevsave: .BYTE 255 ; previous save option selected ctrcol: .byte 0 ; centre image horizontally ctrrow: .byte 0 ; centre image vertically, should always be ; immediately after ctrcol ;; ;; draw image data. This gets called when 8 lines of image ;; data have been read and decoded from jpeg image. Renderer ;; should display/save/etc the 8 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 8 lines of 320 pixels ($A00 consecutive bytes). ;; If DISPCOLS < 40 then remaining data in line will be empty, i.e. ;; if DISPCOLS = 10 then each line will be 80 (10*8) bytes of image ;; data followed by 240 (30*8) bytes that should be ignored ;; *** DISPCOLS should NEVER be set to > 40 *** ;; RafRendDraw: sta rendpt ; save data buffer address sty rendpt+1 ldy #8 ; draw 8 lines sty rendline lda RENDMODE cmp #'S' bne @getadr lda SAVEMODE jsr getsvaddr lda @optsvadr2,y pha dey lda @optsvadr2,y pha rts @getadr: jsr getaddr lda @optadr2,y pha dey lda @optadr2,y pha rts @optadr2: .WORD rendgr8-1, rendgr15-1, rendgr15-1, rendgr9-1, rendgr9-1 .WORD rendgr9-1, 0, 0 .WORD rendgr8-1, rendgr152-1, rendgr152-1 .WORD rendgr92-1, rendgr92-1, rendgr92-1, rendhip-1, rendhip-1 @optsvadr2: .WORD rendgr15-1, rendgr15-1, rendhip-1, rendhip-1, savefile-1 ;; save data to file savefile: ldx #(SAVECHN*16) ; IOCB should already be open lda rendpt sta putadr2 lda rendpt+1 sta putadr2+1 ldy #38 jsr SetICBPut bpl @rts jmp saveerr ; error saving data @rts: UnusedVec: rts ;; draw data in gr.9 mode rendgr9: jsr filter ; reduce pixels horizontally ldy #0 ldx #0 gr9loop: lda (rendpt),y ; get 1st pixel and #%11110000 ; use top 4 bits for grey level sta drawtemp iny lda (rendpt),y ; get 2nd pixel lsr ; shift top 4 bits to the low 4 bits lsr ; of pixel data lsr lsr ora drawtemp gr9scr: sta 30000,x ; put pixel on screen gr9scrd: sta 30000,x ; double pixel vertically iny inx cpx #40 ; do 40 bytes worth of data at a time bcc gr9loop ; 40 bytes = 80 pixels lda gr9scr+1 ; move screen pointer onto next line clc adc LINEBYTES sta gr9scr+1 bcc @nogr9hi inc gr9scr+2 @nogr9hi: lda RENDMODE cmp #'6' bne @nogr9hi2 lda gr9scrd+1 ; move screen pointer onto next line clc adc #80 sta gr9scrd+1 bcc @nogr9hi2 inc gr9scrd+2 @nogr9hi2: ; move data pointer onto next line skip the other 240 bytes as they ; are empty jsr addtopt320 ; add 320 onto rendpt dec rendline ; have all 8 lines been drawn? bne rendgr9 ; no, go back and do the rest rts ;; draw data in HIP mode rendhip: lda RENDMODE cmp #'S' ; if we are saving, then use last 160 bytes bne @dodraw ; of 1st line and first 160 bytes of 2nd line clc ; for temporary disk buffer. This will work lda rendpt ; 'cos 1 line of disk buffer = 40 (8*40)=320 adc #160 ; and HIP only uses first 160 bytes (after sta svrend+1 sta hipsvclr+1 sta hipscr+1 ; filter) of every line, so by the time we use lda rendpt+1 ; space for disk buffer, data will already have adc #0 ; been extracted sta svrend2+1 sta hipsvclr+2 sta hipscr+2 bne dohip @dodraw: jsr OSRAMON dohip: jsr filter ; reduce pixels horizontally ldy #0 ldx #0 hiploop: lda (rendpt),y ; get 1st pixel and #%11110000 ; use top 4 bits for grey level sta drawtemp iny iny lda (rendpt),y ; get 2nd pixel lsr ; shift top 4 bits to the low 4 bits lsr ; of pixel data lsr lsr ora drawtemp hipscr: sta 30000,x ; put pixel on screen dey lda (rendpt),y ; get 1st pixel lsr ; only 9 colour registers in gr.10 so and #%01110000 ; use 3 bits for grey level (8 colours) sta drawtemp iny iny lda (rendpt),y ; get 2nd pixel lsr ; shift top 3 bits to the low 3 bits lsr ; of pixel data lsr lsr lsr ora drawtemp hipscr2: sta 30000,x ; put pixel on screen iny inx cpx #40 ; do 40 bytes worth of data at a time bcc hiploop lda hipscr+1 ; move screen pointer onto next line clc adc #40 sta hipscr+1 bcc @nohiphi inc hipscr+2 @nohiphi: lda hipscr2+1 ; move screen pointer onto next line clc adc #40 sta hipscr2+1 bcc @nohiphi2 inc hipscr2+2 @nohiphi2: jsr addtopt320 ; add 320 onto rendpt, moving it onto next line dec rendline ; have all 8 lines been drawn? bne dohip ; no, go back and do the rest lda RENDMODE cmp #'S' bne hipend dec savecount svrend: lda #0 ; gets changed, save block address lo svrend2: ldy #0 ; gets changed, save block address hi jsr saveblkadr bpl @lneclear jmp saveerr @lneclear: ; now clear out data saved in first 160 bytes lda #0 ; of second line in buffer. This needs to be tay ; done 'cos if jpeg is less than 160 pixels tax hipsvlp: dey ; wide, then this data won't be overwritten hipsvclr: sta 30000,y ; by next buffer full of data and will get bne hipsvlp ; saved to file as part of the image txa bne @rts inc hipsvclr+2 ldy #64 inx bne hipsvlp ; forced branch @rts: rts hipend: jmp OSRAMOFF ;; ;; draw data in gr.15 mode ;; rendgr15: jsr filter ldy #0 ldx #0 gr15loop: lda (rendpt),y ; get 1st pixel and #%11000000 ; use top 2 bits for grey level sta drawtemp iny lda (rendpt),y ; get 2nd pixel and #%11000000 ; use top 2 bits and shift into position lsr lsr ora drawtemp sta drawtemp iny lda (rendpt),y ; get 3rd pixel and #%11000000 lsr lsr lsr lsr ora drawtemp sta drawtemp iny lda (rendpt),y ; get 4th pixel rol rol rol and #%00000011 ora drawtemp gr15scr: sta 30000,x ; display on screen iny inx cpx #40 ; do 40 bytes worth of data bcc gr15loop lda USE2SCR beq @nogr152 jsr rendgr152 ; save data for 2nd GR.15 screen @nogr152: lda gr15scr+1 ; move screen pointer onto next line clc adc #40 sta gr15scr+1 bcc @nogr15hi inc gr15scr+2 @nogr15hi: jsr addtopt320 ; add 320 onto rendpt dec rendline ; have we done 8 lines yet? bne rendgr15 ; if not then go do the rest @chksv: lda RENDMODE cmp #'S' bne @rts dec savecount lda #SCRADR sta gr15scr+2 jsr saveblock ; dump 8 lines of data to disk bpl @rts jmp saveerr @rts: rts ;; draw data in gr.8 mode rendgr8: lda USE2SCR ; are we using 2 screens? beq dogr8 jsr OSRAMON dogr8: ldy #0 ldx #0 gr8loop: lda #0 sta drawtemp sta drawtemp+1 @pixlp: lda (rendpt),y ; get 1st pixel rol ; set GR.8 pixel if value >= 128 rol drawtemp rol rol drawtemp+1 iny tya and #%00000111 bne @pixlp lda drawtemp gr8scr: sta 30000,x tya bne @noinchi inc rendpt+1 @noinchi: lda USE2SCR beq gr8noram lda drawtemp+1 gr8scr2: sta 30000,x gr8noram: inx cpx #40 ; do 40 bytes worth of data bcc gr8loop lda gr8scr+1 ; move screen pointer onto next line clc adc #40 sta gr8scr+1 bcc @nogr8hi inc gr8scr+2 @nogr8hi: lda gr8scr2+1 ; move screen pointer onto next line clc adc #40 sta gr8scr2+1 bcc @nogr8hi2 inc gr8scr2+2 @nogr8hi2: lda #64 jsr addtopt ; move data pointer onto next line dec rendline beq @exit jmp dogr8 @exit: lda USE2SCR ; are we using 2 screens? beq @rts jsr OSRAMOFF ; yes, so disable OS RAM @rts: rts saveerr: jsr reset ; restore DMA + IRQ jsr OpenGr0 saveerr2: jsr strout .BYTE 155, "Save error ",0 ldx #(SAVECHN*16) lda ICSTA,x jsr DecOut jsr strout .BYTE 155,0 jsr CloseOutFile ; close output file jsr CloseInFile ; close input file jmp WRunMenu ; wait for key press and rerun ;; do a 1-2-1 filter on pixels to keep aspect ratio in gr.9 and 15 filter: lda USEFILT beq @nofilt lda rendpt sta filtpt sta @destadr+1 lda rendpt+1 sta filtpt+1 sta @destadr+2 ldx #0 @filtlp: ldy #0 lda (filtpt),y ; get 1st pixel lsr ; divide it by 4 lsr sta drawtemp+1 iny lda (filtpt),y ; get 2nd pixel lsr ; divide it by 2 clc adc drawtemp+1 ; and add to pixel 2 cpx #159 ; make sure we don't go over right edge beq @destadr sta drawtemp+1 iny lda (filtpt),y ; get 3rd pixel lsr ; divide it by 4 lsr clc adc drawtemp+1 ; and add to pixel 1 + pixel 2 @destadr: sta 30000,x ; store result back at original pos lda #2 ; and skip a pixel jsr addtofl @noinchi: inx cpx #160 bcc @filtlp @nofilt: rts ;; ;; copy the second gr.15 screen to under the OS ;; rendgr152: jsr OSRAMON dogr152: jsr filter ldy #0 sty index gr152loop: jsr getpixel clc ror ror ror sta drawtemp txa clc ror ror ror sta drawtemp+1 jsr getpixel asl asl asl asl ora drawtemp sta drawtemp txa asl asl asl asl ora drawtemp+1 sta drawtemp+1 jsr getpixel asl asl ora drawtemp sta drawtemp txa asl asl ora drawtemp+1 sta drawtemp+1 jsr getpixel ora drawtemp pha txa ora drawtemp+1 ldx index gr15scr2b: sta 30000,x ; display on screen pla gr15scr2: sta 30000,x inx stx index cpx #40 ; do 40 bytes worth of data bcc gr152loop lda gr15scr2+1 ; move screen pointer onto next line clc adc #40 sta gr15scr2+1 bcc @nogr152hi inc gr15scr2+2 @nogr152hi: lda gr15scr2b+1 ; move screen pointer onto next line clc adc #40 sta gr15scr2b+1 bcc @nogr152bhi inc gr15scr2b+2 @nogr152bhi: jsr addtopt320 ; add 320 onto rendpt, moving it onto next line dec rendline ; have we done 8 lines yet? beq @skpjmp jmp dogr152 ; if not then go do the rest @skpjmp: jsr OSRAMOFF @rts: rts index: .BYTE 0 getpixel: lda (rendpt),y ; get 1st pixel iny and #%11110000 lsr lsr lsr lsr tax lda @lowclrtab,x pha lda @hiclrtab,x tax pla rts ;; C0 = 0, C1 = 2, C2 = 6, C3 = 8 ;; C0 = 0, C1 = 2, C2 = 4, C3 = 10 ;; 0 1 2 3 4 5 6 8 9 @lowclrtab: .BYTE 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 2, 3 @hiclrtab: .BYTE 0, 0, 1, 1, 1, 1, 2, 2, 1, 1, 2, 2, 2, 2, 3, 3 ;; ;; copy the second gr.9 screen to under the OS ;; rendgr92: jsr OSRAMON dogr92: jsr filter ; reduce pixels horizontaly ldy #0 ldx #0 gr9loop2: lda #0 sta drawtemp sta drawtemp+1 lda (rendpt),y ; get 1st pixel and #%11110000 sta drawtemp sta drawtemp+1 lda (rendpt),y ; with 2 gr.9 screens we have 31 grey levels and #%00001000 ; or 5 bits worth. If 5th bit is clear then beq @noinc ; both screens have same colour lda drawtemp+1 cmp #240 ; if at max brightness, leave alone beq @noinc clc ; otherwise bump 2nd screen grey level up 1 adc #16 sta drawtemp+1 @noinc: iny lda (rendpt),y lsr lsr lsr lsr pha ora drawtemp sta drawtemp pla pha ora drawtemp+1 sta drawtemp+1 pla bcc @noinc2 and #%00001111 cmp #15 bcs @noinc2 inc drawtemp+1 @noinc2: lda drawtemp gr9scr2: sta 30000,x ; put pixel on screen gr9scr2d: sta 30000,x ; double pixel vertically lda drawtemp+1 gr9scr22: sta 30000,x ; put second pixel into temp memory gr9scr22d: sta 30000,x ; double pixel vertically iny inx cpx #40 ; do 40 bytes worth of data at a time bcc gr9loop2 ; 40 bytes = 80 pixels lda gr9scr2+1 ; move screen pointer onto next line clc adc LINEBYTES sta gr9scr2+1 bcc @nogr92hi inc gr9scr2+2 @nogr92hi: lda gr9scr22+1 ; move screen pointer onto next line clc adc LINEBYTES sta gr9scr22+1 bcc @nogr92hi2 inc gr9scr22+2 @nogr92hi2: lda RENDMODE cmp #'C' bne @nogr92hi4 @doscr2d: lda gr9scr2d+1 ; move screen pointer onto next line clc adc #80 sta gr9scr2d+1 bcc @nogr92hi3 inc gr9scr2d+2 @nogr92hi3: lda gr9scr22d+1 ; move screen pointer onto next line clc adc #80 sta gr9scr22d+1 bcc @nogr92hi4 inc gr9scr22d+2 @nogr92hi4: ; move data pointer onto next line skip the other 240 bytes as they ; are empty jsr addtopt320 ; add 320 onto rendpt dec rendline ; have all 8 lines been drawn? beq @exit jmp dogr92 ; no, go back and do the rest @exit: jmp OSRAMOFF rendline: .BYTE 0 ;; ;; end of data. Gets called when image has finished. Renderer ;; 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 ;; close input file jsr CloseInFile ldx #255 ; clear last key press stx CH inx stx COLOR4 ; set background colour to black lda RENDMODE cmp #'S' beq @dosave jmp notsave @dosave: lda ERROR ; check if user aborted bne @endsave ldx SAVEMODE dex txa lsr cmp #24 ; saving in Micropainter format beq @issav cmp #25 ; saving in HIP format beq @hipsave jmp @endsave ; must be mode 5 save @hipsave: lda savecount ; make sure we've saved enough data beq @svhip2 lda #>SCR2ADR ldx #2 ; clear 2 pages worth jsr clrarea ; clear temporary data area for save @hiplp: lda #SCR2ADR jsr saveblkadr bmi @sverr dec savecount bne @hiplp @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 @issav: lda savecount ; make sure we've saved enough data beq @saveclr lda #>SCRADR ldx #2 ; clear 2 pages worth jsr clrarea ; clear temporary data area for save @savlp: jsr saveblock bpl @ok @sverr: jmp saveerr @ok: dec savecount bne @savlp @saveclr: ldx #(SAVECHN*16) ldy #16 jsr SetICBPut ; save colours (grey levels) for image @endsave: ;; we are saving to disk, so close file jsr CloseOutFile jsr OpenGr0 lda ERROR bne waitmsg jsr strout .BYTE 155,"Saved!",0 waitmsg: jsr strout .BYTE 155,0 lda ERROR cmp #ABORTCD beq rerun tax beq WRunMenu WaitandRun: clc .BYTE $24 WRunMenu: sec jsr WAITKEYP ; wait for key press rerun: jmp (RERUN) ; and rerun decoder notsave: ldx ERROR ; did decoder complete successfully? bne waitmsg LDY #IMVBI LDA #6 JSR SETVBV 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 #64 sta NMIEN ; make sure DLIs are disabled lda RENDMODE cmp #'7' ; is this GR.8 mode? beq @gr8dli and #%11111110 cmp #'D' ; check for HIP mode (D and E) beq @hipdli cmp #'8' ; check for GR.15 modes (8 and 9) bne @waitkp lda #2 sta COLOR0 lda #4 sta dlicol1 lda #6 sta COLOR1 lda #8 sta COLOR2 lda #10 sta dlicol2 lda #GR15DLI bne @stupdl @gr8dli: lda #2 sta COLOR2 sta dlicol2 lda #6 sta COLOR1 lda #4 sta dlicol1 lda #GR8DLI bne @stupdl @hipdli: lda #HIPDLI @stupdl: sta VDSLST stx VDSLST+1 jsr setdli lda #192 sta NMIEN ; enable DLIs ;; get key press @waitkp: lda #0 lda CH and #%00111111 ; ignore Shift and Control keys cmp #28 ; ESC key - rerun decoder beq @exit cmp #33 ; SPACE BAR - rerun decode beq @exit cmp #22 beq @exit cmp #40 ; 'R' key repeats same image again beq @repeat .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 .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 @doagain ; forced branch @scr2: lda #255 sta vbiscr bne @doagain ; forced branch @scr12: lda #0 sta vbiscr beq @doagain ; forced branch .endif @repeat: inc repeat ; redisplay current image bne @exit @incclr: ; increase colour number used ; leave luminance unchanged lda COLOR4 clc adc #16 @dorest: sta COLOR4 inc changeclr @doagain: lda #255 sta CH jmp @waitkp @exit: pha LDY #EXITIM LDA #6 JSR SETVBV lda #64 sta NMIEN ; make sure DLI's are disabled ;; clear key press and return lda #255 sta CH pla cmp #22 ; if this was X key, then go do to DOS beq resrts jmp (RERUN) ;; reset IRQ and DMA reset: lda VKEYBD+1 cmp #>dmatoggle ; if IRQ was not put in place, bne resrts ; then leave it alone lda VKEYBD cmp #SCRADR bne @swscr1 ; only display screen 1 .endif @both: lda #>SCRADR ; if currently on screen 1, switch to 2 cmp DLADR+3 beq @swscr2 @swscr1: sta DLADR+3 ; otherwise switch to screen 1 lda #(>SCRADR)+16 sta DLADR+107 lda RENDMODE @ckhip: and #%11111110 cmp #'D' ; check for HIP mode (modes D and E) bne @swclr lda #64 ; HIP mode, so change DLI sta hipgr1+1 lda #128 sta hipgr2+1 bne @exit @swclr: .if DEBUG lda vbiscr ; check if scr 1, 2 or both should be displayed bne @exit ; display both screens alternately .endif lda COLOR1 ; swap colours for odd and even screen lines ldx dlicol1 ; around in 2 screen GR.8 and GR.15 modes stx COLOR1 sta dlicol1 lda COLOR2 ; don't need to do this for GR.8 but it doesn't ldx dlicol2 ; hurt do to it anyway and saves a few bytes stx COLOR2 sta dlicol2 @exit: pla tax jmp EXITIM @swscr2: ; switch to screen 2 lda #>SCR2ADR sta DLADR+3 lda #(>SCR2ADR)+16 sta DLADR+107 lda RENDMODE and #%11111110 cmp #'D' ; check for HIP mode (modes D and E) bne @swclr lda #128 ; HIP mode, so change DLI sta hipgr1+1 lda #64 sta hipgr2+1 jmp @exit changeclr: .byte 0 .if DEBUG vbiscr: .byte 0 ; display screen 1, 2 or both in flicker modes .endif ; display embeded string on screen ; string data should follow strout call and be terminated ; with a 0 strout: PLA ; get string address from stack TAY PLA TAX INY BNE @NIN2 INX @NIN2: sty putadr3 ; point at start of string STX @loop+2 stx putadr3+1 LDX #0 ; no data yet stx putadr3+2 stx putadr3+3 @loop: lda $c000,y ; - this address gets modified beq @exit ; continue until we find terminating 0 inc putadr3+2 ; increase length count bne @skplh inc putadr3+3 @skplh: iny bne @loop inc @loop+2 ; bump up to next page bne @loop @exit: TYA PHA jsr putbuf ; X should already be 0 PLA TAY ; modify return address to lda @loop+2 ; return immediately after pha ; terminating 0 tya pha rts ;; ;; use CIO to read a line of input from user ;; RDLINE: php lda COLCRS ; remember current cursor pos - 2 sec sbc #2 sta @hpos lda ROWCRS sta @vpos jsr GetLine lda SAVMSC sta devpt lda SAVMSC+1 sta devpt+1 ldy @vpos lda devpt @dvloop: dey ; calculate position on screen of bmi @vdone ; device name clc adc #40 bcc @dvloop inc devpt+1 bne @dvloop @vdone: sta devpt lda @hpos clc adc devpt sta devpt bcc @nopt1 inc devpt+1 @nopt1: ldy #1 @cpdev: lda (devpt),y ; get device name from screen jsr int2asc ; convert char to ATASCII sta RDBUF,y ; add device name to buffer dey bpl @cpdev plp bcs @rts ; don't update filename length if saving ldx ICBLL stx fnlen inx lda #155 sta RDBUF,x dex lda #':' ; find position of last ':' in filename @fndcln: ; not counting last character in filename ; anything before this will be device dex ; and directory name bmi @nocoln ; make sure we don't get into infinite loop cmp RDBUF,x ; if no ':' in filename bne @fndcln dex @nocoln: inx ; if no colon, set devlen to 0 stx devlen ; remember position for later stx savedevlen @rts: rts @hpos: .BYTE 0 @vpos: .BYTE 0 ;; ;; get a line worth of data ;; GetLine: ldx #0 lda #GETLNE ; read line of data into buffer into RDBUF+2 ldy #4 jmp SetICBICC ;; ;; convert a number from internal screen code to ATASCII ;; (this doesn't work for inverse characters) int2asc: cmp #96 bcs @rts ; if >= 96 then don't change cmp #64 bcs ascbit6 ; carry already clear adc #32 ; bit 6 not set, < 64, so add 32 @rts: rts ascbit6: eor #%01000000 ; if >= 64 and < 96 then toggle bit 6 ;; for int2asc this will clear it, for asc2int it will set it rts ;; ;; convert a number from ATASCII to internal screen code ;; (this doesn't work for inverse characters) asc2int: cmp #96 bcs @rts ; if >= 96 then don't change cmp #32 bcc ascbit6 ; carry already clear sbc #32 ; bit 6 not set, < 64, so add 32 @rts: rts ;; ;; close IOCB ;; CloseInFile: ldx #(LODCHN*16) .BYTE $2C CloseOutFile: ldx #(SAVECHN*16) CloseChX: lda #CLOSE jmp SetICBICC ;; ;; wait for key press ;; WAITKEYP: php jsr strout .BYTE 155,'S'+128,'p'+128,'a'+128,'c'+128,'e'+128, "-Run again",155 .BYTE 'X'+128, "-Exit to DOS",155,0 plp bcc @nomenu jsr strout .BYTE 'R'+128,"-Redisplay",155,0 @nomenu: lda #255 sta CH @waitkpd: lda CH cmp #255 beq @waitkpd ldx #255 stx CH cmp #40 ; 'R' key bne @notr inc repeat ; redisplay current image @rerun: jmp (RERUN) @notr: cmp #33 ; Space key beq @rerun cmp #22 ; X key bne @nomenu jmp DOS ;; switch interrupts off and enable OS RAM OSRAMON: sei ; disable interrupts lda #0 sta NMIEN lda PORTB sta portbtmp and #%11111110 ; enable OS RAM sta PORTB rts portbtmp: .BYTE 0 ;; switch interrupts on and disable OS RAM OSRAMOFF: lda portbtmp sta PORTB lda #64 sta NMIEN cli rts DLADR = $630 ; display list address SCRADR = $A010 ; 1st screen address SCR2ADR = $5010 ; 2nd screen address SCR2TMP = $E000 ; temporary storage for screen 2 until ; decoder finishes ;; ;; open screen in appropriate graphics mode ;; graphics mode in y register ;; OpenGr15: lda #15 OpenGr: sta @mode ldy #0 lda #$60 sta DLADR,y iny lda #64 ; LMS clc adc @mode ; add in LMS screen mode sta DLADR,y iny lda #SCRADR sta DLADR,y ldy #202 lda @mode @filgr1: dey sta DLADR+4,y bne @filgr1 ldy #105 lda #64 ; LMS clc adc @mode ; add in LMS screen mode sta DLADR,y iny lda #0 sta DLADR,y ; set up data address for LMS iny lda #(>SCRADR)+16 sta DLADR,y ldy #205 lda #65 ; end the DL sta DLADR,y iny lda #dldata1 sta DLADR,y lda #>SCRADR ; clear screen memory area jsr clrscr ; clear 8k of ram lda USE2SCR ; check if temp scr area needs cleaning too beq @notmp jsr OSRAMON ; enable OS RAM lda #>SCR2TMP ; clear temp area too jsr clrscr jsr OSRAMOFF @notmp: lda #0 sta SDMCTL lda #dldata1 sta SDLSTL+1 lda #34 sta SDMCTL rts @mode: .BYTE 0 ;; set up DLI interrups on every other display list line setdli: ldx #0 ldy #2 jsr @setdlisec ldx #4 ldy #105 jsr @setdlisec ldx #108 ldy #205 @setdlisec: sty @lastps+1 @setlp: lda DLADR,x ora #128 sta DLADR,x inx inx @lastps: cpx #0 ; gets changed bcc @setlp rts ;; first 2 lines of display list followed by a jump to page 6 ;; this won't fit in the space available in page 6 so have it here dldata1: .BYTE $60,$60,$1,$30,$06 ;; clear 8K worth of RAM for screen clrscr: ldx #32 ; 8K worth of screen data clrarea: sta filtpt+1 ; screen starting page ldy #0 tya sta filtpt @clrlp1: dey sta (filtpt),y bne @clrlp1 inc filtpt+1 dex bne @clrlp1 rts ;; ;; Print 1 byte decimal number as ATASCII on screen ;; DecOut: ldx #'0' ; initialise to ATASCII char 0 stx reslt stx reslt+1 ldx #0 @numlp: cmp numtbl,X bcc @endb1 sbc numtbl,X ; carry already set inc reslt,X bne @numlp @endb1: inx cpx #2 bcc @numlp ora #48 ; add 48 to number sta reslt+2 ldx #0 ldy #2 lda #'0' @fndst: cmp reslt,X bne @endlp inx dey bne @fndst @endlp: iny @disp: sty putadr3+2 ldy #>reslt txa clc adc # open file for read ;; Acc = 6 -> open for directory read ;; Acc = 8 -> open file for write ;; OpenFile: STA ICAX1,X ldy #24 ; open file, filename in RDBUF jmp SetICBOpen OSRAM: .BYTE 255 ;; copy screen data back down again from OS RAM copyscr: jsr OSRAMON lda #SCR2TMP sta rendpt+1 lda #SCR2ADR sta filtpt+1 ;; flicker modes alternate lines (i.e GR.9/10/9/10) to reduce flicker lda #<(SCRADR+40) sta drawtemp lda #>(SCRADR+40) sta drawtemp+1 ldx #100 ; copy 200 lines worth, 2 at a time @hiplp: ;; copy even lines (0,2,4...) over to screen 2 ldy #39 @hipcp: lda (rendpt),y sta (filtpt),y dey bpl @hipcp jsr addtofl40 ;; copy odd GR.9 lines (1,3,5...) over to screen 2 ldy #39 @hipcp2: lda (drawtemp),y sta (filtpt),y dey bpl @hipcp2 jsr addtopt40 ; increase rendpt pointer by 40 bytes ;; copy odd GR.10 lines (1,3,5...) over to screen 1 ldy #39 @hipcp3: lda (rendpt),y sta (drawtemp),y dey bpl @hipcp3 jsr addtofl40 lda drawtemp clc adc #80 sta drawtemp bcc @nodhi inc drawtemp+1 @nodhi: jsr addtopt40 ; increase rendpt by 40 bytes dex bne @hiplp jmp OSRAMOFF ;; ;; check if extension needs to be added to filename ;; CheckExt: lda #0 sta wild ; no wild card found yet lda #':' ; find position of last ':' in filename ldx fnlen ; not counting last character in filename dex ; anything before this will be device dex ; and directory name cmp RDBUF+2,x ; if last char is ':' this is a dir command bne @chkdt ; not a dir command, check if extension needed dec fnlen inc wild ; this is a dir command cmp RDBUF+1,x beq @dodir ; if two ':' are together at end, do *.JPG lda RDBUF+1 ; if last and 2nd char are both ':' cmp #':' ; then this is a dir command beq @gt1 cpx #1 ; if only ':' in filename, then do *.JPG bcs @gt1 ; if more than 1 char, this is a dir command jmp DOS ; otherwise go to DOS @gt1: lda #155 ; so replace that ':' with a return sta RDBUF+2,x @defd: dex bpl @chkdt inx @dodir: inc fnlen lda #'*' sta RDBUF+2,x bne @addext @chkdt: lda #'.' cmp RDBUF+2,x ; if last char is '.' no extension needed bne @skpdt lda #155 ; so replace that '.' with a return sta RDBUF+2,x rts @skpdt: ldy #4 ; check last 4 characters @extlp: dex beq @addext ; end of name, add extension cmp RDBUF+2,x beq @rts ; extension is already there, ignore dey bne @extlp @addext: ldx fnlen ldy #0 dex @cpextlp: lda @extname,y ; add extension to filename buffer beq @rts sta RDBUF+2,x inx iny bne @cpextlp @rts: rts @extname: .BYTE ".JPG",155,0 devlen: .byte 0 ; length of device + directory name in filename savedevlen: .byte 0 ; length of device + dir name in saved filename ;; set up IOCB, y register selects data to load into IOCB addresses SetICBOpen: lda #0 sta ICAX2,x lda #OPEN .byte $2C SetICBPut: ; set up IOCB for put buffer lda #PUTBUF SetICBICC: sta ICCOM,X SetICB: lda icbdat,y sta ICBAL,x lda icbdat+1,y sta ICBAH,x SetICBL: lda icbdat+2,y sta ICBLL,x lda icbdat+3,y sta ICBLH,x jmp CIOV ;; address, length icbdat: .WORD SCRADR, 8000 ; y=0 .WORD RDBUF+2, MAXLEN-6 ; y=4 .WORD hiphdr10, 6 ; y=8 .WORD hiphdr9, 6 ; y=12 .WORD micclrs, 6 ; y=16 .WORD K ; y=20 - 2 bytes .WORD E ; y=22 - 2 bytes .WORD RDBUF ; 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 RDBUF, MAXLEN ; y=30 putadr1: .WORD 0, 40*8 ; y=34 putadr2: .WORD 0, 320*8 ; y=38 putadr3: .WORD 0, 0 ; y=42 wild: .BYTE 0 ;; ;; display the disk directory using an open IOCB ;; FNCHAR1 = 4 ; position of first character in filename DispDir: ldx #(KEYBCHN*16) ; open keyboard so we can read single keypress jsr CloseChX LDA #4 STA ICAX1,x lda #0 sta dirend ; directory end not found yet sta dircount ; not filenames read from directory yet ldy #20 jsr SetICBOpen ; this will set ICBLL/H as well, but they are ; ignored anyway lda #<(SCRADR+2) sta rendpt lda #>(SCRADR+2) sta rendpt+1 @dirlp: ldx #(LODCHN*16) @notlast: lda #GETLNE ; first read directory into buffer sta ICCOM,x lda rendpt sta ICBAL,x lda rendpt+1 sta ICBAH,x 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 #'X' ; menu letters run from A-W bcc @nxtline @dirend: @prompt: jsr strout .byte 'S'+128, 'p'+128, 'a'+128, 'c'+128, 'e'+128, "Next " .byte '-'+128, "Prev " .byte '<'+128, "Up Dir " .byte 'E'+128, 's'+128, 'c'+128, "Quit",0 @getagain: ldx #(KEYBCHN*16) lda #GETBUF ldy #24 ; set ICBLL/H to 0, don't care about ICBAL/H jsr SetICBICC ; get keypress cmp #'<' bne @notudir ldx devlen cpx #3 ; we're not in a subdirectory, so can't move bcc @getagain ; up one level @fnddv: dex lda RDBUF,x ; find start of current subdirectory name cmp #':' bne @fnddv ldy savedevlen stx devlen ; update current device/directory name position 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 #<920 ; update current top of screen position sta rendpt ; this should be on the previous screenfull now txa sbc #>920 sta rendpt+1 clc lda svdircount adc #23 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 @jmexit @nextscr: jsr strout ; clear screen ready for next screenfull .byte 125,0 jmp @doagain @ntspc: cmp #27 ; is this the Esc key? bne @skjmpx @jmexit: jmp @exit ; yes, so exit @skjmpx: cmp #'A' ; did user press a menu key? bcc @getagain ; unknown key, go get another one cmp lastdirc ; key is above last valid keypress bcs @getagain ; go get another one 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: ldx devlen inx 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 #32 ; an extension beq @noext 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 ldy savedevlen stx devlen @cpdrct: lda lastfn,y sta RDBUF,x iny inx cmp #155 bne @cpdrct dex bpl @endcpdr @norddir: lda #155 ; end last filename with RETURN char sta RDBUF,x inc repeat @endcpdr: dex stx fnlen @exit: ldx #(KEYBCHN*16) ; close keyboard and exit jmp CloseChX K: .byte "K:" 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 ;; 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 lda rendpt STA ICBAL lda rendpt+1 STA ICBAH lda #PUTLNE sta ICCOM ldx #0 ldy #30 jsr SetICBL jmp addtopt40 ;; ;; save filename for later ;; SaveFN: ldx #MAXLEN dex @cpflnam: lda RDBUF,x ; copy filename from buffer to storage area sta lastfn,x dex bpl @cpflnam rts ;; ;; restore previously saved file name ;; RestFN: ldx #MAXLEN dex @cpflnam: lda lastfn,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 ldx #0 jmp SetICBICC ;; ;; set up ready to display prompt ;; SetPrmt: sta displast ; display character for last menu selection lda ROWCRS sta svrowcrs sec ; start from top of menu rts ;; ;; display prompt ;; DispPrmt: sta ROWCRS lda #1 sta COLCRS jsr strout .BYTE ">",0 lda #9 sta COLCRS lda svrowcrs sta ROWCRS jsr strout displast: .BYTE " ",30,0 rts svrowcrs: .byte 0 ;; open screen for GR.0 OpenGr0: ldx #0 jsr CloseChX LDA #12 STA ICAX1 ldy #22 ; open E: for Gr.0 jmp SetICBOpen E: .BYTE "E:" ;; ;; save one block of data for Micropainter format ;; X = IOCB number * 16 ;; A = data address low byte ;; Y = data address high byte ;; saveblock: lda #SCRADR saveblkadr: LDX #(SAVECHN*16) sta putadr1 sty putadr1+1 ldy #34 jmp SetICBPut getsvaddr: pha lda #svmenuopts ldy #6 bne skipmn ; forced branch getaddr: pha lda #menuopts ldy optlen skipmn: sta menupt stx menupt+1 pla @optloop: cmp (menupt),y ; check if key is on menu beq @optfnd dey bpl @optloop clc rts @optfnd: tya asl tay iny sec rts menuopts: .BYTE "1", "2", "3", "4", "5", "6", "N", "S" .BYTE "7", "8", "9", "A", "B", "C", "D", "E" svmenuopts: .BYTE "1", "2", "3", "4", "5", "N" optlen: .BYTE 0 ;; add value onto pointer addtopt320: inc rendpt+1 ; 320 = 256+64, increasing hi byte by 1 = 256 lda #<320 ; then add 64 into lo byte .byte $2c ; skip next instruction addtopt40: lda #40 addtopt: clc adc rendpt sta rendpt bcc @noinhi inc rendpt+1 @noinhi: rts ;; add value onto pointer addtofl40: lda #40 addtofl: clc adc filtpt sta filtpt bcc @noinhi inc filtpt+1 @noinhi: rts ;; DLI routine for HIP modes HIPDLI: pha hipgr1: lda #64 ; gets changed during VBI sta WSYNC sta PRIOR ; enable either gr.9 or gr.10 hipgr2: lda #128 ; gets changed during VBI sta WSYNC ; wait for next horizontal blank sta PRIOR ; enable other mode for next line pla rti ; all done ;; DLI routine for GR.15 modes GR15DLI: pha txa pha lda COLOR1 ldx COLOR2 sta WSYNC sta COLPF1 stx COLPF2 lda dlicol1 ldx dlicol2 sta WSYNC ; wait for next horizontal blank sta COLPF1 stx COLPF2 pla tax pla rti ; all done ;; DLI routine for GR.8 modes GR8DLI: pha lda COLOR1 sta WSYNC sta COLPF1 lda dlicol1 sta WSYNC ; wait for next horizontal blank sta COLPF1 pla rti ; all done dlicol1: .BYTE 0 ; alternate colour register 1 used in DLI dlicol2: .BYTE 0 ; alternate colour register 2 used in DLI micclrs: .BYTE 0, 4, 8, 12 ; ; HIP file format is: ; hiphdr10, ; 8000 bytes of GR.10 screen, ; hiphdr9 ; 8000 bytes of GR.9 screen hiphdr10: .BYTE $FF, $FF, $10, $60, $4F, $7F hiphdr9: .BYTE $FF, $FF, $10, $80, $4F, $9F segcodeend: ;; end of renderer code .if (segcodeend >= (SCRADR/256)*256) ;; there seems to be a bug in the .error command, so that giving it ;; a string constant causes an error, so do it this way instead .out .concat("Error - Code overrun into Screen area ", .string(*)) ; .error .else .out .concat("Code ends at ", .string(*)) .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 it later .ORG $B000 initcode: OSRTST = 65000 ; check this address to see if it is RAM or not ;; check if there is any RAM under the OS Init: lda #'D' sta lastfn lda #':' sta lastfn+1 lda #155 sta lastfn+2 sta lastfn+3 lda #0 sta coloff sta rowoff 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 LDY #0 @ISRAM: STY OSRAM jmp OSRAMOFF initcodeend: .ADDR 738 .ADDR 739 .WORD initcode ; run init code as soon as it is loaded