Source Code file: castlemaster2-annotated.asm
AddressPosition in BinarySizeTimingAssembledCode
#0000#0000
; --------------------------------
#0000#0000
; "Castle Master 2: The Crypt" by Incentive Software Ltd., 1990
#0000#0000
; Disassembled by Santiago Ontañón in 2023
#0000#0000
;
#0000#0000
; Disclaimer: All the comments, label and constant names in this disassembly are my best interpretation of what the
#0000#0000
;   code actually does. Fully annotating a disassembly like this one requires a large amount of work (this one took
#0000#0000
;   me over a month, dedicating 2-3 hours every day). Therefore, it might contain errors or misunderstandings.
#0000#0000
;   Please report if you find something that is incorrect. When I am very unsure of what some code does, I added
#0000#0000
;   a note, but I might have missed many.
#0000#0000
; 
#0000#0000
; Notes and curiosities from the codebase:
#0000#0000
; - There are two identical functions:
#0000#0000
;   - La9de_hl_eq_h_times_64
#0000#0000
;   - Lcb6d_hl_eq_h_times_64
#0000#0000
; - There are two implemented versions of the same multiplication operation "(de,hl) = de * hl":
#0000#0000
;   - La15e_de_times_hl_signed
#0000#0000
;   - L8ab4_de_times_hl_signed
#0000#0000
;   - Interestingly: the first is redundant, since the second is smaller and faster. However, it is the
#0000#0000
;     first that is the most commonly used in the code!!!
#0000#0000
; - There is self-modifying code
#0000#0000
; - The 3d rendering engine is quite advanced for the year it was written:
#0000#0000
;   - It contains all basic elements of later 3d engines
#0000#0000
;   - It only considers 2 rotation angles (pitch and yaw), but it would be trivial to add a third, if it
#0000#0000
;       wasn't because of the skybox (which would have to rotate if we added "roll").
#0000#0000
;   - It contains skybox rendering code for outdoor areas (and even a "lightning" animation over the skybox!)
#0000#0000
;   - It supports textured shapes
#0000#0000
;   - Objects can be lines, triangles, quads or pentagons
#0000#0000
;   - It implements many levels of culling (quick rendering cube, rendering frustum/pyramid)
#0000#0000
;   - It implements polygon clipping for those that are only partly within the screen
#0000#0000
;   - Different stages of rendering are "cached" in memory, so that we do not need to repeat them. For example,
#0000#0000
;     when entering a menu, and going back to the game, all the 3d -> 2d projection does not need to be redone,
#0000#0000
;     as positions have not changed. So, this is skipped. Similarly, when player does not move, rotation matrices
#0000#0000
;     are not recalculated.
#0000#0000
;   - All in all, even if the individual functions are not very optimized (things can be done significantly faster),
#0000#0000
;     the overall structure is very nice (and some of the low-level functions are indeed quite optimized, such as
#0000#0000
;     the one that renders textured horizontal lines).
#0000#0000
; - All the computations are done with fixed point arithmetic. Even line and polygon drawing uses this fixed-point
#0000#0000
;   calculations, rather than the more optimized Bresenham routines. This makes the code simpler, even if
#0000#0000
;   slower than it could be.
#0000#0000
; - Sorting of objects for rendering is quite curious, as it happens in coordinates *before* they are projected to
#0000#0000
;   camera coordinates (just distance from player in each separate axis). I am sure this causes many issues in corner
#0000#0000
;   cases. 
#0000#0000
; - I think the code has a couple of bugs, I marked them with "BUG?" tags. Of course, I am not 100% sure, but I
#0000#0000
;   think they are bugs.
#0000#0000
; 
#0000#0000
; Potential optimization of the code:
#0000#0000
; - The code seems more functional than optimized. The lowest level drawing routines seem to be optimized well, but most
#0000#0000
;   of the math routines are not. So, there is a lot of opportunity to make the engine faster.
#0000#0000
; - I have added "OPTIMIZATION" tags in places where small things could be optimized. I only added those that
#0000#0000
;   an automatic optimizer (in this case MDL: https://github.com/santiontanon/mdlz80optimizer) would not already
#0000#0000
;   detect automatically. Basically, these are notes for an potential optimized version. Only small things are noted
#0000#0000
;   large architectural changes (like moving from fixed-point arithmetic line-drawing to Bresenham-style, are not
#0000#0000
;   annotated in the code).
#0000#0000
;
#0000#0000
; Related work:
#0000#0000
; - See Phantasma, a reimplementation of the Freescape engine: https://github.com/TomHarte/Phantasma
#0000#0000
; - See the information on the Freescape reimplementation in SCUMMVM: https://wiki.scummvm.org/index.php?title=Freescape
#0000#0000
;
#0000#0000
#0000#0000
; --------------------------------
#0000#0000
; BIOS Functions and constants:
#0000#0000
; - Information obtained from "The Spectrum Machine Code Reference Guide" book.
#0000#0000
#0000#0000
; Saves a collection of bytes to tape
#0000#0000
; Input:
#0000#0000
; - ix: address to save
#0000#0000
; - de: byte count
#0000#0000
L04c6_BIOS_CASSETTE_SAVE_NO_BREAK_TEST: equ #04c6
#0000#0000
#0000#0000
; Saves a collection of bytes to tape
#0000#0000
; Input:
#0000#0000
; - ix: address where to load
#0000#0000
; - de: byte count
#0000#0000
L0562_BIOS_READ_FROM_TAPE_SKIP_TESTS: equ #0562
#0000#0000
#0000#0000
ULA_PORT: equ #fe  ; Writing to this port ignores the high 8bits.
#0000#0000
                   ; The 8 bit value written is used as follows:
#0000#0000
                   ; - bits 0, 1, 2: border color
#0000#0000
                   ; - bit 3: MIC (tape output)
#0000#0000
                   ; - bit 4: speaker output
#0000#0000
#0000#0000
#0000#0000
; --------------------------------
#0000#0000
; Video memory constants:
#0000#0000
; - Information obtained from: http://www.breakintoprogram.co.uk/hardware/computers/zx-spectrum/screen-memory-layout
#0000#0000
L4000_VIDEOMEM_PATTERNS: equ #4000
#0000#0000
L5800_VIDEOMEM_ATTRIBUTES: equ #5800
#0000#0000
#0000#0000
SCREEN_WIDTH: equ 24
#0000#0000
SCREEN_HEIGHT: equ 14
#0000#0000
SCREEN_WIDTH_IN_PIXELS: equ SCREEN_WIDTH * 8  ; 192  ; mdl: SCREEN_WIDTH_IN_PIXELS = 192 (#00c0)
#0000#0000
SCREEN_HEIGHT_IN_PIXELS: equ SCREEN_HEIGHT * 8  ; 112  ; mdl: SCREEN_HEIGHT_IN_PIXELS = 112 (#0070)
#0000#0000
#0000#0000
; --------------------------------
#0000#0000
; Game constants:
#0000#0000
CONTROL_MODE_KEYBOARD: equ 0
#0000#0000
CONTROL_MODE_SINCLAIR_JOYSTICK: equ 1
#0000#0000
CONTROL_MODE_KEMPSTON_JOYSTICK: equ 2
#0000#0000
CONTROL_MODE_CURSOR_JOYSTICK: equ 3
#0000#0000
#0000#0000
MAX_COORDINATE: equ 127 * 64  ; mdl: MAX_COORDINATE = 8128 (#1fc0)
#0000#0000
#0000#0000
MAX_PRESSED_KEYS: equ 5
#0000#0000
FILENAME_BUFFER_SIZE: equ 12
#0000#0000
#0000#0000
SPIRIT_METER_MAX: equ 64
#0000#0000
MAX_STRENGTH: equ 24
#0000#0000
#0000#0000
GAME_OVER_REASON_OVERPOWERED: equ 1
#0000#0000
GAME_OVER_REASON_YOU_COLLAPSE: equ 2
#0000#0000
GAME_OVER_REASON_CRUSHED: equ 3
#0000#0000
GAME_OVER_REASON_FATAL_FALL: equ 4
#0000#0000
GAME_OVER_REASON_ESCAPED: equ 5
#0000#0000
#0000#0000
; Sound FX:
#0000#0000
SFX_MENU_SELECT: equ 3  ; Also used for when player collides with an object
#0000#0000
SFX_THROW_ROCK_OR_LAND: equ 5
#0000#0000
SFX_FALLING: equ 6
#0000#0000
SFX_GAME_START: equ 7
#0000#0000
SFX_LIGHTNING: equ 8
#0000#0000
SFX_GATE_CLOSE: equ 9
#0000#0000
SFX_PICK_UP_ITEM: equ 10
#0000#0000
SFX_OPEN_CHEST: equ 11
#0000#0000
SFX_CLIMB_DROP: equ 12
#0000#0000
SFX_OPEN_ESCAPED: equ 13
#0000#0000
; There are other SFX defined, but only used in the game scripts:
#0000#0000
; 1  ; sounds like if you die / get hit / error
#0000#0000
; 2  ; sounds like game over
#0000#0000
; 4  ; short high -> higher pitch beep
#0000#0000
; 14  ; low-pitch repeated sound, not sure what
#0000#0000
; 15  ; tiny short SFX
#0000#0000
#0000#0000
INPUT_FORWARD: equ 3
#0000#0000
INPUT_BACKWARD: equ 4
#0000#0000
INPUT_TURN_LEFT: equ 5
#0000#0000
INPUT_TURN_RIGHT: equ 6
#0000#0000
INPUT_LOOK_UP: equ 7
#0000#0000
INPUT_LOOK_DOWN: equ 8
#0000#0000
INPUT_CRAWL: equ 9
#0000#0000
INPUT_WALK: equ 10
#0000#0000
INPUT_RUN: equ 11
#0000#0000
INPUT_FACE_FORWARD: equ 12
#0000#0000
INPUT_U_TURN: equ 13
#0000#0000
#0000#0000
INPUT_MOVEMENT_POINTER_ON_OFF: equ 21
#0000#0000
INPUT_THROW_ROCK: equ 22
#0000#0000
INPUT_MOVE_POINTER_RIGHT: equ 23
#0000#0000
INPUT_MOVE_POINTER_LEFT: equ 24
#0000#0000
INPUT_MOVE_POINTER_DOWN: equ 25
#0000#0000
INPUT_MOVE_POINTER_UP: equ 26
#0000#0000
INPUT_ACTION: equ 27
#0000#0000
#0000#0000
INPUT_SWITCH_BETWEEN_MOVEMENT_AND_POINTER: equ 30
#0000#0000
#0000#0000
INPUT_INFO_MENU: equ 41
#0000#0000
#0000#0000
; How many degrees is a full circle:
#0000#0000
FULL_ROTATION_DEGREES: equ 72
#0000#0000
#0000#0000
; Datablock structures:
#0000#0000
AREA_HEADER_SIZE: equ 8
#0000#0000
#0000#0000
; Area struct:
#0000#0000
AREA_FLAGS: equ 0
#0000#0000
AREA_N_OBJECTS: equ 1
#0000#0000
AREA_ID: equ 2
#0000#0000
AREA_RULES_OFFSET: equ 3  ; 2 bytes
#0000#0000
AREA_SCALE: equ 5
#0000#0000
AREA_ATTRIBUTE: equ 6
#0000#0000
AREA_NAME: equ 7
#0000#0000
#0000#0000
; Object struct:
#0000#0000
OBJECT_TYPE_AND_FLAGS: equ 0
#0000#0000
OBJECT_X: equ 1
#0000#0000
OBJECT_Y: equ 2
#0000#0000
OBJECT_Z: equ 3
#0000#0000
OBJECT_SIZE_X: equ 4
#0000#0000
OBJECT_SIZE_Y: equ 5
#0000#0000
OBJECT_SIZE_Z: equ 6
#0000#0000
OBJECT_ID: equ 7
#0000#0000
OBJECT_SIZE: equ 8
#0000#0000
OBJECT_ADDITIONAL_DATA: equ 9
#0000#0000
#0000#0000
; Object types:
#0000#0000
OBJECT_TYPE_ENTRANCE: equ 0
#0000#0000
OBJECT_TYPE_CUBE: equ 1
#0000#0000
OBJECT_TYPE_SPIRIT: equ 2
#0000#0000
OBJECT_TYPE_RECTANGLE: equ 3
#0000#0000
; - Object types in between 4 and 9 are different solids, like pyramids,
#0000#0000
; hourglasses, wedges, etc. that are synthesized on the fly.
#0000#0000
; - I believe the object ID here just indicates their orientation (one of
#0000#0000
; the 6 possible cardinal directions in 3d), and their additional data
#0000#0000
; is used to determine their exact shape (via some checks in at the bedinning of
#0000#0000
; function "L97bb_project_other_solids").
#0000#0000
OBJECT_TYPE_LINE: equ 10
#0000#0000
OBJECT_TYPE_TRIANGLE: equ 11
#0000#0000
OBJECT_TYPE_QUAD: equ 12
#0000#0000
OBJECT_TYPE_PENTAGON: equ 13
#0000#0000
OBJECT_TYPE_HEXAGON: equ 14
#0000#0000
#0000#0000
; Rule types:
#0000#0000
RULE_TYPE_ADD_TO_SCORE: equ 1
#0000#0000
RULE_TYPE_TOGGLE_OBJECT_VISIBILITY: equ 3
#0000#0000
RULE_TYPE_MAKE_OBJECT_VISIBILE: equ 4
#0000#0000
RULE_TYPE_MAKE_OBJECT_INVISIBILE: equ 5
#0000#0000
RULE_TYPE_TOGGLE_OBJECT_FROM_AREA_VISIBILITY: equ 6
#0000#0000
RULE_TYPE_MAKE_OBJECT_FROM_AREA_VISIBILE: equ 7
#0000#0000
RULE_TYPE_MAKE_OBJECT_FROM_AREA_INVISIBILE: equ 8
#0000#0000
RULE_TYPE_INCREMENT_VARIABLE: equ 9
#0000#0000
RULE_TYPE_DECREMENT_VARIABLE: equ 10
#0000#0000
RULE_TYPE_END_RULE_IF_VARIABLE_DIFFERENT: equ 11
#0000#0000
RULE_TYPE_SET_BOOLEAN_TRUE: equ 12
#0000#0000
RULE_TYPE_SET_BOOLEAN_FALSE: equ 13
#0000#0000
RULE_TYPE_END_RULE_IF_BOOLEAN_DIFFERENT: equ 14
#0000#0000
RULE_TYPE_PLAY_SFX: equ 15
#0000#0000
RULE_TYPE_DESTROY_OBJECT: equ 16
#0000#0000
RULE_TYPE_DESTROY_OBJECT_FROM_AREA: equ 17
#0000#0000
RULE_TYPE_TELEPORT: equ 18
#0000#0000
RULE_TYPE_STRENGTH_UPDATE: equ 19
#0000#0000
RULE_TYPE_SET_VARIABLE: equ 20
#0000#0000
RULE_TYPE_REDRAW: equ 26
#0000#0000
RULE_TYPE_PAUSE: equ 27
#0000#0000
RULE_TYPE_REQUEST_SFX_NEXT_FRAME: equ 28
#0000#0000
RULE_TYPE_TOGGLE_BOOLEAN: equ 29
#0000#0000
RULE_TYPE_END_RULE_IF_OBJECT_INVISIBLE: equ 30
#0000#0000
RULE_TYPE_END_RULE_IF_OBJECT_VISIBLE: equ 31
#0000#0000
RULE_TYPE_END_RULE_IF_OBJECT_FROM_AREA_INVISIBLE: equ 32
#0000#0000
RULE_TYPE_END_RULE_IF_OBJECT_FROM_AREA_VISIBLE: equ 33
#0000#0000
RULE_TYPE_SHOW_MESSAGE: equ 34
#0000#0000
RULE_TYPE_RENDER_EFFECT: equ 35
#0000#0000
RULE_TYPE_FLIP_SKIP_RULE: equ 44
#0000#0000
RULE_TYPE_UNSET_SKIP_RULE: equ 45
#0000#0000
RULE_TYPE_END_RULE_IF_VARIABLE_LARGER: equ 46
#0000#0000
RULE_TYPE_END_RULE_IF_VARIABLE_LOWER: equ 47
#0000#0000
RULE_TYPE_SELECT_OBJECT: equ 48
#0000#0000
#0000#0000
#0000#0000
; --------------------------------
#0000#0000
; RAM Variables before the game data:
#0000#0000
L5cbc_render_buffer: equ #5cbc  ; 2712 bytes ((SCREEN_HEIGHT * 8 + 1) * SCREEN_WIDTH)
#0000#0000
#0000#0000
; Variables that overlap with the render buffer, these are used when projecting
#0000#0000
; the 3d vertices into 2d, so, they are discarded and not needed when using the 
#0000#0000
; render buffer.
#0000#0000
L5e4c_pitch_rotation_matrix: equ #5e4c
#0000#0000
L5e55_rotation_matrix: equ #5e55
#0000#0000
L5e5e_at_least_one_vertex_outside_rendering_frustum: equ #5e5e
#0000#0000
L5e5f_add_to_projected_objects_flag: equ #5e5f  ; If this is 1, the current object being projected from 3d to 2d, will be added to the list of objects to draw.
#0000#0000
L5e60_projection_pre_work_type: equ #5e60  ; Indicates whether we need to do additional computations before projecting each face.
#0000#0000
L5e61_object_currently_being_processed_type: equ #5e61
#0000#0000
L5e62_player_collision_with_object_flags: equ #5e62
#0000#0000
L5e63_3d_vertex_coordinates_relative_to_player: equ #5e63
#0000#0000
L5e75_48_bit_accumulator: equ #5e75
#0000#0000
L5e7b_48bitmul_tmp1: equ #5e7b
#0000#0000
L5e7d_48bitmul_tmp2: equ #5e7d
#0000#0000
#0000#0000
L5e9f_3d_vertex_coordinates_after_rotation_matrix: equ #5e9f  ; 16 bit representation.
#0000#0000
#0000#0000
L5edc_vertex_rendering_frustum_checks: equ #5edc  ; 5 bits per vertex, indicating if they passed or not each of the 5 culling tests for the rendering frustum.
#0000#0000
#0000#0000
L5ee8_already_projected_vertex_coordinates: equ #5ee8
#0000#0000
#0000#0000
L5f24_shape_edges_ptr: equ #5f24  ; Pointer to the array with the order of edges to use for projection.
#0000#0000
L5f26_alternative_shape_edges_ptr: equ #5f26  ; Alternative edges pointer (for when object is seen from below, this is only needed for flat shapes).
#0000#0000
L5f28_cull_face_when_no_projected_vertices: equ #5f28
#0000#0000
L5f29_extra_solid_dimensions: equ #5f29  ; stores 4 additional dimensions used temporarily to synthesize solids like pyramids, hourglasses, etc. on the fly. (4 16bit numbers).
#0000#0000
L5f31_sorting_comparison_result: equ #5f31  ; result of comparing the coordinates of two objects to see if they should be flipped for rendering.
#0000#0000
L5f32_sorting_any_change: equ #5f32
#0000#0000
L5f33_sorting_boundingbox_ptr1: equ #5f33
#0000#0000
L5f35_sorting_boundingbox_ptr2: equ #5f35
#0000#0000
L5f37_sorting_bbox1_c1: equ #5f37  ; These four variables hold the values of the min/max coordinates for the current axis of the two bounding boxes being compared for sorting.
#0000#0000
L5f39_sorting_bbox2_c1: equ #5f39
#0000#0000
L5f3b_sorting_bbox1_c2: equ #5f3b
#0000#0000
L5f3d_sorting_bbox2_c2: equ #5f3d
#0000#0000
L5f3f_n_objects_covering_the_whole_screen_left: equ #5f3f
#0000#0000
L5f40_16_bit_tmp_matrix: equ #5f40  ; Used internally to save the results of matrix multiplication.
#0000#0000
L5f52_16_bit_tmp_matrix_ptr: equ #5f52  ; Used to keep track of the elements in the matrix above.
#0000#0000
#0000#0000
L5fa2_3d_object_bounding_boxes_relative_to_player: equ #5fa2  ; in 16 bit precision: x1, x2, y1, y2, z1, z2
#0000#0000
#0000#0000
L6664_row_pointers: equ #6664  ; Pointers to each row of pixels in the buffer.
#0000#0000
L6754_end_of_render_buffer: equ #6754
#0000#0000
#0000#0000
; This contains the current room objects, already projected to 2d coordinates:
#0000#0000
; - Each entry has 2 pointers:
#0000#0000
;   - One pointer to the "L67f4_projected_vertex_data" (with the projected vertices)
#0000#0000
;   - One pointer to the "L5fa2_3d_object_bounding_boxes_relative_to_player"
#0000#0000
L6754_current_room_object_projected_data: equ #6754
#0000#0000
#0000#0000
; For each projected object, the data is organized as follows:
#0000#0000
; - 1 byte: object ID
#0000#0000
; - 1 byte: number of primitives/faces:
#0000#0000
;   - If the most significant bit is set, it means this object covers the whole screen.
#0000#0000
; - face data:
#0000#0000
;   - 1 byte (texture / # vertices),
#0000#0000
;   - and then 2 bytes per vertex screen x, screen y (screen y is reversed, 0 = bottom).
#0000#0000
L67f4_projected_vertex_data: equ #67f4
#0000#0000
#0000#0000
#0000#0000
    org #6a00
#6a00#0000
#6a00#0000
; --------------------------------
#6a00#0000
; Program start
#6a00#0000
L6a00_start:
#6a00#0000311
c3 2f 6a 
    jp L6a2f_game_init
#6a03#0003
#6a03#0003
#6a03#0003
; --------------------------------
#6a03#0003
; Set up the interrupt routine to "Lbe66_interrupt_routine".
#6a03#0003
L6a03_setup_interrupts:
#6a03#000315
f3 
    di
#6a04#0004112
e5 
    push hl
#6a05#0005112
d5 
    push de
#6a06#0006112
c5 
    push bc
#6a07#0007112
f5 
    push af
#6a08#000815
af 
        xor a
#6a09#0009314
32 7c 74 
        ld (L747c_within_interrupt_flag), a
#6a0c#000c311
21 00 fe 
        ld hl, Lfe00_interrupt_vector_table
#6a0f#000f15
7c 
        ld a, h
#6a10#0010211
ed 47 
        ld i, a
#6a12#001215
54 
        ld d, h
#6a13#001315
5d 
        ld e, l
#6a14#001415
1c 
        inc e
#6a15#0015211
36 fd 
        ld (hl), #fd
#6a17#0017311
01 00 01 
        ld bc, 256
#6a1a#001a223/18
ed b0 
        ldir
#6a1c#001c28
3e c3 
        ld a, #c3  ; jp opcode
#6a1e#001e311
21 66 be 
        ld hl, Lbe66_interrupt_routine
#6a21#0021314
32 fd fd 
        ld (Lfdfd_interrupt_jp), a
#6a24#0024317
22 fe fd 
        ld (Lfdfe_interrupt_pointer), hl
#6a27#0027210
ed 5e 
        im 2
#6a29#0029111
f1 
    pop af
#6a2a#002a111
c1 
    pop bc
#6a2b#002b111
d1 
    pop de
#6a2c#002c111
e1 
    pop hl
#6a2d#002d15
fb 
    ei
#6a2e#002e111
c9 
    ret
#6a2f#002f
#6a2f#002f
#6a2f#002f
; --------------------------------
#6a2f#002f
; Initializes the game the very first time.
#6a2f#002f
L6a2f_game_init:
#6a2f#002f311
31 f8 ff 
    ld sp, #fff8  ; initialize the stack
#6a32#003215
f3 
    di
#6a33#003315
af 
    xor a  ; Set control mode to keyboard
#6a34#0034314
32 83 76 
    ld (L7683_control_mode), a
#6a37#0037422
fd 22 7d 74 
    ld (L747d), iy  ; Note: This instruction is very strange, as at this point "iy" is undefined.
#6a3b#003b318
cd c9 a4 
    call La4c9_init_game_state
#6a3e#003e318
cd 03 6a 
    call L6a03_setup_interrupts
#6a41#0041311
c3 7e 6a 
    jp L6a7e_main_application_loop
#6a44#0044
#6a44#0044
#6a44#0044
; --------------------------------
#6a44#0044
; Unused?
#6a44#004416
    db #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00
#6a54#005416
    db #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00
#6a64#006416
    db #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00
#6a74#007410
    db #00, #00, #00, #00, #00, #00, #00, #00, #00, #00
#6a7e#007e
#6a7e#007e
#6a7e#007e
; --------------------------------
#6a7e#007e
; Main application loop: calls title screen, starts game, restarts title screen, etc.
#6a7e#007e
; I think this is a game loop
#6a7e#007e
L6a7e_main_application_loop:
#6a7e#007e311
21 fd ff 
    ld hl, #fffd
#6a81#0081317
22 6c 74 
    ld (L746c_game_flags), hl
#6a84#008428
3e 02 
    ld a, 2
#6a86#0086314
32 77 74 
    ld (L7477_render_buffer_effect), a  ; Request gate opening effect
#6a89#0089318
cd 2e c7 
    call Lc72e_title_screen_loop
#6a8c#008c318
cd aa 83 
    call L83aa_redraw_whole_screen
#6a8f#008f15
af 
    xor a
#6a90#0090314
32 79 74 
    ld (L7479_current_game_state), a
#6a93#0093311
c3 99 6a 
    jp L6a99
#6a96#0096
L6a96_game_loop:
#6a96#0096318
cd aa 83 
    call L83aa_redraw_whole_screen
#6a99#0099
L6a99:
#6a99#0099318
cd ec 9d 
    call L9dec_game_tick
#6a9c#009c318
cd 05 a0 
    call La005_check_rules
#6a9f#009f317
2a 6c 74 
    ld hl, (L746c_game_flags)
#6aa2#00a2210
cb 4d 
    bit 1, l  ; check the "game over" flag
#6aa4#00a4311
ca 96 6a 
    jp z, L6a96_game_loop
#6aa7#00a7318
cd c9 a4 
    call La4c9_init_game_state
#6aaa#00aa311
c3 7e 6a 
    jp L6a7e_main_application_loop
#6aad#00ad
#6aad#00ad
#6aad#00ad
; --------------------------------
#6aad#00ad
; Game state variables:
#6aad#00ad
; Saving game saves data starting from here:
#6aad#00ad
L6aad_savegame_data_start:
#6aad#00ad
L6aad_player_current_x:
#6aad#00ad2
    dw #00a0
#6aaf#00af
L6aaf_player_current_y:
#6aaf#00af2
    dw #09e0
#6ab1#00b1
L6ab1_player_current_z:
#6ab1#00b12
    dw #1b60
#6ab3#00b3
L6ab3_current_speed_in_this_room:
#6ab3#00b32
    dw #11d0
#6ab5#00b5
L6ab5_current_speed:  ; This is a value form the Ld0c8_speed_when_crawling array, depending on L6b0b_selected_movement_mode.
#6ab5#00b51
    db #f0
#6ab6#00b6
L6ab6_player_pitch_angle:  ; from 18 to -18 (54)
#6ab6#00b61
    db #00
#6ab7#00b7
L6ab7_player_yaw_angle:  ; from 0 - 71
#6ab7#00b71
    db #1a
#6ab8#00b8
L6ab8_player_crawling:  ; 2 when standing up, 1 when crawling.
#6ab8#00b81
    db 2
#6ab9#00b9
L6ab9_player_height:  ; player height * room scale
#6ab9#00b91
    db #26
#6aba#00ba
L6aba_max_falling_height_without_damage:  ; 2 * room scale
#6aba#00ba1
    db #26
#6abb#00bb
L6abb_max_climbable_height:
#6abb#00bb1
    db #13
#6abc#00bc
L6abc_current_room_scale:
#6abc#00bc1
    db #13
#6abd#00bd
L6abd_cull_by_rendering_volume_flag:
#6abd#00bd1
    db #00
#6abe#00be
L6abe_use_eye_player_coordinate:  ; When this is 0, we will use "feet" coordinates for collision checks, when 1, we will use "eye" coordinates.
#6abe#00be1
    db #00
#6abf#00bf
L6abf_current_area_name_string:
#6abf#00bf16
    db 0, "   THE CRYPT   "
#6acf#00cf
L6acf_current_area_id:
#6acf#00cf1
    db #02
#6ad0#00d0
L6ad0_current_area_n_objects:
#6ad0#00d01
    db #18
#6ad1#00d1
L6ad1_current_area_objects:
#6ad1#00d12
    dw #d6ca
#6ad3#00d3
#6ad3#00d32
    db #00, #00  ; unused?
#6ad5#00d5
L6ad5_current_area_rules:
#6ad5#00d52
    dw #d8ce
#6ad7#00d7
L6ad7_current_border_color:
#6ad7#00d72
    db #14, #00
#6ad9#00d9
L6ad9_current_attribute_color:
#6ad9#00d92
    db #16, #0b
#6adb#00db
L6adb_desired_border_color:
#6adb#00db2
    db #14, #00
#6add#00dd
L6add_desired_attribute_color:
#6add#00dd2
    db #47, #0b
#6adf#00df
L6adf_game_boolean_variables:
#6adf#00df
    ; One bit corresponding to each variable. 
#6adf#00df
    ; The first few correspond to collected keys.
#6adf#00df4
    db #00, #00, #00, #00
#6ae3#00e3
L6ae3_visited_areas:  ; one bit per area (keeps track of which areas the player has already visited).
#6ae3#00e38
    db #00, #00, #00, #00, #00, #00, #00, #00
#6aeb#00eb
L6aeb_score:  ; 3 bytes
#6aeb#00eb3
    db #00, #00, #00
#6aee#00ee
L6aee_game_variables:  ; These can be accessed by the game scripts.
#6aee#00ee8
    db #00, #00, #00, #00, #00, #00, #00, #00
#6af6#00f68
    db #00, #00, #00, #00, #00, #00, #00, #00
#6afe#00fe8
    db #00, #00, #00, #00, #00, #00, #00, #00
#6b06#01063
    db #00, #00, #00
#6b09#0109
L6b09_number_of_spirits_destroyed:
#6b09#01091
    db 0
#6b0a#010a
L6b0a_current_strength:
#6b0a#010a1
    db 16
#6b0b#010b
L6b0b_selected_movement_mode:  ; 0: crawl, 1: walk, 2: run
#6b0b#010b1
    db 2
#6b0c#010c
L6b0c_num_collected_keys:
#6b0c#010c1
    db 0
#6b0d#010d
L6b0d_new_key_taken:
#6b0d#010d1
    db 0  ; Contains the ID of a key just picked up, before being added to the inventory.
#6b0e#010e
L6b0e_lightning_time_seconds_countdown:
#6b0e#010e1
    db #14
#6b0f#010f
L6b0f_collected_keys:
#6b0f#010f
    ; Different from "L6adf_game_boolean_variables", this array has the keys in
#6b0f#010f
    ; the order the player picked them, directly as a list of IDs.
#6b0f#010f10
    db #00, #00, #00, #00, #00, #00, #00, #00, #00, #00
#6b19#0119
L6b19_current_area_flags:
#6b19#01191
    db #00
#6b1a#011a
L6b1a_pointer_x:
#6b1a#011a1
    db 0
#6b1b#011b
L6b1b_pointer_y:
#6b1b#011b1
    db 0
#6b1c#011c
L6b1c_movement_or_pointer:
#6b1c#011c1
    db 0  ; 0: movement, otherwise: pointer
#6b1d#011d
L6b1d_time_interrupts:
#6b1d#011d1
    db #01
#6b1e#011e
L6b1e_time_unit5:  ; Changes once per second, counting from 10 to 1
#6b1e#011e1
    db #00
#6b1f#011f
L6b1f_current_spirit_meter:  ; Increments in 1 each time Lbe65_time_unit3 wraps around (each 120 seconds).
#6b1f#011f1
    db #20
#6b20#0120
L6b20_display_movement_pointer_flag:  ; Whether to draw a small cross in the center of the screen when in movement mode.
#6b20#01201
    db #ff
#6b21#0121
L6b21_time_unit6_previous:  ; to keep track of when L6b22_time_unit6 changes.
#6b21#01211
    db #00
#6b22#0122
L6b22_time_unit6:  ; Increments by one each time L6b1e_time_unit5 cycles.
#6b22#01221
    db #00
#6b23#0123
L6b23_set_bit7_byte_3_flag_at_start:  ; If this is != 0, when starting a game, bit 7 of the 3rd byte of the boolean variables is set to 1 (not sure of the effect of this).
#6b23#01231
    db #00
#6b24#0124
L6b24_savegame_data_end:
#6b24#0124
#6b24#01244
    db #00, #00, #07, #00  ; Unused?
#6b28#0128
L6b28_player_radius:
#6b28#01282
    dw #000a
#6b2a#012a
L6b2a_spirit_in_room:  ; 0: no spirit, 1: spirit
#6b2a#012a1
    db 0
#6b2b#012b
L6b2b_desired_eye_compass_frame:
#6b2b#012b1
    db #00
#6b2c#012c
#6b2c#012c
; If an object definition has more bytes than this, it means there are rule effects associated with it:
#6b2c#012c
L6b2c_expected_object_size_by_type:
#6b2c#012c16
    db #09, #0c, #0e, #0a, #10, #10, #10, #10, #10, #10, #10, #13, #16, #19, #1c, #00
#6b3c#013c
#6b3c#013c
L6b3c_rule_size_by_type:  ; Assuming there are only 49 rule types (maximum type if 48).
#6b3c#013c16
    db #01, #04, #02, #02, #02, #02, #03, #03, #03, #02, #02, #03, #02, #02, #03, #02
#6b4c#014c16
    db #02, #03, #03, #02, #03, #00, #00, #00, #00, #02, #01, #02, #02, #02, #02, #02
#6b5c#015c16
    db #03, #03, #02, #02, #00, #00, #00, #00, #00, #02, #01, #00, #01, #01, #03, #03
#6b6c#016c1
    db #02
#6b6d#016d
#6b6d#016d
; Edges for cubes:
#6b6d#016d
L6b6d_cube_edges:
#6b6d#016d1
    db #0c
#6b6e#016e8
    db #00, #01, #01, #02, #02, #03, #03, #00
#6b76#01768
    db #04, #05, #05, #06, #06, #07, #07, #04
#6b7e#017e8
    db #00, #04, #01, #05, #02, #06, #03, #07
#6b86#0186
#6b86#0186
; byte 0: number of faces
#6b86#0186
; Each face then:
#6b86#0186
; - byte: texture
#6b86#0186
; - byte: number of vertices/edges
#6b86#0186
; - bytes 2+: edge indexes from where to get the vertices
#6b86#0186
; - the msb in the index indicates if we need to flip the vertexes in the edge in question.
#6b86#0186
L6b86_face_definition_for_cubes:
#6b86#01861
    db #06
#6b87#01876
    db #00, #04, #83, #0b, #07, #88
#6b8d#018d6
    db #00, #04, #05, #8a, #81, #09
#6b93#01936
    db #00, #04, #08, #04, #89, #80
#6b99#01996
    db #00, #04, #0a, #06, #8b, #82
#6b9f#019f6
    db #00, #04, #00, #01, #02, #03
#6ba5#01a56
    db #00, #04, #84, #87, #86, #85
#6bab#01ab
#6bab#01ab
; Edges for pyramids:
#6bab#01ab
L6bab_pyramid_edges:
#6bab#01ab1
    db #08
#6bac#01ac8
    db #00, #01, #01, #02, #02, #03, #03, #00
#6bb4#01b48
    db #00, #04, #01, #04, #02, #04, #03, #04
#6bbc#01bc
#6bbc#01bc
L6bbc_face_definition_for_pyramids:
#6bbc#01bc1
    db #05
#6bbd#01bd5
    db #00, #03, #83, #07, #84
#6bc2#01c25
    db #00, #03, #82, #06, #87
#6bc7#01c75
    db #00, #03, #81, #05, #86
#6bcc#01cc5
    db #00, #03, #80, #04, #85
#6bd1#01d16
    db #00, #04, #00, #01, #02, #03
#6bd7#01d7
#6bd7#01d7
L6bd7_wedge_edges:
#6bd7#01d71
    db #09
#6bd8#01d86
    db #00, #01, #01, #02, #02, #03
#6bde#01de6
    db #03, #00, #00, #04, #01, #04
#6be4#01e46
    db #02, #05, #03, #05, #04, #05
#6bea#01ea
#6bea#01ea
L6bea_face_definition_for_wedges:
#6bea#01ea1
    db #05
#6beb#01eb6
    db #00, #04, #83, #07, #88, #84
#6bf1#01f15
    db #00, #03, #82, #06, #87
#6bf6#01f66
    db #00, #04, #81, #05, #08, #86
#6bfc#01fc5
    db #00, #03, #80, #04, #85
#6c01#02016
    db #00, #04, #00, #01, #02, #03
#6c07#0207
#6c07#0207
L6c07_triangle_houglass_edges:
#6c07#02071
    db #09
#6c08#02086
    db #00, #01, #01, #02, #02, #03
#6c0e#020e6
    db #03, #00, #00, #04, #01, #05
#6c14#02146
    db #02, #05, #03, #04, #04, #05
#6c1a#021a
#6c1a#021a
L6c1a_face_definition_for_triangle_hourglasses:
#6c1a#021a1
    db #05
#6c1b#021b5
    db #00, #03, #83, #07, #84
#6c20#02206
    db #00, #04, #82, #06, #88, #87
#6c26#02265
    db #00, #03, #81, #05, #86
#6c2b#022b6
    db #00, #04, #80, #04, #08, #85
#6c31#02316
    db #00, #04, #00, #01, #02, #03
#6c37#0237
#6c37#0237
L6c37_hourglass_edges:
#6c37#02371
    db #0c
#6c38#02388
    db #00, #01, #01, #02, #02, #03, #03, #00
#6c40#02408
    db #04, #07, #07, #05, #05, #06, #06, #04
#6c48#02488
    db #00, #04, #01, #06, #02, #05, #03, #07
#6c50#0250
#6c50#0250
L6c50_face_definition_for_hourglasses:
#6c50#02501
    db #06
#6c51#02516
    db #00, #04, #83, #0b, #84, #88
#6c57#02576
    db #00, #04, #82, #0a, #85, #8b
#6c5d#025d6
    db #00, #04, #81, #09, #86, #8a
#6c63#02636
    db #00, #04, #80, #08, #87, #89
#6c69#02696
    db #00, #04, #00, #01, #02, #03
#6c6f#026f6
    db #00, #04, #04, #05, #06, #07
#6c75#0275
#6c75#0275
; Edge definition for different shapes (lines, triangles, rectangles and pentagons),
#6c75#0275
; - the first byte is the # of edges
#6c75#0275
; - after that, each pair of bytes defines an edge.
#6c75#0275
L6c75_line_edges:
#6c75#02755
    db #02, #00, #01, #01, #00
#6c7a#027a
#6c7a#027a
; Edges for triangles:
#6c7a#027a
L6c7a_triangle_edges_top:
#6c7a#027a7
    db #03, #00, #01, #01, #02, #02, #00
#6c81#0281
L6c81_triangle_edges_bottom:
#6c81#02817
    db #03, #00, #02, #02, #01, #01, #00
#6c88#0288
#6c88#0288
; Edges for rectangles:
#6c88#0288
L6c88_rectangle_edges_top:
#6c88#02889
    db #04, #00, #01, #01, #02, #02, #03, #03, #00
#6c91#0291
L6c91_rectangle_edges_bottom:
#6c91#02919
    db #04, #00, #03, #03, #02, #02, #01, #01, #00
#6c9a#029a
#6c9a#029a
; Edges for pentagons:
#6c9a#029a
L6c9a_pentagon_edges_top:
#6c9a#029a11
    db #05, #00, #01, #01, #02, #02, #03, #03, #04, #04, #00
#6ca5#02a5
L6ca5_pentagon_edges_bottom:
#6ca5#02a511
    db #05, #00, #04, #04, #03, #03, #02, #02, #01, #01, #00
#6cb0#02b0
#6cb0#02b0
L6cb0_face_definition_for_flat_objects:
#6cb0#02b01
    db #01
#6cb1#02b18
    db #00, #06, #00, #01, #02, #03, #04, #05
#6cb9#02b9
#6cb9#02b9
#6cb9#02b9
; --------------------------------
#6cb9#02b9
L6cb9_game_text:
#6cb9#02b916
    db 0, " PRESS ANY KEY "
#6cc9#02c9
L6cc9_text_overpowered:
#6cc9#02c916
    db 0, "  OVERPOWERED  "
#6cd9#02d916
    db 1, " YOU COLLAPSE  "
#6ce9#02e916
    db 0, "    CRUSHED    "
#6cf9#02f916
    db 1, "  FATAL FALL   "
#6d09#030916
    db 0, "   ESCAPE !!   "
#6d19#031916
    db 0, "   THE CRYPT   "
#6d29#0329
L6d29_text_out_of_reach:
#6d29#032916
    db 1, " OUT OF REACH  "
#6d39#0339
L6d39_text_no_effect:
#6d39#033916
    db 0, "   NO EFFECT   "
#6d49#034916
    db 1, "   NO ENTRY    "
#6d59#035916
    db 0, "  WAY BLOCKED  "
#6d69#0369
L6d69_text_not_enough_room:
#6d69#036916
    db 0, "NOT ENOUGH ROOM"
#6d79#0379
L6d79_text_too_weak:
#6d79#037916
    db 1, "   TOO WEAK    "
#6d89#0389
L6d89_text_crawl:
#6d89#038916
    db 1, "CRAWL SELECTED "
#6d99#0399
L6d99_text_walk:
#6d99#039916
    db 0, " WALK SELECTED "
#6da9#03a9
L6da9_text_run:
#6da9#03a916
    db 1, " RUN SELECTED  "
#6db9#03b916
    db 1, " AAAAAARRRGH!  "
#6dc9#03c916
    db 0, " KEY COLLECTED "
#6dd9#03d916
    db 0, " NO KEYS FOUND "
#6de9#03e916
    db 1, "NEED RIGHT KEY "
#6df9#03f916
    db 0, "  IT IS EMPTY  "
#6e09#040916
    db 1, "  DOOR TO...   "
#6e19#041916
    db 0, "IN CASE OF FIRE"
#6e29#042916
    db 0, "CHOMP CHOMP AHH"
#6e39#043916
    db 1, "   OOOOFFF!    "
#6e49#044916
    db 1, "TREASURE FOUND "
#6e59#045916
    db 1, "THE DOOR OPENS "
#6e69#046916
    db 0, "THE DOOR CLOSES"
#6e79#047916
    db 0, "   PADLOCKED   "
#6e89#048916
    db 0, "IT'S VERY HEAVY"
#6e99#049916
    db 0, "    SMASH !    "
#6ea9#04a916
    db 0, "SHOWS LEVEL NO."
#6eb9#04b916
    db 0, "   PADLOCKED   "
#6ec9#04c916
    db 0, "HMM, NEED A BIT"
#6ed9#04d916
    db 1, "MORE SPRING IN "
#6ee9#04e916
    db 1, "YOUR STEP HERE "
#6ef9#04f916
    db 0, " THE LID OPENS "
#6f09#050916
    db 1, "THE LID CLOSES "
#6f19#051916
    db 1, "GLUG GLUG GLUG "
#6f29#052916
    db 0, "RETRY THE CHEST"
#6f39#053916
    db 1, "REVITALISATION "
#6f49#0549
L6f49_area_names:
#6f49#054916
    db 1, "  WILDERNESS   "
#6f59#055916
    db 0, "   THE CRYPT   "
#6f69#056916
    db 1, "CRYPT CORRIDOR "
#6f79#057916
    db 0, " THE MOUSETRAP "
#6f89#058916
    db 0, " LAST TREASURE "
#6f99#059916
    db 1, "   TANTALUS    "
#6fa9#05a916
    db 0, "    BELENUS    "
#6fb9#05b916
    db 0, "    POTHOLE    "
#6fc9#05c916
    db 0, "   THE STEPS   "
#6fd9#05d916
    db 1, " LOOKOUT POST  "
#6fe9#05e916
    db 1, "   KERBEROS    "
#6ff9#05f916
    db 0, "   CRYPT KEY   "
#7009#060916
    db 0, "   GATEHOUSE   "
#7019#061916
    db 0, "  BELENUS KEY  "
#7029#062916
    db 1, "SPIRITS' ABODE "
#7039#063916
    db 1, "    RAVINE     "
#7049#064916
    db 1, "  LIFT SHAFT   "
#7059#065916
    db 0, "  LEVEL 2 KEY  "
#7069#066916
    db 0, "LIFT ENTRANCE 6"
#7079#067916
    db 1, "    TUNNEL     "
#7089#068916
    db 0, "  LEVEL 3 KEY  "
#7099#069916
    db 1, "   THE TUBE    "
#70a9#06a916
    db 0, "LIFT ENTRANCE 5"
#70b9#06b916
    db 0, "     EPONA     "
#70c9#06c916
    db 0, "  LEVEL 4 KEY  "
#70d9#06d916
    db 0, "LIFT ENTRANCE 4"
#70e9#06e916
    db 0, "  NANTOSUELTA  "
#70f9#06f916
    db 0, "  STALACTITES  "
#7109#070916
    db 0, "    NO ROOM    "
#7119#071916
    db 0, "  THE TRAPEZE  "
#7129#072916
    db 1, "TREASURE CHEST "
#7139#073916
    db 0, "LIFT ENTRANCE 3"
#7149#074916
    db 1, "  THE SWITCH   "
#7159#075916
    db 1, "  THE PILLAR   "
#7169#076916
    db 1, " GROUND FLOOR  "
#7179#077916
    db 1, " THE RAT TRAP  "
#7189#078916
    db 0, "    YIN KEY    "
#7199#079916
    db 1, "     LIFT      "
#71a9#07a916
    db 0, "  TRAPEZE KEY  "
#71b9#07b916
    db 1, "   YANG KEY    "
#71c9#07c9
#71c9#07c9
#71c9#07c9
; --------------------------------
#71c9#07c9
L71c9_text_status_array:
#71c9#07c911
    db 0, "FEEBLE    "
#71d4#07d411
    db 0, "WEAK      "
#71df#07df11
    db 0, "HEALTHY   "
#71ea#07ea11
    db 0, "STRONG    "
#71f5#07f511
    db 0, "MIGHTY    "
#7200#080011
    db 0, "HERCULEAN "
#720b#080b
#720b#080b
; --------------------------------
#720b#080b
; Used to store the filename the user inputs in the load/save menu.
#720b#080b
L720b_text_input_buffer:
#720b#080b14
    db 0, "             "
#7219#08191
    db 19  ; Unused?
#721a#081a
#721a#081a
L721a_text_asterisks:
#721a#081a22
    db 0, "*********************"
#7230#0830
#7230#083013
    db 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
#723d#083d16
    db #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #00, #30, #72, #30, #72, #30
#724d#084d15
    db #72, #30, #72, #30, #72, #30, #72, #30, #72, #30, #72, #30, #72, #30, #72
#725c#085c
#725c#085c
#725c#085c
; --------------------------------
#725c#085c
L725c_videomem_row_pointers:
#725c#085c16
    dw #4084, #4184, #4284, #4384, #4484, #4584, #4684, #4784
#726c#086c16
    dw #40a4, #41a4, #42a4, #43a4, #44a4, #45a4, #46a4, #47a4
#727c#087c16
    dw #40c4, #41c4, #42c4, #43c4, #44c4, #45c4, #46c4, #47c4
#728c#088c16
    dw #40e4, #41e4, #42e4, #43e4, #44e4, #45e4, #46e4, #47e4
#729c#089c16
    dw #4804, #4904, #4a04, #4b04, #4c04, #4d04, #4e04, #4f04
#72ac#08ac16
    dw #4824, #4924, #4a24, #4b24, #4c24, #4d24, #4e24, #4f24
#72bc#08bc16
    dw #4844, #4944, #4a44, #4b44, #4c44, #4d44, #4e44, #4f44
#72cc#08cc16
    dw #4864, #4964, #4a64, #4b64, #4c64, #4d64, #4e64, #4f64
#72dc#08dc16
    dw #4884, #4984, #4a84, #4b84, #4c84, #4d84, #4e84, #4f84
#72ec#08ec16
    dw #48a4, #49a4, #4aa4, #4ba4, #4ca4, #4da4, #4ea4, #4fa4
#72fc#08fc16
    dw #48c4, #49c4, #4ac4, #4bc4, #4cc4, #4dc4, #4ec4, #4fc4
#730c#090c16
    dw #48e4, #49e4, #4ae4, #4be4, #4ce4, #4de4, #4ee4, #4fe4
#731c#091c16
    dw #5004, #5104, #5204, #5304, #5404, #5504, #5604, #5704
#732c#092c16
    dw #5024, #5124, #5224, #5324, #5424, #5524, #5624, #5724
#733c#093c
#733c#093c
#733c#093c
; --------------------------------
#733c#093c
; Unused?
#733c#093c
L733c:
#733c#093c10
    db #30, #72, #30, #72, #30, #72, #30, #72, #30, #72
#7346#094610
    db #30, #72, #30, #72, #30, #72, #30, #72, #30, #72
#7350#0950
#7350#0950
L7350_compass_eye_ui_row_pointers:
#7350#0950
    ; from (25, 158) to (25, 162)    
#7350#095010
    dw #5679, #5779, #5099, #5199, #5299
#735a#095a
#735a#095a
L735a_ui_message_row_pointers:
#735a#095a
    ; from (11, 176) to (11, 183)
#735a#095a8
    dw #50cb, #51cb, #52cb, #53cb
#7362#09628
    dw #54cb, #55cb, #56cb, #57cb
#736a#096a
#736a#096a
L736a_spirit_count_ui_row_pointers:
#736a#096a
    ; from (14, 152) to (14, 159)
#736a#096a8
    dw #506e, #516e, #526e, #536e
#7372#09728
    dw #546e, #556e, #566e, #576e
#737a#097a
#737a#097a
L737a_strength_ui_row_pointers:
#737a#097a
    ; from (4, 151) to (4, 165)
#737a#097a8
    dw #5744, #5064, #5164, #5264
#7382#09828
    dw #5364, #5464, #5564, #5664
#738a#098a8
    dw #5764, #5084, #5184, #5284
#7392#09926
    dw #5384, #5484, #5584
#7398#0998
#7398#0998
L7398_key_count_ui_row_pointers:
#7398#0998
    ; from (4, 173) to (4, 186)
#7398#09988
    dw #55a4, #56a4, #57a4, #50c4
#73a0#09a08
    dw #51c4, #52c4, #53c4, #54c4
#73a8#09a88
    dw #55c4, #56c4, #57c4, #50e4
#73b0#09b04
    dw #51e4, #52e4
#73b4#09b4
#73b4#09b4
L73b4_waving_flag_row_pointers:
#73b4#09b48
    dw #451d, #461d, #471d, #403d
#73bc#09bc8
    dw #413d, #423d, #433d, #443d
#73c4#09c42
    dw #453d
#73c6#09c6
#73c6#09c6
L73c6_cosine_sine_table:
#73c6#09c6
    ; Each "dw" contains (cos, sin) (one byte each):
#73c6#09c6
    ; [-64, 64]
#73c6#09c6
    ; 72 steps is a whole turn.
#73c6#09c68
    dw #4000, #4006, #3f0b, #3e11
#73ce#09ce8
    dw #3c16, #3a1b, #3720, #3425
#73d6#09d68
    dw #3129, #2d2d, #2931, #2534
#73de#09de8
    dw #2037, #1b3a, #163c, #113e
#73e6#09e68
    dw #0b3f, #0640, #0040, #fa40
#73ee#09ee8
    dw #f53f, #ef3e, #ea3c, #e53a
#73f6#09f68
    dw #e037, #db34, #d731, #d32d
#73fe#09fe8
    dw #cf29, #cc25, #c920, #c61b
#7406#0a068
    dw #c416, #c211, #c10b, #c006
#740e#0a0e8
    dw #c000, #c0fa, #c1f5, #c2ef
#7416#0a168
    dw #c4ea, #c6e5, #c9e0, #ccdb
#741e#0a1e8
    dw #cfd7, #d3d3, #d7cf, #dbcc
#7426#0a268
    dw #e0c9, #e5c6, #eac4, #efc2
#742e#0a2e8
    dw #f5c1, #fac0, #00c0, #06c0
#7436#0a368
    dw #0bc1, #11c2, #16c4, #1bc6
#743e#0a3e8
    dw #20c9, #25cc, #29cf, #2dd3
#7446#0a468
    dw #31d7, #34db, #37e0, #3ae5
#744e#0a4e8
    dw #3cea, #3eef, #3ff5, #40fa
#7456#0a56
#7456#0a56
L7456_player_desired_x:
#7456#0a562
    dw 0
#7458#0a58
L7458_player_desired_y:
#7458#0a582
    dw 0
#745a#0a5a
L745a_player_desired_z:
#745a#0a5a2
    dw 0
#745c#0a5c1
    db #00  ; Unused?
#745d#0a5d
L745d_rendering_cube_volume:  ; max/min x, max/min y, max/min z (objects outside this will not be rendered).
#745d#0a5d6
    db #00, #00, #00, #00, #00, #00
#7463#0a63
L7463_global_area_objects:
#7463#0a632
    dw #d2c6
#7465#0a65
L7465_global_area_n_objects:
#7465#0a651
    db #4b
#7466#0a66
#7466#0a66
L7466_need_attribute_refresh_flag:
#7466#0a661
    db 1
#7467#0a67
L7467_player_starting_position_object_id:
#7467#0a671
    db #01
#7468#0a68
L7468_focus_object_id:
#7468#0a681
    db #01
#7469#0a69
L7469_n_spirits_found_in_current_area:
#7469#0a691
    db #00
#746a#0a6a
L746a_current_drawing_texture_id:
#746a#0a6a1
    db #00
#746b#0a6b
L746b_n_objects_to_draw:
#746b#0a6b1
    db #00
#746c#0a6c
L746c_game_flags:
#746c#0a6c2
    db #fd, #ff  ; 1st byte :
#746e#0a6e
                ; - bit 0: ????
#746e#0a6e
                ; - bit 1: game over indicator.
#746e#0a6e
                ; - bit 2: indicates that we need to "reproject" 3d objects to the 2d viewport.
#746e#0a6e
                ; - bit 3: ????
#746e#0a6e
                ; - bit 4/5: trigger redraw of compass eye.
#746e#0a6e
                ; - bit 6: ????
#746e#0a6e
                ; - bit 7: ????
#746e#0a6e
                ; 2nd byte: 
#746e#0a6e
                ; - bit 0: ????
#746e#0a6e
                ; - bit 1: ????
#746e#0a6e
                ; - bit 2: ????
#746e#0a6e
                ; - bit 3: trigger a re-render.
#746e#0a6e
                ; - bit 4: flag to refresh spirit meter.
#746e#0a6e
                ; - bit 5: in the update function, it triggers waiting until interrupt timer is 0, and then reprints the current room name.
#746e#0a6e
                ; - bit 6: flag to refresh # of keys in UI.
#746e#0a6e
                ; - bit 7: flag to redraw keys in the UI.
#746e#0a6e
L746e_global_rules_ptr:
#746e#0a6e2
    dw #d11f
#7470#0a70
L7470_previous_area_id:  ; Set when a RULE_TYPE_TELEPORT is triggered, but it is unused.
#7470#0a701
    db #00
#7471#0a71
L7471_event_rule_found:  ; 1 indicates that a rule for the corresponding event was found (0 otherwise).
#7471#0a711
    db #00
#7472#0a72
L7472_symbol_shift_pressed:
#7472#0a721
    db 0  ; 0 = not pressed, 1 = pressed.
#7473#0a73
L7473_timer_event:  ; every time L6b22_time_unit6 changes, this is set to 8.
#7473#0a731
    db #00
#7474#0a74
L7474_check_if_object_crushed_player_flag:
#7474#0a741
    db #00
#7475#0a75
L7475_call_Lcba4_check_for_player_falling_flag:  ; Indicates whether we should call Lcba4_check_for_player_falling this game cycle.
#7475#0a751
    db #01
#7476#0a76
L7476_trigger_collision_event_flag:  ; If this is "1" after the player has tried to move, it means we collided with an object.
#7476#0a761
    db #00
#7477#0a77
L7477_render_buffer_effect:
#7477#0a771
    db #02
#7478#0a78
L7478_interrupt_executed_flag:  ; some methods use this to wait for the interrupt to be executed.
#7478#0a781
    db #01
#7479#0a79
L7479_current_game_state:
#7479#0a79
    ; This is:
#7479#0a79
    ; - 0: for when player is controlling
#7479#0a79
    ; - 1: some times game state is 1 even not at game over (e.g. before game starts).
#7479#0a79
    ; - 1-5: when game is over (and number identifies the reason, including successful escape!)
#7479#0a79
    ; - 6: for when in the load/save/quit menu
#7479#0a791
    db #01
#747a#0a7a
L747a_requested_SFX:
#747a#0a7a1
    db #00
#747b#0a7b
L747b:  ; Unused, set to 63 at game start, unused afterwards.
#747b#0a7b1
    db #3f
#747c#0a7c
L747c_within_interrupt_flag:
#747c#0a7c1
    db 0  ; This is changed to #80 when we are inside the interrupt.
#747d#0a7d
#747d#0a7d
L747d:  ; Note: This saves the value of "iy" at game start, and restores it each time tape is accessed.
#747d#0a7d
        ; But it makes no sense, as the tape load/save functions do not make use of iy. Very strange!
#747d#0a7d2
    dw #5c3a
#747f#0a7f
L747f_player_event:  ; player events (they can be "or-ed"): 1: moving, 2: interact, 4: trow rock
#747f#0a7f1
    db #00
#7480#0a80
L7480_under_pointer_object_ID:  ; stores the ID of the object under the player pointer.
#7480#0a801
    db #00
#7481#0a81
L7481_n_objects_covering_the_whole_screen:
#7481#0a811
    db 0
#7482#0a82
; Copy of the projected vertex coordinates used by the Lb607_find_object_under_pointer
#7482#0a82
; function:
#7482#0a82
L7482_object_under_pointer__current_face_vertices:
#7482#0a8210
    db #00, #00, #00, #00, #00, #00, #00, #00, #00, #00
#748c#0a8c10
    db #00, #00, #00, #00, #00, #00, #00, #00, #00, #00
#7496#0a96
L7496_current_drawing_primitive_n_vertices:
#7496#0a961
    db #00
#7497#0a97
L7497_next_projected_vertex_ptr:  ; initialized at 'L67f4_projected_vertex_data', and keeps increasing as we project objects from 3d to 2d.
#7497#0a972
    dw #0000
#7499#0a99
L7499_3d_object_bounding_box_relative_to_player_ptr:
#7499#0a992
    dw #0000
#749b#0a9b
L749b_next_object_projected_data_ptr:
#749b#0a9b2
    dw #0000
#749d#0a9d
L749d_object_currently_being_processed_ptr:
#749d#0a9d2
    dw #0000
#749f#0a9f
L749f_number_of_pressed_keys:
#749f#0a9f1
    db #00
#74a0#0aa0
L74a0_pressed_keys_buffer:
#74a0#0aa05
    db #ef, #4e, #cd, #77, #66
#74a5#0aa5
L74a5_interrupt_timer:
#74a5#0aa51
    db #00  ; Decreases by 1 at each interrupt until reaching 0. It is used by the game to create pauses.
#74a6#0aa6
L74a6_player_movement_delta:
#74a6#0aa66
    dw #0000, #0000, #0000
#74ac#0aac
; These two sets of coordinates are used to define the volume the player will traverse when moving.
#74ac#0aac
; It is used because, due to the low frame rate, the player moves in very large steps, and we need
#74ac#0aac
; to ensure small objects are not skipped.
#74ac#0aac
L74ac_movement_volume_max_coordinate:
#74ac#0aac6
    dw #0000, #0000, #0000
#74b2#0ab2
L74b2_movement_volume_min_coordinate:
#74b2#0ab26
    dw #0000, #0000, #0000
#74b8#0ab8
; These 3 sets of coordinates are used in the "Lab6d_correct_player_movement_if_collision_internal"
#74b8#0ab8
; function to store the target movement position after correcting them in case there is a
#74b8#0ab8
; collision with an object.
#74b8#0ab8
L74b8_collision_corrected_coordinates_2:
#74b8#0ab86
    dw #0000, #0000, #0000
#74be#0abe
L74be_collision_corrected_climb_coordinates:
#74be#0abe6
    dw #0000, #0000, #0000
#74c4#0ac4
L74c4_collision_corrected_coordinates_1:
#74c4#0ac46
    dw #0000, #0000, #0000
#74ca#0aca
L74ca_movement_target_coordinates_2:  ; used when there is falling involved in movement
#74ca#0aca6
    dw #0000, #0000, #0000
#74d0#0ad0
L74d0_target_object_climb_coordinates:
#74d0#0ad02
    dw #0000  ; x
#74d2#0ad22
    dw #0000  ; y
#74d4#0ad42
    dw #0000  ; z
#74d6#0ad6
L74d6_movement_target_coordinates_1:  ; used when there is no falling involved in movement
#74d6#0ad66
    dw #0000, #0000, #0000
#74dc#0adc
L74dc_falling_reference_coordinates:
#74dc#0adc6
    dw #0000, #0000, #0000
#74e2#0ae2
L74e2_movement_direction_bits:
#74e2#0ae2
    ; bit 0 means negative movement on x, bit 1 means positive movement on y
#74e2#0ae2
    ; bits 2, 3 the same for y, and 4, 5 the same for z.
#74e2#0ae21
    db #00
#74e3#0ae3
L74e3_player_height_16bits:  ; (L6ab9_player_height) * 64
#74e3#0ae32
    dw #0000
#74e5#0ae5
L74e5_collision_correction_object_shape_type:  ; the game prefers 3d shapes to 2d shapes when correcting movement upon collisions. This variable is used to implemetn such preference.
#74e5#0ae51
    db #00
#74e6#0ae6
L74e6_movement_involves_falling_flag:  ; 0: no falling, 1: we fell
#74e6#0ae61
    db #00
#74e7#0ae7
L74e7_closest_object_below_distance:
#74e7#0ae72
    dw #0000
#74e9#0ae9
L74e9_closest_object_below_ptr:
#74e9#0ae92
    dw #0000
#74eb#0aeb
L74eb_closest_object_below_ID:
#74eb#0aeb1
    db #00
#74ec#0aec
L74ec_previous_pressed_keys_buffer:
#74ec#0aec5
    db #16, #5e, #3a, #d6, #1f
#74f1#0af1
L74f1:  ; Note: I believe this is unused (written, but never read)
#74f1#0af11
    db #0e
#74f2#0af2
L74f2_keyboard_input:  ; 8 bytes, one per keyboard half row
#74f2#0af28
    db #00, #00, #00, #00, #00, #00, #00, #00
#74fa#0afa
#74fa#0afa
L74fa_object_under_pointer__current_face:  ; saves the pointer to the current face we are checking when trying to find which object is under the pointer.
#74fa#0afa2
    dw #0c67
#74fc#0afc
#74fc#0afc
L74fc_object_under_pointer__projected_xs_at_pointer_y:
#74fc#0afc1
    db #18  ; number of points in the lsit below
#74fd#0afd5
    db #f5, #c6, #30, #23, #77  ; screen x coordinates of face edges at the y coordinate of the pointer.
#7502#0b02
                                ; Used by the "Lb607_find_object_under_pointer" function, to determine
#7502#0b02
                                ; which object is under the player pointer.
#7502#0b02
#7502#0b02
; --------------------------------
#7502#0b02
; Rendering variables:
#7502#0b02
L7502_sp_tmp:  ; Used to temporarily save the 'sp' register.
#7502#0b022
    dw 0
#7504#0b04
L7504_line_drawing_slope:  ; Amount we need to move in the X axis each time we move one pixel in the Y axis.
#7504#0b042
    dw #0000  ; Uses fixed point arithmetic (with 8 bits of decimal part).
#7506#0b06
L7506_polygon_drawing_second_slope:  ; When drawing polygons, we calculate two lines at once, and draw 
#7506#0b062
    dw #0000  ; horizontal lines between them. This is the slopw of the second line.
#7508#0b08
L7508_current_drawing_row:
#7508#0b081
    db #00
#7509#0b09
L7509_line_drawing_thinning_direction:
#7509#0b091
    db #00
#750a#0b0a
L750a_first_loop_flags:  ; Used to indicate if we have drawn at least one pixel when drawing polygons.
#750a#0b0a1
    db #00
#750b#0b0b
L750b_current_drawing_n_vertices_left:  ; How many vertices are there to draw in the current primitive.
#750b#0b0b1
    db 0
#750c#0b0c
L750c_current_drawing_row_ptr:
#750c#0b0c2
    dw 0
#750e#0b0e
L750e_current_drawing_texture_ptr:
#750e#0b0e2
    dw 0
#7510#0b10
L7510_current_drawing_2d_vertex_buffer:
#7510#0b108
    db #00, #00, #00, #00, #00, #00, #00, #00
#7518#0b188
    db #00, #00, #00, #00, #00, #00, #00, #00
#7520#0b208
    db #00, #00, #00, #00, #00, #00, #00, #00
#7528#0b288
    db #00, #00, #00, #00, #00, #00, #00, #00
#7530#0b308
    db #00, #00, #00, #00, #00, #00, #00, #00
#7538#0b38
#7538#0b38
; --------------------------------
#7538#0b38
L7538_text_spaces:
#7538#0b3815
    db 0, "              "
#7547#0b47
L7547_text_play_record:
#7547#0b4715
    db 0, "PLAY & RECORD,"
#7556#0b56
L7556_text_invalid_file:
#7556#0b5615
    db 0, "INVALID FILE  "
#7565#0b65
L7565_text_loading_error:
#7565#0b6515
    db 0, "LOADING ERROR "
#7574#0b74
L7574_text_loading:
#7574#0b7415
    db 0, "LOADING :     "
#7583#0b83
L7583_text_then_any_key:
#7583#0b8315
    db 0, "THEN ANY KEY  "
#7592#0b92
L7592_text_saving_file:
#7592#0b9215
    db 0, "SAVING FILE   "
#75a1#0ba1
L75a1_text_found:
#75a1#0ba115
    db 0, "FOUND :       "
#75b0#0bb0
L75b0_text_searching:
#75b0#0bb015
    db 0, "SEARCHING     "
#75bf#0bbf
#75bf#0bbf
; --------------------------------
#75bf#0bbf
L75bf_SFX_table:
#75bf#0bbf4
    db 30, #59, #00, #01  ; 30,  89 (dw), 1
#75c3#0bc34
    db 18, #47, #00, #01  ; 18,  71 (dw), 1
#75c7#0bc74
    db 1, #ef, #00, #01  ;  1, 239 (dw), 1
#75cb#0bcb4
    db 9, #00, #03, #01  ;  9, 768 (dw), 1
#75cf#0bcf4
    db 2, #00, #00, #01  ;  2,   0 (dw), 1
#75d3#0bd34
    db 8, #50, #00, #01  ;  8,  90 (dw), 1
#75d7#0bd74
    db 26, #00, #03, #03  ; 26, 768 (dw), 3
#75db#0bdb4
    db 11, #02, #00, #01  ; 11,   2 (dw), 1
#75df#0bdf4
    db 30, #59, #00, #01  ; ...
#75e3#0be34
    db 13, #43, #00, #0c
#75e7#0be74
    db 22, #f7, #09, #01
#75eb#0beb4
    db 0, #77, #00, #01
#75ef#0bef4
    db 4, #52, #01, #01
#75f3#0bf34
    db 28, #96, #00, #08
#75f7#0bf74
    db 16, #00, #00, #09
#75fb#0bfb
L75fb_SFX_data:
#75fb#0bfb4
    db #01, #17, #01, #01
#75ff#0bff4
    db #01, #02, #81, #04
#7603#0c034
    db #81, #2e, #00, #01
#7607#0c074
    db #00, #14, #00, #00
#760b#0c0b4
    db #04, #02, #81, #10
#760f#0c0f4
    db #07, #ff, #0a, #34
#7613#0c134
    db #ff, #01, #06, #00
#7617#0c174
    db #02, #00, #00, #00
#761b#0c1b4
    db #01, #7f, #01, #03
#761f#0c1f4
    db #81, #4c, #00, #01
#7623#0c234
    db #00, #f6, #ff, #00
#7627#0c274
    db #81, #20, #00, #02
#762b#0c2b4
    db #00, #30, #00, #00
#762f#0c2f4
    db #03, #06, #00, #01
#7633#0c334
    db #04, #01, #02, #08
#7637#0c374
    db #ff, #01, #00, #00
#763b#0c3b4
    db #80, #07, #02, #00
#763f#0c3f4
    db #00, #00, #00, #00
#7643#0c434
    db #05, #02, #7f, #04
#7647#0c474
    db #02, #7f, #0a, #03
#764b#0c4b4
    db #7f, #0f, #06, #7f
#764f#0c4f4
    db #10, #06, #7f, #22
#7653#0c534
    db #04, #02, #81, #43
#7657#0c574
    db #04, #81, #0a, #08
#765b#0c5b4
    db #81, #0a, #05, #81
#765f#0c5f4
    db #09, #00, #00, #00
#7663#0c634
    db #81, #4c, #00, #01
#7667#0c674
    db #00, #fa, #ff, #00
#766b#0c6b4
    db #02, #04, #7f, #02
#766f#0c6f4
    db #04, #81, #04, #00
#7673#0c734
    db #04, #03, #7f, #03
#7677#0c774
    db #04, #7f, #05, #07
#767b#0c7b4
    db #7f, #06, #06, #00
#767f#0c7f4
    db #04, #00, #00, #00
#7683#0c83
#7683#0c83
L7683_control_mode:
#7683#0c831
    db #00  ; Current control mode:
#7684#0c84
            ; 0: keyboard
#7684#0c84
            ; 1: sinclair joystick
#7684#0c84
            ; 2: kempston joystick
#7684#0c84
            ; 3: cursor joystick
#7684#0c84
#7684#0c84
#7684#0c84
; --------------------------------
#7684#0c84
; Input mapping (table that assigns keys to game functions).
#7684#0c84
L7684_input_mapping:
#7684#0c84
    ; Each row has 3 values: (key, game function while in movement, game function while in pointer)
#7684#0c843
    db "7", INPUT_FORWARD, INPUT_MOVE_POINTER_UP
#7687#0c873
    db "O", INPUT_FORWARD, INPUT_MOVE_POINTER_UP
#768a#0c8a3
    db #91, INPUT_FORWARD, INPUT_MOVE_POINTER_UP
#768d#0c8d3
    db "6", INPUT_BACKWARD, INPUT_MOVE_POINTER_DOWN
#7690#0c903
    db "K", INPUT_BACKWARD, INPUT_MOVE_POINTER_DOWN
#7693#0c933
    db #92, INPUT_BACKWARD, INPUT_MOVE_POINTER_DOWN
#7696#0c963
    db "5", INPUT_TURN_LEFT, INPUT_MOVE_POINTER_LEFT
#7699#0c993
    db "Z", INPUT_TURN_LEFT, INPUT_MOVE_POINTER_LEFT
#769c#0c9c3
    db #93, INPUT_TURN_LEFT, INPUT_MOVE_POINTER_LEFT
#769f#0c9f3
    db "8", INPUT_TURN_RIGHT, INPUT_MOVE_POINTER_RIGHT
#76a2#0ca23
    db "X", INPUT_TURN_RIGHT, INPUT_MOVE_POINTER_RIGHT
#76a5#0ca53
    db #94, INPUT_TURN_RIGHT, INPUT_MOVE_POINTER_RIGHT
#76a8#0ca8
    ; Each row has 2 values: (key, game function)
#76a8#0ca82
    db "0", INPUT_THROW_ROCK
#76aa#0caa2
    db #95, INPUT_THROW_ROCK
#76ac#0cac2
    db "B", INPUT_MOVEMENT_POINTER_ON_OFF
#76ae#0cae2
    db "C", INPUT_CRAWL
#76b0#0cb02
    db "W", INPUT_WALK
#76b2#0cb22
    db "R", INPUT_RUN
#76b4#0cb42
    db " ", INPUT_SWITCH_BETWEEN_MOVEMENT_AND_POINTER
#76b6#0cb62
    db "A", INPUT_ACTION
#76b8#0cb82
    db "U", INPUT_U_TURN
#76ba#0cba2
    db "F", INPUT_FACE_FORWARD
#76bc#0cbc2
    db "P", INPUT_LOOK_UP
#76be#0cbe2
    db "L", INPUT_LOOK_DOWN
#76c0#0cc02
    db "I", INPUT_INFO_MENU
#76c2#0cc2
#76c2#0cc2
; Temporary sprite attribute buffer for method Lcc19_draw_viewport_sprite_with_offset:
#76c2#0cc2
L76c2_buffer_sprite_x:
#76c2#0cc21
    db 0
#76c3#0cc3
L76c3_buffer_sprite_y:
#76c3#0cc31
    db 0
#76c4#0cc4
L76c4_buffer_sprite_width:
#76c4#0cc41
    db 0
#76c5#0cc5
L76c5_buffer_sprite_height:
#76c5#0cc51
    db 0
#76c6#0cc6
L76c6_buffer_sprite_ptr:
#76c6#0cc62
    dw #0000
#76c8#0cc8
L76c8_buffer_sprite_bytes_to_skip_at_start:
#76c8#0cc81
    db #00
#76c9#0cc91
    db #00  ; unused
#76ca#0cca
L76ca_bytes_to_skip_after_row:
#76ca#0cca1
    db #00
#76cb#0ccb1
    db #00  ; unused
#76cc#0ccc
#76cc#0ccc
L76cc_ui_key_bg_sprite:
#76cc#0ccc3
    db 6, 14, #ff  ; width, height, and-mask
#76cf#0ccf2
    dw 84  ; frame size
#76d1#0cd16
    db #63, #ff, #ff, #ff, #ff, #f8
#76d7#0cd76
    db #41, #ff, #ff, #ff, #ff, #f0
#76dd#0cdd6
    db #41, #ff, #ff, #ff, #ff, #f0
#76e3#0ce36
    db #54, #00, #00, #00, #00, #05
#76e9#0ce96
    db #40, #00, #00, #00, #00, #00
#76ef#0cef6
    db #55, #55, #55, #55, #55, #55
#76f5#0cf56
    db #41, #ff, #ff, #ff, #ff, #f0
#76fb#0cfb6
    db #41, #ff, #ff, #ff, #ff, #f0
#7701#0d016
    db #63, #ff, #ff, #ff, #ff, #f8
#7707#0d076
    db #7f, #ff, #ff, #ff, #ff, #ff
#770d#0d0d6
    db #bf, #ff, #ff, #ff, #ff, #ff
#7713#0d136
    db #aa, #ba, #aa, #af, #ba, #a8
#7719#0d196
    db #27, #47, #7e, #aa, #aa, #aa
#771f#0d1f6
    db #55, #55, #75, #55, #55, #55  ; MDL bitmap visualization
#7725#0d25
#7725#0d25
L7725_ui_key_sprite:
#7725#0d253
    db 1, 14, #fc  ; width, height, and-mask
#7728#0d282
    dw 14  ; frame size
#772a#0d2a14
    db #fc, #80, #b8, #08, #08, #f8, #80, #ec, #ec, #ec, #ec, #74, #34, #74  ; MDL bitmap visualization
#7738#0d38
#7738#0d38
L7738_ui_spirit_meter_bg_sprite:
#7738#0d383
    db 8, 8, #ff  ; width, height, and-mask
#773b#0d3b2
    dw 72  ; frame size
#773d#0d3d
           ; Note: this value is wrong, it should be 64, but it does not matter, as there is only one frame in this sprite.
#773d#0d3d8
    db #ff, #ff, #ff, #ff, #ff, #ff, #ff, #ff
#7745#0d458
    db #ff, #ff, #ff, #ff, #ff, #ff, #ff, #ff
#774d#0d4d8
    db #ff, #ff, #ff, #ff, #ff, #ff, #ff, #ff
#7755#0d558
    db #fb, #ff, #ff, #ff, #ff, #ff, #fd, #ff
#775d#0d5d8
    db #ff, #ff, #ff, #ff, #ff, #ff, #df, #ff
#7765#0d658
    db #ff, #ef, #ff, #bf, #ef, #ff, #ff, #bb
#776d#0d6d8
    db #ff, #7f, #f7, #ff, #ff, #fe, #ff, #ff
#7775#0d758
    db #ff, #ff, #ff, #ff, #ff, #ff, #ff, #ff  ; MDL bitmap visualization
#777d#0d7d
#777d#0d7d
L777d_ui_spirit_meter_indicator_sprite:
#777d#0d7d3
    db 2, 8, #fc  ; width, height, and-mask
#7780#0d802
    dw 16  ; frame size
#7782#0d828
    db #f0, #3f, #c0, #0f, #82, #87, #01, #43
#778a#0d8a8
    db #00, #03, #80, #07, #c0, #0f, #f0, #3f  ; MDL bitmap visualization
#7792#0d92
#7792#0d92
L7792_ui_compass_eye_sprites:
#7792#0d923
    db 2, 5, #ff
#7795#0d952
    dw 10
#7797#0d9710
    db #00, #fc, #43, #df, #73, #87, #43, #cf, #00, #fc  ; MDL bitmap visualization
#77a1#0da110
    db #00, #dc, #43, #87, #73, #cf, #43, #ff, #00, #fc  ; MDL bitmap visualization
#77ab#0dab10
    db #00, #fc, #43, #ff, #73, #df, #43, #87, #00, #cc  ; MDL bitmap visualization
#77b5#0db510
    db #00, #fc, #43, #7f, #72, #1f, #43, #3f, #01, #fc  ; MDL bitmap visualization
#77bf#0dbf10
    db #00, #fc, #43, #f7, #73, #e1, #43, #f3, #00, #fc  ; MDL bitmap visualization
#77c9#0dc910
    db #00, #00, #40, #d8, #73, #87, #43, #cf, #00, #fc  ; MDL bitmap visualization
#77d3#0dd310
    db #00, #00, #40, #00, #71, #8c, #43, #cf, #00, #fc  ; MDL bitmap visualization
#77dd#0ddd10
    db #00, #00, #40, #00, #70, #00, #43, #ce, #00, #fc  ; MDL bitmap visualization
#77e7#0de710
    db #00, #00, #40, #00, #70, #00, #40, #00, #00, #00  ; MDL bitmap visualization
#77f1#0df110
    db #00, #fc, #43, #cf, #73, #87, #43, #cf, #00, #fc  ; MDL bitmap visualization
#77fb#0dfb10
    db #00, #fc, #43, #ff, #73, #cf, #43, #cf, #00, #fc  ; MDL bitmap visualization
#7805#0e05
#7805#0e05
L7805_ui_strength_bg_sprite:
#7805#0e053
    db 9, 15, #ff  ; width, height, and-mask
#7808#0e082
    dw 135  ; frame size
#780a#0e0a9
    db #00, #00, #00, #00, #00, #00, #00, #00, #00
#7813#0e139
    db #00, #00, #00, #00, #00, #00, #00, #00, #00
#781c#0e1c9
    db #00, #00, #00, #00, #00, #00, #00, #00, #00
#7825#0e259
    db #00, #00, #00, #00, #00, #00, #00, #00, #00
#782e#0e2e9
    db #00, #00, #00, #00, #00, #00, #00, #00, #00
#7837#0e379
    db #00, #00, #00, #00, #00, #00, #00, #00, #00
#7840#0e409
    db #00, #00, #00, #00, #00, #00, #00, #00, #00
#7849#0e499
    db #00, #00, #00, #00, #00, #00, #00, #00, #00
#7852#0e529
    db #00, #00, #00, #00, #00, #00, #00, #00, #00
#785b#0e5b9
    db #00, #00, #00, #00, #00, #00, #00, #00, #00
#7864#0e649
    db #00, #00, #00, #00, #00, #00, #00, #00, #00
#786d#0e6d9
    db #00, #00, #15, #55, #55, #55, #50, #00, #00
#7876#0e769
    db #0a, #aa, #aa, #aa, #aa, #aa, #aa, #aa, #a0
#787f#0e7f9
    db #1f, #ff, #ff, #ff, #ff, #ff, #ff, #ff, #f8
#7888#0e889
    db #3f, #ff, #ff, #ff, #ff, #ff, #ff, #ff, #fc  ; MDL bitmap visualization
#7891#0e91
#7891#0e91
L7891_ui_strength_bar_sprite:
#7891#0e913
    db 9, 3, #ff
#7894#0e942
    dw 27
#7896#0e969
    db #1f, #ff, #ff, #ff, #ff, #ff, #ff, #ff, #f8
#789f#0e9f9
    db #1f, #ff, #ff, #ff, #ff, #ff, #ff, #ff, #f8
#78a8#0ea89
    db #0a, #aa, #aa, #aa, #aa, #aa, #aa, #aa, #a8  ; MDL bitmap visualization
#78b1#0eb1
#78b1#0eb1
L78b1_ui_strength_weight_sprite:
#78b1#0eb13
    db #01, #0f, #f0
#78b4#0eb42
    dw #000f
#78b6#0eb615
    db #60, #60, #60, #60, #60, #60, #60, #60, #60, #60, #60, #60, #60, #40, #20  ; MDL bitmap visualization
#78c5#0ec515
    db #00, #60, #60, #60, #60, #60, #60, #60, #60, #60, #60, #60, #40, #20, #f0  ; MDL bitmap visualization
#78d4#0ed415
    db #00, #00, #60, #60, #60, #60, #60, #60, #60, #60, #60, #40, #20, #f0, #f0  ; MDL bitmap visualization
#78e3#0ee315
    db #00, #00, #00, #60, #60, #60, #60, #60, #60, #60, #40, #20, #f0, #f0, #f0  ; MDL bitmap visualization
#78f2#0ef2
#78f2#0ef2
L78f2_background_mountains_gfx:
#78f2#0ef216
    db #06, #00, #00, #10, #00, #00, #00, #00, #00, #00, #0c, #00, #00, #00, #00, #00
#7902#0f0216
    db #0b, #00, #00, #38, #01, #a0, #00, #00, #00, #00, #1e, #30, #00, #00, #00, #00
#7912#0f1216
    db #17, #80, #00, #7c, #03, #d0, #00, #00, #01, #00, #3f, #5c, #80, #00, #18, #00
#7922#0f2216
    db #23, #c0, #00, #fe, #0d, #fe, #06, #00, #02, #80, #7e, #be, #c0, #00, #3c, #00
#7932#0f3216
    db #47, #e0, #01, #57, #56, #ff, #0f, #80, #05, #40, #f5, #fb, #40, #00, #76, #00
#7942#0f4216
    db #93, #d0, #0a, #ab, #ab, #ff, #af, #c3, #2a, #a1, #eb, #fe, #a8, #00, #de, #00
#7952#0f5216
    db #21, #a8, #75, #55, #41, #ff, #d6, #ef, #d5, #53, #57, #fc, #14, #01, #b7, #07
#7962#0f6216
    db #42, #d5, #ea, #aa, #92, #fb, #eb, #ab, #ea, #aa, #ae, #fa, #4a, #82, #ea, #be
#7972#0f7216
    db #97, #ab, #d5, #55, #25, #dd, #75, #45, #f5, #55, #7d, #dd, #25, #55, #d5, #54
#7982#0f8216
    db #2f, #f7, #aa, #aa, #53, #ea, #a8, #13, #fa, #aa, #ea, #be, #42, #ab, #aa, #a9
#7992#0f9216
    db #5f, #dd, #d5, #55, #07, #55, #02, #45, #fd, #51, #55, #57, #15, #57, #d5, #52
#79a2#0fa216
    db #af, #ee, #fa, #aa, #2b, #aa, #80, #8b, #fe, #aa, #aa, #ae, #aa, #be, #aa, #a4
#79b2#0fb216
    db #5a, #b5, #5d, #5c, #56, #d5, #29, #1f, #ff, #55, #55, #5b, #55, #7d, #55, #09
#79c2#0fc216
    db #b5, #5a, #af, #ba, #ad, #aa, #92, #3e, #bf, #ea, #aa, #af, #ab, #ea, #aa, #02
#79d2#0fd216
    db #5a, #f5, #55, #fd, #57, #55, #05, #5f, #57, #fd, #55, #55, #57, #55, #50, #15
#79e2#0fe216
    db #af, #ba, #aa, #fe, #ae, #fa, #aa, #be, #aa, #bf, #aa, #aa, #aa, #aa, #82, #aa
#79f2#0ff216
    db #55, #55, #55, #5f, #d5, #fd, #55, #55, #55, #5f, #fd, #55, #55, #55, #55, #55
#7a02#100216
    db #ea, #aa, #aa, #aa, #aa, #aa, #aa, #aa, #aa, #aa, #ff, #ff, #ff, #ff, #ff, #ff  ; MDL bitmap visualization
#7a12#1012
#7a12#1012
L7a12_waving_flag_gfx_properties:
#7a12#10123
    db 3, 9, #ff  ; width (in bytes), height, and-mask of last byte
#7a15#10152
    dw 27  ; 3 * 9 (bytes of each frame)
#7a17#1017
    ; Waving flag frame 1:
#7a17#10173
    db #00, #03, #f6
#7a1a#101a3
    db #00, #07, #fe
#7a1d#101d3
    db #07, #ff, #f6
#7a20#10203
    db #01, #ff, #f6
#7a23#10233
    db #00, #7f, #f6
#7a26#10263
    db #00, #1f, #f6
#7a29#10293
    db #01, #ff, #f6
#7a2c#102c3
    db #03, #ff, #7e
#7a2f#102f3
    db #03, #9f, #06  ; MDL bitmap visualization
#7a32#1032
    ; Waving flag frame 2:
#7a32#10323
    db #00, #1f, #06
#7a35#10353
    db #00, #3f, #fe
#7a38#10383
    db #0f, #ff, #f6
#7a3b#103b3
    db #01, #ff, #f6
#7a3e#103e3
    db #00, #7f, #f6
#7a41#10413
    db #00, #3f, #f6
#7a44#10443
    db #01, #ff, #f6
#7a47#10473
    db #03, #f9, #fe
#7a4a#104a3
    db #0f, #f0, #06  ; MDL bitmap visualization
#7a4d#104d
    ; Waving flag frame 3:
#7a4d#104d3
    db #00, #7e, #06
#7a50#10503
    db #01, #ff, #1e
#7a53#10533
    db #07, #ff, #f6
#7a56#10563
    db #00, #ff, #f6
#7a59#10593
    db #00, #3f, #f6
#7a5c#105c3
    db #01, #ff, #f6
#7a5f#105f3
    db #07, #ff, #f6
#7a62#10623
    db #0f, #87, #fe
#7a65#10653
    db #00, #01, #e6  ; MDL bitmap visualization
#7a68#1068
    ; Waving flag frame 4:    
#7a68#10683
    db #00, #00, #7e
#7a6b#106b3
    db #01, #fd, #f6
#7a6e#106e3
    db #07, #ff, #f6
#7a71#10713
    db #00, #7f, #f6
#7a74#10743
    db #00, #1f, #f6
#7a77#10773
    db #00, #3f, #f6
#7a7a#107a3
    db #00, #ff, #f6
#7a7d#107d3
    db #07, #ff, #fe
#7a80#10803
    db #0f, #1f, #06  ; MDL bitmap visualization
#7a83#1083
#7a83#1083
; --------------------------------
#7a83#1083
; Unused graphics?
#7a83#1083
L7a83:
#7a83#10833
    db #0f, #ff, #f0
#7a86#10863
    db #08, #00, #10
#7a89#10893
    db #09, #ff, #90
#7a8c#108c3
    db #09, #00, #90  ; MDL bitmap visualization
#7a8f#108f
#7a8f#108f
L7a8f:
#7a8f#108f5
    db #01, #07, #ff, #07, #00  ; MDL bitmap visualization
#7a94#1094
L7a94:
#7a94#10947
    db #7e, #c3, #81, #81, #81, #c3, #7e  ; MDL bitmap visualization
#7a9b#109b
#7a9b#109b
; --------------------------------
#7a9b#109b
L7a9b_lightning_gfx:
#7a9b#109b4
    db #00, #40, #00, #40
#7a9f#109f4
    db #00, #40, #00, #40
#7aa3#10a34
    db #00, #40, #00, #40
#7aa7#10a74
    db #00, #40, #00, #80
#7aab#10ab4
    db #01, #80, #01, #00
#7aaf#10af4
    db #01, #00, #02, #00
#7ab3#10b34
    db #04, #00, #08, #00
#7ab7#10b74
    db #18, #00, #10, #00
#7abb#10bb4
    db #30, #00, #20, #00
#7abf#10bf4
    db #20, #00, #20, #00
#7ac3#10c34
    db #70, #00, #50, #00
#7ac7#10c74
    db #50, #00, #88, #00
#7acb#10cb4
    db #08, #00, #04, #00
#7acf#10cf4
    db #02, #00, #02, #00
#7ad3#10d34
    db #01, #00, #01, #00
#7ad7#10d74
    db #01, #00, #00, #80
#7adb#10db4
    db #00, #c0, #00, #40
#7adf#10df4
    db #00, #20, #00, #10
#7ae3#10e34
    db #00, #08, #00, #0c
#7ae7#10e74
    db #00, #1c, #00, #32
#7aeb#10eb4
    db #00, #22, #00, #c2
#7aef#10ef4
    db #01, #81, #02, #01
#7af3#10f34
    db #02, #00, #02, #00
#7af7#10f74
    db #02, #00, #02, #00
#7afb#10fb4
    db #02, #00, #02, #00
#7aff#10ff4
    db #06, #00, #04, #00
#7b03#11034
    db #04, #00, #0c, #00
#7b07#11074
    db #18, #00, #30, #00
#7b0b#110b4
    db #20, #00, #20, #00
#7b0f#110f4
    db #70, #00, #4c, #00
#7b13#11134
    db #86, #00, #02, #00
#7b17#11174
    db #01, #00, #01, #00
#7b1b#111b4
    db #00, #80, #00, #80
#7b1f#111f4
    db #00, #80, #00, #80
#7b23#11234
    db #00, #80, #00, #80
#7b27#11274
    db #00, #80, #00, #60
#7b2b#112b4
    db #00, #30, #00, #08
#7b2f#112f4
    db #00, #08, #00, #06
#7b33#11334
    db #00, #02, #00, #02
#7b37#11374
    db #00, #02, #00, #02
#7b3b#113b4
    db #00, #02, #00, #02
#7b3f#113f4
    db #00, #03, #00, #01
#7b43#11434
    db #00, #01, #00, #00  ; MDL bitmap visualization
#7b47#1147
#7b47#1147
#7b47#1147
; --------------------------------
#7b47#1147
; Each block of 8 bytes correspods to a character. So, this includes
#7b47#1147
; a definition of the font being used, the first character is ' ':
#7b47#1147
; these tags inside comments are used to visualize the gfx using MDL with the "-mdl-asm+:html" flag.
#7b47#1147
L7b47_font:
#7b47#11478
    db #00, #00, #00, #00, #00, #00, #00, #00  ; MDL bitmap visualization
#7b4f#114f8
    db #1c, #1c, #1c, #18, #18, #00, #18, #18  ; MDL bitmap visualization
#7b57#11578
    db #66, #66, #44, #22, #00, #00, #00, #00  ; MDL bitmap visualization
#7b5f#115f8
    db #00, #7f, #7f, #7f, #7f, #7f, #7f, #00  ; MDL bitmap visualization
#7b67#11678
    db #10, #54, #38, #fe, #38, #54, #10, #00  ; MDL bitmap visualization
#7b6f#116f8
    db #3c, #42, #9d, #b1, #b1, #9d, #42, #3c  ; MDL bitmap visualization
#7b77#11778
    db #78, #cc, #cc, #78, #db, #cf, #ce, #7b  ; MDL bitmap visualization
#7b7f#117f8
    db #30, #30, #10, #20, #00, #00, #00, #00  ; MDL bitmap visualization
#7b87#11878
    db #10, #20, #40, #40, #40, #40, #20, #10  ; MDL bitmap visualization
#7b8f#118f8
    db #10, #08, #04, #04, #04, #04, #08, #10  ; MDL bitmap visualization
#7b97#11978
    db #10, #54, #38, #fe, #38, #54, #10, #00  ; MDL bitmap visualization
#7b9f#119f8
    db #00, #00, #10, #10, #7c, #10, #10, #00  ; MDL bitmap visualization
#7ba7#11a78
    db #00, #00, #00, #00, #18, #18, #08, #10  ; MDL bitmap visualization
#7baf#11af8
    db #00, #00, #00, #00, #3c, #00, #00, #00  ; MDL bitmap visualization
#7bb7#11b78
    db #00, #00, #00, #00, #00, #00, #18, #18  ; MDL bitmap visualization
#7bbf#11bf8
    db #01, #02, #04, #08, #10, #20, #40, #80  ; MDL bitmap visualization
#7bc7#11c78
    db #18, #66, #c3, #c3, #c3, #c3, #66, #18  ; MDL bitmap visualization
#7bcf#11cf8
    db #18, #38, #18, #18, #18, #18, #18, #18  ; MDL bitmap visualization
#7bd7#11d78
    db #9e, #61, #01, #7e, #e0, #c6, #e3, #fe  ; MDL bitmap visualization
#7bdf#11df8
    db #ee, #73, #03, #3e, #03, #01, #7f, #e6  ; MDL bitmap visualization
#7be7#11e78
    db #0e, #1c, #38, #71, #fd, #e6, #0c, #0c  ; MDL bitmap visualization
#7bef#11ef8
    db #fd, #86, #80, #7e, #07, #63, #c7, #7c  ; MDL bitmap visualization
#7bf7#11f78
    db #3d, #66, #c0, #f0, #fc, #c6, #66, #3c  ; MDL bitmap visualization
#7bff#11ff8
    db #b3, #4e, #06, #0c, #0c, #18, #18, #3c  ; MDL bitmap visualization
#7c07#12078
    db #7c, #c6, #c6, #7c, #c6, #c2, #fe, #4c  ; MDL bitmap visualization
#7c0f#120f8
    db #3c, #4e, #c6, #c6, #4e, #36, #46, #3c  ; MDL bitmap visualization
#7c17#12178
    db #00, #18, #18, #00, #00, #18, #18, #00  ; MDL bitmap visualization
#7c1f#121f8
    db #00, #18, #18, #00, #00, #18, #08, #10  ; MDL bitmap visualization
#7c27#12278
    db #03, #0c, #30, #c0, #30, #0c, #03, #00  ; MDL bitmap visualization
#7c2f#122f8
    db #00, #00, #ff, #00, #ff, #00, #00, #00  ; MDL bitmap visualization
#7c37#12378
    db #c0, #30, #0c, #03, #0c, #30, #c0, #00  ; MDL bitmap visualization
#7c3f#123f8
    db #7c, #c6, #06, #0c, #30, #30, #00, #30  ; MDL bitmap visualization
#7c47#12478
    db #00, #08, #0c, #fe, #ff, #fe, #0c, #08  ; MDL bitmap visualization
#7c4f#124f8
    db #1e, #1c, #1e, #66, #be, #26, #43, #e3  ; MDL bitmap visualization
#7c57#12578
    db #ee, #73, #23, #3e, #23, #21, #7f, #e6  ; MDL bitmap visualization
#7c5f#125f8
    db #39, #6e, #c6, #c0, #c0, #c2, #63, #3e  ; MDL bitmap visualization
#7c67#12678
    db #ec, #72, #23, #23, #23, #23, #72, #ec  ; MDL bitmap visualization
#7c6f#126f8
    db #ce, #7f, #61, #6c, #78, #61, #7f, #ce  ; MDL bitmap visualization
#7c77#12778
    db #ce, #7f, #61, #6c, #78, #60, #60, #f0  ; MDL bitmap visualization
#7c7f#127f8
    db #3d, #66, #c0, #c1, #ce, #c6, #66, #3c  ; MDL bitmap visualization
#7c87#12878
    db #e7, #66, #66, #6e, #76, #66, #66, #e7  ; MDL bitmap visualization
#7c8f#128f8
    db #66, #3c, #18, #18, #18, #18, #3c, #66  ; MDL bitmap visualization
#7c97#12978
    db #33, #1e, #0c, #8c, #4c, #cc, #dc, #78  ; MDL bitmap visualization
#7c9f#129f8
    db #f2, #67, #64, #68, #7e, #66, #66, #f3  ; MDL bitmap visualization
#7ca7#12a78
    db #d8, #70, #60, #60, #66, #61, #f3, #7e  ; MDL bitmap visualization
#7caf#12af8
    db #c3, #66, #6e, #76, #56, #46, #46, #ef  ; MDL bitmap visualization
#7cb7#12b78
    db #87, #62, #72, #7a, #5e, #4e, #46, #e1  ; MDL bitmap visualization
#7cbf#12bf8
    db #18, #66, #c3, #c3, #c3, #c3, #66, #18  ; MDL bitmap visualization
#7cc7#12c78
    db #ec, #72, #63, #63, #72, #6c, #60, #f0  ; MDL bitmap visualization
#7ccf#12cf8
    db #3c, #66, #c3, #c3, #66, #3c, #31, #1e  ; MDL bitmap visualization
#7cd7#12d78
    db #ec, #72, #63, #63, #76, #6c, #66, #f1  ; MDL bitmap visualization
#7cdf#12df8
    db #79, #86, #80, #7e, #07, #63, #c7, #7c  ; MDL bitmap visualization
#7ce7#12e78
    db #01, #7f, #fe, #98, #58, #18, #18, #3c  ; MDL bitmap visualization
#7cef#12ef8
    db #f7, #62, #62, #62, #62, #62, #f2, #3c  ; MDL bitmap visualization
#7cf7#12f78
    db #f3, #61, #72, #72, #32, #32, #1c, #3e  ; MDL bitmap visualization
#7cff#12ff8
    db #c3, #62, #62, #6a, #6e, #76, #66, #c3  ; MDL bitmap visualization
#7d07#13078
    db #f3, #72, #3c, #38, #1c, #3c, #4e, #cf  ; MDL bitmap visualization
#7d0f#130f8
    db #e3, #72, #34, #38, #18, #18, #18, #3c  ; MDL bitmap visualization
#7d17#13178
    db #7f, #87, #0e, #1c, #38, #71, #fd, #e6  ; MDL bitmap visualization
#7d1f#131f
#7d1f#131f
#7d1f#131f
; --------------------------------
#7d1f#131f
L7d1f_text_save_load_quit:
#7d1f#131f21
    db 0, "S-SAVE L-LOAD Q-QUIT"
#7d34#1334
L7d34_text_keys:
#7d34#13345
    db 0, "KEYS"
#7d39#1339
L7d39_text_spirits:
#7d39#13398
    db 0, "SPIRITS"
#7d41#1341
L7d41_text_strength:
#7d41#13419
    db 0, "STRENGTH"
#7d4a#134a
L7d4a_text_collected:
#7d4a#134a16
    db 0, " XX COLLECTED  "
#7d5a#135a
L7d5a_text_destroyed:
#7d5a#135a16
    db 0, " XX DESTROYED  "
#7d6a#136a
L7d6a_text_score:
#7d6a#136a16
    db 1, "SCORE  XXXXXXX "
#7d7a#137a
L7d7a_text_filename:
#7d7a#137a14
    db 0, "FILENAME :   "
#7d88#1388
#7d88#1388
L7d88_action_pointer_viewport_sprite:
#7d88#13884
    db 0, 0, 2, 9
#7d8c#138c2
    dw L7e3e  ; empty buffer
#7d8e#138e8
    dw L7dae, L7dc0, L7dd2, L7de4  ; pointer mask
#7d96#13968
    dw L7df6, L7e08, L7e1a, L7e2c
#7d9e#139e8
    dw L7e50, L7e62, L7e74, L7e86  ; action/throw pointer
#7da6#13a68
    dw L7e98, L7eaa, L7ebc, L7ece
#7dae#13ae
L7dae:
#7dae#13ae6
    db #e3, #ff, #e3, #ff, #e3, #ff
#7db4#13b46
    db #1c, #7f, #1c, #7f, #1c, #7f
#7dba#13ba6
    db #e3, #ff, #e3, #ff, #e3, #ff  ; MDL bitmap visualization 
#7dc0#13c0
L7dc0:
#7dc0#13c06
    db #f1, #ff, #f1, #ff, #f1, #ff
#7dc6#13c66
    db #8e, #3f, #8e, #3f, #8e, #3f
#7dcc#13cc6
    db #f1, #ff, #f1, #ff, #f1, #ff  ; MDL bitmap visualization 
#7dd2#13d2
L7dd2:
#7dd2#13d26
    db #f8, #ff, #f8, #ff, #f8, #ff
#7dd8#13d86
    db #c7, #1f, #c7, #1f, #c7, #1f
#7dde#13de6
    db #f8, #ff, #f8, #ff, #f8, #ff  ; MDL bitmap visualization 
#7de4#13e4
L7de4:
#7de4#13e46
    db #fc, #7f, #fc, #7f, #fc, #7f
#7dea#13ea6
    db #e3, #8f, #e3, #8f, #e3, #8f
#7df0#13f06
    db #fc, #7f, #fc, #7f, #fc, #7f  ; MDL bitmap visualization 
#7df6#13f6
L7df6:
#7df6#13f66
    db #fe, #3f, #fe, #3f, #fe, #3f
#7dfc#13fc6
    db #f1, #c7, #f1, #c7, #f1, #c7
#7e02#14026
    db #fe, #3f, #fe, #3f, #fe, #3f  ; MDL bitmap visualization 
#7e08#1408
L7e08:
#7e08#14086
    db #ff, #1f, #ff, #1f, #ff, #1f
#7e0e#140e6
    db #f8, #e3, #f8, #e3, #f8, #e3
#7e14#14146
    db #ff, #1f, #ff, #1f, #ff, #1f  ; MDL bitmap visualization 
#7e1a#141a
L7e1a:
#7e1a#141a6
    db #ff, #8f, #ff, #8f, #ff, #8f
#7e20#14206
    db #fc, #71, #fc, #71, #fc, #71
#7e26#14266
    db #ff, #8f, #ff, #8f, #ff, #8f  ; MDL bitmap visualization 
#7e2c#142c
L7e2c:
#7e2c#142c6
    db #ff, #c7, #ff, #c7, #ff, #c7
#7e32#14326
    db #fe, #38, #fe, #38, #fe, #38
#7e38#14386
    db #ff, #c7, #ff, #c7, #ff, #c7  ; MDL bitmap visualization 
#7e3e#143e
L7e3e:
#7e3e#143e6
    db #00, #00, #00, #00, #00, #00
#7e44#14446
    db #00, #00, #00, #00, #00, #00
#7e4a#144a6
    db #00, #00, #00, #00, #00, #00  ; MDL bitmap visualization 
#7e50#1450
L7e50:
#7e50#14506
    db #14, #00, #14, #00, #14, #00
#7e56#14566
    db #00, #00, #e3, #80, #00, #00
#7e5c#145c6
    db #14, #00, #14, #00, #14, #00  ; MDL bitmap visualization 
#7e62#1462
L7e62:
#7e62#14626
    db #0a, #00, #0a, #00, #0a, #00
#7e68#14686
    db #00, #00, #71, #c0, #00, #00
#7e6e#146e6
    db #0a, #00, #0a, #00, #0a, #00  ; MDL bitmap visualization 
#7e74#1474
L7e74:
#7e74#14746
    db #05, #00, #05, #00, #05, #00
#7e7a#147a6
    db #00, #00, #38, #e0, #00, #00
#7e80#14806
    db #05, #00, #05, #00, #05, #00  ; MDL bitmap visualization 
#7e86#1486
L7e86:
#7e86#14866
    db #02, #80, #02, #80, #02, #80
#7e8c#148c6
    db #00, #00, #1c, #70, #00, #00
#7e92#14926
    db #02, #80, #02, #80, #02, #80  ; MDL bitmap visualization 
#7e98#1498
L7e98:
#7e98#14986
    db #01, #40, #01, #40, #01, #40
#7e9e#149e6
    db #00, #00, #0e, #38, #00, #00
#7ea4#14a46
    db #01, #40, #01, #40, #01, #40  ; MDL bitmap visualization 
#7eaa#14aa
L7eaa:
#7eaa#14aa6
    db #00, #a0, #00, #a0, #00, #a0
#7eb0#14b06
    db #00, #00, #07, #1c, #00, #00
#7eb6#14b66
    db #00, #a0, #00, #a0, #00, #a0  ; MDL bitmap visualization 
#7ebc#14bc
L7ebc:
#7ebc#14bc6
    db #00, #50, #00, #50, #00, #50
#7ec2#14c26
    db #00, #00, #03, #8e, #00, #00
#7ec8#14c86
    db #00, #50, #00, #50, #00, #50  ; MDL bitmap visualization 
#7ece#14ce
L7ece:
#7ece#14ce6
    db #00, #28, #00, #28, #00, #28
#7ed4#14d46
    db #00, #00, #01, #c7, #00, #00
#7eda#14da6
    db #00, #28, #00, #28, #00, #28  ; MDL bitmap visualization 
#7ee0#14e0
#7ee0#14e0
#7ee0#14e0
L7ee0_stone_viewport_sprite_size1:
#7ee0#14e04
    db 0, 0, 2, 4
#7ee4#14e42
    dw L7f16  ; empty buffer
#7ee6#14e68
    dw L7f06, L7f06, L7f06, L7f06
#7eee#14ee8
    dw L7f06, L7f06, L7f06, L7f06
#7ef6#14f68
    dw L7f0e, L7f0e, L7f0e, L7f0e
#7efe#14fe8
    dw L7f0e, L7f0e, L7f0e, L7f0e
#7f06#1506
L7f06:
#7f06#15068
    db #00, #3f, #00, #3f, #80, #7f, #c0, #ff  ; MDL bitmap visualization 
#7f0e#150e
L7f0e:
#7f0e#150e8
    db #ff, #80, #ff, #80, #7f, #00, #3e, #00  ; MDL bitmap visualization 
#7f16#1516
L7f16:
#7f16#15168
    db #00, #00, #00, #00, #00, #00, #00, #00  ; MDL bitmap visualization 
#7f1e#151e
#7f1e#151e
#7f1e#151e
L7f1e_stone_viewport_sprite_size2:
#7f1e#151e4
    db 0, 0, 2, 5
#7f22#15222
    dw L7fe4  ; empty buffer
#7f24#15248
    dw L7f44, L7f4e, L7f58, L7f62
#7f2c#152c8
    dw L7f6c, L7f76, L7f80, L7f8a
#7f34#15348
    dw L7f94, L7f9e, L7fa8, L7fb2
#7f3c#153c8
    dw L7fbc, L7fc6, L7fd0, L7fda
#7f44#1544
L7f44:
#7f44#154410
    db #c1, #ff, #80, #ff, #00, #7f, #80, #ff, #c1, #ff  ; MDL bitmap visualization 
#7f4e#154e
L7f4e:
#7f4e#154e10
    db #e0, #ff, #c0, #7f, #80, #3f, #c0, #7f, #e0, #ff  ; MDL bitmap visualization 
#7f58#1558
L7f58:
#7f58#155810
    db #f0, #7f, #e0, #3f, #c0, #1f, #e0, #3f, #f0, #7f  ; MDL bitmap visualization 
#7f62#1562
L7f62:
#7f62#156210
    db #f8, #3f, #f0, #1f, #e0, #0f, #f0, #1f, #f8, #3f  ; MDL bitmap visualization 
#7f6c#156c
L7f6c:
#7f6c#156c10
    db #fc, #1f, #f8, #0f, #f0, #07, #f8, #0f, #fc, #1f  ; MDL bitmap visualization 
#7f76#1576
L7f76:
#7f76#157610
    db #fe, #0f, #fc, #07, #f8, #03, #fc, #07, #fe, #0f  ; MDL bitmap visualization 
#7f80#1580
L7f80:
#7f80#158010
    db #ff, #07, #fe, #03, #fc, #01, #fe, #03, #ff, #07  ; MDL bitmap visualization 
#7f8a#158a
L7f8a:
#7f8a#158a10
    db #ff, #83, #ff, #01, #fe, #00, #ff, #01, #ff, #83  ; MDL bitmap visualization 
#7f94#1594
L7f94:
#7f94#159410
    db #3c, #00, #7e, #00, #ff, #00, #7e, #00, #3c, #00  ; MDL bitmap visualization 
#7f9e#159e
L7f9e:
#7f9e#159e10
    db #1e, #00, #3f, #00, #7f, #80, #3f, #00, #1e, #00  ; MDL bitmap visualization 
#7fa8#15a8
L7fa8:
#7fa8#15a810
    db #0f, #00, #1f, #80, #3f, #c0, #1f, #80, #0f, #00  ; MDL bitmap visualization 
#7fb2#15b2
L7fb2:
#7fb2#15b210
    db #07, #80, #0f, #c0, #1f, #e0, #0f, #c0, #07, #80  ; MDL bitmap visualization 
#7fbc#15bc
L7fbc:
#7fbc#15bc10
    db #03, #c0, #07, #e0, #0f, #f0, #07, #e0, #03, #c0  ; MDL bitmap visualization 
#7fc6#15c6
L7fc6:
#7fc6#15c610
    db #01, #e0, #03, #f0, #07, #f8, #03, #f0, #01, #e0  ; MDL bitmap visualization 
#7fd0#15d0
L7fd0:
#7fd0#15d010
    db #00, #f0, #01, #f8, #03, #fc, #01, #f8, #00, #f0  ; MDL bitmap visualization 
#7fda#15da
L7fda:
#7fda#15da10
    db #00, #78, #00, #fc, #01, #fe, #00, #fc, #00, #78  ; MDL bitmap visualization 
#7fe4#15e4
L7fe4:
#7fe4#15e410
    db #00, #00, #00, #00, #00, #00, #00, #00, #00, #00  ; MDL bitmap visualization 
#7fee#15ee
#7fee#15ee
L7fee_stone_viewport_sprite_size3:
#7fee#15ee4
    db 0, 0, 2, 4
#7ff2#15f22
    dw L8094  ; empty buffer
#7ff4#15f48
    dw L8014, L801c, L8024, L802c
#7ffc#15fc8
    dw L8034, L803c, L8044, L804c
#8004#16048
    dw L8054, L805c, L8064, L806c
#800c#160c8
    dw L8074, L807c, L8084, L808c
#8014#1614
L8014:
#8014#16148
    db #87, #ff, #03, #ff, #03, #ff, #87, #ff  ; MDL bitmap visualization 
#801c#161c
L801c:
#801c#161c8
    db #c3, #ff, #81, #ff, #81, #ff, #c3, #ff  ; MDL bitmap visualization 
#8024#1624
L8024:
#8024#16248
    db #e1, #ff, #c0, #ff, #c0, #ff, #e1, #ff  ; MDL bitmap visualization 
#802c#162c
L802c:
#802c#162c8
    db #f0, #ff, #e0, #7f, #e0, #7f, #f0, #ff  ; MDL bitmap visualization 
#8034#1634
L8034:
#8034#16348
    db #f8, #7f, #f0, #3f, #f0, #3f, #f8, #7f  ; MDL bitmap visualization 
#803c#163c
L803c:
#803c#163c8
    db #fc, #3f, #f8, #1f, #f8, #1f, #fc, #3f  ; MDL bitmap visualization 
#8044#1644
L8044:
#8044#16448
    db #fe, #1f, #fc, #0f, #fc, #0f, #fe, #1f  ; MDL bitmap visualization 
#804c#164c
L804c:
#804c#164c8
    db #ff, #0f, #fe, #07, #fe, #07, #ff, #0f  ; MDL bitmap visualization 
#8054#1654
L8054:
#8054#16548
    db #70, #00, #f8, #00, #f8, #00, #70, #00  ; MDL bitmap visualization 
#805c#165c
L805c:
#805c#165c8
    db #38, #00, #7c, #00, #7c, #00, #38, #00  ; MDL bitmap visualization 
#8064#1664
L8064:
#8064#16648
    db #1c, #00, #3e, #00, #3e, #00, #1c, #00  ; MDL bitmap visualization 
#806c#166c
L806c:
#806c#166c8
    db #0e, #00, #1f, #00, #1f, #00, #0e, #00  ; MDL bitmap visualization 
#8074#1674
L8074:
#8074#16748
    db #07, #00, #0f, #80, #0f, #80, #07, #00  ; MDL bitmap visualization 
#807c#167c
L807c:
#807c#167c8
    db #03, #80, #07, #c0, #07, #c0, #03, #80  ; MDL bitmap visualization 
#8084#1684
L8084:
#8084#16848
    db #01, #c0, #03, #e0, #03, #e0, #01, #c0  ; MDL bitmap visualization 
#808c#168c
L808c:
#808c#168c8
    db #00, #e0, #01, #f0, #01, #f0, #00, #e0  ; MDL bitmap visualization 
#8094#1694
L8094:
#8094#16948
    db #00, #00, #00, #00, #00, #00, #00, #00  ; MDL bitmap visualization 
#809c#169c
#809c#169c
L909c_stone_viewport_sprite_size4:
#809c#169c4
    db 0, 0, 2, 3
#80a0#16a02
    dw L8122  ; empty buffer
#80a2#16a28
    dw L80c2, L80c8, L80ce, L80d4
#80aa#16aa8
    dw L80da, L80e0, L80e6, L80ec
#80b2#16b28
    dw L80f2, L80f8, L80fe, L8104
#80ba#16ba8
    dw L810a, L8110, L8116, L811c
#80c2#16c2
L80c2:
#80c2#16c26
    db #8f, #ff, #07, #ff, #8f, #ff  ; MDL bitmap visualization 
#80c8#16c8
L80c8:
#80c8#16c86
    db #c7, #ff, #83, #ff, #c7, #ff  ; MDL bitmap visualization 
#80ce#16ce
L80ce:
#80ce#16ce6
    db #e3, #ff, #c1, #ff, #e3, #ff  ; MDL bitmap visualization 
#80d4#16d4
L80d4:
#80d4#16d46
    db #f1, #ff, #e0, #ff, #f1, #ff  ; MDL bitmap visualization 
#80da#16da
L80da:
#80da#16da6
    db #f8, #ff, #f0, #7f, #f8, #ff  ; MDL bitmap visualization 
#80e0#16e0
L80e0:
#80e0#16e06
    db #fc, #7f, #f8, #3f, #fc, #7f  ; MDL bitmap visualization 
#80e6#16e6
L80e6:
#80e6#16e66
    db #fe, #3f, #fc, #1f, #fe, #3f  ; MDL bitmap visualization 
#80ec#16ec
L80ec:
#80ec#16ec6
    db #ff, #1f, #fe, #0f, #ff, #1f  ; MDL bitmap visualization 
#80f2#16f2
L80f2:
#80f2#16f26
    db #60, #00, #f0, #00, #60, #00  ; MDL bitmap visualization 
#80f8#16f8
L80f8:
#80f8#16f86
    db #30, #00, #78, #00, #30, #00  ; MDL bitmap visualization 
#80fe#16fe
L80fe:
#80fe#16fe6
    db #18, #00, #3c, #00, #18, #00  ; MDL bitmap visualization 
#8104#1704
L8104:
#8104#17046
    db #0c, #00, #1e, #00, #0c, #00  ; MDL bitmap visualization 
#810a#170a
L810a:
#810a#170a6
    db #06, #00, #0f, #00, #06, #00  ; MDL bitmap visualization 
#8110#1710
L8110:
#8110#17106
    db #03, #00, #07, #80, #03, #00  ; MDL bitmap visualization 
#8116#1716
L8116:
#8116#17166
    db #01, #80, #03, #c0, #01, #80  ; MDL bitmap visualization 
#811c#171c
L811c:
#811c#171c6
    db #00, #c0, #01, #e0, #00, #c0  ; MDL bitmap visualization 
#8122#1722
L8122:
#8122#17226
    db #00, #00, #00, #00, #00, #00  ; MDL bitmap visualization 
#8128#1728
#8128#1728
#8128#1728
; --------------------------------
#8128#1728
; Temporary data for function 'L8132_load_or_save_to_tape'
#8128#1728
L8128_initial_pressed_key:
#8128#17281
    db #c0
#8129#1729
L8129_filename_length:
#8129#17291
    db #01
#812a#172a
L812a_filename_ptr:
#812a#172a2
    dw #0080
#812c#172c
l812c_savegame_data_size:
#812c#172c2
    dw #01c0
#812e#172e
L812e_savegame_data_end_ptr:
#812e#172e2
    dw #00e0
#8130#1730
L8130_checksum:
#8130#17302
    dw #00c0
#8132#1732
#8132#1732
#8132#1732
; --------------------------------
#8132#1732
; Asks the player to type a savegame name, and load/ssaves a game.
#8132#1732
L8132_load_or_save_to_tape:
#8132#1732314
32 28 81 
    ld (L8128_initial_pressed_key), a  ; Remember whether we had pressed 'S' or 'L'.
#8135#1735318
cd f4 c3 
    call Lc3f4_read_filename
#8138#1738314
32 29 81 
    ld (L8129_filename_length), a
#813b#173b317
22 2a 81 
    ld (L812a_filename_ptr), hl
#813e#173e314
3a 28 81 
    ld a, (L8128_initial_pressed_key)  ; Restore which key was pressed to get to this menu.
#8141#174128
fe 53 
    cp "S"  ; Check if we wanted to save or load:
#8143#1743311
c2 19 82 
    jp nz, L8219_load
#8146#1746
#8146#1746
    ; Save game:
#8146#1746
    ; Copy the game data to save to the buffer:
#8146#1746311
11 bc 5c 
    ld de, L5cbc_render_buffer
#8149#1749311
21 ad 6a 
    ld hl, L6aad_player_current_x
#814c#174c311
01 77 00 
    ld bc, L6b24_savegame_data_end - L6aad_savegame_data_start
#814f#174f223/18
ed b0 
    ldir
#8151#1751
#8151#1751
    ; Add area object states:
#8151#1751314
3a 82 d0 
    ld a, (Ld082_n_areas)
#8154#175415
4f 
    ld c, a
#8155#1755416
fd 21 d1 d0 
    ld iy, Ld0d1_area_offsets
#8159#175915
eb 
    ex de, hl  ; hl = ptr to the next position in the save game data
#815a#175a
L815a_save_game_area_loop:
#815a#175a321
fd 5e 00 
    ld e, (iy)
#815d#175d321
fd 56 01 
    ld d, (iy + 1)  ; Get area pointer offset
#8160#1760212
fd 23 
    inc iy
#8162#1762212
fd 23 
    inc iy
#8164#1764416
dd 21 82 d0 
    ld ix, Ld082_area_reference_start
#8168#1768217
dd 19 
    add ix, de
#816a#176a321
dd 46 01 
    ld b, (ix + AREA_N_OBJECTS)
#816d#176d311
11 08 00 
    ld de, 8
#8170#1770217
dd 19 
    add ix, de  ; skip the header
#8172#1772
L8172_save_game_next_object_loop:
#8172#1772321
dd 7e 00 
    ld a, (ix + OBJECT_TYPE_AND_FLAGS)  ; save the object state
#8175#177518
77 
    ld (hl), a
#8176#177617
23 
    inc hl
#8177#1777321
dd 5e 08 
    ld e, (ix + OBJECT_SIZE)
#817a#177a217
dd 19 
    add ix, de  ; next object
#817c#177c214/9
10 f4 
    djnz L8172_save_game_next_object_loop
#817e#177e15
0d 
    dec c
#817f#177f213/8
20 d9 
    jr nz, L815a_save_game_area_loop
#8181#1781
#8181#1781317
22 2e 81 
    ld (L812e_savegame_data_end_ptr), hl
#8184#1784311
11 bc 5c 
    ld de, L5cbc_render_buffer
#8187#178715
b7 
    or a
#8188#1788217
ed 52 
    sbc hl, de
#818a#178a317
22 2c 81 
    ld (l812c_savegame_data_size), hl
#818d#178d318
cd 8c 83 
    call L838c_checksum
#8190#1790422
ed 43 30 81 
    ld (L8130_checksum), bc
#8194#1794
#8194#1794422
ed 5b 2e 81 
    ld de, (L812e_savegame_data_end_ptr)
#8198#1798
#8198#1798
    ; Generate the savegame header (19 bytes):
#8198#1798
    ; - 1 byte: 30
#8198#1798
    ; - 12 bytes: filename
#8198#1798
    ; - 2 bytes: savegame size
#8198#1798
    ; - 2 bytes: (Ld083_game_version)
#8198#1798
    ; - 2 bytes: checksum
#8198#179828
3e 1e 
    ld a, 30  ; Header start
#819a#179a18
12 
    ld (de), a
#819b#179b17
13 
    inc de
#819c#179c317
2a 2a 81 
    ld hl, (L812a_filename_ptr)
#819f#179f311
01 0c 00 
    ld bc, 12
#81a2#17a2223/18
ed b0 
    ldir  ; Append the filename to the savegame data.
#81a4#17a4
#81a4#17a4
    ; Append the savegame datasize to the savegame header:
#81a4#17a4317
2a 2c 81 
    ld hl, (l812c_savegame_data_size)
#81a7#17a715
eb 
    ex de, hl
#81a8#17a818
73 
    ld (hl), e
#81a9#17a917
23 
    inc hl
#81aa#17aa18
72 
    ld (hl), d
#81ab#17ab17
23 
    inc hl
#81ac#17ac
#81ac#17ac
    ; Append the 2 bytes at (Ld083_game_version) to the savegame header:
#81ac#17ac422
ed 5b 83 d0 
    ld de, (Ld083_game_version)
#81b0#17b018
73 
    ld (hl), e
#81b1#17b117
23 
    inc hl
#81b2#17b218
72 
    ld (hl), d
#81b3#17b317
23 
    inc hl
#81b4#17b4
#81b4#17b4
    ; Append the checksum to the header:
#81b4#17b4422
ed 5b 30 81 
    ld de, (L8130_checksum)
#81b8#17b818
73 
    ld (hl), e
#81b9#17b917
23 
    inc hl
#81ba#17ba18
72 
    ld (hl), d
#81bb#17bb
#81bb#17bb416
dd 21 e4 72 
    ld ix, L725c_videomem_row_pointers + 68 * 2
#81bf#17bf311
21 47 75 
    ld hl, L7547_text_play_record
#81c2#17c2311
11 27 0d 
    ld de, #0d27
#81c5#17c5318
cd 1c d0 
    call Ld01c_draw_string
#81c8#17c8416
dd 21 fa 72 
    ld ix, L725c_videomem_row_pointers + 79 * 2
#81cc#17cc311
21 83 75 
    ld hl, L7583_text_then_any_key
#81cf#17cf318
cd 1c d0 
    call Ld01c_draw_string
#81d2#17d2
#81d2#17d2
    ; Wait until a key is pressed & released:
#81d2#17d2
L81d2_key_press_wait_loop:
#81d2#17d2318
cd d4 bf 
    call Lbfd4_read_keyboard_and_joystick_input
#81d5#17d5213/8
38 fb 
    jr c, L81d2_key_press_wait_loop
#81d7#17d7
L81d7_key_release_wait_loop:
#81d7#17d7318
cd d4 bf 
    call Lbfd4_read_keyboard_and_joystick_input
#81da#17da213/8
30 fb 
    jr nc, L81d7_key_release_wait_loop
#81dc#17dc
#81dc#17dc311
21 38 75 
    ld hl, L7538_text_spaces
#81df#17df318
cd 1c d0 
    call Ld01c_draw_string
#81e2#17e2416
dd 21 e4 72 
    ld ix, L725c_videomem_row_pointers + 68 * 2
#81e6#17e6311
21 92 75 
    ld hl, L7592_text_saving_file
#81e9#17e9318
cd 1c d0 
    call Ld01c_draw_string
#81ec#17ec422
fd 2a 7d 74 
    ld iy, (L747d)
#81f0#17f0422
dd 2a 2e 81 
    ld ix, (L812e_savegame_data_end_ptr)  ; address to save
#81f4#17f4311
11 13 00 
    ld de, 19  ; Size of the header
#81f7#17f715
af 
    xor a
#81f8#17f8
    ; Save the header:
#81f8#17f8318
cd c6 04 
    call L04c6_BIOS_CASSETTE_SAVE_NO_BREAK_TEST
#81fb#17fb311
d2 62 83 
    jp nc, L8362_done_loading_saving_with_pause
#81fe#17fe15
fb 
    ei
#81ff#17ff
    ; Wait 50 interrupts:
#81ff#17ff28
3e 32 
    ld a, 50
#8201#1801314
32 a5 74 
    ld (L74a5_interrupt_timer), a
#8204#1804
L8204_pause_loop:
#8204#1804314
3a a5 74 
    ld a, (L74a5_interrupt_timer)
#8207#180715
b7 
    or a
#8208#1808213/8
20 fa 
    jr nz, L8204_pause_loop
#820a#180a416
dd 21 bc 5c 
    ld ix, L5cbc_render_buffer  ; address to save
#820e#180e422
ed 5b 2c 81 
    ld de, (l812c_savegame_data_size)  ; amount of bytes to save
#8212#181215
3d 
    dec a
#8213#1813
    ; Save the savegame:
#8213#1813318
cd c6 04 
    call L04c6_BIOS_CASSETTE_SAVE_NO_BREAK_TEST
#8216#1816311
c3 62 83 
    jp L8362_done_loading_saving_with_pause
#8219#1819
#8219#1819
L8219_load:
#8219#1819
    ; Load game:
#8219#1819422
fd 2a 7d 74 
    ld iy, (L747d)
#821d#181d416
dd 21 e4 72 
    ld ix, L725c_videomem_row_pointers + 68 * 2
#8221#1821311
21 b0 75 
    ld hl, L75b0_text_searching
#8224#1824311
11 27 0e 
    ld de, #0e27
#8227#1827318
cd 1c d0 
    call Ld01c_draw_string
#822a#182a
L822a:
#822a#182a416
dd 21 bc 5c 
    ld ix, L5cbc_render_buffer  ; address to load to
#822e#182e311
11 12 00 
    ld de, 19 - 1  ; Amount of bytes to load (19)
#8231#1831
    ; Set the flags/values the BIOS routine expects to load:
#8231#183115
af 
    xor a
#8232#183215
37 
    scf
#8233#183315
1c 
    inc e
#8234#183415
08 
    ex af, af'  ; since we are skipping tests, we need to pre "ex" af, since the function would do this during the tests.
#8235#183515
f3 
    di
#8236#1836318
cd 62 05 
    call L0562_BIOS_READ_FROM_TAPE_SKIP_TESTS
#8239#1839112
f5 
    push af
#823a#183a28
3e 7f 
        ld a, #7f
#823c#183c212
db fe 
        in a, (ULA_PORT)
#823e#183e15
1f 
        rra
#823f#183f311
d2 1d 83 
        jp nc, L831d_done_loading_saving_pop  ; If space (break?) was pressed, cancel.
#8242#1842111
f1 
    pop af
#8243#1843213/8
30 e5 
    jr nc, L822a  ; If load failed, retry.
#8245#1845416
dd 21 bc 5c 
    ld ix, L5cbc_render_buffer
#8249#1849
#8249#1849
    ; Check header is correct:
#8249#1849
    ; - 1 byte: 30
#8249#1849
    ; - 12 bytes: filename
#8249#1849
    ; - 2 bytes: savegame size
#8249#1849
    ; - 2 bytes: (Ld083_game_version)
#8249#1849
    ; - 2 bytes: checksum
#8249#184928
3e 1e 
    ld a, 30
#824b#184b321
dd be 00 
    cp (ix)
#824e#184e213/8
20 da 
    jr nz, L822a  ; If the first byte is not the header start byte, retry.
#8250#1850416
dd 21 e4 72 
    ld ix, L725c_videomem_row_pointers + 68 * 2
#8254#1854311
21 a1 75 
    ld hl, L75a1_text_found
#8257#1857311
11 27 0e 
    ld de, #0e27
#825a#185a318
cd 1c d0 
    call Ld01c_draw_string
#825d#185d
#825d#185d416
dd 21 bd 5c 
    ld ix, L5cbc_render_buffer + 1
#8261#1861321
dd 5e 0c 
    ld e, (ix + 12)
#8264#1864321
dd 56 0d 
    ld d, (ix + 13)  ; de = savegame size
#8267#1867421
dd 36 0c 20 
    ld (ix + 12), " "  ; replace savegame size by spaces, to print to screen.
#826b#186b421
dd 36 0d 20 
    ld (ix + 13), " "
#826f#186f217
dd e5 
    push ix
#8271#1871112
d5 
    push de
#8272#1872311
21 bc 5c 
        ld hl, L5cbc_render_buffer
#8275#1875211
36 00 
        ld (hl), 0  ; Replace "30" by a 0, so we can draw the string.
#8277#1877416
dd 21 fa 72 
        ld ix, L725c_videomem_row_pointers + 79 * 2
#827b#187b311
11 27 0e 
        ld de, #0e27
#827e#187e318
cd 1c d0 
        call Ld01c_draw_string  ; Draw savegame name
#8281#1881111
d1 
    pop de
#8282#1882216
dd e1 
    pop ix
#8284#1884
#8284#1884
    ; Check if the savegame name matches what the player entered:
#8284#1884314
3a 29 81 
    ld a, (L8129_filename_length)
#8287#188715
b7 
    or a
#8288#1888213/8
20 07 
    jr nz, L8291
#828a#188a
    ; If player did not enter any name, accept any savegame:
#828a#188a311
01 0c 00 
    ld bc, 12
#828d#188d217
dd 09 
    add ix, bc
#828f#188f213
18 10 
    jr L82a1_savegame_name_matches
#8291#1891
L8291:
#8291#1891
    ; Check if names match:
#8291#189128
06 0c 
    ld b, 12
#8293#1893317
2a 2a 81 
    ld hl, (L812a_filename_ptr)
#8296#1896
L8296_savegame_name_check_loop:
#8296#1896321
dd 7e 00 
    ld a, (ix)
#8299#189918
be 
    cp (hl)
#829a#189a213/8
20 8e 
    jr nz, L822a  ; If name does not match, retry
#829c#189c212
dd 23 
    inc ix
#829e#189e17
23 
    inc hl
#829f#189f214/9
10 f5 
    djnz L8296_savegame_name_check_loop
#82a1#18a1
#82a1#18a1
L82a1_savegame_name_matches:
#82a1#18a1422
ed 53 2c 81 
    ld (l812c_savegame_data_size), de
#82a5#18a515
7a 
    ld a, d
#82a6#18a628
e6 f0 
    and 240
#82a8#18a8213/8
20 56 
    jr nz, L8300
#82aa#18aa
    ; Check that the version matches this game:
#82aa#18aa321
dd 5e 02 
    ld e, (ix + 2)
#82ad#18ad321
dd 56 03 
    ld d, (ix + 3)
#82b0#18b0317
2a 83 d0 
    ld hl, (Ld083_game_version)
#82b3#18b315
b7 
    or a
#82b4#18b4217
ed 52 
    sbc hl, de
#82b6#18b6213/8
20 48 
    jr nz, L8300
#82b8#18b8321
dd 6e 04 
    ld l, (ix + 4)
#82bb#18bb321
dd 66 05 
    ld h, (ix + 5)
#82be#18be317
22 30 81 
    ld (L8130_checksum), hl
#82c1#18c1
    
#82c1#18c1416
dd 21 e4 72 
    ld ix, L725c_videomem_row_pointers + 68 * 2
#82c5#18c5311
21 74 75 
    ld hl, L7574_text_loading
#82c8#18c8311
11 27 0e 
    ld de, #0e27
#82cb#18cb318
cd 1c d0 
    call Ld01c_draw_string
#82ce#18ce
#82ce#18ce
    ; Load savegame data:
#82ce#18ce416
dd 21 bc 5c 
    ld ix, L5cbc_render_buffer
#82d2#18d2422
ed 5b 2c 81 
    ld de, (l812c_savegame_data_size)
#82d6#18d6
#82d6#18d6
    ; Set the flags/values the BIOS routine expects to load:
#82d6#18d615
37 
    scf
#82d7#18d728
3e ff 
    ld a, 255
#82d9#18d915
14 
    inc d
#82da#18da15
08 
    ex af, af'
#82db#18db15
15 
    dec d
#82dc#18dc15
f3 
    di
#82dd#18dd318
cd 62 05 
    call L0562_BIOS_READ_FROM_TAPE_SKIP_TESTS
#82e0#18e0112
f5 
    push af
#82e1#18e128
3e 7f 
        ld a, 127
#82e3#18e3212
db fe 
        in a, (ULA_PORT)
#82e5#18e515
1f 
        rra
#82e6#18e6213/8
30 35 
        jr nc, L831d_done_loading_saving_pop  ; If space (break?) was pressed, cancel.
#82e8#18e8111
f1 
    pop af
#82e9#18e9416
dd 21 65 75 
    ld ix, L7565_text_loading_error
#82ed#18ed213/8
30 15 
    jr nc, L8304
#82ef#18ef
#82ef#18ef311
11 bc 5c 
    ld de, L5cbc_render_buffer
#82f2#18f2317
2a 2c 81 
    ld hl, (l812c_savegame_data_size)
#82f5#18f5318
cd 8c 83 
    call L838c_checksum
#82f8#18f8317
2a 30 81 
    ld hl, (L8130_checksum)
#82fb#18fb15
b7 
    or a
#82fc#18fc217
ed 42 
    sbc hl, bc
#82fe#18fe213/8
28 20 
    jr z, L8320_found_valid_savegame
#8300#1900
#8300#1900
L8300:
#8300#1900
    ; Load error: version mismatch
#8300#1900416
dd 21 56 75 
    ld ix, L7556_text_invalid_file
#8304#1904
L8304:
#8304#1904
    ; Load error
#8304#1904217
dd e5 
    push ix
#8306#1906111
e1 
    pop hl
#8307#1907416
dd 21 e4 72 
    ld ix, L725c_videomem_row_pointers + 68 * 2
#830b#190b311
11 27 0e 
    ld de, #0e27
#830e#190e318
cd 1c d0 
    call Ld01c_draw_string
#8311#1911311
21 38 75 
    ld hl, L7538_text_spaces
#8314#1914416
dd 21 fa 72 
    ld ix, L725c_videomem_row_pointers + 79 * 2
#8318#1918318
cd 1c d0 
    call Ld01c_draw_string
#831b#191b213
18 45 
    jr L8362_done_loading_saving_with_pause
#831d#191d
#831d#191d
L831d_done_loading_saving_pop:
#831d#191d111
f1 
    pop af
#831e#191e213
18 42 
    jr L8362_done_loading_saving_with_pause
#8320#1920
#8320#1920
L8320_found_valid_savegame:
#8320#1920
    ; Found a valid savegame, restore state:
#8320#1920311
21 bc 5c 
    ld hl, L5cbc_render_buffer
#8323#1923311
11 ad 6a 
    ld de, L6aad_savegame_data_start
#8326#1926311
01 77 00 
    ld bc, 119
#8329#1929223/18
ed b0 
    ldir
#832b#192b
#832b#192b
    ; Restore the additional area state information:
#832b#192b314
3a 82 d0 
    ld a, (Ld082_n_areas)
#832e#192e15
4f 
    ld c, a
#832f#192f416
fd 21 d1 d0 
    ld iy, Ld0d1_area_offsets
#8333#1933
L8333_area_loop:
#8333#1933321
fd 5e 00 
    ld e, (iy)
#8336#1936321
fd 56 01 
    ld d, (iy + 1)
#8339#1939212
fd 23 
    inc iy
#833b#193b212
fd 23 
    inc iy
#833d#193d416
dd 21 82 d0 
    ld ix, Ld082_area_reference_start
#8341#1941217
dd 19 
    add ix, de
#8343#1943321
dd 46 01 
    ld b, (ix + AREA_N_OBJECTS)
#8346#1946311
11 08 00 
    ld de, 8
#8349#1949217
dd 19 
    add ix, de
#834b#194b
L834b:
#834b#194b18
7e 
    ld a, (hl)
#834c#194c321
dd 77 00 
    ld (ix + OBJECT_TYPE_AND_FLAGS), a
#834f#194f17
23 
    inc hl
#8350#1950321
dd 5e 08 
    ld e, (ix + OBJECT_SIZE)
#8353#1953217
dd 19 
    add ix, de
#8355#1955214/9
10 f4 
    djnz L834b
#8357#195715
0d 
    dec c
#8358#1958213/8
20 d9 
    jr nz, L8333_area_loop
#835a#195a15
fb 
    ei
#835b#195b314
3a d7 6a 
    ld a, (L6ad7_current_border_color)
#835e#195e212
d3 fe 
    out (ULA_PORT), a
#8360#1960213
18 11 
    jr L8373_done_loading_saving
#8362#1962
#8362#1962
L8362_done_loading_saving_with_pause:
#8362#196215
fb 
    ei
#8363#1963314
3a d7 6a 
    ld a, (L6ad7_current_border_color)
#8366#1966212
d3 fe 
    out (ULA_PORT), a  ; Set the border color.
#8368#1968
    ; Wait 50 interrupts:
#8368#196828
3e 32 
    ld a, 50
#836a#196a314
32 a5 74 
    ld (L74a5_interrupt_timer), a
#836d#196d
L836d_pause_loop:
#836d#196d314
3a a5 74 
    ld a, (L74a5_interrupt_timer)
#8370#197015
b7 
    or a
#8371#1971213/8
20 fa 
    jr nz, L836d_pause_loop
#8373#1973
#8373#1973
L8373_done_loading_saving:
#8373#1973311
21 fd ff 
    ld hl, #fffd
#8376#1976317
22 6c 74 
    ld (L746c_game_flags), hl
#8379#197928
3e 01 
    ld a, 1
#837b#197b314
32 66 74 
    ld (L7466_need_attribute_refresh_flag), a
#837e#197e314
32 77 74 
    ld (L7477_render_buffer_effect), a  ; fade in effect when rendering
#8381#1981318
cd aa 83 
    call L83aa_redraw_whole_screen
#8384#198415
af 
    xor a
#8385#1985314
32 6c 74 
    ld (L746c_game_flags), a
#8388#1988314
32 6d 74 
    ld (L746c_game_flags + 1), a
#838b#198b111
c9 
    ret
#838c#198c
#838c#198c
#838c#198c
; --------------------------------
#838c#198c
; Calculates the check sum of a block of data.
#838c#198c
; Input:
#838c#198c
; - de: ptr to the data to calculate the checksum for
#838c#198c
; - hl: length of the data
#838c#198c
; Output:
#838c#198c
; - bc: 16bit checksum
#838c#198c
L838c_checksum:
#838c#198c
    ; Initialize checksum to 0:
#838c#198c15
af 
    xor a
#838d#198d15
47 
    ld b, a
#838e#198e15
4f 
    ld c, a
#838f#198f
L838f_checksum_loop:
#838f#198f
    ; xor each pair of bytes in the data with "bc":
#838f#198f18
1a 
    ld a, (de)
#8390#199015
a8 
    xor b
#8391#199115
47 
    ld b, a
#8392#199217
13 
    inc de
#8393#199317
2b 
    dec hl
#8394#199415
7d 
    ld a, l
#8395#199515
b4 
    or h
#8396#1996213/8
28 09 
    jr z, L83a1_checksum_done
#8398#199818
1a 
    ld a, (de)
#8399#199915
a9 
    xor c
#839a#199a15
4f 
    ld c, a
#839b#199b17
13 
    inc de
#839c#199c17
2b 
    dec hl
#839d#199d15
7d 
    ld a, l
#839e#199e15
b4 
    or h
#839f#199f213/8
20 ee 
    jr nz, L838f_checksum_loop
#83a1#19a1
L83a1_checksum_done:
#83a1#19a1210
cb 20 
    sla b
#83a3#19a3210
cb 11 
    rl c
#83a5#19a5210
cb 10 
    rl b
#83a7#19a7210
cb 11 
    rl c
#83a9#19a9111
c9 
    ret
#83aa#19aa
#83aa#19aa
#83aa#19aa
; --------------------------------
#83aa#19aa
; Redraws the whole screen, including:
#83aa#19aa
; - UI elements
#83aa#19aa
; - 3d view
#83aa#19aa
L83aa_redraw_whole_screen:
#83aa#19aa217
dd e5 
    push ix
#83ac#19ac217
fd e5 
    push iy
#83ae#19ae112
e5 
    push hl
#83af#19af112
d5 
    push de
#83b0#19b0112
c5 
    push bc
#83b1#19b1112
f5 
    push af
#83b2#19b2314
3a 74 74 
        ld a, (L7474_check_if_object_crushed_player_flag)
#83b5#19b515
b7 
        or a
#83b6#19b6213/8
28 07 
        jr z, L83bf_no_need_to_check_crush
#83b8#19b8318
cd aa ca 
        call Lcaaa_check_if_object_crushed_player
#83bb#19bb15
b7 
        or a
#83bc#19bc311
c2 0b 84 
        jp nz, L840b_update_ui_and_done
#83bf#19bf
L83bf_no_need_to_check_crush:
#83bf#19bf314
3a 6c 74 
        ld a, (L746c_game_flags)
#83c2#19c2210
cb 57 
        bit 2, a
#83c4#19c4213/8
28 25 
        jr z, L83eb_no_need_to_reproject_objects
#83c6#19c6318
cd b7 8b 
        call L8bb7_determine_rendering_volume
#83c9#19c9318
cd de 95 
        call L95de_init_rotation_matrix
#83cc#19cc15
af 
        xor a
#83cd#19cd314
32 69 74 
        ld (L7469_n_spirits_found_in_current_area), a
#83d0#19d0422
dd 2a 63 74 
        ld ix, (L7463_global_area_objects)
#83d4#19d4314
3a 65 74 
        ld a, (L7465_global_area_n_objects)
#83d7#19d715
b7 
        or a
#83d8#19d8318/11
c4 31 84 
        call nz, L8431_project_objects
#83db#19db422
dd 2a d1 6a 
        ld ix, (L6ad1_current_area_objects)
#83df#19df314
3a d0 6a 
        ld a, (L6ad0_current_area_n_objects)
#83e2#19e215
b7 
        or a
#83e3#19e3318/11
c4 31 84 
        call nz, L8431_project_objects
#83e6#19e6318
cd 2d 9c 
        call L9c2d_sort_objects_for_rendering
#83e9#19e9213
18 13 
        jr L83fe_rerender
#83eb#19eb
L83eb_no_need_to_reproject_objects:
#83eb#19eb
        ; If there is a lightning, re-render for sure:
#83eb#19eb314
3a 0e 6b 
        ld a, (L6b0e_lightning_time_seconds_countdown)
#83ee#19ee15
b7 
        or a
#83ef#19ef213/8
20 06 
        jr nz, L83f7  ; if there is no lightning, rerender only if the re-render flag is set.
#83f1#19f1314
3a 19 6b 
        ld a, (L6b19_current_area_flags)
#83f4#19f415
b7 
        or a
#83f5#19f5213/8
20 07 
        jr nz, L83fe_rerender
#83f7#19f7
L83f7:
#83f7#19f7314
3a 6d 74 
        ld a, (L746c_game_flags + 1)
#83fa#19fa210
cb 5f 
        bit 3, a  ; check if we need to re-render.
#83fc#19fc213/8
28 0d 
        jr z, L840b_update_ui_and_done  ; skip re-render
#83fe#19fe
L83fe_rerender:
#83fe#19fe318
cd 52 bc 
        call Lbc52_update_UI
#8401#1a01318
cd 46 9d 
        call L9d46_render_3d_view
#8404#1a04318
cd bc 9d 
        call L9dbc_render_buffer_with_effects
#8407#1a07213
18 05 
        jr L840e_continue
#8409#1a09213
18 03 
        jr L840e_continue  ; Note: unreachable?
#840b#1a0b
L840b_update_ui_and_done:
#840b#1a0b318
cd 52 bc 
        call Lbc52_update_UI
#840e#1a0e
L840e_continue:
#840e#1a0e
        ; Check if we need to reprint the current room name:
#840e#1a0e314
3a 6d 74 
        ld a, (L746c_game_flags + 1)
#8411#1a11210
cb 6f 
        bit 5, a
#8413#1a13213/8
28 13 
        jr z, L8428_done
#8415#1a15
#8415#1a15
L8415_pause_loop:
#8415#1a15314
3a a5 74 
        ld a, (L74a5_interrupt_timer)
#8418#1a1815
b7 
        or a
#8419#1a19213/8
20 fa 
        jr nz, L8415_pause_loop
#841b#1a1b
        ; Print the current room name:
#841b#1a1b311
21 bf 6a 
        ld hl, L6abf_current_area_name_string
#841e#1a1e416
dd 21 5a 73 
        ld ix, L735a_ui_message_row_pointers
#8422#1a22311
11 00 0f 
        ld de, #0f00  ; string length = 15, no x offset
#8425#1a25318
cd 1c d0 
        call Ld01c_draw_string
#8428#1a28
L8428_done:
#8428#1a28111
f1 
    pop af
#8429#1a29111
c1 
    pop bc
#842a#1a2a111
d1 
    pop de
#842b#1a2b111
e1 
    pop hl
#842c#1a2c216
fd e1 
    pop iy
#842e#1a2e216
dd e1 
    pop ix
#8430#1a30111
c9 
    ret
#8431#1a31
#8431#1a31
#8431#1a31
; --------------------------------
#8431#1a31
; Projects all objects from 3d coordinates to 2d coordinates, determining which ones have to be drawn.
#8431#1a31
; - When this function is called this has already happened:
#8431#1a31
;   - rendering cube volume has been calculated
#8431#1a31
;   - rotation matrix has already been set
#8431#1a31
; Input:
#8431#1a31
; - a: number of objects
#8431#1a31
; - ix: pointer to objects
#8431#1a31
L8431_project_objects:
#8431#1a31
L8431_object_loop:
#8431#1a31112
f5 
    push af
#8432#1a32422
dd 22 9d 74 
        ld (L749d_object_currently_being_processed_ptr), ix
#8436#1a36321
dd 7e 00 
        ld a, (ix + OBJECT_TYPE_AND_FLAGS)
#8439#1a3928
e6 0f 
        and #0f
#843b#1a3b311
ca fb 84 
        jp z, L84fb_next_object
#843e#1a3e422
dd cb 00 76 
        bit 6, (ix + OBJECT_TYPE_AND_FLAGS)
#8442#1a42311
c2 fb 84 
        jp nz, L84fb_next_object
#8445#1a4528
fe 02 
        cp 2  ; Check if it's a spirit
#8447#1a47213/8
20 07 
        jr nz, L8450_not_a_spirit
#8449#1a49
        ; It is a spirit, increment the counter of spirits found:
#8449#1a49311
21 69 74 
        ld hl, L7469_n_spirits_found_in_current_area
#844c#1a4c112
34 
        inc (hl)
#844d#1a4d311
c3 fb 84 
        jp L84fb_next_object
#8450#1a50
L8450_not_a_spirit:
#8450#1a50314
3a bd 6a 
        ld a, (L6abd_cull_by_rendering_volume_flag)
#8453#1a5315
b7 
        or a
#8454#1a54213/8
20 1b 
        jr nz, L8471_skip_rendering_volume_cull_check
#8456#1a56
#8456#1a56
        ; Rendering cube cull check:
#8456#1a56
        ; Check if the object intersects with the rendering volume:
#8456#1a56311
21 5d 74 
        ld hl, L745d_rendering_cube_volume
#8459#1a5928
06 03 
        ld b, 3  ; 3 iterations, one for X, one for Y, one for Z
#845b#1a5b
L845b_rendering_volume_cull_check_loop:
#845b#1a5b321
dd 7e 01 
        ld a, (ix + OBJECT_X)
#845e#1a5e18
be 
        cp (hl)
#845f#1a5f213/8
28 03 
        jr z, L8464
#8461#1a61311
f2 fb 84 
        jp p, L84fb_next_object  ; Outside of rendering volume
#8464#1a64
L8464:
#8464#1a64321
dd 86 04 
        add a, (ix + OBJECT_SIZE_X)
#8467#1a6717
23 
        inc hl
#8468#1a6818
be 
        cp (hl)
#8469#1a69311
fa fb 84 
        jp m, L84fb_next_object  ; Outside of rendering volume
#846c#1a6c212
dd 23 
        inc ix
#846e#1a6e17
23 
        inc hl
#846f#1a6f214/9
10 ea 
        djnz L845b_rendering_volume_cull_check_loop
#8471#1a71
#8471#1a71
        ; Passed the cull check, object intersects with the rendering volume!
#8471#1a71
L8471_skip_rendering_volume_cull_check:
#8471#1a71422
ed 5b 99 74 
        ld de, (L7499_3d_object_bounding_box_relative_to_player_ptr)
#8475#1a7515
af 
        xor a
#8476#1a76422
dd 2a 9d 74 
        ld ix, (L749d_object_currently_being_processed_ptr)
#847a#1a7a311
21 ad 6a 
        ld hl, L6aad_player_current_x
#847d#1a7d28
06 03 
        ld b, 3
#847f#1a7f
        ; This loop iterates 3 times, one for x, one for y and one for z:
#847f#1a7f
        ; It is used to:
#847f#1a7f
        ; - check if the player is within the bounding box defined by the object (stored in L5e62_player_collision_with_object_flags).
#847f#1a7f
        ; - store the relative bounding box coordinates relative to the player in the pointer in L7499_3d_object_bounding_box_relative_to_player_ptr.
#847f#1a7f
L847f_player_coordinate_loop:
#847f#1a7f112
c5 
        push bc
#8480#1a8018
4e 
            ld c, (hl)
#8481#1a8117
23 
            inc hl
#8482#1a8218
46 
            ld b, (hl)  ; bc = player coordinate (x, y or z)
#8483#1a8317
23 
            inc hl
#8484#1a84112
e5 
            push hl
#8485#1a8528
2e 00 
                ld l, 0
#8487#1a87321
dd 66 01 
                ld h, (ix + OBJECT_X)
#848a#1a8a210
cb 3c 
                srl h
#848c#1a8c210
cb 1d 
                rr l
#848e#1a8e210
cb 3c 
                srl h
#8490#1a90210
cb 1d 
                rr l  ; hl = object coordinate * 64
#8492#1a9215
b7 
                or a
#8493#1a93217
ed 42 
                sbc hl, bc  ; hl = object coordinate * 64 - player coordinate
#8495#1a95213/8
28 05 
                jr z, L849c
#8497#1a97311
fa 9c 84 
                jp m, L849c
#849a#1a9a
                ; player coordinate < object coordinate 1
#849a#1a9a210
cb f7 
                set 6, a
#849c#1a9c
L849c:
#849c#1a9c210
cb 3f 
                srl a
#849e#1a9e
                ; save the object coordinate - player coordinate to (de)
#849e#1a9e15
eb 
                ex de, hl
#849f#1a9f18
73 
                    ld (hl), e
#84a0#1aa017
23 
                    inc hl
#84a1#1aa118
72 
                    ld (hl), d
#84a2#1aa217
23 
                    inc hl
#84a3#1aa315
eb 
                ex de, hl
#84a4#1aa4
#84a4#1aa428
0e 00 
                ld c, 0
#84a6#1aa6321
dd 46 04 
                ld b, (ix + OBJECT_SIZE_X)
#84a9#1aa9210
cb 38 
                srl b
#84ab#1aab210
cb 19 
                rr c
#84ad#1aad210
cb 38 
                srl b
#84af#1aaf210
cb 19 
                rr c  ; bc = object size * 64
#84b1#1ab115
b7 
                or a
#84b2#1ab2217
ed 4a 
                adc hl, bc  ; hl = (coordinate + size) * 64 - player coordinate
#84b4#1ab4311
f2 b9 84 
                jp p, L84b9
#84b7#1ab7
                ; player coordinate > object coordinate 2
#84b7#1ab7210
cb f7 
                set 6, a
#84b9#1ab9
L84b9:
#84b9#1ab9210
cb 3f 
                srl a
#84bb#1abb
                ; save the object coordinate 2 - player coordinate to (de)
#84bb#1abb15
eb 
                ex de, hl
#84bc#1abc18
73 
                    ld (hl), e
#84bd#1abd17
23 
                    inc hl
#84be#1abe18
72 
                    ld (hl), d
#84bf#1abf17
23 
                    inc hl
#84c0#1ac015
eb 
                ex de, hl
#84c1#1ac1212
dd 23 
                inc ix
#84c3#1ac3111
e1 
            pop hl
#84c4#1ac4111
c1 
        pop bc
#84c5#1ac5214/9
10 b8 
        djnz L847f_player_coordinate_loop
#84c7#1ac7
        ; If player is inside of the bounding box, we will have a = #3f
#84c7#1ac7
        ; Each of the 6 bits represents a collision in one of the 6 directions one can collide with a cube,
#84c7#1ac7
        ; If this is 0, it means collision, any other thing than 0 is NO collision.
#84c7#1ac7314
32 62 5e 
        ld (L5e62_player_collision_with_object_flags), a
#84ca#1aca422
dd 2a 9d 74 
        ld ix, (L749d_object_currently_being_processed_ptr)
#84ce#1ace321
dd 7e 07 
        ld a, (ix + OBJECT_ID)
#84d1#1ad1314
32 68 74 
        ld (L7468_focus_object_id), a
#84d4#1ad4321
dd 7e 00 
        ld a, (ix + OBJECT_TYPE_AND_FLAGS)
#84d7#1ad728
e6 0f 
        and #0f
#84d9#1ad9314
32 61 5e 
        ld (L5e61_object_currently_being_processed_type), a
#84dc#1adc28
fe 01 
        cp OBJECT_TYPE_CUBE
#84de#1ade213/8
20 05 
        jr nz, L84e5
#84e0#1ae0318
cd 61 96 
        call L9661_project_cube_objects
#84e3#1ae3213
18 16 
        jr L84fb_next_object
#84e5#1ae5
L84e5:
#84e5#1ae528
fe 03 
        cp OBJECT_TYPE_RECTANGLE
#84e7#1ae7213/8
20 05 
        jr nz, L84ee
#84e9#1ae9318
cd 5b 9b 
        call L9b5b_project_rectangle_objects
#84ec#1aec213
18 0d 
        jr L84fb_next_object
#84ee#1aee
L84ee:
#84ee#1aee28
fe 0a 
        cp OBJECT_TYPE_LINE
#84f0#1af0311
f2 f8 84 
        jp p, L84f8
#84f3#1af3318
cd bb 97 
        call L97bb_project_other_solids
#84f6#1af6213
18 03 
        jr L84fb_next_object
#84f8#1af8
L84f8:
#84f8#1af8
        ; Objects with ID >= 10 means that they are basic shapes (line, triangle, quad, pentagon, hexagon.):
#84f8#1af8
        ; - they have ID - 8 vertices in their geometry.
#84f8#1af8318
cd c5 9a 
        call L9ac5_project_flat_shape_object
#84fb#1afb
L84fb_next_object:
#84fb#1afb
        ; Get the pointer to the next object, and loop:
#84fb#1afb422
dd 2a 9d 74 
        ld ix, (L749d_object_currently_being_processed_ptr)
#84ff#1aff321
dd 5e 08 
        ld e, (ix + OBJECT_SIZE)
#8502#1b0228
16 00 
        ld d, 0
#8504#1b04217
dd 19 
        add ix, de
#8506#1b06111
f1 
    pop af
#8507#1b0715
3d 
    dec a
#8508#1b08311
c2 31 84 
    jp nz, L8431_object_loop
#850b#1b0b111
c9 
    ret
#850c#1b0c
#850c#1b0c
#850c#1b0c
; --------------------------------
#850c#1b0c
; Auxiliary variables for L850f_apply_rotation_matrix_to_object_vertices
#850c#1b0c
L850c_vertex_times_matrix_24bit_accumulator:  ; 24 bit number buffer
#850c#1b0c3
    db #00, #00, #00
#850f#1b0f
#850f#1b0f
#850f#1b0f
; --------------------------------
#850f#1b0f
; Given object vertex coordinates already relative to the player,
#850f#1b0f
; stored in (L5e63_3d_vertex_coordinates_relative_to_player), this
#850f#1b0f
; method multiplies them by the rotation matrix (L5e55_rotation_matrix),
#850f#1b0f
; and stores the results in (L5e9f_3d_vertex_coordinates_after_rotation_matrix).
#850f#1b0f
L850f_apply_rotation_matrix_to_object_vertices:
#850f#1b0f416
fd 21 63 5e 
    ld iy, L5e63_3d_vertex_coordinates_relative_to_player
#8513#1b13311
21 9f 5e 
    ld hl, L5e9f_3d_vertex_coordinates_after_rotation_matrix
#8516#1b16314
3a 96 74 
    ld a, (L7496_current_drawing_primitive_n_vertices)
#8519#1b1915
4f 
    ld c, a
#851a#1b1a
L851a_vertex_loop:
#851a#1b1a
    ; Multiply the vertex 3d vector by the rotation matrix:
#851a#1b1a416
dd 21 55 5e 
    ld ix, L5e55_rotation_matrix
#851e#1b1e28
06 03 
    ld b, 3
#8520#1b20
L8520_coordinate_loop:
#8520#1b20112
e5 
    push hl
#8521#1b21
        ; Zero out the 24bit accumulator:
#8521#1b2115
af 
        xor a
#8522#1b22314
32 0c 85 
        ld (L850c_vertex_times_matrix_24bit_accumulator), a
#8525#1b25314
32 0d 85 
        ld (L850c_vertex_times_matrix_24bit_accumulator + 1), a
#8528#1b28314
32 0e 85 
        ld (L850c_vertex_times_matrix_24bit_accumulator + 2), a
#852b#1b2b
        ; x * matrix[b][0]
#852b#1b2b321
dd 7e 00 
        ld a, (ix)  ; ix points to the rotation matrix
#852e#1b2e212
dd 23 
        inc ix
#8530#1b3015
b7 
        or a
#8531#1b31213/8
28 0f 
        jr z, L8542_multiply_by_0
#8533#1b33321
fd 6e 00 
        ld l, (iy)
#8536#1b36321
fd 66 01 
        ld h, (iy + 1)  ; Get "x" vertex coordinate
#8539#1b39318
cd 08 a1 
        call La108_a_times_hl_signed
#853c#1b3c314
32 0e 85 
        ld (L850c_vertex_times_matrix_24bit_accumulator + 2), a
#853f#1b3f317
22 0c 85 
        ld (L850c_vertex_times_matrix_24bit_accumulator), hl
#8542#1b42
L8542_multiply_by_0:
#8542#1b42
        ; y * matrix[b][1]
#8542#1b42321
dd 7e 00 
        ld a, (ix)
#8545#1b45212
dd 23 
        inc ix
#8547#1b4715
b7 
        or a
#8548#1b48213/8
28 19 
        jr z, L8563_multiply_by_0
#854a#1b4a321
fd 6e 02 
        ld l, (iy + 2)
#854d#1b4d321
fd 66 03 
        ld h, (iy + 3)  ; Get "y" vertex coordinate
#8550#1b50318
cd 08 a1 
        call La108_a_times_hl_signed
#8553#1b53
        ; Add to the 24 bit accumulator:
#8553#1b53422
ed 5b 0c 85 
        ld de, (L850c_vertex_times_matrix_24bit_accumulator)
#8557#1b57112
19 
        add hl, de
#8558#1b58317
22 0c 85 
        ld (L850c_vertex_times_matrix_24bit_accumulator), hl
#855b#1b5b15
5f 
        ld e, a
#855c#1b5c314
3a 0e 85 
        ld a, (L850c_vertex_times_matrix_24bit_accumulator + 2)
#855f#1b5f15
8b 
        adc a, e
#8560#1b60314
32 0e 85 
        ld (L850c_vertex_times_matrix_24bit_accumulator + 2), a
#8563#1b63
L8563_multiply_by_0:
#8563#1b63
        ; z * matrix[b][2]
#8563#1b63321
dd 7e 00 
        ld a, (ix)
#8566#1b66212
dd 23 
        inc ix
#8568#1b6815
b7 
        or a
#8569#1b69213/8
28 19 
        jr z, L8584_multiply_by_0
#856b#1b6b321
fd 6e 04 
        ld l, (iy + 4)
#856e#1b6e321
fd 66 05 
        ld h, (iy + 5)  ; Get "z" vertex coordinate
#8571#1b71318
cd 08 a1 
        call La108_a_times_hl_signed
#8574#1b74
        ; Add to the 24 bit accumulator:
#8574#1b74422
ed 5b 0c 85 
        ld de, (L850c_vertex_times_matrix_24bit_accumulator)
#8578#1b78112
19 
        add hl, de
#8579#1b79317
22 0c 85 
        ld (L850c_vertex_times_matrix_24bit_accumulator), hl
#857c#1b7c15
5f 
        ld e, a
#857d#1b7d314
3a 0e 85 
        ld a, (L850c_vertex_times_matrix_24bit_accumulator + 2)
#8580#1b8015
8b 
        adc a, e
#8581#1b81314
32 0e 85 
        ld (L850c_vertex_times_matrix_24bit_accumulator + 2), a
#8584#1b84
L8584_multiply_by_0:
#8584#1b84317
2a 0c 85 
        ld hl, (L850c_vertex_times_matrix_24bit_accumulator)
#8587#1b87314
3a 0e 85 
        ld a, (L850c_vertex_times_matrix_24bit_accumulator + 2)
#858a#1b8a
        ; (a, e) = (a, hl) / 64
#858a#1b8a112
29 
        add hl, hl
#858b#1b8b15
17 
        rla
#858c#1b8c112
29 
        add hl, hl
#858d#1b8d15
17 
        rla
#858e#1b8e15
5c 
        ld e, h
#858f#1b8f111
e1 
    pop hl
#8590#1b9018
73 
    ld (hl), e
#8591#1b9117
23 
    inc hl
#8592#1b9218
77 
    ld (hl), a
#8593#1b9317
23 
    inc hl
#8594#1b94214/9
10 8a 
    djnz L8520_coordinate_loop
#8596#1b96
    ; next vertex:
#8596#1b96311
11 06 00 
    ld de, 6
#8599#1b99217
fd 19 
    add iy, de
#859b#1b9b15
0d 
    dec c
#859c#1b9c311
c2 1a 85 
    jp nz, L851a_vertex_loop
#859f#1b9f111
c9 
    ret
#85a0#1ba0
#85a0#1ba0
#85a0#1ba0
; --------------------------------
#85a0#1ba0
; Auxiliary variables for L85ae_clip_edge
#85a0#1ba0
L85a0_vertex1_coordinates:
#85a0#1ba06
    dw #0000, #0000, #0000
#85a6#1ba6
L85a6_vertex2_coordinates:
#85a6#1ba66
    dw #0000, #0000, #0000
#85ac#1bac
L85ac_vertex_frustum_checks:
#85ac#1bac2
    db #00, #00
#85ae#1bae
#85ae#1bae
#85ae#1bae
; --------------------------------
#85ae#1bae
; This function clips an edge to make sure both vertices are inside of the viewing area.
#85ae#1bae
; - If both fail the same frustum visibility check, the edge is discarded.
#85ae#1bae
; - Otherwise, for each failed test, a point that intersects with the viewing volume is calculated and the
#85ae#1bae
;   point that was outside of the volume is replaced.
#85ae#1bae
; - At the end, if all 5 frustum checks were able to be successfully passed, the points are
#85ae#1bae
;   projected.
#85ae#1bae
; Note: I think this method contains several bugs, that might be hard to detect, as
#85ae#1bae
;       they might only show up in certain corner cases. But this should be verified.
#85ae#1bae
;       I am, of course, not 100% sure.
#85ae#1bae
; Input:
#85ae#1bae
; - ix: ptr to "L5ee8_already_projected_vertex_coordinates" entry for this edge (+ 1)
#85ae#1bae
; - hl: pointer to 3d vertex 1 (after rotation matrix)
#85ae#1bae
; - iy: pointer to 3d vertex 2 (after rotation matrix)
#85ae#1bae
; - c: frustum checks of vertex 1
#85ae#1bae
; - b: frustum checks of vertex 2
#85ae#1bae
L85ae_clip_edge:
#85ae#1bae
    ; Save the vertex info to local variables:
#85ae#1bae422
ed 43 ac 85 
    ld (L85ac_vertex_frustum_checks), bc
#85b2#1bb2311
01 06 00 
    ld bc, 6
#85b5#1bb5311
11 a0 85 
    ld de, L85a0_vertex1_coordinates
#85b8#1bb8223/18
ed b0 
    ldir
#85ba#1bba28
0e 06 
    ld c, 6
#85bc#1bbc217
fd e5 
    push iy
#85be#1bbe111
e1 
    pop hl
#85bf#1bbf223/18
ed b0 
    ldir
#85c1#1bc1
#85c1#1bc1217
dd e5 
    push ix
#85c3#1bc3216
fd e1 
    pop iy  ; iy = ptr to "L5ee8_already_projected_vertex_coordinates" entry for this edge (+ 1)
#85c5#1bc5422
ed 4b ac 85 
    ld bc, (L85ac_vertex_frustum_checks)
#85c9#1bc9210
cb 41 
    bit 0, c
#85cb#1bcb213/8
20 07 
    jr nz, L85d4
#85cd#1bcd
    ; First vertex is behind the camera
#85cd#1bcd210
cb 40 
    bit 0, b
#85cf#1bcf311
ca cb 88 
    jp z, L88cb_mark_as_processed_and_return  ; if both are behind the camera, this edge projects no points.
#85d2#1bd2213
18 05 
    jr L85d9
#85d4#1bd4
#85d4#1bd4
L85d4:
#85d4#1bd4
    ; First point is in front of the camera
#85d4#1bd4210
cb 40 
    bit 0, b
#85d6#1bd6311
c2 66 86 
    jp nz, L8666
#85d9#1bd9
#85d9#1bd9
L85d9:
#85d9#1bd9
    ; One point is behind the camera, and one in front:
#85d9#1bd9317
2a a4 85 
    ld hl, (L85a0_vertex1_coordinates + 2 * 2)  ; hl = v1.z
#85dc#1bdc422
ed 5b aa 85 
    ld de, (L85a6_vertex2_coordinates + 2 * 2)  ; de = v2.z
#85e0#1be015
af 
    xor a
#85e1#1be1217
ed 52 
    sbc hl, de  ; hl = (v1.z - v2.z)
#85e3#1be3112
e5 
    push hl
#85e4#1be415
44 
        ld b, h  ; bc = (v1.z - v2.z)
#85e5#1be515
4d 
        ld c, l
#85e6#1be6317
2a a6 85 
        ld hl, (L85a6_vertex2_coordinates)  ; hl = v1.x
#85e9#1be9422
ed 5b a0 85 
        ld de, (L85a0_vertex1_coordinates)  ; de = v2.x
#85ed#1bed15
b7 
        or a
#85ee#1bee217
ed 52 
        sbc hl, de  ; hl = v1.x - v2.x
#85f0#1bf0422
ed 5b a4 85 
        ld de, (L85a0_vertex1_coordinates + 2 * 2)  ; d2 = v1.z
#85f4#1bf4318
cd 5e a1 
        call La15e_de_times_hl_signed  ; (de, hl) = v1.z * (v1.x - v2.x)
#85f7#1bf7318
cd b7 b1 
        call Lb1b7_de_hl_divided_by_bc_signed  ; (de, hl) = (v1.z * (v1.x - v2.x)) / (v1.z - v2.z)
#85fa#1bfa422
ed 5b a0 85 
        ld de, (L85a0_vertex1_coordinates)  ; v1.x
#85fe#1bfe112
19 
        add hl, de  ; hl = v1.z * (v1.x - v2.x) / (v1.z - v2.z) + v1.x
#85ff#1bff111
c1 
    pop bc  ; bc = (v1.z - v2.z)
#8600#1c00112
e5 
    push hl
#8601#1c01317
2a a8 85 
        ld hl, (L85a6_vertex2_coordinates + 1 * 2)
#8604#1c04422
ed 5b a2 85 
        ld de, (L85a0_vertex1_coordinates + 1 * 2)
#8608#1c0815
b7 
        or a
#8609#1c09217
ed 52 
        sbc hl, de  ; hl = v2.y - v1.y
#860b#1c0b422
ed 5b a4 85 
        ld de, (L85a0_vertex1_coordinates + 2 * 2)
#860f#1c0f318
cd 5e a1 
        call La15e_de_times_hl_signed  ; (de, hl) = v1.z * (v2.y - v1.y)
#8612#1c12318
cd b7 b1 
        call Lb1b7_de_hl_divided_by_bc_signed  ; (de, hl) = v1.z * (v2.y - v1.y) / (v1.z - v2.z)
#8615#1c15422
ed 5b a2 85 
        ld de, (L85a0_vertex1_coordinates + 1 * 2)
#8619#1c19112
19 
        add hl, de  ; (de, hl) = v1.z * (v2.y - v1.y) / (v1.z - v2.z) + v1.y
#861a#1c1a311
11 00 00 
        ld de, 0
#861d#1c1d314
3a ac 85 
        ld a, (L85ac_vertex_frustum_checks)
#8620#1c20210
cb 47 
        bit 0, a
#8622#1c22213/8
20 0d 
        jr nz, L8631_overwrite_vertex2
#8624#1c24
        ; Overwrite vertex 1:
#8624#1c24
        ; BUG? I think y and z are flipped here and this is wrongly calculated
#8624#1c24317
22 a2 85 
        ld (L85a0_vertex1_coordinates + 1 * 2), hl  ; v1.y = 0
#8627#1c27111
e1 
    pop hl
#8628#1c28317
22 a0 85 
    ld (L85a0_vertex1_coordinates), hl  ; v1.x = v1.z * (v1.x - v2.x) / (v1.z - v2.z) + v1.x
#862b#1c2b422
ed 53 a4 85 
    ld (L85a0_vertex1_coordinates + 2 * 2), de  ; v1.z = v1.z * (v2.y - v1.y) / (v1.z - v2.z) + v1.y
#862f#1c2f213
18 0b 
    jr L863c_both_vertices_in_front_of_camera
#8631#1c31
L8631_overwrite_vertex2:
#8631#1c31
    ; BUG? I think y and z are flipped here and this is wrongly calculated
#8631#1c31317
22 a8 85 
    ld (L85a6_vertex2_coordinates + 1 * 2), hl
#8634#1c34111
e1 
    pop hl
#8635#1c35317
22 a6 85 
    ld (L85a6_vertex2_coordinates), hl
#8638#1c38422
ed 53 aa 85 
    ld (L85a6_vertex2_coordinates + 2 * 2), de
#863c#1c3c
#863c#1c3c
L863c_both_vertices_in_front_of_camera:
#863c#1c3c
    ; Update bit 1 of the frustum checks for the new points:
#863c#1c3c422
ed 4b ac 85 
    ld bc, (L85ac_vertex_frustum_checks)
#8640#1c40
    ; BUG? bit "1" was "x - z" check in "L9246_object_visibility_check", but here
#8640#1c40
    ;      the code is doing "y - z" instead, which should be bit 2.
#8640#1c40210
cb c8 
    set 1, b
#8642#1c42210
cb c9 
    set 1, c
#8644#1c44317
2a a4 85 
    ld hl, (L85a0_vertex1_coordinates + 2 * 2)  ; v1.z
#8647#1c47422
ed 5b a2 85 
    ld de, (L85a0_vertex1_coordinates + 1 * 2)  ; v1.y
#864b#1c4b15
b7 
    or a
#864c#1c4c217
ed 52 
    sbc hl, de  ; hl = v1.y - v1.z
#864e#1c4e311
f2 53 86 
    jp p, L8653
#8651#1c51210
cb 89 
    res 1, c
#8653#1c53
L8653:
#8653#1c53317
2a aa 85 
    ld hl, (L85a6_vertex2_coordinates + 2 * 2)  ; v2.z
#8656#1c56422
ed 5b a8 85 
    ld de, (L85a6_vertex2_coordinates + 1 * 2)  ; v2.y
#865a#1c5a15
b7 
    or a
#865b#1c5b217
ed 52 
    sbc hl, de  ; hl = v2.y - v2.z
#865d#1c5d311
f2 62 86 
    jp p, L8662
#8660#1c60210
cb 88 
    res 1, b
#8662#1c62
L8662:
#8662#1c62422
ed 43 ac 85 
    ld (L85ac_vertex_frustum_checks), bc
#8666#1c66
#8666#1c66
    ; The remainder of this function is made out of 4 blocks analogous to the one above,
#8666#1c66
    ; in each block, if both vertexes are found to fail the same frustum check, the edge is
#8666#1c66
    ; ignored, otherwise, if only one of them fails it, a point that intersects with the plane
#8666#1c66
    ; that defines the frustum check in question is found, and the point that failed the check 
#8666#1c66
    ; is overwritten. This is done for all 4 remaining frustum checks, and at the end, 
#8666#1c66
    ; we can be sure that both points are inside the viewing area.
#8666#1c66
    ; Ensure both vertices pass frustum check 1:
#8666#1c66
L8666:
#8666#1c66210
cb 49 
    bit 1, c
#8668#1c68213/8
20 07 
    jr nz, L8671
#866a#1c6a210
cb 48 
    bit 1, b
#866c#1c6c311
ca cb 88 
    jp z, L88cb_mark_as_processed_and_return
#866f#1c6f213
18 05 
    jr L8676
#8671#1c71
L8671:
#8671#1c71210
cb 48 
    bit 1, b
#8673#1c73311
c2 e6 86 
    jp nz, L86e6_both_vertices_pass_frustum_check1
#8676#1c76
L8676:
#8676#1c76317
2a a4 85 
    ld hl, (L85a0_vertex1_coordinates + 2 * 2)
#8679#1c79422
ed 5b a2 85 
    ld de, (L85a0_vertex1_coordinates + 1 * 2)
#867d#1c7d15
af 
    xor a
#867e#1c7e217
ed 52 
    sbc hl, de
#8680#1c80112
e5 
    push hl
#8681#1c81317
2a a8 85 
    ld hl, (L85a6_vertex2_coordinates + 1 * 2)
#8684#1c84416
dd 21 b3 86 
    ld ix, L86b3
#8688#1c88
L8688:
#8688#1c8815
af 
    xor a
#8689#1c89217
ed 52 
    sbc hl, de
#868b#1c8b422
ed 5b aa 85 
    ld de, (L85a6_vertex2_coordinates + 2 * 2)
#868f#1c8f15
af 
    xor a
#8690#1c90217
ed 52 
    sbc hl, de
#8692#1c92422
ed 5b a4 85 
    ld de, (L85a0_vertex1_coordinates + 2 * 2)
#8696#1c96112
19 
    add hl, de
#8697#1c9715
44 
    ld b, h
#8698#1c9815
4d 
    ld c, l
#8699#1c99317
2a aa 85 
    ld hl, (L85a6_vertex2_coordinates + 2 * 2)
#869c#1c9c15
b7 
    or a
#869d#1c9d217
ed 52 
    sbc hl, de
#869f#1c9f111
d1 
    pop de
#86a0#1ca0112
d5 
    push de
#86a1#1ca1112
c5 
    push bc
#86a2#1ca2318
cd 5e a1 
    call La15e_de_times_hl_signed
#86a5#1ca5318
cd b7 b1 
    call Lb1b7_de_hl_divided_by_bc_signed
#86a8#1ca8422
ed 5b a4 85 
    ld de, (L85a0_vertex1_coordinates + 2 * 2)
#86ac#1cac112
19 
    add hl, de
#86ad#1cad111
c1 
    pop bc
#86ae#1cae111
d1 
    pop de
#86af#1caf112
e5 
    push hl
#86b0#1cb0112
d5 
    push de
#86b1#1cb1210
dd e9 
    jp ix
#86b3#1cb3
L86b3:
#86b3#1cb3317
2a a6 85 
    ld hl, (L85a6_vertex2_coordinates)
#86b6#1cb6422
ed 5b a0 85 
    ld de, (L85a0_vertex1_coordinates)
#86ba#1cba15
af 
    xor a
#86bb#1cbb217
ed 52 
    sbc hl, de
#86bd#1cbd111
d1 
    pop de
#86be#1cbe318
cd 5e a1 
    call La15e_de_times_hl_signed
#86c1#1cc1318
cd b7 b1 
    call Lb1b7_de_hl_divided_by_bc_signed
#86c4#1cc4422
ed 5b a0 85 
    ld de, (L85a0_vertex1_coordinates)
#86c8#1cc8112
19 
    add hl, de
#86c9#1cc9314
3a ac 85 
    ld a, (L85ac_vertex_frustum_checks)
#86cc#1ccc210
cb 4f 
    bit 1, a
#86ce#1cce213/8
20 0c 
    jr nz, L86dc
#86d0#1cd0317
22 a0 85 
    ld (L85a0_vertex1_coordinates), hl
#86d3#1cd3111
e1 
    pop hl
#86d4#1cd4317
22 a2 85 
    ld (L85a0_vertex1_coordinates + 1 * 2), hl
#86d7#1cd7317
22 a4 85 
    ld (L85a0_vertex1_coordinates + 2 * 2), hl
#86da#1cda213
18 0a 
    jr L86e6_both_vertices_pass_frustum_check1
#86dc#1cdc
L86dc:
#86dc#1cdc317
22 a6 85 
    ld (L85a6_vertex2_coordinates), hl
#86df#1cdf111
e1 
    pop hl
#86e0#1ce0317
22 a8 85 
    ld (L85a6_vertex2_coordinates + 1 * 2), hl
#86e3#1ce3317
22 aa 85 
    ld (L85a6_vertex2_coordinates + 2 * 2), hl
#86e6#1ce6
#86e6#1ce6
L86e6_both_vertices_pass_frustum_check1:
#86e6#1ce6
    ; BUG? Same as above, I think these are flipped!
#86e6#1ce6
    ;      bit "2" was "y - z" check in "L9246_object_visibility_check", but here
#86e6#1ce6
    ;      the code is doing "x - z" instead, which should be bit 1.
#86e6#1ce6422
ed 4b ac 85 
    ld bc, (L85ac_vertex_frustum_checks)
#86ea#1cea210
cb d0 
    set 2, b
#86ec#1cec210
cb d1 
    set 2, c
#86ee#1cee317
2a a4 85 
    ld hl, (L85a0_vertex1_coordinates + 2 * 2)  ; v1.z
#86f1#1cf1422
ed 5b a0 85 
    ld de, (L85a0_vertex1_coordinates)  ; v1.x
#86f5#1cf515
b7 
    or a
#86f6#1cf6217
ed 52 
    sbc hl, de
#86f8#1cf8311
f2 fd 86 
    jp p, L86fd
#86fb#1cfb210
cb 91 
    res 2, c
#86fd#1cfd
L86fd:
#86fd#1cfd317
2a aa 85 
    ld hl, (L85a6_vertex2_coordinates + 2 * 2)  ; v2.z
#8700#1d00422
ed 5b a6 85 
    ld de, (L85a6_vertex2_coordinates)  ; v2.x
#8704#1d0415
b7 
    or a
#8705#1d05217
ed 52 
    sbc hl, de
#8707#1d07311
f2 0c 87 
    jp p, L870c
#870a#1d0a210
cb 90 
    res 2, b
#870c#1d0c
L870c:
#870c#1d0c422
ed 43 ac 85 
    ld (L85ac_vertex_frustum_checks), bc
#8710#1d10210
cb 51 
    bit 2, c
#8712#1d12213/8
20 07 
    jr nz, L871b
#8714#1d14210
cb 50 
    bit 2, b
#8716#1d16311
ca cb 88 
    jp z, L88cb_mark_as_processed_and_return
#8719#1d19213
18 05 
    jr L8720
#871b#1d1b
L871b:
#871b#1d1b210
cb 50 
    bit 2, b
#871d#1d1d311
c2 68 87 
    jp nz, L8768_both_vertices_pass_frustum_check2
#8720#1d20
L8720:
#8720#1d20317
2a a4 85 
    ld hl, (L85a0_vertex1_coordinates + 2 * 2)
#8723#1d23422
ed 5b a0 85 
    ld de, (L85a0_vertex1_coordinates)
#8727#1d2715
af 
    xor a
#8728#1d28217
ed 52 
    sbc hl, de
#872a#1d2a112
e5 
    push hl
#872b#1d2b317
2a a6 85 
    ld hl, (L85a6_vertex2_coordinates)
#872e#1d2e416
dd 21 35 87 
    ld ix, L8735
#8732#1d32311
c3 88 86 
    jp L8688
#8735#1d35
L8735:
#8735#1d35317
2a a8 85 
    ld hl, (L85a6_vertex2_coordinates + 1 * 2)
#8738#1d38422
ed 5b a2 85 
    ld de, (L85a0_vertex1_coordinates + 1 * 2)
#873c#1d3c15
af 
    xor a
#873d#1d3d217
ed 52 
    sbc hl, de
#873f#1d3f111
d1 
    pop de
#8740#1d40318
cd 5e a1 
    call La15e_de_times_hl_signed
#8743#1d43318
cd b7 b1 
    call Lb1b7_de_hl_divided_by_bc_signed
#8746#1d46422
ed 5b a2 85 
    ld de, (L85a0_vertex1_coordinates + 1 * 2)
#874a#1d4a112
19 
    add hl, de
#874b#1d4b314
3a ac 85 
    ld a, (L85ac_vertex_frustum_checks)
#874e#1d4e210
cb 57 
    bit 2, a
#8750#1d50213/8
20 0c 
    jr nz, L875e
#8752#1d52317
22 a2 85 
    ld (L85a0_vertex1_coordinates + 1 * 2), hl
#8755#1d55111
e1 
    pop hl
#8756#1d56317
22 a0 85 
    ld (L85a0_vertex1_coordinates), hl
#8759#1d59317
22 a4 85 
    ld (L85a0_vertex1_coordinates + 2 * 2), hl
#875c#1d5c213
18 0a 
    jr L8768_both_vertices_pass_frustum_check2
#875e#1d5e
L875e:
#875e#1d5e317
22 a8 85 
    ld (L85a6_vertex2_coordinates + 1 * 2), hl
#8761#1d61111
e1 
    pop hl
#8762#1d62317
22 a6 85 
    ld (L85a6_vertex2_coordinates), hl
#8765#1d65317
22 aa 85 
    ld (L85a6_vertex2_coordinates + 2 * 2), hl
#8768#1d68
#8768#1d68
L8768_both_vertices_pass_frustum_check2:
#8768#1d68422
ed 4b ac 85 
    ld bc, (L85ac_vertex_frustum_checks)
#876c#1d6c210
cb d8 
    set 3, b
#876e#1d6e210
cb d9 
    set 3, c
#8770#1d70317
2a a4 85 
    ld hl, (L85a0_vertex1_coordinates + 2 * 2)  ; v1.z
#8773#1d73422
ed 5b a2 85 
    ld de, (L85a0_vertex1_coordinates + 1 * 2)  ; v1.y
#8777#1d7715
b7 
    or a
#8778#1d78217
ed 5a 
    adc hl, de
#877a#1d7a311
f2 7f 87 
    jp p, L877f
#877d#1d7d210
cb 99 
    res 3, c
#877f#1d7f
L877f:
#877f#1d7f317
2a aa 85 
    ld hl, (L85a6_vertex2_coordinates + 2 * 2)  ; v2.z
#8782#1d82422
ed 5b a8 85 
    ld de, (L85a6_vertex2_coordinates + 1 * 2)  ; v2.y
#8786#1d8615
b7 
    or a
#8787#1d87217
ed 5a 
    adc hl, de
#8789#1d89311
f2 8e 87 
    jp p, L878e
#878c#1d8c210
cb 98 
    res 3, b
#878e#1d8e
L878e:
#878e#1d8e422
ed 43 ac 85 
    ld (L85ac_vertex_frustum_checks), bc
#8792#1d92210
cb 59 
    bit 3, c
#8794#1d94213/8
20 07 
    jr nz, L879d
#8796#1d96210
cb 58 
    bit 3, b
#8798#1d98311
ca cb 88 
    jp z, L88cb_mark_as_processed_and_return
#879b#1d9b213
18 05 
    jr L87a2
#879d#1d9d
L879d:
#879d#1d9d210
cb 58 
    bit 3, b
#879f#1d9f311
c2 1b 88 
    jp nz, L881b_both_vertices_pass_frustum_check3
#87a2#1da2
L87a2:
#87a2#1da2317
2a a4 85 
    ld hl, (L85a0_vertex1_coordinates + 2 * 2)
#87a5#1da5422
ed 5b a2 85 
    ld de, (L85a0_vertex1_coordinates + 1 * 2)
#87a9#1da9112
19 
    add hl, de
#87aa#1daa112
e5 
    push hl
#87ab#1dab317
2a a8 85 
    ld hl, (L85a6_vertex2_coordinates + 1 * 2)
#87ae#1dae416
dd 21 de 87 
    ld ix, L87de
#87b2#1db2
L87b2:
#87b2#1db215
eb 
    ex de, hl
#87b3#1db315
af 
    xor a
#87b4#1db4217
ed 52 
    sbc hl, de
#87b6#1db6422
ed 5b aa 85 
    ld de, (L85a6_vertex2_coordinates + 2 * 2)
#87ba#1dba15
af 
    xor a
#87bb#1dbb217
ed 52 
    sbc hl, de
#87bd#1dbd422
ed 5b a4 85 
    ld de, (L85a0_vertex1_coordinates + 2 * 2)
#87c1#1dc1112
19 
    add hl, de
#87c2#1dc215
44 
    ld b, h
#87c3#1dc315
4d 
    ld c, l
#87c4#1dc4317
2a aa 85 
    ld hl, (L85a6_vertex2_coordinates + 2 * 2)
#87c7#1dc715
b7 
    or a
#87c8#1dc8217
ed 52 
    sbc hl, de
#87ca#1dca111
d1 
    pop de
#87cb#1dcb112
d5 
    push de
#87cc#1dcc112
c5 
    push bc
#87cd#1dcd318
cd 5e a1 
    call La15e_de_times_hl_signed
#87d0#1dd0318
cd b7 b1 
    call Lb1b7_de_hl_divided_by_bc_signed
#87d3#1dd3422
ed 5b a4 85 
    ld de, (L85a0_vertex1_coordinates + 2 * 2)
#87d7#1dd7112
19 
    add hl, de
#87d8#1dd8111
c1 
    pop bc
#87d9#1dd9111
d1 
    pop de
#87da#1dda112
e5 
    push hl
#87db#1ddb112
d5 
    push de
#87dc#1ddc210
dd e9 
    jp ix
#87de#1dde
L87de:
#87de#1dde317
2a a6 85 
    ld hl, (L85a6_vertex2_coordinates)
#87e1#1de1422
ed 5b a0 85 
    ld de, (L85a0_vertex1_coordinates)
#87e5#1de515
af 
    xor a
#87e6#1de6217
ed 52 
    sbc hl, de
#87e8#1de8111
d1 
    pop de
#87e9#1de9318
cd 5e a1 
    call La15e_de_times_hl_signed
#87ec#1dec318
cd b7 b1 
    call Lb1b7_de_hl_divided_by_bc_signed
#87ef#1def422
ed 5b a0 85 
    ld de, (L85a0_vertex1_coordinates)
#87f3#1df3112
19 
    add hl, de
#87f4#1df4111
d1 
    pop de
#87f5#1df515
7b 
    ld a, e
#87f6#1df615
2f 
    cpl
#87f7#1df715
4f 
    ld c, a
#87f8#1df815
7a 
    ld a, d
#87f9#1df915
2f 
    cpl
#87fa#1dfa15
47 
    ld b, a
#87fb#1dfb17
03 
    inc bc
#87fc#1dfc314
3a ac 85 
    ld a, (L85ac_vertex_frustum_checks)
#87ff#1dff210
cb 5f 
    bit 3, a
#8801#1e01213/8
20 0d 
    jr nz, L8810
#8803#1e03317
22 a0 85 
    ld (L85a0_vertex1_coordinates), hl
#8806#1e06422
ed 43 a2 85 
    ld (L85a0_vertex1_coordinates + 1 * 2), bc
#880a#1e0a422
ed 53 a4 85 
    ld (L85a0_vertex1_coordinates + 2 * 2), de
#880e#1e0e213
18 0b 
    jr L881b_both_vertices_pass_frustum_check3
#8810#1e10
L8810:
#8810#1e10317
22 a6 85 
    ld (L85a6_vertex2_coordinates), hl
#8813#1e13422
ed 43 a8 85 
    ld (L85a6_vertex2_coordinates + 1 * 2), bc
#8817#1e17422
ed 53 aa 85 
    ld (L85a6_vertex2_coordinates + 2 * 2), de
#881b#1e1b
#881b#1e1b
L881b_both_vertices_pass_frustum_check3:
#881b#1e1b422
ed 4b ac 85 
    ld bc, (L85ac_vertex_frustum_checks)
#881f#1e1f210
cb e0 
    set 4, b
#8821#1e21210
cb e1 
    set 4, c
#8823#1e23317
2a a4 85 
    ld hl, (L85a0_vertex1_coordinates + 2 * 2)
#8826#1e26422
ed 5b a0 85 
    ld de, (L85a0_vertex1_coordinates)
#882a#1e2a15
b7 
    or a
#882b#1e2b217
ed 5a 
    adc hl, de
#882d#1e2d311
f2 32 88 
    jp p, L8832
#8830#1e30210
cb a1 
    res 4, c
#8832#1e32
L8832:
#8832#1e32317
2a aa 85 
    ld hl, (L85a6_vertex2_coordinates + 2 * 2)
#8835#1e35422
ed 5b a6 85 
    ld de, (L85a6_vertex2_coordinates)
#8839#1e3915
b7 
    or a
#883a#1e3a217
ed 5a 
    adc hl, de
#883c#1e3c311
f2 41 88 
    jp p, L8841
#883f#1e3f210
cb a0 
    res 4, b
#8841#1e41
L8841:
#8841#1e41422
ed 43 ac 85 
    ld (L85ac_vertex_frustum_checks), bc
#8845#1e45210
cb 61 
    bit 4, c
#8847#1e47213/8
20 07 
    jr nz, L8850
#8849#1e49210
cb 60 
    bit 4, b
#884b#1e4b311
ca cb 88 
    jp z, L88cb_mark_as_processed_and_return
#884e#1e4e213
18 05 
    jr L8855
#8850#1e50
L8850:
#8850#1e50210
cb 60 
    bit 4, b
#8852#1e52311
c2 a5 88 
    jp nz, L88a5_both_vertices_pass_frustum_check4
#8855#1e55
L8855:
#8855#1e55317
2a a4 85 
    ld hl, (L85a0_vertex1_coordinates + 2 * 2)
#8858#1e58422
ed 5b a0 85 
    ld de, (L85a0_vertex1_coordinates)
#885c#1e5c112
19 
    add hl, de
#885d#1e5d112
e5 
    push hl
#885e#1e5e317
2a a6 85 
    ld hl, (L85a6_vertex2_coordinates)
#8861#1e61416
dd 21 68 88 
    ld ix, L8868
#8865#1e65311
c3 b2 87 
    jp L87b2
#8868#1e68
L8868:
#8868#1e68317
2a a8 85 
    ld hl, (L85a6_vertex2_coordinates + 1 * 2)
#886b#1e6b422
ed 5b a2 85 
    ld de, (L85a0_vertex1_coordinates + 1 * 2)
#886f#1e6f15
af 
    xor a
#8870#1e70217
ed 52 
    sbc hl, de
#8872#1e72111
d1 
    pop de
#8873#1e73318
cd 5e a1 
    call La15e_de_times_hl_signed
#8876#1e76318
cd b7 b1 
    call Lb1b7_de_hl_divided_by_bc_signed
#8879#1e79422
ed 5b a2 85 
    ld de, (L85a0_vertex1_coordinates + 1 * 2)
#887d#1e7d112
19 
    add hl, de
#887e#1e7e111
d1 
    pop de
#887f#1e7f15
7b 
    ld a, e
#8880#1e8015
2f 
    cpl
#8881#1e8115
4f 
    ld c, a
#8882#1e8215
7a 
    ld a, d
#8883#1e8315
2f 
    cpl
#8884#1e8415
47 
    ld b, a
#8885#1e8517
03 
    inc bc
#8886#1e86314
3a ac 85 
    ld a, (L85ac_vertex_frustum_checks)
#8889#1e89210
cb 67 
    bit 4, a
#888b#1e8b213/8
20 0d 
    jr nz, L889a
#888d#1e8d422
ed 43 a0 85 
    ld (L85a0_vertex1_coordinates), bc
#8891#1e91317
22 a2 85 
    ld (L85a0_vertex1_coordinates + 1 * 2), hl
#8894#1e94422
ed 53 a4 85 
    ld (L85a0_vertex1_coordinates + 2 * 2), de
#8898#1e98213
18 0b 
    jr L88a5_both_vertices_pass_frustum_check4
#889a#1e9a
L889a:
#889a#1e9a422
ed 43 a6 85 
    ld (L85a6_vertex2_coordinates), bc
#889e#1e9e317
22 a8 85 
    ld (L85a6_vertex2_coordinates + 1 * 2), hl
#88a1#1ea1422
ed 53 aa 85 
    ld (L85a6_vertex2_coordinates + 2 * 2), de
#88a5#1ea5
#88a5#1ea5
L88a5_both_vertices_pass_frustum_check4:
#88a5#1ea5
    ; We are done clipping the edge, now project whichever vertex needs
#88a5#1ea5
    ; projecting:
#88a5#1ea528
3e ff 
    ld a, #ff
#88a7#1ea7321
fd be 00 
    cp (iy)
#88aa#1eaa213/8
20 0d 
    jr nz, L88b9
#88ac#1eac
    ; Vertex 1 needs projecting:
#88ac#1eac416
dd 21 a0 85 
    ld ix, L85a0_vertex1_coordinates
#88b0#1eb0318
cd fc 90 
    call L90fc_project_one_vertex
#88b3#1eb3321
fd 71 00 
    ld (iy), c  ; x
#88b6#1eb6321
fd 70 01 
    ld (iy + 1), b  ; y
#88b9#1eb9
L88b9:
#88b9#1eb9321
fd be 02 
    cp (iy + 2)
#88bc#1ebc213/8
20 0d 
    jr nz, L88cb_mark_as_processed_and_return
#88be#1ebe
    ; Vertex 2 needs projecting:
#88be#1ebe416
dd 21 a6 85 
    ld ix, L85a6_vertex2_coordinates
#88c2#1ec2318
cd fc 90 
    call L90fc_project_one_vertex
#88c5#1ec5321
fd 71 02 
    ld (iy + 2), c  ; x
#88c8#1ec8321
fd 70 03 
    ld (iy + 3), b  ; y
#88cb#1ecb
L88cb_mark_as_processed_and_return:
#88cb#1ecb28
3e 01 
    ld a, 1
#88cd#1ecd321
fd 77 ffff 
    ld (iy - 1), a  ; mark edge as processed
#88d0#1ed0111
c9 
    ret
#88d1#1ed1
#88d1#1ed1
#88d1#1ed1
; --------------------------------
#88d1#1ed1
; Checks if the normal of a face points away from the camera (and potentially we do not need to draw the face).
#88d1#1ed1
; Input:
#88d1#1ed1
; - iy: pointer to the vertex indexes of this face
#88d1#1ed1
; Output:
#88d1#1ed1
; - a: 1 (back face), 0 (front face).
#88d1#1ed1
; - carry flag set (same as "a"): back face.
#88d1#1ed1
L88d1_normal_direction_check:
#88d1#1ed1217
dd e5 
    push ix
#88d3#1ed3112
e5 
    push hl
#88d4#1ed4112
d5 
    push de
#88d5#1ed5112
c5 
    push bc
#88d6#1ed6422
dd 2a 24 5f 
        ld ix, (L5f24_shape_edges_ptr)
#88da#1eda212
dd 23 
        inc ix  ; skip the number of edges
#88dc#1edc217
dd e5 
        push ix
#88de#1ede321
fd 7e 00 
            ld a, (iy)
#88e1#1ee115
6f 
            ld l, a
#88e2#1ee228
e6 7f 
            and #7f  ; get the index (remove a potential flag in the msb)
#88e4#1ee415
4f 
            ld c, a
#88e5#1ee528
06 00 
            ld b, 0
#88e7#1ee7210
cb 21 
            sla c
#88e9#1ee9217
dd 09 
            add ix, bc  ; ix = ptr to the edge
#88eb#1eeb321
dd 4e 00 
            ld c, (ix)  ; c = vertex index 1
#88ee#1eee321
dd 7e 01 
            ld a, (ix + 1)  ; a = vertex index 2
#88f1#1ef1210
cb 7d 
            bit 7, l  ; vertex index flag check
#88f3#1ef3213/8
28 03 
            jr z, L88f8
#88f5#1ef5
            ; Invert the vertexes in the current edge:
#88f5#1ef515
61 
            ld h, c
#88f6#1ef615
4f 
            ld c, a
#88f7#1ef715
7c 
            ld a, h
#88f8#1ef8
L88f8:
#88f8#1ef8311
21 9f 5e 
            ld hl, L5e9f_3d_vertex_coordinates_after_rotation_matrix
#88fb#1efb210
cb 21 
            sla c
#88fd#1efd112
09 
            add hl, bc
#88fe#1efe210
cb 21 
            sla c
#8900#1f00112
09 
            add hl, bc  ; hl += 6 * vertex index 1
#8901#1f01311
11 63 5e 
            ld de, L5e63_3d_vertex_coordinates_relative_to_player
#8904#1f04
            ; Copy vertex 1:
#8904#1f0428
0e 06 
            ld c, 6
#8906#1f06223/18
ed b0 
            ldir
#8908#1f0815
4f 
            ld c, a
#8909#1f09311
21 9f 5e 
            ld hl, L5e9f_3d_vertex_coordinates_after_rotation_matrix
#890c#1f0c210
cb 21 
            sla c
#890e#1f0e112
09 
            add hl, bc
#890f#1f0f210
cb 21 
            sla c
#8911#1f11112
09 
            add hl, bc  ; hl += 6 * vertex index 2
#8912#1f12
            ; Copy vertex 2:
#8912#1f1228
0e 06 
            ld c, 6
#8914#1f14223/18
ed b0 
            ldir
#8916#1f16321
fd 7e 01 
            ld a, (iy + 1)
#8919#1f1915
6f 
            ld l, a
#891a#1f1a28
e6 7f 
            and #7f  ; get the index (remove a potential flag in the msb)
#891c#1f1c216
dd e1 
        pop ix
#891e#1f1e15
4f 
        ld c, a
#891f#1f1f210
cb 21 
        sla c
#8921#1f21217
dd 09 
        add ix, bc  ; ix = ptr to the edge
#8923#1f23321
dd 4e 00 
        ld c, (ix)
#8926#1f26210
cb 7d 
        bit 7, l
#8928#1f28213/8
20 03 
        jr nz, L892d
#892a#1f2a321
dd 4e 01 
        ld c, (ix + 1)  ; if edge flip flag is 1, get the other vertex.
#892d#1f2d
L892d:
#892d#1f2d311
21 9f 5e 
        ld hl, L5e9f_3d_vertex_coordinates_after_rotation_matrix
#8930#1f30210
cb 21 
        sla c
#8932#1f32112
09 
        add hl, bc
#8933#1f33210
cb 21 
        sla c
#8935#1f35112
09 
        add hl, bc  ; hl += 6 * vertex index 1
#8936#1f36
        ; Copy vertex 3:
#8936#1f3628
0e 06 
        ld c, 6
#8938#1f38223/18
ed b0 
        ldir
#893a#1f3a
#893a#1f3a
        ; At this point we have picked the first 3 vertices of the face.
#893a#1f3a
        ; We now calculate the normal vector:
#893a#1f3a
        ; OPTIMIZATION: faces should have the normal pre-calculated, rather than doing all this calculation each time.
#893a#1f3a
        ;   Just one additional point to be ran through the rotation matrix, and we avoid all of this.
#893a#1f3a15
60 
        ld h, b
#893b#1f3b15
69 
        ld l, c  ; hl = 0
#893c#1f3c317
22 75 5e 
        ld (L5e75_48_bit_accumulator), hl
#893f#1f3f317
22 77 5e 
        ld (L5e75_48_bit_accumulator + 2), hl
#8942#1f42317
22 79 5e 
        ld (L5e75_48_bit_accumulator + 4), hl
#8945#1f45317
2a 67 5e 
        ld hl, (L5e63_3d_vertex_coordinates_relative_to_player + 2 * 2)  ; z vertex 1
#8948#1f48422
ed 5b 6d 5e 
        ld de, (L5e63_3d_vertex_coordinates_relative_to_player + 5 * 2)  ; z vertex 2
#894c#1f4c15
b7 
        or a
#894d#1f4d217
ed 52 
        sbc hl, de  ; hl = z1 - z2
#894f#1f4f112
e5 
        push hl
#8950#1f50317
2a 71 5e 
            ld hl, (L5e63_3d_vertex_coordinates_relative_to_player + 7 * 2)  ; y vertex 3
#8953#1f53422
ed 5b 6b 5e 
            ld de, (L5e63_3d_vertex_coordinates_relative_to_player + 4 * 2)  ; y vertex 2
#8957#1f5715
b7 
            or a
#8958#1f58217
ed 52 
            sbc hl, de  ; hl = y3 - y2
#895a#1f5a111
d1 
        pop de
#895b#1f5b318
cd 5e a1 
        call La15e_de_times_hl_signed  ; (de, hl) = (z1 - z2) * (y3 - y2)
#895e#1f5e112
d5 
        push de
#895f#1f5f112
e5 
            push hl
#8960#1f60317
2a 65 5e 
                ld hl, (L5e63_3d_vertex_coordinates_relative_to_player + 1 * 2)  ; y vertex 1
#8963#1f63422
ed 5b 6b 5e 
                ld de, (L5e63_3d_vertex_coordinates_relative_to_player + 4 * 2)  ; y vertex 2
#8967#1f6715
b7 
                or a
#8968#1f68217
ed 52 
                sbc hl, de
#896a#1f6a112
e5 
                push hl
#896b#1f6b317
2a 73 5e 
                    ld hl, (L5e63_3d_vertex_coordinates_relative_to_player + 8 * 2)  ; z vertex 3
#896e#1f6e422
ed 5b 6d 5e 
                    ld de, (L5e63_3d_vertex_coordinates_relative_to_player + 5 * 2)  ; z vertex 2
#8972#1f7215
b7 
                    or a
#8973#1f73217
ed 52 
                    sbc hl, de
#8975#1f75111
d1 
                pop de
#8976#1f76318
cd 5e a1 
                call La15e_de_times_hl_signed
#8979#1f79111
c1 
            pop bc
#897a#1f7a15
af 
            xor a
#897b#1f7b217
ed 42 
            sbc hl, bc
#897d#1f7d111
c1 
        pop bc
#897e#1f7e15
eb 
        ex de, hl
#897f#1f7f217
ed 42 
            sbc hl, bc
#8981#1f8115
eb 
        ex de, hl
#8982#1f82422
ed 4b 69 5e 
        ld bc, (L5e63_3d_vertex_coordinates_relative_to_player + 3 * 2)  ; x vertex 2
#8986#1f86318
cd 30 8a 
        call L8a30_48bitmul_add
#8989#1f89317
2a 63 5e 
        ld hl, (L5e63_3d_vertex_coordinates_relative_to_player)  ; x vertex 1
#898c#1f8c422
ed 5b 69 5e 
        ld de, (L5e63_3d_vertex_coordinates_relative_to_player + 3 * 2)  ; x vertex 2
#8990#1f9015
b7 
        or a
#8991#1f91217
ed 52 
        sbc hl, de
#8993#1f93112
e5 
        push hl
#8994#1f94317
2a 73 5e 
            ld hl, (L5e63_3d_vertex_coordinates_relative_to_player + 8 * 2)  ; z vertex 3
#8997#1f97422
ed 5b 6d 5e 
            ld de, (L5e63_3d_vertex_coordinates_relative_to_player + 5 * 2)  ; z vertex 2
#899b#1f9b15
b7 
            or a
#899c#1f9c217
ed 52 
            sbc hl, de
#899e#1f9e111
d1 
        pop de
#899f#1f9f318
cd 5e a1 
        call La15e_de_times_hl_signed
#89a2#1fa2112
d5 
        push de
#89a3#1fa3112
e5 
            push hl
#89a4#1fa4317
2a 67 5e 
                ld hl, (L5e63_3d_vertex_coordinates_relative_to_player + 2 * 2)  ; z vertex 1
#89a7#1fa7422
ed 5b 6d 5e 
                ld de, (L5e63_3d_vertex_coordinates_relative_to_player + 5 * 2)  ; z vertex 2
#89ab#1fab15
b7 
                or a
#89ac#1fac217
ed 52 
                sbc hl, de
#89ae#1fae112
e5 
                push hl
#89af#1faf317
2a 6f 5e 
                    ld hl, (L5e63_3d_vertex_coordinates_relative_to_player + 6 * 2)  ; x vertex 3
#89b2#1fb2422
ed 5b 69 5e 
                    ld de, (L5e63_3d_vertex_coordinates_relative_to_player + 3 * 2)  ; x vertex 2
#89b6#1fb615
b7 
                    or a
#89b7#1fb7217
ed 52 
                    sbc hl, de
#89b9#1fb9111
d1 
                pop de
#89ba#1fba318
cd 5e a1 
                call La15e_de_times_hl_signed
#89bd#1fbd111
c1 
            pop bc
#89be#1fbe15
af 
            xor a
#89bf#1fbf217
ed 42 
            sbc hl, bc
#89c1#1fc1111
c1 
        pop bc
#89c2#1fc215
eb 
        ex de, hl
#89c3#1fc3217
ed 42 
        sbc hl, bc
#89c5#1fc515
eb 
        ex de, hl
#89c6#1fc6422
ed 4b 6b 5e 
        ld bc, (L5e63_3d_vertex_coordinates_relative_to_player + 4 * 2)
#89ca#1fca318
cd 30 8a 
        call L8a30_48bitmul_add
#89cd#1fcd317
2a 65 5e 
        ld hl, (L5e63_3d_vertex_coordinates_relative_to_player + 2)
#89d0#1fd0422
ed 5b 6b 5e 
        ld de, (L5e63_3d_vertex_coordinates_relative_to_player + 4 * 2)
#89d4#1fd415
b7 
        or a
#89d5#1fd5217
ed 52 
        sbc hl, de
#89d7#1fd7112
e5 
        push hl
#89d8#1fd8317
2a 6f 5e 
            ld hl, (L5e63_3d_vertex_coordinates_relative_to_player + 6 * 2)
#89db#1fdb422
ed 5b 69 5e 
            ld de, (L5e63_3d_vertex_coordinates_relative_to_player + 3 * 2)
#89df#1fdf15
b7 
            or a
#89e0#1fe0217
ed 52 
            sbc hl, de
#89e2#1fe2111
d1 
        pop de
#89e3#1fe3318
cd 5e a1 
        call La15e_de_times_hl_signed  ; (de, hl) = (z v1 - y v2) * (x v3 - x v2)
#89e6#1fe6112
d5 
        push de
#89e7#1fe7112
e5 
            push hl
#89e8#1fe8317
2a 63 5e 
                ld hl, (L5e63_3d_vertex_coordinates_relative_to_player)
#89eb#1feb422
ed 5b 69 5e 
                ld de, (L5e63_3d_vertex_coordinates_relative_to_player + 3 * 2)
#89ef#1fef15
b7 
                or a
#89f0#1ff0217
ed 52 
                sbc hl, de
#89f2#1ff2112
e5 
                push hl
#89f3#1ff3317
2a 71 5e 
                    ld hl, (L5e63_3d_vertex_coordinates_relative_to_player + 7 * 2)
#89f6#1ff6422
ed 5b 6b 5e 
                    ld de, (L5e63_3d_vertex_coordinates_relative_to_player + 4 * 2)
#89fa#1ffa15
b7 
                    or a
#89fb#1ffb217
ed 52 
                    sbc hl, de
#89fd#1ffd111
d1 
                pop de
#89fe#1ffe318
cd 5e a1 
                call La15e_de_times_hl_signed  ; (de, hl) = (x v1 - x v2) * (y v3 - y v2)
#8a01#2001111
c1 
            pop bc
#8a02#200215
af 
            xor a
#8a03#2003217
ed 42 
            sbc hl, bc
#8a05#2005111
c1 
        pop bc
#8a06#200615
eb 
        ex de, hl
#8a07#2007217
ed 42 
        sbc hl, bc  ; hl = (z v1 - y v2) * (x v3 - x v2) - (x v1 - x v2) * (y v3 - y v2)
#8a09#2009112
e5 
        push hl
#8a0a#200a15
eb 
            ex de, hl
#8a0b#200b422
ed 4b 6d 5e 
            ld bc, (L5e63_3d_vertex_coordinates_relative_to_player + 5 * 2)
#8a0f#200f318
cd 30 8a 
            call L8a30_48bitmul_add
#8a12#2012111
e1 
        pop hl
#8a13#2013314
3a 7a 5e 
        ld a, (L5e75_48_bit_accumulator + 5)
#8a16#2016
#8a16#2016
        ; Check if the normal points forward or backwards:
#8a16#2016
        ; At this point:
#8a16#2016
        ; - a: has the most significant byte of the accumulator (to get the sign of the z coordinate of the normal).
#8a16#2016
        ; - hl: most significant word of "(x v1 - x v2) * (y v3 - y v2) - (z v1 - y v2) * (x v3 - x v2)"
#8a16#201615
6f 
        ld l, a
#8a17#201715
ac 
        xor h
#8a18#2018311
f2 1f 8a 
        jp p, L8a1f
#8a1b#201b
        ; We get here if the "normal z" has the same sign as "(x v1 - x v2) * (y v3 - y v2) - (z v1 - y v2) * (x v3 - x v2)".
#8a1b#201b
        ; This gives the face a chance to be drawn if when projected to the screen no vertices fall inside of the view area,
#8a1b#201b
        ; and the code will check if it's that the face is too big and covers the whole screen.
#8a1b#201b
        ; Note: I am not sure of what the math means here, as I have not tried to derive what the value of the calculation
#8a1b#201b
        ;       means.
#8a1b#201b15
af 
        xor a
#8a1c#201c314
32 28 5f 
        ld (L5f28_cull_face_when_no_projected_vertices), a
#8a1f#201f
L8a1f:
#8a1f#201f15
7d 
        ld a, l
#8a20#202015
b7 
        or a
#8a21#2021311
f2 27 8a 
        jp p, L8a27
#8a24#2024
        ; Normal points towrads the camera, this is a front face.
#8a24#202415
af 
        xor a
#8a25#2025213
18 03 
        jr L8a2a
#8a27#2027
L8a27:
#8a27#2027
        ; Normal points away from camera, this is a back face.
#8a27#202728
3e 01 
        ld a, 1
#8a29#202915
37 
        scf
#8a2a#202a
L8a2a:
#8a2a#202a111
c1 
    pop bc
#8a2b#202b111
d1 
    pop de
#8a2c#202c111
e1 
    pop hl
#8a2d#202d216
dd e1 
    pop ix
#8a2f#202f111
c9 
    ret
#8a30#2030
#8a30#2030
#8a30#2030
; --------------------------------
#8a30#2030
; Performs the following operation:
#8a30#2030
; - First, calculate the multiplication bc * (bc, hl)
#8a30#2030
; - Then add the 48 bit result to the 48 bit number in (L5e75_48_bit_accumulator)
#8a30#2030
; - Result is saved in (L5e75_48_bit_accumulator)
#8a30#2030
; The signed 32 bit result is returned in (DE,HL)
#8a30#2030
; Input:
#8a30#2030
; - bc
#8a30#2030
; - (de, hl)
#8a30#2030
; - 6 bytes in (L5e75_48_bit_accumulator)
#8a30#2030
; Output:
#8a30#2030
; - 6 bytes in (L5e75_48_bit_accumulator)
#8a30#2030
L8a30_48bitmul_add:
#8a30#2030210
cb 7a 
    bit 7, d
#8a32#2032213/8
28 14 
    jr z, L8a48_de_hl_positive
#8a34#2034
    ; if (de, hl) is negative, calculate the absolute value:
#8a34#203415
7c 
    ld a, h
#8a35#203515
2f 
    cpl
#8a36#203615
67 
    ld h, a
#8a37#203715
7d 
    ld a, l
#8a38#203815
2f 
    cpl
#8a39#203915
6f 
    ld l, a
#8a3a#203a15
7a 
    ld a, d
#8a3b#203b15
2f 
    cpl
#8a3c#203c15
57 
    ld d, a
#8a3d#203d15
7b 
    ld a, e
#8a3e#203e15
2f 
    cpl
#8a3f#203f15
5f 
    ld e, a
#8a40#2040
#8a40#204017
23 
    inc hl
#8a41#204115
7d 
    ld a, l
#8a42#204215
b4 
    or h
#8a43#204328
3e 01 
    ld a, 1  ; mark that we changed the sign
#8a45#2045213/8
20 01 
    jr nz, L8a48_de_hl_positive
#8a47#204717
13 
    inc de
#8a48#2048
L8a48_de_hl_positive:
#8a48#2048210
cb 78 
    bit 7, b
#8a4a#204a213/8
28 0b 
    jr z, L8a57_bc_positive
#8a4c#204c
    ; if bc is negative, calculate the absolute value:
#8a4c#204c112
f5 
    push af
#8a4d#204d15
78 
        ld a, b
#8a4e#204e15
2f 
        cpl
#8a4f#204f15
47 
        ld b, a
#8a50#205015
79 
        ld a, c
#8a51#205115
2f 
        cpl
#8a52#205215
4f 
        ld c, a
#8a53#205317
03 
        inc bc
#8a54#2054111
f1 
    pop af
#8a55#205528
ee 01 
    xor 1  ; mark that we changed the sign (if we changed the sign of only one of bc, or (de, hl), a = 1).
#8a57#2057
L8a57_bc_positive:
#8a57#2057
    ; Perform a multiplciation in the following way:
#8a57#2057
    ;    de hl
#8a57#2057
    ;  *    bc
#8a57#2057
    ;  -------
#8a57#2057
    ;    AA BB <- bc * hl
#8a57#2057
    ; CC DD
#8a57#2057
    ; --------
#8a57#2057
    ; hl bc de
#8a57#2057112
d5 
    push de
#8a58#205815
59 
        ld e, c
#8a59#205915
50 
        ld d, b
#8a5a#205a318
cd b4 8a 
        call L8ab4_de_times_hl_signed  ; (AA, BB) = hl * bc
#8a5d#205d
        ; We temporarily save the result in RAM:
#8a5d#205d317
22 7b 5e 
        ld (L5e7b_48bitmul_tmp1), hl  ; save BB
#8a60#2060422
ed 53 7d 5e 
        ld (L5e7d_48bitmul_tmp2), de  ; save AA
#8a64#2064111
d1 
    pop de
#8a65#206515
69 
    ld l, c
#8a66#206615
60 
    ld h, b
#8a67#2067318
cd b4 8a 
    call L8ab4_de_times_hl_signed  ; (CC, DD) = de * bc
#8a6a#206a422
ed 4b 7d 5e 
    ld bc, (L5e7d_48bitmul_tmp2)  ; recover AA
#8a6e#206e112
09 
    add hl, bc  ; AA + DD
#8a6f#206f15
44 
    ld b, h
#8a70#207015
4d 
    ld c, l  ; bc = AA + DD
#8a71#2071311
21 00 00 
    ld hl, 0
#8a74#2074217
ed 5a 
    adc hl, de  ; hl = CC + (carry of AA + DD)
#8a76#2076422
ed 5b 7b 5e 
    ld de, (L5e7b_48bitmul_tmp1)  ; recover BB
#8a7a#207a
    ; At this point (hl, bc, de) has the absolute value of the 48 bit result of the multiplication
#8a7a#207a
    ; Check if we need to change the sign of the result:
#8a7a#207a15
b7 
    or a
#8a7b#207b213/8
28 1d 
    jr z, L8a9a_result_is_positive
#8a7d#207d
    ; Make the result negative:
#8a7d#207d15
7c 
    ld a, h
#8a7e#207e15
2f 
    cpl
#8a7f#207f15
67 
    ld h, a
#8a80#208015
7d 
    ld a, l
#8a81#208115
2f 
    cpl
#8a82#208215
6f 
    ld l, a
#8a83#208315
78 
    ld a, b
#8a84#208415
2f 
    cpl
#8a85#208515
47 
    ld b, a
#8a86#208615
79 
    ld a, c
#8a87#208715
2f 
    cpl
#8a88#208815
4f 
    ld c, a
#8a89#208915
7a 
    ld a, d
#8a8a#208a15
2f 
    cpl
#8a8b#208b15
57 
    ld d, a
#8a8c#208c15
7b 
    ld a, e
#8a8d#208d15
2f 
    cpl
#8a8e#208e15
5f 
    ld e, a
#8a8f#208f17
13 
    inc de
#8a90#209015
7b 
    ld a, e
#8a91#209115
b2 
    or d
#8a92#2092213/8
20 06 
    jr nz, L8a9a_result_is_positive
#8a94#209417
03 
    inc bc
#8a95#209515
79 
    ld a, c
#8a96#209615
b0 
    or b
#8a97#2097213/8
20 01 
    jr nz, L8a9a_result_is_positive
#8a99#209917
23 
    inc hl
#8a9a#209a
L8a9a_result_is_positive:
#8a9a#209a
    ; At this point (hl, bc, de) has the 48 bit result of the multiplication
#8a9a#209a
    ; Add this 48 bit number with the 48 bit number stored in (L5e75_48_bit_accumulator)
#8a9a#209a112
e5 
    push hl
#8a9b#209b317
2a 75 5e 
        ld hl, (L5e75_48_bit_accumulator)
#8a9e#209e112
19 
        add hl, de
#8a9f#209f317
22 75 5e 
        ld (L5e75_48_bit_accumulator), hl
#8aa2#20a2317
2a 77 5e 
        ld hl, (L5e75_48_bit_accumulator + 2)
#8aa5#20a5217
ed 4a 
        adc hl, bc
#8aa7#20a7317
22 77 5e 
        ld (L5e75_48_bit_accumulator + 2), hl
#8aaa#20aa317
2a 79 5e 
        ld hl, (L5e75_48_bit_accumulator + 4)
#8aad#20ad111
c1 
    pop bc
#8aae#20ae217
ed 4a 
    adc hl, bc
#8ab0#20b0317
22 79 5e 
    ld (L5e75_48_bit_accumulator + 4), hl
#8ab3#20b3111
c9 
    ret
#8ab4#20b4
#8ab4#20b4
#8ab4#20b4
; --------------------------------
#8ab4#20b4
; Signed multiplication between DE and HL.
#8ab4#20b4
; The signed 32 bit result is returned in (DE,HL)
#8ab4#20b4
; Input:
#8ab4#20b4
; - de
#8ab4#20b4
; - hl
#8ab4#20b4
; Output:
#8ab4#20b4
; - de, hl
#8ab4#20b4
L8ab4_de_times_hl_signed:
#8ab4#20b4112
c5 
    push bc
#8ab5#20b5112
f5 
    push af
#8ab6#20b615
7c 
        ld a, h
#8ab7#20b715
4d 
        ld c, l
#8ab8#20b828
06 10 
        ld b, 16
#8aba#20ba311
21 00 00 
        ld hl, 0
#8abd#20bd
L8abd:
#8abd#20bd210
cb 21 
        sla c
#8abf#20bf15
17 
        rla
#8ac0#20c0213/8
38 0c 
        jr c, L8ace
#8ac2#20c2214/9
10 f9 
        djnz L8abd
#8ac4#20c415
50 
        ld d, b
#8ac5#20c515
58 
        ld e, b
#8ac6#20c6213
18 11 
        jr L8ad9
#8ac8#20c8
L8ac8:
#8ac8#20c8112
29 
        add hl, hl
#8ac9#20c9210
cb 11 
        rl c
#8acb#20cb15
17 
        rla
#8acc#20cc213/8
30 07 
        jr nc, L8ad5
#8ace#20ce
L8ace:
#8ace#20ce112
19 
        add hl, de
#8acf#20cf213/8
30 04 
        jr nc, L8ad5
#8ad1#20d115
0c 
        inc c
#8ad2#20d2213/8
20 01 
        jr nz, L8ad5
#8ad4#20d415
3c 
        inc a
#8ad5#20d5
L8ad5:
#8ad5#20d5214/9
10 f1 
        djnz L8ac8
#8ad7#20d715
57 
        ld d, a
#8ad8#20d815
59 
        ld e, c
#8ad9#20d9
L8ad9:
#8ad9#20d9111
f1 
    pop af
#8ada#20da111
c1 
    pop bc
#8adb#20db111
c9 
    ret
#8adc#20dc
#8adc#20dc
#8adc#20dc
; --------------------------------
#8adc#20dc
; Projects an object, and if it falls within the screen, add it to the list of objects to draw,
#8adc#20dc
; assuming that all vertexes are in screen (if a single one is out, whole object is discarted)
#8adc#20dc
; Input:
#8adc#20dc
; - iy: face definition ptr:
#8adc#20dc
;   - first byte is number of faces
#8adc#20dc
;   - then, each face has:
#8adc#20dc
;     - attribute
#8adc#20dc
;     - number of vertices
#8adc#20dc
;     - then one byte per vertex (index)
#8adc#20dc
L8adc_project_object_and_add_to_render_list_internal:
#8adc#20dc314
3a 96 74 
    ld a, (L7496_current_drawing_primitive_n_vertices)
#8adf#20df15
47 
    ld b, a
#8ae0#20e0
    ; Initialize the L5ee8_already_projected_vertex_coordinates array:
#8ae0#20e0
    ; Since different edges might share vertexes, when we project a vertex,
#8ae0#20e0
    ; we mark it in this array, to prevent projecting them again.
#8ae0#20e0311
21 e8 5e 
    ld hl, L5ee8_already_projected_vertex_coordinates
#8ae3#20e328
3e ff 
    ld a, #ff  ; mark that a vertex has not been projected.
#8ae5#20e5
L8ae5:
#8ae5#20e518
77 
    ld (hl), a
#8ae6#20e617
23 
    inc hl
#8ae7#20e717
23 
    inc hl
#8ae8#20e8214/9
10 fb 
    djnz L8ae5
#8aea#20ea
#8aea#20ea15
af 
    xor a
#8aeb#20eb314
32 5f 5e 
    ld (L5e5f_add_to_projected_objects_flag), a
#8aee#20ee317
2a 97 74 
    ld hl, (L7497_next_projected_vertex_ptr)
#8af1#20f1314
3a 68 74 
    ld a, (L7468_focus_object_id)
#8af4#20f4
    ; Start writing the projected vertex data:
#8af4#20f418
77 
    ld (hl), a  ; object ID
#8af5#20f517
23 
    inc hl
#8af6#20f6211
36 00 
    ld (hl), 0  ; number of primitives (init to zero, and will be incremented each time a face is added).
#8af8#20f817
23 
    inc hl
#8af9#20f9321
fd 46 00 
    ld b, (iy)  ; number of faces
#8afc#20fc212
fd 23 
    inc iy
#8afe#20fe
L8afe_face_loop:
#8afe#20fe112
c5 
    push bc
#8aff#20ff321
fd 7e 00 
        ld a, (iy)  ; a = texture ID.
#8b02#2102212
fd 23 
        inc iy
#8b04#2104321
fd 46 00 
        ld b, (iy)  ; b = number of vertices in the face.
#8b07#2107212
fd 23 
        inc iy
#8b09#2109
        ; If it's a transparent face, ignore:
#8b09#210915
b7 
        or a
#8b0a#210a213/8
28 18 
        jr z, L8b24_skip_face_bytes_and_next_face
#8b0c#210c210
cb 27 
        sla a
#8b0e#210e210
cb 27 
        sla a
#8b10#2110210
cb 27 
        sla a
#8b12#2112210
cb 27 
        sla a
#8b14#211415
4f 
        ld c, a
#8b15#2115314
3a 60 5e 
        ld a, (L5e60_projection_pre_work_type)
#8b18#2118
        ;   - if a == 0: indicates that vertices can be projected directly.
#8b18#2118
        ;   - if a == 1: we need to call L88d1_normal_direction_check before projection, and if back-face, we cull
#8b18#2118
        ;   - if a == 2: we need to call L88d1_normal_direction_check before projection, and if back-face we need to use L5f26_alternative_shape_edges_ptr
#8b18#211815
b7 
        or a
#8b19#2119213/8
28 35 
        jr z, L8b50_ready_to_project
#8b1b#211b28
fe 01 
        cp 1
#8b1d#211d213/8
20 0d 
        jr nz, L8b2c
#8b1f#211f
#8b1f#211f
        ; Normal check, and cull if failed:
#8b1f#211f318
cd d1 88 
        call L88d1_normal_direction_check
#8b22#2122213/8
30 2c 
        jr nc, L8b50_ready_to_project
#8b24#2124
        ; back face!
#8b24#2124
L8b24_skip_face_bytes_and_next_face:
#8b24#212415
48 
        ld c, b
#8b25#212528
06 00 
        ld b, 0
#8b27#2127217
fd 09 
        add iy, bc
#8b29#2129311
c3 b1 8b 
        jp L8bb1_next_face
#8b2c#212c
#8b2c#212c
L8b2c:
#8b2c#212c318
cd d1 88 
        call L88d1_normal_direction_check
#8b2f#212f314
3a 6a 74 
        ld a, (L746a_current_drawing_texture_id)
#8b32#2132213/8
30 0f 
        jr nc, L8b43
#8b34#2134
        ; back face, we need to swap texture ID, and use alternative shape edges ptr:
#8b34#213428
e6 f0 
        and #f0
#8b36#2136213/8
28 ec 
        jr z, L8b24_skip_face_bytes_and_next_face
#8b38#213815
4f 
        ld c, a
#8b39#2139422
ed 5b 26 5f 
        ld de, (L5f26_alternative_shape_edges_ptr)
#8b3d#213d422
ed 53 24 5f 
        ld (L5f24_shape_edges_ptr), de
#8b41#2141213
18 0d 
        jr L8b50_ready_to_project
#8b43#2143
#8b43#2143
L8b43:
#8b43#214328
e6 0f 
        and #0f
#8b45#2145213/8
28 dd 
        jr z, L8b24_skip_face_bytes_and_next_face
#8b47#2147210
cb 27 
        sla a
#8b49#2149210
cb 27 
        sla a
#8b4b#214b210
cb 27 
        sla a
#8b4d#214d210
cb 27 
        sla a
#8b4f#214f15
4f 
        ld c, a
#8b50#2150
L8b50_ready_to_project:
#8b50#2150
        ; At this point:
#8b50#2150
        ; - b: number of vertices
#8b50#2150
        ; - c: texture ID (in the most significant 4 bits)
#8b50#2150
        ; - hl: pointer to the resulting projected vertex data (about to write texture byte)
#8b50#2150
        ; - iy: ptr to the face vertex indexes
#8b50#215015
79 
        ld a, c
#8b51#215115
b0 
        or b
#8b52#215218
77 
        ld (hl), a  ; save # vertices and texture ID
#8b53#215317
23 
        inc hl
#8b54#2154
L8b54_vertex_loop:
#8b54#2154112
c5 
        push bc
#8b55#2155321
fd 7e 00 
            ld a, (iy)  ; edge index
#8b58#215828
e6 7f 
            and #7f  ; get the index (remove a potential flag in the msb)
#8b5a#215a210
cb 27 
            sla a
#8b5c#215c15
4f 
            ld c, a
#8b5d#215d28
06 00 
            ld b, 0
#8b5f#215f422
dd 2a 24 5f 
            ld ix, (L5f24_shape_edges_ptr)
#8b63#2163212
dd 23 
            inc ix  ; skip the number of edges
#8b65#2165217
dd 09 
            add ix, bc  ; ix = ptr to the edge
#8b67#2167422
fd cb 00 7e 
            bit 7, (iy)
#8b6b#216b213/8
28 02 
            jr z, L8b6f
#8b6d#216d212
dd 23 
            inc ix  ; If the msb flag is set, we invert the vertex in the edge
#8b6f#216f
L8b6f:
#8b6f#216f212
fd 23 
            inc iy  ; next index
#8b71#2171321
dd 4e 00 
            ld c, (ix)  ; get the vertex index
#8b74#2174210
cb 21 
            sla c  ; vertex index * 2
#8b76#2176416
dd 21 e8 5e 
            ld ix, L5ee8_already_projected_vertex_coordinates
#8b7a#217a217
dd 09 
            add ix, bc
#8b7c#217c321
dd 7e 00 
            ld a, (ix)
#8b7f#217f
            ; Check if we have already projected the vertex:
#8b7f#217f28
fe ff 
            cp #ff
#8b81#2181213/8
20 18 
            jr nz, L8b9b_already_projected
#8b83#2183
            ; We have not projected it yet, so, we project it now:
#8b83#2183217
dd e5 
            push ix
#8b85#2185416
dd 21 9f 5e 
                ld ix, L5e9f_3d_vertex_coordinates_after_rotation_matrix
#8b89#2189217
dd 09 
                add ix, bc
#8b8b#218b217
dd 09 
                add ix, bc
#8b8d#218d217
dd 09 
                add ix, bc
#8b8f#218f318
cd fc 90 
                call L90fc_project_one_vertex
#8b92#2192216
dd e1 
            pop ix
#8b94#2194
            ; Save the projected coordinates (c, b) to the temporary L5ee8_already_projected_vertex_coordinates array.
#8b94#2194321
dd 71 00 
            ld (ix), c
#8b97#2197321
dd 70 01 
            ld (ix + 1), b
#8b9a#219a15
79 
            ld a, c  ; a = projected x
#8b9b#219b
L8b9b_already_projected:
#8b9b#219b
            ; Write the projected coordiantes to the projected vertices data for rendering:
#8b9b#219b18
77 
            ld (hl), a  ; x
#8b9c#219c17
23 
            inc hl
#8b9d#219d321
dd 7e 01 
            ld a, (ix + 1)  ; y
#8ba0#21a018
77 
            ld (hl), a
#8ba1#21a117
23 
            inc hl
#8ba2#21a2111
c1 
        pop bc
#8ba3#21a3214/9
10 af 
        djnz L8b54_vertex_loop
#8ba5#21a5
#8ba5#21a528
3e 01 
        ld a, 1
#8ba7#21a7314
32 5f 5e 
        ld (L5e5f_add_to_projected_objects_flag), a
#8baa#21aa422
dd 2a 97 74 
        ld ix, (L7497_next_projected_vertex_ptr)
#8bae#21ae325
dd 34 01 
        inc (ix + 1)  ; increment the number of primitives counter
#8bb1#21b1
L8bb1_next_face:
#8bb1#21b1111
c1 
    pop bc
#8bb2#21b215
05 
    dec b
#8bb3#21b3311
c2 fe 8a 
    jp nz, L8afe_face_loop
#8bb6#21b6111
c9 
    ret
#8bb7#21b7
#8bb7#21b7
#8bb7#21b7
; --------------------------------
#8bb7#21b7
; Sets the rendering volume, a cube (to prune which obejcts to render).
#8bb7#21b7
L8bb7_determine_rendering_volume:
#8bb7#21b7314
3a bd 6a 
    ld a, (L6abd_cull_by_rendering_volume_flag)  ; if we are not culling by volume, just return
#8bba#21ba15
b7 
    or a
#8bbb#21bb112/6
c0 
    ret nz
#8bbc#21bc
    ; Clear the L745d_rendering_cube_volume to #ff:
#8bbc#21bc311
21 5d 74 
    ld hl, L745d_rendering_cube_volume
#8bbf#21bf28
06 06 
    ld b, 6
#8bc1#21c115
3d 
    dec a
#8bc2#21c2
L8bc2_clear_memory_loop:
#8bc2#21c218
77 
    ld (hl), a  ; a == #ff here
#8bc3#21c317
23 
    inc hl
#8bc4#21c4214/9
10 fc 
    djnz L8bc2_clear_memory_loop
#8bc6#21c6
#8bc6#21c6
    ; This loop is executed 4 times, each time adjusting the
#8bc6#21c6
    ; pitch/yaw andles up or down by 8 units, and each time,
#8bc6#21c6
    ; the values in L745d_rendering_cube_volume are being set: 
#8bc6#21c628
26 04 
    ld h, 4
#8bc8#21c8416
dd 21 5d 74 
    ld ix, L745d_rendering_cube_volume
#8bcc#21cc
L8bcc_angle_loop:
#8bcc#21cc15
7c 
    ld a, h  ; h is the iteration index (4, 3, 2, 1)
#8bcd#21cd28
fe 03 
    cp 3
#8bcf#21cf314
3a b7 6a 
    ld a, (L6ab7_player_yaw_angle)
#8bd2#21d2213/8
30 0a 
    jr nc, L8bde
#8bd4#21d4
    ; Iterations 1, 2:
#8bd4#21d4
    ; If we are too close to the upper limit, wrap around
#8bd4#21d428
c6 08 
    add a, 8
#8bd6#21d628
fe 48 
    cp FULL_ROTATION_DEGREES
#8bd8#21d8213/8
38 0a 
    jr c, L8be4_yaw_set
#8bda#21da28
d6 48 
    sub FULL_ROTATION_DEGREES
#8bdc#21dc213
18 06 
    jr L8be4_yaw_set
#8bde#21de
L8bde:
#8bde#21de
    ; Iterations 3, 4:
#8bde#21de
    ; If we are too close to the lower limit, wrap around
#8bde#21de28
d6 08 
    sub 8
#8be0#21e0213/8
30 02 
    jr nc, L8be4_yaw_set
#8be2#21e228
c6 48 
    add a, FULL_ROTATION_DEGREES
#8be4#21e4
L8be4_yaw_set:
#8be4#21e4
    ; Here we have a = (L6ab7_player_yaw_angle)
#8be4#21e415
47 
    ld b, a
#8be5#21e5210
cb 44 
    bit 0, h
#8be7#21e7314
3a b6 6a 
    ld a, (L6ab6_player_pitch_angle)
#8bea#21ea213/8
28 0a 
    jr z, L8bf6
#8bec#21ec
    ; Iterations 1 and 3:
#8bec#21ec28
c6 08 
    add a, 8
#8bee#21ee28
fe 48 
    cp FULL_ROTATION_DEGREES
#8bf0#21f0213/8
38 0a 
    jr c, L8bfc_pitch_set
#8bf2#21f228
d6 48 
    sub FULL_ROTATION_DEGREES
#8bf4#21f4213
18 06 
    jr L8bfc_pitch_set
#8bf6#21f6
L8bf6:
#8bf6#21f6
    ; Iterations 2 and 4:
#8bf6#21f628
d6 08 
    sub 8
#8bf8#21f8213/8
30 02 
    jr nc, L8bfc_pitch_set
#8bfa#21fa28
c6 48 
    add a, FULL_ROTATION_DEGREES
#8bfc#21fc
L8bfc_pitch_set:
#8bfc#21fc15
4f 
    ld c, a
#8bfd#21fd
    ; Here: b = yaw, c = pitch.
#8bfd#21fd15
78 
    ld a, b
#8bfe#21fe28
fe 12 
    cp FULL_ROTATION_DEGREES / 4
#8c00#2200213/8
30 04 
    jr nc, L8c06
#8c02#220228
16 01 
    ld d, 1
#8c04#2204213
18 12 
    jr L8c18
#8c06#2206
L8c06:
#8c06#220628
fe 24 
    cp FULL_ROTATION_DEGREES / 2
#8c08#2208213/8
30 04 
    jr nc, L8c0e
#8c0a#220a28
16 03 
    ld d, 3
#8c0c#220c213
18 0a 
    jr L8c18
#8c0e#220e
L8c0e:
#8c0e#220e28
fe 36 
    cp 3 * FULL_ROTATION_DEGREES / 4
#8c10#2210213/8
30 04 
    jr nc, L8c16
#8c12#221228
16 05 
    ld d, 5
#8c14#2214213
18 02 
    jr L8c18
#8c16#2216
L8c16:
#8c16#221628
16 07 
    ld d, 7
#8c18#2218
L8c18:
#8c18#221815
79 
    ld a, c
#8c19#221928
fe 12 
    cp FULL_ROTATION_DEGREES / 4
#8c1b#221b213/8
30 03 
    jr nc, L8c20
#8c1d#221d15
14 
    inc d
#8c1e#221e213
18 16 
    jr L8c36
#8c20#2220
L8c20:
#8c20#222028
fe 24 
    cp FULL_ROTATION_DEGREES / 2
#8c22#2222213/8
30 04 
    jr nc, L8c28
#8c24#222428
3e 05 
    ld a, 5
#8c26#2226213
18 06 
    jr L8c2e
#8c28#2228
L8c28:
#8c28#222828
fe 36 
    cp 3 * FULL_ROTATION_DEGREES / 4
#8c2a#222a213/8
30 0a 
    jr nc, L8c36
#8c2c#222c28
3e 04 
    ld a, 4
#8c2e#222e
L8c2e:
#8c2e#222e15
82 
    add a, d
#8c2f#222f28
fe 09 
    cp 9
#8c31#2231213/8
38 02 
    jr c, L8c35
#8c33#223328
d6 08 
    sub 8
#8c35#2235
L8c35:
#8c35#223515
57 
    ld d, a
#8c36#2236
L8c36:
#8c36#2236
    ; Here:
#8c36#2236
    ; - d has some number based on the quadrants of pitch/yaw
#8c36#2236
    ; - these are used to set the limits:
#8c36#2236
    ;   - in the x/z axis, the maximum limits are [0, 127]
#8c36#2236
    ;   - in the y axis it is [0, 63]
#8c36#2236
    ; - all limits that are not set will be replaced by player coordinates.
#8c36#223615
15 
    dec d
#8c37#2237213/8
20 06 
    jr nz, L8c3f
#8c39#2239
    ; d == 1: yaw 1st quadrant, pitch 4th quadrant:
#8c39#2239421
dd 36 00 7f 
    ld (ix), 127
#8c3d#223d213
18 44 
    jr L8c83
#8c3f#223f
L8c3f:
#8c3f#223f15
15 
    dec d
#8c40#2240213/8
20 06 
    jr nz, L8c48
#8c42#2242
    ; d == 2: yaw 1st quadrant, pitch 1st quadrant:
#8c42#2242421
dd 36 00 7f 
    ld (ix), 127
#8c46#2246213
18 45 
    jr L8c8d
#8c48#2248
L8c48:
#8c48#224815
15 
    dec d
#8c49#2249213/8
20 0a 
    jr nz, L8c55
#8c4b#224b
    ; d == 3: yaw 2nd quadrant, pitch 4th quadrant:
#8c4b#224b421
dd 36 00 7f 
    ld (ix), 127
#8c4f#224f421
dd 36 02 3f 
    ld (ix + 2), 63
#8c53#2253213
18 21 
    jr L8c76
#8c55#2255
L8c55:
#8c55#225515
15 
    dec d
#8c56#2256213/8
20 06 
    jr nz, L8c5e
#8c58#2258
    ; d == 4: yaw 2nd quadrant, pitch 1st quadrant:
#8c58#2258421
dd 36 00 7f 
    ld (ix), 127
#8c5c#225c213
18 14 
    jr L8c72
#8c5e#225e
L8c5e:
#8c5e#225e15
15 
    dec d
#8c5f#225f213/8
20 0a 
    jr nz, L8c6b
#8c61#2261
    ; d == 5: yaw 1st quadrant, pitch 4th quadrant, or
#8c61#2261
    ;         yaw 2nd quadrant, pitch 1st quadrant
#8c61#2261421
dd 36 01 00 
    ld (ix + 1), 0
#8c65#2265421
dd 36 02 3f 
    ld (ix + 2), 63
#8c69#2269213
18 0b 
    jr L8c76
#8c6b#226b
L8c6b:
#8c6b#226b15
15 
    dec d
#8c6c#226c213/8
20 0e 
    jr nz, L8c7c
#8c6e#226e
    ; d == 6: ...
#8c6e#226e421
dd 36 01 00 
    ld (ix + 1), 0
#8c72#2272
L8c72:
#8c72#2272421
dd 36 03 00 
    ld (ix + 3), 0
#8c76#2276
L8c76:
#8c76#2276421
dd 36 05 00 
    ld (ix + 5), 0
#8c7a#227a213
18 19 
    jr L8c95
#8c7c#227c
L8c7c:
#8c7c#227c15
15 
    dec d
#8c7d#227d213/8
20 0a 
    jr nz, L8c89
#8c7f#227f
    ; d == 7: ...
#8c7f#227f421
dd 36 01 00 
    ld (ix + 1), 0
#8c83#2283
L8c83:
#8c83#2283421
dd 36 02 3f 
    ld (ix + 2), 63
#8c87#2287213
18 08 
    jr L8c91
#8c89#2289
L8c89:
#8c89#2289
    ; d == 8: ...
#8c89#2289421
dd 36 01 00 
    ld (ix + 1), 0
#8c8d#228d
L8c8d:
#8c8d#228d421
dd 36 03 00 
    ld (ix + 3), 0
#8c91#2291
L8c91:
#8c91#2291421
dd 36 04 7f 
    ld (ix + 4), 127
#8c95#2295
L8c95:
#8c95#229515
25 
    dec h
#8c96#2296311
c2 cc 8b 
    jp nz, L8bcc_angle_loop
#8c99#2299
#8c99#2299
    ; Replace any of the coordinates we have not set above,
#8c99#2299
    ; with the player x, y, or z coordinates:
#8c99#2299
    ; These correspond to areas that are "behind" the player, and
#8c99#2299
    ; hence we use the player coordinates to prune.
#8c99#2299317
2a ad 6a 
    ld hl, (L6aad_player_current_x)
#8c9c#229c112
29 
    add hl, hl
#8c9d#229d112
29 
    add hl, hl
#8c9e#229e28
3e ff 
    ld a, 255
#8ca0#22a0321
dd be 00 
    cp (ix)
#8ca3#22a3213/8
20 06 
    jr nz, L8cab
#8ca5#22a515
24 
    inc h
#8ca6#22a6321
dd 74 00 
    ld (ix), h
#8ca9#22a9213
18 08 
    jr L8cb3
#8cab#22ab
L8cab:
#8cab#22ab321
dd be 01 
    cp (ix + 1)
#8cae#22ae213/8
20 03 
    jr nz, L8cb3
#8cb0#22b0321
dd 74 01 
    ld (ix + 1), h
#8cb3#22b3
L8cb3:
#8cb3#22b3317
2a af 6a 
    ld hl, (L6aaf_player_current_y)
#8cb6#22b6112
29 
    add hl, hl
#8cb7#22b7112
29 
    add hl, hl
#8cb8#22b8321
dd be 02 
    cp (ix + 2)
#8cbb#22bb213/8
20 06 
    jr nz, L8cc3
#8cbd#22bd15
24 
    inc h
#8cbe#22be321
dd 74 02 
    ld (ix + 2), h
#8cc1#22c1213
18 08 
    jr L8ccb
#8cc3#22c3
L8cc3:
#8cc3#22c3321
dd be 03 
    cp (ix + 3)
#8cc6#22c6213/8
20 03 
    jr nz, L8ccb
#8cc8#22c8321
dd 74 03 
    ld (ix + 3), h
#8ccb#22cb
L8ccb:
#8ccb#22cb317
2a b1 6a 
    ld hl, (L6ab1_player_current_z)
#8cce#22ce112
29 
    add hl, hl
#8ccf#22cf112
29 
    add hl, hl
#8cd0#22d0321
dd be 04 
    cp (ix + 4)
#8cd3#22d3213/8
20 06 
    jr nz, L8cdb
#8cd5#22d515
24 
    inc h
#8cd6#22d6321
dd 74 04 
    ld (ix + 4), h
#8cd9#22d9213
18 08 
    jr L8ce3
#8cdb#22db
L8cdb:
#8cdb#22db321
dd be 05 
    cp (ix + 5)
#8cde#22de213/8
20 03 
    jr nz, L8ce3
#8ce0#22e0321
dd 74 05 
    ld (ix + 5), h
#8ce3#22e3
L8ce3:
#8ce3#22e3111
c9 
    ret
#8ce4#22e4
#8ce4#22e4
#8ce4#22e4
; --------------------------------
#8ce4#22e4
; Auxiliary variables for L8cf0_project_object_and_add_to_render_list_clipping_internal
#8ce4#22e4
L8ce4_projected_data_current_ptr_tmp:  ; Temporary storage of the current vertex data ptr.
#8ce4#22e42
    dw #0000
#8ce6#22e6
L8ce6_current_face_texture_ID:
#8ce6#22e61
    db #00
#8ce7#22e7
L8ce7_current_face_normal_check_result:  ; Caches the result of the normal check for the current face
#8ce7#22e71
    db #00
#8ce8#22e8
L8ce8_screen_corner_coordinates:  ; Used to insert new vertices when clipping.
#8ce8#22e82
    db SCREEN_WIDTH_IN_PIXELS, SCREEN_HEIGHT_IN_PIXELS
#8cea#22ea2
    db #00, SCREEN_HEIGHT_IN_PIXELS
#8cec#22ec2
    db #00, #00
#8cee#22ee2
    db SCREEN_WIDTH_IN_PIXELS, #00
#8cf0#22f0
#8cf0#22f0
#8cf0#22f0
; --------------------------------
#8cf0#22f0
; Projects an object, and if it falls within the screen, add it to the list of objects to draw,
#8cf0#22f0
; assuming that we will have to clip some of the edges as some vertices are outside the viewing area.
#8cf0#22f0
; Input:
#8cf0#22f0
; - iy: face definition ptr:
#8cf0#22f0
;   - first byte is number of faces
#8cf0#22f0
;   - then, each face has:
#8cf0#22f0
;     - attribute
#8cf0#22f0
;     - number of vertices
#8cf0#22f0
;     - then one byte per vertex (index)
#8cf0#22f0
L8cf0_project_object_and_add_to_render_list_clipping_internal:
#8cf0#22f0317
2a 24 5f 
    ld hl, (L5f24_shape_edges_ptr)
#8cf3#22f318
46 
    ld b, (hl)  ; number of edges
#8cf4#22f4
    ; Initialize the L5ee8_already_projected_vertex_coordinates array:
#8cf4#22f4
    ; (5 bytes per edge):
#8cf4#22f4
    ;   - 0 if not processed, 1 if processed
#8cf4#22f4
    ;   - projected x (vertex 1)
#8cf4#22f4
    ;   - projected y (vertex 1)
#8cf4#22f4
    ;   - projected x (vertex 2)
#8cf4#22f4
    ;   - projected y (vertex 2)
#8cf4#22f4311
21 e8 5e 
    ld hl, L5ee8_already_projected_vertex_coordinates
#8cf7#22f728
3e ff 
    ld a, #ff
#8cf9#22f928
0e 00 
    ld c, 0
#8cfb#22fb
L8cfb:
#8cfb#22fb18
71 
    ld (hl), c
#8cfc#22fc17
23 
    inc hl
#8cfd#22fd18
77 
    ld (hl), a
#8cfe#22fe17
23 
    inc hl
#8cff#22ff17
23 
    inc hl
#8d00#230018
77 
    ld (hl), a
#8d01#230117
23 
    inc hl
#8d02#230217
23 
    inc hl
#8d03#2303214/9
10 f6 
    djnz L8cfb
#8d05#2305
#8d05#230515
af 
    xor a
#8d06#2306314
32 5f 5e 
    ld (L5e5f_add_to_projected_objects_flag), a
#8d09#2309317
2a 97 74 
    ld hl, (L7497_next_projected_vertex_ptr)
#8d0c#230c314
3a 68 74 
    ld a, (L7468_focus_object_id)
#8d0f#230f
    ; Start writing the projected vertex data:
#8d0f#230f18
77 
    ld (hl), a  ; object ID
#8d10#231017
23 
    inc hl
#8d11#2311211
36 00 
    ld (hl), 0  ; number of primitives (init to zero, and will be incremented each time a face is added).
#8d13#231317
23 
    inc hl
#8d14#2314321
fd 46 00 
    ld b, (iy)  ; number of faces
#8d17#2317212
fd 23 
    inc iy
#8d19#2319
L8d19_face_loop:
#8d19#2319112
c5 
    push bc
#8d1a#231a321
fd 7e 00 
        ld a, (iy)  ; a = texture ID.
#8d1d#231d212
fd 23 
        inc iy
#8d1f#231f321
fd 46 00 
        ld b, (iy)  ; b = number of vertexes/edges in the face.
#8d22#2322212
fd 23 
        inc iy
#8d24#2324
        ; If it's a transparent face, ignore:
#8d24#232415
b7 
        or a
#8d25#2325213/8
28 69 
        jr z, L8d90_skip_face_bytes_and_next_face
#8d27#2327210
cb 27 
        sla a
#8d29#2329210
cb 27 
        sla a
#8d2b#232b210
cb 27 
        sla a
#8d2d#232d210
cb 27 
        sla a
#8d2f#232f15
4f 
        ld c, a  ; c = texture ID (in the most significant nibble).
#8d30#2330
        ; This first loop goes over the edges looking to see if any vertex passed all the
#8d30#2330
        ; frustum checks:
#8d30#2330217
fd e5 
        push iy
#8d32#2332112
e5 
        push hl
#8d33#2333112
c5 
        push bc
#8d34#233428
3e 01 
            ld a, 1
#8d36#2336314
32 28 5f 
            ld (L5f28_cull_face_when_no_projected_vertices), a
#8d39#233928
0e 1e 
            ld c, #1e  ; "c" will accumulate the frustum checks of all the vertexes
#8d3b#233b15
04 
            inc b
#8d3c#233c210
cb 38 
            srl b  ; b = (number of vertexes + 1) * 2
#8d3e#233e28
16 00 
            ld d, 0
#8d40#2340
L8d40_edge_loop:
#8d40#2340321
fd 7e 00 
            ld a, (iy)  ; vertex/edge index
#8d43#234328
e6 7f 
            and #7f  ; get the index (remove a potential flag in the msb)
#8d45#2345210
cb 27 
            sla a
#8d47#234715
5f 
            ld e, a
#8d48#2348422
dd 2a 24 5f 
            ld ix, (L5f24_shape_edges_ptr)
#8d4c#234c212
dd 23 
            inc ix  ; skip the number of vertexes
#8d4e#234e217
dd 19 
            add ix, de  ; ix = ptr to the edge
#8d50#2350321
dd 5e 00 
            ld e, (ix)  ; vertex index 1
#8d53#2353311
21 dc 5e 
            ld hl, L5edc_vertex_rendering_frustum_checks
#8d56#2356112
19 
            add hl, de
#8d57#235718
7e 
            ld a, (hl)
#8d58#235828
fe 1f 
            cp #1f
#8d5a#235a213/8
28 1a 
            jr z, L8d76
#8d5c#235c
            ; vertex outside of view frustum:
#8d5c#235c15
a1 
            and c
#8d5d#235d15
4f 
            ld c, a
#8d5e#235e321
dd 5e 01 
            ld e, (ix + 1)  ; vertex index 2
#8d61#2361311
21 dc 5e 
            ld hl, L5edc_vertex_rendering_frustum_checks
#8d64#2364112
19 
            add hl, de
#8d65#236518
7e 
            ld a, (hl)
#8d66#236628
fe 1f 
            cp #1f
#8d68#2368213/8
28 0c 
            jr z, L8d76
#8d6a#236a
            ; vertex outside of view frustum:
#8d6a#236a15
a1 
            and c
#8d6b#236b15
4f 
            ld c, a
#8d6c#236c212
fd 23 
            inc iy
#8d6e#236e212
fd 23 
            inc iy
#8d70#2370214/9
10 ce 
            djnz L8d40_edge_loop
#8d72#2372
#8d72#237215
4f 
            ld c, a  ; OPTIMIZATION: useless instruction "ld c, a" was just executed above.
#8d73#237315
b7 
            or a
#8d74#2374213/8
28 04 
            jr z, L8d7a
#8d76#2376
L8d76:
#8d76#2376
            ; We get here if one vertex has passed all frustum tests,
#8d76#2376
            ; or if the frustum test accumulator (c) is non zero.
#8d76#237615
af 
            xor a
#8d77#2377314
32 28 5f 
            ld (L5f28_cull_face_when_no_projected_vertices), a
#8d7a#237a
L8d7a:
#8d7a#237a111
c1 
        pop bc  ; restore c: texture ID, b: number of edges of face
#8d7b#237b111
e1 
        pop hl  ; restore hl: ptr to projected vertex data
#8d7c#237c216
fd e1 
        pop iy  ; restore iy: face edge data ptr.
#8d7e#237e
#8d7e#237e314
3a 60 5e 
        ld a, (L5e60_projection_pre_work_type)
#8d81#2381
        ;   - if a == 0: indicates that vertices can be projected directly.
#8d81#2381
        ;   - if a == 1: we need to call L88d1_normal_direction_check before projection, and if back-face, we cull
#8d81#2381
        ;   - if a == 2: we need to call L88d1_normal_direction_check before projection, and if back-face we need to use L5f26_alternative_shape_edges_ptr        
#8d81#238115
b7 
        or a
#8d82#2382213/8
28 43 
        jr z, L8dc7_ready_to_project
#8d84#238428
fe 01 
        cp 1
#8d86#2386213/8
20 10 
        jr nz, L8d98
#8d88#2388
#8d88#2388
        ; Normal check, and cull if failed:
#8d88#2388318
cd d1 88 
        call L88d1_normal_direction_check
#8d8b#238b314
32 e7 8c 
        ld (L8ce7_current_face_normal_check_result), a
#8d8e#238e213/8
30 37 
        jr nc, L8dc7_ready_to_project
#8d90#2390
L8d90_skip_face_bytes_and_next_face:
#8d90#239015
48 
        ld c, b
#8d91#239128
06 00 
        ld b, 0
#8d93#2393217
fd 09 
        add iy, bc
#8d95#2395311
c3 9f 8f 
        jp L8f9f_next_face
#8d98#2398
#8d98#2398
L8d98:
#8d98#2398
        ; Normal check, and use back texture if failed:
#8d98#2398318
cd d1 88 
        call L88d1_normal_direction_check
#8d9b#239b314
32 e7 8c 
        ld (L8ce7_current_face_normal_check_result), a
#8d9e#239e314
3a 6a 74 
        ld a, (L746a_current_drawing_texture_id)
#8da1#23a1213/8
30 17 
        jr nc, L8dba_front_face
#8da3#23a3
        ; back face, we need to swap texture ID, and use alternative shape edges ptr:
#8da3#23a328
e6 f0 
        and #f0
#8da5#23a5213/8
28 e9 
        jr z, L8d90_skip_face_bytes_and_next_face
#8da7#23a715
4f 
        ld c, a
#8da8#23a8422
ed 5b 26 5f 
        ld de, (L5f26_alternative_shape_edges_ptr)
#8dac#23ac422
ed 53 24 5f 
        ld (L5f24_shape_edges_ptr), de
#8db0#23b0
        ; OPTIMIZATION: at this point (L8ce7_current_face_normal_check_result) always contains a 1, so no need to read it and xor, just set to 0.
#8db0#23b0314
3a e7 8c 
        ld a, (L8ce7_current_face_normal_check_result)
#8db3#23b328
ee 01 
        xor 1
#8db5#23b5314
32 e7 8c 
        ld (L8ce7_current_face_normal_check_result), a
#8db8#23b8213
18 0d 
        jr L8dc7_ready_to_project
#8dba#23ba
#8dba#23ba
L8dba_front_face:
#8dba#23ba
        ; Get the texture ID into the most significant nibble of c
#8dba#23ba28
e6 0f 
        and #0f
#8dbc#23bc213/8
28 d2 
        jr z, L8d90_skip_face_bytes_and_next_face
#8dbe#23be210
cb 27 
        sla a
#8dc0#23c0210
cb 27 
        sla a
#8dc2#23c2210
cb 27 
        sla a
#8dc4#23c4210
cb 27 
        sla a
#8dc6#23c615
4f 
        ld c, a
#8dc7#23c7
#8dc7#23c7
L8dc7_ready_to_project:
#8dc7#23c7
        ; At this point:
#8dc7#23c7
        ; c = texture ID (most significant nibble)
#8dc7#23c7
        ; b = number of edges in the face
#8dc7#23c7
        ; hl = ptr to projected vertex data
#8dc7#23c7
        ; iy = face edge data ptr.
#8dc7#23c715
79 
        ld a, c
#8dc8#23c8314
32 e6 8c 
        ld (L8ce6_current_face_texture_ID), a
#8dcb#23cb15
78 
        ld a, b
#8dcc#23cc28
fe 02 
        cp 2
#8dce#23ce213/8
20 01 
        jr nz, L8dd1
#8dd0#23d015
05 
        dec b  ; If the number of edges is 2, make it 1 (a line).
#8dd1#23d1
L8dd1:
#8dd1#23d1
        ; This second edge loop: projects all the vertices, clipping them if necessary.
#8dd1#23d1217
fd e5 
        push iy
#8dd3#23d3112
e5 
        push hl
#8dd4#23d4112
c5 
        push bc
#8dd5#23d528
16 00 
            ld d, 0
#8dd7#23d7
L8dd7_edge_loop_2:
#8dd7#23d7112
c5 
            push bc
#8dd8#23d8321
fd 7e 00 
                ld a, (iy)  ; edge index
#8ddb#23db212
fd 23 
                inc iy
#8ddd#23dd217
fd e5 
                push iy
#8ddf#23df28
e6 7f 
                    and #7f  ; get rid of the edge flip flag.
#8de1#23e115
5f 
                    ld e, a
#8de2#23e2210
cb 27 
                    sla a
#8de4#23e415
4f 
                    ld c, a  ; c = edge index * 2
#8de5#23e5210
cb 27 
                    sla a
#8de7#23e715
83 
                    add a, e
#8de8#23e815
5f 
                    ld e, a  ; e = edge index * 5
#8de9#23e9
                    ; Check if this edge had already been processed:
#8de9#23e9416
dd 21 e8 5e 
                    ld ix, L5ee8_already_projected_vertex_coordinates
#8ded#23ed217
dd 19 
                    add ix, de
#8def#23ef15
af 
                    xor a
#8df0#23f0321
dd be 00 
                    cp (ix)
#8df3#23f3213/8
20 67 
                    jr nz, L8e5c_next_edge
#8df5#23f5212
dd 23 
                    inc ix
#8df7#23f7317
2a 24 5f 
                    ld hl, (L5f24_shape_edges_ptr)
#8dfa#23fa17
23 
                    inc hl
#8dfb#23fb15
59 
                    ld e, c
#8dfc#23fc112
19 
                    add hl, de  ; ptr to the edge
#8dfd#23fd18
5e 
                    ld e, (hl)  ; first vertex of the edge
#8dfe#23fe416
fd 21 dc 5e 
                    ld iy, L5edc_vertex_rendering_frustum_checks
#8e02#2402217
fd 19 
                    add iy, de
#8e04#2404321
fd 7e 00 
                    ld a, (iy)  ; frustum checks for first vertex of the edge
#8e07#240715
4f 
                    ld c, a
#8e08#240828
fe 1f 
                    cp #1f
#8e0a#240a213/8
20 0a 
                    jr nz, L8e16_second_vertex
#8e0c#240c
                    ; Vertex passed all frustum checks:
#8e0c#240c321
dd 7e 00 
                    ld a, (ix)  ; Check if we have already projected it
#8e0f#240f28
fe ff 
                    cp #ff
#8e11#2411213/8
20 03 
                    jr nz, L8e16_second_vertex
#8e13#2413
                    ; We need to project it:
#8e13#2413318
cd a5 8f 
                    call L8fa5_project_one_vertex_for_clipping_projection
#8e16#2416
L8e16_second_vertex:
#8e16#241617
23 
                    inc hl
#8e17#241718
5e 
                    ld e, (hl)  ; second vertex of the edge
#8e18#241817
2b 
                    dec hl
#8e19#2419416
fd 21 dc 5e 
                    ld iy, L5edc_vertex_rendering_frustum_checks
#8e1d#241d217
fd 19 
                    add iy, de
#8e1f#241f321
fd 7e 00 
                    ld a, (iy)  ; frustum checks for second vertex of the edge
#8e22#242215
47 
                    ld b, a
#8e23#242328
fe 1f 
                    cp #1f
#8e25#2425213/8
20 0a 
                    jr nz, L8e31
#8e27#2427321
dd 7e 02 
                    ld a, (ix + 2)  ; second vertex x
#8e2a#242a28
fe ff 
                    cp #ff
#8e2c#242c213/8
20 03 
                    jr nz, L8e31
#8e2e#242e318
cd a5 8f 
                    call L8fa5_project_one_vertex_for_clipping_projection
#8e31#2431
L8e31:
#8e31#2431
                    ; Here c and b contain the frustum checks of the two vertices:
#8e31#243115
78 
                    ld a, b
#8e32#243215
a1 
                    and c
#8e33#243328
fe 1f 
                    cp #1f
#8e35#2435213/8
20 05 
                    jr nz, L8e3c_at_least_one_vertex_outside
#8e37#2437
L8e37:
#8e37#2437
                    ; If both vertices were within the viewable area, or both outside
#8e37#2437
                    ; mark this edge as processed:
#8e37#2437325
dd 34 ffff 
                    inc (ix - 1)  ; mark the eedge as processed
#8e3a#243a213
18 20 
                    jr L8e5c_next_edge
#8e3c#243c
L8e3c_at_least_one_vertex_outside:
#8e3c#243c
                    ; At least one vertex was outside the view frustum, we need to clip:
#8e3c#243c15
78 
                    ld a, b
#8e3d#243d15
b1 
                    or c
#8e3e#243e28
fe 1f 
                    cp #1f
#8e40#2440213/8
20 f5 
                    jr nz, L8e37  ; both vertexes were outside, mark as processed too.
#8e42#2442
                    ; One vertex was in, the other was out:
#8e42#2442210
cb 23 
                    sla e
#8e44#2444416
fd 21 9f 5e 
                    ld iy, L5e9f_3d_vertex_coordinates_after_rotation_matrix
#8e48#2448217
fd 19 
                    add iy, de
#8e4a#244a217
fd 19 
                    add iy, de
#8e4c#244c217
fd 19 
                    add iy, de  ; iy = ptr to 3d vertex 2
#8e4e#244e18
5e 
                    ld e, (hl)  ; get the vertex 1 index again
#8e4f#244f210
cb 23 
                    sla e
#8e51#2451311
21 9f 5e 
                    ld hl, L5e9f_3d_vertex_coordinates_after_rotation_matrix
#8e54#2454112
19 
                    add hl, de
#8e55#2455112
19 
                    add hl, de
#8e56#2456112
19 
                    add hl, de  ; hl = ptr to 3d vertex 1
#8e57#2457318
cd ae 85 
                    call L85ae_clip_edge
#8e5a#245a28
16 00 
                    ld d, 0
#8e5c#245c
L8e5c_next_edge:
#8e5c#245c216
fd e1 
                pop iy
#8e5e#245e111
c1 
            pop bc
#8e5f#245f15
05 
            dec b
#8e60#2460311
c2 d7 8d 
            jp nz, L8dd7_edge_loop_2
#8e63#2463111
c1 
        pop bc
#8e64#2464111
e1 
        pop hl
#8e65#2465216
fd e1 
        pop iy
#8e67#2467
#8e67#2467
        ; At this point:
#8e67#2467
        ; b = number of edges in the face
#8e67#2467
        ; hl = ptr to projected vertex data we are writing to
#8e67#2467
        ; iy = face edge data ptr.
#8e67#2467
#8e67#2467
        ; This third loop adds projected vertices to the projected data, inserting connecting points if necessary.
#8e67#246715
4a 
        ld c, d  ; c = 0 (number of vertices added)
#8e68#2468317
22 e4 8c 
        ld (L8ce4_projected_data_current_ptr_tmp), hl  ; Save the current pointer to the projected vertex data we are writing to
#8e6b#246b17
23 
        inc hl
#8e6c#246c112
c5 
        push bc
#8e6d#246d
L8e6d_edge_loop_3:
#8e6d#246d321
fd 7e 00 
            ld a, (iy)  ; edge index
#8e70#247028
e6 7f 
            and #7f  ; get rid of the edge flip flag.
#8e72#247215
5f 
            ld e, a
#8e73#2473210
cb 27 
            sla a
#8e75#2475210
cb 27 
            sla a
#8e77#247715
83 
            add a, e  ; e = edge index * 5
#8e78#247815
5f 
            ld e, a
#8e79#247928
16 00 
            ld d, 0
#8e7b#247b416
dd 21 e8 5e 
            ld ix, L5ee8_already_projected_vertex_coordinates
#8e7f#247f217
dd 19 
            add ix, de
#8e81#2481
            ; If this edge was not projected, it means it was fully outside of the
#8e81#2481
            ; view area, just ignore:
#8e81#2481321
dd 7e 01 
            ld a, (ix + 1)
#8e84#248428
fe ff 
            cp #ff
#8e86#2486213/8
28 6d 
            jr z, L8ef5_next_edge
#8e88#2488422
fd cb 00 7e 
            bit 7, (iy)  ; Check edge flip flag
#8e8c#248c213/8
28 19 
            jr z, L8ea7_vertices_in_the_correct_order
#8e8e#248e
            ; Flip the projected vertex info:
#8e8e#248e321
dd 5e 01 
            ld e, (ix + 1)
#8e91#2491321
dd 56 03 
            ld d, (ix + 3)
#8e94#249415
7a 
            ld a, d  ; overwrite a with the x projection of the new first vertex.
#8e95#2495321
dd 72 01 
            ld (ix + 1), d
#8e98#2498321
dd 73 03 
            ld (ix + 3), e
#8e9b#249b321
dd 5e 02 
            ld e, (ix + 2)
#8e9e#249e321
dd 56 04 
            ld d, (ix + 4)
#8ea1#24a1321
dd 72 02 
            ld (ix + 2), d
#8ea4#24a4321
dd 73 04 
            ld (ix + 4), e
#8ea7#24a7
L8ea7_vertices_in_the_correct_order:
#8ea7#24a715
5f 
            ld e, a  ; vertex 1 x
#8ea8#24a8321
dd 56 02 
            ld d, (ix + 2)  ; vertex 1 y
#8eab#24ab15
79 
            ld a, c
#8eac#24ac15
b7 
            or a
#8ead#24ad213/8
28 0f 
            jr z, L8ebe  ; If it's the first vertex we project, skip
#8eaf#24af
            ; Check if the coordinates of vertex 1 are the same as the last projected vertex.
#8eaf#24af
            ; These should match if there was no clipping, but when there is clipping, we might
#8eaf#24af
            ; need to insert additional edges to connect clipped points:
#8eaf#24af15
7b 
            ld a, e
#8eb0#24b017
2b 
            dec hl
#8eb1#24b117
2b 
            dec hl
#8eb2#24b218
be 
            cp (hl)  ; compare x coordiantes
#8eb3#24b317
23 
            inc hl
#8eb4#24b4213/8
20 02 
            jr nz, L8eb8  ; no x match
#8eb6#24b615
7a 
            ld a, d
#8eb7#24b718
be 
            cp (hl)  ; compare y coordinates
#8eb8#24b8
L8eb8:
#8eb8#24b817
23 
            inc hl
#8eb9#24b9213/8
28 08 
            jr z, L8ec3_skip_vertex1_insertion
#8ebb#24bb318
cd ea 8f 
            call L8fea_add_connecting_projected_vertices
#8ebe#24be
L8ebe:
#8ebe#24be
            ; Add vertex to projection and increment vertex count ("c"):
#8ebe#24be18
73 
            ld (hl), e
#8ebf#24bf17
23 
            inc hl
#8ec0#24c018
72 
            ld (hl), d
#8ec1#24c117
23 
            inc hl
#8ec2#24c215
0c 
            inc c
#8ec3#24c3
L8ec3_skip_vertex1_insertion:
#8ec3#24c3321
dd 7e 04 
            ld a, (ix + 4)  ; vertex 2 y
#8ec6#24c615
ba 
            cp d
#8ec7#24c7321
dd 7e 03 
            ld a, (ix + 3)  ; vertex 2 x
#8eca#24ca213/8
20 03 
            jr nz, L8ecf
#8ecc#24cc15
bb 
            cp e
#8ecd#24cd213/8
28 08 
            jr z, L8ed7
#8ecf#24cf
L8ecf:
#8ecf#24cf
            ; Vertex 2 is different from vertex 1:
#8ecf#24cf18
77 
            ld (hl), a  ; x coordinate
#8ed0#24d017
23 
            inc hl
#8ed1#24d1321
dd 7e 04 
            ld a, (ix + 4)  ; y coordinate
#8ed4#24d418
77 
            ld (hl), a
#8ed5#24d517
23 
            inc hl
#8ed6#24d615
0c 
            inc c  ; incremenr number of projected vertices
#8ed7#24d7
L8ed7:
#8ed7#24d7
            ; If we had flipped the vertices, put them back in their original order:
#8ed7#24d7422
fd cb 00 7e 
            bit 7, (iy)
#8edb#24db213/8
28 18 
            jr z, L8ef5_next_edge
#8edd#24dd321
dd 5e 01 
            ld e, (ix + 1)
#8ee0#24e0321
dd 56 03 
            ld d, (ix + 3)
#8ee3#24e3321
dd 72 01 
            ld (ix + 1), d
#8ee6#24e6321
dd 73 03 
            ld (ix + 3), e
#8ee9#24e9321
dd 5e 02 
            ld e, (ix + 2)
#8eec#24ec321
dd 56 04 
            ld d, (ix + 4)
#8eef#24ef321
dd 72 02 
            ld (ix + 2), d
#8ef2#24f2321
dd 73 04 
            ld (ix + 4), e
#8ef5#24f5
L8ef5_next_edge:
#8ef5#24f5212
fd 23 
            inc iy
#8ef7#24f715
05 
            dec b
#8ef8#24f8311
c2 6d 8e 
            jp nz, L8e6d_edge_loop_3
#8efb#24fb
#8efb#24fb15
79 
            ld a, c
#8efc#24fc111
c1 
        pop bc  ; restore the number of edges in b
#8efd#24fd422
dd 2a e4 8c 
        ld ix, (L8ce4_projected_data_current_ptr_tmp)
#8f01#250115
4f 
        ld c, a  ; number of projected vertices
#8f02#250228
fe 02 
        cp 2
#8f04#2504213/8
30 63 
        jr nc, L8f69_2_or_more_vertices_projected
#8f06#250628
fe 01 
        cp 1
#8f08#2508213/8
20 02 
        jr nz, L8f0c_0_vertices_projected
#8f0a#250a17
2b 
        dec hl
#8f0b#250b17
2b 
        dec hl
#8f0c#250c
#8f0c#250c
L8f0c_0_vertices_projected:
#8f0c#250c15
78 
        ld a, b
#8f0d#250d28
fe 01 
        cp 1
#8f0f#250f213/8
28 06 
        jr z, L8f17_discard_current_face
#8f11#2511314
3a 28 5f 
        ld a, (L5f28_cull_face_when_no_projected_vertices)
#8f14#251415
b7 
        or a
#8f15#2515213/8
20 06 
        jr nz, L8f1d
#8f17#2517
L8f17_discard_current_face:
#8f17#2517
        ; Ignore this face and all projected points so far
#8f17#2517317
2a e4 8c 
        ld hl, (L8ce4_projected_data_current_ptr_tmp)
#8f1a#251a311
c3 9f 8f 
        jp L8f9f_next_face
#8f1d#251d
#8f1d#251d
L8f1d:
#8f1d#251d314
3a 60 5e 
        ld a, (L5e60_projection_pre_work_type)
#8f20#252015
b7 
        or a
#8f21#2521213/8
20 18 
        jr nz, L8f3b_we_already_did_normal_check
#8f23#2523
        ; When L5e60_projection_pre_work_type is zero, we had not done a normal check,
#8f23#2523
        ; and hence "L5f28_cull_face_when_no_projected_vertices" might not be fully populated,
#8f23#2523
        ; do it now:
#8f23#2523217
fd e5 
        push iy
#8f25#252515
78 
            ld a, b
#8f26#2526210
ed 44 
            neg
#8f28#252815
5f 
            ld e, a
#8f29#252928
16 ff 
            ld d, 255
#8f2b#252b217
fd 19 
            add iy, de  ; iy -= number of edes of the face (to reset to the beginning of this face data)
#8f2d#252d318
cd d1 88 
            call L88d1_normal_direction_check
#8f30#2530216
fd e1 
        pop iy
#8f32#2532314
32 e7 8c 
        ld (L8ce7_current_face_normal_check_result), a
#8f35#2535314
3a 28 5f 
        ld a, (L5f28_cull_face_when_no_projected_vertices)
#8f38#253815
b7 
        or a
#8f39#2539213/8
28 dc 
        jr z, L8f17_discard_current_face
#8f3b#253b
#8f3b#253b
L8f3b_we_already_did_normal_check:
#8f3b#253b318
cd 68 90 
        call L9068_face_covers_whole_screen_check
#8f3e#253e314
3a 28 5f 
        ld a, (L5f28_cull_face_when_no_projected_vertices)
#8f41#254115
b7 
        or a
#8f42#2542213/8
28 d3 
        jr z, L8f17_discard_current_face
#8f44#2544
#8f44#2544
        ; Object occupies the whole screen, set screen coordinates as the projected vertices:
#8f44#2544311
11 e8 8c 
        ld de, L8ce8_screen_corner_coordinates
#8f47#254715
eb 
        ex de, hl
#8f48#2548311
01 08 00 
        ld bc, 8
#8f4b#254b223/18
ed b0 
        ldir
#8f4d#254d314
3a e6 8c 
        ld a, (L8ce6_current_face_texture_ID)
#8f50#255028
f6 04 
        or 4
#8f52#2552321
dd 77 00 
        ld (ix), a
#8f55#2555
        ; Mark that we have objects covering the whols screen:
#8f55#2555311
21 81 74 
        ld hl, L7481_n_objects_covering_the_whole_screen
#8f58#2558112
34 
        inc (hl)
#8f59#2559317
2a 97 74 
        ld hl, (L7497_next_projected_vertex_ptr)
#8f5c#255c17
23 
        inc hl
#8f5d#255d112
34 
        inc (hl)
#8f5e#255e217
cb fe 
        set 7, (hl)
#8f60#256015
eb 
        ex de, hl
#8f61#256128
3e 01 
        ld a, 1
#8f63#2563314
32 5f 5e 
        ld (L5e5f_add_to_projected_objects_flag), a
#8f66#2566111
c1 
    pop bc
#8f67#2567213
18 3b 
    jr L8fa4_ret
#8f69#2569
#8f69#2569
L8f69_2_or_more_vertices_projected:
#8f69#256928
fe 02 
        cp 2
#8f6b#256b213/8
20 05 
        jr nz, L8f72_close_shape
#8f6d#256d
        ; If we projected just 2 vertices:
#8f6d#256d15
78 
        ld a, b
#8f6e#256e28
fe 01 
        cp 1  ; If the original object was just a line, we are done
#8f70#2570213/8
28 1a 
        jr z, L8f8c_successful_face_projection
#8f72#2572
L8f72_close_shape:
#8f72#2572
        ; Check if the last vertex we added matches the very first vertex,
#8f72#2572
        ; if it does, remove it. If it does not, check if we need to insert connecting projected vertices:
#8f72#2572321
dd 5e 01 
        ld e, (ix + 1)
#8f75#2575321
dd 56 02 
        ld d, (ix + 2)
#8f78#257815
7b 
        ld a, e
#8f79#257917
2b 
        dec hl
#8f7a#257a17
2b 
        dec hl
#8f7b#257b18
be 
        cp (hl)
#8f7c#257c17
23 
        inc hl
#8f7d#257d213/8
20 02 
        jr nz, L8f81
#8f7f#257f15
7a 
        ld a, d
#8f80#258018
be 
        cp (hl)
#8f81#2581
L8f81:
#8f81#258117
23 
        inc hl
#8f82#2582213/8
20 05 
        jr nz, L8f89
#8f84#2584
        ; Match, remove the last vertex:
#8f84#258415
0d 
        dec c
#8f85#258517
2b 
        dec hl
#8f86#258617
2b 
        dec hl
#8f87#2587213
18 03 
        jr L8f8c_successful_face_projection
#8f89#2589
L8f89:
#8f89#2589
        ; No match, check for necessary connecting vertices:
#8f89#2589318
cd ea 8f 
        call L8fea_add_connecting_projected_vertices
#8f8c#258c
L8f8c_successful_face_projection:
#8f8c#258c
        ; Add the texture / # of vertices byte, mark as projected, and move to next face.
#8f8c#258c314
3a e6 8c 
        ld a, (L8ce6_current_face_texture_ID)
#8f8f#258f15
b1 
        or c
#8f90#2590321
dd 77 00 
        ld (ix), a
#8f93#2593
#8f93#259328
3e 01 
        ld a, 1
#8f95#2595314
32 5f 5e 
        ld (L5e5f_add_to_projected_objects_flag), a
#8f98#2598422
dd 2a 97 74 
        ld ix, (L7497_next_projected_vertex_ptr)
#8f9c#259c325
dd 34 01 
        inc (ix + 1)  ; increment the number of primitives counter
#8f9f#259f
L8f9f_next_face:
#8f9f#259f111
c1 
    pop bc
#8fa0#25a015
05 
    dec b
#8fa1#25a1311
c2 19 8d 
    jp nz, L8d19_face_loop
#8fa4#25a4
L8fa4_ret:
#8fa4#25a4111
c9 
    ret
#8fa5#25a5
#8fa5#25a5
#8fa5#25a5
; --------------------------------
#8fa5#25a5
; This method projects the vertex with index 'e', and writes the projected coordinates
#8fa5#25a5
; to the L5ee8_already_projected_vertex_coordinates buffer, assuming we are using
#8fa5#25a5
; projection method L8cf0_project_object_and_add_to_render_list_clipping_internal.
#8fa5#25a5
; Input:
#8fa5#25a5
; - e: vertex index.
#8fa5#25a5
L8fa5_project_one_vertex_for_clipping_projection:
#8fa5#25a5217
dd e5 
    push ix
#8fa7#25a7112
e5 
    push hl
#8fa8#25a8112
d5 
    push de
#8fa9#25a9112
c5 
    push bc
#8faa#25aa15
7b 
        ld a, e  ; a = vertex index
#8fab#25ab210
cb 23 
        sla e
#8fad#25ad416
dd 21 9f 5e 
        ld ix, L5e9f_3d_vertex_coordinates_after_rotation_matrix
#8fb1#25b1217
dd 19 
        add ix, de
#8fb3#25b3217
dd 19 
        add ix, de
#8fb5#25b5217
dd 19 
        add ix, de  ; get the 3d vertex pointer
#8fb7#25b7318
cd fc 90 
        call L90fc_project_one_vertex
#8fba#25ba317
2a 24 5f 
        ld hl, (L5f24_shape_edges_ptr)
#8fbd#25bd416
dd 21 e9 5e 
        ld ix, L5ee8_already_projected_vertex_coordinates + 1
#8fc1#25c115
58 
        ld e, b  ; e = projected y
#8fc2#25c218
46 
        ld b, (hl)  ; number of edges
#8fc3#25c317
23 
        inc hl
#8fc4#25c4
        ; This loop goes through the L5ee8_already_projected_vertex_coordinates array,
#8fc4#25c4
        ; and writes projected coordinates for all the vertices that match the current vertex
#8fc4#25c4
        ; we just projected.
#8fc4#25c4
L8fc4_write_projected_vertex_to_buffer:
#8fc4#25c418
be 
        cp (hl)  ; is this the right vertex?
#8fc5#25c5213/8
20 06 
        jr nz, L8fcd
#8fc7#25c7
        ; Yes, write projection data!
#8fc7#25c7321
dd 71 00 
        ld (ix), c  ; projected x
#8fca#25ca321
dd 73 01 
        ld (ix + 1), e  ; projected y
#8fcd#25cd
L8fcd:
#8fcd#25cd212
dd 23 
        inc ix
#8fcf#25cf212
dd 23 
        inc ix
#8fd1#25d117
23 
        inc hl
#8fd2#25d218
be 
        cp (hl)  ; is this the right vertex?
#8fd3#25d3213/8
20 06 
        jr nz, L8fdb
#8fd5#25d5
        ; Yes, write projection data!
#8fd5#25d5321
dd 71 00 
        ld (ix), c  ; projected x
#8fd8#25d8321
dd 73 01 
        ld (ix + 1), e  ; projected y
#8fdb#25db
L8fdb:
#8fdb#25db212
dd 23 
        inc ix
#8fdd#25dd212
dd 23 
        inc ix
#8fdf#25df212
dd 23 
        inc ix
#8fe1#25e117
23 
        inc hl
#8fe2#25e2214/9
10 e0 
        djnz L8fc4_write_projected_vertex_to_buffer
#8fe4#25e4111
c1 
    pop bc
#8fe5#25e5111
d1 
    pop de
#8fe6#25e6111
e1 
    pop hl
#8fe7#25e7216
dd e1 
    pop ix
#8fe9#25e9111
c9 
    ret
#8fea#25ea
#8fea#25ea
#8fea#25ea
; --------------------------------
#8fea#25ea
; Checks if we need to insert additional projected vertices along the screen edges to
#8fea#25ea
; connect the previous projected vertex with the new one we want to add.
#8fea#25ea
; Input:
#8fea#25ea
; - c: number of inserted vertexes so far
#8fea#25ea
; - e: new vertex projected x
#8fea#25ea
; - d: new vertex projected y
#8fea#25ea
; - hl: ptr to projected vertex data we are writing to
#8fea#25ea
; Output:
#8fea#25ea
; - c: updated number of inserted vertexes so far
#8fea#25ea
; - hl: updated ptr to projected vertex data we are writing to
#8fea#25ea
L8fea_add_connecting_projected_vertices:
#8fea#25ea217
dd e5 
    push ix
#8fec#25ec217
fd e5 
    push iy
#8fee#25ee112
e5 
        push hl
#8fef#25ef216
dd e1 
        pop ix
#8ff1#25f1311
21 01 00 
        ld hl, 1
#8ff4#25f428
3e 70 
        ld a, SCREEN_HEIGHT_IN_PIXELS
#8ff6#25f6321
dd be ffff 
        cp (ix - 1)  ; is previous vertex y at the top of the screen?
#8ff9#25f9213/8
28 0e 
        jr z, L9009
#8ffb#25fb15
2c 
        inc l  ; l = 2
#8ffc#25fc15
af 
        xor a
#8ffd#25fd321
dd be fffe 
        cp (ix - 2)  ; is previous vertex x in the left of the screen?
#9000#2600213/8
28 07 
        jr z, L9009
#9002#260215
2c 
        inc l  ; l = 3
#9003#2603321
dd be ffff 
        cp (ix - 1)  ; is previous vertex y in the bottom of the screen?
#9006#2606213/8
28 01 
        jr z, L9009
#9008#260815
6f 
        ld l, a  ; l = 0
#9009#2609
L9009:
#9009#2609
        ; At this point:
#9009#2609
        ; - l = 0: previous vertex at right side of screen
#9009#2609
        ; - l = 1: previous vertex at top of screen
#9009#2609
        ; - l = 2: previous vertex at left side of screen
#9009#2609
        ; - l = 3: previous vertex at bottom of screen
#9009#260928
3e c0 
        ld a, SCREEN_WIDTH_IN_PIXELS
#900b#260b15
bb 
        cp e
#900c#260c213/8
28 0c 
        jr z, L901a
#900e#260e15
24 
        inc h
#900f#260f28
3e 70 
        ld a, SCREEN_HEIGHT_IN_PIXELS
#9011#261115
ba 
        cp d
#9012#2612213/8
28 06 
        jr z, L901a
#9014#261415
24 
        inc h
#9015#261515
af 
        xor a
#9016#261615
bb 
        cp e
#9017#2617213/8
28 01 
        jr z, L901a
#9019#261915
24 
        inc h
#901a#261a
L901a:
#901a#261a
        ; At this point:
#901a#261a
        ; - h = 0: new vertex at right side of screen
#901a#261a
        ; - h = 1: new vertex at top of screen
#901a#261a
        ; - h = 2: new vertex at left side of screen
#901a#261a
        ; - h = 3: new vertex at bottom of screen
#901a#261a15
7c 
        ld a, h
#901b#261b15
bd 
        cp l
#901c#261c213/8
28 42 
        jr z, L9060_done  ; both vertexes are in the same side of the screen, we can just insert the new vertex.
#901e#261e
        ; Previous and new vertex are not on the same edges of the screen, we need to insert an auxiliary vertex:
#901e#261e112
d5 
        push de
#901f#261f
L901f:
#901f#261f
            ; Get the coordinates of the screen corner that would help us come closer to
#901f#261f
            ; the edge of the new projected vertex:
#901f#261f15
5d 
            ld e, l
#9020#262028
16 00 
            ld d, 0
#9022#2622210
cb 23 
            sla e
#9024#2624416
fd 21 e8 8c 
            ld iy, L8ce8_screen_corner_coordinates
#9028#2628217
fd 19 
            add iy, de
#902a#262a321
fd 7e 00 
            ld a, (iy)
#902d#262d321
fd 56 01 
            ld d, (iy + 1)
#9030#2630
            ; Check that we are not adding a point that is identical to the previous one, just in case:
#9030#263015
5f 
            ld e, a
#9031#2631321
dd be fffe 
            cp (ix - 2)
#9034#2634213/8
20 06 
            jr nz, L903c
#9036#263615
7a 
            ld a, d
#9037#2637321
dd be ffff 
            cp (ix - 1)
#903a#263a213/8
28 1b 
            jr z, L9057
#903c#263c
L903c:
#903c#263c
            ; Check that it is not identical to the very first vertex of the face:
#903c#263c422
fd 2a e4 8c 
            ld iy, (L8ce4_projected_data_current_ptr_tmp)
#9040#264015
7b 
            ld a, e
#9041#2641321
fd be 01 
            cp (iy + 1)
#9044#2644213/8
20 06 
            jr nz, L904c
#9046#264615
7a 
            ld a, d
#9047#2647321
fd be 02 
            cp (iy + 2)
#904a#264a213/8
28 0b 
            jr z, L9057
#904c#264c
L904c:
#904c#264c
            ; Insert a new projected vertex:
#904c#264c321
dd 73 00 
            ld (ix), e  ; x
#904f#264f321
dd 72 01 
            ld (ix + 1), d  ; y
#9052#2652212
dd 23 
            inc ix
#9054#2654212
dd 23 
            inc ix
#9056#265615
0c 
            inc c  ; increase number of projected vertices count.
#9057#2657
L9057:
#9057#2657
              ; update the screen edge the new previous vertex is at
#9057#265715
2c 
            inc l
#9058#265815
7d 
            ld a, l
#9059#265928
e6 03 
            and #03
#905b#265b15
6f 
            ld l, a
#905c#265c
            ; Have we brought it to the same edge as the new vertex? if so, we are done.
#905c#265c15
bc 
            cp h
#905d#265d213/8
20 c0 
            jr nz, L901f
#905f#265f111
d1 
        pop de
#9060#2660
L9060_done:
#9060#2660217
dd e5 
        push ix
#9062#2662111
e1 
        pop hl
#9063#2663216
fd e1 
    pop iy
#9065#2665216
dd e1 
    pop ix
#9067#2667111
c9 
    ret
#9068#2668
#9068#2668
#9068#2668
; --------------------------------
#9068#2668
; Checks a face that has resulted in no projected vertices covers the whole screen.
#9068#2668
; Note: I am not sure about how the math in this function works, as I have not tried to derive the
#9068#2668
;       interpretration of he calculations. So, I have named this function based on the effect that
#9068#2668
;       it later has when called.
#9068#2668
; Input:
#9068#2668
; - b: number of edges of face
#9068#2668
; - e: number of edges in current face
#9068#2668
; - iy: face edge data (pointing to the end of the data)
#9068#2668
L9068_face_covers_whole_screen_check:
#9068#2668217
dd e5 
    push ix
#906a#266a217
fd e5 
    push iy
#906c#266c112
e5 
    push hl
#906d#266d15
af 
        xor a
#906e#266e314
32 28 5f 
        ld (L5f28_cull_face_when_no_projected_vertices), a
#9071#267115
78 
        ld a, b
#9072#2672210
ed 44 
        neg
#9074#267415
5f 
        ld e, a
#9075#267528
16 ff 
        ld d, 255
#9077#2677217
fd 19 
        add iy, de  ; iy -= number of edes of the face (to reset to the beginning of this face data)
#9079#2679
L9079:
#9079#2679112
c5 
        push bc
#907a#267a321
fd 7e 00 
            ld a, (iy)
#907d#267d28
e6 7f 
            and #7f
#907f#267f15
5f 
            ld e, a
#9080#268028
16 00 
            ld d, 0
#9082#268215
62 
            ld h, d
#9083#2683210
cb 23 
            sla e
#9085#2685422
dd 2a 24 5f 
            ld ix, (L5f24_shape_edges_ptr)
#9089#2689212
dd 23 
            inc ix
#908b#268b217
dd 19 
            add ix, de
#908d#268d321
dd 6e 00 
            ld l, (ix)
#9090#2690321
dd 5e 01 
            ld e, (ix + 1)
#9093#2693422
fd cb 00 7e 
            bit 7, (iy)  ; vertex flip flag
#9097#2697213/8
28 01 
            jr z, L909a
#9099#269915
eb 
            ex de, hl
#909a#269a
L909a:
#909a#269a217
fd e5 
            push iy
#909c#269c210
cb 23 
                sla e
#909e#269e416
dd 21 9f 5e 
                ld ix, L5e9f_3d_vertex_coordinates_after_rotation_matrix
#90a2#26a2217
dd 19 
                add ix, de
#90a4#26a4210
cb 23 
                sla e
#90a6#26a6217
dd 19 
                add ix, de  ; ix += vertex 1 index * 6
#90a8#26a815
eb 
                ex de, hl
#90a9#26a9210
cb 23 
                sla e
#90ab#26ab416
fd 21 9f 5e 
                ld iy, L5e9f_3d_vertex_coordinates_after_rotation_matrix
#90af#26af217
fd 19 
                add iy, de
#90b1#26b1210
cb 23 
                sla e
#90b3#26b3217
fd 19 
                add iy, de  ; iy += vertex 1 index * 6
#90b5#26b5321
dd 5e 02 
                ld e, (ix + 2)
#90b8#26b8321
dd 56 03 
                ld d, (ix + 3)  ; de = vertex 1 y
#90bb#26bb321
fd 6e 00 
                ld l, (iy)
#90be#26be321
fd 66 01 
                ld h, (iy + 1)  ; hl = vertex 2 x
#90c1#26c1318
cd 5e a1 
                call La15e_de_times_hl_signed  ; (de, hl) = vertex 1 y * vertex 2 x
#90c4#26c4112
d5 
                push de
#90c5#26c5112
e5 
                    push hl
#90c6#26c6321
dd 5e 00 
                        ld e, (ix)
#90c9#26c9321
dd 56 01 
                        ld d, (ix + 1)  ; de = vertex 1 x
#90cc#26cc321
fd 6e 02 
                        ld l, (iy + 2)
#90cf#26cf321
fd 66 03 
                        ld h, (iy + 3)  ; hl = vertex 2 y
#90d2#26d2318
cd 5e a1 
                        call La15e_de_times_hl_signed  ; (de, hl) = vertex 1 x * vertex 2 y
#90d5#26d5111
c1 
                    pop bc
#90d6#26d615
b7 
                    or a
#90d7#26d7217
ed 42 
                    sbc hl, bc  ; (low word) hl = (vertex 1 x * vertex 2 y) - (vertex 1 y * vertex 2 x) (this is done only to get the carry flag for the next operation)
#90d9#26d9111
c1 
                pop bc
#90da#26da15
eb 
                ex de, hl
#90db#26db217
ed 42 
                sbc hl, bc  ; (high word) hl = (vertex 1 x * vertex 2 y) - (vertex 1 y * vertex 2 x)
#90dd#26dd28
2e 01 
                ld l, 1
#90df#26df311
f2 e4 90 
                jp p, L90e4
#90e2#26e228
2e 00 
                ld l, 0
#90e4#26e4
L90e4:
#90e4#26e4216
fd e1 
            pop iy
#90e6#26e6111
c1 
        pop bc
#90e7#26e7314
3a e7 8c 
        ld a, (L8ce7_current_face_normal_check_result)
#90ea#26ea15
bd 
        cp l
#90eb#26eb213/8
20 09 
        jr nz, L90f6
#90ed#26ed212
fd 23 
        inc iy
#90ef#26ef214/9
10 88 
        djnz L9079
#90f1#26f1
        ; Face covers the whole screen!
#90f1#26f128
3e 01 
        ld a, 1
#90f3#26f3314
32 28 5f 
        ld (L5f28_cull_face_when_no_projected_vertices), a
#90f6#26f6
L90f6:
#90f6#26f6111
e1 
    pop hl
#90f7#26f7216
fd e1 
    pop iy
#90f9#26f9216
dd e1 
    pop ix
#90fb#26fb111
c9 
    ret
#90fc#26fc
#90fc#26fc
#90fc#26fc
; --------------------------------
#90fc#26fc
; Projects one vertex from 3d camera coordinates to screen coordinates.
#90fc#26fc
; Input:
#90fc#26fc
; - ix: pointer to a 3d vertex, already transformed, relative to camera view (16 bits per coordinate).
#90fc#26fc
; Output:
#90fc#26fc
; - bc: projected coordinates (c, b) = (x, y)
#90fc#26fc
L90fc_project_one_vertex:
#90fc#26fc112
e5 
    push hl
#90fd#26fd112
d5 
    push de
#90fe#26fe112
f5 
    push af
#90ff#26ff321
dd 56 05 
        ld d, (ix + 5)
#9102#2702321
dd 5e 04 
        ld e, (ix + 4)  ; de = z
#9105#270515
7b 
        ld a, e
#9106#270615
b2 
        or d
#9107#2707213/8
20 05 
        jr nz, L910e_project_x
#9109#2709
        ; If z = 0, just project to the center of the screen.
#9109#2709311
01 60 38 
        ld bc, #3860  ; (96, 56)  (center of the screen).
#910c#270c213
18 62 
        jr L9170_return
#910e#270e
L910e_project_x:
#910e#270e321
dd 66 01 
        ld h, (ix + 1)
#9111#2711321
dd 6e 00 
        ld l, (ix)  ; hl = x
#9114#2714210
cb 7c 
        bit 7, h
#9116#2716213/8
28 0a 
        jr z, L9122
#9118#2718
        ; x is negative:
#9118#2718112
19 
        add hl, de
#9119#271917
2b 
        dec hl  ; hl = x + z - 1
#911a#271a210
cb 7c 
        bit 7, h
#911c#271c213/8
28 0e 
        jr z, L912c
#911e#271e
        ; x + z - 1 is negative
#911e#271e28
0e 00 
        ld c, 0  ; screen x = 0
#9120#2720213
18 1e 
        jr L9140_project_y
#9122#2722
L9122:
#9122#272215
b7 
        or a
#9123#2723217
ed 52 
        sbc hl, de  ; hl = x - z
#9125#2725311
fa 2c 91 
        jp m, L912c
#9128#2728
        ; x - z is negative
#9128#272828
0e c0 
        ld c, SCREEN_WIDTH_IN_PIXELS  ; screen x = 192
#912a#272a213
18 14 
        jr L9140_project_y
#912c#272c
L912c:
#912c#272c28
3e 60 
        ld a, SCREEN_WIDTH_IN_PIXELS / 2
#912e#272e321
dd 66 01 
        ld h, (ix + 1)
#9131#2731321
dd 6e 00 
        ld l, (ix)  ; hl = x
#9134#2734112
d5 
        push de
#9135#2735
            ; OPTIMIZATION: multiplication by 96 can be accelerated with a custom routine.
#9135#2735
            ; (a, hl) = 96 * x
#9135#2735318
cd 08 a1 
            call La108_a_times_hl_signed
#9138#2738
            ; (a, hl) = 96 * x / z
#9138#2738318
cd cc a1 
            call La1cc_a_hl_divided_by_de_signed
#913b#273b111
d1 
        pop de
#913c#273c28
3e 60 
        ld a, SCREEN_WIDTH_IN_PIXELS / 2
#913e#273e15
85 
        add a, l
#913f#273f15
4f 
        ld c, a  ; screen x = 96 * x / z + 96
#9140#2740
L9140_project_y:
#9140#2740321
dd 66 03 
        ld h, (ix + 3)
#9143#2743321
dd 6e 02 
        ld l, (ix + 2)  ; hl = y
#9146#2746210
cb 7c 
        bit 7, h
#9148#2748213/8
28 0a 
        jr z, L9154
#914a#274a
        ; y is negative:
#914a#274a112
19 
        add hl, de
#914b#274b17
2b 
        dec hl  ; hl = y + z - 1
#914c#274c210
cb 7c 
        bit 7, h
#914e#274e213/8
28 0e 
        jr z, L915e
#9150#2750
        ; y + z - 1 is negative
#9150#275028
06 00 
        ld b, 0  ; screen y = 0
#9152#2752213
18 1c 
        jr L9170_return
#9154#2754
L9154:
#9154#275415
b7 
        or a
#9155#2755217
ed 52 
        sbc hl, de  ; hl = y - z
#9157#2757311
fa 5e 91 
        jp m, L915e
#915a#275a
        ; y - z is negative:
#915a#275a28
06 70 
        ld b, SCREEN_HEIGHT_IN_PIXELS  ; screen y = 112  
#915c#275c213
18 12 
        jr L9170_return
#915e#275e
L915e:
#915e#275e28
3e 38 
        ld a, SCREEN_HEIGHT_IN_PIXELS / 2
#9160#2760321
dd 66 03 
        ld h, (ix + 3)
#9163#2763321
dd 6e 02 
        ld l, (ix + 2)  ; hl = y
#9166#2766
        ; OPTIMIZATION: multiplication by 96 can be accelerated with a custom routine.
#9166#2766
        ; (a, hl) = 96 * y
#9166#2766318
cd 08 a1 
        call La108_a_times_hl_signed
#9169#2769
        ; (a, hl) = 96 * y / z
#9169#2769318
cd cc a1 
        call La1cc_a_hl_divided_by_de_signed
#916c#276c28
3e 38 
        ld a, SCREEN_HEIGHT_IN_PIXELS / 2
#916e#276e15
85 
        add a, l
#916f#276f15
47 
        ld b, a  ; screen x = 56 * x / z + 56
#9170#2770
L9170_return:
#9170#2770111
f1 
    pop af
#9171#2771111
d1 
    pop de
#9172#2772111
e1 
    pop hl
#9173#2773111
c9 
    ret
#9174#2774
#9174#2774
#9174#2774
; --------------------------------
#9174#2774
; Auxiliary variables for L9177_rotate_relative_bounding_box
#9174#2774
L9174_24bit_accumulator:
#9174#27743
    db #00, #00, #00
#9177#2777
#9177#2777
#9177#2777
; --------------------------------
#9177#2777
; This method does two things:
#9177#2777
; - Applies the rotation matrix to the first coordinate in (L7499_3d_object_bounding_box_relative_to_player_ptr),
#9177#2777
;   saving it to (L5e9f_3d_vertex_coordinates_after_rotation_matrix).
#9177#2777
; - It then multiplies the width, height, length of the object by each row of the
#9177#2777
;   rotation matrix, and stores the 9 resulting values in (L5e63_3d_vertex_coordinates_relative_to_player).
#9177#2777
; - This method is used for generating vertices for cubes and rectangle objects.
#9177#2777
L9177_rotate_relative_bounding_box:
#9177#2777422
fd 2a 99 74 
    ld iy, (L7499_3d_object_bounding_box_relative_to_player_ptr)  ; These are in 16bit precision.
#917b#277b416
dd 21 55 5e 
    ld ix, L5e55_rotation_matrix
#917f#277f311
21 9f 5e 
    ld hl, L5e9f_3d_vertex_coordinates_after_rotation_matrix
#9182#278228
06 03 
    ld b, 3
#9184#2784
    ; Apply the rotation matrix to the first coordinate in the bounding box, and
#9184#2784
    ; save it in L5e9f_3d_vertex_coordinates_after_rotation_matrix
#9184#2784
L9184_matrix_coordinate_loop:
#9184#2784112
e5 
    push hl
#9185#2785
        ; Multiply the bounding box coordinate 1 by one column of the rotation matrix:
#9185#278515
af 
        xor a
#9186#2786314
32 74 91 
        ld (L9174_24bit_accumulator), a
#9189#2789314
32 75 91 
        ld (L9174_24bit_accumulator + 1), a
#918c#278c314
32 76 91 
        ld (L9174_24bit_accumulator + 2), a
#918f#278f321
dd 7e 00 
        ld a, (ix)  ; get the rotation matrix element
#9192#2792212
dd 23 
        inc ix
#9194#279415
b7 
        or a
#9195#2795213/8
28 0f 
        jr z, L91a6_matrix_cell_is_zero
#9197#2797
        ; L9174_24bit_accumulator = bounding box x * matrix[b][0]
#9197#2797321
fd 6e 00 
        ld l, (iy)
#919a#279a321
fd 66 01 
        ld h, (iy + 1)
#919d#279d318
cd 08 a1 
        call La108_a_times_hl_signed
#91a0#27a0314
32 76 91 
        ld (L9174_24bit_accumulator + 2), a
#91a3#27a3317
22 74 91 
        ld (L9174_24bit_accumulator), hl
#91a6#27a6
L91a6_matrix_cell_is_zero:
#91a6#27a6321
dd 7e 00 
        ld a, (ix)  ; get the next rotation matrix element
#91a9#27a9212
dd 23 
        inc ix
#91ab#27ab15
b7 
        or a
#91ac#27ac213/8
28 19 
        jr z, L91c7_matrix_cell_is_zero
#91ae#27ae
        ; L9174_24bit_accumulator += bounding box z * matrix[b][1]
#91ae#27ae321
fd 6e 04 
        ld l, (iy + 4)
#91b1#27b1321
fd 66 05 
        ld h, (iy + 5)
#91b4#27b4318
cd 08 a1 
        call La108_a_times_hl_signed
#91b7#27b7422
ed 5b 74 91 
        ld de, (L9174_24bit_accumulator)
#91bb#27bb112
19 
        add hl, de
#91bc#27bc317
22 74 91 
        ld (L9174_24bit_accumulator), hl
#91bf#27bf15
5f 
        ld e, a
#91c0#27c0314
3a 76 91 
        ld a, (L9174_24bit_accumulator + 2)
#91c3#27c315
8b 
        adc a, e
#91c4#27c4314
32 76 91 
        ld (L9174_24bit_accumulator + 2), a
#91c7#27c7
L91c7_matrix_cell_is_zero:
#91c7#27c7321
dd 7e 00 
        ld a, (ix)  ; get the next rotation matrix element
#91ca#27ca212
dd 23 
        inc ix
#91cc#27cc15
b7 
        or a
#91cd#27cd213/8
28 19 
        jr z, L91e8_matrix_cell_is_zero
#91cf#27cf
        ; L9174_24bit_accumulator += bounding box y * matrix[b][2]
#91cf#27cf321
fd 6e 08 
        ld l, (iy + 8)
#91d2#27d2321
fd 66 09 
        ld h, (iy + 9)
#91d5#27d5318
cd 08 a1 
        call La108_a_times_hl_signed
#91d8#27d8422
ed 5b 74 91 
        ld de, (L9174_24bit_accumulator)
#91dc#27dc112
19 
        add hl, de
#91dd#27dd317
22 74 91 
        ld (L9174_24bit_accumulator), hl
#91e0#27e015
5f 
        ld e, a
#91e1#27e1314
3a 76 91 
        ld a, (L9174_24bit_accumulator + 2)
#91e4#27e415
8b 
        adc a, e
#91e5#27e5314
32 76 91 
        ld (L9174_24bit_accumulator + 2), a
#91e8#27e8
L91e8_matrix_cell_is_zero:
#91e8#27e8317
2a 74 91 
        ld hl, (L9174_24bit_accumulator)
#91eb#27eb314
3a 76 91 
        ld a, (L9174_24bit_accumulator + 2)
#91ee#27ee112
29 
        add hl, hl
#91ef#27ef15
17 
        rla
#91f0#27f0112
29 
        add hl, hl
#91f1#27f115
17 
        rla
#91f2#27f215
5c 
        ld e, h  ; (a, e) = (a, hl) / 64
#91f3#27f3111
e1 
    pop hl
#91f4#27f418
73 
    ld (hl), e
#91f5#27f517
23 
    inc hl
#91f6#27f618
77 
    ld (hl), a
#91f7#27f717
23 
    inc hl
#91f8#27f8214/9
10 8a 
    djnz L9184_matrix_coordinate_loop
#91fa#27fa
#91fa#27fa
    ; Calculate the 9 terms resulting from multiplying (width, height, length) of the object by
#91fa#27fa
    ; each row of the rotation matrix, and store them in L5e63_3d_vertex_coordinates_relative_to_player (16 bit precision).
#91fa#27fa317
2a 9d 74 
    ld hl, (L749d_object_currently_being_processed_ptr)
#91fd#27fd311
11 04 00 
    ld de, 4
#9200#2800112
19 
    add hl, de
#9201#280115
eb 
    ex de, hl  ; de = points to the (width, height, length) of the object
#9202#2802416
dd 21 55 5e 
    ld ix, L5e55_rotation_matrix
#9206#2806416
fd 21 63 5e 
    ld iy, L5e63_3d_vertex_coordinates_relative_to_player
#920a#280a28
06 03 
    ld b, 3
#920c#280c
    ; 3 iterations, one for width, onr for height, one for length:
#920c#280c
L920c_coordinate_loop:
#920c#280c18
1a 
    ld a, (de)  ; get the w, h or l
#920d#280d17
13 
    inc de
#920e#280e
    ; Multiply by the first row element:
#920e#280e321
dd 6e 00 
    ld l, (ix)
#9211#281115
67 
    ld h, a
#9212#2812318
cd 53 a2 
    call La253_h_times_l_signed
#9215#2815321
fd 75 00 
    ld (iy), l
#9218#2818321
fd 74 01 
    ld (iy + 1), h
#921b#281b212
fd 23 
    inc iy
#921d#281d212
fd 23 
    inc iy
#921f#281f
    ; Multiply by the second row element:
#921f#281f321
dd 6e 03 
    ld l, (ix + 3)
#9222#282215
67 
    ld h, a
#9223#2823318
cd 53 a2 
    call La253_h_times_l_signed
#9226#2826321
fd 75 00 
    ld (iy), l
#9229#2829321
fd 74 01 
    ld (iy + 1), h
#922c#282c212
fd 23 
    inc iy
#922e#282e212
fd 23 
    inc iy
#9230#2830
    ; Multiply by the third row element:
#9230#2830321
dd 6e 06 
    ld l, (ix + 6)
#9233#283315
67 
    ld h, a
#9234#2834318
cd 53 a2 
    call La253_h_times_l_signed
#9237#2837321
fd 75 00 
    ld (iy), l
#923a#283a321
fd 74 01 
    ld (iy + 1), h
#923d#283d212
fd 23 
    inc iy
#923f#283f212
fd 23 
    inc iy
#9241#2841212
dd 23 
    inc ix  ; move to next row of the matrix
#9243#2843214/9
10 c7 
    djnz L920c_coordinate_loop
#9245#2845111
c9 
    ret
#9246#2846
#9246#2846
#9246#2846
; --------------------------------
#9246#2846
; Checks if vertices of an object fall inside of the rendering frustum.
#9246#2846
;
#9246#2846
; Note: in modern 3d engines, the rendering volume is a frustum, but in
#9246#2846
; this engine, this is simplified and they use a pyramid. We could easily
#9246#2846
; turn it to a frustum, changing the 5th test to be a bit a head of the
#9246#2846
; player, rather than exactly at the player. I am still calling it "frustum"
#9246#2846
; for clarity (for those familiar with modern engines).
#9246#2846
; Input:
#9246#2846
; - a: number of vertices.
#9246#2846
; Returns:
#9246#2846
; - z: object is visible, nz: object is not visible.
#9246#2846
; - updates (L5e5e_at_least_one_vertex_outside_rendering_frustum)
#9246#2846
L9246_object_visibility_check:
#9246#2846314
32 96 74 
    ld (L7496_current_drawing_primitive_n_vertices), a
#9249#2849416
fd 21 9f 5e 
    ld iy, L5e9f_3d_vertex_coordinates_after_rotation_matrix
#924d#284d311
11 dc 5e 
    ld de, L5edc_vertex_rendering_frustum_checks
#9250#285015
47 
    ld b, a
#9251#285115
af 
    xor a
#9252#2852314
32 5e 5e 
    ld (L5e5e_at_least_one_vertex_outside_rendering_frustum), a
#9255#285515
4f 
    ld c, a
#9256#2856
L9256_vertex_loop:
#9256#2856112
c5 
    push bc
#9257#2857
        ; This code performs 5 culling checks:
#9257#2857
        ; - assuming the rendering volume is a pyramid (with vertex in the player):
#9257#2857
        ; - 4 checks for the 4 walls of the pyramid
#9257#2857
        ; - a 5th check to see if the object is behind the player
#9257#2857
        ; - 'a' is initialized with 5 bits set to 1, indicating the tests pass.
#9257#2857
        ; - Each time a test fails (vertex is outside the rendering volume), one bit is set to 0.
#9257#285728
3e 1f 
        ld a, #1f
#9259#2859321
fd 4e 04 
        ld c, (iy + 4)
#925c#285c321
fd 46 05 
        ld b, (iy + 5)  ; bc = vertex z
#925f#285f321
fd 6e 00 
        ld l, (iy)
#9262#2862321
fd 66 01 
        ld h, (iy + 1)  ; hl = vertex x
#9265#2865112
e5 
        push hl
#9266#286615
b7 
            or a
#9267#2867217
ed 4a 
            adc hl, bc  ; hl = x + z
#9269#2869311
f2 6e 92 
            jp p, L926e_positive
#926c#286c28
e6 0f 
            and #0f  ; zero out bit 4
#926e#286e
L926e_positive:
#926e#286e321
fd 6e 02 
            ld l, (iy + 2)
#9271#2871321
fd 66 03 
            ld h, (iy + 3)  ; hl = vertex y
#9274#2874112
e5 
            push hl
#9275#287515
b7 
                or a
#9276#2876217
ed 4a 
                adc hl, bc  ; hl = y + z
#9278#2878311
f2 7d 92 
                jp p, L927d_positive
#927b#287b28
e6 17 
                and #17  ; zero out bit 3
#927d#287d
L927d_positive:
#927d#287d111
e1 
            pop hl
#927e#287e15
b7 
            or a
#927f#287f217
ed 42 
            sbc hl, bc  ; hl = y - z
#9281#2881311
fa 86 92 
            jp m, L9286_negative
#9284#288428
e6 1d 
            and #1d  ; zero out bit 2
#9286#2886
L9286_negative:
#9286#2886111
e1 
        pop hl
#9287#288715
b7 
        or a
#9288#2888217
ed 42 
        sbc hl, bc  ; hl = x - z
#928a#288a311
fa 8f 92 
        jp m, L928f_negative
#928d#288d28
e6 1b 
        and #1b  ; zero out bit 1
#928f#288f
L928f_negative:
#928f#288f210
cb 78 
        bit 7, b
#9291#2891213/8
28 02 
        jr z, L9295_z_positive
#9293#2893
        ; vertex behind the camera
#9293#289328
e6 1e 
        and #1e  ; zero out bit 0
#9295#2895
L9295_z_positive:
#9295#2895311
01 06 00 
        ld bc, 6
#9298#2898217
fd 09 
        add iy, bc  ; next vertex
#929a#289a111
c1 
    pop bc
#929b#289b18
12 
    ld (de), a
#929c#289c17
13 
    inc de
#929d#289d28
fe 1f 
    cp #1f
#929f#289f213/8
28 07 
    jr z, L92a8
#92a1#28a1
    ; At least one of the culling tests failed (point is outside the view frustum).
#92a1#28a1
    ; Hence, mark that we will use "L8cf0_project_object_and_add_to_render_list_clipping_internal"
#92a1#28a1
    ; instead of "L8adc_project_object_and_add_to_render_list_internal".
#92a1#28a1
    ; OPTIMIZATION: below, better do ld hl,L5e5e_at_least_one_vertex_outside_rendering_frustum; ld (hl),1
#92a1#28a115
67 
    ld h, a  ; save 'a'
#92a2#28a228
3e 01 
    ld a, 1
#92a4#28a4314
32 5e 5e 
    ld (L5e5e_at_least_one_vertex_outside_rendering_frustum), a
#92a7#28a715
7c 
    ld a, h  ; restore 'a'
#92a8#28a8
L92a8:
#92a8#28a8
    ; - 'c' accumulates the culling checks. 
#92a8#28a8
    ; - 'c' will be #1f if at least one point has passed all the tests, or if collectively,
#92a8#28a8
    ;   each test has been passed by at least one point.
#92a8#28a815
b1 
    or c
#92a9#28a915
4f 
    ld c, a
#92aa#28aa214/9
10 aa 
    djnz L9256_vertex_loop
#92ac#28ac15
79 
    ld a, c
#92ad#28ad28
fe 1f 
    cp #1f
#92af#28af111
c9 
    ret
#92b0#28b0
#92b0#28b0
#92b0#28b0
; --------------------------------
#92b0#28b0
; Projects an object, and if it falls within the screen, add it to the list of objects to draw.
#92b0#28b0
; Input:
#92b0#28b0
; - a: projection type.
#92b0#28b0
;   - if a == 0: indicates that vertices can be projected directly.
#92b0#28b0
;   - if a == 1: we need to call L88d1_normal_direction_check before projection, and if back-face, we cull
#92b0#28b0
;   - if a == 2: we need to call L88d1_normal_direction_check before projection,  we need to use L5f26_alternative_shape_edges_ptr
#92b0#28b0
; - iy: face definition pointer.
#92b0#28b0
L92b0_project_object_and_add_to_render_list:
#92b0#28b0314
32 60 5e 
    ld (L5e60_projection_pre_work_type), a
#92b3#28b3314
3a 5e 5e 
    ld a, (L5e5e_at_least_one_vertex_outside_rendering_frustum)
#92b6#28b615
b7 
    or a
#92b7#28b7213/8
20 05 
    jr nz, L92be
#92b9#28b9
    ; Easy case, all vertices within rendering volume:
#92b9#28b9318
cd dc 8a 
    call L8adc_project_object_and_add_to_render_list_internal
#92bc#28bc213
18 03 
    jr L92c1_continue
#92be#28be
L92be:
#92be#28be
    ; Complex case, not all vertices within rendering volume:
#92be#28be318
cd f0 8c 
    call L8cf0_project_object_and_add_to_render_list_clipping_internal
#92c1#28c1
L92c1_continue:
#92c1#28c1314
3a 5f 5e 
    ld a, (L5e5f_add_to_projected_objects_flag)
#92c4#28c415
b7 
    or a
#92c5#28c5213/8
28 24 
    jr z, L92eb_do_not_draw
#92c7#28c7
    ; This object has to be drawn, add it to the list of projected objects:
#92c7#28c7422
ed 5b 97 74 
    ld de, (L7497_next_projected_vertex_ptr)  ; Get the ptr we just wrote the object to.
#92cb#28cb317
22 97 74 
    ld (L7497_next_projected_vertex_ptr), hl  ; Update the ptr for the next object to just after the current one.
#92ce#28ce317
2a 9b 74 
    ld hl, (L749b_next_object_projected_data_ptr)
#92d1#28d1
    ; Save the pointer to the projected vertex data for this object:
#92d1#28d118
73 
    ld (hl), e
#92d2#28d217
23 
    inc hl
#92d3#28d318
72 
    ld (hl), d
#92d4#28d417
23 
    inc hl
#92d5#28d5
    ; Save the pointer to the relative bounding box for this object:
#92d5#28d5422
ed 5b 99 74 
    ld de, (L7499_3d_object_bounding_box_relative_to_player_ptr)
#92d9#28d918
73 
    ld (hl), e
#92da#28da17
23 
    inc hl
#92db#28db18
72 
    ld (hl), d
#92dc#28dc17
23 
    inc hl
#92dd#28dd317
22 9b 74 
    ld (L749b_next_object_projected_data_ptr), hl
#92e0#28e0
    ; hl = de + 12 (next bounding box ptr)
#92e0#28e0311
21 0c 00 
    ld hl, 12
#92e3#28e3112
19 
    add hl, de
#92e4#28e4317
22 99 74 
    ld (L7499_3d_object_bounding_box_relative_to_player_ptr), hl
#92e7#28e7311
21 6b 74 
    ld hl, L746b_n_objects_to_draw
#92ea#28ea112
34 
    inc (hl)
#92eb#28eb
L92eb_do_not_draw:
#92eb#28eb111
c9 
    ret
#92ec#28ec
#92ec#28ec
#92ec#28ec
; --------------------------------
#92ec#28ec
; Draws a primitive (line, or polygon)
#92ec#28ec
; Input:
#92ec#28ec
; - ix: pointer to the primitive data
#92ec#28ec
; - (L7496_current_drawing_primitive_n_vertices): number of vertices of the primitive.
#92ec#28ec
; Output:
#92ec#28ec
; - ix: ptr to the next primitive.
#92ec#28ec
L92ec_draw_primitive:
#92ec#28ec112
e5 
    push hl
#92ed#28ed112
d5 
    push de
#92ee#28ee112
c5 
    push bc
#92ef#28ef15
08 
    ex af, af'
#92f0#28f0112
f5 
    push af
#92f1#28f115
d9 
    exx
#92f2#28f2112
e5 
    push hl
#92f3#28f3112
d5 
    push de
#92f4#28f4112
c5 
    push bc
#92f5#28f5314
3a 96 74 
        ld a, (L7496_current_drawing_primitive_n_vertices)
#92f8#28f828
fe 02 
        cp 2
#92fa#28fa311
c2 7d 93 
        jp nz, L937d_draw_polygon
#92fd#28fd
        ; Case 1: Draw a line.
#92fd#28fd
        ; Lines are defined by just 4 bytes:
#92fd#28fd321
dd 6e 00 
        ld l, (ix)  ; x1
#9300#2900321
dd 66 01 
        ld h, (ix + 1)  ; y1
#9303#2903321
dd 5e 02 
        ld e, (ix + 2)  ; x2
#9306#2906321
dd 56 03 
        ld d, (ix + 3)  ; y2
#9309#2909311
01 04 00 
        ld bc, 4
#930c#290c217
dd 09 
        add ix, bc  ; move ix to the next primitive.
#930e#290e217
dd e5 
        push ix
#9310#291015
7a 
            ld a, d
#9311#291115
94 
            sub h
#9312#2912213/8
30 03 
            jr nc, L9317_line_drawing_order_set
#9314#2914210
ed 44 
            neg
#9316#291615
eb 
            ex de, hl  ; if the line was to be drawn downwards, swap the points
#9317#2917
L9317_line_drawing_order_set:
#9317#291715
47 
            ld b, a  ; y2 - y1
#9318#291815
7c 
            ld a, h  ; y1
#9319#2919314
32 08 75 
            ld (L7508_current_drawing_row), a
#931c#291c318
cd c4 94 
            call L94c4_calculate_row_ptr_and_texture_ptr
#931f#291f15
78 
            ld a, b
#9320#292015
b7 
            or a
#9321#2921213/8
20 05 
            jr nz, L9328_line_is_not_horizontal
#9323#2923
            ; It is a horizontal line:
#9323#292315
65 
            ld h, l
#9324#292415
53 
            ld d, e
#9325#2925311
c3 b5 94 
            jp L94b5_draw_texture_row_and_return
#9328#2928
L9328_line_is_not_horizontal:
#9328#292815
4d 
            ld c, l  ; starting x pixel
#9329#2929112
c5 
            push bc
#932a#292a15
7c 
                ld a, h
#932b#292b15
92 
                sub d
#932c#292c15
4f 
                ld c, a
#932d#292d28
06 ff 
                ld b, #ff  ; bc = y1 - y2
#932f#292f17
0b 
                dec bc
#9330#293015
7d 
                ld a, l
#9331#293115
93 
                sub e  ; a = x1 - x2
#9332#2932311
11 00 00 
                ld de, 0
#9335#293515
6b 
                ld l, e
#9336#2936213/8
30 01 
                jr nc, L9339
#9338#2938
                ; (de, hl) should be negative
#9338#293817
1b 
                dec de
#9339#2939
L9339:
#9339#293915
67 
                ld h, a  ; hl = (x1 - x2) * 256
#933a#293a
                ; Calculate the line slope:
#933a#293a
                ; - slope = dx * 256 / dy
#933a#293a318
cd b7 b1 
                call Lb1b7_de_hl_divided_by_bc_signed
#933d#293d317
22 04 75 
                ld (L7504_line_drawing_slope), hl
#9340#294028
3e 01 
                ld a, 1
#9342#2942210
cb 7a 
                bit 7, d
#9344#2944213/8
20 02 
                jr nz, L9348_thinning_direction_set
#9346#2946
                ; If the result is negative, mark that we want to thicken the line
#9346#2946
                ; to the left in case the line is to thin to be drawn in any row.
#9346#294628
3e ff 
                ld a, -1
#9348#2948
L9348_thinning_direction_set:
#9348#2948314
32 09 75 
                ld (L7509_line_drawing_thinning_direction), a
#934b#294b111
c1 
            pop bc
#934c#294c15
04 
            inc b  ; number of rows to draw
#934d#294d
            ; Initialize both points (de and hl) to the beginning of the line (bottom):
#934d#294d15
61 
            ld h, c  ; c == starting x pixel
#934e#294e28
2e 00 
            ld l, 0
#9350#295015
54 
            ld d, h
#9351#295115
5d 
            ld e, l
#9352#2952112
c5 
            push bc
#9353#2953
                ; Advance one of the two pixels, so they are offset by 1 vertical pixel.
#9353#2953422
ed 4b 04 75 
                ld bc, (L7504_line_drawing_slope)
#9357#2957112
09 
                add hl, bc
#9358#2958213
18 14 
                jr L936e_line_draw_entry_point
#935a#295a
L935a_line_draw_row_loop:
#935a#295a
            ; Line drawing using fixed-point arithmetic:
#935a#295a
            ; - Keeps two points (one in the current Y position, and one in the previous), and draws
#935a#295a
            ;   horizontal lines at each row to connect them.
#935a#295a112
c5 
            push bc
#935b#295b
                ; Draw pixels from 'd' -> 'h'
#935b