Jump to content


Photo

Free space and code optimisation in Manic Miner


  • Please log in to reply
22 replies to this topic

#11 Norman Sword

Norman Sword

    Advanced Member

  • Member
  • PipPipPip
  • 223 posts

Posted 28 November 2018 - 05:58 PM

Modification of Manic miner tune. The above routine was an exercise in compacting the code. Code that was never used. 

The original modification from.

 

SUB 8
RRCA

RRCA

RRCA

CPL

OR #E0

 

TO 

 

RRCA

RRCA

RRCA

NEG

OR #E0 --- my code has OR L where L=#e0

 

This modification works fine and gives exactly the correct results. And does not need any additional code.

my error was just swapping the routine around, whilst still thinking about the CPL instruction used in the first routine

NEG
RRCA
RRCA
RRCA
OR #E0 --- my code has OR L where L=#e0

Moving the NEG from after the RRCA instructions to before the RRCA instruction changed what it did.

Simpler to revert back to the sequence

SUB 8
CPL

RRCA

RRCA

RRCA

AND 31
OR L
 

Since the above code has been tested. I know it works for all values, in exactly the same way as the original.

 

Works 100%

The routine is still 70 plus bytes smaller than the original.

 

Addendum. 

The AND 31 instruction was added before the code change to compacted data. The reason for its inclusion was to permit the moving of the piano graphics to any screen line, and not be forced via the old code to one of 3  fixed screen positions. 

2nd addendum:
The code I reference is the original layout. Unfortunately for me, I have edited so much code and done so many changes in the code, that I would need to keep going backward and forward checking items for reference.

The code listed in the Manic Miner music routine, is a derivative of the original code, which because of dual usage no longer OR's in the screen address,  It now adds it. This slight change is what the next post #12 is referring to as the difference in opcode usage. 

The code I listed is relocatable and not fixed to any memory address. In the context of being placed back into Manic Miner, it can again have the restrictions imposed on it. The tune data and the two data tables used for data expansion, can be fixed into a page, along with the fixed screen keyboard reference. This can remove several opcodes and make it even smaller. (something I will not do). I prefer the options of being able to have code where ever I place it


 


Edited by Norman Sword, 28 November 2018 - 10:07 PM.


#12 IRF

IRF

    Advanced Member

  • Contributor
  • 4,213 posts

Posted 28 November 2018 - 06:53 PM

my code has OR L where L=#e0


Isn't it ADD A, L (to facilitate the multiple use of the subroutine, which is also used for decompressing the tune data)?

Edited by IRF, 28 November 2018 - 07:59 PM.


#13 IRF

IRF

    Advanced Member

  • Contributor
  • 4,213 posts

Posted 29 November 2018 - 08:00 AM

The code I listed is relocatable and not fixed to any memory address. In the context of being placed back into Manic Miner, it can again have the restrictions imposed on it. The tune data and the two data tables used for data expansion, can be fixed into a page, along with the fixed screen keyboard reference. This can remove several opcodes and make it even smaller. (something I will not do). I prefer the options of being able to have code where ever I place it


You previously mentioned the trade-off between size versus speed when optimising code. The above quote highlights a third element to throw into the balance - flexibility (relocatability).

#14 IRF

IRF

    Advanced Member

  • Contributor
  • 4,213 posts

Posted 11 December 2018 - 12:28 AM

This doesn't relate to code optimisation, but it's an observation about a bug/glitch/peculiarity in the original Manic Miner.

 

