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
ld hl, LIT_4 ; parameter
688
call SCREEN ; action call
691
ld hl, LIT_8 ; parameter
693
call BASE ; action call
694
call PRINT ; action call
695
ld hl, PRINT.TAB ; parameter
697
call PRINT ; action call
698
ld hl, LIT_9 ; parameter
700
call BASE ; action call
701
call PRINT ; action call
702
ld hl, PRINT.TAB ; parameter
704
call PRINT ; action call
705
ld hl, LIT_10 ; parameter
707
call BASE ; action call
708
call PRINT ; action call
709
ld hl, PRINT.TAB ; parameter
711
call PRINT ; action call
712
ld hl, LIT_11 ; parameter
714
call BASE ; action call
715
call PRINT ; action call
716
ld hl, PRINT.CRLF ; parameter
718
call PRINT ; action call
721
ld hl, LIT_13 ; parameter
723
call PEEK ; action call
724
call PRINT ; action call
725
ld hl, PRINT.CRLF ; parameter
727
call PRINT ; action call
730
ld hl, LIT_15 ; parameter
732
call BASE ; action call
733
call VPEEK ; action call
734
call PRINT ; action call
735
ld hl, PRINT.CRLF ; parameter
737
call PRINT ; action call
740
call PAUSE ; action call
742
;---------------------------------------------------------------------------------------------------------
744
;---------------------------------------------------------------------------------------------------------
746
end_pgm: __call_bios BIOS_DSPFNK ; turn on function keys display
748
ld (BIOS_CLIKSW), a ; enable keyboard click
750
if defined COMPILE_TO_ROM
753
__call_basic BASIC_READYR ; warm start Basic
756
ret ; end of the program
758
;__call_bios BIOS_GICINI ; initialize sound system
759
;if defined COMPILE_TO_DOS or defined COMPILE_TO_ROM
760
; __call_bios BIOS_RESET ; restart Basic
762
; __call_basic BASIC_END ; end to Basic
766
;---------------------------------------------------------------------------------------------------------
768
;---------------------------------------------------------------------------------------------------------
773
; out IX = variable assigned address
774
pop.parm ; get variable address parameter
775
push hl ; just to transfer hl to ix
777
ld a, (ix) ; get variable type
778
cp 3 ; test if string
779
jr nz, LET.PARM ; if not a string, it isn't necessary to free memory
780
ld a, (ix + 3) ; get variable string length
782
jr z, LET.PARM ; if zero, it isn't necessary to free memory
783
ld c, (ix + 4) ; get old string address low
784
ld b, (ix + 5) ; get old string address high
785
push ix ; save variable address
786
push bc ; just to transfer bc (old string address) to ix
788
call memory.free ; free memory
789
pop ix ; restore variable address
790
LET.PARM: pop.parm ; get data address parameter (out hl = data address)
791
ld a, (ix + 2) ; get variable type flag
792
or a ; cp 0 - test type flag (0=any, 255=fixed)
793
jr nz, LET.FIXED ; if type flag is fixed, so casting is necessary
794
LET.ANY: push ix ; just to transfer ix (variable address) to de
796
ldi ; copy 1 byte from hl (data address) to de (variable address)
797
inc de ; go to variable data area
799
inc hl ; go to data data area
801
ld bc, 8 ; data = 8 bytes
802
ldir ; copy bc bytes from hl (data address) to de (variable address)
803
ld a, (ix) ; get variable type
804
cp 3 ; test if string
805
ret nz ; if not string, return
806
jp LET.STRING ; else do string treatment (in ix = variable address)
807
LET.FIXED: push ix ; save variable destination address
808
push hl ; save variable source address
809
ld a, (ix) ; get variable fixed type, and hl has parameter data address
810
call CAST_TO ; cast data to type (in hl = variable address, a = type to, out hl = casted data address)
812
pop ix ; restore variable address
813
ld a, (ix) ; get variable destination type again
814
cp 3 ; test if string
815
jr nz, LET.VALUE ; if not string, do value treatment
816
ld a, (de) ; get variable source type again
817
cp 3 ; test if string
818
jr nz, LET.FIX1 ; if not string, get casted string size
823
ld (ix + 3), a ; source string size
826
call GET_STR.LENGTH ; get string length (in HL, out B)
828
ld (ix + 3), b ; set variable length
829
LET.FIX2: ld (ix + 4), l ; casted data address low
830
ld (ix + 5), h ; casted data address high
831
jp LET.STRING ; do string treatment (in ix = variable address)
832
LET.VALUE: push ix ; just to transfer ix (variable address) to de
834
inc de ; go to variable data area (and the data from its casted)
837
ld bc, 8 ; data = 8 bytes
838
ldir ; copy bc bytes from hl (data address) to de (variable address)
840
LET.STRING: ld a, (ix + 3) ; string size
841
or a ; cp 0 - test if null
842
jr nz, LET.ALLOC ; if not null, allocate new string (in ix = variable address)
843
ld bc, LIT_NULL_STR ; else, set to a null string literal
844
ld (ix + 4), c ; variable address low
845
ld (ix + 5), b ; variable address high
847
LET.ALLOC: push ix ; save variable address
848
ld l, (ix + 4) ; source string address low
849
ld h, (ix + 5) ; source string address high
850
push hl ; save copy from address
851
ld c, (ix + 3) ; get variable length
853
inc bc ; string length have one more byte from zero terminator
854
push bc ; save variable lenght + 1
855
call memory.alloc ; in bc = size, out ix = address, nz=OK
857
push ix ; just to transfer memory address from ix to de
859
pop bc ; restore bytes to be copied
860
pop hl ; restore copy from string address
861
push de ; save copy to address
862
ldir ; copy bc bytes from hl (data address) to de (variable address)
865
pop de ; restore copy to address
866
pop ix ; restore variable address
867
ld (ix + 4), e ; put memory address low into variable
868
ld (ix + 5), d ; put memory address high into variable
869
ret ; variable assigned
874
pop.parm ; get parameter boolean result in hl
877
ld a, (ix+5) ; put boolean integer result in a
883
pop.parm ; get first parameter
885
call GET_INT.VALUE ; output BC with integer value
886
ld a, c ; A = screen number (0 to 3)
888
jr c, SCREEN.1 ; if mode < 9, jump
889
ld a, 8 ; else, fix to 8
891
if defined EXIST_DATA_SET
892
call gfxSetScreenMode
896
jp gfxClearTileScreen
904
pop.parm ; get first parameter
907
ret z ; return if string size zero
908
if defined EXIST_DATA_SET
909
ld (BIOS_TEMP), a ; size of string
913
; discard if first char < 32 or > 126
920
; adjust default color
933
call gfxSetTileDefaultColor
942
;call MATH.MULT.16 ; slow y * 32
969
pop.parm ; get first parameter in HL
971
call GET_INT.VALUE ; put parameter into BC
980
call COPY_TO.VAR_DUMMY.INT ; create a fake integer variable from BC in HL
986
pop.parm ; get first parameter in HL
988
call GET_INT.VALUE ; put parameter into BC
989
;push bc ; address of data
999
call COPY_TO.VAR_DUMMY.INT ; create a fake integer variable from BC in HL
1005
pop.parm ; get first parameter in HL
1007
call GET_INT.VALUE ; put parameter into BC
1008
;push bc ; address of data
1018
call COPY_TO.VAR_DUMMY.INT ; create a fake integer variable from BC in HL
1030
__call_bios BIOS_BEEP
1031
__call_bios BIOS_CHGET
1042
;---------------------------------------------------------------------------------------------------------
1043
; MSX BASIC SUPPORT CODE
1044
;---------------------------------------------------------------------------------------------------------
1046
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
1050
RUN_TRAPS.1: push hl
1061
; in hl = trap block address (handle trap: sts=5? has handler? ackn, pause, run trap, sts=1? unpause)
1063
ld a, (hl) ; trap status
1064
cp 5 ; trap occured AND trap not paused AND trap enabled ?
1065
ret nz ; return if false
1067
ld e, (hl) ; get trap address
1074
ret z ; return if address zero
1076
__call_basic BASIC_TRAP_ACKNW
1077
__call_basic BASIC_TRAP_PAUSE
1078
ld hl, TRAP_HANDLER.1
1079
ld a, (BASIC_ONGSBF) ; save traps execution
1082
ld (BASIC_ONGSBF), a ; disable traps execution
1083
push hl ; next return will be to trap handler
1084
push de ; indirect jump to trap address
1086
TRAP_HANDLER.1: pop af
1087
ld (BASIC_ONGSBF), a ; restore traps execution
1090
cp 1 ; trap enabled?
1092
__call_basic BASIC_TRAP_UNPAUSE
1095
; hl = trap block, de = trap handler
1097
ld (hl), a ; trap block status
1099
ld (hl), e ; trap block handler (pointer)
1106
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
1109
ld (BIOS_TEMP), a ; save voice number
1113
ret nz ; return if not string
1116
ld (BIOS_TEMP2), a ; save string size
1117
push hl ; string address
1118
ld a, (BIOS_TEMP) ; restore voice number
1119
call BIOS_GETVCP ; get PSG voice buffer address (in A = voice number, out HL = address of byte 2)
1121
ld a, (BIOS_TEMP2) ; restore string size
1122
ld (hl), a ; string size
1124
ld (hl), e ; string address
1128
ld D,H ; voice stack
1143
ld hl, BIOS_TEMP ; voice count
1157
__call_basic BASIC_PLAY_DIRECT
1164
;---------------------------------------------------------------------------------------------------------
1165
; VARIABLES ROUTINES
1166
;---------------------------------------------------------------------------------------------------------
1168
; input hl = variable address
1169
; input bc = variable name
1170
; input d = variable type
1171
INIT_VAR: ld (hl), d ; variable type
1173
ld (hl), c ; variable name 1
1175
ld (hl), b ; variable name 2
1189
CLEAR.VAR.LOOP: inc hl
1190
ld (hl), 0 ; data address/value
1193
; input HL = variable address
1194
; input A = variable output type
1195
; output HL = casted data address
1205
; input HL = variable address
1206
; output HL = variable address
1207
CAST_TO.INT: ;push af
1212
jp z, CAST_STR_TO.INT
1214
jp z, CAST_SGL_TO.INT
1216
jp z, CAST_DBL_TO.INT
1219
; input HL = variable address
1220
; output HL = variable address
1221
CAST_TO.STR: ;push af
1224
jp z, CAST_INT_TO.STR
1228
jp z, CAST_SGL_TO.STR
1230
jp z, CAST_DBL_TO.STR
1233
; input HL = variable address
1234
; output HL = variable address
1235
CAST_TO.SGL: ;push af
1238
jp z, CAST_INT_TO.SGL
1240
jp z, CAST_STR_TO.SGL
1244
jp z, CAST_DBL_TO.SGL
1247
; input HL = variable address
1248
; output HL = variable address
1249
CAST_TO.DBL: ;push af
1252
jp z, CAST_INT_TO.DBL
1254
jp z, CAST_STR_TO.DBL
1256
jp z, CAST_SGL_TO.DBL
1261
CAST_SGL_TO.STR: ; same as CAST_INT_TO.STR
1262
CAST_DBL_TO.STR: ; same as CAST_INT_TO.STR
1263
CAST_INT_TO.STR: call COPY_TO.DAC
1265
__call_bios MATH_FOUT ; convert DAC to string
1268
CAST_INT_TO.SGL: call COPY_TO.DAC
1269
__call_bios MATH_FRCSGL
1272
CAST_INT_TO.DBL: call COPY_TO.DAC
1273
__call_bios MATH_FRCDBL
1276
CAST_SGL_TO.INT: ; same as CAST_DBL_TO.INT
1277
CAST_DBL_TO.INT: call COPY_TO.DAC
1278
__call_bios MATH_FRCINT
1281
CAST_STR_TO.INT: call CAST_STR_TO.VAL ;
1282
__call_bios MATH_FRCINT ;
1285
CAST_STR_TO.SGL: call CAST_STR_TO.VAL ;
1286
__call_bios MATH_FRCSGL ;
1289
CAST_STR_TO.DBL: call CAST_STR_TO.VAL ;
1290
__call_bios MATH_FRCDBL ;
1293
CAST_STR_TO.VAL: call GET_STR.ADDR ;
1295
__call_bios MATH_FIN ; convert string to a value type
1298
GET_INT.VALUE: inc hl ; output BC with integer value
1304
CAST_SGL_TO.DBL: ; same as GET_DBL.ADDR
1305
CAST_DBL_TO.SGL: ; same as GET_DBL.ADDR
1306
GET_INT.ADDR: ; same as GET_DBL.ADDR
1307
GET_SGL.ADDR: ; same as GET_DBL.ADDR
1308
GET_DBL.ADDR: inc hl
1313
GET_STR.ADDR: push hl
1319
; input hl = string address
1320
; output b = string length
1321
GET_STR.LENGTH: ld b, 0
1322
GET_STR.LEN.NEXT: ld a, (hl)
1329
jr z, GET_STR.LEN.ERR
1331
GET_STR.LEN.ERR: ld b, 0
1333
STRING.COMPARE: ld ix, (BASIC_DAC+1) ; string 1
1334
ld iy, (BASIC_ARG+1) ; string 2
1335
STRING.COMPARE.NX: ld a, (ix) ; next char from string 1
1336
cp (iy) ; char s1 = char s2?
1337
jr nz, STRING.COMPARE.NE ; if not equal...
1339
jr z, STRING.COMPARE.F1 ; if string 1 has finished...
1340
ld a, (iy) ; next char from string 2
1342
jr z, STRING.COMPARE.GT ; if s2 has finished, s1 has not finished yet, so s1 is greater than s2
1345
jr STRING.COMPARE.NX ; get next char pair
1346
STRING.COMPARE.F1: ld a, (iy) ; verify if string 2 has finished too
1348
jr z, STRING.COMPARE.EQ ; if s2 has finished, then they are equals
1349
jr STRING.COMPARE.LT ; else, result = s1 is less than s2
1350
STRING.COMPARE.NE: jr c, STRING.COMPARE.GT ; verify if s1 is greater than s2...
1351
STRING.COMPARE.LT: ld a, 1 ; ...else, result = s1 less than s2
1353
STRING.COMPARE.GT: ld a, 0xFF ; result = s1 is greater than s2
1355
STRING.COMPARE.EQ: xor a ; result = s1 is equal to s2
1357
STRING.CONCAT: ld ix, BASIC_DAC ; s1 size
1358
ld a, (BASIC_ARG) ; s2 size
1359
add a, (ix) ; s3 size = s1 size + s2 size
1363
inc bc ; add 1 byte to size
1364
call memory.alloc ; in bc size, out ix new memory address, nz=OK
1365
jp z, memory.error ;
1369
ld a, (BASIC_DAC) ; s1 size
1370
ld hl, (BASIC_DAC + 1) ; string 1
1371
call COPY_TO.STR ; copy to new memory
1372
ld a, (BASIC_ARG) ; s2 size
1373
ld hl, (BASIC_ARG + 1) ; string 2
1374
call COPY_TO.STR ; copy to new memory
1376
ld (de), a ; null terminated
1379
call COPY_TO.VAR_DUMMY.STR ;
1380
ret.parm ; WARNING - VERIFY STRING MEMORY LEAKs
1381
STRING.PRINT: ld a, (BIOS_SCRMOD) ; 0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode
1383
jr nc, STRING.PRINT.G2 ; jump if graphic screen mode MSX2 (>=5)
1385
jr nc, STRING.PRINT.G1 ; jump if graphic screen mode MSX1 (>=2)
1386
STRING.PRINT.T: ld a, (hl) ; get a char from a string parameter
1387
or a ; cp 0 - is it the string end?
1389
__call_bios BIOS_CHPUT ; put the char (a) into text screen
1391
jr STRING.PRINT.T ; repeat
1392
STRING.PRINT.G1: ld a, (hl) ; get a char from a string parameter
1393
or a ; cp 0 - is it the string end?
1395
__call_bios BIOS_GRPPRT ; put the char (a) into graphical screen
1397
jr STRING.PRINT.G1 ; repeat
1398
STRING.PRINT.G2: ld a, (hl) ; get a char from a string parameter
1399
or a ; cp 0 - is it the string end?
1401
ld ix, BIOS_GRPPRT2 ; put the char (a) into graphical screen
1404
jr STRING.PRINT.G2 ; repeat
1406
; a = string size to copy
1407
; input hl = string from
1408
; input de = string to
1410
ret z ; avoid copy if size = zero
1412
ld c, a ; string size
1413
ldir ; copy bc bytes from hl to de
1415
COPY_TO.BASIC_BUF: ld bc, BASIC_BUF
1416
ld a, (LIT_QUOTE_CHAR)
1419
COPY_BAS_BUF.LOOP: ld a, (hl)
1421
jr z, COPY_BAS_BUF.EXIT
1425
jr COPY_BAS_BUF.LOOP
1426
COPY_BAS_BUF.EXIT: ld a, (LIT_QUOTE_CHAR)
1433
COPY_TO.VAR_DUMMY: ld a, (BASIC_VALTYP) ; create dummy variable from VALTYPE
1435
jr nz, COPY_TO.VAR_DUMMY.DBL
1437
call GET_STR.LENGTH ; get string length
1439
ld a, b ; string length
1440
COPY_TO.VAR_DUMMY.STR: call GET_VAR_DUMMY.ADDR ; create dummy string variable from HL
1441
ld (ix), 3 ; data type string
1443
ld (ix+2), 255 ; var type fixed
1444
ld (ix+3), a ; string length
1445
ld (ix+4), l ; data address low
1446
ld (ix+5), h ; data address high
1447
;call GET_STR.LENGTH ; get string length
1448
;ld (ix+3), b ; string length
1449
push ix ; output var address...
1452
COPY_TO.VAR_DUMMY.INT: call GET_VAR_DUMMY.ADDR ; create dummy integer variable from BC
1453
ld (ix), 2 ; data type string
1464
push ix ; output var address...
1467
COPY_TO.VAR_DUMMY.DBL: call GET_VAR_DUMMY.ADDR ; create dummy value variable from DAC
1468
ld (ix), a ; data type
1473
push ix ; just to copy ix to de
1478
ldir ; copy bc bytes from hl (data address) to de (variable address)
1479
push ix ; output var address...
1482
GET_VAR_DUMMY.ADDR: push af ;
1485
ld ix, (VAR_DUMMY.POINTER) ;
1486
ld a, (VAR_DUMMY.COUNTER) ;
1487
GET_VAR_DUMMY.NEXT: add ix, de ;
1490
jr nz, GET_VAR_DUMMY.EXIT ;
1492
ld ix, VAR_DUMMY.DATA ;
1493
GET_VAR_DUMMY.EXIT: ld (VAR_DUMMY.POINTER), ix ;
1494
ld (VAR_DUMMY.COUNTER), a ;
1495
ld a, (ix) ; get last var dummy type
1496
cp 3 ; is it string?
1497
call z, GET_VAR_DUMMY.FREE ; free string memory
1504
ld l, (ix+4) ; get string data address
1508
call memory.free ; free memory
1512
; input hl = variable address
1513
COPY_TO.DAC: ld de, BASIC_DAC
1514
COPY_TO.DAC.DATA: ld a, (hl)
1515
ld (BASIC_VALTYP), a
1519
ld bc, 8 ; data = 8 bytes
1520
ldir ; copy bc bytes from hl (data address) to de (variable address)
1522
COPY_TO.ARG: ld de, BASIC_ARG ;
1523
jr COPY_TO.DAC.DATA ;
1524
COPY_TO.DAC_ARG: ld hl, BASIC_DAC ;
1526
ld bc, 8 ; data = 8 bytes
1527
ldir ; copy bc bytes from hl (data address) to de (variable address)
1529
COPY_TO.ARG_DAC: ld hl, BASIC_ARG ;
1531
ld bc, 8 ; data = 8 bytes
1532
ldir ; copy bc bytes from hl (data address) to de (variable address)
1534
COPY_TO.DAC_TMP: ld hl, BASIC_DAC ;
1535
ld de, BASIC_SWPTMP ;
1536
ld bc, 8 ; data = 8 bytes
1537
ldir ; copy bc bytes from hl (data address) to de (variable address)
1539
COPY_TO.TMP_DAC: ld hl, BASIC_SWPTMP ;
1541
ld bc, 8 ; data = 8 bytes
1542
ldir ; copy bc bytes from hl (data address) to de (variable address)
1545
exx ; save registers
1548
ld de, BASIC_SWPTMP ;
1549
ldir ; copy bc bytes from hl to de
1553
ldir ; copy bc bytes from hl to de
1555
ld hl, BASIC_SWPTMP ;
1557
ldir ; copy bc bytes from hl to de
1558
exx ; restore registers
1561
CLEAR.DAC: ld de, BASIC_DAC
1562
CLEAR.DAC.DATA: ld hl, BASIC_VALTYP
1565
ld bc, 8 ; data = 8 bytes
1566
ldir ; copy bc bytes from hl (data address) to de (variable address)
1568
CLEAR.ARG: ld de, BASIC_ARG
1573
;---------------------------------------------------------------------------------------------------------
1574
; MATH 16 BITS ROUTINES
1575
;---------------------------------------------------------------------------------------------------------
1577
MATH.PARM.POP: pop af ; get PC from caller stack
1578
ex af, af' ; save PC to temp
1579
pop.parm ; get first parameter
1580
call COPY_TO.ARG ; put HL in ARG (return var type in A)
1581
pop.parm ; get second parameter
1582
ex af, af' ; restore PC from temp
1583
push af ; put again PC from caller in stack
1584
ex af, af' ; restore 1st data type
1585
push af ; save 1st data type
1586
call COPY_TO.DAC ; put HL in DAC (return var type in A)
1587
pop bc ; restore 1st data type (ARG) in B
1588
cp b ; test if data type in A (DAC) = data type in B (ARG)
1589
ret z ; return if is equal data types
1590
MATH.PARM.CAST: push bc ; else cast both to double
1591
and 12 ; test if single/double
1592
jr nz, MATH.PARM.CST1 ; avoid cast if already single/double
1593
__call_bios MATH_FRCDBL ; convert DAC to double
1594
MATH.PARM.CST1: pop af ;
1595
and 12 ; test if single/double
1596
jr nz, MATH.PARM.CST2 ; avoid cast if already single/double
1597
ld (BASIC_VALTYP), a ;
1598
call COPY_TO.DAC_TMP ;
1599
call COPY_TO.ARG_DAC ;
1600
__call_bios MATH_FRCDBL ; convert ARG to double
1601
call COPY_TO.DAC_ARG ;
1602
call COPY_TO.TMP_DAC ;
1603
MATH.PARM.CST2: ld a, 8 ;
1604
ld (BASIC_VALTYP), a ;
1606
MATH.PARM.POP.INT: ; return result in DAC/ARG as integer
1607
pop af ; get PC from caller stack
1608
ex af, af' ; save PC to temp
1609
pop.parm ; get first parameter
1610
ld a, (hl) ; get parameter type
1611
and 2 ; test if integer
1612
jr z, MATH.PARM.POP.I1 ; do cast if not integer
1613
call COPY_TO.ARG ; put HL in ARG (return var type in A)
1614
jr MATH.PARM.POP.I2 ; go to next parameter
1615
MATH.PARM.POP.I1: call COPY_TO.DAC ; put HL in DAC (return var type in A)
1616
__call_bios MATH_FRCINT ; convert DAC to int
1617
call COPY_TO.DAC_ARG ; copy DAC to ARG
1618
MATH.PARM.POP.I2: pop.parm ; get second parameter
1619
call COPY_TO.DAC ; put HL in DAC (return var type in A)
1620
and 2 ; test if integer
1621
jr nz, MATH.PARM.POP.I3 ; avoid cast if already integer
1622
__call_bios MATH_FRCINT ; convert DAC to int
1624
ld (BASIC_VALTYP), a ;
1626
ex af, af' ; restore PC from temp
1627
push af ; put again PC from caller in stack
1629
MATH.PARM.PUSH: call COPY_TO.VAR_DUMMY ;
1635
; output in parm stack
1636
; http://www.z80.info/zip/zaks_book.pdf - page 104
1637
MATH.ADD.INT: ld hl, (BASIC_DAC+2) ;
1638
ld bc, (BASIC_ARG+2) ;
1640
ld (BASIC_DAC+2), hl ;
1645
if defined MATH.SUB or defined MATH.NEG
1648
; output in parm stack
1649
; http://www.z80.info/zip/zaks_book.pdf - page 104
1650
MATH.SUB.INT: ld hl, (BASIC_DAC+2) ;
1651
ld de, (BASIC_ARG+2) ;
1654
ld (BASIC_DAC+2), hl ;
1659
if defined MATH.MULT
1662
; output in parm stack
1663
MATH.MULT.INT: ld hl, (BASIC_DAC+2) ;
1664
ld bc, (BASIC_ARG+2) ;
1666
ld (BASIC_DAC+2), hl ;
1669
; input HL = multiplicand
1670
; input BC = multiplier
1671
; output HL = result
1672
; http://www.z80.info/zip/zaks_book.pdf - page 131
1673
MATH.MULT.16: ld a, c ; low multiplier
1674
ld c, b ; high multiplier
1676
ld d, h ; multiplicand
1679
MULT16LOOP: srl c ; right shift multiplier high
1680
rra ; rotate right multiplier low
1681
jr nc, MULT16NOADD ; test carry
1682
add hl, de ; add multiplicand to result
1683
MULT16NOADD: ex de, hl
1684
add hl, hl ; double - shift multiplicand
1691
if defined MATH.DIV or defined MATH.IDIV or defined MATH.MOD
1693
; input AC = dividend
1694
; input DE = divisor
1695
; output AC = quotient
1696
; output HL = remainder
1697
; http://www.z80.info/zip/zaks_book.pdf - page 140
1698
MATH.DIV.16: ld hl, 0 ; clear accumulator
1699
ld b, 16 ; set counter
1700
DIV16LOOP: rl c ; rotate accumulator result left
1702
adc hl, hl ; left shift
1703
sbc hl, de ; trial subtract divisor
1704
jr nc, $ + 3 ; subtract was OK ($ = current location)
1705
add hl, de ; restore accumulator
1706
ccf ; calculate result bit
1707
djnz DIV16LOOP ; counter not zero
1708
rl c ; shift in last result bit
1714
if defined GFX_FAST or defined LINE
1716
; compare two signed 16 bits integers
1717
; HL < DE: Carry flag
1718
; HL = DE: Zero flag
1719
; http://www.z80.info/zip/zaks_book.pdf - page 531
1720
MATH.COMP.S16: ld a, h ; test high order byte
1721
and 0x80 ; test sign, clear carry
1722
jr nz, MATH.COMP.S16.NEGM1 ; jump if hl is negative
1724
ret nz ; de is negative (and hl is positive)
1726
cp d ; signs are both positive, so normal compare
1728
ld a, l ; test low order byte
1731
MATH.COMP.S16.NEGM1:
1733
rla ; sign bit into carry
1734
ret c ; signs different
1736
cp d ; both signs negative
1746
MATH.ADD.SGL: ld a, 8 ;
1747
ld (BASIC_VALTYP), a ;
1748
MATH.ADD.DBL: __call_bios MATH_DECADD ;
1753
if defined MATH.SUB or defined MATH.NEG
1755
MATH.SUB.SGL: ld a, 8 ;
1756
ld (BASIC_VALTYP), a ;
1757
MATH.SUB.DBL: __call_bios MATH_DECSUB ;
1762
if defined MATH.MULT
1764
MATH.MULT.SGL: ld a, 8 ;
1765
ld (BASIC_VALTYP), a ;
1766
MATH.MULT.DBL: __call_bios MATH_DECMUL ;
1774
; output in parm stack
1775
MATH.DIV.INT: __call_bios MATH_FRCDBL ; convert DAC to double
1778
ld (BASIC_VALTYP), a ;
1779
__call_bios MATH_FRCDBL ; convert ARG to double
1781
MATH.DIV.SGL: ld a, 8 ;
1782
ld (BASIC_VALTYP), a ;
1783
MATH.DIV.DBL: __call_bios MATH_DECDIV ;
1788
if defined MATH.IDIV
1791
; output in parm stack
1792
MATH.IDIV.SGL: ld a, 8 ;
1793
ld (BASIC_VALTYP), a ;
1794
MATH.IDIV.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
1797
ld (BASIC_VALTYP), a ;
1798
__call_bios MATH_FRCINT ; convert ARG to integer
1800
MATH.IDIV.INT: ld hl, (BASIC_DAC+2) ;
1803
ld de, (BASIC_ARG+2) ;
1807
ld (BASIC_DAC+2), hl ; quotient
1814
MATH.POW.INT: ld (BASIC_VALTYP), a ;
1815
__call_bios MATH_FRCDBL ; convert DAC to double
1818
ld (BASIC_VALTYP), a ;
1819
__call_bios MATH_FRCDBL ; convert ARG to double
1821
MATH.POW.SGL: ld a, 8 ;
1822
ld (BASIC_VALTYP), a ;
1823
MATH.POW.DBL: __call_bios MATH_DBLEXP ;
1830
;MATH.MOD.SGL: ld a, 8 ;
1831
; ld (BASIC_VALTYP), a ;
1832
;MATH.MOD.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
1833
; call SWAP.DAC.ARG ;
1835
; ld (BASIC_VALTYP), a ;
1836
; __call_bios MATH_FRCINT ; convert ARG to integer
1837
; call SWAP.DAC.ARG ;
1838
MATH.MOD.INT: ld hl, (BASIC_DAC+2) ;
1841
ld de, (BASIC_ARG+2) ;
1843
ld (BASIC_DAC+2), hl ; remainder
1850
; fast 16-bit integer square root
1851
; http://www.retroprogramming.com/2017/07/a-fast-z80-integer-square-root.html
1852
; 92 bytes, 344-379 cycles (average 362)
1853
; v2 - 3 t-state optimization spotted by Russ McNulty
1854
; call with hl = number to square root
1855
; returns a = square root
1932
if defined RANDOMIZE or defined SEED
1934
MATH.RANDOMIZE: di ;
1935
ld bc, (BIOS_JIFFY) ;
1938
MATH.SEED: ld (BASIC_RNDX), bc ; seed to IRND
1939
push bc ; in bc = new integer seed
1943
ld (BASIC_DAC+2), bc ; copy bc to dac
1944
ld a, 2 ; type integer
1945
ld (BASIC_VALTYP), a ;
1946
__call_bios MATH_FRCDBL ; convert DAC integer to DAC double
1947
__call_bios MATH_NEG ; DAC = -DAC
1948
__call_bios MATH_RND ; put in DAC a new random number from previous DAC parameter
1953
MATH.ERROR: ld e, 13 ; type mismatch
1954
__call_basic BASIC_ERROR_HANDLER ;
1958
;---------------------------------------------------------------------------------------------------------
1960
;---------------------------------------------------------------------------------------------------------
1962
BOOLEAN.RET.TRUE: ld hl, LIT_TRUE ;
1964
BOOLEAN.RET.FALSE: ld hl, LIT_FALSE ;
1966
BOOLEAN.CMP.INT: ld hl, (BASIC_DAC+2) ;
1967
ld de, (BASIC_ARG+2) ;
1968
__call_bios MATH_ICOMP ;
1970
BOOLEAN.CMP.SGL: ld bc, (BASIC_ARG) ;
1971
ld de, (BASIC_ARG+2) ;
1972
__call_bios MATH_DCOMP ;
1974
BOOLEAN.CMP.DBL: __call_bios MATH_XDCOMP ;
1976
BOOLEAN.CMP.STR: call STRING.COMPARE ;
1979
if defined BOOLEAN.GT
1981
BOOLEAN.GT.INT: call BOOLEAN.CMP.INT ;
1983
BOOLEAN.GT.STR: call BOOLEAN.CMP.STR ;
1985
BOOLEAN.GT.SGL: call BOOLEAN.CMP.SGL ;
1987
BOOLEAN.GT.DBL: call BOOLEAN.CMP.DBL ;
1989
BOOLEAN.GT.RET: cp 0x01 ;
1990
jp z, BOOLEAN.RET.TRUE ;
1991
jp BOOLEAN.RET.FALSE ;
1994
if defined BOOLEAN.LT
1996
BOOLEAN.LT.INT: call BOOLEAN.CMP.INT ;
1998
BOOLEAN.LT.STR: call BOOLEAN.CMP.STR ;
2000
BOOLEAN.LT.SGL: call BOOLEAN.CMP.SGL ;
2002
BOOLEAN.LT.DBL: call BOOLEAN.CMP.DBL ;
2004
BOOLEAN.LT.RET: cp 0xFF ;
2005
jp z, BOOLEAN.RET.TRUE ;
2006
jp BOOLEAN.RET.FALSE ;
2010
if defined BOOLEAN.GE
2012
BOOLEAN.GE.INT: call BOOLEAN.CMP.INT ;
2014
BOOLEAN.GE.STR: call BOOLEAN.CMP.STR ;
2016
BOOLEAN.GE.SGL: call BOOLEAN.CMP.SGL ;
2018
BOOLEAN.GE.DBL: call BOOLEAN.CMP.DBL ;
2020
BOOLEAN.GE.RET: cp 0x01 ;
2021
jp z, BOOLEAN.RET.TRUE ;
2023
jp z, BOOLEAN.RET.TRUE ;
2024
jp BOOLEAN.RET.FALSE ;
2028
if defined BOOLEAN.LE
2030
BOOLEAN.LE.INT: call BOOLEAN.CMP.INT ;
2032
BOOLEAN.LE.STR: call BOOLEAN.CMP.STR ;
2034
BOOLEAN.LE.SGL: call BOOLEAN.CMP.SGL ;
2036
BOOLEAN.LE.DBL: call BOOLEAN.CMP.DBL ;
2038
BOOLEAN.LE.RET: cp 0xFF ;
2039
jp z, BOOLEAN.RET.TRUE ;
2041
jp z, BOOLEAN.RET.TRUE ;
2042
jp BOOLEAN.RET.FALSE ;
2046
if defined BOOLEAN.NE
2048
BOOLEAN.NE.INT: call BOOLEAN.CMP.INT ;
2050
BOOLEAN.NE.STR: call BOOLEAN.CMP.STR ;
2052
BOOLEAN.NE.SGL: call BOOLEAN.CMP.SGL ;
2054
BOOLEAN.NE.DBL: call BOOLEAN.CMP.DBL ;
2056
BOOLEAN.NE.RET: or a ; cp 0
2057
jp nz, BOOLEAN.RET.TRUE ;
2058
jp BOOLEAN.RET.FALSE ;
2062
if defined BOOLEAN.EQ
2064
BOOLEAN.EQ.INT: call BOOLEAN.CMP.INT ;
2066
BOOLEAN.EQ.STR: call BOOLEAN.CMP.STR ;
2068
BOOLEAN.EQ.SGL: call BOOLEAN.CMP.SGL ;
2070
BOOLEAN.EQ.DBL: call BOOLEAN.CMP.DBL ;
2072
BOOLEAN.EQ.RET: or a ; cp 0
2073
jp z, BOOLEAN.RET.TRUE ;
2074
jp BOOLEAN.RET.FALSE ;
2078
if defined BOOLEAN.AND
2080
BOOLEAN.AND.INT: ld a, (BASIC_DAC+2) ;
2081
ld hl, BASIC_ARG+2 ;
2083
ld (BASIC_DAC+2), a ;
2085
ld a, (BASIC_DAC+3) ;
2087
ld (BASIC_DAC+3), a ;
2093
if defined BOOLEAN.OR
2095
BOOLEAN.OR.INT: ld a, (BASIC_DAC+2) ;
2096
ld hl, BASIC_ARG+2 ;
2098
ld (BASIC_DAC+2), a ;
2100
ld a, (BASIC_DAC+3) ;
2102
ld (BASIC_DAC+3), a ;
2108
if defined BOOLEAN.XOR
2110
BOOLEAN.XOR.INT: ld a, (BASIC_DAC+2) ;
2111
ld hl, BASIC_ARG+2 ;
2113
ld (BASIC_DAC+2), a ;
2115
ld a, (BASIC_DAC+3) ;
2117
ld (BASIC_DAC+3), a ;
2123
if defined BOOLEAN.EQV
2125
BOOLEAN.EQV.INT: ld a, (BASIC_DAC+2) ;
2126
ld hl, BASIC_ARG+2 ;
2129
ld (BASIC_DAC+2), a ;
2131
ld a, (BASIC_DAC+3) ;
2134
ld (BASIC_DAC+3), a ;
2140
if defined BOOLEAN.IMP
2142
BOOLEAN.IMP.INT: ld a, (BASIC_DAC+2) ;
2143
ld hl, BASIC_ARG+2 ;
2146
ld (BASIC_DAC+2), a ;
2148
ld a, (BASIC_DAC+3) ;
2151
ld (BASIC_DAC+3), a ;
2157
if defined BOOLEAN.SHR
2159
BOOLEAN.SHR.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to right (bits 15...0-->)
2160
ld a, (BASIC_ARG+2) ;
2162
jp z, MATH.PARM.PUSH ; return if not shift
2163
ld b, a ; shift count
2164
BOOLEAN.SHR.INT.N: rr (ix+1) ;
2167
djnz BOOLEAN.SHR.INT.N ; next shift
2169
jp MATH.PARM.PUSH ; return DAC
2173
if defined BOOLEAN.SHL
2175
BOOLEAN.SHL.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to left (<--bits 15...0)
2176
ld a, (BASIC_ARG+2) ;
2178
jp z, MATH.PARM.PUSH ; return if not shift
2179
ld b, a ; shift count
2180
BOOLEAN.SHL.INT.N: rl (ix) ;
2183
djnz BOOLEAN.SHL.INT.N ; next shift
2185
jp MATH.PARM.PUSH ; return DAC
2189
if defined BOOLEAN.NOT
2191
BOOLEAN.NOT.INT: ld a, (BASIC_DAC+2) ;
2193
ld (BASIC_DAC+2), a ;
2194
ld a, (BASIC_DAC+3) ;
2196
ld (BASIC_DAC+3), a ;
2204
;---------------------------------------------------------------------------------------------------------
2205
; MEMORY ALLOCATION ROUTINES
2206
;---------------------------------------------------------------------------------------------------------
2207
; Adapted from memory allocator code by SamSaga2, Spain, 2015
2208
; https://www.msx.org/forum/msx-talk/development/asm-memory-allocator
2209
; https://www.msx.org/users/samsaga2
2210
;---------------------------------------------------------------------------------------------------------
2211
memory.heap_start: equ VAR_STACK.END + 1 ; start at end of variable stack
2212
memory.heap_end: equ 0xF0A0 - 100 ; end at start of work area for stack (100 bytes reserved), BIOS and BASIC interpreter
2213
block.next: equ 0 ; next free block address
2214
block.size: equ 2 ; size of block including header
2215
block: equ 4 ; block.next + block.size
2219
ld ix,memory.heap_start ; first block
2220
ld hl,memory.heap_start+block ; second block
2221
;; first block NEXT=secondblock, SIZE=0
2222
;; with this block we have a fixed start location
2223
;; because never will be allocated
2224
ld (ix+block.next),l
2225
ld (ix+block.next+1),h
2226
ld (ix+block.size),0
2227
ld (ix+block.size+1),0
2228
;; second block NEXT=0, SIZE=all
2229
;; the first and only free block have all available memory
2230
ld (ix+block.next+block),0
2231
ld (ix+block.next+block+1),0
2233
;ld hl,memory.heap_end ; size = @heap_end (stack) - heap_start - block_header * 2 - 100 (buffer for stack)
2236
ld de, memory.heap_start + (block * 2) + 100
2238
;ld de, block * 2 + 100
2240
ld (ix+block.size+block),l
2241
ld (ix+block.size+block+1),h
2245
;; IN BC=size, OUT IX=memptr, NZ=ok
2253
ld ix,memory.heap_start ; this
2256
ld l,(ix+block.size)
2257
ld h,(ix+block.size+1)
2260
jp z, memory.alloc.exactfit
2261
jp c, memory.alloc.nextblock
2262
;; split found block
2263
memory.alloc.splitfit:
2264
;; free space must allow at least two blocks headers (current + next)
2266
jr nz, memory.alloc.splitfit.do ; if free space > 0xFF, do split
2269
jr c, memory.alloc.nextblock ; if free space < 4, skip to next block
2270
memory.alloc.splitfit.do:
2271
;; newfreeblock = this + BC
2275
;; prevblock->next = newfreeblock
2276
ld (iy+block.next),l
2277
ld (iy+block.next+1),h
2278
;; newfreeblock->next = this->next
2280
pop iy ; iy = newfreeblock
2281
ld l,(ix+block.next)
2282
ld h,(ix+block.next+1)
2283
ld (iy+block.next),l
2284
ld (iy+block.next+1),h
2285
;; newfreeblock->size = this->size - BC
2286
ld l,(ix+block.size)
2287
ld h,(ix+block.size+1)
2290
ld (iy+block.size),l
2291
ld (iy+block.size+1),h
2293
ld (ix+block.size),c
2294
ld (ix+block.size+1),b
2296
;; use whole found block
2297
memory.alloc.exactfit:
2298
;; prevblock->next = this->next - remove block from free list
2299
ld l,(ix+block.next)
2300
ld h,(ix+block.next+1)
2301
ld (iy+block.next),l
2302
ld (iy+block.next+1),h
2311
memory.alloc.nextblock:
2312
ld l,(ix+block.next)
2313
ld h,(ix+block.next+1)
2320
;; this = this->next
2323
jp memory.alloc.find
2328
;; HL = IX - block_header_size
2335
ld ix,memory.heap_start
2337
ld e,(ix+block.next)
2338
ld d,(ix+block.next+1)
2341
jp z, memory.free.passedend
2342
sbc hl,de ; test this (HL) against next (DE)
2343
jr c, memory.free.found ; if DE > HL
2344
add hl,de ; restore hl value
2346
pop ix ; current = next
2349
;; ix=prev, hl=this, de=next
2351
add hl,de ; restore hl value
2352
ld (ix+block.next), l
2353
ld (ix+block.next+1), h ; prev->next = this
2356
ld (iy+block.next), e
2357
ld (iy+block.next+1), d ; this->next = next
2358
push ix ; prev x this
2363
call memory.free.coalesce
2364
pop ix ; this x next
2365
jr memory.free.coalesce
2369
memory.free.coalesce:
2370
ld c, (iy+block.size)
2371
ld b, (iy+block.size+1) ; bc = this->size
2375
adc hl, bc ; hl = this + this->size
2379
sbc hl, de ; if this + this->size == next, then this->size += next->size, this->next = next->next
2380
jr z, memory.free.coalesce.do
2381
push ix ; else, new *this = *next
2384
memory.free.coalesce.do:
2385
ld l, (ix+block.size)
2386
ld h, (ix+block.size+1) ; hl = next->size
2388
adc hl, bc ; hl += this->size
2389
ld (iy+block.size), l
2390
ld (iy+block.size+1), h ; this->size = hl
2391
ld l, (ix+block.next)
2392
ld h, (ix+block.next+1) ; hl = next->next
2393
ld (iy+block.next), l
2394
ld (iy+block.next+1), h ; this->next = hl
2397
memory.free.passedend:
2398
;; append block at the end of the free list
2399
ld (ix+block.next),l
2400
ld (ix+block.next+1),h
2403
ld (iy+block.next),0
2404
ld (iy+block.next+1),0
2410
ld ix,memory.heap_start
2412
memory.get_free.count:
2414
add a,(ix+block.size)
2417
adc a,(ix+block.size+1)
2419
ld l,(ix+block.next)
2420
ld h,(ix+block.next+1)
2426
jr memory.get_free.count
2428
memory.error: ld e, 7 ; out of memory
2429
__call_basic BASIC_ERROR_HANDLER ;
2434
;---------------------------------------------------------------------------------------------------------
2436
; By: Amaury Carvalho, 2019
2437
;---------------------------------------------------------------------------------------------------------
2439
; https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#Algorithm_for_integer_arithmetic
2440
; https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
2441
; https://rosettacode.org/wiki/Bitmap/Midpoint_circle_algorithm#C
2442
; https://www.msx.org/wiki/MSX-BASIC_Instructions
2443
;---------------------------------------------------------------------------------------------------------
2445
;---------------------------------------------------------------------------------------------------------
2447
;---------------------------------------------------------------------------------------------------------
2449
BIOS_WRTVDP: EQU 0x0047
2450
BIOS_RDVRM: EQU 0x004A
2451
BIOS_WRTVRM: EQU 0x004D
2452
BIOS_LDIRVM: EQU 0x005C
2453
BIOS_LDIRMV: EQU 0x0059
2454
BIOS_PNTINI: EQU 0x18CF
2455
BIOS_RIGHTC: EQU 0x16C5 ; Move current pixel physical address right
2456
BIOS_TRIGHTC: EQU 0x16AC ; Test then RIGHTC if legal
2457
BIOS_LEFTC: EQU 0x16EE ; Move current pixel physical address left
2458
BIOS_TLEFTC: EQU 0x16D8 ; Test then LEFTC if legal
2459
BIOS_UPC: EQU 0x175D ; Move current pixel physical address up
2460
BIOS_TUPC: EQU 0x173C ; Test then UPC if legal
2461
BIOS_DOWNC: EQU 0x172A ; Move current pixel physical address down
2462
BIOS_TDOWNC: EQU 0x170A ; Test then DOWNC if legal
2463
BIOS_DCOMPR: EQU 0x146A ; compare HL and DE (Flag NC if HL>DE, Flag Z if HL=DE, Flag C if HL<DE)
2464
BIOS_FILVRM: EQU 0x0056 ; fill VRAM with value
2466
BIOS_BIGFIL: EQU 0x016B ; msx 2
2467
BIOS_NRDVRM: EQU 0x0174 ; msx 2
2468
BIOS_NWRVRM: EQU 0x0177 ; msx 2
2469
BIOS_NRDVDP: EQU 0x013E ; msx 2
2470
BIOS_VDPSTA: EQU 0x0131 ; msx 2
2471
BIOS_NWRVDP: EQU 0x012D ; msx 2 (0x0647)
2473
BASIC_SUB_LINE: equ 0x58fc
2474
BASIC_SUB_LINEBOX: equ 0x5912
2475
BASIC_SUB_LINEBOXFILLED: equ 0x58C1
2476
BASIC_SUB_CIRCLE: equ 0x5B19
2477
BASIC_SUB_PAINT1: equ 0x59DA ;0x59C8
2478
BASIC_SUB_PAINT2: equ 0x0069 ;0x2664 ;0x2651+3
2480
;---------------------------------------------------------------------------------------------------------
2482
;---------------------------------------------------------------------------------------------------------
2484
BIOS_RG0SAV: EQU 0xF3DF
2485
BIOS_RG1SAV: EQU 0xF3E0
2486
BIOS_RG8SAV: EQU 0xFFE7
2487
BIOS_BDRATR: EQU 0xFCB2
2488
BIOS_STATFL: EQU 0xF3E7 ; VDP status register
2490
BIOS_CXOFF: EQU 0xF945
2491
BIOS_CYOFF: EQU 0xF947
2492
BIOS_GXPOS: EQU 0xFCB3
2493
BIOS_GYPOS: EQU 0xFCB5
2495
BIOS_GRPNAM: EQU 0xF3C7 ; pattern name table
2496
BIOS_GRPCOL: EQU 0xF3C9 ; colour table
2497
BIOS_GRPCGP: EQU 0xF3CB ; pattern generator table
2498
BIOS_GRPATR: EQU 0xF3CD ; sprite attribute table
2499
BIOS_GRPPAT: EQU 0xF3CF ; sprite generator table
2500
BIOS_CGPNT: EQU 0xF920 ; 2 - current MSX Font location (0x1BBF)
2501
BIOS_ATRBAS: EQU 0xF928 ; sprite attribute table
2503
BIOS_MLTNAM: EQU 0xF3D1 ; pattern name table (screen 3, multicolor)
2504
BIOS_MLTCOL: EQU 0xF3D3 ; colour table (screen 3, multicolor)
2505
BIOS_MLTCGP: EQU 0xF3D5 ; pattern generator table (screen 3, multicolor)
2506
BIOS_MLTATR: EQU 0xF3D7 ; sprite attribute table (screen 3, multicolor)
2507
BIOS_MLTPAT: EQU 0xF3D9 ; sprite generator table (screen 3, multicolor)
2509
BIOS_ASPECT: equ 0xF931 ;2 Aspect ratio of the circle; set by <ratio> of CIRCLE.
2510
BIOS_CENCNT: equ 0xF933 ;2 Counter used by CIRCLE.
2511
BIOS_CLINEF: equ 0xF935 ;1 Flag to draw line to centre, Used set by CIRCLE
2512
BIOS_CNPNTS: equ 0xF936 ;2 Point to be plottted in a 45° segment, Used set by CIRCLE
2513
BIOS_CPLOTF: equ 0xF938 ;1 Plot polarity flag, Used set by CIRCLE
2514
BIOS_CPCNT: equ 0xF939 ;2 Number of points in 1/8 of circle, Used set by CIRCLE.
2515
BIOS_CPCNT8: equ 0xF93B ;2 Number of points in the circle. Used by CIRCLE.
2516
BIOS_CRCSUM: equ 0xF93D ;2 Cyclic redundancy check sum of the circle. Used by CIRCLE.
2517
BIOS_CSTCNT: equ 0xF93F ;2 Variable to maintain the number of points of the starting angle. Used by the instruction CIRCLE
2518
BIOS_CSCLXY: equ 0xF941 ;1 Scale of X & Y. Used by the instruction CIRCLE
2519
BIOS_ASPCT1: equ 0xF40B ;2 256/aspect ratio for Basic instruction CIRCLE.
2520
BIOS_ASPCT2: equ 0xF40D ;2 256*aspect ratio for Basic instruction CIRCLE.
2521
BIOS_MAXUPD: equ 0xF3EC ;3 Work area used by the instruction CIRCLE, contains JP 0000h at start.
2522
BIOS_MINUPD: equ 0xF3EF ;3 Work area used by the instruction CIRCLE, contains JP 0000h at start.
2524
BIOS_PARM1: EQU 0xF6E8 ; 100
2525
BIOS_PARM2: EQU 0xF750 ; 100
2527
GFX_TEMP: EQU BIOS_PARM1 ; 2
2528
GFX_TEMP1: EQU GFX_TEMP + 2 ; 2
2529
GFX_TEMP2: EQU GFX_TEMP1 + 2 ; 2
2530
GFX_TEMP3: EQU GFX_TEMP2 + 2 ; 2
2531
GFX_TEMP4: EQU GFX_TEMP3 + 2 ; 2
2532
GFX_TEMP5: EQU GFX_TEMP4 + 2 ; 2
2533
GFX_TEMP6: EQU GFX_TEMP5 + 2 ; 2
2534
GFX_TEMP7: EQU GFX_TEMP6 + 2 ; 2
2535
GFX_TEMP8: EQU GFX_TEMP7 + 2 ; 2
2536
GFX_TEMP9: EQU GFX_TEMP8 + 2 ; 2
2538
GFX_MAX_X: EQU 0xFCA4 ; 1 (CASSETE LOWLIM)
2539
GFX_MAX_Y: EQU 0xFCA5 ; 1 (CASSETE WINWID)
2541
GFX_SPRITE_FLAGS: EQU 0xF40A ; 1 (CASSETE HEADER) - bits 0=check screen limits, 1=check walls, 2=check hotspots,
2542
; 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
2543
GFX_SPRITE_SIZE_DAT: EQU 0xF3FC ; 1 (CASSETE CS1200)
2544
GFX_SPRITE_SIZE_SCR: EQU 0xF3FD ; 1
2545
GFX_SPRITE_WALLS: EQU 0xF406 ; 2 (CASSETE LOW)
2546
GFX_SPRITE_HOTSPOTS: EQU 0xF408 ; 2 (CASSETE HIGH)
2547
GFX_SPRITE_HOTSPOT_TILE: EQU 0xF405 ; 1 (CASSETE CS2400)
2548
GFX_SPRITE_COLLIDER: EQU 0xF400 ; 1 (CASSETE CS1200)
2549
GFX_SPRITE_COLLISION: EQU 0xF7B5 ; 2 (ARYTA2)
2551
GFX_MUSIC_START: EQU 0xF401 ; 2 (CASSETE CS2400)
2552
GFX_MUSIC_NEXT: EQU 0xF403 ; 2
2553
GFX_MUSIC_PREV: EQU 0xF74C ; 2 (PRMPRV)
2555
GFX_TEMP10: EQU 0xF3FE ; 2 (CASSETE CS1200)
2557
;BIOS_SCR_SIZE_X: dw 240, 256, 256, 64, 256, 256, 512, 512, 256, 512, 256, 256, 256
2558
;BIOS_SCR_SIZE_Y: dw 192, 192, 192, 48, 192, 212, 212, 212, 212, 384, 212, 212, 212
2559
BIOS_SCR_SIZE_X: db 239, 255, 255, 63, 255, 255, 255, 255, 255, 255, 255, 255, 255
2560
BIOS_SCR_SIZE_Y: db 191, 191, 191, 47, 191, 211, 211, 211, 211, 255, 211, 211, 211
2563
;---------------------------------------------------------------------------------------------------------
2564
; gfxIsScreenModeMSX2
2565
; return if screen mode is from MSX 2
2566
; out C is set, if MSX2 and screen mode above 3
2567
;---------------------------------------------------------------------------------------------------------
2569
gfxIsScreenModeMSX2:
2570
ld a, (BIOS_VERSION)
2572
jp nz, BIOS_CHKNEW ; if not MSX1, jump to CHKNEW
2576
;---------------------------------------------------------------------------------------------------------
2578
; set current screen mode
2579
; in A = screen number
2580
;---------------------------------------------------------------------------------------------------------
2584
call gfxInitScreenWorkspace
2586
ld a, (BIOS_VERSION)
2588
jr nz, gfxSetScreenMode.1 ; if not MSX1, jump
2591
call nc, gfxSetScreenMode.0 ; if screen mode >= 4, change to screen 2
2592
__call_bios BIOS_CHGMOD ; change the screen mode (msx1)
2593
jr gfxSetScreenMode.2
2601
ld ix, BIOS_CHGMOD2 ; change the screen mode (msx2)
2605
call gfxGetScreenHeight
2606
call gfxGetScreenWidth
2607
call gfxGetSpriteSize
2608
jp gfxFillSpriteCollisionTable
2610
gfxInitScreenWorkspace:
2612
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
2614
ld (GFX_SPRITE_WALLS), a
2615
ld (GFX_SPRITE_WALLS+1), a
2616
ld (GFX_SPRITE_HOTSPOTS), a
2617
ld (GFX_SPRITE_HOTSPOTS+1), a
2618
ld (GFX_MUSIC_START), a
2619
ld (GFX_MUSIC_START+1), a
2620
ld (GFX_MUSIC_NEXT), a
2621
ld (GFX_MUSIC_NEXT+1), a
2622
ld (GFX_MUSIC_PREV), a
2623
ld (GFX_MUSIC_PREV+1), a
2624
ld (GFX_SPRITE_HOTSPOT_TILE), a
2625
ld (GFX_SPRITE_COLLIDER), a
2628
;---------------------------------------------------------------------------------------------------------
2630
; return current screen mode
2631
; out A = screen number (0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode)
2632
;---------------------------------------------------------------------------------------------------------
2638
;---------------------------------------------------------------------------------------------------------
2640
; set current screen location
2643
;---------------------------------------------------------------------------------------------------------
2646
ld (BIOS_GRPACX), bc ; x
2647
;ld (BIOS_GXPOS), bc
2648
ld (BIOS_GRPACY), de ; y
2649
;ld (BIOS_GYPOS), de
2652
;---------------------------------------------------------------------------------------------------------
2654
; refresh current screen location
2655
;---------------------------------------------------------------------------------------------------------
2658
ld bc, (BIOS_GRPACX) ; x
2659
ld de, (BIOS_GRPACY) ; y
2661
call gfxIsScreenModeMSX2
2662
jr nc, gfxRefreshXY.2 ; if MSX2 and screen mode above 3
2663
__call_bios BIOS_SCALXY ; BC = X, DE = Y
2664
__call_bios BIOS_MAPXYC ; in BC = X, DE = Y
2667
ld ix, BIOS_SCALXY2 ; BC = X, DE = Y
2669
ld ix, BIOS_MAPXYC2 ; in BC = X, DE = Y
2672
;---------------------------------------------------------------------------------------------------------
2674
; get current screen location
2677
;---------------------------------------------------------------------------------------------------------
2680
ld bc, (BIOS_GRPACX) ; x
2681
ld de, (BIOS_GRPACY) ; y
2684
;---------------------------------------------------------------------------------------------------------
2685
; gfxGetScreenHeight
2687
; out a = screen height
2688
;---------------------------------------------------------------------------------------------------------
2693
ld hl, BIOS_SCR_SIZE_Y
2704
;---------------------------------------------------------------------------------------------------------
2707
; out a = screen height
2708
;---------------------------------------------------------------------------------------------------------
2713
ld hl, BIOS_SCR_SIZE_X
2724
;---------------------------------------------------------------------------------------------------------
2726
; get sprite data size
2727
; out a = sprite data size
2728
;---------------------------------------------------------------------------------------------------------
2733
ld a, (BIOS_RG1SAV) ; bit 0 = double size, bit 1 = sprite size (0=8 pixels, 1=16 pixels)
2735
jr z, gfxGetSpriteSize.1
2740
jr z, gfxGetSpriteSize.2
2744
ld (GFX_SPRITE_SIZE_DAT), bc
2750
if defined GFX_FAST and defined PAINT
2752
;---------------------------------------------------------------------------------------------------------
2754
; move screen current location up
2755
; out: carry if off screen
2756
;---------------------------------------------------------------------------------------------------------
2761
ld de, (BIOS_GRPACY)
2766
ld (BIOS_GRPACY), de
2777
;---------------------------------------------------------------------------------------------------------
2779
; move screen current location down
2780
; out: carry if off screen
2781
;---------------------------------------------------------------------------------------------------------
2788
ld de, (BIOS_GRPACY)
2793
ld (BIOS_GRPACY), de
2804
;---------------------------------------------------------------------------------------------------------
2806
; move screen current location left
2807
; out: carry if off screen
2808
;---------------------------------------------------------------------------------------------------------
2813
ld de, (BIOS_GRPACX)
2818
ld (BIOS_GRPACX), de
2829
;---------------------------------------------------------------------------------------------------------
2831
; move screen current location right
2832
; out: carry if off screen
2833
;---------------------------------------------------------------------------------------------------------
2840
ld de, (BIOS_GRPACX)
2845
ld (BIOS_GRPACX), de
2858
;---------------------------------------------------------------------------------------------------------
2860
; push current screen location
2861
;---------------------------------------------------------------------------------------------------------
2866
ld iy, (BIOS_GRPACX) ; x
2868
ld iy, (BIOS_GRPACY) ; y
2874
;---------------------------------------------------------------------------------------------------------
2876
; pop current screen location
2879
;---------------------------------------------------------------------------------------------------------
2888
;---------------------------------------------------------------------------------------------------------
2890
; set current foreground color
2892
;---------------------------------------------------------------------------------------------------------
2895
ld (BIOS_FORCLR), a ; foreground color
2899
;---------------------------------------------------------------------------------------------------------
2901
; get current foreground color
2903
;---------------------------------------------------------------------------------------------------------
2906
ld a, (BIOS_FORCLR) ; foreground color
2909
;---------------------------------------------------------------------------------------------------------
2911
; set current background color
2913
;---------------------------------------------------------------------------------------------------------
2916
ld (BIOS_BAKCLR), a ; foreground color
2919
;---------------------------------------------------------------------------------------------------------
2921
; get current background color
2923
;---------------------------------------------------------------------------------------------------------
2926
ld a, (BIOS_BAKCLR) ; foreground color
2929
;---------------------------------------------------------------------------------------------------------
2931
; set current border color
2933
;---------------------------------------------------------------------------------------------------------
2936
ld (BIOS_BDRCLR), a ; border color
2939
;---------------------------------------------------------------------------------------------------------
2941
; get current border color
2943
;---------------------------------------------------------------------------------------------------------
2946
ld a, (BIOS_BDRCLR) ; border color
2949
;---------------------------------------------------------------------------------------------------------
2951
; set fill border color
2953
;---------------------------------------------------------------------------------------------------------
2956
ld (BIOS_BDRATR), a ; border color
2959
;---------------------------------------------------------------------------------------------------------
2961
; get fill border color
2963
;---------------------------------------------------------------------------------------------------------
2966
ld a, (BIOS_BDRATR) ; border color
2969
;---------------------------------------------------------------------------------------------------------
2971
; set current color (foreground, background and border)
2972
;---------------------------------------------------------------------------------------------------------
2977
jp nz, BIOS_CHGCLR2 ; change VDP colors - msx2
2979
jp nz, BIOS_CHGCLR2 ; change VDP colors - msx2
2980
jp BIOS_CHGCLR ; change VDP colors
2981
; __call_bios BIOS_SETATR ; change the pixel color
2984
;---------------------------------------------------------------------------------------------------------
2986
; set pixel in current position to current foreground color
2987
;---------------------------------------------------------------------------------------------------------
2990
call gfxIsScreenModeMSX2
2991
jr nc, gfxSetPixel.1 ; if MSX2 and screen mode above 3
2992
__call_bios BIOS_SETC
2998
;---------------------------------------------------------------------------------------------------------
3000
; get pixel color in current position
3001
; out A = pixel color
3002
;---------------------------------------------------------------------------------------------------------
3005
call gfxIsScreenModeMSX2
3006
jr nc, gfxGetPixel.1 ; if MSX2 and screen mode above 3
3007
__call_bios BIOS_READC
3015
;---------------------------------------------------------------------------------------------------------
3017
; plot a line from current position to informed destination
3018
; in BC = destination x
3019
; DE = destination y
3020
; https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm#Algorithm_for_integer_arithmetic
3021
; https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#C
3022
;---------------------------------------------------------------------------------------------------------
3023
;void line(int x0, int y0, int x1, int y1) {
3024
; int dx = abs(x1-x0), sx = x0<x1 ? 1 : -1;
3025
; int dy = abs(y1-y0), sy = y0<y1 ? 1 : -1;
3026
; int err = (dx>dy ? dx : -dy)/2, e2;
3029
; if (x0==x1 && y0==y1) break;
3031
; if (e2 >-dx) { err -= dy; x0 += sx; }
3032
; if (e2 < dy) { err += dx; y0 += sy; }
3037
if not defined GFX_FAST
3038
ld hl, (BIOS_GRPACX)
3040
ld hl, (BIOS_GRPACY)
3042
__call_basic BASIC_SUB_LINE
3047
ld (GFX_TEMP2), bc ; x
3048
ld (GFX_TEMP3), de ; y
3050
ld hl, (BIOS_GRPACX) ; x0
3051
ld de, (GFX_TEMP2) ; x
3054
;jp po, gfxLine.1 ; x0 >= x? else jump to endif
3056
jp c, gfxLine.1 ; x0 < x? jump
3059
ld (GFX_TEMP4), hl ; dx = x0 - x
3061
ld (GFX_TEMP5), hl ; sx = -1
3065
ld de, (BIOS_GRPACX) ; x0
3066
ld hl, (GFX_TEMP2) ; x
3069
ld (GFX_TEMP4), hl ; dx = x - x0
3071
ld (GFX_TEMP5), hl ; sx = 1
3074
ld hl, (BIOS_GRPACY) ; y0
3075
ld de, (GFX_TEMP3) ; y
3078
;jp po, gfxLine.3 ; y0 >= y? else, jump to endif
3080
jp c, gfxLine.3 ; y0 < y? jump
3083
ld (GFX_TEMP6), hl ; dy = y0 - y
3085
ld (GFX_TEMP7), hl ; sy = -1
3089
ld de, (BIOS_GRPACY) ; y0
3090
ld hl, (GFX_TEMP3) ; y
3093
ld (GFX_TEMP6), hl ; dy = y - y0
3095
ld (GFX_TEMP7), hl ; sy = 1
3098
ld hl, (GFX_TEMP6) ; dy
3101
jp z, gfxLine.h ; dy = 0?
3103
ld hl, (GFX_TEMP4) ; dx
3106
jp z, gfxLine.v ; dx = 0?
3108
ld de, (GFX_TEMP4) ; dx
3109
ld hl, (GFX_TEMP6) ; dy
3114
ld (GFX_TEMP9), hl ; dxy = dx + dy
3116
ld de, (GFX_TEMP4) ; dx
3117
ld hl, (GFX_TEMP6) ; dy
3120
;jp pe, gfxLine.5 ; dy < dx? else, jump to endif
3122
jp z, gfxLine.5 ; dy = dx? jump
3123
jp nc, gfxLine.5 ; dy > dx? jump
3125
ld de, (GFX_TEMP6) ; dy
3131
ld (GFX_TEMP8), de ; err = -dy / 2
3135
ld hl, (GFX_TEMP4) ; dx
3139
ld (GFX_TEMP8), hl ; err = dx/2
3144
ld hl, (GFX_TEMP9) ; dxy
3147
jp nc, gfxLine.loop.0 ; dxy > 0? jump
3148
;jp z, gfxLine.loop.0 ; dxy = 0? jump
3154
ld de, (GFX_TEMP4) ; dx
3157
ld de, (GFX_TEMP8) ; e2 = err
3161
;jp pe, gfxLine.loop.1 ; if -dx < e2, else jump to endif
3163
jp z, gfxLine.loop.1 ; -dx = e2? jump
3164
jp nc, gfxLine.loop.1 ; -dx > e2? jump
3165
ld hl, (GFX_TEMP8) ; err
3166
ld de, (GFX_TEMP6) ; dy
3169
ld (GFX_TEMP8), hl ; err -= dy
3171
ld hl, (GFX_TEMP9) ; dxy
3173
ld (GFX_TEMP9), hl ; dxy -= 1
3175
ld hl, (BIOS_GRPACX)
3179
ld (BIOS_GRPACX), hl ; x0 += sx
3183
ld de, (GFX_TEMP6) ; dy
3186
;jp pe, gfxLine.loop.2 ; if e2 < dy, else jump to endif
3188
jp z, gfxLine.loop.2 ; e2 = dy? jump
3189
jp nc, gfxLine.loop.2 ; e2 > dy? jump
3190
ld hl, (GFX_TEMP8) ; err
3191
ld de, (GFX_TEMP4) ; dx
3194
ld (GFX_TEMP8), hl ; err += dx
3196
ld hl, (GFX_TEMP9) ; dxy
3198
ld (GFX_TEMP9), hl ; dxy -= 1
3200
ld hl, (BIOS_GRPACY)
3204
ld (BIOS_GRPACY), hl ; y0 += sy
3211
ld a, (GFX_TEMP5) ; sx
3213
jr z, gfxLine.h.1 ; if a is positive
3214
ld hl, (BIOS_GRPACX)
3218
ld (BIOS_GRPACX), hl
3222
__call_bios BIOS_FETCHC
3223
ld hl, (GFX_TEMP4) ; dx
3225
call gfxDrawHorLine ; HL = pixel count
3229
ld a, (GFX_TEMP7) ; sy
3231
jr z, gfxLine.v.1 ; if a is positive
3232
ld hl, (BIOS_GRPACY)
3236
ld (BIOS_GRPACY), hl
3241
ld hl, (GFX_TEMP6) ; dy
3244
jp gfxBox.drawVerLine
3249
if defined BOX or defined FBOX or defined BOX_STEP or defined FBOX_STEP
3251
;---------------------------------------------------------------------------------------------------------
3253
; plot a box from current position to informed destination
3254
; in BC = destination x
3255
; DE = destination y
3256
; A = filled flag (0 = not filled, <>0 = filled)
3257
;---------------------------------------------------------------------------------------------------------
3261
if not defined GFX_FAST
3262
ld hl, (BIOS_GRPACX)
3264
ld hl, (BIOS_GRPACY)
3266
ld hl, BASIC_SUB_LINEBOX
3269
call gfxIsScreenModeMSX2
3271
ld hl, BASIC_SUB_LINEBOXFILLED
3276
jp BIOS_CALBAS ; BIOS_CALSLT
3287
call gfxAdjustDestXY
3289
jr nz, gfxBox.filled
3293
call gfxBox.drawHorLine
3295
ld de, (BIOS_GRPACX)
3299
ld (BIOS_GRPACX), hl
3301
call gfxBox.drawVerLine
3303
call gfxBox.drawVerLine
3304
call gfxBox.drawHorLine
3312
ld bc, (BIOS_GRPACX)
3314
call gfxBox.drawHorLine
3316
ld (BIOS_GRPACX), bc
3317
ld bc, (BIOS_GRPACY)
3319
ld (BIOS_GRPACY), bc
3325
jr nz, gfxBox.filled.loop
3337
gfxBox.drawVerLine.loop:
3345
jr nz, gfxBox.drawVerLine.loop
3348
;---------------------------------------------------------------------------------------------------------
3350
; draw a horizontal line
3352
;---------------------------------------------------------------------------------------------------------
3357
jr c, gfxDrawHorLine.2 ; if screen mode < 5 then jump
3359
ret nz ; return if negative
3362
jr nz, gfxDrawHorLine.1
3364
ret z ; return if hl = 0
3373
jr nz, gfxDrawHorLine.1
3377
__call_bios BIOS_NSETCX ; HL = fill count
3380
;---------------------------------------------------------------------------------------------------------
3382
; invert if dest XY is less than current XY position
3385
;---------------------------------------------------------------------------------------------------------
3389
ld (GFX_TEMP2), bc ; x
3390
ld (GFX_TEMP3), de ; y
3392
; verify x againt current position
3393
ld hl, (BIOS_GRPACX)
3396
sbc hl, de ; dx = x1 - x0
3397
bit 7, h ; result is negative?
3398
jr z, gfxAdjustDestXY.1
3400
ld (BIOS_GRPACX), hl
3408
; verify y againt current position
3409
ld hl, (BIOS_GRPACY)
3412
sbc hl, de ; dy = y1 - y0
3413
bit 7, h ; result is negative?
3414
jr z, gfxAdjustDestXY.2
3416
ld (BIOS_GRPACY), hl
3424
; refresh new position
3434
;---------------------------------------------------------------------------------------------------------
3436
; plot a circle centered in current position
3437
; BC = tracing end x
3438
; DE = tracing end y
3440
; A = filled flag (0 = not filled, <>0 = filled)
3441
; https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
3442
; https://rosettacode.org/wiki/Bitmap/Midpoint_circle_algorithm#C
3443
;---------------------------------------------------------------------------------------------------------
3447
if not defined GFX_FAST
3449
ret nz ; return if negative radius
3450
ld (BIOS_GXPOS), hl ; circle ray
3452
ld bc, (BIOS_GRPACY)
3456
;jr nz, gfxDrawCircle.1
3463
__call_basic BASIC_SUB_CIRCLE
3466
ld (GFX_TEMP), hl ; radius
3469
ret z ; return if zero radius
3471
ret nz ; return if negative radius
3474
ld (GFX_TEMP1), bc ; x0
3475
ld (GFX_TEMP2), de ; y0
3478
jp nz, gfxCircle.filled
3480
gfxCircle.notFilled:
3482
ld de, (GFX_TEMP) ; radius
3485
ld (GFX_TEMP3), hl ; f = 1 - radius
3488
ld (GFX_TEMP4), hl ; ddF_x = 0
3497
ld (GFX_TEMP5), hl ; ddF_y = -2 * radius
3500
ld (GFX_TEMP6), hl ; x = 0
3503
ld (GFX_TEMP7), hl ; y = radius
3505
; plot(x0, y0 + radius)
3506
ld de, (GFX_TEMP) ; radius
3507
ld hl, (GFX_TEMP2) ; y0
3511
ld bc, (GFX_TEMP1) ; x0
3515
; plot(x0, y0 - radius)
3516
ld de, (GFX_TEMP) ; radius
3517
ld hl, (GFX_TEMP2) ; y0
3521
ld bc, (GFX_TEMP1) ; x0
3525
; plot(x0 + radius, y0)
3526
ld hl, (GFX_TEMP1) ; x0
3527
ld de, (GFX_TEMP) ; radius
3534
ld de, (GFX_TEMP2) ; y0
3538
; plot(x0 - radius, y0)
3539
ld hl, (GFX_TEMP1) ; x0
3540
ld de, (GFX_TEMP) ; radius
3547
ld de, (GFX_TEMP2) ; y0
3550
jp gfxCircle.notFilled.3
3552
gfxCircle.notFilled.1:
3553
ld hl, (GFX_TEMP3) ; f
3555
jr nz, gfxCircle.notFilled.2 ; if( f < 0 ), jump
3557
ld hl, (GFX_TEMP7) ; y -= 1
3561
ld hl, (GFX_TEMP5) ; ddF_y += 2
3566
ld hl, (GFX_TEMP3) ; f
3567
ld de, (GFX_TEMP5) ; ddF_y
3570
ld (GFX_TEMP3), hl ; f += ddF_y
3572
gfxCircle.notFilled.2:
3573
ld hl, (GFX_TEMP6) ; x
3575
ld (GFX_TEMP6), hl ; x++
3577
ld hl, (GFX_TEMP4) ; ddF_x += 2
3582
ld hl, (GFX_TEMP3) ; f
3583
ld de, (GFX_TEMP4) ; ddF_x
3587
ld (GFX_TEMP3), hl ; f += ddF_x + 1
3589
; plot(x0 + x, y0 + y)
3590
ld hl, (GFX_TEMP1) ; x0
3591
ld de, (GFX_TEMP6) ; x
3598
ld hl, (GFX_TEMP2) ; y0
3599
ld de, (GFX_TEMP7) ; y
3606
; plot(x0 - x, y0 + y)
3607
ld hl, (GFX_TEMP1) ; x0
3608
ld de, (GFX_TEMP6) ; x
3615
ld hl, (GFX_TEMP2) ; y0
3616
ld de, (GFX_TEMP7) ; y
3623
; plot(x0 + x, y0 - y)
3624
ld hl, (GFX_TEMP1) ; x0
3625
ld de, (GFX_TEMP6) ; x
3632
ld hl, (GFX_TEMP2) ; y0
3633
ld de, (GFX_TEMP7) ; y
3640
; plot(x0 - x, y0 - y)
3641
ld hl, (GFX_TEMP1) ; x0
3642
ld de, (GFX_TEMP6) ; x
3649
ld hl, (GFX_TEMP2) ; y0
3650
ld de, (GFX_TEMP7) ; y
3657
; plot(x0 + y, y0 + x)
3658
ld hl, (GFX_TEMP1) ; x0
3659
ld de, (GFX_TEMP7) ; y
3666
ld hl, (GFX_TEMP2) ; y0
3667
ld de, (GFX_TEMP6) ; x
3674
; plot(x0 - y, y0 + x)
3675
ld hl, (GFX_TEMP1) ; x0
3676
ld de, (GFX_TEMP7) ; y
3683
ld hl, (GFX_TEMP2) ; y0
3684
ld de, (GFX_TEMP6) ; x
3691
; plot(x0 + y, y0 - x)
3692
ld hl, (GFX_TEMP1) ; x0
3693
ld de, (GFX_TEMP7) ; y
3700
ld hl, (GFX_TEMP2) ; y0
3701
ld de, (GFX_TEMP6) ; x
3708
; plot(x0 - y, y0 - x)
3709
ld hl, (GFX_TEMP1) ; x0
3710
ld de, (GFX_TEMP7) ; y
3717
ld hl, (GFX_TEMP2) ; y0
3718
ld de, (GFX_TEMP6) ; x
3725
gfxCircle.notFilled.3:
3726
ld hl, (GFX_TEMP6) ; x
3727
ld de, (GFX_TEMP7) ; y
3730
jp c, gfxCircle.notFilled.1 ; while( x < y )
3731
ld bc, (GFX_TEMP1) ; x0
3732
ld de, (GFX_TEMP2) ; y0
3737
call gfxCircle.notFilled
3738
ld hl, (BIOS_BDRATR)
3740
ld hl, (BIOS_FORCLR)
3741
ld (BIOS_BDRATR), hl
3745
ld (BIOS_BDRATR), hl
3751
if defined PAINT or (defined CIRCLE and defined GFX_FAST)
3753
;---------------------------------------------------------------------------------------------------------
3755
; Fill current region delimited by border attribute color changing pixels to foreground color
3756
; in: a = fill type (0 = not symmetric, 1 = symmetric)
3757
;---------------------------------------------------------------------------------------------------------
3761
if not defined GFX_FAST
3763
ld bc, (BIOS_GRPACX)
3765
ld de, (BIOS_GRPACY)
3783
call gfxIsScreenModeMSX2
3784
jr nc, gfxBorderFill.1 ; if MSX2 and screen mode above 3, jump
3788
__call_basic BASIC_SUB_PAINT1
3792
__call_basic BASIC_SUB_PAINT1
3794
;ld ix, BASIC_SUB_PAINT2
3800
ld hl, (BIOS_GRPACY)
3802
call gfxBorderFill.down
3805
ld (BIOS_GRPACY), hl
3807
call gfxBorderFill.up
3809
ld (BIOS_GRPACY), hl
3814
call gfxBorderFill.line
3820
jr nz, gfxBorderFill.down
3824
call gfxBorderFill.line
3830
jr nz, gfxBorderFill.up
3834
ld de, (BIOS_GRPACX)
3837
ld de, 1 ; skip count
3838
__call_bios BIOS_SCANR
3840
;ld (GFX_TEMP2), hl ; pixel count transversed
3843
ld (BIOS_GRPACX), de
3846
;cp 0 ; 0 = not symmetric, 1 = symmetric
3847
;jr z, gfxBorderFill.line.1
3849
;__call_bios BIOS_NSETCX ; HL = fill count
3850
;jr gfxBorderFill.line.2
3851
gfxBorderFill.line.1:
3853
ld de, 0 ; skip count
3854
__call_bios BIOS_SCANL
3855
gfxBorderFill.line.2:
3857
ld (BIOS_GRPACX), de
3861
;---------------------------------------------------------------------------------------------------------
3863
; Fload fill current region changing current pixel color to foreground color
3864
; https://en.wikipedia.org/wiki/Flood_fill
3865
;---------------------------------------------------------------------------------------------------------
3869
ld (GFX_TEMP), a ; replacement-color
3870
call gfxFloadFill.recursive
3873
gfxFloadFill.recursive:
3874
; 1. If target-color is equal to replacement-color, return.
3880
; 2. ElseIf the color of node is not equal to target-color, return.
3886
; 3. Else Set the color of node to replacement-color.
3889
; 4. Perform Flood-fill (one step to the left of node, target-color, replacement-color).
3890
gfxFloadFill.recursive.left:
3892
jr c, gfxFloadFill.recursive.right
3893
call gfxFloadFill.recursive
3896
; Perform Flood-fill (one step to the right of node, target-color, replacement-color).
3897
gfxFloadFill.recursive.right:
3899
jr c, gfxFloadFill.recursive.up
3900
call gfxFloadFill.recursive
3903
; Perform Flood-fill (one step to the up of node, target-color, replacement-color).
3904
gfxFloadFill.recursive.up:
3906
jr c, gfxFloadFill.recursive.down
3907
call gfxFloadFill.recursive
3910
; Perform Flood-fill (one step to the down of node, target-color, replacement-color).
3911
gfxFloadFill.recursive.down:
3914
call gfxFloadFill.recursive
3922
if defined SPRITEMODE
3924
;---------------------------------------------------------------------------------------------------------
3926
; set current sprite mode
3928
; 0: Spritesize is 8 by 8 pixels - default value
3929
; 1: Spritesize is 8 by 8 pixels, magnified to 16 by 16 pixels
3930
; 2: Spritesize is 16 by 16 pixels
3931
; 3: Spritesize is 16 by 16 pixels, magnified to 32 by 32 pixels
3932
; RG1SAV bit 0 = magnify sprite (double size)
3933
; RG1SAV bit 1 = sprite size (0=8 pixels, 1=16 pixels)
3934
;---------------------------------------------------------------------------------------------------------
3937
and 3 ; keeps only bits 0 and 1 from A
3940
ld a, (BIOS_RG1SAV) ; get copy from register #1 of VDP
3941
and 0xFC ; clear bits 0 and 1 from A
3942
or b ; put parameter to A (bits 0 and 1)
3943
ld (BIOS_RG1SAV), a ; restore to register #1 of VDP
3944
ld b, a ; value to write
3945
ld c, 1 ; register number to write
3946
call gfxWRTVDP ; write register to VDP
3947
call gfxCLRSPR ; clear sprites
3949
call gfxGetSpriteSize
3950
jp gfxFillSpriteCollisionTable
3954
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
3956
;---------------------------------------------------------------------------------------------------------
3957
; gfxSetSpriteColorInt
3960
;---------------------------------------------------------------------------------------------------------
3962
gfxSetSpriteColorInt:
3963
ld b, a ; save sprite number
3964
call gfxCALATR ; get sprite attribute table address
3969
;call gfxSetSpriteColor.Adjust
3974
ret c ; if screen mode < 3, do not adjust sprite multicolor
3976
ld a, b ; recover sprite number
3977
call gfxGetSpriteColorTable
3980
;call gfxSetSpriteColor.Adjust
3981
ld b, 16 ; array of 16 bytes
3983
gfxSetSpriteColorInt.1:
3988
djnz gfxSetSpriteColorInt.1
3991
;gfxSetSpriteColor.Adjust:
4001
;---------------------------------------------------------------------------------------------------------
4002
; gfxSetSpriteColorStr
4004
; DE = address to color byte array
4005
; BC = color byte array size
4006
;---------------------------------------------------------------------------------------------------------
4008
gfxSetSpriteColorStr:
4009
ld b, a ; save sprite number
4012
call gfxCALATR ; get sprite attribute table
4016
ld a, c ; byte array zero length?
4024
;call gfxSetSpriteColor.Adjust
4029
ret c ; if screen mode < 3, do not adjust sprite mode 2
4031
ld a, b ; recover sprite number
4032
call gfxGetSpriteColorTable
4034
ld b, 16 ; array of 16 bytes (color table)
4035
ld a, c ; size of color array
4037
pop iy ; save array start
4039
gfxSetSpriteColorStr.1:
4042
;call gfxSetSpriteColor.Adjust
4048
jr nz, gfxSetSpriteColorStr.2
4049
ld a, c ; recover array size
4051
pop de ; recover array start
4053
gfxSetSpriteColorStr.2:
4054
djnz gfxSetSpriteColorStr.1
4059
;---------------------------------------------------------------------------------------------------------
4060
; gfxGetSpriteColorTable
4062
; HL = address to color table
4063
;---------------------------------------------------------------------------------------------------------
4065
gfxGetSpriteColorTable:
4069
ld l, a ; recover sprite number
4073
add hl, hl ; multiply by 16 (shift left 4)
4076
call gfxCALATR ; get sprite attribute table address
4081
sbc hl, de ; address of color table from sprite multicolor
4088
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
4090
;---------------------------------------------------------------------------------------------------------
4094
;---------------------------------------------------------------------------------------------------------
4098
call gfxCALATR ; get sprite attribute table address
4107
;---------------------------------------------------------------------------------------------------------
4108
; gfxSpriteStepCheck
4110
;---------------------------------------------------------------------------------------------------------
4117
ld de, (GFX_SPRITE_SIZE_DAT)
4118
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
4119
and 7 ; clear touched and collided flags
4120
ld (GFX_SPRITE_FLAGS), a
4123
jr z, gfxSpriteStepCheck.corners
4125
gfxSpriteStepCheck.limits:
4128
cp b ; jump if x > max_x?
4129
jr c, gfxSpriteStepCheck.limits.touched
4133
cp c ; jump if y > max_y?
4134
jr c, gfxSpriteStepCheck.limits.touched
4136
jr gfxSpriteStepCheck.corners
4138
gfxSpriteStepCheck.limits.touched:
4139
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
4140
or 8 ; set limit touched flag
4141
ld (GFX_SPRITE_FLAGS), a
4143
gfxSpriteStepCheck.corners:
4144
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
4145
and 2+4 ; check walls or hotspots
4146
jr z, gfxSpriteStepCheck.end
4149
call gfxSpriteStepCheck.corner
4151
ld a, (GFX_SPRITE_SIZE_DAT)
4154
call gfxSpriteStepCheck.corner
4156
ld a, (GFX_SPRITE_SIZE_DAT)
4159
call gfxSpriteStepCheck.corner
4162
ld a, (GFX_SPRITE_SIZE_DAT)
4165
call gfxSpriteStepCheck.corner
4167
gfxSpriteStepCheck.end:
4173
gfxSpriteStepCheck.corner:
4174
call gfxGetTileFromXY
4175
ld d, a ; tile to be searched
4177
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
4179
call nz, gfxSpriteStepCheck.walls
4181
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
4183
call nz, gfxSpriteStepCheck.hotspots
4186
gfxSpriteStepCheck.walls:
4187
ld hl, (GFX_SPRITE_WALLS)
4188
ld e, 16 ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4189
jp gfxSpriteStepCheck.search
4191
gfxSpriteStepCheck.hotspots:
4192
ld hl, (GFX_SPRITE_HOTSPOTS)
4193
ld e, 32 ; bits 0=chk limits, 1=chk walls, 2=chk hotspots, 3=limit touched, 4=wall touched, 5=hotspot touched, 6=collided
4194
call gfxSpriteStepCheck.search
4196
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
4201
ld (GFX_SPRITE_HOTSPOT_TILE), a
4204
; d = tile, hl = search table, e = flag
4205
gfxSpriteStepCheck.search:
4210
ld a, d ; search for this tile
4216
cpir ; inc HL searching for A until BC=0 (Z flag settled if found)
4220
gfxSpriteStepCheck.search.found:
4221
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
4222
or e ; set touched flag
4223
ld (GFX_SPRITE_FLAGS), a
4238
call gfxGetScreenTile ; b = y, c = x, a = tile
4244
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
4246
;---------------------------------------------------------------------------------------------------------
4247
; gfxSetSpritePattern
4249
; BC = pattern number
4250
;---------------------------------------------------------------------------------------------------------
4252
gfxSetSpritePattern:
4254
call gfxCALATR ; get sprite attribute table address
4258
ld a, (BIOS_RG1SAV) ; bit 0 = double size, bit 1 = sprite size (0=8 pixels, 1=16 pixels)
4260
jr z, gfxSetSpritePattern.1
4263
gfxSetSpritePattern.1:
4264
ld a, c ; pattern number
4272
;---------------------------------------------------------------------------------------------------------
4274
; HL = point to sprite data as a string of 8 or 32 characters according the sprites size (8x8 or 16x16)
4276
;---------------------------------------------------------------------------------------------------------
4281
call gfxCALPAT ; get sprite pattern data address
4283
call gfxGSPSIZ ; return in 'a' sprite default size
4287
jr z, gfxSetSpriteData.1
4288
jr nc, gfxSetSpriteData.1
4302
if defined GFX_SPRITES
4304
;---------------------------------------------------------------------------------------------------------
4306
; initialises all sprites
4307
;---------------------------------------------------------------------------------------------------------
4313
;---------------------------------------------------------------------------------------------------------
4315
; set sprite default x, y, pattern and color
4317
;---------------------------------------------------------------------------------------------------------
4322
ld a, (BIOS_GRPACY) ; y
4325
ld a, (BIOS_GRPACX) ; x
4331
ld a, (BIOS_FORCLR) ; color
4337
if defined EXIST_DATA_SET
4339
;---------------------------------------------------------------------------------------------------------
4341
;---------------------------------------------------------------------------------------------------------
4350
;---------------------------------------------------------------------------------------------------------
4351
; gfxClearTileScreen
4352
;---------------------------------------------------------------------------------------------------------
4357
ld hl, (BIOS_GRPNAM)
4361
call gfxSetTileDefaultColor
4363
call gfxSetTileDefaultColor
4365
jp gfxSetTileDefaultColor
4367
gfxSetTileDefaultColor
4368
call gfxGetTileColorAddr
4370
call gfxGetTileDefaultColor
4374
gfxGetTileDefaultColor:
4387
; HL = tile color pointer
4388
; BC = tile color size
4391
;---------------------------------------------------------------------------------------------------------
4394
; HL = tile data pointer
4395
; BC = tile data size
4397
; A = flip (0=no, 1=yes)
4398
;---------------------------------------------------------------------------------------------------------
4402
jr nz, gfxSetTileDataFlip
4404
gfxSetTileDataNoFlip:
4405
call gfxGetTileDataAddr
4409
call gfxGetTileDataAddr
4411
gfxSetTileDataFlip.Loop:
4420
jr nz, gfxSetTileDataFlip.Loop
4423
; in de = tile number
4424
; out de = tile number address
4430
add hl, hl ; tile number * 8
4431
ld de, (BIOS_GRPCGP)
4437
;---------------------------------------------------------------------------------------------------------
4440
; HL = tile color pointer
4441
; BC = tile color size
4443
;---------------------------------------------------------------------------------------------------------
4446
call gfxGetTileColorAddr
4449
; in de = tile number
4450
; out de = tile number address
4451
gfxGetTileColorAddr:
4456
add hl, hl ; tile number * 8
4457
ld de, (BIOS_GRPCOL)
4463
;---------------------------------------------------------------------------------------------------------
4465
; set screen tile at x,y
4469
;---------------------------------------------------------------------------------------------------------
4481
; ld l, b ; slow y * 32
4498
ld de, (BIOS_GRPNAM)
4505
if defined gfxGetTileFromXY or defined EXIST_DATA_SET
4507
;---------------------------------------------------------------------------------------------------------
4509
; get screen tile at x,y
4513
;---------------------------------------------------------------------------------------------------------
4535
ld de, (BIOS_GRPNAM)
4541
;---------------------------------------------------------------------------------------------------------
4542
; Sprite collision table routines
4543
;---------------------------------------------------------------------------------------------------------
4545
gfxInitSpriteCollisionTable:
4548
ld (GFX_SPRITE_COLLISION), ix
4551
; copy sprite attribute table to ram
4552
gfxFillSpriteCollisionTable:
4553
ld hl, (BIOS_ATRBAS) ; source: attribute table
4554
ld de, (GFX_SPRITE_COLLISION) ; dest: ram
4555
ld bc, 128 ; 32*4 = size of attribute table
4558
; pre-calculate each sprite width
4559
gfxCalculateSpriteCollisionTable:
4560
ld ix, (GFX_SPRITE_COLLISION) ; start of sprites attributes
4561
ld b, 32 ; sprite count
4563
gfxCalculateSpriteCollisionTable.start:
4564
ld a, (GFX_SPRITE_SIZE_DAT) ; sprite size
4566
ld d, 208 ; Y no-display flag
4570
cp 3 ; above screen 3?
4571
jr c, gfxCalculateSpriteCollisionTable.next
4572
ld d, 216 ; Y no-display flag
4574
gfxCalculateSpriteCollisionTable.next:
4575
; test IC flag (no collision) in color table
4576
; test EC flag (early clock, shift 32 dots to the left) in color table
4577
; set x1 = x + size, y1 = y + size
4579
cp d ; test if sprite will not be displayed
4592
djnz gfxCalculateSpriteCollisionTable.next
4595
; BC = sprite number to check
4596
gfxUpdateSpriteCollisionTable:
4604
ld hl, (GFX_SPRITE_COLLISION) ; dest: ram
4607
ld hl, (BIOS_ATRBAS) ; source: attribute table
4609
ld bc, 4 ; 4 = size of attribute table to 1 sprite]
4613
ld b, 1 ; sprite count
4615
call gfxCalculateSpriteCollisionTable.start
4618
ld a, (GFX_SPRITE_FLAGS)
4619
and 0xBF ; clear collision flag
4620
ld (GFX_SPRITE_FLAGS), a
4622
ld a, (BIOS_STATFL) ; verify if collision occurred
4625
jr gfxCheckSpriteCollisionTable.start
4627
; BC = sprite number to check
4628
gfxCheckSpriteCollisionTable:
4629
; get target sprite address (iy)
4633
ld de, (GFX_SPRITE_COLLISION)
4640
gfxCheckSpriteCollisionTable.start:
4641
; start test against others sprites
4642
ld ix, (GFX_SPRITE_COLLISION)
4644
ld d, 208 ; Y no-display flag
4648
cp 3 ; above screen 3?
4649
jr c, gfxCheckSpriteCollisionTable.test
4650
ld d, 216 ; Y no-display flag
4652
gfxCheckSpriteCollisionTable.test:
4653
; skip target sprite
4656
jr z, gfxCheckSpriteCollisionTable.next
4658
; test if x1 > nx and x < nx1 and y1 > ny and y < ny1
4660
cp d ; test if sprite will not be displayed
4661
ret z ; return false
4664
jr nc, gfxCheckSpriteCollisionTable.next
4668
jr nc, gfxCheckSpriteCollisionTable.next
4672
jr nc, gfxCheckSpriteCollisionTable.next
4676
jr nc, gfxCheckSpriteCollisionTable.next
4678
; if so, save collider sprite
4680
ld (GFX_SPRITE_COLLIDER), a
4682
ld a, (GFX_SPRITE_FLAGS)
4683
or 0x40 ; set collision flag
4684
ld (GFX_SPRITE_FLAGS), a
4688
gfxCheckSpriteCollisionTable.next:
4696
ret z ; return false
4697
jr gfxCheckSpriteCollisionTable.test
4701
;---------------------------------------------------------------------------------------------------------
4702
; VDP / VRAM support routines
4703
;---------------------------------------------------------------------------------------------------------
4707
; c = register number
4708
; a = register number
4711
ret nz ; is negative? read only
4713
ret z ; is register 8? then status register 0 (read only)
4714
jr nc, gfxWRTVDP.1 ; is > 8? then control registers numbers added 1
4729
;jp nz, BIOS_NWRVDP ; msx 2
4731
;jp nz, BIOS_NWRVDP ; msx 2
4732
jp BIOS_WRTVDP ; msx 1
4735
; in a = register number
4739
jr nz, gfxRDVDP.1 ; is negative? then status register 1 to 9
4741
jr z, gfxRDVDP.2 ; is register 8? then status register 0
4743
jr nc, gfxRDVDP.3 ; is >= 9? then control registers numbers added 1
4744
ld hl, BIOS_RG0SAV ; else is correct control registers numbers
4756
jp BIOS_NRDVDP ;BIOS_VDPSTA
4763
ld hl, BIOS_RG8SAV-9
4771
; in: A=Data byte, BC=Length, HL=VRAM address
4780
; in: A=Sprite pattern number
4781
; out: HL=Sprite pattern address
4791
; in: A=Sprite number
4792
; out: HL=Sprite attribute address
4802
; out: A=Bytes in sprite pattern (8 or 32)
4822
; in: BC=Length, dest DE=VRAM address, source HL=RAM address
4827
; in: BC=Length, dest DE=RAM address, source HL=VRAM address
4854
; 8 bytes / 206 cycles
4855
; http://www.retroprogramming.com/2014/01/fast-z80-bit-reversal.html
4862
djnz gfxReverseA.loop
4867
;---------------------------------------------------------------------------------------------------------
4869
;---------------------------------------------------------------------------------------------------------
4878
RET_MATH_LIB: call COPY_TO.TMP_DAC
4884
MATH_DECADD: ld ix, addSingle
4889
if defined MATH.SUB or defined MATH.NEG
4891
MATH_DECSUB: ld ix, subSingle
4896
if defined MATH.MULT
4898
MATH_DECMUL: ld ix, mulSingle
4905
MATH_DECDIV: ld ix, divSingle
4913
MATH_SNGEXP: ld ix, powSingle
4920
MATH_COS: ld ix, cosSingle
4927
MATH_SIN: ld ix, sinSingle
4934
MATH_TAN: ld ix, tanSingle
4941
MATH_ATN: ld ix, atanSingle
4948
MATH_SQR: ld ix, sqrtSingle
4955
MATH_LOG: ld ix, lnSingle
4962
MATH_EXP: ld ix, expSingle
4969
MATH_ABSFN: ld ix, absSingle
4974
if defined MATH.SEED or defined MATH.NEG
4976
MATH_NEG: ld ix, negSingle
4983
MATH_SGN: ld ix, sgnSingle
4988
if defined RND or defined MATH.SEED
4990
MATH_RND: ld ix, randSingle
4995
MATH_FRCINT: ld hl, BASIC_DAC
5008
ld (BASIC_VALTYP), a
5011
MATH_FRCDBL: ; same as MATH_FRCSGL
5012
MATH_FRCSGL: ld hl, BASIC_DAC+2 ; input address
5013
ld bc, BASIC_DAC ; output address
5016
ld (BASIC_VALTYP), a
5019
MATH_ICOMP: ld a, h ; cp hl, de (alternative to bios DCOMPR)
5021
jr nz, MATH_ICOMP.NE.HIGH
5024
jr nz, MATH_ICOMP.NE.LOW
5026
MATH_ICOMP.NE.HIGH: jr c, MATH_ICOMP.GT.HIGH
5028
jr nz, MATH_DCOMP.GT
5030
MATH_ICOMP.GT.HIGH: bit 7, d
5033
MATH_ICOMP.NE.LOW: jr c, MATH_DCOMP.GT
5036
MATH_XDCOMP: ; same as MATH_DCOMP
5037
MATH_DCOMP: ld ix, cmpSingle
5041
MATH_DCOMP.GT: ld a, 0xFF ; DAC > ARG
5043
MATH_DCOMP.EQ: ld a, 0 ; DAC = ARG
5045
MATH_DCOMP.LT: ld a, 1 ; DAC < ARG
5048
if defined CAST_STR_TO.VAL
5050
MATH_FIN: ; HL has the source string
5051
ld a, (BASIC_VALTYP)
5052
cp 2 ; test if integer
5054
ld hl, (BASIC_DAC+2)
5059
MATH_FIN.1: ld BC, BASIC_DAC
5065
if defined CAST_INT_TO.STR
5067
MATH_FOUT: ld a, (BASIC_VALTYP)
5068
cp 2 ; test if integer
5070
ld hl, (BASIC_DAC+2)
5075
MATH_FOUT.1: ld hl, BASIC_DAC
5086
;---------------------------------------------------------------------------------------------------------
5088
; Copyright 2018 Zeda A.K. Thomas
5089
;---------------------------------------------------------------------------------------------------------
5091
; https://github.com/Zeda/z80float
5092
; https://www.omnimaga.org/asm-language/(z80)-floating-point-routines/
5093
; https://en.wikipedia.org/wiki/Single-precision_floating-point_format
5094
;---------------------------------------------------------------------------------------------------------
5096
; HL points to the first operand
5097
; DE points to the second operand (if needed)
5098
; IX points to the third operand (if needed, rare)
5099
; BC points to where the result should be output
5100
; Floats are stored by a little-endian 24-bit mantissa. However, the highest bit
5101
; is taken as implicitly 1, so we replace it as a sign bit. Next comes an 8-bit
5102
; exponent biased by +128.
5103
;---------------------------------------------------------------------------------------------------------
5104
; Adapted to MSXBas2Asm by Amaury Carvalho, 2019
5105
;---------------------------------------------------------------------------------------------------------
5107
;---------------------------------------------------------------------------------------------------------
5109
;---------------------------------------------------------------------------------------------------------
5111
BASIC_HOLD8: equ 0xF806 ; 48 Work area for decimal multiplications.
5112
BASIC_HOLD2: equ 0xF836 ; 8 Work area in the execution of numerical operators.
5113
BASIC_HOLD: equ 0xF83E ; 8 Work area in the execution of numerical operators.
5114
scrap: equ BASIC_HOLD8
5115
seed0: equ BASIC_RNDX
5116
seed1: equ seed0 + 4
5117
var48: equ scrap + 4
5120
addend2: equ scrap+7 ;4 bytes
5121
var_x: equ BASIC_HOLD8 + 4 ;4 bytes
5122
var_y: equ var_x + 4 ;4 bytes
5123
var_z: equ var_y + 4 ;4 bytes
5124
var_a: equ var_z + 4 ;4 bytes
5125
var_b: equ var_a + 4 ;4 bytes
5126
var_c: equ var_b + 4 ;4 bytes
5127
temp: equ var_c + 4 ;4 bytes
5128
temp1: equ temp + 4 ;4 bytes
5129
temp2: equ temp1 + 4 ;4 bytes
5130
temp3: equ temp2 + 4 ;4 bytes
5132
pow10exp_single: equ scrap+9
5133
strout_single: equ 0xF750 ; PARM2 - BASIC_BUF ;pow10exp_single+2
5135
;---------------------------------------------------------------------------------------------------------
5137
;---------------------------------------------------------------------------------------------------------
5139
;;Still need to tend to special cases
5207
pop hl ;bigger float
5339
;;Need to adjust sign flag
5362
;;How many push/pops are needed?
5370
;;How many push/pops are needed?
5376
;;How many push/pops are needed?
5377
;;Return bigger number
5384
;---------------------------------------------------------------------------------------------------------
5386
;---------------------------------------------------------------------------------------------------------
5409
jp addInject ;jumps in to the addSingle routine
5411
;---------------------------------------------------------------------------------------------------------
5413
;---------------------------------------------------------------------------------------------------------
5416
;Inputs: HL points to float1, DE points to float2, BC points to where the result is copied
5417
;Outputs: float1*float2 is stored to (BC)
5418
;573+mul24+{0,35}+{0,30}
5421
;avg: 2055.13839751681cc
5447
;;return float in CHLB
5457
jr z,mulSingle_case0
5469
;jr z,mulSingle_case1
5473
jp z,mulSingle_case1
5478
rra ; |Lots of help from Runer112 and
5479
adc a,a ; |calc84maniac for optimizing
5480
jp po,bad ; |this exponent check.
5489
call mul24 ;BDE*CHL->HLBCDE, returns sign info
5546
;special*x = special
5567
;basically, if b|c has bit 5 set, return NaN
5600
;;avg :1464.9033203125cc (1464+925/1024)
5603
;avg: 1449.63839751681cc
5644
;---------------------------------------------------------------------------------------------------------
5646
;---------------------------------------------------------------------------------------------------------
5649
;;HL points to numerator
5650
;;DE points to denominator
5651
;;BC points to where the quotient gets written
5653
divSingle_no_pushpop:
5659
xor (hl) ; |Get sign of output
5666
ex de,hl ; |Get exponent
5773
call divsub1 ;34 or 66
5791
;34cc or 66cc or 93cc
5806
;---------------------------------------------------------------------------------------------------------
5808
; https://www.geeksforgeeks.org/write-a-c-program-to-calculate-powxn/
5809
; https://stackoverflow.com/questions/3518973/floating-point-exponentiation-without-power-function
5810
;---------------------------------------------------------------------------------------------------------
5811
;double mypow( double base, double power, double precision )
5813
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
5814
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
5815
; else if ( precision >= 1 ) {
5816
; if( base >= 0 ) return sqrt( base );
5817
; else return sqrt( -base );
5818
; } else return sqrt( mypow( base, power*2, precision*2 ) );
5821
if defined MATH.POW or defined MATH_EXP or defined MATH_LOG or defined MATH_LN
5827
;;BC points to output
5831
ld bc, var_y ; power
5836
ld hl, const_precision
5837
ld bc, var_a ; precision
5840
ld bc, var_z ; result
5849
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
5855
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
5861
; else if ( precision >= 1 ) {
5862
; if( base >= 0 ) return sqrt( base );
5863
; else return sqrt( -base );
5869
; } else return sqrt( mypow( base, power*2, precision*2 ) );
5894
; return 1 / mypow( base, -power, precision );
5913
; return base * mypow( base, power-1, precision );
5932
; if( base >= 0 ) return sqrt( base );
5933
; else return sqrt( -base );
5959
; 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)))))
5960
;Please note that usually I like to reduce to [-.5,.5] as the extra overhead is usually worth it.
5961
;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.
5964
;x-=int(x) ;leaves x in [0,1)
5966
;;if x==inf -> out==inf
5967
;;if x==-inf -> out==0
5968
;;if x==NAN -> out==NAN
5975
push af ;keep track of sign
5985
jr c,_pow_1 ;int(x)=0
5998
jr nz,exp_normalized
6009
jr exp_normalized ;.db $11 ;start of `ld de,**`
6016
jr comp_exp ;.db $06 ;start of 'ld b,*` just to eat the next byte
6025
jp z,exp_underflow+1
6026
;perform 1-(var48+10)--> var48+10
6034
;our 'x' is at var48+10
6035
;our `temp` is at var48+6 so as not to cause issues with mulSingle)
6036
;uses 14 bytes of RAM
6078
;-inf -> +0 because lim approaches 0 from the right
6100
;-inf -> +0 because lim approaches 0 from the right
6102
sbc a,a ;FF if should be 0,
6117
;---------------------------------------------------------------------------------------------------------
6119
;---------------------------------------------------------------------------------------------------------
6121
if defined MATH_SQR or defined MATH_EXP
6123
;Uses 3 bytes at scrap
6125
;552+{0,19}+8{0,3+{0,3}}+pushpop+sqrtHLIX
6144
jp z,sqrtSingle_special
6147
push af ;new exponent
6157
;AHL is the new remainder
6158
;Need to divide by 2, then divide by the 16-bit (var_x+4)
6162
;We are just going to approximate it
6244
;Output: DE is the sqrt, AHL is the remainder
6245
;speed: 754+{0,1}+6{0,6}+{0,3+{0,18}}+{0,38}+sqrtHL
6269
jr _15a ;.db $FE ;start of `cp *`
6283
jr _16a ;.db $FE ;start of `cp *`
6297
jr _17a ;.db $FE ;start of `cp *`
6311
jr _18a ;.db $FE ;start of `cp *`
6315
;Now we have four more iterations
6316
;The first two are no problem
6328
jr _19a ;.db $FE ;start of `cp *`
6342
jr _20a ;.db $FE ;start of `cp *`
6347
;On the next iteration, HL might temporarily overflow by 1 bit
6349
rl d ;sla e \ rl d \ inc e
6353
adc hl,hl ;This might overflow!
6354
jr c,sqrt32_iter15_br0
6367
;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
6370
ld b,a ;either 0x00 or 0x80
6391
;returns A as the sqrt, HL as the remainder, D = 0
6405
jr _23a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
6416
jr _24a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
6427
dec d ;this resets the low bit of D, so `srl d` resets carry.
6428
jr _25a ;.db $06 ;start of ld b,* which is 7cc to skip the next byte.
6450
jr _27a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
6463
jr _28a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
6485
;---------------------------------------------------------------------------------------------------------
6487
;---------------------------------------------------------------------------------------------------------
6489
if defined MATH_LOG or defined MATH_LN
6492
; x / (1 + x/(2-x+4x/(3-2x+9x/(4-3x+16x/(5-4x)))))
6493
; a * x ^ (1/a) - a, where a = 100
6496
ld de, const_100_inv
6498
call powSingle ; temp = x ^ (1/100)
6502
call mulSingle ; temp1 = temp * 100
6505
call subSingle ; bc = temp1 - 100
6510
;---------------------------------------------------------------------------------------------------------
6512
;---------------------------------------------------------------------------------------------------------
6529
;---------------------------------------------------------------------------------------------------------
6531
;---------------------------------------------------------------------------------------------------------
6538
;;BC points to the output
6543
;;DE points to lg(y), HL points to x, BC points to output
6552
;---------------------------------------------------------------------------------------------------------
6554
; https://en.wikipedia.org/wiki/List_of_trigonometric_identities
6555
; https://en.wikipedia.org/wiki/Taylor_series#Trigonometric_functions
6556
; https://cs.stackexchange.com/questions/89245/how-approximate-sine-using-taylor-series
6557
; https://stackoverflow.com/questions/42217069/approximating-sinex-with-a-taylor-series-in-c-and-having-a-lot-of-problems
6558
;---------------------------------------------------------------------------------------------------------
6560
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
6563
; taylor: x - x^3/6 + x^5/120 - x^7/5040
6564
; x(1 - x^2(1/6 - x^2(1/120 - x^2/5040)) )
6566
; var_b = round( x / (2*PI), 0 )
6567
; var_c = x - var_b*2*PI
6568
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
6569
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
6570
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
6577
call copySingle ; return 0
6581
call trigRangeReductionSinCos
6586
call mulSingle ; var_b = var_a * var_a
6590
call mulSingle ; temp = x^2/5040
6594
call subSingle ; temp1 = 1/120 - temp
6598
call mulSingle ; temp = x^2 * temp1
6602
call subSingle ; temp1 = 1/6 - temp
6606
call mulSingle ; temp = x^2 * temp1
6610
call subSingle ; temp1 = 1 - temp
6614
call mulSingle ; return x * temp1
6617
trigRangeReductionSinCos:
6620
; var_b = round( x / (2*PI), 0 )
6628
; var_c = x - var_b*2*PI
6632
call mulSingle ; temp = var_b*2*PI
6636
call subSingle ; var_c = x - temp
6637
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
6641
jr nc, trigRangeReductionSinCos.else.2
6644
call copySingle ; temp1 = var_c
6645
jr trigRangeReductionSinCos.endif.2
6646
trigRangeReductionSinCos.else.2:
6650
call addSingle ; temp1 = var_c + 2*PI
6651
trigRangeReductionSinCos.endif.2:
6652
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
6656
jr c, trigRangeReductionSinCos.else.3
6657
jr z, trigRangeReductionSinCos.else.3
6661
call subSingle ; temp2
6662
jr trigRangeReductionSinCos.endif.3
6663
trigRangeReductionSinCos.else.3:
6666
call copySingle ; temp2 = temp1
6667
trigRangeReductionSinCos.endif.3:
6668
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
6669
ld hl, const_half_pi
6672
jr c, trigRangeReductionSinCos.else.4
6673
jr z, trigRangeReductionSinCos.else.4
6677
call subSingle ; var_a
6678
jr trigRangeReductionSinCos.endif.4
6679
trigRangeReductionSinCos.else.4:
6682
call copySingle ; var_a = temp2
6683
trigRangeReductionSinCos.endif.4:
6684
; if( temp > PI, -1, 1 )
6688
jr nc, trigRangeReductionSinCos.endif.5
6692
ld (ix+2), a ; turn var_a to negative
6693
trigRangeReductionSinCos.endif.5:
6699
;---------------------------------------------------------------------------------------------------------
6701
;---------------------------------------------------------------------------------------------------------
6703
if defined MATH_COS or defined MATH_TAN
6706
; taylor: 1 - x^2/2 + x^4/24 - x^6/720
6707
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
6708
; reduction: same as sin
6717
call copySingle ; return 1
6721
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
6722
call trigRangeReductionSinCos
6727
call mulSingle ; var_b = var_a * var_a
6731
call mulSingle ; temp = x^2/720
6735
call subSingle ; temp1 = 1/24 - temp
6739
call mulSingle ; temp = x^2 * temp1
6743
call subSingle ; temp1 = 1/2 - temp
6747
call mulSingle ; temp = x^2 * temp1
6751
call subSingle ; temp1 = 1 - temp
6753
; temp3 = abs(var_c)
6754
; temp1 = temp1 * if( temp3 >= PI/2, -1, 1 ) ==> cos sign
6761
ld (ix+2), a ; temp3 = abs(var_c)
6763
ld de, const_half_pi
6764
call cmpSingle ; if temp3 >= PI/2 then temp1 = -temp1
6765
jr nc, cosSingle.endif.1
6769
ld (ix+2), a ; temp1 = -temp1
6773
call copySingle ; return temp1
6778
;---------------------------------------------------------------------------------------------------------
6780
;---------------------------------------------------------------------------------------------------------
6801
;---------------------------------------------------------------------------------------------------------
6803
;---------------------------------------------------------------------------------------------------------
6808
;taylor: x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
6809
; x < -1: atan - PI/2
6810
; x >= 1: PI/2 - atan
6811
;reduction: abs(X) > 1 : Y = 1 / X
6812
; abs(X) <= 1: Y = X
6821
call copySingle ; return 0
6825
;x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
6826
call trigRangeReductionAtan
6832
call mulSingle ; var_b = var_a * var_a
6836
call mulSingle ; temp = (4*x)^2
6840
call divSingle ; temp1 = temp/9
6844
call addSingle ; temp = 7 + temp1
6848
call mulSingle ; temp1 = var_b * 9
6852
call divSingle ; temp2 = temp1 / temp
6856
call addSingle ; temp = 5 + temp2
6860
call mulSingle ; temp1 = var_b * 4
6864
call divSingle ; temp2 = temp1 / temp
6868
call addSingle ; temp = 3 + temp2
6872
call divSingle ; temp2 = var_b / temp
6876
call addSingle ; temp = 1 + temp2
6880
call divSingle ; temp2 = var_a / temp
6882
; x >= 1: PI/2 - atan
6886
ld hl, const_half_pi
6893
; x < -1: atan - PI/2
6904
ld de, const_half_pi
6913
call copySingle ; return temp2
6916
trigRangeReductionAtan:
6917
;reduction: abs(X) > 1 : Y = 1 / X
6918
; abs(X) <= 1: Y = X
6927
ld (ix+2), a ; abs(x)
6931
jr nc, trigRangeReductionAtan.1
6937
jr trigRangeReductionAtan.2
6938
trigRangeReductionAtan.1:
6943
trigRangeReductionAtan.2:
6947
jr c, trigRangeReductionAtan.3
6951
ld (ix+2), a ; y = -y
6952
trigRangeReductionAtan.3:
6957
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
6959
;---------------------------------------------------------------------------------------------------------
6961
;---------------------------------------------------------------------------------------------------------
6975
;---------------------------------------------------------------------------------------------------------
6977
;---------------------------------------------------------------------------------------------------------
7046
if defined MATH_ABSFN
7048
;---------------------------------------------------------------------------------------------------------
7050
;---------------------------------------------------------------------------------------------------------
7053
;;HL points to the float
7054
;;BC points to where to output the result
7073
;---------------------------------------------------------------------------------------------------------
7075
;---------------------------------------------------------------------------------------------------------
7078
;;HL points to the float
7079
;;BC points to where to output the result
7084
if defined powSingle or defined sgnSingle or defined MATH_NEG
7086
;---------------------------------------------------------------------------------------------------------
7088
;---------------------------------------------------------------------------------------------------------
7091
;;HL points to the float
7092
;;BC points to where to output the result
7098
jr nz, negSingle.test.sign
7101
jr nz, negSingle.test.sign
7104
jr nz, negSingle.test.sign
7107
jr nz, negSingle.test.sign
7118
negSingle.test.sign:
7121
jr z, negSingle.positive
7125
call negSingle.positive
7144
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
7146
;---------------------------------------------------------------------------------------------------------
7148
;---------------------------------------------------------------------------------------------------------
7151
;Input: HL points to float1, DE points to float2
7153
; float1 >= float2 : nc
7154
; float1 < float2 : c,nz
7155
; float1 == float2 : z
7156
; There is a margin of error allowed in the lower 2 bits of the mantissa.
7158
;Currently fails when both numbers have magnitude less than about 2^-106
7193
ld a,(scrap+3) ;new power
7194
pop bc ;B is old power
7204
or 1 ;not equal, so reset z flag
7205
rla ;if negative, float1<float2, setting c flag as wanted, else nc.
7215
;---------------------------------------------------------------------------------------------------------
7217
;---------------------------------------------------------------------------------------------------------
7220
;Stores a pseudo-random number on [0,1)
7221
;it won't produce values on (0,2^-23)
7230
;DEHL is the mantissa, B is the exponent
7246
;If we needed to shift more than 8 bits, we'll load in more random data
7251
jp nc,rand_no_more_rand_data
7259
rand_no_more_rand_data:
7278
;;Tested and passes all CAcert tests
7279
;;Uses a very simple 32-bit LCG and 32-bit LFSR
7280
;;it has a period of 18,446,744,069,414,584,320
7281
;;roughly 18.4 quintillion.
7282
;;LFSR taps: 0,2,6,7 = 11000101
7284
;;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.
7285
;Uses 64 bits of state
7321
if defined MATH_FOUT
7323
;---------------------------------------------------------------------------------------------------------
7325
; in HL = Single address
7326
; BC = String address
7327
; out A = String size
7328
; http://0x80.pl/notesen/2015-12-29-float-to-string.html
7329
; http://0x80.pl/articles/convert-float-to-integer.html
7330
;---------------------------------------------------------------------------------------------------------
7344
; Move the float to scrap
7348
; Make the float negative, write a '-' if already negative
7357
ld a,'-' ; write '-' simbol
7365
; Check if the exponent field is 0 (a special value)
7372
; We should write '0' next. When rounding 9.999999... for example, not padding with a 0 will return '.' instead of '1.'
7380
; Now we need to perform signed (A-128)*77 (approximation of exponent*log10(2))
7388
ld (pow10exp_single),a ;The base-10 exponent
7392
ld de,pow10LUT ;get the table of 10^-(2^k)
7394
ld hl, pow10exp_single
7396
call singletostr_mul
7397
call singletostr_mul
7398
call singletostr_mul
7399
call singletostr_mul
7400
call singletostr_mul
7401
call singletostr_mul
7402
;now the number is pretty close to a nice value
7404
; If it is less than 1, multiply by 10
7409
;ld hl,scrap ;Since singletostr_mul returns BC = scrap, can do this cheaper
7415
ld hl,pow10exp_single
7421
; Convert to a fixed-point number !
7435
;We need to get 7 digits
7437
pop hl ;Points to the string
7439
;The first digit can be as large as 20, so it'll actually be two digits
7443
;Increment the exponent :)
7444
ld de,(pow10exp_single-1)
7446
ld (pow10exp_single-1),de
7455
; Get the remaining digits.
7462
call singletostrmul10
7467
;Save the pointer to the end of the string
7474
jr c,rounding_done_single
7475
jr _40a ;.db $DA ;start of `jp c,*` in order to skip the next instruction
7484
rounding_done_single:
7487
;Strip the leading zero if it exists (rounding may have bumped this to `1`)
7499
;Now lets move HL-DE bytes at DE+1 to DE
7511
;If z flag is reset, this means that the exponent should be bumped up 1
7512
ld a,(pow10exp_single)
7515
ld (pow10exp_single),a
7518
;if -4<=A<=6, then need to insert the decimal place somewhere.
7523
;for this, we need to insert the decimal after the first digit
7524
;Then, we need to append the exponent string
7526
ld de,strout_single-1
7528
cp '-' ;negative sign
7536
;remove any stray zeroes at the end before appending the exponent
7540
; Write the exponent
7543
ld a,(pow10exp_single)
7546
ld (hl),'-' ;negative sign
7564
ld de, strout_single
7567
ld a, l ; string size
7569
ld hl,strout_single-1
7573
ld a,(pow10exp_single)
7577
;need to put zeroes before everything
7580
cp '-' ;negative sign
7606
ld de,strout_single-1
7610
cp '-' ;negative sign
7621
ld hl,strout_single-1
7639
;multiply the 0.24 fixed point number at scrap by 10
7640
;overflow in A register
7675
;Check that the last digit isn't a decimal!
7729
;---------------------------------------------------------------------------------------------------------
7731
; https://www.ticalc.org/pub/86/asm/source/routines/atof.asm
7732
;---------------------------------------------------------------------------------------------------------
7737
ptr_sto: equ scrap+9
7739
;;#Routines/Single Precision
7741
;; HL points to the string
7742
;; BC points to where the float is output
7744
;; scrap+9 is the pointer to the end of the string
7746
;; 11 bytes at scrap ?
7751
;Check if there is a negative sign.
7760
;Skip all leading zeroes
7763
jr z,$-4 ;jumps back to the `inc hl`
7766
;Check if the next char is char_DEC
7768
or a ;to reset the carry flag
7770
jr _54a ;.db $FE ;start of cp *
7777
jr z,$-5 ;jumps back to the `dec b`
7780
;Now we read in the next 8 digits
7786
;Now `scrap` holds the 4-digit base-100 number.
7788
;if carry flag is set, just need to get rid of remaining digits
7789
;Otherwise, need to get rid of remaining digits, while incrementing the exponent
7800
jp z,strToSingle_inf
7803
;Now check for engineering `E` to modify the exponent
7807
;Gotta multiply the number at (scrap) by 2^24
7810
call scrap_times_256
7813
call scrap_times_256
7816
call scrap_times_256
7819
call scrap_times_256
7822
;Now scrap+3 is a 4-byte mantissa that needs to be normalized
7830
jp z,strToSingle_zero-1
7834
jp m,strToSingle_normed
7835
;Will need to iterate at most three times
7848
;Move the number to scrap
7857
;now (scrap) is our number, need to multiply by power of 10!
7858
;Power of 10 is stored in B, need to put in A first
7866
jp nc,strToSingle_inf+1
7869
jp nc,strToSingle_zero
7893
cp char_NEG ;negative exponent?
7945
call scrap_times_sub
7958
jr nz,strToSingle_inf
7976
if defined roundSingle or defined MATH_FRCSGL
7978
;---------------------------------------------------------------------------------------------------------
7980
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
7981
;---------------------------------------------------------------------------------------------------------
7988
ld l, (ix) ; convert integer parameter to single float
7990
ld bc, 0x1000 ; bynary digits count + sign
7992
int2Single.test.zero:
7994
or h ; test if hl is not zero
7995
jr nz, int2Single.test.negative
7997
jr nz, int2Single.test.negative
8002
int2Single.test.negative:
8003
bit 7, h ; test if hl is negative
8004
jr z, int2Single.normalize
8005
ld c, 0x80 ; sign negative
8014
int2Single.normalize:
8017
jr nz, int2Single.mount
8020
jr int2Single.normalize
8023
res 7, h ; turn off upper bit
8025
ld a, c ; restore sign
8027
ld h, a ; ...into upper mantissa
8029
ld e, h ; sign+mantissa
8030
ld h, l ; high mantissa
8031
ld l, 0 ; low mantissa
8033
ld a, b ; binary digits count
8034
or 0x80 ; exponent bias
8039
ld (ix), l ; low mantissa
8040
ld (ix+1), h ; high mantissa
8041
ld (ix+2), e ; sign + mantissa
8042
ld (ix+3), d ; expoent
8051
if defined roundSingle or defined MATH_FRCINT
8053
;---------------------------------------------------------------------------------------------------------
8055
; http://0x80.pl/articles/convert-float-to-integer.html
8056
;---------------------------------------------------------------------------------------------------------
8059
; HL points to the single-precision float
8061
; HL is the 16-bit signed integer part of the float
8062
; BC points to 16-bit signed integer
8079
jr c,no_shift_single_to_int16
8081
jr nc,no_shift_single_to_int16
8103
jr _67a ;.db $11 ;start of ld de,*
8115
no_shift_single_to_int16:
8137
;---------------------------------------------------------------------------------------------------------
8138
; Auxiliary routines
8139
;---------------------------------------------------------------------------------------------------------
8146
const_pi: db $DB,$0F,$49,$81
8147
const_e: db $54,$f8,$2d,$81
8148
const_lg_e: db $3b,$AA,$38,$80
8149
const_ln_2: db $18,$72,$31,$7f
8150
const_log2: db $9b,$20,$1a,$7e
8151
const_lg10: db $78,$9a,$54,$81
8152
const_0: db $00,$00,$00,$00
8153
const_1: db $00,$00,$00,$80
8154
const_2: dw 0, 33024
8155
const_3: dw 0, 33088
8156
const_4: dw 0, 33280
8157
const_5: dw 0, 33312
8158
const_7: dw 0, 33376
8159
const_9: dw 0, 33552
8160
const_16: dw 0, 33792
8161
const_100: db $00,$00,$48,$86
8162
const_100_inv: dw 55050, 31011
8163
const_precision: db $77,$CC,$2B,$65 ;10^-8
8164
const_half_1: dw 0, 32512
8165
const_inf: db $00,$00,$40,$00
8166
const_NegInf: db $00,$00,$C0,$00
8167
const_NaN: db $00,$00,$20,$00
8168
const_log10_e: db $D9,$5B,$5E,$7E
8169
const_2pi: db $DB,$0F,$49,$82
8170
const_2pi_inv: db $83,$F9,$22,$7D
8171
const_half_pi: dw 4059, 32841
8172
const_p25: db $00,$00,$00,$7E
8173
const_p5: db $00,$00,$00,$7F
8176
sin_a1: dw 43691, 32042
8177
sin_a2: dw 34952, 30984
8178
sin_a3: dw 3329, 29520
8179
cos_a1: equ const_half_1
8180
cos_a2: dw 43691, 31530
8181
cos_a3: dw 2914, 30262
8182
exp_a1: db $15,$72,$31,$7F ;.693146989552
8183
exp_a2: db $CE,$FE,$75,$7D ;.2402298085906
8184
exp_a3: db $7B,$42,$63,$7B ;.0554833215071
8185
exp_a4: db $FD,$94,$1E,$79 ;.00967907584392
8186
exp_a5: db $5E,$01,$23,$76 ;.001243632065103
8187
exp_a6: db $5F,$B7,$63,$73 ;.0002171671843714
8188
const_1p40625: db $00,$00,$34,$80 ;1.40625
8190
if defined MATH_CONSTSINGLE
8198
;A is the constant ID#
8199
;returns nc if failed, c otherwise
8200
;HL points to the constant
8201
cp (end_const-start_const)>>2
8208
;#if ((end_const-4)>>8)!=(start_const>>8)
8221
db $CD,$CC,$4C,$7C ;.1
8222
db $0A,$D7,$23,$79 ;.01
8223
db $17,$B7,$51,$72 ;.0001
8224
db $77,$CC,$2B,$65 ;10^-8
8225
db $95,$95,$66,$4A ;10^-16
8226
db $1F,$B1,$4F,$15 ;10^-32
8229
db $00,$00,$20,$83 ;10
8230
db $00,$00,$48,$86 ;100
8231
db $00,$40,$1C,$8D ;10000
8232
db $20,$BC,$3E,$9A ;10^8
8233
db $CA,$1B,$0E,$B5 ;10^16
8234
db $AE,$C5,$1D,$EA ;10^32
8241
;C>=128 135+6{0,33+{0,1}}+{0,20+{0,8}}
8242
;C>=64 115+5{0,33+{0,1}}+{0,20+{0,8}}
8243
;C>=32 95+4{0,33+{0,1}}+{0,20+{0,8}}
8244
;C>=16 75+3{0,33+{0,1}}+{0,20+{0,8}}
8245
;C>=8 55+2{0,33+{0,1}}+{0,20+{0,8}}
8246
;C>=4 35+{0,33+{0,1}}+{0,20+{0,8}}
8247
;C>=2 15+{0,20+{0,8}}
8250
;avg: 349.21279907227cc
8341
;26 bytes, adds 118cc to the traditional routine
8376
;c flag means don't increment the exponent
8379
jr c,ascii_to_uint8_noexp
8381
jr z,ascii_to_uint8_noexp-2
8385
jr nc,ascii_to_uint8_noexp_end
8397
jr z,ascii_to_uint8_noexp_2nd
8401
jr nc,ascii_to_uint8_noexp_end
8412
ascii_to_uint8_noexp:
8415
jr nc,ascii_to_uint8_noexp_end
8422
ascii_to_uint8_noexp_2nd:
8427
jr nc,ascii_to_uint8_noexp_end
8430
jr ascii_2 ;.db $FE ;start of `cp **`, saves 1cc
8431
ascii_to_uint8_noexp_end:
8441
if defined MATH_RSUBSINGLE
8462
jp addInject ;jumps in to the addSingle routine
8466
if defined MATH_MOD1SINGLE
8468
;This routine performs `x mod 1`, returning a non-negative value.
8491
jr z,mod1Single_special
8504
;If it is zero, need to set exponent to zero and return
8527
;make sure it isn't zero else we need to add 1
8539
;If INF, need to return NaN instead
8540
;For 0 and NaN, just return itself :)
8560
if defined MATH_FOUT
8562
; --------------------------------------------------------------
8563
; Converts a signed integer value to a zero-terminated ASCII
8564
; string representative of that value (using radix 10).
8566
; Brandon Wilson WikiTI
8567
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Other:DispA#Decimal_Signed_Version
8568
; --------------------------------------------------------------
8570
; HL Value to convert (two's complement integer).
8571
; DE Base address of string destination. (pointer).
8572
; --------------------------------------------------------------
8575
; --------------------------------------------------------------
8576
; REGISTERS/MEMORY DESTROYED
8578
; --------------------------------------------------------------
8584
; Detect sign of HL.
8588
; HL is negative. Output '-' to string and negate HL.
8593
; Negate HL (using two's complement)
8597
ld a, 0 ; Note that XOR A or SUB A would disturb CF
8601
; Convert HL to digit characters
8603
ld b, 0 ; B will count character length of number
8606
call div_hl_c; HL = HL / A, A = remainder
8613
; Retrieve digits from stack
8621
; Terminate string with NULL
8632
ld a, l ; string size
8640
;===============================================================
8641
; Convert a string of base-10 digits to a 16-bit value.
8642
; http://z80-heaven.wikidot.com/math#toc32
8644
; DE points to the base 10 number string in RAM.
8646
; HL is the 16-bit value of the number
8647
; DE points to the byte after the number
8652
; A (actually, add 30h and you get the ending token)
8655
; n is the number of digits
8657
; at most 595 cycles for any 16-bit decimal value
8658
;===============================================================
8661
ld hl,0 ; 10 : 210000
8678
jr nc,ConvLoop ;12|23: 30EE
8680
jr ConvLoop ; --- : 18EB
8687
; return remainder in a
8688
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division
8709
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
8739
djnz div_dehl_c.loop
8747
;---------------------------------------------------------------------------------------------------------
8748
; VARIABLES INITIALIZE
8749
;---------------------------------------------------------------------------------------------------------
8753
ld (VAR_DUMMY.COUNTER), a ; max circular queue = 8 dummys
8754
ld hl, VAR_DUMMY.DATA ; start of variable dummy circular queue
8755
ld (VAR_DUMMY.POINTER), hl
8756
ld b, VAR_DUMMY.LENGTH
8761
djnz INITIALIZE_DUMMY.1
8766
ld (BASIC_DATPTR), hl ; next DATA pointer to use by READ command
8768
ld (BASIC_DATLIN), hl ; index of DATA item to use by READ command
8771
INITIALIZE_VARIABLES:
8772
call INITIALIZE_DATA
8773
call INITIALIZE_DUMMY
8776
call gfxInitSpriteCollisionTable
8779
;if defined COMPILE_TO_ROM
8780
; ld ix, BIOS_JIFFY ; initialize rom clock
8790
;---------------------------------------------------------------------------------------------------------
8791
; MAIN WORK AREA - LITERALS / VARIABLES / CONFIGURATIONS
8792
;---------------------------------------------------------------------------------------------------------
8794
if defined COMPILE_TO_ROM
8797
pgmPage1.pad: equ pageSize - (workAreaPad - pgmArea)
8799
if pgmPage1.pad >= 0
8802
; .WARNING "There's no free space left on program page 1"
8807
VAR_STACK.START: equ ramArea
8808
;VAR_STACK.END: equ VAR_STACK.START + 0x800 ; 2kb (~200 variables)
8810
VAR_STACK.POINTER: equ VAR_STACK.START
8812
PRINT.CRLF: db 3, 0, 0, 2
8813
dw PRINT.CRLF.DATA, 0, 0, 0
8814
PRINT.CRLF.DATA: db 13,10,0
8816
PRINT.TAB: db 3, 0, 0, 1
8817
dw PRINT.TAB.DATA, 0, 0, 0
8818
PRINT.TAB.DATA: db 09,0
8821
LIT_NULL_DBL: dw 0, 0, 0, 0
8827
LIT_QUOTE_CHAR: db '\"'
8830
LIT_TRUE: db 2, 0, 0
8834
LIT_FALSE: db 2, 0, 0
8866
AFTER_LAST_VARIABLE: equ VAR_STACK.POINTER + 0
8868
VAR_DUMMY.START: equ AFTER_LAST_VARIABLE ; variable dummy circular queue area
8869
VAR_DUMMY.COUNTER: equ VAR_DUMMY.START ; variable dummy circular queue count
8870
VAR_DUMMY.POINTER: equ VAR_DUMMY.COUNTER + 1 ; pointer to next variable dummy
8871
VAR_DUMMY.DATA: equ VAR_DUMMY.POINTER + 2 ; first variable dummy
8873
VAR_DUMMY.SIZE: equ 8
8874
VAR_DUMMY.LENGTH: equ (11 * VAR_DUMMY.SIZE)
8875
VAR_DUMMY.END: equ VAR_DUMMY.DATA + VAR_DUMMY.LENGTH
8876
VAR_STACK.END: equ VAR_DUMMY.END + 1
8878
;--------------------------------------------------------
8880
;--------------------------------------------------------
8883
DATA_ITEMS_COUNT: equ 0
8885
DATA_SET_ITEMS_START:
8886
DATA_SET_ITEMS_COUNT: equ 0
8889
;---------------------------------------------------------------------------------------------------------
8891
;---------------------------------------------------------------------------------------------------------
8893
if defined COMPILE_TO_ROM
8897
pgmPage2.pad: equ romSize - (romPad - pgmArea)
8899
if pgmPage2.pad >= 0
8902
if pgmPage2.pad < lowLimitSize
8903
.WARNING "There's only less than 5% free space on this ROM"
8906
.ERROR "There's no free space left on this ROM"
8911
end_file: end start_pgm ; label start is the entry point