; omni.asm - omnidirectional scroll (v0.1) ; Copyright (C) 2001 ag0ny@ag0ny.com ; Omnidirectional smooth scroll for MSX2 and higher. Uses screen horizontal ; and vertical adjust for smooth movement. The screen can move in any ; direction, at any speed. ; Heavily unoptimized! CPU usage is at around 25% on a 3.57Mhz Z80! ; TODO: - Mask screen borders using sprites ; - Fix the garbage in the first line of the screen ; - Use V9938 vertical scroll for vertical adjustment ; - Optimize code ; This code is freeware. You can use, modify or eat it freely. ; ag0ny@ag0ny.com, Sep 18, 2001 ; http://www.aamsx.org ; ***************** ; ** definitions ** ; ***************** ; external labels ; extrn map ; MAP.DAT: map data extrn patterns ; PATTERNS.DAT: patterns extrn patt.colors ; PATTERNS.DAT: patterns' colors extrn sinus extrn sinus.end ; double buffer ; BUFFER0 equ 10 BUFFER1 equ 11 BUFFER0.ADD equ 02800h BUFFER1.ADD equ 02c00h MAPWIDTH equ 128 ; system variables and functions ; BDOS equ 00005h ; BDOS entry point KEYINT equ 00038h ; keyboard interrupt STROUT equ 009h ; MSX-DOS: string output ; RAM copy of VDP registers ; vdpregs0 -> R#0...R#7 ; vdpresg1 -> R#8...R#23 ; vdpregs0 equ 0f3dfh vdpregs1 equ 0ffe7h-8 ; VDP ports ; vdpport0 equ 098h ; VRAM read/write vdpport1 equ 099h ; VDP registers read/write vdpport2 equ 09ah ; Palette registers write vdpport3 equ 09bh ; Indirect register write ; i8255 ports ; i8255porta equ 0a8h ; slot selection i8255portb equ 0a9h ; keyboard column input i8255portc equ 0aah ; leds, motor, cassette, kbd line i8255portd equ 0abh ; mode select for i8255 ports A,B,C ; ************ ; ** macros ** ; ************ ; vdp: send value in A to VDP register ; syntax: vdp reg# ; modifies: A ; note: **** INTERRUPTS MUST BE DISABLED **** ; (6 bytes/31 cycles) ; vdp macro reg out (vdpport1),a ld a,reg or 010000000b out (vdpport1),a endm ; rdvdp: read VDP status register ; syntax: rdvdp reg# ; modifies: A ; note: **** INTERRUPTS MUST BE DISABLED **** ; (13 bytes/62 cycles) ; rdvdp macro reg ld a,reg ld (vdpregs1+15),a vdp 15 ; set VDP status register to read in a,(vdpport1) endm ; selvram: select VRAM access from VDP (instead of expanded RAM) ; syntax: selvram ; modifies: A ; note: **** INTERRUPTS MUST BE DISABLED **** ; selvram macro ld a,(vdpregs1+45) and 010111111b ld (vdpregs1+45),a vdp 45 endm ; r2v8: copy RAM block to VRAM (up to 256 bytes) (low 64Kb) ; syntax: r2v8 , ; modifies: AF,HL,BC,DE ; note: **** INTERRUPTS MUST BE DISABLED **** ; (15 bytes/151+21*num cycles) ; r2v8 macro ram,vram,num ld de,vram ; 3/10 call setvramaddr_w ; 3/(17+104) ld hl,ram ; 3/10 ld bc,num*256+vdpport0 ; 3/10 otir ; 2/(21*num) endm ; r2v16: copy RAM block to VRAM (up to 65535 bytes) (low 64Kb) ; syntax: r2v16 , , ; modifies: AF,HL,DE,C ; note: **** INTERRUPTS MUST BE DISABLED **** ; (25 bytes/158+(49*num) cycles) ; r2v16 macro ram,vram,num ld de,vram ; 3/10 15/158 call setvramaddr_w ; 3/(17+104) ld de,num ; 3/10 ld hl,ram ; 3/10 ld c,vdpport0 ; 2/7 ; jp jumps here ; this loop repeated for each byte sent to VRAM ld a,(hl) ; 1/7 10/49 out (c),a ; 2/12 inc hl ; 1/6 dec de ; 1/6 ld a,d ; 1/4 or e ; 1/4 ; JP used for speed instead ; of JP (10 vs 12 cycles) jp nz,$-7 ; 3/10 endm ; set192lines: set 192 lines VDP display mode ; syntax: set192lines ; modifies: A ; note: **** INTERRUPTS MUST BE DISABLED **** ; (14 bytes/64 cycles) ; set192lines macro ld a,(vdpregs1+9) ; 3/13 and 001111111b ; 2/7 ld (vdpregs1+9),a ; 3/13 vdp 9 ; 6/31 endm ; set212lines: set 212 lines VDP display mode ; syntax: set212lines ; modifies: A ; note: **** INTERRUPTS MUST BE DISABLED **** ; (14 bytes/64 cycles) ; set212lines macro ld a,(vdpregs1+9) ; 3/13 or 010000000b ; 2/7 ld (vdpregs1+9),a ; 3/13 vdp 9 ; 6/31 endm ; spriteson: turn ON V99x8 sprites display ; syntax: spriteson ; modifies: A ; note: **** INTERRUPTS MUST BE DISABLED **** ; spriteson macro ld a,(vdpregs1+8) and 011111101b ld (vdpregs1+8),a vdp 8 endm ; spritesoff: turn OFF V99x8 sprites display ; syntax: spritesoff ; modifies: A ; note: **** INTERRUPTS MUST BE DISABLED **** ; spritesoff macro ld a,(vdpregs1+8) or 000000010b ld (vdpregs1+8),a vdp 8 endm ; setintline: set line number for vertical scanning interrupt ; syntax: setintline line# ; modifies: AF,HL ; note: **** INTERRUPTS MUST BE DISABLED **** ; note: The status if VDP register #23 is read and the interrupt ; line is set accordingly, so the interrupt line specifies ; the interrupt line ON THE PHYSICAL SCREEN, not on the ; VDP space. ; (15 bytes/68 bytes) ; setintline macro line ld hl,vdpregs1+23 ; 3/10 ld a,line ; 2/7 add a,(hl) ; 1/7 ld (vdpregs1+19),a ; 3/13 vdp 19 ; 6/31 endm ; vblankon: turn ON vblank interrupts ; syntax: vblankon ; modifies: A ; note: **** INTERRUPTS MUST BE DISABLED **** ; vblankon macro ld a,(vdpregs0+1) or 000100000b ld (vdpregs0+1),a vdp 1 endm ; vblankoff: turn OFF vblank interrupts ; syntax: vblankoff ; modifies: A ; note: **** INTERRUPTS MUST BE DISABLED **** ; vblankoff macro ld a,(vdpregs0+1) and 011011111b ld (vdpregs0+1),a vdp 1 endm ; lineinton: turn ON line interrupts ; syntax: lineinton ; modifies: A ; note: **** INTERRUPTS MUST BE DISABLED **** ; lineinton macro ld a,(vdpregs0+0) or 000010000b ld (vdpregs0+0),a vdp 0 endm ; lineintoff: turn OFF line interrupts ; syntax: lineintoff ; modifies: A ; note: **** INTERRUPTS MUST BE DISABLED **** ; lineintoff macro ld a,(vdpregs0+0) and 011101111b ld (vdpregs0+0),a vdp 0 endm ; checkkbd: ckeck keyboard line ; syntax: checkkbd ; modifies: AF ; (8 bytes/41 cycles) ; checkkbd macro kbdline in a,(i8255portc) and 011110000b ; upper 4 bits contain info to preserve or kbdline out (i8255portc),a in a,(i8255portb) endm ; fillvram: fill VRAM with a value ; syntax: fillvram
,, ; modifies: AF,BC,DE ; note: **** INTERRUPTS MUST BE DISABLED **** ; fillvram macro address,count,value ld de,address call setvramaddr_w ld de,count ld bc,value*256 + vdpport0 out (c),b ; jp jumps here dec de ld a,d or e jp nz,$-5 endm ; sprsize16: set sprites size to 16x16 pixels ; syntax: sprsize16 ; modifies: AF ; note: **** INTERRUPTS MUST BE DISABLED **** ; sprsize16 macro ld a,(vdpregs0+1) or 000000010b ld (vdpregs0+1),a vdp 1 endm ; pushstd: push standard set of registers (not alternate) ; syntax: pushstd ; modifies: SP ; note: alternate registers are not PUSHed ; (4 bytes/44 cycles) ; pushstd macro push hl ; 1/11 push bc ; 1/11 push de ; 1/11 push af ; 1/11 endm ; popstd: pop standard set of registers (not alternate) ; syntax: popstd ; modifies: SP ; note: alternate registers are not POPed ; (4 bytes/40 cycles) ; popstd macro pop af ; 1/10 pop de ; 1/10 pop bc ; 1/10 pop hl ; 1/10 endm ; ax32: multiply A times 32, leave result in HL ; syntax: ax32 ; modifies: AF,HL,DE ; (16 bytes/62 cycles) ; ax32 macro ld e,a ; 1/4 ; high part and 011111000b ; 2/7 6/23 rrca ; 1/4 rrca ; 1/4 rrca ; 1/4 ld h,a ; 1/4 ; low part ld a,e ; 1/4 9/35 and 000000111b ; 2/7 rlca ; 1/4 rlca ; 1/4 rlca ; 1/4 rlca ; 1/4 rlca ; 1/4 ld l,a ; 1/4 endm ; hlx16: multiply HL times 16, leave result in HL ; syntax: hlx16 ; modifies: AF,DE,HL ; (32 bytes/105 cycles) ; hlx16 macro ; low part ld a,l ; 1/4 and 000001111b ; 2/7 rrca ; 1/4 rrca ; 1/4 rrca ; 1/4 rrca ; 1/4 ; low value in e ld e,a ; 1/4 ; middle part ld a,l ; 1/4 and 011110000b ; 2/7 rrca ; 1/4 rrca ; 1/4 rrca ; 1/4 rrca ; 1/4 ; temp value in d ld d,a ; 1/4 ld a,h ; 1/4 and 000001111b ; 2/7 rrca ; 1/4 rrca ; 1/4 rrca ; 1/4 rrca ; 1/4 or d ; 1/4 ld h,a ; 1/4 ld l,e ; 1/4 endm ; hldiv8: divide HL by 8, leave result in HL ; syntax: hldiv16 ; modifies: AF,HL,DE ; () ; hldiv8 macro ; high part ld a,h and 011111000b rrca rrca rrca ; high part stored in D ld d,a ; middle part ld a,h and 000000111b rrca rrca rrca ;temp value stored in E ld e,a ; lower part ld a,l and 011111000b rrca rrca rrca or e ld h,d ld l,a endm ; hlx128: multiply HL times 128, leave result in HL ; syntax: hlx128 ; modifies: AF,DE,HL ; (32 bytes/105 cycles) ; hlx128 macro ; lower part ld a,l and 000000001b rrca ; lower value in E ld e,a ; middle part ld a,l and 011111110b rrca ; lower value in l ld l,e ld e,a ld a,h and 000000001b rrca or e ld h,a endm ; ************************* ; ** jump to entry point ** ; ************************* jp start ; *************** ; ** functions ** ; *************** ; install our own interrupt code ; input: none ; output: none ; modifies: HL ; note: the old jump address is stored in (oldint) ; note: **** INTERRUPTS MUST BE DISABLED **** ; freezesys: ld hl,(KEYINT+1) ld (oldint),hl ; install my own interrupt code ld hl,int200 ld (KEYINT+1),hl ; save SET ADJUST state ld a,(vdpregs1+18) ld (oldadjust),a ret ; restore the original interrupt pointer handler and SET ADJUST state ; note: **** INTERRUPTS MUST BE DISABLED **** ; restoresys: ld hl,(oldint) ld (KEYINT+1),hl ld hl,(oldstack) ; saved in the main program ld sp,hl lineintoff vblankon ; clear pending interrupts rdvdp 1 rdvdp 0 ld a,(oldadjust) ; saved by freezesys ld (vdpregs1+18),a vdp 18 ; set SCREE 0 ld ix,0005fh ld iy,00000h xor a call 0001ch ei ; original MSX-DOS(2) environment restored ld c,STROUT ld de,infomsg jp BDOS ; print exit msg and exit ; set GRAPHIC 3 mode ; input: none ; output: none ; modifies: A ; note: **** INTERRUPTS MUST BE DISABLED **** ; note: this routine sets a special GRAPHIC 3 mode: only one tileset ; for all the screen (as opposed to the three tilesets in the ; 'standard' GRAPHIC 3 mode) ; graphic3: ld a,(vdpregs0+0) and 011110001b or 000000100b ld (vdpregs0+0),a ; R#0 -> xxxx010xb vdp 0 ld a,(vdpregs0+1) and 011100111b ld (vdpregs0+1),a ; R#1 -> xxx00xxxb vdp 1 ; pattern name table in 02800h ld a,10 ld (vdpregs0+2),a vdp 2 ; pattern color table in 02000h ld a,159 ld (vdpregs0+3),a vdp 3 xor a ld (vdpregs1+10),a vdp 10 ; pattern generator table at 00000h xor a ld (vdpregs0+4),a vdp 4 ; sprite name table at 01000h ld a,2 ld (vdpregs0+6),a vdp 6 ; sprite attribute table at 00c00h ; and sprite color table at 00a00h ld a,28 ld (vdpregs0+5),a vdp 5 xor a ld (vdpregs1+11),a vdp 11 ret ; set VRAM address for read/write (low 64Kb) ; input: DE = VRAM address (bits A0-A15 ; modifies: A ; note: **** INTERRUPTS MUST BE DISABLED **** ; (22 bytes/104 cycles) ; setvramaddr_w: ld a,d ; 1/4 5/19 rlca ; 1/4 rlca ; 1/4 and 000000011b ; 2/7 ; bits A16-A14 vdp 14 ; 6/31 ld a,e ; 1/4 ; bist A7-A0 (low 16Kb) out (vdpport1),a ; 2/11 ld a,d ; 1/4 5/18 and 000111111b ; 2/7 or 001000000b ; 2/7 ; bits A13-A8, bit 6=1 (w) out (vdpport1),a ; 2/11 ret ; 1/10 ; ******************** ; ** interrupt code ** ; ******************** ; interrupt code for scanline 0 ; keyboard control (ESC key), move display window ; int000: pushstd in a,(vdpport1) ; 2/11 ld hl,int012 ; 3/10 6/26 ld (KEYINT+1),hl ; 3/16 setintline 12 ; 15/68 ; exit if ESCape pressed checkkbd 7 ; 8/41 13/58 and 000000100b ; 2/7 jp z,restoresys ; 3/10 ld hl,(sinusptr) ld de,map.x ld bc,4 ldir ld (sinusptr),hl ld hl,sinus.end ld a,(sinusptr) cp l jp nz,int000.exit ld a,(sinusptr+1) cp h jp nz,int000.exit ld hl,sinus ld (sinusptr),hl int000.exit: popstd ei ; 1/4 ret ; 1/10 ; interrupt code for scanline 12 ; prepare next display buffer ; int012: pushstd in a,(vdpport1) ; 2/11 ld hl,int200 ; 3/10 ld (KEYINT+1),hl ; 3/16 setintline 200 ; 15/68 ; set VRAM address of passive buffer ld a,(scrbuffer) cp BUFFER0 jp nz,int012.1 ld a,BUFFER1 ld (scrbuffer),a ld de,BUFFER1.ADD call setvramaddr_w jp int012.2 int012.1: ld a,BUFFER0 ld (scrbuffer),a ld de,BUFFER0.ADD call setvramaddr_w int012.2: ; get start address in the map ld hl,(map.y) hldiv8 hlx128 ld b,h ld c,l ld hl,(map.x) push bc hldiv8 pop bc add hl,bc ld de,map add hl,de ; HL points to the start ; of the area to copy ; for adding faster later ld de,MAPWIDTH-32 ; copy 32 bytes [1/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [2/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [3/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [4/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [5/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [6/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [7/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [8/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [9/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [10/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [11/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [12/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [13/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [14/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [15/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [16/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [17/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [18/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [19/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [20/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [21/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [22/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [23/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [24/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [25/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [26/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [27/28] ld bc,32*256+vdpport0 otir add hl,de ; copy 32 bytes [28/28] ld bc,32*256+vdpport0 otir int012.exit: popstd ei ret ; interrupt code for scanline 200 ; overscan control [1/2] ; (43 bytes/225 cycles) (0.97 scanlines) ; int200: push hl ; 1/11 push af ; 1/11 in a,(vdpport1) ; 2/11 ld hl,int224 ; 3/10 ld (KEYINT+1),hl ; 3/16 set192lines ; 14/64 setintline 224 ; 15/68 pop af ; 1/10 pop hl ; 1/10 ei ; 1/4 ret ; 1/10 ; interrupt code for scanline 224 ; overscan control [2/2], screen adjustment, switch view buffer ; int224: pushstd ; 4/44 41/213 in a,(vdpport1) ; 2/11 ld hl,int000 ; 3/10 ld (KEYINT+1),hl ; 3/16 set212lines ; 14/64 setintline 0 ; 15/68 ; switch screen buffer ld a,(scrbuffer) vdp 2 ; set adjust ld a,(map.x) and 000000111b ld e,a ld a,(map.y) and 000000111b rlca rlca rlca rlca or e vdp 18 int224.exit: popstd ; 4/40 6/54 ei ; 1/4 ret ; 1/10 ; initalize interrupts ; init: vblankoff lineinton setintline 0 set212lines spriteson sprsize16 fillvram 0,65535,0 r2v8 patterns,00000h,8*16 ; pattern data r2v8 patt.colors,02000h,8*16 ; pattern color data r2v16 map,02800h,1024 ; screen data ret ; ************************* ; ** PROGRAM STARTS HERE ** ; ************************* start: di ld (oldstack),sp call graphic3 call freezesys call init ; color ,,0 xor a ld (vdpregs0+7),a vdp 7 ; clear pending interrupts rdvdp 1 ei loop: halt jp loop ; ****************** ; ** data section ** ; ****************** infomsg: db 'omni.asm - omnidirectional scroll (v0.1)',10,13 db 'Copyright (C) 2001 ag0ny@ag0ny.com',10,13 db 10,13 db 'This code is freeware. You can use, modify or eat it freely.',10,13 db '$' ; *************** ; ** variables ** ; *************** ; system state saved to restore it on exit oldstack: defs 2 ; original stack pointer oldint: defs 2 ; original interrupt vector oldadjust: defs 2 ; original SET ADJUST state ; double buffer active page scrbuffer: db BUFFER1 ; x and y positions in the map map.x: ds 2 map.y: ds 2 sinusptr: dw sinus