; Draw a moving Tony in the border... some of this code must run below $8000 or he'll jitter. ; ; Code written by Simon Brattel (Tony) and Rob Crawford (Moving him) ; ; We were both in a rush when we wrote this, so this code is a mess. Sorry. ; ; This code is for the 48K Spectrum, and can be used to demonstrate a number of things. ; ; First, click "Assemble then Emulate" and see that Zeus's emulator can now handle border timing ; ; Next, while the emulator is still running, click back onto this editor tab and use a right-click of the mouse ; to select "Toggle Profile", then scroll up and down in the source watching the timings change in real-time ; in the gutter. ; ; Use a mouse to click on the blue dots to place breakpoints and use F7/F8 to step through the source. ; To keep the size down we start near the top of contended RAM emulate_spectrum "48K","ULA+" ; Set the right model and enable ULA+ org $7F00 ; profile = true ; Start di ; ld sp,#0 ; ; Set the ULA+ Palette ld hl,TestPalette ; This one call Set_Palette_HL ; ; Show the screen call ShowARuler ; Show the screen StartTony LD HL,IntVectors ; LD DE,IntVectors+1 ; LD BC,$0100 ; LD (HL),high DrawTony ; LDIR ; LD A,high IntVectors ; LD I,A ; TimingLoop ld hl,TimingTable ; Behaviour table ld (pTimingTable),hl ; IM 2 ; TonyLp EI ; ld sp,#0 ; ld ix,(pTimingTable) ; ld a,(ix) ; Get the pause time inc ix ; ld (pTimingTable),ix ; ; End of table? or a ; jr z TimingLoop ; ld iyl,a ; hl -> iy ld iyh,33 ; HALT ; Wait for an interrupt ; When we get here we're done ; See if SPACE or RETURN are pressed ld a,0 ld a,1 ld a,2 ld a,3 ld a,4 ld a,5 ld a,6 ld a,7 ld a,8 ld a,9 ld a,10 ld a,11 ld a,12 ld a,13 LD A,$BF ; IN A,($FE) ; BIT 0,A ; JR Z L8F3D ; LD A,$7F ; IN A,($FE) ; BIT 0,A ; JR NZ TonyLp ; ; One of the keys DI ; XOR A ; LD I,A ; JP Start ; ; t'other one L8F3D DI ; XOR A ; LD I,A ; JP Start ; ; Draw a ruler for timing checks if false ShowARuler ld hl,$4000 ; call DCL_Block ; ld hl,$4800 ; call DCL_Block ; ld hl,$5000 ; DCL_Block ld b,8 DCL_Lp1 push bc ; call DrawCharLine ; ; ld de,$20 ; add hl,de ; pop bc ; djnz DCL_Lp1 ; ret ; DrawCharLine push hl ; ld a,$AA ; call DrawLine ; inc h ; ld a,$55 ; call DrawLine ; inc h ; ld a,$AA ; call DrawLine ; inc h ; ld a,$55 ; call DrawLine ; inc h ; ld a,$AA ; call DrawLine ; inc h ; ld a,$55 ; call DrawLine ; inc h ; ld a,$AA ; call DrawLine ; inc h ; ld a,$55 ; call DrawLine ; pop hl ; ret ; DrawLine push hl ; ld b,$20 ; DL_Lp1 ld (hl),a ; inc l ; djnz DL_Lp1 ; pop hl ; ret ; else ShowARuler ld hl,$4000 ; Clear the screen ld de,$4001 ; ld bc,$1800 ; ld (hl),$00 ; ldir ; ld hl,$4000 ; Pixels -> dots ld de,$4001 ; ld bc,$1F ; ld (hl),$AA ; ldir ; ld hl,$5800 ; Attr ld de,$5801 ; ld bc,$2FF ; ld (hl),$0f ; ldir ; ld hl,$4100 ; Pixels -> dots ld de,$4102 ; ld bc,$1e ; ld (hl),$80 ; ldir ; ld hl,$4200 ; Pixels -> dots ld de,$4204 ; ld bc,$1c ; ld (hl),$80 ; ldir ; ld hl,$4300 ; Pixels -> dots ld de,$4308 ; ld bc,$18 ; ld (hl),$80 ; ldir ; ld hl,$4400 ; Pixels -> dots ld de,$4410 ; ld bc,$10 ; ld (hl),$80 ; ldir ; ret ; Done Set_Palette_HL ld bc,$BF3B ; register select $BF3B ld a,%01000000 ; mode group out (c),a ; 64 -> $BF3B ld a,1 ; 1 -> $FF3B turn palette mode on ld b,$FF ; out (c),a ; xor a ; first register SetRegister_Lp ld b,$BF ; choose register port out (c),a ; Reg -> $BF3B ex af,af' ; save current register select ld a,(hl) ; get data ld b,$FF ; choose data port out (c),a ; Reg -> $FF3B ex af,af' ; restore current register inc hl ; Next inc a ; cp 64 ; Done? jr nz SetRegister_Lp ; Loop ret ; return ; this is where the actual data is stored. The following is an example palette. TestPalette db $00, $02, $18, $1B, $C0, $C3, $D8, $DB ; INK db $00, $24, $48, $6C, $90, $B4, $D8, $FC ; PAPER ; db $00, $20, $40, $60, $80, $A0, $C0, $E0 ; PAPER ; db $00, $02, $18, $1B, $C0, $C3, $D8, $DB ; PAPER db $00, $03, $1C, $1F, $E0, $E3, $FC, $FF ; INK + BRIGHT db $00, $03, $1C, $1F, $E0, $E3, $FC, $FF ; PAPER + BRIGHT db $DB, $D8, $C3, $C0, $1B, $18, $02, $00 ; INK + FLASH db $DB, $D8, $C3, $C0, $1B, $18, $02, $00 ; PAPER + FLASH db $FF, $FC, $E3, $E0, $1F, $1C, $03, $00 ; INK + BRIGHT + FLASH db $FF, $FC, $E3, $E0, $1F, $1C, $03, $00 ; PAPER + BRIGHT + FLASH endif ; The code above here can run above $8000 - the code below can't without flickering ; I suspect only the HALT needs to be below $8000, but never mind org $8000 ; Right, we need to align this to a funny address where the ; top and bottom bytes are the same - like $F0F0 or $F1F1 ; It's because we've no idea which bytes of the interrupt table actually get read ; so we have to make them all the same value and that way we don't care what ; the value actually is. ; This space could be used. IRQ_Handler equ ( high * ) << 8 + ( high * ); address = abab Wasted_Below equ IRQ_Handler - * ; Spare room ; Check we haven't moved past this... if * > IRQ_Handler zeuserror "The setup code is overwriting the IRQ handler - move something!" endif org IRQ_Handler ; Place it here ; This is the code that actually draws Tony DrawTony LD B,$A2 ; Wait a long time for a visible bit DelayToTop PUSH IX ; POP IX ; DJNZ DelayToTop ; loop 8 ; Fine-tune the position nop ; lend ; ; Draw him in sections where each section ; can be as many lines high as you like ; but each line is the same ; Tony must be symmetrical and 7pixels wide ; Colours are set in registers E,D,L,H,L,D,E ; Delay a number of lines (iyl) of black LD HL,($0000) ; LD HL,$0000 ; LD C,$FE ; ld B,iyl ; N lines LD DE,$0000 ; LD HL,$0000 ; LD A,$00 ; CALL DrawTonysPixels ; NOP ; 4 t-states LD HL,($0000) ; LD HL,$0000 ; LD BC,$02FE ; 2 lines LD DE,$0000 ; LD HL,$0700 ; LD A,$00 ; CALL DrawTonysPixels ; ld a,1 ; 6 t-states LD HL,($0000) ; LD HL,$0000 ; LD BC,$01FE ; 1 line LD DE,$0000 ; LD HL,$0707 ; LD A,$00 ; CALL DrawTonysPixels ; ld a,1 ; 6 t-states LD HL,($0000) ; LD HL,$0000 ; LD BC,$03FE ; 3 lines LD DE,$0700 ; LD HL,$0101 ; LD A,$00 ; CALL DrawTonysPixels ; nop ; 4 t-states (WHY ISN'T THIS 6???) LD HL,($0000) ; LD HL,$0000 ; LD BC,$05FE ; 5 lines LD DE,$0700 ; LD HL,$0601 ; LD A,$00 ; CALL DrawTonysPixels ; ld a,1 ; 6 t-states LD HL,($0000) ; LD HL,$0000 ; LD BC,$03FE ; 3 lines LD DE,$0700 ; LD HL,$0607 ; LD A,$00 ; CALL DrawTonysPixels ; ld a,1 ; 6 t-states LD HL,($0000) ; LD HL,$0000 ; LD BC,$02FE ; 2 lines LD DE,$0707 ; LD HL,$0600 ; LD A,$00 ; CALL DrawTonysPixels ; NOP ; 4 t-states (WHY THE FUCK ISN'T THIS 6???) LD HL,($0000) ; LD HL,$0000 ; LD BC,$03FE ; 3 lines LD DE,$0707 ; LD HL,$0000 ; LD A,$00 ; CALL DrawTonysPixels ; ; Done, 'ees drawn. RETI ; ; This loop takes a bit less than exactly one scan-line of time to execute ; Summat weird is happening DrawTonyLp LD IX,($0000) ; Sync back up so this loop LD IX,($0000) ; takes one scan-line exactly LD IX,($0000) ; LD IX,($0000) ; nop ; nop ; nop ; nop ; DrawTonysPixels dec iyh ; See if we've drawn enough lines jp m ExitTony ; Done ; Draw the "pixels" OUT (C),E ; LHS OUT (C),D ; OUT (C),L ; OUT (C),H ; MIDDLE OUT (C),L ; OUT (C),D ; OUT (C),E ; RHS OUT (C),A ; black border DEC B ; Faster than a DJNZ + deterministic JP NZ DrawTonyLp ; ExitTony RET ; ; Define the behaviour - a list of height values TimingTable equ * ; Pause mPos(0,10) ; Arise mPos(1,5) mPos(2,5) mPos(3,4) mPos(4,3) mPos(5,2) mPos(6,1) mPos(7,50) mPos(6,6) mPos(5,5) mPos(4,4) mPos(3,3) mPos(2,2) mPos(1,1) mPos(0,30) mPos(1,5) mPos(2,5) mPos(3,4) mPos(4,4) mPos(5,3) mPos(6,3) mPos(7,2) mPos(8,2) mPos(9,2) mPos(10,1) mPos(11,1) mPos(12,1) mPos(13,1) mPos(14,1) mPos(15,1) mPos(16,1) mPos(18,1) mPos(20,1) mPos(22,1) mPos(24,1) mPos(26,1) mPos(27,1) mPos(28,2) mPos(29,3) mPos(28,2) mPos(27,255) mPos(26,5) mPos(25,4) mPos(24,3) mPos(23,2) mPos(22,1) mPos(21,1) mPos(20,1) mPos(19,1) mPos(18,1) mPos(17,1) mPos(16,1) mPos(15,1) mPos(14,1) mPos(13,1) mPos(12,1) mPos(11,1) mPos(10,1) mPos(9,1) mPos(8,1) mPos(7,1) mPos(6,1) mPos(5,1) mPos(4,1) mPos(3,1) mPos(2,1) mPos(1,1) db 0 Ye_End equ * ; Just set this height for a while mPos macro(Height,Time) loop Time db 33-Height lend mend ; Space to the end of this page can be used. zeusprint Wasted_Below," bytes wasted below handler ",IntVectors-*," bytes wasted after it" ; The interrupt vector table align 256 ; IntVectors ds $101 ; ; Vars pTimingTable dw 0 ; Points at the table ; Generate the output files output_para $5B00, Ye_End-$5B00 ; Never you mind ;) ; Since we want a fancy loader we need to load a loading screen ; If this file doesn't exist, just comment it out here and assemble/emulate ; then use the "save screen" button on the emulator tab to generate a file "ruler.scr" import_bin "ruler.scr",$4000 ; Load a loading screen ; Now, generate a tzx file that uses the loader output_tzx"tony.tzx","","",Start,Ye_End-Start,1,Start,$0101114 ; A tzx file using the loader ; And also generate one that does not output_tzx"tony_no.tzx","","",Start,Ye_End-Start,2,Start ; A tzx file not using the loader ; And also generate a SZX file (which also tells the emulator where to start running) output_szx"tony.szx",$0000,Start ; The szx file