1
;---------------------------------------------------------------------------------------------------------
2
; Source code converted by MSXBAS2ASM - MSX BASIC TO Z80 ASSEMBLY CONVERTER
3
; MSXBAS2ASM developed by Amaury Carvalho, 2019, Brazil
4
; http://launchpad.net/msxbas2asm
5
;---------------------------------------------------------------------------------------------------------
7
;--------------------------------------------------------
8
; MSX BIOS DATA/FUNCTION POINTERS
9
;--------------------------------------------------------
11
;---------------------------------------------------------------------------------------------------------
13
;---------------------------------------------------------------------------------------------------------
15
BIOS_CALBAS: equ 0x0159
16
BIOS_OUTDO: equ 0x0018 ; output to current device (i.e. screen)
17
BIOS_CHPUT: equ 0x00A2
19
BIOS_POSIT: equ 0x00C6
21
BIOS_CHGET: equ 0x009F
22
BIOS_CHSNS: equ 0x009C
23
BIOS_INLIN: equ 0x00B1
24
BIOS_PINLIN: equ 0x00AE
25
BIOS_QINLIN: equ 0x00B4
26
BIOS_GTSTCK: equ 0x00D5
27
BIOS_GTTRIG: equ 0x00D8
28
BIOS_GTPAD: equ 0x00DB
29
BIOS_GTPDL: equ 0x00DE
30
BIOS_DISSCR: equ 0x0041
31
BIOS_ENASCR: equ 0x0044
32
BIOS_CHGMOD: equ 0x005F
33
BIOS_CHGCLR: equ 0x0062
34
BIOS_CLRSPR: equ 0x0069
35
BIOS_INITXT: equ 0x006C ; init text mode 40 columns
36
BIOS_INIT32: equ 0x006F ; init text mode 32 columns
37
BIOS_INIGRP: equ 0x0072
38
BIOS_INIMLT: equ 0x0075
39
BIOS_SETTXT: equ 0x0078 ; set text mode 40 columns
40
BIOS_SETT32: equ 0x007B ; set text mode 32 columns
41
BIOS_SETGRP: equ 0x007E
42
BIOS_SETMLT: equ 0x0081
43
BIOS_CALPAT: equ 0x0084
44
BIOS_CALATR: equ 0x0087
45
BIOS_GSPSIZ: equ 0x008A
46
BIOS_GRPPRT: equ 0x008D
47
BIOS_ERAFNK: equ 0x00CC
48
BIOS_DSPFNK: equ 0x00CF
49
BIOS_TOTEXT: equ 0x00D2
50
BIOS_BREAKX: equ 0x00B7
51
BIOS_ISCNTC: equ 0x03FB
52
BIOS_CHKRAM: equ 0x0000
53
BIOS_GICINI: equ 0x0090
54
BIOS_WRTPSG: equ 0x0093
55
BIOS_REDPSG: equ 0x0096
56
BIOS_STRTMS: equ 0x0099
57
BIOS_KEYINT: equ 0x0038
58
BIOS_CALSLT: equ 0x001C
59
BIOS_ENASLT: equ 0x0024
60
BIOS_RSLREG: equ 0x0138
61
BIOS_SCALXY: equ 0x010E
62
BIOS_MAPXYC: equ 0x0111 ; in BC = X, DE = Y
63
BIOS_READC: equ 0x011D ; out A = color of current pixel
64
BIOS_SETATR: equ 0x011A ; in A = color code
65
BIOS_SETC: equ 0x0120 ; set current pixel to color from SETATR
66
BIOS_NSETCX: equ 0x0123 ; in HL = pixel fill count
67
BIOS_SCANR: equ 0x012C ; in B=Fill switch, DE=Skip count, out DE=Skip remainder, HL=Pixel count
68
BIOS_SCANL: equ 0x012F ; out HL=Pixel count
69
BIOS_FETCHC: equ 0x0114 ; out A = cursor mask, HL = VRAM address of cursor
70
BIOS_STOREC: equ 0x0117 ; in A = cursor mask, HL = VRAM address of cursor
71
BIOS_RESET: equ 0x7D17 ; restart BASIC
72
BIOS_IOALLOC: equ 0X7e6b ; memory setup
74
BIOS_GETVCP: equ 0x0150 ; get PSG voice buffer address (in A = voice number, out HL = address of byte 2)
75
BIOS_GETVC2: equ 0x0153 ; get PSG voice buffer address (VOICEN = voice number, in L = byte number 0-36, out HL = address)
77
BIOS_CHPUT_LF: equ 0x0908
78
BIOS_CHPUT_CR: equ 0x0A81
79
BIOS_CHPUT_TAB: equ 0x0A71
82
BIOS_CHKNEW: equ 0x0165 ; C-flag set if screenmode = 5, 6, 7 or 8
83
BIOS_EXTROM: equ 0x015F
84
BIOS_SCALXY2: equ 0x008D ; in BC = X, DE = Y
85
BIOS_MAPXYC2: equ 0x0091 ; in BC = X, DE = Y
86
BIOS_SETC2: equ 0x009D ; set current pixel to color from SETATR
87
BIOS_READC2: equ 0x0095 ; out A = color of current pixel
88
BIOS_CHGMOD2: equ 0x00D1 ; in A = screenmode
89
BIOS_DOBOXF: equ 0x0079 ; hl = basic text pointer
90
BIOS_GRPPRT2: equ 0x0089 ; a = character
91
BIOS_CHGCLR2: equ 0x0111 ; change color, a = screen mode
92
BIOS_CALPAT2: equ 0x00F9
93
BIOS_CALATR2: equ 0x00FD
94
BIOS_GSPSIZ2: equ 0x0101
95
BIOS_CLRSPR2: equ 0x00F5
97
;---------------------------------------------------------------------------------------------------------
99
;---------------------------------------------------------------------------------------------------------
101
BIOS_VERSION: equ 0x002D ; 0 = MSX1, 1 = MSX2, 2 = MSX2+, 3 = MSXturboR
102
BIOS_FORCLR: equ 0xF3E9
103
BIOS_BAKCLR: equ 0xF3EA
104
BIOS_BDRCLR: equ 0xF3EB
105
BIOS_ATRBYT: equ 0xF3F2
106
BIOS_INTFLG: equ 0xFC9B
107
BIOS_EXPTBL: equ 0xFCC1
108
BIOS_JIFFY: equ 0xFC9E
109
BIOS_BOTTOM: equ 0xFC48
110
BIOS_HIMEM: equ 0xFC4A
111
BIOS_SCRMOD: equ 0xFCAF ; 0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode.
112
BIOS_CLIKSW: equ 0xF3DB ; 0=keyboard click off, 1=keyboard click on
113
BIOS_GRPACX: equ 0xFCB7
114
BIOS_GRPACY: equ 0xFCB9
115
BIOS_DATLIN: equ 0xF6A3 ; 2 - line number of DATA statement read by READ statement
116
BIOS_DATPTR: equ 0xF6C8 ; 2 - address of data read by executing READ statement
117
BIOS_FLGINP: equ 0xF6A6 ; 1 - flag used in INPUT or READ
118
BIOS_TEMP: equ 0xF6A7 ; 2
119
BIOS_TEMP2: equ 0xF6BC ; 2
120
BIOS_TEMP3: equ 0xF69D ; 2
121
BIOS_TEMP8: equ 0xF69F ; 2
122
BIOS_TEMP9: equ 0xF7B8 ; 2
123
BIOS_OLDSCR: equ 0xFCB0 ; screen mode of the last text mode set
124
BIOS_LINL40: equ 0xF3AE ; width for 40 columns screen mode
125
BIOS_LINL32: equ 0xF3AF ; width for 32 columns screen mode
126
BIOS_LINLEN: equ 0xF3B0 ; current width for text screen mode
127
BIOS_CLMLST: equ 0xF3B2 ; minimum number of columns that must still be available on a line for a CRLF
128
BIOS_TXTNAM: equ 0xF3B3 ; characters table name
130
BIOS_VOICEN: equ 0xFB38 ; PSG voice number
131
BIOS_MCLTAB: equ 0xF956
132
BIOS_PRSCNT: equ 0xFB35
133
BIOS_SAVSP: equ 0xFB36
134
BIOS_QUEUEN: equ 0xFB3E
135
BIOS_MUSICF: equ 0xFB3F ;contains 3 bit flags set by the STRTMS. Bits 0, 1 and 2 correspond to VOICAQ, VOICBQ and VOICCQ.
136
BIOS_PLYCNT: equ 0xFB40
138
BIOS_DRWFLG: equ 0xFCBB
139
BIOS_MCLFLG: equ 0xF958
141
BIOS_SLTROM: equ 0xFCC1
142
BIOS_RAMAD0: equ 0xF341 ; Main-RAM Slot (00000h~03FFFh)
143
BIOS_RAMAD1: equ 0xF342 ; Main-RAM Slot (04000h~07FFFh)
144
BIOS_RAMAD2: equ 0xF343 ; Main-RAM Slot (08000h~0BFFFh)
145
BIOS_RAMAD3: equ 0xF344 ; Main-RAM Slot (0C000h~0FFFFh)
149
;--------------------------------------------------------
150
; MSX BASIC DATA/FUNCTION POINTERS
151
;--------------------------------------------------------
153
;---------------------------------------------------------------------------------------------------------
154
; MSX BASIC FUNCTIONS
155
;---------------------------------------------------------------------------------------------------------
157
BASIC_AUTO: equ 0x3973
158
BASIC_AND: equ 0x3A18
159
BASIC_ATTR: equ 0x39FE
160
BASIC_BASE: equ 0x39BE
161
BASIC_BSAVE: equ 0x39CC
162
BASIC_BLOAD: equ 0x39CA
163
BASIC_BEEP: equ 0x39AC
164
BASIC_CALL: equ 0x39C0
165
BASIC_CLOSE: equ 0x3994
166
BASIC_COPY: equ 0x39D8
167
BASIC_CONT: equ 0x395E
168
BASIC_CLEAR: equ 0x3950
169
BASIC_CLOAD: equ 0x3962
170
BASIC_CSAVE: equ 0x3960
171
BASIC_CSRLIN: equ 0x39FC
172
BASIC_CIRCLE: equ 0x39A4
173
BASIC_COLOR: equ 0x39A6
174
BASIC_CLS: equ 0x396A
175
BASIC_CMD: equ 0x39DA
176
BASIC_DELETE: equ 0x397C
177
BASIC_DATA: equ 0x3934
178
BASIC_DIM: equ 0x3938
179
BASIC_DEFSTR: equ 0x3982
180
BASIC_DEFINT: equ 0x3984
181
BASIC_DEFSNG: equ 0x3986
182
BASIC_DEFDBL: equ 0x3988
183
BASIC_DSKO: equ 0x39CE
184
BASIC_DEF: equ 0x395A
185
BASIC_DSKI: equ 0x3A00
186
BASIC_DRAW: equ 0x39A8
187
BASIC_ELSE: equ 0x396E
188
BASIC_END: equ 0x392E
189
BASIC_ERASE: equ 0x3976
190
BASIC_ERROR: equ 0x3978
191
BASIC_ERL: equ 0x39EE
192
BASIC_ERR: equ 0x39F0
193
BASIC_EQU: equ 0x3A1E
194
BASIC_FOR: equ 0x3920
195
BASIC_FIELD: equ 0x398E
196
BASIC_FILES: equ 0x39AA
198
BASIC_GOTO: equ 0x393E
199
BASIC_GOSUB: equ 0x3948
200
BASIC_GET: equ 0x3990
201
BASIC_INPUT: equ 0x3936
203
BASIC_INSTR: equ 0x39F6
204
BASIC_IMP: equ 0x3A20
205
BASIC_INKEY: equ 0x3A04
206
BASIC_IPL: equ 0x39D6
207
BASIC_KILL: equ 0x39D4
208
BASIC_KEY: equ 0x3964
209
BASIC_LPRINT: equ 0x394C
210
BASIC_LLIST: equ 0x3968
211
BASIC_LET: equ 0x393C
212
BASIC_LOCATE: equ 0x39DC
213
BASIC_LINE: equ 0x398A
214
BASIC_LOAD: equ 0x3996
215
BASIC_LSET: equ 0x399C
216
BASIC_LIST: equ 0x3952
217
BASIC_LFILES: equ 0x39A2
218
BASIC_MOTOR: equ 0x39C8
219
BASIC_MERGE: equ 0x3998
220
BASIC_MOD: equ 0x3A22
221
BASIC_MAX: equ 0x39C6
222
BASIC_NEXT: equ 0x3932
223
BASIC_NAME: equ 0x39D2
224
BASIC_NEW: equ 0x3954
225
BASIC_NOT: equ 0x39EC
226
BASIC_OPEN: equ 0x398C
227
BASIC_OUT: equ 0x3964
230
BASIC_OFF: equ 0x3A02
231
BASIC_PRINT: equ 0x394E
232
BASIC_PUT: equ 0x3992
233
BASIC_POKE: equ 0x395C
234
BASIC_PSET: equ 0x39B0
235
BASIC_PRESET: equ 0x39B2
236
BASIC_POINT: equ 0x3A06
237
BASIC_PAINT: equ 0x39AA
238
BASIC_PLAY: equ 0x39AE
239
BASIC_RETURN: equ 0x3948
240
BASIC_READ: equ 0x393A
241
BASIC_RUN: equ 0x3940
242
BASIC_RESTORE:equ 0x3944
243
BASIC_REM: equ 0x394A
244
BASIC_RESUME: equ 0x397A
245
BASIC_RSET: equ 0x399E
246
BASIC_RENUM: equ 0x3980
247
BASIC_SCREEN: equ 0x39B6
248
BASIC_SPRITE: equ 0x39BA
249
BASIC_STOP: equ 0x394C
250
BASIC_SWAP: equ 0x3974
251
BASIC_SET: equ 0x39D0
252
BASIC_SAVE: equ 0x39A0
253
BASIC_SPC: equ 0x39EA
254
BASIC_STEP: equ 0x39E4
255
BASIC_STRING: equ 0x39F2
256
BASIC_SPACE1: equ 0x397E
257
BASIC_SOUND: equ 0x39B4
258
BASIC_THEN: equ 0x39E0
259
BASIC_TRON: equ 0x3970
260
BASIC_TROFF: equ 0x3972
261
BASIC_TAB: equ 0x39E2
263
BASIC_TIME: equ 0x39C2
264
BASIC_USING: equ 0x39F4
265
BASIC_USR: equ 0x39E6
266
BASIC_VARPTR: equ 0x39FA
267
BASIC_VDP: equ 0x39BC
268
BASIC_VPOKE: equ 0x39B8
269
BASIC_WIDTH: equ 0x396C
270
BASIC_WAIT: equ 0x3958
271
BASIC_XOR: equ 0x3A1C
272
BASIC_ABS: equ 0x39E8
273
BASIC_ATN: equ 0x39F8
274
BASIC_ASC: equ 0x3A06
275
BASIC_BIN: equ 0x3A16
276
BASIC_CINT: equ 0x3A18
277
BASIC_CSNG: equ 0x3A1A
278
BASIC_CDBL: equ 0x3A1C
279
BASIC_CVI: equ 0x3A2C
280
BASIC_CVS: equ 0x3A2E
281
BASIC_CVD: equ 0x3A30
282
BASIC_COS: equ 0x39F4
283
BASIC_CHR: equ 0x3A08
284
BASIC_DSKF: equ 0x3A28
285
BASIC_EXP: equ 0x39F2
286
BASIC_EOF: equ 0x3A32
287
BASIC_FRE: equ 0x39FA
288
BASIC_FIX: equ 0x3A1E
289
BASIC_FPOS: equ 0x3A2A
290
BASIC_HEX: equ 0x3A12
291
BASIC_INT: equ 0x39E6
292
BASIC_INP: equ 0x39FC
293
BASIC_LPOS: equ 0x3A14
294
BASIC_LOG: equ 0x39F0
295
BASIC_LOC: equ 0x3A34
296
BASIC_LEN: equ 0x3A00
297
BASIC_LEFT: equ 0x39DE
298
BASIC_LOF: equ 0x3A36
299
BASIC_MKI: equ 0x3A38
300
BASIC_MKS: equ 0x3A3A
301
BASIC_MKD: equ 0x3A3C
302
BASIC_MID: equ 0x39E2
303
BASIC_OCT: equ 0x3A10
304
BASIC_POS: equ 0x39FE
305
BASIC_PEEK: equ 0x3A0A
306
BASIC_PDL: equ 0x3A24
307
BASIC_PAD: equ 0x3A26
308
BASIC_RIGHT: equ 0x39E0
309
BASIC_RND: equ 0x39EC
310
BASIC_SGN: equ 0x39E4
311
BASIC_SQR: equ 0x39EA
312
BASIC_SIN: equ 0x39EE
313
BASIC_STR: equ 0x3A02
314
BASIC_SPACE2: equ 0x3A0E
315
BASIC_STICK: equ 0x3A20
316
BASIC_STRIG: equ 0x3A22
317
BASIC_TAN: equ 0x39F6
318
BASIC_VAL: equ 0x3A04
319
BASIC_VPEEK: equ 0x3A0C
321
BASIC_TRAP_ENABLE: equ 0x631B ; ON INTERVAL/KEY/SPRITE/STOP/STRIG - hl = pointer to trap block
322
BASIC_TRAP_DISABLE: equ 0x632B ; hl = pointer to trap block
323
BASIC_TRAP_ACKNW: equ 0x6358 ; hl, acknowledge trap (handle trap: sts=5? has handler? ackn, pause, run trap, sts=1? unpause)
324
BASIC_TRAP_PAUSE: equ 0x6331 ; hl
325
BASIC_TRAP_UNPAUSE: equ 0x633E ; hl
326
BASIC_TRAP_CLEAR: equ 0x636E
328
BASIC_PLAY_DIRECT: equ 0x744C
329
BASIC_DRAW_DIRECT: equ 0x568C
331
BASIC_READYR: equ 0x409B
332
BASIC_READYC: equ 0x7D17
333
BASIC_FACEVAL: equ 0x4DC7
335
BASIC_ERROR_HANDLER:equ 0x406F
336
BASIC_ERROR_SYNTAX: equ 0x4055
337
BASIC_ERROR_DIVZER: equ 0x4058
338
BASIC_ERROR_OVRFLW: equ 0x4067
339
BASIC_ERROR_ARRAY: equ 0x405E
340
BASIC_ERROR_TYPMIS: equ 0x406D
342
; BASIC ERROR CODES TO BASIC_ERROR_HANDLER
343
; 01 NEXT without FOR 19 Device I/O error
344
; 02 Syntax error 20 Verify error
345
; 03 RETURN without GOSUB 21 No RESUME
346
; 04 Out of DATA 22 RESUME without error
347
; 05 Illegal function call 23 Unprintable error
348
; 06 Overflow 24 Missing operand
349
; 07 Out of memory 25 Line buffer overflow
350
; 08 Undefined line number 50 FIELD overflow
351
; 09 Subscript out of range 51 Internal error
352
; 10 Redimensioned array 52 Bad file number
353
; 11 Division by zero 53 File not found
354
; 12 Illegal direct 54 File already open
355
; 13 Type mismatch 55 Input past end
356
; 14 Out of string space 56 Bad file name
357
; 15 String too long 57 Direct statement in file
358
; 16 String formula too complex 58 Sequential I/O only
359
; 17 Can't CONTINUE 59 File not OPEN
360
; 18 Undefined user function
362
;---------------------------------------------------------------------------------------------------------
363
; MSX BASIC WORK AREAS
364
;---------------------------------------------------------------------------------------------------------
366
BASIC_DAC: equ 0xF7F6 ; 16
367
BASIC_ARG: equ 0xF847 ; 16
368
BASIC_VALTYP: equ 0xF663
369
BASIC_RNDX: equ 0xF857
370
BASIC_BUF: equ 0xF55E ; 259
371
BASIC_KBUF: equ 0xF41F ; 318
372
BASIC_SWPTMP: equ 0xF7BC ; 8
373
BASIC_STRBUF: equ 0xF7C5 ; 43
374
BASIC_TXTTAB: equ 0xF676
375
BASIC_VARTAB: equ 0xF6C2
376
BASIC_ARYTAB: equ 0xF6C4
377
BASIC_STREND: equ 0xF6C6
378
BASIC_STKTOP: equ 0xF674
379
BASIC_FRETOP: equ 0xF69B
380
BASIC_MEMSIZ: equ 0xF672
382
BASIC_TEMPPT: equ 0xF678 ; 2 Starting address of unused area of temporary descriptor.
383
BASIC_TEMPST: equ 0xF67A ; 30 Temporary descriptors.
385
BASIC_DATPTR: equ 0xF6C8 ; 2 Pointer to next data to read from the instruction DATA. Modified by RESTORE.
386
BASIC_DATLIN: equ 0xF6A3 ; 2 Número de linha do comando DATA para o comando READ.
387
BASIC_DORES: equ 0xF664 ; 1 Usada pelo comando DATA para manter o texto no formato ASCII.
388
BASIC_DEFTBL: equ 0xF6CA ; 26 table of variables defined by DEFINT, DEFSTR, DEFSNG and DEFDBL for each alphabet letter (2 = integer, 3 = String, 4 = Simple precision, 8 = Double precision).
390
BASIC_CURLIN: equ 0xF41C ; BASIC current line number
391
BASIC_INTVAL: equ 0xFCA0 ; interval value
392
BASIC_INTCNT: equ 0xFCA2 ; interval current count
394
BASIC_PRMPRV: equ 0xF74C ; Pointer to previous parameter block in PARM1
396
BASIC_TRPTBL: equ 0xFC4C ; 78 trap table - array of 3 bytes - state[1] (bit 0=on, bit 1=stop, bit 2=active) + address[2]
398
BASIC_TRPTBL_KEY: equ 0xFC4C ; 30 ON KEY GOSUB
399
BASIC_TRPTBL_STOP: equ 0xFC6A ; 3 ON STOP GOSUB
400
BASIC_TRPTBL_SPRITE: equ 0xFC6D ; 3 ON SPRITE GOSUB
401
BASIC_TRPTBL_STRIG: equ 0xFC70 ; 15 ON STRIG GOSUB
402
BASIC_TRPTBL_INTERVAL: equ 0xFC7F ; 3 ON INTERVAL GOSUB
403
BASIC_TRPTBL_OTHER: equ 0xFC82 ; 24 reserved for expansion
405
BASIC_ONGSBF: equ 0xFBD8 ; 1 trap occurred counter (0=not occurred)
409
;--------------------------------------------------------
411
;--------------------------------------------------------
413
;--------------------------------------------------------
415
;--------------------------------------------------------
416
COMPILE_TO_ROM: EQU 1
418
MACRO __call_basic,CALL_PARM
423
if defined COMPILE_TO_DOS
425
MACRO __call_bios,CALL_PARM
426
;ld iy,(BIOS_EXPTBL-1)
428
call BIOS_CALBAS ; BIOS_CALSLT
433
MACRO __call_bios,CALL_PARM
440
push hl ; save parameter
444
pop iy ; restore PC of caller
445
pop hl ; get next parameter
446
push iy ; save PC of caller
450
pop iy ; restore PC of caller
451
push hl ; save return parameter
452
push iy ; save PC of caller
456
pop iy ; restore PC of caller
457
push hl ; save return parameter
458
push iy ; save PC of caller
462
MACRO set.line.number, line_number
463
ld bc, line_number ; current line number
464
ld (BASIC_CURLIN), bc
468
ld a, (BIOS_INTFLG) ; verify CTRL+BREAK
474
ld a, (BASIC_ONGSBF) ; trap occured counter
480
;---------------------------------------------------------------------------------------------------------
482
;---------------------------------------------------------------------------------------------------------
484
romSize: equ 0x8000 ; ROM size (32k)
485
pageSize: equ 0x4000 ; Page size (16k)
486
lowLimitSize: equ 0x400 ; 10% of a page size
488
if defined COMPILE_TO_BIN
490
pgmArea: equ 0x8000 ; page 2 - program area
491
ramArea: equ 0xc000 ; page 3 - free RAM start area
493
org pgmArea ; program binary type start address
494
db 0FEh ; binary file ID
495
dw start_pgm ; begin address
496
dw end_file - 1 ; end address
497
dw start_pgm ; program execution address (for ,R option)
500
if defined COMPILE_TO_ROM
502
pgmArea: equ 0x4000 ; page 1 and 2 - program area
503
ramArea: equ 0xc000 ; page 3 - free RAM start area
505
org pgmArea ; program rom type start address
506
db 'AB' ; rom file ID
508
dw 0x0000 ; STATEMENT
515
pgmArea: equ 0x8000 ; page 2 - program area
516
ramArea: equ 0xc000 ; page 3 - free RAM start area
518
org pgmArea ; program DOS type start address ; 0x0100
524
;---------------------------------------------------------------------------------------------------------
526
;---------------------------------------------------------------------------------------------------------
528
if defined COMPILE_TO_ROM
531
call PROGRAM_SLOT_2_RESTORE
532
__call_basic BASIC_READYR ; warm start Basic
537
call PROGRAM_SLOT_GET
538
ld (BIOS_RAMAD2), a ; Save RAM slot of page 8000h-BFFFh
541
PROGRAM_SLOT_2_RESTORE:
544
jp BIOS_ENASLT ; Select the RAM on page 8000h-BFFFh
546
PROGRAM_SLOT_2_ENABLE:
548
call PROGRAM_SLOT_ENABLE_SUB
550
jp BIOS_ENASLT ; Select the ROM on page 8000h-BFFFh
552
PROGRAM_SLOT_1_ENABLE:
556
call PROGRAM_SLOT_ENABLE_SUB
558
jp BIOS_ENASLT ; Select the ROM on page 4000h-7FFFh
560
PROGRAM_SLOT_ENABLE_SUB:
563
and 3 ;Keep bits corresponding to the page
582
; a <- slot ID formatted FxxxSSPP
583
; Modifies: af, bc, de, hl
584
; ref: https://www.msx.org/forum/msx-talk/development/fusion-c-and-htimi#comment-366469
588
jr z,PrimaryShiftContinue
593
PrimaryShiftContinue:
595
jr z,PrimaryShiftDone
610
inc hl ; move to SLTTBL
617
jr z,SecondaryShiftContinue
622
SecondaryShiftContinue:
624
jr nz,SecondaryShiftDone
634
if defined COMPILE_TO_DOS
639
__call_bios BIOS_ENASLT ; Select main ROM on page 0 (0000h~3FFFh)
645
__call_bios BIOS_ENASLT ; Select main ROM on page 1 (4000h~7FFFh)
652
;---------------------------------------------------------------------------------------------------------
654
;---------------------------------------------------------------------------------------------------------
656
start_pgm: ; start of the program
658
if defined COMPILE_TO_DOS
660
call BIOS_SLOT_ENABLE ; enable bios on page 0
661
call BASIC_SLOT_ENABLE ; enable basic on page 1
664
if defined COMPILE_TO_ROM
666
call PROGRAM_SLOT_2_SAVE ; save slot on page 2
667
call PROGRAM_SLOT_2_ENABLE ; enable program on page 2
672
__call_bios BIOS_ERAFNK ; turn off function keys display
673
__call_bios BIOS_GICINI ; initialize sound system
674
__call_bios BIOS_INITXT ; initialize text screen
676
ld (BIOS_CLIKSW), a ; disable keyboard click
678
ld (BASIC_CURLIN), bc ; interpreter in direct mode
679
__call_basic BASIC_TRAP_CLEAR ; clear traps work space
680
;call INITIALIZE_PARAMETERS ; initialize parameters stack
681
call memory.init ; initialize memory allocation
682
call INITIALIZE_VARIABLES ; initialize variables
686
set.line.number 10 ; current line number
687
ld hl, LIT_4 ; parameter
689
call SCREEN ; action call
692
set.line.number 20 ; current line number
693
ld hl, LIT_10 ; parameter
695
call PSET.COLOR ; action call
696
ld hl, LIT_8 ; parameter
698
ld hl, LIT_7 ; parameter
700
call PSET.XY ; action call
701
call PSET ; action call
704
set.line.number 30 ; current line number
705
ld hl, LIT_13 ; parameter
707
call PSET.COLOR ; action call
708
ld hl, LIT_12 ; parameter
710
ld hl, LIT_11 ; parameter
712
call PSET.XY ; action call
713
call PSET ; action call
716
set.line.number 40 ; current line number
717
ld hl, LIT_19 ; parameter
719
call PSET.COLOR ; action call
720
ld hl, LIT_18 ; parameter
722
ld hl, LIT_17 ; parameter
724
ld hl, LIT_16 ; parameter
726
ld hl, LIT_15 ; parameter
728
call PSET.XY ; action call
729
call LINE ; action call
732
set.line.number 50 ; current line number
733
ld hl, LIT_24 ; parameter
735
call PSET.COLOR ; action call
736
ld hl, LIT_23 ; parameter
738
ld hl, LIT_22 ; parameter
740
ld hl, LIT_21 ; parameter
742
ld hl, LIT_20 ; parameter
744
call PSET.XY ; action call
745
call LINE ; action call
748
set.line.number 60 ; current line number
749
ld hl, LIT_29 ; parameter
751
call PSET.COLOR ; action call
752
ld hl, LIT_28 ; parameter
754
ld hl, LIT_27 ; parameter
756
ld hl, LIT_26 ; parameter
758
ld hl, LIT_25 ; parameter
760
call PSET.XY ; action call
761
call LINE ; action call
764
set.line.number 70 ; current line number
765
ld hl, LIT_35 ; parameter
767
call PSET.COLOR ; action call
768
ld hl, LIT_34 ; parameter
770
ld hl, LIT_33 ; parameter
772
ld hl, LIT_32 ; parameter
774
ld hl, LIT_31 ; parameter
776
call PSET.XY ; action call
777
call BOX ; action call
780
set.line.number 80 ; current line number
781
ld hl, LIT_41 ; parameter
783
call PSET.COLOR ; action call
784
ld hl, LIT_40 ; parameter
786
ld hl, LIT_39 ; parameter
788
ld hl, LIT_38 ; parameter
790
ld hl, LIT_37 ; parameter
792
call PSET.XY ; action call
793
call FBOX ; action call
796
set.line.number 90 ; current line number
797
ld hl, LIT_46 ; parameter
799
call PSET.COLOR ; action call
800
ld hl, LIT_45 ; parameter
802
ld hl, LIT_44 ; parameter
804
ld hl, LIT_43 ; parameter
806
ld hl, LIT_42 ; parameter
808
call PSET.XY ; action call
809
call LINE ; action call
812
set.line.number 100 ; current line number
813
ld hl, LIT_51 ; parameter
815
call PSET.COLOR ; action call
816
ld hl, LIT_50 ; parameter
818
ld hl, LIT_49 ; parameter
820
ld hl, LIT_48 ; parameter
822
ld hl, LIT_47 ; parameter
824
call PSET.XY ; action call
825
call LINE ; action call
828
set.line.number 110 ; current line number
829
ld hl, LIT_56 ; parameter
831
call PSET.COLOR ; action call
832
ld hl, LIT_55 ; parameter
834
ld hl, LIT_54 ; parameter
836
ld hl, LIT_53 ; parameter
838
call PSET.XY ; action call
839
call CIRCLE ; action call
842
set.line.number 120 ; current line number
843
ld hl, LIT_62 ; parameter
845
call PAINT.BORDER ; action call
846
ld hl, LIT_60 ; parameter
848
call PSET.COLOR ; action call
849
ld hl, LIT_59 ; parameter
851
ld hl, LIT_58 ; parameter
853
call PSET.XY ; action call
854
call PAINT ; action call
857
set.line.number 130 ; current line number
860
;---------------------------------------------------------------------------------------------------------
862
;---------------------------------------------------------------------------------------------------------
864
end_pgm: __call_bios BIOS_DSPFNK ; turn on function keys display
866
ld (BIOS_CLIKSW), a ; enable keyboard click
868
if defined COMPILE_TO_ROM
871
__call_basic BASIC_READYR ; warm start Basic
874
ret ; end of the program
876
;__call_bios BIOS_GICINI ; initialize sound system
877
;if defined COMPILE_TO_DOS or defined COMPILE_TO_ROM
878
; __call_bios BIOS_RESET ; restart Basic
880
; __call_basic BASIC_END ; end to Basic
884
;---------------------------------------------------------------------------------------------------------
886
;---------------------------------------------------------------------------------------------------------
891
; out IX = variable assigned address
892
pop.parm ; get variable address parameter
893
push hl ; just to transfer hl to ix
895
ld a, (ix) ; get variable type
896
cp 3 ; test if string
897
jr nz, LET.PARM ; if not a string, it isn't necessary to free memory
898
ld a, (ix + 3) ; get variable string length
900
jr z, LET.PARM ; if zero, it isn't necessary to free memory
901
ld c, (ix + 4) ; get old string address low
902
ld b, (ix + 5) ; get old string address high
903
push ix ; save variable address
904
push bc ; just to transfer bc (old string address) to ix
906
call memory.free ; free memory
907
pop ix ; restore variable address
908
LET.PARM: pop.parm ; get data address parameter (out hl = data address)
909
ld a, (ix + 2) ; get variable type flag
910
or a ; cp 0 - test type flag (0=any, 255=fixed)
911
jr nz, LET.FIXED ; if type flag is fixed, so casting is necessary
912
LET.ANY: push ix ; just to transfer ix (variable address) to de
914
ldi ; copy 1 byte from hl (data address) to de (variable address)
915
inc de ; go to variable data area
917
inc hl ; go to data data area
919
ld bc, 8 ; data = 8 bytes
920
ldir ; copy bc bytes from hl (data address) to de (variable address)
921
ld a, (ix) ; get variable type
922
cp 3 ; test if string
923
ret nz ; if not string, return
924
jp LET.STRING ; else do string treatment (in ix = variable address)
925
LET.FIXED: push ix ; save variable destination address
926
push hl ; save variable source address
927
ld a, (ix) ; get variable fixed type, and hl has parameter data address
928
call CAST_TO ; cast data to type (in hl = variable address, a = type to, out hl = casted data address)
930
pop ix ; restore variable address
931
ld a, (ix) ; get variable destination type again
932
cp 3 ; test if string
933
jr nz, LET.VALUE ; if not string, do value treatment
934
ld a, (de) ; get variable source type again
935
cp 3 ; test if string
936
jr nz, LET.FIX1 ; if not string, get casted string size
941
ld (ix + 3), a ; source string size
944
call GET_STR.LENGTH ; get string length (in HL, out B)
946
ld (ix + 3), b ; set variable length
947
LET.FIX2: ld (ix + 4), l ; casted data address low
948
ld (ix + 5), h ; casted data address high
949
jp LET.STRING ; do string treatment (in ix = variable address)
950
LET.VALUE: push ix ; just to transfer ix (variable address) to de
952
inc de ; go to variable data area (and the data from its casted)
955
ld bc, 8 ; data = 8 bytes
956
ldir ; copy bc bytes from hl (data address) to de (variable address)
958
LET.STRING: ld a, (ix + 3) ; string size
959
or a ; cp 0 - test if null
960
jr nz, LET.ALLOC ; if not null, allocate new string (in ix = variable address)
961
ld bc, LIT_NULL_STR ; else, set to a null string literal
962
ld (ix + 4), c ; variable address low
963
ld (ix + 5), b ; variable address high
965
LET.ALLOC: push ix ; save variable address
966
ld l, (ix + 4) ; source string address low
967
ld h, (ix + 5) ; source string address high
968
push hl ; save copy from address
969
ld c, (ix + 3) ; get variable length
971
inc bc ; string length have one more byte from zero terminator
972
push bc ; save variable lenght + 1
973
call memory.alloc ; in bc = size, out ix = address, nz=OK
975
push ix ; just to transfer memory address from ix to de
977
pop bc ; restore bytes to be copied
978
pop hl ; restore copy from string address
979
push de ; save copy to address
980
ldir ; copy bc bytes from hl (data address) to de (variable address)
983
pop de ; restore copy to address
984
pop ix ; restore variable address
985
ld (ix + 4), e ; put memory address low into variable
986
ld (ix + 5), d ; put memory address high into variable
987
ret ; variable assigned
992
pop.parm ; get parameter boolean result in hl
995
ld a, (ix+5) ; put boolean integer result in a
1001
pop.parm ; get first parameter
1003
call GET_INT.VALUE ; output BC with integer value
1004
ld a, c ; A = screen number (0 to 3)
1006
jr c, SCREEN.1 ; if mode < 9, jump
1007
ld a, 8 ; else, fix to 8
1009
if defined EXIST_DATA_SET
1010
call gfxSetScreenMode
1014
jp gfxClearTileScreen
1027
pop.parm ; get first parameter
1029
call GET_INT.VALUE ; output BC with integer value
1030
ld (BIOS_GRPACX), bc ; X
1031
pop.parm ; get second parameter
1033
call GET_INT.VALUE ; output BC with integer value
1034
ld (BIOS_GRPACY), bc ; Y
1040
pop.parm ; get first parameter
1042
call GET_INT.VALUE ; output BC with integer value
1044
call gfxSetForeColor
1050
pop.parm ; get first parameter
1052
call GET_INT.VALUE ; output BC with integer value
1053
ld (GFX_TEMP1), bc ; dX
1054
pop.parm ; get second parameter
1056
call GET_INT.VALUE ; output BC with integer value
1061
ld bc, (GFX_TEMP1) ; dX
1067
pop.parm ; get first parameter
1069
call GET_INT.VALUE ; output BC with integer value
1070
ld (GFX_TEMP1), bc ; dX
1071
pop.parm ; get second parameter
1073
call GET_INT.VALUE ; output BC with integer value
1078
ld bc, (GFX_TEMP1) ; dX
1079
xor a ; a = 0 (framed box)
1085
pop.parm ; get first parameter
1087
call GET_INT.VALUE ; output BC with integer value
1088
ld (GFX_TEMP1), bc ; dX
1089
pop.parm ; get second parameter
1091
call GET_INT.VALUE ; output BC with integer value
1096
ld bc, (GFX_TEMP1) ; dX
1097
or 1 ; a = 1 (filled box)
1103
pop.parm ; get first parameter
1105
call GET_INT.VALUE ; output BC with integer value
1116
xor a ; not symmetric
1122
pop.parm ; get first parameter
1124
call GET_INT.VALUE ; output BC with integer value
1132
; abstract virtual GOTO
1135
;---------------------------------------------------------------------------------------------------------
1136
; MSX BASIC SUPPORT CODE
1137
;---------------------------------------------------------------------------------------------------------
1139
if defined ON_ERROR or defined ON_INTERVAL or defined ON_KEY_START or defined ON_SPRITE or defined ON_STOP or defined ON_STRIG_START or defined TRAP_ENABLED or defined TRAP_DISABLED or defined TRAP_PAUSE or defined TRAP_UNPAUSE
1143
RUN_TRAPS.1: push hl
1154
; in hl = trap block address (handle trap: sts=5? has handler? ackn, pause, run trap, sts=1? unpause)
1156
ld a, (hl) ; trap status
1157
cp 5 ; trap occured AND trap not paused AND trap enabled ?
1158
ret nz ; return if false
1160
ld e, (hl) ; get trap address
1167
ret z ; return if address zero
1169
__call_basic BASIC_TRAP_ACKNW
1170
__call_basic BASIC_TRAP_PAUSE
1171
ld hl, TRAP_HANDLER.1
1172
ld a, (BASIC_ONGSBF) ; save traps execution
1175
ld (BASIC_ONGSBF), a ; disable traps execution
1176
push hl ; next return will be to trap handler
1177
push de ; indirect jump to trap address
1179
TRAP_HANDLER.1: pop af
1180
ld (BASIC_ONGSBF), a ; restore traps execution
1183
cp 1 ; trap enabled?
1185
__call_basic BASIC_TRAP_UNPAUSE
1188
; hl = trap block, de = trap handler
1190
ld (hl), a ; trap block status
1192
ld (hl), e ; trap block handler (pointer)
1199
if defined SET_PLAY_VOICE_1 or defined SET_PLAY_VOICE_2 or defined SET_PLAY_VOICE_3 or defined DO_PLAY or defined MUSIC_PLAY or defined MUSIC_NEXT or defined MUSIC_STOP
1202
ld (BIOS_TEMP), a ; save voice number
1206
ret nz ; return if not string
1209
ld (BIOS_TEMP2), a ; save string size
1210
push hl ; string address
1211
ld a, (BIOS_TEMP) ; restore voice number
1212
call BIOS_GETVCP ; get PSG voice buffer address (in A = voice number, out HL = address of byte 2)
1214
ld a, (BIOS_TEMP2) ; restore string size
1215
ld (hl), a ; string size
1217
ld (hl), e ; string address
1221
ld D,H ; voice stack
1236
ld hl, BIOS_TEMP ; voice count
1250
__call_basic BASIC_PLAY_DIRECT
1257
;---------------------------------------------------------------------------------------------------------
1258
; VARIABLES ROUTINES
1259
;---------------------------------------------------------------------------------------------------------
1261
; input hl = variable address
1262
; input bc = variable name
1263
; input d = variable type
1264
INIT_VAR: ld (hl), d ; variable type
1266
ld (hl), c ; variable name 1
1268
ld (hl), b ; variable name 2
1282
CLEAR.VAR.LOOP: inc hl
1283
ld (hl), 0 ; data address/value
1286
; input HL = variable address
1287
; input A = variable output type
1288
; output HL = casted data address
1298
; input HL = variable address
1299
; output HL = variable address
1300
CAST_TO.INT: ;push af
1305
jp z, CAST_STR_TO.INT
1307
jp z, CAST_SGL_TO.INT
1309
jp z, CAST_DBL_TO.INT
1312
; input HL = variable address
1313
; output HL = variable address
1314
CAST_TO.STR: ;push af
1317
jp z, CAST_INT_TO.STR
1321
jp z, CAST_SGL_TO.STR
1323
jp z, CAST_DBL_TO.STR
1326
; input HL = variable address
1327
; output HL = variable address
1328
CAST_TO.SGL: ;push af
1331
jp z, CAST_INT_TO.SGL
1333
jp z, CAST_STR_TO.SGL
1337
jp z, CAST_DBL_TO.SGL
1340
; input HL = variable address
1341
; output HL = variable address
1342
CAST_TO.DBL: ;push af
1345
jp z, CAST_INT_TO.DBL
1347
jp z, CAST_STR_TO.DBL
1349
jp z, CAST_SGL_TO.DBL
1354
CAST_SGL_TO.STR: ; same as CAST_INT_TO.STR
1355
CAST_DBL_TO.STR: ; same as CAST_INT_TO.STR
1356
CAST_INT_TO.STR: call COPY_TO.DAC
1358
__call_bios MATH_FOUT ; convert DAC to string
1361
CAST_INT_TO.SGL: call COPY_TO.DAC
1362
__call_bios MATH_FRCSGL
1365
CAST_INT_TO.DBL: call COPY_TO.DAC
1366
__call_bios MATH_FRCDBL
1369
CAST_SGL_TO.INT: ; same as CAST_DBL_TO.INT
1370
CAST_DBL_TO.INT: call COPY_TO.DAC
1371
__call_bios MATH_FRCINT
1374
CAST_STR_TO.INT: call CAST_STR_TO.VAL ;
1375
__call_bios MATH_FRCINT ;
1378
CAST_STR_TO.SGL: call CAST_STR_TO.VAL ;
1379
__call_bios MATH_FRCSGL ;
1382
CAST_STR_TO.DBL: call CAST_STR_TO.VAL ;
1383
__call_bios MATH_FRCDBL ;
1386
CAST_STR_TO.VAL: call GET_STR.ADDR ;
1388
__call_bios MATH_FIN ; convert string to a value type
1391
GET_INT.VALUE: inc hl ; output BC with integer value
1397
CAST_SGL_TO.DBL: ; same as GET_DBL.ADDR
1398
CAST_DBL_TO.SGL: ; same as GET_DBL.ADDR
1399
GET_INT.ADDR: ; same as GET_DBL.ADDR
1400
GET_SGL.ADDR: ; same as GET_DBL.ADDR
1401
GET_DBL.ADDR: inc hl
1406
GET_STR.ADDR: push hl
1412
; input hl = string address
1413
; output b = string length
1414
GET_STR.LENGTH: ld b, 0
1415
GET_STR.LEN.NEXT: ld a, (hl)
1422
jr z, GET_STR.LEN.ERR
1424
GET_STR.LEN.ERR: ld b, 0
1426
STRING.COMPARE: ld ix, (BASIC_DAC+1) ; string 1
1427
ld iy, (BASIC_ARG+1) ; string 2
1428
STRING.COMPARE.NX: ld a, (ix) ; next char from string 1
1429
cp (iy) ; char s1 = char s2?
1430
jr nz, STRING.COMPARE.NE ; if not equal...
1432
jr z, STRING.COMPARE.F1 ; if string 1 has finished...
1433
ld a, (iy) ; next char from string 2
1435
jr z, STRING.COMPARE.GT ; if s2 has finished, s1 has not finished yet, so s1 is greater than s2
1438
jr STRING.COMPARE.NX ; get next char pair
1439
STRING.COMPARE.F1: ld a, (iy) ; verify if string 2 has finished too
1441
jr z, STRING.COMPARE.EQ ; if s2 has finished, then they are equals
1442
jr STRING.COMPARE.LT ; else, result = s1 is less than s2
1443
STRING.COMPARE.NE: jr c, STRING.COMPARE.GT ; verify if s1 is greater than s2...
1444
STRING.COMPARE.LT: ld a, 1 ; ...else, result = s1 less than s2
1446
STRING.COMPARE.GT: ld a, 0xFF ; result = s1 is greater than s2
1448
STRING.COMPARE.EQ: xor a ; result = s1 is equal to s2
1450
STRING.CONCAT: ld ix, BASIC_DAC ; s1 size
1451
ld a, (BASIC_ARG) ; s2 size
1452
add a, (ix) ; s3 size = s1 size + s2 size
1456
inc bc ; add 1 byte to size
1457
call memory.alloc ; in bc size, out ix new memory address, nz=OK
1458
jp z, memory.error ;
1462
ld a, (BASIC_DAC) ; s1 size
1463
ld hl, (BASIC_DAC + 1) ; string 1
1464
call COPY_TO.STR ; copy to new memory
1465
ld a, (BASIC_ARG) ; s2 size
1466
ld hl, (BASIC_ARG + 1) ; string 2
1467
call COPY_TO.STR ; copy to new memory
1469
ld (de), a ; null terminated
1472
call COPY_TO.VAR_DUMMY.STR ;
1473
ret.parm ; WARNING - VERIFY STRING MEMORY LEAKs
1474
STRING.PRINT: ld a, (BIOS_SCRMOD) ; 0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode
1476
jr nc, STRING.PRINT.G2 ; jump if graphic screen mode MSX2 (>=5)
1478
jr nc, STRING.PRINT.G1 ; jump if graphic screen mode MSX1 (>=2)
1479
STRING.PRINT.T: ld a, (hl) ; get a char from a string parameter
1480
or a ; cp 0 - is it the string end?
1482
__call_bios BIOS_CHPUT ; put the char (a) into text screen
1484
jr STRING.PRINT.T ; repeat
1485
STRING.PRINT.G1: ld a, (hl) ; get a char from a string parameter
1486
or a ; cp 0 - is it the string end?
1488
__call_bios BIOS_GRPPRT ; put the char (a) into graphical screen
1490
jr STRING.PRINT.G1 ; repeat
1491
STRING.PRINT.G2: ld a, (hl) ; get a char from a string parameter
1492
or a ; cp 0 - is it the string end?
1494
ld ix, BIOS_GRPPRT2 ; put the char (a) into graphical screen
1497
jr STRING.PRINT.G2 ; repeat
1499
; a = string size to copy
1500
; input hl = string from
1501
; input de = string to
1503
ret z ; avoid copy if size = zero
1505
ld c, a ; string size
1506
ldir ; copy bc bytes from hl to de
1508
COPY_TO.BASIC_BUF: ld bc, BASIC_BUF
1509
ld a, (LIT_QUOTE_CHAR)
1512
COPY_BAS_BUF.LOOP: ld a, (hl)
1514
jr z, COPY_BAS_BUF.EXIT
1518
jr COPY_BAS_BUF.LOOP
1519
COPY_BAS_BUF.EXIT: ld a, (LIT_QUOTE_CHAR)
1526
COPY_TO.VAR_DUMMY: ld a, (BASIC_VALTYP) ; create dummy variable from VALTYPE
1528
jr nz, COPY_TO.VAR_DUMMY.DBL
1530
call GET_STR.LENGTH ; get string length
1532
ld a, b ; string length
1533
COPY_TO.VAR_DUMMY.STR: call GET_VAR_DUMMY.ADDR ; create dummy string variable from HL
1534
ld (ix), 3 ; data type string
1536
ld (ix+2), 255 ; var type fixed
1537
ld (ix+3), a ; string length
1538
ld (ix+4), l ; data address low
1539
ld (ix+5), h ; data address high
1540
;call GET_STR.LENGTH ; get string length
1541
;ld (ix+3), b ; string length
1542
push ix ; output var address...
1545
COPY_TO.VAR_DUMMY.INT: call GET_VAR_DUMMY.ADDR ; create dummy integer variable from BC
1546
ld (ix), 2 ; data type string
1557
push ix ; output var address...
1560
COPY_TO.VAR_DUMMY.DBL: call GET_VAR_DUMMY.ADDR ; create dummy value variable from DAC
1561
ld (ix), a ; data type
1566
push ix ; just to copy ix to de
1571
ldir ; copy bc bytes from hl (data address) to de (variable address)
1572
push ix ; output var address...
1575
GET_VAR_DUMMY.ADDR: push af ;
1578
ld ix, (VAR_DUMMY.POINTER) ;
1579
ld a, (VAR_DUMMY.COUNTER) ;
1580
GET_VAR_DUMMY.NEXT: add ix, de ;
1583
jr nz, GET_VAR_DUMMY.EXIT ;
1585
ld ix, VAR_DUMMY.DATA ;
1586
GET_VAR_DUMMY.EXIT: ld (VAR_DUMMY.POINTER), ix ;
1587
ld (VAR_DUMMY.COUNTER), a ;
1588
ld a, (ix) ; get last var dummy type
1589
cp 3 ; is it string?
1590
call z, GET_VAR_DUMMY.FREE ; free string memory
1597
ld l, (ix+4) ; get string data address
1601
call memory.free ; free memory
1605
; input hl = variable address
1606
COPY_TO.DAC: ld de, BASIC_DAC
1607
COPY_TO.DAC.DATA: ld a, (hl)
1608
ld (BASIC_VALTYP), a
1612
ld bc, 8 ; data = 8 bytes
1613
ldir ; copy bc bytes from hl (data address) to de (variable address)
1615
COPY_TO.ARG: ld de, BASIC_ARG ;
1616
jr COPY_TO.DAC.DATA ;
1617
COPY_TO.DAC_ARG: ld hl, BASIC_DAC ;
1619
ld bc, 8 ; data = 8 bytes
1620
ldir ; copy bc bytes from hl (data address) to de (variable address)
1622
COPY_TO.ARG_DAC: ld hl, BASIC_ARG ;
1624
ld bc, 8 ; data = 8 bytes
1625
ldir ; copy bc bytes from hl (data address) to de (variable address)
1627
COPY_TO.DAC_TMP: ld hl, BASIC_DAC ;
1628
ld de, BASIC_SWPTMP ;
1629
ld bc, 8 ; data = 8 bytes
1630
ldir ; copy bc bytes from hl (data address) to de (variable address)
1632
COPY_TO.TMP_DAC: ld hl, BASIC_SWPTMP ;
1634
ld bc, 8 ; data = 8 bytes
1635
ldir ; copy bc bytes from hl (data address) to de (variable address)
1638
exx ; save registers
1641
ld de, BASIC_SWPTMP ;
1642
ldir ; copy bc bytes from hl to de
1646
ldir ; copy bc bytes from hl to de
1648
ld hl, BASIC_SWPTMP ;
1650
ldir ; copy bc bytes from hl to de
1651
exx ; restore registers
1654
CLEAR.DAC: ld de, BASIC_DAC
1655
CLEAR.DAC.DATA: ld hl, BASIC_VALTYP
1658
ld bc, 8 ; data = 8 bytes
1659
ldir ; copy bc bytes from hl (data address) to de (variable address)
1661
CLEAR.ARG: ld de, BASIC_ARG
1666
;---------------------------------------------------------------------------------------------------------
1667
; MATH 16 BITS ROUTINES
1668
;---------------------------------------------------------------------------------------------------------
1670
MATH.PARM.POP: pop af ; get PC from caller stack
1671
ex af, af' ; save PC to temp
1672
pop.parm ; get first parameter
1673
call COPY_TO.ARG ; put HL in ARG (return var type in A)
1674
pop.parm ; get second parameter
1675
ex af, af' ; restore PC from temp
1676
push af ; put again PC from caller in stack
1677
ex af, af' ; restore 1st data type
1678
push af ; save 1st data type
1679
call COPY_TO.DAC ; put HL in DAC (return var type in A)
1680
pop bc ; restore 1st data type (ARG) in B
1681
cp b ; test if data type in A (DAC) = data type in B (ARG)
1682
ret z ; return if is equal data types
1683
MATH.PARM.CAST: push bc ; else cast both to double
1684
and 12 ; test if single/double
1685
jr nz, MATH.PARM.CST1 ; avoid cast if already single/double
1686
__call_bios MATH_FRCDBL ; convert DAC to double
1687
MATH.PARM.CST1: pop af ;
1688
and 12 ; test if single/double
1689
jr nz, MATH.PARM.CST2 ; avoid cast if already single/double
1690
ld (BASIC_VALTYP), a ;
1691
call COPY_TO.DAC_TMP ;
1692
call COPY_TO.ARG_DAC ;
1693
__call_bios MATH_FRCDBL ; convert ARG to double
1694
call COPY_TO.DAC_ARG ;
1695
call COPY_TO.TMP_DAC ;
1696
MATH.PARM.CST2: ld a, 8 ;
1697
ld (BASIC_VALTYP), a ;
1699
MATH.PARM.POP.INT: ; return result in DAC/ARG as integer
1700
pop af ; get PC from caller stack
1701
ex af, af' ; save PC to temp
1702
pop.parm ; get first parameter
1703
ld a, (hl) ; get parameter type
1704
and 2 ; test if integer
1705
jr z, MATH.PARM.POP.I1 ; do cast if not integer
1706
call COPY_TO.ARG ; put HL in ARG (return var type in A)
1707
jr MATH.PARM.POP.I2 ; go to next parameter
1708
MATH.PARM.POP.I1: call COPY_TO.DAC ; put HL in DAC (return var type in A)
1709
__call_bios MATH_FRCINT ; convert DAC to int
1710
call COPY_TO.DAC_ARG ; copy DAC to ARG
1711
MATH.PARM.POP.I2: pop.parm ; get second parameter
1712
call COPY_TO.DAC ; put HL in DAC (return var type in A)
1713
and 2 ; test if integer
1714
jr nz, MATH.PARM.POP.I3 ; avoid cast if already integer
1715
__call_bios MATH_FRCINT ; convert DAC to int
1717
ld (BASIC_VALTYP), a ;
1719
ex af, af' ; restore PC from temp
1720
push af ; put again PC from caller in stack
1722
MATH.PARM.PUSH: call COPY_TO.VAR_DUMMY ;
1728
; output in parm stack
1729
; http://www.z80.info/zip/zaks_book.pdf - page 104
1730
MATH.ADD.INT: ld hl, (BASIC_DAC+2) ;
1731
ld bc, (BASIC_ARG+2) ;
1733
ld (BASIC_DAC+2), hl ;
1738
if defined MATH.SUB or defined MATH.NEG
1741
; output in parm stack
1742
; http://www.z80.info/zip/zaks_book.pdf - page 104
1743
MATH.SUB.INT: ld hl, (BASIC_DAC+2) ;
1744
ld de, (BASIC_ARG+2) ;
1747
ld (BASIC_DAC+2), hl ;
1752
if defined MATH.MULT
1755
; output in parm stack
1756
MATH.MULT.INT: ld hl, (BASIC_DAC+2) ;
1757
ld bc, (BASIC_ARG+2) ;
1759
ld (BASIC_DAC+2), hl ;
1762
; input HL = multiplicand
1763
; input BC = multiplier
1764
; output HL = result
1765
; http://www.z80.info/zip/zaks_book.pdf - page 131
1766
MATH.MULT.16: ld a, c ; low multiplier
1767
ld c, b ; high multiplier
1769
ld d, h ; multiplicand
1772
MULT16LOOP: srl c ; right shift multiplier high
1773
rra ; rotate right multiplier low
1774
jr nc, MULT16NOADD ; test carry
1775
add hl, de ; add multiplicand to result
1776
MULT16NOADD: ex de, hl
1777
add hl, hl ; double - shift multiplicand
1784
if defined MATH.DIV or defined MATH.IDIV or defined MATH.MOD
1786
; input AC = dividend
1787
; input DE = divisor
1788
; output AC = quotient
1789
; output HL = remainder
1790
; http://www.z80.info/zip/zaks_book.pdf - page 140
1791
MATH.DIV.16: ld hl, 0 ; clear accumulator
1792
ld b, 16 ; set counter
1793
DIV16LOOP: rl c ; rotate accumulator result left
1795
adc hl, hl ; left shift
1796
sbc hl, de ; trial subtract divisor
1797
jr nc, $ + 3 ; subtract was OK ($ = current location)
1798
add hl, de ; restore accumulator
1799
ccf ; calculate result bit
1800
djnz DIV16LOOP ; counter not zero
1801
rl c ; shift in last result bit
1807
if defined GFX_FAST or defined LINE
1809
; compare two signed 16 bits integers
1810
; HL < DE: Carry flag
1811
; HL = DE: Zero flag
1812
; http://www.z80.info/zip/zaks_book.pdf - page 531
1813
MATH.COMP.S16: ld a, h ; test high order byte
1814
and 0x80 ; test sign, clear carry
1815
jr nz, MATH.COMP.S16.NEGM1 ; jump if hl is negative
1817
ret nz ; de is negative (and hl is positive)
1819
cp d ; signs are both positive, so normal compare
1821
ld a, l ; test low order byte
1824
MATH.COMP.S16.NEGM1:
1826
rla ; sign bit into carry
1827
ret c ; signs different
1829
cp d ; both signs negative
1839
MATH.ADD.SGL: ld a, 8 ;
1840
ld (BASIC_VALTYP), a ;
1841
MATH.ADD.DBL: __call_bios MATH_DECADD ;
1846
if defined MATH.SUB or defined MATH.NEG
1848
MATH.SUB.SGL: ld a, 8 ;
1849
ld (BASIC_VALTYP), a ;
1850
MATH.SUB.DBL: __call_bios MATH_DECSUB ;
1855
if defined MATH.MULT
1857
MATH.MULT.SGL: ld a, 8 ;
1858
ld (BASIC_VALTYP), a ;
1859
MATH.MULT.DBL: __call_bios MATH_DECMUL ;
1867
; output in parm stack
1868
MATH.DIV.INT: __call_bios MATH_FRCDBL ; convert DAC to double
1871
ld (BASIC_VALTYP), a ;
1872
__call_bios MATH_FRCDBL ; convert ARG to double
1874
MATH.DIV.SGL: ld a, 8 ;
1875
ld (BASIC_VALTYP), a ;
1876
MATH.DIV.DBL: __call_bios MATH_DECDIV ;
1881
if defined MATH.IDIV
1884
; output in parm stack
1885
MATH.IDIV.SGL: ld a, 8 ;
1886
ld (BASIC_VALTYP), a ;
1887
MATH.IDIV.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
1890
ld (BASIC_VALTYP), a ;
1891
__call_bios MATH_FRCINT ; convert ARG to integer
1893
MATH.IDIV.INT: ld hl, (BASIC_DAC+2) ;
1896
ld de, (BASIC_ARG+2) ;
1900
ld (BASIC_DAC+2), hl ; quotient
1907
MATH.POW.INT: ld (BASIC_VALTYP), a ;
1908
__call_bios MATH_FRCDBL ; convert DAC to double
1911
ld (BASIC_VALTYP), a ;
1912
__call_bios MATH_FRCDBL ; convert ARG to double
1914
MATH.POW.SGL: ld a, 8 ;
1915
ld (BASIC_VALTYP), a ;
1916
MATH.POW.DBL: __call_bios MATH_DBLEXP ;
1923
;MATH.MOD.SGL: ld a, 8 ;
1924
; ld (BASIC_VALTYP), a ;
1925
;MATH.MOD.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
1926
; call SWAP.DAC.ARG ;
1928
; ld (BASIC_VALTYP), a ;
1929
; __call_bios MATH_FRCINT ; convert ARG to integer
1930
; call SWAP.DAC.ARG ;
1931
MATH.MOD.INT: ld hl, (BASIC_DAC+2) ;
1934
ld de, (BASIC_ARG+2) ;
1936
ld (BASIC_DAC+2), hl ; remainder
1943
; fast 16-bit integer square root
1944
; http://www.retroprogramming.com/2017/07/a-fast-z80-integer-square-root.html
1945
; 92 bytes, 344-379 cycles (average 362)
1946
; v2 - 3 t-state optimization spotted by Russ McNulty
1947
; call with hl = number to square root
1948
; returns a = square root
2025
if defined RANDOMIZE or defined SEED
2027
MATH.RANDOMIZE: di ;
2028
ld bc, (BIOS_JIFFY) ;
2031
MATH.SEED: ld (BASIC_RNDX), bc ; seed to IRND
2032
push bc ; in bc = new integer seed
2036
ld (BASIC_DAC+2), bc ; copy bc to dac
2037
ld a, 2 ; type integer
2038
ld (BASIC_VALTYP), a ;
2039
__call_bios MATH_FRCDBL ; convert DAC integer to DAC double
2040
__call_bios MATH_NEG ; DAC = -DAC
2041
__call_bios MATH_RND ; put in DAC a new random number from previous DAC parameter
2046
MATH.ERROR: ld e, 13 ; type mismatch
2047
__call_basic BASIC_ERROR_HANDLER ;
2051
;---------------------------------------------------------------------------------------------------------
2053
;---------------------------------------------------------------------------------------------------------
2055
BOOLEAN.RET.TRUE: ld hl, LIT_TRUE ;
2057
BOOLEAN.RET.FALSE: ld hl, LIT_FALSE ;
2059
BOOLEAN.CMP.INT: ld hl, (BASIC_DAC+2) ;
2060
ld de, (BASIC_ARG+2) ;
2061
__call_bios MATH_ICOMP ;
2063
BOOLEAN.CMP.SGL: ld bc, (BASIC_ARG) ;
2064
ld de, (BASIC_ARG+2) ;
2065
__call_bios MATH_DCOMP ;
2067
BOOLEAN.CMP.DBL: __call_bios MATH_XDCOMP ;
2069
BOOLEAN.CMP.STR: call STRING.COMPARE ;
2072
if defined BOOLEAN.GT
2074
BOOLEAN.GT.INT: call BOOLEAN.CMP.INT ;
2076
BOOLEAN.GT.STR: call BOOLEAN.CMP.STR ;
2078
BOOLEAN.GT.SGL: call BOOLEAN.CMP.SGL ;
2080
BOOLEAN.GT.DBL: call BOOLEAN.CMP.DBL ;
2082
BOOLEAN.GT.RET: cp 0x01 ;
2083
jp z, BOOLEAN.RET.TRUE ;
2084
jp BOOLEAN.RET.FALSE ;
2087
if defined BOOLEAN.LT
2089
BOOLEAN.LT.INT: call BOOLEAN.CMP.INT ;
2091
BOOLEAN.LT.STR: call BOOLEAN.CMP.STR ;
2093
BOOLEAN.LT.SGL: call BOOLEAN.CMP.SGL ;
2095
BOOLEAN.LT.DBL: call BOOLEAN.CMP.DBL ;
2097
BOOLEAN.LT.RET: cp 0xFF ;
2098
jp z, BOOLEAN.RET.TRUE ;
2099
jp BOOLEAN.RET.FALSE ;
2103
if defined BOOLEAN.GE
2105
BOOLEAN.GE.INT: call BOOLEAN.CMP.INT ;
2107
BOOLEAN.GE.STR: call BOOLEAN.CMP.STR ;
2109
BOOLEAN.GE.SGL: call BOOLEAN.CMP.SGL ;
2111
BOOLEAN.GE.DBL: call BOOLEAN.CMP.DBL ;
2113
BOOLEAN.GE.RET: cp 0x01 ;
2114
jp z, BOOLEAN.RET.TRUE ;
2116
jp z, BOOLEAN.RET.TRUE ;
2117
jp BOOLEAN.RET.FALSE ;
2121
if defined BOOLEAN.LE
2123
BOOLEAN.LE.INT: call BOOLEAN.CMP.INT ;
2125
BOOLEAN.LE.STR: call BOOLEAN.CMP.STR ;
2127
BOOLEAN.LE.SGL: call BOOLEAN.CMP.SGL ;
2129
BOOLEAN.LE.DBL: call BOOLEAN.CMP.DBL ;
2131
BOOLEAN.LE.RET: cp 0xFF ;
2132
jp z, BOOLEAN.RET.TRUE ;
2134
jp z, BOOLEAN.RET.TRUE ;
2135
jp BOOLEAN.RET.FALSE ;
2139
if defined BOOLEAN.NE
2141
BOOLEAN.NE.INT: call BOOLEAN.CMP.INT ;
2143
BOOLEAN.NE.STR: call BOOLEAN.CMP.STR ;
2145
BOOLEAN.NE.SGL: call BOOLEAN.CMP.SGL ;
2147
BOOLEAN.NE.DBL: call BOOLEAN.CMP.DBL ;
2149
BOOLEAN.NE.RET: or a ; cp 0
2150
jp nz, BOOLEAN.RET.TRUE ;
2151
jp BOOLEAN.RET.FALSE ;
2155
if defined BOOLEAN.EQ
2157
BOOLEAN.EQ.INT: call BOOLEAN.CMP.INT ;
2159
BOOLEAN.EQ.STR: call BOOLEAN.CMP.STR ;
2161
BOOLEAN.EQ.SGL: call BOOLEAN.CMP.SGL ;
2163
BOOLEAN.EQ.DBL: call BOOLEAN.CMP.DBL ;
2165
BOOLEAN.EQ.RET: or a ; cp 0
2166
jp z, BOOLEAN.RET.TRUE ;
2167
jp BOOLEAN.RET.FALSE ;
2171
if defined BOOLEAN.AND
2173
BOOLEAN.AND.INT: ld a, (BASIC_DAC+2) ;
2174
ld hl, BASIC_ARG+2 ;
2176
ld (BASIC_DAC+2), a ;
2178
ld a, (BASIC_DAC+3) ;
2180
ld (BASIC_DAC+3), a ;
2186
if defined BOOLEAN.OR
2188
BOOLEAN.OR.INT: ld a, (BASIC_DAC+2) ;
2189
ld hl, BASIC_ARG+2 ;
2191
ld (BASIC_DAC+2), a ;
2193
ld a, (BASIC_DAC+3) ;
2195
ld (BASIC_DAC+3), a ;
2201
if defined BOOLEAN.XOR
2203
BOOLEAN.XOR.INT: ld a, (BASIC_DAC+2) ;
2204
ld hl, BASIC_ARG+2 ;
2206
ld (BASIC_DAC+2), a ;
2208
ld a, (BASIC_DAC+3) ;
2210
ld (BASIC_DAC+3), a ;
2216
if defined BOOLEAN.EQV
2218
BOOLEAN.EQV.INT: ld a, (BASIC_DAC+2) ;
2219
ld hl, BASIC_ARG+2 ;
2222
ld (BASIC_DAC+2), a ;
2224
ld a, (BASIC_DAC+3) ;
2227
ld (BASIC_DAC+3), a ;
2233
if defined BOOLEAN.IMP
2235
BOOLEAN.IMP.INT: ld a, (BASIC_DAC+2) ;
2236
ld hl, BASIC_ARG+2 ;
2239
ld (BASIC_DAC+2), a ;
2241
ld a, (BASIC_DAC+3) ;
2244
ld (BASIC_DAC+3), a ;
2250
if defined BOOLEAN.SHR
2252
BOOLEAN.SHR.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to right (bits 15...0-->)
2253
ld a, (BASIC_ARG+2) ;
2255
jp z, MATH.PARM.PUSH ; return if not shift
2256
ld b, a ; shift count
2257
BOOLEAN.SHR.INT.N: rr (ix+1) ;
2260
djnz BOOLEAN.SHR.INT.N ; next shift
2262
jp MATH.PARM.PUSH ; return DAC
2266
if defined BOOLEAN.SHL
2268
BOOLEAN.SHL.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to left (<--bits 15...0)
2269
ld a, (BASIC_ARG+2) ;
2271
jp z, MATH.PARM.PUSH ; return if not shift
2272
ld b, a ; shift count
2273
BOOLEAN.SHL.INT.N: rl (ix) ;
2276
djnz BOOLEAN.SHL.INT.N ; next shift
2278
jp MATH.PARM.PUSH ; return DAC
2282
if defined BOOLEAN.NOT
2284
BOOLEAN.NOT.INT: ld a, (BASIC_DAC+2) ;
2286
ld (BASIC_DAC+2), a ;
2287
ld a, (BASIC_DAC+3) ;
2289
ld (BASIC_DAC+3), a ;
2297
;---------------------------------------------------------------------------------------------------------
2298
; MEMORY ALLOCATION ROUTINES
2299
;---------------------------------------------------------------------------------------------------------
2300
; Adapted from memory allocator code by SamSaga2, Spain, 2015
2301
; https://www.msx.org/forum/msx-talk/development/asm-memory-allocator
2302
; https://www.msx.org/users/samsaga2
2303
;---------------------------------------------------------------------------------------------------------
2304
memory.heap_start: equ VAR_STACK.END + 1 ; start at end of variable stack
2305
memory.heap_end: equ 0xF0A0 - 100 ; end at start of work area for stack (100 bytes reserved), BIOS and BASIC interpreter
2306
block.next: equ 0 ; next free block address
2307
block.size: equ 2 ; size of block including header
2308
block: equ 4 ; block.next + block.size
2312
ld ix,memory.heap_start ; first block
2313
ld hl,memory.heap_start+block ; second block
2314
;; first block NEXT=secondblock, SIZE=0
2315
;; with this block we have a fixed start location
2316
;; because never will be allocated
2317
ld (ix+block.next),l
2318
ld (ix+block.next+1),h
2319
ld (ix+block.size),0
2320
ld (ix+block.size+1),0
2321
;; second block NEXT=0, SIZE=all
2322
;; the first and only free block have all available memory
2323
ld (ix+block.next+block),0
2324
ld (ix+block.next+block+1),0
2326
;ld hl,memory.heap_end ; size = @heap_end (stack) - heap_start - block_header * 2 - 100 (buffer for stack)
2329
ld de, memory.heap_start + (block * 2) + 100
2331
;ld de, block * 2 + 100
2333
ld (ix+block.size+block),l
2334
ld (ix+block.size+block+1),h
2338
;; IN BC=size, OUT IX=memptr, NZ=ok
2346
ld ix,memory.heap_start ; this
2349
ld l,(ix+block.size)
2350
ld h,(ix+block.size+1)
2353
jp z, memory.alloc.exactfit
2354
jp c, memory.alloc.nextblock
2355
;; split found block
2356
memory.alloc.splitfit:
2357
;; free space must allow at least two blocks headers (current + next)
2359
jr nz, memory.alloc.splitfit.do ; if free space > 0xFF, do split
2362
jr c, memory.alloc.nextblock ; if free space < 4, skip to next block
2363
memory.alloc.splitfit.do:
2364
;; newfreeblock = this + BC
2368
;; prevblock->next = newfreeblock
2369
ld (iy+block.next),l
2370
ld (iy+block.next+1),h
2371
;; newfreeblock->next = this->next
2373
pop iy ; iy = newfreeblock
2374
ld l,(ix+block.next)
2375
ld h,(ix+block.next+1)
2376
ld (iy+block.next),l
2377
ld (iy+block.next+1),h
2378
;; newfreeblock->size = this->size - BC
2379
ld l,(ix+block.size)
2380
ld h,(ix+block.size+1)
2383
ld (iy+block.size),l
2384
ld (iy+block.size+1),h
2386
ld (ix+block.size),c
2387
ld (ix+block.size+1),b
2389
;; use whole found block
2390
memory.alloc.exactfit:
2391
;; prevblock->next = this->next - remove block from free list
2392
ld l,(ix+block.next)
2393
ld h,(ix+block.next+1)
2394
ld (iy+block.next),l
2395
ld (iy+block.next+1),h
2404
memory.alloc.nextblock:
2405
ld l,(ix+block.next)
2406
ld h,(ix+block.next+1)
2413
;; this = this->next
2416
jp memory.alloc.find
2421
;; HL = IX - block_header_size
2428
ld ix,memory.heap_start
2430
ld e,(ix+block.next)
2431
ld d,(ix+block.next+1)
2434
jp z, memory.free.passedend
2435
sbc hl,de ; test this (HL) against next (DE)
2436
jr c, memory.free.found ; if DE > HL
2437
add hl,de ; restore hl value
2439
pop ix ; current = next
2442
;; ix=prev, hl=this, de=next
2444
add hl,de ; restore hl value
2445
ld (ix+block.next), l
2446
ld (ix+block.next+1), h ; prev->next = this
2449
ld (iy+block.next), e
2450
ld (iy+block.next+1), d ; this->next = next
2451
push ix ; prev x this
2456
call memory.free.coalesce
2457
pop ix ; this x next
2458
jr memory.free.coalesce
2462
memory.free.coalesce:
2463
ld c, (iy+block.size)
2464
ld b, (iy+block.size+1) ; bc = this->size
2468
adc hl, bc ; hl = this + this->size
2472
sbc hl, de ; if this + this->size == next, then this->size += next->size, this->next = next->next
2473
jr z, memory.free.coalesce.do
2474
push ix ; else, new *this = *next
2477
memory.free.coalesce.do:
2478
ld l, (ix+block.size)
2479
ld h, (ix+block.size+1) ; hl = next->size
2481
adc hl, bc ; hl += this->size
2482
ld (iy+block.size), l
2483
ld (iy+block.size+1), h ; this->size = hl
2484
ld l, (ix+block.next)
2485
ld h, (ix+block.next+1) ; hl = next->next
2486
ld (iy+block.next), l
2487
ld (iy+block.next+1), h ; this->next = hl
2490
memory.free.passedend:
2491
;; append block at the end of the free list
2492
ld (ix+block.next),l
2493
ld (ix+block.next+1),h
2496
ld (iy+block.next),0
2497
ld (iy+block.next+1),0
2503
ld ix,memory.heap_start
2505
memory.get_free.count:
2507
add a,(ix+block.size)
2510
adc a,(ix+block.size+1)
2512
ld l,(ix+block.next)
2513
ld h,(ix+block.next+1)
2519
jr memory.get_free.count
2521
memory.error: ld e, 7 ; out of memory
2522
__call_basic BASIC_ERROR_HANDLER ;
2527
;---------------------------------------------------------------------------------------------------------
2529
; By: Amaury Carvalho, 2019
2530
;---------------------------------------------------------------------------------------------------------
2532
; https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#Algorithm_for_integer_arithmetic
2533
; https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
2534
; https://rosettacode.org/wiki/Bitmap/Midpoint_circle_algorithm#C
2535
; https://www.msx.org/wiki/MSX-BASIC_Instructions
2536
;---------------------------------------------------------------------------------------------------------
2538
;---------------------------------------------------------------------------------------------------------
2540
;---------------------------------------------------------------------------------------------------------
2542
BIOS_WRTVDP: EQU 0x0047
2543
BIOS_RDVRM: EQU 0x004A
2544
BIOS_WRTVRM: EQU 0x004D
2545
BIOS_LDIRVM: EQU 0x005C
2546
BIOS_LDIRMV: EQU 0x0059
2547
BIOS_PNTINI: EQU 0x18CF
2548
BIOS_RIGHTC: EQU 0x16C5 ; Move current pixel physical address right
2549
BIOS_TRIGHTC: EQU 0x16AC ; Test then RIGHTC if legal
2550
BIOS_LEFTC: EQU 0x16EE ; Move current pixel physical address left
2551
BIOS_TLEFTC: EQU 0x16D8 ; Test then LEFTC if legal
2552
BIOS_UPC: EQU 0x175D ; Move current pixel physical address up
2553
BIOS_TUPC: EQU 0x173C ; Test then UPC if legal
2554
BIOS_DOWNC: EQU 0x172A ; Move current pixel physical address down
2555
BIOS_TDOWNC: EQU 0x170A ; Test then DOWNC if legal
2556
BIOS_DCOMPR: EQU 0x146A ; compare HL and DE (Flag NC if HL>DE, Flag Z if HL=DE, Flag C if HL<DE)
2557
BIOS_FILVRM: EQU 0x0056 ; fill VRAM with value
2559
BIOS_BIGFIL: EQU 0x016B ; msx 2
2560
BIOS_NRDVRM: EQU 0x0174 ; msx 2
2561
BIOS_NWRVRM: EQU 0x0177 ; msx 2
2562
BIOS_NRDVDP: EQU 0x013E ; msx 2
2563
BIOS_VDPSTA: EQU 0x0131 ; msx 2
2564
BIOS_NWRVDP: EQU 0x012D ; msx 2 (0x0647)
2566
BASIC_SUB_LINE: equ 0x58fc
2567
BASIC_SUB_LINEBOX: equ 0x5912
2568
BASIC_SUB_LINEBOXFILLED: equ 0x58C1
2569
BASIC_SUB_CIRCLE: equ 0x5B19
2570
BASIC_SUB_PAINT1: equ 0x59DA ;0x59C8
2571
BASIC_SUB_PAINT2: equ 0x0069 ;0x2664 ;0x2651+3
2573
;---------------------------------------------------------------------------------------------------------
2575
;---------------------------------------------------------------------------------------------------------
2577
BIOS_RG0SAV: EQU 0xF3DF
2578
BIOS_RG1SAV: EQU 0xF3E0
2579
BIOS_RG8SAV: EQU 0xFFE7
2580
BIOS_BDRATR: EQU 0xFCB2
2581
BIOS_STATFL: EQU 0xF3E7 ; VDP status register
2583
BIOS_CXOFF: EQU 0xF945
2584
BIOS_CYOFF: EQU 0xF947
2585
BIOS_GXPOS: EQU 0xFCB3
2586
BIOS_GYPOS: EQU 0xFCB5
2588
BIOS_GRPNAM: EQU 0xF3C7 ; pattern name table
2589
BIOS_GRPCOL: EQU 0xF3C9 ; colour table
2590
BIOS_GRPCGP: EQU 0xF3CB ; pattern generator table
2591
BIOS_GRPATR: EQU 0xF3CD ; sprite attribute table
2592
BIOS_GRPPAT: EQU 0xF3CF ; sprite generator table
2593
BIOS_CGPNT: EQU 0xF920 ; 2 - current MSX Font location (0x1BBF)
2594
BIOS_ATRBAS: EQU 0xF928 ; sprite attribute table
2596
BIOS_MLTNAM: EQU 0xF3D1 ; pattern name table (screen 3, multicolor)
2597
BIOS_MLTCOL: EQU 0xF3D3 ; colour table (screen 3, multicolor)
2598
BIOS_MLTCGP: EQU 0xF3D5 ; pattern generator table (screen 3, multicolor)
2599
BIOS_MLTATR: EQU 0xF3D7 ; sprite attribute table (screen 3, multicolor)
2600
BIOS_MLTPAT: EQU 0xF3D9 ; sprite generator table (screen 3, multicolor)
2602
BIOS_ASPECT: equ 0xF931 ;2 Aspect ratio of the circle; set by <ratio> of CIRCLE.
2603
BIOS_CENCNT: equ 0xF933 ;2 Counter used by CIRCLE.
2604
BIOS_CLINEF: equ 0xF935 ;1 Flag to draw line to centre, Used set by CIRCLE
2605
BIOS_CNPNTS: equ 0xF936 ;2 Point to be plottted in a 45° segment, Used set by CIRCLE
2606
BIOS_CPLOTF: equ 0xF938 ;1 Plot polarity flag, Used set by CIRCLE
2607
BIOS_CPCNT: equ 0xF939 ;2 Number of points in 1/8 of circle, Used set by CIRCLE.
2608
BIOS_CPCNT8: equ 0xF93B ;2 Number of points in the circle. Used by CIRCLE.
2609
BIOS_CRCSUM: equ 0xF93D ;2 Cyclic redundancy check sum of the circle. Used by CIRCLE.
2610
BIOS_CSTCNT: equ 0xF93F ;2 Variable to maintain the number of points of the starting angle. Used by the instruction CIRCLE
2611
BIOS_CSCLXY: equ 0xF941 ;1 Scale of X & Y. Used by the instruction CIRCLE
2612
BIOS_ASPCT1: equ 0xF40B ;2 256/aspect ratio for Basic instruction CIRCLE.
2613
BIOS_ASPCT2: equ 0xF40D ;2 256*aspect ratio for Basic instruction CIRCLE.
2614
BIOS_MAXUPD: equ 0xF3EC ;3 Work area used by the instruction CIRCLE, contains JP 0000h at start.
2615
BIOS_MINUPD: equ 0xF3EF ;3 Work area used by the instruction CIRCLE, contains JP 0000h at start.
2617
BIOS_PARM1: EQU 0xF6E8 ; 100
2618
BIOS_PARM2: EQU 0xF750 ; 100
2620
GFX_TEMP: EQU BIOS_PARM1 ; 2
2621
GFX_TEMP1: EQU GFX_TEMP + 2 ; 2
2622
GFX_TEMP2: EQU GFX_TEMP1 + 2 ; 2
2623
GFX_TEMP3: EQU GFX_TEMP2 + 2 ; 2
2624
GFX_TEMP4: EQU GFX_TEMP3 + 2 ; 2
2625
GFX_TEMP5: EQU GFX_TEMP4 + 2 ; 2
2626
GFX_TEMP6: EQU GFX_TEMP5 + 2 ; 2
2627
GFX_TEMP7: EQU GFX_TEMP6 + 2 ; 2
2628
GFX_TEMP8: EQU GFX_TEMP7 + 2 ; 2
2629
GFX_TEMP9: EQU GFX_TEMP8 + 2 ; 2
2631
GFX_MAX_X: EQU 0xFCA4 ; 1 (CASSETE LOWLIM)
2632
GFX_MAX_Y: EQU 0xFCA5 ; 1 (CASSETE WINWID)
2634
GFX_SPRITE_FLAGS: EQU 0xF40A ; 1 (CASSETE HEADER) - bits 0=check screen limits, 1=check walls, 2=check hotspots,
2635
; 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
2636
GFX_SPRITE_SIZE_DAT: EQU 0xF3FC ; 1 (CASSETE CS1200)
2637
GFX_SPRITE_SIZE_SCR: EQU 0xF3FD ; 1
2638
GFX_SPRITE_WALLS: EQU 0xF406 ; 2 (CASSETE LOW)
2639
GFX_SPRITE_HOTSPOTS: EQU 0xF408 ; 2 (CASSETE HIGH)
2640
GFX_SPRITE_HOTSPOT_TILE: EQU 0xF405 ; 1 (CASSETE CS2400)
2641
GFX_SPRITE_COLLIDER: EQU 0xF400 ; 1 (CASSETE CS1200)
2642
GFX_SPRITE_COLLISION: EQU 0xF7B5 ; 2 (ARYTA2)
2644
GFX_MUSIC_START: EQU 0xF401 ; 2 (CASSETE CS2400)
2645
GFX_MUSIC_NEXT: EQU 0xF403 ; 2
2646
GFX_MUSIC_PREV: EQU 0xF74C ; 2 (PRMPRV)
2648
GFX_TEMP10: EQU 0xF3FE ; 2 (CASSETE CS1200)
2650
;BIOS_SCR_SIZE_X: dw 240, 256, 256, 64, 256, 256, 512, 512, 256, 512, 256, 256, 256
2651
;BIOS_SCR_SIZE_Y: dw 192, 192, 192, 48, 192, 212, 212, 212, 212, 384, 212, 212, 212
2652
BIOS_SCR_SIZE_X: db 239, 255, 255, 63, 255, 255, 255, 255, 255, 255, 255, 255, 255
2653
BIOS_SCR_SIZE_Y: db 191, 191, 191, 47, 191, 211, 211, 211, 211, 255, 211, 211, 211
2656
;---------------------------------------------------------------------------------------------------------
2657
; gfxIsScreenModeMSX2
2658
; return if screen mode is from MSX 2
2659
; out C is set, if MSX2 and screen mode above 3
2660
;---------------------------------------------------------------------------------------------------------
2662
gfxIsScreenModeMSX2:
2663
ld a, (BIOS_VERSION)
2665
jp nz, BIOS_CHKNEW ; if not MSX1, jump to CHKNEW
2669
;---------------------------------------------------------------------------------------------------------
2671
; set current screen mode
2672
; in A = screen number
2673
;---------------------------------------------------------------------------------------------------------
2677
call gfxInitScreenWorkspace
2679
ld a, (BIOS_VERSION)
2681
jr nz, gfxSetScreenMode.1 ; if not MSX1, jump
2684
call nc, gfxSetScreenMode.0 ; if screen mode >= 4, change to screen 2
2685
__call_bios BIOS_CHGMOD ; change the screen mode (msx1)
2686
jr gfxSetScreenMode.2
2694
ld ix, BIOS_CHGMOD2 ; change the screen mode (msx2)
2698
call gfxGetScreenHeight
2699
call gfxGetScreenWidth
2700
call gfxGetSpriteSize
2701
jp gfxFillSpriteCollisionTable
2703
gfxInitScreenWorkspace:
2705
ld (GFX_SPRITE_FLAGS), a ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
2707
ld (GFX_SPRITE_WALLS), a
2708
ld (GFX_SPRITE_WALLS+1), a
2709
ld (GFX_SPRITE_HOTSPOTS), a
2710
ld (GFX_SPRITE_HOTSPOTS+1), a
2711
ld (GFX_MUSIC_START), a
2712
ld (GFX_MUSIC_START+1), a
2713
ld (GFX_MUSIC_NEXT), a
2714
ld (GFX_MUSIC_NEXT+1), a
2715
ld (GFX_MUSIC_PREV), a
2716
ld (GFX_MUSIC_PREV+1), a
2717
ld (GFX_SPRITE_HOTSPOT_TILE), a
2718
ld (GFX_SPRITE_COLLIDER), a
2721
;---------------------------------------------------------------------------------------------------------
2723
; return current screen mode
2724
; out A = screen number (0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode)
2725
;---------------------------------------------------------------------------------------------------------
2731
;---------------------------------------------------------------------------------------------------------
2733
; set current screen location
2736
;---------------------------------------------------------------------------------------------------------
2739
ld (BIOS_GRPACX), bc ; x
2740
;ld (BIOS_GXPOS), bc
2741
ld (BIOS_GRPACY), de ; y
2742
;ld (BIOS_GYPOS), de
2745
;---------------------------------------------------------------------------------------------------------
2747
; refresh current screen location
2748
;---------------------------------------------------------------------------------------------------------
2751
ld bc, (BIOS_GRPACX) ; x
2752
ld de, (BIOS_GRPACY) ; y
2754
call gfxIsScreenModeMSX2
2755
jr nc, gfxRefreshXY.2 ; if MSX2 and screen mode above 3
2756
__call_bios BIOS_SCALXY ; BC = X, DE = Y
2757
__call_bios BIOS_MAPXYC ; in BC = X, DE = Y
2760
ld ix, BIOS_SCALXY2 ; BC = X, DE = Y
2762
ld ix, BIOS_MAPXYC2 ; in BC = X, DE = Y
2765
;---------------------------------------------------------------------------------------------------------
2767
; get current screen location
2770
;---------------------------------------------------------------------------------------------------------
2773
ld bc, (BIOS_GRPACX) ; x
2774
ld de, (BIOS_GRPACY) ; y
2777
;---------------------------------------------------------------------------------------------------------
2778
; gfxGetScreenHeight
2780
; out a = screen height
2781
;---------------------------------------------------------------------------------------------------------
2786
ld hl, BIOS_SCR_SIZE_Y
2797
;---------------------------------------------------------------------------------------------------------
2800
; out a = screen height
2801
;---------------------------------------------------------------------------------------------------------
2806
ld hl, BIOS_SCR_SIZE_X
2817
;---------------------------------------------------------------------------------------------------------
2819
; get sprite data size
2820
; out a = sprite data size
2821
;---------------------------------------------------------------------------------------------------------
2826
ld a, (BIOS_RG1SAV) ; bit 0 = double size, bit 1 = sprite size (0=8 pixels, 1=16 pixels)
2828
jr z, gfxGetSpriteSize.1
2833
jr z, gfxGetSpriteSize.2
2837
ld (GFX_SPRITE_SIZE_DAT), bc
2843
if defined GFX_FAST and defined PAINT
2845
;---------------------------------------------------------------------------------------------------------
2847
; move screen current location up
2848
; out: carry if off screen
2849
;---------------------------------------------------------------------------------------------------------
2854
ld de, (BIOS_GRPACY)
2859
ld (BIOS_GRPACY), de
2870
;---------------------------------------------------------------------------------------------------------
2872
; move screen current location down
2873
; out: carry if off screen
2874
;---------------------------------------------------------------------------------------------------------
2881
ld de, (BIOS_GRPACY)
2886
ld (BIOS_GRPACY), de
2897
;---------------------------------------------------------------------------------------------------------
2899
; move screen current location left
2900
; out: carry if off screen
2901
;---------------------------------------------------------------------------------------------------------
2906
ld de, (BIOS_GRPACX)
2911
ld (BIOS_GRPACX), de
2922
;---------------------------------------------------------------------------------------------------------
2924
; move screen current location right
2925
; out: carry if off screen
2926
;---------------------------------------------------------------------------------------------------------
2933
ld de, (BIOS_GRPACX)
2938
ld (BIOS_GRPACX), de
2951
;---------------------------------------------------------------------------------------------------------
2953
; push current screen location
2954
;---------------------------------------------------------------------------------------------------------
2959
ld iy, (BIOS_GRPACX) ; x
2961
ld iy, (BIOS_GRPACY) ; y
2967
;---------------------------------------------------------------------------------------------------------
2969
; pop current screen location
2972
;---------------------------------------------------------------------------------------------------------
2981
;---------------------------------------------------------------------------------------------------------
2983
; set current foreground color
2985
;---------------------------------------------------------------------------------------------------------
2988
ld (BIOS_FORCLR), a ; foreground color
2992
;---------------------------------------------------------------------------------------------------------
2994
; get current foreground color
2996
;---------------------------------------------------------------------------------------------------------
2999
ld a, (BIOS_FORCLR) ; foreground color
3002
;---------------------------------------------------------------------------------------------------------
3004
; set current background color
3006
;---------------------------------------------------------------------------------------------------------
3009
ld (BIOS_BAKCLR), a ; foreground color
3012
;---------------------------------------------------------------------------------------------------------
3014
; get current background color
3016
;---------------------------------------------------------------------------------------------------------
3019
ld a, (BIOS_BAKCLR) ; foreground color
3022
;---------------------------------------------------------------------------------------------------------
3024
; set current border color
3026
;---------------------------------------------------------------------------------------------------------
3029
ld (BIOS_BDRCLR), a ; border color
3032
;---------------------------------------------------------------------------------------------------------
3034
; get current border color
3036
;---------------------------------------------------------------------------------------------------------
3039
ld a, (BIOS_BDRCLR) ; border color
3042
;---------------------------------------------------------------------------------------------------------
3044
; set fill border color
3046
;---------------------------------------------------------------------------------------------------------
3049
ld (BIOS_BDRATR), a ; border color
3052
;---------------------------------------------------------------------------------------------------------
3054
; get fill border color
3056
;---------------------------------------------------------------------------------------------------------
3059
ld a, (BIOS_BDRATR) ; border color
3062
;---------------------------------------------------------------------------------------------------------
3064
; set current color (foreground, background and border)
3065
;---------------------------------------------------------------------------------------------------------
3070
jp nz, BIOS_CHGCLR2 ; change VDP colors - msx2
3072
jp nz, BIOS_CHGCLR2 ; change VDP colors - msx2
3073
jp BIOS_CHGCLR ; change VDP colors
3074
; __call_bios BIOS_SETATR ; change the pixel color
3077
;---------------------------------------------------------------------------------------------------------
3079
; set pixel in current position to current foreground color
3080
;---------------------------------------------------------------------------------------------------------
3083
call gfxIsScreenModeMSX2
3084
jr nc, gfxSetPixel.1 ; if MSX2 and screen mode above 3
3085
__call_bios BIOS_SETC
3091
;---------------------------------------------------------------------------------------------------------
3093
; get pixel color in current position
3094
; out A = pixel color
3095
;---------------------------------------------------------------------------------------------------------
3098
call gfxIsScreenModeMSX2
3099
jr nc, gfxGetPixel.1 ; if MSX2 and screen mode above 3
3100
__call_bios BIOS_READC
3108
;---------------------------------------------------------------------------------------------------------
3110
; plot a line from current position to informed destination
3111
; in BC = destination x
3112
; DE = destination y
3113
; https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#Algorithm_for_integer_arithmetic
3114
; https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#C
3115
;---------------------------------------------------------------------------------------------------------
3116
;void line(int x0, int y0, int x1, int y1) {
3117
; int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
3118
; int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1;
3119
; int err = (dx>dy ? dx : -dy)/2, e2;
3122
; if (x0==x1 && y0==y1) break;
3124
; if (e2 >-dx) { err -= dy; x0 += sx; }
3125
; if (e2 < dy) { err += dx; y0 += sy; }
3130
if not defined GFX_FAST
3131
ld hl, (BIOS_GRPACX)
3133
ld hl, (BIOS_GRPACY)
3135
__call_basic BASIC_SUB_LINE
3140
ld (GFX_TEMP2), bc ; x
3141
ld (GFX_TEMP3), de ; y
3143
ld hl, (BIOS_GRPACX) ; x0
3144
ld de, (GFX_TEMP2) ; x
3147
;jp po, gfxLine.1 ; x0 >= x? else jump to endif
3149
jp c, gfxLine.1 ; x0 < x? jump
3152
ld (GFX_TEMP4), hl ; dx = x0 - x
3154
ld (GFX_TEMP5), hl ; sx = -1
3158
ld de, (BIOS_GRPACX) ; x0
3159
ld hl, (GFX_TEMP2) ; x
3162
ld (GFX_TEMP4), hl ; dx = x - x0
3164
ld (GFX_TEMP5), hl ; sx = 1
3167
ld hl, (BIOS_GRPACY) ; y0
3168
ld de, (GFX_TEMP3) ; y
3171
;jp po, gfxLine.3 ; y0 >= y? else, jump to endif
3173
jp c, gfxLine.3 ; y0 < y? jump
3176
ld (GFX_TEMP6), hl ; dy = y0 - y
3178
ld (GFX_TEMP7), hl ; sy = -1
3182
ld de, (BIOS_GRPACY) ; y0
3183
ld hl, (GFX_TEMP3) ; y
3186
ld (GFX_TEMP6), hl ; dy = y - y0
3188
ld (GFX_TEMP7), hl ; sy = 1
3191
ld hl, (GFX_TEMP6) ; dy
3194
jp z, gfxLine.h ; dy = 0?
3196
ld hl, (GFX_TEMP4) ; dx
3199
jp z, gfxLine.v ; dx = 0?
3201
ld de, (GFX_TEMP4) ; dx
3202
ld hl, (GFX_TEMP6) ; dy
3207
ld (GFX_TEMP9), hl ; dxy = dx + dy
3209
ld de, (GFX_TEMP4) ; dx
3210
ld hl, (GFX_TEMP6) ; dy
3213
;jp pe, gfxLine.5 ; dy < dx? else, jump to endif
3215
jp z, gfxLine.5 ; dy = dx? jump
3216
jp nc, gfxLine.5 ; dy > dx? jump
3218
ld de, (GFX_TEMP6) ; dy
3224
ld (GFX_TEMP8), de ; err = -dy / 2
3228
ld hl, (GFX_TEMP4) ; dx
3232
ld (GFX_TEMP8), hl ; err = dx/2
3237
ld hl, (GFX_TEMP9) ; dxy
3240
jp nc, gfxLine.loop.0 ; dxy > 0? jump
3241
;jp z, gfxLine.loop.0 ; dxy = 0? jump
3247
ld de, (GFX_TEMP4) ; dx
3250
ld de, (GFX_TEMP8) ; e2 = err
3254
;jp pe, gfxLine.loop.1 ; if -dx < e2, else jump to endif
3256
jp z, gfxLine.loop.1 ; -dx = e2? jump
3257
jp nc, gfxLine.loop.1 ; -dx > e2? jump
3258
ld hl, (GFX_TEMP8) ; err
3259
ld de, (GFX_TEMP6) ; dy
3262
ld (GFX_TEMP8), hl ; err -= dy
3264
ld hl, (GFX_TEMP9) ; dxy
3266
ld (GFX_TEMP9), hl ; dxy -= 1
3268
ld hl, (BIOS_GRPACX)
3272
ld (BIOS_GRPACX), hl ; x0 += sx
3276
ld de, (GFX_TEMP6) ; dy
3279
;jp pe, gfxLine.loop.2 ; if e2 < dy, else jump to endif
3281
jp z, gfxLine.loop.2 ; e2 = dy? jump
3282
jp nc, gfxLine.loop.2 ; e2 > dy? jump
3283
ld hl, (GFX_TEMP8) ; err
3284
ld de, (GFX_TEMP4) ; dx
3287
ld (GFX_TEMP8), hl ; err += dx
3289
ld hl, (GFX_TEMP9) ; dxy
3291
ld (GFX_TEMP9), hl ; dxy -= 1
3293
ld hl, (BIOS_GRPACY)
3297
ld (BIOS_GRPACY), hl ; y0 += sy
3304
ld a, (GFX_TEMP5) ; sx
3306
jr z, gfxLine.h.1 ; if a is positive
3307
ld hl, (BIOS_GRPACX)
3311
ld (BIOS_GRPACX), hl
3315
__call_bios BIOS_FETCHC
3316
ld hl, (GFX_TEMP4) ; dx
3318
call gfxDrawHorLine ; HL = pixel count
3322
ld a, (GFX_TEMP7) ; sy
3324
jr z, gfxLine.v.1 ; if a is positive
3325
ld hl, (BIOS_GRPACY)
3329
ld (BIOS_GRPACY), hl
3334
ld hl, (GFX_TEMP6) ; dy
3337
jp gfxBox.drawVerLine
3342
if defined BOX or defined FBOX or defined BOX_STEP or defined FBOX_STEP
3344
;---------------------------------------------------------------------------------------------------------
3346
; plot a box from current position to informed destination
3347
; in BC = destination x
3348
; DE = destination y
3349
; A = filled flag (0 = not filled, <>0 = filled)
3350
;---------------------------------------------------------------------------------------------------------
3354
if not defined GFX_FAST
3355
ld hl, (BIOS_GRPACX)
3357
ld hl, (BIOS_GRPACY)
3359
ld hl, BASIC_SUB_LINEBOX
3362
call gfxIsScreenModeMSX2
3364
ld hl, BASIC_SUB_LINEBOXFILLED
3369
jp BIOS_CALBAS ; BIOS_CALSLT
3380
call gfxAdjustDestXY
3382
jr nz, gfxBox.filled
3386
call gfxBox.drawHorLine
3388
ld de, (BIOS_GRPACX)
3392
ld (BIOS_GRPACX), hl
3394
call gfxBox.drawVerLine
3396
call gfxBox.drawVerLine
3397
call gfxBox.drawHorLine
3405
ld bc, (BIOS_GRPACX)
3407
call gfxBox.drawHorLine
3409
ld (BIOS_GRPACX), bc
3410
ld bc, (BIOS_GRPACY)
3412
ld (BIOS_GRPACY), bc
3418
jr nz, gfxBox.filled.loop
3430
gfxBox.drawVerLine.loop:
3438
jr nz, gfxBox.drawVerLine.loop
3441
;---------------------------------------------------------------------------------------------------------
3443
; draw a horizontal line
3445
;---------------------------------------------------------------------------------------------------------
3450
jr c, gfxDrawHorLine.2 ; if screen mode < 5 then jump
3452
ret nz ; return if negative
3455
jr nz, gfxDrawHorLine.1
3457
ret z ; return if hl = 0
3466
jr nz, gfxDrawHorLine.1
3470
__call_bios BIOS_NSETCX ; HL = fill count
3473
;---------------------------------------------------------------------------------------------------------
3475
; invert if dest XY is less than current XY position
3478
;---------------------------------------------------------------------------------------------------------
3482
ld (GFX_TEMP2), bc ; x
3483
ld (GFX_TEMP3), de ; y
3485
; verify x againt current position
3486
ld hl, (BIOS_GRPACX)
3489
sbc hl, de ; dx = x1 - x0
3490
bit 7, h ; result is negative?
3491
jr z, gfxAdjustDestXY.1
3493
ld (BIOS_GRPACX), hl
3501
; verify y againt current position
3502
ld hl, (BIOS_GRPACY)
3505
sbc hl, de ; dy = y1 - y0
3506
bit 7, h ; result is negative?
3507
jr z, gfxAdjustDestXY.2
3509
ld (BIOS_GRPACY), hl
3517
; refresh new position
3527
;---------------------------------------------------------------------------------------------------------
3529
; plot a circle centered in current position
3530
; BC = tracing end x
3531
; DE = tracing end y
3533
; A = filled flag (0 = not filled, <>0 = filled)
3534
; https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
3535
; https://rosettacode.org/wiki/Bitmap/Midpoint_circle_algorithm#C
3536
;---------------------------------------------------------------------------------------------------------
3540
if not defined GFX_FAST
3542
ret nz ; return if negative radius
3543
ld (BIOS_GXPOS), hl ; circle ray
3545
ld bc, (BIOS_GRPACY)
3549
;jr nz, gfxDrawCircle.1
3556
__call_basic BASIC_SUB_CIRCLE
3559
ld (GFX_TEMP), hl ; radius
3562
ret z ; return if zero radius
3564
ret nz ; return if negative radius
3567
ld (GFX_TEMP1), bc ; x0
3568
ld (GFX_TEMP2), de ; y0
3571
jp nz, gfxCircle.filled
3573
gfxCircle.notFilled:
3575
ld de, (GFX_TEMP) ; radius
3578
ld (GFX_TEMP3), hl ; f = 1 - radius
3581
ld (GFX_TEMP4), hl ; ddF_x = 0
3590
ld (GFX_TEMP5), hl ; ddF_y = -2 * radius
3593
ld (GFX_TEMP6), hl ; x = 0
3596
ld (GFX_TEMP7), hl ; y = radius
3598
; plot(x0, y0 + radius)
3599
ld de, (GFX_TEMP) ; radius
3600
ld hl, (GFX_TEMP2) ; y0
3604
ld bc, (GFX_TEMP1) ; x0
3608
; plot(x0, y0 - radius)
3609
ld de, (GFX_TEMP) ; radius
3610
ld hl, (GFX_TEMP2) ; y0
3614
ld bc, (GFX_TEMP1) ; x0
3618
; plot(x0 + radius, y0)
3619
ld hl, (GFX_TEMP1) ; x0
3620
ld de, (GFX_TEMP) ; radius
3627
ld de, (GFX_TEMP2) ; y0
3631
; plot(x0 - radius, y0)
3632
ld hl, (GFX_TEMP1) ; x0
3633
ld de, (GFX_TEMP) ; radius
3640
ld de, (GFX_TEMP2) ; y0
3643
jp gfxCircle.notFilled.3
3645
gfxCircle.notFilled.1:
3646
ld hl, (GFX_TEMP3) ; f
3648
jr nz, gfxCircle.notFilled.2 ; if( f < 0 ), jump
3650
ld hl, (GFX_TEMP7) ; y -= 1
3654
ld hl, (GFX_TEMP5) ; ddF_y += 2
3659
ld hl, (GFX_TEMP3) ; f
3660
ld de, (GFX_TEMP5) ; ddF_y
3663
ld (GFX_TEMP3), hl ; f += ddF_y
3665
gfxCircle.notFilled.2:
3666
ld hl, (GFX_TEMP6) ; x
3668
ld (GFX_TEMP6), hl ; x++
3670
ld hl, (GFX_TEMP4) ; ddF_x += 2
3675
ld hl, (GFX_TEMP3) ; f
3676
ld de, (GFX_TEMP4) ; ddF_x
3680
ld (GFX_TEMP3), hl ; f += ddF_x + 1
3682
; plot(x0 + x, y0 + y)
3683
ld hl, (GFX_TEMP1) ; x0
3684
ld de, (GFX_TEMP6) ; x
3691
ld hl, (GFX_TEMP2) ; y0
3692
ld de, (GFX_TEMP7) ; y
3699
; plot(x0 - x, y0 + y)
3700
ld hl, (GFX_TEMP1) ; x0
3701
ld de, (GFX_TEMP6) ; x
3708
ld hl, (GFX_TEMP2) ; y0
3709
ld de, (GFX_TEMP7) ; y
3716
; plot(x0 + x, y0 - y)
3717
ld hl, (GFX_TEMP1) ; x0
3718
ld de, (GFX_TEMP6) ; x
3725
ld hl, (GFX_TEMP2) ; y0
3726
ld de, (GFX_TEMP7) ; y
3733
; plot(x0 - x, y0 - y)
3734
ld hl, (GFX_TEMP1) ; x0
3735
ld de, (GFX_TEMP6) ; x
3742
ld hl, (GFX_TEMP2) ; y0
3743
ld de, (GFX_TEMP7) ; y
3750
; plot(x0 + y, y0 + x)
3751
ld hl, (GFX_TEMP1) ; x0
3752
ld de, (GFX_TEMP7) ; y
3759
ld hl, (GFX_TEMP2) ; y0
3760
ld de, (GFX_TEMP6) ; x
3767
; plot(x0 - y, y0 + x)
3768
ld hl, (GFX_TEMP1) ; x0
3769
ld de, (GFX_TEMP7) ; y
3776
ld hl, (GFX_TEMP2) ; y0
3777
ld de, (GFX_TEMP6) ; x
3784
; plot(x0 + y, y0 - x)
3785
ld hl, (GFX_TEMP1) ; x0
3786
ld de, (GFX_TEMP7) ; y
3793
ld hl, (GFX_TEMP2) ; y0
3794
ld de, (GFX_TEMP6) ; x
3801
; plot(x0 - y, y0 - x)
3802
ld hl, (GFX_TEMP1) ; x0
3803
ld de, (GFX_TEMP7) ; y
3810
ld hl, (GFX_TEMP2) ; y0
3811
ld de, (GFX_TEMP6) ; x
3818
gfxCircle.notFilled.3:
3819
ld hl, (GFX_TEMP6) ; x
3820
ld de, (GFX_TEMP7) ; y
3823
jp c, gfxCircle.notFilled.1 ; while( x < y )
3824
ld bc, (GFX_TEMP1) ; x0
3825
ld de, (GFX_TEMP2) ; y0
3830
call gfxCircle.notFilled
3831
ld hl, (BIOS_BDRATR)
3833
ld hl, (BIOS_FORCLR)
3834
ld (BIOS_BDRATR), hl
3838
ld (BIOS_BDRATR), hl
3844
if defined PAINT or (defined CIRCLE and defined GFX_FAST)
3846
;---------------------------------------------------------------------------------------------------------
3848
; Fill current region delimited by border attribute color changing pixels to foreground color
3849
; in: a = fill type (0 = not symmetric, 1 = symmetric)
3850
;---------------------------------------------------------------------------------------------------------
3854
if not defined GFX_FAST
3856
ld bc, (BIOS_GRPACX)
3858
ld de, (BIOS_GRPACY)
3876
call gfxIsScreenModeMSX2
3877
jr nc, gfxBorderFill.1 ; if MSX2 and screen mode above 3, jump
3881
__call_basic BASIC_SUB_PAINT1
3885
__call_basic BASIC_SUB_PAINT1
3887
;ld ix, BASIC_SUB_PAINT2
3893
ld hl, (BIOS_GRPACY)
3895
call gfxBorderFill.down
3898
ld (BIOS_GRPACY), hl
3900
call gfxBorderFill.up
3902
ld (BIOS_GRPACY), hl
3907
call gfxBorderFill.line
3913
jr nz, gfxBorderFill.down
3917
call gfxBorderFill.line
3923
jr nz, gfxBorderFill.up
3927
ld de, (BIOS_GRPACX)
3930
ld de, 1 ; skip count
3931
__call_bios BIOS_SCANR
3933
;ld (GFX_TEMP2), hl ; pixel count transversed
3936
ld (BIOS_GRPACX), de
3939
;cp 0 ; 0 = not symmetric, 1 = symmetric
3940
;jr z, gfxBorderFill.line.1
3942
;__call_bios BIOS_NSETCX ; HL = fill count
3943
;jr gfxBorderFill.line.2
3944
gfxBorderFill.line.1:
3946
ld de, 0 ; skip count
3947
__call_bios BIOS_SCANL
3948
gfxBorderFill.line.2:
3950
ld (BIOS_GRPACX), de
3954
;---------------------------------------------------------------------------------------------------------
3956
; Fload fill current region changing current pixel color to foreground color
3957
; https://en.wikipedia.org/wiki/Flood_fill
3958
;---------------------------------------------------------------------------------------------------------
3962
ld (GFX_TEMP), a ; replacement-color
3963
call gfxFloadFill.recursive
3966
gfxFloadFill.recursive:
3967
; 1. If target-color is equal to replacement-color, return.
3973
; 2. ElseIf the color of node is not equal to target-color, return.
3979
; 3. Else Set the color of node to replacement-color.
3982
; 4. Perform Flood-fill (one step to the left of node, target-color, replacement-color).
3983
gfxFloadFill.recursive.left:
3985
jr c, gfxFloadFill.recursive.right
3986
call gfxFloadFill.recursive
3989
; Perform Flood-fill (one step to the right of node, target-color, replacement-color).
3990
gfxFloadFill.recursive.right:
3992
jr c, gfxFloadFill.recursive.up
3993
call gfxFloadFill.recursive
3996
; Perform Flood-fill (one step to the up of node, target-color, replacement-color).
3997
gfxFloadFill.recursive.up:
3999
jr c, gfxFloadFill.recursive.down
4000
call gfxFloadFill.recursive
4003
; Perform Flood-fill (one step to the down of node, target-color, replacement-color).
4004
gfxFloadFill.recursive.down:
4007
call gfxFloadFill.recursive
4015
if defined SPRITEMODE
4017
;---------------------------------------------------------------------------------------------------------
4019
; set current sprite mode
4021
; 0: Spritesize is 8 by 8 pixels - default value
4022
; 1: Spritesize is 8 by 8 pixels, magnified to 16 by 16 pixels
4023
; 2: Spritesize is 16 by 16 pixels
4024
; 3: Spritesize is 16 by 16 pixels, magnified to 32 by 32 pixels
4025
; RG1SAV bit 0 = magnify sprite (double size)
4026
; RG1SAV bit 1 = sprite size (0=8 pixels, 1=16 pixels)
4027
;---------------------------------------------------------------------------------------------------------
4030
and 3 ; keeps only bits 0 and 1 from A
4033
ld a, (BIOS_RG1SAV) ; get copy from register #1 of VDP
4034
and 0xFC ; clear bits 0 and 1 from A
4035
or b ; put parameter to A (bits 0 and 1)
4036
ld (BIOS_RG1SAV), a ; restore to register #1 of VDP
4037
ld b, a ; value to write
4038
ld c, 1 ; register number to write
4039
call gfxWRTVDP ; write register to VDP
4040
call gfxCLRSPR ; clear sprites
4042
call gfxGetSpriteSize
4043
jp gfxFillSpriteCollisionTable
4047
if defined SPRITE or defined COLOR_SPRITE or defined PUT_SPRITE_COLOR or defined PUT_SPRITE_STEP_COLOR or defined PUT_SPRITE_COLOR_PATNUM or defined PUT_SPRITE_STEP_COLOR_PATNUM or defined LOAD_SET
4049
;---------------------------------------------------------------------------------------------------------
4050
; gfxSetSpriteColorInt
4053
;---------------------------------------------------------------------------------------------------------
4055
gfxSetSpriteColorInt:
4056
ld b, a ; save sprite number
4057
call gfxCALATR ; get sprite attribute table address
4062
;call gfxSetSpriteColor.Adjust
4067
ret c ; if screen mode < 3, do not adjust sprite multicolor
4069
ld a, b ; recover sprite number
4070
call gfxGetSpriteColorTable
4073
;call gfxSetSpriteColor.Adjust
4074
ld b, 16 ; array of 16 bytes
4076
gfxSetSpriteColorInt.1:
4081
djnz gfxSetSpriteColorInt.1
4084
;gfxSetSpriteColor.Adjust:
4094
;---------------------------------------------------------------------------------------------------------
4095
; gfxSetSpriteColorStr
4097
; DE = address to color byte array
4098
; BC = color byte array size
4099
;---------------------------------------------------------------------------------------------------------
4101
gfxSetSpriteColorStr:
4102
ld b, a ; save sprite number
4105
call gfxCALATR ; get sprite attribute table
4109
ld a, c ; byte array zero length?
4117
;call gfxSetSpriteColor.Adjust
4122
ret c ; if screen mode < 3, do not adjust sprite mode 2
4124
ld a, b ; recover sprite number
4125
call gfxGetSpriteColorTable
4127
ld b, 16 ; array of 16 bytes (color table)
4128
ld a, c ; size of color array
4130
pop iy ; save array start
4132
gfxSetSpriteColorStr.1:
4135
;call gfxSetSpriteColor.Adjust
4141
jr nz, gfxSetSpriteColorStr.2
4142
ld a, c ; recover array size
4144
pop de ; recover array start
4146
gfxSetSpriteColorStr.2:
4147
djnz gfxSetSpriteColorStr.1
4152
;---------------------------------------------------------------------------------------------------------
4153
; gfxGetSpriteColorTable
4155
; HL = address to color table
4156
;---------------------------------------------------------------------------------------------------------
4158
gfxGetSpriteColorTable:
4162
ld l, a ; recover sprite number
4166
add hl, hl ; multiply by 16 (shift left 4)
4169
call gfxCALATR ; get sprite attribute table address
4174
sbc hl, de ; address of color table from sprite multicolor
4181
if defined PUT_SPRITE or defined PUT_SPRITE_STEP or defined PUT_SPRITE_COLOR or defined PUT_SPRITE_STEP_COLOR or defined PUT_SPRITE_COLOR_PATNUM or defined PUT_SPRITE_STEP_COLOR_PATNUM or defined PUT_SPRITE_PATNUM or defined PUT_SPRITE_STEP_PATNUM
4183
;---------------------------------------------------------------------------------------------------------
4187
;---------------------------------------------------------------------------------------------------------
4191
call gfxCALATR ; get sprite attribute table address
4200
;---------------------------------------------------------------------------------------------------------
4201
; gfxSpriteStepCheck
4203
;---------------------------------------------------------------------------------------------------------
4210
ld de, (GFX_SPRITE_SIZE_DAT)
4211
ld a, (GFX_SPRITE_FLAGS) ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4212
and 7 ; clear touched and collided flags
4213
ld (GFX_SPRITE_FLAGS), a
4216
jr z, gfxSpriteStepCheck.corners
4218
gfxSpriteStepCheck.limits:
4221
cp b ; jump if x > max_x?
4222
jr c, gfxSpriteStepCheck.limits.touched
4226
cp c ; jump if y > max_y?
4227
jr c, gfxSpriteStepCheck.limits.touched
4229
jr gfxSpriteStepCheck.corners
4231
gfxSpriteStepCheck.limits.touched:
4232
ld a, (GFX_SPRITE_FLAGS) ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4233
or 8 ; set limit touched flag
4234
ld (GFX_SPRITE_FLAGS), a
4236
gfxSpriteStepCheck.corners:
4237
ld a, (GFX_SPRITE_FLAGS) ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4238
and 2+4 ; check walls or hotspots
4239
jr z, gfxSpriteStepCheck.end
4242
call gfxSpriteStepCheck.corner
4244
ld a, (GFX_SPRITE_SIZE_DAT)
4247
call gfxSpriteStepCheck.corner
4249
ld a, (GFX_SPRITE_SIZE_DAT)
4252
call gfxSpriteStepCheck.corner
4255
ld a, (GFX_SPRITE_SIZE_DAT)
4258
call gfxSpriteStepCheck.corner
4260
gfxSpriteStepCheck.end:
4266
gfxSpriteStepCheck.corner:
4267
call gfxGetTileFromXY
4268
ld d, a ; tile to be searched
4270
ld a, (GFX_SPRITE_FLAGS) ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4272
call nz, gfxSpriteStepCheck.walls
4274
ld a, (GFX_SPRITE_FLAGS) ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4276
call nz, gfxSpriteStepCheck.hotspots
4279
gfxSpriteStepCheck.walls:
4280
ld hl, (GFX_SPRITE_WALLS)
4281
ld e, 16 ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4282
jp gfxSpriteStepCheck.search
4284
gfxSpriteStepCheck.hotspots:
4285
ld hl, (GFX_SPRITE_HOTSPOTS)
4286
ld e, 32 ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4287
call gfxSpriteStepCheck.search
4289
ld a, (GFX_SPRITE_FLAGS) ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4294
ld (GFX_SPRITE_HOTSPOT_TILE), a
4297
; d = tile, hl = search table, e = flag
4298
gfxSpriteStepCheck.search:
4303
ld a, d ; search for this tile
4309
cpir ; inc HL searching for A until BC=0 (Z flag settled if found)
4313
gfxSpriteStepCheck.search.found:
4314
ld a, (GFX_SPRITE_FLAGS) ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4315
or e ; set touched flag
4316
ld (GFX_SPRITE_FLAGS), a
4331
call gfxGetScreenTile ; b = y, c = x, a = tile
4337
if defined SPRITE or defined PUT_SPRITE or defined PUT_SPRITE_PATNUM or defined PUT_SPRITE_STEP_PATNUM or defined PUT_SPRITE_COLOR_PATNUM or defined PUT_SPRITE_STEP_COLOR_PATNUM
4339
;---------------------------------------------------------------------------------------------------------
4340
; gfxSetSpritePattern
4342
; BC = pattern number
4343
;---------------------------------------------------------------------------------------------------------
4345
gfxSetSpritePattern:
4347
call gfxCALATR ; get sprite attribute table address
4351
ld a, (BIOS_RG1SAV) ; bit 0 = double size, bit 1 = sprite size (0=8 pixels, 1=16 pixels)
4353
jr z, gfxSetSpritePattern.1
4356
gfxSetSpritePattern.1:
4357
ld a, c ; pattern number
4365
;---------------------------------------------------------------------------------------------------------
4367
; HL = point to sprite data as a string of 8 or 32 characters according the sprites size (8x8 or 16x16)
4369
;---------------------------------------------------------------------------------------------------------
4374
call gfxCALPAT ; get sprite pattern data address
4376
call gfxGSPSIZ ; return in 'a' sprite default size
4380
jr z, gfxSetSpriteData.1
4381
jr nc, gfxSetSpriteData.1
4395
if defined GFX_SPRITES
4397
;---------------------------------------------------------------------------------------------------------
4399
; initialises all sprites
4400
;---------------------------------------------------------------------------------------------------------
4406
;---------------------------------------------------------------------------------------------------------
4408
; set sprite default x, y, pattern and color
4410
;---------------------------------------------------------------------------------------------------------
4415
ld a, (BIOS_GRPACY) ; y
4418
ld a, (BIOS_GRPACX) ; x
4424
ld a, (BIOS_FORCLR) ; color
4430
if defined EXIST_DATA_SET
4432
;---------------------------------------------------------------------------------------------------------
4434
;---------------------------------------------------------------------------------------------------------
4443
;---------------------------------------------------------------------------------------------------------
4444
; gfxClearTileScreen
4445
;---------------------------------------------------------------------------------------------------------
4450
ld hl, (BIOS_GRPNAM)
4454
call gfxSetTileDefaultColor
4456
call gfxSetTileDefaultColor
4458
jp gfxSetTileDefaultColor
4460
gfxSetTileDefaultColor
4461
call gfxGetTileColorAddr
4463
call gfxGetTileDefaultColor
4467
gfxGetTileDefaultColor:
4480
; HL = tile color pointer
4481
; BC = tile color size
4484
;---------------------------------------------------------------------------------------------------------
4487
; HL = tile data pointer
4488
; BC = tile data size
4490
; A = flip (0=no, 1=yes)
4491
;---------------------------------------------------------------------------------------------------------
4495
jr nz, gfxSetTileDataFlip
4497
gfxSetTileDataNoFlip:
4498
call gfxGetTileDataAddr
4502
call gfxGetTileDataAddr
4504
gfxSetTileDataFlip.Loop:
4513
jr nz, gfxSetTileDataFlip.Loop
4516
; in de = tile number
4517
; out de = tile number address
4523
add hl, hl ; tile number * 8
4524
ld de, (BIOS_GRPCGP)
4530
;---------------------------------------------------------------------------------------------------------
4533
; HL = tile color pointer
4534
; BC = tile color size
4536
;---------------------------------------------------------------------------------------------------------
4539
call gfxGetTileColorAddr
4542
; in de = tile number
4543
; out de = tile number address
4544
gfxGetTileColorAddr:
4549
add hl, hl ; tile number * 8
4550
ld de, (BIOS_GRPCOL)
4556
;---------------------------------------------------------------------------------------------------------
4558
; set screen tile at x,y
4562
;---------------------------------------------------------------------------------------------------------
4574
; ld l, b ; slow y * 32
4591
ld de, (BIOS_GRPNAM)
4598
if defined gfxGetTileFromXY or defined EXIST_DATA_SET
4600
;---------------------------------------------------------------------------------------------------------
4602
; get screen tile at x,y
4606
;---------------------------------------------------------------------------------------------------------
4628
ld de, (BIOS_GRPNAM)
4634
;---------------------------------------------------------------------------------------------------------
4635
; Sprite collision table routines
4636
;---------------------------------------------------------------------------------------------------------
4638
gfxInitSpriteCollisionTable:
4641
ld (GFX_SPRITE_COLLISION), ix
4644
; copy sprite attribute table to ram
4645
gfxFillSpriteCollisionTable:
4646
ld hl, (BIOS_ATRBAS) ; source: attribute table
4647
ld de, (GFX_SPRITE_COLLISION) ; dest: ram
4648
ld bc, 128 ; 32*4 = size of attribute table
4651
; pre-calculate each sprite width
4652
gfxCalculateSpriteCollisionTable:
4653
ld ix, (GFX_SPRITE_COLLISION) ; start of sprites attributes
4654
ld b, 32 ; sprite count
4656
gfxCalculateSpriteCollisionTable.start:
4657
ld a, (GFX_SPRITE_SIZE_DAT) ; sprite size
4659
ld d, 208 ; Y no-display flag
4663
cp 3 ; above screen 3?
4664
jr c, gfxCalculateSpriteCollisionTable.next
4665
ld d, 216 ; Y no-display flag
4667
gfxCalculateSpriteCollisionTable.next:
4668
; test IC flag (no collision) in color table
4669
; test EC flag (early clock, shift 32 dots to the left) in color table
4670
; set x1 = x + size, y1 = y + size
4672
cp d ; test if sprite will not be displayed
4685
djnz gfxCalculateSpriteCollisionTable.next
4688
; BC = sprite number to check
4689
gfxUpdateSpriteCollisionTable:
4697
ld hl, (GFX_SPRITE_COLLISION) ; dest: ram
4700
ld hl, (BIOS_ATRBAS) ; source: attribute table
4702
ld bc, 4 ; 4 = size of attribute table to 1 sprite]
4706
ld b, 1 ; sprite count
4708
call gfxCalculateSpriteCollisionTable.start
4711
ld a, (GFX_SPRITE_FLAGS)
4712
and 0xBF ; clear collision flag
4713
ld (GFX_SPRITE_FLAGS), a
4715
ld a, (BIOS_STATFL) ; verify if collision occurred
4718
jr gfxCheckSpriteCollisionTable.start
4720
; BC = sprite number to check
4721
gfxCheckSpriteCollisionTable:
4722
; get target sprite address (iy)
4726
ld de, (GFX_SPRITE_COLLISION)
4733
gfxCheckSpriteCollisionTable.start:
4734
; start test against others sprites
4735
ld ix, (GFX_SPRITE_COLLISION)
4737
ld d, 208 ; Y no-display flag
4741
cp 3 ; above screen 3?
4742
jr c, gfxCheckSpriteCollisionTable.test
4743
ld d, 216 ; Y no-display flag
4745
gfxCheckSpriteCollisionTable.test:
4746
; skip target sprite
4749
jr z, gfxCheckSpriteCollisionTable.next
4751
; test if x1 > nx and x < nx1 and y1 > ny and y < ny1
4753
cp d ; test if sprite will not be displayed
4754
ret z ; return false
4757
jr nc, gfxCheckSpriteCollisionTable.next
4761
jr nc, gfxCheckSpriteCollisionTable.next
4765
jr nc, gfxCheckSpriteCollisionTable.next
4769
jr nc, gfxCheckSpriteCollisionTable.next
4771
; if so, save collider sprite
4773
ld (GFX_SPRITE_COLLIDER), a
4775
ld a, (GFX_SPRITE_FLAGS)
4776
or 0x40 ; set collision flag
4777
ld (GFX_SPRITE_FLAGS), a
4781
gfxCheckSpriteCollisionTable.next:
4789
ret z ; return false
4790
jr gfxCheckSpriteCollisionTable.test
4794
;---------------------------------------------------------------------------------------------------------
4795
; VDP / VRAM support routines
4796
;---------------------------------------------------------------------------------------------------------
4800
; c = register number
4801
; a = register number
4804
ret nz ; is negative? read only
4806
ret z ; is register 8? then status register 0 (read only)
4807
jr nc, gfxWRTVDP.1 ; is > 8? then control registers numbers added 1
4822
;jp nz, BIOS_NWRVDP ; msx 2
4824
;jp nz, BIOS_NWRVDP ; msx 2
4825
jp BIOS_WRTVDP ; msx 1
4828
; in a = register number
4832
jr nz, gfxRDVDP.1 ; is negative? then status register 1 to 9
4834
jr z, gfxRDVDP.2 ; is register 8? then status register 0
4836
jr nc, gfxRDVDP.3 ; is >= 9? then control registers numbers added 1
4837
ld hl, BIOS_RG0SAV ; else is correct control registers numbers
4849
jp BIOS_NRDVDP ;BIOS_VDPSTA
4856
ld hl, BIOS_RG8SAV-9
4864
; in: A=Data byte, BC=Length, HL=VRAM address
4873
; in: A=Sprite pattern number
4874
; out: HL=Sprite pattern address
4884
; in: A=Sprite number
4885
; out: HL=Sprite attribute address
4895
; out: A=Bytes in sprite pattern (8 or 32)
4915
; in: BC=Length, dest DE=VRAM address, source HL=RAM address
4920
; in: BC=Length, dest DE=RAM address, source HL=VRAM address
4947
; 8 bytes / 206 cycles
4948
; http://www.retroprogramming.com/2014/01/fast-z80-bit-reversal.html
4955
djnz gfxReverseA.loop
4960
;---------------------------------------------------------------------------------------------------------
4962
;---------------------------------------------------------------------------------------------------------
4971
RET_MATH_LIB: call COPY_TO.TMP_DAC
4977
MATH_DECADD: ld ix, addSingle
4982
if defined MATH.SUB or defined MATH.NEG
4984
MATH_DECSUB: ld ix, subSingle
4989
if defined MATH.MULT
4991
MATH_DECMUL: ld ix, mulSingle
4998
MATH_DECDIV: ld ix, divSingle
5006
MATH_SNGEXP: ld ix, powSingle
5013
MATH_COS: ld ix, cosSingle
5020
MATH_SIN: ld ix, sinSingle
5027
MATH_TAN: ld ix, tanSingle
5034
MATH_ATN: ld ix, atanSingle
5041
MATH_SQR: ld ix, sqrtSingle
5048
MATH_LOG: ld ix, lnSingle
5055
MATH_EXP: ld ix, expSingle
5062
MATH_ABSFN: ld ix, absSingle
5067
if defined MATH.SEED or defined MATH.NEG
5069
MATH_NEG: ld ix, negSingle
5076
MATH_SGN: ld ix, sgnSingle
5081
if defined RND or defined MATH.SEED
5083
MATH_RND: ld ix, randSingle
5088
MATH_FRCINT: ld hl, BASIC_DAC
5101
ld (BASIC_VALTYP), a
5104
MATH_FRCDBL: ; same as MATH_FRCSGL
5105
MATH_FRCSGL: ld hl, BASIC_DAC+2 ; input address
5106
ld bc, BASIC_DAC ; output address
5109
ld (BASIC_VALTYP), a
5112
MATH_ICOMP: ld a, h ; cp hl, de (alternative to bios DCOMPR)
5114
jr nz, MATH_ICOMP.NE.HIGH
5117
jr nz, MATH_ICOMP.NE.LOW
5119
MATH_ICOMP.NE.HIGH: jr c, MATH_ICOMP.GT.HIGH
5121
jr nz, MATH_DCOMP.GT
5123
MATH_ICOMP.GT.HIGH: bit 7, d
5126
MATH_ICOMP.NE.LOW: jr c, MATH_DCOMP.GT
5129
MATH_XDCOMP: ; same as MATH_DCOMP
5130
MATH_DCOMP: ld ix, cmpSingle
5134
MATH_DCOMP.GT: ld a, 0xFF ; DAC > ARG
5136
MATH_DCOMP.EQ: ld a, 0 ; DAC = ARG
5138
MATH_DCOMP.LT: ld a, 1 ; DAC < ARG
5141
if defined CAST_STR_TO.VAL
5143
MATH_FIN: ; HL has the source string
5144
ld a, (BASIC_VALTYP)
5145
cp 2 ; test if integer
5147
ld hl, (BASIC_DAC+2)
5152
MATH_FIN.1: ld BC, BASIC_DAC
5158
if defined CAST_INT_TO.STR
5160
MATH_FOUT: ld a, (BASIC_VALTYP)
5161
cp 2 ; test if integer
5163
ld hl, (BASIC_DAC+2)
5168
MATH_FOUT.1: ld hl, BASIC_DAC
5179
;---------------------------------------------------------------------------------------------------------
5181
; Copyright 2018 Zeda A.K. Thomas
5182
;---------------------------------------------------------------------------------------------------------
5184
; https://github.com/Zeda/z80float
5185
; https://www.omnimaga.org/asm-language/(z80)-floating-point-routines/
5186
; https://en.wikipedia.org/wiki/Single-precision_floating-point_format
5187
;---------------------------------------------------------------------------------------------------------
5189
; HL points to the first operand
5190
; DE points to the second operand (if needed)
5191
; IX points to the third operand (if needed, rare)
5192
; BC points to where the result should be output
5193
; Floats are stored by a little-endian 24-bit mantissa. However, the highest bit
5194
; is taken as implicitly 1, so we replace it as a sign bit. Next comes an 8-bit
5195
; exponent biased by +128.
5196
;---------------------------------------------------------------------------------------------------------
5197
; Adapted to MSXBas2Asm by Amaury Carvalho, 2019
5198
;---------------------------------------------------------------------------------------------------------
5200
;---------------------------------------------------------------------------------------------------------
5202
;---------------------------------------------------------------------------------------------------------
5204
BASIC_HOLD8: equ 0xF806 ; 48 Work area for decimal multiplications.
5205
BASIC_HOLD2: equ 0xF836 ; 8 Work area in the execution of numerical operators.
5206
BASIC_HOLD: equ 0xF83E ; 8 Work area in the execution of numerical operators.
5207
scrap: equ BASIC_HOLD8
5208
seed0: equ BASIC_RNDX
5209
seed1: equ seed0 + 4
5210
var48: equ scrap + 4
5213
addend2: equ scrap+7 ;4 bytes
5214
var_x: equ BASIC_HOLD8 + 4 ;4 bytes
5215
var_y: equ var_x + 4 ;4 bytes
5216
var_z: equ var_y + 4 ;4 bytes
5217
var_a: equ var_z + 4 ;4 bytes
5218
var_b: equ var_a + 4 ;4 bytes
5219
var_c: equ var_b + 4 ;4 bytes
5220
temp: equ var_c + 4 ;4 bytes
5221
temp1: equ temp + 4 ;4 bytes
5222
temp2: equ temp1 + 4 ;4 bytes
5223
temp3: equ temp2 + 4 ;4 bytes
5225
pow10exp_single: equ scrap+9
5226
strout_single: equ 0xF750 ; PARM2 - BASIC_BUF ;pow10exp_single+2
5228
;---------------------------------------------------------------------------------------------------------
5230
;---------------------------------------------------------------------------------------------------------
5232
;;Still need to tend to special cases
5300
pop hl ;bigger float
5432
;;Need to adjust sign flag
5455
;;How many push/pops are needed?
5463
;;How many push/pops are needed?
5469
;;How many push/pops are needed?
5470
;;Return bigger number
5477
;---------------------------------------------------------------------------------------------------------
5479
;---------------------------------------------------------------------------------------------------------
5502
jp addInject ;jumps in to the addSingle routine
5504
;---------------------------------------------------------------------------------------------------------
5506
;---------------------------------------------------------------------------------------------------------
5509
;Inputs: HL points to float1, DE points to float2, BC points to where the result is copied
5510
;Outputs: float1*float2 is stored to (BC)
5511
;573+mul24+{0,35}+{0,30}
5514
;avg: 2055.13839751681cc
5540
;;return float in CHLB
5550
jr z,mulSingle_case0
5562
;jr z,mulSingle_case1
5566
jp z,mulSingle_case1
5571
rra ; |Lots of help from Runer112 and
5572
adc a,a ; |calc84maniac for optimizing
5573
jp po,bad ; |this exponent check.
5582
call mul24 ;BDE*CHL->HLBCDE, returns sign info
5639
;special*x = special
5660
;basically, if b|c has bit 5 set, return NaN
5693
;;avg :1464.9033203125cc (1464+925/1024)
5696
;avg: 1449.63839751681cc
5737
;---------------------------------------------------------------------------------------------------------
5739
;---------------------------------------------------------------------------------------------------------
5742
;;HL points to numerator
5743
;;DE points to denominator
5744
;;BC points to where the quotient gets written
5746
divSingle_no_pushpop:
5752
xor (hl) ; |Get sign of output
5759
ex de,hl ; |Get exponent
5866
call divsub1 ;34 or 66
5884
;34cc or 66cc or 93cc
5899
;---------------------------------------------------------------------------------------------------------
5901
; https://www.geeksforgeeks.org/write-a-c-program-to-calculate-powxn/
5902
; https://stackoverflow.com/questions/3518973/floating-point-exponentiation-without-power-function
5903
;---------------------------------------------------------------------------------------------------------
5904
;double mypow( double base, double power, double precision )
5906
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
5907
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
5908
; else if ( precision >= 1 ) {
5909
; if( base >= 0 ) return sqrt( base );
5910
; else return sqrt( -base );
5911
; } else return sqrt( mypow( base, power*2, precision*2 ) );
5914
if defined MATH.POW or defined MATH_EXP or defined MATH_LOG or defined MATH_LN
5920
;;BC points to output
5924
ld bc, var_y ; power
5929
ld hl, const_precision
5930
ld bc, var_a ; precision
5933
ld bc, var_z ; result
5942
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
5948
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
5954
; else if ( precision >= 1 ) {
5955
; if( base >= 0 ) return sqrt( base );
5956
; else return sqrt( -base );
5962
; } else return sqrt( mypow( base, power*2, precision*2 ) );
5987
; return 1 / mypow( base, -power, precision );
6006
; return base * mypow( base, power-1, precision );
6025
; if( base >= 0 ) return sqrt( base );
6026
; else return sqrt( -base );
6052
; 2^x = 1.000000001752 + x * (0.693146989552 + x * (0.2402298085906 + x * (5.54833215071e-2 + x * (9.67907584392e-3 + x * (1.243632065103e-3 + x * 2.171671843714e-4)))))
6053
;Please note that usually I like to reduce to [-.5,.5] as the extra overhead is usually worth it.
6054
;In this case, our polynomial is the same degree, with error different by less than 1 bit, so it's just a waste to range-reduce in this way.
6057
;x-=int(x) ;leaves x in [0,1)
6059
;;if x==inf -> out==inf
6060
;;if x==-inf -> out==0
6061
;;if x==NAN -> out==NAN
6068
push af ;keep track of sign
6078
jr c,_pow_1 ;int(x)=0
6091
jr nz,exp_normalized
6102
jr exp_normalized ;.db $11 ;start of `ld de,**`
6109
jr comp_exp ;.db $06 ;start of 'ld b,*` just to eat the next byte
6118
jp z,exp_underflow+1
6119
;perform 1-(var48+10)--> var48+10
6127
;our 'x' is at var48+10
6128
;our `temp` is at var48+6 so as not to cause issues with mulSingle)
6129
;uses 14 bytes of RAM
6171
;-inf -> +0 because lim approaches 0 from the right
6193
;-inf -> +0 because lim approaches 0 from the right
6195
sbc a,a ;FF if should be 0,
6210
;---------------------------------------------------------------------------------------------------------
6212
;---------------------------------------------------------------------------------------------------------
6214
if defined MATH_SQR or defined MATH_EXP
6216
;Uses 3 bytes at scrap
6218
;552+{0,19}+8{0,3+{0,3}}+pushpop+sqrtHLIX
6237
jp z,sqrtSingle_special
6240
push af ;new exponent
6250
;AHL is the new remainder
6251
;Need to divide by 2, then divide by the 16-bit (var_x+4)
6255
;We are just going to approximate it
6337
;Output: DE is the sqrt, AHL is the remainder
6338
;speed: 754+{0,1}+6{0,6}+{0,3+{0,18}}+{0,38}+sqrtHL
6362
jr _15a ;.db $FE ;start of `cp *`
6376
jr _16a ;.db $FE ;start of `cp *`
6390
jr _17a ;.db $FE ;start of `cp *`
6404
jr _18a ;.db $FE ;start of `cp *`
6408
;Now we have four more iterations
6409
;The first two are no problem
6421
jr _19a ;.db $FE ;start of `cp *`
6435
jr _20a ;.db $FE ;start of `cp *`
6440
;On the next iteration, HL might temporarily overflow by 1 bit
6442
rl d ;sla e \ rl d \ inc e
6446
adc hl,hl ;This might overflow!
6447
jr c,sqrt32_iter15_br0
6460
;On the next iteration, HL is allowed to overflow, DE could overflow with our current routine, but it needs to be shifted right at the end, anyways
6463
ld b,a ;either 0x00 or 0x80
6484
;returns A as the sqrt, HL as the remainder, D = 0
6498
jr _23a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
6509
jr _24a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
6520
dec d ;this resets the low bit of D, so `srl d` resets carry.
6521
jr _25a ;.db $06 ;start of ld b,* which is 7cc to skip the next byte.
6543
jr _27a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
6556
jr _28a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
6578
;---------------------------------------------------------------------------------------------------------
6580
;---------------------------------------------------------------------------------------------------------
6582
if defined MATH_LOG or defined MATH_LN
6585
; x / (1 + x/(2-x+4x/(3-2x+9x/(4-3x+16x/(5-4x)))))
6586
; a * x ^ (1/a) - a, where a = 100
6589
ld de, const_100_inv
6591
call powSingle ; temp = x ^ (1/100)
6595
call mulSingle ; temp1 = temp * 100
6598
call subSingle ; bc = temp1 - 100
6603
;---------------------------------------------------------------------------------------------------------
6605
;---------------------------------------------------------------------------------------------------------
6622
;---------------------------------------------------------------------------------------------------------
6624
;---------------------------------------------------------------------------------------------------------
6631
;;BC points to the output
6636
;;DE points to lg(y), HL points to x, BC points to output
6645
;---------------------------------------------------------------------------------------------------------
6647
; https://en.wikipedia.org/wiki/List_of_trigonometric_identities
6648
; https://en.wikipedia.org/wiki/Taylor_series#Trigonometric_functions
6649
; https://cs.stackexchange.com/questions/89245/how-approximate-sine-using-taylor-series
6650
; https://stackoverflow.com/questions/42217069/approximating-sinex-with-a-taylor-series-in-c-and-having-a-lot-of-problems
6651
;---------------------------------------------------------------------------------------------------------
6653
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
6656
; taylor: x - x^3/6 + x^5/120 - x^7/5040
6657
; x(1 - x^2(1/6 - x^2(1/120 - x^2/5040)) )
6659
; var_b = round( x / (2*PI), 0 )
6660
; var_c = x - var_b*2*PI
6661
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
6662
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
6663
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
6670
call copySingle ; return 0
6674
call trigRangeReductionSinCos
6679
call mulSingle ; var_b = var_a * var_a
6683
call mulSingle ; temp = x^2/5040
6687
call subSingle ; temp1 = 1/120 - temp
6691
call mulSingle ; temp = x^2 * temp1
6695
call subSingle ; temp1 = 1/6 - temp
6699
call mulSingle ; temp = x^2 * temp1
6703
call subSingle ; temp1 = 1 - temp
6707
call mulSingle ; return x * temp1
6710
trigRangeReductionSinCos:
6713
; var_b = round( x / (2*PI), 0 )
6721
; var_c = x - var_b*2*PI
6725
call mulSingle ; temp = var_b*2*PI
6729
call subSingle ; var_c = x - temp
6730
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
6734
jr nc, trigRangeReductionSinCos.else.2
6737
call copySingle ; temp1 = var_c
6738
jr trigRangeReductionSinCos.endif.2
6739
trigRangeReductionSinCos.else.2:
6743
call addSingle ; temp1 = var_c + 2*PI
6744
trigRangeReductionSinCos.endif.2:
6745
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
6749
jr c, trigRangeReductionSinCos.else.3
6750
jr z, trigRangeReductionSinCos.else.3
6754
call subSingle ; temp2
6755
jr trigRangeReductionSinCos.endif.3
6756
trigRangeReductionSinCos.else.3:
6759
call copySingle ; temp2 = temp1
6760
trigRangeReductionSinCos.endif.3:
6761
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
6762
ld hl, const_half_pi
6765
jr c, trigRangeReductionSinCos.else.4
6766
jr z, trigRangeReductionSinCos.else.4
6770
call subSingle ; var_a
6771
jr trigRangeReductionSinCos.endif.4
6772
trigRangeReductionSinCos.else.4:
6775
call copySingle ; var_a = temp2
6776
trigRangeReductionSinCos.endif.4:
6777
; if( temp > PI, -1, 1 )
6781
jr nc, trigRangeReductionSinCos.endif.5
6785
ld (ix+2), a ; turn var_a to negative
6786
trigRangeReductionSinCos.endif.5:
6792
;---------------------------------------------------------------------------------------------------------
6794
;---------------------------------------------------------------------------------------------------------
6796
if defined MATH_COS or defined MATH_TAN
6799
; taylor: 1 - x^2/2 + x^4/24 - x^6/720
6800
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
6801
; reduction: same as sin
6810
call copySingle ; return 1
6814
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
6815
call trigRangeReductionSinCos
6820
call mulSingle ; var_b = var_a * var_a
6824
call mulSingle ; temp = x^2/720
6828
call subSingle ; temp1 = 1/24 - temp
6832
call mulSingle ; temp = x^2 * temp1
6836
call subSingle ; temp1 = 1/2 - temp
6840
call mulSingle ; temp = x^2 * temp1
6844
call subSingle ; temp1 = 1 - temp
6846
; temp3 = abs(var_c)
6847
; temp1 = temp1 * if( temp3 >= PI/2, -1, 1 ) ==> cos sign
6854
ld (ix+2), a ; temp3 = abs(var_c)
6856
ld de, const_half_pi
6857
call cmpSingle ; if temp3 >= PI/2 then temp1 = -temp1
6858
jr nc, cosSingle.endif.1
6862
ld (ix+2), a ; temp1 = -temp1
6866
call copySingle ; return temp1
6871
;---------------------------------------------------------------------------------------------------------
6873
;---------------------------------------------------------------------------------------------------------
6894
;---------------------------------------------------------------------------------------------------------
6896
;---------------------------------------------------------------------------------------------------------
6901
;taylor: x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
6902
; x < -1: atan - PI/2
6903
; x >= 1: PI/2 - atan
6904
;reduction: abs(X) > 1 : Y = 1 / X
6905
; abs(X) <= 1: Y = X
6914
call copySingle ; return 0
6918
;x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
6919
call trigRangeReductionAtan
6925
call mulSingle ; var_b = var_a * var_a
6929
call mulSingle ; temp = (4*x)^2
6933
call divSingle ; temp1 = temp/9
6937
call addSingle ; temp = 7 + temp1
6941
call mulSingle ; temp1 = var_b * 9
6945
call divSingle ; temp2 = temp1 / temp
6949
call addSingle ; temp = 5 + temp2
6953
call mulSingle ; temp1 = var_b * 4
6957
call divSingle ; temp2 = temp1 / temp
6961
call addSingle ; temp = 3 + temp2
6965
call divSingle ; temp2 = var_b / temp
6969
call addSingle ; temp = 1 + temp2
6973
call divSingle ; temp2 = var_a / temp
6975
; x >= 1: PI/2 - atan
6979
ld hl, const_half_pi
6986
; x < -1: atan - PI/2
6997
ld de, const_half_pi
7006
call copySingle ; return temp2
7009
trigRangeReductionAtan:
7010
;reduction: abs(X) > 1 : Y = 1 / X
7011
; abs(X) <= 1: Y = X
7020
ld (ix+2), a ; abs(x)
7024
jr nc, trigRangeReductionAtan.1
7030
jr trigRangeReductionAtan.2
7031
trigRangeReductionAtan.1:
7036
trigRangeReductionAtan.2:
7040
jr c, trigRangeReductionAtan.3
7044
ld (ix+2), a ; y = -y
7045
trigRangeReductionAtan.3:
7050
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
7052
;---------------------------------------------------------------------------------------------------------
7054
;---------------------------------------------------------------------------------------------------------
7068
;---------------------------------------------------------------------------------------------------------
7070
;---------------------------------------------------------------------------------------------------------
7139
if defined MATH_ABSFN
7141
;---------------------------------------------------------------------------------------------------------
7143
;---------------------------------------------------------------------------------------------------------
7146
;;HL points to the float
7147
;;BC points to where to output the result
7166
;---------------------------------------------------------------------------------------------------------
7168
;---------------------------------------------------------------------------------------------------------
7171
;;HL points to the float
7172
;;BC points to where to output the result
7177
if defined powSingle or defined sgnSingle or defined MATH_NEG
7179
;---------------------------------------------------------------------------------------------------------
7181
;---------------------------------------------------------------------------------------------------------
7184
;;HL points to the float
7185
;;BC points to where to output the result
7191
jr nz, negSingle.test.sign
7194
jr nz, negSingle.test.sign
7197
jr nz, negSingle.test.sign
7200
jr nz, negSingle.test.sign
7211
negSingle.test.sign:
7214
jr z, negSingle.positive
7218
call negSingle.positive
7237
if defined MATH_DCOMP or defined MATH.POW or defined MATH_EXP or defined MATH_LOG or defined MATH_LN or defined MATH_SIN or defined MATH_TAN or defined MATH_COS or defined MATH_ATN
7239
;---------------------------------------------------------------------------------------------------------
7241
;---------------------------------------------------------------------------------------------------------
7244
;Input: HL points to float1, DE points to float2
7246
; float1 >= float2 : nc
7247
; float1 < float2 : c,nz
7248
; float1 == float2 : z
7249
; There is a margin of error allowed in the lower 2 bits of the mantissa.
7251
;Currently fails when both numbers have magnitude less than about 2^-106
7286
ld a,(scrap+3) ;new power
7287
pop bc ;B is old power
7297
or 1 ;not equal, so reset z flag
7298
rla ;if negative, float1<float2, setting c flag as wanted, else nc.
7308
;---------------------------------------------------------------------------------------------------------
7310
;---------------------------------------------------------------------------------------------------------
7313
;Stores a pseudo-random number on [0,1)
7314
;it won't produce values on (0,2^-23)
7323
;DEHL is the mantissa, B is the exponent
7339
;If we needed to shift more than 8 bits, we'll load in more random data
7344
jp nc,rand_no_more_rand_data
7352
rand_no_more_rand_data:
7371
;;Tested and passes all CAcert tests
7372
;;Uses a very simple 32-bit LCG and 32-bit LFSR
7373
;;it has a period of 18,446,744,069,414,584,320
7374
;;roughly 18.4 quintillion.
7375
;;LFSR taps: 0,2,6,7 = 11000101
7377
;;Thanks to Runer112 for his help on optimizing the LCG and suggesting to try the much simpler LCG. On their own, the two are terrible, but together they are great.
7378
;Uses 64 bits of state
7414
if defined MATH_FOUT
7416
;---------------------------------------------------------------------------------------------------------
7418
; in HL = Single address
7419
; BC = String address
7420
; out A = String size
7421
; http://0x80.pl/notesen/2015-12-29-float-to-string.html
7422
; http://0x80.pl/articles/convert-float-to-integer.html
7423
;---------------------------------------------------------------------------------------------------------
7437
; Move the float to scrap
7441
; Make the float negative, write a '-' if already negative
7450
ld a,'-' ; write '-' simbol
7458
; Check if the exponent field is 0 (a special value)
7465
; We should write '0' next. When rounding 9.999999... for example, not padding with a 0 will return '.' instead of '1.'
7473
; Now we need to perform signed (A-128)*77 (approximation of exponent*log10(2))
7481
ld (pow10exp_single),a ;The base-10 exponent
7485
ld de,pow10LUT ;get the table of 10^-(2^k)
7487
ld hl, pow10exp_single
7489
call singletostr_mul
7490
call singletostr_mul
7491
call singletostr_mul
7492
call singletostr_mul
7493
call singletostr_mul
7494
call singletostr_mul
7495
;now the number is pretty close to a nice value
7497
; If it is less than 1, multiply by 10
7502
;ld hl,scrap ;Since singletostr_mul returns BC = scrap, can do this cheaper
7508
ld hl,pow10exp_single
7514
; Convert to a fixed-point number !
7528
;We need to get 7 digits
7530
pop hl ;Points to the string
7532
;The first digit can be as large as 20, so it'll actually be two digits
7536
;Increment the exponent :)
7537
ld de,(pow10exp_single-1)
7539
ld (pow10exp_single-1),de
7548
; Get the remaining digits.
7555
call singletostrmul10
7560
;Save the pointer to the end of the string
7567
jr c,rounding_done_single
7568
jr _40a ;.db $DA ;start of `jp c,*` in order to skip the next instruction
7577
rounding_done_single:
7580
;Strip the leading zero if it exists (rounding may have bumped this to `1`)
7592
;Now lets move HL-DE bytes at DE+1 to DE
7604
;If z flag is reset, this means that the exponent should be bumped up 1
7605
ld a,(pow10exp_single)
7608
ld (pow10exp_single),a
7611
;if -4<=A<=6, then need to insert the decimal place somewhere.
7616
;for this, we need to insert the decimal after the first digit
7617
;Then, we need to append the exponent string
7619
ld de,strout_single-1
7621
cp '-' ;negative sign
7629
;remove any stray zeroes at the end before appending the exponent
7633
; Write the exponent
7636
ld a,(pow10exp_single)
7639
ld (hl),'-' ;negative sign
7657
ld de, strout_single
7660
ld a, l ; string size
7662
ld hl,strout_single-1
7666
ld a,(pow10exp_single)
7670
;need to put zeroes before everything
7673
cp '-' ;negative sign
7699
ld de,strout_single-1
7703
cp '-' ;negative sign
7714
ld hl,strout_single-1
7732
;multiply the 0.24 fixed point number at scrap by 10
7733
;overflow in A register
7768
;Check that the last digit isn't a decimal!
7822
;---------------------------------------------------------------------------------------------------------
7824
; https://www.ticalc.org/pub/86/asm/source/routines/atof.asm
7825
;---------------------------------------------------------------------------------------------------------
7830
ptr_sto: equ scrap+9
7832
;;#Routines/Single Precision
7834
;; HL points to the string
7835
;; BC points to where the float is output
7837
;; scrap+9 is the pointer to the end of the string
7839
;; 11 bytes at scrap ?
7844
;Check if there is a negative sign.
7853
;Skip all leading zeroes
7856
jr z,$-4 ;jumps back to the `inc hl`
7859
;Check if the next char is char_DEC
7861
or a ;to reset the carry flag
7863
jr _54a ;.db $FE ;start of cp *
7870
jr z,$-5 ;jumps back to the `dec b`
7873
;Now we read in the next 8 digits
7879
;Now `scrap` holds the 4-digit base-100 number.
7881
;if carry flag is set, just need to get rid of remaining digits
7882
;Otherwise, need to get rid of remaining digits, while incrementing the exponent
7893
jp z,strToSingle_inf
7896
;Now check for engineering `E` to modify the exponent
7900
;Gotta multiply the number at (scrap) by 2^24
7903
call scrap_times_256
7906
call scrap_times_256
7909
call scrap_times_256
7912
call scrap_times_256
7915
;Now scrap+3 is a 4-byte mantissa that needs to be normalized
7923
jp z,strToSingle_zero-1
7927
jp m,strToSingle_normed
7928
;Will need to iterate at most three times
7941
;Move the number to scrap
7950
;now (scrap) is our number, need to multiply by power of 10!
7951
;Power of 10 is stored in B, need to put in A first
7959
jp nc,strToSingle_inf+1
7962
jp nc,strToSingle_zero
7986
cp char_NEG ;negative exponent?
8038
call scrap_times_sub
8051
jr nz,strToSingle_inf
8069
if defined roundSingle or defined MATH_FRCSGL
8071
;---------------------------------------------------------------------------------------------------------
8073
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
8074
;---------------------------------------------------------------------------------------------------------
8081
ld l, (ix) ; convert integer parameter to single float
8083
ld bc, 0x1000 ; bynary digits count + sign
8085
int2Single.test.zero:
8087
or h ; test if hl is not zero
8088
jr nz, int2Single.test.negative
8090
jr nz, int2Single.test.negative
8095
int2Single.test.negative:
8096
bit 7, h ; test if hl is negative
8097
jr z, int2Single.normalize
8098
ld c, 0x80 ; sign negative
8107
int2Single.normalize:
8110
jr nz, int2Single.mount
8113
jr int2Single.normalize
8116
res 7, h ; turn off upper bit
8118
ld a, c ; restore sign
8120
ld h, a ; ...into upper mantissa
8122
ld e, h ; sign+mantissa
8123
ld h, l ; high mantissa
8124
ld l, 0 ; low mantissa
8126
ld a, b ; binary digits count
8127
or 0x80 ; exponent bias
8132
ld (ix), l ; low mantissa
8133
ld (ix+1), h ; high mantissa
8134
ld (ix+2), e ; sign + mantissa
8135
ld (ix+3), d ; expoent
8144
if defined roundSingle or defined MATH_FRCINT
8146
;---------------------------------------------------------------------------------------------------------
8148
; http://0x80.pl/articles/convert-float-to-integer.html
8149
;---------------------------------------------------------------------------------------------------------
8152
; HL points to the single-precision float
8154
; HL is the 16-bit signed integer part of the float
8155
; BC points to 16-bit signed integer
8172
jr c,no_shift_single_to_int16
8174
jr nc,no_shift_single_to_int16
8196
jr _67a ;.db $11 ;start of ld de,*
8208
no_shift_single_to_int16:
8230
;---------------------------------------------------------------------------------------------------------
8231
; Auxiliary routines
8232
;---------------------------------------------------------------------------------------------------------
8239
const_pi: db $DB,$0F,$49,$81
8240
const_e: db $54,$f8,$2d,$81
8241
const_lg_e: db $3b,$AA,$38,$80
8242
const_ln_2: db $18,$72,$31,$7f
8243
const_log2: db $9b,$20,$1a,$7e
8244
const_lg10: db $78,$9a,$54,$81
8245
const_0: db $00,$00,$00,$00
8246
const_1: db $00,$00,$00,$80
8247
const_2: dw 0, 33024
8248
const_3: dw 0, 33088
8249
const_4: dw 0, 33280
8250
const_5: dw 0, 33312
8251
const_7: dw 0, 33376
8252
const_9: dw 0, 33552
8253
const_16: dw 0, 33792
8254
const_100: db $00,$00,$48,$86
8255
const_100_inv: dw 55050, 31011
8256
const_precision: db $77,$CC,$2B,$65 ;10^-8
8257
const_half_1: dw 0, 32512
8258
const_inf: db $00,$00,$40,$00
8259
const_NegInf: db $00,$00,$C0,$00
8260
const_NaN: db $00,$00,$20,$00
8261
const_log10_e: db $D9,$5B,$5E,$7E
8262
const_2pi: db $DB,$0F,$49,$82
8263
const_2pi_inv: db $83,$F9,$22,$7D
8264
const_half_pi: dw 4059, 32841
8265
const_p25: db $00,$00,$00,$7E
8266
const_p5: db $00,$00,$00,$7F
8269
sin_a1: dw 43691, 32042
8270
sin_a2: dw 34952, 30984
8271
sin_a3: dw 3329, 29520
8272
cos_a1: equ const_half_1
8273
cos_a2: dw 43691, 31530
8274
cos_a3: dw 2914, 30262
8275
exp_a1: db $15,$72,$31,$7F ;.693146989552
8276
exp_a2: db $CE,$FE,$75,$7D ;.2402298085906
8277
exp_a3: db $7B,$42,$63,$7B ;.0554833215071
8278
exp_a4: db $FD,$94,$1E,$79 ;.00967907584392
8279
exp_a5: db $5E,$01,$23,$76 ;.001243632065103
8280
exp_a6: db $5F,$B7,$63,$73 ;.0002171671843714
8281
const_1p40625: db $00,$00,$34,$80 ;1.40625
8283
if defined MATH_CONSTSINGLE
8291
;A is the constant ID#
8292
;returns nc if failed, c otherwise
8293
;HL points to the constant
8294
cp (end_const-start_const)>>2
8301
;#if ((end_const-4)>>8)!=(start_const>>8)
8314
db $CD,$CC,$4C,$7C ;.1
8315
db $0A,$D7,$23,$79 ;.01
8316
db $17,$B7,$51,$72 ;.0001
8317
db $77,$CC,$2B,$65 ;10^-8
8318
db $95,$95,$66,$4A ;10^-16
8319
db $1F,$B1,$4F,$15 ;10^-32
8322
db $00,$00,$20,$83 ;10
8323
db $00,$00,$48,$86 ;100
8324
db $00,$40,$1C,$8D ;10000
8325
db $20,$BC,$3E,$9A ;10^8
8326
db $CA,$1B,$0E,$B5 ;10^16
8327
db $AE,$C5,$1D,$EA ;10^32
8334
;C>=128 135+6{0,33+{0,1}}+{0,20+{0,8}}
8335
;C>=64 115+5{0,33+{0,1}}+{0,20+{0,8}}
8336
;C>=32 95+4{0,33+{0,1}}+{0,20+{0,8}}
8337
;C>=16 75+3{0,33+{0,1}}+{0,20+{0,8}}
8338
;C>=8 55+2{0,33+{0,1}}+{0,20+{0,8}}
8339
;C>=4 35+{0,33+{0,1}}+{0,20+{0,8}}
8340
;C>=2 15+{0,20+{0,8}}
8343
;avg: 349.21279907227cc
8434
;26 bytes, adds 118cc to the traditional routine
8469
;c flag means don't increment the exponent
8472
jr c,ascii_to_uint8_noexp
8474
jr z,ascii_to_uint8_noexp-2
8478
jr nc,ascii_to_uint8_noexp_end
8490
jr z,ascii_to_uint8_noexp_2nd
8494
jr nc,ascii_to_uint8_noexp_end
8505
ascii_to_uint8_noexp:
8508
jr nc,ascii_to_uint8_noexp_end
8515
ascii_to_uint8_noexp_2nd:
8520
jr nc,ascii_to_uint8_noexp_end
8523
jr ascii_2 ;.db $FE ;start of `cp **`, saves 1cc
8524
ascii_to_uint8_noexp_end:
8534
if defined MATH_RSUBSINGLE
8555
jp addInject ;jumps in to the addSingle routine
8559
if defined MATH_MOD1SINGLE
8561
;This routine performs `x mod 1`, returning a non-negative value.
8584
jr z,mod1Single_special
8597
;If it is zero, need to set exponent to zero and return
8620
;make sure it isn't zero else we need to add 1
8632
;If INF, need to return NaN instead
8633
;For 0 and NaN, just return itself :)
8653
if defined MATH_FOUT
8655
; --------------------------------------------------------------
8656
; Converts a signed integer value to a zero-terminated ASCII
8657
; string representative of that value (using radix 10).
8659
; Brandon Wilson WikiTI
8660
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Other:DispA#Decimal_Signed_Version
8661
; --------------------------------------------------------------
8663
; HL Value to convert (two's complement integer).
8664
; DE Base address of string destination. (pointer).
8665
; --------------------------------------------------------------
8668
; --------------------------------------------------------------
8669
; REGISTERS/MEMORY DESTROYED
8671
; --------------------------------------------------------------
8677
; Detect sign of HL.
8681
; HL is negative. Output '-' to string and negate HL.
8686
; Negate HL (using two's complement)
8690
ld a, 0 ; Note that XOR A or SUB A would disturb CF
8694
; Convert HL to digit characters
8696
ld b, 0 ; B will count character length of number
8699
call div_hl_c; HL = HL / A, A = remainder
8706
; Retrieve digits from stack
8714
; Terminate string with NULL
8725
ld a, l ; string size
8733
;===============================================================
8734
; Convert a string of base-10 digits to a 16-bit value.
8735
; http://z80-heaven.wikidot.com/math#toc32
8737
; DE points to the base 10 number string in RAM.
8739
; HL is the 16-bit value of the number
8740
; DE points to the byte after the number
8745
; A (actually, add 30h and you get the ending token)
8748
; n is the number of digits
8750
; at most 595 cycles for any 16-bit decimal value
8751
;===============================================================
8754
ld hl,0 ; 10 : 210000
8771
jr nc,ConvLoop ;12|23: 30EE
8773
jr ConvLoop ; --- : 18EB
8780
; return remainder in a
8781
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division
8802
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
8832
djnz div_dehl_c.loop
8840
;---------------------------------------------------------------------------------------------------------
8841
; VARIABLES INITIALIZE
8842
;---------------------------------------------------------------------------------------------------------
8846
ld (VAR_DUMMY.COUNTER), a ; max circular queue = 8 dummys
8847
ld hl, VAR_DUMMY.DATA ; start of variable dummy circular queue
8848
ld (VAR_DUMMY.POINTER), hl
8849
ld b, VAR_DUMMY.LENGTH
8854
djnz INITIALIZE_DUMMY.1
8859
ld (BASIC_DATPTR), hl ; next DATA pointer to use by READ command
8861
ld (BASIC_DATLIN), hl ; index of DATA item to use by READ command
8864
INITIALIZE_VARIABLES:
8865
call INITIALIZE_DATA
8866
call INITIALIZE_DUMMY
8869
call gfxInitSpriteCollisionTable
8872
;if defined COMPILE_TO_ROM
8873
; ld ix, BIOS_JIFFY ; initialize rom clock
8883
;---------------------------------------------------------------------------------------------------------
8884
; MAIN WORK AREA - LITERALS / VARIABLES / CONFIGURATIONS
8885
;---------------------------------------------------------------------------------------------------------
8887
if defined COMPILE_TO_ROM
8890
pgmPage1.pad: equ pageSize - (workAreaPad - pgmArea)
8892
if pgmPage1.pad >= 0
8895
; .WARNING "There's no free space left on program page 1"
8900
VAR_STACK.START: equ ramArea
8901
;VAR_STACK.END: equ VAR_STACK.START + 0x800 ; 2kb (~200 variables)
8903
VAR_STACK.POINTER: equ VAR_STACK.START
8905
PRINT.CRLF: db 3, 0, 0, 2
8906
dw PRINT.CRLF.DATA, 0, 0, 0
8907
PRINT.CRLF.DATA: db 13,10,0
8909
PRINT.TAB: db 3, 0, 0, 1
8910
dw PRINT.TAB.DATA, 0, 0, 0
8911
PRINT.TAB.DATA: db 09,0
8914
LIT_NULL_DBL: dw 0, 0, 0, 0
8920
LIT_QUOTE_CHAR: db '\"'
8923
LIT_TRUE: db 2, 0, 0
8927
LIT_FALSE: db 2, 0, 0
9135
AFTER_LAST_VARIABLE: equ VAR_STACK.POINTER + 0
9137
VAR_DUMMY.START: equ AFTER_LAST_VARIABLE ; variable dummy circular queue area
9138
VAR_DUMMY.COUNTER: equ VAR_DUMMY.START ; variable dummy circular queue count
9139
VAR_DUMMY.POINTER: equ VAR_DUMMY.COUNTER + 1 ; pointer to next variable dummy
9140
VAR_DUMMY.DATA: equ VAR_DUMMY.POINTER + 2 ; first variable dummy
9142
VAR_DUMMY.SIZE: equ 8
9143
VAR_DUMMY.LENGTH: equ (11 * VAR_DUMMY.SIZE)
9144
VAR_DUMMY.END: equ VAR_DUMMY.DATA + VAR_DUMMY.LENGTH
9145
VAR_STACK.END: equ VAR_DUMMY.END + 1
9147
;--------------------------------------------------------
9149
;--------------------------------------------------------
9152
DATA_ITEMS_COUNT: equ 0
9154
DATA_SET_ITEMS_START:
9155
DATA_SET_ITEMS_COUNT: equ 0
9158
;---------------------------------------------------------------------------------------------------------
9160
;---------------------------------------------------------------------------------------------------------
9162
if defined COMPILE_TO_ROM
9166
pgmPage2.pad: equ romSize - (romPad - pgmArea)
9168
if pgmPage2.pad >= 0
9171
if pgmPage2.pad < lowLimitSize
9172
.WARNING "There's only less than 5% free space on this ROM"
9175
.ERROR "There's no free space left on this ROM"
9180
end_file: end start_pgm ; label start is the entry point