; Cartridge Generator for the GMod3 cartridge ; based on "Magic Desk Cartridge Generator", taken from ; https://bitbucket.org/zzarko/magic-desk-cartridge-generator/src/master/ ; Magic Desk Compatible Cartridge Generator (c) 2013-2019 Žarko Živanov ; Cartridge schematics and PCB design (c) 2013-2014 Marko Šolajić ; E-mails: zzarko and msolajic at gmail ; This program is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 3 of the License, or ; (at your option) any later version. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; You should have received a copy of the GNU General Public License ; along with this program. If not, see . ; ZP usage: ; ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ; 02 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A FB FC FD FE ; ** ** ** ** ** ** ; 52 57 58 59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E ;-------------------------------- ; RAM and ROM ;-------------------------------- SCREEN = $0400 COLORR = $D800 CHROUT = $FFD2 GETIN = $FFE4 SCNKEY = $FF9F COLDRES = $FCE2 BORDER = $D020 PAPER = $D021 IRQ_Vector = $0314 IRQ_Kernel = $EA31 CursorColor = $0286 drive = $BA status = $90 LISTEN = $FFB1 SECOND = $FF93 UNLSTN = $FFAE ;-------------------------------- ; menu variables ;-------------------------------- ; current menu variables current_menu = $25 prev_menu = $5C cmenu_offset = $1A ; 2B offset from screen start cmenu_offset_it = $1C ; 2B offset from screen start for items cmenu_offset_key = $26 ; 2B offset from screen start for item key cmenu_width = $1E ; 1B menu inside width cmenu_height = $1F ; 1B menu inside height cmenu_iwidth = $20 ; 1B menu inside width minus chars for key cmenu_items_no = $21 ; 1B number of items cmenu_max_first = $22 ; 1B max value for first item cmenu_items = $23 ; 1B pointer to list of items cmenu_first_idx = $19 ; 1B index of first item to show cmenu_item_adr = $14 ; 2B pointer to current item text cmenu_item_idx = $16 ; 1B current item index cmenu_spacing = $2A ; 1B menu items spacing (0-no spacing,1-1 line) ; misc menu variables temp = $02 ; 1B temorary value chrmem = $FB ; 2B pointer to addres on screen colmem = $FD ; 2B pointer to addres in color ram chrkeymem = $28 ; 2B pointer to addres on screen for key drawm_line = $17 ; 1B current line when drawing items drawm_idx = $18 ; 1B current item index when drawing items ;-------------------------------- ; IRQ variables ;-------------------------------- SCR_FirstLine = 58 SCR_LastLine = 242 ; IRQ wave variables WaveSpeed = 2 CurrentLine = $58 CurrentWave = $59 FirstWave = $5A WavePause = $5B ;-------------------------------- ; cartridge start ;-------------------------------- * = $8000 ;* = $0820 !word cold_start !word init_menu !byte $c3,$c2,$cd,$38,$30 ; cartridge magic bytes, CBM80 cold_start: stx $d016 jsr $FDA3 ; IOINIT, init CIA,IRQ jsr $FD50 ; RAMTAS, init memory jsr $FD15 ; RESTOR, init I/O jsr $FF5B ; SCINIT, init video cli jsr $E453 ; load BASIC vectors jsr $E3BF ; init BASIC RAM jsr $E422 ; print BASIC start up messages ldx #$FB ; init BASIC stack txs ; debug ; lda #5 ; sta current_menu ; lda #12 ; jmp prepare_run jmp init_menu ;-------------------------------- ; menu drawing ;-------------------------------- scrolld: lda cmenu_first_idx cmp cmenu_max_first bpl + inc cmenu_first_idx + jmp dm1 scrollu: lda cmenu_first_idx beq + dec cmenu_first_idx + jmp dm1 paged: lda cmenu_first_idx clc adc cmenu_height sta cmenu_first_idx cmp cmenu_max_first bmi + lda cmenu_max_first sta cmenu_first_idx + jmp dm1 pageu: lda cmenu_first_idx sec sbc cmenu_height sta cmenu_first_idx cmp #0 bpl + lda #0 sta cmenu_first_idx + jmp dm1 init_menu: lda DoSound beq + jsr sound_init + jsr WaveVIC lda #$08 ; disable c=+sh jsr CHROUT lda #$0e ; lower chars jsr CHROUT lda #0 sta current_menu lda #$FF sta prev_menu dm0: lda current_menu cmp prev_menu beq dm2 sta prev_menu lda #$60 ldx #$A0 ldy #6 jsr clear_screen jsr setup_menu lda #0 sta cmenu_first_idx jsr draw_menu_border dm1: lda cmenu_first_idx sta drawm_idx jsr draw_menu_items dm2: jsr read_key beq dm2 cmp #$1D ; left-right key beq scrolld cmp #$11 ; up-down key beq scrollu cmp #$9D ; shift+left-right key beq paged cmp #$91 ; shift+up-down key beq pageu cmp #$0D ; return beq nextmenu cmp #$5F ; left arrow beq basic cmp #$85 ; F1 bmi prgkey cmp #$8D ; 89 - F7+1, 8D - F8+1 bpl prgkey sec sbc #$85 tay lda menu_items_no,y beq dm2 sty current_menu jmp dm0 prgkey: ldy #0 ; check if some program is selected - cmp menu_keys,y beq + iny cpy #menu_keys_no bne - jmp dm2 + iny ; last item is basic, do not check that key tya ldy current_menu cmp menu_items_no,y bpl dm2 tay dey tya jmp prepare_run basic: ldy cmenu_items_no dey tya jmp prepare_run nextmenu: ldy current_menu sty temp iny cpy #8 bne + ldy #0 + lda menu_items_no,y sty current_menu bne + lda #0 sta current_menu + jmp dm0 draw_menu_border: lda cmenu_offset sta chrmem sta colmem lda #>SCREEN clc adc cmenu_offset+1 sta chrmem+1 lda #>COLORR clc adc cmenu_offset+1 sta colmem+1 ldx #0 jsr draw_menu_border_line jsr menu_next_line lda cmenu_height ldy cmenu_spacing beq dmb1 asl tay dey tya dmb1: pha ldx #4 jsr draw_menu_border_line lda cmenu_spacing beq + pla pha and #1 beq dmb2 + ldy #1 ldx #0 - lda menu_key_chr,x sta (chrmem),y lda menu_key_col,x sta (colmem),y iny inx cpx #3 bmi - dmb2: jsr menu_next_line pla tay dey tya bne dmb1 ldx #8 jsr draw_menu_border_line jsr menu_next_line ldx #12 jsr draw_menu_border_line rts draw_menu_border_line: ldy #0 lda menu_border_chr,x sta (chrmem),y lda menu_border_col,x sta (colmem),y iny lda cmenu_width sta temp inc temp inx - lda menu_border_chr,x sta (chrmem),y lda menu_border_col,x sta (colmem),y iny cpy temp bmi - inx lda menu_border_chr,x sta (chrmem),y lda menu_border_col,x sta (colmem),y inx iny lda menu_border_chr,x sta (chrmem),y lda menu_border_col,x sta (colmem),y rts menu_next_line: clc lda chrmem adc #40 sta chrmem sta colmem bcc + inc chrmem+1 inc colmem+1 + clc lda chrkeymem adc #40 sta chrkeymem bcc + inc chrkeymem+1 + rts draw_menu_items: lda cmenu_offset_it sta chrmem sta colmem lda #>SCREEN clc adc cmenu_offset_it+1 sta chrmem+1 lda #>COLORR clc adc cmenu_offset_it+1 sta colmem+1 lda cmenu_offset_key sta chrkeymem lda #>SCREEN clc adc cmenu_offset_key+1 sta chrkeymem+1 lda drawm_idx jsr find_menu_item lda #0 sta drawm_line - jsr draw_menu_item_line jsr menu_next_line jsr find_next_menu_item ldy cmenu_spacing beq dmi1 jsr menu_next_line dmi1: inc drawm_idx inc drawm_line lda drawm_line cmp cmenu_height bne - rts draw_menu_item_line: ldy #0 - lda (cmenu_item_adr),y beq + sta (chrmem),y iny cpy cmenu_iwidth bne - + - lda menu_chr_back cpy cmenu_iwidth beq + sta (chrmem),y iny bne - + ldy drawm_idx ; convert PETSCII to inverted SCREEN codes iny ; last item will always be "Basic", and its key is left arrow cpy cmenu_items_no bne + ldy #menu_keys_no+1 + dey lda menu_keys,y cmp #$C1 ; codes C1-DA translate to C1-DA bcs dmil1 cmp #$40 bcc + clc ; codes 41-5A translate to 81-9A adc #$40 !byte $2C ; bit instruction + ora #$80 ; codes 41-5A translate to B0-B9 dmil1: ldy #0 sta (chrkeymem),y rts ;-------------------------------- ; item list functions ;-------------------------------- ; find menu item whose index is in A register and set menu item pointer find_menu_item: sta cmenu_item_idx lda cmenu_items sta cmenu_item_adr lda cmenu_items+1 sta cmenu_item_adr+1 ldx #0 fmi1: cpx cmenu_item_idx beq fmi2 ldy #0 - lda (cmenu_item_adr),y beq + iny bne - + iny tya clc adc cmenu_item_adr bcc + inc cmenu_item_adr+1 + sta cmenu_item_adr inx jmp fmi1 fmi2: rts find_next_menu_item: ldx cmenu_item_idx inc cmenu_item_idx jmp fmi1 ;-------------------------------- ; menu setup ;-------------------------------- ; setup all current menu variables for menu whose index is in current_menu setup_menu: ldx current_menu ldy menu_items_no,x sty cmenu_items_no tya sec sbc menu_height,x sta cmenu_max_first ldy menu_width,x sty cmenu_width dey dey dey sty cmenu_iwidth ; menu_width-3 (width of key display) ldy menu_height,x sty cmenu_height ldy menu_spacing,x sty cmenu_spacing txa ; offsets are 2-byte asl tax lda menu_offset,x sta cmenu_offset lda menu_offset+1,x sta cmenu_offset+1 lda menu_items,x sta cmenu_items lda menu_items+1,x sta cmenu_items+1 ; calculate offset for menu items lda cmenu_offset+1 sta cmenu_offset_it+1 lda cmenu_offset clc adc #44 ; 1 line + 3 chars for key display sta cmenu_offset_it bcc + inc cmenu_offset_it+1 ; calculate offset for menu item keys + lda cmenu_offset+1 sta cmenu_offset_key+1 lda cmenu_offset clc adc #42 ; 1 line + 2 sta cmenu_offset_key bcc + inc cmenu_offset_key+1 + lda menu_names,x sta chrmem lda menu_names+1,x sta chrmem+1 ldy #0 - lda (chrmem),y ora #$80 sta SCREEN,y lda menu_help,y ora #$80 sta SCREEN+24*40,y iny cpy #40 bne - rts ;-------------------------------- ; utility functions ;-------------------------------- ; A - border/paper, X - char, Y - color clear_screen: sta PAPER lsr lsr lsr lsr sta BORDER txa ldx #0 - sta SCREEN,x sta SCREEN+$100,x sta SCREEN+$200,x sta SCREEN+$2E8,x pha tya sta COLORR,x sta COLORR+$100,x sta COLORR+$200,x sta COLORR+$2E8,x pla inx bne - rts ; returns key as read by GETIN, makes a small pause before reading read_key: ldy #40 ldx #0 - dex bne - dey bne - - jsr SCNKEY jsr GETIN beq - rts ; find first active drive FindFirstDrive: lda #8 sta drive - ldx #0 stx status jsr LISTEN lda #$FF jsr SECOND lda status bpl + jsr UNLSTN inc drive lda drive cmp #31 bne - lda #8 ; if not found, set 08 sta drive + jsr UNLSTN rts ;-------------------------------- ; IRQ ;-------------------------------- ; setup VIC II for waving menu WaveVIC: sei lda #WaveIRQ sta IRQ_Vector+1 lda #$7f ;disable cia irq sta $dc0d sta $dd0d lda $dc0d lda $dd0d lda $d01a ;enable raster irq ora #01 sta $d01a lda $d011 ;raster irq bit 8 and #$7f sta $d011 lda #SCR_FirstLine sta $d012 sta CurrentLine lda #06 sta $d020 lda #00 sta $d021 sta CurrentWave sta FirstWave sta WavePause ; lda $dd00 ;vic bank ; and #$fc ; ora #00 !0-c000,1-8000 ; sta $dd00 ; ;vic setup $d018 ; ;bit 7-4 screen 0001-$0400 ; ;bit 3-1 charmem 001-$0800 ; lda #%00010100 ; sta $d018 cli rts ; return VIC II to normal state NormalVIC: sei lda #IRQ_Kernel sta IRQ_Vector+1 jsr $FDA3 ; IOINIT, init CIA,IRQ ;jsr $FD50 ; RAMTAS, init memory jsr $FD15 ; RESTOR, init I/O jsr $FF5B ; SCINIT, init video ;lda $d01a ;and #$fe ;sta $d01a ;lda #$c8 ;sta $d016 lda RunBorderColor sta $d020 lda RunPaperColor sta $d021 lda RunInkColor sta CursorColor ldx #$20 ; clear SID registers lda #$00 - sta $D400,x dex bpl - cli rts ; IRQ for menu waves WaveIRQ: lda DoWave beq dosound nop nop nop nop nop nop ldy CurrentWave lda WaveTable,y sta $d016 iny cpy #WaveTableMax bne irq0 ldy #00 irq0: sty CurrentWave ldy CurrentLine cpy #SCR_LastLine-8 bcc + dey + tya clc adc #08 cmp #SCR_LastLine bcc irq1 lda #SCR_FirstLine irq1: sta CurrentLine sta $d012 cmp #SCR_FirstLine bne irqend lda #$c8 sta $d016 lda FirstWave sta CurrentWave ldy WavePause iny cpy #WaveSpeed bne irq3 ldy #00 ldx FirstWave inx cpx #WaveTableMax bne irq4 ldx #00 irq4: stx FirstWave irq3: sty WavePause dosound: ldx #$50 - lda $D41B ;Oscillator 3 Output sta $D401 ;Voice 1: Frequency Control - High-Byte dex bne - irqend: ldy $d019 sty $d019 pla tay pla tax pla rti WaveTable: !byte $c0,$c0,$c1,$c1,$c2,$c3,$c4,$c5,$c5,$c6,$c6,$c7,$c7 !byte $c7,$c7,$c6,$c6,$c5,$c5,$c4,$c3,$c2,$c1,$c1,$c0,$c0 WaveTableMax = *-WaveTable ;-------------------------------- ; memory copy ;-------------------------------- TableAddress = $FB ProgramIndex = $FD prepare_run: sta ProgramIndex ldy #0 - cpy current_menu beq + ldx menu_items_no,y txa clc adc ProgramIndex sta ProgramIndex iny bne - + jsr NormalVIC jsr $E453 ; load BASIC vectors jsr $E3BF ; init BASIC RAM ;jsr $A68E ; set current character pointer to start of basic - 1 ;jsr $E422 ; print BASIC start up messages ;ldx #$FB ;txs jsr FindFirstDrive startCopy: ldy #CartCopyLen ; copy routine to 0340 - lda CartCopy0340,y sta $0340,y dey bpl - ldy #0 ;calculate program table element address sty TableAddress+1 ;each table element is 9 bytes lda ProgramIndex ;ProgramIndex*8 asl rol TableAddress+1 asl rol TableAddress+1 asl rol TableAddress+1 sta TableAddress lda ProgramIndex ;ProgramIndex*8 + ProgramIndex clc adc TableAddress sta TableAddress bcc sc2 inc TableAddress+1 sc2: lda ProgramTable ;add Program table address clc adc TableAddress sta TableAddress lda ProgramTable+1 adc TableAddress+1 sta TableAddress+1 lda (TableAddress),y ;set values in copy routine sta CartBank iny lda (TableAddress),y sta CartAddr iny lda (TableAddress),y sta CartAddr+1 iny lda (TableAddress),y sta PrgLenLo iny lda (TableAddress),y sta PrgLenHi iny lda (TableAddress),y sta MemAddr iny lda (TableAddress),y sta MemAddr+1 iny lda (TableAddress),y sta pstart+1 iny lda (TableAddress),y sta pstart+2 beq sc3 ;if high byte of start address != 0 lda #$4c ;change lda to jmp sta pstart ;else, run as basic sc3: jmp $0340 CartCopy0340: !pseudopc $0340 { PrgLenLo = * + 1 ldx #00 ;program length lo PrgLenHi = * + 1 ldy #00 ;program length hi CartBank = * + 1 cbank: lda #00 ;cartridge start bank sta $de00 ;cartridge bank switching address CartAddr = * + 1 caddr: lda $8200 ;cartridge start adr MemAddr = * + 1 sta $0801 ;c64 memory start adr dex cpx #$ff bne cc1 dey cpy #$ff beq crtoff cc1: inc MemAddr bne cc2 inc MemAddr+1 cc2: inc CartAddr bne caddr inc CartAddr+1 lda CartAddr+1 cmp #$a0 ;next bank? bne caddr inc CartBank lda #$80 ;cartridge bank is on $8000-$9fff sta CartAddr+1 jmp cbank crtoff: ;turn off cartridge (gmod3) lda #%01100000 sta $de08 lda MemAddr ;set end of program (var start) sta $2d sta $2f sta $31 sta $ae lda MemAddr+1 sta $2e sta $30 sta $32 sta $af pstart: lda $0801 ;start the program lda #00 ; basic start jsr $A871 ; clr jsr $A533 ; re-link jsr $A68E ; set current character pointer to start of basic - 1 jmp $A7AE ; run } CartCopyLen = *-CartCopy0340 ;-------------------------------- ; menu sound ;-------------------------------- sound_init: lda #$00 sta $D40F ;Voice 3: Frequency Control - High-Byte lda #$1E sta $D40E ;Voice 3: Frequency Control - Low-Byte lda #$F0 sta $D414 ;Voice 3: Sustain / Release Cycle Control sta $D406 ;Voice 1: Sustain / Release Cycle Control lda #$81 sta $D412 ;Voice 3: Control Register lda #$11 sta $D404 ;Voice 1: Control Register lda #$83 sta $D418 ;Select Filter Mode and Volume rts ;-------------------------------- ; menu data ;-------------------------------- ; menu chars corners MENU_CHR_UL = $EC MENU_CHR_UR = $FB MENU_CHR_DL = $FC MENU_CHR_DR = $FE MENU_COL_UL = 10 MENU_COL_UR = 10 MENU_COL_DL = 10 MENU_COL_DR = 10 ; menu chars sides MENU_CHR_L = $61 MENU_CHR_R = $E1 MENU_CHR_U = $E2 MENU_CHR_D = $62 MENU_COL_L = 10 MENU_COL_R = 10 MENU_COL_U = 10 MENU_COL_D = 10 ; menu chars misc MENU_CHR_BACK = $20 MENU_CHR_SHAD = $E6 MENU_COL_BACK = 5 MENU_COL_SHAD = 2 MENU_COL1_KEY = 12 MENU_COL2_KEY = 15 MENU_COL_NAME = 4 SCREEN_CHR = $A0 SCREEN_COL = 6 ; menu border first, middle, bottom and last line chars menu_border_chr: !byte MENU_CHR_UL,MENU_CHR_U,MENU_CHR_UR,SCREEN_CHR !byte MENU_CHR_L,MENU_CHR_BACK,MENU_CHR_R,MENU_CHR_SHAD !byte MENU_CHR_DL,MENU_CHR_D,MENU_CHR_DR,MENU_CHR_SHAD !byte SCREEN_CHR,MENU_CHR_SHAD,MENU_CHR_SHAD,MENU_CHR_SHAD ; menu border first, middle, bottom and last line colors menu_border_col: !byte MENU_COL_UL,MENU_COL_U,MENU_COL_UR,SCREEN_COL !byte MENU_COL_L,MENU_COL_BACK,MENU_COL_R,MENU_COL_SHAD !byte MENU_COL_DL,MENU_COL_D,MENU_COL_DR,MENU_COL_SHAD !byte SCREEN_COL,MENU_COL_SHAD,MENU_COL_SHAD,MENU_COL_SHAD ; misc menu chars menu_chr_back: !byte MENU_CHR_BACK menu_key_chr: !byte $F5,$B1,$F6 menu_key_col: !byte MENU_COL1_KEY,MENU_COL2_KEY,MENU_COL1_KEY ; keys for selecting a program menu_keys: ; 1-9 !for i,0,8 { !byte i+$31 } !byte $30 ; 0 ; a-z !for i,0,25 { !byte i+$41 } ; A-Z !for i,0,25 { !byte i+$c1 } menu_keys_no = *-menu_keys !byte $5F ; left arrow ProgramTable = * RunBorderColor = ProgramTable + 2 RunPaperColor = RunBorderColor + 1 RunInkColor = RunPaperColor + 1 DoWave = RunInkColor +1 DoSound = DoWave + 1 menu_items_no = DoSound + 1 menu_offset = menu_items_no + 8 menu_width = menu_offset + 16 menu_height = menu_width + 8 menu_spacing = menu_height + 8 ; menu_names = menu_spacing + 8 menu_items = menu_names + 16 menu_help = menu_items + 16 menu_name1 = menu_help + 40 ;menu_items1 = menu_name1 + 40 ; ;programs = menu_items1 + (20 * 2) ;ProgramTable: !word programs ; program run colors ; RunBorderColor: !byte 12 ;14 ; RunPaperColor: !byte 15 ;6 ; RunInkColor: !byte 0 ;14 ; DoWave: !byte 1 ;menu waving ; DoSound: !byte 1 ;menu sound ; menu data for 8 menus ; menu_items_no: !byte 1,0,0,0,0,0,0,0 ; menu_offset: !word 85,0,0,0,0,0,0,0 ; menu_width: !byte 15,0,0,0,0,0,0,0 ; menu_height: !byte 10,0,0,0,0,0,0,0 ; menu_spacing: !byte 1,0,0,0,0,0,0,0 ; menu_names: !word menu_name1,0,0,0,0,0,0,0 ; menu_items: !word menu_items1,0,0,0,0,0,0,0 ; ; ;1234567890123456789012345678901234567890 ; menu_help: !text "(Shift)CRSR: Scroll, Fn/Ret: Menu select" ; ; menu_name1: !text " Testing... " ; ; menu_items1: ; ;!text @"Testing1\$00" ; ;!text @"Testing2\$00" ; !text "Testing1",0 ; !text "Testing2",0 ; ; ; programs: ; program table ; !byte bank ; !word address in bank ; !word length ; !word load address ; !word start address ; if hi byte=0, then run as basic ; ne radi ; f4/6 64tester ; f4/c basic 64 compiler ; f5/e turbocopy 5 ; f5/h turbo nibbler 5