NAME !gameMTX.ROMS.GAMERom TYPE rom ; temporary storage, well out of the way of the incomming game ; approx 200 bytes free between stack at &FD4B coming down ; and keyboard buffer &FB4B (160 bytes) sector EQU &FBF0 key EQU &fbf1 lstpg equ &fa7a page equ &fad2 ; Code starts at &2000, as only the upper half of the 16k rom space is paged. ORG &2000 ;if bytes 0 - 7 are 8-1 resp. autoboot rom via &2010 on power up/reset ;but after high memory cleared, any variables set will be retained DB 8 DB 7 DB 6 DB 5 DB 4 DB 3 DB 2 DB 1 ; BASIC's ROM command enters at &200C ORG &200C JP run DB 0 ;boot entry point &2010 JP boot ; Skeleton for the rom X entry point routine ;not used here, kept in place for completeness. .run PUSH HL PUSH DE PUSH BC PUSH AF PUSH IX PUSH IY ;code goes here POP IY POP IX POP AF POP BC POP DE ;tidy the stack on exit LD L,&00 EX (SP),HL JP &0089 .boot ;setup the stack ld hl,&fd48 ld (&fa96),hl ;this is the MTX VDINIT routine, which is run immediately after the autostart rom check. rst &28 db &42 ;select and clear VS 5 usinf RST 10 functions, which now work, thanks to VDinit rst &10 db &4d ;make sure the CTC iterrupts are off just in case call ctc_off call welcome_beep ;make sure we're in the correct page (0 for 32k, 1 for 64k, 3 for 128k ;or the data rom wont be acessible ld a,(lstpg) or &70 ld (page),a out (0),a CALL char_map ;make sure we have a readable text screen,with box graphics .key_loop CALL welcome_page call scan call readkey ;returns an index value in A only accepts 0-9 and A-Z CALL cls call get_dir ;returns with HL pointing to the start of the directory ld a,(hl) ld (sector),a ;save the location for later inc a jr z,key_loop ;loop if empty entry call loading_message ; first sector paged in on exit ; call space ;wait only needed while developing call loadgame ;wont exit unless the game tries to exit to basic (ie reversi) jp boot ;so re-start if it does .welcome_beep ;the sound chip seems to default to making a noise at startup, ; and we're loading before the OS kills the volume ;so kill all 4 channels call sound_off ;now make the hello! beep ld hl,20000 call beep_hl ld hl,0 call delay call sound_off ld hl,20000 call delay ld hl,28000 call beep_hl ld hl,30000 call delay call sound_off ret .get_dir push bc and &3f ld l,a ld h,0 ;multipy index by 16 add hl,hl add hl,hl add hl,hl add hl,hl ld bc,directory add hl,bc pop bc ret .space ; make sure space key up LD A,&7F OUT (5),A IN A,(6) AND 1 JR Z space ; no test for key press .space2 LD A,&7F OUT (5),A IN A,(6) AND 1 JR NZ space2 RET ;catalogue entry in HL .loadgame call find_sector ld e,(hl) inc hl ld d,(hl) ;DE now holds the loading address inc hl ld c,(hl) inc hl ld b,(hl) ;BC now has the file size inc hl ;HL now points to the first byte which is also the codes entry point push de ;push the start address onto the stack push BC ;save the file size ld BC,&7fc ;first sector is 4 bytes short because of the header LDIR ;transfer the short sector, no file size check as all entrys must be ;at least one sector ld bc,&f804 ;-&7fC .block_loop pop hl ;get the remaining file size add hl,bc ret nc ;if Carry isn't set then there were less than &800 (7FC) bytes left ;so exit via run address stacked above push hl ;re-stack the remaining file size ld bc,&800 ;and set BC for a full sector transfer ld a,(sector) inc a ld (sector),a call find_sector ldir ;transfer 1 sector full, DE ld bc,&f800 ;set BC to -&800 for 2nd and later sectors jr block_loop ; scan the directory that's in the rom image at &3C00 ; offset &00 starting "sector" byte ; &01 to &0E is the name, padded out with spaces ; offset &0F is zero terminator for the print routine .scan push af push bc push de push hl ld de,directory ld h,0 ;file counter .scan_loop ld a,(de) ;get the sector byte inc a ;&FF means we're reached the end of the table jr z,scan_done ;we have a valid entry, so work out the screen position ld a,h add a,8 rra ld b,a ;row position A/2 +4 , carry now set if we had an odd number in h ld a,5 ;screen is set up for 14 char names starting in col 5 or 24 jr nc scan1 add a,19 .scan1 ld c,a inc de ;start of the name call print_at ; add 15 to DE, for the next directory entry and increment the screen position ld a,e add a,15 ld e,a ld a,d adc a,0 ld d,a inc h jr scan_loop .scan_done pop hl pop de pop bc pop af ret ;Character defintion table for text mode is stored at &1800, with Space at &1900 ; bit 6 of 2nd transfer set to indicate writing to Vram .char_map push af push bc push hl LD A,0 OUT (2),A LD A,&59 OUT (2),A LD HL,char_table ld bc,&300 .char_loop ld a,(hl) out (1),a inc hl dec bc ld a,b or c jr nz,char_loop pop hl pop bc pop af RET ;Screem character map for text mode is stored at &1c00, 40 rows of 24 = 960 characters ; bit 6 of 2nd transfer set to indicate writing to Vram .cls push af push de LD A,0 OUT (2),A LD A,&5C OUT (2),A LD de,960 .cls_1 LD A,32 OUT (1),A dec de ld a,d or e jr nz,cls_1 pop de pop af RET ;Screem character map for text mode is stored at &1c00, 40 rows of 24 = 960 characters ;bit 6 of 2nd transfer set to indicate writing to Vram, simce there is plenty of rom ;space the basic layout is stored uncompressed. .welcome_page push af push de push hl LD HL,page_text LD A,0 OUT (2),A nop LD A,&5C OUT (2),A ld de,960 .welcome_loop ld a,(hl) out (1),a inc hl dec DE ld a,E or d jr nz,welcome_loop pop hl pop de pop af RET ;MTX's main keyboard routine is at &0079, ans returned Z set if no key ;or the ASCII code in A .readkey call &0079 ;read keyboard, jr z,readkey ;wait for something to be pressed cp +ASC"0" jr c,readkey cp +ASC":" jr c,zero_to_nine and +%11011111 ;take care of lower case cp +ASC"A" jr c,readkey cp +ASC"[" jr c,a_to_z jr readkey .a_to_z sub 7 .zero_to_nine sub +asc"0" ld (key),a RET ;on entry HL point to the directory entry ;this routine prints the load & run address on screen ;used for diagnostics / development abd isn't needed ;otherwise. .loading_message push af push bc push de push hl ld de,mess ld b,1 ld c,1 call print_at ld d,h ld e,l inc de ;skip over the location pointer ld c,10 call print_at ;print the name call find_sector ld b,3 ld c,1 ld de,mess1 call print_at ld e,(hl) inc hl ld d,(hl) inc hl call print_hex ;print load address ld b,5 ld de,mess2 call print_at ld e,(hl) inc hl ld d,(hl) call print_hex ;print size ld c,1 ld b,7 ld de,mess3 call print_at pop hl pop de pop bc pop af ret .mess dz "Loading:" .mess1 dz "Address:" .mess2 Dz "Length:" .mess3 Dz "< Press Space >" ;"usefull" routines here ;return HL pointing to the start of a 2k secotor, with the relevant ;chunk paged in .find_sector push af ld a,(sector) ; sectors are 2k, banked pages 16k, so divide by 8 RRCA RRCA RRCA and &1f out (&ff),a ;page the rom ld a,(sector) ;now work out where the load data is on the page and &07 ;intra page offset, needs multiplying by 2k add a,a add a,a add a,a add a,&40 ;page offset in memory ld h,a ld l,0 pop af ret ;generic intrrupt cancelling routine. .ctc_off push bc push af ld b,2 .ctc_loop ld a,3 out (&8),a out (&9),a out (&a),a out (&b),a djnz ctc_loop pop af pop bc ret ; set all 4 sounc channels to silence, using the data table that follows .sound_off PUSH AF PUSH BC push hl Ld hl,sound_data ld b,12 .sound_loop ld a,(hl) OUT (6),A IN A,(3) nop ; at least 32 cycles needed between accesses nop ; 8 nops used to ensure the requirement is met nop nop nop nop nop nop inc hl DJNZ sound_loop pop hl pop bc pop af ret .sound_data db &80,00 ;Channel 0, frequency 0 db &9f ;channel 0, attenuation off db &A0,00 ;Channel 1, frequency 0 db &Bf ;channel 1, attenuation off db &C0,00 ;Channel 2, frequency 0 db &Df ;channel 2, attenuation off db &Ff ;channel 3, attenuation off db &E0,00 ;Channel 3, periodic noise N/512 .print_at ;c=across ;b=down ;de=data ,zero terminated PUSH HL PUSH DE PUSH BC PUSH AF PUSH DE LD L,B LD H,0 PUSH HL ADD HL,HL ;*2 ADD HL,HL ;*4 POP DE ADD HL,DE ;*5 ADD HL,HL ;*10 ADD HL,HL ;*20 ADD HL,HL ;*40 LD B,&5C ADD HL,BC LD A,L OUT (2),A LD A,H OUT (2),A POP DE .print_next LD A,(DE) CP 0 JR Z printed OUT (1),A INC DE JR print_next .printed POP AF POP BC POP DE POP HL RET ;recursive print value in DE as 4 digit hex, direct to screen with out(1) ;as assums screen pointers already set up by the print at routine above .print_hex ld a,d call print_a ld a,e .print_a push af srl a srl a srl a srl a call print_digit pop af .print_digit and &0f add a,&30 cp &3A jr c no_add add a,7 .no_add out (1),a ret .delay PUSH HL PUSH BC .delay_loop DEC HL LD A,H OR L JR NZ,delay_loop POP BC POP HL RET .beep_hl push AF push BC LD A,L AND &0F OR &80 OUT (6),A ; SEND TONE 1 + 4 BITS OF FREQUENCY NOP IN A,(3) LD A,L SRL A SRL A SRL A SRL A LD C,A LD A,H SLA A SLA A SLA A SLA A OR C AND &3F ; REMAINING 10 BITS OF FREQUENCY OUT (6),A NOP IN A,(3) LD A,&90 ; ATTENUATION 0DB TONE 1 NOP NOP NOP NOP OUT (6),A NOP IN A,(3) pop bc pop AF RET org &3500 ;first column of screen needs to be empty due to the overscan ;problems on an analog TVs game names are not included at ;this point, as they are placed in a directory by the 512k rom ;construction program meaning the 8k rom image does not have ;to be re-built if the games selection is changed. .page_text DS " MTX Rom based Games system V0.12" DS " [~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~]" DS " | The Following games were found: |" DS " \~~~~~~~~~~~~~~~~~~{~~~~~~~~~~~~~~~~~~^" DS " | 0 | 1 |" DS " | 2 | 3 |" DS " | 4 | 5 |" DS " | 6 | 7 |" DS " | 8 | 9 |" DS " | A | B |" DS " | C | D |" DS " | E | F |" DS " | G | H |" DS " | I | J |" DS " | K | L |" DS " | M | N |" DS " | O | P |" DS " | Q | R |" DS " | S | T |" DS " | U | V |" DS " | W | X |" DS " | Y | Z |" DS " _~~~~~~~~~~~~~~~~~~}~~~~~~~~~~~~~~~~~~`" DS " Press 0-9 or A-Z to load game " ORG &3900 .char_table DB &00,&00,&00,&00,&00,&00,&00,&00 DB &20,&20,&20,&20,&00,&00,&20,&00 DB &50,&50,&50,&00,&00,&00,&00,&00 DB &50,&50,&F8,&50,&F8,&50,&50,&00 DB &20,&78,&A0,&70,&28,&F0,&20,&00 DB &C8,&C8,&10,&20,&40,&98,&98,&00 DB &40,&A0,&A0,&40,&A8,&90,&68,&00 DB &60,&20,&40,&00,&00,&00,&00,&00 DB &10,&20,&40,&40,&40,&20,&10,&00 DB &40,&20,&10,&10,&10,&20,&40,&00 DB &00,&A8,&70,&F8,&70,&A8,&00,&00 DB &00,&20,&20,&F8,&20,&20,&00,&00 DB &00,&00,&00,&60,&60,&20,&40,&00 DB &00,&00,&00,&F8,&00,&00,&00,&00 DB &00,&00,&00,&00,&60,&60,&00,&00 DB &08,&08,&10,&20,&40,&80,&80,&00 DB &20,&50,&88,&88,&88,&50,&20,&00 DB &20,&60,&20,&20,&20,&20,&70,&00 DB &70,&88,&08,&10,&20,&40,&F8,&00 DB &F8,&10,&20,&10,&08,&88,&70,&00 DB &10,&30,&50,&90,&90,&F8,&10,&00 DB &F8,&80,&F0,&08,&08,&88,&70,&00 DB &70,&80,&80,&F0,&88,&88,&70,&00 DB &F8,&08,&08,&10,&20,&40,&40,&00 DB &70,&88,&88,&70,&88,&88,&70,&00 DB &70,&88,&88,&78,&08,&08,&70,&00 DB &00,&00,&60,&60,&00,&60,&60,&00 DB &00,&60,&60,&00,&60,&60,&20,&40 DB &10,&20,&40,&80,&40,&20,&10,&00 DB &00,&00,&F8,&00,&F8,&00,&00,&00 DB &40,&20,&10,&08,&10,&20,&40,&00 DB &70,&88,&08,&10,&20,&00,&20,&00 DB &70,&88,&B8,&A8,&B8,&80,&78,&00 DB &20,&50,&88,&88,&F8,&88,&88,&00 DB &F0,&88,&88,&F0,&88,&88,&F0,&00 DB &70,&88,&80,&80,&80,&88,&70,&00 DB &F0,&88,&88,&88,&88,&88,&F0,&00 DB &F8,&80,&80,&F0,&80,&80,&F8,&00 DB &F8,&80,&80,&F0,&80,&80,&80,&00 DB &70,&88,&80,&B8,&88,&88,&70,&00 DB &88,&88,&88,&F8,&88,&88,&88,&00 DB &70,&20,&20,&20,&20,&20,&70,&00 DB &78,&10,&10,&10,&90,&90,&60,&00 DB &88,&90,&A0,&C0,&A0,&90,&88,&00 DB &80,&80,&80,&80,&80,&80,&F8,&00 DB &88,&D8,&A8,&A8,&88,&88,&88,&00 DB &88,&88,&C8,&A8,&98,&88,&88,&00 DB &70,&88,&88,&88,&88,&88,&70,&00 DB &F0,&88,&88,&F0,&80,&80,&80,&00 DB &70,&88,&88,&88,&A8,&90,&68,&00 DB &F0,&88,&88,&F0,&A0,&90,&88,&00 DB &70,&88,&80,&70,&08,&88,&70,&00 DB &F8,&20,&20,&20,&20,&20,&20,&00 DB &88,&88,&88,&88,&88,&88,&70,&00 DB &88,&88,&88,&88,&50,&50,&20,&00 DB &88,&88,&88,&A8,&A8,&A8,&50,&00 DB &88,&88,&50,&20,&50,&88,&88,&00 DB &88,&88,&50,&20,&20,&20,&20,&00 DB &F8,&08,&10,&20,&40,&80,&F8,&00 DB &00,&00,&7C,&40,&40,&4C,&48,&48 DB &48,&48,&4c,&40,&40,&4c,&48,&48 DB &00,&00,&f8,&08,&08,&c8,&48,&48 DB &48,&48,&c8,&08,&08,&c8,&48,&48 DB &48,&48,&4c,&40,&40,&7c,&00,&00 DB &48,&48,&c8,&08,&08,&f8,&00,&00 DB &00,&00,&60,&10,&70,&90,&78,&00 DB &80,&80,&E0,&90,&90,&90,&E0,&00 DB &00,&00,&70,&80,&80,&80,&70,&00 DB &10,&10,&70,&90,&90,&90,&78,&00 DB &00,&00,&70,&88,&F8,&80,&78,&00 DB &30,&40,&40,&E0,&40,&40,&40,&00 DB &00,&00,&78,&88,&78,&08,&88,&70 DB &80,&80,&E0,&90,&90,&90,&90,&00 DB &20,&00,&60,&20,&20,&20,&70,&00 DB &20,&00,&60,&20,&20,&20,&20,&C0 DB &80,&80,&90,&A0,&C0,&A0,&90,&00 DB &60,&20,&20,&20,&20,&20,&70,&00 DB &00,&00,&50,&A8,&A8,&A8,&A8,&00 DB &00,&00,&E0,&90,&90,&90,&90,&00 DB &00,&00,&70,&88,&88,&88,&70,&00 DB &00,&00,&E0,&90,&90,&E0,&80,&80 DB &00,&00,&38,&48,&48,&38,&08,&08 DB &00,&00,&B0,&C0,&80,&80,&80,&00 DB &00,&00,&70,&80,&70,&08,&F0,&00 DB &40,&40,&F0,&40,&40,&40,&30,&00 DB &00,&00,&90,&90,&90,&90,&68,&00 DB &00,&00,&88,&88,&50,&20,&20,&00 DB &00,&00,&88,&88,&A8,&A8,&50,&00 DB &00,&00,&88,&50,&20,&50,&88,&00 DB &00,&00,&48,&48,&48,&38,&08,&70 DB &00,&00,&F8,&10,&20,&40,&F8,&00 DB &00,&00,&FC,&00,&00,&CC,&48,&48 DB &48,&48,&48,&48,&48,&48,&48,&48 DB &48,&48,&CC,&00,&00,&FC,&00,&00 DB &00,&00,&FC,&00,&00,&FC,&00,&00 DB &F8,&F8,&F8,&F8,&F8,&F8,&F8,&F8 .directory ;dieectory added in by the 512k rom builder program ;space for 63, 16 byte entries, 64 is always end of table/empty ;NB the menu currently only has room for 36 entries and for ;a 512k rom that's an average of 13k per game. ;Format ;starting sector, &FF should be present in the first unused entry ;14 bytes of name ;0 byte name terminator