When the title screen tune is playing, there is a pause towards the end (enacted by having a pair of 'zero notes', at #8541 and #8542).  You might expect that none of the piano keys are highlighted at that point, since no notes are being played, but in fact the left-most key of the piano is highlighted (in cyan).

 

I know that the positioning of the coloured keys isn't actually representative of the notes that would be played on a real piano, but the above seems particularly incongruous.

 

I've been playing around with the MM title tune routine, and I've managed fix the 'glitch' mentioned above.  If the routine encounters a 'zero note', then that note is now bypassed.  i.e. no piano key is highlighted, but also no corresponding note is played.

 

This also allows you to have 'pure tones' in the title tune.  (In the original MM, attempting to play a pair of notes with identical pitch values causes no note at all to be played for the allocated duration of that pair of notes.  So when a 'single note' is played - as opposed to a chord of two notes of different pitch - this is achieved by assigning values to a pair of notes that differ by 1. e.g. #846F and #8470 hold #80 and #81 respectively.  The slight difference in pitch gives a 'harpsichord' quality to such notes.)

 

 

In the attached test file, there is a mixture of pure tones (where the original tune data held a pair of notes differing by 1, one of the note bytes now holds a zero value, whilst the other note is unchanged (e.g. the first note pair at #846F and #8470 hold values #80 and #00) and 'chords' (all pairs of notes in the original tune data that differed significantly from each other have been left with their original values. e.g. the last pair of notes in the tune #8589 and #858A hold values #80 and #CB, as per the original MM).  And of course the 'silent note' near the end of the tune (#8541 and #8542 both hold zero values) no longer illuminates the left-most (or indeed any) of the piano keys.

 

The resulting tune sounds smoother and less harsh.  (Although admittedly the harshness in the original adds to the 'manic' quality, in keeping with the theme of the game: 'Manic Miner'.  So this test file was created purely for experimental purposes, and to further my own knowledge of how this particular tune-playing routine operates.)

Attached Files


Edited by IRF, 12 December 2018 - 05:45 PM.


#15 Spider

Spider

    DEC (HL)

  • Administrator
  • 3,903 posts

Posted 11 December 2018 - 10:41 AM

Does sound quite uniquely different. :) It does make the 'harsh' tones stand out more though but that's expected (not a negative thing) just they are more apparent now.


  • IRF likes this
Changing order to chaos since 1984

#16 IRF

IRF

    Advanced Member

  • Contributor
  • 4,213 posts

Posted 11 December 2018 - 12:33 PM

In theory, on those occasions when two notes are played simultaneously, they are two notes in a harmonic series, so they aren't actually 'harsh' as such (compared with playing two notes which are slightly different in pitch to each other, which can sound quite disharmonic especially at higher pitches).

 

EDIT: That's the theory anyway!  All the pair of notes in the original definitely sound harsher than they do if you separate each half of the pair out and play them separately (by NOPping out one or other of the two XOR #18 commands from the title tune routine).


Edited by IRF, 13 December 2018 - 12:26 AM.


#17 IRF

IRF

    Advanced Member

  • Contributor
  • 4,213 posts

Posted 12 December 2018 - 06:04 PM

A few posts back, I uploaded an experimental file ('File 0') in which the MM title tune routine and data had been modified, so that pairs of notes which were previously close in value (differing by 1) now play a pure tone, giving the tune a 'smoother' quality than in the original game. (This was achieved by replacing one of each near-matching notes with the value zero, and then hacking the title tune routine so that such 'zero notes' get bypassed instead of being played.)

I then thought I would see what would happen if I experimented with the reverse approach. I have attached a selection of further test files to this post:

For comparison, I have attached 'File 1' which is a copy of the original Manic Miner with no changes to the game file except that it has been renamed for consistency with the sequence that follows. The '1' refers to the fact that many of the pairs of notes are assigned pitch values which differ by #01.

In 'File 2', those notes which previously differed by #01 are separated in pitch by a difference of #02.

In 'File 3', the 'close note pairs' differ by a value of #04; 'File 4' by #08, and in 'File 5' the pairs of notes which originally almost matched each other in pitch now differ by #10 (16 in decimal).

So the 'close notes' are spread apart by increasingly large amounts as you progress through the files - I hope that makes sense? For example, in 'File 5' the values stored at addresses #846F and #8470 (the first pair of notes in the title tune) are #80 and #90, instead of #80 and #81 at those same addresses in the original game.

The net result is that the quality of the tune sounds increasingly harsher and more dischordant as you work your way through the files, which are thus named accordingly.

Er, enjoy!

Attached Files


Edited by IRF, 13 December 2018 - 07:52 AM.


#18 IRF

IRF

    Advanced Member

  • Contributor
  • 4,213 posts

Posted 17 December 2018 - 12:21 PM

Number 5 is aptly named!



#19 Spider

Spider

    DEC (HL)

  • Administrator
  • 3,903 posts

Posted 17 December 2018 - 01:10 PM

Number 5 is aptly named!

Indeed! :D


  • IRF likes this
Changing order to chaos since 1984

#20 Norman Sword

Norman Sword

    Advanced Member

  • Member
  • PipPipPip
  • 223 posts

Posted 19 January 2019 - 10:38 AM

; MANIC MINER PIANO ROUTINE for the ZX spectrum

; The only reason for this updated routine is the change in lighting the keyboard on silent notes
; if two notes are defined, and with the same values the keyboard will still light up. For silence use 0,0

 

 

;the complete title music routine for Manic Miner
manic_tune:
      LD DE,L33902                ; music data
manic_loop:
; is the music finished
      LD A,(DE)                       ; length and pitch shift
      OR A                              ; terminate on zero
      RET Z
; else find out the note length
      AND 3                            ; a third bit is available if needed
      LD HL,TIME_SHIFT
      CALL STEP_INDEX   
      LD C,A                           ; NOTE LENGTH
; and extract the pitch shift
    LD A,(DE)                        ; THE note shift INDEX
    LD HL,NOTE_SHIFT
    CALL STEP1_INDEX
    LD B,A                            ; THE NOTE SHIFT
    INC DE
; extract the first note and light keyboard
    LD A,(DE)                      ; (NOTE 1)
    INC DE
    PUSH DE                      ; the data pointer saved - we want to use the registers
    LD D,A                          ; (NOTE 1)
;This must calculate at least one value for HL
    CALL NOTE_PLACE    ;  we want HL the pointer
    PUSH HL
;calculate 2nd  NOTE
    LD A,D                          ; NOTE 1
    ADD A,B                       ; NOTE 1+NOTE SHIFT = NOTE 2
    LD E,A                          ; NOTE 2
; here we light the keyboard only if this NOTE HAS A VALUE
    INC D
    DEC D

    JR Z,same_notes1
    LD (HL),PAPER2+BRIGHT ;#50 ; press the key  #50=(PAPER RED +BRIGHT)
same_notes1:
    CALL NOTE_PLACE          ; returns HL
    PUSH    HL
; same as above, only light if this has a value
    DEC E
    INC E
    JR  Z,same_notes2
    LD (HL),PAPER5             ; PAPER5=40=$28   PAPER5=(PAPER CYAN)
same_notes2:
    LD B,0                              ; initialise b
    LD A,L                              ; the border is set from L
;   XOR A                              ; an alternative to the line above, force no border flash 
    LD L,E                              ; NOTE 2  (LOWS)
    LD H,D                             ; NOTE 1  (HIGHS)

;what's what now
;E   =  L - NOTE 2  (low registers)
;D   =  H - NOTE 1  (high registers)
;BC =  - overall duration ; note B=0 length is in "C"
;A    =  last piano keyboard position and this dictates the border colour

tune_loop:
        OUT  (#FE), A             ; note 1 (HIGH REGS)
        DEC  D
        JR  NZ,tl_1
        LD  D,H
        XOR  #18
tl_1:
        DEC  E                        ; note 2 (LOW REGS)
        JR  NZ,tl_2
        LD  E,L
        XOR  #18
tl_2:
        DJNZ  tune_loop       
        DEC  C
       JR  NZ, tune_loop
; noise completed, erase the pressed keys
    POP HL
    LD (HL),PWHITE                ; PWHITE=56=$38   ; the default keyboard colour 56=$38=(paper white + ink black)
    POP HL
    LD (HL),PWHITE                ; PWHITE=56=$38      ;
; restore the data  pointer
    POP DE                             ; restore the note pointer
;check for exit and then loop
    CALL test_enter                  ;#9337    ; Check whether ENTER or the fire button is being pressed
    RET NZ
    JR manic_loop

 

ATT15  equ $59e0                 ; The attribute position of the piano, which is defined as line 15

 

; calculate the screen position to press a key based on "a" the note
NOTE_PLACE:
    LD HL,ATT15                   ; this address is free to move up or down the screen
    SUB 8
    CPL
STEP1_INDEX:
    RRCA
    RRCA
STEP2_INDEX:                  ;used by the delay routine
    RRCA
    AND 31
STEP_INDEX:
    ADD A,L
    LD L,A
    ADC A,H
    SUB L
    LD H,A

    LD A,(HL)
    RET

;: Title screen tune data (The Blue Danube)
;  The tune data is organised into 95 groups of two bytes
;      two bytes of data for each dual note played.
; The first byte is split into two values.
;
; byte 1 ------xxb the xxb value is an index into the TIME_SHIFT array. This supplies the note duration.
; byte 1 xxxxx---b the xxxxxxb value is an index into the NOTE_SHIFT array. this supplies the shift from the first note to the second note
; byte 1 -----x--b the xb value is unused in this data
;
; byte 2 xxxxxxxxb the xxxxxxxxb value is raw data and is the value of the first pair of music notes. This value is identical to the middle
;  value in the original 3 byte layout. It is the NOTE value for NOTE 1
;
; The value extracted from the NOTE_SHIFT array is simply added to the value of NOTE 1
; the sum of NOTE 1 and the NOTE_SHIFT value is the value for NOTE 2

TIME_SHIFT:
;            0     1      2       3     ; notice I have used 75 (which is the logical value to use)

      DB 25,  50,   75,   100


 

NOTE_SHIFT:

;              0  1    2   3    4     5     6     7     8     9    10   11   12   13    14    15

    DB      0,  1,  6,  8,  10,  11,  13,  20,  21,  24,  32,  43,  75,  77,  128,  154

;                            
L33902:
DB 2+8*1,128
DB 2+8*1,102
DB 2+8*1,86
DB 1+8*1,86
DB 1+8*10,171
DB 1+8*3,43
DB 1+8*3,43
DB 1+8*10,51
DB 1+8*6,64
DB 1+8*6,64
DB 1+8*10,171
DB 1+8*1,128
DB 1+8*1,128
DB 1+8*1,102
DB 1+8*1,86
DB 1+8*4,86
DB 1+8*8,171
DB 1+8*4,43
DB 1+8*4,43
DB 1+8*8,171
DB 1+8*7,48 
DB 1+8*7,48 
DB 1+8*8,171
DB 1+8*1,136
DB 1+8*1,136
DB 1+8*1,114
DB 1+8*1,76
DB 1+8*1,76 
DB 1+8*8,171
DB 1+8*8,38
DB 1+8*8,38 
DB 1+8*8,171
DB 1+8*7,48 
DB 1+8*7,48 
DB 1+8*8,171
DB 1+8*1,136
DB 1+8*1,136
DB 1+8*1,114
DB 1+8*1,76 
DB 1+8*1,76
DB 1+8*10,171
DB 1+8*6,38
DB 1+8*6,38 
DB 1+8*10,171
DB 1+8*6,51
DB 1+8*6,51
DB 1+8*10,171
DB 1+8*1,128
DB 1+8*1,128
DB 1+8*1,102
DB 1+8*1,86 
DB 1+8*1,64 
DB 1+8*11,128
DB 1+8*5,32
DB 1+8*5,32
DB 1+8*11,128
DB 1+8*3,43
DB 1+8*3,43
DB 1+8*11,128
DB 1+8*1,128
DB 1+8*1,128
DB 1+8*1,102
DB 1+8*1,86 
DB 1+8*1,64 
DB 1+8*9,128
DB 1+8*2,32 
DB 1+8*2,32 
DB 1+8*9,128 
DB 1+8*4,38 
DB 1+8*4,38 
DB 1+8*0,0  
DB 1+8*1,114
DB 1+8*1,114
DB 1+8*1,96 
DB 1+8*1,76 
DB 1+8*13,76 
DB 1+8*1,77 
DB 1+8*1,77 
DB 1+8*13,76 
DB 1+8*1,91 
DB 1+8*1,86
DB 1+8*15,51 
DB 1+8*1,51 
DB 1+8*1,51
DB 1+8*15,51
DB 1+8*1,64 
DB 1+8*1,102
DB 1+8*1,102
DB 3+8*1,114
DB 1+8*1,76 
DB 1+8*1,86 
DB 1+8*12,128
DB 0+8*14,128  
DB 0+8*1,128
DB 1+8*12,128

DB  0 ;End marker


Edited by Norman Sword, 19 January 2019 - 10:46 AM.





0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users