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
;--------------------------------------------------------
414
;--------------------------------------------------------
416
;--------------------------------------------------------
417
COMPILE_TO_ROM: EQU 1
419
MACRO __call_basic,CALL_PARM
424
if defined COMPILE_TO_DOS
426
MACRO __call_bios,CALL_PARM
427
;ld iy,(BIOS_EXPTBL-1)
429
call BIOS_CALBAS ; BIOS_CALSLT
434
MACRO __call_bios,CALL_PARM
441
push hl ; save parameter
445
pop iy ; restore PC of caller
446
pop hl ; get next parameter
447
push iy ; save PC of caller
451
pop iy ; restore PC of caller
452
push hl ; save return parameter
453
push iy ; save PC of caller
457
pop iy ; restore PC of caller
458
push hl ; save return parameter
459
push iy ; save PC of caller
463
MACRO set.line.number, line_number
464
ld bc, line_number ; current line number
465
ld (BASIC_CURLIN), bc
469
ld a, (BIOS_INTFLG) ; verify CTRL+BREAK
475
ld a, (BASIC_ONGSBF) ; trap occured counter
481
;---------------------------------------------------------------------------------------------------------
483
;---------------------------------------------------------------------------------------------------------
485
romSize: equ 0x8000 ; ROM size (32k)
486
pageSize: equ 0x4000 ; Page size (16k)
487
lowLimitSize: equ 0x400 ; 10% of a page size
489
if defined COMPILE_TO_BIN
491
pgmArea: equ 0x8000 ; page 2 - program area
492
ramArea: equ 0xc000 ; page 3 - free RAM start area
494
org pgmArea ; program binary type start address
495
db 0FEh ; binary file ID
496
dw start_pgm ; begin address
497
dw end_file - 1 ; end address
498
dw start_pgm ; program execution address (for ,R option)
501
if defined COMPILE_TO_ROM
503
pgmArea: equ 0x4000 ; page 1 and 2 - program area
504
ramArea: equ 0xc000 ; page 3 - free RAM start area
506
org pgmArea ; program rom type start address
507
db 'AB' ; rom file ID
509
dw 0x0000 ; STATEMENT
516
pgmArea: equ 0x8000 ; page 2 - program area
517
ramArea: equ 0xc000 ; page 3 - free RAM start area
519
org pgmArea ; program DOS type start address ; 0x0100
525
;---------------------------------------------------------------------------------------------------------
527
;---------------------------------------------------------------------------------------------------------
529
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
532
call PROGRAM_SLOT_2_RESTORE
533
__call_basic BASIC_READYR ; warm start Basic
538
call PROGRAM_SLOT_GET
539
ld (BIOS_RAMAD2), a ; Save RAM slot of page 8000h-BFFFh
542
PROGRAM_SLOT_2_RESTORE:
545
jp BIOS_ENASLT ; Select the RAM on page 8000h-BFFFh
547
PROGRAM_SLOT_2_ENABLE:
551
and 3 ;Keep bits corresponding to the page
568
jp BIOS_ENASLT ; Select the ROM on page 8000h-BFFFh
571
; a <- slot ID formatted FxxxSSPP
572
; Modifies: af, bc, de, hl
573
; ref: https://www.msx.org/forum/msx-talk/development/fusion-c-and-htimi#comment-366469
577
jr z,PrimaryShiftContinue
582
PrimaryShiftContinue:
584
jr z,PrimaryShiftDone
599
inc hl ; move to SLTTBL
606
jr z,SecondaryShiftContinue
611
SecondaryShiftContinue:
613
jr nz,SecondaryShiftDone
623
if defined COMPILE_TO_DOS
628
__call_bios BIOS_ENASLT ; Select main ROM on page 0 (0000h~3FFFh)
635
;---------------------------------------------------------------------------------------------------------
637
;---------------------------------------------------------------------------------------------------------
639
start_pgm: ; start of the program
641
if defined COMPILE_TO_DOS
643
call BIOS_SLOT_ENABLE ; enable bios on page 0
644
;call BASIC_SLOT_ENABLE ; enable basic on page 1
648
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
650
call PROGRAM_SLOT_2_SAVE ; save slot on page 2
651
call PROGRAM_SLOT_2_ENABLE ; enable program on page 2
655
__call_bios BIOS_ERAFNK ; turn off function keys display
656
__call_bios BIOS_GICINI ; initialize sound system
657
__call_bios BIOS_INITXT ; initialize text screen
659
ld (BIOS_CLIKSW), a ; disable keyboard click
661
ld (BASIC_CURLIN), bc ; interpreter in direct mode
662
__call_basic BASIC_TRAP_CLEAR ; clear traps work space
663
;call INITIALIZE_PARAMETERS ; initialize parameters stack
664
call memory.init ; initialize memory allocation
665
call INITIALIZE_VARIABLES ; initialize variables
669
call CLS ; action call
672
ld hl, LIT_5 ; parameter
674
call PRINT ; action call
675
ld hl, PRINT.CRLF ; parameter
677
call PRINT ; action call
680
ld hl, LIT_6 ; parameter
682
call PRINT ; action call
683
ld hl, IDF_8 ; parameter
685
call INPUT ; action call
688
ld hl, LIT_9 ; parameter
690
call PRINT ; action call
691
ld hl, IDF_10 ; parameter
693
call INPUT ; action call
696
ld hl, LIT_12 ; parameter
698
ld hl, IDF_11 ; parameter
700
call LET ; action call
703
ld hl, LIT_13 ; parameter
705
call PRINT ; action call
706
ld hl, IDF_8 ; parameter
708
call PRINT ; action call
709
ld hl, LIT_14 ; parameter
711
call PRINT ; action call
712
ld hl, IDF_10 ; parameter
714
call PRINT ; action call
715
ld hl, LIT_15 ; parameter
717
call PRINT ; action call
718
ld hl, IDF_11 ; parameter
720
call PRINT ; action call
721
ld hl, PRINT.CRLF ; parameter
723
call PRINT ; action call
726
IF_1 : ; start of IF command
727
ld hl, IDF_8 ; parameter
729
ld hl, LIT_18 ; parameter
731
call BOOLEAN.EQ ; action call
732
ld hl, IDF_10 ; parameter
734
ld hl, LIT_20 ; parameter
736
call BOOLEAN.EQ ; action call
737
call BOOLEAN.AND ; action call
738
call BOOLEAN.IF ; verify IF condition result, out in A
740
jp z, ELSE_1 ; if false, jump to ELSE actions
741
THEN_1 : ; THEN actions
743
jp ENDIF_1 ; jump to END of IF command
744
ELSE_1 : ; ELSE actions
745
ENDIF_1 : ; end of IF command
748
IF_2 : ; start of IF command
749
ld hl, IDF_8 ; parameter
751
ld hl, IDF_10 ; parameter
753
call BOOLEAN.EQ ; action call
754
call BOOLEAN.IF ; verify IF condition result, out in A
756
jp z, ELSE_2 ; if false, jump to ELSE actions
757
THEN_2 : ; THEN actions
758
ld hl, LIT_24 ; parameter
760
call PRINT ; action call
761
ld hl, PRINT.CRLF ; parameter
763
call PRINT ; action call
764
jp ENDIF_2 ; jump to END of IF command
765
ELSE_2 : ; ELSE actions
766
ENDIF_2 : ; end of IF command
769
IF_3 : ; start of IF command
770
ld hl, IDF_8 ; parameter
772
ld hl, IDF_10 ; parameter
774
call BOOLEAN.GT ; action call
775
call BOOLEAN.IF ; verify IF condition result, out in A
777
jp z, ELSE_3 ; if false, jump to ELSE actions
778
THEN_3 : ; THEN actions
779
ld hl, LIT_26 ; parameter
781
call PRINT ; action call
782
ld hl, PRINT.CRLF ; parameter
784
call PRINT ; action call
785
jp ENDIF_3 ; jump to END of IF command
786
ELSE_3 : ; ELSE actions
787
IF_4 : ; start of IF command
788
ld hl, IDF_8 ; parameter
790
ld hl, IDF_10 ; parameter
792
call BOOLEAN.LT ; action call
793
call BOOLEAN.IF ; verify IF condition result, out in A
795
jp z, ELSE_4 ; if false, jump to ELSE actions
796
THEN_4 : ; THEN actions
797
ld hl, LIT_29 ; parameter
799
call PRINT ; action call
800
ld hl, PRINT.CRLF ; parameter
802
call PRINT ; action call
803
jp ENDIF_4 ; jump to END of IF command
804
ELSE_4 : ; ELSE actions
805
ENDIF_4 : ; end of IF command
806
ENDIF_3 : ; end of IF command
811
;---------------------------------------------------------------------------------------------------------
813
;---------------------------------------------------------------------------------------------------------
815
end_pgm: __call_bios BIOS_DSPFNK ; turn on function keys display
817
ld (BIOS_CLIKSW), a ; enable keyboard click
819
if defined COMPILE_TO_ROM
822
if defined COMPILE_TO_DOS
825
__call_basic BASIC_READYR ; warm start Basic
829
ret ; end of the program
831
;__call_bios BIOS_GICINI ; initialize sound system
832
;if defined COMPILE_TO_DOS or defined COMPILE_TO_ROM
833
; __call_bios BIOS_RESET ; restart Basic
835
; __call_basic BASIC_END ; end to Basic
839
;---------------------------------------------------------------------------------------------------------
841
;---------------------------------------------------------------------------------------------------------
846
; out IX = variable assigned address
847
pop.parm ; get variable address parameter
848
push hl ; just to transfer hl to ix
850
ld a, (ix) ; get variable type
851
cp 3 ; test if string
852
jr nz, LET.PARM ; if not a string, it isn't necessary to free memory
853
ld a, (ix + 3) ; get variable string length
855
jr z, LET.PARM ; if zero, it isn't necessary to free memory
856
ld c, (ix + 4) ; get old string address low
857
ld b, (ix + 5) ; get old string address high
858
push ix ; save variable address
859
push bc ; just to transfer bc (old string address) to ix
861
call memory.free ; free memory
862
pop ix ; restore variable address
863
LET.PARM: pop.parm ; get data address parameter (out hl = data address)
864
ld a, (ix + 2) ; get variable type flag
865
or a ; cp 0 - test type flag (0=any, 255=fixed)
866
jr nz, LET.FIXED ; if type flag is fixed, so casting is necessary
867
LET.ANY: push ix ; just to transfer ix (variable address) to de
869
ldi ; copy 1 byte from hl (data address) to de (variable address)
870
inc de ; go to variable data area
872
inc hl ; go to data data area
874
ld bc, 8 ; data = 8 bytes
875
ldir ; copy bc bytes from hl (data address) to de (variable address)
876
ld a, (ix) ; get variable type
877
cp 3 ; test if string
878
ret nz ; if not string, return
879
jp LET.STRING ; else do string treatment (in ix = variable address)
880
LET.FIXED: push ix ; save variable destination address
881
push hl ; save variable source address
882
ld a, (ix) ; get variable fixed type, and hl has parameter data address
883
call CAST_TO ; cast data to type (in hl = variable address, a = type to, out hl = casted data address)
885
pop ix ; restore variable address
886
ld a, (ix) ; get variable destination type again
887
cp 3 ; test if string
888
jr nz, LET.VALUE ; if not string, do value treatment
889
ld a, (de) ; get variable source type again
890
cp 3 ; test if string
891
jr nz, LET.FIX1 ; if not string, get casted string size
896
ld (ix + 3), a ; source string size
898
LET.FIX1: call GET_STR.LENGTH ; get string length (in HL, out a)
899
ld (ix + 3), a ; set variable length
900
LET.FIX2: ld (ix + 4), l ; casted data address low
901
ld (ix + 5), h ; casted data address high
902
jp LET.STRING ; do string treatment (in ix = variable address)
903
LET.VALUE: push ix ; just to transfer ix (variable address) to de
905
inc de ; go to variable data area (and the data from its casted)
908
ld bc, 8 ; data = 8 bytes
909
ldir ; copy bc bytes from hl (data address) to de (variable address)
911
LET.STRING: ld a, (ix + 3) ; string size
912
or a ; cp 0 - test if null
913
jr nz, LET.ALLOC ; if not null, allocate new string (in ix = variable address)
914
ld bc, LIT_NULL_STR ; else, set to a null string literal
915
ld (ix + 4), c ; variable address low
916
ld (ix + 5), b ; variable address high
918
LET.ALLOC: push ix ; save variable address
919
ld l, (ix + 4) ; source string address low
920
ld h, (ix + 5) ; source string address high
921
push hl ; save copy from address
922
ld c, (ix + 3) ; get variable length
924
inc bc ; string length have one more byte from zero terminator
925
push bc ; save variable lenght + 1
926
call memory.alloc ; in bc = size, out ix = address, nz=OK
928
push ix ; just to transfer memory address from ix to de
930
pop bc ; restore bytes to be copied
931
pop hl ; restore copy from string address
932
push de ; save copy to address
933
ldir ; copy bc bytes from hl (data address) to de (variable address)
936
pop de ; restore copy to address
937
pop ix ; restore variable address
938
ld (ix + 4), e ; put memory address low into variable
939
ld (ix + 5), d ; put memory address high into variable
940
ret ; variable assigned
945
pop.parm ; get parameter boolean result in hl
948
ld a, (ix+5) ; put boolean integer result in a
954
if defined EXIST_DATA_SET
956
jp z, gfxClearTileScreen
959
__call_bios BIOS_CLS ; clear screen
965
pop.parm ; get first parameter
968
ret z ; return if string size zero
969
if defined EXIST_DATA_SET
970
ld (BIOS_TEMP), a ; size of string
974
; discard if first char < 32 or > 126
981
; adjust default color
994
call gfxSetTileDefaultColor
1001
ld hl, (BIOS_GRPACY)
1003
;call MATH.MULT.16 ; slow y * 32
1013
ld de, (BIOS_GRPACX)
1015
ld de, (BIOS_GRPNAM)
1030
pop.parm ; get first parameter
1031
ld (BIOS_TEMP), hl ; input variable
1033
ld (BIOS_CLIKSW), a ; enable keyboard click
1034
__call_bios BIOS_QINLIN ; get user input (hl = text start, carry if STOP)
1040
call MATH.PARM.POP ; get parameters into DAC/ARG
1041
ld a, (BASIC_VALTYP) ;
1042
cp 2 ; test if integer
1043
jp z, BOOLEAN.EQ.INT ;
1044
cp 3 ; test if string
1045
jp z, BOOLEAN.EQ.STR ;
1046
cp 4 ; test if single
1047
jp z, BOOLEAN.EQ.SGL ;
1048
jp BOOLEAN.EQ.DBL ; it is a double
1053
call MATH.PARM.POP ; get parameters into DAC/ARG
1054
ld a, (BASIC_VALTYP) ;
1055
cp 2 ; test if integer
1056
jp z, BOOLEAN.AND.INT ;
1057
jp MATH.ERROR ; it is a double
1062
call MATH.PARM.POP ; get parameters into DAC/ARG
1063
ld a, (BASIC_VALTYP) ;
1064
cp 2 ; test if integer
1065
jp z, BOOLEAN.GT.INT ;
1066
cp 3 ; test if string
1067
jp z, BOOLEAN.GT.STR ;
1068
cp 4 ; test if single
1069
jp z, BOOLEAN.GT.SGL ;
1070
jp BOOLEAN.GT.DBL ; it is a double
1075
call MATH.PARM.POP ; get parameters into DAC/ARG
1076
ld a, (BASIC_VALTYP) ;
1077
cp 2 ; test if integer
1078
jp z, BOOLEAN.LT.INT ;
1079
cp 3 ; test if string
1080
jp z, BOOLEAN.LT.STR ;
1081
cp 4 ; test if single
1082
jp z, BOOLEAN.LT.SGL ;
1083
jp BOOLEAN.LT.DBL ; it is a double
1087
; abstract virtual GOTO
1090
;---------------------------------------------------------------------------------------------------------
1091
; MSX BASIC SUPPORT CODE
1092
;---------------------------------------------------------------------------------------------------------
1094
if defined CHR or defined INKEY.STR
1100
ld bc, 2 ; string size
1101
call memory.alloc ; in bc size, out ix new memory address, nz=OK
1102
jr z, COPY_CHAR_TO_STR.ERROR
1108
pop hl ; HL = string address
1111
COPY_CHAR_TO_STR.ERROR:
1117
;if defined INPUT or defined LINE_INPUT or defined SPC or defined SPACE or defined INPUT.FUNCTION.STR
1120
; out: a = size, hl = address
1129
call memory.alloc ; in bc = size, out ix = address, nz=OK
1139
djnz GET_STRING_SPACE.1
1146
; in hl = string, out hl = temp string
1148
call GET_STR.LENGTH ; get string length, in hl = address, out a = size
1152
call GET_STRING_SPACE ; in bc = size, out hl = address, out a = string size
1160
ldir ; copy bc bytes from hl to de
1167
if defined INPUT or defined LINE_INPUT
1170
jr c, INPUT.EXIT ; exit if CTRL+STOP
1172
inc hl ; string start
1173
call COPY_TO.TEMP_STR
1174
call COPY_TO.VAR_DUMMY.STR ; make a fake string variable from HL
1175
push.parm ; LET parameter 2 - fake string variable as right operand
1177
push.parm ; LET parameter 1 - input variable as left operand
1178
call LET ; put string into variable
1181
ld (BIOS_CLIKSW), a ; disable keyboard click
1186
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
1190
RUN_TRAPS.1: push hl
1201
; in hl = trap block address (handle trap: sts=5? has handler? ackn, pause, run trap, sts=1? unpause)
1203
ld a, (hl) ; trap status
1204
cp 5 ; trap occured AND trap not paused AND trap enabled ?
1205
ret nz ; return if false
1207
ld e, (hl) ; get trap address
1214
ret z ; return if address zero
1216
__call_basic BASIC_TRAP_ACKNW
1217
__call_basic BASIC_TRAP_PAUSE
1218
ld hl, TRAP_HANDLER.1
1219
ld a, (BASIC_ONGSBF) ; save traps execution
1222
ld (BASIC_ONGSBF), a ; disable traps execution
1223
push hl ; next return will be to trap handler
1224
push de ; indirect jump to trap address
1226
TRAP_HANDLER.1: pop af
1227
ld (BASIC_ONGSBF), a ; restore traps execution
1230
cp 1 ; trap enabled?
1232
__call_basic BASIC_TRAP_UNPAUSE
1235
; hl = trap block, de = trap handler
1237
ld (hl), a ; trap block status
1239
ld (hl), e ; trap block handler (pointer)
1246
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
1249
ld (BIOS_TEMP), a ; save voice number
1253
ret nz ; return if not string
1256
ld (BIOS_TEMP2), a ; save string size
1257
push hl ; string address
1258
ld a, (BIOS_TEMP) ; restore voice number
1259
call BIOS_GETVCP ; get PSG voice buffer address (in A = voice number, out HL = address of byte 2)
1261
ld a, (BIOS_TEMP2) ; restore string size
1262
ld (hl), a ; string size
1264
ld (hl), e ; string address
1268
ld D,H ; voice stack
1282
;---------------------------------------------------------------------------------------------------------
1283
; VARIABLES ROUTINES
1284
;---------------------------------------------------------------------------------------------------------
1286
; input hl = variable address
1287
; input bc = variable name
1288
; input d = variable type
1289
INIT_VAR: ld (hl), d ; variable type
1291
ld (hl), c ; variable name 1
1293
ld (hl), b ; variable name 2
1307
CLEAR.VAR.LOOP: inc hl
1308
ld (hl), 0 ; data address/value
1311
; input HL = variable address
1312
; input A = variable output type
1313
; output HL = casted data address
1323
; input HL = variable address
1324
; output HL = variable address
1325
CAST_TO.INT: ;push af
1330
jp z, CAST_STR_TO.INT
1332
jp z, CAST_SGL_TO.INT
1334
jp z, CAST_DBL_TO.INT
1337
; input HL = variable address
1338
; output HL = variable address
1339
CAST_TO.STR: ;push af
1342
jp z, CAST_INT_TO.STR
1346
jp z, CAST_SGL_TO.STR
1348
jp z, CAST_DBL_TO.STR
1351
; input HL = variable address
1352
; output HL = variable address
1353
CAST_TO.SGL: ;push af
1356
jp z, CAST_INT_TO.SGL
1358
jp z, CAST_STR_TO.SGL
1362
jp z, CAST_DBL_TO.SGL
1365
; input HL = variable address
1366
; output HL = variable address
1367
CAST_TO.DBL: ;push af
1370
jp z, CAST_INT_TO.DBL
1372
jp z, CAST_STR_TO.DBL
1374
jp z, CAST_SGL_TO.DBL
1379
CAST_SGL_TO.STR: ; same as CAST_INT_TO.STR
1380
CAST_DBL_TO.STR: ; same as CAST_INT_TO.STR
1381
CAST_INT_TO.STR: call COPY_TO.DAC
1383
__call_bios MATH_FOUT ; convert DAC to string
1384
call COPY_TO.TEMP_STR
1386
CAST_INT_TO.SGL: call COPY_TO.DAC
1387
__call_bios MATH_FRCSGL
1390
CAST_INT_TO.DBL: call COPY_TO.DAC
1391
__call_bios MATH_FRCDBL
1394
CAST_SGL_TO.INT: ; same as CAST_DBL_TO.INT
1395
CAST_DBL_TO.INT: call COPY_TO.DAC
1396
__call_bios MATH_FRCINT
1399
CAST_STR_TO.INT: ld a, 2
1400
call CAST_STR_TO.VAL ;
1402
__call_bios MATH_FRCINT
1406
CAST_STR_TO.SGL: ld a, 4
1407
call CAST_STR_TO.VAL ;
1409
__call_bios MATH_FRCSGL
1413
CAST_STR_TO.DBL: ld a, 8
1414
call CAST_STR_TO.VAL ;
1416
__call_bios MATH_FRCDBL
1420
CAST_STR_TO.VAL: ld (BASIC_VALTYP), a
1423
__call_bios MATH_FIN ; convert string to a value type
1426
GET_INT.VALUE: inc hl ; output BC with integer value
1432
CAST_SGL_TO.DBL: ; same as GET_DBL.ADDR
1433
CAST_DBL_TO.SGL: ; same as GET_DBL.ADDR
1434
GET_INT.ADDR: ; same as GET_DBL.ADDR
1435
GET_SGL.ADDR: ; same as GET_DBL.ADDR
1436
GET_DBL.ADDR: inc hl
1441
GET_STR.ADDR: push hl
1447
; input hl = string address
1448
; output a = string length
1449
GET_STR.LENGTH: push hl
1452
cpir ; hl = source, a = compare char, bc = count
1459
STRING.COMPARE: ld ix, (BASIC_DAC+1) ; string 1
1460
ld iy, (BASIC_ARG+1) ; string 2
1461
STRING.COMPARE.NX: ld a, (ix) ; next char from string 1
1462
cp (iy) ; char s1 = char s2?
1463
jr nz, STRING.COMPARE.NE ; if not equal...
1465
jr z, STRING.COMPARE.F1 ; if string 1 has finished...
1466
ld a, (iy) ; next char from string 2
1468
jr z, STRING.COMPARE.GT ; if s2 has finished, s1 has not finished yet, so s1 is greater than s2
1471
jr STRING.COMPARE.NX ; get next char pair
1472
STRING.COMPARE.F1: ld a, (iy) ; verify if string 2 has finished too
1474
jr z, STRING.COMPARE.EQ ; if s2 has finished, then they are equals
1475
jr STRING.COMPARE.LT ; else, result = s1 is less than s2
1476
STRING.COMPARE.NE: jr c, STRING.COMPARE.GT ; verify if s1 is greater than s2...
1477
STRING.COMPARE.LT: ld a, 1 ; ...else, result = s1 less than s2
1479
STRING.COMPARE.GT: ld a, 0xFF ; result = s1 is greater than s2
1481
STRING.COMPARE.EQ: xor a ; result = s1 is equal to s2
1483
STRING.CONCAT: ld ix, BASIC_DAC ; s1 size
1484
ld a, (BASIC_ARG) ; s2 size
1485
add a, (ix) ; s3 size = s1 size + s2 size
1489
inc bc ; add 1 byte to size
1490
call memory.alloc ; in bc size, out ix new memory address, nz=OK
1491
jp z, memory.error ;
1495
ld a, (BASIC_DAC) ; s1 size
1496
ld hl, (BASIC_DAC + 1) ; string 1
1497
call COPY_TO.STR ; copy to new memory
1498
ld a, (BASIC_ARG) ; s2 size
1499
ld hl, (BASIC_ARG + 1) ; string 2
1500
call COPY_TO.STR ; copy to new memory
1502
ld (de), a ; null terminated
1505
call COPY_TO.VAR_DUMMY.STR ;
1506
ret.parm ; WARNING - VERIFY STRING MEMORY LEAKs
1507
STRING.PRINT: ld a, (BIOS_SCRMOD) ; 0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode
1509
jr nc, STRING.PRINT.G2 ; jump if graphic screen mode MSX2 (>=5)
1511
jr nc, STRING.PRINT.G1 ; jump if graphic screen mode MSX1 (>=2)
1512
STRING.PRINT.T: ld a, (hl) ; get a char from a string parameter
1513
or a ; cp 0 - is it the string end?
1515
__call_bios BIOS_CHPUT ; put the char (a) into text screen
1517
jr STRING.PRINT.T ; repeat
1518
STRING.PRINT.G1: ld a, (hl) ; get a char from a string parameter
1519
or a ; cp 0 - is it the string end?
1521
__call_bios BIOS_GRPPRT ; put the char (a) into graphical screen
1523
jr STRING.PRINT.G1 ; repeat
1524
STRING.PRINT.G2: ld a, (hl) ; get a char from a string parameter
1525
or a ; cp 0 - is it the string end?
1527
ld ix, BIOS_GRPPRT2 ; put the char (a) into graphical screen
1530
jr STRING.PRINT.G2 ; repeat
1532
; a = string size to copy
1533
; input hl = string from
1534
; input de = string to
1536
ret z ; avoid copy if size = zero
1538
ld c, a ; string size
1539
ldir ; copy bc bytes from hl to de
1541
COPY_TO.BASIC_BUF: ld bc, BASIC_BUF
1542
ld a, (LIT_QUOTE_CHAR)
1545
COPY_BAS_BUF.LOOP: ld a, (hl)
1547
jr z, COPY_BAS_BUF.EXIT
1551
jr COPY_BAS_BUF.LOOP
1552
COPY_BAS_BUF.EXIT: ld a, (LIT_QUOTE_CHAR)
1559
COPY_TO.VAR_DUMMY: ld a, (BASIC_VALTYP) ; create dummy variable from VALTYPE
1561
jr nz, COPY_TO.VAR_DUMMY.DBL
1562
call GET_STR.LENGTH ; get string length, in hl = address, out a = size
1563
COPY_TO.VAR_DUMMY.STR: call GET_VAR_DUMMY.ADDR ; create dummy string variable from HL
1564
ld (ix), 3 ; data type string
1566
ld (ix+2), 255 ; var type fixed
1567
ld (ix+3), a ; string length
1568
ld (ix+4), l ; data address low
1569
ld (ix+5), h ; data address high
1570
push ix ; output var address...
1573
COPY_TO.VAR_DUMMY.INT: call GET_VAR_DUMMY.ADDR ; create dummy integer variable from BC
1574
ld (ix), 2 ; data type string
1585
push ix ; output var address...
1588
COPY_TO.VAR_DUMMY.DBL: call GET_VAR_DUMMY.ADDR ; create dummy value variable from DAC
1589
ld (ix), a ; data type
1594
push ix ; just to copy ix to de
1599
ldir ; copy bc bytes from hl (data address) to de (variable address)
1600
push ix ; output var address...
1603
GET_VAR_DUMMY.ADDR: push af ;
1606
ld ix, (VAR_DUMMY.POINTER) ;
1607
ld a, (VAR_DUMMY.COUNTER) ;
1608
GET_VAR_DUMMY.NEXT: add ix, de ;
1611
jr nz, GET_VAR_DUMMY.EXIT ;
1613
ld ix, VAR_DUMMY.DATA ;
1614
GET_VAR_DUMMY.EXIT: ld (VAR_DUMMY.POINTER), ix ;
1615
ld (VAR_DUMMY.COUNTER), a ;
1616
ld a, (ix) ; get last var dummy type
1617
cp 3 ; is it string?
1618
call z, GET_VAR_DUMMY.FREE ; free string memory
1625
ld a, (ix+3) ; string size
1626
ld l, (ix+4) ; get string data address
1631
call nz, memory.free ; free memory
1635
; input hl = variable address
1636
COPY_TO.DAC: ld de, BASIC_DAC
1637
COPY_TO.DAC.DATA: ld a, (hl)
1638
ld (BASIC_VALTYP), a
1642
ld bc, 8 ; data = 8 bytes
1643
ldir ; copy bc bytes from hl (data address) to de (variable address)
1645
COPY_TO.ARG: ld de, BASIC_ARG ;
1646
jr COPY_TO.DAC.DATA ;
1647
COPY_TO.DAC_ARG: ld hl, BASIC_DAC ;
1649
ld bc, 8 ; data = 8 bytes
1650
ldir ; copy bc bytes from hl (data address) to de (variable address)
1652
COPY_TO.ARG_DAC: ld hl, BASIC_ARG ;
1654
ld bc, 8 ; data = 8 bytes
1655
ldir ; copy bc bytes from hl (data address) to de (variable address)
1657
COPY_TO.DAC_TMP: ld hl, BASIC_DAC ;
1658
ld de, BASIC_SWPTMP ;
1659
ld bc, 8 ; data = 8 bytes
1660
ldir ; copy bc bytes from hl (data address) to de (variable address)
1662
COPY_TO.TMP_DAC: ld hl, BASIC_SWPTMP ;
1664
ld bc, 8 ; data = 8 bytes
1665
ldir ; copy bc bytes from hl (data address) to de (variable address)
1668
exx ; save registers
1671
ld de, BASIC_SWPTMP ;
1672
ldir ; copy bc bytes from hl to de
1676
ldir ; copy bc bytes from hl to de
1678
ld hl, BASIC_SWPTMP ;
1680
ldir ; copy bc bytes from hl to de
1681
exx ; restore registers
1684
CLEAR.DAC: ld de, BASIC_DAC
1685
CLEAR.DAC.DATA: ld hl, BASIC_VALTYP
1688
ld bc, 8 ; data = 8 bytes
1689
ldir ; copy bc bytes from hl (data address) to de (variable address)
1691
CLEAR.ARG: ld de, BASIC_ARG
1696
;---------------------------------------------------------------------------------------------------------
1697
; MATH 16 BITS ROUTINES
1698
;---------------------------------------------------------------------------------------------------------
1700
MATH.PARM.POP: pop af ; get PC from caller stack
1701
ex af, af' ; save PC to temp
1702
pop.parm ; get first parameter
1703
call COPY_TO.ARG ; put HL in ARG (return var type in A)
1704
pop.parm ; get second parameter
1705
ex af, af' ; restore PC from temp
1706
push af ; put again PC from caller in stack
1707
ex af, af' ; restore 1st data type
1708
push af ; save 1st data type
1709
call COPY_TO.DAC ; put HL in DAC (return var type in A)
1710
pop bc ; restore 1st data type (ARG) in B
1711
cp b ; test if data type in A (DAC) = data type in B (ARG)
1712
ret z ; return if is equal data types
1713
MATH.PARM.CAST: push bc ; else cast both to double
1714
and 12 ; test if single/double
1715
jr nz, MATH.PARM.CST1 ; avoid cast if already single/double
1716
__call_bios MATH_FRCDBL ; convert DAC to double
1717
MATH.PARM.CST1: pop af ;
1718
and 12 ; test if single/double
1719
jr nz, MATH.PARM.CST2 ; avoid cast if already single/double
1720
ld (BASIC_VALTYP), a ;
1721
call COPY_TO.DAC_TMP ;
1722
call COPY_TO.ARG_DAC ;
1723
__call_bios MATH_FRCDBL ; convert ARG to double
1724
call COPY_TO.DAC_ARG ;
1725
call COPY_TO.TMP_DAC ;
1726
MATH.PARM.CST2: ld a, 8 ;
1727
ld (BASIC_VALTYP), a ;
1729
MATH.PARM.POP.INT: ; return result in DAC/ARG as integer
1730
pop af ; get PC from caller stack
1731
ex af, af' ; save PC to temp
1732
pop.parm ; get first parameter
1733
ld a, (hl) ; get parameter type
1734
and 2 ; test if integer
1735
jr z, MATH.PARM.POP.I1 ; do cast if not integer
1736
call COPY_TO.ARG ; put HL in ARG (return var type in A)
1737
jr MATH.PARM.POP.I2 ; go to next parameter
1738
MATH.PARM.POP.I1: call COPY_TO.DAC ; put HL in DAC (return var type in A)
1739
__call_bios MATH_FRCINT ; convert DAC to int
1740
call COPY_TO.DAC_ARG ; copy DAC to ARG
1741
MATH.PARM.POP.I2: pop.parm ; get second parameter
1742
call COPY_TO.DAC ; put HL in DAC (return var type in A)
1743
and 2 ; test if integer
1744
jr nz, MATH.PARM.POP.I3 ; avoid cast if already integer
1745
__call_bios MATH_FRCINT ; convert DAC to int
1747
ld (BASIC_VALTYP), a ;
1749
ex af, af' ; restore PC from temp
1750
push af ; put again PC from caller in stack
1752
MATH.PARM.PUSH: call COPY_TO.VAR_DUMMY ;
1758
; output in parm stack
1759
; http://www.z80.info/zip/zaks_book.pdf - page 104
1760
MATH.ADD.INT: ld hl, (BASIC_DAC+2) ;
1761
ld bc, (BASIC_ARG+2) ;
1763
ld (BASIC_DAC+2), hl ;
1768
if defined MATH.SUB or defined MATH.NEG
1771
; output in parm stack
1772
; http://www.z80.info/zip/zaks_book.pdf - page 104
1773
MATH.SUB.INT: ld hl, (BASIC_DAC+2) ;
1774
ld de, (BASIC_ARG+2) ;
1777
ld (BASIC_DAC+2), hl ;
1782
if defined MATH.MULT
1785
; output in parm stack
1786
MATH.MULT.INT: ld hl, (BASIC_DAC+2) ;
1787
ld bc, (BASIC_ARG+2) ;
1789
ld (BASIC_DAC+2), hl ;
1792
; input HL = multiplicand
1793
; input BC = multiplier
1794
; output HL = result
1795
; http://www.z80.info/zip/zaks_book.pdf - page 131
1796
MATH.MULT.16: ld a, c ; low multiplier
1797
ld c, b ; high multiplier
1799
ld d, h ; multiplicand
1802
MULT16LOOP: srl c ; right shift multiplier high
1803
rra ; rotate right multiplier low
1804
jr nc, MULT16NOADD ; test carry
1805
add hl, de ; add multiplicand to result
1806
MULT16NOADD: ex de, hl
1807
add hl, hl ; double - shift multiplicand
1814
if defined MATH.DIV or defined MATH.IDIV or defined MATH.MOD
1816
; input AC = dividend
1817
; input DE = divisor
1818
; output AC = quotient
1819
; output HL = remainder
1820
; http://www.z80.info/zip/zaks_book.pdf - page 140
1821
MATH.DIV.16: ld hl, 0 ; clear accumulator
1822
ld b, 16 ; set counter
1823
DIV16LOOP: rl c ; rotate accumulator result left
1825
adc hl, hl ; left shift
1826
sbc hl, de ; trial subtract divisor
1827
jr nc, $ + 3 ; subtract was OK ($ = current location)
1828
add hl, de ; restore accumulator
1829
ccf ; calculate result bit
1830
djnz DIV16LOOP ; counter not zero
1831
rl c ; shift in last result bit
1837
if defined GFX_FAST or defined LINE
1839
; compare two signed 16 bits integers
1840
; HL < DE: Carry flag
1841
; HL = DE: Zero flag
1842
; http://www.z80.info/zip/zaks_book.pdf - page 531
1843
MATH.COMP.S16: ld a, h ; test high order byte
1844
and 0x80 ; test sign, clear carry
1845
jr nz, MATH.COMP.S16.NEGM1 ; jump if hl is negative
1847
ret nz ; de is negative (and hl is positive)
1849
cp d ; signs are both positive, so normal compare
1851
ld a, l ; test low order byte
1854
MATH.COMP.S16.NEGM1:
1856
rla ; sign bit into carry
1857
ret c ; signs different
1859
cp d ; both signs negative
1869
MATH.ADD.SGL: ld a, 8 ;
1870
ld (BASIC_VALTYP), a ;
1871
MATH.ADD.DBL: __call_bios MATH_DECADD ;
1876
if defined MATH.SUB or defined MATH.NEG
1878
MATH.SUB.SGL: ld a, 8 ;
1879
ld (BASIC_VALTYP), a ;
1880
MATH.SUB.DBL: __call_bios MATH_DECSUB ;
1885
if defined MATH.MULT
1887
MATH.MULT.SGL: ld a, 8 ;
1888
ld (BASIC_VALTYP), a ;
1889
MATH.MULT.DBL: __call_bios MATH_DECMUL ;
1897
; output in parm stack
1898
MATH.DIV.INT: __call_bios MATH_FRCDBL ; convert DAC to double
1901
ld (BASIC_VALTYP), a ;
1902
__call_bios MATH_FRCDBL ; convert ARG to double
1904
MATH.DIV.SGL: ld a, 8 ;
1905
ld (BASIC_VALTYP), a ;
1906
MATH.DIV.DBL: __call_bios MATH_DECDIV ;
1911
if defined MATH.IDIV
1914
; output in parm stack
1915
MATH.IDIV.SGL: ld a, 8 ;
1916
ld (BASIC_VALTYP), a ;
1917
MATH.IDIV.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
1920
ld (BASIC_VALTYP), a ;
1921
__call_bios MATH_FRCINT ; convert ARG to integer
1923
MATH.IDIV.INT: ld hl, (BASIC_DAC+2) ;
1926
ld de, (BASIC_ARG+2) ;
1930
ld (BASIC_DAC+2), hl ; quotient
1937
MATH.POW.INT: ld (BASIC_VALTYP), a ;
1938
__call_bios MATH_FRCDBL ; convert DAC to double
1941
ld (BASIC_VALTYP), a ;
1942
__call_bios MATH_FRCDBL ; convert ARG to double
1944
MATH.POW.SGL: ld a, 8 ;
1945
ld (BASIC_VALTYP), a ;
1946
MATH.POW.DBL: __call_bios MATH_DBLEXP ;
1953
;MATH.MOD.SGL: ld a, 8 ;
1954
; ld (BASIC_VALTYP), a ;
1955
;MATH.MOD.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
1956
; call SWAP.DAC.ARG ;
1958
; ld (BASIC_VALTYP), a ;
1959
; __call_bios MATH_FRCINT ; convert ARG to integer
1960
; call SWAP.DAC.ARG ;
1961
MATH.MOD.INT: ld hl, (BASIC_DAC+2) ;
1964
ld de, (BASIC_ARG+2) ;
1966
ld (BASIC_DAC+2), hl ; remainder
1973
; fast 16-bit integer square root
1974
; http://www.retroprogramming.com/2017/07/a-fast-z80-integer-square-root.html
1975
; 92 bytes, 344-379 cycles (average 362)
1976
; v2 - 3 t-state optimization spotted by Russ McNulty
1977
; call with hl = number to square root
1978
; returns a = square root
2055
if defined RANDOMIZE or defined SEED
2057
MATH.RANDOMIZE: di ;
2058
ld bc, (BIOS_JIFFY) ;
2061
MATH.SEED: ld (BASIC_RNDX), bc ; seed to IRND
2062
push bc ; in bc = new integer seed
2066
ld (BASIC_DAC+2), bc ; copy bc to dac
2067
ld a, 2 ; type integer
2068
ld (BASIC_VALTYP), a ;
2069
__call_bios MATH_FRCDBL ; convert DAC integer to DAC double
2070
__call_bios MATH_NEG ; DAC = -DAC
2071
__call_bios MATH_RND ; put in DAC a new random number from previous DAC parameter
2076
MATH.ERROR: ld e, 13 ; type mismatch
2077
__call_basic BASIC_ERROR_HANDLER ;
2081
;---------------------------------------------------------------------------------------------------------
2083
;---------------------------------------------------------------------------------------------------------
2085
BOOLEAN.RET.TRUE: ld hl, LIT_TRUE ;
2087
BOOLEAN.RET.FALSE: ld hl, LIT_FALSE ;
2089
BOOLEAN.CMP.INT: ld hl, (BASIC_DAC+2) ;
2090
ld de, (BASIC_ARG+2) ;
2091
__call_bios MATH_ICOMP ;
2093
BOOLEAN.CMP.SGL: ld bc, (BASIC_ARG) ;
2094
ld de, (BASIC_ARG+2) ;
2095
__call_bios MATH_DCOMP ;
2097
BOOLEAN.CMP.DBL: __call_bios MATH_XDCOMP ;
2099
BOOLEAN.CMP.STR: call STRING.COMPARE ;
2102
if defined BOOLEAN.GT
2104
BOOLEAN.GT.INT: call BOOLEAN.CMP.INT ;
2106
BOOLEAN.GT.STR: call BOOLEAN.CMP.STR ;
2108
BOOLEAN.GT.SGL: call BOOLEAN.CMP.SGL ;
2110
BOOLEAN.GT.DBL: call BOOLEAN.CMP.DBL ;
2112
BOOLEAN.GT.RET: cp 0x01 ;
2113
jp z, BOOLEAN.RET.TRUE ;
2114
jp BOOLEAN.RET.FALSE ;
2117
if defined BOOLEAN.LT
2119
BOOLEAN.LT.INT: call BOOLEAN.CMP.INT ;
2121
BOOLEAN.LT.STR: call BOOLEAN.CMP.STR ;
2123
BOOLEAN.LT.SGL: call BOOLEAN.CMP.SGL ;
2125
BOOLEAN.LT.DBL: call BOOLEAN.CMP.DBL ;
2127
BOOLEAN.LT.RET: cp 0xFF ;
2128
jp z, BOOLEAN.RET.TRUE ;
2129
jp BOOLEAN.RET.FALSE ;
2133
if defined BOOLEAN.GE
2135
BOOLEAN.GE.INT: call BOOLEAN.CMP.INT ;
2137
BOOLEAN.GE.STR: call BOOLEAN.CMP.STR ;
2139
BOOLEAN.GE.SGL: call BOOLEAN.CMP.SGL ;
2141
BOOLEAN.GE.DBL: call BOOLEAN.CMP.DBL ;
2143
BOOLEAN.GE.RET: cp 0x01 ;
2144
jp z, BOOLEAN.RET.TRUE ;
2146
jp z, BOOLEAN.RET.TRUE ;
2147
jp BOOLEAN.RET.FALSE ;
2151
if defined BOOLEAN.LE
2153
BOOLEAN.LE.INT: call BOOLEAN.CMP.INT ;
2155
BOOLEAN.LE.STR: call BOOLEAN.CMP.STR ;
2157
BOOLEAN.LE.SGL: call BOOLEAN.CMP.SGL ;
2159
BOOLEAN.LE.DBL: call BOOLEAN.CMP.DBL ;
2161
BOOLEAN.LE.RET: cp 0xFF ;
2162
jp z, BOOLEAN.RET.TRUE ;
2164
jp z, BOOLEAN.RET.TRUE ;
2165
jp BOOLEAN.RET.FALSE ;
2169
if defined BOOLEAN.NE
2171
BOOLEAN.NE.INT: call BOOLEAN.CMP.INT ;
2173
BOOLEAN.NE.STR: call BOOLEAN.CMP.STR ;
2175
BOOLEAN.NE.SGL: call BOOLEAN.CMP.SGL ;
2177
BOOLEAN.NE.DBL: call BOOLEAN.CMP.DBL ;
2179
BOOLEAN.NE.RET: or a ; cp 0
2180
jp nz, BOOLEAN.RET.TRUE ;
2181
jp BOOLEAN.RET.FALSE ;
2185
if defined BOOLEAN.EQ
2187
BOOLEAN.EQ.INT: call BOOLEAN.CMP.INT ;
2189
BOOLEAN.EQ.STR: call BOOLEAN.CMP.STR ;
2191
BOOLEAN.EQ.SGL: call BOOLEAN.CMP.SGL ;
2193
BOOLEAN.EQ.DBL: call BOOLEAN.CMP.DBL ;
2195
BOOLEAN.EQ.RET: or a ; cp 0
2196
jp z, BOOLEAN.RET.TRUE ;
2197
jp BOOLEAN.RET.FALSE ;
2201
if defined BOOLEAN.AND
2203
BOOLEAN.AND.INT: ld a, (BASIC_DAC+2) ;
2204
ld hl, BASIC_ARG+2 ;
2206
ld (BASIC_DAC+2), a ;
2208
ld a, (BASIC_DAC+3) ;
2210
ld (BASIC_DAC+3), a ;
2216
if defined BOOLEAN.OR
2218
BOOLEAN.OR.INT: ld a, (BASIC_DAC+2) ;
2219
ld hl, BASIC_ARG+2 ;
2221
ld (BASIC_DAC+2), a ;
2223
ld a, (BASIC_DAC+3) ;
2225
ld (BASIC_DAC+3), a ;
2231
if defined BOOLEAN.XOR
2233
BOOLEAN.XOR.INT: ld a, (BASIC_DAC+2) ;
2234
ld hl, BASIC_ARG+2 ;
2236
ld (BASIC_DAC+2), a ;
2238
ld a, (BASIC_DAC+3) ;
2240
ld (BASIC_DAC+3), a ;
2246
if defined BOOLEAN.EQV
2248
BOOLEAN.EQV.INT: ld a, (BASIC_DAC+2) ;
2249
ld hl, BASIC_ARG+2 ;
2252
ld (BASIC_DAC+2), a ;
2254
ld a, (BASIC_DAC+3) ;
2257
ld (BASIC_DAC+3), a ;
2263
if defined BOOLEAN.IMP
2265
BOOLEAN.IMP.INT: ld a, (BASIC_DAC+2) ;
2266
ld hl, BASIC_ARG+2 ;
2269
ld (BASIC_DAC+2), a ;
2271
ld a, (BASIC_DAC+3) ;
2274
ld (BASIC_DAC+3), a ;
2280
if defined BOOLEAN.SHR
2282
BOOLEAN.SHR.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to right (bits 15...0-->)
2283
ld a, (BASIC_ARG+2) ;
2285
jp z, MATH.PARM.PUSH ; return if not shift
2286
ld b, a ; shift count
2287
BOOLEAN.SHR.INT.N: rr (ix+1) ;
2290
djnz BOOLEAN.SHR.INT.N ; next shift
2292
jp MATH.PARM.PUSH ; return DAC
2296
if defined BOOLEAN.SHL
2298
BOOLEAN.SHL.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to left (<--bits 15...0)
2299
ld a, (BASIC_ARG+2) ;
2301
jp z, MATH.PARM.PUSH ; return if not shift
2302
ld b, a ; shift count
2303
BOOLEAN.SHL.INT.N: rl (ix) ;
2306
djnz BOOLEAN.SHL.INT.N ; next shift
2308
jp MATH.PARM.PUSH ; return DAC
2312
if defined BOOLEAN.NOT
2314
BOOLEAN.NOT.INT: ld a, (BASIC_DAC+2) ;
2316
ld (BASIC_DAC+2), a ;
2317
ld a, (BASIC_DAC+3) ;
2319
ld (BASIC_DAC+3), a ;
2327
;---------------------------------------------------------------------------------------------------------
2328
; MEMORY ALLOCATION ROUTINES
2329
;---------------------------------------------------------------------------------------------------------
2330
; Adapted from memory allocator code by SamSaga2, Spain, 2015
2331
; https://www.msx.org/forum/msx-talk/development/asm-memory-allocator
2332
; https://www.msx.org/users/samsaga2
2333
;---------------------------------------------------------------------------------------------------------
2334
memory.heap_start: equ VAR_STACK.END + 1 ; start at end of variable stack
2335
memory.heap_end: equ 0xF0A0 - 100 ; end at start of work area for stack (100 bytes reserved), BIOS and BASIC interpreter
2336
block.next: equ 0 ; next free block address
2337
block.size: equ 2 ; size of block including header
2338
block: equ 4 ; block.next + block.size
2342
ld ix,memory.heap_start ; first block
2343
ld hl,memory.heap_start+block ; second block
2344
;; first block NEXT=secondblock, SIZE=0
2345
;; with this block we have a fixed start location
2346
;; because never will be allocated
2347
ld (ix+block.next),l
2348
ld (ix+block.next+1),h
2349
ld (ix+block.size),0
2350
ld (ix+block.size+1),0
2351
;; second block NEXT=0, SIZE=all
2352
;; the first and only free block have all available memory
2353
ld (ix+block.next+block),0
2354
ld (ix+block.next+block+1),0
2356
;ld hl,memory.heap_end ; size = @heap_end (stack) - heap_start - block_header * 2 - 100 (buffer for stack)
2359
ld de, memory.heap_start + (block * 2) + 100
2361
;ld de, block * 2 + 100
2363
ld (ix+block.size+block),l
2364
ld (ix+block.size+block+1),h
2368
;; IN BC=size, OUT IX=memptr, NZ=ok
2376
ld ix,memory.heap_start ; this
2379
ld l,(ix+block.size)
2380
ld h,(ix+block.size+1)
2383
jp z, memory.alloc.exactfit
2384
jp c, memory.alloc.nextblock
2385
;; split found block
2386
memory.alloc.splitfit:
2387
;; free space must allow at least two blocks headers (current + next)
2389
jr nz, memory.alloc.splitfit.do ; if free space > 0xFF, do split
2392
jr c, memory.alloc.nextblock ; if free space < 4, skip to next block
2393
memory.alloc.splitfit.do:
2394
;; newfreeblock = this + BC
2398
;; prevblock->next = newfreeblock
2399
ld (iy+block.next),l
2400
ld (iy+block.next+1),h
2401
;; newfreeblock->next = this->next
2403
pop iy ; iy = newfreeblock
2404
ld l,(ix+block.next)
2405
ld h,(ix+block.next+1)
2406
ld (iy+block.next),l
2407
ld (iy+block.next+1),h
2408
;; newfreeblock->size = this->size - BC
2409
ld l,(ix+block.size)
2410
ld h,(ix+block.size+1)
2413
ld (iy+block.size),l
2414
ld (iy+block.size+1),h
2416
ld (ix+block.size),c
2417
ld (ix+block.size+1),b
2419
;; use whole found block
2420
memory.alloc.exactfit:
2421
;; prevblock->next = this->next - remove block from free list
2422
ld l,(ix+block.next)
2423
ld h,(ix+block.next+1)
2424
ld (iy+block.next),l
2425
ld (iy+block.next+1),h
2434
memory.alloc.nextblock:
2435
ld l,(ix+block.next)
2436
ld h,(ix+block.next+1)
2443
;; this = this->next
2446
jp memory.alloc.find
2451
;; HL = IX - block_header_size
2458
ld ix,memory.heap_start
2460
ld e,(ix+block.next)
2461
ld d,(ix+block.next+1)
2464
jp z, memory.free.passedend
2465
sbc hl,de ; test this (HL) against next (DE)
2466
jr c, memory.free.found ; if DE > HL
2467
add hl,de ; restore hl value
2469
pop ix ; current = next
2472
;; ix=prev, hl=this, de=next
2474
add hl,de ; restore hl value
2475
ld (ix+block.next), l
2476
ld (ix+block.next+1), h ; prev->next = this
2479
ld (iy+block.next), e
2480
ld (iy+block.next+1), d ; this->next = next
2481
push ix ; prev x this
2486
call memory.free.coalesce
2487
pop ix ; this x next
2488
jr memory.free.coalesce
2492
memory.free.coalesce:
2493
ld c, (iy+block.size)
2494
ld b, (iy+block.size+1) ; bc = this->size
2498
adc hl, bc ; hl = this + this->size
2502
sbc hl, de ; if this + this->size == next, then this->size += next->size, this->next = next->next
2503
jr z, memory.free.coalesce.do
2504
push ix ; else, new *this = *next
2507
memory.free.coalesce.do:
2508
ld l, (ix+block.size)
2509
ld h, (ix+block.size+1) ; hl = next->size
2511
adc hl, bc ; hl += this->size
2512
ld (iy+block.size), l
2513
ld (iy+block.size+1), h ; this->size = hl
2514
ld l, (ix+block.next)
2515
ld h, (ix+block.next+1) ; hl = next->next
2516
ld (iy+block.next), l
2517
ld (iy+block.next+1), h ; this->next = hl
2520
memory.free.passedend:
2521
;; append block at the end of the free list
2522
ld (ix+block.next),l
2523
ld (ix+block.next+1),h
2526
ld (iy+block.next),0
2527
ld (iy+block.next+1),0
2533
ld ix,memory.heap_start
2535
memory.get_free.count:
2537
add a,(ix+block.size)
2540
adc a,(ix+block.size+1)
2542
ld l,(ix+block.next)
2543
ld h,(ix+block.next+1)
2549
jr memory.get_free.count
2551
memory.error: ld e, 7 ; out of memory
2552
__call_basic BASIC_ERROR_HANDLER ;
2557
;---------------------------------------------------------------------------------------------------------
2559
;---------------------------------------------------------------------------------------------------------
2568
RET_MATH_LIB: call COPY_TO.TMP_DAC
2574
MATH_DECADD: ld ix, addSingle
2579
if defined MATH.SUB or defined MATH.NEG
2581
MATH_DECSUB: ld ix, subSingle
2586
if defined MATH.MULT
2588
MATH_DECMUL: ld ix, mulSingle
2595
MATH_DECDIV: ld ix, divSingle
2603
MATH_SNGEXP: ld ix, powSingle
2610
MATH_COS: ld ix, cosSingle
2617
MATH_SIN: ld ix, sinSingle
2624
MATH_TAN: ld ix, tanSingle
2631
MATH_ATN: ld ix, atanSingle
2638
MATH_SQR: ld ix, sqrtSingle
2645
MATH_LOG: ld ix, lnSingle
2652
MATH_EXP: ld ix, expSingle
2659
MATH_ABSFN: ld ix, absSingle
2664
if defined MATH.SEED or defined MATH.NEG
2666
MATH_NEG: ld ix, negSingle
2673
MATH_SGN: ld ix, sgnSingle
2678
if defined RND or defined MATH.SEED
2680
MATH_RND: ld ix, randSingle
2685
MATH_FRCINT: ld hl, BASIC_DAC
2698
ld (BASIC_VALTYP), a
2701
MATH_FRCDBL: ; same as MATH_FRCSGL
2702
MATH_FRCSGL: ld hl, BASIC_DAC+2 ; input address
2703
ld bc, BASIC_DAC ; output address
2706
ld (BASIC_VALTYP), a
2709
MATH_ICOMP: ld a, h ; cp hl, de (alternative to bios DCOMPR)
2711
jr nz, MATH_ICOMP.NE.HIGH
2714
jr nz, MATH_ICOMP.NE.LOW
2716
MATH_ICOMP.NE.HIGH: jr c, MATH_ICOMP.GT.HIGH
2718
jr nz, MATH_DCOMP.GT
2720
MATH_ICOMP.GT.HIGH: bit 7, d
2723
MATH_ICOMP.NE.LOW: jr c, MATH_DCOMP.GT
2726
MATH_XDCOMP: ; same as MATH_DCOMP
2727
MATH_DCOMP: ld ix, cmpSingle
2731
MATH_DCOMP.GT: ld a, 0xFF ; DAC > ARG
2733
MATH_DCOMP.EQ: ld a, 0 ; DAC = ARG
2735
MATH_DCOMP.LT: ld a, 1 ; DAC < ARG
2738
if defined CAST_STR_TO.VAL
2740
MATH_FIN: ; HL has the source string
2741
ld a, (BASIC_VALTYP)
2742
cp 2 ; test if integer
2744
;ld hl, (BASIC_DAC+2)
2745
;ld de, BASIC_STRBUF
2750
ld (BASIC_DAC+2), hl
2751
ld (BASIC_DAC+4), bc
2752
ld (BASIC_DAC+6), bc
2753
;ld hl, BASIC_STRBUF
2755
MATH_FIN.1: ld BC, BASIC_DAC
2761
if defined CAST_INT_TO.STR
2763
MATH_FOUT: ld a, (BASIC_VALTYP)
2764
cp 2 ; test if integer
2766
ld hl, (BASIC_DAC+2)
2771
MATH_FOUT.1: ld hl, BASIC_DAC
2782
;---------------------------------------------------------------------------------------------------------
2784
; Copyright 2018 Zeda A.K. Thomas
2785
;---------------------------------------------------------------------------------------------------------
2787
; https://github.com/Zeda/z80float
2788
; https://www.omnimaga.org/asm-language/(z80)-floating-point-routines/
2789
; https://en.wikipedia.org/wiki/Single-precision_floating-point_format
2790
;---------------------------------------------------------------------------------------------------------
2792
; HL points to the first operand
2793
; DE points to the second operand (if needed)
2794
; IX points to the third operand (if needed, rare)
2795
; BC points to where the result should be output
2796
; Floats are stored by a little-endian 24-bit mantissa. However, the highest bit
2797
; is taken as implicitly 1, so we replace it as a sign bit. Next comes an 8-bit
2798
; exponent biased by +128.
2799
;---------------------------------------------------------------------------------------------------------
2800
; Adapted to MSXBas2Asm by Amaury Carvalho, 2019
2801
;---------------------------------------------------------------------------------------------------------
2803
;---------------------------------------------------------------------------------------------------------
2805
;---------------------------------------------------------------------------------------------------------
2807
BASIC_HOLD8: equ 0xF806 ; 48 Work area for decimal multiplications.
2808
BASIC_HOLD2: equ 0xF836 ; 8 Work area in the execution of numerical operators.
2809
BASIC_HOLD: equ 0xF83E ; 8 Work area in the execution of numerical operators.
2810
scrap: equ BASIC_HOLD8
2811
seed0: equ BASIC_RNDX
2812
seed1: equ seed0 + 4
2813
var48: equ scrap + 4
2816
addend2: equ scrap+7 ;4 bytes
2817
var_x: equ BASIC_HOLD8 + 4 ;4 bytes
2818
var_y: equ var_x + 4 ;4 bytes
2819
var_z: equ var_y + 4 ;4 bytes
2820
var_a: equ var_z + 4 ;4 bytes
2821
var_b: equ var_a + 4 ;4 bytes
2822
var_c: equ var_b + 4 ;4 bytes
2823
temp: equ var_c + 4 ;4 bytes
2824
temp1: equ temp + 4 ;4 bytes
2825
temp2: equ temp1 + 4 ;4 bytes
2826
temp3: equ temp2 + 4 ;4 bytes
2828
pow10exp_single: equ scrap+9
2829
strout_single: equ 0xF750 ; PARM2 - BASIC_BUF ;pow10exp_single+2
2831
;---------------------------------------------------------------------------------------------------------
2833
;---------------------------------------------------------------------------------------------------------
2835
;;Still need to tend to special cases
2903
pop hl ;bigger float
3035
;;Need to adjust sign flag
3058
;;How many push/pops are needed?
3066
;;How many push/pops are needed?
3072
;;How many push/pops are needed?
3073
;;Return bigger number
3080
;---------------------------------------------------------------------------------------------------------
3082
;---------------------------------------------------------------------------------------------------------
3105
jp addInject ;jumps in to the addSingle routine
3107
;---------------------------------------------------------------------------------------------------------
3109
;---------------------------------------------------------------------------------------------------------
3112
;Inputs: HL points to float1, DE points to float2, BC points to where the result is copied
3113
;Outputs: float1*float2 is stored to (BC)
3114
;573+mul24+{0,35}+{0,30}
3117
;avg: 2055.13839751681cc
3143
;;return float in CHLB
3153
jr z,mulSingle_case0
3165
;jr z,mulSingle_case1
3169
jp z,mulSingle_case1
3174
rra ; |Lots of help from Runer112 and
3175
adc a,a ; |calc84maniac for optimizing
3176
jp po,bad ; |this exponent check.
3185
call mul24 ;BDE*CHL->HLBCDE, returns sign info
3242
;special*x = special
3263
;basically, if b|c has bit 5 set, return NaN
3296
;;avg :1464.9033203125cc (1464+925/1024)
3299
;avg: 1449.63839751681cc
3340
;---------------------------------------------------------------------------------------------------------
3342
;---------------------------------------------------------------------------------------------------------
3345
;;HL points to numerator
3346
;;DE points to denominator
3347
;;BC points to where the quotient gets written
3349
divSingle_no_pushpop:
3355
xor (hl) ; |Get sign of output
3362
ex de,hl ; |Get exponent
3469
call divsub1 ;34 or 66
3487
;34cc or 66cc or 93cc
3502
;---------------------------------------------------------------------------------------------------------
3504
; https://www.geeksforgeeks.org/write-a-c-program-to-calculate-powxn/
3505
; https://stackoverflow.com/questions/3518973/floating-point-exponentiation-without-power-function
3506
;---------------------------------------------------------------------------------------------------------
3507
;double mypow( double base, double power, double precision )
3509
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
3510
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
3511
; else if ( precision >= 1 ) {
3512
; if( base >= 0 ) return sqrt( base );
3513
; else return sqrt( -base );
3514
; } else return sqrt( mypow( base, power*2, precision*2 ) );
3517
if defined MATH.POW or defined MATH_EXP or defined MATH_LOG or defined MATH_LN
3523
;;BC points to output
3527
ld bc, var_y ; power
3532
ld hl, const_precision
3533
ld bc, var_a ; precision
3536
ld bc, var_z ; result
3545
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
3551
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
3557
; else if ( precision >= 1 ) {
3558
; if( base >= 0 ) return sqrt( base );
3559
; else return sqrt( -base );
3565
; } else return sqrt( mypow( base, power*2, precision*2 ) );
3590
; return 1 / mypow( base, -power, precision );
3609
; return base * mypow( base, power-1, precision );
3628
; if( base >= 0 ) return sqrt( base );
3629
; else return sqrt( -base );
3655
; 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)))))
3656
;Please note that usually I like to reduce to [-.5,.5] as the extra overhead is usually worth it.
3657
;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.
3660
;x-=int(x) ;leaves x in [0,1)
3662
;;if x==inf -> out==inf
3663
;;if x==-inf -> out==0
3664
;;if x==NAN -> out==NAN
3671
push af ;keep track of sign
3681
jr c,_pow_1 ;int(x)=0
3694
jr nz,exp_normalized
3705
jr exp_normalized ;.db $11 ;start of `ld de,**`
3712
jr comp_exp ;.db $06 ;start of 'ld b,*` just to eat the next byte
3721
jp z,exp_underflow+1
3722
;perform 1-(var48+10)--> var48+10
3730
;our 'x' is at var48+10
3731
;our `temp` is at var48+6 so as not to cause issues with mulSingle)
3732
;uses 14 bytes of RAM
3774
;-inf -> +0 because lim approaches 0 from the right
3796
;-inf -> +0 because lim approaches 0 from the right
3798
sbc a,a ;FF if should be 0,
3813
;---------------------------------------------------------------------------------------------------------
3815
;---------------------------------------------------------------------------------------------------------
3817
if defined MATH_SQR or defined MATH_EXP
3819
;Uses 3 bytes at scrap
3821
;552+{0,19}+8{0,3+{0,3}}+pushpop+sqrtHLIX
3840
jp z,sqrtSingle_special
3843
push af ;new exponent
3853
;AHL is the new remainder
3854
;Need to divide by 2, then divide by the 16-bit (var_x+4)
3858
;We are just going to approximate it
3940
;Output: DE is the sqrt, AHL is the remainder
3941
;speed: 754+{0,1}+6{0,6}+{0,3+{0,18}}+{0,38}+sqrtHL
3965
jr _15a ;.db $FE ;start of `cp *`
3979
jr _16a ;.db $FE ;start of `cp *`
3993
jr _17a ;.db $FE ;start of `cp *`
4007
jr _18a ;.db $FE ;start of `cp *`
4011
;Now we have four more iterations
4012
;The first two are no problem
4024
jr _19a ;.db $FE ;start of `cp *`
4038
jr _20a ;.db $FE ;start of `cp *`
4043
;On the next iteration, HL might temporarily overflow by 1 bit
4045
rl d ;sla e \ rl d \ inc e
4049
adc hl,hl ;This might overflow!
4050
jr c,sqrt32_iter15_br0
4063
;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
4066
ld b,a ;either 0x00 or 0x80
4087
;returns A as the sqrt, HL as the remainder, D = 0
4101
jr _23a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
4112
jr _24a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
4123
dec d ;this resets the low bit of D, so `srl d` resets carry.
4124
jr _25a ;.db $06 ;start of ld b,* which is 7cc to skip the next byte.
4146
jr _27a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
4159
jr _28a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
4181
;---------------------------------------------------------------------------------------------------------
4183
;---------------------------------------------------------------------------------------------------------
4185
if defined MATH_LOG or defined MATH_LN
4188
; x / (1 + x/(2-x+4x/(3-2x+9x/(4-3x+16x/(5-4x)))))
4189
; a * x ^ (1/a) - a, where a = 100
4192
ld de, const_100_inv
4194
call powSingle ; temp = x ^ (1/100)
4198
call mulSingle ; temp1 = temp * 100
4201
call subSingle ; bc = temp1 - 100
4206
;---------------------------------------------------------------------------------------------------------
4208
;---------------------------------------------------------------------------------------------------------
4225
;---------------------------------------------------------------------------------------------------------
4227
;---------------------------------------------------------------------------------------------------------
4234
;;BC points to the output
4239
;;DE points to lg(y), HL points to x, BC points to output
4248
;---------------------------------------------------------------------------------------------------------
4250
; https://en.wikipedia.org/wiki/List_of_trigonometric_identities
4251
; https://en.wikipedia.org/wiki/Taylor_series#Trigonometric_functions
4252
; https://cs.stackexchange.com/questions/89245/how-approximate-sine-using-taylor-series
4253
; https://stackoverflow.com/questions/42217069/approximating-sinex-with-a-taylor-series-in-c-and-having-a-lot-of-problems
4254
;---------------------------------------------------------------------------------------------------------
4256
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
4259
; taylor: x - x^3/6 + x^5/120 - x^7/5040
4260
; x(1 - x^2(1/6 - x^2(1/120 - x^2/5040)) )
4262
; var_b = round( x / (2*PI), 0 )
4263
; var_c = x - var_b*2*PI
4264
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
4265
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
4266
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
4273
call copySingle ; return 0
4277
call trigRangeReductionSinCos
4282
call mulSingle ; var_b = var_a * var_a
4286
call mulSingle ; temp = x^2/5040
4290
call subSingle ; temp1 = 1/120 - temp
4294
call mulSingle ; temp = x^2 * temp1
4298
call subSingle ; temp1 = 1/6 - temp
4302
call mulSingle ; temp = x^2 * temp1
4306
call subSingle ; temp1 = 1 - temp
4310
call mulSingle ; return x * temp1
4313
trigRangeReductionSinCos:
4316
; var_b = round( x / (2*PI), 0 )
4324
; var_c = x - var_b*2*PI
4328
call mulSingle ; temp = var_b*2*PI
4332
call subSingle ; var_c = x - temp
4333
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
4337
jr nc, trigRangeReductionSinCos.else.2
4340
call copySingle ; temp1 = var_c
4341
jr trigRangeReductionSinCos.endif.2
4342
trigRangeReductionSinCos.else.2:
4346
call addSingle ; temp1 = var_c + 2*PI
4347
trigRangeReductionSinCos.endif.2:
4348
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
4352
jr c, trigRangeReductionSinCos.else.3
4353
jr z, trigRangeReductionSinCos.else.3
4357
call subSingle ; temp2
4358
jr trigRangeReductionSinCos.endif.3
4359
trigRangeReductionSinCos.else.3:
4362
call copySingle ; temp2 = temp1
4363
trigRangeReductionSinCos.endif.3:
4364
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
4365
ld hl, const_half_pi
4368
jr c, trigRangeReductionSinCos.else.4
4369
jr z, trigRangeReductionSinCos.else.4
4373
call subSingle ; var_a
4374
jr trigRangeReductionSinCos.endif.4
4375
trigRangeReductionSinCos.else.4:
4378
call copySingle ; var_a = temp2
4379
trigRangeReductionSinCos.endif.4:
4380
; if( temp > PI, -1, 1 )
4384
jr nc, trigRangeReductionSinCos.endif.5
4388
ld (ix+2), a ; turn var_a to negative
4389
trigRangeReductionSinCos.endif.5:
4395
;---------------------------------------------------------------------------------------------------------
4397
;---------------------------------------------------------------------------------------------------------
4399
if defined MATH_COS or defined MATH_TAN
4402
; taylor: 1 - x^2/2 + x^4/24 - x^6/720
4403
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
4404
; reduction: same as sin
4413
call copySingle ; return 1
4417
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
4418
call trigRangeReductionSinCos
4423
call mulSingle ; var_b = var_a * var_a
4427
call mulSingle ; temp = x^2/720
4431
call subSingle ; temp1 = 1/24 - temp
4435
call mulSingle ; temp = x^2 * temp1
4439
call subSingle ; temp1 = 1/2 - temp
4443
call mulSingle ; temp = x^2 * temp1
4447
call subSingle ; temp1 = 1 - temp
4449
; temp3 = abs(var_c)
4450
; temp1 = temp1 * if( temp3 >= PI/2, -1, 1 ) ==> cos sign
4457
ld (ix+2), a ; temp3 = abs(var_c)
4459
ld de, const_half_pi
4460
call cmpSingle ; if temp3 >= PI/2 then temp1 = -temp1
4461
jr nc, cosSingle.endif.1
4465
ld (ix+2), a ; temp1 = -temp1
4469
call copySingle ; return temp1
4474
;---------------------------------------------------------------------------------------------------------
4476
;---------------------------------------------------------------------------------------------------------
4497
;---------------------------------------------------------------------------------------------------------
4499
;---------------------------------------------------------------------------------------------------------
4504
;taylor: x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
4505
; x < -1: atan - PI/2
4506
; x >= 1: PI/2 - atan
4507
;reduction: abs(X) > 1 : Y = 1 / X
4508
; abs(X) <= 1: Y = X
4517
call copySingle ; return 0
4521
;x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
4522
call trigRangeReductionAtan
4528
call mulSingle ; var_b = var_a * var_a
4532
call mulSingle ; temp = (4*x)^2
4536
call divSingle ; temp1 = temp/9
4540
call addSingle ; temp = 7 + temp1
4544
call mulSingle ; temp1 = var_b * 9
4548
call divSingle ; temp2 = temp1 / temp
4552
call addSingle ; temp = 5 + temp2
4556
call mulSingle ; temp1 = var_b * 4
4560
call divSingle ; temp2 = temp1 / temp
4564
call addSingle ; temp = 3 + temp2
4568
call divSingle ; temp2 = var_b / temp
4572
call addSingle ; temp = 1 + temp2
4576
call divSingle ; temp2 = var_a / temp
4578
; x >= 1: PI/2 - atan
4582
ld hl, const_half_pi
4589
; x < -1: atan - PI/2
4600
ld de, const_half_pi
4609
call copySingle ; return temp2
4612
trigRangeReductionAtan:
4613
;reduction: abs(X) > 1 : Y = 1 / X
4614
; abs(X) <= 1: Y = X
4623
ld (ix+2), a ; abs(x)
4627
jr nc, trigRangeReductionAtan.1
4633
jr trigRangeReductionAtan.2
4634
trigRangeReductionAtan.1:
4639
trigRangeReductionAtan.2:
4643
jr c, trigRangeReductionAtan.3
4647
ld (ix+2), a ; y = -y
4648
trigRangeReductionAtan.3:
4653
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
4655
;---------------------------------------------------------------------------------------------------------
4657
;---------------------------------------------------------------------------------------------------------
4671
;---------------------------------------------------------------------------------------------------------
4673
;---------------------------------------------------------------------------------------------------------
4742
if defined MATH_ABSFN
4744
;---------------------------------------------------------------------------------------------------------
4746
;---------------------------------------------------------------------------------------------------------
4749
;;HL points to the float
4750
;;BC points to where to output the result
4769
;---------------------------------------------------------------------------------------------------------
4771
;---------------------------------------------------------------------------------------------------------
4774
;;HL points to the float
4775
;;BC points to where to output the result
4780
if defined powSingle or defined sgnSingle or defined MATH_NEG
4782
;---------------------------------------------------------------------------------------------------------
4784
;---------------------------------------------------------------------------------------------------------
4787
;;HL points to the float
4788
;;BC points to where to output the result
4794
jr nz, negSingle.test.sign
4797
jr nz, negSingle.test.sign
4800
jr nz, negSingle.test.sign
4803
jr nz, negSingle.test.sign
4814
negSingle.test.sign:
4817
jr z, negSingle.positive
4821
call negSingle.positive
4840
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
4842
;---------------------------------------------------------------------------------------------------------
4844
;---------------------------------------------------------------------------------------------------------
4847
;Input: HL points to float1, DE points to float2
4849
; float1 >= float2 : nc
4850
; float1 < float2 : c,nz
4851
; float1 == float2 : z
4852
; There is a margin of error allowed in the lower 2 bits of the mantissa.
4854
;Currently fails when both numbers have magnitude less than about 2^-106
4889
ld a,(scrap+3) ;new power
4890
pop bc ;B is old power
4900
or 1 ;not equal, so reset z flag
4901
rla ;if negative, float1<float2, setting c flag as wanted, else nc.
4911
;---------------------------------------------------------------------------------------------------------
4913
;---------------------------------------------------------------------------------------------------------
4916
;Stores a pseudo-random number on [0,1)
4917
;it won't produce values on (0,2^-23)
4926
;DEHL is the mantissa, B is the exponent
4942
;If we needed to shift more than 8 bits, we'll load in more random data
4947
jp nc,rand_no_more_rand_data
4955
rand_no_more_rand_data:
4974
;;Tested and passes all CAcert tests
4975
;;Uses a very simple 32-bit LCG and 32-bit LFSR
4976
;;it has a period of 18,446,744,069,414,584,320
4977
;;roughly 18.4 quintillion.
4978
;;LFSR taps: 0,2,6,7 = 11000101
4980
;;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.
4981
;Uses 64 bits of state
5017
if defined MATH_FOUT
5019
;---------------------------------------------------------------------------------------------------------
5021
; in HL = Single address
5022
; BC = String address
5023
; out A = String size
5024
; http://0x80.pl/notesen/2015-12-29-float-to-string.html
5025
; http://0x80.pl/articles/convert-float-to-integer.html
5026
;---------------------------------------------------------------------------------------------------------
5040
; Move the float to scrap
5044
; Make the float negative, write a '-' if already negative
5053
ld a,'-' ; write '-' simbol
5061
; Check if the exponent field is 0 (a special value)
5068
; We should write '0' next. When rounding 9.999999... for example, not padding with a 0 will return '.' instead of '1.'
5076
; Now we need to perform signed (A-128)*77 (approximation of exponent*log10(2))
5084
ld (pow10exp_single),a ;The base-10 exponent
5088
ld de,pow10LUT ;get the table of 10^-(2^k)
5090
ld hl, pow10exp_single
5092
call singletostr_mul
5093
call singletostr_mul
5094
call singletostr_mul
5095
call singletostr_mul
5096
call singletostr_mul
5097
call singletostr_mul
5098
;now the number is pretty close to a nice value
5100
; If it is less than 1, multiply by 10
5105
;ld hl,scrap ;Since singletostr_mul returns BC = scrap, can do this cheaper
5111
ld hl,pow10exp_single
5117
; Convert to a fixed-point number !
5131
;We need to get 7 digits
5133
pop hl ;Points to the string
5135
;The first digit can be as large as 20, so it'll actually be two digits
5139
;Increment the exponent :)
5140
ld de,(pow10exp_single-1)
5142
ld (pow10exp_single-1),de
5151
; Get the remaining digits.
5158
call singletostrmul10
5163
;Save the pointer to the end of the string
5170
jr c,rounding_done_single
5171
jr _40a ;.db $DA ;start of `jp c,*` in order to skip the next instruction
5180
rounding_done_single:
5183
;Strip the leading zero if it exists (rounding may have bumped this to `1`)
5195
;Now lets move HL-DE bytes at DE+1 to DE
5207
;If z flag is reset, this means that the exponent should be bumped up 1
5208
ld a,(pow10exp_single)
5211
ld (pow10exp_single),a
5214
;if -4<=A<=6, then need to insert the decimal place somewhere.
5219
;for this, we need to insert the decimal after the first digit
5220
;Then, we need to append the exponent string
5222
ld de,strout_single-1
5224
cp '-' ;negative sign
5232
;remove any stray zeroes at the end before appending the exponent
5236
; Write the exponent
5239
ld a,(pow10exp_single)
5242
ld (hl),'-' ;negative sign
5260
ld de, strout_single
5263
ld a, l ; string size
5265
ld hl,strout_single-1
5269
ld a,(pow10exp_single)
5273
;need to put zeroes before everything
5276
cp '-' ;negative sign
5302
ld de,strout_single-1
5306
cp '-' ;negative sign
5317
ld hl,strout_single-1
5335
;multiply the 0.24 fixed point number at scrap by 10
5336
;overflow in A register
5371
;Check that the last digit isn't a decimal!
5425
;---------------------------------------------------------------------------------------------------------
5427
; https://www.ticalc.org/pub/86/asm/source/routines/atof.asm
5428
;---------------------------------------------------------------------------------------------------------
5433
ptr_sto: equ scrap+9
5435
;;#Routines/Single Precision
5437
;; HL points to the string
5438
;; BC points to where the float is output
5440
;; scrap+9 is the pointer to the end of the string
5442
;; 11 bytes at scrap ?
5447
;Check if there is a negative sign.
5456
;Skip all leading zeroes
5459
jr z,$-4 ;jumps back to the `inc hl`
5462
;Check if the next char is char_DEC
5464
or a ;to reset the carry flag
5466
jr _54a ;.db $FE ;start of cp *
5473
jr z,$-5 ;jumps back to the `dec b`
5476
;Now we read in the next 8 digits
5482
;Now `scrap` holds the 4-digit base-100 number.
5484
;if carry flag is set, just need to get rid of remaining digits
5485
;Otherwise, need to get rid of remaining digits, while incrementing the exponent
5496
jp z,strToSingle_inf
5499
;Now check for engineering `E` to modify the exponent
5503
;Gotta multiply the number at (scrap) by 2^24
5506
call scrap_times_256
5509
call scrap_times_256
5512
call scrap_times_256
5515
call scrap_times_256
5518
;Now scrap+3 is a 4-byte mantissa that needs to be normalized
5526
jp z,strToSingle_zero-1
5530
jp m,strToSingle_normed
5531
;Will need to iterate at most three times
5544
;Move the number to scrap
5553
;now (scrap) is our number, need to multiply by power of 10!
5554
;Power of 10 is stored in B, need to put in A first
5562
jp nc,strToSingle_inf+1
5565
jp nc,strToSingle_zero
5589
cp char_NEG ;negative exponent?
5641
call scrap_times_sub
5654
jr nz,strToSingle_inf
5672
if defined roundSingle or defined MATH_FRCSGL
5674
;---------------------------------------------------------------------------------------------------------
5676
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
5677
;---------------------------------------------------------------------------------------------------------
5684
ld l, (ix) ; convert integer parameter to single float
5686
ld bc, 0x1000 ; bynary digits count + sign
5688
int2Single.test.zero:
5690
or h ; test if hl is not zero
5691
jr nz, int2Single.test.negative
5693
jr nz, int2Single.test.negative
5698
int2Single.test.negative:
5699
bit 7, h ; test if hl is negative
5700
jr z, int2Single.normalize
5701
ld c, 0x80 ; sign negative
5710
int2Single.normalize:
5713
jr nz, int2Single.mount
5716
jr int2Single.normalize
5719
res 7, h ; turn off upper bit
5721
ld a, c ; restore sign
5723
ld h, a ; ...into upper mantissa
5725
ld e, h ; sign+mantissa
5726
ld h, l ; high mantissa
5727
ld l, 0 ; low mantissa
5729
ld a, b ; binary digits count
5730
or 0x80 ; exponent bias
5735
ld (ix), l ; low mantissa
5736
ld (ix+1), h ; high mantissa
5737
ld (ix+2), e ; sign + mantissa
5738
ld (ix+3), d ; expoent
5747
if defined roundSingle or defined MATH_FRCINT
5749
;---------------------------------------------------------------------------------------------------------
5751
; http://0x80.pl/articles/convert-float-to-integer.html
5752
;---------------------------------------------------------------------------------------------------------
5755
; HL points to the single-precision float
5757
; HL is the 16-bit signed integer part of the float
5758
; BC points to 16-bit signed integer
5775
jr c,no_shift_single_to_int16
5777
jr nc,no_shift_single_to_int16
5799
jr _67a ;.db $11 ;start of ld de,*
5811
no_shift_single_to_int16:
5833
;---------------------------------------------------------------------------------------------------------
5834
; Auxiliary routines
5835
;---------------------------------------------------------------------------------------------------------
5842
const_pi: db $DB,$0F,$49,$81
5843
const_e: db $54,$f8,$2d,$81
5844
const_lg_e: db $3b,$AA,$38,$80
5845
const_ln_2: db $18,$72,$31,$7f
5846
const_log2: db $9b,$20,$1a,$7e
5847
const_lg10: db $78,$9a,$54,$81
5848
const_0: db $00,$00,$00,$00
5849
const_1: db $00,$00,$00,$80
5850
const_2: dw 0, 33024
5851
const_3: dw 0, 33088
5852
const_4: dw 0, 33280
5853
const_5: dw 0, 33312
5854
const_7: dw 0, 33376
5855
const_9: dw 0, 33552
5856
const_16: dw 0, 33792
5857
const_100: db $00,$00,$48,$86
5858
const_100_inv: dw 55050, 31011
5859
const_precision: db $77,$CC,$2B,$65 ;10^-8
5860
const_half_1: dw 0, 32512
5861
const_inf: db $00,$00,$40,$00
5862
const_NegInf: db $00,$00,$C0,$00
5863
const_NaN: db $00,$00,$20,$00
5864
const_log10_e: db $D9,$5B,$5E,$7E
5865
const_2pi: db $DB,$0F,$49,$82
5866
const_2pi_inv: db $83,$F9,$22,$7D
5867
const_half_pi: dw 4059, 32841
5868
const_p25: db $00,$00,$00,$7E
5869
const_p5: db $00,$00,$00,$7F
5872
sin_a1: dw 43691, 32042
5873
sin_a2: dw 34952, 30984
5874
sin_a3: dw 3329, 29520
5875
cos_a1: equ const_half_1
5876
cos_a2: dw 43691, 31530
5877
cos_a3: dw 2914, 30262
5878
exp_a1: db $15,$72,$31,$7F ;.693146989552
5879
exp_a2: db $CE,$FE,$75,$7D ;.2402298085906
5880
exp_a3: db $7B,$42,$63,$7B ;.0554833215071
5881
exp_a4: db $FD,$94,$1E,$79 ;.00967907584392
5882
exp_a5: db $5E,$01,$23,$76 ;.001243632065103
5883
exp_a6: db $5F,$B7,$63,$73 ;.0002171671843714
5884
const_1p40625: db $00,$00,$34,$80 ;1.40625
5886
if defined MATH_CONSTSINGLE
5894
;A is the constant ID#
5895
;returns nc if failed, c otherwise
5896
;HL points to the constant
5897
cp (end_const-start_const)>>2
5904
;#if ((end_const-4)>>8)!=(start_const>>8)
5917
db $CD,$CC,$4C,$7C ;.1
5918
db $0A,$D7,$23,$79 ;.01
5919
db $17,$B7,$51,$72 ;.0001
5920
db $77,$CC,$2B,$65 ;10^-8
5921
db $95,$95,$66,$4A ;10^-16
5922
db $1F,$B1,$4F,$15 ;10^-32
5925
db $00,$00,$20,$83 ;10
5926
db $00,$00,$48,$86 ;100
5927
db $00,$40,$1C,$8D ;10000
5928
db $20,$BC,$3E,$9A ;10^8
5929
db $CA,$1B,$0E,$B5 ;10^16
5930
db $AE,$C5,$1D,$EA ;10^32
5937
;C>=128 135+6{0,33+{0,1}}+{0,20+{0,8}}
5938
;C>=64 115+5{0,33+{0,1}}+{0,20+{0,8}}
5939
;C>=32 95+4{0,33+{0,1}}+{0,20+{0,8}}
5940
;C>=16 75+3{0,33+{0,1}}+{0,20+{0,8}}
5941
;C>=8 55+2{0,33+{0,1}}+{0,20+{0,8}}
5942
;C>=4 35+{0,33+{0,1}}+{0,20+{0,8}}
5943
;C>=2 15+{0,20+{0,8}}
5946
;avg: 349.21279907227cc
6037
;26 bytes, adds 118cc to the traditional routine
6072
;c flag means don't increment the exponent
6075
jr c,ascii_to_uint8_noexp
6077
jr z,ascii_to_uint8_noexp-2
6081
jr nc,ascii_to_uint8_noexp_end
6093
jr z,ascii_to_uint8_noexp_2nd
6097
jr nc,ascii_to_uint8_noexp_end
6108
ascii_to_uint8_noexp:
6111
jr nc,ascii_to_uint8_noexp_end
6118
ascii_to_uint8_noexp_2nd:
6123
jr nc,ascii_to_uint8_noexp_end
6126
jr ascii_2 ;.db $FE ;start of `cp **`, saves 1cc
6127
ascii_to_uint8_noexp_end:
6137
if defined MATH_RSUBSINGLE
6158
jp addInject ;jumps in to the addSingle routine
6162
if defined MATH_MOD1SINGLE
6164
;This routine performs `x mod 1`, returning a non-negative value.
6187
jr z,mod1Single_special
6200
;If it is zero, need to set exponent to zero and return
6223
;make sure it isn't zero else we need to add 1
6235
;If INF, need to return NaN instead
6236
;For 0 and NaN, just return itself :)
6256
if defined MATH_FOUT
6258
; --------------------------------------------------------------
6259
; Converts a signed integer value to a zero-terminated ASCII
6260
; string representative of that value (using radix 10).
6262
; Brandon Wilson WikiTI
6263
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Other:DispA#Decimal_Signed_Version
6264
; --------------------------------------------------------------
6266
; HL Value to convert (two's complement integer).
6267
; DE Base address of string destination. (pointer).
6268
; --------------------------------------------------------------
6271
; --------------------------------------------------------------
6272
; REGISTERS/MEMORY DESTROYED
6274
; --------------------------------------------------------------
6280
; Detect sign of HL.
6284
; HL is negative. Output '-' to string and negate HL.
6289
; Negate HL (using two's complement)
6293
ld a, 0 ; Note that XOR A or SUB A would disturb CF
6297
; Convert HL to digit characters
6299
ld b, 0 ; B will count character length of number
6302
call div_hl_c; HL = HL / A, A = remainder
6309
; Retrieve digits from stack
6317
; Terminate string with NULL
6328
ld a, l ; string size
6336
;===============================================================
6337
; Convert a string of base-10 digits to a 16-bit value.
6338
; http://z80-heaven.wikidot.com/math#toc32
6340
; DE points to the base 10 number string in RAM.
6342
; HL is the 16-bit value of the number
6343
; DE points to the byte after the number
6348
; A (actually, add 30h and you get the ending token)
6351
; n is the number of digits
6353
; at most 595 cycles for any 16-bit decimal value
6354
;===============================================================
6357
ld hl,0 ; 10 : 210000
6359
StrToInt.Skip_spaces:
6365
jr z, StrToInt.Skip_spaces
6369
jr nz, StrToInt.ConvLoop
6371
StrToInt.ConvLoop: ;
6389
jr nc, StrToInt.ConvLoop ;12|23: 30EE
6391
jr StrToInt.ConvLoop ; --- : 18EB
6409
; return remainder in a
6410
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division
6431
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
6461
djnz div_dehl_c.loop
6469
;---------------------------------------------------------------------------------------------------------
6470
; VARIABLES INITIALIZE
6471
;---------------------------------------------------------------------------------------------------------
6475
ld (VAR_DUMMY.COUNTER), a ; max circular queue = 8 dummys
6476
ld hl, VAR_DUMMY.DATA ; start of variable dummy circular queue
6477
ld (VAR_DUMMY.POINTER), hl
6478
ld b, VAR_DUMMY.LENGTH
6483
djnz INITIALIZE_DUMMY.1
6488
ld (BASIC_DATPTR), hl ; next DATA pointer to use by READ command
6490
ld (BASIC_DATLIN), hl ; index of DATA item to use by READ command
6493
INITIALIZE_VARIABLES:
6494
call INITIALIZE_DATA
6495
call INITIALIZE_DUMMY
6498
call gfxInitSpriteCollisionTable
6501
;if defined COMPILE_TO_ROM
6502
; ld ix, BIOS_JIFFY ; initialize rom clock
6511
ld c, 0 ; variable name 1 (variable number)
6512
ld b, 255 ; variable name 2 (type flag=fixed)
6513
call INIT_VAR ; variable initialize
6516
ld c, 1 ; variable name 1 (variable number)
6517
ld b, 255 ; variable name 2 (type flag=fixed)
6518
call INIT_VAR ; variable initialize
6521
ld c, 2 ; variable name 1 (variable number)
6522
ld b, 255 ; variable name 2 (type flag=fixed)
6523
call INIT_VAR ; variable initialize
6527
;---------------------------------------------------------------------------------------------------------
6528
; MAIN WORK AREA - LITERALS / VARIABLES / CONFIGURATIONS
6529
;---------------------------------------------------------------------------------------------------------
6531
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6534
pgmPage1.pad: equ pageSize - (workAreaPad - pgmArea)
6536
if pgmPage1.pad >= 0
6539
; .WARNING "There's no free space left on program page 1"
6544
VAR_STACK.START: equ ramArea
6545
;VAR_STACK.END: equ VAR_STACK.START + 0x800 ; 2kb (~200 variables)
6547
VAR_STACK.POINTER: equ VAR_STACK.START
6549
PRINT.CRLF: db 3, 0, 0, 2
6550
dw PRINT.CRLF.DATA, 0, 0, 0
6551
PRINT.CRLF.DATA: db 13,10,0
6553
PRINT.TAB: db 3, 0, 0, 1
6554
dw PRINT.TAB.DATA, 0, 0, 0
6555
PRINT.TAB.DATA: db 09,0
6558
LIT_NULL_DBL: dw 0, 0, 0, 0
6564
LIT_QUOTE_CHAR: db '\"'
6567
LIT_TRUE: db 2, 0, 0
6571
LIT_FALSE: db 2, 0, 0
6576
LIT_5: db 3, 0, 0, 29
6579
LIT_5_DATA: db "<<<< IF - COMPARE FLOATS >>>>", 0
6582
LIT_6: db 3, 0, 0, 7
6585
LIT_6_DATA: db "VALUE A", 0
6588
IDF_8: equ VAR_STACK.POINTER + 0
6591
LIT_9: db 3, 0, 0, 7
6594
LIT_9_DATA: db "VALUE B", 0
6597
IDF_10: equ VAR_STACK.POINTER + 11
6600
IDF_11: equ VAR_STACK.POINTER + 22
6602
; double decimal literal
6604
dw 62915, 33096, 0, 0
6605
;LIT_12_DATA: db '3.14',0
6608
LIT_13: db 3, 0, 0, 4
6609
dw LIT_13_DATA, 0, 0
6611
LIT_13_DATA: db "A = ", 0
6614
LIT_14: db 3, 0, 0, 5
6615
dw LIT_14_DATA, 0, 0
6617
LIT_14_DATA: db " B = ", 0
6620
LIT_15: db 3, 0, 0, 5
6621
dw LIT_15_DATA, 0, 0
6623
LIT_15_DATA: db " C = ", 0
6634
LIT_24: db 3, 0, 0, 15
6635
dw LIT_24_DATA, 0, 0
6637
LIT_24_DATA: db "A IS EQUAL TO B", 0
6640
LIT_26: db 3, 0, 0, 19
6641
dw LIT_26_DATA, 0, 0
6643
LIT_26_DATA: db "A IS GREATER THAN B", 0
6646
LIT_29: db 3, 0, 0, 16
6647
dw LIT_29_DATA, 0, 0
6649
LIT_29_DATA: db "A IS LESS THAN B", 0
6655
AFTER_LAST_VARIABLE: equ VAR_STACK.POINTER + 33
6657
VAR_DUMMY.START: equ AFTER_LAST_VARIABLE ; variable dummy circular queue area
6658
VAR_DUMMY.COUNTER: equ VAR_DUMMY.START ; variable dummy circular queue count
6659
VAR_DUMMY.POINTER: equ VAR_DUMMY.COUNTER + 1 ; pointer to next variable dummy
6660
VAR_DUMMY.DATA: equ VAR_DUMMY.POINTER + 2 ; first variable dummy
6662
VAR_DUMMY.SIZE: equ 8
6663
VAR_DUMMY.LENGTH: equ (11 * VAR_DUMMY.SIZE)
6664
VAR_DUMMY.END: equ VAR_DUMMY.DATA + VAR_DUMMY.LENGTH
6665
VAR_STACK.END: equ VAR_DUMMY.END + 1
6667
;--------------------------------------------------------
6669
;--------------------------------------------------------
6672
DATA_ITEMS_COUNT: equ 0
6674
DATA_SET_ITEMS_START:
6675
DATA_SET_ITEMS_COUNT: equ 0
6678
;---------------------------------------------------------------------------------------------------------
6679
; PROGRAM BASIC ROM HOOKS
6680
;---------------------------------------------------------------------------------------------------------
6682
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6687
__call_bios BIOS_ENASLT ; Select main ROM on page 1 (4000h~7FFFh)
6690
PROGRAM_SLOT_1_ENABLE:
6696
and 3 ;Keep bits corresponding to the page
6713
jp BIOS_ENASLT ; Select the ROM on page 4000h-7FFFh
6720
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6721
ld de, PROGRAM_SLOT_1_ENABLE
6729
push hl ; address of string
6730
push af ; size of string
6738
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6739
call BASIC_SLOT_ENABLE
6742
jp BASIC_DRAW_DIRECT
6746
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
6749
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6750
ld de, PROGRAM_SLOT_1_ENABLE
6758
ld hl, BIOS_TEMP ; voice count
6773
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6774
call BASIC_SLOT_ENABLE
6777
jp BASIC_PLAY_DIRECT
6781
if defined gfxBorderFill
6784
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6785
ld de, PROGRAM_SLOT_1_ENABLE
6789
ld bc, (BIOS_GRPACX)
6791
ld de, (BIOS_GRPACY)
6812
call gfxIsScreenModeMSX2
6813
jr nc, PAINT_HOOK.1 ; if MSX2 and screen mode above 3, jump (BASIC_SUB_PAINT2)
6818
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6819
call BASIC_SLOT_ENABLE
6831
;---------------------------------------------------------------------------------------------------------
6833
;---------------------------------------------------------------------------------------------------------
6835
if defined COMPILE_TO_ROM or defined COMPILE_TO_DOS
6839
pgmPage2.pad: equ romSize - (romPad - pgmArea)
6841
if pgmPage2.pad >= 0
6844
if pgmPage2.pad < lowLimitSize
6845
.WARNING "There's only less than 5% free space on this ROM"
6848
.ERROR "There's no free space left on this ROM"
6853
end_file: end start_pgm ; label start is the entry point