;; ;; JpegShow - JPEG slide show program ;; ;; Copyright 2001, Raphael Espino ;; last updated 07-Aug-01 ;; ;; to assemble: ;; ca65 jpegshow.asm ;; ld65 jpegshow.o jpy1223-8.o -o jpegshow -t atari ;; ;; ---------- 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 maskpt = 200 ; 2 byte pointer to current mask ;; ---------- 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 ; Column offset to display image at rowoff = $603 ; Row offset to display image at 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 width = $60E ; width of image in pixels (2 bytes) ;; 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 ABORTCD = 128 ; code to tell decoder to abort RTCLOK = 18 ; real time clock ROWCRS = 84 ; current cursor row COLCRS = 85 ; current cursor column SAVMSC = 88 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 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 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 MAXLEN = 64 ; max file name length RDBUF = $630 ; use page 6 for temporary buffer ;; 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 USEFILT sta USE2SCR sta dmaenbl ldx #2 jsr SetRowCol ;; display renderer information jsr strout .BYTE "JpegShow 0.1(07Aug01) Raphael Espino",155,155 .BYTE "Press ",'E'+128,'s'+128,'c'+128," to change configuration" .BYTE 155,155,0 lda dispconf beq @noconf jmp Config ; display configuration menu - doesn't return @noconf: lda firsttime ; only read the config file first time round bne @skjmno jmp @nocfg @skjmno: lda #0 sta firsttime sta SAVEDM sta dmaenbl ;; set up our own keyboard IRQ to toggle DMA if key pressed ldx #dmatoggle 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 ;; make sure IOCB is available jsr CloseInFile lda #READ ; not try to read config file ldx #(LODCHN*16) ldy #8 jsr OpenFile bmi @nocfg ;; config file exists, so now read data into temporary area ldy #0 lda #GETBUF jsr SetICBICC bmi @cfgerr ;; config was read OK, so copy data from temp area into config area ldy #cfglen-1 @cpcfg: lda SCRADR,y sta cfginfo,y dey bpl @cpcfg ldy #0 ; now find length of filename mask @fndmke: lda masknm,y cmp #155 beq @msken iny cpy #60 bcc @fndmke ;; no return char at end of mask! we've probably got a bad file ;; so copy default mask into place ldy #0 @cpdefmk: lda defmask,y sta masknm,y iny cmp #155 bne @cpdefmk sty masklen jsr FindDevLen jmp @cfgerr @msken: sty masklen jsr FindDevLen jmp @nocfg @cfgerr: ;; error reading config, give an error message jsr strout .BYTE 155, "Bad D:JPEGSHOW.CFG file", 155 .BYTE "Using default configuration",155,0 @nocfg: jsr CloseInFile ldy RENDMODE jmp @chklst ; check if we've reached last display mode @modelp: lda displist,y bne @gotmode iny @chklst: lda OSRAM bne @is64 ; 64K machines have more display options cpy #6 ; 48K machines have 7 options bcc @modelp ; not reached last option yet bcs @en48 ; forced branch - out ot display modes @is64: cpy #14 bcc @modelp ;; no more display modes selected, check what number we started at @en48: lda RENDMODE ; if we started with mode 0, then no modes beq @defmode ; selected, use default! inc lastfile ; we've done last mode, move to next file ldy #0 ; and go find next mode sty RENDMODE beq @modelp ; forced branch @defmode: lda OSRAM beq @noosram ldy #13 ; if 64K machine, then use mode E (HIP 1/1) .byte $2C @noosram: ldy #5 ; if 48K machine, use mode 5 (GR.9 2/1) lda #1 sta displist,y @gotmode: iny sty RENDMODE lda #READDIR ; do a directory ldx #(LODCHN*16) ldy #24 jsr OpenFile bpl @notdend jsr strout .BYTE "Can't find files in ",0 ldy #42 ; display mask value ldx #0 jsr SetICBPut jsr ClrScr jmp WAITKEYP @notdend: ;; now get next filename lda lastfile sta filecnt @nxtfile: lda #GETLNE ldy #12 jsr SetICBICC bpl @skeodir jmp @endofdir @skeodir: lda filecnt dec filecnt tay bne @nxtfile ;; make sure we're not on last line (free sector count) lda #GETLNE ldy #16 jsr SetICBICC bpl @skjmp jmp @endofdir @skjmp: jsr CloseInFile ;; first copy device and directory name across jsr FindDevLen ldx devlen @cpdrdv: lda masknm,x sta SCRADR+80,x dex bpl @cpdrdv ldy #2 ldx devlen inx @cplp: lda SCRADR,y ; then copy file name and put in correct cmp #32 ; format beq @skspc sta SCRADR+80,x inx @skspc: iny cpy #10 ; and don't do any more than 8(+2) characters bcc @cplp lda SCRADR,y ; if there is a space here, then there isn't cmp #32 ; an extension beq @noext lda #'.' ; otherwise add '.' char for extender sta SCRADR+80,x inx @cplp2: lda SCRADR,y ; now copy extender into filename cmp #32 ; if we find a space then finished beq @noext ; with extender sta SCRADR+80,x inx iny cpy #15 ; no more than 8+1+3(+2) chars for filename bcc @cplp2 @noext: lda #155 sta SCRADR+80,x ;; we've got the filename now, so go and open it lda #READ ; open the file ldx #(LODCHN*16) stx IOCBNUM ; tell decoder what IOCB to use ldy #30 jsr OpenFile bmi @error jsr strout .BYTE 'S'+128," Toggles screen while decoding",155,155,0 jsr strout .BYTE "Showing ",0 jsr DispFN ; display filename and return to decoder jsr strout .BYTE "Using option ",0 lda RENDMODE jsr HexOut1 jmp ClrScr ;; we've reached the end of the directory @endofdir: jsr CloseInFile lda lastfile ; if we've not read a file yet, then ; there are no suitable files on this disk beq @nofiles lda #0 sta lastfile sta RENDMODE jmp RafRendInit @nofiles: jsr strout .BYTE 155,"No files on disk",155,0 jmp WAITKEYP ;; 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 WAITKEYP lastfile: .BYTE 0 ; position of last file read in dir list filecnt: .BYTE 0 ; current position in dir list ;; ;; 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 dispconf ; if display config option set beq @ntconf ; then user pressed Esc key so restart jmp (RERUN) @ntconf: dec width ; set right edge for filter if used lsr width+1 beq @nohiwd ; high byte not set, so ignore lda #255 ; high byte is set, so set width to max sta width sec @nohiwd: ror width lda #25 sta DISPROWS jsr getmodepos cpy #12 ; options >= 7-9 and A-E use 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 @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 ; forced branch @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: lda dispconf ; if display config option set beq @ntconf ; then user pressed Esc key so restart jmp (RERUN) @ntconf: lda disblpic ; should we disable picture? beq @nodisbl lda SDMCTL ; if so, switch DMA off now sta SAVEDM lda #0 sta SDMCTL sta DMACTL @nodisbl: ldx #1 @cpcfoff: lda cfgcoloff,x sta coloff,x dex bpl @cpcfoff 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 lda #1 sta dmaenbl ; allow DMA to be toggled now UnusedVec: rts ;; set up for displaying 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 bmi exitstup ; forced branch @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 #62 ; toggle DMA if 's' key is pressed beq @togdma cmp #28 ; ESC key calls up user config bne @notesc lda #1 ; display configuration menu .byte $2C @notesc: lda #0 ; don't display configuration menu sta dispconf bne @abrt ; abort if Esc key was pressed lda dmaenbl ; if dma toggling not enabled (image not being beq OSKEYBIRQ ; displayed) then pass key to OS, otherwise @abrt: lda #ABORTCD ; tell decoder to abort sta ERROR pla rti @togdma: lda dmaenbl ; don't toggle DMA if we're not beq OSKEYBIRQ ; displaying image txa pha lda SDMCTL ; toggle DMA value ldx SAVEDM stx SDMCTL stx DMACTL sta SAVEDM pla tax pla rti OSKEYBIRQ: jmp 30000 ; gets changed - jump to previous keyb IRQ SAVEDM: .BYTE 0 ; save previous DMA value RENDMODE: .BYTE 0 ; graphics mode menu selection USE2SCR: .BYTE 0 ; use 2 screens? USEFILT: .BYTE 0 ; use filter? LINEBYTES: .BYTE 0 ; number of bytes to skip for next GR.9 line firsttime: .BYTE 1 ; is this the first run? dmaenbl: .BYTE 0 ; enable dma toggle keypress ;; ;; 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 jsr getmodepos 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 .WORD rendgr8-1, rendgr152-1, rendgr152-1 .WORD rendgr92-1, rendgr92-1, rendgr92-1, rendhip-1, rendhip-1 ;; ;; 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: 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 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 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 ClrScr jsr CloseInFile ; close input file jmp WAITKEYP ; 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 cpx width bcc @destadr lda #0 @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: lda #0 sta dmaenbl jsr reset ; reset DMA ;; close input file jsr CloseInFile ldx #255 ; clear last key press stx CH inx stx COLOR4 ; set background colour to black lda ERROR ; did decoder complete successfully? beq noerr cmp #ABORTCD beq rerun jsr ClrScr jsr WAITKEYP ; wait for key press rerun: jmp (RERUN) ; and rerun decoder noerr: 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 cmp #$D ; check for HIP mode (D and E) beq @hipdli cmp #$E beq @hipdli and #%11111110 cmp #8 ; check for GR.15 modes (8 and 9) 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 ; forced branch @gr8dli: lda #2 sta COLOR2 sta dlicol2 asl ; Acc = 4 sta dlicol1 lda #6 sta COLOR1 lda #GR8DLI bne @stupdl ; forced branch @hipdli: lda #HIPDLI @stupdl: sta VDSLST stx VDSLST+1 jsr setdli lda #192 sta NMIEN ; enable DLIs ;; get key press @waitkp: lda RTCLOK+1 clc adc imagedel ; wait for imagedel*256 jiffies sta @endtime @doagain: lda #255 sta CH @getk: lda RTCLOK+1 ; if real time clock equals end time cmp @endtime beq @exit ; then it is time to display next image lda dispconf ; if user pressed ESC then rerun bne @exit lda CH and #%00111111 ; ignore Shift and Control keys .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 #63 ; 63 = (255 AND %00111111) bne @exit beq @getk ; forced branch .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 @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) @endtime: .byte 0 ; time to wait before displaying next image ;; ;; reset IRQ and DMA ;; reset: ;; if DMA disabled, then re-enable it lda SAVEDM beq resrts ; DMA already enabled sta SDMCTL ; enable DMA sta DMACTL resrts: rts ;; ;; immediate VBI code to switch between 2 screens ;; IMVBI: txa pha ;; update other colour registers based on colour in COLOR4 ;; leave luminance values unchanged do this in VBI to make ;; sure DLI doesn't swap values on us half way through lda changeclr beq @nochng ldx #7 @clrloop: lda PCOLR0,x and #%00001111 ora COLOR4 sta PCOLR0,x dex bpl @clrloop ldx #1 @clr2loop: lda dlicol1,x and #%00001111 ora COLOR4 sta dlicol1,x dex bpl @clr2loop lda #0 sta changeclr @nochng: lda USE2SCR beq @exit .if DEBUG lda vbiscr ; check if scr 1, 2 or both should be displayed beq @both ; display both screens alternately bmi @swscr2 ; only display screen 2 lda #>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: cmp #$D ; check for HIP mode (modes D and E) beq @dohipmd cmp #$E bne @swclr @dohipmd: lda #64 ; HIP mode, so change DLI sta hipgr1+1 asl ; put 128 (64*2) into accumulator 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 cmp #$D ; check for HIP mode (modes D and E) beq @dohipmd2 cmp #$E bne @swclr @dohipmd2: lda #128 ; HIP mode, so change DLI sta hipgr1+1 lsr ; put 64 (128/2) into accumulator 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 ;; ;; get a line worth of data ;; GetLine: ldy #0 ldx #0 lda #GETLNE ; read line of data into buffer into SCRADR 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 set sbc #32 ; bit 6 not set, < 64, so add 32 @rts: rts ;; ;; close IOCB ;; CloseInFile: ldx #(LODCHN*16) CloseChX: lda #CLOSE jmp SetICBICC ;; ;; wait for key press ;; WAITKEYP: jsr strout .BYTE 'E'+128,'s'+128,'c'+128," for config, " .BYTE 'S'+128,'p'+128,'a'+128,'c'+128,'e'+128," to continue",155,0 @nomenu: lda #255 sta CH @waitkpd: lda CH cmp #255 beq @waitkpd ldx #255 stx CH cmp #28 ; ESC key - exit to config bne @ntesc inc dispconf bne @exit @ntesc: cmp #33 ; Space bar bne @waitkpd @exit: jmp (RERUN) ;; ;; 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 # 9, then convert it to A bcc @c1 adc #6 ; carry is set, so 6 + 48 + 1 = 55 @c1: adc #48 ldx #PUTBUF stx ICCOM ldx #0 stx ICBLL stx ICBLH jmp CIOV ;; ;; get keypress for menu selection ;; getmenu: ldx #0 lda #GETLNE ldy #24 ; set ICBLL/H to 0, don't care about ICBAL/H jsr SetICBICC ; get keypress cmp #155 beq @rts pha jsr CIOV ; ignore return key press pla @rts: rts ;; ;; get number from user ;; getnum: JSR GetLine ; read input data from user lda #0 TAY @loop2: sta @temp @loop: LDA SCRADR,Y cmp #155 ; finished if return key press found beq @done cmp #'C' ; C means centre image bne @notctr sec ; return with carry set for centre rts @notctr: iny and #%00001111 ; convert from ATASCII to number sta @temp+1 ldx #10 lda #0 clc @l2: adc @temp ; multiply by 10 dex bne @l2 adc @temp+1 jmp @loop2 @done: lda @temp clc ; don't centre image rts @temp: .WORD 0 ;; ;; 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 OSRAM: .BYTE 255 ; is there any RAM under the OS? ;; ;; 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 devlen: .byte 2 ; length of device + directory name in 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 lda icbdat,y sta ICBAL,x lda icbdat+1,y sta ICBAH,x lda icbdat+2,y sta ICBLL,x lda icbdat+3,y sta ICBLH,x jmp CIOV ;; address, length icbdat: .WORD SCRADR, cfglen ; y=0 .WORD RDBUF, MAXLEN ; y=4 - not used? .WORD cfgname, 15 ; y=8 .WORD SCRADR,40 ; y=12 .WORD SCRADR+40,40 ; y=16 .WORD K ; y=20 - 2 bytes .WORD E ; y=22 - 2 bytes .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 SCRADR+80, MAXLEN ; y=30 .WORD cfginfo, cfglen ; y=34 putadr3: .WORD 0, 0 ; y=38 .WORD masknm ; y=42 masklen: .WORD 7 wild: .BYTE 0 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 ;; ;; set current row/column values ;; x = new column ;; y = new row ;; SetRowCol: stx COLCRS sta ROWCRS rts ;; ;; read new mask value from screen ;; MASKPOS = 287 ; starting position of mask on screen ReadMask: lda SAVMSC clc adc #MASKPOS sta maskpt+1 ldy #0 ; read mask from screen ldx #0 ; copy the whole thing after device @cpdirn: lda (maskpt),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 jmp FindDevLen ;; ;; update position of last device+directory name, ':' or '>' used ;; to separate directory names ;; FindDevLen: ldx masklen FindDevLenX: lda #':' @schcln: cmp masknm,x lda masknm,x ; ':' is 58, '>' is 62 and #%11111011 ; converts 62 to 58 cmp #':' beq @fndcln dex bpl @schcln @fndcln: stx devlen rts ;; ;; 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 file name ;; RestFN: ldx #MAXLEN dex @cpflnam: lda masknm,x ; copy filename from buffer to storage area sta RDBUF,x dex bpl @cpflnam rts ;; ;; display file name ;; DispFN: ldy #30 lda #PUTLNE ; write out file name setx0: ldx #0 jmp SetICBICC ;; ;; display file name ;; DispMask: ldy #42 lda #PUTBUF bne setx0 ; forced branch ;; ;; user configuration options ;; Config: jsr strout .BYTE 155, " *** Configuration Options ***",155 .BYTE 155, "Mask:",0 lda #0 sta dispconf jsr DispMask ; display current mask jsr GetLine ; wait for input from user jsr ReadMask ; read user input from screen jsr strout .BYTE 155, "Delay:", 0 lda imagedel jsr DecOut ; display current image delay lda #8 sta COLCRS jsr strout .BYTE 30,31,0 jsr getnum ; get new delay value sta imagedel jsr strout .BYTE 155,"Offset col:", 0 ldx #0 jsr RowColInpt jsr strout .BYTE "Offset row:",0 ldx #1 jsr RowColInpt askagain: ;; display menu jsr strout .BYTE 155, "[ ] 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 #8 ; 9 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 #16 ; 17 options available on 64K machines sta optlen @noram: jsr strout .BYTE "[ ] O) Disable screen while decoding",155 .BYTE " S) Save D:JPEGSHOW.CFG",155 .BYTE " X) Exit",155 .BYTE "Choice?" .BYTE 0 lda SAVMSC ; point to screen area sta rendpt lda SAVMSC+1 sta rendpt+1 lda ROWCRS sec sbc optlen ; figure out difference between current pos tax ; and first menu option dex @poslp: jsr addtopt40 ; move to first line in menu dex bne @poslp ldx #0 ldy #3 @modelp: lda displist,x ; if mode is enabled, then add 'X' to menu bne @modeset lda #0 ; space in internal .byte $2C @modeset: lda #56 ; X in internal sta (rendpt),y jsr addtopt40 inx lda OSRAM bne @is64 cpx #6 bcc @modelp bcs @cont @is64: cpx #14 ; check all 15 display modes bcc @modelp @cont: lda disblpic ; if picture is disabled on decoding beq @nodisbl lda #56 ; then put an X in there too sta (rendpt),y @nodisbl: jsr getmenu ; get keypress for menu ldy optlen @optloop: cmp menuopts,y ; check if key is on menu beq @optfnd dey bpl @optloop jmp askagain ; key selected is not on menu @optfnd: cpy #6 beq @togpic cpy #7 beq @savcfg cpy #8 beq @exit bcc @no64K dey ; 64K options are 3 bytes higher dey dey @no64K: lda displist,y eor #1 sta displist,y jmp askagain @togpic: lda disblpic beq @disbl lda #0 ; don't disable picture .byte $2C @disbl: lda #1 ; do disable picture sta disblpic jmp askagain ;; save configuration to disk @savcfg: jsr CloseInFile lda #WRITE ; try to save config info to disk ldx #(LODCHN*16) ldy #8 jsr OpenFile bpl @cfgopen @cfgerr: 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 " Creating ",0 ldx #0 ldy #8 jsr SetICBPut jmp askagain @cfgopen: ldy #34 ; config file is open, dump config data to disk jsr SetICBPut bmi @cfgerr jsr CloseInFile @exit: lda #0 sta lastfile ; reset last file displayed sta RENDMODE ; reset last render mode used jmp (RERUN) ;; ;; get user input for row/col offset ;; RowColInpt: stx @tmpstx lda ctrcol,x beq @ntctrc jsr strout .BYTE "C", 30,0 jmp @ctrc @ntctrc: ldx @tmpstx lda cfgcoloff,x jsr DecOut lda #13 sta COLCRS jsr strout .BYTE 30,31,0 @ctrc: ldx @tmpstx lda #0 sta ctrcol,x jsr getnum ; get number from user ldx @tmpstx rol ctrcol,x ; if image to be centred, then carry set bne @ctrcl ; so move carry into ctrcol to set it sta cfgcoloff,x @ctrcl: rts @tmpstx: .byte 0 ; temporary storage dispconf: .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:" ;; ;; clear the screen ;; ClrScr: jsr strout .byte 155,0 rts ;; recognised options for configuration menu menuopts: .BYTE "1", "2", "3", "4", "5", "6", "O", "S", "X" .BYTE "7", "8", "9", "A", "B", "C", "D", "E" optlen: .BYTE 0 ;; ;; get position of render mode in mode address list, used for ;; jumping to code that will handle current display mode ;; returns list position in y register ;; getmodepos: ldy RENDMODE dey tya asl tay iny rts ;; ;; add value onto rendpt 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 filtpt 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 ;; keep dlicol1 and dlicol2 together dlicol1: .BYTE 0 ; alternate colour register 1 used in DLI dlicol2: .BYTE 0 ; alternate colour register 2 used in DLI ;; colours used for GR.15 formats micclrs: .BYTE 0, 4, 8, 12 ;; configuratio file name cfgname: .BYTE "D:JPEGSHOW.CFG",155 defmask: .BYTE "D:*.JPG", 155 ; default directory mask cfginfo: masknm: .BYTE "D:*.JPG", 155 ; directory mask .RES 60 ;; keep ctrcol and ctrrow together in this order ctrcol: .byte 1 ; centre image horizontally? ctrrow: .byte 1 ; centre image vertically, should always be ; immediately after ctrcol ;; keep cfgcoloff and cfgrowoff together in this order cfgcoloff: .byte 0 ; column offset if image is not centred cfgrowoff: .byte 0 ; row offset if image is not centred, imagedel: .byte 5 ; time to wait before displaying next image disblpic: .byte 0 ; should dma be disabled during decoding? displist: .byte 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0 cfgend: cfglen = cfgend-cfginfo+1 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: ;; ;; 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 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 sty displist+14 ; default to mode E (HIP 1/1) if 64K BNE @ISRAM @NTRAM: STY OSRTST ; RESTORE JUST IN CASE inc displist+5 ; default to mode 5 (GR.9 2/1) if 48K LDY #0 @ISRAM: STY OSRAM jmp OSRAMOFF initcodeend: .ADDR 738 .ADDR 739 .WORD initcode ; run init code as soon as it is loaded