-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathbytestream.s
More file actions
2111 lines (1781 loc) · 54.7 KB
/
Copy pathbytestream.s
File metadata and controls
2111 lines (1781 loc) · 54.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;
; Bytestream module.
; This implement a kind of bytecode based scripting system with simple compact commands.
; This is not definitely not as fast as pure assembler, but it's much more compact
;
#include "params.h"
#include "game_enums.h"
.zero
_gCurrentStream .dsb 2 ; @stream script_command
_gCurrentStreamStop .dsb 1 ; @enum stream_stop_flags (1=Stop 2=Wait 4=Refresh 8=PartialRefresh, OR'd)
_gDelayStream .dsb 2
_gStreamCutScene .dsb 1 ; 1 = In a cut scene @bool
_gStreamSkipPoint .dsb 2 ; Pointer to a label where we can jump if the user presses spaces during a cut scene
_gCurrentItem .dsb 1 ; Used to handle the e_ITEM_CURRENT value, set by DispatchStream @enum item_id
_gStreamItemPtr .dsb 2 ; Used to store the address of an item of interest (gItems+6*item id) @ptr16 item
_gStreamAssociatedItemPtr .dsb 2 ; associated item pointer, needs to be behind _gStreamItemPtr in memory @ptr16 item
_gCurrentAssociatedItem .dsb 1 ; Similar to _gCurrentItem but for containers @enum item_id
_gStreamLocationPtr .dsb 2 ; Used to store the address of a location of interest (gLocations+10*location id) @ptr16 location
_gStreamNextPtr .dsb 2 ; Updated after the functions that prints stuff to know how long the string was
_gStreamReturnPtr .dsb 2 ; The ONE level of subfunction we are allowed to call using GOSUB and RETURN
.text
_ByteStreamCallbacks
.word _ByteStreamCommand_END
.word _ByteStreamCommand_RECTANGLE
.word _ByteStreamCommand_FILL_RECTANGLE
.word _ByteStreamCommand_TEXT
.word _ByteStreamCommand_WHITE_BUBBLE
.word _ByteStreamCommand_BLACK_BUBBLE
.word _ByteStreamCommand_WAIT
.word _ByteStreamCommand_BITMAP
.word _ByteStreamCommand_FADE_BUFFER
.word _ByteStreamCommand_JUMP
.word _ByteStreamCommand_JUMP_IF_TRUE
.word _ByteStreamCommand_JUMP_IF_FALSE
.word _ByteStreamCommand_INFO_MESSAGE
.word _ByteStreamCommand_DISPLAY_IMAGE
.word _ByteStreamCommand_DISPLAY_IMAGE_ONLY
.word _ByteStreamCommand_END_AND_REFRESH
.word _ByteStreamCommand_ERROR_MESSAGE
.word _ByteStreamCommand_SET_ITEM_LOCATION
.word _ByteStreamCommand_SET_ITEM_FLAGS
.word _ByteStreamCommand_UNSET_ITEM_FLAGS
.word _ByteStreamCommand_SET_ITEM_DESCRIPTION
.word _ByteStreamCommand_SET_LOCATION_DIRECTION
.word _ByteStreamCommand_UNLOCK_ACHIEVEMENT
.word _ByteStreamCommand_INCREASE_SCORE
.word _ByteStreamCommand_GAME_OVER
.word _ByteStreamCommand_CLEAR_FULL_TEXT_AREA
.word _ByteStreamCommand_SET_SCENE_IMAGE
.word _ByteStreamCommand_DISPLAY_IMAGE_NOBLIT
.word _ByteStreamCommand_CLEAR_TEXT_AREA
.word _ByteStreamCommand_GOSUB
.word _ByteStreamCommand_RETURN
.word _ByteStreamCommand_DO_ONCE
.word _ByteStreamCommand_SET_CUTSCENE
.word _ByteStreamCommand_PLAY_SOUND
.word _ByteStreamCommand_WAIT_RANDOM
.word _ByteStreamCommand_START_CLOCK
.word _ByteStreamCommand_STOP_CLOCK
.word _ByteStreamCommand_END_AND_PARTIAL_REFRESH
.word _ByteStreamCommand_LOAD_MUSIC
.word _ByteStreamCommand_STOP_MUSIC ; Implemented in akyplayer.s
.word _ByteStreamCommand_WAIT_KEYPRESS ; Implemented in keyboard.s -> Result in _gInputKey
.word _ByteStreamCommand_QUICK_MESSAGE
.word _ByteStreamCommand_SET_SKIP_POINT
.word _ByteStreamCommand_SET_PLAYER_LOCATTION
.word _ByteStreamCommand_SET_CURRENT_ITEM
.word _ByteStreamCommand_COMMAND_CALL_NATIVE
.word _ByteStreamCommand_COMBINE_ITEMS
.word _ByteStreamCommand_KEYPRESS_MESSAGE
.word _ClearBubbleCount ; COMMAND_CLEAR_BUBBLES — alias to the existing helper (no args, returns via rts)
.word _ByteStreamCommand_SET_STRIDE ; COMMAND_SET_STRIDE — sets _gSourceStride for the next BITMAP, auto-reverts to 40
.word _ByteStreamCommand_DISPLAY_IMAGE_NO_CLEAR_TEXT
; Checks if there's a stream delay active.
; If no delay is active, does nothing and returns zero
; If there's a delay, decrements it and returns 1
_DecrementDelayStream
.(
lda _gDelayStream+0 ; Are we executing a WAIT command?
ora _gDelayStream+1
bne decrement
rts ; A contains zero value
decrement
lda _gDelayStream+0 ; Decrement and wait
bne skip
dec _gDelayStream+1
skip
dec _gDelayStream+0
lda #1
rts
.)
; Loop that executes a stream stream.
; Handles the skipable cut scenes as well as timed delays (in numbers of frames)
_HandleByteStream
;jmp _HandleByteStream
.(
jsr _DecrementDelayStream
bne end_stream ; Still decrementing the delay
lda _gCurrentStream+0 ; Do we have a valid stream pointer?
ora _gCurrentStream+1
beq end_stream
lda #<$a000 ; By default, all the draw commands to to the screen
sta _gDrawAddress+0
lda #>$a000
sta _gDrawAddress+1
lda #0 ; And we make sure we are in running mode
sta _gCurrentStreamStop
loop_next_command ; Command loop
jsr _ByteStreamGetNextByte ; Fetch the next command id (increments the pointer, return the value in X and a)
cpx #_COMMAND_COUNT ; Is it a valid command?
bcc valid_command
jsr _Panic ; Probably corrupted stream, definitely not a valid command
valid_command
asl ; Multiply the command id by 2...
tax
lda _ByteStreamCallbacks+0,x ; ...and use it as an index in the command callbak table
sta auto_callback+0
lda _ByteStreamCallbacks+1,x
sta auto_callback+1
auto_callback = *+1
jsr $0000 ; Call the function
lda _gStreamCutScene ; Is the cut scene mode enabled?
beq check_stream_end
delay_stream_loop ; Delay loop
jsr _DecrementDelayStream ; Decrement the delay loop
beq check_stream_end ; Done
jsr _WaitIRQ ; Wait one frame
lda _gStreamSkipPoint+0 ; If we have a skip point and the keyboard is pressed, jump there immediately
ora _gStreamSkipPoint+1 ; This is used to skip the intro sequence when reaching the market place.
beq delay_stream_loop
jsr _ReadKeyNoBounce
beq delay_stream_loop
lda _gStreamSkipPoint+0 ; Jump the stream to the skip point position
sta _gCurrentStream+0
lda _gStreamSkipPoint+1
sta _gCurrentStream+1
lda #0
sta _gStreamSkipPoint+0 ; Clear the skip point
sta _gStreamSkipPoint+1
sta _gDelayStream+0
sta _gDelayStream+1
check_stream_end
lda _gCurrentStreamStop ; Should we contine the execution of commands?
beq loop_next_command ; Can be triggered by END, WAIT, END_AND_REFRESH
end_stream
rts
.)
; _param0=pointer to the new byteStream
_PlayStreamAsm
.(
lda _param0+0
sta _gCurrentStream+0
lda _param0+1
sta _gCurrentStream+1
+_PlayStreamAsm_gCurrentStream
lda #0
sta _gDelayStream
sta _gCurrentStreamStop
sta _gStreamCutScene
loop
jsr _WaitIRQ
jsr _HandleByteStream ; Eventually perform a delay if gDelayStream is set
lda _gCurrentStreamStop
and #FLAG_END_STREAM
bne stream_stop
lda _gCurrentStream+0
bne loop
lda _gCurrentStream+1
bne loop
quit
rts
stream_stop
lda _gCurrentStreamStop
and #FLAG_REFRESH_SCENE
beq check_partial_refresh
jmp _LoadScene
check_partial_refresh
lda _gCurrentStreamStop
and #FLAG_PARTIAL_REFRESH
beq quit
+_RefreshSceneInformation
lda #16+4
sta _param0
jsr _ClearMessageWindowAsm
jmp _PrintSceneInformation
.)
; _param0=id of the element we are searching for in the table
; _param1=pointer to a table with id:stream pointer
;
; Each entry is 3 bytes (id, stream_lo, stream_hi) so the table with 85+ entries is now longer than 255 bytes
_DispatchStream
.(
; Store the item id into "CurrentItem"
lda _param0 ; @enum item_id
sta _gCurrentItem
search_loop
ldy #0
lda (_param1),y ; Check the ID in the table @enum item_id
cmp _param0 ; Does that match the ID we are looking for?
beq matched
cmp #MAPPING_REDIRECT ; Redirect: retry with a different table?
beq redirect
cmp #MAPPING_DEFAULT ; End of table (default handler)?
beq matched
; Advance _param1 to the next entry
clc
lda _param1
adc #3
sta _param1
bcc search_loop
inc _param1+1
bne search_loop ; Always (table never starts in zero page)
matched
iny ; Y=1, _PlayMatchedStream expects Y at the stream-pointer lo byte
jmp _PlayMatchedStream
redirect
; Replace the table pointer with the redirect target and restart the search
iny ; Y=1
lda (_param1),y
pha
iny ; Y=2
lda (_param1),y
sta _param1+1
pla
sta _param1
jmp _DispatchStream
.)
; Dispatches a two-item action by searching a mapping table for a (param0, param2) pair.
; Currently used exclusively by the COMBINE instruction.
;
; _param0=id of the first item
; _param1=pointer to a mapping table [id1, id2, stream_lo, stream_hi, ...] terminated by id2=255
; _param2=id of the second item
;
; The two item IDs are sorted (smaller first) before searching, matching the COMBINE_MAPPING
; macro which auto-sorts its entries the same way. This halves the table size vs storing both
; orderings, and since containers always have the smallest IDs (0..e_ITEM__Last_Container),
; the sorted _param0 is the only one that needs checking for the container fallback.
;
; If a match is found in the table, plays the associated stream.
; If no match is found, checks whether _param0 is a container and _param2 a compatible item.
; If so, interprets as a TAKE command with both items. Otherwise plays the default error stream.
_DispatchStream2
.(
; Store the item id into "CurrentItem" and sort so the smaller ID
; is in _param0, larger in _param2. This matches the COMBINE_MAPPING
; table which auto-sorts entries the same way.
lda _param0
sta _gCurrentItem
cmp _param2
bcc sorted ; _param0 < _param2, already in order
beq sorted ; Equal, no swap needed
ldx _param2 ; Swap: A = _param0 (larger), X = _param2 (smaller)
sta _param2
stx _param0
sorted
ldy #0
search_loop
lda (_param1),y ; Get the first ID from the table
iny
tax ; Copy to X
lda (_param1),y ; Check the second in the table
iny
cmp #MAPPING_DEFAULT ; Is it the end of the table?
beq end_of_table
cpx _param0 ; Does that match the first ID we are looking for?
bne no_match
cmp _param2 ; Does that match the second ID we are looking for?
beq _PlayMatchedStream
no_match
iny ; Skip the stream pointer
iny
jmp search_loop
; Shared by _DispatchStream and _DispatchStream2:
; Reads the stream pointer at (_param1),y and plays it.
+_PlayMatchedStream
lda (_param1),y ; Copy the stream pointer to _param0
sta _param0+0
iny
lda (_param1),y
sta _param0+1
jmp _PlayStreamAsm
end_of_table
; No explicit match found in the table.
; Y currently points at the default error stream pointer.
; Since items are sorted, _param0 has the smaller ID.
; Containers have the smallest IDs, so only _param0 can be a container.
lda _param0+0
cmp #(e_ITEM__Last_Container + 1)
bcs _PlayMatchedStream ; _param0 is NOT a container -> play default error stream
; _param0 might be a container. Check if _param2 needs one.
sty tmp2 ; Preserve Y (points to default error stream)
ldx _param0+0 ; X = potential container ID
lda _param2+0 ; A = potential item ID
jsr _ByteStreamComputeItemPtr ; _gStreamItemPtr -> item[_param2]
ldy #5
lda (_gStreamItemPtr),y ; usable_containers of _param2
pha ; Save A — the LDY below would clobber Z and we still need to test for zero
ldy tmp2 ; Restore Y for _PlayMatchedStream
pla ; A back; PLA sets Z based on popped value
beq _PlayMatchedStream ; _param2 doesn't need a container -> play default error stream
; Match: _param0 = container, _param2 = item needing container
; Set up word buffer as [TAKE, item, container] and hand off to TakeItem
lda _param2+0
sta _gWordBuffer+1 ; Item
lda _param0+0
sta _gWordBuffer+2 ; Container
lda #e_WORD_TAKE
sta _gWordBuffer+0
lda #3
sta _gWordCount
jmp _TakeItem
.)
; Fetch the value in _gCurrentStream, increment the pointer, return the value in X and a
_ByteStreamGetNextByte
.(
ldy #0
lda (_gCurrentStream),y
inc _gCurrentStream+0
bne skip
inc _gCurrentStream+1
skip
tax
rts
.)
_ByteStreamFetchLocationID
.(
lda (_gCurrentStream),y // location ID @enum location_id
cmp #e_LOC_CURRENT
bne keep_location_id
use_current_location_id
lda _gCurrentLocation // Use the current location
keep_location_id
iny
rts
.)
; Fetches the next byte from the stream and if matches e_ITEM_CURRENT replaces by _gCurrentItem
; Then uses the value to compute the _gStreamItemPtr pointer
_ByteStreamFetchItemID
.(
lda (_gCurrentStream),y // item ID @enum item_id
cmp #e_ITEM_CURRENT
bne keep_item_id
use_current_item_id
lda _gCurrentItem // Use the current item
keep_item_id
iny
; fall-through into _ByteStreamComputeItemPtr
; A=Item ID
; Return a pointer on the item in _gStreamItemPtr
; sizeof(item) = 6
; 6 = 4n+2n = n<<2 + n<<1
+_ByteStreamComputeItemPtr
stx tmp1
ldx #0
jsr _ByteStreamComputeItemPtrIndexX
ldx tmp1
rts
+_ByteStreamComputeItemPtrIndexX
// Item ID
sta _gStreamItemPtr+0,x
lda #0
sta _gStreamItemPtr+1,x
// x2
asl _gStreamItemPtr+0,x
rol _gStreamItemPtr+1,x
// x4
lda _gStreamItemPtr+0,x
asl
sta tmp0+0
lda _gStreamItemPtr+1,x
rol
sta tmp0+1
// x6
clc
lda _gStreamItemPtr+0,x
adc tmp0+0
sta _gStreamItemPtr+0,x
lda _gStreamItemPtr+1,x
adc tmp0+1
sta _gStreamItemPtr+1,x
// Item pointer
clc
lda _gStreamItemPtr+0,x
adc #<_gItems
sta _gStreamItemPtr+0,x
lda _gStreamItemPtr+1,x
adc #>_gItems
sta _gStreamItemPtr+1,x
rts
.)
; A=Refernce Item ID
; Return a pointer on the item in _gStreamItemPtr as well as _gStreamAssociatedItemPtr if there's an associated item (or a copy of it if not)
_ByteStreamComputeDualItemPtr
.(
; Fetch the first item
jsr _ByteStreamComputeItemPtr
ldy #3
lda (_gStreamItemPtr),y // gItems->associated_item (+3) = read associated_item id
cmp #255
beq end_associated_item
ldx #2
jsr _ByteStreamComputeItemPtrIndexX
end_associated_item
rts
.)
; A=Location ID
; Return a pointer on the location in _gStreamLocationPtr
; sizeof(location) = 8
; 10 = 8n+2n = n<<3 + n<<1
_ByteStreamComputeLocationPtr
// Location ID
sta _gStreamLocationPtr+0
lda #0
sta _gStreamLocationPtr+1
// x2
asl _gStreamLocationPtr+0
rol _gStreamLocationPtr+1
// x4
asl _gStreamLocationPtr+0
rol _gStreamLocationPtr+1
// x8
asl _gStreamLocationPtr+0
rol _gStreamLocationPtr+1
// Item pointer
clc
lda _gStreamLocationPtr+0
adc #<_gLocations
sta _gStreamLocationPtr+0
lda _gStreamLocationPtr+1
adc #>_gLocations
sta _gStreamLocationPtr+1
rts
_ByteStreamCommand_END
.(
lda #FLAG_END_STREAM ; Stop
+_ByteStreamCommandEndWithStopCode
sta _gCurrentStreamStop
lda #0
sta _gCurrentStream+0
sta _gCurrentStream+1
sta _gDelayStream
rts
.)
; .byt COMMAND_END_AND_REFRESH
_ByteStreamCommand_END_AND_REFRESH
.(
;jmp _ByteStreamCommandEndAndRefresh
lda #FLAG_END_STREAM|FLAG_REFRESH_SCENE ; Stop and refresh scene
bne _ByteStreamCommandEndWithStopCode
.)
; .byt COMMAND_END_AND_PARTIAL_REFRESH
_ByteStreamCommand_END_AND_PARTIAL_REFRESH
.(
;jmp _ByteStreamCommandEndAndRefresh
lda #FLAG_END_STREAM|FLAG_PARTIAL_REFRESH ; Stop and refresh the scene information (ie: we don't run all the image loading)
bne _ByteStreamCommandEndWithStopCode
.)
; .byt COMMAND_WAIT,duration
_ByteStreamCommand_WAIT
.(
; In theory the delay could be 8 or 16 bits based on the value, but the code does not use that at the moment
jsr _ByteStreamGetNextByte
stx _gDelayStream+0
; If we are in a cutscene we don't count WAIT instructions as a way to go back to gameplay
lda _gStreamCutScene
bne end_stream_stop
lda #FLAG_WAIT
sta _gCurrentStreamStop
end_stream_stop
rts
.)
;.byt COMMAND_WAIT_RANDOM,base_duration,rand_mask
_ByteStreamCommand_WAIT_RANDOM
.(
; In theory the delay could be 8 or 16 bits based on the value, but the code does not use that at the moment
jsr _ByteStreamGetNextByte
stx _gDelayStream+0
; Call the internal 32 bit randomize function, result is in _randseedLow+0/+1/+2/+3
jsr _rand32
jsr _ByteStreamGetNextByte
txa
and _randseedLow+0
clc
adc _gDelayStream+0
sta _gDelayStream+0
; If we are in a cutscene we don't count WAIT instructions as a way to go back to gameplay
lda _gStreamCutScene
bne end_stream_stop
lda #FLAG_WAIT
sta _gCurrentStreamStop
end_stream_stop
rts
.)
_ByteStreamCommand_FADE_BUFFER
jmp _BlitBufferToHiresWindow
; We can only use GOSUB once, and then we need to RETURN
; Multiple level of GOSUB are not supported, and calling another stream while we are in a GOSUB will probably cause havok
_ByteStreamCommand_GOSUB
; Store the current pointer+2 into the return register
clc
lda _gCurrentStream+0
adc #2
sta _gStreamReturnPtr+0
lda _gCurrentStream+1
adc #0
sta _gStreamReturnPtr+1
; Then overwrite the current stream pointer with the one provided in the GOSUB instruction parameters
ldy #0
lda (_gCurrentStream),y
tax ; Temporary so we don't change the stream pointer while using it
iny
lda (_gCurrentStream),y
stx _gCurrentStream+0
sta _gCurrentStream+1
rts
; We can only use RETURN if GOSUB has been called before
_ByteStreamCommand_RETURN
lda _gStreamReturnPtr+0
sta _gCurrentStream+0
lda _gStreamReturnPtr+1
sta _gCurrentStream+1
rts
; .byt COMMAND_SET_CUT_SCENE,flag
_ByteStreamCommand_SET_CUTSCENE
.(
ldy #0
lda (_gCurrentStream),y ; Get the cut scene flag
sta _gStreamCutScene
lda #1
jmp _ByteStreamMoveByA
.)
; param0.ptr=registerList;asm("jsr _PlaySoundAsm"); }
; .byt COMMAND_PLAY_SOUND,<sound,>sound
_ByteStreamCommand_PLAY_SOUND
.(
lda _gSoundEnabled
beq no_sound
ldy #0
lda (_gCurrentStream),y ; Get the sound file address (low byte)
sta _param0+0
iny
lda (_gCurrentStream),y ; Get the sound file address (high byte)
sta _param0+1
jsr _PlaySoundAsm
no_sound
lda #2
jmp _ByteStreamMoveByA
.)
; .byt COMMAND_LOAD_MUSIC,musicId
_ByteStreamCommand_LOAD_MUSIC
.(
; unsigned char loaderId = *gCurrentStream++;
jsr _ByteStreamGetNextByte
cpx _gCurrentMusicFileIndex
beq music_already_loaded ; We only load the music if it's not already the one in memory
; Load the requested bitmap
stx _gCurrentMusicFileIndex
stx _LoaderEntryIndex
lda #<_ArkosMusic
sta _LoaderAddressLow
lda #>_ArkosMusic
sta _LoaderAddressHigh
jsr _LoadFileFromDirectory
music_already_loaded
lda _gMusicEnabled
beq no_music
lda #1+2+4+8+16+32 ; All the three channels are used
sta _MusicMixerMask
lda #<_ArkosMusic
sta _param0+0
lda #>_ArkosMusic
sta _param0+1
jmp _StartMusic
no_music
rts
.)
; .byt COMMAND_DO_ONCE,1,<label,>label
_ByteStreamCommand_DO_ONCE
.(
ldy #0
lda (_gCurrentStream),y ; Get the counter
bne not_done
iny
lda (_gCurrentStream),y ; Script address
tax ; Temporary so we don't change the stream pointer while using it
iny
lda (_gCurrentStream),y
stx _gCurrentStream+0
sta _gCurrentStream+1
rts
not_done
tax ; Decrement the counter
dex
txa
sta (_gCurrentStream),y ; Write down the counter
lda #3 ; Skip counter and address
jmp _ByteStreamMoveByA
.)
; .byt COMMAND_SET_SKIP_POINT,<label,>label
_ByteStreamCommand_SET_SKIP_POINT
.(
jsr _ByteStreamGetNextByte
stx _gStreamSkipPoint+0
jsr _ByteStreamGetNextByte
stx _gStreamSkipPoint+1
rts
.)
; .byt COMMAND_CALL_NATIVE,<address,>address
_ByteStreamCommand_COMMAND_CALL_NATIVE
.(
; Fetch the native routine address
jsr _ByteStreamGetNextByte
sta _auto_jmp+0
jsr _ByteStreamGetNextByte
sta _auto_jmp+1
; jmp to it
_auto_jmp = *+1
jmp $1234
.)
; .byt COMMAND_JUMP_IF_TRUE,<label,>label,expression
_ByteStreamCommand_JUMP_IF_TRUE
lda #OPCODE_BEQ // F0
bne common
_ByteStreamCommand_JUMP_IF_FALSE
lda #OPCODE_BNE // D0
common
.(
sta _auto_conditionCheckItemLocation+0
sta _auto_conditionCheckPlayerLocation+0
sta _auto_conditionCheckItemContainer+0
sta _auto_conditionCheckAdressValue+0
eor #OPCODE_BEQ-OPCODE_BNE // 0b100000
sta _auto_conditionCheckItemFlag+0 // Item flags check is inverted
ldy #2
lda (_gCurrentStream),y ; @enum operator_id
bne checkItemFlag
checkItemLocation // OPERATOR_CHECK_ITEM_LOCATION 0
; check = (gItems[itemId].location == locationId);
iny
jsr _ByteStreamFetchItemID
jsr _ByteStreamFetchLocationID // location id
ldy #2
cmp (_gStreamItemPtr),y // gItems->location (+2)
_auto_conditionCheckItemLocation
bne _ByteStreamCommand_JUMP // BNE/BEQ depending of the command
jmp _ByteStreamMoveBy5
checkPlayerLocation // OPERATOR_CHECK_PLAYER_LOCATION 2
; check = (gItems[itemId].location == locationId);
iny
jsr _ByteStreamFetchLocationID // location id
ldy #2
cmp _gCurrentLocation // Player position
_auto_conditionCheckPlayerLocation
bne _ByteStreamCommand_JUMP // BNE/BEQ depending of the command
lda #4
jmp _ByteStreamMoveByA
; .byt COMMAND_JUMP,<label,>label
+_ByteStreamCommand_JUMP
ldy #0
lda (_gCurrentStream),y ; @word @stream script_command
tax ; Temporary so we don't change the stream pointer while using it
iny
lda (_gCurrentStream),y
stx _gCurrentStream+0
sta _gCurrentStream+1
rts
checkItemContainer
; check = (gItems[itemId].flags & flagId);
iny
jsr _ByteStreamFetchItemID
lda (_gCurrentStream),y // container item ID (compared to associated_item) @enum item_id
ldy #3
cmp (_gStreamItemPtr),y // gItems->associated_item (+3)
_auto_conditionCheckItemContainer
bne _ByteStreamCommand_JUMP // BNE/BEQ depending of the command
jmp _ByteStreamMoveBy5
checkItemFlag // OPERATOR_CHECK_ITEM_FLAG 1
cmp #OPERATOR_CHECK_PLAYER_LOCATION
beq checkPlayerLocation
cmp #OPERATOR_CHECK_ITEM_CONTAINER
beq checkItemContainer
cmp #OPERATOR_CHECK_ADDRESS_VALUE
beq checkAdressValue
; check = (gItems[itemId].flags & flagId);
iny
jsr _ByteStreamFetchItemID
lda (_gCurrentStream),y // flag ID @enum item_flags
ldy #4
and (_gStreamItemPtr),y // gItems->flags (+4)
_auto_conditionCheckItemFlag
bne _ByteStreamCommand_JUMP // BNE/BEQ depending of the command
+_ByteStreamMoveBy5 ; Continue (jump not taken)
lda #5
+_ByteStreamMoveByA
clc
adc _gCurrentStream+0
sta _gCurrentStream+0
lda _gCurrentStream+1
adc #0
sta _gCurrentStream+1
rts
checkAdressValue // OPERATOR_CHECK_ADDRESS_VALUE,<address,>address,value
; check = (*address == value);
iny
lda (_gCurrentStream),y // <address
sta _auto_cmp+0
iny
lda (_gCurrentStream),y // >address
sta _auto_cmp+1
iny
lda (_gCurrentStream),y // value
_auto_cmp = *+1
cmp $1234
_auto_conditionCheckAdressValue
bne _ByteStreamCommand_JUMP // BNE/BEQ depending of the command
lda #6
jmp _ByteStreamMoveByA
.)
; .byt COMMAND_SET_PLAYER_LOCATION,location
_ByteStreamCommand_SET_PLAYER_LOCATTION
.(
jsr _ByteStreamGetNextByte
stx _gCurrentLocation
rts
.)
; Can be used to change in a script what the current item is (by default that is set to the item that triggered the script)
; .byt COMMAND_SET_CURRENT_ITEM,item
_ByteStreamCommand_SET_CURRENT_ITEM
.(
jsr _ByteStreamGetNextByte
stx _gCurrentItem
rts
.)
; Can be used to set the location of any item
; .byt COMMAND_SET_ITEM_LOCATION,item,location
_ByteStreamCommand_SET_ITEM_LOCATION
.(
ldy #0
jsr _ByteStreamFetchItemID
lda (_gCurrentStream),y // location id @enum location_id
cmp #e_LOC_CURRENT
bne store_location
lda _gCurrentLocation // Use the current player location
store_location
ldy #2
sta (_gStreamItemPtr),y // gItems->location (+2) = location id
sta tmp2 // Save the location in tmp2 (tmp0 AND tmp1 are modified by _ByteStreamComputeItemPtr)
; If the item is in a container and the location is not the inventory, we need to empty it
cmp #e_LOC_INVENTORY
beq end_container
iny
lda (_gStreamItemPtr),y // gItems->associated_item (+3) = read associated_item id
cmp #255
beq end_container
pha
lda #255
sta (_gStreamItemPtr),y // gItems->associated_item (+3) = clear associated_item id in item
pla
jsr _ByteStreamComputeItemPtr // Get the container pointer
lda #255
sta (_gStreamItemPtr),y // gItems->associated_item (+3) = clear associated_item id in container
iny
lda (_gStreamItemPtr),y // gItems->flags (+4) = read flags
and #ITEM_FLAG_IS_CONTAINER
bne end_container // If the associated object is a container, it stays where it is
ldy #2
lda tmp2 // Reload the saved location so the transported items don't stay in the inventory
sta (_gStreamItemPtr),y // gItems->location (+2) = location id
end_container
lda #2
jmp _ByteStreamMoveByA
.)
; Combines two or three items into a result, intelligently placing it based on input locations
; .byt COMMAND_COMBINE_ITEMS,item1,item2,item3_or_255,result
_ByteStreamCommand_COMBINE_ITEMS
.(
; Start with destination = current location, no container
destinationLocation = tmp2
destinationContainer = tmp3
destinationID = tmp4
lda _gCurrentLocation
sta destinationLocation ; Use current location by default
lda #255
sta destinationContainer ; no container by default
ldy #0
jsr _CombineProcessSourceItem ; First source item
ldy #1
jsr _CombineProcessSourceItem ; Second source item
ldy #2
jsr _CombineProcessSourceItem ; Third source item
ldy #3
lda (_gCurrentStream),y ; Target item ID @enum item_id
sta destinationID
lda destinationContainer ; If one of the source items was in a container, we try to reuse it to store the result...
cmp #255
beq no_container ; ...there was not container to reuse
jsr _ByteStreamComputeItemPtr ; Fetch the container status
ldy #2
lda (_gStreamItemPtr),y ; Location field
cmp #e_LOC_GONE_FOREVER
beq no_container ; ...the container was destroyed
lda destinationID
jsr _ByteStreamComputeItemPtr ; Fetch the destination item status
ldy #5
lda (_gStreamItemPtr),y ; Does that item require a container?
beq no_container ; ...no container needed
; Put the result in a container
ldy #2
lda destinationLocation
sta (_gStreamItemPtr),y ; result->location = destination
iny
lda destinationContainer
sta (_gStreamItemPtr),y ; result->associated_item = container
jsr _ByteStreamComputeItemPtr
lda destinationID
bne done
no_container
lda destinationID
jsr _ByteStreamComputeItemPtr
ldy #2
lda destinationLocation
sta (_gStreamItemPtr),y ; result->location = destination
lda #255
done
ldy #3
sta (_gStreamItemPtr),y ; result->associated_item
lda #4
jmp _ByteStreamMoveByA
; Destructively process a source items from the stream,
; We need to remember the location and eventual container to reuse that in the final result item
_CombineProcessSourceItem
lda (_gCurrentStream),y
cmp #255 ; Valid item ID?
beq end_process_source_item
jsr _ByteStreamComputeDualItemPtr ; _gStreamItemPtr = item, _gStreamAssociatedItemPtr = container
ldy #2
lda (_gStreamItemPtr),y ; item->location
cmp _gCurrentLocation
beq no_location_update
sta destinationLocation ; Update destination to item's location
no_location_update
lda #e_LOC_GONE_FOREVER
sta (_gStreamItemPtr),y ; item->location = gone forever
iny
lda (_gStreamItemPtr),y ; item->associated_item
cmp #255
beq end_process_source_item
sta destinationContainer ; Update destination container
lda #255 ; Clear the container's back-reference to the consumed item
sta (_gStreamAssociatedItemPtr),y ; container->associated_item = 255
end_process_source_item
rts
.)
// .byt COMMAND_SET_ITEM_FLAGS,item,flags
_ByteStreamCommand_SET_ITEM_FLAGS
.(
ldy #0
jsr _ByteStreamFetchItemID
lda (_gCurrentStream),y // flag mask @enum item_flags
ldy #4
ora (_gStreamItemPtr),y // gItems->flags (+4) |= flag mask
sta (_gStreamItemPtr),y // gItems->flags (+4) |= flag mask
lda #2
jmp _ByteStreamMoveByA
.)
; .byt COMMAND_UNSET_ITEM_FLAGS,item,flags